ucg/src/build/opcode/test.rs
Jeremy Wall e256abfee6 DEV: Plugin the VM to our FileBuilder.
Most of the tests do not yet pass and the main.rs doesn't compile
but we are snapshotting here so we can start fixing unit tests
before we hook directly into the main.rs.
2019-10-17 17:57:06 -05:00

908 lines
28 KiB
Rust

// Copyright 2019 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cell::RefCell;
use std::rc::Rc;
use super::environment::Environment;
use super::translate::PositionMap;
use super::Composite::{List, Tuple};
use super::Op::{
Add, Bang, Bind, BindOver, Cp, DeRef, Div, Element, Equal, FCall, Field, Func, Index, InitList,
InitThunk, InitTuple, Jump, JumpIfFalse, JumpIfTrue, Module, Mul, NewScope, Noop, Pop, Render,
Return, SelectJump, Sub, Sym, Typ, Val,
};
use super::Primitive::{Bool, Empty, Float, Int, Str};
use super::Value::{C, P};
use super::VM;
use crate::ast::Position;
macro_rules! assert_cases {
(__impl__ $cases:expr) => {
for case in $cases.drain(0..) {
let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new())));
let mut positions = Vec::with_capacity(case.0.len());
for _ in 0..case.0.len() {
positions.push(Position::new(0, 0, 0));
}
let map = PositionMap{
ops: case.0,
pos: positions,
};
let mut vm = VM::new(Rc::new(map), env);
vm.run().unwrap();
assert_eq!(vm.pop().unwrap().0, Rc::new(case.1));
}
};
(($input:expr, $result:expr), $($tok:tt)*) => {
assert_cases!(__impl__ vec![($input, $result), $($tok)*])
};
($($input:expr => $result:expr, )* ) => {
assert_cases!(
$(($input, $result),)*
)
}
}
#[test]
fn math_ops() {
assert_cases!(
// 1+1;
vec![Val(Int(1)), Val(Int(1)), Add] => P(Int(2)),
// 1-1;
vec![Val(Int(1)), Val(Int(1)), Sub] => P(Int(0)),
// 2*2;
vec![Val(Int(2)), Val(Int(2)), Mul] => P(Int(4)),
// 6/3;
vec![Val(Int(2)), Val(Int(6)), Div] => P(Int(3)),
// 1.0+1.0;
vec![Val(Float(1.0)), Val(Float(1.0)), Add] => P(Float(2.0)),
// 1.0-1.0;
vec![Val(Float(1.0)), Val(Float(1.0)), Sub] => P(Float(0.0)),
// 2.0*2.0;
vec![Val(Float(2.0)), Val(Float(2.0)), Mul] => P(Float(4.0)),
// 6.0/3.0;
vec![Val(Float(2.0)), Val(Float(6.0)), Div] => P(Float(3.0)),
// string concatenation
vec![Val(Str("bar".to_owned())), Val(Str("foo".to_owned())), Add] => P(Str("foobar".to_owned())),
// Composite operations
vec![
Val(Int(1)),
Val(Int(1)),
Add, // 1 + 1
Val(Int(1)),
Add, // 2 + 1
Val(Int(1)),
Add, // 3 + 1
] => P(Int(4)),
);
}
#[test]
fn new_scopes() {
assert_cases![
vec![
Sym("foo".to_owned()),
Val(Int(1)),
Bind,
NewScope(5),
Sym("foo".to_owned()),
Val(Int(2)),
BindOver,
DeRef("foo".to_owned()),
Return,
] => P(Int(2)),
vec![
Sym("bar".to_owned()),
Val(Int(1)),
Bind,
NewScope(5),
Sym("foo".to_owned()),
Val(Int(2)),
Bind,
DeRef("bar".to_owned()),
Return,
] => P(Int(1)),
];
}
#[test]
fn bind_op() {
let mut cases = vec![(
vec![Sym("foo".to_owned()), Val(Int(1)), Bind],
("foo", P(Int(1))),
vec![Sym("foo".to_owned()), Val(Int(1)), Val(Int(1)), Add, Bind],
("foo", P(Int(2))),
)];
for case in cases.drain(0..) {
let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new())));
let mut positions = Vec::with_capacity(case.0.len());
for _ in 0..case.0.len() {
positions.push(Position::new(0, 0, 0));
}
let map = PositionMap {
ops: case.0,
pos: positions,
};
let mut vm = VM::new(Rc::new(map), env);
vm.run().unwrap();
let (name, result) = case.1;
let (v, _) = vm.get_binding(name, &Position::new(0, 0, 0)).unwrap();
assert_eq!(&result, v.as_ref());
}
}
#[test]
fn list_ops() {
assert_cases!(
vec![InitList] => C(List(Vec::new())),
vec![InitList, Val(Int(1)), Element] => C(List(vec![Rc::new(P(Int(1)))])),
vec![InitList, Val(Int(2)), Element, Val(Int(1)), Element] => C(List(vec![Rc::new(P(Int(2))), Rc::new(P(Int(1)))])),
vec![
InitList,
Val(Int(1)),
Element,
Val(Int(1)),
Val(Int(1)),
Add,
Element,
] => C(List(vec![Rc::new(P(Int(1))), Rc::new(P(Int(2)))])),
);
}
#[test]
fn tuple_ops() {
assert_cases!(
vec![InitTuple] => C(Tuple(Vec::new())),
vec![
InitTuple,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
] => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(1)))),
])),
vec![
InitTuple,
Sym("bar".to_owned()),
Val(Str("quux".to_owned())),
Field,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
] => C(Tuple(vec![
("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))),
("foo".to_owned(), Rc::new(P(Int(1)))),
])),
vec![
InitTuple,
Sym("bar".to_owned()),
Val(Str("quux".to_owned())),
Field,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
Val(Str("foo".to_owned())),
Val(Int(2)),
Field,
] => C(Tuple(vec![
("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))),
("foo".to_owned(), Rc::new(P(Int(2)))),
])),
vec![
InitTuple,
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,
Val(Str("foo".to_owned())),
Val(Int(2)),
Field,
] => C(Tuple(vec![
("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))),
("foo".to_owned(), Rc::new(P(Int(2)))),
])),
vec![
Sym("tpl".to_owned()),
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,
Bind,
DeRef("tpl".to_owned()),
InitTuple,
// Begin the field overrides
Val(Str("foo".to_owned())),
Val(Int(2)),
Field,
Cp, // Do the tuple copy operation
] => C(Tuple(vec![
("bar".to_owned(), Rc::new(P(Str("quux".to_owned())))),
("foo".to_owned(), Rc::new(P(Int(2)))),
])),
);
}
#[test]
fn jump_ops() {
assert_cases!(
vec![Jump(1), Val(Int(1)), Noop, Val(Int(1))] => P(Int(1)),
);
}
#[test]
fn equality_ops() {
assert_cases![
vec![
Val(Str("foo".to_owned())),
Val(Str("foo".to_owned())),
Equal,
] => P(Bool(true)),
vec![
Val(Str("bar".to_owned())),
Val(Str("foo".to_owned())),
Equal,
] => P(Bool(false)),
vec![Val(Int(1)), Val(Int(1)), Equal] => P(Bool(true)),
vec![Val(Int(1)), Val(Int(2)), Equal] => P(Bool(false)),
vec![Val(Bool(true)), Val(Bool(true)), Equal] => P(Bool(true)),
vec![Val(Bool(false)), Val(Bool(false)), Equal] => P(Bool(true)),
vec![Val(Bool(true)), Val(Bool(false)), Equal] => P(Bool(false)),
vec![
InitTuple,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
InitTuple,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
Equal,
] => P(Bool(true)),
vec![
InitTuple,
Val(Str("foo".to_owned())),
Val(Int(1)),
Field,
InitTuple,
Val(Str("bar".to_owned())),
Val(Int(1)),
Field,
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)),
];
}
#[test]
fn conditional_jump_ops() {
assert_cases![
vec![
Val(Bool(false)),
JumpIfTrue(2),
Val(Bool(true)),
JumpIfTrue(2),
Val(Int(1)),
Jump(1),
Val(Int(2)),
Noop,
] => P(Int(2)),
vec![
Val(Bool(true)),
JumpIfTrue(2),
Val(Bool(false)),
JumpIfTrue(2),
Val(Int(1)),
Jump(1),
Val(Int(2)),
Noop,
] => P(Int(1)),
vec![
Val(Int(1)),
Val(Int(1)),
Equal,
JumpIfTrue(2),
Val(Bool(false)),
JumpIfTrue(2),
Val(Int(1)),
Jump(1),
Val(Int(2)),
Noop,
] => P(Int(1)),
vec![
Val(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)),
];
}
#[test]
fn function_definition_and_call() {
assert_cases![
vec![
Sym("f".to_owned()), // 0
InitList, // 1
Sym("arg".to_owned()), // 2
Element, // 3
Func(2), // 4
DeRef("arg".to_owned()), // 5
Return, // 6
Bind, // 7
Val(Int(1)), // 8
DeRef("f".to_owned()), // 9
FCall, // 10
] => P(Int(1)),
vec![
Sym("closed".to_owned()), // 0
Val(Int(1)), // 1
Bind, // 2
Sym("f".to_owned()), // 3
InitList, // 4
Sym("arg".to_owned()), // 5
Element, // 6
Func(4), // 7
DeRef("arg".to_owned()), // 8
DeRef("closed".to_owned()), // 9
Add, // 10
Return, // 11
Bind, // 12
Val(Int(1)), // 13
DeRef("f".to_owned()), // 14
FCall, // 16
] => P(Int(2)),
];
}
#[test]
fn module_call() {
assert_cases![
vec![
Sym("m".to_owned()), // 4 // binding name for module
InitTuple, // 5 // Module tuple bindings
Sym("one".to_owned()), // 6
Val(Int(1)), // 7
Field, // 8
Sym("two".to_owned()), // 9
Val(Int(2)), // 10
Field, // 11
Module(7), // 12 // Module body definition
Bind, // 13 // bind the mod tuple
Sym("foo".to_owned()), // 14
DeRef("mod".to_owned()), // 15
Val(Str("one".to_owned())), // 16
Index, // 17
Bind, // 18 // bind mod tuple to foo
Return, // 19 // end the module
Bind, // 20 // bind module to the binding name
DeRef("m".to_owned()), // 21
InitTuple, // 0 // override tuple
Sym("one".to_owned()), // 1
Val(Int(11)), // 2
Field, // 3
Cp, // 22 // Call the module
] => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(11)))),
])),
vec![
Sym("m".to_owned()), // 4 // binding name for module
InitTuple, // 5 // Module tuple bindings
Sym("one".to_owned()), // 6
Val(Int(1)), // 7
Field, // 8
Sym("two".to_owned()), // 9
Val(Int(2)), // 10
Field, // 11
InitThunk(2), // 12 // Module Return expression
Val(Int(1)), // 13
Return, // 14
Module(5), // 15 // Module definition
Bind, // 16
Sym("foo".to_owned()), // 17
DeRef("mod".to_owned()), // 18
Bind, // 19 // bind mod tuple to foo
Return, // 20 // end the module
Bind, // 21 // bind module to the binding name
DeRef("m".to_owned()), // 22
InitTuple, // 0 // override tuple
Sym("one".to_owned()), // 1
Val(Int(11)), // 2
Field, // 3
Cp, // 23
] => P(Int(1)),
];
}
#[test]
fn select_short_circuit() {
assert_cases![
vec![
Val(Str("field".to_owned())), // 0 // search field
Sym("not_field".to_owned()), // 1 // first field to compare
SelectJump(2), // 2
Val(Str("not our value".to_owned())), // 3 // expression for first field
Jump(4), // 4
Sym("field".to_owned()), // 5 // second field to compare
SelectJump(2), // 6
Val(Int(1)), // 7 // expression for second field
Jump(2), // 8
Pop, // 9 // pop the search field off
Bang, // 10 // default case
] => P(Int(1)),
vec![
Val(Str("field".to_owned())), // 0 // search field
Sym("not_field".to_owned()), // 1 // first field to compare
SelectJump(2), // 2
Val(Str("not our value".to_owned())), // 3 // expression for first field
Jump(4), // 4
Sym("als not field".to_owned()), // 5 // second field to compare
SelectJump(2), // 6
Val(Int(2)), // 7 // expression for second field
Jump(1), // 8
Pop, // 9 // pop the search field off
Val(Int(1)), // 10 // default case
] => P(Int(1)),
];
}
#[test]
fn index_operation() {
assert_cases![
vec![
InitTuple,
Sym("foo".to_owned()),
InitTuple,
Sym("bar".to_owned()),
Val(Int(1)),
Field,
Field,
Val(Str("foo".to_owned())),
Index,
Val(Str("bar".to_owned())),
Index,
] => P(Int(1)),
vec![
InitList,
Val(Str("foo".to_owned())),
Element,
Val(Str("bar".to_owned())),
Element,
Val(Int(0)),
Index,
] => P(Str("foo".to_owned())),
vec![
InitTuple,
Sym("field".to_owned()),
InitList,
Val(Str("foo".to_owned())),
Element,
Val(Str("bar".to_owned())),
Element,
Field,
Val(Str("field".to_owned())),
Index,
Val(Int(0)),
Index,
] => P(Str("foo".to_owned())),
];
}
#[test]
fn type_comparisons() {
assert_cases![
vec![
Val(Str("foo".to_owned())),
Typ,
] => P(Str("str".to_owned())),
vec![
Val(Int(1)),
Typ,
] => P(Str("int".to_owned())),
vec![
Val(Float(1.0)),
Typ,
] => P(Str("float".to_owned())),
vec![
Val(Bool(true)),
Typ,
] => P(Str("bool".to_owned())),
vec![
Val(Empty),
Typ,
] => P(Str("null".to_owned())),
vec![
InitTuple,
Typ,
] => P(Str("tuple".to_owned())),
vec![
InitList,
Typ,
] => P(Str("list".to_owned())),
vec![
Val(Str("str".to_owned())),
Val(Str("foo".to_owned())),
Typ,
Equal,
] => P(Bool(true)),
];
}
use super::translate;
use crate::iter::OffsetStrIter;
use crate::parse::parse;
macro_rules! assert_parse_cases {
(__impl__ $cases:expr) => {
for case in $cases.drain(0..) {
let stmts = parse(OffsetStrIter::from(dbg!(case.0)), None).unwrap();
let root = std::env::current_dir().unwrap();
// TODO(jwall): preprocessor
let ops = Rc::new(translate::AST::translate(stmts, &root));
assert!(ops.len() > 0);
let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new())));
let mut vm = VM::new(ops.clone(), env);
vm.run().unwrap();
assert_eq!(vm.last.unwrap().0, Rc::new(case.1));
}
};
( ($input:expr, $result:expr), $( $tok:tt )* ) => {
assert_parse_cases!(__impl__ vec![($input, $result), $($tok)*])
};
( $( $input:expr => $result:expr, )* ) => {
assert_parse_cases!($(($input, $result),)*)
}
}
#[test]
fn simple_expr_scalar_value() {
assert_parse_cases!(
"1;" => P(Int(1)),
"(1);" => P(Int(1)),
"1.0;" => P(Float(1.0)),
"true;" => P(Bool(true)),
"NULL;" => P(Empty),
"\"foo\";" => P(Str("foo".to_owned())),
)
}
#[test]
fn simple_binary_expr() {
assert_parse_cases!(
"1+1;" => P(Int(2)),
"2-1;" => P(Int(1)),
"2*2;" => P(Int(4)),
"6/2;" => P(Int(3)),
"4 %% 2;" => P(Int(0)),
"5 %% 2;" => P(Int(1)),
"1.0+1.0;" => P(Float(2.0)),
"\"foo\"+\"bar\";" => P(Str("foobar".to_owned())),
"1==1;" => P(Bool(true)),
"1>1;" => P(Bool(false)),
"1<1;" => P(Bool(false)),
"2>1;" => P(Bool(true)),
"2<1;" => P(Bool(false)),
"2<=1;" => P(Bool(false)),
"2>=1;" => P(Bool(true)),
"1!=1;" => P(Bool(false)),
"\"foo\" ~ \"bar\";" => P(Bool(false)),
"\"foo\" !~ \"bar\";" => P(Bool(true)),
"\"foo\" is \"str\";" => P(Bool(true)),
"true && true;" => P(Bool(true)),
"true && false;" => P(Bool(false)),
"false && false;" => P(Bool(false)),
"false && true;" => P(Bool(false)),
"false || false;" => P(Bool(false)),
"true || false;" => P(Bool(true)),
"false || true;" => P(Bool(true)),
"true || true;" => P(Bool(true)),
"foo in {foo = 1};" => P(Bool(true)),
"bar in {foo = 1};" => P(Bool(false)),
)
}
#[test]
fn simple_let_statements() {
assert_parse_cases![
"let foo = 1; foo;" => P(Int(1)),
"let foo = 1 + 1; foo;" => P(Int(2)),
];
}
#[test]
fn dot_expressions() {
assert_parse_cases![
"let foo = [0,1,2]; foo.0;" => P(Int(0)),
"let foo = [0,1,2]; foo.2;" => P(Int(2)),
"let tpl = { foo = 1 }; tpl.foo;" => P(Int(1)),
"let tpl = { foo = { bar = 2 } }; tpl.foo.bar;" => P(Int(2)),
"let tpl = { foo = [ 3 ] }; tpl.foo.0;" => P(Int(3)),
];
}
#[test]
fn simple_not_expr() {
assert_parse_cases!(
"not 1==1;" => P(Bool(false)),
"not 1!=1;" => P(Bool(true)),
)
}
#[test]
fn simple_render_operation() {
assert_cases![
vec![
Val(Int(1)),
Render,
] => P(Str("1".to_owned())),
vec![
Val(Float(1.1)),
Render,
] => P(Str("1.1".to_owned())),
vec![
Val(Bool(true)),
Render,
] => P(Str("true".to_owned())),
vec![
Val(Bool(false)),
Render,
] => P(Str("false".to_owned())),
vec![
Val(Empty),
Render,
] => P(Str("NULL".to_owned())),
vec![
Val(Str("foo".to_owned())),
Render,
] => P(Str("foo".to_owned())),
];
}
#[test]
fn simple_format_expressions() {
assert_parse_cases![
"\"@\" % (1);" => P(Str("1".to_owned())),
"\"@\" % (1.1);" => P(Str("1.1".to_owned())),
"\"@\" % (\"foo\");" => P(Str("foo".to_owned())),
"\"@\" % (NULL);" => P(Str("NULL".to_owned())),
"\"@ @ @\" % (1, 2, 3);" => P(Str("1 2 3".to_owned())),
"\"@ \\\\@\" % (1);" => P(Str("1 @".to_owned())),
"\"@{item.num}\" % {num=1};" => P(Str("1".to_owned())),
];
}
#[test]
fn simple_functions() {
assert_parse_cases![
"let f = func(val) => val; f(1);" => P(Int(1)),
"let f = func(val1, val2) => val1 + val2; f(1, 1);" => P(Int(2)),
];
}
#[test]
fn simple_modules() {
assert_parse_cases![
"let m = module{} => { let v = 1; }; m{};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(1))))])),
"let m = module{} => (v) { let v = 1; }; m{};" => P(Int(1)),
"let m = module{val=NULL} => (v) { let v = mod.val; }; m{val=1};" => P(Int(1)),
"let m = module{val=NULL} => (v) { let v = mod.val + 1; }; m{val=1};" => P(Int(2)),
];
}
#[test]
fn tuple_copies() {
assert_parse_cases![
"let tpl = { v = 1, }; tpl{};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(1))))])),
"let tpl = { v = 1, }; tpl{v=2};" => C(Tuple(vec![("v".to_owned(), Rc::new(P(Int(2))))])),
// Tests for Self lookups
"let tpl = { v = 1, w = 2}; tpl{v=self.w, x=self.v};" => C(Tuple(vec![
("v".to_owned(), Rc::new(P(Int(2)))),
("w".to_owned(), Rc::new(P(Int(2)))),
("x".to_owned(), Rc::new(P(Int(1)))),
])),
// the deep copy case identity copy
"let tpl = { v = 1, inner = { w = 2 }}; tpl{inner=self.inner{}};" => C(Tuple(vec![
("v".to_owned(), Rc::new(P(Int(1)))),
("inner".to_owned(), Rc::new(C(Tuple(vec![
("w".to_owned(), Rc::new(P(Int(2)))),
])))),
])),
// the deep copy case modifications
"let tpl = { v = 1, inner = { w = 2 }}; tpl{inner=self.inner{w=3}};" => C(Tuple(vec![
("v".to_owned(), Rc::new(P(Int(1)))),
("inner".to_owned(), Rc::new(C(Tuple(vec![
("w".to_owned(), Rc::new(P(Int(3)))),
])))),
])),
];
}
#[test]
fn simple_range() {
assert_parse_cases![
"1:2;" => C(List(vec![
Rc::new(P(Int(1))),
Rc::new(P(Int(2))),
])),
"0:2:4;" => C(List(vec![
Rc::new(P(Int(0))),
Rc::new(P(Int(2))),
Rc::new(P(Int(4))),
])),
"0:3:4;" => C(List(vec![
Rc::new(P(Int(0))),
Rc::new(P(Int(3))),
])),
];
}
#[test]
fn simple_selects() {
assert_parse_cases![
"select \"foo\", { foo = 1, bar = 2, };" => P(Int(1)),
"select \"bar\", { foo = 1, bar = 2, };" => P(Int(2)),
"select \"quux\", 3, { foo = 1, bar = 2, };" => P(Int(3)),
];
}
#[test]
#[should_panic]
fn select_fails() {
assert_parse_cases![
"select \"quux\", { foo = 1, bar = 2, };" => P(Int(1)),
];
}
#[test]
fn select_compound_expressions() {
assert_parse_cases![
"select \"foo\", { foo = 1, bar = 2, } == 1;" => P(Bool(true)),
"select \"foo\", { foo = 1, bar = 2, } == 2;" => P(Bool(false)),
"select \"foo\", 3, { foo = 1, bar = 2, } == 2;" => P(Bool(false)),
"select \"quux\", 3, { foo = 1, bar = 2, } == 3;" => P(Bool(true)),
"let want = \"foo\"; select want, { foo = 1, bar = 2, } == 1;" => P(Bool(true)),
"let want = \"foo\"; select want, 3, { foo = 1, bar = 2, } == 1;" => P(Bool(true)),
"{ok = select \"foo\", { foo = 1, bar = 2, } == 2}.ok;" => P(Bool(false)),
];
}
#[test]
fn simple_trace() {
let stmts = parse(OffsetStrIter::from("TRACE 1+1;"), None).unwrap();
let root = std::env::current_dir().unwrap();
let ops = Rc::new(translate::AST::translate(stmts, &root));
assert!(ops.len() > 0);
let env = Rc::new(RefCell::new(Environment::new(Vec::new(), Vec::new())));
let mut vm = VM::new(ops.clone(), env);
vm.run().unwrap();
assert_eq!(vm.last.unwrap().0, Rc::new(P(Int(2))));
let err_out = &vm.env.borrow().stderr;
assert_eq!(
String::from_utf8_lossy(err_out).to_owned(),
"TRACE: 1 + 1 = 2 at line: 1 column: 7\n"
);
}
#[test]
fn simple_maps() {
assert_parse_cases![
"map(func(el) => el, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(1))),
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"map(func(el) => el + 1, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
Rc::new(P(Int(4))),
])),
"map(func(el) => el, \"foo\");" => P(Str("foo".to_owned())),
"map(func(el) => el + \"-\", \"foo\");" => P(Str("f-o-o-".to_owned())),
"map(func(k, v) => [k, v], {foo = 1});" => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(1)))),
])),
"map(func(k, v) => [k, v + 1], {foo = 1});" => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(2)))),
])),
];
}
#[test]
fn simple_filters() {
assert_parse_cases![
"filter(func(el) => true, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(1))),
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"filter(func(el) => false, [1,2,3]);" => C(List(vec![
])),
"filter(func(el) => el != 1, [1,2,3]);" => C(List(vec![
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"filter(func(el) => true, \"foo\");" => P(Str("foo".to_owned())),
"filter(func(el) => false, \"foo\");" => P(Str("".to_owned())),
"filter(func(el) => el != \"f\", \"foo\");" => P(Str("oo".to_owned())),
"filter(func(k, v) => true, {foo = 1});" => C(Tuple(vec![
("foo".to_owned(), Rc::new(P(Int(1)))),
])),
"filter(func(k, v) => false, {foo = 1});" => C(Tuple(vec![
])),
"filter(func(k, v) => k != \"foo\", {foo = 1});" => C(Tuple(vec![
])),
"filter(func(k, v) => v != 1, {foo = 1});" => C(Tuple(vec![
])),
];
}
#[test]
fn simple_reduces() {
assert_parse_cases![
"reduce(func(acc, el) => acc + [el], [], [1,2,3]);" => C(List(vec![
Rc::new(P(Int(1))),
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
])),
"reduce(func(acc, el) => acc + [el+1], [], [1,2,3]);" => C(List(vec![
Rc::new(P(Int(2))),
Rc::new(P(Int(3))),
Rc::new(P(Int(4))),
])),
"reduce(func(acc, s) => acc + s, \"\", \"foo\");" => P(Str("foo".to_owned())),
"reduce(func(acc, k, v) => acc + [[k, v]], [], {foo = 1});" => C(List(vec![
Rc::new(C(List(vec![
Rc::new(P(Str("foo".to_owned()))),
Rc::new(P(Int(1))),
]))),
])),
];
}