Continue Value/VM overhaul
This commit is contained in:
parent
73247446c7
commit
302bc9ce6c
@ -8,18 +8,18 @@ use std::fmt::{self, Debug, Display, Formatter};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{value::ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type};
|
use crate::{value::Value, Disassembler, FunctionType, Instruction, Scope, Span, Type};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// In-memory representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, PartialOrd, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
name: Option<String>, // TODO: Use a bool indicating whether the chunk is named. If it is, get
|
name: Option<String>,
|
||||||
r#type: FunctionType, // the name from C0.
|
r#type: FunctionType,
|
||||||
|
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ impl Chunk {
|
|||||||
pub fn with_data(
|
pub fn with_data(
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<Value>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -77,15 +77,15 @@ impl Chunk {
|
|||||||
self.instructions.is_empty()
|
self.instructions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants(&self) -> &Vec<ConcreteValue> {
|
pub fn constants(&self) -> &Vec<Value> {
|
||||||
&self.constants
|
&self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constants_mut(&mut self) -> &mut Vec<ConcreteValue> {
|
pub fn constants_mut(&mut self) -> &mut Vec<Value> {
|
||||||
&mut self.constants
|
&mut self.constants
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: u8) -> Result<&ConcreteValue, ChunkError> {
|
pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> {
|
||||||
self.constants
|
self.constants
|
||||||
.get(index as usize)
|
.get(index as usize)
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
.ok_or(ChunkError::ConstantIndexOutOfBounds {
|
||||||
@ -93,7 +93,7 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 {
|
pub fn push_or_get_constant(&mut self, value: Value) -> u8 {
|
||||||
if let Some(index) = self
|
if let Some(index) = self
|
||||||
.constants
|
.constants
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -13,7 +13,7 @@ use std::{
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction,
|
value::Value, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction,
|
||||||
LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind,
|
LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind,
|
||||||
TokenOwned, Type, TypeConflict,
|
TokenOwned, Type, TypeConflict,
|
||||||
};
|
};
|
||||||
@ -44,7 +44,7 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
|
|||||||
/// Low-level tool for compiling the input a token at a time while assembling a chunk.
|
/// Low-level tool for compiling the input a token at a time while assembling a chunk.
|
||||||
///
|
///
|
||||||
/// See the [`compile`] function an example of how to create and use a Compiler.
|
/// See the [`compile`] function an example of how to create and use a Compiler.
|
||||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Eq, PartialEq, PartialOrd)]
|
||||||
pub struct Compiler<'src> {
|
pub struct Compiler<'src> {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
lexer: Lexer<'src>,
|
lexer: Lexer<'src>,
|
||||||
@ -158,7 +158,7 @@ impl<'src> Compiler<'src> {
|
|||||||
.chunk
|
.chunk
|
||||||
.constants()
|
.constants()
|
||||||
.get(local.identifier_index as usize)?;
|
.get(local.identifier_index as usize)?;
|
||||||
let identifier = if let ConcreteValue::String(identifier) = constant {
|
let identifier = if let Value::String(identifier) = constant {
|
||||||
identifier
|
identifier
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
@ -186,7 +186,7 @@ impl<'src> Compiler<'src> {
|
|||||||
) -> (u8, u8) {
|
) -> (u8, u8) {
|
||||||
log::debug!("Declare local {identifier}");
|
log::debug!("Declare local {identifier}");
|
||||||
|
|
||||||
let identifier = ConcreteValue::string(identifier);
|
let identifier = Value::string(identifier);
|
||||||
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
let identifier_index = self.chunk.push_or_get_constant(identifier);
|
||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
@ -368,11 +368,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.chunk.instructions_mut().push((instruction, position));
|
self.chunk.instructions_mut().push((instruction, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_constant(
|
fn emit_constant(&mut self, constant: Value, position: Span) -> Result<(), CompileError> {
|
||||||
&mut self,
|
|
||||||
constant: ConcreteValue,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), CompileError> {
|
|
||||||
let constant_index = self.chunk.push_or_get_constant(constant);
|
let constant_index = self.chunk.push_or_get_constant(constant);
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
|
||||||
@ -418,7 +414,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let byte = u8::from_str_radix(&text[2..], 16)
|
let byte = u8::from_str_radix(&text[2..], 16)
|
||||||
.map_err(|error| CompileError::ParseIntError { error, position })?;
|
.map_err(|error| CompileError::ParseIntError { error, position })?;
|
||||||
let value = ConcreteValue::byte(byte);
|
let value = Value::byte(byte);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
@ -440,7 +436,7 @@ impl<'src> Compiler<'src> {
|
|||||||
if let Token::Character(character) = self.current_token {
|
if let Token::Character(character) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let value = ConcreteValue::character(character);
|
let value = Value::character(character);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
@ -468,7 +464,7 @@ impl<'src> Compiler<'src> {
|
|||||||
error,
|
error,
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
let value = ConcreteValue::float(float);
|
let value = Value::float(float);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
@ -496,7 +492,7 @@ impl<'src> Compiler<'src> {
|
|||||||
error,
|
error,
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
let value = ConcreteValue::integer(integer);
|
let value = Value::integer(integer);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
@ -518,7 +514,7 @@ impl<'src> Compiler<'src> {
|
|||||||
if let Token::String(text) = self.current_token {
|
if let Token::String(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let value = ConcreteValue::string(text);
|
let value = Value::string(text);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
@ -1476,7 +1472,7 @@ impl<'src> Compiler<'src> {
|
|||||||
value_parameters,
|
value_parameters,
|
||||||
return_type,
|
return_type,
|
||||||
};
|
};
|
||||||
let function = ConcreteValue::function(function_compiler.finish());
|
let function = Value::function(function_compiler.finish());
|
||||||
let constant_index = self.chunk.push_or_get_constant(function);
|
let constant_index = self.chunk.push_or_get_constant(function);
|
||||||
let function_end = self.current_position.1;
|
let function_end = self.current_position.1;
|
||||||
let register = self.next_register();
|
let register = self.next_register();
|
||||||
|
@ -45,7 +45,7 @@ use std::env::current_exe;
|
|||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
use crate::{value::ConcreteValue, Chunk, Local};
|
use crate::{value::Value, Chunk, Local};
|
||||||
|
|
||||||
const INSTRUCTION_HEADER: [&str; 4] = [
|
const INSTRUCTION_HEADER: [&str; 4] = [
|
||||||
"Instructions",
|
"Instructions",
|
||||||
@ -313,9 +313,8 @@ impl<'a> Disassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants().iter().enumerate() {
|
for (index, value) in self.chunk.constants().iter().enumerate() {
|
||||||
if let ConcreteValue::Function(function) = value {
|
if let Value::Function(chunk) = value {
|
||||||
let function_disassembly = function
|
let function_disassembly = chunk
|
||||||
.chunk()
|
|
||||||
.disassembler()
|
.disassembler()
|
||||||
.styled(self.styled)
|
.styled(self.styled)
|
||||||
.indent(self.indent + 1)
|
.indent(self.indent + 1)
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{Chunk, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub struct Function {
|
|
||||||
chunk: Chunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
|
||||||
pub fn new(chunk: Chunk) -> Self {
|
|
||||||
Self { chunk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk(&self) -> &Chunk {
|
|
||||||
&self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
Type::Function(self.chunk.r#type().clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_borrowed(&self) -> FunctionBorrowed {
|
|
||||||
FunctionBorrowed::new(&self.chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Function {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.chunk.r#type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub struct FunctionBorrowed<'a> {
|
|
||||||
chunk: &'a Chunk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FunctionBorrowed<'a> {
|
|
||||||
pub fn new(chunk: &'a Chunk) -> Self {
|
|
||||||
Self { chunk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn chunk(&self) -> &Chunk {
|
|
||||||
self.chunk
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
Type::Function(self.chunk.r#type().clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_owned(&self) -> Function {
|
|
||||||
Function::new(self.chunk.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Display for FunctionBorrowed<'a> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.chunk.r#type())
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ pub mod compiler;
|
|||||||
pub mod disassembler;
|
pub mod disassembler;
|
||||||
pub mod dust_error;
|
pub mod dust_error;
|
||||||
pub mod formatter;
|
pub mod formatter;
|
||||||
pub mod function;
|
|
||||||
pub mod instruction;
|
pub mod instruction;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod native_function;
|
pub mod native_function;
|
||||||
@ -22,7 +21,6 @@ pub use crate::compiler::{compile, CompileError, Compiler};
|
|||||||
pub use crate::disassembler::Disassembler;
|
pub use crate::disassembler::Disassembler;
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||||
pub use crate::formatter::{format, Formatter};
|
pub use crate::formatter::{format, Formatter};
|
||||||
pub use crate::function::{Function, FunctionBorrowed};
|
|
||||||
pub use crate::instruction::Instruction;
|
pub use crate::instruction::Instruction;
|
||||||
pub use crate::lexer::{lex, LexError, Lexer};
|
pub use crate::lexer::{lex, LexError, Lexer};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
@ -31,7 +29,7 @@ pub use crate::optimizer::Optimizer;
|
|||||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||||
pub use crate::scope::Scope;
|
pub use crate::scope::Scope;
|
||||||
pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned};
|
pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned};
|
||||||
pub use crate::value::{ConcreteValue, Value, ValueError};
|
pub use crate::value::{Value, ValueError};
|
||||||
pub use crate::vm::{run, Vm, VmError};
|
pub use crate::vm::{run, Vm, VmError};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
use std::io::{self, stdout, Write};
|
use std::io::{self, stdout, Write};
|
||||||
|
|
||||||
use crate::{ConcreteValue, Instruction, NativeFunctionError, Span, Value, Vm, VmError};
|
use crate::{Instruction, NativeFunctionError, Span, Value, Vm, VmError};
|
||||||
|
|
||||||
pub fn panic(
|
pub fn panic(vm: &Vm, instruction: Instruction) -> Result<Option<Value>, VmError> {
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let message = if argument_count == 0 {
|
let message = if argument_count == 0 {
|
||||||
None
|
None
|
||||||
@ -18,7 +14,7 @@ pub fn panic(
|
|||||||
message.push(' ');
|
message.push(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument = vm.open_register(argument_index, position)?;
|
let argument = vm.open_register(argument_index)?;
|
||||||
|
|
||||||
message.push_str(&argument.to_string());
|
message.push_str(&argument.to_string());
|
||||||
}
|
}
|
||||||
@ -28,15 +24,11 @@ pub fn panic(
|
|||||||
|
|
||||||
Err(VmError::NativeFunction(NativeFunctionError::Panic {
|
Err(VmError::NativeFunction(NativeFunctionError::Panic {
|
||||||
message,
|
message,
|
||||||
position,
|
position: vm.current_position(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(
|
pub fn to_string(vm: &Vm, instruction: Instruction) -> Result<Option<Value>, VmError> {
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
|
|
||||||
if argument_count != 1 {
|
if argument_count != 1 {
|
||||||
@ -44,7 +36,7 @@ pub fn to_string(
|
|||||||
NativeFunctionError::ExpectedArgumentCount {
|
NativeFunctionError::ExpectedArgumentCount {
|
||||||
expected: 1,
|
expected: 1,
|
||||||
found: argument_count as usize,
|
found: argument_count as usize,
|
||||||
position,
|
position: vm.current_position(),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -52,19 +44,15 @@ pub fn to_string(
|
|||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
|
|
||||||
for argument_index in 0..argument_count {
|
for argument_index in 0..argument_count {
|
||||||
let argument = vm.open_register(argument_index, position)?;
|
let argument = vm.open_register(argument_index)?;
|
||||||
|
|
||||||
string.push_str(&argument.to_string());
|
string.push_str(&argument.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(ConcreteValue::String(string)))
|
Ok(Some(Value::String(string)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_line(
|
pub fn read_line(vm: &Vm, instruction: Instruction) -> Result<Option<Value>, VmError> {
|
||||||
_: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
|
|
||||||
if argument_count != 0 {
|
if argument_count != 0 {
|
||||||
@ -72,7 +60,7 @@ pub fn read_line(
|
|||||||
NativeFunctionError::ExpectedArgumentCount {
|
NativeFunctionError::ExpectedArgumentCount {
|
||||||
expected: 0,
|
expected: 0,
|
||||||
found: argument_count as usize,
|
found: argument_count as usize,
|
||||||
position,
|
position: vm.current_position(),
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -80,28 +68,24 @@ pub fn read_line(
|
|||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
match io::stdin().read_line(&mut buffer) {
|
match io::stdin().read_line(&mut buffer) {
|
||||||
Ok(_) => Ok(Some(ConcreteValue::String(
|
Ok(_) => Ok(Some(Value::String(
|
||||||
buffer.trim_end_matches('\n').to_string(),
|
buffer.trim_end_matches('\n').to_string(),
|
||||||
))),
|
))),
|
||||||
Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io {
|
Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
error: error.kind(),
|
error: error.kind(),
|
||||||
position,
|
position: vm.current_position(),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(
|
pub fn write(vm: &Vm, instruction: Instruction) -> Result<Option<Value>, VmError> {
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
let map_err = |io_error: io::Error| {
|
let map_err = |io_error: io::Error| {
|
||||||
VmError::NativeFunction(NativeFunctionError::Io {
|
VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
error: io_error.kind(),
|
error: io_error.kind(),
|
||||||
position,
|
position: vm.current_position(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,7 +96,7 @@ pub fn write(
|
|||||||
stdout.write(b" ").map_err(map_err)?;
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument_string = vm.open_register(argument_index, position)?.to_string();
|
let argument_string = vm.open_register(argument_index)?.to_string();
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(argument_string.as_bytes())
|
.write_all(argument_string.as_bytes())
|
||||||
@ -122,18 +106,14 @@ pub fn write(
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(
|
pub fn write_line(vm: &Vm, instruction: Instruction) -> Result<Option<Value>, VmError> {
|
||||||
vm: &Vm,
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
let map_err = |io_error: io::Error| {
|
let map_err = |io_error: io::Error| {
|
||||||
VmError::NativeFunction(NativeFunctionError::Io {
|
VmError::NativeFunction(NativeFunctionError::Io {
|
||||||
error: io_error.kind(),
|
error: io_error.kind(),
|
||||||
position,
|
position: vm.current_position(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,7 +124,7 @@ pub fn write_line(
|
|||||||
stdout.write(b" ").map_err(map_err)?;
|
stdout.write(b" ").map_err(map_err)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argument_string = vm.open_register(argument_index, position)?.to_string();
|
let argument_string = vm.open_register(argument_index)?.to_string();
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
.write_all(argument_string.as_bytes())
|
.write_all(argument_string.as_bytes())
|
||||||
|
@ -12,7 +12,7 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Vm, VmError};
|
use crate::{AnnotatedError, FunctionType, Instruction, Span, Type, Value, Vm, VmError};
|
||||||
|
|
||||||
macro_rules! define_native_function {
|
macro_rules! define_native_function {
|
||||||
($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => {
|
||||||
@ -31,11 +31,10 @@ macro_rules! define_native_function {
|
|||||||
&self,
|
&self,
|
||||||
vm: &mut Vm,
|
vm: &mut Vm,
|
||||||
instruction: Instruction,
|
instruction: Instruction,
|
||||||
span: Span
|
) -> Result<Option<Value>, VmError> {
|
||||||
) -> Result<Option<ConcreteValue>, VmError> {
|
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
NativeFunction::$name => $function(vm, instruction, span),
|
NativeFunction::$name => $function(vm, instruction),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,983 +0,0 @@
|
|||||||
//! Runtime values used by the VM.
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
ops::{Range, RangeInclusive},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{function::Function, Chunk, FunctionBorrowed, Type};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum ReferenceValue<'a> {
|
|
||||||
Concrete(&'a ConcreteValue),
|
|
||||||
Function(FunctionBorrowed<'a>),
|
|
||||||
List(&'a Vec<&'a ConcreteValue>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ReferenceValue<'a> {
|
|
||||||
pub fn to_value(self) -> Value<'a> {
|
|
||||||
Value::Reference(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
ReferenceValue::Concrete(concrete) => concrete.r#type(),
|
|
||||||
ReferenceValue::Function(function) => function.r#type(),
|
|
||||||
ReferenceValue::List(list) => {
|
|
||||||
let item_type = list
|
|
||||||
.first()
|
|
||||||
.map(|value| value.r#type())
|
|
||||||
.unwrap_or(Type::Any);
|
|
||||||
|
|
||||||
Type::List {
|
|
||||||
item_type: Box::new(item_type),
|
|
||||||
length: list.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_concrete(self) -> ConcreteValue {
|
|
||||||
match self {
|
|
||||||
ReferenceValue::Concrete(concrete) => concrete.clone(),
|
|
||||||
ReferenceValue::Function(function) => {
|
|
||||||
ConcreteValue::Function(Function::new(function.chunk().clone()))
|
|
||||||
}
|
|
||||||
ReferenceValue::List(list) => {
|
|
||||||
let mut items = Vec::new();
|
|
||||||
|
|
||||||
for value in list {
|
|
||||||
items.push((*value).clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcreteValue::List(items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Value<'a> {
|
|
||||||
Concrete(ConcreteValue),
|
|
||||||
Reference(ReferenceValue<'a>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Value<'a> {
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete) => concrete.r#type(),
|
|
||||||
Value::Reference(reference) => reference.r#type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_concrete(self) -> ConcreteValue {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete) => concrete,
|
|
||||||
Value::Reference(reference) => reference.into_concrete(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<bool> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => Some(*boolean),
|
|
||||||
Value::Reference(ReferenceValue::Concrete(ConcreteValue::Boolean(boolean))) => {
|
|
||||||
Some(*boolean)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_function(&self) -> Option<FunctionBorrowed> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(ConcreteValue::Function(function)) => Some(function.as_borrowed()),
|
|
||||||
Value::Reference(ReferenceValue::Concrete(ConcreteValue::Function(function))) => {
|
|
||||||
Some(function.as_borrowed())
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.add(right),
|
|
||||||
(Value::Reference(ReferenceValue::Concrete(left)), Value::Concrete(right)) => {
|
|
||||||
left.add(right)
|
|
||||||
}
|
|
||||||
(Value::Concrete(left), Value::Reference(ReferenceValue::Concrete(right))) => {
|
|
||||||
left.add(right)
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Value::Reference(ReferenceValue::Concrete(left)),
|
|
||||||
Value::Reference(ReferenceValue::Concrete(right)),
|
|
||||||
) => left.add(right),
|
|
||||||
_ => Err(ValueError::CannotAdd(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotSubtract(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.subtract(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.multiply(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotDivide(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.divide(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotModulo(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.modulo(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete) => concrete.negate(),
|
|
||||||
Value::Reference(reference) => reference.negate(),
|
|
||||||
_ => Err(ValueError::CannotNegate(self.clone().into_concrete())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete) => concrete.not(),
|
|
||||||
Value::Reference(reference) => reference.not(),
|
|
||||||
_ => Err(ValueError::CannotNot(self.clone().into_concrete())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.equal(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.less_than(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than_or_equal(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
let (left, right) = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => (*left, *right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().into_concrete(),
|
|
||||||
other.clone().into_concrete(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
left.less_than_or_equal(right)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Display for Value<'a> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete) => write!(f, "{}", concrete),
|
|
||||||
Value::List(list) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (index, value) in list.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", value)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
Value::Reference(reference) => write!(f, "{}", reference),
|
|
||||||
Value::FunctionBorrowed(function_reference) => {
|
|
||||||
write!(f, "{}", function_reference.chunk().r#type())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Eq for Value<'a> {}
|
|
||||||
|
|
||||||
impl<'a> PartialOrd for Value<'a> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Ord for Value<'a> {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.cmp(right),
|
|
||||||
(Value::Concrete(_), _) => Ordering::Greater,
|
|
||||||
(Value::List(left), Value::List(right)) => left.cmp(right),
|
|
||||||
(Value::List(_), _) => Ordering::Greater,
|
|
||||||
(Value::Reference(left), Value::Reference(right)) => left.cmp(right),
|
|
||||||
(Value::Reference(_), _) => Ordering::Greater,
|
|
||||||
(Value::FunctionBorrowed(left), Value::FunctionBorrowed(right)) => left.cmp(right),
|
|
||||||
(Value::FunctionBorrowed(_), _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum ConcreteValue {
|
|
||||||
Boolean(bool),
|
|
||||||
Byte(u8),
|
|
||||||
Character(char),
|
|
||||||
Float(f64),
|
|
||||||
Function(Function),
|
|
||||||
Integer(i64),
|
|
||||||
List(Vec<ConcreteValue>),
|
|
||||||
Range(RangeValue),
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConcreteValue {
|
|
||||||
pub fn boolean(value: bool) -> Self {
|
|
||||||
ConcreteValue::Boolean(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn byte(value: u8) -> Self {
|
|
||||||
ConcreteValue::Byte(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn character(value: char) -> Self {
|
|
||||||
ConcreteValue::Character(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float(value: f64) -> Self {
|
|
||||||
ConcreteValue::Float(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn function(chunk: Chunk) -> Self {
|
|
||||||
ConcreteValue::Function(Function::new(chunk))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
|
||||||
ConcreteValue::Integer(into_i64.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
|
||||||
ConcreteValue::List(into_list.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn range(range: RangeValue) -> Self {
|
|
||||||
ConcreteValue::Range(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string<T: ToString>(to_string: T) -> Self {
|
|
||||||
ConcreteValue::String(to_string.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&String> {
|
|
||||||
if let ConcreteValue::String(string) = self {
|
|
||||||
Some(string)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_owned_value<'a>(self) -> Value<'a> {
|
|
||||||
Value::Concrete(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_reference_value(&self) -> Value {
|
|
||||||
Value::Reference(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(_) => Type::Boolean,
|
|
||||||
ConcreteValue::Byte(_) => Type::Byte,
|
|
||||||
ConcreteValue::Character(_) => Type::Character,
|
|
||||||
ConcreteValue::Float(_) => Type::Float,
|
|
||||||
ConcreteValue::Function(function) => function.r#type(),
|
|
||||||
ConcreteValue::Integer(_) => Type::Integer,
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
|
||||||
|
|
||||||
Type::List {
|
|
||||||
item_type: Box::new(item_type),
|
|
||||||
length: list.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range) => range.r#type(),
|
|
||||||
ConcreteValue::String(string) => Type::String {
|
|
||||||
length: Some(string.len()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let sum = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_add(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::float(*left + *right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_add(*right)),
|
|
||||||
(String(left), String(right)) => ConcreteValue::string(format!("{}{}", left, right)),
|
|
||||||
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(sum)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let difference = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_sub(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::float(left - right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_sub(*right)),
|
|
||||||
_ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(difference)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_mul(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::float(left * right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_mul(*right)),
|
|
||||||
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let quotient = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_div(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::float(left / right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_div(*right)),
|
|
||||||
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(quotient)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::byte(left.wrapping_rem(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::float(left % right),
|
|
||||||
(Integer(left), Integer(right)) => {
|
|
||||||
ConcreteValue::integer(left.wrapping_rem_euclid(*right))
|
|
||||||
}
|
|
||||||
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let negated = match self {
|
|
||||||
Boolean(value) => ConcreteValue::boolean(!value),
|
|
||||||
Byte(value) => ConcreteValue::byte(value.wrapping_neg()),
|
|
||||||
Float(value) => ConcreteValue::float(-value),
|
|
||||||
Integer(value) => ConcreteValue::integer(value.wrapping_neg()),
|
|
||||||
_ => return Err(ValueError::CannotNegate(self.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(negated)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let not = match self {
|
|
||||||
Boolean(value) => ConcreteValue::boolean(!value),
|
|
||||||
_ => return Err(ValueError::CannotNot(self.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(not)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let equal = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Function(left), Function(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::boolean(left == right),
|
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(equal)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Function(left), Function(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::boolean(left < right),
|
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than_or_equal(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than_or_equal = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Function(left), Function(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::boolean(left <= right),
|
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than_or_equal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for ConcreteValue {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
log::trace!("Cloning concrete value {}", self);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean),
|
|
||||||
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
|
|
||||||
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
|
||||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
|
||||||
ConcreteValue::Function(function) => ConcreteValue::Function(function.clone()),
|
|
||||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
|
||||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
|
||||||
ConcreteValue::Range(range) => ConcreteValue::Range(range.clone()),
|
|
||||||
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for ConcreteValue {}
|
|
||||||
|
|
||||||
impl PartialOrd for ConcreteValue {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for ConcreteValue {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match (self, other) {
|
|
||||||
(ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Boolean(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Byte(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Character(left), ConcreteValue::Character(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Character(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
|
||||||
left.to_bits().cmp(&right.to_bits())
|
|
||||||
}
|
|
||||||
(ConcreteValue::Float(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Function(left), ConcreteValue::Function(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Function(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Integer(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::List(left), ConcreteValue::List(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::List(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::Range(left), ConcreteValue::Range(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::Range(_), _) => Ordering::Greater,
|
|
||||||
(ConcreteValue::String(left), ConcreteValue::String(right)) => left.cmp(right),
|
|
||||||
(ConcreteValue::String(_), _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ConcreteValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"),
|
|
||||||
ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"),
|
|
||||||
ConcreteValue::Character(character) => write!(f, "{character}"),
|
|
||||||
ConcreteValue::Float(float) => {
|
|
||||||
write!(f, "{float}")?;
|
|
||||||
|
|
||||||
if float.fract() == 0.0 {
|
|
||||||
write!(f, ".0")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ConcreteValue::Function(function) => write!(f, "{function}"),
|
|
||||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (index, element) in list.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{element}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range_value) => {
|
|
||||||
write!(f, "{range_value}")
|
|
||||||
}
|
|
||||||
ConcreteValue::String(string) => write!(f, "{string}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum RangeValue {
|
|
||||||
ByteRange { start: u8, end: u8 },
|
|
||||||
ByteRangeInclusive { start: u8, end: u8 },
|
|
||||||
CharacterRange { start: char, end: char },
|
|
||||||
CharacterRangeInclusive { start: char, end: char },
|
|
||||||
FloatRange { start: f64, end: f64 },
|
|
||||||
FloatRangeInclusive { start: f64, end: f64 },
|
|
||||||
IntegerRange { start: i64, end: i64 },
|
|
||||||
IntegerRangeInclusive { start: i64, end: i64 },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RangeValue {
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
let inner_type = match self {
|
|
||||||
RangeValue::ByteRange { .. } | RangeValue::ByteRangeInclusive { .. } => Type::Byte,
|
|
||||||
RangeValue::CharacterRange { .. } | RangeValue::CharacterRangeInclusive { .. } => {
|
|
||||||
Type::Character
|
|
||||||
}
|
|
||||||
RangeValue::FloatRange { .. } | RangeValue::FloatRangeInclusive { .. } => Type::Float,
|
|
||||||
RangeValue::IntegerRange { .. } | RangeValue::IntegerRangeInclusive { .. } => {
|
|
||||||
Type::Integer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Type::Range {
|
|
||||||
r#type: Box::new(inner_type),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Range<u8>> for RangeValue {
|
|
||||||
fn from(range: Range<u8>) -> Self {
|
|
||||||
RangeValue::ByteRange {
|
|
||||||
start: range.start,
|
|
||||||
end: range.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RangeInclusive<u8>> for RangeValue {
|
|
||||||
fn from(range: RangeInclusive<u8>) -> Self {
|
|
||||||
RangeValue::ByteRangeInclusive {
|
|
||||||
start: *range.start(),
|
|
||||||
end: *range.end(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Range<char>> for RangeValue {
|
|
||||||
fn from(range: Range<char>) -> Self {
|
|
||||||
RangeValue::CharacterRange {
|
|
||||||
start: range.start,
|
|
||||||
end: range.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RangeInclusive<char>> for RangeValue {
|
|
||||||
fn from(range: RangeInclusive<char>) -> Self {
|
|
||||||
RangeValue::CharacterRangeInclusive {
|
|
||||||
start: *range.start(),
|
|
||||||
end: *range.end(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Range<f64>> for RangeValue {
|
|
||||||
fn from(range: Range<f64>) -> Self {
|
|
||||||
RangeValue::FloatRange {
|
|
||||||
start: range.start,
|
|
||||||
end: range.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RangeInclusive<f64>> for RangeValue {
|
|
||||||
fn from(range: RangeInclusive<f64>) -> Self {
|
|
||||||
RangeValue::FloatRangeInclusive {
|
|
||||||
start: *range.start(),
|
|
||||||
end: *range.end(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Range<i32>> for RangeValue {
|
|
||||||
fn from(range: Range<i32>) -> Self {
|
|
||||||
RangeValue::IntegerRange {
|
|
||||||
start: range.start as i64,
|
|
||||||
end: range.end as i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RangeInclusive<i32>> for RangeValue {
|
|
||||||
fn from(range: RangeInclusive<i32>) -> Self {
|
|
||||||
RangeValue::IntegerRangeInclusive {
|
|
||||||
start: *range.start() as i64,
|
|
||||||
end: *range.end() as i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Range<i64>> for RangeValue {
|
|
||||||
fn from(range: Range<i64>) -> Self {
|
|
||||||
RangeValue::IntegerRange {
|
|
||||||
start: range.start,
|
|
||||||
end: range.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RangeInclusive<i64>> for RangeValue {
|
|
||||||
fn from(range: RangeInclusive<i64>) -> Self {
|
|
||||||
RangeValue::IntegerRangeInclusive {
|
|
||||||
start: *range.start(),
|
|
||||||
end: *range.end(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for RangeValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
RangeValue::ByteRange { start, end } => write!(f, "{}..{}", start, end),
|
|
||||||
RangeValue::ByteRangeInclusive { start, end } => {
|
|
||||||
write!(f, "{}..={}", start, end)
|
|
||||||
}
|
|
||||||
RangeValue::CharacterRange { start, end } => {
|
|
||||||
write!(f, "{}..{}", start, end)
|
|
||||||
}
|
|
||||||
RangeValue::CharacterRangeInclusive { start, end } => {
|
|
||||||
write!(f, "{}..={}", start, end)
|
|
||||||
}
|
|
||||||
RangeValue::FloatRange { start, end } => write!(f, "{}..{}", start, end),
|
|
||||||
RangeValue::FloatRangeInclusive { start, end } => {
|
|
||||||
write!(f, "{}..={}", start, end)
|
|
||||||
}
|
|
||||||
RangeValue::IntegerRange { start, end } => write!(f, "{}..{}", start, end),
|
|
||||||
RangeValue::IntegerRangeInclusive { start, end } => {
|
|
||||||
write!(f, "{}..={}", start, end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for RangeValue {}
|
|
||||||
|
|
||||||
impl PartialOrd for RangeValue {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for RangeValue {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
match (self, other) {
|
|
||||||
(
|
|
||||||
RangeValue::ByteRange {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::ByteRange {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::ByteRange { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::ByteRangeInclusive {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::ByteRangeInclusive {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(&right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(&right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::ByteRangeInclusive { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::CharacterRange {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::CharacterRange {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::CharacterRange { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::CharacterRangeInclusive {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::CharacterRangeInclusive {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::CharacterRangeInclusive { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::FloatRange {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::FloatRange {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.partial_cmp(right_start).unwrap();
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.partial_cmp(right_end).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::FloatRange { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::FloatRangeInclusive {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::FloatRangeInclusive {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.partial_cmp(right_start).unwrap();
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.partial_cmp(right_end).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::FloatRangeInclusive { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::IntegerRange {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::IntegerRange {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::IntegerRange { .. }, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
RangeValue::IntegerRangeInclusive {
|
|
||||||
start: left_start,
|
|
||||||
end: left_end,
|
|
||||||
},
|
|
||||||
RangeValue::IntegerRangeInclusive {
|
|
||||||
start: right_start,
|
|
||||||
end: right_end,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let start_cmp = left_start.cmp(right_start);
|
|
||||||
|
|
||||||
if start_cmp != Ordering::Equal {
|
|
||||||
start_cmp
|
|
||||||
} else {
|
|
||||||
left_end.cmp(right_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(RangeValue::IntegerRangeInclusive { .. }, _) => Ordering::Greater,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum ValueError {
|
|
||||||
CannotAdd(ConcreteValue, ConcreteValue),
|
|
||||||
CannotAnd(ConcreteValue, ConcreteValue),
|
|
||||||
CannotCompare(ConcreteValue, ConcreteValue),
|
|
||||||
CannotDivide(ConcreteValue, ConcreteValue),
|
|
||||||
CannotModulo(ConcreteValue, ConcreteValue),
|
|
||||||
CannotMultiply(ConcreteValue, ConcreteValue),
|
|
||||||
CannotNegate(ConcreteValue),
|
|
||||||
CannotNot(ConcreteValue),
|
|
||||||
CannotSubtract(ConcreteValue, ConcreteValue),
|
|
||||||
CannotOr(ConcreteValue, ConcreteValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ValueError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ValueError::CannotAdd(left, right) => {
|
|
||||||
write!(f, "Cannot add {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotAnd(left, right) => {
|
|
||||||
write!(f, "Cannot use logical AND operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotCompare(left, right) => {
|
|
||||||
write!(f, "Cannot compare {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotDivide(left, right) => {
|
|
||||||
write!(f, "Cannot divide {left} by {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotModulo(left, right) => {
|
|
||||||
write!(f, "Cannot use modulo operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotMultiply(left, right) => {
|
|
||||||
write!(f, "Cannot multiply {left} by {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotNegate(value) => {
|
|
||||||
write!(f, "Cannot negate {value}")
|
|
||||||
}
|
|
||||||
ValueError::CannotNot(value) => {
|
|
||||||
write!(f, "Cannot use logical NOT operation on {value}")
|
|
||||||
}
|
|
||||||
ValueError::CannotSubtract(left, right) => {
|
|
||||||
write!(f, "Cannot subtract {right} from {left}")
|
|
||||||
}
|
|
||||||
ValueError::CannotOr(left, right) => {
|
|
||||||
write!(f, "Cannot use logical OR operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
347
dust-lang/src/value/mod.rs
Normal file
347
dust-lang/src/value/mod.rs
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
//! Runtime values used by the VM.
|
||||||
|
mod range;
|
||||||
|
|
||||||
|
pub use range::RangeValue;
|
||||||
|
|
||||||
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{Chunk, Type};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
|
pub enum Value {
|
||||||
|
Boolean(bool),
|
||||||
|
Byte(u8),
|
||||||
|
Character(char),
|
||||||
|
Float(f64),
|
||||||
|
Function(Chunk),
|
||||||
|
Integer(i64),
|
||||||
|
List(Vec<Value>),
|
||||||
|
Range(RangeValue),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn boolean(value: bool) -> Self {
|
||||||
|
Value::Boolean(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn byte(value: u8) -> Self {
|
||||||
|
Value::Byte(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn character(value: char) -> Self {
|
||||||
|
Value::Character(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float(value: f64) -> Self {
|
||||||
|
Value::Float(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function(chunk: Chunk) -> Self {
|
||||||
|
Value::Function(chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
||||||
|
Value::Integer(into_i64.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list<T: Into<Vec<Value>>>(into_list: T) -> Self {
|
||||||
|
Value::List(into_list.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(range: RangeValue) -> Self {
|
||||||
|
Value::Range(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string<T: ToString>(to_string: T) -> Self {
|
||||||
|
Value::String(to_string.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_string(&self) -> Option<&String> {
|
||||||
|
if let Value::String(string) = self {
|
||||||
|
Some(string)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#type(&self) -> Type {
|
||||||
|
match self {
|
||||||
|
Value::Boolean(_) => Type::Boolean,
|
||||||
|
Value::Byte(_) => Type::Byte,
|
||||||
|
Value::Character(_) => Type::Character,
|
||||||
|
Value::Float(_) => Type::Float,
|
||||||
|
Value::Function(chunk) => Type::Function(chunk.r#type().clone()),
|
||||||
|
Value::Integer(_) => Type::Integer,
|
||||||
|
Value::List(list) => {
|
||||||
|
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
||||||
|
|
||||||
|
Type::List {
|
||||||
|
item_type: Box::new(item_type),
|
||||||
|
length: list.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Range(range) => range.r#type(),
|
||||||
|
Value::String(string) => Type::String {
|
||||||
|
length: Some(string.len()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, other: &Self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let sum = match (self, other) {
|
||||||
|
(Byte(left), Byte(right)) => Value::byte(left.saturating_add(*right)),
|
||||||
|
(Float(left), Float(right)) => Value::float(*left + *right),
|
||||||
|
(Integer(left), Integer(right)) => Value::integer(left.saturating_add(*right)),
|
||||||
|
(String(left), String(right)) => Value::string(format!("{}{}", left, right)),
|
||||||
|
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtract(&self, other: &Self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let difference = match (self, other) {
|
||||||
|
(Byte(left), Byte(right)) => Value::byte(left.saturating_sub(*right)),
|
||||||
|
(Float(left), Float(right)) => Value::float(left - right),
|
||||||
|
(Integer(left), Integer(right)) => Value::integer(left.saturating_sub(*right)),
|
||||||
|
_ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(difference)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply(&self, other: &Self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let product = match (self, other) {
|
||||||
|
(Byte(left), Byte(right)) => Value::byte(left.saturating_mul(*right)),
|
||||||
|
(Float(left), Float(right)) => Value::float(left * right),
|
||||||
|
(Integer(left), Integer(right)) => Value::integer(left.saturating_mul(*right)),
|
||||||
|
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide(&self, other: &Self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let quotient = match (self, other) {
|
||||||
|
(Byte(left), Byte(right)) => Value::byte(left.saturating_div(*right)),
|
||||||
|
(Float(left), Float(right)) => Value::float(left / right),
|
||||||
|
(Integer(left), Integer(right)) => Value::integer(left.saturating_div(*right)),
|
||||||
|
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(quotient)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modulo(&self, other: &Self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let product = match (self, other) {
|
||||||
|
(Byte(left), Byte(right)) => Value::byte(left.wrapping_rem(*right)),
|
||||||
|
(Float(left), Float(right)) => Value::float(left % right),
|
||||||
|
(Integer(left), Integer(right)) => Value::integer(left.wrapping_rem_euclid(*right)),
|
||||||
|
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn negate(&self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let negated = match self {
|
||||||
|
Boolean(value) => Value::boolean(!value),
|
||||||
|
Byte(value) => Value::byte(value.wrapping_neg()),
|
||||||
|
Float(value) => Value::float(-value),
|
||||||
|
Integer(value) => Value::integer(value.wrapping_neg()),
|
||||||
|
_ => return Err(ValueError::CannotNegate(self.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(negated)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not(&self) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let not = match self {
|
||||||
|
Boolean(value) => Value::boolean(!value),
|
||||||
|
_ => return Err(ValueError::CannotNot(self.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(not)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let equal = match (self, other) {
|
||||||
|
(Boolean(left), Boolean(right)) => Value::boolean(left == right),
|
||||||
|
(Byte(left), Byte(right)) => Value::boolean(left == right),
|
||||||
|
(Character(left), Character(right)) => Value::boolean(left == right),
|
||||||
|
(Float(left), Float(right)) => Value::boolean(left == right),
|
||||||
|
(Function(left), Function(right)) => Value::boolean(left == right),
|
||||||
|
(Integer(left), Integer(right)) => Value::boolean(left == right),
|
||||||
|
(List(left), List(right)) => Value::boolean(left == right),
|
||||||
|
(Range(left), Range(right)) => Value::boolean(left == right),
|
||||||
|
(String(left), String(right)) => Value::boolean(left == right),
|
||||||
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(equal)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let less_than = match (self, other) {
|
||||||
|
(Boolean(left), Boolean(right)) => Value::boolean(left < right),
|
||||||
|
(Byte(left), Byte(right)) => Value::boolean(left < right),
|
||||||
|
(Character(left), Character(right)) => Value::boolean(left < right),
|
||||||
|
(Float(left), Float(right)) => Value::boolean(left < right),
|
||||||
|
(Function(left), Function(right)) => Value::boolean(left < right),
|
||||||
|
(Integer(left), Integer(right)) => Value::boolean(left < right),
|
||||||
|
(List(left), List(right)) => Value::boolean(left < right),
|
||||||
|
(Range(left), Range(right)) => Value::boolean(left < right),
|
||||||
|
(String(left), String(right)) => Value::boolean(left < right),
|
||||||
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(less_than)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn less_than_or_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
|
use Value::*;
|
||||||
|
|
||||||
|
let less_than_or_equal = match (self, other) {
|
||||||
|
(Boolean(left), Boolean(right)) => Value::boolean(left <= right),
|
||||||
|
(Byte(left), Byte(right)) => Value::boolean(left <= right),
|
||||||
|
(Character(left), Character(right)) => Value::boolean(left <= right),
|
||||||
|
(Float(left), Float(right)) => Value::boolean(left <= right),
|
||||||
|
(Function(left), Function(right)) => Value::boolean(left <= right),
|
||||||
|
(Integer(left), Integer(right)) => Value::boolean(left <= right),
|
||||||
|
(List(left), List(right)) => Value::boolean(left <= right),
|
||||||
|
(Range(left), Range(right)) => Value::boolean(left <= right),
|
||||||
|
(String(left), String(right)) => Value::boolean(left <= right),
|
||||||
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(less_than_or_equal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Value {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
log::trace!("Cloning concrete value {}", self);
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Value::Boolean(boolean) => Value::Boolean(*boolean),
|
||||||
|
Value::Byte(byte) => Value::Byte(*byte),
|
||||||
|
Value::Character(character) => Value::Character(*character),
|
||||||
|
Value::Float(float) => Value::Float(*float),
|
||||||
|
Value::Function(function) => Value::Function(function.clone()),
|
||||||
|
Value::Integer(integer) => Value::Integer(*integer),
|
||||||
|
Value::List(list) => Value::List(list.clone()),
|
||||||
|
Value::Range(range) => Value::Range(range.clone()),
|
||||||
|
Value::String(string) => Value::String(string.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Value {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
|
Value::Byte(byte) => write!(f, "0x{byte:02x}"),
|
||||||
|
Value::Character(character) => write!(f, "{character}"),
|
||||||
|
Value::Float(float) => {
|
||||||
|
write!(f, "{float}")?;
|
||||||
|
|
||||||
|
if float.fract() == 0.0 {
|
||||||
|
write!(f, ".0")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
|
Value::Integer(integer) => write!(f, "{integer}"),
|
||||||
|
Value::List(list) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
for (index, element) in list.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{element}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
Value::Range(range_value) => {
|
||||||
|
write!(f, "{range_value}")
|
||||||
|
}
|
||||||
|
Value::String(string) => write!(f, "{string}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ValueError {
|
||||||
|
CannotAdd(Value, Value),
|
||||||
|
CannotAnd(Value, Value),
|
||||||
|
CannotCompare(Value, Value),
|
||||||
|
CannotDivide(Value, Value),
|
||||||
|
CannotModulo(Value, Value),
|
||||||
|
CannotMultiply(Value, Value),
|
||||||
|
CannotNegate(Value),
|
||||||
|
CannotNot(Value),
|
||||||
|
CannotSubtract(Value, Value),
|
||||||
|
CannotOr(Value, Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ValueError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ValueError::CannotAdd(left, right) => {
|
||||||
|
write!(f, "Cannot add {left} and {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotAnd(left, right) => {
|
||||||
|
write!(f, "Cannot use logical AND operation on {left} and {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotCompare(left, right) => {
|
||||||
|
write!(f, "Cannot compare {left} and {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotDivide(left, right) => {
|
||||||
|
write!(f, "Cannot divide {left} by {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotModulo(left, right) => {
|
||||||
|
write!(f, "Cannot use modulo operation on {left} and {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotMultiply(left, right) => {
|
||||||
|
write!(f, "Cannot multiply {left} by {right}")
|
||||||
|
}
|
||||||
|
ValueError::CannotNegate(value) => {
|
||||||
|
write!(f, "Cannot negate {value}")
|
||||||
|
}
|
||||||
|
ValueError::CannotNot(value) => {
|
||||||
|
write!(f, "Cannot use logical NOT operation on {value}")
|
||||||
|
}
|
||||||
|
ValueError::CannotSubtract(left, right) => {
|
||||||
|
write!(f, "Cannot subtract {right} from {left}")
|
||||||
|
}
|
||||||
|
ValueError::CannotOr(left, right) => {
|
||||||
|
write!(f, "Cannot use logical OR operation on {left} and {right}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
dust-lang/src/value/range.rs
Normal file
154
dust-lang/src/value/range.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::{self, Display, Formatter},
|
||||||
|
ops::{Range, RangeInclusive},
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::Type;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
|
pub enum RangeValue {
|
||||||
|
ByteRange { start: u8, end: u8 },
|
||||||
|
ByteRangeInclusive { start: u8, end: u8 },
|
||||||
|
CharacterRange { start: char, end: char },
|
||||||
|
CharacterRangeInclusive { start: char, end: char },
|
||||||
|
FloatRange { start: f64, end: f64 },
|
||||||
|
FloatRangeInclusive { start: f64, end: f64 },
|
||||||
|
IntegerRange { start: i64, end: i64 },
|
||||||
|
IntegerRangeInclusive { start: i64, end: i64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RangeValue {
|
||||||
|
pub fn r#type(&self) -> Type {
|
||||||
|
let inner_type = match self {
|
||||||
|
RangeValue::ByteRange { .. } | RangeValue::ByteRangeInclusive { .. } => Type::Byte,
|
||||||
|
RangeValue::CharacterRange { .. } | RangeValue::CharacterRangeInclusive { .. } => {
|
||||||
|
Type::Character
|
||||||
|
}
|
||||||
|
RangeValue::FloatRange { .. } | RangeValue::FloatRangeInclusive { .. } => Type::Float,
|
||||||
|
RangeValue::IntegerRange { .. } | RangeValue::IntegerRangeInclusive { .. } => {
|
||||||
|
Type::Integer
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::Range {
|
||||||
|
r#type: Box::new(inner_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<u8>> for RangeValue {
|
||||||
|
fn from(range: Range<u8>) -> Self {
|
||||||
|
RangeValue::ByteRange {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<u8>> for RangeValue {
|
||||||
|
fn from(range: RangeInclusive<u8>) -> Self {
|
||||||
|
RangeValue::ByteRangeInclusive {
|
||||||
|
start: *range.start(),
|
||||||
|
end: *range.end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<char>> for RangeValue {
|
||||||
|
fn from(range: Range<char>) -> Self {
|
||||||
|
RangeValue::CharacterRange {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<char>> for RangeValue {
|
||||||
|
fn from(range: RangeInclusive<char>) -> Self {
|
||||||
|
RangeValue::CharacterRangeInclusive {
|
||||||
|
start: *range.start(),
|
||||||
|
end: *range.end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<f64>> for RangeValue {
|
||||||
|
fn from(range: Range<f64>) -> Self {
|
||||||
|
RangeValue::FloatRange {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<f64>> for RangeValue {
|
||||||
|
fn from(range: RangeInclusive<f64>) -> Self {
|
||||||
|
RangeValue::FloatRangeInclusive {
|
||||||
|
start: *range.start(),
|
||||||
|
end: *range.end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<i32>> for RangeValue {
|
||||||
|
fn from(range: Range<i32>) -> Self {
|
||||||
|
RangeValue::IntegerRange {
|
||||||
|
start: range.start as i64,
|
||||||
|
end: range.end as i64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<i32>> for RangeValue {
|
||||||
|
fn from(range: RangeInclusive<i32>) -> Self {
|
||||||
|
RangeValue::IntegerRangeInclusive {
|
||||||
|
start: *range.start() as i64,
|
||||||
|
end: *range.end() as i64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Range<i64>> for RangeValue {
|
||||||
|
fn from(range: Range<i64>) -> Self {
|
||||||
|
RangeValue::IntegerRange {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<i64>> for RangeValue {
|
||||||
|
fn from(range: RangeInclusive<i64>) -> Self {
|
||||||
|
RangeValue::IntegerRangeInclusive {
|
||||||
|
start: *range.start(),
|
||||||
|
end: *range.end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RangeValue {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
RangeValue::ByteRange { start, end } => write!(f, "{}..{}", start, end),
|
||||||
|
RangeValue::ByteRangeInclusive { start, end } => {
|
||||||
|
write!(f, "{}..={}", start, end)
|
||||||
|
}
|
||||||
|
RangeValue::CharacterRange { start, end } => {
|
||||||
|
write!(f, "{}..{}", start, end)
|
||||||
|
}
|
||||||
|
RangeValue::CharacterRangeInclusive { start, end } => {
|
||||||
|
write!(f, "{}..={}", start, end)
|
||||||
|
}
|
||||||
|
RangeValue::FloatRange { start, end } => write!(f, "{}..{}", start, end),
|
||||||
|
RangeValue::FloatRangeInclusive { start, end } => {
|
||||||
|
write!(f, "{}..={}", start, end)
|
||||||
|
}
|
||||||
|
RangeValue::IntegerRange { start, end } => write!(f, "{}..{}", start, end),
|
||||||
|
RangeValue::IntegerRangeInclusive { start, end } => {
|
||||||
|
write!(f, "{}..={}", start, end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,21 +3,28 @@ use std::{
|
|||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
mem::replace,
|
|
||||||
rc::Weak,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionBorrowed,
|
compile, value::Value, AnnotatedError, Chunk, ChunkError, DustError, Instruction,
|
||||||
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
NativeFunction, NativeFunctionError, Operation, Span, Type, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
|
let has_return_value = *chunk.r#type().return_type != Type::None;
|
||||||
let mut vm = Vm::new(&chunk, None);
|
let mut vm = Vm::new(&chunk, None);
|
||||||
|
|
||||||
vm.run()
|
vm.run()
|
||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })?;
|
||||||
|
|
||||||
|
if has_return_value {
|
||||||
|
vm.take_top_of_stack_as_value()
|
||||||
|
.map(Some)
|
||||||
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_and_display_output(source: &str) {
|
pub fn run_and_display_output(source: &str) {
|
||||||
@ -31,36 +38,43 @@ pub fn run_and_display_output(source: &str) {
|
|||||||
/// Dust virtual machine.
|
/// Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Vm<'chunk, 'parent, 'stack> {
|
pub struct Vm<'a> {
|
||||||
|
chunk: &'a Chunk,
|
||||||
|
stack: Vec<Register>,
|
||||||
|
parent: Option<&'a Vm<'a>>,
|
||||||
|
|
||||||
ip: usize,
|
ip: usize,
|
||||||
chunk: &'chunk Chunk,
|
|
||||||
stack: Vec<Register<'stack>>,
|
|
||||||
local_definitions: HashMap<u8, u8>,
|
local_definitions: HashMap<u8, u8>,
|
||||||
last_assigned_register: Option<u8>,
|
last_assigned_register: Option<u8>,
|
||||||
parent: Option<&'parent Vm<'chunk, 'stack, 'parent>>,
|
current_position: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
impl<'a> Vm<'a> {
|
||||||
const STACK_LIMIT: usize = u16::MAX as usize;
|
const STACK_LIMIT: usize = u16::MAX as usize;
|
||||||
|
|
||||||
pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent, 'stack>>) -> Self {
|
pub fn new(chunk: &'a Chunk, parent: Option<&'a Vm<'a>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ip: 0,
|
|
||||||
chunk,
|
chunk,
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
|
parent,
|
||||||
|
ip: 0,
|
||||||
local_definitions: HashMap::new(),
|
local_definitions: HashMap::new(),
|
||||||
last_assigned_register: None,
|
last_assigned_register: None,
|
||||||
parent,
|
current_position: Span(0, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&'stack mut self) -> Result<Option<ConcreteValue>, VmError> {
|
pub fn current_position(&self) -> Span {
|
||||||
while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() {
|
self.current_position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) -> Result<(), VmError> {
|
||||||
|
while let Ok(instruction) = self.read() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"{} | {} | {} | {}",
|
"{} | {} | {} | {}",
|
||||||
self.ip - 1,
|
self.ip - 1,
|
||||||
position,
|
self.current_position,
|
||||||
instruction.operation(),
|
instruction.operation(),
|
||||||
instruction.disassembly_info(self.chunk)
|
instruction.disassembly_info(self.chunk)
|
||||||
);
|
);
|
||||||
@ -73,13 +87,10 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
.stack
|
.stack
|
||||||
.get(from_register as usize)
|
.get(from_register as usize)
|
||||||
.is_some_and(|register| !matches!(register, Register::Empty));
|
.is_some_and(|register| !matches!(register, Register::Empty));
|
||||||
|
let register = Register::Pointer(Pointer::Stack(from_register));
|
||||||
|
|
||||||
if from_register_has_value {
|
if from_register_has_value {
|
||||||
self.set_register(
|
self.set_register(to_register, register)?;
|
||||||
to_register,
|
|
||||||
Register::StackPointer(from_register),
|
|
||||||
position,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
@ -87,7 +98,9 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let to_register = instruction.c();
|
let to_register = instruction.c();
|
||||||
|
|
||||||
if self.stack.len() < to_register as usize {
|
if self.stack.len() < to_register as usize {
|
||||||
return Err(VmError::StackUnderflow { position });
|
return Err(VmError::StackUnderflow {
|
||||||
|
position: self.current_position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for register_index in from_register..to_register {
|
for register_index in from_register..to_register {
|
||||||
@ -98,9 +111,9 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let boolean = instruction.b_as_boolean();
|
let boolean = instruction.b_as_boolean();
|
||||||
let jump = instruction.c_as_boolean();
|
let jump = instruction.c_as_boolean();
|
||||||
let boolean = ConcreteValue::boolean(boolean);
|
let boolean = Value::boolean(boolean);
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(boolean), position)?;
|
self.set_register(to_register, Register::Value(boolean))?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
@ -113,8 +126,7 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
|
|
||||||
self.set_register(
|
self.set_register(
|
||||||
to_register,
|
to_register,
|
||||||
Register::ConstantPointer(from_constant),
|
Register::Pointer(Pointer::Constant(from_constant)),
|
||||||
position,
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if jump {
|
if jump {
|
||||||
@ -127,18 +139,19 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
|
|
||||||
for register_index in start_register..to_register {
|
for register_index in start_register..to_register {
|
||||||
let value = self.open_register(register_index, position)?;
|
let value = self.open_register(register_index)?;
|
||||||
|
|
||||||
list.push(value);
|
list.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_register(to_register, Register::List(list), position)?;
|
// self.set_register(to_register, Register::List(list))?;
|
||||||
|
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
Operation::LoadSelf => {
|
Operation::LoadSelf => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let function = Value::FunctionBorrowed(FunctionBorrowed::new(self.chunk));
|
|
||||||
|
|
||||||
// self.set_register(to_register, Register::Value(function), position)?;
|
// self.set_register(to_register, Register::Value(function))?;
|
||||||
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@ -154,15 +167,12 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
|
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
|
||||||
VmError::UndefinedLocal {
|
VmError::UndefinedLocal {
|
||||||
local_index,
|
local_index,
|
||||||
position,
|
position: self.current_position,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
let register = Register::Pointer(Pointer::Stack(local_register));
|
||||||
|
|
||||||
self.set_register(
|
self.set_register(to_register, register)?;
|
||||||
to_register,
|
|
||||||
Register::StackPointer(local_register),
|
|
||||||
position,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let from_register = instruction.a();
|
let from_register = instruction.a();
|
||||||
@ -170,71 +180,73 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
|
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
|
||||||
VmError::UndefinedLocal {
|
VmError::UndefinedLocal {
|
||||||
local_index: to_local,
|
local_index: to_local,
|
||||||
position,
|
position: self.current_position,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
let register = Register::Pointer(Pointer::Stack(from_register));
|
||||||
|
|
||||||
self.set_register(
|
self.set_register(local_register, register)?;
|
||||||
local_register,
|
|
||||||
Register::StackPointer(from_register),
|
|
||||||
position,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let sum = left
|
let sum = left.add(right).map_err(|error| VmError::Value {
|
||||||
.add(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(sum), position)?;
|
self.set_register(to_register, Register::Value(sum))?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let difference = left
|
let difference = left.subtract(right).map_err(|error| VmError::Value {
|
||||||
.subtract(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(difference), position)?;
|
self.set_register(to_register, Register::Value(difference))?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let product = left
|
let product = left.multiply(right).map_err(|error| VmError::Value {
|
||||||
.multiply(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(product), position)?;
|
self.set_register(to_register, Register::Value(product))?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let quotient = left
|
let quotient = left.divide(right).map_err(|error| VmError::Value {
|
||||||
.divide(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(quotient), position)?;
|
self.set_register(to_register, Register::Value(quotient))?;
|
||||||
}
|
}
|
||||||
Operation::Modulo => {
|
Operation::Modulo => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let remainder = left
|
let remainder = left.modulo(right).map_err(|error| VmError::Value {
|
||||||
.modulo(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(remainder), position)?;
|
self.set_register(to_register, Register::Value(remainder))?;
|
||||||
}
|
}
|
||||||
Operation::Test => {
|
Operation::Test => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
let test_value = instruction.c_as_boolean();
|
let test_value = instruction.c_as_boolean();
|
||||||
let value = self.open_register(register, position)?;
|
let value = self.open_register(register)?;
|
||||||
let boolean = if let Some(boolean) = value.as_boolean() {
|
let boolean = if let Value::Boolean(boolean) = value {
|
||||||
boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
found: value.into_concrete(),
|
found: value.clone(),
|
||||||
position,
|
position: self.current_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -245,270 +257,250 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
Operation::TestSet => todo!(),
|
Operation::TestSet => todo!(),
|
||||||
Operation::Equal => {
|
Operation::Equal => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let equal_result = left
|
let equal_result = left.equal(right).map_err(|error| VmError::Value {
|
||||||
.equal(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result {
|
})?;
|
||||||
|
let is_equal = if let Value::Boolean(boolean) = equal_result {
|
||||||
boolean
|
boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
found: equal_result.clone(),
|
found: equal_result.clone(),
|
||||||
position,
|
position: self.current_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_equal == compare_to {
|
if is_equal == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.get_instruction(self.ip, position)?.0;
|
let jump = self.get_instruction(self.ip)?.0;
|
||||||
|
|
||||||
self.jump(jump);
|
self.jump(jump);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Less => {
|
Operation::Less => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let less_result = left
|
let less_result = left.less_than(right).map_err(|error| VmError::Value {
|
||||||
.less_than(&right)
|
error,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
position: self.current_position,
|
||||||
let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result {
|
})?;
|
||||||
|
let is_less_than = if let Value::Boolean(boolean) = less_result {
|
||||||
boolean
|
boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
found: less_result.clone(),
|
found: less_result.clone(),
|
||||||
position,
|
position: self.current_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_less_than == compare_to {
|
if is_less_than == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.get_instruction(self.ip, position)?.0;
|
let jump = self.get_instruction(self.ip)?.0;
|
||||||
|
|
||||||
self.jump(jump);
|
self.jump(jump);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LessEqual => {
|
Operation::LessEqual => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.get_instruction(self.ip, position)?.0.operation(),
|
self.get_instruction(self.ip)?.0.operation(),
|
||||||
Operation::Jump
|
Operation::Jump
|
||||||
);
|
);
|
||||||
|
|
||||||
let compare_to = instruction.a_as_boolean();
|
let compare_to = instruction.a_as_boolean();
|
||||||
let (left, right) = self.get_arguments(instruction, position)?;
|
let (left, right) = self.get_arguments(instruction)?;
|
||||||
let less_or_equal_result = left
|
let less_or_equal_result =
|
||||||
.less_than_or_equal(&right)
|
left.less_than_or_equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value {
|
||||||
|
error,
|
||||||
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
let is_less_than_or_equal =
|
let is_less_than_or_equal =
|
||||||
if let ConcreteValue::Boolean(boolean) = less_or_equal_result {
|
if let Value::Boolean(boolean) = less_or_equal_result {
|
||||||
boolean
|
boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
found: less_or_equal_result.clone(),
|
found: less_or_equal_result.clone(),
|
||||||
position,
|
position: self.current_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_less_than_or_equal == compare_to {
|
if is_less_than_or_equal == compare_to {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
} else {
|
} else {
|
||||||
let jump = self.get_instruction(self.ip, position)?.0;
|
let jump = self.get_instruction(self.ip)?.0;
|
||||||
|
|
||||||
self.jump(jump);
|
self.jump(jump);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value =
|
let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
|
||||||
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
let negated = value.negate().map_err(|error| VmError::Value {
|
||||||
let negated = value
|
error,
|
||||||
.negate()
|
position: self.current_position,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
})?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), Register::Value(negated), position)?;
|
self.set_register(instruction.a(), Register::Value(negated))?;
|
||||||
}
|
}
|
||||||
Operation::Not => {
|
Operation::Not => {
|
||||||
let value =
|
let value = self.get_argument(instruction.b(), instruction.b_is_constant())?;
|
||||||
self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
let not = value.not().map_err(|error| VmError::Value {
|
||||||
let not = value
|
error,
|
||||||
.not()
|
position: self.current_position,
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
})?;
|
||||||
|
|
||||||
self.set_register(instruction.a(), Register::Value(not), position)?;
|
self.set_register(instruction.a(), Register::Value(not))?;
|
||||||
}
|
}
|
||||||
Operation::Jump => self.jump(instruction),
|
Operation::Jump => self.jump(instruction),
|
||||||
Operation::Call => {
|
Operation::Call => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let function_register = instruction.b();
|
let function_register = instruction.b();
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let value = self.open_register(function_register, position)?;
|
let value = self.open_register(function_register)?;
|
||||||
let function = if let Some(function) = value.as_function() {
|
let chunk = if let Value::Function(chunk) = value {
|
||||||
function
|
chunk
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedFunction {
|
return Err(VmError::ExpectedFunction {
|
||||||
found: value.into_concrete(),
|
found: value.clone(),
|
||||||
position,
|
position: self.current_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
let mut function_vm = Vm::new(function.chunk(), Some(self));
|
let has_return_value = *chunk.r#type().return_type != Type::None;
|
||||||
|
let mut function_vm = Vm::new(chunk, Some(self));
|
||||||
let first_argument_index = function_register + 1;
|
let first_argument_index = function_register + 1;
|
||||||
|
let last_argument_index = first_argument_index + argument_count;
|
||||||
|
|
||||||
for argument_index in
|
for argument_index in first_argument_index..last_argument_index {
|
||||||
first_argument_index..first_argument_index + argument_count
|
|
||||||
{
|
|
||||||
let top_of_stack = function_vm.stack.len() as u8;
|
let top_of_stack = function_vm.stack.len() as u8;
|
||||||
|
|
||||||
function_vm.set_register(
|
function_vm.set_register(
|
||||||
top_of_stack,
|
top_of_stack,
|
||||||
Register::ParentStackPointer(argument_index),
|
Register::Pointer(Pointer::ParentStack(argument_index)),
|
||||||
position,
|
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_value = function_vm.run()?;
|
function_vm.run()?;
|
||||||
|
|
||||||
if let Some(value) = return_value {
|
if has_return_value {
|
||||||
self.set_register(to_register, Register::Value(value), position)?;
|
let top_of_stack = function_vm.stack.len() as u8 - 1;
|
||||||
|
|
||||||
|
self.set_register(
|
||||||
|
to_register,
|
||||||
|
Register::Pointer(Pointer::ParentStack(top_of_stack)),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let native_function = NativeFunction::from(instruction.b());
|
let native_function = NativeFunction::from(instruction.b());
|
||||||
let return_value = native_function.call(self, instruction, position)?;
|
let return_value = native_function.call(self, instruction)?;
|
||||||
|
|
||||||
if let Some(concrete_value) = return_value {
|
if let Some(concrete_value) = return_value {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
|
||||||
self.set_register(to_register, Register::Value(concrete_value), position)?;
|
self.set_register(to_register, Register::Value(concrete_value))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let should_return_value = instruction.b_as_boolean();
|
let should_return_value = instruction.b_as_boolean();
|
||||||
|
|
||||||
if !should_return_value {
|
if !should_return_value {
|
||||||
return Ok(None);
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
return if let Some(register_index) = self.last_assigned_register {
|
return if let Some(register_index) = self.last_assigned_register {
|
||||||
self.open_register(register_index, position)
|
let top_of_stack = self.stack.len() as u8 - 1;
|
||||||
.map(|value| Some(value.into_concrete()))
|
|
||||||
|
if register_index != top_of_stack {
|
||||||
|
self.stack
|
||||||
|
.push(Register::Pointer(Pointer::Stack(register_index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::StackUnderflow { position })
|
Err(VmError::StackUnderflow {
|
||||||
|
position: self.current_position,
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn open_register(
|
fn resolve_pointer(&self, pointer: Pointer) -> Result<&Value, VmError> {
|
||||||
&'stack self,
|
match pointer {
|
||||||
register_index: u8,
|
Pointer::Stack(register_index) => self.open_register(register_index),
|
||||||
position: Span,
|
Pointer::Constant(constant_index) => self.get_constant(constant_index),
|
||||||
) -> Result<&'stack ConcreteValue, VmError> {
|
Pointer::ParentStack(register_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| VmError::ExpectedParent {
|
||||||
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
parent.open_register(register_index)
|
||||||
|
}
|
||||||
|
Pointer::ParentConstant(constant_index) => {
|
||||||
|
let parent = self
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| VmError::ExpectedParent {
|
||||||
|
position: self.current_position,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
parent.get_constant(constant_index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn open_register(&self, register_index: u8) -> Result<&Value, VmError> {
|
||||||
let register_index = register_index as usize;
|
let register_index = register_index as usize;
|
||||||
let register =
|
let register =
|
||||||
self.stack
|
self.stack
|
||||||
.get(register_index)
|
.get(register_index)
|
||||||
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
||||||
index: register_index,
|
index: register_index,
|
||||||
position,
|
position: self.current_position,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
log::trace!("Open R{register_index} to {register}");
|
log::trace!("Open R{register_index} to {register}");
|
||||||
|
|
||||||
match register {
|
match register {
|
||||||
Register::Value(value) => Ok(value),
|
Register::Value(value) => Ok(value),
|
||||||
Register::List(list) => Ok(ConcreteValue::List(
|
Register::Pointer(pointer) => self.resolve_pointer(*pointer),
|
||||||
list.into_iter()
|
|
||||||
.map(|concrete_value| (*concrete_value).clone())
|
|
||||||
.collect(),
|
|
||||||
)),
|
|
||||||
Register::StackPointer(register_index) => self.open_register(*register_index, position),
|
|
||||||
Register::ConstantPointer(constant_index) => {
|
|
||||||
self.get_constant(*constant_index, position)
|
|
||||||
}
|
|
||||||
Register::ParentStackPointer(register_index) => {
|
|
||||||
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.open_register(*register_index, position)
|
|
||||||
}
|
|
||||||
Register::ParentConstantPointer(constant_index) => {
|
|
||||||
let parent = self
|
|
||||||
.parent
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
let constant = parent.get_constant(*constant_index, position)?;
|
|
||||||
|
|
||||||
Ok(constant)
|
|
||||||
}
|
|
||||||
Register::Empty => Err(VmError::EmptyRegister {
|
Register::Empty => Err(VmError::EmptyRegister {
|
||||||
index: register_index,
|
index: register_index,
|
||||||
position,
|
position: self.current_position,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_concrete_from_register(
|
fn take_top_of_stack_as_value(&mut self) -> Result<Value, VmError> {
|
||||||
&'stack self,
|
let top_of_stack = self.stack.pop().ok_or(VmError::StackUnderflow {
|
||||||
register_index: u8,
|
position: self.current_position,
|
||||||
position: Span,
|
})?;
|
||||||
) -> Result<ConcreteValue, VmError> {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
let register =
|
|
||||||
self.stack
|
|
||||||
.get(register_index)
|
|
||||||
.ok_or_else(|| VmError::RegisterIndexOutOfBounds {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let value = match register {
|
match top_of_stack {
|
||||||
Register::Value(concrete_value) => concrete_value.clone(),
|
Register::Value(value) => Ok(value),
|
||||||
Register::List(list) => {
|
_ => Err(VmError::ExpectedValue {
|
||||||
let items = list.into_iter().map(|value| (*value).clone()).collect();
|
found: top_of_stack,
|
||||||
|
position: self.current_position,
|
||||||
ConcreteValue::List(items)
|
}),
|
||||||
}
|
}
|
||||||
Register::StackPointer(register_index) => {
|
|
||||||
self.get_concrete_from_register(*register_index, position)?
|
|
||||||
}
|
|
||||||
Register::ConstantPointer(constant_pointer) => {
|
|
||||||
self.get_constant(*constant_pointer, position)?.clone()
|
|
||||||
}
|
|
||||||
Register::ParentStackPointer(register_index) => {
|
|
||||||
let parent = self.parent.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.get_concrete_from_register(*register_index, position)?
|
|
||||||
}
|
|
||||||
Register::ParentConstantPointer(constant_index) => {
|
|
||||||
let parent = self
|
|
||||||
.parent
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(VmError::ExpectedParent { position })?;
|
|
||||||
|
|
||||||
parent.get_constant(*constant_index, position)?.clone()
|
|
||||||
}
|
|
||||||
Register::Empty => {
|
|
||||||
return Err(VmError::EmptyRegister {
|
|
||||||
index: register_index,
|
|
||||||
position,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper for handling JUMP instructions
|
/// DRY helper for handling JUMP instructions
|
||||||
@ -524,46 +516,34 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper to get a constant or register values
|
/// DRY helper to get a constant or register values
|
||||||
fn get_argument(
|
fn get_argument(&self, index: u8, is_constant: bool) -> Result<&Value, VmError> {
|
||||||
&'stack self,
|
|
||||||
index: u8,
|
|
||||||
is_constant: bool,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<Value<'stack>, VmError> {
|
|
||||||
let argument = if is_constant {
|
let argument = if is_constant {
|
||||||
self.get_constant(index, position)?.as_reference_value()
|
self.get_constant(index)?
|
||||||
} else {
|
} else {
|
||||||
self.open_register(index, position)?
|
self.open_register(index)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(argument)
|
Ok(argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// DRY helper to get two arguments for binary operations
|
/// DRY helper to get two arguments for binary operations
|
||||||
fn get_arguments(
|
fn get_arguments(&self, instruction: Instruction) -> Result<(&Value, &Value), VmError> {
|
||||||
&'stack self,
|
let left = self.get_argument(instruction.b(), instruction.b_is_constant())?;
|
||||||
instruction: Instruction,
|
let right = self.get_argument(instruction.c(), instruction.c_is_constant())?;
|
||||||
position: Span,
|
|
||||||
) -> Result<(Value<'stack>, Value<'stack>), VmError> {
|
|
||||||
let left = self.get_argument(instruction.b(), instruction.b_is_constant(), position)?;
|
|
||||||
let right = self.get_argument(instruction.c(), instruction.c_is_constant(), position)?;
|
|
||||||
|
|
||||||
Ok((left, right))
|
Ok((left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_register(
|
fn set_register(&mut self, to_register: u8, register: Register) -> Result<(), VmError> {
|
||||||
&mut self,
|
|
||||||
to_register: u8,
|
|
||||||
register: Register<'stack>,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<(), VmError> {
|
|
||||||
self.last_assigned_register = Some(to_register);
|
self.last_assigned_register = Some(to_register);
|
||||||
|
|
||||||
let length = self.stack.len();
|
let length = self.stack.len();
|
||||||
let to_register = to_register as usize;
|
let to_register = to_register as usize;
|
||||||
|
|
||||||
if length == Self::STACK_LIMIT {
|
if length == Self::STACK_LIMIT {
|
||||||
return Err(VmError::StackOverflow { position });
|
return Err(VmError::StackOverflow {
|
||||||
|
position: self.current_position,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match to_register.cmp(&length) {
|
match to_register.cmp(&length) {
|
||||||
@ -599,28 +579,31 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_constant(&self, index: u8, position: Span) -> Result<&'chunk ConcreteValue, VmError> {
|
fn get_constant(&self, index: u8) -> Result<&Value, VmError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_constant(index)
|
.get_constant(index)
|
||||||
.map_err(|error| VmError::Chunk { error, position })
|
.map_err(|error| VmError::Chunk {
|
||||||
|
error,
|
||||||
|
position: self.current_position,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
fn read(&mut self) -> Result<Instruction, VmError> {
|
||||||
let ip = self.ip;
|
let (instruction, position) = *self.get_instruction(self.ip)?;
|
||||||
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
self.current_position = position;
|
||||||
|
|
||||||
self.get_instruction(ip, position)
|
Ok(instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_instruction(
|
fn get_instruction(&self, index: usize) -> Result<&(Instruction, Span), VmError> {
|
||||||
&self,
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
) -> Result<&(Instruction, Span), VmError> {
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_instruction(index)
|
.get_instruction(index)
|
||||||
.map_err(|error| VmError::Chunk { error, position })
|
.map_err(|error| VmError::Chunk {
|
||||||
|
error,
|
||||||
|
position: self.current_position,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
|
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
|
||||||
@ -632,39 +615,38 @@ impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum Register<'stack> {
|
enum Register {
|
||||||
Empty,
|
Empty,
|
||||||
Value(ConcreteValue),
|
Value(Value),
|
||||||
List(Vec<&'stack ConcreteValue>),
|
Pointer(Pointer),
|
||||||
StackPointer(u8),
|
|
||||||
ConstantPointer(u8),
|
|
||||||
ParentStackPointer(u8),
|
|
||||||
ParentConstantPointer(u8),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'stack> Display for Register<'stack> {
|
impl Display for Register {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => write!(f, "empty"),
|
Self::Empty => write!(f, "empty"),
|
||||||
Self::Value(value) => write!(f, "{}", value),
|
Self::Value(value) => write!(f, "{}", value),
|
||||||
Self::List(values) => {
|
Self::Pointer(pointer) => write!(f, "{}", pointer),
|
||||||
write!(f, "[")?;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (index, value) in values.iter().enumerate() {
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
if index > 0 {
|
pub enum Pointer {
|
||||||
write!(f, ", ")?;
|
Stack(u8),
|
||||||
}
|
Constant(u8),
|
||||||
|
ParentStack(u8),
|
||||||
|
ParentConstant(u8),
|
||||||
|
}
|
||||||
|
|
||||||
write!(f, "{}", value)?;
|
impl Display for Pointer {
|
||||||
}
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
write!(f, "]")
|
Self::Stack(index) => write!(f, "R{}", index),
|
||||||
}
|
Self::Constant(index) => write!(f, "C{}", index),
|
||||||
Self::StackPointer(index) => write!(f, "R{}", index),
|
Self::ParentStack(index) => write!(f, "PR{}", index),
|
||||||
Self::ConstantPointer(index) => write!(f, "C{}", index),
|
Self::ParentConstant(index) => write!(f, "PC{}", index),
|
||||||
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
|
|
||||||
Self::ParentConstantPointer(index) => write!(f, "PC{}", index),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,52 +654,26 @@ impl<'stack> Display for Register<'stack> {
|
|||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
// Stack errors
|
// Stack errors
|
||||||
StackOverflow {
|
StackOverflow { position: Span },
|
||||||
position: Span,
|
StackUnderflow { position: Span },
|
||||||
},
|
|
||||||
StackUnderflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Register errors
|
// Register errors
|
||||||
EmptyRegister {
|
EmptyRegister { index: usize, position: Span },
|
||||||
index: usize,
|
ExpectedValue { found: Register, position: Span },
|
||||||
position: Span,
|
RegisterIndexOutOfBounds { index: usize, position: Span },
|
||||||
},
|
|
||||||
RegisterIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Local errors
|
// Local errors
|
||||||
UndefinedLocal {
|
UndefinedLocal { local_index: u8, position: Span },
|
||||||
local_index: u8,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Execution errors
|
// Execution errors
|
||||||
ExpectedBoolean {
|
ExpectedBoolean { found: Value, position: Span },
|
||||||
found: ConcreteValue,
|
ExpectedFunction { found: Value, position: Span },
|
||||||
position: Span,
|
ExpectedParent { position: Span },
|
||||||
},
|
|
||||||
ExpectedFunction {
|
|
||||||
found: ConcreteValue,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
ExpectedParent {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
Chunk {
|
Chunk { error: ChunkError, position: Span },
|
||||||
error: ChunkError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
NativeFunction(NativeFunctionError),
|
NativeFunction(NativeFunctionError),
|
||||||
Value {
|
Value { error: ValueError, position: Span },
|
||||||
error: ValueError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnotatedError for VmError {
|
impl AnnotatedError for VmError {
|
||||||
@ -732,6 +688,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
Self::ExpectedParent { .. } => "Expected parent",
|
Self::ExpectedParent { .. } => "Expected parent",
|
||||||
|
Self::ExpectedValue { .. } => "Expected value",
|
||||||
Self::NativeFunction(error) => error.description(),
|
Self::NativeFunction(error) => error.description(),
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
@ -763,6 +720,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::ExpectedParent { position } => *position,
|
Self::ExpectedParent { position } => *position,
|
||||||
|
Self::ExpectedValue { position, .. } => *position,
|
||||||
Self::NativeFunction(error) => error.position(),
|
Self::NativeFunction(error) => error.position(),
|
||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackOverflow { position } => *position,
|
Self::StackOverflow { position } => *position,
|
||||||
|
Loading…
Reference in New Issue
Block a user