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
|
run: cargo test util
|
||||||
- name: test garbage collection
|
- name: test garbage collection
|
||||||
run: cargo test heap
|
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"
|
name = "push"
|
||||||
args = ["operand"]
|
args = ["operand"]
|
||||||
output = ""
|
output = ""
|
||||||
description = "pushes operand onto stack."
|
description = "pushes deep copy of operand onto stack."
|
||||||
|
|
||||||
[[instructions]]
|
[[instructions]]
|
||||||
name = "pop"
|
name = "pop"
|
||||||
|
|
@ -50,7 +50,7 @@ output = ""
|
||||||
description = "delete current stack frame"
|
description = "delete current stack frame"
|
||||||
|
|
||||||
[[instructions]]
|
[[instructions]]
|
||||||
name = "load"
|
name = "link"
|
||||||
args = ["src", "dest"]
|
args = ["src", "dest"]
|
||||||
output = ""
|
output = ""
|
||||||
description = "shallow copies src into dest"
|
description = "shallow copies src into dest"
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use core::ptr::NonNull;
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
use alloc::fmt::Debug;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
|
||||||
use organelle::Number;
|
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> {
|
impl<T> Drop for Gc<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -132,7 +142,7 @@ impl<T: Clone> Gc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum Datum {
|
pub enum Datum {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
Bool(bool),
|
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>>);
|
pub struct Cons(pub Option<Gc<Datum>>, pub Option<Gc<Datum>>);
|
||||||
|
|
||||||
impl Cons {
|
impl Cons {
|
||||||
|
|
|
||||||
|
|
@ -131,10 +131,10 @@ impl<T: Debug> Debug for StackStack<T> {
|
||||||
let mut ss_idx = 1;
|
let mut ss_idx = 1;
|
||||||
let mut ss_cur = &*self.0;
|
let mut ss_cur = &*self.0;
|
||||||
while let Some(inner) = ss_cur {
|
while let Some(inner) = ss_cur {
|
||||||
write!(f, "Frame {ss_idx}:")?;
|
write!(f, "Frame {ss_idx}:\n")?;
|
||||||
let mut s_cur = &*inner.stack.0;
|
let mut s_cur = &*inner.stack.0;
|
||||||
while let Some(node) = s_cur {
|
while let Some(node) = s_cur {
|
||||||
write!(f, " {:#?}", node.data)?;
|
write!(f, " > {:?}\n", node.data)?;
|
||||||
s_cur = &*node.next.0;
|
s_cur = &*node.next.0;
|
||||||
}
|
}
|
||||||
write!(f, "\n")?;
|
write!(f, "\n")?;
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -258,23 +258,23 @@ mod tests {
|
||||||
assert_eq!(oe_bytes.unwrap(), vec![instr::TRAP.0, 0xf3]);
|
assert_eq!(oe_bytes.unwrap(), vec![instr::TRAP.0, 0xf3]);
|
||||||
|
|
||||||
let two_operands =
|
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());
|
assert!(two_operands.is_ok());
|
||||||
let two_oper = two_operands.unwrap();
|
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);
|
assert_eq!(two_oper.1.len(), 2);
|
||||||
let two_bytes =
|
let two_bytes =
|
||||||
TryInto::<Vec<u8>>::try_into(two_oper.clone());
|
TryInto::<Vec<u8>>::try_into(two_oper.clone());
|
||||||
assert!(two_bytes.is_ok());
|
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[0], Operand(Address::Oper1, 0));
|
||||||
assert_eq!(two_oper.1[1], Operand(Address::Oper2, 0));
|
assert_eq!(two_oper.1[1], Operand(Address::Oper2, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_program_parse() {
|
fn test_program_parse() {
|
||||||
let bytes1 = [instr::LOAD.0, 0xf3, 0xf4];
|
let bytes1 = [instr::LINK.0, 0xf3, 0xf4];
|
||||||
let out1 = vec![Instruction(instr::LOAD,
|
let out1 = vec![Instruction(instr::LINK,
|
||||||
vec![Operand(Address::Oper1, 0), Operand(Address::Oper2, 0)])];
|
vec![Operand(Address::Oper1, 0), Operand(Address::Oper2, 0)])];
|
||||||
let res1 =
|
let res1 =
|
||||||
TryInto::<Program>::try_into(&bytes1[..]);
|
TryInto::<Program>::try_into(&bytes1[..]);
|
||||||
|
|
@ -282,11 +282,11 @@ mod tests {
|
||||||
assert_eq!(res1.unwrap().0, out1);
|
assert_eq!(res1.unwrap().0, out1);
|
||||||
|
|
||||||
let bytes2 = [
|
let bytes2 = [
|
||||||
instr::LOAD.0, 0xf3, 0xf4,
|
instr::LINK.0, 0xf3, 0xf4,
|
||||||
instr::CLEAR.0, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
instr::CLEAR.0, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
];
|
];
|
||||||
let out2 = vec![
|
let out2 = vec![
|
||||||
Instruction(instr::LOAD, vec![
|
Instruction(instr::LINK, vec![
|
||||||
Operand(Address::Oper1, 0),
|
Operand(Address::Oper1, 0),
|
||||||
Operand(Address::Oper2, 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 {
|
impl From<Program> for VM {
|
||||||
fn from(value: Program) -> Self {
|
fn from(value: Program) -> Self {
|
||||||
VM{
|
VM{
|
||||||
stack: StackStack::new(),
|
stack: StackStack::<Gc<Datum>>::new(),
|
||||||
symtab: QuickMap::new(),
|
symtab: QuickMap::new(),
|
||||||
prog: value,
|
prog: value,
|
||||||
traps: vec![],
|
traps: vec![],
|
||||||
|
|
@ -143,8 +143,6 @@ impl VM {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn execute_instruction(&mut self) {
|
fn execute_instruction(&mut self) {
|
||||||
let instr = &self.prog.0[self.ictr].clone();
|
|
||||||
|
|
||||||
macro_rules! e {
|
macro_rules! e {
|
||||||
( $err:expr ) => {
|
( $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
|
// get or set according to addressing mode
|
||||||
macro_rules! access {
|
macro_rules! access {
|
||||||
( $oper:expr ) => {
|
( $oper:expr ) => {
|
||||||
|
|
@ -193,7 +196,7 @@ impl VM {
|
||||||
e!("out of bounds jump caught");
|
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");
|
e!("illegal argument to BOUND instruction");
|
||||||
};
|
};
|
||||||
let tag = unsafe { str::from_utf8_unchecked(tag) };
|
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
|
// stack ops
|
||||||
i::PUSH => self.stack.push_current_stack(
|
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::POP => _ = self.stack.pop_current_stack(),
|
||||||
i::ENTER => self.stack.add_stack(),
|
i::ENTER => self.stack.add_stack(),
|
||||||
i::EXIT => self.stack.destroy_top_stack(),
|
i::EXIT => self.stack.destroy_top_stack(),
|
||||||
|
|
||||||
// movement ops
|
// 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::DUPL => access!(&instr.1[1], access!(&instr.1[0]).deep_copy()),
|
||||||
i::CLEAR => access!(&instr.1[0], Datum::None.into()),
|
i::CLEAR => access!(&instr.1[0], Datum::None.into()),
|
||||||
|
|
||||||
|
|
@ -368,7 +371,7 @@ impl VM {
|
||||||
if let Datum::Char(schr) = **access!(&instr.1[0]) {
|
if let Datum::Char(schr) = **access!(&instr.1[0]) {
|
||||||
Datum::Number(Number::Fra(Fraction(schr as isize, 1))).into()
|
Datum::Number(Number::Fra(Fraction(schr as isize, 1))).into()
|
||||||
} else {
|
} 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()
|
Datum::Char(n.0.trunc() as u64 as u8).into()
|
||||||
} else {
|
} 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], {
|
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");
|
e!("illegal argument to CONST instruction");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -588,123 +591,14 @@ 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
|
use organelle::Float;
|
||||||
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 {
|
||||||
|
|
@ -723,16 +617,28 @@ mod tests {
|
||||||
|
|
||||||
impl TestResult {
|
impl TestResult {
|
||||||
fn chk_expr(&self, state: &VM) -> bool {
|
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 {
|
fn chk_err(&self, state: &VM) -> bool {
|
||||||
if let Datum::String(ref msg) = *state.errr {
|
if let Datum::String(ref msg) = *state.errr {
|
||||||
let msg = unsafe { str::from_utf8_unchecked(msg) };
|
let msg = unsafe { str::from_utf8_unchecked(msg) };
|
||||||
msg == self.errr.unwrap()
|
if msg == self.errr.unwrap() {
|
||||||
} else if let Datum::None = *state.errr {
|
true
|
||||||
self.errr.is_none()
|
|
||||||
} else {
|
} 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
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -740,6 +646,9 @@ 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);
|
||||||
|
print!("instead has {:?}\n", state.stack[i.0].deref());
|
||||||
|
print!("\nfull stack:\n{:#?}\n", state.stack);
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -749,33 +658,492 @@ mod tests {
|
||||||
fn chk_syms(&self, state: &VM) -> bool {
|
fn chk_syms(&self, state: &VM) -> bool {
|
||||||
for i in &self.syms {
|
for i in &self.syms {
|
||||||
if !has_sym(&state, i.0, i.1.clone()) {
|
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
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
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.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))
|
||||||
(self.errr.is_none() || self.chk_err(&state))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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, 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