Refine values to be either abstract or concrete
This commit is contained in:
parent
a16f7795de
commit
f08c7c6f1f
@ -7,7 +7,32 @@
|
|||||||
//! # Disassembly
|
//! # Disassembly
|
||||||
//!
|
//!
|
||||||
//! Chunks can be disassembled into a human-readable format using the `disassemble` method. The
|
//! Chunks can be disassembled into a human-readable format using the `disassemble` method. The
|
||||||
//! output is designed to be displayed in a terminal and is styled for readability.
|
//! output is designed to be displayed in a terminal and can be styled for better readability.
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! ┌──────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
//! │ /var/home/jeff/Repositories/dust/target/debug/dust-shell │
|
||||||
|
//! │ 3 instructions, 1 constants, 0 locals, returns none │
|
||||||
|
//! │ Instructions │
|
||||||
|
//! │ ------------ │
|
||||||
|
//! │ INDEX BYTECODE OPERATION INFO TYPE POSITION │
|
||||||
|
//! │ ----- -------- ------------- ------------------------- --------- ----------- │
|
||||||
|
//! │ 0 00000003 LOAD_CONSTANT R0 = C0 str (11, 26) │
|
||||||
|
//! │ 1 01390117 CALL_NATIVE write_line(R0) (0, 27) │
|
||||||
|
//! │ 2 00000018 RETURN (27, 27) │
|
||||||
|
//! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│
|
||||||
|
//! │ Locals │
|
||||||
|
//! │ ------ │
|
||||||
|
//! │ INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER │
|
||||||
|
//! │ ----- ---------- -------- ------- ------- -------- │
|
||||||
|
//! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│
|
||||||
|
//! │ Constants │
|
||||||
|
//! │ --------- │
|
||||||
|
//! │ INDEX VALUE │
|
||||||
|
//! │ ----- --------------- │
|
||||||
|
//! │ 0 Hello, world! │
|
||||||
|
//! └──────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
@ -18,7 +43,7 @@ use std::{
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{Instruction, Operation, Span, Type, Value};
|
use crate::{ConcreteValue, Instruction, Operation, Span, Type, Value};
|
||||||
|
|
||||||
/// In-memory representation of a Dust program or function.
|
/// In-memory representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
@ -26,6 +51,7 @@ use crate::{Instruction, Operation, Span, Type, Value};
|
|||||||
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
pub is_poisoned: bool,
|
||||||
|
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Span)>,
|
||||||
constants: Vec<Value>,
|
constants: Vec<Value>,
|
||||||
@ -39,6 +65,7 @@ impl Chunk {
|
|||||||
pub fn new(name: Option<String>) -> Self {
|
pub fn new(name: Option<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
is_poisoned: false,
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
constants: Vec::new(),
|
constants: Vec::new(),
|
||||||
locals: Vec::new(),
|
locals: Vec::new(),
|
||||||
@ -55,6 +82,7 @@ impl Chunk {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
is_poisoned: false,
|
||||||
instructions,
|
instructions,
|
||||||
constants,
|
constants,
|
||||||
locals,
|
locals,
|
||||||
@ -214,6 +242,17 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn return_type(&self) -> Option<Type> {
|
pub fn return_type(&self) -> Option<Type> {
|
||||||
|
let returns_value = self
|
||||||
|
.instructions()
|
||||||
|
.last()
|
||||||
|
.map(|(instruction, _)| {
|
||||||
|
debug_assert!(matches!(instruction.operation(), Operation::Return));
|
||||||
|
|
||||||
|
instruction.b_as_boolean()
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if returns_value {
|
||||||
self.instructions.iter().rev().find_map(|(instruction, _)| {
|
self.instructions.iter().rev().find_map(|(instruction, _)| {
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
instruction.yielded_type(self)
|
instruction.yielded_type(self)
|
||||||
@ -221,6 +260,9 @@ impl Chunk {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disassembler(&self) -> ChunkDisassembler {
|
pub fn disassembler(&self) -> ChunkDisassembler {
|
||||||
@ -333,8 +375,12 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"----- -------- ------------- ------------------------- --------- -----------",
|
"----- -------- ------------- ------------------------- --------- -----------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&'static str; 4] =
|
const CONSTANT_HEADER: [&'static str; 4] = [
|
||||||
["Constants", "---------", "INDEX VALUE", "----- -----"];
|
"Constants",
|
||||||
|
"---------",
|
||||||
|
"INDEX VALUE ",
|
||||||
|
"----- ---------------",
|
||||||
|
];
|
||||||
|
|
||||||
const LOCAL_HEADER: [&'static str; 4] = [
|
const LOCAL_HEADER: [&'static str; 4] = [
|
||||||
"Locals",
|
"Locals",
|
||||||
@ -571,12 +617,21 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants.iter().enumerate() {
|
for (index, value) in self.chunk.constants.iter().enumerate() {
|
||||||
let constant_display = format!("{index:<5} {value:<5}");
|
let value_display = {
|
||||||
|
let value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 15 {
|
||||||
|
format!("{value_string:.12}...")
|
||||||
|
} else {
|
||||||
|
value_string
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let constant_display = format!("{index:<5} {value_display:^15}");
|
||||||
|
|
||||||
self.push_details(&constant_display);
|
self.push_details(&constant_display);
|
||||||
|
|
||||||
if let Some(function_disassembly) = match value {
|
if let Some(function_disassembly) = match value {
|
||||||
Value::Function(function) => Some({
|
Value::Concrete(ConcreteValue::Function(function)) => Some({
|
||||||
function
|
function
|
||||||
.chunk()
|
.chunk()
|
||||||
.disassembler()
|
.disassembler()
|
||||||
@ -584,8 +639,7 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
.indent(self.indent + 1)
|
.indent(self.indent + 1)
|
||||||
.disassemble()
|
.disassemble()
|
||||||
}),
|
}),
|
||||||
Value::Primitive(_) => None,
|
_ => None,
|
||||||
Value::Object(_) => None,
|
|
||||||
} {
|
} {
|
||||||
self.output.push_str(&function_disassembly);
|
self.output.push_str(&function_disassembly);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
//! - Bits 0-6: The operation code.
|
//! - Bits 0-6: The operation code.
|
||||||
//! - Bit 7: A flag indicating whether the B argument is a constant.
|
//! - Bit 7: A flag indicating whether the B argument is a constant.
|
||||||
//! - Bit 8: A flag indicating whether the C argument is a constant.
|
//! - Bit 8: A flag indicating whether the C argument is a constant.
|
||||||
//! - Bits 9-16: The A argument.
|
//! - Bits 9-16: The A argument,
|
||||||
//! - Bits 17-24: The B argument.
|
//! - Bits 17-24: The B argument.
|
||||||
//! - Bits 25-32: The C argument.
|
//! - Bits 25-32: The C argument.
|
||||||
//!
|
//!
|
||||||
@ -49,8 +49,8 @@ impl Instruction {
|
|||||||
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
let mut instruction = Instruction(Operation::LoadBoolean as u32);
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_b(if value { 1 } else { 0 });
|
instruction.set_b_to_boolean(value);
|
||||||
instruction.set_c(if skip { 1 } else { 0 });
|
instruction.set_c_to_boolean(skip);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -65,12 +65,11 @@ impl Instruction {
|
|||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_list(to_register: u8, start_register: u8, end_register: u8) -> Instruction {
|
pub fn load_list(to_register: u8, start_register: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LoadList as u32);
|
let mut instruction = Instruction(Operation::LoadList as u32);
|
||||||
|
|
||||||
instruction.set_a(to_register);
|
instruction.set_a(to_register);
|
||||||
instruction.set_b(start_register);
|
instruction.set_b(start_register);
|
||||||
instruction.set_c(end_register);
|
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -183,7 +182,7 @@ impl Instruction {
|
|||||||
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Equal as u32);
|
let mut instruction = Instruction(Operation::Equal as u32);
|
||||||
|
|
||||||
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a_to_boolean(comparison_boolean);
|
||||||
instruction.set_b(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_c(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
@ -193,7 +192,7 @@ impl Instruction {
|
|||||||
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Less as u32);
|
let mut instruction = Instruction(Operation::Less as u32);
|
||||||
|
|
||||||
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a_to_boolean(comparison_boolean);
|
||||||
instruction.set_b(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_c(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
@ -203,7 +202,7 @@ impl Instruction {
|
|||||||
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::LessEqual as u32);
|
let mut instruction = Instruction(Operation::LessEqual as u32);
|
||||||
|
|
||||||
instruction.set_a(if comparison_boolean { 1 } else { 0 });
|
instruction.set_a_to_boolean(comparison_boolean);
|
||||||
instruction.set_b(left_index);
|
instruction.set_b(left_index);
|
||||||
instruction.set_c(right_index);
|
instruction.set_c(right_index);
|
||||||
|
|
||||||
@ -265,7 +264,7 @@ impl Instruction {
|
|||||||
pub fn r#return(should_return_value: bool) -> Instruction {
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
let mut instruction = Instruction(Operation::Return as u32);
|
let mut instruction = Instruction(Operation::Return as u32);
|
||||||
|
|
||||||
instruction.set_b(if should_return_value { 1 } else { 0 });
|
instruction.set_b_to_boolean(should_return_value);
|
||||||
|
|
||||||
instruction
|
instruction
|
||||||
}
|
}
|
||||||
@ -398,7 +397,7 @@ impl Instruction {
|
|||||||
chunk.get_register_type(self.b())
|
chunk.get_register_type(self.b())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Equal | Less | LessEqual | Test | Not | LoadBoolean => Some(Type::Boolean),
|
LoadBoolean | Not => Some(Type::Boolean),
|
||||||
Negate => {
|
Negate => {
|
||||||
if self.b_is_constant() {
|
if self.b_is_constant() {
|
||||||
chunk.get_constant_type(self.b())
|
chunk.get_constant_type(self.b())
|
||||||
@ -725,12 +724,11 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_list() {
|
fn load_list() {
|
||||||
let instruction = Instruction::load_list(0, 1, 2);
|
let instruction = Instruction::load_list(0, 1);
|
||||||
|
|
||||||
assert_eq!(instruction.operation(), Operation::LoadList);
|
assert_eq!(instruction.operation(), Operation::LoadList);
|
||||||
assert_eq!(instruction.a(), 0);
|
assert_eq!(instruction.a(), 0);
|
||||||
assert_eq!(instruction.b(), 1);
|
assert_eq!(instruction.b(), 1);
|
||||||
assert_eq!(instruction.c(), 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -23,7 +23,7 @@ pub use crate::operation::Operation;
|
|||||||
pub use crate::parser::{parse, ParseError};
|
pub use crate::parser::{parse, ParseError};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict};
|
||||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
pub use crate::value::{Function, Primitive, Value, ValueError};
|
pub use crate::value::{ConcreteValue, Function, Value, ValueError};
|
||||||
pub use crate::vm::{run, Vm, VmError};
|
pub use crate::vm::{run, Vm, VmError};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -10,7 +10,9 @@ use std::{
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{AnnotatedError, FunctionType, Instruction, Primitive, Span, Type, Value, Vm, VmError};
|
use crate::{
|
||||||
|
AnnotatedError, ConcreteValue, 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)),*) => {
|
($(($name:ident, $byte:literal, $str:expr, $type:expr)),*) => {
|
||||||
@ -259,7 +261,7 @@ impl NativeFunction {
|
|||||||
string.push_str(&argument.to_string());
|
string.push_str(&argument.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Value::Primitive(Primitive::String(string)))
|
Some(Value::Concrete(ConcreteValue::String(string)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
@ -275,7 +277,7 @@ impl NativeFunction {
|
|||||||
|
|
||||||
buffer = buffer.trim_end_matches('\n').to_string();
|
buffer = buffer.trim_end_matches('\n').to_string();
|
||||||
|
|
||||||
Some(Value::Primitive(Primitive::String(buffer)))
|
Some(Value::Concrete(ConcreteValue::String(buffer)))
|
||||||
}
|
}
|
||||||
NativeFunction::Write => {
|
NativeFunction::Write => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
|
@ -810,41 +810,10 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> {
|
||||||
let start_position = self.current_position;
|
let start_position = self.current_position;
|
||||||
|
let identifier = if let Token::Identifier(text) = self.current_token {
|
||||||
|
self.advance()?;
|
||||||
|
|
||||||
let local_index = if let Token::Identifier(text) = self.current_token {
|
text
|
||||||
if let Ok(local_index) = self.get_local_index(text) {
|
|
||||||
local_index
|
|
||||||
} else if let Some(name) = self.chunk.name() {
|
|
||||||
if name.as_str() == text {
|
|
||||||
let register = self.next_register();
|
|
||||||
let scope = self.chunk.current_scope();
|
|
||||||
|
|
||||||
self.emit_instruction(Instruction::load_self(register), start_position);
|
|
||||||
self.declare_local(text, None, false, scope, register);
|
|
||||||
|
|
||||||
self.current_is_expression = true;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
return if NativeFunction::from_str(text).is_some() {
|
|
||||||
self.parse_native_call(allowed)
|
|
||||||
} else {
|
|
||||||
Err(ParseError::UndeclaredVariable {
|
|
||||||
identifier: text.to_string(),
|
|
||||||
position: start_position,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return if NativeFunction::from_str(text).is_some() {
|
|
||||||
self.parse_native_call(allowed)
|
|
||||||
} else {
|
|
||||||
Err(ParseError::UndeclaredVariable {
|
|
||||||
identifier: text.to_string(),
|
|
||||||
position: start_position,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Err(ParseError::ExpectedToken {
|
return Err(ParseError::ExpectedToken {
|
||||||
expected: TokenKind::Identifier,
|
expected: TokenKind::Identifier,
|
||||||
@ -852,8 +821,26 @@ impl<'src> Parser<'src> {
|
|||||||
position: start_position,
|
position: start_position,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
let local_index = if let Ok(local_index) = self.get_local_index(identifier) {
|
||||||
|
local_index
|
||||||
|
} else if let Some(native_function) = NativeFunction::from_str(identifier) {
|
||||||
|
return self.parse_native_call(native_function);
|
||||||
|
} else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) {
|
||||||
|
let register = self.next_register();
|
||||||
|
let scope = self.chunk.current_scope();
|
||||||
|
|
||||||
self.advance()?;
|
self.emit_instruction(Instruction::load_self(register), start_position);
|
||||||
|
self.declare_local(identifier, None, false, scope, register);
|
||||||
|
|
||||||
|
self.current_is_expression = true;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::UndeclaredVariable {
|
||||||
|
identifier: identifier.to_string(),
|
||||||
|
position: start_position,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
let (is_mutable, local_scope) = {
|
let (is_mutable, local_scope) = {
|
||||||
let local = self.get_local(local_index)?;
|
let local = self.get_local(local_index)?;
|
||||||
@ -988,11 +975,10 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let end_register = to_register.saturating_sub(1);
|
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::load_list(to_register, start_register, end_register),
|
Instruction::load_list(to_register, start_register),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1187,21 +1173,10 @@ impl<'src> Parser<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_native_call(&mut self, _: Allowed) -> Result<(), ParseError> {
|
fn parse_native_call(&mut self, function: NativeFunction) -> Result<(), ParseError> {
|
||||||
let native_function = if let Token::Identifier(text) = self.current_token {
|
let start = self.previous_position.0;
|
||||||
NativeFunction::from_str(text).unwrap()
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::ExpectedToken {
|
|
||||||
expected: TokenKind::Identifier,
|
|
||||||
found: self.current_token.to_owned(),
|
|
||||||
position: self.current_position,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let start = self.current_position.0;
|
|
||||||
let start_register = self.next_register();
|
let start_register = self.next_register();
|
||||||
|
|
||||||
self.advance()?;
|
|
||||||
|
|
||||||
self.expect(Token::LeftParenthesis)?;
|
self.expect(Token::LeftParenthesis)?;
|
||||||
|
|
||||||
while !self.allow(Token::RightParenthesis)? {
|
while !self.allow(Token::RightParenthesis)? {
|
||||||
@ -1224,10 +1199,10 @@ impl<'src> Parser<'src> {
|
|||||||
let end = self.previous_position.1;
|
let end = self.previous_position.1;
|
||||||
let to_register = self.next_register();
|
let to_register = self.next_register();
|
||||||
let argument_count = to_register - start_register;
|
let argument_count = to_register - start_register;
|
||||||
self.current_is_expression = native_function.r#type().return_type.is_some();
|
self.current_is_expression = function.r#type().return_type.is_some();
|
||||||
|
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::call_native(to_register, native_function, argument_count),
|
Instruction::call_native(to_register, function, argument_count),
|
||||||
Span(start, end),
|
Span(start, end),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -26,53 +26,53 @@ use std::{
|
|||||||
ops::{Range, RangeInclusive},
|
ops::{Range, RangeInclusive},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{
|
use serde::{Deserialize, Serialize};
|
||||||
de::{self, Visitor},
|
|
||||||
Deserialize, Deserializer, Serialize, Serializer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError};
|
use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError};
|
||||||
|
|
||||||
/// Dust value representation
|
/// Dust value representation
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation][self] for more.
|
/// See the [module-level documentation][self] for more.
|
||||||
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Primitive(Primitive),
|
Concrete(ConcreteValue),
|
||||||
Function(Function),
|
Abstract(AbstractValue),
|
||||||
Object(Object),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn boolean(value: bool) -> Self {
|
pub fn boolean(value: bool) -> Self {
|
||||||
Value::Primitive(Primitive::Boolean(value))
|
Value::Concrete(ConcreteValue::Boolean(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn byte(value: u8) -> Self {
|
pub fn byte(value: u8) -> Self {
|
||||||
Value::Primitive(Primitive::Byte(value))
|
Value::Concrete(ConcreteValue::Byte(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn character(value: char) -> Self {
|
pub fn character(value: char) -> Self {
|
||||||
Value::Primitive(Primitive::Character(value))
|
Value::Concrete(ConcreteValue::Character(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn float(value: f64) -> Self {
|
pub fn float(value: f64) -> Self {
|
||||||
Value::Primitive(Primitive::Float(value))
|
Value::Concrete(ConcreteValue::Float(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn function(body: Chunk, r#type: FunctionType) -> Self {
|
pub fn function(body: Chunk, r#type: FunctionType) -> Self {
|
||||||
Value::Function(Function {
|
Value::Concrete(ConcreteValue::Function(Function {
|
||||||
chunk: body,
|
chunk: body,
|
||||||
r#type: Type::Function(r#type),
|
r#type: Type::Function(r#type),
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
pub fn integer<T: Into<i64>>(into_i64: T) -> Self {
|
||||||
Value::Primitive(Primitive::Integer(into_i64.into()))
|
Value::Concrete(ConcreteValue::Integer(into_i64.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(start: u8, end: u8, item_type: Type) -> Self {
|
pub fn list<T: Into<Vec<Value>>>(items: T) -> Self {
|
||||||
Value::Object(Object::List {
|
Value::Concrete(ConcreteValue::List(items.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abstract_list(start: u8, end: u8, item_type: Type) -> Self {
|
||||||
|
Value::Abstract(AbstractValue::List {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
item_type,
|
item_type,
|
||||||
@ -80,11 +80,11 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn string<T: ToString>(to_string: T) -> Self {
|
pub fn string<T: ToString>(to_string: T) -> Self {
|
||||||
Value::Primitive(Primitive::String(to_string.to_string()))
|
Value::Concrete(ConcreteValue::String(to_string.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&String> {
|
pub fn as_string(&self) -> Option<&String> {
|
||||||
if let Value::Primitive(Primitive::String(string)) = self {
|
if let Value::Concrete(ConcreteValue::String(string)) = self {
|
||||||
Some(string)
|
Some(string)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -92,14 +92,13 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_function(&self) -> bool {
|
pub fn is_function(&self) -> bool {
|
||||||
matches!(self, Value::Function(_))
|
matches!(self, Value::Concrete(ConcreteValue::Function(_)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
pub fn r#type(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(data) => data.r#type(),
|
Value::Concrete(data) => data.r#type(),
|
||||||
Value::Function(function) => function.r#type().clone(),
|
Value::Abstract(AbstractValue::List {
|
||||||
Value::Object(Object::List {
|
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
item_type,
|
item_type,
|
||||||
@ -116,67 +115,67 @@ impl Value {
|
|||||||
|
|
||||||
pub fn add(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn add(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
let sum = left
|
let sum = left
|
||||||
.add(right)
|
.add(right)
|
||||||
.ok_or_else(|| ValueError::CannotAdd(self.clone(), other.clone()))?;
|
.ok_or_else(|| ValueError::CannotAdd(self.clone(), other.clone()))?;
|
||||||
|
|
||||||
Ok(Value::Primitive(sum))
|
Ok(Value::Concrete(sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
let difference = left
|
let difference = left
|
||||||
.subtract(right)
|
.subtract(right)
|
||||||
.ok_or_else(|| ValueError::CannotSubtract(self.clone(), other.clone()))?;
|
.ok_or_else(|| ValueError::CannotSubtract(self.clone(), other.clone()))?;
|
||||||
|
|
||||||
Ok(Value::Primitive(difference))
|
Ok(Value::Concrete(difference))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
let product = left
|
let product = left
|
||||||
.multiply(right)
|
.multiply(right)
|
||||||
.ok_or_else(|| ValueError::CannotMultiply(self.clone(), other.clone()))?;
|
.ok_or_else(|| ValueError::CannotMultiply(self.clone(), other.clone()))?;
|
||||||
|
|
||||||
Ok(Value::Primitive(product))
|
Ok(Value::Concrete(product))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotDivide(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotDivide(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
let quotient = left
|
let quotient = left
|
||||||
.divide(right)
|
.divide(right)
|
||||||
.ok_or_else(|| ValueError::CannotDivide(self.clone(), other.clone()))?;
|
.ok_or_else(|| ValueError::CannotDivide(self.clone(), other.clone()))?;
|
||||||
|
|
||||||
Ok(Value::Primitive(quotient))
|
Ok(Value::Concrete(quotient))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotModulo(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotModulo(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
let remainder = left
|
let remainder = left
|
||||||
.modulo(right)
|
.modulo(right)
|
||||||
.ok_or_else(|| ValueError::CannotModulo(self.clone(), other.clone()))?;
|
.ok_or_else(|| ValueError::CannotModulo(self.clone(), other.clone()))?;
|
||||||
|
|
||||||
Ok(Value::Primitive(remainder))
|
Ok(Value::Concrete(remainder))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,7 +184,7 @@ impl Value {
|
|||||||
|
|
||||||
pub fn less_than_or_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn less_than_or_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -194,7 +193,7 @@ impl Value {
|
|||||||
|
|
||||||
pub fn equal(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,7 +202,7 @@ impl Value {
|
|||||||
|
|
||||||
pub fn not_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn not_equal(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotCompare(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,42 +211,53 @@ impl Value {
|
|||||||
|
|
||||||
pub fn negate(&self) -> Result<Value, ValueError> {
|
pub fn negate(&self) -> Result<Value, ValueError> {
|
||||||
let data = match self {
|
let data = match self {
|
||||||
Value::Primitive(data) => data,
|
Value::Concrete(data) => data,
|
||||||
_ => return Err(ValueError::CannotNot(self.clone())),
|
_ => return Err(ValueError::CannotNot(self.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
data.negate()
|
data.negate()
|
||||||
.ok_or_else(|| ValueError::CannotNot(self.clone()))
|
.ok_or_else(|| ValueError::CannotNot(self.clone()))
|
||||||
.map(Value::Primitive)
|
.map(Value::Concrete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(&self) -> Result<Value, ValueError> {
|
pub fn not(&self) -> Result<Value, ValueError> {
|
||||||
let data = match self {
|
let data = match self {
|
||||||
Value::Primitive(data) => data,
|
Value::Concrete(data) => data,
|
||||||
_ => return Err(ValueError::CannotNot(self.clone())),
|
_ => return Err(ValueError::CannotNot(self.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
data.not()
|
data.not()
|
||||||
.ok_or_else(|| ValueError::CannotNot(self.clone()))
|
.ok_or_else(|| ValueError::CannotNot(self.clone()))
|
||||||
.map(Value::Primitive)
|
.map(Value::Concrete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn and(&self, other: &Value) -> Result<Value, ValueError> {
|
pub fn and(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
let (left, right) = match (self, other) {
|
let (left, right) = match (self, other) {
|
||||||
(Value::Primitive(left), Value::Primitive(right)) => (left, right),
|
(Value::Concrete(left), Value::Concrete(right)) => (left, right),
|
||||||
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
_ => return Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
||||||
};
|
};
|
||||||
|
|
||||||
left.and(right)
|
left.and(right)
|
||||||
.ok_or_else(|| ValueError::CannotAnd(self.clone(), other.clone()))
|
.ok_or_else(|| ValueError::CannotAnd(self.clone(), other.clone()))
|
||||||
.map(Value::Primitive)
|
.map(Value::Concrete)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display(&self, vm: &Vm, position: Span) -> Result<String, ValueError> {
|
pub fn to_concrete(self, vm: &mut Vm, position: Span) -> Result<Value, VmError> {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(primitive) => Ok(primitive.to_string()),
|
Value::Concrete(_) => Ok(self),
|
||||||
Value::Function(function) => Ok(function.to_string()),
|
Value::Abstract(AbstractValue::List { start, end, .. }) => {
|
||||||
Value::Object(object) => object.display(vm, position),
|
let mut items = Vec::new();
|
||||||
|
|
||||||
|
for register_index in start..end {
|
||||||
|
let get_value = vm.empty_register(register_index, position);
|
||||||
|
|
||||||
|
if let Ok(value) = get_value {
|
||||||
|
items.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Concrete(ConcreteValue::List(items)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,9 +315,8 @@ impl Clone for Value {
|
|||||||
log::trace!("Cloning value {self}");
|
log::trace!("Cloning value {self}");
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(data) => Value::Primitive(data.clone()),
|
Value::Abstract(object) => Value::Abstract(object.clone()),
|
||||||
Value::Function(function) => Value::Function(function.clone()),
|
Value::Concrete(concrete) => Value::Concrete(concrete.clone()),
|
||||||
Value::Object(object) => Value::Object(object.clone()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,84 +324,43 @@ impl Clone for Value {
|
|||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::Primitive(primitive) => write!(f, "{primitive}"),
|
Value::Abstract(object) => write!(f, "{object}"),
|
||||||
Value::Function(function) => write!(f, "{function}"),
|
Value::Concrete(concrete) => write!(f, "{concrete}"),
|
||||||
Value::Object(object) => write!(f, "{object}"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Value {
|
|
||||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
match self {
|
|
||||||
Value::Primitive(data) => data.serialize(serializer),
|
|
||||||
Value::Function(function) => function.serialize(serializer),
|
|
||||||
Value::Object(object) => object.serialize(serializer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Value {
|
|
||||||
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
struct ValueVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for ValueVisitor {
|
|
||||||
type Value = Value;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str("a value")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_bool<E: de::Error>(self, value: bool) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::Boolean(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::Integer(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::Integer(value as i64)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::Float(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::String(value.to_string())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
|
|
||||||
Ok(Value::Primitive(Primitive::String(value)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_any(ValueVisitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum Primitive {
|
pub enum ConcreteValue {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Byte(u8),
|
Byte(u8),
|
||||||
Character(char),
|
Character(char),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
|
Function(Function),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
|
List(Vec<Value>),
|
||||||
Range(RangeValue),
|
Range(RangeValue),
|
||||||
String(String),
|
String(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Primitive {
|
impl ConcreteValue {
|
||||||
pub fn r#type(&self) -> Type {
|
pub fn r#type(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Boolean(_) => Type::Boolean,
|
ConcreteValue::Boolean(_) => Type::Boolean,
|
||||||
Primitive::Byte(_) => Type::Byte,
|
ConcreteValue::Byte(_) => Type::Byte,
|
||||||
Primitive::Character(_) => Type::Character,
|
ConcreteValue::Character(_) => Type::Character,
|
||||||
Primitive::Float(_) => Type::Float,
|
ConcreteValue::Float(_) => Type::Float,
|
||||||
Primitive::Integer(_) => Type::Integer,
|
ConcreteValue::Function(Function { r#type, .. }) => r#type.clone(),
|
||||||
Primitive::Range(range) => range.r#type(),
|
ConcreteValue::Integer(_) => Type::Integer,
|
||||||
Primitive::String(string) => Type::String {
|
ConcreteValue::List(list) => Type::List {
|
||||||
|
item_type: list
|
||||||
|
.first()
|
||||||
|
.map(|value| Box::new(value.r#type()))
|
||||||
|
.unwrap_or_else(|| Box::new(Type::Any)),
|
||||||
|
length: list.len(),
|
||||||
|
},
|
||||||
|
ConcreteValue::Range(range) => range.r#type(),
|
||||||
|
ConcreteValue::String(string) => Type::String {
|
||||||
length: Some(string.len()),
|
length: Some(string.len()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -401,194 +369,194 @@ impl Primitive {
|
|||||||
pub fn is_rangeable(&self) -> bool {
|
pub fn is_rangeable(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
Primitive::Integer(_)
|
ConcreteValue::Integer(_)
|
||||||
| Primitive::Float(_)
|
| ConcreteValue::Float(_)
|
||||||
| Primitive::Character(_)
|
| ConcreteValue::Character(_)
|
||||||
| Primitive::Byte(_)
|
| ConcreteValue::Byte(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn add(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Byte(left), Primitive::Byte(right)) => {
|
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => {
|
||||||
Some(Primitive::Byte(left.saturating_add(*right)))
|
Some(ConcreteValue::Byte(left.saturating_add(*right)))
|
||||||
}
|
}
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Float(left + right))
|
Some(ConcreteValue::Float(left + right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Integer(left.saturating_add(*right)))
|
Some(ConcreteValue::Integer(left.saturating_add(*right)))
|
||||||
}
|
}
|
||||||
(Primitive::String(left), Primitive::String(right)) => {
|
(ConcreteValue::String(left), ConcreteValue::String(right)) => {
|
||||||
Some(Primitive::String(format!("{}{}", left, right)))
|
Some(ConcreteValue::String(format!("{}{}", left, right)))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn subtract(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Byte(left), Primitive::Byte(right)) => {
|
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => {
|
||||||
Some(Primitive::Byte(left.saturating_sub(*right)))
|
Some(ConcreteValue::Byte(left.saturating_sub(*right)))
|
||||||
}
|
}
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Float(left - right))
|
Some(ConcreteValue::Float(left - right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Integer(left.saturating_sub(*right)))
|
Some(ConcreteValue::Integer(left.saturating_sub(*right)))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn multiply(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Byte(left), Primitive::Byte(right)) => {
|
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => {
|
||||||
Some(Primitive::Byte(left.saturating_mul(*right)))
|
Some(ConcreteValue::Byte(left.saturating_mul(*right)))
|
||||||
}
|
}
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Float(left * right))
|
Some(ConcreteValue::Float(left * right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Integer(left.saturating_mul(*right)))
|
Some(ConcreteValue::Integer(left.saturating_mul(*right)))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn divide(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn divide(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Byte(left), Primitive::Byte(right)) => {
|
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => {
|
||||||
Some(Primitive::Byte(left.saturating_div(*right)))
|
Some(ConcreteValue::Byte(left.saturating_div(*right)))
|
||||||
}
|
}
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Float(left / right))
|
Some(ConcreteValue::Float(left / right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Integer(left.saturating_div(*right)))
|
Some(ConcreteValue::Integer(left.saturating_div(*right)))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn modulo(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Float(left % right))
|
Some(ConcreteValue::Float(left % right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Integer(left % right))
|
Some(ConcreteValue::Integer(left % right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_than(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn less_than(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Boolean(left < right))
|
Some(ConcreteValue::Boolean(left < right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Boolean(left < right))
|
Some(ConcreteValue::Boolean(left < right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn less_than_or_equal(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn less_than_or_equal(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Boolean(left <= right))
|
Some(ConcreteValue::Boolean(left <= right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Boolean(left <= right))
|
Some(ConcreteValue::Boolean(left <= right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn greater_than(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn greater_than(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Boolean(left > right))
|
Some(ConcreteValue::Boolean(left > right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Boolean(left > right))
|
Some(ConcreteValue::Boolean(left > right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn greater_than_or_equal(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn greater_than_or_equal(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
Some(Primitive::Boolean(left >= right))
|
Some(ConcreteValue::Boolean(left >= right))
|
||||||
}
|
}
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => {
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => {
|
||||||
Some(Primitive::Boolean(left >= right))
|
Some(ConcreteValue::Boolean(left >= right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn and(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn and(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Boolean(left), Primitive::Boolean(right)) => {
|
(ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => {
|
||||||
Some(Primitive::Boolean(*left && *right))
|
Some(ConcreteValue::Boolean(*left && *right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn or(&self, other: &Primitive) -> Option<Primitive> {
|
pub fn or(&self, other: &ConcreteValue) -> Option<ConcreteValue> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Boolean(left), Primitive::Boolean(right)) => {
|
(ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => {
|
||||||
Some(Primitive::Boolean(*left || *right))
|
Some(ConcreteValue::Boolean(*left || *right))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_even(&self) -> Option<Primitive> {
|
pub fn is_even(&self) -> Option<ConcreteValue> {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Integer(integer) => Some(Primitive::Boolean(integer % 2 == 0)),
|
ConcreteValue::Integer(integer) => Some(ConcreteValue::Boolean(integer % 2 == 0)),
|
||||||
Primitive::Float(float) => Some(Primitive::Boolean(float % 2.0 == 0.0)),
|
ConcreteValue::Float(float) => Some(ConcreteValue::Boolean(float % 2.0 == 0.0)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_odd(&self) -> Option<Primitive> {
|
pub fn is_odd(&self) -> Option<ConcreteValue> {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Integer(integer) => Some(Primitive::Boolean(integer % 2 != 0)),
|
ConcreteValue::Integer(integer) => Some(ConcreteValue::Boolean(integer % 2 != 0)),
|
||||||
Primitive::Float(float) => Some(Primitive::Boolean(float % 2.0 != 0.0)),
|
ConcreteValue::Float(float) => Some(ConcreteValue::Boolean(float % 2.0 != 0.0)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn negate(&self) -> Option<Primitive> {
|
pub fn negate(&self) -> Option<ConcreteValue> {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Byte(value) => Some(Primitive::Byte(!value)),
|
ConcreteValue::Byte(value) => Some(ConcreteValue::Byte(!value)),
|
||||||
Primitive::Float(value) => Some(Primitive::Float(-value)),
|
ConcreteValue::Float(value) => Some(ConcreteValue::Float(-value)),
|
||||||
Primitive::Integer(value) => Some(Primitive::Integer(-value)),
|
ConcreteValue::Integer(value) => Some(ConcreteValue::Integer(-value)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not(&self) -> Option<Primitive> {
|
pub fn not(&self) -> Option<ConcreteValue> {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Boolean(value) => Some(Primitive::Boolean(!value)),
|
ConcreteValue::Boolean(value) => Some(ConcreteValue::Boolean(!value)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Primitive {
|
impl Display for ConcreteValue {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Primitive::Boolean(boolean) => write!(f, "{boolean}"),
|
ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
Primitive::Byte(byte) => write!(f, "0x{byte:02x}"),
|
ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"),
|
||||||
Primitive::Character(character) => write!(f, "{character}"),
|
ConcreteValue::Character(character) => write!(f, "{character}"),
|
||||||
Primitive::Float(float) => {
|
ConcreteValue::Float(float) => {
|
||||||
write!(f, "{float}")?;
|
write!(f, "{float}")?;
|
||||||
|
|
||||||
if float.fract() == 0.0 {
|
if float.fract() == 0.0 {
|
||||||
@ -597,33 +565,49 @@ impl Display for Primitive {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Primitive::Integer(integer) => write!(f, "{integer}"),
|
ConcreteValue::Function(Function { r#type, .. }) => {
|
||||||
Primitive::Range(range_value) => {
|
write!(f, "{}", r#type)
|
||||||
|
}
|
||||||
|
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
||||||
|
ConcreteValue::List(items) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
for (index, item) in items.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{item}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
|
ConcreteValue::Range(range_value) => {
|
||||||
write!(f, "{range_value}")
|
write!(f, "{range_value}")
|
||||||
}
|
}
|
||||||
Primitive::String(string) => write!(f, "{string}"),
|
ConcreteValue::String(string) => write!(f, "{string}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Primitive {}
|
impl Eq for ConcreteValue {}
|
||||||
|
|
||||||
impl PartialOrd for Primitive {
|
impl PartialOrd for ConcreteValue {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Primitive {
|
impl Ord for ConcreteValue {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Primitive::Boolean(left), Primitive::Boolean(right)) => left.cmp(right),
|
(ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => left.cmp(right),
|
||||||
(Primitive::Boolean(_), _) => Ordering::Greater,
|
(ConcreteValue::Boolean(_), _) => Ordering::Greater,
|
||||||
(Primitive::Byte(left), Primitive::Byte(right)) => left.cmp(right),
|
(ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => left.cmp(right),
|
||||||
(Primitive::Byte(_), _) => Ordering::Greater,
|
(ConcreteValue::Byte(_), _) => Ordering::Greater,
|
||||||
(Primitive::Character(left), Primitive::Character(right)) => left.cmp(right),
|
(ConcreteValue::Character(left), ConcreteValue::Character(right)) => left.cmp(right),
|
||||||
(Primitive::Character(_), _) => Ordering::Greater,
|
(ConcreteValue::Character(_), _) => Ordering::Greater,
|
||||||
(Primitive::Float(left), Primitive::Float(right)) => {
|
(ConcreteValue::Float(left), ConcreteValue::Float(right)) => {
|
||||||
if left.is_nan() && right.is_nan() {
|
if left.is_nan() && right.is_nan() {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else if left.is_nan() {
|
} else if left.is_nan() {
|
||||||
@ -634,13 +618,17 @@ impl Ord for Primitive {
|
|||||||
left.partial_cmp(right).unwrap()
|
left.partial_cmp(right).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Primitive::Float(_), _) => Ordering::Greater,
|
(ConcreteValue::Float(_), _) => Ordering::Greater,
|
||||||
(Primitive::Integer(left), Primitive::Integer(right)) => left.cmp(right),
|
(ConcreteValue::Function(left), ConcreteValue::Function(right)) => left.cmp(right),
|
||||||
(Primitive::Integer(_), _) => Ordering::Greater,
|
(ConcreteValue::Function(_), _) => Ordering::Greater,
|
||||||
(Primitive::Range(left), Primitive::Range(right)) => left.cmp(right),
|
(ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => left.cmp(right),
|
||||||
(Primitive::Range(_), _) => Ordering::Greater,
|
(ConcreteValue::Integer(_), _) => Ordering::Greater,
|
||||||
(Primitive::String(left), Primitive::String(right)) => left.cmp(right),
|
(ConcreteValue::List(left), ConcreteValue::List(right)) => left.cmp(right),
|
||||||
(Primitive::String(_), _) => Ordering::Greater,
|
(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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -885,48 +873,16 @@ impl Ord for RangeValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Value representation that can be resolved to a concrete value by the VM.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum Object {
|
pub enum AbstractValue {
|
||||||
List { start: u8, end: u8, item_type: Type },
|
List { start: u8, end: u8, item_type: Type },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object {
|
impl Display for AbstractValue {
|
||||||
fn display(&self, vm: &Vm, position: Span) -> Result<String, ValueError> {
|
|
||||||
match self {
|
|
||||||
Object::List { start, end, .. } => {
|
|
||||||
let mut display = String::from("[");
|
|
||||||
let (start, end) = (*start, *end);
|
|
||||||
|
|
||||||
for register in start..=end {
|
|
||||||
if register > start {
|
|
||||||
display.push_str(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let value_display = match vm.get_register(register, position) {
|
|
||||||
Ok(value) => value.display(vm, position)?,
|
|
||||||
Err(error) => {
|
|
||||||
return Err(ValueError::CannotDisplay {
|
|
||||||
value: Value::Object(self.clone()),
|
|
||||||
vm_error: Box::new(error),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
display.push_str(&value_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.push(']');
|
|
||||||
|
|
||||||
Ok(display)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Object {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Object::List { start, end, .. } => {
|
AbstractValue::List { start, end, .. } => {
|
||||||
write!(f, "List [R{}..=R{}]", start, end)
|
write!(f, "List [R{}..=R{}]", start, end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -938,10 +894,6 @@ pub enum ValueError {
|
|||||||
CannotAdd(Value, Value),
|
CannotAdd(Value, Value),
|
||||||
CannotAnd(Value, Value),
|
CannotAnd(Value, Value),
|
||||||
CannotCompare(Value, Value),
|
CannotCompare(Value, Value),
|
||||||
CannotDisplay {
|
|
||||||
value: Value,
|
|
||||||
vm_error: Box<VmError>,
|
|
||||||
},
|
|
||||||
CannotDivide(Value, Value),
|
CannotDivide(Value, Value),
|
||||||
CannotModulo(Value, Value),
|
CannotModulo(Value, Value),
|
||||||
CannotMultiply(Value, Value),
|
CannotMultiply(Value, Value),
|
||||||
@ -953,85 +905,36 @@ pub enum ValueError {
|
|||||||
|
|
||||||
impl Display for ValueError {
|
impl Display for ValueError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
let get_value_display = |value: &Value| -> String {
|
|
||||||
match value {
|
|
||||||
Value::Primitive(primitive) => primitive.to_string(),
|
|
||||||
Value::Function(function) => function.to_string(),
|
|
||||||
Value::Object(_) => "Object".to_string(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
ValueError::CannotAdd(left, right) => {
|
ValueError::CannotAdd(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot add {left} and {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot add {} and {}", left_display, right_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotAnd(left, right) => {
|
ValueError::CannotAnd(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot use logical AND operation on {left} and {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Cannot use logical and operation on {} and {}",
|
|
||||||
left_display, right_display
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotCompare(left, right) => {
|
ValueError::CannotCompare(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot compare {left} and {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot compare {} and {}", left_display, right_display)
|
|
||||||
}
|
|
||||||
ValueError::CannotDisplay { value, vm_error } => {
|
|
||||||
let value_display = get_value_display(value);
|
|
||||||
|
|
||||||
write!(f, "Cannot display {}: {:?}", value_display, vm_error)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotDivide(left, right) => {
|
ValueError::CannotDivide(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot divide {left} by {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot divide {} by {}", left_display, right_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotModulo(left, right) => {
|
ValueError::CannotModulo(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot use modulo operation on {left} and {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot modulo {} by {}", left_display, right_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotMultiply(left, right) => {
|
ValueError::CannotMultiply(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot multiply {left} by {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot multiply {} by {}", left_display, right_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotNegate(value) => {
|
ValueError::CannotNegate(value) => {
|
||||||
let value_display = get_value_display(value);
|
write!(f, "Cannot negate {value}")
|
||||||
|
|
||||||
write!(f, "Cannot negate {}", value_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotNot(value) => {
|
ValueError::CannotNot(value) => {
|
||||||
let value_display = get_value_display(value);
|
write!(f, "Cannot use logical NOT operation on {value}")
|
||||||
|
|
||||||
write!(f, "Cannot use logical not operation on {}", value_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotSubtract(left, right) => {
|
ValueError::CannotSubtract(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot subtract {right} from {left}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(f, "Cannot subtract {} from {}", right_display, left_display)
|
|
||||||
}
|
}
|
||||||
ValueError::CannotOr(left, right) => {
|
ValueError::CannotOr(left, right) => {
|
||||||
let left_display = get_value_display(left);
|
write!(f, "Cannot use logical OR operation on {left} and {right}")
|
||||||
let right_display = get_value_display(right);
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Cannot use logical or operation on {} and {}",
|
|
||||||
left_display, right_display
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
use std::{cmp::Ordering, mem::replace};
|
use std::{cmp::Ordering, mem::replace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local,
|
parse, value::ConcreteValue, AnnotatedError, Chunk, DustError, FunctionType, Instruction,
|
||||||
NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
|
||||||
@ -121,23 +121,17 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
let to_register = instruction.a();
|
let to_register = instruction.a();
|
||||||
let first_register = instruction.b();
|
let start_register = instruction.b();
|
||||||
let last_register = instruction.c();
|
let item_type = (start_register..to_register)
|
||||||
let is_empty = to_register == first_register && first_register == last_register;
|
.find_map(|register_index| {
|
||||||
let item_type = if is_empty {
|
if let Ok(value) = self.get_register(register_index, position) {
|
||||||
Type::Any
|
Some(value.r#type())
|
||||||
} else {
|
} else {
|
||||||
let register_range = (first_register as usize)..=(last_register as usize);
|
None
|
||||||
|
}
|
||||||
self.stack[register_range]
|
|
||||||
.iter()
|
|
||||||
.find_map(|register| match register {
|
|
||||||
Register::Value(value) => Some(value.r#type()),
|
|
||||||
_ => None,
|
|
||||||
})
|
})
|
||||||
.unwrap_or(Type::Any)
|
.unwrap_or(Type::Any);
|
||||||
};
|
let value = Value::abstract_list(start_register, to_register, item_type);
|
||||||
let value = Value::list(first_register, last_register, item_type);
|
|
||||||
|
|
||||||
self.set_register(to_register, value, position)?;
|
self.set_register(to_register, value, position)?;
|
||||||
}
|
}
|
||||||
@ -170,17 +164,6 @@ impl Vm {
|
|||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let register = instruction.a();
|
let register = instruction.a();
|
||||||
let local_index = instruction.b();
|
let local_index = instruction.b();
|
||||||
let local = self.get_local(local_index, position)?;
|
|
||||||
|
|
||||||
if !local.is_mutable {
|
|
||||||
return Err(VmError::CannotMutateImmutableLocal {
|
|
||||||
identifier: self
|
|
||||||
.chunk
|
|
||||||
.get_identifier(local.identifier_index)
|
|
||||||
.unwrap_or_else(|| "unknown".to_string()),
|
|
||||||
position,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.define_local(local_index, register, position)?;
|
self.define_local(local_index, register, position)?;
|
||||||
}
|
}
|
||||||
@ -228,7 +211,7 @@ impl Vm {
|
|||||||
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.get_register(register, position)?;
|
let value = self.get_register(register, position)?;
|
||||||
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value {
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
||||||
*boolean
|
*boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
@ -253,7 +236,7 @@ impl Vm {
|
|||||||
.equal(right)
|
.equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let boolean =
|
let boolean =
|
||||||
if let Value::Primitive(Primitive::Boolean(boolean)) = equal_result {
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result {
|
||||||
boolean
|
boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
@ -288,8 +271,8 @@ impl Vm {
|
|||||||
let less_result = left
|
let less_result = left
|
||||||
.less_than(right)
|
.less_than(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = less_result
|
let boolean =
|
||||||
{
|
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result {
|
||||||
boolean
|
boolean
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedBoolean {
|
return Err(VmError::ExpectedBoolean {
|
||||||
@ -324,7 +307,7 @@ impl Vm {
|
|||||||
let less_or_equal_result = left
|
let less_or_equal_result = left
|
||||||
.less_than_or_equal(right)
|
.less_than_or_equal(right)
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) =
|
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) =
|
||||||
less_or_equal_result
|
less_or_equal_result
|
||||||
{
|
{
|
||||||
boolean
|
boolean
|
||||||
@ -400,7 +383,8 @@ impl Vm {
|
|||||||
let function_register = instruction.b();
|
let function_register = instruction.b();
|
||||||
let argument_count = instruction.c();
|
let argument_count = instruction.c();
|
||||||
let value = self.get_register(function_register, position)?.clone();
|
let value = self.get_register(function_register, position)?.clone();
|
||||||
let function = if let Value::Function(function) = value {
|
let function = if let Value::Concrete(ConcreteValue::Function(function)) = value
|
||||||
|
{
|
||||||
function
|
function
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::ExpectedFunction {
|
return Err(VmError::ExpectedFunction {
|
||||||
@ -448,7 +432,9 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(register) = self.last_assigned_register {
|
if let Some(register) = self.last_assigned_register {
|
||||||
let value = self.empty_register(register, position)?;
|
let value = self
|
||||||
|
.empty_register(register, position)?
|
||||||
|
.to_concrete(&mut self, position)?;
|
||||||
|
|
||||||
return Ok(Some(value));
|
return Ok(Some(value));
|
||||||
} else {
|
} else {
|
||||||
@ -623,7 +609,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_register(mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
pub fn empty_register(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
|
||||||
if index >= self.stack.len() {
|
if index >= self.stack.len() {
|
||||||
@ -640,7 +626,12 @@ impl Vm {
|
|||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
Register::Constant(constant_index) => {
|
Register::Constant(constant_index) => {
|
||||||
let value = self.chunk.take_constants().remove(constant_index as usize);
|
let constants = &mut self.chunk.constants_mut();
|
||||||
|
|
||||||
|
constants.push(Value::integer(0));
|
||||||
|
|
||||||
|
let value = constants.swap_remove(constant_index as usize);
|
||||||
|
self.chunk.is_poisoned = true;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
@ -649,6 +640,10 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
|
||||||
|
if self.chunk.is_poisoned {
|
||||||
|
return Err(VmError::PoisonedChunk { position });
|
||||||
|
}
|
||||||
|
|
||||||
if self.ip >= self.chunk.len() {
|
if self.ip >= self.chunk.len() {
|
||||||
self.ip = self.chunk.len() - 1;
|
self.ip = self.chunk.len() - 1;
|
||||||
}
|
}
|
||||||
@ -721,59 +716,27 @@ enum Register {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum VmError {
|
pub enum VmError {
|
||||||
CannotMutateImmutableLocal {
|
// Stack errors
|
||||||
identifier: String,
|
StackOverflow { position: Span },
|
||||||
position: Span,
|
StackUnderflow { position: Span },
|
||||||
},
|
|
||||||
EmptyRegister {
|
// Register errors
|
||||||
index: usize,
|
EmptyRegister { index: usize, position: Span },
|
||||||
position: Span,
|
RegisterIndexOutOfBounds { index: usize, position: Span },
|
||||||
},
|
|
||||||
ExpectedBoolean {
|
// Chunk errors
|
||||||
found: Value,
|
ConstantIndexOutOfBounds { index: usize, position: Span },
|
||||||
position: Span,
|
InstructionIndexOutOfBounds { index: usize, position: Span },
|
||||||
},
|
LocalIndexOutOfBounds { index: usize, position: Span },
|
||||||
ExpectedFunction {
|
PoisonedChunk { position: Span },
|
||||||
found: Value,
|
|
||||||
position: Span,
|
// Execution errors
|
||||||
},
|
ExpectedBoolean { found: Value, position: Span },
|
||||||
ConstantIndexOutOfBounds {
|
ExpectedFunction { found: Value, position: Span },
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
RegisterIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
InstructionIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
LocalIndexOutOfBounds {
|
|
||||||
index: usize,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
InvalidInstruction {
|
|
||||||
instruction: Instruction,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
StackOverflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
StackUnderflow {
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
UndefinedVariable {
|
|
||||||
identifier: String,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Wrappers for foreign errors
|
// Wrappers for foreign errors
|
||||||
NativeFunction(NativeFunctionError),
|
NativeFunction(NativeFunctionError),
|
||||||
Value {
|
Value { error: ValueError, position: Span },
|
||||||
error: ValueError,
|
|
||||||
position: Span,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnnotatedError for VmError {
|
impl AnnotatedError for VmError {
|
||||||
@ -783,19 +746,17 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable",
|
|
||||||
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds",
|
||||||
Self::EmptyRegister { .. } => "Empty register",
|
Self::EmptyRegister { .. } => "Empty register",
|
||||||
Self::ExpectedBoolean { .. } => "Expected boolean",
|
Self::ExpectedBoolean { .. } => "Expected boolean",
|
||||||
Self::ExpectedFunction { .. } => "Expected function",
|
Self::ExpectedFunction { .. } => "Expected function",
|
||||||
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
|
||||||
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds",
|
||||||
Self::InvalidInstruction { .. } => "Invalid instruction",
|
|
||||||
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds",
|
||||||
|
Self::NativeFunction(error) => error.description(),
|
||||||
|
Self::PoisonedChunk { .. } => "Poisoned chunk",
|
||||||
|
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
|
||||||
Self::StackOverflow { .. } => "Stack overflow",
|
Self::StackOverflow { .. } => "Stack overflow",
|
||||||
Self::StackUnderflow { .. } => "Stack underflow",
|
Self::StackUnderflow { .. } => "Stack underflow",
|
||||||
Self::UndefinedVariable { .. } => "Undefined variable",
|
|
||||||
Self::NativeFunction(error) => error.description(),
|
|
||||||
Self::Value { .. } => "Value error",
|
Self::Value { .. } => "Value error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,7 +766,7 @@ impl AnnotatedError for VmError {
|
|||||||
Self::ConstantIndexOutOfBounds { index, .. } => {
|
Self::ConstantIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Constant C{index} does not exist"))
|
Some(format!("Constant C{index} does not exist"))
|
||||||
}
|
}
|
||||||
Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")),
|
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
|
||||||
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
|
||||||
Self::RegisterIndexOutOfBounds { index, .. } => {
|
Self::RegisterIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Register {index} does not exist"))
|
Some(format!("Register {index} does not exist"))
|
||||||
@ -816,9 +777,6 @@ impl AnnotatedError for VmError {
|
|||||||
Self::LocalIndexOutOfBounds { index, .. } => {
|
Self::LocalIndexOutOfBounds { index, .. } => {
|
||||||
Some(format!("Local L{index} does not exist"))
|
Some(format!("Local L{index} does not exist"))
|
||||||
}
|
}
|
||||||
Self::UndefinedVariable { identifier, .. } => {
|
|
||||||
Some(format!("{identifier} is not in scope"))
|
|
||||||
}
|
|
||||||
Self::NativeFunction(error) => error.details(),
|
Self::NativeFunction(error) => error.details(),
|
||||||
Self::Value { error, .. } => Some(error.to_string()),
|
Self::Value { error, .. } => Some(error.to_string()),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -827,19 +785,17 @@ impl AnnotatedError for VmError {
|
|||||||
|
|
||||||
fn position(&self) -> Span {
|
fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::CannotMutateImmutableLocal { position, .. } => *position,
|
|
||||||
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
Self::ConstantIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::EmptyRegister { position, .. } => *position,
|
Self::EmptyRegister { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position, .. } => *position,
|
Self::ExpectedBoolean { position, .. } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
|
||||||
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
Self::InstructionIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::InvalidInstruction { position, .. } => *position,
|
|
||||||
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
Self::LocalIndexOutOfBounds { position, .. } => *position,
|
||||||
Self::StackUnderflow { position } => *position,
|
|
||||||
Self::StackOverflow { position } => *position,
|
|
||||||
Self::UndefinedVariable { position, .. } => *position,
|
|
||||||
Self::NativeFunction(error) => error.position(),
|
Self::NativeFunction(error) => error.position(),
|
||||||
|
Self::PoisonedChunk { position } => *position,
|
||||||
|
Self::RegisterIndexOutOfBounds { position, .. } => *position,
|
||||||
|
Self::StackOverflow { position } => *position,
|
||||||
|
Self::StackUnderflow { position } => *position,
|
||||||
Self::Value { position, .. } => *position,
|
Self::Value { position, .. } => *position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ fn empty_list() {
|
|||||||
Ok(Chunk::with_data(
|
Ok(Chunk::with_data(
|
||||||
None,
|
None,
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_list(0, 0, 0), Span(0, 2)),
|
(Instruction::load_list(0, 0), Span(0, 2)),
|
||||||
(Instruction::r#return(true), Span(2, 2)),
|
(Instruction::r#return(true), Span(2, 2)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
@ -17,7 +17,7 @@ fn empty_list() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any))));
|
assert_eq!(run(source), Ok(Some(Value::list(vec![]))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -32,7 +32,7 @@ fn list() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||||
(Instruction::load_constant(1, 1, false), Span(4, 5)),
|
(Instruction::load_constant(1, 1, false), Span(4, 5)),
|
||||||
(Instruction::load_constant(2, 2, false), Span(7, 8)),
|
(Instruction::load_constant(2, 2, false), Span(7, 8)),
|
||||||
(Instruction::load_list(3, 0, 2), Span(0, 9)),
|
(Instruction::load_list(3, 0), Span(0, 9)),
|
||||||
(Instruction::r#return(true), Span(9, 9)),
|
(Instruction::r#return(true), Span(9, 9)),
|
||||||
],
|
],
|
||||||
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
vec![Value::integer(1), Value::integer(2), Value::integer(3),],
|
||||||
@ -40,7 +40,14 @@ fn list() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer))));
|
assert_eq!(
|
||||||
|
run(source),
|
||||||
|
Ok(Some(Value::list([
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2),
|
||||||
|
Value::integer(3)
|
||||||
|
])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -67,7 +74,7 @@ fn list_with_complex_expression() {
|
|||||||
),
|
),
|
||||||
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
||||||
(Instruction::close(1, 3), Span(17, 18)),
|
(Instruction::close(1, 3), Span(17, 18)),
|
||||||
(Instruction::load_list(4, 0, 3), Span(0, 18)),
|
(Instruction::load_list(4, 0), Span(0, 18)),
|
||||||
(Instruction::r#return(true), Span(18, 18)),
|
(Instruction::r#return(true), Span(18, 18)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -81,7 +88,13 @@ fn list_with_complex_expression() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::list(0, 3, Type::Integer))));
|
assert_eq!(
|
||||||
|
run(source),
|
||||||
|
Ok(Some(Value::list([
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2 + 3 - 4 * 5)
|
||||||
|
])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -101,7 +114,7 @@ fn list_with_simple_expression() {
|
|||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
||||||
(Instruction::load_list(3, 0, 2), Span(0, 13)),
|
(Instruction::load_list(3, 0), Span(0, 13)),
|
||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -114,5 +127,12 @@ fn list_with_simple_expression() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer))));
|
assert_eq!(
|
||||||
|
run(source),
|
||||||
|
Ok(Some(Value::list([
|
||||||
|
Value::integer(1),
|
||||||
|
Value::integer(2 + 3),
|
||||||
|
Value::integer(4)
|
||||||
|
])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user