WIP ISA Unit tests
Some checks failed
per-push tests / build (push) Successful in 2m55s
per-push tests / test-utility (push) Failing after 1m29s
per-push tests / test-frontend (push) Failing after 1m31s
per-push tests / timed-decomposer-parse (push) Has been skipped
per-push tests / test-backend (push) Failing after 1m4s

Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
Ava Apples Affine 2025-07-30 00:01:07 +00:00
parent 609e65a8db
commit 034913e40e
2 changed files with 252 additions and 128 deletions

View file

@ -278,4 +278,12 @@ mod tests {
assert_eq!(g[1], 1); assert_eq!(g[1], 1);
assert_eq!(g[2], 0); 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);
}
} }

View file

@ -62,7 +62,11 @@ pub struct VM {
impl From<Program> for VM { impl From<Program> for VM {
fn from(value: Program) -> Self { fn from(value: Program) -> Self {
VM{ VM{
stack: StackStack::new(), stack: {
let mut s = StackStack::new();
s.add_stack();
s
},
symtab: QuickMap::new(), symtab: QuickMap::new(),
prog: value, prog: value,
traps: vec![], traps: vec![],
@ -588,123 +592,13 @@ impl VM {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::array;
use super::*; use super::*;
use crate::instr; use crate::instr as i;
use crate::util::{Program, Instruction, Operand};
const ISA_TESTS: [Option<Vec<(VM, TestResult)>>; instr::TOTAL_INSTRUCTIONS] = [ use core::ops::Deref;
// 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,
];
fn has_stack(state: &VM, idx: usize, target: Gc<Datum>) -> bool { 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 { fn has_sym(state: &VM, sym: &str, operand: Option<Operand>) -> bool {
@ -740,6 +634,7 @@ mod tests {
fn chk_stack(&self, state: &VM) -> bool { fn chk_stack(&self, state: &VM) -> bool {
for i in &self.stack { for i in &self.stack {
if !has_stack(&state, i.0, i.1.clone()) { if !has_stack(&state, i.0, i.1.clone()) {
print!("expected stack to have {:?} at {}\n", *i.1, i.0);
return false return false
} }
} }
@ -755,7 +650,7 @@ mod tests {
true true
} }
fn test_passes(&self, state: VM) -> bool { fn test_passes(&self, state: &VM) -> bool {
(self.expr.is_none() || self.chk_expr(&state)) && (self.expr.is_none() || self.chk_expr(&state)) &&
(self.stack.is_empty() || self.chk_stack(&state)) && (self.stack.is_empty() || self.chk_stack(&state)) &&
(self.syms.is_empty() || self.chk_syms(&state)) && (self.syms.is_empty() || self.chk_syms(&state)) &&
@ -764,18 +659,239 @@ mod tests {
} }
#[test] #[test]
fn run_isa_tests() { fn isa_trap_tests() {
for i in &ISA_TESTS.to_vec() { let mut vm = VM::from(Program(vec![
let Some(test_case) = i else { Instruction(i::TRAP, vec![Operand(Address::Numer, 0)])
assert!(false); // dont let untested instructions happen ])).with_traps(Some(vec![
return 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(); 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, 0),
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![
(0, Datum::String("test".into()).into()),
(1, Datum::String("value".into()).into()),
],
syms: vec![("test", Some(Operand(Address::Stack, 1)))],
errr: None,
};
vm.run_program();
assert!(case.test_passes(&vm));
let mut vm = VM::from(Program(vec![
Instruction(i::BIND, vec![Operand(Address::Stack, 0),
Operand(Address::Stack, 1)]),
Instruction(i::BOUND, vec![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: Some(Datum::Bool(true).into()),
stack: vec![
(0, Datum::String("test".into()).into()),
(1, Datum::String("value".into()).into()),
],
syms: vec![("test", Some(Operand(Address::Stack, 1)))],
errr: None,
};
vm.run_program();
assert!(case.test_passes(&vm));
let mut vm = VM::from(Program(vec![
Instruction(i::BIND, vec![Operand(Address::Stack, 0),
Operand(Address::Stack, 1)]),
Instruction(i::UNBIND, vec![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();
TestResult{
expr: None,
stack: vec![
(0, Datum::String("test".into()).into()),
(1, 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![
(0, 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_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));
} }
} }