1
0

Add parents to the VM; Improve the disassembler

This commit is contained in:
Jeff 2024-11-07 02:10:02 -05:00
parent f54d2d0d22
commit 04a1c81a2a
7 changed files with 151 additions and 231 deletions

View File

@ -95,18 +95,21 @@ impl<'src> Compiler<'src> {
} }
fn next_register(&mut self) -> u8 { fn next_register(&mut self) -> u8 {
self.chunk let current = self
.chunk
.instructions() .instructions()
.iter() .iter()
.rev() .rev()
.find_map(|(instruction, _)| { .find_map(|(instruction, _)| {
if instruction.yields_value() { if instruction.yields_value() {
Some(instruction.a() + 1) Some(instruction.a())
} else { } else {
None None
} }
}) })
.unwrap_or(self.minimum_register) .unwrap_or(self.minimum_register);
current.overflowing_add(1).0
} }
fn advance(&mut self) -> Result<(), CompileError> { fn advance(&mut self) -> Result<(), CompileError> {

View File

@ -50,8 +50,8 @@ use crate::{Chunk, ConcreteValue, Local, Value};
const INSTRUCTION_HEADER: [&str; 4] = [ const INSTRUCTION_HEADER: [&str; 4] = [
"Instructions", "Instructions",
"------------", "------------",
" i BYTECODE OPERATION INFO TYPE POSITION ", " i BYTECODE OPERATION INFO TYPE POSITION ",
"--- -------- ------------- -------------------- ---------------- ------------", "--- -------- ------------- -------------------- ---------------- ----------",
]; ];
const CONSTANT_HEADER: [&str; 4] = [ const CONSTANT_HEADER: [&str; 4] = [
@ -277,7 +277,7 @@ impl<'a> Disassembler<'a> {
let position = position.to_string(); let position = position.to_string();
let instruction_display = format!( let instruction_display = format!(
"{index:^3} {bytecode:>8} {operation:13} {info:20} {type_display:^16} {position:12}" "{index:^3} {bytecode:>8} {operation:13} {info:^20} {type_display:^16} {position:10}"
); );
self.push_details(&instruction_display); self.push_details(&instruction_display);

View File

@ -28,7 +28,7 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError}; use crate::{Chunk, FunctionType, RangeableType, Type};
/// Dust value representation /// Dust value representation
/// ///
@ -255,25 +255,6 @@ impl Value {
Ok(not) Ok(not)
} }
pub fn to_concrete(self, vm: &mut Vm, position: Span) -> Result<Value, VmError> {
match self {
Value::Concrete(_) => Ok(self),
Value::Abstract(AbstractValue::List { start, end, .. }) => {
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)))
}
}
}
} }
impl From<bool> for Value { impl From<bool> for Value {

View File

@ -1,5 +1,8 @@
//! Virtual machine and errors //! Virtual machine and errors
use std::{cmp::Ordering, mem::replace}; use std::{
cmp::Ordering,
fmt::{self, Display, Formatter},
};
use crate::{ use crate::{
compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType,
@ -9,42 +12,53 @@ use crate::{
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let chunk = compile(source)?; let chunk = compile(source)?;
let vm = Vm::new(chunk); let mut vm = Vm::new(chunk, None);
vm.run() vm.run()
.map(|option| option.cloned())
.map_err(|error| DustError::Runtime { error, source }) .map_err(|error| DustError::Runtime { error, source })
} }
pub fn run_and_display_output(source: &str) {
match run(source) {
Ok(Some(value)) => println!("{}", value),
Ok(None) => {}
Err(error) => eprintln!("{}", error.report()),
}
}
/// 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, Eq, PartialEq)]
pub struct Vm { pub struct Vm<'parent> {
ip: usize, ip: usize,
chunk: Chunk, chunk: Chunk,
stack: Vec<Register>, stack: Vec<Register>,
last_assigned_register: Option<u8>, last_assigned_register: Option<u8>,
parent: Option<&'parent Vm<'parent>>,
} }
impl Vm { impl<'parent> Vm<'parent> {
const STACK_LIMIT: usize = u16::MAX as usize; const STACK_LIMIT: usize = u16::MAX as usize;
pub fn new(chunk: Chunk) -> Self { pub fn new(chunk: Chunk, parent: Option<&'parent Vm<'parent>>) -> Self {
Self { Self {
ip: 0, ip: 0,
chunk, chunk,
stack: Vec::new(), stack: Vec::new(),
last_assigned_register: None, last_assigned_register: None,
parent,
} }
} }
pub fn run(mut self) -> Result<Option<Value>, VmError> { pub fn run(&mut self) -> Result<Option<&Value>, VmError> {
// DRY helper to get constant or register values for binary operations // DRY helper to get constant or register values for binary operations
fn get_arguments( fn get_arguments<'a>(
vm: &mut Vm, vm: &'a mut Vm,
instruction: Instruction, instruction: Instruction,
position: Span, position: Span,
) -> Result<(&Value, &Value), VmError> { ) -> Result<(&'a Value, &'a Value), VmError> {
let left = if instruction.b_is_constant() { let left = if instruction.b_is_constant() {
vm.get_constant(instruction.b(), position)? vm.get_constant(instruction.b(), position)?
} else { } else {
@ -78,7 +92,11 @@ impl Vm {
.is_some_and(|register| !matches!(register, Register::Empty)); .is_some_and(|register| !matches!(register, Register::Empty));
if from_register_has_value { if from_register_has_value {
self.set_pointer(to_register, from_register, position)?; self.set_register(
to_register,
Register::StackPointer(from_register),
position,
)?;
} }
} }
Operation::Close => { Operation::Close => {
@ -99,7 +117,7 @@ impl Vm {
let jump = instruction.c_as_boolean(); let jump = instruction.c_as_boolean();
let value = Value::boolean(boolean); let value = Value::boolean(boolean);
self.set_register(to_register, value, position)?; self.set_register(to_register, Register::Value(value), position)?;
if jump { if jump {
self.ip += 1; self.ip += 1;
@ -110,7 +128,11 @@ impl Vm {
let from_constant = instruction.b(); let from_constant = instruction.b();
let jump = instruction.c_as_boolean(); let jump = instruction.c_as_boolean();
self.set_constant(to_register, from_constant, position)?; self.set_register(
to_register,
Register::ConstantPointer(from_constant),
position,
)?;
if jump { if jump {
self.ip += 1 self.ip += 1
@ -130,7 +152,7 @@ impl Vm {
.unwrap_or(Type::Any); .unwrap_or(Type::Any);
let value = Value::abstract_list(start_register, to_register, item_type); let value = Value::abstract_list(start_register, to_register, item_type);
self.set_register(to_register, value, position)?; self.set_register(to_register, Register::Value(value), position)?;
} }
Operation::LoadSelf => { Operation::LoadSelf => {
let to_register = instruction.a(); let to_register = instruction.a();
@ -143,7 +165,7 @@ impl Vm {
}, },
); );
self.set_register(to_register, value, position)?; self.set_register(to_register, Register::Value(value), position)?;
} }
Operation::DefineLocal => { Operation::DefineLocal => {
let from_register = instruction.a(); let from_register = instruction.a();
@ -156,7 +178,11 @@ impl Vm {
let local_index = instruction.b(); let local_index = instruction.b();
let local = self.get_local(local_index, position)?; let local = self.get_local(local_index, position)?;
self.set_pointer(to_register, local.register_index, position)?; self.set_register(
to_register,
Register::StackPointer(local.register_index),
position,
)?;
} }
Operation::SetLocal => { Operation::SetLocal => {
let register = instruction.a(); let register = instruction.a();
@ -165,44 +191,49 @@ impl Vm {
self.define_local(local_index, register, position)?; self.define_local(local_index, register, position)?;
} }
Operation::Add => { Operation::Add => {
let (left, right) = get_arguments(&mut self, instruction, position)?; let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?;
let sum = left let sum = left
.add(right) .add(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), sum, position)?; self.set_register(to_register, Register::Value(sum), position)?;
} }
Operation::Subtract => { Operation::Subtract => {
let (left, right) = get_arguments(&mut self, instruction, position)?; let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?;
let difference = left let difference = left
.subtract(right) .subtract(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), difference, position)?; self.set_register(to_register, Register::Value(difference), position)?;
} }
Operation::Multiply => { Operation::Multiply => {
let (left, right) = get_arguments(&mut self, instruction, position)?; let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?;
let product = left let product = left
.multiply(right) .multiply(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), product, position)?; self.set_register(to_register, Register::Value(product), position)?;
} }
Operation::Divide => { Operation::Divide => {
let (left, right) = get_arguments(&mut self, instruction, position)?; let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?;
let quotient = left let quotient = left
.divide(right) .divide(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), quotient, position)?; self.set_register(to_register, Register::Value(quotient), position)?;
} }
Operation::Modulo => { Operation::Modulo => {
let (left, right) = get_arguments(&mut self, instruction, position)?; let to_register = instruction.a();
let (left, right) = get_arguments(self, instruction, position)?;
let remainder = left let remainder = left
.modulo(right) .modulo(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), remainder, position)?; self.set_register(to_register, Register::Value(remainder), position)?;
} }
Operation::Test => { Operation::Test => {
let register = instruction.a(); let register = instruction.a();
@ -228,7 +259,7 @@ impl Vm {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(&mut self, instruction, position)?; let (left, right) = get_arguments(self, instruction, position)?;
let equal_result = left let equal_result = left
.equal(right) .equal(right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
@ -264,7 +295,7 @@ impl Vm {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(&mut self, instruction, position)?; let (left, right) = get_arguments(self, instruction, position)?;
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 })?;
@ -300,7 +331,7 @@ impl Vm {
Operation::Jump Operation::Jump
); );
let (left, right) = get_arguments(&mut self, instruction, position)?; let (left, right) = get_arguments(self, instruction, position)?;
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 })?;
@ -341,7 +372,7 @@ impl Vm {
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), negated, position)?; self.set_register(instruction.a(), Register::Value(negated), position)?;
} }
Operation::Not => { Operation::Not => {
let value = if instruction.b_is_constant() { let value = if instruction.b_is_constant() {
@ -353,7 +384,7 @@ impl Vm {
.not() .not()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.set_register(instruction.a(), not, position)?; self.set_register(instruction.a(), Register::Value(not), position)?;
} }
Operation::Jump => { Operation::Jump => {
let jump_distance = instruction.b(); let jump_distance = instruction.b();
@ -379,36 +410,35 @@ impl Vm {
position, position,
}); });
}; };
let mut function_vm = Vm::new(function.take_chunk()); let mut function_vm = Vm::new(function.take_chunk(), Some(self));
let first_argument_index = function_register + 1; let first_argument_index = function_register + 1;
for argument_index in for argument_index in
first_argument_index..first_argument_index + argument_count first_argument_index..first_argument_index + argument_count
{ {
let argument = match self.get_register(argument_index, position) {
Ok(value) => value.clone(),
Err(VmError::EmptyRegister { .. }) => continue,
Err(error) => return Err(error),
};
let top_of_stack = function_vm.stack.len() as u8; let top_of_stack = function_vm.stack.len() as u8;
function_vm.set_register(top_of_stack, argument, position)?; function_vm.set_register(
top_of_stack,
Register::ParentStackPointer(argument_index),
position,
)?
} }
let return_value = function_vm.run()?; let return_value = function_vm.run()?.cloned();
if let Some(value) = return_value { if let Some(value) = return_value {
self.set_register(to_register, value, position)?; self.set_register(to_register, Register::Value(value), position)?;
} }
} }
Operation::CallNative => { Operation::CallNative => {
let native_function = NativeFunction::from(instruction.b()); let native_function = NativeFunction::from(instruction.b());
let return_value = native_function.call(instruction, &self, position)?; let return_value = native_function.call(instruction, self, position)?;
if let Some(value) = return_value { if let Some(value) = return_value {
let to_register = instruction.a(); let to_register = instruction.a();
self.set_register(to_register, value, position)?; self.set_register(to_register, Register::Value(value), position)?;
} }
} }
Operation::Return => { Operation::Return => {
@ -418,15 +448,13 @@ impl Vm {
return Ok(None); return Ok(None);
} }
if let Some(register) = self.last_assigned_register { let return_value = if let Some(register_index) = self.last_assigned_register {
let value = self self.get_register(register_index, position)?
.empty_register(register, position)?
.to_concrete(&mut self, position)?;
return Ok(Some(value));
} else { } else {
return Err(VmError::StackUnderflow { position }); return Err(VmError::StackUnderflow { position });
} };
return Ok(Some(return_value));
} }
} }
} }
@ -437,7 +465,7 @@ impl Vm {
fn set_register( fn set_register(
&mut self, &mut self,
to_register: u8, to_register: u8,
value: Value, register: Register,
position: Span, position: Span,
) -> Result<(), VmError> { ) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register); self.last_assigned_register = Some(to_register);
@ -451,16 +479,16 @@ impl Vm {
match to_register.cmp(&length) { match to_register.cmp(&length) {
Ordering::Less => { Ordering::Less => {
log::trace!("Change R{to_register} to {value}"); log::trace!("Change R{to_register} to {register}");
self.stack[to_register] = Register::Value(value); self.stack[to_register] = register;
Ok(()) Ok(())
} }
Ordering::Equal => { Ordering::Equal => {
log::trace!("Set R{to_register} to {value}"); log::trace!("Set R{to_register} to {register}");
self.stack.push(Register::Value(value)); self.stack.push(register);
Ok(()) Ok(())
} }
@ -473,157 +501,58 @@ impl Vm {
self.stack.push(Register::Empty); self.stack.push(Register::Empty);
} }
log::trace!("Set R{to_register} to {value}"); log::trace!("Set R{to_register} to {register}");
self.stack.push(Register::Value(value)); self.stack.push(register);
Ok(()) Ok(())
} }
} }
} }
fn set_pointer(
&mut self,
to_register: u8,
from_register: u8,
position: Span,
) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register);
let length = self.stack.len();
let to_register = to_register as usize;
if length == Self::STACK_LIMIT {
return Err(VmError::StackOverflow { position });
}
match to_register.cmp(&length) {
Ordering::Less => {
log::trace!("Change R{to_register} to R{from_register}");
self.stack[to_register] = Register::Pointer(from_register);
}
Ordering::Equal => {
log::trace!("Set R{to_register} to R{from_register}");
self.stack.push(Register::Pointer(from_register));
}
Ordering::Greater => {
let difference = to_register - length;
for index in 0..difference {
log::trace!("Set R{index} to empty");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to R{from_register}");
self.stack.push(Register::Pointer(from_register));
}
}
Ok(())
}
fn set_constant(
&mut self,
to_register: u8,
constant_index: u8,
position: Span,
) -> Result<(), VmError> {
self.last_assigned_register = Some(to_register);
let length = self.stack.len();
let to_register = to_register as usize;
if length == Self::STACK_LIMIT {
return Err(VmError::StackOverflow { position });
}
match to_register.cmp(&length) {
Ordering::Less => {
log::trace!("Change R{to_register} to C{constant_index}");
self.stack[to_register] = Register::Constant(constant_index);
}
Ordering::Equal => {
log::trace!("Set R{to_register} to C{constant_index}");
self.stack.push(Register::Constant(constant_index));
}
Ordering::Greater => {
let difference = to_register - length;
for index in 0..difference {
log::trace!("Set R{index} to empty");
self.stack.push(Register::Empty);
}
log::trace!("Set R{to_register} to C{constant_index}");
self.stack.push(Register::Constant(constant_index));
}
}
Ok(())
}
fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { fn get_constant(&self, index: u8, position: Span) -> 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 })
} }
pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> { pub fn get_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> {
let index = index as usize; let register_index = register_index as usize;
let register = self let register =
.stack self.stack
.get(index) .get(register_index)
.ok_or_else(|| VmError::RegisterIndexOutOfBounds { index, position })?; .ok_or_else(|| VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
})?;
match register { match register {
Register::Value(value) => Ok(value), Register::Value(value) => Ok(value),
Register::Pointer(register_index) => self.get_register(*register_index, position), Register::StackPointer(register_index) => self.get_register(*register_index, position),
Register::Constant(constant_index) => self.get_constant(*constant_index, position), Register::ConstantPointer(constant_index) => {
Register::Empty => Err(VmError::EmptyRegister { index, position }), self.get_constant(*constant_index, position)
}
}
pub fn empty_register(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize;
if index >= self.stack.len() {
return Err(VmError::RegisterIndexOutOfBounds { index, position });
}
let register = replace(&mut self.stack[index], Register::Empty);
let value = match register {
Register::Value(value) => value,
Register::Pointer(register_index) => self.empty_register(register_index, position)?,
Register::Constant(constant_index) => {
let constant_index = constant_index as usize;
if constant_index >= self.chunk.constants().len() {
return Err(VmError::Chunk {
error: ChunkError::ConstantIndexOutOfBounds {
index: constant_index,
},
position,
});
}
let constant = &mut self.chunk.constants_mut()[constant_index];
replace(constant, Value::integer(0))
} }
Register::Empty => return Err(VmError::EmptyRegister { index, position }), Register::ParentStackPointer(register_index) => {
}; let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
self.chunk.is_poisoned = true; parent.get_register(*register_index, position)
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
Ok(value) parent.get_constant(*constant_index, position)
}
Register::Empty => Err(VmError::EmptyRegister {
index: register_index,
position,
}),
}
} }
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
@ -681,8 +610,23 @@ impl Vm {
enum Register { enum Register {
Empty, Empty,
Value(Value), Value(Value),
Pointer(u8), StackPointer(u8),
Constant(u8), ConstantPointer(u8),
ParentStackPointer(u8),
ParentConstantPointer(u8),
}
impl Display for Register {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{}", value),
Self::StackPointer(index) => write!(f, "R{}", index),
Self::ConstantPointer(index) => write!(f, "C{}", index),
Self::ParentStackPointer(index) => write!(f, "PR{}", index),
Self::ParentConstantPointer(index) => write!(f, "PC{}", index),
}
}
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -698,6 +642,7 @@ pub enum VmError {
// Execution errors // Execution errors
ExpectedBoolean { found: Value, position: Span }, ExpectedBoolean { found: Value, position: Span },
ExpectedFunction { found: Value, position: Span }, ExpectedFunction { found: Value, position: Span },
ExpectedParent { position: Span },
// Wrappers for foreign errors // Wrappers for foreign errors
Chunk { error: ChunkError, position: Span }, Chunk { error: ChunkError, position: Span },
@ -716,6 +661,7 @@ impl AnnotatedError for VmError {
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::ExpectedParent { .. } => "Expected parent",
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",
@ -744,6 +690,7 @@ impl AnnotatedError for VmError {
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::ExpectedParent { 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,

View File

@ -17,7 +17,7 @@ fn empty_list() {
)), )),
); );
assert_eq!(run(source), Ok(Some(Value::list(vec![])))); assert_eq!(run(source), Ok(Some(Value::abstract_list(0, 0, Type::Any))));
} }
#[test] #[test]
@ -35,18 +35,14 @@ fn list() {
(Instruction::load_list(3, 0), 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)],
vec![] vec![]
)), )),
); );
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::list([ Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
Value::integer(1),
Value::integer(2),
Value::integer(3)
])))
); );
} }
@ -90,10 +86,7 @@ fn list_with_complex_expression() {
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::list([ Ok(Some(Value::abstract_list(0, 4, Type::Integer)))
Value::integer(1),
Value::integer(2 + 3 - 4 * 5)
])))
); );
} }
@ -129,10 +122,6 @@ fn list_with_simple_expression() {
assert_eq!( assert_eq!(
run(source), run(source),
Ok(Some(Value::list([ Ok(Some(Value::abstract_list(0, 3, Type::Integer)))
Value::integer(1),
Value::integer(2 + 3),
Value::integer(4)
])))
); );
} }

View File

@ -8,4 +8,4 @@ function fib(n) {
} }
} }
console.log(fib(10)); console.log(fib(20));

View File

@ -5,4 +5,4 @@ fn fib (n: int) -> int {
fib(n - 1) + fib(n - 2) fib(n - 1) + fib(n - 2)
} }
fib(10) fib(20)