1
0

Continue thread-based VM refactor

This commit is contained in:
Jeff 2024-12-17 07:10:47 -05:00
parent bd590e0643
commit 4527f7b6ef
26 changed files with 566 additions and 340 deletions

View File

@ -196,10 +196,10 @@ fn main() {
} }
} }
let chunk = compiler.finish(None, None); let chunk = compiler.finish();
let compile_end = start_time.elapsed(); let compile_end = start_time.elapsed();
let vm = Vm::new(&chunk, None, None); let vm = Vm::new(chunk);
let return_value = vm.run(); let return_value = vm.run();
let run_end = start_time.elapsed(); let run_end = start_time.elapsed();

View File

@ -231,7 +231,7 @@ impl AnnotatedError for CompileError {
) )
] ]
} }
_ => todo!(), _ => SmallVec::new(),
} }
} }
@ -248,7 +248,7 @@ impl AnnotatedError for CompileError {
Span(left_position.0, right_position.1) Span(left_position.0, right_position.1)
)] )]
} }
_ => todo!(), _ => SmallVec::new(),
} }
} }
} }

View File

@ -20,8 +20,7 @@ use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
instruction::{ instruction::{
CallNative, Close, GetLocal, Jump, LoadList, LoadSelf, Negate, Not, Point, Return, CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Point, Return, SetLocal, Test,
SetLocal, Test,
}, },
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value, NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
@ -46,7 +45,7 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
.compile() .compile()
.map_err(|error| DustError::compile(error, source))?; .map_err(|error| DustError::compile(error, source))?;
let chunk = compiler.finish(None, None); let chunk = compiler.finish();
Ok(chunk) Ok(chunk)
} }
@ -63,9 +62,9 @@ pub struct Compiler<'src> {
/// [`Compiler::finish`] is called. /// [`Compiler::finish`] is called.
self_name: Option<DustString>, self_name: Option<DustString>,
/// Return type of the function being compiled. This is assigned to the chunk when /// Type of the function being compiled. This is assigned to the chunk when [`Compiler::finish`]
/// [`Compiler::finish`] is called. /// is called.
return_type: Option<Type>, r#type: FunctionType,
/// Instructions, along with their types and positions, that have been compiled. The /// Instructions, along with their types and positions, that have been compiled. The
/// instructions and positions are assigned to the chunk when [`Compiler::finish`] is called. /// instructions and positions are assigned to the chunk when [`Compiler::finish`] is called.
@ -76,8 +75,8 @@ pub struct Compiler<'src> {
/// is called. /// is called.
constants: SmallVec<[Value; 16]>, constants: SmallVec<[Value; 16]>,
/// Locals that have been compiled. These are assigned to the chunk when [`Compiler::finish`] is /// Block-local variables and their types. The locals are assigned to the chunk when
/// called. /// [`Compiler::finish`] is called. The types are discarded after compilation.
locals: SmallVec<[(Local, Type); 8]>, locals: SmallVec<[(Local, Type); 8]>,
/// Prototypes that have been compiled. These are assigned to the chunk when /// Prototypes that have been compiled. These are assigned to the chunk when
@ -108,6 +107,12 @@ pub struct Compiler<'src> {
/// Index of the record (i.e. runtime data) that the VM will use when calling the function. This /// Index of the record (i.e. runtime data) that the VM will use when calling the function. This
/// is a depth-first index. /// is a depth-first index.
record_index: u8, record_index: u8,
/// Record index for the next nested chunk that is compiled. When a function is compiled, its
/// `record_index` is assigned from this value. Then `next_record_index` is incremented to
/// maintain the depth-first index. After the function is compiled, the its `next_record_index`
/// is assigned to this chunk.
next_record_index: u8,
} }
impl<'src> Compiler<'src> { impl<'src> Compiler<'src> {
@ -122,6 +127,11 @@ impl<'src> Compiler<'src> {
Ok(Compiler { Ok(Compiler {
self_name: None, self_name: None,
r#type: FunctionType {
type_parameters: None,
value_parameters: None,
return_type: Type::None,
},
instructions: SmallVec::new(), instructions: SmallVec::new(),
constants: SmallVec::new(), constants: SmallVec::new(),
locals: SmallVec::new(), locals: SmallVec::new(),
@ -132,26 +142,17 @@ impl<'src> Compiler<'src> {
current_position, current_position,
previous_token: Token::Eof, previous_token: Token::Eof,
previous_position: Span(0, 0), previous_position: Span(0, 0),
return_type: None,
minimum_register: 0, minimum_register: 0,
block_index: 0, block_index: 0,
current_scope: Scope::default(), current_scope: Scope::default(),
record_index: 0, record_index: 0,
next_record_index: 1,
}) })
} }
pub fn finish( pub fn finish(self) -> Chunk {
self,
type_parameters: Option<SmallVec<[u8; 4]>>,
value_parameters: Option<SmallVec<[(u8, Type); 4]>>,
) -> Chunk {
log::info!("End chunk"); log::info!("End chunk");
let r#type = FunctionType {
type_parameters,
value_parameters,
return_type: self.return_type.unwrap_or(Type::None),
};
let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self let (instructions, positions): (SmallVec<[Instruction; 32]>, SmallVec<[Span; 32]>) = self
.instructions .instructions
.into_iter() .into_iter()
@ -165,7 +166,7 @@ impl<'src> Compiler<'src> {
Chunk::new( Chunk::new(
self.self_name, self.self_name,
r#type, self.r#type,
instructions, instructions,
positions, positions,
self.constants, self.constants,
@ -400,26 +401,22 @@ impl<'src> Compiler<'src> {
}) })
} }
/// Updates [Self::return_type] with the given [Type]. /// Updates [`Self::type`] with the given [Type] as `return_type`.
/// ///
/// If [Self::return_type] is already set, it will check if the given [Type] is compatible with /// If [`Self::type`] is already set, it will check if the given [Type] is compatible.
/// it and set it to the least restrictive of the two.
fn update_return_type(&mut self, new_return_type: Type) -> Result<(), CompileError> { fn update_return_type(&mut self, new_return_type: Type) -> Result<(), CompileError> {
if let Some(return_type) = &self.return_type { if self.r#type.return_type != Type::None {
return_type.check(&new_return_type).map_err(|conflict| { self.r#type
CompileError::ReturnTypeConflict { .return_type
.check(&new_return_type)
.map_err(|conflict| CompileError::ReturnTypeConflict {
conflict, conflict,
position: self.current_position, position: self.current_position,
} })?;
})?;
if *return_type != Type::Any {
self.return_type = Some(new_return_type);
};
} else {
self.return_type = Some(new_return_type);
} }
self.r#type.return_type = new_return_type;
Ok(()) Ok(())
} }
@ -1026,9 +1023,9 @@ impl<'src> Compiler<'src> {
return self.parse_call_native(native_function); return self.parse_call_native(native_function);
} else if self.self_name.as_deref() == Some(identifier) { } else if self.self_name.as_deref() == Some(identifier) {
let destination = self.next_register(); let destination = self.next_register();
let load_self = Instruction::from(LoadSelf { destination }); let load_function = Instruction::load_function(destination, self.record_index);
self.emit_instruction(load_self, Type::SelfFunction, start_position); self.emit_instruction(load_function, Type::SelfFunction, start_position);
return Ok(()); return Ok(());
} else { } else {
@ -1572,8 +1569,8 @@ impl<'src> Compiler<'src> {
let function_start = self.current_position.0; let function_start = self.current_position.0;
let mut function_compiler = Compiler::new(self.lexer)?; let mut function_compiler = Compiler::new(self.lexer)?;
self.record_index += 1; function_compiler.record_index = self.next_record_index;
function_compiler.record_index = self.record_index; function_compiler.next_record_index = self.next_record_index + 1;
let identifier_info = if let Token::Identifier(text) = function_compiler.current_token { let identifier_info = if let Token::Identifier(text) = function_compiler.current_token {
let position = function_compiler.current_position; let position = function_compiler.current_position;
@ -1646,8 +1643,13 @@ impl<'src> Compiler<'src> {
} else { } else {
Type::None Type::None
}; };
let function_type = FunctionType {
type_parameters: None,
value_parameters,
return_type,
};
function_compiler.return_type = Some((return_type).clone()); function_compiler.r#type = function_type.clone();
function_compiler.expect(Token::LeftBrace)?; function_compiler.expect(Token::LeftBrace)?;
function_compiler.compile()?; function_compiler.compile()?;
@ -1657,20 +1659,15 @@ impl<'src> Compiler<'src> {
self.previous_position = function_compiler.previous_position; self.previous_position = function_compiler.previous_position;
self.current_token = function_compiler.current_token; self.current_token = function_compiler.current_token;
self.current_position = function_compiler.current_position; self.current_position = function_compiler.current_position;
self.record_index = function_compiler.record_index; self.next_record_index = function_compiler.next_record_index;
self.lexer.skip_to(self.current_position.1); self.lexer.skip_to(self.current_position.1);
let function_end = function_compiler.previous_position.1; let function_end = function_compiler.previous_position.1;
let prototype = function_compiler.finish(None, value_parameters.clone()); let chunk = function_compiler.finish();
let destination = self.next_register(); let destination = self.next_register();
let function_type = FunctionType {
type_parameters: None,
value_parameters,
return_type,
};
self.prototypes.push(prototype); self.prototypes.push(chunk);
if let Some((identifier, _)) = identifier_info { if let Some((identifier, _)) = identifier_info {
self.declare_local( self.declare_local(
@ -1680,20 +1677,24 @@ impl<'src> Compiler<'src> {
false, false,
self.current_scope, self.current_scope,
); );
} else {
let load_function = Instruction::load_function(destination, self.record_index);
self.emit_instruction(
load_function,
Type::function(function_type),
Span(function_start, function_end),
);
} }
let load_function = Instruction::load_function(destination, self.record_index);
self.emit_instruction(
load_function,
Type::function(function_type),
Span(function_start, function_end),
);
Ok(()) Ok(())
} }
fn parse_call(&mut self) -> Result<(), CompileError> { fn parse_call(&mut self) -> Result<(), CompileError> {
let start = self.current_position.0;
self.advance()?;
let (last_instruction, last_instruction_type, _) = let (last_instruction, last_instruction_type, _) =
self.instructions self.instructions
.last() .last()
@ -1710,10 +1711,10 @@ impl<'src> Compiler<'src> {
}); });
} }
let prototype_index = last_instruction.b_field(); let function_register = last_instruction.a_field();
let function_return_type = match last_instruction_type { let function_return_type = match last_instruction_type {
Type::Function(function_type) => function_type.return_type.clone(), Type::Function(function_type) => function_type.return_type.clone(),
Type::SelfFunction => self.return_type.clone().unwrap_or(Type::None), Type::SelfFunction => self.r#type.return_type.clone(),
_ => { _ => {
return Err(CompileError::ExpectedFunction { return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(), found: self.previous_token.to_owned(),
@ -1722,9 +1723,6 @@ impl<'src> Compiler<'src> {
}); });
} }
}; };
let start = self.current_position.0;
self.advance()?;
let mut argument_count = 0; let mut argument_count = 0;
@ -1752,7 +1750,7 @@ impl<'src> Compiler<'src> {
let end = self.current_position.1; let end = self.current_position.1;
let destination = self.next_register(); let destination = self.next_register();
let call = Instruction::call(destination, prototype_index, argument_count); let call = Instruction::call(destination, function_register, argument_count);
self.emit_instruction(call, function_return_type, Span(start, end)); self.emit_instruction(call, function_return_type, Span(start, end));

View File

@ -5,7 +5,7 @@ use std::fmt::{self, Display, Formatter};
use annotate_snippets::{Level, Renderer, Snippet}; use annotate_snippets::{Level, Renderer, Snippet};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{CompileError, Span}; use crate::{CompileError, NativeFunctionError, Span};
/// A top-level error that can occur during the interpretation of Dust code. /// A top-level error that can occur during the interpretation of Dust code.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -14,6 +14,10 @@ pub enum DustError<'src> {
error: CompileError, error: CompileError,
source: &'src str, source: &'src str,
}, },
NativeFunction {
error: NativeFunctionError,
source: &'src str,
},
} }
impl<'src> DustError<'src> { impl<'src> DustError<'src> {
@ -57,12 +61,19 @@ impl<'src> DustError<'src> {
error.detail_snippets(), error.detail_snippets(),
error.help_snippets(), error.help_snippets(),
), ),
Self::NativeFunction { error, .. } => (
NativeFunctionError::title(),
error.description(),
error.detail_snippets(),
error.help_snippets(),
),
} }
} }
fn source(&self) -> &str { fn source(&self) -> &str {
match self { match self {
Self::Compile { source, .. } => source, Self::Compile { source, .. } => source,
Self::NativeFunction { source, .. } => source,
} }
} }
} }

View File

@ -1,20 +1,36 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct Call { pub struct Call {
pub destination: u8, pub destination: u8,
pub prototype_index: u8, pub function_register: u8,
pub argument_count: u8, pub argument_count: u8,
} }
impl From<&Instruction> for Call { impl From<&Instruction> for Call {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let prototype_index = instruction.b_field(); let function_register = instruction.b_field();
let argument_count = instruction.c_field(); let argument_count = instruction.c_field();
Call { Call {
destination, destination,
prototype_index, function_register,
argument_count,
}
}
}
impl From<InstructionData> for Call {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
let function_register = instruction.b_field;
let argument_count = instruction.c_field;
Call {
destination,
function_register,
argument_count, argument_count,
} }
} }
@ -23,7 +39,7 @@ impl From<&Instruction> for Call {
impl From<Call> for Instruction { impl From<Call> for Instruction {
fn from(call: Call) -> Self { fn from(call: Call) -> Self {
let a = call.destination; let a = call.destination;
let b = call.prototype_index; let b = call.function_register;
let c = call.argument_count; let c = call.argument_count;
Instruction::new(Operation::CALL, a, b, c, false, false, false) Instruction::new(Operation::CALL, a, b, c, false, false, false)

View File

@ -1,5 +1,7 @@
use crate::{Instruction, NativeFunction, Operation}; use crate::{Instruction, NativeFunction, Operation};
use super::InstructionData;
pub struct CallNative { pub struct CallNative {
pub destination: u8, pub destination: u8,
pub function: NativeFunction, pub function: NativeFunction,
@ -19,6 +21,16 @@ impl From<&Instruction> for CallNative {
} }
} }
impl From<InstructionData> for CallNative {
fn from(data: InstructionData) -> Self {
CallNative {
destination: data.a_field,
function: NativeFunction::from(data.b_field),
argument_count: data.c_field,
}
}
}
impl From<CallNative> for Instruction { impl From<CallNative> for Instruction {
fn from(call_native: CallNative) -> Self { fn from(call_native: CallNative) -> Self {
let operation = Operation::CALL_NATIVE; let operation = Operation::CALL_NATIVE;

View File

@ -19,8 +19,8 @@ impl From<&Instruction> for Close {
impl From<InstructionData> for Close { impl From<InstructionData> for Close {
fn from(instruction: InstructionData) -> Self { fn from(instruction: InstructionData) -> Self {
Close { Close {
from: instruction.b, from: instruction.b_field,
to: instruction.c, to: instruction.c_field,
} }
} }
} }

View File

@ -10,9 +10,9 @@ pub struct LoadBoolean {
impl From<InstructionData> for LoadBoolean { impl From<InstructionData> for LoadBoolean {
fn from(instruction: InstructionData) -> Self { fn from(instruction: InstructionData) -> Self {
let destination = instruction.a; let destination = instruction.a_field;
let value = instruction.b != 0; let value = instruction.b_field != 0;
let jump_next = instruction.c != 0; let jump_next = instruction.c_field != 0;
LoadBoolean { LoadBoolean {
destination, destination,

View File

@ -26,9 +26,9 @@ impl From<&Instruction> for LoadConstant {
impl From<InstructionData> for LoadConstant { impl From<InstructionData> for LoadConstant {
fn from(instruction: InstructionData) -> Self { fn from(instruction: InstructionData) -> Self {
let destination = instruction.a; let destination = instruction.a_field;
let constant_index = instruction.b; let constant_index = instruction.b_field;
let jump_next = instruction.c != 0; let jump_next = instruction.c_field != 0;
LoadConstant { LoadConstant {
destination, destination,

View File

@ -1,32 +1,39 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use super::{Instruction, Operation}; use super::{Instruction, InstructionData, Operation};
pub struct LoadFunction { pub struct LoadFunction {
pub destination: u8, pub destination: u8,
pub prototype_index: u8, pub record_index: u8,
} }
impl From<&Instruction> for LoadFunction { impl From<&Instruction> for LoadFunction {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let destination = instruction.a_field(); let destination = instruction.a_field();
let prototype_index = instruction.b_field(); let record_index = instruction.b_field();
LoadFunction { LoadFunction {
destination, destination,
prototype_index, record_index,
}
}
}
impl From<InstructionData> for LoadFunction {
fn from(instruction: InstructionData) -> Self {
LoadFunction {
destination: instruction.a_field,
record_index: instruction.b_field,
} }
} }
} }
impl From<LoadFunction> for Instruction { impl From<LoadFunction> for Instruction {
fn from(load_function: LoadFunction) -> Self { fn from(load_function: LoadFunction) -> Self {
let operation = Operation::LOAD_FUNCTION;
Instruction::new( Instruction::new(
operation, Operation::LOAD_FUNCTION,
load_function.destination, load_function.destination,
load_function.prototype_index, load_function.record_index,
0, 0,
false, false,
false, false,
@ -37,6 +44,6 @@ impl From<LoadFunction> for Instruction {
impl Display for LoadFunction { impl Display for LoadFunction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "R{} = P{}", self.destination, self.prototype_index) write!(f, "R{} = P{}", self.destination, self.record_index)
} }
} }

View File

@ -1,5 +1,7 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadList { pub struct LoadList {
pub destination: u8, pub destination: u8,
pub start_register: u8, pub start_register: u8,
@ -17,6 +19,18 @@ impl From<&Instruction> for LoadList {
} }
} }
impl From<InstructionData> for LoadList {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
let start_register = instruction.b_field;
LoadList {
destination,
start_register,
}
}
}
impl From<LoadList> for Instruction { impl From<LoadList> for Instruction {
fn from(load_list: LoadList) -> Self { fn from(load_list: LoadList) -> Self {
let operation = Operation::LOAD_LIST; let operation = Operation::LOAD_LIST;

View File

@ -1,5 +1,7 @@
use crate::{Instruction, Operation}; use crate::{Instruction, Operation};
use super::InstructionData;
pub struct LoadSelf { pub struct LoadSelf {
pub destination: u8, pub destination: u8,
} }
@ -12,6 +14,14 @@ impl From<&Instruction> for LoadSelf {
} }
} }
impl From<InstructionData> for LoadSelf {
fn from(instruction: InstructionData) -> Self {
let destination = instruction.a_field;
LoadSelf { destination }
}
}
impl From<LoadSelf> for Instruction { impl From<LoadSelf> for Instruction {
fn from(load_self: LoadSelf) -> Self { fn from(load_self: LoadSelf) -> Self {
let operation = Operation::LOAD_SELF; let operation = Operation::LOAD_SELF;

View File

@ -154,16 +154,6 @@ use crate::NativeFunction;
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Instruction(u32); pub struct Instruction(u32);
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionData {
pub a: u8,
pub b: u8,
pub c: u8,
pub b_is_constant: bool,
pub c_is_constant: bool,
pub d: bool,
}
impl Instruction { impl Instruction {
pub fn new( pub fn new(
operation: Operation, operation: Operation,
@ -231,12 +221,12 @@ impl Instruction {
( (
self.operation(), self.operation(),
InstructionData { InstructionData {
a: self.a_field(), a_field: self.a_field(),
b: self.b_field(), b_field: self.b_field(),
c: self.c_field(), c_field: self.c_field(),
b_is_constant: self.b_is_constant(), b_is_constant: self.b_is_constant(),
c_is_constant: self.c_is_constant(), c_is_constant: self.c_is_constant(),
d: self.d_field(), d_field: self.d_field(),
}, },
) )
} }
@ -265,10 +255,10 @@ impl Instruction {
}) })
} }
pub fn load_function(destination: u8, prototype_index: u8) -> Instruction { pub fn load_function(destination: u8, record_index: u8) -> Instruction {
Instruction::from(LoadFunction { Instruction::from(LoadFunction {
destination, destination,
prototype_index, record_index,
}) })
} }
@ -385,10 +375,10 @@ impl Instruction {
}) })
} }
pub fn call(destination: u8, prototype_index: u8, argument_count: u8) -> Instruction { pub fn call(destination: u8, function_register: u8, argument_count: u8) -> Instruction {
Instruction::from(Call { Instruction::from(Call {
destination, destination,
prototype_index, function_register,
argument_count, argument_count,
}) })
} }
@ -487,6 +477,7 @@ impl Instruction {
match self.operation() { match self.operation() {
Operation::LOAD_BOOLEAN Operation::LOAD_BOOLEAN
| Operation::LOAD_CONSTANT | Operation::LOAD_CONSTANT
| Operation::LOAD_FUNCTION
| Operation::LOAD_LIST | Operation::LOAD_LIST
| Operation::LOAD_SELF | Operation::LOAD_SELF
| Operation::GET_LOCAL | Operation::GET_LOCAL
@ -697,16 +688,18 @@ impl Instruction {
Operation::CALL => { Operation::CALL => {
let Call { let Call {
destination, destination,
prototype_index, function_register: record_index,
argument_count, argument_count,
} = Call::from(self); } = Call::from(self);
let arguments_start = destination.saturating_sub(argument_count); let arguments_start = destination.saturating_sub(argument_count);
match argument_count { match argument_count {
0 => format!("R{destination} = P{prototype_index}()"), 0 => format!("R{destination} = P{record_index}()"),
1 => format!("R{destination} = P{prototype_index}(R{arguments_start})"), 1 => format!("R{destination} = P{record_index}(R{arguments_start})"),
_ => { _ => {
format!("R{destination} = P{prototype_index}(R{arguments_start}..R{destination})") format!(
"R{destination} = P{record_index}(R{arguments_start}..R{destination})"
)
} }
} }
} }
@ -760,10 +753,32 @@ impl Instruction {
impl Debug for Instruction { impl Debug for Instruction {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} {}", self.operation(), self.disassembly_info()) write!(f, "{} {}", self.operation(), self.disassembly_info())
} }
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct InstructionData {
pub a_field: u8,
pub b_field: u8,
pub c_field: u8,
pub d_field: bool,
pub b_is_constant: bool,
pub c_is_constant: bool,
}
impl Display for InstructionData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{self:?}")
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Argument { pub enum Argument {
Constant(u8), Constant(u8),

View File

@ -21,8 +21,8 @@ impl From<&Instruction> for Point {
impl From<InstructionData> for Point { impl From<InstructionData> for Point {
fn from(instruction: InstructionData) -> Self { fn from(instruction: InstructionData) -> Self {
Point { Point {
from: instruction.b, from: instruction.b_field,
to: instruction.c, to: instruction.c_field,
} }
} }
} }

View File

@ -1,27 +1,26 @@
use std::panic; use std::{ops::Range, panic};
use smallvec::SmallVec; use crate::{
vm::{Record, ThreadSignal},
use crate::{vm::Record, DustString, NativeFunctionError, Value}; NativeFunctionError,
};
pub fn panic( pub fn panic(
record: &mut Record, record: &mut Record,
arguments: SmallVec<[&Value; 4]>, _: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { argument_range: Range<u8>,
let mut message: Option<DustString> = None; ) -> Result<ThreadSignal, NativeFunctionError> {
let position = record.current_position();
let mut message = format!("Dust panic at {position}!");
for value_ref in arguments { for register_index in argument_range {
let string = value_ref.display(record); let value = record.open_register(register_index);
match message { if let Some(string) = value.as_string() {
Some(ref mut message) => message.push_str(&string), message.push_str(&string);
None => message = Some(string), message.push('\n');
} }
} }
if let Some(message) = message { panic!("{}", message)
panic!("{message}");
} else {
panic!("Explicit panic");
}
} }

View File

@ -1,13 +1,15 @@
use std::io::{stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::ops::Range;
use smallvec::SmallVec; use crate::vm::{Register, ThreadSignal};
use crate::{vm::Record, ConcreteValue, NativeFunctionError, Value}; use crate::{vm::Record, ConcreteValue, NativeFunctionError, Value};
pub fn read_line( pub fn read_line(
record: &mut Record, record: &mut Record,
_: SmallVec<[&Value; 4]>, destination: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { _argument_range: Range<u8>,
) -> Result<ThreadSignal, NativeFunctionError> {
let destination = destination.unwrap();
let mut buffer = String::new(); let mut buffer = String::new();
match stdin().read_line(&mut buffer) { match stdin().read_line(&mut buffer) {
@ -16,54 +18,77 @@ pub fn read_line(
buffer.truncate(length.saturating_sub(1)); buffer.truncate(length.saturating_sub(1));
Ok(Some(Value::Concrete(ConcreteValue::string(buffer)))) let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
record.set_register(destination, register);
}
Err(error) => {
return Err(NativeFunctionError::Io {
error: error.kind(),
position: record.current_position(),
})
} }
Err(error) => Err(NativeFunctionError::Io {
error: error.kind(),
}),
} }
Ok(ThreadSignal::Continue)
} }
pub fn write( pub fn write(
record: &mut Record, record: &mut Record,
arguments: SmallVec<[&Value; 4]>, _destination: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { argument_range: Range<u8>,
) -> Result<ThreadSignal, NativeFunctionError> {
let mut stdout = stdout(); let mut stdout = stdout();
for argument in arguments { for register_index in argument_range {
let string = argument.display(record); let value = record.open_register(register_index);
let string = value.display(record);
stdout stdout
.write_all(string.as_bytes()) .write(string.as_bytes())
.map_err(|io_error| NativeFunctionError::Io { .map_err(|io_error| NativeFunctionError::Io {
error: io_error.kind(), error: io_error.kind(),
position: record.current_position(),
})?; })?;
} }
Ok(None) stdout.flush().map_err(|io_error| NativeFunctionError::Io {
error: io_error.kind(),
position: record.current_position(),
})?;
Ok(ThreadSignal::Continue)
} }
pub fn write_line( pub fn write_line(
record: &mut Record, record: &mut Record,
arguments: SmallVec<[&Value; 4]>, _destination: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { argument_range: Range<u8>,
let mut stdout = stdout(); ) -> Result<ThreadSignal, NativeFunctionError> {
let mut stdout = stdout().lock();
for argument in arguments { for register_index in argument_range {
let string = argument.display(record); let value = record.open_register(register_index);
let string = value.display(record);
stdout stdout
.write_all(string.as_bytes()) .write(string.as_bytes())
.map_err(|io_error| NativeFunctionError::Io { .map_err(|io_error| NativeFunctionError::Io {
error: io_error.kind(), error: io_error.kind(),
position: record.current_position(),
})?;
stdout
.write(b"\n")
.map_err(|io_error| NativeFunctionError::Io {
error: io_error.kind(),
position: record.current_position(),
})?; })?;
} }
stdout stdout.flush().map_err(|io_error| NativeFunctionError::Io {
.write(b"\n") error: io_error.kind(),
.map_err(|io_error| NativeFunctionError::Io { position: record.current_position(),
error: io_error.kind(), })?;
})?;
Ok(None) Ok(ThreadSignal::Continue)
} }

View File

@ -9,13 +9,17 @@ mod string;
use std::{ use std::{
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
io::ErrorKind as IoErrorKind, io::ErrorKind as IoErrorKind,
ops::Range,
string::ParseError, string::ParseError,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{AnnotatedError, FunctionType, Span, Type, Value, Vm}; use crate::{
vm::{Record, ThreadSignal},
AnnotatedError, FunctionType, Span, Type,
};
macro_rules! define_native_function { macro_rules! define_native_function {
($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => {
@ -32,12 +36,13 @@ macro_rules! define_native_function {
impl NativeFunction { impl NativeFunction {
pub fn call( pub fn call(
&self, &self,
vm: &Vm<'_>, record: &mut Record,
arguments: SmallVec<[&Value; 4]>, destination: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { argument_range: Range<u8>,
) -> Result<ThreadSignal, NativeFunctionError> {
match self { match self {
$( $(
NativeFunction::$name => $function(vm, arguments), NativeFunction::$name => $function(record, destination, argument_range),
)* )*
} }
} }
@ -258,7 +263,7 @@ pub enum NativeFunctionError {
position: Span, position: Span,
}, },
Panic { Panic {
message: Option<String>, message: String,
position: Span, position: Span,
}, },
Parse { Parse {
@ -288,10 +293,28 @@ impl AnnotatedError for NativeFunctionError {
} }
fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn detail_snippets(&self) -> SmallVec<[(String, Span); 2]> {
todo!() match self {
NativeFunctionError::ExpectedArgumentCount {
expected,
found,
position,
} => smallvec![(
format!("Expected {expected} arguments, found {found}"),
*position
)],
NativeFunctionError::Panic { message, position } => {
smallvec![(format!("Dust panic!\n{message}"), *position)]
}
NativeFunctionError::Parse { error, position } => {
smallvec![(format!("{error}"), *position)]
}
NativeFunctionError::Io { error, position } => {
smallvec![(format!("{error}"), *position)]
}
}
} }
fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> { fn help_snippets(&self) -> SmallVec<[(String, Span); 2]> {
todo!() SmallVec::new()
} }
} }

View File

@ -1,22 +1,21 @@
use smallvec::SmallVec; use std::ops::Range;
use crate::{ConcreteValue, NativeFunctionError, Value, Vm}; use crate::{
vm::{Record, Register, ThreadSignal},
ConcreteValue, NativeFunctionError, Value,
};
pub fn to_string( pub fn to_string(
vm: &Vm, record: &mut Record,
arguments: SmallVec<[&Value; 4]>, destination: Option<u8>,
) -> Result<Option<Value>, NativeFunctionError> { argument_range: Range<u8>,
if arguments.len() != 1 { ) -> Result<ThreadSignal, NativeFunctionError> {
return Err(NativeFunctionError::ExpectedArgumentCount { let argument_value = record.open_register(argument_range.start);
expected: 1, let argument_string = argument_value.display(record);
found: 0, let destination = destination.unwrap();
position: vm.current_position(), let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
});
}
let argument_string = arguments[0].display(vm); record.set_register(destination, register);
Ok(Some(Value::Concrete(ConcreteValue::string( Ok(ThreadSignal::Continue)
argument_string,
))))
} }

View File

@ -8,6 +8,7 @@ use super::DustString;
pub struct Function { pub struct Function {
pub name: Option<DustString>, pub name: Option<DustString>,
pub r#type: FunctionType, pub r#type: FunctionType,
pub record_index: usize,
pub prototype_index: usize, pub prototype_index: usize,
} }

View File

@ -45,6 +45,14 @@ impl Value {
} }
} }
pub fn as_string(&self) -> Option<&DustString> {
if let Value::Concrete(ConcreteValue::String(value)) = self {
Some(value)
} else {
None
}
}
pub fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self { match self {
Value::Concrete(concrete_value) => concrete_value.r#type(), Value::Concrete(concrete_value) => concrete_value.r#type(),

View File

@ -1,6 +1,6 @@
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use super::FunctionCall; use super::{FunctionCall, VmError};
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct CallStack { pub struct CallStack {
@ -31,6 +31,22 @@ impl CallStack {
pub fn pop(&mut self) -> Option<FunctionCall> { pub fn pop(&mut self) -> Option<FunctionCall> {
self.calls.pop() self.calls.pop()
} }
pub fn last(&self) -> Option<&FunctionCall> {
self.calls.last()
}
pub fn pop_or_panic(&mut self) -> FunctionCall {
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
self.calls.pop().unwrap()
}
pub fn last_or_panic(&self) -> &FunctionCall {
assert!(!self.is_empty(), "{}", VmError::CallStackUnderflow);
self.calls.last().unwrap()
}
} }
impl Debug for CallStack { impl Debug for CallStack {
@ -43,8 +59,8 @@ impl Display for CallStack {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "-- DUST CALL STACK --")?; writeln!(f, "-- DUST CALL STACK --")?;
for FunctionCall { function, .. } in &self.calls { for function_call in &self.calls {
writeln!(f, "{function}")?; writeln!(f, "{function_call:?}")?;
} }
Ok(()) Ok(())

View File

@ -1,12 +1,13 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use crate::DustString; use crate::{DustString, InstructionData, Value};
use super::call_stack::CallStack; use super::call_stack::CallStack;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum VmError { pub enum VmError {
CallStackUnderflow { thread_name: DustString }, CallStackUnderflow,
ExpectedFunction { value: Value },
InstructionIndexOutOfBounds { call_stack: CallStack, ip: usize }, InstructionIndexOutOfBounds { call_stack: CallStack, ip: usize },
MalformedInstruction { instruction: InstructionData }, MalformedInstruction { instruction: InstructionData },
} }
@ -14,12 +15,18 @@ pub enum VmError {
impl Display for VmError { impl Display for VmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::CallStackUnderflow { thread_name } => { Self::CallStackUnderflow => {
write!(f, "Call stack underflow in thread {thread_name}") write!(f, "Call stack underflow")
}
Self::ExpectedFunction { value } => {
write!(f, "Expected function, found {value}")
} }
Self::InstructionIndexOutOfBounds { call_stack, ip } => { Self::InstructionIndexOutOfBounds { call_stack, ip } => {
write!(f, "Instruction index {} out of bounds\n{call_stack}", ip) write!(f, "Instruction index {} out of bounds\n{call_stack}", ip)
} }
Self::MalformedInstruction { instruction } => {
write!(f, "Malformed instruction {instruction}")
}
} }
} }
} }

View File

@ -11,9 +11,10 @@ use std::{
thread::spawn, thread::spawn,
}; };
pub use call_stack::CallStack;
pub use error::VmError; pub use error::VmError;
pub use record::Record; pub use record::Record;
use thread::Thread; pub use thread::{Thread, ThreadSignal};
use crate::{compile, Chunk, DustError, Value}; use crate::{compile, Chunk, DustError, Value};
@ -94,7 +95,7 @@ impl Display for Pointer {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct FunctionCall { pub struct FunctionCall {
prototype_index: usize,
record_index: usize, record_index: usize,
return_register: u8, return_register: u8,
argument_count: u8,
} }

View File

@ -1,4 +1,4 @@
use std::env::consts::OS; use std::mem::replace;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -134,6 +134,30 @@ impl Record {
} }
} }
pub fn replace_register_or_clone_constant(
&mut self,
register_index: u8,
new_register: Register,
) -> Value {
let register_index = register_index as usize;
assert!(
register_index < self.stack.len(),
"VM Error: Register index out of bounds"
);
let old_register = replace(&mut self.stack[register_index], new_register);
match old_register {
Register::Value(value) => value,
Register::Pointer(pointer) => match pointer {
Pointer::Stack(register_index) => self.open_register(register_index).clone(),
Pointer::Constant(constant_index) => self.get_constant(constant_index).clone(),
},
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
}
}
/// DRY helper to get a value from an Argument /// DRY helper to get a value from an Argument
pub fn get_argument(&self, index: u8, is_constant: bool) -> &Value { pub fn get_argument(&self, index: u8, is_constant: bool) -> &Value {
if is_constant { if is_constant {

View File

@ -1,11 +1,15 @@
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
instruction::{Close, LoadBoolean, LoadConstant, Point}, instruction::{
AbstractList, ConcreteValue, Instruction, InstructionData, NativeFunction, Type, Value, Call, CallNative, Close, LoadBoolean, LoadConstant, LoadFunction, LoadList, LoadSelf, Point,
},
vm::VmError,
AbstractList, ConcreteValue, Function, Instruction, InstructionData, NativeFunction, Type,
Value,
}; };
use super::{thread::ThreadSignal, Pointer, Record, Register}; use super::{thread::ThreadSignal, FunctionCall, Pointer, Record, Register};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RunAction { pub struct RunAction {
@ -33,11 +37,12 @@ impl From<Instruction> for RunAction {
pub type RunnerLogic = fn(InstructionData, &mut Record) -> ThreadSignal; pub type RunnerLogic = fn(InstructionData, &mut Record) -> ThreadSignal;
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 24] = [ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
r#move, r#move,
close, close,
load_boolean, load_boolean,
load_constant, load_constant,
load_function,
load_list, load_list,
load_self, load_self,
get_local, get_local,
@ -127,11 +132,14 @@ pub fn load_constant(instruction_data: InstructionData, record: &mut Record) ->
} }
pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { a, b, .. } = instruction_data; let LoadList {
let mut item_pointers = Vec::with_capacity((a - b) as usize); destination,
start_register,
} = instruction_data.into();
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
let mut item_type = Type::Any; let mut item_type = Type::Any;
for register_index in b..a { for register_index in start_register..destination {
match record.get_register(register_index) { match record.get_register(register_index) {
Register::Empty => continue, Register::Empty => continue,
Register::Value(value) => { Register::Value(value) => {
@ -157,37 +165,62 @@ pub fn load_list(instruction_data: InstructionData, record: &mut Record) -> Thre
}); });
let register = Register::Value(list_value); let register = Register::Value(list_value);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue
}
pub fn load_function(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let LoadFunction {
destination,
record_index,
} = instruction_data.into();
ThreadSignal::Continue
} }
pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn load_self(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { a, .. } = instruction_data; let LoadSelf { destination } = instruction_data.into();
let register = Register::Value(Value::SelfFunction); let register = Register::Value(Value::SelfFunction);
record.set_register(a, register); record.set_register(destination, register);
ThreadSignal::Continue
} }
pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn get_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { a, b, .. } = instruction_data; let InstructionData {
a_field: a,
b_field: b,
..
} = instruction_data;
let local_register_index = record.get_local_register(b); let local_register_index = record.get_local_register(b);
let register = Register::Pointer(Pointer::Stack(local_register_index)); let register = Register::Pointer(Pointer::Stack(local_register_index));
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn set_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn set_local(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { b, c, .. } = instruction_data; let InstructionData {
b_field: b,
c_field: c,
..
} = instruction_data;
let local_register_index = record.get_local_register(c); let local_register_index = record.get_local_register(c);
let register = Register::Pointer(Pointer::Stack(b)); let register = Register::Pointer(Pointer::Stack(b));
record.set_register(local_register_index, register); record.set_register(local_register_index, register);
ThreadSignal::Continue
} }
pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
.. ..
@ -206,13 +239,15 @@ pub fn add(instruction_data: InstructionData, record: &mut Record) -> ThreadSign
let register = Register::Value(sum); let register = Register::Value(sum);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
.. ..
@ -231,13 +266,15 @@ pub fn subtract(instruction_data: InstructionData, record: &mut Record) -> Threa
let register = Register::Value(difference); let register = Register::Value(difference);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
.. ..
@ -256,13 +293,15 @@ pub fn multiply(instruction_data: InstructionData, record: &mut Record) -> Threa
let register = Register::Value(product); let register = Register::Value(product);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
.. ..
@ -281,13 +320,15 @@ pub fn divide(instruction_data: InstructionData, record: &mut Record) -> ThreadS
let register = Register::Value(quotient); let register = Register::Value(quotient);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
.. ..
@ -306,13 +347,15 @@ pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadS
let register = Register::Value(remainder); let register = Register::Value(remainder);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
b, b_field: b,
b_is_constant, b_is_constant,
c, c_field: c,
.. ..
} = instruction_data; } = instruction_data;
let value = record.get_argument(b, b_is_constant); let value = record.get_argument(b, b_is_constant);
@ -324,16 +367,17 @@ pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSig
let test_value = c != 0; let test_value = c != 0;
if boolean == test_value { if boolean == test_value {
record.ip += 2; record.ip += 1;
} else {
} }
ThreadSignal::Continue
} }
pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
.. ..
} = instruction_data; } = instruction_data;
@ -346,7 +390,7 @@ pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> Threa
let test_value = c != 0; let test_value = c != 0;
if boolean == test_value { if boolean == test_value {
record.ip += 2; record.ip += 1;
} else { } else {
let pointer = if b_is_constant { let pointer = if b_is_constant {
Pointer::Constant(b) Pointer::Constant(b)
@ -357,15 +401,17 @@ pub fn test_set(instruction_data: InstructionData, record: &mut Record) -> Threa
record.set_register(a, register); record.set_register(a, register);
} }
ThreadSignal::Continue
} }
pub fn equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
d, d_field: d,
.. ..
} = instruction_data; } = instruction_data;
let left = record.get_argument(b, b_is_constant); let left = record.get_argument(b, b_is_constant);
@ -373,18 +419,19 @@ pub fn equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSi
let is_equal = left == right; let is_equal = left == right;
if is_equal == d { if is_equal == d {
record.ip += 2; record.ip += 1;
} else {
} }
ThreadSignal::Continue
} }
pub fn less(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn less(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
d, d_field: d,
.. ..
} = instruction_data; } = instruction_data;
let left = record.get_argument(b, b_is_constant); let left = record.get_argument(b, b_is_constant);
@ -392,18 +439,19 @@ pub fn less(instruction_data: InstructionData, record: &mut Record) -> ThreadSig
let is_less = left < right; let is_less = left < right;
if is_less == d { if is_less == d {
record.ip += 2; record.ip += 1;
} else {
} }
ThreadSignal::Continue
} }
pub fn less_equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn less_equal(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
b, b_field: b,
c, c_field: c,
b_is_constant, b_is_constant,
c_is_constant, c_is_constant,
d, d_field: d,
.. ..
} = instruction_data; } = instruction_data;
let left = record.get_argument(b, b_is_constant); let left = record.get_argument(b, b_is_constant);
@ -411,15 +459,16 @@ pub fn less_equal(instruction_data: InstructionData, record: &mut Record) -> Thr
let is_less_or_equal = left <= right; let is_less_or_equal = left <= right;
if is_less_or_equal == d { if is_less_or_equal == d {
record.ip += 2; record.ip += 1;
} else {
} }
ThreadSignal::Continue
} }
pub fn negate(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn negate(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
b_is_constant, b_is_constant,
.. ..
} = instruction_data; } = instruction_data;
@ -428,12 +477,14 @@ pub fn negate(instruction_data: InstructionData, record: &mut Record) -> ThreadS
let register = Register::Value(negated); let register = Register::Value(negated);
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
a, a_field: a,
b, b_field: b,
b_is_constant, b_is_constant,
.. ..
} = instruction_data; } = instruction_data;
@ -445,10 +496,16 @@ pub fn not(instruction_data: InstructionData, record: &mut Record) -> ThreadSign
let register = Register::Value(Value::Concrete(not)); let register = Register::Value(Value::Concrete(not));
record.set_register(a, register); record.set_register(a, register);
ThreadSignal::Continue
} }
pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { b, c, .. } = instruction_data; let InstructionData {
b_field: b,
c_field: c,
..
} = instruction_data;
let offset = b as usize; let offset = b as usize;
let is_positive = c != 0; let is_positive = c != 0;
@ -457,88 +514,55 @@ pub fn jump(instruction_data: InstructionData, record: &mut Record) -> ThreadSig
} else { } else {
record.ip -= offset record.ip -= offset
} }
ThreadSignal::Continue
} }
pub fn call(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn call(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { a, b, c, .. } = instruction_data; let Call {
let prototype = record.get_prototype(b); destination,
function_register,
argument_count,
} = instruction_data.into();
let function_value = record.open_register(function_register);
let function = match function_value {
Value::Function(function) => function,
_ => panic!(
"{}",
VmError::ExpectedFunction {
value: function_value.clone()
}
),
};
let first_argument_index = a - c; ThreadSignal::Call(FunctionCall {
let mut argument_index = 0; record_index: function.record_index,
return_register: destination,
for argument_register_index in first_argument_index..a { argument_count,
let target_register_is_empty = matches!( })
record.stack[argument_register_index as usize],
Register::Empty
);
if target_register_is_empty {
continue;
}
function_record.set_register(
argument_index as u8,
Register::Pointer(Pointer::ParentStack(argument_register_index)),
);
argument_index += 1;
}
let return_value = function_record.run();
if let Some(concrete_value) = return_value {
let register = Register::Value(concrete_value);
record.set_register(a, register);
}
} }
pub fn call_native(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn call_native(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { a, b, c, .. } = instruction_data; let CallNative {
let first_argument_index = (a - c) as usize; destination,
let argument_range = first_argument_index..a as usize; function,
let mut arguments: SmallVec<[&Value; 4]> = SmallVec::new(); argument_count,
} = instruction_data.into();
let first_argument_index = destination - argument_count;
let argument_range = first_argument_index..destination;
for register_index in argument_range { let function = NativeFunction::from(function);
let register = &record.stack[register_index]; let thread_signal = function
let value = match register { .call(record, Some(destination), argument_range)
Register::Value(value) => value, .unwrap_or_else(|error| panic!("{error:?}"));
Register::Pointer(pointer) => {
let value_option = record.follow_pointer_allow_empty(*pointer);
match value_option { thread_signal
Some(value) => value,
None => continue,
}
}
Register::Empty => continue,
};
arguments.push(value);
}
let function = NativeFunction::from(b);
let return_value = function.call(record.arguments).unwrap();
if let Some(value) = return_value {
let register = Register::Value(value);
record.set_register(a, register);
}
} }
pub fn r#return(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn r#return(instruction_data: InstructionData, _: &mut Record) -> ThreadSignal {
let should_return_value = instruction_data.b != 0; let should_return_value = instruction_data.b_field != 0;
if !should_return_value {} ThreadSignal::Return(should_return_value)
if let Some(register_index) = &record.last_assigned_register {
let return_value = record.open_register(*register_index).clone();
record.return_value = Some(return_value);
} else {
panic!("Stack underflow");
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,16 +1,15 @@
use crate::{Chunk, Value}; use std::mem::swap;
use super::{call_stack::CallStack, record::Record, runner::RunAction, FunctionCall, VmError}; use crate::{vm::Register, Chunk, Value};
use super::{record::Record, runner::RunAction, CallStack, FunctionCall, VmError};
fn create_records(chunk: Chunk, records: &mut Vec<Record>) { fn create_records(chunk: Chunk, records: &mut Vec<Record>) {
let (_, _, instructions, positions, constants, locals, prototypes, stack_size) = let (_, _, instructions, positions, constants, locals, prototypes, stack_size) =
chunk.take_data(); chunk.take_data();
let actions = instructions let actions = instructions.into_iter().map(RunAction::from).collect();
.into_iter()
.map(|instruction| RunAction::from(instruction))
.collect();
let record = Record::new( let record = Record::new(
Vec::with_capacity(stack_size), vec![Register::Empty; stack_size],
constants, constants,
locals, locals,
actions, actions,
@ -27,27 +26,23 @@ fn create_records(chunk: Chunk, records: &mut Vec<Record>) {
pub struct Thread { pub struct Thread {
call_stack: CallStack, call_stack: CallStack,
records: Vec<Record>, records: Vec<Record>,
return_register: Option<u8>,
} }
impl Thread { impl Thread {
pub fn new(chunk: Chunk) -> Self { pub fn new(chunk: Chunk) -> Self {
let call_stack = CallStack::new(); let call_stack = CallStack::with_capacity(chunk.prototypes().len() + 1);
let mut records = Vec::with_capacity(chunk.prototypes().len()); let mut records = Vec::with_capacity(chunk.prototypes().len() + 1);
create_records(chunk, &mut records); create_records(chunk, &mut records);
Thread { Thread {
call_stack, call_stack,
records, records,
return_register: None,
} }
} }
pub fn run(&mut self) -> Option<Value> { pub fn run(&mut self) -> Option<Value> {
assert!(!self.call_stack.is_empty()); let (record, remaining_records) = self.records.split_first_mut().unwrap();
let mut record = &mut self.records[0];
loop { loop {
assert!( assert!(
@ -60,25 +55,46 @@ impl Thread {
); );
let action = record.actions[record.ip]; let action = record.actions[record.ip];
let signal = (action.logic)(action.data, &mut record); let signal = (action.logic)(action.data, record);
match signal { match signal {
ThreadSignal::Continue => { ThreadSignal::Continue => {
record.ip += 1; record.ip += 1;
} }
ThreadSignal::Call(FunctionCall { ThreadSignal::Call(function_call) => {
record_index, swap(record, &mut remaining_records[function_call.record_index]);
return_register, self.call_stack.push(function_call);
..
}) => {
record = &mut self.records[record_index];
self.return_register = Some(return_register);
} }
ThreadSignal::Return(value_option) => { ThreadSignal::Return(should_return_value) => {
let outer_call = self.call_stack.pop(); let returning_call = match self.call_stack.pop() {
Some(function_call) => function_call,
None => {
if should_return_value {
return record.last_assigned_register().map(|register| {
record.replace_register_or_clone_constant(
register,
Register::Empty,
)
});
} else {
return None;
}
}
};
let outer_call = self.call_stack.last_or_panic();
if outer_call.is_none() { if should_return_value {
return value_option; let return_register = record
.last_assigned_register()
.unwrap_or_else(|| panic!("Expected return value"));
let value = record
.replace_register_or_clone_constant(return_register, Register::Empty);
swap(record, &mut remaining_records[outer_call.record_index]);
record.set_register(returning_call.return_register, Register::Value(value));
} else {
swap(record, &mut remaining_records[outer_call.record_index]);
} }
} }
} }
@ -89,5 +105,5 @@ impl Thread {
pub enum ThreadSignal { pub enum ThreadSignal {
Continue, Continue,
Call(FunctionCall), Call(FunctionCall),
Return(Option<Value>), Return(bool),
} }