Continue thread-based VM refactor
This commit is contained in:
parent
bd590e0643
commit
4527f7b6ef
@ -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();
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,7 +1677,8 @@ impl<'src> Compiler<'src> {
|
|||||||
false,
|
false,
|
||||||
self.current_scope,
|
self.current_scope,
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
let load_function = Instruction::load_function(destination, self.record_index);
|
let load_function = Instruction::load_function(destination, self.record_index);
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
@ -1688,12 +1686,15 @@ impl<'src> Compiler<'src> {
|
|||||||
Type::function(function_type),
|
Type::function(function_type),
|
||||||
Span(function_start, function_end),
|
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));
|
||||||
|
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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) => Err(NativeFunctionError::Io {
|
Err(error) => {
|
||||||
|
return Err(NativeFunctionError::Io {
|
||||||
error: error.kind(),
|
error: error.kind(),
|
||||||
}),
|
position: record.current_position(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
stdout
|
||||||
.write(b"\n")
|
.write(b"\n")
|
||||||
.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.flush().map_err(|io_error| NativeFunctionError::Io {
|
||||||
|
error: io_error.kind(),
|
||||||
|
position: record.current_position(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(None)
|
Ok(ThreadSignal::Continue)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
|
||||||
))))
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
|
@ -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(())
|
||||||
|
@ -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}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
let first_argument_index = a - c;
|
argument_count,
|
||||||
let mut argument_index = 0;
|
} = instruction_data.into();
|
||||||
|
let function_value = record.open_register(function_register);
|
||||||
for argument_register_index in first_argument_index..a {
|
let function = match function_value {
|
||||||
let target_register_is_empty = matches!(
|
Value::Function(function) => function,
|
||||||
record.stack[argument_register_index as usize],
|
_ => panic!(
|
||||||
Register::Empty
|
"{}",
|
||||||
);
|
VmError::ExpectedFunction {
|
||||||
|
value: function_value.clone()
|
||||||
if target_register_is_empty {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
function_record.set_register(
|
ThreadSignal::Call(FunctionCall {
|
||||||
argument_index as u8,
|
record_index: function.record_index,
|
||||||
Register::Pointer(Pointer::ParentStack(argument_register_index)),
|
return_register: destination,
|
||||||
);
|
argument_count,
|
||||||
|
})
|
||||||
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)]
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user