660c0c70ae
format only reducer reformat lint multi-line test spelling multi-line semantic mapping todo multi-line eval multi-line tests todo change context to bindings simplify tests rename exception test methods bindings is an expression value make bindings callable reformat Emphasize the nature of Lisp AST Initial definition of macros make functions private fixed functionNode type casting macro call skeleton sort ReducerInterface fix test macros skeleton bindings is not a value assignment semantics let semantics defined format reformat reformat TODO function calls and list hd variables are confused reformat tmp works reformat reformat add test reformat add test
106 lines
4.2 KiB
Plaintext
106 lines
4.2 KiB
Plaintext
open Jest
|
|
open Reducer_TestHelpers
|
|
|
|
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
|
|
|
let testDescParseToBe = (desc, expr, answer) => test(desc, () => expectParseToBe(expr, answer))
|
|
|
|
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
|
|
|
let testDescEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
|
|
|
describe("reducer using mathjs parse", () => {
|
|
// Test the MathJs parser compatibility
|
|
// Those tests toString that there is a semantic mapping from MathJs to Expression
|
|
// Reducer.parse is called by Reducer.eval
|
|
// See https://mathjs.org/docs/expressions/syntax.html
|
|
// See https://mathjs.org/docs/reference/functions.html
|
|
// Those tests toString that we are converting mathjs parse tree to what we need
|
|
|
|
describe("expressions", () => {
|
|
testParseToBe("1", "Ok(1)")
|
|
testParseToBe("(1)", "Ok(1)")
|
|
testParseToBe("1+2", "Ok((:add 1 2))")
|
|
testParseToBe("1+2", "Ok((:add 1 2))")
|
|
testParseToBe("1+2", "Ok((:add 1 2))")
|
|
testParseToBe("1+2*3", "Ok((:add 1 (:multiply 2 3)))")
|
|
})
|
|
describe("arrays", () => {
|
|
//Note. () is a empty list in Lisp
|
|
// The only builtin structure in Lisp is list. There are no arrays
|
|
// [1,2,3] becomes (1 2 3)
|
|
testDescParseToBe("empty", "[]", "Ok(())")
|
|
testParseToBe("[1, 2, 3]", "Ok((1 2 3))")
|
|
testParseToBe("['hello', 'world']", "Ok(('hello' 'world'))")
|
|
testDescParseToBe("index", "([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))")
|
|
})
|
|
describe("records", () => {
|
|
testDescParseToBe("define", "{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))")
|
|
testDescParseToBe(
|
|
"use",
|
|
"{a: 1, b: 2}.a",
|
|
"Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))",
|
|
)
|
|
})
|
|
describe("multi-line", () => {
|
|
testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))")
|
|
testParseToBe("1+1; 2+1", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))")
|
|
})
|
|
describe("assignment", () => {
|
|
testParseToBe("x=1; x", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))")
|
|
testParseToBe("x=1+1; x+1", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))")
|
|
})
|
|
|
|
})
|
|
|
|
describe("eval", () => {
|
|
// All MathJs operators and functions are builtin for string, float and boolean
|
|
// .e.g + - / * > >= < <= == /= not and or
|
|
// See https://mathjs.org/docs/expressions/syntax.html
|
|
// See https://mathjs.org/docs/reference/functions.html
|
|
describe("expressions", () => {
|
|
testEvalToBe("1", "Ok(1)")
|
|
testEvalToBe("1+2", "Ok(3)")
|
|
testEvalToBe("(1+2)*3", "Ok(9)")
|
|
testEvalToBe("2>1", "Ok(true)")
|
|
testEvalToBe("concat('a ', 'b')", "Ok('a b')")
|
|
testEvalToBe("log(10)", "Ok(2.302585092994046)")
|
|
testEvalToBe("cos(10)", "Ok(-0.8390715290764524)")
|
|
// TODO more built ins
|
|
})
|
|
describe("arrays", () => {
|
|
test("empty array", () => expectEvalToBe("[]", "Ok([])"))
|
|
testEvalToBe("[1, 2, 3]", "Ok([1, 2, 3])")
|
|
testEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])")
|
|
testEvalToBe("([0,1,2])[1]", "Ok(1)")
|
|
testDescEvalToBe("index not found", "([0,1,2])[10]", "Error(Array index not found: 10)")
|
|
})
|
|
describe("records", () => {
|
|
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})"))
|
|
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
|
|
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
|
|
})
|
|
|
|
describe("multi-line", () => {
|
|
testEvalToBe("1; 2", "Error(Assignment expected)")
|
|
testEvalToBe("1+1; 2+1", "Error(Assignment expected)")
|
|
})
|
|
describe("assignment", () => {
|
|
testEvalToBe("x=1; x", "Ok(1)")
|
|
testEvalToBe("x=1+1; x+1", "Ok(3)")
|
|
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
|
|
testEvalToBe("1; x=1", "Error(Assignment expected)")
|
|
testEvalToBe("1; 1", "Error(Assignment expected)")
|
|
testEvalToBe("x=1; x=1", "Error(Expression expected)")
|
|
})
|
|
})
|
|
|
|
describe("test exceptions", () => {
|
|
testDescEvalToBe(
|
|
"javascript exception",
|
|
"javascriptraise('div by 0')",
|
|
"Error(JS Exception: Error: 'div by 0')",
|
|
)
|
|
testDescEvalToBe("rescript exception", "rescriptraise()", "Error(TODO: unhandled rescript exception)")
|
|
})
|