DEV: DSL for table based testing using macros.

This commit is contained in:
Jeremy Wall 2019-07-08 20:11:19 -05:00
parent 3bb6f6e8eb
commit aac263be2c

View File

@ -22,49 +22,58 @@ use super::Primitive::{Bool, Float, Int, Str};
use super::Value::{C, P, T}; use super::Value::{C, P, T};
use super::VM; use super::VM;
macro_rules! assert_cases {
(__impl__ $cases:expr) => {
for case in $cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
};
(($input:expr, $result:expr), $($tok:tt)*) => {
assert_cases!(__impl__ vec![($input, $result), $($tok)*])
};
($($input:expr => $result:expr, )* ) => {
assert_cases!(
$(($input, $result),)*
)
}
}
#[test] #[test]
fn test_math_ops() { fn test_math_ops() {
let mut cases = vec![ assert_cases!(
// 1+1; // 1+1;
(vec![Val(Int(1)), Val(Int(1)), Add], P(Int(2))), vec![Val(Int(1)), Val(Int(1)), Add] => P(Int(2)),
// 1-1; // 1-1;
(vec![Val(Int(1)), Val(Int(1)), Sub], P(Int(0))), vec![Val(Int(1)), Val(Int(1)), Sub] => P(Int(0)),
// 2*2; // 2*2;
(vec![Val(Int(2)), Val(Int(2)), Mul], P(Int(4))), vec![Val(Int(2)), Val(Int(2)), Mul] => P(Int(4)),
// 6/3; // 6/3;
(vec![Val(Int(2)), Val(Int(6)), Div], P(Int(3))), vec![Val(Int(2)), Val(Int(6)), Div] => P(Int(3)),
// 1.0+1.0; // 1.0+1.0;
(vec![Val(Float(1.0)), Val(Float(1.0)), Add], P(Float(2.0))), vec![Val(Float(1.0)), Val(Float(1.0)), Add] => P(Float(2.0)),
// 1.0-1.0; // 1.0-1.0;
(vec![Val(Float(1.0)), Val(Float(1.0)), Sub], P(Float(0.0))), vec![Val(Float(1.0)), Val(Float(1.0)), Sub] => P(Float(0.0)),
// 2.0*2.0; // 2.0*2.0;
(vec![Val(Float(2.0)), Val(Float(2.0)), Mul], P(Float(4.0))), vec![Val(Float(2.0)), Val(Float(2.0)), Mul] => P(Float(4.0)),
// 6.0/3.0; // 6.0/3.0;
(vec![Val(Float(2.0)), Val(Float(6.0)), Div], P(Float(3.0))), vec![Val(Float(2.0)), Val(Float(6.0)), Div] => P(Float(3.0)),
// string concatenation // string concatenation
( vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add] => P(Str("foobar".to_owned())),
vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add],
P(Str("foobar".to_owned())),
),
// Composite operations // Composite operations
( vec![
vec![ Val(Int(1)),
Val(Int(1)), Val(Int(1)),
Val(Int(1)), Add, // 1 + 1
Add, // 1 + 1 Val(Int(1)),
Val(Int(1)), Add, // 2 + 1
Add, // 2 + 1 Val(Int(1)),
Val(Int(1)), Add, // 3 + 1
Add, // 3 + 1 ] => P(Int(4)),
], );
P(Int(4)),
),
];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
@ -87,415 +96,308 @@ fn test_bind_op() {
#[test] #[test]
fn test_list_ops() { fn test_list_ops() {
let mut cases = vec![ assert_cases!(
(vec![InitList], C(List(Vec::new()))), vec![InitList] => C(List(Vec::new())),
( vec![InitList, Val(Int(1)), Element] => C(List(vec![P(Int(1))])),
vec![InitList, Val(Int(1)), Element], vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element] => C(List(vec![P(Int(2)), P(Int(1))])),
C(List(vec![P(Int(1))])), vec![
), InitList,
( Val(Int(1)),
vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element], Element,
C(List(vec![P(Int(2)), P(Int(1))])), Val(Int(1)),
), Val(Int(1)),
( Add,
vec![ Element,
InitList, ] => C(List(vec![P(Int(1)), P(Int(2))])),
Val(Int(1)), );
Element,
Val(Int(1)),
Val(Int(1)),
Add,
Element,
],
C(List(vec![P(Int(1)), P(Int(2))])),
),
];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_tuple_ops() { fn test_tuple_ops() {
let mut cases = vec![ assert_cases!(
(vec![InitTuple], C(Tuple(Vec::new()))), vec![InitTuple] => C(Tuple(Vec::new())),
( vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), Field] => C(Tuple(vec![("foo".to_owned(), P(Int(1)))])),
vec![InitTuple, Val(Str("foo".to_owned())), Val(Int(1)), Field], vec![
C(Tuple(vec![("foo".to_owned(), P(Int(1)))])), InitTuple,
), Sym("bar".to_owned()),
( Val(Str("quux".to_owned())),
vec![ Field,
InitTuple, Val(Str("foo".to_owned())),
Sym("bar".to_owned()), Val(Int(1)),
Val(Str("quux".to_owned())), Field,
Field, ] => C(Tuple(vec![
Val(Str("foo".to_owned())), ("bar".to_owned(), P(Str("quux".to_owned()))),
Val(Int(1)), ("foo".to_owned(), P(Int(1))),
Field, ])),
], vec![
C(Tuple(vec![ InitTuple,
("bar".to_owned(), P(Str("quux".to_owned()))), Sym("bar".to_owned()),
("foo".to_owned(), P(Int(1))), Val(Str("quux".to_owned())),
])), Field,
), Val(Str("foo".to_owned())),
( Val(Int(1)),
vec![ Field,
InitTuple, Val(Str("foo".to_owned())),
Sym("bar".to_owned()), Val(Int(2)),
Val(Str("quux".to_owned())), Field,
Field, ] => C(Tuple(vec![
Val(Str("foo".to_owned())), ("bar".to_owned(), P(Str("quux".to_owned()))),
Val(Int(1)), ("foo".to_owned(), P(Int(2))),
Field, ])),
Val(Str("foo".to_owned())), vec![
Val(Int(2)), InitTuple,
Field, Sym("bar".to_owned()),
], Val(Str("ux".to_owned())),
C(Tuple(vec![ Val(Str("qu".to_owned())),
("bar".to_owned(), P(Str("quux".to_owned()))), Add,
("foo".to_owned(), P(Int(2))), Field,
])), Val(Str("foo".to_owned())),
), Val(Int(1)),
( Field,
vec![ Val(Str("foo".to_owned())),
InitTuple, Val(Int(2)),
Sym("bar".to_owned()), Field,
Val(Str("ux".to_owned())), ] => C(Tuple(vec![
Val(Str("qu".to_owned())), ("bar".to_owned(), P(Str("quux".to_owned()))),
Add, ("foo".to_owned(), P(Int(2))),
Field, ])),
Val(Str("foo".to_owned())), vec![
Val(Int(1)), InitTuple, // Override tuple
Field, Val(Str("foo".to_owned())),
Val(Str("foo".to_owned())), Val(Int(2)),
Val(Int(2)), Field,
Field, InitTuple, // Target tuple
], Sym("bar".to_owned()),
C(Tuple(vec![ Val(Str("ux".to_owned())),
("bar".to_owned(), P(Str("quux".to_owned()))), Val(Str("qu".to_owned())),
("foo".to_owned(), P(Int(2))), Add,
])), Field,
), Val(Str("foo".to_owned())),
( Val(Int(1)),
vec![ Field,
InitTuple, // Override tuple Cp, // Do the tuple copy operation
Val(Str("foo".to_owned())), ] => C(Tuple(vec![
Val(Int(2)), ("bar".to_owned(), P(Str("quux".to_owned()))),
Field, ("foo".to_owned(), P(Int(2))),
InitTuple, // Target tuple ])),
Sym("bar".to_owned()), );
Val(Str("ux".to_owned())),
Val(Str("qu".to_owned())),
Add,
Field,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
Cp, // Do the tuple copy operation
],
C(Tuple(vec![
("bar".to_owned(), P(Str("quux".to_owned()))),
("foo".to_owned(), P(Int(2))),
])),
),
];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_jump_ops() { fn test_jump_ops() {
let mut cases = vec![ assert_cases!(
(vec![InitThunk(1), Val(Int(1)), Noop], T(0)), vec![InitThunk(1), Val(Int(1)), Noop] => T(0),
(vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))], P(Int(1))), vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)),
]; );
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_equality_ops() { fn test_equality_ops() {
let mut cases = vec![ assert_cases![
( vec![
vec![ Val(Str("foo".to_owned())),
Val(Str("foo".to_owned())), Val(Str("foo".to_owned())),
Val(Str("foo".to_owned())), Equal,
Equal, ] => P(Bool(true)),
], vec![
P(Bool(true)), Val(Str("bar".to_owned())),
), Val(Str("foo".to_owned())),
( Equal,
vec![ ] => P(Bool(false)),
Val(Str("bar".to_owned())), vec![Val(Int(1)), Val(Int(1)), Equal] => P(Bool(true)),
Val(Str("foo".to_owned())), vec![Val(Int(1)), Val(Int(2)), Equal] => P(Bool(false)),
Equal, vec![Val(Bool(true)), Val(Bool(true)), Equal] => P(Bool(true)),
], vec![Val(Bool(false)), Val(Bool(false)), Equal] => P(Bool(true)),
P(Bool(false)), vec![Val(Bool(true)), Val(Bool(false)), Equal] => P(Bool(false)),
), vec![
(vec![Val(Int(1)), Val(Int(1)), Equal], P(Bool(true))), InitTuple,
(vec![Val(Int(1)), Val(Int(2)), Equal], P(Bool(false))), Val(Str("foo".to_owned())),
(vec![Val(Bool(true)), Val(Bool(true)), Equal], P(Bool(true))), Val(Int(1)),
( Field,
vec![Val(Bool(false)), Val(Bool(false)), Equal], InitTuple,
P(Bool(true)), Val(Str("foo".to_owned())),
), Val(Int(1)),
( Field,
vec![Val(Bool(true)), Val(Bool(false)), Equal], Equal,
P(Bool(false)), ] => P(Bool(true)),
), vec![
( InitTuple,
vec![ Val(Str("foo".to_owned())),
InitTuple, Val(Int(1)),
Val(Str("foo".to_owned())), Field,
Val(Int(1)), InitTuple,
Field, Val(Str("bar".to_owned())),
InitTuple, Val(Int(1)),
Val(Str("foo".to_owned())), Field,
Val(Int(1)), Equal,
Field, ] => P(Bool(false)),
Equal, vec![
], InitList,
P(Bool(true)), Val(Str("foo".to_owned())),
), Element,
( InitList,
vec![ Val(Str("foo".to_owned())),
InitTuple, Element,
Val(Str("foo".to_owned())), Equal,
Val(Int(1)), ] => P(Bool(true)),
Field, vec![
InitTuple, InitList,
Val(Str("bar".to_owned())), Val(Str("foo".to_owned())),
Val(Int(1)), Element,
Field, InitList,
Equal, Val(Str("bar".to_owned())),
], Element,
P(Bool(false)), Equal,
), ] => P(Bool(false)),
(
vec![
InitList,
Val(Str("foo".to_owned())),
Element,
InitList,
Val(Str("foo".to_owned())),
Element,
Equal,
],
P(Bool(true)),
),
(
vec![
InitList,
Val(Str("foo".to_owned())),
Element,
InitList,
Val(Str("bar".to_owned())),
Element,
Equal,
],
P(Bool(false)),
),
]; ];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_conditional_jump_ops() { fn test_conditional_jump_ops() {
let mut cases = vec![ assert_cases![
( vec![
vec![ Val(Bool(false)),
Val(Bool(false)), JumpIfTrue(2),
JumpIfTrue(2), Val(Bool(true)),
Val(Bool(true)), JumpIfTrue(2),
JumpIfTrue(2), Val(Int(1)),
Val(Int(1)), Jump(1),
Jump(1), Val(Int(2)),
Val(Int(2)), Noop,
Noop, ] => P(Int(2)),
], vec![
P(Int(2)), Val(Bool(true)),
), JumpIfTrue(2),
( Val(Bool(false)),
vec![ JumpIfTrue(2),
Val(Bool(true)), Val(Int(1)),
JumpIfTrue(2), Jump(1),
Val(Bool(false)), Val(Int(2)),
JumpIfTrue(2), Noop,
Val(Int(1)), ] => P(Int(1)),
Jump(1), vec![
Val(Int(2)), Val(Int(1)),
Noop, Val(Int(1)),
], Equal,
P(Int(1)), JumpIfTrue(2),
), Val(Bool(false)),
( JumpIfTrue(2),
vec![ Val(Int(1)),
Val(Int(1)), Jump(1),
Val(Int(1)), Val(Int(2)),
Equal, Noop,
JumpIfTrue(2), ] => P(Int(1)),
Val(Bool(false)), vec![
JumpIfTrue(2), Val(Int(1)),
Val(Int(1)), Val(Int(2)),
Jump(1), Equal,
Val(Int(2)), JumpIfFalse(2),
Noop, Val(Bool(false)),
], JumpIfTrue(2),
P(Int(1)), Val(Int(1)),
), Jump(1),
( Val(Int(2)),
vec![ Noop,
Val(Int(1)), ] => P(Int(1)),
Val(Int(2)),
Equal,
JumpIfFalse(2),
Val(Bool(false)),
JumpIfTrue(2),
Val(Int(1)),
Jump(1),
Val(Int(2)),
Noop,
],
P(Int(1)),
),
]; ];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_function_definition_and_call() { fn test_function_definition_and_call() {
let mut cases = vec![ assert_cases![
( vec![
vec![ Sym("f".to_owned()), // 0
Sym("f".to_owned()), // 0 InitList, // 1
InitList, // 1 Sym("arg".to_owned()), // 2
Sym("arg".to_owned()), // 2 Element, // 3
Element, // 3 Func(6), // 4
Func(6), // 4 DeRef("arg".to_owned()), // 5
DeRef("arg".to_owned()), // 5 Return, // 6
Return, // 6 Bind, // 7
Bind, // 7 Val(Int(1)), // 8
Val(Int(1)), // 8 DeRef("f".to_owned()), // 9
DeRef("f".to_owned()), // 9 FCall, // 10
FCall, // 10 ] => P(Int(1)),
], vec![
P(Int(1)), Sym("closed".to_owned()), // 0
), Val(Int(1)), // 1
( Bind, // 2
vec![ Sym("f".to_owned()), // 3
Sym("closed".to_owned()), // 0 InitList, // 4
Val(Int(1)), // 1 Sym("arg".to_owned()), // 5
Bind, // 2 Element, // 6
Sym("f".to_owned()), // 3 Func(11), // 7
InitList, // 4 DeRef("arg".to_owned()), // 8
Sym("arg".to_owned()), // 5 DeRef("closed".to_owned()), // 9
Element, // 6 Add, // 10
Func(11), // 7 Return, // 11
DeRef("arg".to_owned()), // 8 Bind, // 12
DeRef("closed".to_owned()), // 9 Val(Int(1)), // 13
Add, // 10 DeRef("f".to_owned()), // 14
Return, // 11 FCall, // 16
Bind, // 12 ] => P(Int(2)),
Val(Int(1)), // 13
DeRef("f".to_owned()), // 14
FCall, // 16
],
P(Int(2)),
),
]; ];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]
fn test_module_call() { fn test_module_call() {
let mut cases = vec![ assert_cases![
( vec![
vec![ InitTuple, // 0 // override tuple
InitTuple, // 0 // override tuple Sym("one".to_owned()), // 1
Sym("one".to_owned()), // 1 Val(Int(11)), // 2
Val(Int(11)), // 2 Field, // 3
Field, // 3 Sym("m".to_owned()), // 4 // binding name for module
Sym("m".to_owned()), // 4 // binding name for module InitTuple, // 5 // Module tuple bindings
InitTuple, // 5 // Module tuple bindings Sym("one".to_owned()), // 6
Sym("one".to_owned()), // 6 Val(Int(1)), // 7
Val(Int(1)), // 7 Field, // 8
Field, // 8 Sym("two".to_owned()), // 9
Sym("two".to_owned()), // 9 Val(Int(2)), // 10
Val(Int(2)), // 10 Field, // 11
Field, // 11 Module(17), // 12 // Module definition
Module(17), // 12 // Module definition Bind, // 13
Bind, // 13 Sym("foo".to_owned()), // 14
Sym("foo".to_owned()), // 14 DeRef("mod".to_owned()), // 15
DeRef("mod".to_owned()), // 15 Bind, // 16 // bind mod tuple to foo
Bind, // 16 // bind mod tuple to foo Return, // 17 // end the module
Return, // 17 // end the module Bind, // 18 // bind module to the binding name
Bind, // 18 // bind module to the binding name DeRef("m".to_owned()), // 19
DeRef("m".to_owned()), // 19 Cp, // 20
Cp, // 20 ] => C(Tuple(vec![(
], "foo".to_owned(),
C(Tuple(vec![( C(Tuple(vec![
"foo".to_owned(), ("one".to_owned(), P(Int(11))),
C(Tuple(vec![ ("two".to_owned(), P(Int(2))),
("one".to_owned(), P(Int(11))), ])),
("two".to_owned(), P(Int(2))), )])),
])), vec![
)])), InitTuple, // 0 // override tuple
), Sym("one".to_owned()), // 1
( Val(Int(11)), // 2
vec![ Field, // 3
InitTuple, // 0 // override tuple Sym("m".to_owned()), // 4 // binding name for module
Sym("one".to_owned()), // 1 InitTuple, // 5 // Module tuple bindings
Val(Int(11)), // 2 Sym("one".to_owned()), // 6
Field, // 3 Val(Int(1)), // 7
Sym("m".to_owned()), // 4 // binding name for module Field, // 8
InitTuple, // 5 // Module tuple bindings Sym("two".to_owned()), // 9
Sym("one".to_owned()), // 6 Val(Int(2)), // 10
Val(Int(1)), // 7 Field, // 11
Field, // 8 InitThunk(2), // 12 // Module Return expression
Sym("two".to_owned()), // 9 Val(Int(1)), // 13
Val(Int(2)), // 10 Return, // 14
Field, // 11 Module(20), // 15 // Module definition
InitThunk(2), // 12 // Module Return expression Bind, // 16
Val(Int(1)), // 13 Sym("foo".to_owned()), // 17
Return, // 14 DeRef("mod".to_owned()), // 18
Module(20), // 15 // Module definition Bind, // 19 // bind mod tuple to foo
Bind, // 16 Return, // 20 // end the module
Sym("foo".to_owned()), // 17 Bind, // 21 // bind module to the binding name
DeRef("mod".to_owned()), // 18 DeRef("m".to_owned()), // 22
Bind, // 19 // bind mod tuple to foo Cp, // 23
Return, // 20 // end the module ] => P(Int(1)),
Bind, // 21 // bind module to the binding name
DeRef("m".to_owned()), // 22
Cp, // 23
],
P(Int(1)),
),
]; ];
for case in cases.drain(0..) {
let mut vm = VM::new(&case.0);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), case.1);
}
} }
#[test] #[test]