DEV: Unit test verifying we can implement a select expression.

* Also unify our jumps as relative jumps
This commit is contained in:
Jeremy Wall 2019-07-09 19:40:13 -05:00
parent aac263be2c
commit 996d8821d7
2 changed files with 59 additions and 17 deletions

View File

@ -99,15 +99,17 @@ pub enum Op {
Cp, Cp,
// Control Flow // Control Flow
Bang, Bang,
Jump(usize), Jump(i32),
JumpIfTrue(usize), JumpIfTrue(i32),
JumpIfFalse(usize), JumpIfFalse(i32),
JumpIfNotEqual(i32),
// FIXME(jwall): Short circuiting operations // FIXME(jwall): Short circuiting operations
// - And(usize) // - And(usize)
// - Or(usize) // - Or(usize)
// Spacer operation, Does nothing. // Spacer operation, Does nothing.
Noop, Noop,
// Pending Computation // Pending Computation
// TODO(jwall): This is unnecessary. Remove it.
InitThunk(usize), InitThunk(usize),
Module(usize), Module(usize),
Func(usize), Func(usize),
@ -153,8 +155,8 @@ impl<'a> VM<'a> {
pub fn run(&mut self) -> Result<(), Error> { pub fn run(&mut self) -> Result<(), Error> {
while self.ops.next().is_some() { while self.ops.next().is_some() {
let idx = self.ops.ptr.unwrap(); let idx = self.ops.ptr.unwrap();
match self.ops.op().unwrap() { match dbg!(self.ops.op()).unwrap() {
Op::Val(p) => self.push(P(p.clone()))?, Op::Val(p) => self.push(dbg!(P(p.clone())))?,
Op::Sym(s) => self.push(S(s.clone()))?, Op::Sym(s) => self.push(S(s.clone()))?,
Op::DeRef(s) => self.op_deref(s.clone())?, Op::DeRef(s) => self.op_deref(s.clone())?,
Op::Add => self.op_add()?, Op::Add => self.op_add()?,
@ -174,15 +176,17 @@ impl<'a> VM<'a> {
Op::Field => self.op_field()?, Op::Field => self.op_field()?,
Op::Element => self.op_element()?, Op::Element => self.op_element()?,
Op::Cp => self.op_copy()?, Op::Cp => self.op_copy()?,
//TODO(jwall): Should this whould take a user provided message? //TODO(jwall): Should this take a user provided message?
Op::Bang => return Err(Error {}), Op::Bang => return dbg!(Err(Error {})),
Op::InitThunk(jp) => self.op_thunk(idx, *jp)?, // TODO(jwall): Remove this
Op::InitThunk(jp) => self.op_thunk(idx, *jp as i32)?,
Op::Noop => { Op::Noop => {
// Do nothing // Do nothing
} }
Op::Jump(jp) => self.op_jump(*jp)?, Op::Jump(jp) => self.op_jump(*jp)?,
Op::JumpIfTrue(jp) => self.op_jump_if_true(*jp)?, Op::JumpIfTrue(jp) => self.op_jump_if_true(*jp)?,
Op::JumpIfFalse(jp) => self.op_jump_if_false(*jp)?, Op::JumpIfFalse(jp) => self.op_jump_if_false(*jp)?,
Op::JumpIfNotEqual(jp) => self.op_jump_if_not_equal(*jp)?,
Op::Module(mptr) => self.op_module(idx, *mptr)?, Op::Module(mptr) => self.op_module(idx, *mptr)?,
Op::Func(jptr) => self.op_func(idx, *jptr)?, Op::Func(jptr) => self.op_func(idx, *jptr)?,
Op::FCall => self.op_fcall()?, Op::FCall => self.op_fcall()?,
@ -200,12 +204,17 @@ impl<'a> VM<'a> {
self.push(val) self.push(val)
} }
fn op_jump(&mut self, jp: usize) -> Result<(), Error> { fn op_jump(&mut self, jp: i32) -> Result<(), Error> {
self.ops.jump(self.ops.ptr.map(|v| v + jp).unwrap_or(jp))?; self.ops.jump(
self.ops
.ptr
.map(|v| (v as i32 + jp) as usize)
.unwrap_or(jp as usize),
)?;
Ok(()) Ok(())
} }
fn op_jump_if_true(&mut self, jp: usize) -> Result<(), Error> { fn op_jump_if_true(&mut self, jp: i32) -> Result<(), Error> {
if let P(Bool(cond)) = self.pop()? { if let P(Bool(cond)) = self.pop()? {
if cond { if cond {
self.op_jump(jp)?; self.op_jump(jp)?;
@ -214,7 +223,7 @@ impl<'a> VM<'a> {
Ok(()) Ok(())
} }
fn op_jump_if_false(&mut self, jp: usize) -> Result<(), Error> { fn op_jump_if_false(&mut self, jp: i32) -> Result<(), Error> {
if let P(Bool(cond)) = self.pop()? { if let P(Bool(cond)) = self.pop()? {
if !cond { if !cond {
self.op_jump(jp)?; self.op_jump(jp)?;
@ -223,6 +232,21 @@ impl<'a> VM<'a> {
Ok(()) Ok(())
} }
fn op_jump_if_not_equal(&mut self, jp: i32) -> Result<(), Error> {
// pop field value off
let field_name = dbg!(self.pop())?;
// pop search value off
let search = dbg!(self.pop())?;
// compare them.
if dbg!(field_name != search) {
self.op_jump(dbg!(jp))?;
self.push(dbg!(search))?;
}
dbg!(self.ops.ptr.unwrap());
// if they aren't equal then push search value back on and jump
Ok(())
}
fn op_module(&mut self, idx: usize, jptr: usize) -> Result<(), Error> { fn op_module(&mut self, idx: usize, jptr: usize) -> Result<(), Error> {
let (result_ptr, flds) = match self.pop()? { let (result_ptr, flds) = match self.pop()? {
C(Tuple(flds)) => (None, flds), C(Tuple(flds)) => (None, flds),
@ -300,7 +324,7 @@ impl<'a> VM<'a> {
Ok(()) Ok(())
} }
fn op_thunk(&mut self, idx: usize, jp: usize) -> Result<(), Error> { fn op_thunk(&mut self, idx: usize, jp: i32) -> Result<(), Error> {
self.push(dbg!(T(idx)))?; self.push(dbg!(T(idx)))?;
self.op_jump(jp) self.op_jump(jp)
} }

View File

@ -15,8 +15,9 @@
use super::scope::Stack; use super::scope::Stack;
use super::Composite::{List, Tuple}; use super::Composite::{List, Tuple};
use super::Op::{ use super::Op::{
Add, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, InitList, InitThunk, InitTuple, Add, Bang, Bind, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, InitList, InitThunk,
Jump, JumpIfFalse, JumpIfTrue, Module, Mul, Noop, Return, Sub, Sym, Val, InitTuple, Jump, JumpIfFalse, JumpIfNotEqual, JumpIfTrue, Module, Mul, Noop, Return, Sub, Sym,
Val,
}; };
use super::Primitive::{Bool, Float, Int, Str}; use super::Primitive::{Bool, Float, Int, Str};
use super::Value::{C, P, T}; use super::Value::{C, P, T};
@ -27,7 +28,7 @@ macro_rules! assert_cases {
for case in $cases.drain(0..) { for case in $cases.drain(0..) {
let mut vm = VM::new(&case.0); let mut vm = VM::new(&case.0);
vm.run().unwrap(); vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1); assert_eq!(dbg!(vm.pop()).unwrap(), case.1);
} }
}; };
@ -186,7 +187,6 @@ fn test_tuple_ops() {
#[test] #[test]
fn test_jump_ops() { fn test_jump_ops() {
assert_cases!( assert_cases!(
vec![InitThunk(1), Val(Int(1)), Noop] => T(0),
vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)), vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)),
); );
} }
@ -400,6 +400,24 @@ fn test_module_call() {
]; ];
} }
#[test]
fn test_select_short_circuit() {
assert_cases![
vec![
Sym("field".to_owned()), // 0 // search field
Sym("not_field".to_owned()), // 1 // first field to compare
JumpIfNotEqual(2), // 2
Val(Str("not our value".to_owned())), // 3
Jump(4), // 4
Sym("field".to_owned()), // 5 // second field to compare
JumpIfNotEqual(2), // 6
Val(Int(1)), // 7
Jump(1), // 8
Bang, // 9
] => P(Int(1)),
];
}
#[test] #[test]
fn test_scope_stacks() { fn test_scope_stacks() {
let mut stack = Stack::new(); let mut stack = Stack::new();