WIP ISA Unit tests
Some checks failed
per-push tests / test-frontend (push) Blocked by required conditions
per-push tests / timed-decomposer-parse (push) Blocked by required conditions
per-push tests / test-utility (push) Blocked by required conditions
per-push tests / test-backend (push) Blocked by required conditions
per-push tests / build (push) Has been cancelled
Some checks failed
per-push tests / test-frontend (push) Blocked by required conditions
per-push tests / timed-decomposer-parse (push) Blocked by required conditions
per-push tests / test-utility (push) Blocked by required conditions
per-push tests / test-backend (push) Blocked by required conditions
per-push tests / build (push) Has been cancelled
Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
609e65a8db
commit
3a06e40e9d
6 changed files with 544 additions and 156 deletions
|
|
@ -29,7 +29,7 @@ description = "test if a name is already bound"
|
|||
name = "push"
|
||||
args = ["operand"]
|
||||
output = ""
|
||||
description = "pushes operand onto stack."
|
||||
description = "pushes deep copy of operand onto stack."
|
||||
|
||||
[[instructions]]
|
||||
name = "pop"
|
||||
|
|
@ -50,7 +50,7 @@ output = ""
|
|||
description = "delete current stack frame"
|
||||
|
||||
[[instructions]]
|
||||
name = "load"
|
||||
name = "link"
|
||||
args = ["src", "dest"]
|
||||
output = ""
|
||||
description = "shallow copies src into dest"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use core::ptr::NonNull;
|
|||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::fmt::Debug;
|
||||
use alloc::string::String;
|
||||
|
||||
use organelle::Number;
|
||||
|
|
@ -112,6 +113,15 @@ impl<T> Clone for Gc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Gc<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let refs = Rc::strong_count(unsafe { self.0.as_ref() });
|
||||
write!(f, "<refs={refs}>: ")?;
|
||||
write!(f, "{:?}", (*(unsafe { self.0.as_ref() })).as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Gc<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
|
@ -132,7 +142,7 @@ impl<T: Clone> Gc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Datum {
|
||||
Number(Number),
|
||||
Bool(bool),
|
||||
|
|
@ -168,7 +178,7 @@ impl Clone for Datum {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Cons(pub Option<Gc<Datum>>, pub Option<Gc<Datum>>);
|
||||
|
||||
impl Cons {
|
||||
|
|
|
|||
|
|
@ -131,10 +131,10 @@ impl<T: Debug> Debug for StackStack<T> {
|
|||
let mut ss_idx = 1;
|
||||
let mut ss_cur = &*self.0;
|
||||
while let Some(inner) = ss_cur {
|
||||
write!(f, "Frame {ss_idx}:")?;
|
||||
write!(f, "Frame {ss_idx}:\n")?;
|
||||
let mut s_cur = &*inner.stack.0;
|
||||
while let Some(node) = s_cur {
|
||||
write!(f, " {:#?}", node.data)?;
|
||||
write!(f, " > {:?}\n", node.data)?;
|
||||
s_cur = &*node.next.0;
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
|
|
@ -278,4 +278,12 @@ mod tests {
|
|||
assert_eq!(g[1], 1);
|
||||
assert_eq!(g[2], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stack_idx_zero() {
|
||||
let mut g = StackStack::<i8>::new();
|
||||
g.add_stack();
|
||||
g.push_current_stack(2);
|
||||
assert_eq!(g[0], 2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,23 +258,23 @@ mod tests {
|
|||
assert_eq!(oe_bytes.unwrap(), vec![instr::TRAP.0, 0xf3]);
|
||||
|
||||
let two_operands =
|
||||
TryInto::<Instruction>::try_into(&[instr::LOAD.0, 0xf3, 0xf4][..]);
|
||||
TryInto::<Instruction>::try_into(&[instr::LINK.0, 0xf3, 0xf4][..]);
|
||||
assert!(two_operands.is_ok());
|
||||
let two_oper = two_operands.unwrap();
|
||||
assert_eq!(two_oper.0, instr::LOAD);
|
||||
assert_eq!(two_oper.0, instr::LINK);
|
||||
assert_eq!(two_oper.1.len(), 2);
|
||||
let two_bytes =
|
||||
TryInto::<Vec<u8>>::try_into(two_oper.clone());
|
||||
assert!(two_bytes.is_ok());
|
||||
assert_eq!(two_bytes.unwrap(), vec![instr::LOAD.0, 0xf3, 0xf4]);
|
||||
assert_eq!(two_bytes.unwrap(), vec![instr::LINK.0, 0xf3, 0xf4]);
|
||||
assert_eq!(two_oper.1[0], Operand(Address::Oper1, 0));
|
||||
assert_eq!(two_oper.1[1], Operand(Address::Oper2, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_program_parse() {
|
||||
let bytes1 = [instr::LOAD.0, 0xf3, 0xf4];
|
||||
let out1 = vec![Instruction(instr::LOAD,
|
||||
let bytes1 = [instr::LINK.0, 0xf3, 0xf4];
|
||||
let out1 = vec![Instruction(instr::LINK,
|
||||
vec![Operand(Address::Oper1, 0), Operand(Address::Oper2, 0)])];
|
||||
let res1 =
|
||||
TryInto::<Program>::try_into(&bytes1[..]);
|
||||
|
|
@ -282,11 +282,11 @@ mod tests {
|
|||
assert_eq!(res1.unwrap().0, out1);
|
||||
|
||||
let bytes2 = [
|
||||
instr::LOAD.0, 0xf3, 0xf4,
|
||||
instr::LINK.0, 0xf3, 0xf4,
|
||||
instr::CLEAR.0, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
let out2 = vec![
|
||||
Instruction(instr::LOAD, vec![
|
||||
Instruction(instr::LINK, vec![
|
||||
Operand(Address::Oper1, 0),
|
||||
Operand(Address::Oper2, 0)
|
||||
]),
|
||||
|
|
|
|||
654
hyphae/src/vm.rs
654
hyphae/src/vm.rs
|
|
@ -62,7 +62,7 @@ pub struct VM {
|
|||
impl From<Program> for VM {
|
||||
fn from(value: Program) -> Self {
|
||||
VM{
|
||||
stack: StackStack::new(),
|
||||
stack: StackStack::<Gc<Datum>>::new(),
|
||||
symtab: QuickMap::new(),
|
||||
prog: value,
|
||||
traps: vec![],
|
||||
|
|
@ -143,8 +143,6 @@ impl VM {
|
|||
|
||||
#[inline(always)]
|
||||
fn execute_instruction(&mut self) {
|
||||
let instr = &self.prog.0[self.ictr].clone();
|
||||
|
||||
macro_rules! e {
|
||||
( $err:expr ) => {
|
||||
{
|
||||
|
|
@ -156,6 +154,11 @@ impl VM {
|
|||
}
|
||||
}
|
||||
|
||||
if self.ictr > self.prog.0.len() {
|
||||
e!("attempt to execute out of bounds instruction");
|
||||
}
|
||||
let instr = &self.prog.0[self.ictr].clone();
|
||||
|
||||
// get or set according to addressing mode
|
||||
macro_rules! access {
|
||||
( $oper:expr ) => {
|
||||
|
|
@ -193,7 +196,7 @@ impl VM {
|
|||
e!("out of bounds jump caught");
|
||||
}
|
||||
|
||||
self.ictr = target;
|
||||
self.ictr = target - 1; // will post increment in calling func
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,18 +247,18 @@ impl VM {
|
|||
e!("illegal argument to BOUND instruction");
|
||||
};
|
||||
let tag = unsafe { str::from_utf8_unchecked(tag) };
|
||||
self.symtab.contains_key(tag);
|
||||
self.expr = Datum::Bool(self.symtab.contains_key(tag)).into();
|
||||
},
|
||||
|
||||
// stack ops
|
||||
i::PUSH => self.stack.push_current_stack(
|
||||
access!(&instr.1[0]).clone()),
|
||||
access!(&instr.1[0]).deep_copy()),
|
||||
i::POP => _ = self.stack.pop_current_stack(),
|
||||
i::ENTER => self.stack.add_stack(),
|
||||
i::EXIT => self.stack.destroy_top_stack(),
|
||||
|
||||
// movement ops
|
||||
i::LOAD => access!(&instr.1[1], access!(&instr.1[0]).clone()),
|
||||
i::LINK => access!(&instr.1[1], access!(&instr.1[0]).clone()),
|
||||
i::DUPL => access!(&instr.1[1], access!(&instr.1[0]).deep_copy()),
|
||||
i::CLEAR => access!(&instr.1[0], Datum::None.into()),
|
||||
|
||||
|
|
@ -368,7 +371,7 @@ impl VM {
|
|||
if let Datum::Char(schr) = **access!(&instr.1[0]) {
|
||||
Datum::Number(Number::Fra(Fraction(schr as isize, 1))).into()
|
||||
} else {
|
||||
e!("illegal argument to INC instruction");
|
||||
e!("illegal argument to CTON instruction");
|
||||
}
|
||||
}),
|
||||
|
||||
|
|
@ -381,7 +384,7 @@ impl VM {
|
|||
}
|
||||
Datum::Char(n.0.trunc() as u64 as u8).into()
|
||||
} else {
|
||||
e!("illegal argument to INC instruction");
|
||||
e!("illegal argument to NTOC instruction");
|
||||
}
|
||||
}),
|
||||
|
||||
|
|
@ -402,7 +405,7 @@ impl VM {
|
|||
},
|
||||
|
||||
i::CONST => access!(&instr.1[0], {
|
||||
let Operand(Address::Numer, num) = instr.1[0] else {
|
||||
let Operand(Address::Numer, num) = instr.1[1] else {
|
||||
e!("illegal argument to CONST instruction");
|
||||
};
|
||||
|
||||
|
|
@ -588,123 +591,14 @@ impl VM {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::array;
|
||||
use super::*;
|
||||
use crate::instr;
|
||||
|
||||
const ISA_TESTS: [Option<Vec<(VM, TestResult)>>; instr::TOTAL_INSTRUCTIONS] = [
|
||||
// TRAP
|
||||
None,
|
||||
// BIND
|
||||
None,
|
||||
// UNBIND
|
||||
None,
|
||||
// BOUND
|
||||
None,
|
||||
// PUSH
|
||||
None,
|
||||
// POP
|
||||
None,
|
||||
// ENTER
|
||||
None,
|
||||
// EXIT
|
||||
None,
|
||||
// LOAD
|
||||
None,
|
||||
// DUPL
|
||||
None,
|
||||
// CLEAR
|
||||
None,
|
||||
// NOP
|
||||
None,
|
||||
// HALT
|
||||
None,
|
||||
// PANIC
|
||||
None,
|
||||
// JMP
|
||||
None,
|
||||
// JMPIF
|
||||
None,
|
||||
// EQ
|
||||
None,
|
||||
// LT
|
||||
None,
|
||||
// GT
|
||||
None,
|
||||
// LTE
|
||||
None,
|
||||
// GTE
|
||||
None,
|
||||
// BOOL_NOT
|
||||
None,
|
||||
// BOOL_AND
|
||||
None,
|
||||
// BOOL_OR
|
||||
None,
|
||||
// BYTE_AND
|
||||
None,
|
||||
// BYTE_OR
|
||||
None,
|
||||
// XOR
|
||||
None,
|
||||
// BYTE_NOT
|
||||
None,
|
||||
// ADD
|
||||
None,
|
||||
// SUB
|
||||
None,
|
||||
// MUL
|
||||
None,
|
||||
// FDIV
|
||||
None,
|
||||
// IDIV
|
||||
None,
|
||||
// POW
|
||||
None,
|
||||
// MODULO
|
||||
None,
|
||||
// REM
|
||||
None,
|
||||
// INC
|
||||
None,
|
||||
// DEC
|
||||
None,
|
||||
// CTON
|
||||
None,
|
||||
// NTOC
|
||||
None,
|
||||
// NTOI
|
||||
None,
|
||||
// NTOE
|
||||
None,
|
||||
// CONST
|
||||
None,
|
||||
// MKVEC
|
||||
None,
|
||||
// MKBVEC
|
||||
None,
|
||||
// INDEX
|
||||
None,
|
||||
// LENGTH
|
||||
None,
|
||||
// SUBSL
|
||||
None,
|
||||
// INSER
|
||||
None,
|
||||
// CONS
|
||||
None,
|
||||
// CAR
|
||||
None,
|
||||
// CDR
|
||||
None,
|
||||
// CONCAT
|
||||
None,
|
||||
// S_APPEND
|
||||
None,
|
||||
];
|
||||
use crate::instr as i;
|
||||
use crate::util::{Program, Instruction, Operand};
|
||||
use core::ops::Deref;
|
||||
use organelle::Float;
|
||||
|
||||
fn has_stack(state: &VM, idx: usize, target: Gc<Datum>) -> bool {
|
||||
*(state.stack[idx]) == *target
|
||||
state.stack[idx].deref() == target.deref()
|
||||
}
|
||||
|
||||
fn has_sym(state: &VM, sym: &str, operand: Option<Operand>) -> bool {
|
||||
|
|
@ -723,16 +617,28 @@ mod tests {
|
|||
|
||||
impl TestResult {
|
||||
fn chk_expr(&self, state: &VM) -> bool {
|
||||
self.expr.is_none() || *(state.expr) == *(self.expr.clone().unwrap())
|
||||
if *(state.expr) == *(self.expr.clone().unwrap()) {
|
||||
true
|
||||
} else {
|
||||
print!("expecting expr: {:?}\n", self.expr);
|
||||
print!("has expr: {:?}\n", state.expr);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn chk_err(&self, state: &VM) -> bool {
|
||||
if let Datum::String(ref msg) = *state.errr {
|
||||
let msg = unsafe { str::from_utf8_unchecked(msg) };
|
||||
msg == self.errr.unwrap()
|
||||
} else if let Datum::None = *state.errr {
|
||||
self.errr.is_none()
|
||||
if msg == self.errr.unwrap() {
|
||||
true
|
||||
} else {
|
||||
print!("expected error {:?}, got {:?}\n", self.errr, msg);
|
||||
false
|
||||
}
|
||||
} else if let Datum::None = *state.errr && self.errr.is_none(){
|
||||
true
|
||||
} else {
|
||||
print!("expected error {:?}, got {:?}\n", self.errr, state.errr);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
@ -740,6 +646,9 @@ mod tests {
|
|||
fn chk_stack(&self, state: &VM) -> bool {
|
||||
for i in &self.stack {
|
||||
if !has_stack(&state, i.0, i.1.clone()) {
|
||||
print!("expected stack to have {:?} at {}\n", *i.1, i.0);
|
||||
print!("instead has {:?}\n", state.stack[i.0].deref());
|
||||
print!("\nfull stack:\n{:#?}\n", state.stack);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -749,33 +658,492 @@ mod tests {
|
|||
fn chk_syms(&self, state: &VM) -> bool {
|
||||
for i in &self.syms {
|
||||
if !has_sym(&state, i.0, i.1.clone()) {
|
||||
print!("expected symbol {} == {:?}\n", i.0, i.1.clone());
|
||||
print!("instead has {:?}\n", state.symtab.get(&i.0.to_owned()));
|
||||
return false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn test_passes(&self, state: VM) -> bool {
|
||||
fn test_passes(&self, state: &VM) -> bool {
|
||||
self.chk_err(&state) &&
|
||||
(self.expr.is_none() || self.chk_expr(&state)) &&
|
||||
(self.stack.is_empty() || self.chk_stack(&state)) &&
|
||||
(self.syms.is_empty() || self.chk_syms(&state)) &&
|
||||
(self.errr.is_none() || self.chk_err(&state))
|
||||
(self.syms.is_empty() || self.chk_syms(&state))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_isa_tests() {
|
||||
for i in &ISA_TESTS.to_vec() {
|
||||
let Some(test_case) = i else {
|
||||
assert!(false); // dont let untested instructions happen
|
||||
return
|
||||
};
|
||||
fn isa_trap_tests() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::TRAP, vec![Operand(Address::Numer, 0)])
|
||||
])).with_traps(Some(vec![
|
||||
Arc::from(|state: &mut VM| {
|
||||
state.expr = Datum::Bool(true).into()
|
||||
})
|
||||
])).to_owned();
|
||||
|
||||
for i in test_case {
|
||||
let (mut vm, result) = (i.0.clone(), i.1.clone());
|
||||
vm.run_program();
|
||||
assert!(result.test_passes(vm));
|
||||
}
|
||||
}
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Bool(true).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_symtable_tests() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::BIND, vec![Operand(Address::Stack, 1),
|
||||
Operand(Address::Stack, 0)]),
|
||||
])).with_stack(Some({
|
||||
let mut i = StackStack::<Gc<Datum>>::new();
|
||||
i.push_current_stack(Datum::String("test".into()).into());
|
||||
i.push_current_stack(Datum::String("value".into()).into());
|
||||
i
|
||||
})).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(1, Datum::String("test".into()).into()),
|
||||
(0, Datum::String("value".into()).into()),
|
||||
],
|
||||
syms: vec![("test", Some(Operand(Address::Stack, 0)))],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::BIND, vec![Operand(Address::Stack, 1),
|
||||
Operand(Address::Stack, 0)]),
|
||||
Instruction(i::BOUND, vec![Operand(Address::Stack, 1)])
|
||||
])).with_stack(Some({
|
||||
let mut i = StackStack::<Gc<Datum>>::new();
|
||||
i.push_current_stack(Datum::String("test".into()).into());
|
||||
i.push_current_stack(Datum::String("value".into()).into());
|
||||
i
|
||||
})).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Bool(true).into()),
|
||||
stack: vec![
|
||||
(1, Datum::String("test".into()).into()),
|
||||
(0, Datum::String("value".into()).into()),
|
||||
],
|
||||
syms: vec![("test", Some(Operand(Address::Stack, 0)))],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::BIND, vec![Operand(Address::Stack, 1),
|
||||
Operand(Address::Stack, 0)]),
|
||||
Instruction(i::UNBIND, vec![Operand(Address::Stack, 1)])
|
||||
])).with_stack(Some({
|
||||
let mut i = StackStack::<Gc<Datum>>::new();
|
||||
i.push_current_stack(Datum::String("test".into()).into());
|
||||
i.push_current_stack(Datum::String("value".into()).into());
|
||||
i
|
||||
})).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(1, Datum::String("test".into()).into()),
|
||||
(0, Datum::String("value".into()).into()),
|
||||
],
|
||||
syms: vec![("test", None)],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_stack_tests() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)])
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(0, Datum::Number(Number::Fra(Fraction(4, 1))).into())
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 5)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::POP, vec![])
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(0, Datum::Number(Number::Fra(Fraction(4, 1))).into())
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::ENTER, vec![]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 5)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(1, Datum::Number(Number::Fra(Fraction(4, 1))).into()),
|
||||
(0, Datum::Number(Number::Fra(Fraction(5, 1))).into())
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::ENTER, vec![]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 5)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 5)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::EXIT, vec![]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(0, Datum::Number(Number::Fra(Fraction(4, 1))).into()),
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_load_dupl_clear() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::LINK, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Oper1, 0)]),
|
||||
Instruction(i::DUPL, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Oper2, 0)]),
|
||||
Instruction(i::INC, vec![Operand(Address::Oper2, 0)]),
|
||||
Instruction(i::CLEAR, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Oper1, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Oper2, 0)]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::None.into()),
|
||||
stack: vec![
|
||||
(1, Datum::Number(Number::Fra(Fraction(4, 1))).into()),
|
||||
(0, Datum::Number(Number::Fra(Fraction(5, 1))).into())
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_nop_halt_panic() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::NOP, vec![])
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::HALT, vec![]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Numer, 1)])
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::PANIC, vec![Operand(Address::Stack, 0)])
|
||||
])).with_stack({
|
||||
let mut i = StackStack::<Gc<Datum>>::new();
|
||||
i.push_current_stack(Datum::String("testerr".into()).into());
|
||||
Some(i)
|
||||
}).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: Some("testerr"),
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_inc_dec() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::INC, vec![Operand(Address::Expr, 0)]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Number(Number::Fra(Fraction(5, 1))).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 4)]),
|
||||
Instruction(i::DEC, vec![Operand(Address::Expr, 0)]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Number(Number::Fra(Fraction(3, 1))).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_jmp_jmpif() {
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::JMP, vec![Operand(Address::Instr, 2)]),
|
||||
Instruction(i::HALT, vec![]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 3)]),
|
||||
]));
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Number(Number::Fra(Fraction(3, 1))).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::LINK, vec![Operand(Address::Stack, 0),
|
||||
Operand(Address::Expr, 0)]),
|
||||
Instruction(i::JMPIF, vec![Operand(Address::Instr, 3)]),
|
||||
Instruction(i::HALT, vec![]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 3)]),
|
||||
])).with_stack({
|
||||
let mut i = StackStack::new();
|
||||
i.push_current_stack(Datum::Bool(true).into());
|
||||
Some(i)
|
||||
}).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Number(Number::Fra(Fraction(3, 1))).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::LINK, vec![Operand(Address::Stack, 0),
|
||||
Operand(Address::Expr, 0)]),
|
||||
Instruction(i::JMPIF, vec![Operand(Address::Instr, 3)]),
|
||||
Instruction(i::HALT, vec![]),
|
||||
Instruction(i::CONST, vec![Operand(Address::Expr, 0),
|
||||
Operand(Address::Numer, 3)]),
|
||||
])).with_stack({
|
||||
let mut i = StackStack::new();
|
||||
i.push_current_stack(Datum::Bool(false).into());
|
||||
Some(i)
|
||||
}).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Bool(false).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
Instruction(i::JMP, vec![Operand(Address::Instr, 300)]),
|
||||
])).with_stack({
|
||||
let mut i = StackStack::new();
|
||||
i.push_current_stack(Datum::Bool(false).into());
|
||||
Some(i)
|
||||
}).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: Some("out of bounds jump caught"),
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
print!("cur: {}\n", vm.ictr);
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_conversions() {
|
||||
/* program:
|
||||
* dupl %0, $ex
|
||||
* cton $ex
|
||||
* push $ex
|
||||
* ntoc $ex
|
||||
* push $ex
|
||||
* ntoi $ex
|
||||
* push $ex
|
||||
* ntoe $ex
|
||||
* push $ex
|
||||
*/
|
||||
|
||||
let mut vm = VM::from(Program(vec![
|
||||
// load from stack into expr
|
||||
Instruction(i::DUPL, vec![Operand(Address::Stack, 0),
|
||||
Operand(Address::Expr, 0)]),
|
||||
|
||||
// create a number and push to stack
|
||||
Instruction(i::CTON, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
|
||||
// convert to inexact and push to stack
|
||||
Instruction(i::NTOI, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
|
||||
// convert back to exact and push to stack
|
||||
Instruction(i::NTOE, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
|
||||
// create a char and push to stack
|
||||
Instruction(i::NTOC, vec![Operand(Address::Expr, 0)]),
|
||||
Instruction(i::PUSH, vec![Operand(Address::Expr, 0)]),
|
||||
])).with_stack({
|
||||
let mut i = StackStack::new();
|
||||
i.push_current_stack(Datum::Char(b'a').into());
|
||||
Some(i)
|
||||
}).to_owned();
|
||||
|
||||
let case = TestResult{
|
||||
expr: None,
|
||||
stack: vec![
|
||||
(0, Datum::Char(b'a').into()),
|
||||
(1, Datum::Number(Number::Fra(Fraction(97, 1))).into()),
|
||||
(2, Datum::Number(Number::Flt(Float(97.0))).into()),
|
||||
(3, Datum::Number(Number::Fra(Fraction(97, 1))).into()),
|
||||
(4, Datum::Char(b'a').into()),
|
||||
],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
vm.run_program();
|
||||
assert!(case.test_passes(&vm));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_index() {
|
||||
// TODO INDEX on V, BV, S, L
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_length() {
|
||||
// TODO LENGTH on V, BV, S, L
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_subsl() {
|
||||
// TODO SUBSL on V, BV, S, L
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_inser() {
|
||||
// TODO INSER on V, BV, S, L
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_cons_ops() {
|
||||
// TODO CAR and CDR and CONS
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn isa_string_ops() {
|
||||
// TODO CONCAT and S_APPEND
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue