WIP ISA Unit tests
All checks were successful
per-push tests / build (push) Successful in 2m15s
per-push tests / test-frontend (push) Successful in 2m27s
per-push tests / test-utility (push) Successful in 2m42s
per-push tests / test-backend (push) Successful in 2m13s
per-push tests / timed-decomposer-parse (push) Successful in 2m6s
All checks were successful
per-push tests / build (push) Successful in 2m15s
per-push tests / test-frontend (push) Successful in 2m27s
per-push tests / test-utility (push) Successful in 2m42s
per-push tests / test-backend (push) Successful in 2m13s
per-push tests / timed-decomposer-parse (push) Successful in 2m6s
Signed-off-by: Ava Affine <ava@sunnypup.io>
This commit is contained in:
parent
609e65a8db
commit
d00c7ea484
3 changed files with 413 additions and 134 deletions
|
|
@ -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,18 @@ impl<T> Clone for Gc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for Gc<T> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("GarbageCollected")
|
||||||
|
.field("refs", &unsafe {
|
||||||
|
Rc::strong_count(self.0.as_ref())
|
||||||
|
})
|
||||||
|
.field("inner",
|
||||||
|
(*(unsafe { self.0.as_ref() })).as_ref())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> Drop for Gc<T> {
|
impl<T> Drop for Gc<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -132,7 +145,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 +181,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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
514
hyphae/src/vm.rs
514
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 ) => {
|
||||||
|
|
@ -244,7 +247,7 @@ 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
|
||||||
|
|
@ -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,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 {
|
||||||
|
|
@ -723,6 +616,8 @@ mod tests {
|
||||||
|
|
||||||
impl TestResult {
|
impl TestResult {
|
||||||
fn chk_expr(&self, state: &VM) -> bool {
|
fn chk_expr(&self, state: &VM) -> bool {
|
||||||
|
print!("expecting expr: {:?}\n", self.expr);
|
||||||
|
print!("has expr: {:?}\n", state.expr.deref());
|
||||||
self.expr.is_none() || *(state.expr) == *(self.expr.clone().unwrap())
|
self.expr.is_none() || *(state.expr) == *(self.expr.clone().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -740,6 +635,8 @@ 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 {:?}", state.stack[i.0].deref());
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -749,13 +646,15 @@ 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.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 +663,377 @@ 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, 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::LOAD, 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::LOAD, 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::LOAD, 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::None.into()),
|
||||||
|
stack: vec![],
|
||||||
|
syms: vec![],
|
||||||
|
errr: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.run_program();
|
||||||
|
assert!(case.test_passes(&vm));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO NTOI, NTOE, NTOC, CTON
|
||||||
|
// TODO INDEX on V, BV, S, L
|
||||||
|
// TODO LENGTH on V, BV, S, L
|
||||||
|
// TODO SUBSL on V, BV, S, L
|
||||||
|
// TODO INSER on V, BV, S, L
|
||||||
|
// TODO CAR and CDR and CONS
|
||||||
|
// TODO CONCAT and S_APPEND
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue