Early 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
This commit provides some but not all unit tests for VM instructions. All tests pass, and this commit includes modifications to logic to allow for that. Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
609e65a8db
commit
3bc256dab2
6 changed files with 544 additions and 156 deletions
|
|
@ -79,4 +79,6 @@ jobs:
|
|||
run: cargo test util
|
||||
- name: test garbage collection
|
||||
run: cargo test heap
|
||||
- name: test instruction set implementation
|
||||
run: cargo test isa_
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
]),
|
||||
|
|
|
|||
650
hyphae/src/vm.rs
650
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();
|
||||
|
||||
let case = TestResult{
|
||||
expr: Some(Datum::Bool(true).into()),
|
||||
stack: vec![],
|
||||
syms: vec![],
|
||||
errr: None,
|
||||
};
|
||||
|
||||
for i in test_case {
|
||||
let (mut vm, result) = (i.0.clone(), i.1.clone());
|
||||
vm.run_program();
|
||||
assert!(result.test_passes(vm));
|
||||
}
|
||||
}
|
||||
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