[rlox] Implement Print Statements

This commit is contained in:
ctsk
2024-08-30 13:10:27 +02:00
parent 78ef9fdc07
commit 80b0c20ccd
5 changed files with 277 additions and 119 deletions

View File

@@ -1,8 +1,8 @@
use crate::gc::{GcHandle, Object}; use crate::gc::{GcHandle, Object, ObjectType};
use std::collections::LinkedList; use std::collections::LinkedList;
use std::convert::From; use std::convert::From;
use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::fmt::{self, Display};
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Op { pub enum Op {
@@ -20,6 +20,8 @@ pub enum Op {
Equal, Equal,
Greater, Greater,
Less, Less,
Print,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@@ -71,6 +73,28 @@ impl From<Object> for Value {
} }
} }
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Nil => write!(f, "nil"),
Value::Bool(true) => write!(f, "true"),
Value::Bool(false) => write!(f, "false"),
Value::Number(number) => {
let stringified = number.to_string();
match stringified.strip_suffix(".0") {
Some(integer) => write!(f, "{}", integer),
None => write!(f, "{}", stringified),
}
}
Value::Obj(object) => match object.get_otype() {
ObjectType::String => {
write!(f, "{}", object)
}
},
}
}
}
pub struct Chunk { pub struct Chunk {
pub code: Vec<Op>, pub code: Vec<Op>,
pub debug_info: Vec<usize>, pub debug_info: Vec<usize>,
@@ -84,16 +108,21 @@ impl Chunk {
code: Vec::new(), code: Vec::new(),
debug_info: Vec::new(), debug_info: Vec::new(),
constants: Vec::new(), constants: Vec::new(),
allocations: LinkedList::new() allocations: LinkedList::new(),
} }
} }
pub fn new_with(code: Vec<Op>, debug_info: Vec<usize>, constants: Vec<Value>, allocations: LinkedList<GcHandle>) -> Self { pub fn new_with(
code: Vec<Op>,
debug_info: Vec<usize>,
constants: Vec<Value>,
allocations: LinkedList<GcHandle>,
) -> Self {
Chunk { Chunk {
code, code,
debug_info, debug_info,
constants, constants,
allocations allocations,
} }
} }
@@ -179,7 +208,7 @@ impl fmt::Debug for TraceInfo<'_> {
.finish()?; .finish()?;
write!(f, "") write!(f, "")
} }
_ => write!(f, "{:?}", op) _ => write!(f, "{:?}", op),
} }
} }
} }
@@ -188,8 +217,8 @@ mod tests {
#[test] #[test]
fn string_value_equality() { fn string_value_equality() {
use crate::gc::allocate_string;
use crate::bc::Value; use crate::bc::Value;
use crate::gc::allocate_string;
let s1 = "bla5"; let s1 = "bla5";
let s2 = "bla6"; let s2 = "bla6";

View File

@@ -1,4 +1,7 @@
use std::{alloc::{alloc, dealloc, Layout, LayoutError}, fmt}; use std::{
alloc::{alloc, dealloc, Layout, LayoutError},
fmt::{self, Display},
};
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
#[repr(usize)] #[repr(usize)]
@@ -32,13 +35,20 @@ pub struct Object {
ptr: *mut ObjectHeader, ptr: *mut ObjectHeader,
} }
impl Display for Object {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get_otype() {
ObjectType::String => {
write!(f, "{}", ObjString::as_str(self.ptr as *mut ObjStringHeader))
}
}
}
}
impl Object { impl Object {
pub fn get_otype(&self) -> ObjectType { pub fn get_otype(&self) -> ObjectType {
unsafe { unsafe { (*self.ptr).otype }
(*self.ptr).otype
} }
}
} }
impl fmt::Debug for Object { impl fmt::Debug for Object {
@@ -46,14 +56,10 @@ impl fmt::Debug for Object {
match self.get_otype() { match self.get_otype() {
ObjectType::String => { ObjectType::String => {
let string = self.ptr as *mut ObjStringHeader; let string = self.ptr as *mut ObjStringHeader;
let data: &[u8] = ObjString::as_slice(string); let data = ObjString::as_str(string);
write!(
f, write!(f, "STR {} {:?}", data.len(), &data[..8.min(data.len())],)
"STR {} {:?}", }
data.len(),
&data[..8.min(data.len())],
)
},
} }
} }
} }
@@ -78,11 +84,11 @@ impl PartialEq for Object {
return false; return false;
} }
let slice = ObjString::as_slice(header); let slice = ObjString::as_str(header);
let other_slice = ObjString::as_slice(other_header); let other_slice = ObjString::as_str(other_header);
slice == other_slice slice == other_slice
}, }
} }
} }
} }
@@ -101,24 +107,29 @@ impl ObjString {
Ok((layout.pad_to_align(), offset)) Ok((layout.pad_to_align(), offset))
} }
fn as_slice<'a>(ptr: *mut ObjStringHeader) -> &'a [u8] { fn as_bytes<'a>(ptr: *const ObjStringHeader) -> &'a [u8] {
unsafe { unsafe {
std::slice::from_raw_parts( std::slice::from_raw_parts((ptr as *mut u8).offset(data_offset() as isize), (*ptr).len)
(ptr as *mut u8).offset(data_offset() as isize),
(*ptr).len
)
} }
} }
fn as_str<'a>(ptr: *const ObjStringHeader) -> &'a str {
unsafe { std::str::from_utf8_unchecked(ObjString::as_bytes(ptr)) }
}
} }
pub unsafe fn allocate_string_obj<'a>(length: usize) -> Result<(GcHandle, &'a mut [u8]), LayoutError> { pub unsafe fn allocate_string_obj<'a>(
length: usize,
) -> Result<(GcHandle, &'a mut [u8]), LayoutError> {
let (layout, offset) = ObjString::layout(length)?; let (layout, offset) = ObjString::layout(length)?;
let allocation = alloc(layout); let allocation = alloc(layout);
let data_ptr = allocation.offset(offset as isize); let data_ptr = allocation.offset(offset as isize);
let header = allocation as *mut ObjStringHeader; let header = allocation as *mut ObjStringHeader;
(*header).len = length; (*header).len = length;
(*header).object_header.otype = ObjectType::String; (*header).object_header.otype = ObjectType::String;
let object = Object { ptr: header as *mut ObjectHeader }; let object = Object {
ptr: header as *mut ObjectHeader,
};
let str = std::slice::from_raw_parts_mut(data_ptr, length); let str = std::slice::from_raw_parts_mut(data_ptr, length);
Ok((GcHandle { object }, str)) Ok((GcHandle { object }, str))
} }
@@ -132,8 +143,8 @@ pub unsafe fn allocate_string(content: &str) -> Result<GcHandle, LayoutError> {
pub unsafe fn concat_string(a: Object, b: Object) -> Result<GcHandle, LayoutError> { pub unsafe fn concat_string(a: Object, b: Object) -> Result<GcHandle, LayoutError> {
let a_head = a.ptr as *mut ObjStringHeader; let a_head = a.ptr as *mut ObjStringHeader;
let b_head = b.ptr as *mut ObjStringHeader; let b_head = b.ptr as *mut ObjStringHeader;
let a_data = ObjString::as_slice(a_head); let a_data = ObjString::as_bytes(a_head);
let b_data = ObjString::as_slice(b_head); let b_data = ObjString::as_bytes(b_head);
let new_len = a_data.len() + b_data.len(); let new_len = a_data.len() + b_data.len();
let (gc_handle, slice) = allocate_string_obj(new_len)?; let (gc_handle, slice) = allocate_string_obj(new_len)?;
@@ -158,7 +169,7 @@ unsafe fn deallocate_object(object: Object) {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct GcHandle { pub struct GcHandle {
object: Object object: Object,
} }
impl Drop for GcHandle { impl Drop for GcHandle {

View File

@@ -1,6 +1,6 @@
use std::{collections::HashMap, convert::identity};
use std::iter::Peekable; use std::iter::Peekable;
use std::str::CharIndices; use std::str::CharIndices;
use std::{collections::HashMap, convert::identity};
use crate::bc::{Chunk, Op}; use crate::bc::{Chunk, Op};
use crate::gc::allocate_string; use crate::gc::allocate_string;
@@ -234,7 +234,10 @@ impl<'src> Iterator for Scanner<'src> {
'+' => make_simple_token(self, TokenType::Plus), '+' => make_simple_token(self, TokenType::Plus),
';' => make_simple_token(self, TokenType::Semicolon), ';' => make_simple_token(self, TokenType::Semicolon),
'/' => match self.consume_if_eq('/') { '/' => match self.consume_if_eq('/') {
Some(_) => { self.scan_comment(); self.next() }, Some(_) => {
self.scan_comment();
self.next()
}
None => make_simple_token(self, TokenType::Slash), None => make_simple_token(self, TokenType::Slash),
}, },
'*' => make_simple_token(self, TokenType::Star), '*' => make_simple_token(self, TokenType::Star),
@@ -288,7 +291,14 @@ enum Precedence {
Primary, Primary,
} }
enum ParseError<'src> {
InvalidNumber(Token<'src>)
}
type Result<'src, T> = std::result::Result<T, ParseError<'src>>;
impl<'src> Parser<'src> { impl<'src> Parser<'src> {
fn new(sc: Scanner<'src>) -> Self { fn new(sc: Scanner<'src>) -> Self {
Parser { Parser {
scanner: sc.into_iter().peekable(), scanner: sc.into_iter().peekable(),
@@ -330,50 +340,50 @@ impl<'src> Parser<'src> {
_ => unreachable!(), _ => unreachable!(),
}; };
chunk.add_op(op, 0); chunk.add_op(op, 0);
}, }
TokenType::Number => { TokenType::Number => {
match token.span.parse::<f64>() { match token.span.parse::<f64>() {
Ok(c) => chunk.add_constant(c.into(), 0), Ok(c) => chunk.add_constant(c.into(), 0),
_ => panic!("Could not parse number"), _ => panic!("Could not parse number"),
}; };
}, }
TokenType::String => { TokenType::String => {
let without_quotes = &token.span[1..(token.span.len() - 1)]; let without_quotes = &token.span[1..(token.span.len() - 1)];
match self.intern_table.get(without_quotes) { match self.intern_table.get(without_quotes) {
Some(&index) => { Some(&index) => {
chunk.add_op( chunk.add_op(Op::Constant { offset: index }, 0);
Op::Constant { }
offset: index,
},
0
);
},
None => { None => {
let object = unsafe { allocate_string(without_quotes) }.unwrap(); let object = unsafe { allocate_string(without_quotes) }.unwrap();
chunk.add_constant(object.get_object().into(), 0); chunk.add_constant(object.get_object().into(), 0);
self.intern_table.insert(without_quotes, chunk.constants.len() as u8 - 1); self.intern_table
.insert(without_quotes, chunk.constants.len() as u8 - 1);
chunk.allocations.push_front(object); chunk.allocations.push_front(object);
}, }
}; };
}, }
TokenType::Nil | TokenType::True | TokenType::False => { TokenType::Nil | TokenType::True | TokenType::False => {
let op = match token.ttype { let op = match token.ttype {
TokenType::Nil => Op::Nil, TokenType::Nil => Op::Nil,
TokenType::True => Op::True, TokenType::True => Op::True,
TokenType::False => Op::False, TokenType::False => Op::False,
_ => unreachable!() _ => unreachable!(),
}; };
chunk.add_op(op, 0); chunk.add_op(op, 0);
}, }
TokenType::LeftParen => { TokenType::LeftParen => {
self._expression(chunk, Precedence::None); self._expression(chunk, Precedence::None);
assert_eq!(self.scanner.next().unwrap().ttype, TokenType::RightParen) assert_eq!(self.scanner.next().unwrap().ttype, TokenType::RightParen)
}, }
_ => panic!("Expected '-' or number"), _ => panic!("Expected '-' or number"),
}, },
}; };
while let Some(op) = self.scanner.next_if(|token| { while let Some(op) = self.scanner.next_if(|token| {
if token.ttype == TokenType::Semicolon {
return false;
}
let op_prec = Self::precedence(token.ttype); let op_prec = Self::precedence(token.ttype);
if op_prec == min_prec { if op_prec == min_prec {
match Self::associativity(min_prec) { match Self::associativity(min_prec) {
@@ -404,17 +414,62 @@ impl<'src> Parser<'src> {
_ => todo!(), _ => todo!(),
}; };
} }
} }
pub fn expression(&mut self, chunk: &mut Chunk) { pub fn expression(&mut self, chunk: &mut Chunk) {
self._expression(chunk, Precedence::None) self._expression(chunk, Precedence::None)
} }
pub fn must_consume(&mut self, ttype: TokenType) -> Option<Token> {
self.scanner.next_if(
|tok| tok.ttype == ttype
).or_else(
|| panic!()
)
}
pub fn print_statement(&mut self, chunk: &mut Chunk) {
self.must_consume(TokenType::Print).unwrap();
self.expression(chunk);
chunk.add_op(Op::Print, 0);
self.must_consume(TokenType::Semicolon).unwrap();
}
pub fn expr_statement(&mut self, chunk: &mut Chunk) {
self.expression(chunk);
self.must_consume(TokenType::Semicolon).unwrap();
}
pub fn statement(&mut self, chunk: &mut Chunk) {
match self.scanner.peek().unwrap().ttype {
TokenType::Print => self.print_statement(chunk),
_ => self.expr_statement(chunk),
}
}
pub fn declaration(&mut self, chunk: &mut Chunk) {
self.statement(chunk);
}
pub fn compile(&mut self, chunk: &mut Chunk) {
while let Some(_) = self.scanner.peek() {
self.declaration(chunk);
}
}
}
#[cfg(test)]
pub fn compile_expr(source: &str, chunk: &mut Chunk) {
let scanner = Scanner::new(source);
let mut parser = Parser::new(scanner);
parser.expression(chunk);
} }
pub fn compile(source: &str, chunk: &mut Chunk) { pub fn compile(source: &str, chunk: &mut Chunk) {
let scanner = Scanner::new(source); let scanner = Scanner::new(source);
let mut parser = Parser::new(scanner); let mut parser = Parser::new(scanner);
parser.expression(chunk); parser.compile(chunk);
} }
#[cfg(test)] #[cfg(test)]
@@ -508,12 +563,10 @@ mod tests {
assert_eq!( assert_eq!(
tokens, tokens,
vec![ vec![Token {
Token {
ttype: TokenType::String, ttype: TokenType::String,
span: &source[0..=12] span: &source[0..=12]
} }]
]
); );
assert_eq!(tokens[0].span, source); assert_eq!(tokens[0].span, source);
@@ -527,6 +580,14 @@ mod tests {
assert!(chunk.instr_eq(expected)); assert!(chunk.instr_eq(expected));
} }
fn test_parse_program(source: &str, expected: &Chunk) {
let scanner = Scanner::new(source);
let mut parser = Parser::new(scanner);
let mut chunk = Chunk::new();
parser.compile(&mut chunk);
assert!(chunk.instr_eq(expected));
}
#[test] #[test]
fn test_parser() { fn test_parser() {
let source = "1 + 1 * (2 + 1)"; let source = "1 + 1 * (2 + 1)";
@@ -553,12 +614,7 @@ mod tests {
fn parse_nil() { fn parse_nil() {
let source = "nil + nil"; let source = "nil + nil";
use crate::bc::Op::*; use crate::bc::Op::*;
let expected = Chunk::new_with( let expected = Chunk::new_with(vec![Nil, Nil, Add], vec![], vec![], LinkedList::new());
vec![Nil, Nil, Add],
vec![],
vec![],
LinkedList::new(),
);
test_parse_expression(source, &expected); test_parse_expression(source, &expected);
} }
@@ -583,12 +639,9 @@ mod tests {
use crate::bc::Op::*; use crate::bc::Op::*;
let expected = Chunk::new_with( let expected = Chunk::new_with(
vec![ vec![
False, Not, True, Not, True, Less, Not, False, Not, True, Not, True, Less, Not, False, Greater, Not, True, Greater, False,
False, Greater, Not, Less, Equal, True, Equal, Not,
True, Greater, ],
False, Less,
Equal,
True, Equal, Not],
vec![], vec![],
vec![], vec![],
LinkedList::new(), LinkedList::new(),
@@ -609,4 +662,49 @@ mod tests {
assert_eq!(chunk.constants.len(), 1); assert_eq!(chunk.constants.len(), 1);
} }
#[test]
fn basic_print_statement() {
let source = "print 1 + 1;";
use crate::bc::Op::*;
let expected = Chunk::new_with(
vec![Constant { offset: 0 }, Constant { offset: 1 }, Add, Print],
vec![],
vec![Value::from(1.0), Value::from(1.0)],
LinkedList::new(),
);
test_parse_program(source, &expected);
}
#[test]
fn basic_print_string_statement() {
let source = "print \"string\";";
let allocation = unsafe { allocate_string("string").unwrap() };
let object = allocation.get_object();
let mut allocations = LinkedList::new();
allocations.push_front(allocation);
use crate::bc::Op::*;
let expected = Chunk::new_with(
vec![Constant { offset: 0 }, Print],
vec![],
vec![Value::from(object)],
allocations,
);
test_parse_program(source, &expected);
}
#[test]
fn basic_expr_statement() {
let source = "1 / 1;";
use crate::bc::Op::*;
let expected = Chunk::new_with(
vec![Constant { offset: 0 }, Constant { offset: 1 }, Divide],
vec![],
vec![Value::from(1.0), Value::from(1.0)],
LinkedList::new(),
);
test_parse_program(source, &expected);
}
} }

View File

@@ -9,6 +9,7 @@ use std::io;
use bc::Chunk; use bc::Chunk;
use vm::VM; use vm::VM;
fn repl() { fn repl() {
let mut buffer = String::new(); let mut buffer = String::new();
@@ -21,7 +22,7 @@ fn repl() {
lc::compile(buffer.as_str(), &mut chunk); lc::compile(buffer.as_str(), &mut chunk);
let mut vm = VM::new(); let mut vm = VM::new();
vm.set_trace(do_trace); vm.set_trace(do_trace);
let result = vm.run(&chunk); let result = vm.stdrun(&chunk);
println!("{:?}", result); println!("{:?}", result);
buffer.clear(); buffer.clear();
} }
@@ -49,26 +50,40 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{bc::{Chunk, Value}, gc::allocate_string, lc::compile, vm::VM}; use std::io::BufWriter;
use crate::{bc::{Chunk, Value}, gc::allocate_string, lc::{compile, compile_expr}, vm::VM};
#[test] #[test]
fn test_compile_and_run_pi_math() { fn test_compile_and_run_pi_math() {
let source = "-(3 * 7 * 11 * 17) / -(500 + 1000 - 250)"; let source = "-(3 * 7 * 11 * 17) / -(500 + 1000 - 250)";
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
compile(source, &mut chunk); compile_expr(source, &mut chunk);
let mut vm = VM::new(); let mut vm = VM::new();
vm.run(&chunk).unwrap(); vm.stdrun(&chunk).unwrap();
} }
#[test] #[test]
fn string_concatenation() { fn string_concatenation() {
let source = "\"hello\" + \" \" + \"world\""; let source = "\"hello\" + \" \" + \"world\"";
let mut chunk = Chunk::new(); let mut chunk = Chunk::new();
compile(source, &mut chunk); compile_expr(source, &mut chunk);
let mut vm = VM::new(); let mut vm = VM::new();
let (result, _allocs) = vm.run(&chunk).unwrap().unwrap(); let (result, _allocs) = vm.stdrun(&chunk).unwrap().unwrap();
let target_alloc = unsafe { allocate_string("hello world").unwrap() }; let target_alloc = unsafe { allocate_string("hello world").unwrap() };
let target = Value::from(target_alloc.get_object()); let target = Value::from(target_alloc.get_object());
assert_eq!(result, target); assert_eq!(result, target);
} }
#[test]
fn print_hello_world() {
let source = "print \"hello\" + \" \" + \"world\";";
let mut chunk = Chunk::new();
let mut vm = VM::new();
compile(source, &mut chunk);
let mut buf = BufWriter::new(Vec::new());
vm.run(&chunk, &mut buf).unwrap();
let stdoutput = String::from_utf8(buf.into_inner().unwrap()).unwrap();
assert_eq!(stdoutput, "hello world\n");
}
} }

View File

@@ -1,6 +1,7 @@
use crate::bc::{Chunk, Op, TraceInfo, Value}; use crate::bc::{Chunk, Op, TraceInfo, Value};
use crate::gc::{concat_string, GcHandle, ObjectType}; use crate::gc::{concat_string, GcHandle, ObjectType};
use std::collections::LinkedList; use std::collections::LinkedList;
use std::io;
use std::rc::Rc; use std::rc::Rc;
pub struct VM { pub struct VM {
@@ -56,7 +57,18 @@ impl VM {
.ok_or(self.type_err("Number", top_of_stack)) .ok_or(self.type_err("Number", top_of_stack))
} }
pub fn run(&mut self, chunk: &Chunk) -> Result<Option<(Value, LinkedList<GcHandle>)>, VMError> { pub fn stdrun(
&mut self,
chunk: &Chunk,
) -> Result<Option<(Value, LinkedList<GcHandle>)>, VMError> {
return self.run(chunk, &mut io::stdout());
}
pub fn run<Output: io::Write>(
&mut self,
chunk: &Chunk,
output: &mut Output,
) -> Result<Option<(Value, LinkedList<GcHandle>)>, VMError> {
let mut allocations: LinkedList<GcHandle> = LinkedList::new(); let mut allocations: LinkedList<GcHandle> = LinkedList::new();
while self.pc < chunk.code.len() { while self.pc < chunk.code.len() {
@@ -106,31 +118,28 @@ impl VM {
let a = self.pop_num()?; let a = self.pop_num()?;
self.push(Value::from(num + a)); self.push(Value::from(num + a));
} }
Value::Obj(b) => { Value::Obj(b) => match b.get_otype() {
match b.get_otype() {
ObjectType::String => { ObjectType::String => {
let a = self.pop()?; let a = self.pop()?;
match a { match a {
Value::Obj(a) => { Value::Obj(a) => match a.get_otype() {
match a.get_otype() {
ObjectType::String => { ObjectType::String => {
let new_obj = unsafe { let new_obj = unsafe { concat_string(a, b).unwrap() };
concat_string(a, b).unwrap()
};
self.push(Value::from(new_obj.get_object())); self.push(Value::from(new_obj.get_object()));
allocations.push_front(new_obj); allocations.push_front(new_obj);
Ok(()) Ok(())
}
}, },
_ => Err(self.type_err("String", a)),
}?
} }
}, },
_ => { _ => {
Err(self.type_err("String", a)) return Err(VMError::Runtime(
"Operands of + need to be numbers or strings".into(),
self.pc,
))
} }
}?
},
}
}
_ => return Err(VMError::Runtime("Operands of + need to be numbers or strings".into(), self.pc))
}; };
} }
Op::Subtract | Op::Multiply | Op::Divide => { Op::Subtract | Op::Multiply | Op::Divide => {
@@ -160,15 +169,21 @@ impl VM {
let r = a == b; let r = a == b;
self.push(r.into()) self.push(r.into())
} }
Op::Print => {
let value = self.pop()?;
writeln!(output, "{}", value)
.map_err(|_| VMError::Runtime("Failed to print".into(), self.pc))?
}
} }
} }
match self.stack.pop() { match self.stack.pop() {
None => Ok(None), None => Ok(None),
Some(result_value) => { Some(result_value) => {
let escaping_allocs = allocations.into_iter().filter( let escaping_allocs = allocations
|handle| Value::from(handle.get_object()) == result_value .into_iter()
).collect(); .filter(|handle| Value::from(handle.get_object()) == result_value)
.collect();
Ok(Some((result_value, escaping_allocs))) Ok(Some((result_value, escaping_allocs)))
} }
@@ -211,7 +226,7 @@ mod tests {
); );
let mut vm = VM::new(); let mut vm = VM::new();
let (result, allocs) = vm.run(&chunk).unwrap().unwrap(); let (result, allocs) = vm.stdrun(&chunk).unwrap().unwrap();
assert_eq!(result, Value::from(3.1416)); assert_eq!(result, Value::from(3.1416));
assert!(vm.stack.is_empty()); assert!(vm.stack.is_empty());
@@ -220,16 +235,11 @@ mod tests {
#[test] #[test]
fn nil_error() { fn nil_error() {
let chunk = Chunk::new_with( let chunk = Chunk::new_with(vec![Op::Nil, Op::Negate], vec![], vec![], LinkedList::new());
vec![Op::Nil, Op::Negate],
vec![],
vec![],
LinkedList::new(),
);
let mut vm = VM::new(); let mut vm = VM::new();
assert_eq!( assert_eq!(
vm.run(&chunk).unwrap_err(), vm.stdrun(&chunk).unwrap_err(),
vm.type_err("Number", Value::Nil) vm.type_err("Number", Value::Nil)
); );
} }
@@ -243,7 +253,7 @@ mod tests {
LinkedList::new(), LinkedList::new(),
); );
let mut vm = VM::new(); let mut vm = VM::new();
let (result, allocs) = vm.run(&chunk)?.unwrap(); let (result, allocs) = vm.stdrun(&chunk)?.unwrap();
assert_eq!(result, true.into()); assert_eq!(result, true.into());
assert!(vm.stack.is_empty()); assert!(vm.stack.is_empty());
@@ -254,14 +264,9 @@ mod tests {
#[test] #[test]
fn not_nil_is_true() { fn not_nil_is_true() {
let chunk = Chunk::new_with( let chunk = Chunk::new_with(vec![Op::Nil, Op::Not], vec![], vec![], LinkedList::new());
vec![Op::Nil, Op::Not],
vec![],
vec![],
LinkedList::new(),
);
let mut vm = VM::new(); let mut vm = VM::new();
let (result, allocs) = vm.run(&chunk).unwrap().unwrap(); let (result, allocs) = vm.stdrun(&chunk).unwrap().unwrap();
assert_eq!(result, true.into()); assert_eq!(result, true.into());
assert!(vm.stack.is_empty()); assert!(vm.stack.is_empty());