project
test remove setResult parse end of outerblock test end of outer block compiles testing bindings tested todo chain bindings topological sort
This commit is contained in:
parent
f1f274127b
commit
2e8e71bbd0
|
@ -1,10 +1,14 @@
|
|||
module ExpressionValue = ReducerInterface.ExternalExpressionValue
|
||||
module ExpressionValue = ReducerInterface.InternalExpressionValue
|
||||
module Expression = Reducer_Expression
|
||||
|
||||
open Jest
|
||||
open Expect
|
||||
|
||||
let expectEvalToBe = (expr: string, answer: string) =>
|
||||
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||
let expectEvalToBe = (sourceCode: string, answer: string) =>
|
||||
Expression.BackCompatible.evaluateString(sourceCode)
|
||||
->ExpressionValue.toStringResult
|
||||
->expect
|
||||
->toBe(answer)
|
||||
|
||||
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
||||
|
||||
|
|
|
@ -3,359 +3,464 @@ open Reducer_Peggy_TestHelpers
|
|||
|
||||
describe("Peggy parse", () => {
|
||||
describe("float", () => {
|
||||
testParse("1.", "{1}")
|
||||
testParse("1.1", "{1.1}")
|
||||
testParse(".1", "{0.1}")
|
||||
testParse("0.1", "{0.1}")
|
||||
testParse("1e1", "{10}")
|
||||
testParse("1e-1", "{0.1}")
|
||||
testParse(".1e1", "{1}")
|
||||
testParse("0.1e1", "{1}")
|
||||
testParse("1.", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("1.1", "{(::$_endOfOuterBlock_$ () 1.1)}")
|
||||
testParse(".1", "{(::$_endOfOuterBlock_$ () 0.1)}")
|
||||
testParse("0.1", "{(::$_endOfOuterBlock_$ () 0.1)}")
|
||||
testParse("1e1", "{(::$_endOfOuterBlock_$ () 10)}")
|
||||
testParse("1e-1", "{(::$_endOfOuterBlock_$ () 0.1)}")
|
||||
testParse(".1e1", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("0.1e1", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
})
|
||||
|
||||
describe("literals operators parenthesis", () => {
|
||||
// Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement
|
||||
testParse("1", "{1}")
|
||||
testParse("'hello'", "{'hello'}")
|
||||
testParse("true", "{true}")
|
||||
testParse("1+2", "{(::add 1 2)}")
|
||||
testParse("add(1,2)", "{(::add 1 2)}")
|
||||
testParse("(1)", "{1}")
|
||||
testParse("(1+2)", "{(::add 1 2)}")
|
||||
testParse("1", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("'hello'", "{(::$_endOfOuterBlock_$ () 'hello')}")
|
||||
testParse("true", "{(::$_endOfOuterBlock_$ () true)}")
|
||||
testParse("1+2", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
|
||||
testParse("add(1,2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
|
||||
testParse("(1)", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("(1+2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
|
||||
})
|
||||
|
||||
describe("unary", () => {
|
||||
testParse("-1", "{(::unaryMinus 1)}")
|
||||
testParse("!true", "{(::not true)}")
|
||||
testParse("1 + -1", "{(::add 1 (::unaryMinus 1))}")
|
||||
testParse("-a[0]", "{(::unaryMinus (::$_atIndex_$ :a 0))}")
|
||||
testParse("!a[0]", "{(::not (::$_atIndex_$ :a 0))}")
|
||||
testParse("-1", "{(::$_endOfOuterBlock_$ () (::unaryMinus 1))}")
|
||||
testParse("!true", "{(::$_endOfOuterBlock_$ () (::not true))}")
|
||||
testParse("1 + -1", "{(::$_endOfOuterBlock_$ () (::add 1 (::unaryMinus 1)))}")
|
||||
testParse("-a[0]", "{(::$_endOfOuterBlock_$ () (::unaryMinus (::$_atIndex_$ :a 0)))}")
|
||||
testParse("!a[0]", "{(::$_endOfOuterBlock_$ () (::not (::$_atIndex_$ :a 0)))}")
|
||||
})
|
||||
|
||||
describe("multiplicative", () => {
|
||||
testParse("1 * 2", "{(::multiply 1 2)}")
|
||||
testParse("1 / 2", "{(::divide 1 2)}")
|
||||
testParse("1 * 2 * 3", "{(::multiply (::multiply 1 2) 3)}")
|
||||
testParse("1 * 2 / 3", "{(::divide (::multiply 1 2) 3)}")
|
||||
testParse("1 / 2 * 3", "{(::multiply (::divide 1 2) 3)}")
|
||||
testParse("1 / 2 / 3", "{(::divide (::divide 1 2) 3)}")
|
||||
testParse("1 * 2 + 3 * 4", "{(::add (::multiply 1 2) (::multiply 3 4))}")
|
||||
testParse("1 * 2 - 3 * 4", "{(::subtract (::multiply 1 2) (::multiply 3 4))}")
|
||||
testParse("1 * 2 .+ 3 * 4", "{(::dotAdd (::multiply 1 2) (::multiply 3 4))}")
|
||||
testParse("1 * 2 .- 3 * 4", "{(::dotSubtract (::multiply 1 2) (::multiply 3 4))}")
|
||||
testParse("1 * 2 + 3 .* 4", "{(::add (::multiply 1 2) (::dotMultiply 3 4))}")
|
||||
testParse("1 * 2 + 3 / 4", "{(::add (::multiply 1 2) (::divide 3 4))}")
|
||||
testParse("1 * 2 + 3 ./ 4", "{(::add (::multiply 1 2) (::dotDivide 3 4))}")
|
||||
testParse("1 * 2 - 3 .* 4", "{(::subtract (::multiply 1 2) (::dotMultiply 3 4))}")
|
||||
testParse("1 * 2 - 3 / 4", "{(::subtract (::multiply 1 2) (::divide 3 4))}")
|
||||
testParse("1 * 2 - 3 ./ 4", "{(::subtract (::multiply 1 2) (::dotDivide 3 4))}")
|
||||
testParse("1 * 2 - 3 * 4^5", "{(::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5)))}")
|
||||
testParse("1 * 2", "{(::$_endOfOuterBlock_$ () (::multiply 1 2))}")
|
||||
testParse("1 / 2", "{(::$_endOfOuterBlock_$ () (::divide 1 2))}")
|
||||
testParse("1 * 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::multiply 1 2) 3))}")
|
||||
testParse("1 * 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::multiply 1 2) 3))}")
|
||||
testParse("1 / 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::divide 1 2) 3))}")
|
||||
testParse("1 / 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::divide 1 2) 3))}")
|
||||
testParse(
|
||||
"1 * 2 + 3 * 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 * 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 .+ 3 * 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 .- 3 * 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 + 3 .* 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 + 3 / 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 + 3 ./ 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 .* 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 / 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 ./ 4",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 * 4^5",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5))))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * 2 - 3 * 4^5^6",
|
||||
"{(::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6)))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6))))}",
|
||||
)
|
||||
testParse(
|
||||
"1 * -a[-2]",
|
||||
"{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}",
|
||||
)
|
||||
testParse("1 * -a[-2]", "{(::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2))))}")
|
||||
})
|
||||
|
||||
describe("multi-line", () => {
|
||||
testParse("x=1; 2", "{:x = {1}; 2}")
|
||||
testParse("x=1; y=2", "{:x = {1}; :y = {2}}")
|
||||
testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}")
|
||||
testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}")
|
||||
})
|
||||
|
||||
describe("variables", () => {
|
||||
testParse("x = 1", "{:x = {1}}")
|
||||
testParse("x", "{:x}")
|
||||
testParse("x = 1; x", "{:x = {1}; :x}")
|
||||
testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}")
|
||||
testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
|
||||
testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}")
|
||||
})
|
||||
|
||||
describe("functions", () => {
|
||||
testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments
|
||||
testParse("identity(x)", "{(::identity :x)}")
|
||||
testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments
|
||||
testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}")
|
||||
})
|
||||
|
||||
describe("arrays", () => {
|
||||
testParse("[]", "{(::$_constructArray_$ ())}")
|
||||
testParse("[0, 1, 2]", "{(::$_constructArray_$ (0 1 2))}")
|
||||
testParse("['hello', 'world']", "{(::$_constructArray_$ ('hello' 'world'))}")
|
||||
testParse("([0,1,2])[1]", "{(::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1)}")
|
||||
testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ()))}")
|
||||
testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ (0 1 2)))}")
|
||||
testParse(
|
||||
"['hello', 'world']",
|
||||
"{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ('hello' 'world')))}",
|
||||
)
|
||||
testParse(
|
||||
"([0,1,2])[1]",
|
||||
"{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1))}",
|
||||
)
|
||||
})
|
||||
|
||||
describe("records", () => {
|
||||
testParse("{a: 1, b: 2}", "{(::$_constructRecord_$ ('a': 1 'b': 2))}")
|
||||
testParse("{1+0: 1, 2+0: 2}", "{(::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2))}") // key can be any expression
|
||||
testParse("record.property", "{(::$_atIndex_$ :record 'property')}")
|
||||
testParse(
|
||||
"{a: 1, b: 2}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}",
|
||||
)
|
||||
testParse(
|
||||
"{1+0: 1, 2+0: 2}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2)))}",
|
||||
) // key can be any expression
|
||||
testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}")
|
||||
})
|
||||
|
||||
describe("post operators", () => {
|
||||
//function call, array and record access are post operators with higher priority than unary operators
|
||||
testParse("a==!b(1)", "{(::equal :a (::not (::b 1)))}")
|
||||
testParse("a==!b[1]", "{(::equal :a (::not (::$_atIndex_$ :b 1)))}")
|
||||
testParse("a==!b.one", "{(::equal :a (::not (::$_atIndex_$ :b 'one')))}")
|
||||
testParse("a==!b(1)", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::b 1))))}")
|
||||
testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}")
|
||||
testParse(
|
||||
"a==!b.one",
|
||||
"{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}",
|
||||
)
|
||||
})
|
||||
|
||||
describe("comments", () => {
|
||||
testParse("1 # This is a line comment", "{1}")
|
||||
testParse("1 // This is a line comment", "{1}")
|
||||
testParse("1 /* This is a multi line comment */", "{1}")
|
||||
testParse("/* This is a multi line comment */ 1", "{1}")
|
||||
testParse("1 # This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("1 // This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("1 /* This is a multi line comment */", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse("/* This is a multi line comment */ 1", "{(::$_endOfOuterBlock_$ () 1)}")
|
||||
testParse(
|
||||
`
|
||||
/* This is
|
||||
a multi line
|
||||
comment */
|
||||
1`,
|
||||
"{1}",
|
||||
/* This is
|
||||
a multi line
|
||||
comment */
|
||||
1`,
|
||||
"{(::$_endOfOuterBlock_$ () 1)}",
|
||||
)
|
||||
})
|
||||
|
||||
describe("ternary operator", () => {
|
||||
testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}")
|
||||
testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}")
|
||||
testParse(
|
||||
"false ? 2 : false ? 4 : 5",
|
||||
"{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}",
|
||||
) // nested ternary
|
||||
})
|
||||
|
||||
describe("if then else", () => {
|
||||
testParse("if true then 2 else 3", "{(::$$_ternary_$$ true {2} {3})}")
|
||||
testParse("if false then {2} else {3}", "{(::$$_ternary_$$ false {2} {3})}")
|
||||
testParse(
|
||||
"if true then 2 else 3",
|
||||
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}",
|
||||
)
|
||||
testParse(
|
||||
"if false then {2} else {3}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} {3}))}",
|
||||
)
|
||||
testParse(
|
||||
"if false then {2} else if false then {4} else {5}",
|
||||
"{(::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5}))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5})))}",
|
||||
) //nested if
|
||||
})
|
||||
|
||||
describe("logical", () => {
|
||||
testParse("true || false", "{(::or true false)}")
|
||||
testParse("true && false", "{(::and true false)}")
|
||||
testParse("a * b + c", "{(::add (::multiply :a :b) :c)}") // for comparison
|
||||
testParse("a && b || c", "{(::or (::and :a :b) :c)}")
|
||||
testParse("a && b || c && d", "{(::or (::and :a :b) (::and :c :d))}")
|
||||
testParse("a && !b || c", "{(::or (::and :a (::not :b)) :c)}")
|
||||
testParse("a && b==c || d", "{(::or (::and :a (::equal :b :c)) :d)}")
|
||||
testParse("a && b!=c || d", "{(::or (::and :a (::unequal :b :c)) :d)}")
|
||||
testParse("a && !(b==c) || d", "{(::or (::and :a (::not (::equal :b :c))) :d)}")
|
||||
testParse("a && b>=c || d", "{(::or (::and :a (::largerEq :b :c)) :d)}")
|
||||
testParse("a && !(b>=c) || d", "{(::or (::and :a (::not (::largerEq :b :c))) :d)}")
|
||||
testParse("a && b<=c || d", "{(::or (::and :a (::smallerEq :b :c)) :d)}")
|
||||
testParse("a && b>c || d", "{(::or (::and :a (::larger :b :c)) :d)}")
|
||||
testParse("a && b<c || d", "{(::or (::and :a (::smaller :b :c)) :d)}")
|
||||
testParse("a && b<c[i] || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d)}")
|
||||
testParse("a && b<c.i || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d)}")
|
||||
testParse("a && b<c(i) || d", "{(::or (::and :a (::smaller :b (::c :i))) :d)}")
|
||||
testParse("a && b<1+2 || d", "{(::or (::and :a (::smaller :b (::add 1 2))) :d)}")
|
||||
testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}")
|
||||
testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}")
|
||||
testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison
|
||||
testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}")
|
||||
testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}")
|
||||
testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}")
|
||||
testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}")
|
||||
testParse(
|
||||
"a && b!=c || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && !(b==c) || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b>=c || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && !(b>=c) || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<=c || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smallerEq :b :c)) :d))}",
|
||||
)
|
||||
testParse("a && b>c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::larger :b :c)) :d))}")
|
||||
testParse(
|
||||
"a && b<c || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b :c)) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<c[i] || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<c.i || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<c(i) || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::c :i))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<1+2 || d",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 2))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<1+2*3 || d",
|
||||
"{(::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d)}",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<1+2*-3+4 || d",
|
||||
"{(::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d)}",
|
||||
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d))}",
|
||||
)
|
||||
testParse(
|
||||
"a && b<1+2*3 || d ? true : false",
|
||||
"{(::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false)}",
|
||||
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false))}",
|
||||
)
|
||||
})
|
||||
|
||||
describe("pipe", () => {
|
||||
testParse("1 -> add(2)", "{(::add 1 2)}")
|
||||
testParse("-1 -> add(2)", "{(::add (::unaryMinus 1) 2)}")
|
||||
testParse("-a[1] -> add(2)", "{(::add (::unaryMinus (::$_atIndex_$ :a 1)) 2)}")
|
||||
testParse("-f(1) -> add(2)", "{(::add (::unaryMinus (::f 1)) 2)}")
|
||||
testParse("1 + 2 -> add(3)", "{(::add 1 (::add 2 3))}")
|
||||
testParse("1 -> add(2) * 3", "{(::multiply (::add 1 2) 3)}")
|
||||
testParse("1 -> subtract(2)", "{(::subtract 1 2)}")
|
||||
testParse("-1 -> subtract(2)", "{(::subtract (::unaryMinus 1) 2)}")
|
||||
testParse("1 -> subtract(2) * 3", "{(::multiply (::subtract 1 2) 3)}")
|
||||
testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
|
||||
testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}")
|
||||
testParse(
|
||||
"-a[1] -> add(2)",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::$_atIndex_$ :a 1)) 2))}",
|
||||
)
|
||||
testParse("-f(1) -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::f 1)) 2))}")
|
||||
testParse("1 + 2 -> add(3)", "{(::$_endOfOuterBlock_$ () (::add 1 (::add 2 3)))}")
|
||||
testParse("1 -> add(2) * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::add 1 2) 3))}")
|
||||
testParse("1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract 1 2))}")
|
||||
testParse("-1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract (::unaryMinus 1) 2))}")
|
||||
testParse(
|
||||
"1 -> subtract(2) * 3",
|
||||
"{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}",
|
||||
)
|
||||
})
|
||||
|
||||
describe("elixir pipe", () => {
|
||||
//handled together with -> so there is no need for seperate tests
|
||||
testParse("1 |> add(2)", "{(::add 1 2)}")
|
||||
testParse("1 |> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
|
||||
})
|
||||
|
||||
describe("to", () => {
|
||||
testParse("1 to 2", "{(::credibleIntervalToDistribution 1 2)}")
|
||||
testParse("-1 to -2", "{(::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2))}") // lower than unary
|
||||
testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}")
|
||||
testParse(
|
||||
"-1 to -2",
|
||||
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}",
|
||||
) // lower than unary
|
||||
testParse(
|
||||
"a[1] to a[2]",
|
||||
"{(::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2)))}",
|
||||
) // lower than post
|
||||
testParse(
|
||||
"a.p1 to a.p2",
|
||||
"{(::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2'))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}",
|
||||
) // lower than post
|
||||
testParse("1 to 2 + 3", "{(::add (::credibleIntervalToDistribution 1 2) 3)}") // higher than binary operators
|
||||
testParse(
|
||||
"1 to 2 + 3",
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}",
|
||||
) // higher than binary operators
|
||||
testParse(
|
||||
"1->add(2) to 3->add(4) -> add(4)",
|
||||
"{(::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4))}",
|
||||
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4)))}",
|
||||
) // lower than chain
|
||||
})
|
||||
|
||||
describe("inner block", () => {
|
||||
// inner blocks are 0 argument lambdas. They can be used whenever a value is required.
|
||||
// Like lambdas they have a local scope.
|
||||
testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; :x}")
|
||||
testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; (::$_endOfOuterBlock_$ () :x)}")
|
||||
})
|
||||
|
||||
describe("lambda", () => {
|
||||
testParse("{|x| x}", "{{|:x| {:x}}}")
|
||||
testParse("f={|x| x}", "{:f = {{|:x| {:x}}}}")
|
||||
testParse("f(x)=x", "{:f = {|:x| {:x}}}") // Function definitions are lambda assignments
|
||||
testParse("f(x)=x ? 1 : 0", "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}") // Function definitions are lambda assignments
|
||||
testParse("{|x| x}", "{(::$_endOfOuterBlock_$ () {|:x| {:x}})}")
|
||||
testParse("f={|x| x}", "{:f = {{|:x| {:x}}}; (::$_endOfOuterBlock_$ () ())}")
|
||||
testParse("f(x)=x", "{:f = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions are lambda assignments
|
||||
testParse(
|
||||
"f(x)=x ? 1 : 0",
|
||||
"{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}; (::$_endOfOuterBlock_$ () ())}",
|
||||
) // Function definitions are lambda assignments
|
||||
})
|
||||
|
||||
describe("Using lambda as value", () => {
|
||||
testParse(
|
||||
"myadd(x,y)=x+y; z=myadd; z",
|
||||
"{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; :z}",
|
||||
"{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; (::$_endOfOuterBlock_$ () :z)}",
|
||||
)
|
||||
testParse(
|
||||
"myadd(x,y)=x+y; z=[myadd]; z",
|
||||
"{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ (:myadd))}; :z}",
|
||||
"{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ (:myadd))}; (::$_endOfOuterBlock_$ () :z)}",
|
||||
)
|
||||
testParse(
|
||||
"myaddd(x,y)=x+y; z={x: myaddd}; z",
|
||||
"{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; :z}",
|
||||
"{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; (::$_endOfOuterBlock_$ () :z)}",
|
||||
)
|
||||
testParse("f({|x| x+1})", "{(::$_endOfOuterBlock_$ () (::f {|:x| {(::add :x 1)}}))}")
|
||||
testParse(
|
||||
"map(arr, {|x| x+1})",
|
||||
"{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}",
|
||||
)
|
||||
testParse("f({|x| x+1})", "{(::f {|:x| {(::add :x 1)}})}")
|
||||
testParse("map(arr, {|x| x+1})", "{(::map :arr {|:x| {(::add :x 1)}})}")
|
||||
testParse(
|
||||
"map([1,2,3], {|x| x+1})",
|
||||
"{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}",
|
||||
"{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
|
||||
)
|
||||
testParse(
|
||||
"[1,2,3]->map({|x| x+1})",
|
||||
"{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}",
|
||||
"{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
|
||||
)
|
||||
})
|
||||
describe("unit", () => {
|
||||
testParse("1m", "{(::fromUnit_m 1)}")
|
||||
testParse("1M", "{(::fromUnit_M 1)}")
|
||||
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
|
||||
testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}")
|
||||
testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}")
|
||||
testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}")
|
||||
})
|
||||
describe("Module", () => {
|
||||
testParse("x", "{:x}")
|
||||
testParse("Math.pi", "{:Math.pi}")
|
||||
testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
|
||||
testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}")
|
||||
})
|
||||
})
|
||||
|
||||
describe("parsing new line", () => {
|
||||
testParse(
|
||||
`
|
||||
a +
|
||||
b`,
|
||||
"{(::add :a :b)}",
|
||||
a +
|
||||
b`,
|
||||
"{(::$_endOfOuterBlock_$ () (::add :a :b))}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x=
|
||||
1`,
|
||||
"{:x = {1}}",
|
||||
x=
|
||||
1`,
|
||||
"{:x = {1}; (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x=1
|
||||
y=2`,
|
||||
"{:x = {1}; :y = {2}}",
|
||||
x=1
|
||||
y=2`,
|
||||
"{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x={
|
||||
y=2;
|
||||
y }
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; :x}",
|
||||
x={
|
||||
y=2;
|
||||
y }
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x={
|
||||
y=2
|
||||
y }
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; :x}",
|
||||
x={
|
||||
y=2
|
||||
y }
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x={
|
||||
y=2
|
||||
y
|
||||
}
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; :x}",
|
||||
x={
|
||||
y=2
|
||||
y
|
||||
}
|
||||
x`,
|
||||
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
x=1
|
||||
y=2
|
||||
z=3
|
||||
`,
|
||||
"{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
f={
|
||||
x=1
|
||||
y=2
|
||||
z=3
|
||||
`,
|
||||
"{:x = {1}; :y = {2}; :z = {3}}",
|
||||
x+y+z
|
||||
}
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
f={
|
||||
x=1
|
||||
y=2
|
||||
z=3
|
||||
x+y+z
|
||||
f={
|
||||
x=1
|
||||
y=2
|
||||
z=3
|
||||
x+y+z
|
||||
}
|
||||
g=f+4
|
||||
g
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () :g)}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
f =
|
||||
{
|
||||
x=1; //x
|
||||
y=2 //y
|
||||
z=
|
||||
3
|
||||
x+
|
||||
y+
|
||||
z
|
||||
}
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}}",
|
||||
g =
|
||||
f +
|
||||
4
|
||||
g ->
|
||||
h ->
|
||||
p ->
|
||||
q
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () (::q (::p (::h :g))))}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
f={
|
||||
x=1
|
||||
y=2
|
||||
z=3
|
||||
x+y+z
|
||||
}
|
||||
g=f+4
|
||||
g
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; :g}",
|
||||
a |>
|
||||
b |>
|
||||
c |>
|
||||
d
|
||||
`,
|
||||
"{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
f =
|
||||
{
|
||||
x=1; //x
|
||||
y=2 //y
|
||||
z=
|
||||
3
|
||||
x+
|
||||
y+
|
||||
z
|
||||
}
|
||||
g =
|
||||
f +
|
||||
4
|
||||
g ->
|
||||
h ->
|
||||
p ->
|
||||
q
|
||||
`,
|
||||
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::q (::p (::h :g)))}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
a |>
|
||||
b |>
|
||||
c |>
|
||||
d
|
||||
`,
|
||||
"{(::d (::c (::b :a)))}",
|
||||
)
|
||||
testParse(
|
||||
`
|
||||
a |>
|
||||
b |>
|
||||
c |>
|
||||
d +
|
||||
e
|
||||
`,
|
||||
"{(::add (::d (::c (::b :a))) :e)}",
|
||||
a |>
|
||||
b |>
|
||||
c |>
|
||||
d +
|
||||
e
|
||||
`,
|
||||
"{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}",
|
||||
)
|
||||
})
|
||||
|
|
|
@ -3,77 +3,83 @@ open Reducer_Peggy_TestHelpers
|
|||
|
||||
describe("Peggy parse type", () => {
|
||||
describe("type of", () => {
|
||||
testParse("p: number", "{(::$_typeOf_$ :p #number)}")
|
||||
testParse("p: number", "{(::$_typeOf_$ :p #number); (::$_endOfOuterBlock_$ () ())}")
|
||||
})
|
||||
describe("type alias", () => {
|
||||
testParse("type index=number", "{(::$_typeAlias_$ #index #number)}")
|
||||
testParse(
|
||||
"type index=number",
|
||||
"{(::$_typeAlias_$ #index #number); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type or", () => {
|
||||
testParse(
|
||||
"answer: number|string",
|
||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type function", () => {
|
||||
testParse(
|
||||
"f: number=>number=>number",
|
||||
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
||||
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("high priority contract", () => {
|
||||
testParse(
|
||||
"answer: number<-min<-max(100)|string",
|
||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
"answer: number<-memberOf([1,3,5])",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("low priority contract", () => {
|
||||
testParse(
|
||||
"answer: number | string $ opaque",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type array", () => {
|
||||
testParse("answer: [number]", "{(::$_typeOf_$ :answer (::$_typeArray_$ #number))}")
|
||||
testParse(
|
||||
"answer: [number]",
|
||||
"{(::$_typeOf_$ :answer (::$_typeArray_$ #number)); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type record", () => {
|
||||
testParse(
|
||||
"answer: {a: number, b: string}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type constructor", () => {
|
||||
testParse(
|
||||
"answer: Age(number)",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
"answer: Complex(number, number)",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number)))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
"answer: Person({age: number, name: string})",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string)))))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string))))))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
testParse(
|
||||
"weekend: Saturday | Sunday",
|
||||
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
||||
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ())))))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("type parenthesis", () => {
|
||||
//$ is introduced to avoid parenthesis
|
||||
testParse(
|
||||
"answer: (number|string)<-opaque",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
describe("squiggle expressions in type contracts", () => {
|
||||
testParse(
|
||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
||||
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2))); (::$_endOfOuterBlock_$ () ())}",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -23,13 +23,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
|||
} else {
|
||||
let a2 =
|
||||
rExpr
|
||||
->Result.flatMap(expr =>
|
||||
Expression.reduceExpression(
|
||||
expr,
|
||||
ReducerInterface_StdLib.internalStdLib,
|
||||
ExpressionValue.defaultEnvironment,
|
||||
)
|
||||
)
|
||||
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
|
||||
->Reducer_Helpers.rRemoveDefaultsInternal
|
||||
->ExpressionValue.toStringResultOkless
|
||||
(a1, a2)->expect->toEqual((answer, v))
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
|
||||
open Jest
|
||||
open Reducer_Peggy_TestHelpers
|
||||
|
||||
describe("Peggy Outer Block", () => {
|
||||
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
|
||||
testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ())
|
||||
testToExpression(
|
||||
"x=1; y=2",
|
||||
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
|
||||
~v="()",
|
||||
(),
|
||||
)
|
||||
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
|
||||
testToExpression(
|
||||
"x={a=1; a}; x",
|
||||
"{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
})
|
|
@ -7,101 +7,138 @@ open Reducer_Peggy_TestHelpers
|
|||
describe("Peggy to Expression", () => {
|
||||
describe("literals operators parenthesis", () => {
|
||||
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
|
||||
testToExpression("1", "{1}", ~v="1", ())
|
||||
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
|
||||
testToExpression("true", "{true}", ~v="true", ())
|
||||
testToExpression("1+2", "{(:add 1 2)}", ~v="3", ())
|
||||
testToExpression("add(1,2)", "{(:add 1 2)}", ~v="3", ())
|
||||
testToExpression("(1)", "{1}", ())
|
||||
testToExpression("(1+2)", "{(:add 1 2)}", ())
|
||||
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
|
||||
testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ())
|
||||
testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ())
|
||||
testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
|
||||
testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
|
||||
testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ())
|
||||
testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ())
|
||||
})
|
||||
|
||||
describe("unary", () => {
|
||||
testToExpression("-1", "{(:unaryMinus 1)}", ~v="-1", ())
|
||||
testToExpression("!true", "{(:not true)}", ~v="false", ())
|
||||
testToExpression("1 + -1", "{(:add 1 (:unaryMinus 1))}", ~v="0", ())
|
||||
testToExpression("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}", ())
|
||||
testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ())
|
||||
testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ())
|
||||
testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ())
|
||||
testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ())
|
||||
})
|
||||
|
||||
describe("multi-line", () => {
|
||||
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); 2}", ~v="2", ())
|
||||
testToExpression("x=1; y=2", "{(:$_let_$ :x {1}); (:$_let_$ :y {2})}", ~v="@{x: 1,y: 2}", ())
|
||||
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
|
||||
testToExpression(
|
||||
"x=1; y=2",
|
||||
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("variables", () => {
|
||||
testToExpression("x = 1", "{(:$_let_$ :x {1})}", ~v="@{x: 1}", ())
|
||||
testToExpression("x", "{:x}", ~v=":x", ()) //TODO: value should return error
|
||||
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); :x}", ~v="1", ())
|
||||
testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ())
|
||||
testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error
|
||||
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ())
|
||||
})
|
||||
|
||||
describe("functions", () => {
|
||||
testToExpression(
|
||||
"identity(x) = x",
|
||||
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x}))}",
|
||||
~v="@{identity: lambda(x=>internal code)}",
|
||||
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
) // Function definitions become lambda assignments
|
||||
testToExpression("identity(x)", "{(:identity :x)}", ()) // Note value returns error properly
|
||||
testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly
|
||||
testToExpression(
|
||||
"f(x) = x> 2 ? 0 : 1; f(3)",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:f 3)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:$_endOfOuterBlock_$ () (:f 3))}",
|
||||
~v="0",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("arrays", () => {
|
||||
testToExpression("[]", "{(:$_constructArray_$ ())}", ~v="[]", ())
|
||||
testToExpression("[0, 1, 2]", "{(:$_constructArray_$ (0 1 2))}", ~v="[0,1,2]", ())
|
||||
testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ()))}", ~v="[]", ())
|
||||
testToExpression(
|
||||
"[0, 1, 2]",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ (0 1 2)))}",
|
||||
~v="[0,1,2]",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"['hello', 'world']",
|
||||
"{(:$_constructArray_$ ('hello' 'world'))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ('hello' 'world')))}",
|
||||
~v="['hello','world']",
|
||||
(),
|
||||
)
|
||||
testToExpression("([0,1,2])[1]", "{(:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1)}", ~v="1", ())
|
||||
testToExpression(
|
||||
"([0,1,2])[1]",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1))}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("records", () => {
|
||||
testToExpression(
|
||||
"{a: 1, b: 2}",
|
||||
"{(:$_constructRecord_$ (('a' 1) ('b' 2)))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}",
|
||||
~v="{a: 1,b: 2}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"{1+0: 1, 2+0: 2}",
|
||||
"{(:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2)))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}",
|
||||
(),
|
||||
) // key can be any expression
|
||||
testToExpression("record.property", "{(:$_atIndex_$ :record 'property')}", ())
|
||||
testToExpression(
|
||||
"record.property",
|
||||
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"record={property: 1}; record.property",
|
||||
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_atIndex_$ :record 'property')}",
|
||||
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("comments", () => {
|
||||
testToExpression("1 # This is a line comment", "{1}", ~v="1", ())
|
||||
testToExpression("1 // This is a line comment", "{1}", ~v="1", ())
|
||||
testToExpression("1 /* This is a multi line comment */", "{1}", ~v="1", ())
|
||||
testToExpression("/* This is a multi line comment */ 1", "{1}", ~v="1", ())
|
||||
testToExpression("1 # This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
|
||||
testToExpression("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
|
||||
testToExpression(
|
||||
"1 /* This is a multi line comment */",
|
||||
"{(:$_endOfOuterBlock_$ () 1)}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"/* This is a multi line comment */ 1",
|
||||
"{(:$_endOfOuterBlock_$ () 1)}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("ternary operator", () => {
|
||||
testToExpression("true ? 1 : 0", "{(:$$_ternary_$$ true 1 0)}", ~v="1", ())
|
||||
testToExpression("false ? 1 : 0", "{(:$$_ternary_$$ false 1 0)}", ~v="0", ())
|
||||
testToExpression(
|
||||
"true ? 1 : 0",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"false ? 1 : 0",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}",
|
||||
~v="0",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"true ? 1 : false ? 2 : 0",
|
||||
"{(:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}",
|
||||
~v="1",
|
||||
(),
|
||||
) // nested ternary
|
||||
testToExpression(
|
||||
"false ? 1 : false ? 2 : 0",
|
||||
"{(:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}",
|
||||
~v="0",
|
||||
(),
|
||||
) // nested ternary
|
||||
|
@ -109,21 +146,21 @@ describe("Peggy to Expression", () => {
|
|||
testToExpression(
|
||||
// expression binding
|
||||
"f(a) = a > 5 ? 1 : 0; f(6)",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:f 6)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
// when true binding
|
||||
"f(a) = a > 5 ? a : 0; f(6)",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:f 6)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
|
||||
~v="6",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
// when false binding
|
||||
"f(a) = a < 5 ? 1 : a; f(6)",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:f 6)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}",
|
||||
~v="6",
|
||||
(),
|
||||
)
|
||||
|
@ -131,23 +168,41 @@ describe("Peggy to Expression", () => {
|
|||
})
|
||||
|
||||
describe("if then else", () => {
|
||||
testToExpression("if true then 2 else 3", "{(:$$_ternary_$$ true {2} {3})}", ())
|
||||
testToExpression("if true then {2} else {3}", "{(:$$_ternary_$$ true {2} {3})}", ())
|
||||
testToExpression(
|
||||
"if true then 2 else 3",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"if true then {2} else {3}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"if false then {2} else if false then {4} else {5}",
|
||||
"{(:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5}))}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}",
|
||||
(),
|
||||
) //nested if
|
||||
})
|
||||
|
||||
describe("pipe", () => {
|
||||
testToExpression("1 -> add(2)", "{(:add 1 2)}", ~v="3", ())
|
||||
testToExpression("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}", ~v="1", ()) // note that unary has higher priority naturally
|
||||
testToExpression("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}", ~v="9", ())
|
||||
testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
|
||||
testToExpression(
|
||||
"-1 -> add(2)",
|
||||
"{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}",
|
||||
~v="1",
|
||||
(),
|
||||
) // note that unary has higher priority naturally
|
||||
testToExpression(
|
||||
"1 -> add(2) * 3",
|
||||
"{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}",
|
||||
~v="9",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("elixir pipe", () => {
|
||||
testToExpression("1 |> add(2)", "{(:add 1 2)}", ~v="3", ())
|
||||
testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
|
||||
})
|
||||
|
||||
// see testParse for priorities of to and credibleIntervalToDistribution
|
||||
|
@ -157,30 +212,31 @@ describe("Peggy to Expression", () => {
|
|||
// Like lambdas they have a local scope.
|
||||
testToExpression(
|
||||
"y=99; x={y=1; y}",
|
||||
"{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y})}",
|
||||
~v="@{x: 1,y: 99}",
|
||||
"{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
||||
describe("lambda", () => {
|
||||
testToExpression("{|x| x}", "{(:$$_lambda_$$ [x] {:x})}", ~v="lambda(x=>internal code)", ())
|
||||
testToExpression(
|
||||
"{|x| x}",
|
||||
"{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}",
|
||||
~v="lambda(x=>internal code)",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"f={|x| x}",
|
||||
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})})}",
|
||||
~v="@{f: lambda(x=>internal code)}",
|
||||
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"f(x)=x",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))}",
|
||||
~v="@{f: lambda(x=>internal code)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
) // Function definitions are lambda assignments
|
||||
testToExpression(
|
||||
"f(x)=x ? 1 : 0",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)}))}",
|
||||
~v="@{f: lambda(x=>internal code)}",
|
||||
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
@ -188,12 +244,12 @@ describe("Peggy to Expression", () => {
|
|||
describe("module", () => {
|
||||
// testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
|
||||
// Only.test("stdlibrary", () => {
|
||||
// ReducerInterface_StdLib.internalStdLib
|
||||
// ->IEvBindings
|
||||
// ->InternalExpressionValue.toString
|
||||
// ->expect
|
||||
// ->toBe("")
|
||||
// ReducerInterface_StdLib.internalStdLib
|
||||
// ->IEvBindings
|
||||
// ->InternalExpressionValue.toString
|
||||
// ->expect
|
||||
// ->toBe("")
|
||||
// })
|
||||
testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
|
||||
testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,92 +5,92 @@ describe("Peggy Types to Expression", () => {
|
|||
describe("type of", () => {
|
||||
testToExpression(
|
||||
"p: number",
|
||||
"{(:$_typeOf_$ :p #number)}",
|
||||
~v="@{_typeReferences_: {p: #number}}",
|
||||
"{(:$_typeOf_$ :p #number); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {p: #number}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("type alias", () => {
|
||||
testToExpression(
|
||||
"type index=number",
|
||||
"{(:$_typeAlias_$ #index #number)}",
|
||||
~v="@{_typeAliases_: {index: #number}}",
|
||||
"{(:$_typeAlias_$ #index #number); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeAliases_: {index: #number}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("type or", () => {
|
||||
testToExpression(
|
||||
"answer: number|string|distribution",
|
||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution))))}",
|
||||
~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution)))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("type function", () => {
|
||||
testToExpression(
|
||||
"f: number=>number=>number",
|
||||
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number))))}",
|
||||
~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
|
||||
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number)))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"f: number=>number",
|
||||
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number))))}",
|
||||
~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
|
||||
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number)))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("high priority contract", () => {
|
||||
testToExpression(
|
||||
"answer: number<-min(1)<-max(100)|string",
|
||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
||||
~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string)))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"answer: number<-memberOf([1,3,5])",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5))))}",
|
||||
~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5)))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"answer: number<-min(1)",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1))}",
|
||||
~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1)); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"answer: number<-max(10)",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10))}",
|
||||
~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10)); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"answer: number<-min(1)<-max(10)",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10))}",
|
||||
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10)); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"answer: number<-max(10)<-min(1)",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1))}",
|
||||
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1)); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("low priority contract", () => {
|
||||
testToExpression(
|
||||
"answer: number | string $ opaque",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
||||
~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
|
||||
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string))))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
describe("squiggle expressions in type contracts", () => {
|
||||
testToExpression(
|
||||
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
|
||||
~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
|
||||
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2))); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -3,18 +3,23 @@ open Reducer_Peggy_TestHelpers
|
|||
|
||||
describe("Peggy void", () => {
|
||||
//literal
|
||||
testToExpression("()", "{()}", ~v="()", ())
|
||||
testToExpression("()", "{(:$_endOfOuterBlock_$ () ())}", ~v="()", ())
|
||||
testToExpression(
|
||||
"fn()=1",
|
||||
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1}))}",
|
||||
~v="@{fn: lambda(_=>internal code)}",
|
||||
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{fn: lambda(_=>internal code)}",
|
||||
(),
|
||||
)
|
||||
testToExpression(
|
||||
"fn()=1; fn()",
|
||||
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () (:fn ()))}",
|
||||
~v="1",
|
||||
(),
|
||||
)
|
||||
testToExpression("fn()=1; fn()", "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:fn ())}", ~v="1", ())
|
||||
testToExpression(
|
||||
"fn(a)=(); call fn(1)",
|
||||
"{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)})}",
|
||||
~v="@{_: (),fn: lambda(a=>internal code)}",
|
||||
"{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)}); (:$_endOfOuterBlock_$ () ())}",
|
||||
// ~v="@{_: (),fn: lambda(a=>internal code)}",
|
||||
(),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
|
||||
open Jest
|
||||
open Expect
|
||||
|
@ -13,25 +14,21 @@ let unwrapRecord = rValue =>
|
|||
}
|
||||
)
|
||||
|
||||
let expectParseToBe = (expr: string, answer: string) =>
|
||||
Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer)
|
||||
let expectParseToBe = (code: string, answer: string) =>
|
||||
Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer)
|
||||
|
||||
let expectEvalToBe = (expr: string, answer: string) =>
|
||||
Reducer.evaluate(expr)
|
||||
let expectEvalToBe = (code: string, answer: string) =>
|
||||
Expression.BackCompatible.evaluateStringAsExternal(code)
|
||||
->Reducer_Helpers.rRemoveDefaultsExternal
|
||||
->ExternalExpressionValue.toStringResult
|
||||
->expect
|
||||
->toBe(answer)
|
||||
|
||||
let expectEvalError = (expr: string) =>
|
||||
Reducer.evaluate(expr)->ExternalExpressionValue.toStringResult->expect->toMatch("Error\(")
|
||||
|
||||
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
|
||||
Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
|
||||
->Reducer_Helpers.rRemoveDefaultsExternal
|
||||
let expectEvalError = (code: string) =>
|
||||
Expression.BackCompatible.evaluateStringAsExternal(code)
|
||||
->ExternalExpressionValue.toStringResult
|
||||
->expect
|
||||
->toBe(answer)
|
||||
->toMatch("Error\(")
|
||||
|
||||
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||
let testDescriptionParseToBe = (desc, expr, answer) =>
|
||||
|
@ -40,18 +37,12 @@ let testDescriptionParseToBe = (desc, expr, answer) =>
|
|||
let testEvalError = expr => test(expr, () => expectEvalError(expr))
|
||||
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
||||
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||
|
||||
module MySkip = {
|
||||
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
||||
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
|
||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||
}
|
||||
module MyOnly = {
|
||||
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
|
||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
open Jest
|
||||
open Expect
|
||||
|
||||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module Expression = Reducer_Expression
|
||||
// module ExpressionValue = ReducerInterface.ExpressionValue
|
||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
|
||||
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
|
||||
module Macro = Reducer_Expression_Macro
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module T = Reducer_Expression_T
|
||||
module Bindings = Reducer_Bindings
|
||||
|
||||
let testMacro_ = (
|
||||
tester,
|
||||
|
@ -21,8 +21,8 @@ let testMacro_ = (
|
|||
expr
|
||||
->Macro.expandMacroCall(
|
||||
bindings,
|
||||
InternalExpressionValue.defaultEnvironment,
|
||||
Expression.reduceExpression,
|
||||
ProjectAccessorsT.identityAccessors,
|
||||
Expression.reduceExpressionInProject,
|
||||
)
|
||||
->ExpressionWithContext.toStringResult
|
||||
->expect
|
||||
|
@ -41,8 +41,8 @@ let testMacroEval_ = (
|
|||
expr
|
||||
->Macro.doMacroCall(
|
||||
bindings,
|
||||
InternalExpressionValue.defaultEnvironment,
|
||||
Expression.reduceExpression,
|
||||
ProjectAccessorsT.identityAccessors,
|
||||
Expression.reduceExpressionInProject,
|
||||
)
|
||||
->InternalExpressionValue.toStringResult
|
||||
->expect
|
||||
|
|
|
@ -8,7 +8,7 @@ open Jest
|
|||
open Expect
|
||||
|
||||
let myIevEval = (aTypeSourceCode: string) =>
|
||||
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
|
||||
let myIevEvalToString = (aTypeSourceCode: string) =>
|
||||
myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
|
||||
|
||||
|
@ -19,7 +19,7 @@ let myIevTest = (test, aTypeSourceCode, answer) =>
|
|||
test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
|
||||
|
||||
let myTypeEval = (aTypeSourceCode: string) =>
|
||||
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
|
||||
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
|
||||
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
|
||||
|
||||
let myTypeExpectEqual = (aTypeSourceCode, answer) =>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module Bindings = Reducer_Bindings
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module T = Reducer_Type_T
|
||||
module TypeChecker = Reducer_Type_TypeChecker
|
||||
|
||||
|
@ -13,10 +14,10 @@ let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): re
|
|||
'v,
|
||||
ErrorValue.t,
|
||||
> => {
|
||||
let reducerFn = Expression.reduceExpression
|
||||
let reducerFn = Expression.reduceExpressionInProject
|
||||
let rResult =
|
||||
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||
Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||
reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
|
||||
)
|
||||
rResult->Belt.Result.flatMap(result =>
|
||||
switch result {
|
||||
|
|
|
@ -5,6 +5,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
|||
module Bindings = Reducer_Bindings
|
||||
module T = Reducer_Type_T
|
||||
module TypeChecker = Reducer_Type_TypeChecker
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
|
||||
open Jest
|
||||
open Expect
|
||||
|
@ -16,10 +17,10 @@ let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
|
|||
'v,
|
||||
ErrorValue.t,
|
||||
> => {
|
||||
let reducerFn = Expression.reduceExpression
|
||||
let reducerFn = Expression.reduceExpressionInProject
|
||||
let rResult =
|
||||
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
|
||||
Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
|
||||
reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
|
||||
)
|
||||
rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
open Jest
|
||||
open Reducer_TestHelpers
|
||||
|
||||
describe("Eval with Bindings", () => {
|
||||
testEvalBindingsToBe("x", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(1)")
|
||||
testEvalBindingsToBe("x+1", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||
testParseToBe("y = x+1; y", "Ok({(:$_let_$ :y {(:add :x 1)}); :y})")
|
||||
testEvalBindingsToBe("y = x+1; y", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||
testEvalBindingsToBe(
|
||||
"y = x+1",
|
||||
list{("x", ExternalExpressionValue.EvNumber(1.))},
|
||||
"Ok(@{x: 1,y: 2})",
|
||||
)
|
||||
})
|
|
@ -2,8 +2,14 @@ open Jest
|
|||
open Reducer_TestHelpers
|
||||
|
||||
describe("Parse function assignment", () => {
|
||||
testParseToBe("f(x)=x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))})")
|
||||
testParseToBe("f(x)=2*x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)}))})")
|
||||
testParseToBe(
|
||||
"f(x)=x",
|
||||
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})",
|
||||
)
|
||||
testParseToBe(
|
||||
"f(x)=2*x",
|
||||
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})",
|
||||
)
|
||||
//MathJs does not allow blocks in function definitions
|
||||
})
|
||||
|
||||
|
|
|
@ -39,33 +39,27 @@ describe("symbol not defined", () => {
|
|||
})
|
||||
|
||||
describe("call and bindings", () => {
|
||||
testEvalToBe("f(x)=x+1", "Ok(@{f: lambda(x=>internal code)})")
|
||||
testEvalToBe("f(x)=x+1; f(0)", "Ok(1)")
|
||||
testEvalToBe("f(x)=x+1; f(1)", "Ok(2)")
|
||||
testEvalToBe("f=1;y=2", "Ok(@{f: 1,y: 2})")
|
||||
testEvalToBe("f(x)=x+1; y=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2})")
|
||||
testEvalToBe("f=1;y=2", "Ok(())")
|
||||
testEvalToBe("f(x)=x+1; y=f(1); y", "Ok(2)")
|
||||
testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)")
|
||||
testEvalToBe("f(x)=x+1; y=f(1); z=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2,z: 2})")
|
||||
testEvalToBe(
|
||||
"f(x)=x+1; g(x)=f(x)+1",
|
||||
"Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code)})",
|
||||
)
|
||||
testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)")
|
||||
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)")
|
||||
testParseToBe(
|
||||
"f=99; g(x)=f; g(2)",
|
||||
"Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:g 2)})",
|
||||
"Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:$_endOfOuterBlock_$ () (:g 2))})",
|
||||
)
|
||||
testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)")
|
||||
testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)")
|
||||
testEvalToBe(
|
||||
"f(x)=x+1; g(x)=f(x)+1; y=g(2)",
|
||||
"Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code),y: 4})",
|
||||
)
|
||||
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2); y", "Ok(4)")
|
||||
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)")
|
||||
})
|
||||
|
||||
describe("function tricks", () => {
|
||||
testEvalError("f(x)=f(y)=2; f(2)") //Error because chain assignment is not allowed
|
||||
testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)")
|
||||
testEvalToBe("y=2;g(x)=inspect(y)+1", "Ok(@{g: lambda(x=>internal code),y: 2})")
|
||||
testEvalToBe("y=2;g(x)=inspect(y)+1;y", "Ok(2)")
|
||||
MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout?
|
||||
MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters
|
||||
testEvalToBe("myadd(x,y)=x+y; z=myadd; z", "Ok(lambda(x,y=>internal code))")
|
||||
|
@ -73,10 +67,7 @@ describe("function tricks", () => {
|
|||
})
|
||||
|
||||
describe("lambda in structures", () => {
|
||||
testEvalToBe(
|
||||
"myadd(x,y)=x+y; z=[myadd]",
|
||||
"Ok(@{myadd: lambda(x,y=>internal code),z: [lambda(x,y=>internal code)]})",
|
||||
)
|
||||
testEvalToBe("myadd(x,y)=x+y; z=[myadd]", "Ok(())")
|
||||
testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0]", "Ok(lambda(x,y=>internal code))")
|
||||
testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0](3,2)", "Ok(5)")
|
||||
testEvalToBe("myaddd(x,y)=x+y; z={x: myaddd}; z", "Ok({x: lambda(x,y=>internal code)})")
|
||||
|
|
|
@ -2,7 +2,10 @@ open Jest
|
|||
open Reducer_TestHelpers
|
||||
|
||||
describe("Parse ternary operator", () => {
|
||||
testParseToBe("true ? 'YES' : 'NO'", "Ok({(:$$_ternary_$$ true 'YES' 'NO')})")
|
||||
testParseToBe(
|
||||
"true ? 'YES' : 'NO'",
|
||||
"Ok({(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 'YES' 'NO'))})",
|
||||
)
|
||||
})
|
||||
|
||||
describe("Evaluate ternary operator", () => {
|
||||
|
|
|
@ -48,7 +48,7 @@ describe("eval", () => {
|
|||
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
|
||||
testEvalError("1; x=1")
|
||||
testEvalError("1; 1")
|
||||
testEvalToBe("x=1; x=1", "Ok(@{x: 1})")
|
||||
testEvalToBe("x=1; x=1; x", "Ok(1)")
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -118,28 +118,40 @@ describe("eval on distribution functions", () => {
|
|||
|
||||
describe("parse on distribution functions", () => {
|
||||
describe("power", () => {
|
||||
testParse("normal(5,2) ^ normal(5,1)", "Ok({(:pow (:normal 5 2) (:normal 5 1))})")
|
||||
testParse("3 ^ normal(5,1)", "Ok({(:pow 3 (:normal 5 1))})")
|
||||
testParse("normal(5,2) ^ 3", "Ok({(:pow (:normal 5 2) 3)})")
|
||||
testParse(
|
||||
"normal(5,2) ^ normal(5,1)",
|
||||
"Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})",
|
||||
)
|
||||
testParse("3 ^ normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:pow 3 (:normal 5 1)))})")
|
||||
testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})")
|
||||
})
|
||||
describe("subtraction", () => {
|
||||
testParse("10 - normal(5,1)", "Ok({(:subtract 10 (:normal 5 1))})")
|
||||
testParse("normal(5,1) - 10", "Ok({(:subtract (:normal 5 1) 10)})")
|
||||
testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})")
|
||||
testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})")
|
||||
})
|
||||
describe("pointwise arithmetic expressions", () => {
|
||||
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
|
||||
testParse(
|
||||
~skip=true,
|
||||
"normal(5,2) .- normal(5,1)",
|
||||
"Ok((:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1))))",
|
||||
"Ok((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))",
|
||||
// TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})"
|
||||
)
|
||||
testParse("normal(5,2) .* normal(5,1)", "Ok({(:dotMultiply (:normal 5 2) (:normal 5 1))})")
|
||||
testParse("normal(5,2) ./ normal(5,1)", "Ok({(:dotDivide (:normal 5 2) (:normal 5 1))})")
|
||||
testParse("normal(5,2) .^ normal(5,1)", "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})")
|
||||
testParse(
|
||||
"normal(5,2) .* normal(5,1)",
|
||||
"Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})",
|
||||
)
|
||||
testParse(
|
||||
"normal(5,2) ./ normal(5,1)",
|
||||
"Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})",
|
||||
)
|
||||
testParse(
|
||||
"normal(5,2) .^ normal(5,1)",
|
||||
"Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})",
|
||||
)
|
||||
})
|
||||
describe("equality", () => {
|
||||
testParse("5 == normal(5,2)", "Ok({(:equal 5 (:normal 5 2))})")
|
||||
testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})")
|
||||
})
|
||||
describe("pointwise adding two normals", () => {
|
||||
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
@@warning("-44")
|
||||
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
|
||||
module Project = ReducerProject
|
||||
module Bindings = Reducer_Bindings
|
||||
module Continuation = ReducerInterface_Value_Continuation
|
||||
|
||||
open Jest
|
||||
open Expect
|
||||
open Expect.Operators
|
||||
|
||||
// test("", () => expect(1)->toBe(1))
|
||||
|
||||
let runFetchResult = (project, sourceId) => {
|
||||
Project.run(project, sourceId)
|
||||
Project.getExternalResult(project, sourceId)->ExternalExpressionValue.toStringOptionResult
|
||||
}
|
||||
|
||||
let runFetchBindings = (project, sourceId) => {
|
||||
Project.run(project, sourceId)
|
||||
Project.getExternalBindings(project, sourceId)
|
||||
->ExternalExpressionValue.EvModule
|
||||
->ExternalExpressionValue.toString
|
||||
}
|
||||
|
||||
test("setting continuation", () => {
|
||||
let project = Project.createProject()
|
||||
let privateProject = project->Project.T.Private.castToInternalProject
|
||||
let sampleBindings = Bindings.emptyBindings->Bindings.set("test", IEvVoid)
|
||||
Project.Private.setContinuation(privateProject, "main", sampleBindings)
|
||||
let answer = Project.Private.getContinuation(privateProject, "main")
|
||||
expect(answer)->toBe(sampleBindings)
|
||||
})
|
||||
|
||||
test("test result true", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "main", "true")
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(true)")
|
||||
})
|
||||
|
||||
test("test result false", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "main", "false")
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(false)")
|
||||
})
|
||||
|
||||
test("test library", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "main", "x=Math.pi; x")
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(3.141592653589793)")
|
||||
})
|
||||
|
||||
test("test bindings", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "variables", "myVariable=666")
|
||||
runFetchBindings(project, "variables")->expect->toBe("@{myVariable: 666}")
|
||||
})
|
||||
|
||||
describe("project1", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setSource(project, "first", "x=1")
|
||||
Project.setSource(project, "main", "x")
|
||||
Project.setContinues(project, "main", ["first"])
|
||||
|
||||
test("runOrder", () => {
|
||||
expect(Project.getRunOrder(project)) == ["first", "main"]
|
||||
})
|
||||
test("dependents first", () => {
|
||||
expect(Project.getDependents(project, "first")) == ["main"]
|
||||
})
|
||||
test("dependencies first", () => {
|
||||
expect(Project.getDependencies(project, "first")) == []
|
||||
})
|
||||
test("dependents main", () => {
|
||||
expect(Project.getDependents(project, "main")) == []
|
||||
})
|
||||
test("dependencies main", () => {
|
||||
expect(Project.getDependencies(project, "main")) == ["first"]
|
||||
})
|
||||
test("test result", () => {
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(1)")
|
||||
})
|
||||
test("test bindings", () => {
|
||||
runFetchBindings(project, "main")->expect->toBe("@{x: 1}")
|
||||
})
|
||||
})
|
||||
|
||||
describe("project2", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setContinues(project, "main", ["second"])
|
||||
Project.setContinues(project, "second", ["first"])
|
||||
Project.setSource(project, "first", "x=1")
|
||||
Project.setSource(project, "second", "y=2")
|
||||
Project.setSource(project, "main", "y")
|
||||
|
||||
test("runOrder", () => {
|
||||
expect(Project.getRunOrder(project)) == ["first", "second", "main"]
|
||||
})
|
||||
test("runOrderFor", () => {
|
||||
expect(Project.getRunOrderFor(project, "first")) == ["first"]
|
||||
})
|
||||
test("dependencies first", () => {
|
||||
expect(Project.getDependencies(project, "first")) == []
|
||||
})
|
||||
test("dependents first", () => {
|
||||
expect(Project.getDependents(project, "first")) == ["second", "main"]
|
||||
})
|
||||
test("dependents main", () => {
|
||||
expect(Project.getDependents(project, "main")) == []
|
||||
})
|
||||
test("dependencies main", () => {
|
||||
expect(Project.getDependencies(project, "main")) == ["first", "second"]
|
||||
})
|
||||
test("test result", () => {
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(2)")
|
||||
})
|
||||
test("test bindings", () => {
|
||||
runFetchBindings(project, "main")->expect->toBe("@{x: 1,y: 2}")
|
||||
})
|
||||
})
|
||||
|
||||
describe("project with include", () => {
|
||||
let project = Project.createProject()
|
||||
Project.setContinues(project, "main", ["second"])
|
||||
Project.setContinues(project, "second", ["first"])
|
||||
|
||||
Project.setSource(
|
||||
project,
|
||||
"first",
|
||||
`
|
||||
#include 'common'
|
||||
x=1`,
|
||||
)
|
||||
Project.parseIncludes(project, "first")
|
||||
Project.parseIncludes(project, "first") //The only way of setting includes
|
||||
//Don't forget to parse includes after changing the source
|
||||
|
||||
Project.setSource(project, "common", "common=0")
|
||||
Project.setSource(
|
||||
project,
|
||||
"second",
|
||||
`
|
||||
#include 'common'
|
||||
y=2`,
|
||||
)
|
||||
Project.parseIncludes(project, "second") //The only way of setting includes
|
||||
|
||||
Project.setSource(project, "main", "y")
|
||||
|
||||
test("runOrder", () => {
|
||||
expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"]
|
||||
})
|
||||
|
||||
test("runOrderFor", () => {
|
||||
expect(Project.getRunOrderFor(project, "first")) == ["common", "first"]
|
||||
})
|
||||
|
||||
test("dependencies first", () => {
|
||||
expect(Project.getDependencies(project, "first")) == ["common"]
|
||||
})
|
||||
test("dependents first", () => {
|
||||
expect(Project.getDependents(project, "first")) == ["second", "main"]
|
||||
})
|
||||
test("dependents main", () => {
|
||||
expect(Project.getDependents(project, "main")) == []
|
||||
})
|
||||
test("dependencies main", () => {
|
||||
expect(Project.getDependencies(project, "main")) == ["common", "first", "second"]
|
||||
})
|
||||
test("test result", () => {
|
||||
runFetchResult(project, "main")->expect->toBe("Ok(2)")
|
||||
})
|
||||
test("test bindings", () => {
|
||||
runFetchBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}")
|
||||
})
|
||||
})
|
||||
|
||||
// test bindings
|
||||
//TODO: test continues
|
||||
//TODO: test include
|
|
@ -2,8 +2,12 @@ open Jest
|
|||
open Expect
|
||||
open Reducer_TestHelpers
|
||||
|
||||
let expectEvalToBeOk = (expr: string) =>
|
||||
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
|
||||
let expectEvalToBeOk = (code: string) =>
|
||||
Reducer_Expression.BackCompatible.evaluateStringAsExternal(code)
|
||||
->Reducer_Helpers.rRemoveDefaultsExternal
|
||||
->E.R.isOk
|
||||
->expect
|
||||
->toBe(true)
|
||||
|
||||
let registry = FunctionRegistry_Library.registry
|
||||
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
|
||||
|
@ -88,7 +92,7 @@ describe("FunctionRegistry Library", () => {
|
|||
((fn, example)) => {
|
||||
let responseType =
|
||||
example
|
||||
->Reducer.evaluate
|
||||
->Reducer_Expression.BackCompatible.evaluateStringAsExternal
|
||||
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
|
||||
let expectedOutputType = fn.output |> E.O.toExn("")
|
||||
expect(responseType)->toEqual(Ok(expectedOutputType))
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
||||
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
|
||||
|
||||
|
@ -46,8 +48,8 @@ type fnDefinition = {
|
|||
run: (
|
||||
array<internalExpressionValue>,
|
||||
array<frValue>,
|
||||
GenericDist.env,
|
||||
Reducer_Expression_T.reducerFn,
|
||||
ProjectAccessorsT.t,
|
||||
ProjectReducerFnT.t,
|
||||
) => result<internalExpressionValue, string>,
|
||||
}
|
||||
|
||||
|
@ -382,12 +384,12 @@ module FnDefinition = {
|
|||
let run = (
|
||||
t: t,
|
||||
args: array<internalExpressionValue>,
|
||||
env: GenericDist.env,
|
||||
reducer: Reducer_Expression_T.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||
switch argValues {
|
||||
| Some(values) => t.run(args, values, env, reducer)
|
||||
| Some(values) => t.run(args, values, accessors, reducer)
|
||||
| None => Error("Incorrect Types")
|
||||
}
|
||||
}
|
||||
|
@ -493,8 +495,8 @@ module Registry = {
|
|||
~registry: registry,
|
||||
~fnName: string,
|
||||
~args: array<internalExpressionValue>,
|
||||
~env: GenericDist.env,
|
||||
~reducer: Reducer_Expression_T.reducerFn,
|
||||
~accessors: ProjectAccessorsT.t,
|
||||
~reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
|
||||
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
|
||||
|
@ -512,7 +514,7 @@ module Registry = {
|
|||
|
||||
switch Matcher.Registry.findMatches(modified, fnName, args) {
|
||||
| Matcher.Match.FullMatch(match) =>
|
||||
match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env, reducer))
|
||||
match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, accessors, reducer))
|
||||
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
|
||||
| _ => None
|
||||
}
|
||||
|
@ -521,10 +523,10 @@ module Registry = {
|
|||
let dispatch = (
|
||||
registry,
|
||||
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
|
||||
env,
|
||||
reducer: Reducer_Expression_T.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
_matchAndRun(~registry, ~fnName, ~args, ~env, ~reducer)->E.O2.fmap(
|
||||
_matchAndRun(~registry, ~fnName, ~args, ~accessors, ~reducer)->E.O2.fmap(
|
||||
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||
~run=(_, inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(_, inputs, accessors, _) =>
|
||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=accessors.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -31,8 +31,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||
~run=(_, inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(_, inputs, accessors, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber
|
||||
->process(~fn, ~env=accessors.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -41,8 +43,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||
~run=(_, inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(_, inputs, accessors, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber
|
||||
->process(~fn, ~env=accessors.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -58,8 +62,8 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber],
|
||||
~run=(_, inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||
~run=(_, inputs, accessors, _) =>
|
||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=accessors.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
|
||||
open FunctionRegistry_Core
|
||||
open FunctionRegistry_Helpers
|
||||
|
||||
|
@ -24,17 +27,19 @@ module Internals = {
|
|||
Belt.Array.reverse(array),
|
||||
)
|
||||
|
||||
let map = (array: array<internalExpressionValue>, environment, eLambdaValue, reducer): result<
|
||||
ReducerInterface_InternalExpressionValue.t,
|
||||
Reducer_ErrorValue.errorValue,
|
||||
> => {
|
||||
let map = (
|
||||
array: array<internalExpressionValue>,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
eLambdaValue,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue> => {
|
||||
let rMappedList = array->E.A.reduceReverse(Ok(list{}), (rAcc, elem) =>
|
||||
rAcc->E.R.bind(_, acc => {
|
||||
let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
|
||||
eLambdaValue,
|
||||
list{elem},
|
||||
environment,
|
||||
reducer,
|
||||
(accessors: ProjectAccessorsT.t),
|
||||
(reducer: ProjectReducerFnT.t),
|
||||
)
|
||||
rNewElem->E.R2.fmap(newElem => list{newElem, ...acc})
|
||||
})
|
||||
|
@ -42,29 +47,46 @@ module Internals = {
|
|||
rMappedList->E.R2.fmap(mappedList => mappedList->Belt.List.toArray->Wrappers.evArray)
|
||||
}
|
||||
|
||||
let reduce = (aValueArray, initialValue, aLambdaValue, environment, reducer) => {
|
||||
let reduce = (
|
||||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
aValueArray->E.A.reduce(Ok(initialValue), (rAcc, elem) =>
|
||||
rAcc->E.R.bind(_, acc =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer)
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let reduceReverse = (aValueArray, initialValue, aLambdaValue, environment, reducer) => {
|
||||
let reduceReverse = (
|
||||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
aValueArray->Belt.Array.reduceReverse(Ok(initialValue), (rAcc, elem) =>
|
||||
rAcc->Belt.Result.flatMap(acc =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer)
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let filter = (aValueArray, aLambdaValue, environment, reducer) => {
|
||||
let filter = (
|
||||
aValueArray,
|
||||
aLambdaValue,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) =>
|
||||
rAcc->E.R.bind(_, acc => {
|
||||
let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
|
||||
aLambdaValue,
|
||||
list{elem},
|
||||
environment,
|
||||
accessors,
|
||||
reducer,
|
||||
)
|
||||
rNewElem->E.R2.fmap(newElem => {
|
||||
|
@ -189,10 +211,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="map",
|
||||
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
|
||||
switch inputs {
|
||||
| [IEvArray(array), IEvLambda(lambda)] =>
|
||||
Internals.map(array, env, lambda, reducer)->E.R2.errMap(_ => "Error!")
|
||||
Internals.map(array, accessors, lambda, reducer)->E.R2.errMap(_ => "Error!")
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -209,10 +231,12 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="reduce",
|
||||
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
|
||||
switch inputs {
|
||||
| [IEvArray(array), initialValue, IEvLambda(lambda)] =>
|
||||
Internals.reduce(array, initialValue, lambda, env, reducer)->E.R2.errMap(_ => "Error!")
|
||||
Internals.reduce(array, initialValue, lambda, accessors, reducer)->E.R2.errMap(_ =>
|
||||
"Error!"
|
||||
)
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -229,12 +253,16 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="reduceReverse",
|
||||
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
|
||||
switch inputs {
|
||||
| [IEvArray(array), initialValue, IEvLambda(lambda)] =>
|
||||
Internals.reduceReverse(array, initialValue, lambda, env, reducer)->E.R2.errMap(_ =>
|
||||
"Error!"
|
||||
)
|
||||
Internals.reduceReverse(
|
||||
array,
|
||||
initialValue,
|
||||
lambda,
|
||||
accessors,
|
||||
reducer,
|
||||
)->E.R2.errMap(_ => "Error!")
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -251,10 +279,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="filter",
|
||||
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
|
||||
switch inputs {
|
||||
| [IEvArray(array), IEvLambda(lambda)] =>
|
||||
Internals.filter(array, lambda, env, reducer)->E.R2.errMap(_ => "Error!")
|
||||
Internals.filter(array, lambda, accessors, reducer)->E.R2.errMap(_ => "Error!")
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
|
|
@ -34,13 +34,13 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(_, inputs, env, _) =>
|
||||
~run=(_, inputs, accessors, _) =>
|
||||
switch inputs {
|
||||
| [FRValueDist(dist)] =>
|
||||
GenericDist.toPointSet(
|
||||
dist,
|
||||
~xyPointLength=env.xyPointLength,
|
||||
~sampleCount=env.sampleCount,
|
||||
~xyPointLength=accessors.environment.xyPointLength,
|
||||
~sampleCount=accessors.environment.sampleCount,
|
||||
(),
|
||||
)
|
||||
->E.R2.fmap(Wrappers.pointSet)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
open FunctionRegistry_Core
|
||||
open FunctionRegistry_Helpers
|
||||
|
||||
|
@ -6,8 +8,14 @@ let requiresNamespace = true
|
|||
|
||||
module Internal = {
|
||||
type t = SampleSetDist.t
|
||||
let doLambdaCall = (aLambdaValue, list, environment, reducer) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) {
|
||||
|
||||
let doLambdaCall = (
|
||||
aLambdaValue,
|
||||
list,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
|
||||
| Ok(IEvNumber(f)) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
@ -22,29 +30,26 @@ module Internal = {
|
|||
}
|
||||
|
||||
//TODO: I don't know why this seems to need at least one input
|
||||
let fromFn = (
|
||||
aLambdaValue,
|
||||
env: ReducerInterface_InternalExpressionValue.environment,
|
||||
reducer,
|
||||
) => {
|
||||
let sampleCount = env.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer)
|
||||
let fromFn = (aLambdaValue, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => {
|
||||
let sampleCount = accessors.environment.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer)
|
||||
Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen
|
||||
}
|
||||
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, env, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer)
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer)
|
||||
SampleSetDist.samplesMap(~fn, sampleSetDist)->toType
|
||||
}
|
||||
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, env, reducer) => {
|
||||
let fn = (a, b) => doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, env, reducer)
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
|
||||
let fn = (a, b) =>
|
||||
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, accessors, reducer)
|
||||
SampleSetDist.map2(~fn, ~t1, ~t2)->toType
|
||||
}
|
||||
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, env, reducer) => {
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
|
||||
let fn = (a, b, c) =>
|
||||
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, env, reducer)
|
||||
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, accessors, reducer)
|
||||
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
||||
}
|
||||
|
||||
|
@ -59,14 +64,19 @@ module Internal = {
|
|||
E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr))
|
||||
}
|
||||
|
||||
let mapN = (aValueArray: array<internalExpressionValue>, aLambdaValue, env, reducer) => {
|
||||
let mapN = (
|
||||
aValueArray: array<internalExpressionValue>,
|
||||
aLambdaValue,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer,
|
||||
) => {
|
||||
switch parseSampleSetArray(aValueArray) {
|
||||
| Some(t1) =>
|
||||
let fn = a =>
|
||||
doLambdaCall(
|
||||
aLambdaValue,
|
||||
list{IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))},
|
||||
env,
|
||||
accessors,
|
||||
reducer,
|
||||
)
|
||||
SampleSetDist.mapN(~fn, ~t1)->toType
|
||||
|
@ -86,10 +96,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(_, inputs, env, _) =>
|
||||
~run=(_, inputs, accessors: ProjectAccessorsT.t, _) =>
|
||||
switch inputs {
|
||||
| [FRValueDist(dist)] =>
|
||||
GenericDist.toSampleSetDist(dist, env.sampleCount)
|
||||
GenericDist.toSampleSetDist(dist, accessors.environment.sampleCount)
|
||||
->E.R2.fmap(Wrappers.sampleSet)
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(_ => "")
|
||||
|
@ -153,10 +163,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="fromFn",
|
||||
~inputs=[FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
|
||||
switch inputs {
|
||||
| [IEvLambda(lambda)] =>
|
||||
switch Internal.fromFn(lambda, env, reducer) {
|
||||
switch Internal.fromFn(lambda, accessors, reducer) {
|
||||
| Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution)
|
||||
| Error(_) => Error("issue")
|
||||
}
|
||||
|
@ -177,10 +187,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="map",
|
||||
~inputs=[FRTypeDist, FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
|
||||
switch inputs {
|
||||
| [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] =>
|
||||
Internal.map1(dist, lambda, env, reducer)->E.R2.errMap(_ => "")
|
||||
Internal.map1(dist, lambda, accessors, reducer)->E.R2.errMap(_ => "")
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -200,14 +210,14 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="map2",
|
||||
~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) => {
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => {
|
||||
switch inputs {
|
||||
| [
|
||||
IEvDistribution(SampleSet(dist1)),
|
||||
IEvDistribution(SampleSet(dist2)),
|
||||
IEvLambda(lambda),
|
||||
] =>
|
||||
Internal.map2(dist1, dist2, lambda, env, reducer)->E.R2.errMap(_ => "")
|
||||
Internal.map2(dist1, dist2, lambda, accessors, reducer)->E.R2.errMap(_ => "")
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
},
|
||||
|
@ -228,7 +238,7 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="map3",
|
||||
~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
|
||||
switch inputs {
|
||||
| [
|
||||
IEvDistribution(SampleSet(dist1)),
|
||||
|
@ -236,7 +246,7 @@ let library = [
|
|||
IEvDistribution(SampleSet(dist3)),
|
||||
IEvLambda(lambda),
|
||||
] =>
|
||||
Internal.map3(dist1, dist2, dist3, lambda, env, reducer)->E.R2.errMap(_ => "")
|
||||
Internal.map3(dist1, dist2, dist3, lambda, accessors, reducer)->E.R2.errMap(_ => "")
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -256,10 +266,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="mapN",
|
||||
~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda],
|
||||
~run=(inputs, _, env, reducer) =>
|
||||
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
|
||||
switch inputs {
|
||||
| [IEvArray(dists), IEvLambda(lambda)] =>
|
||||
Internal.mapN(dists, lambda, env, reducer)->E.R2.errMap(_e => {
|
||||
Internal.mapN(dists, lambda, accessors, reducer)->E.R2.errMap(_e => {
|
||||
"AHHH doesn't work"
|
||||
})
|
||||
| _ => Error(impossibleError)
|
||||
|
|
|
@ -30,16 +30,16 @@ let library = [
|
|||
("prior", FRTypeDist),
|
||||
]),
|
||||
],
|
||||
~run=(_, inputs, env, _) => {
|
||||
~run=(_, inputs, accessors, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
|
||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), env)
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), accessors.environment)
|
||||
| Ok([
|
||||
FRValueDist(estimate),
|
||||
FRValueDistOrNumber(FRValueNumber(d)),
|
||||
FRValueDist(prior),
|
||||
]) =>
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), env)
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), accessors.environment)
|
||||
| Error(e) => Error(e)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -49,12 +49,12 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="logScore",
|
||||
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||
~run=(_, inputs, env, _) => {
|
||||
~run=(_, inputs, accessors, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
|
||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
|
||||
runScoring(estimate, Score_Dist(d), None, env)
|
||||
runScoring(estimate, Score_Dist(d), None, accessors.environment)
|
||||
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
|
||||
runScoring(estimate, Score_Scalar(d), None, env)
|
||||
runScoring(estimate, Score_Scalar(d), None, accessors.environment)
|
||||
| Error(e) => Error(e)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -74,10 +74,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="klDivergence",
|
||||
~inputs=[FRTypeDist, FRTypeDist],
|
||||
~run=(_, inputs, env, _) => {
|
||||
~run=(_, inputs, accessors, _) => {
|
||||
switch inputs {
|
||||
| [FRValueDist(estimate), FRValueDist(d)] =>
|
||||
runScoring(estimate, Score_Dist(d), None, env)
|
||||
runScoring(estimate, Score_Dist(d), None, accessors.environment)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module Lambda = Reducer_Expression_Lambda
|
||||
|
||||
type environment = ReducerInterface_InternalExpressionValue.environment
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
|
@ -10,26 +8,10 @@ type expressionValue = ExternalExpressionValue.t
|
|||
type externalBindings = ReducerInterface_ExternalExpressionValue.externalBindings
|
||||
type lambdaValue = ExternalExpressionValue.lambdaValue
|
||||
|
||||
let evaluate = Expression.evaluate
|
||||
let evaluateUsingOptions = Expression.evaluateUsingOptions
|
||||
let evaluatePartialUsingExternalBindings = Expression.evaluatePartialUsingExternalBindings
|
||||
let parse = Expression.parse
|
||||
|
||||
let foreignFunctionInterface = (
|
||||
lambdaValue: ExternalExpressionValue.lambdaValue,
|
||||
argArray: array<expressionValue>,
|
||||
environment: ExternalExpressionValue.environment,
|
||||
) => {
|
||||
let internallambdaValue = InternalExpressionValue.lambdaValueToInternal(lambdaValue)
|
||||
let internalArgArray = argArray->Js.Array2.map(InternalExpressionValue.toInternal)
|
||||
Lambda.foreignFunctionInterface(
|
||||
internallambdaValue,
|
||||
internalArgArray,
|
||||
environment,
|
||||
Expression.reduceExpression,
|
||||
)->Belt.Result.map(InternalExpressionValue.toExternal)
|
||||
}
|
||||
/*
|
||||
Use Reducer_Project instead
|
||||
*/
|
||||
|
||||
let defaultEnvironment = ExternalExpressionValue.defaultEnvironment
|
||||
|
||||
let defaultExternalBindings = ReducerInterface_StdLib.externalStdLib
|
||||
// let defaultExternalBindings = ReducerInterface_StdLib.externalStdLib
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
|
||||
@genType
|
||||
@genType0
|
||||
type environment = ReducerInterface_ExternalExpressionValue.environment
|
||||
@genType
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
|
@ -12,34 +12,34 @@ type externalBindings = ReducerInterface_ExternalExpressionValue.externalBinding
|
|||
@genType
|
||||
type lambdaValue = ReducerInterface_ExternalExpressionValue.lambdaValue
|
||||
|
||||
@genType
|
||||
let evaluateUsingOptions: (
|
||||
~environment: option<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment>,
|
||||
~externalBindings: option<
|
||||
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
|
||||
>,
|
||||
string,
|
||||
) => result<expressionValue, errorValue>
|
||||
@genType
|
||||
let evaluatePartialUsingExternalBindings: (
|
||||
string,
|
||||
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
|
||||
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
|
||||
) => result<externalBindings, errorValue>
|
||||
@genType
|
||||
let evaluate: string => result<expressionValue, errorValue>
|
||||
// @genType
|
||||
// let evaluateUsingOptions: (
|
||||
// ~environment: option<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment>,
|
||||
// ~externalBindings: option<
|
||||
// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
|
||||
// >,
|
||||
// string,
|
||||
// ) => result<expressionValue, errorValue>
|
||||
// @genType
|
||||
// let evaluatePartialUsingExternalBindings: (
|
||||
// string,
|
||||
// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
|
||||
// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
|
||||
// ) => result<externalBindings, errorValue>
|
||||
// @genType
|
||||
// let evaluate: string => result<expressionValue, errorValue>
|
||||
|
||||
let parse: string => result<Expression.expression, errorValue>
|
||||
// let parse: string => result<Expression.t, errorValue>
|
||||
|
||||
@genType
|
||||
let foreignFunctionInterface: (
|
||||
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.lambdaValue,
|
||||
array<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.t>,
|
||||
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
|
||||
) => result<expressionValue, errorValue>
|
||||
// @genType
|
||||
// let foreignFunctionInterface: (
|
||||
// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.lambdaValue,
|
||||
// array<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.t>,
|
||||
// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
|
||||
// ) => result<expressionValue, errorValue>
|
||||
|
||||
@genType
|
||||
let defaultEnvironment: environment
|
||||
|
||||
@genType
|
||||
let defaultExternalBindings: externalBindings
|
||||
// @genType
|
||||
// let defaultExternalBindings: externalBindings
|
||||
|
|
|
@ -74,6 +74,7 @@ let set = (nameSpace: t, id: string, value): t => {
|
|||
|
||||
let emptyModule: t = NameSpace(emptyMap)
|
||||
let emptyBindings = emptyModule
|
||||
let emptyNameSpace = emptyModule
|
||||
|
||||
let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
|
||||
let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module Continuation = ReducerInterface_Value_Continuation
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
||||
module Lambda = Reducer_Expression_Lambda
|
||||
module MathJs = Reducer_MathJs
|
||||
module Bindings = Reducer_Bindings
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module Result = Belt.Result
|
||||
module TypeBuilder = Reducer_Type_TypeBuilder
|
||||
|
||||
open ReducerInterface_InternalExpressionValue
|
||||
open Reducer_ErrorValue
|
||||
|
||||
|
@ -19,10 +23,11 @@ open Reducer_ErrorValue
|
|||
|
||||
exception TestRescriptException
|
||||
|
||||
let callInternal = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result<
|
||||
'b,
|
||||
errorValue,
|
||||
> => {
|
||||
let callInternal = (
|
||||
call: functionCall,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<'b, errorValue> => {
|
||||
let callMathJs = (call: functionCall): result<'b, errorValue> =>
|
||||
switch call {
|
||||
| ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
|
||||
|
@ -95,9 +100,17 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
|||
|
||||
let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok
|
||||
|
||||
let doIdentity = (value: internalExpressionValue) => value->Ok
|
||||
|
||||
let doDumpBindings = (continuation: nameSpace, value: internalExpressionValue) => {
|
||||
// let _ = Continuation.inspect(continuation, "doDumpBindings")
|
||||
accessors.continuation = continuation
|
||||
value->Ok
|
||||
}
|
||||
|
||||
module SampleMap = {
|
||||
let doLambdaCall = (aLambdaValue, list) =>
|
||||
switch Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) {
|
||||
switch Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
|
||||
| Ok(IEvNumber(f)) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
@ -137,12 +150,14 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
|||
| ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok
|
||||
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
||||
| ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace)
|
||||
| ("$_exportBindings_$", [evValue]) => doIdentity(evValue)
|
||||
| ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
|
||||
doSetBindings(nameSpace, symbol, value)
|
||||
| ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) =>
|
||||
doSetTypeAliasBindings(nameSpace, symbol, value)
|
||||
| ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
|
||||
doSetTypeOfBindings(nameSpace, symbol, value)
|
||||
| ("$_dumpBindings_$", [IEvBindings(nameSpace), _, evValue]) => doDumpBindings(nameSpace, evValue)
|
||||
| ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) =>
|
||||
TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr))
|
||||
| ("$_typeModifier_memberOf_$", [IEvType(typeRecord), IEvArray(arr)]) =>
|
||||
|
@ -182,15 +197,16 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
|||
/*
|
||||
Reducer uses Result monad while reducing expressions
|
||||
*/
|
||||
let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result<
|
||||
internalExpressionValue,
|
||||
errorValue,
|
||||
> =>
|
||||
let dispatch = (
|
||||
call: functionCall,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<internalExpressionValue, errorValue> =>
|
||||
try {
|
||||
let (fn, args) = call
|
||||
// There is a bug that prevents string match in patterns
|
||||
// So we have to recreate a copy of the string
|
||||
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, reducer, callInternal)
|
||||
ExternalLibrary.dispatch((Js.String.make(fn), args), accessors, reducer, callInternal)
|
||||
} catch {
|
||||
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||
| _ => RETodo("unhandled rescript exception")->Error
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
they take expressions as parameters and return a new expression.
|
||||
Macros are used to define language building blocks. They are like Lisp macros.
|
||||
*/
|
||||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||
module Bindings = Reducer_Bindings
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module Result = Belt.Result
|
||||
|
||||
open Reducer_Expression_ExpressionBuilder
|
||||
|
||||
type environment = InternalExpressionValue.environment
|
||||
type errorValue = ErrorValue.errorValue
|
||||
type expression = ExpressionT.expression
|
||||
type expressionWithContext = ExpressionWithContext.expressionWithContext
|
||||
|
@ -21,11 +23,11 @@ type expressionWithContext = ExpressionWithContext.expressionWithContext
|
|||
let dispatchMacroCall = (
|
||||
macroExpression: expression,
|
||||
bindings: ExpressionT.bindings,
|
||||
environment,
|
||||
reduceExpression: ExpressionT.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reduceExpression: ProjectReducerFnT.t,
|
||||
): result<expressionWithContext, errorValue> => {
|
||||
let useExpressionToSetBindings = (bindingExpr: expression, environment, statement, newCode) => {
|
||||
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
||||
let useExpressionToSetBindings = (bindingExpr: expression, accessors, statement, newCode) => {
|
||||
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, accessors)
|
||||
|
||||
rExternalBindingsValue->Result.flatMap(nameSpaceValue => {
|
||||
let newBindings = Bindings.fromExpressionValue(nameSpaceValue)
|
||||
|
@ -45,16 +47,17 @@ let dispatchMacroCall = (
|
|||
| "$_let_$" => "$_setBindings_$"
|
||||
| "$_typeOf_$" => "$_setTypeOfBindings_$"
|
||||
| "$_typeAlias_$" => "$_setTypeAliasBindings_$"
|
||||
| "$_endOfOuterBlock_$" => "$_dumpBindings_$"
|
||||
| _ => ""
|
||||
}
|
||||
|
||||
let doBindStatement = (bindingExpr: expression, statement: expression, environment) => {
|
||||
let doBindStatement = (bindingExpr: expression, statement: expression, accessors) => {
|
||||
let defaultStatement = ErrorValue.REAssignmentExpected->Error
|
||||
switch statement {
|
||||
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
|
||||
let setBindingsFn = correspondingSetBindingsFn(callName)
|
||||
if setBindingsFn !== "" {
|
||||
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||
useExpressionToSetBindings(bindingExpr, accessors, statement, (
|
||||
newBindingsExpr,
|
||||
boundStatement,
|
||||
) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement}))
|
||||
|
@ -66,12 +69,12 @@ let dispatchMacroCall = (
|
|||
}
|
||||
}
|
||||
|
||||
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result<
|
||||
let doBindExpression = (bindingExpr: expression, statement: expression, accessors): result<
|
||||
expressionWithContext,
|
||||
errorValue,
|
||||
> => {
|
||||
let defaultStatement = () =>
|
||||
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||
useExpressionToSetBindings(bindingExpr, accessors, statement, (
|
||||
_newBindingsExpr,
|
||||
boundStatement,
|
||||
) => boundStatement)
|
||||
|
@ -80,13 +83,13 @@ let dispatchMacroCall = (
|
|||
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
|
||||
let setBindingsFn = correspondingSetBindingsFn(callName)
|
||||
if setBindingsFn !== "" {
|
||||
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||
useExpressionToSetBindings(bindingExpr, accessors, statement, (
|
||||
newBindingsExpr,
|
||||
boundStatement,
|
||||
) =>
|
||||
eFunction(
|
||||
"$_exportBindings_$",
|
||||
list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})},
|
||||
list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})}, // expression returning bindings
|
||||
)
|
||||
)
|
||||
} else {
|
||||
|
@ -97,7 +100,7 @@ let dispatchMacroCall = (
|
|||
}
|
||||
}
|
||||
|
||||
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
|
||||
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _accessors): result<
|
||||
expressionWithContext,
|
||||
errorValue,
|
||||
> => {
|
||||
|
@ -130,10 +133,10 @@ let dispatchMacroCall = (
|
|||
ifTrue: expression,
|
||||
ifFalse: expression,
|
||||
bindings: ExpressionT.bindings,
|
||||
environment,
|
||||
accessors,
|
||||
): result<expressionWithContext, errorValue> => {
|
||||
let blockCondition = ExpressionBuilder.eBlock(list{condition})
|
||||
let rCondition = reduceExpression(blockCondition, bindings, environment)
|
||||
let rCondition = reduceExpression(blockCondition, bindings, accessors)
|
||||
rCondition->Result.flatMap(conditionValue =>
|
||||
switch conditionValue {
|
||||
| InternalExpressionValue.IEvBool(false) => {
|
||||
|
@ -149,7 +152,7 @@ let dispatchMacroCall = (
|
|||
)
|
||||
}
|
||||
|
||||
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result<
|
||||
let expandExpressionList = (aList, bindings: ExpressionT.bindings, accessors): result<
|
||||
expressionWithContext,
|
||||
errorValue,
|
||||
> =>
|
||||
|
@ -159,21 +162,21 @@ let dispatchMacroCall = (
|
|||
bindingExpr: ExpressionT.expression,
|
||||
statement,
|
||||
} =>
|
||||
doBindStatement(bindingExpr, statement, environment)
|
||||
doBindStatement(bindingExpr, statement, accessors)
|
||||
| list{ExpressionT.EValue(IEvCall("$$_bindStatement_$$")), statement} =>
|
||||
// bindings of the context are used when there is no binding expression
|
||||
doBindStatement(eModule(bindings), statement, environment)
|
||||
doBindStatement(eModule(bindings), statement, accessors)
|
||||
| list{
|
||||
ExpressionT.EValue(IEvCall("$$_bindExpression_$$")),
|
||||
bindingExpr: ExpressionT.expression,
|
||||
expression,
|
||||
} =>
|
||||
doBindExpression(bindingExpr, expression, environment)
|
||||
doBindExpression(bindingExpr, expression, accessors)
|
||||
| list{ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), expression} =>
|
||||
// bindings of the context are used when there is no binding expression
|
||||
doBindExpression(eModule(bindings), expression, environment)
|
||||
doBindExpression(eModule(bindings), expression, accessors)
|
||||
| list{ExpressionT.EValue(IEvCall("$$_block_$$")), ...exprs} =>
|
||||
doBlock(exprs, bindings, environment)
|
||||
doBlock(exprs, bindings, accessors)
|
||||
| list{
|
||||
ExpressionT.EValue(IEvCall("$$_lambda_$$")),
|
||||
ExpressionT.EValue(IEvArrayString(parameters)),
|
||||
|
@ -181,12 +184,12 @@ let dispatchMacroCall = (
|
|||
} =>
|
||||
doLambdaDefinition(bindings, parameters, lambdaDefinition)
|
||||
| list{ExpressionT.EValue(IEvCall("$$_ternary_$$")), condition, ifTrue, ifFalse} =>
|
||||
doTernary(condition, ifTrue, ifFalse, bindings, environment)
|
||||
doTernary(condition, ifTrue, ifFalse, bindings, accessors)
|
||||
| _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok
|
||||
}
|
||||
|
||||
switch macroExpression {
|
||||
| EList(aList) => expandExpressionList(aList, bindings, environment)
|
||||
| EList(aList) => expandExpressionList(aList, bindings, accessors)
|
||||
| _ => ExpressionWithContext.noContext(macroExpression)->Ok
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ type location
|
|||
|
||||
@genType
|
||||
type errorValue =
|
||||
| REArityError(option<string>, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting
|
||||
| REArityError(option<string>, int, int)
|
||||
| REArrayIndexNotFound(string, int)
|
||||
| REAssignmentExpected
|
||||
| REDistributionError(DistributionTypes.error)
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module BuiltIn = Reducer_Dispatch_BuiltIn
|
||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
|
||||
module Extra = Reducer_Extra
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module Lambda = Reducer_Expression_Lambda
|
||||
module Macro = Reducer_Expression_Macro
|
||||
module MathJs = Reducer_MathJs
|
||||
module Bindings = Reducer_Bindings
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module Result = Belt.Result
|
||||
module T = Reducer_Expression_T
|
||||
|
||||
type environment = InternalExpressionValue.environment
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
type expression = T.expression
|
||||
type internalExpressionValue = InternalExpressionValue.t
|
||||
type externalExpressionValue = ReducerInterface_ExternalExpressionValue.t
|
||||
type t = expression
|
||||
type t = T.t
|
||||
|
||||
/*
|
||||
Converts a Squigle code to expression
|
||||
Recursively evaluate/reduce the expression (Lisp AST/Lambda calculus)
|
||||
*/
|
||||
let parse = (peggyCode: string): result<t, errorValue> =>
|
||||
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
|
||||
|
||||
/*
|
||||
Recursively evaluate/reduce the expression (Lisp AST)
|
||||
*/
|
||||
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
|
||||
internalExpressionValue,
|
||||
'e,
|
||||
> => {
|
||||
let rec reduceExpressionInProject = (
|
||||
expression: t,
|
||||
continuation: T.bindings,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): result<InternalExpressionValue.t, 'e> => {
|
||||
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
|
||||
switch expression {
|
||||
| T.EValue(value) => value->Ok
|
||||
|
@ -38,41 +32,40 @@ let rec reduceExpression = (expression: t, bindings: T.bindings, environment: en
|
|||
| list{EValue(IEvCall(fName)), ..._args} =>
|
||||
switch Macro.isMacroName(fName) {
|
||||
// A macro expands then reduces itself
|
||||
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression)
|
||||
| false => reduceExpressionList(list, bindings, environment)
|
||||
| true => Macro.doMacroCall(expression, continuation, accessors, reduceExpressionInProject)
|
||||
| false => reduceExpressionList(list, continuation, accessors)
|
||||
}
|
||||
| _ => reduceExpressionList(list, bindings, environment)
|
||||
| _ => reduceExpressionList(list, continuation, accessors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
and reduceExpressionList = (
|
||||
expressions: list<t>,
|
||||
bindings: T.bindings,
|
||||
environment: environment,
|
||||
): result<internalExpressionValue, 'e> => {
|
||||
continuation: T.bindings,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): result<InternalExpressionValue.t, 'e> => {
|
||||
let racc: result<
|
||||
list<internalExpressionValue>,
|
||||
list<InternalExpressionValue.t>,
|
||||
'e,
|
||||
> = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
|
||||
> = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: t) =>
|
||||
racc->Result.flatMap(acc => {
|
||||
each
|
||||
->reduceExpression(bindings, environment)
|
||||
->reduceExpressionInProject(continuation, accessors)
|
||||
->Result.map(newNode => {
|
||||
acc->Belt.List.add(newNode)
|
||||
})
|
||||
})
|
||||
)
|
||||
racc->Result.flatMap(acc => acc->reduceValueList(environment))
|
||||
racc->Result.flatMap(acc => acc->reduceValueList(accessors))
|
||||
}
|
||||
|
||||
/*
|
||||
After reducing each level of expression(Lisp AST), we have a value list to evaluate
|
||||
*/
|
||||
and reduceValueList = (valueList: list<internalExpressionValue>, environment): result<
|
||||
internalExpressionValue,
|
||||
'e,
|
||||
> =>
|
||||
and reduceValueList = (
|
||||
valueList: list<InternalExpressionValue.t>,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): result<InternalExpressionValue.t, 'e> =>
|
||||
switch valueList {
|
||||
| list{IEvCall(fName), ...args} => {
|
||||
let rCheckedArgs = switch fName {
|
||||
|
@ -81,7 +74,10 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
|
|||
}
|
||||
|
||||
rCheckedArgs->Result.flatMap(checkedArgs =>
|
||||
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(environment, reduceExpression)
|
||||
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(
|
||||
accessors,
|
||||
reduceExpressionInProject,
|
||||
)
|
||||
)
|
||||
}
|
||||
| list{IEvLambda(_)} =>
|
||||
|
@ -91,11 +87,11 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
|
|||
->Result.flatMap(reducedValueList =>
|
||||
reducedValueList->Belt.List.toArray->InternalExpressionValue.IEvArray->Ok
|
||||
)
|
||||
| list{IEvLambda(lamdaCall), ...args} =>
|
||||
| list{IEvLambda(lambdaCall), ...args} =>
|
||||
args
|
||||
->Lambda.checkIfReduced
|
||||
->Result.flatMap(checkedArgs =>
|
||||
Lambda.doLambdaCall(lamdaCall, checkedArgs, environment, reduceExpression)
|
||||
Lambda.doLambdaCall(lambdaCall, checkedArgs, accessors, reduceExpressionInProject)
|
||||
)
|
||||
|
||||
| _ =>
|
||||
|
@ -106,53 +102,37 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
|
|||
)
|
||||
}
|
||||
|
||||
let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result<
|
||||
internalExpressionValue,
|
||||
'e,
|
||||
> => reduceExpression(aExpression, bindings, environment)
|
||||
|
||||
let evaluateUsingOptions = (
|
||||
~environment: option<ReducerInterface_ExternalExpressionValue.environment>,
|
||||
~externalBindings: option<ReducerInterface_ExternalExpressionValue.externalBindings>,
|
||||
code: string,
|
||||
): result<externalExpressionValue, errorValue> => {
|
||||
let anEnvironment = Belt.Option.getWithDefault(
|
||||
environment,
|
||||
ReducerInterface_ExternalExpressionValue.defaultEnvironment,
|
||||
)
|
||||
|
||||
let mergedBindings: InternalExpressionValue.nameSpace = Bindings.merge(
|
||||
ReducerInterface_StdLib.internalStdLib,
|
||||
Belt.Option.map(externalBindings, Bindings.fromTypeScriptBindings)->Belt.Option.getWithDefault(
|
||||
Bindings.emptyModule,
|
||||
),
|
||||
)
|
||||
|
||||
parse(code)
|
||||
->Result.flatMap(expr => evalUsingBindingsExpression_(expr, mergedBindings, anEnvironment))
|
||||
->Result.map(ReducerInterface_InternalExpressionValue.toExternal)
|
||||
let reduceReturningBindings = (
|
||||
expression: t,
|
||||
continuation: T.bindings,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): (result<InternalExpressionValue.t, 'e>, T.bindings) => {
|
||||
let result = reduceExpressionInProject(expression, continuation, accessors)
|
||||
(result, accessors.continuation)
|
||||
}
|
||||
|
||||
/*
|
||||
IEvaluates Squiggle code and bindings via Reducer and answers the result
|
||||
*/
|
||||
let evaluate = (code: string): result<externalExpressionValue, errorValue> => {
|
||||
evaluateUsingOptions(~environment=None, ~externalBindings=None, code)
|
||||
}
|
||||
let evaluatePartialUsingExternalBindings = (
|
||||
code: string,
|
||||
externalBindings: ReducerInterface_ExternalExpressionValue.externalBindings,
|
||||
environment: ReducerInterface_ExternalExpressionValue.environment,
|
||||
): result<ReducerInterface_ExternalExpressionValue.externalBindings, errorValue> => {
|
||||
let rAnswer = evaluateUsingOptions(
|
||||
~environment=Some(environment),
|
||||
~externalBindings=Some(externalBindings),
|
||||
code,
|
||||
)
|
||||
switch rAnswer {
|
||||
| Ok(EvModule(externalBindings)) => Ok(externalBindings)
|
||||
| Ok(_) =>
|
||||
Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`, None))
|
||||
| Error(err) => err->Error
|
||||
module BackCompatible = {
|
||||
// Those methods are used to support the existing tests
|
||||
// If they are used outside limited testing context, error location reporting will fail
|
||||
let parse = (peggyCode: string): result<t, errorValue> =>
|
||||
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
|
||||
|
||||
let evaluate = (expression: t): result<InternalExpressionValue.t, errorValue> => {
|
||||
let accessors = ProjectAccessorsT.identityAccessors
|
||||
expression->reduceExpressionInProject(accessors.stdLib, accessors)
|
||||
}
|
||||
|
||||
let evaluateString = (peggyCode: string): result<InternalExpressionValue.t, errorValue> =>
|
||||
parse(peggyCode)->Result.flatMap(evaluate)
|
||||
|
||||
let evaluateAsExternal = (expression: t): result<ExternalExpressionValue.t, errorValue> =>
|
||||
{
|
||||
let accessors = ProjectAccessorsT.identityAccessors
|
||||
expression->reduceExpressionInProject(accessors.stdLib, accessors)
|
||||
}->Result.map(InternalExpressionValue.toExternal)
|
||||
|
||||
let evaluateStringAsExternal = (peggyCode: string): result<
|
||||
ExternalExpressionValue.t,
|
||||
errorValue,
|
||||
> => parse(peggyCode)->Result.flatMap(evaluateAsExternal)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module Result = Belt.Result
|
||||
module Bindings = Reducer_Bindings
|
||||
|
||||
type bindings = ExpressionT.bindings
|
||||
type context = bindings
|
||||
|
@ -11,7 +13,6 @@ type environment = InternalExpressionValue.environment
|
|||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
type expression = ExpressionT.expression
|
||||
type internalExpressionValue = InternalExpressionValue.t
|
||||
type reducerFn = ExpressionT.reducerFn
|
||||
|
||||
type expressionWithContext =
|
||||
| ExpressionWithContext(expression, context)
|
||||
|
@ -20,16 +21,16 @@ type expressionWithContext =
|
|||
let callReducer = (
|
||||
expressionWithContext: expressionWithContext,
|
||||
bindings: bindings,
|
||||
environment: environment,
|
||||
reducer: reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<internalExpressionValue, errorValue> => {
|
||||
switch expressionWithContext {
|
||||
| ExpressionNoContext(expr) =>
|
||||
// Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`)
|
||||
reducer(expr, bindings, environment)
|
||||
reducer(expr, bindings, accessors)
|
||||
| ExpressionWithContext(expr, context) =>
|
||||
// Js.log(`callReducer: context ${Bindings.toString(context)} expr ${ExpressionT.toString(expr)}`)
|
||||
reducer(expr, context, environment)
|
||||
reducer(expr, context, accessors)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module ExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module Bindings = Reducer_Bindings
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module Result = Belt.Result
|
||||
|
||||
type environment = ReducerInterface_InternalExpressionValue.environment
|
||||
type expression = ExpressionT.expression
|
||||
type expressionOrFFI = ExpressionT.expressionOrFFI
|
||||
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
|
||||
|
@ -44,7 +45,13 @@ let checkIfReduced = (args: list<internalExpressionValue>) =>
|
|||
)
|
||||
)
|
||||
|
||||
let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environment, reducer) => {
|
||||
let caseNotFFI = (
|
||||
lambdaValue: ExpressionValue.lambdaValue,
|
||||
expr,
|
||||
args,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => {
|
||||
let parameterList = lambdaValue.parameters->Belt.List.fromArray
|
||||
let zippedParameterList = parameterList->Belt.List.zip(args)
|
||||
let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, (
|
||||
|
@ -52,39 +59,43 @@ let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environm
|
|||
(variable, variableValue),
|
||||
) => acc->Bindings.set(variable, variableValue))
|
||||
let newExpression = ExpressionBuilder.eBlock(list{expr})
|
||||
reducer(newExpression, bindings, environment)
|
||||
reducer(newExpression, bindings, accessors)
|
||||
}
|
||||
|
||||
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, environment) => {
|
||||
ffiFn(args->Belt.List.toArray, environment)
|
||||
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, accessors: ProjectAccessorsT.t) => {
|
||||
ffiFn(args->Belt.List.toArray, accessors.environment)
|
||||
}
|
||||
|
||||
let applyParametersToLambda = (
|
||||
lambdaValue: ExpressionValue.lambdaValue,
|
||||
args,
|
||||
environment,
|
||||
reducer: ExpressionT.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<internalExpressionValue, 'e> => {
|
||||
checkArity(lambdaValue, args)->Result.flatMap(args =>
|
||||
checkIfReduced(args)->Result.flatMap(args => {
|
||||
let exprOrFFI = castInternalCodeToExpression(lambdaValue.body)
|
||||
switch exprOrFFI {
|
||||
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, environment, reducer)
|
||||
| FFI(ffiFn) => caseFFI(ffiFn, args, environment)
|
||||
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, accessors, reducer)
|
||||
| FFI(ffiFn) => caseFFI(ffiFn, args, accessors)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) =>
|
||||
applyParametersToLambda(lambdaValue, args, environment, reducer)
|
||||
let doLambdaCall = (
|
||||
lambdaValue: ExpressionValue.lambdaValue,
|
||||
args,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
) => applyParametersToLambda(lambdaValue, args, accessors, reducer)
|
||||
|
||||
let foreignFunctionInterface = (
|
||||
lambdaValue: ExpressionValue.lambdaValue,
|
||||
argArray: array<internalExpressionValue>,
|
||||
environment: ExpressionValue.environment,
|
||||
reducer: ExpressionT.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
): result<internalExpressionValue, 'e> => {
|
||||
let args = argArray->Belt.List.fromArray
|
||||
applyParametersToLambda(lambdaValue, args, environment, reducer)
|
||||
applyParametersToLambda(lambdaValue, args, accessors, reducer)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ module ExpressionT = Reducer_Expression_T
|
|||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||
module Result = Belt.Result
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
|
||||
type environment = InternalExpressionValue.environment
|
||||
type expression = ExpressionT.expression
|
||||
|
@ -11,34 +13,29 @@ type expressionWithContext = ExpressionWithContext.expressionWithContext
|
|||
let expandMacroCall = (
|
||||
macroExpression: expression,
|
||||
bindings: ExpressionT.bindings,
|
||||
environment: environment,
|
||||
reduceExpression: ExpressionT.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reduceExpression: ProjectReducerFnT.t,
|
||||
): result<expressionWithContext, 'e> =>
|
||||
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
|
||||
macroExpression,
|
||||
bindings,
|
||||
environment,
|
||||
accessors,
|
||||
reduceExpression,
|
||||
)
|
||||
|
||||
let doMacroCall = (
|
||||
macroExpression: expression,
|
||||
bindings: ExpressionT.bindings,
|
||||
environment: environment,
|
||||
reduceExpression: ExpressionT.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reduceExpression: ProjectReducerFnT.t,
|
||||
): result<internalExpressionValue, 'e> =>
|
||||
expandMacroCall(
|
||||
macroExpression,
|
||||
bindings,
|
||||
environment,
|
||||
reduceExpression,
|
||||
(accessors: ProjectAccessorsT.t),
|
||||
(reduceExpression: ProjectReducerFnT.t),
|
||||
)->Result.flatMap(expressionWithContext =>
|
||||
ExpressionWithContext.callReducer(
|
||||
expressionWithContext,
|
||||
bindings,
|
||||
environment,
|
||||
reduceExpression,
|
||||
)
|
||||
ExpressionWithContext.callReducer(expressionWithContext, bindings, accessors, reduceExpression)
|
||||
)
|
||||
|
||||
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")
|
||||
|
|
|
@ -17,6 +17,8 @@ type rec expression =
|
|||
| EValue(internalExpressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
|
||||
and bindings = InternalExpressionValue.nameSpace
|
||||
|
||||
type t = expression
|
||||
|
||||
type reducerFn = (
|
||||
expression,
|
||||
bindings,
|
||||
|
|
|
@ -9,12 +9,25 @@ start
|
|||
|
||||
zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
|
||||
|
||||
// { return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])}
|
||||
// {return [h.nodeVoid()];}
|
||||
outerBlock
|
||||
= statements:array_statements finalExpression: (statementSeparator @expression)?
|
||||
{ if (finalExpression != null) { statements.push(finalExpression) }
|
||||
return h.nodeBlock(statements) }
|
||||
{ if (finalExpression != null)
|
||||
{
|
||||
var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]);
|
||||
statements.push(newFinalExpression);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newFinalStatement = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), h.nodeVoid()]);
|
||||
statements.push(newFinalStatement);
|
||||
}
|
||||
return h.nodeBlock(statements) }
|
||||
/ finalExpression: expression
|
||||
{ return h.nodeBlock([finalExpression])}
|
||||
{
|
||||
var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]);
|
||||
return h.nodeBlock([newFinalExpression])}
|
||||
|
||||
innerBlockOrExpression
|
||||
= quotedInnerBlock
|
||||
|
|
|
@ -1,40 +1,37 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module Bindings = Reducer_Bindings
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module T = Reducer_Type_T
|
||||
|
||||
let ievFromTypeExpression = (
|
||||
typeExpressionSourceCode: string,
|
||||
reducerFn: ExpressionT.reducerFn,
|
||||
reducerFn: ProjectReducerFnT.t,
|
||||
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||
let sIndex = "compiled"
|
||||
let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}`
|
||||
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr => {
|
||||
let rContext = reducerFn(
|
||||
expr,
|
||||
Bindings.emptyBindings,
|
||||
InternalExpressionValue.defaultEnvironment,
|
||||
)
|
||||
Belt.Result.map(rContext, context =>
|
||||
switch context {
|
||||
| IEvBindings(nameSpace) =>
|
||||
switch Bindings.getType(nameSpace, sIndex) {
|
||||
| Some(value) => value
|
||||
| None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
|
||||
}
|
||||
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-raise"))
|
||||
Reducer_Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr => {
|
||||
let accessors = ProjectAccessorsT.identityAccessors
|
||||
let result = reducerFn(expr, Bindings.emptyBindings, accessors)
|
||||
let nameSpace = accessors.continuation
|
||||
|
||||
switch result {
|
||||
| Ok(_) =>
|
||||
switch Bindings.getType(nameSpace, sIndex) {
|
||||
| Some(value) => value->Ok
|
||||
| None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
|
||||
}
|
||||
)
|
||||
| err => err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let fromTypeExpression = (
|
||||
typeExpressionSourceCode: string,
|
||||
reducerFn: ExpressionT.reducerFn,
|
||||
): result<T.t, ErrorValue.t> => {
|
||||
ievFromTypeExpression(
|
||||
(typeExpressionSourceCode: string),
|
||||
(reducerFn: ExpressionT.reducerFn),
|
||||
)->Belt.Result.map(T.fromIEvValue)
|
||||
let fromTypeExpression = (typeExpressionSourceCode: string, reducerFn: ProjectReducerFnT.t): result<
|
||||
T.t,
|
||||
ErrorValue.t,
|
||||
> => {
|
||||
ievFromTypeExpression(typeExpressionSourceCode, reducerFn)->Belt.Result.map(T.fromIEvValue)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
module T = Reducer_Type_T
|
||||
module TypeContracts = Reducer_Type_Contracts
|
||||
open InternalExpressionValue
|
||||
|
@ -124,7 +126,7 @@ let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.typeErrorValue> =
|
|||
let isTypeOf = (
|
||||
typeExpressionSourceCode: string,
|
||||
aValue: InternalExpressionValue.t,
|
||||
reducerFn: ExpressionT.reducerFn,
|
||||
reducerFn: ProjectReducerFnT.t,
|
||||
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||
| Ok(anIType) =>
|
||||
|
@ -152,7 +154,7 @@ let checkITypeArguments = (anIType: T.iType, args: array<InternalExpressionValue
|
|||
let checkArguments = (
|
||||
typeExpressionSourceCode: string,
|
||||
args: array<InternalExpressionValue.t>,
|
||||
reducerFn: ExpressionT.reducerFn,
|
||||
reducerFn: ProjectReducerFnT.t,
|
||||
): result<InternalExpressionValue.t, ErrorValue.t> => {
|
||||
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
|
||||
| Ok(anIType) =>
|
||||
|
|
|
@ -92,6 +92,18 @@ let toStringResult = x =>
|
|||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||
}
|
||||
|
||||
let toStringOptionResult = x =>
|
||||
switch x {
|
||||
| Some(a) => toStringResult(a)
|
||||
| None => `None`
|
||||
}
|
||||
|
||||
let toStringOption = x =>
|
||||
switch x {
|
||||
| Some(a) => toString(a)
|
||||
| None => `None`
|
||||
}
|
||||
|
||||
@genType
|
||||
type environment = GenericDist.env
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
|
||||
type internalExpressionValue = InternalExpressionValue.t
|
||||
|
||||
/*
|
||||
|
@ -6,17 +8,17 @@ type internalExpressionValue = InternalExpressionValue.t
|
|||
*/
|
||||
let dispatch = (
|
||||
call: InternalExpressionValue.functionCall,
|
||||
environment,
|
||||
reducer: Reducer_Expression_T.reducerFn,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
reducer: ProjectReducerFnT.t,
|
||||
chain,
|
||||
): result<internalExpressionValue, 'e> => {
|
||||
E.A.O.firstSomeFn([
|
||||
() => ReducerInterface_GenericDistribution.dispatch(call, environment),
|
||||
() => ReducerInterface_Date.dispatch(call, environment),
|
||||
() => ReducerInterface_Duration.dispatch(call, environment),
|
||||
() => ReducerInterface_Number.dispatch(call, environment),
|
||||
() => FunctionRegistry_Library.dispatch(call, environment, reducer),
|
||||
])->E.O2.defaultFn(() => chain(call, environment, reducer))
|
||||
() => ReducerInterface_GenericDistribution.dispatch(call, accessors.environment),
|
||||
() => ReducerInterface_Date.dispatch(call, accessors.environment),
|
||||
() => ReducerInterface_Duration.dispatch(call, accessors.environment),
|
||||
() => ReducerInterface_Number.dispatch(call, accessors.environment),
|
||||
() => FunctionRegistry_Library.dispatch(call, accessors, reducer),
|
||||
])->E.O2.defaultFn(() => chain(call, accessors, reducer))
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -132,6 +132,12 @@ let toStringResult = x =>
|
|||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||
}
|
||||
|
||||
let toStringOptionResult = x =>
|
||||
switch x {
|
||||
| Some(a) => `${toStringResult(a)})`
|
||||
| None => "None"
|
||||
}
|
||||
|
||||
let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string =>
|
||||
switch codeResult {
|
||||
| Ok(a) => toString(a)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
|
||||
let internalStdLib =
|
||||
let internalStdLib: Bindings.t =
|
||||
Bindings.emptyBindings->SquiggleLibrary_Math.makeBindings->SquiggleLibrary_Versions.makeBindings
|
||||
|
||||
@genType
|
||||
let externalStdLib = internalStdLib->Bindings.toTypeScriptBindings
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
|
||||
type t = InternalExpressionValue.nameSpace
|
||||
|
||||
let toValue = nameSpace => InternalExpressionValue.IEvBindings(nameSpace)
|
||||
let toString = nameSpace => InternalExpressionValue.toString(toValue(nameSpace))
|
||||
let toStringResult = rNameSpace =>
|
||||
Belt.Result.map(rNameSpace, toValue(_))->InternalExpressionValue.toStringResult
|
||||
let toStringOptionResult = orNameSpace =>
|
||||
Belt.Option.map(
|
||||
orNameSpace,
|
||||
Belt.Result.map(_, toValue(_)),
|
||||
)->InternalExpressionValue.toStringOptionResult
|
||||
|
||||
let inspect = (nameSpace, label: string) => Js.log(`${label}: ${toString(nameSpace)}`)
|
||||
|
||||
let inspectOption = (oNameSpace, label: string) =>
|
||||
switch oNameSpace {
|
||||
| Some(nameSpace) => inspect(nameSpace, label)
|
||||
| None => Js.log(`${label}: None`)
|
||||
}
|
||||
|
||||
let minus = (NameSpace(thisContainer): t, NameSpace(thatContainer): t) => {
|
||||
Belt.Map.String.removeMany(
|
||||
thisContainer,
|
||||
Belt.Map.String.keysToArray(thatContainer),
|
||||
)->InternalExpressionValue.NameSpace
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
// TODO: Restore run FFI?
|
||||
// TODO: Auto clean project based on topology
|
||||
|
||||
module Bindings = Reducer_Bindings
|
||||
module Continuation = ReducerInterface_Value_Continuation
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ProjectItem = ReducerProject_ProjectItem
|
||||
module T = ReducerProject_T
|
||||
|
||||
@genType.opaque
|
||||
type project = T.t
|
||||
type t = T.t
|
||||
|
||||
module Private = {
|
||||
type internalProject = T.Private.t
|
||||
type t = T.Private.t
|
||||
|
||||
let getSourceIds = (this: t): array<string> => Belt.Map.String.keysToArray(this["items"])
|
||||
|
||||
let getItem = (this: t, sourceId: string) =>
|
||||
Belt.Map.String.getWithDefault(this["items"], sourceId, ProjectItem.emptyItem)
|
||||
|
||||
let getImmediateDependencies = (this: t, sourceId: string): ProjectItem.T.includesType =>
|
||||
getItem(this, sourceId)->ProjectItem.getImmediateDependencies
|
||||
|
||||
type topologicalSortState = (Belt.Map.String.t<bool>, list<string>)
|
||||
let rec topologicalSortUtil = (
|
||||
this: t,
|
||||
sourceId: string,
|
||||
state: topologicalSortState,
|
||||
): topologicalSortState => {
|
||||
let dependencies = getImmediateDependencies(this, sourceId)->Belt.Result.getWithDefault([])
|
||||
let (visited, stack) = state
|
||||
let myVisited = Belt.Map.String.set(visited, sourceId, true)
|
||||
let (newVisited, newStack) = dependencies->Belt.Array.reduce((myVisited, stack), (
|
||||
(currVisited, currStack),
|
||||
dependency,
|
||||
) => {
|
||||
if !Belt.Map.String.getWithDefault(currVisited, dependency, false) {
|
||||
topologicalSortUtil(this, dependency, (currVisited, currStack))
|
||||
} else {
|
||||
(currVisited, currStack)
|
||||
}
|
||||
})
|
||||
(newVisited, list{sourceId, ...newStack})
|
||||
}
|
||||
|
||||
let getTopologicalSort = (this: t): array<string> => {
|
||||
let (_visited, stack) = getSourceIds(this)->Belt.Array.reduce((Belt.Map.String.empty, list{}), (
|
||||
(currVisited, currStack),
|
||||
currId,
|
||||
) =>
|
||||
if !Belt.Map.String.getWithDefault(currVisited, currId, false) {
|
||||
topologicalSortUtil(this, currId, (currVisited, currStack))
|
||||
} else {
|
||||
(currVisited, currStack)
|
||||
}
|
||||
)
|
||||
Belt.List.reverse(stack)->Belt.List.toArray
|
||||
}
|
||||
|
||||
let getTopologicalSortFor = (this: t, sourceId) => {
|
||||
let runOrder = getTopologicalSort(this)
|
||||
let index = runOrder->Js.Array2.indexOf(sourceId)
|
||||
let after = Belt.Array.sliceToEnd(runOrder, index + 1)
|
||||
let before = Js.Array2.slice(runOrder, ~start=0, ~end_=index + 1)
|
||||
(before, after)
|
||||
}
|
||||
|
||||
let getRunOrder = getTopologicalSort
|
||||
|
||||
let createProject = () => {
|
||||
let this: t = {
|
||||
"items": Belt.Map.String.empty,
|
||||
"stdLib": ReducerInterface_StdLib.internalStdLib,
|
||||
"environment": InternalExpressionValue.defaultEnvironment,
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
let getRunOrderFor = (this: t, sourceId: string) => {
|
||||
let (runOrder, _) = getTopologicalSortFor(this, sourceId)
|
||||
runOrder
|
||||
}
|
||||
|
||||
let getDependencies = (this: t, sourceId: string): array<string> => {
|
||||
let runOrder = getRunOrderFor(this, sourceId)
|
||||
|
||||
let _ = Js.Array2.pop(runOrder)
|
||||
runOrder
|
||||
}
|
||||
|
||||
let getDependents = (this: t, sourceId: string): array<string> => {
|
||||
let (_, dependents) = getTopologicalSortFor(this, sourceId)
|
||||
dependents
|
||||
}
|
||||
|
||||
let rec touchSource = (this: t, sourceId: string): unit => {
|
||||
let item = this->getItem(sourceId)
|
||||
let newItem = ProjectItem.touchSource(item)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
touchDependents(this, sourceId)
|
||||
}
|
||||
and touchDependents = (this: t, sourceId: string): unit => {
|
||||
let _ = getDependents(this, sourceId)->Belt.Array.forEach(_, touchSource(this, _))
|
||||
}
|
||||
|
||||
let getSource = (this: t, sourceId: string): option<string> =>
|
||||
Belt.Map.String.get(this["items"], sourceId)->Belt.Option.map(ProjectItem.getSource)
|
||||
|
||||
let setSource = (this: t, sourceId: string, value: string): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.setSource(value)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
touchDependents(this, sourceId)
|
||||
}
|
||||
|
||||
let clean = (this: t, sourceId: string): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.clean
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let cleanAll = (this: t): unit =>
|
||||
getSourceIds(this)->Belt.Array.forEach(sourceId => clean(this, sourceId))
|
||||
|
||||
let cleanResults = (this: t, sourceId: string): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.cleanResults
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let cleanAllResults = (this: t): unit =>
|
||||
getSourceIds(this)->Belt.Array.forEach(sourceId => cleanResults(this, sourceId))
|
||||
|
||||
let getIncludes = (this: t, sourceId: string): ProjectItem.T.includesType =>
|
||||
this->getItem(sourceId)->ProjectItem.getIncludes
|
||||
|
||||
let setContinues = (this: t, sourceId: string, continues: array<string>): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.setContinues(continues)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
touchSource(this, sourceId)
|
||||
}
|
||||
let getContinues = (this: t, sourceId: string): array<string> =>
|
||||
ProjectItem.getContinues(this->getItem(sourceId))
|
||||
|
||||
let removeContinues = (this: t, sourceId: string): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.removeContinues
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
touchSource(this, sourceId)
|
||||
}
|
||||
|
||||
let getContinuation = (this: t, sourceId: string): ProjectItem.T.continuationArgumentType =>
|
||||
this->getItem(sourceId)->ProjectItem.getContinuation
|
||||
|
||||
let setContinuation = (
|
||||
this: t,
|
||||
sourceId: string,
|
||||
continuation: ProjectItem.T.continuationArgumentType,
|
||||
): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.setContinuation(continuation)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let getResult = (this: t, sourceId: string): ProjectItem.T.resultType =>
|
||||
this->getItem(sourceId)->ProjectItem.getResult
|
||||
|
||||
let setResult = (this: t, sourceId: string, value: ProjectItem.T.resultArgumentType): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.setResult(value)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let getExternalResult = (this: t, sourceId: string): ProjectItem.T.externalResultType =>
|
||||
this->getItem(sourceId)->ProjectItem.getExternalResult
|
||||
|
||||
let parseIncludes = (this: t, sourceId: string): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.parseIncludes
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let rawParse = (this: t, sourceId): unit => {
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.rawParse
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
}
|
||||
|
||||
let getStdLib = (this: t): Reducer_Bindings.t => this["stdLib"]
|
||||
let setStdLib = (this: t, value: Reducer_Bindings.t): unit =>
|
||||
T.Private.setFieldStdLib(this, value)
|
||||
|
||||
let getEnvironment = (this: t): InternalExpressionValue.environment => this["environment"]
|
||||
let setEnvironment = (this: t, value: InternalExpressionValue.environment): unit =>
|
||||
T.Private.setFieldEnvironment(this, value)
|
||||
|
||||
let getExternalBindings = (
|
||||
this: t,
|
||||
sourceId: string,
|
||||
): ProjectItem.T.externalBindingsArgumentType => {
|
||||
let those = this->getContinuation(sourceId)
|
||||
let these = this->getStdLib
|
||||
let ofUser = Continuation.minus(those, these)
|
||||
ofUser->InternalExpressionValue.nameSpaceToTypeScriptBindings
|
||||
}
|
||||
|
||||
let buildProjectAccessors = (this: t): ProjectAccessorsT.t => {
|
||||
continuation: Bindings.emptyBindings,
|
||||
stdLib: getStdLib(this),
|
||||
environment: getEnvironment(this),
|
||||
}
|
||||
|
||||
let doRunWithContinuation = (
|
||||
this: t,
|
||||
sourceId: string,
|
||||
continuation: ProjectItem.T.continuation,
|
||||
): unit => {
|
||||
let accessors = buildProjectAccessors(this)
|
||||
let newItem = this->getItem(sourceId)->ProjectItem.run(continuation, accessors)
|
||||
Belt.Map.String.set(this["items"], sourceId, newItem)->T.Private.setFieldItems(this, _)
|
||||
setContinuation(this, sourceId, accessors.continuation)
|
||||
}
|
||||
|
||||
type runState = (ProjectItem.T.resultArgumentType, ProjectItem.T.continuation)
|
||||
|
||||
let tryRunWithContinuation = (
|
||||
this: t,
|
||||
sourceId: string,
|
||||
(rPrevResult: ProjectItem.T.resultArgumentType, continuation: ProjectItem.T.continuation),
|
||||
): (ProjectItem.T.resultArgumentType, ProjectItem.T.continuation) => {
|
||||
switch getResult(this, sourceId) {
|
||||
| Some(result) => (result, getContinuation(this, sourceId)) // already ran
|
||||
| None =>
|
||||
switch rPrevResult {
|
||||
| Error(error) => {
|
||||
setResult(this, sourceId, Error(error))
|
||||
(Error(error), continuation)
|
||||
}
|
||||
| Ok(_prevResult) => {
|
||||
doRunWithContinuation(this, sourceId, continuation)
|
||||
(
|
||||
getResult(this, sourceId)->Belt.Option.getWithDefault(rPrevResult),
|
||||
getContinuation(this, sourceId),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let runAll = (this: t): unit => {
|
||||
let runOrder = getTopologicalSort(this)
|
||||
let initialState = (Ok(InternalExpressionValue.IEvVoid), getStdLib(this))
|
||||
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) =>
|
||||
tryRunWithContinuation(this, currId, currState)
|
||||
)
|
||||
}
|
||||
|
||||
let run = (this: t, sourceId: string): unit => {
|
||||
let runOrder = getRunOrderFor(this, sourceId)
|
||||
let initialState = (Ok(InternalExpressionValue.IEvVoid), getStdLib(this))
|
||||
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) =>
|
||||
tryRunWithContinuation(this, currId, currState)
|
||||
)
|
||||
}
|
||||
|
||||
let evaluate = (sourceCode: string) => {
|
||||
let project = createProject()
|
||||
setSource(project, "main", sourceCode)
|
||||
runAll(project)
|
||||
(
|
||||
getResult(project, "main")->Belt.Option.getWithDefault(IEvVoid->Ok),
|
||||
getContinuation(project, "main"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
PUBLIC FUNCTIONS
|
||||
*/
|
||||
|
||||
// Create a new this to hold the sources, executables, bindings and other data.
|
||||
// The this is a mutable object for use in TypeScript.
|
||||
let createProject = (): t => Private.createProject()->T.Private.castFromInternalProject
|
||||
|
||||
// Answers the array of existing source ids to enumerate over.
|
||||
let getSourceIds = (this: t): array<string> =>
|
||||
this->T.Private.castToInternalProject->Private.getSourceIds
|
||||
|
||||
// Sets the source for a given source id.
|
||||
let setSource = (this: t, sourceId: string, value: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.setSource(sourceId, value)
|
||||
|
||||
// Gets the source for a given source id.
|
||||
let getSource = (this: t, sourceId: string): option<string> =>
|
||||
this->T.Private.castToInternalProject->Private.getSource(sourceId)
|
||||
|
||||
// Touches the source for a given source id. This forces the dependency graph to be re-evaluated.
|
||||
// Touching source code clears the includes so that they can be reevaluated.
|
||||
let touchSource = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.touchSource(sourceId)
|
||||
|
||||
// Cleans the compilation artifacts for a given source id. The results stay untouched.
|
||||
let clean = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.clean(sourceId)
|
||||
|
||||
// Cleans all compilation artifacts for all the this. The results stay untouched.
|
||||
let cleanAll = (this: t): unit => this->T.Private.castToInternalProject->Private.cleanAll
|
||||
|
||||
// Cleans results. Compilation stays untouched to rerun the source.
|
||||
let cleanResults = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.cleanResults(sourceId)
|
||||
|
||||
// Cleans all results. Compilations stays untouched to rerun the source.
|
||||
let cleanAllResults = (this: t): unit =>
|
||||
this->T.Private.castToInternalProject->Private.cleanAllResults
|
||||
|
||||
let getIncludes = (this: t, sourceId: string): ProjectItem.T.includesType =>
|
||||
this->T.Private.castToInternalProject->Private.getIncludes(sourceId)
|
||||
|
||||
let getContinues = (this: t, sourceId: string): array<string> =>
|
||||
this->T.Private.castToInternalProject->Private.getContinues(sourceId)
|
||||
|
||||
// setContinues acts like an include hidden in the source. It is used to define a continuation.
|
||||
let setContinues = (this: t, sourceId: string, continues: array<string>): unit =>
|
||||
this->T.Private.castToInternalProject->Private.setContinues(sourceId, continues)
|
||||
|
||||
// This source is not continuing any other source. It is a standalone source.
|
||||
// Touches this source also.
|
||||
let removeContinues = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.removeContinues(sourceId)
|
||||
|
||||
// Gets includes and continues for a given source id. SourceId is depending on them
|
||||
let getDependencies = (this: t, sourceId: string): array<string> =>
|
||||
this->T.Private.castToInternalProject->Private.getDependencies(sourceId)
|
||||
|
||||
// Get source ids depending on a given source id.
|
||||
let getDependents = (this: t, sourceId: string): array<string> =>
|
||||
this->T.Private.castToInternalProject->Private.getDependents(sourceId)
|
||||
|
||||
// Get run order for all sources. It is a topological sort of the dependency graph.
|
||||
let getRunOrder = (this: t) => this->T.Private.castToInternalProject->Private.getRunOrder
|
||||
|
||||
// Get run order for a given source id. It is a topological sort of the dependency graph.
|
||||
let getRunOrderFor = (this: t, sourceId: string) =>
|
||||
this->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId)
|
||||
|
||||
// Parse includes so that you can load them before running. Use getIncludes to get the includes.
|
||||
// It is your responsibility to load the includes before running.
|
||||
let parseIncludes = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.parseIncludes(sourceId)
|
||||
|
||||
// Parse the source code if it is not done already. Use getRawParse to get the parse tree
|
||||
let rawParse = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.rawParse(sourceId)
|
||||
|
||||
// Runs the source code.
|
||||
// The code is parsed if it is not already done.
|
||||
// If it continues/includes another source then it will run that source also if is not already done.
|
||||
let run = (this: t, sourceId: string): unit =>
|
||||
this->T.Private.castToInternalProject->Private.run(sourceId)
|
||||
|
||||
// Runs all the sources.
|
||||
let runAll = (this: t): unit => this->T.Private.castToInternalProject->Private.runAll
|
||||
|
||||
// WARNING" getExternalBindings will be deprecated. Cyclic directed graph problems
|
||||
// Get the bindings after running the source code.
|
||||
let getExternalBindings = (this: t, sourceId: string): ExternalExpressionValue.record =>
|
||||
this->T.Private.castToInternalProject->Private.getExternalBindings(sourceId)
|
||||
|
||||
//WARNING: externalResult will be deprecated. Cyclic directed graph problems
|
||||
let getExternalResult = (this: t, sourceId: string): option<
|
||||
result<ExternalExpressionValue.t, Reducer_ErrorValue.errorValue>,
|
||||
> => this->T.Private.castToInternalProject->Private.getExternalResult(sourceId)
|
||||
|
||||
// This is a convenience function to get the result of a single source.
|
||||
// You cannot use includes
|
||||
let evaluate = (sourceCode: string): ('r, 'b) => {
|
||||
let (result, continuation) = Private.evaluate(sourceCode)
|
||||
(
|
||||
result->Belt.Result.map(InternalExpressionValue.toExternal),
|
||||
continuation->InternalExpressionValue.nameSpaceToTypeScriptBindings,
|
||||
)
|
||||
}
|
||||
|
||||
let foreignFunctionInterface = (
|
||||
lambdaValue: ExternalExpressionValue.lambdaValue,
|
||||
argArray: array<ExternalExpressionValue.t>,
|
||||
environment: ExternalExpressionValue.environment,
|
||||
) => {
|
||||
let internallambdaValue = InternalExpressionValue.lambdaValueToInternal(lambdaValue)
|
||||
let internalArgArray = argArray->Js.Array2.map(InternalExpressionValue.toInternal)
|
||||
let accessors = ProjectAccessorsT.identityAccessorsWithEnvironment(environment)
|
||||
Reducer_Expression_Lambda.foreignFunctionInterface(
|
||||
internallambdaValue,
|
||||
internalArgArray,
|
||||
accessors,
|
||||
Reducer_Expression.reduceExpressionInProject,
|
||||
)->Belt.Result.map(InternalExpressionValue.toExternal)
|
||||
}
|
|
@ -0,0 +1,915 @@
|
|||
// Generated by Peggy 2.0.1.
|
||||
//
|
||||
// https://peggyjs.org/
|
||||
|
||||
"use strict";
|
||||
|
||||
function peg$subclass(child, parent) {
|
||||
function C() { this.constructor = child; }
|
||||
C.prototype = parent.prototype;
|
||||
child.prototype = new C();
|
||||
}
|
||||
|
||||
function peg$SyntaxError(message, expected, found, location) {
|
||||
var self = Error.call(this, message);
|
||||
// istanbul ignore next Check is a necessary evil to support older environments
|
||||
if (Object.setPrototypeOf) {
|
||||
Object.setPrototypeOf(self, peg$SyntaxError.prototype);
|
||||
}
|
||||
self.expected = expected;
|
||||
self.found = found;
|
||||
self.location = location;
|
||||
self.name = "SyntaxError";
|
||||
return self;
|
||||
}
|
||||
|
||||
peg$subclass(peg$SyntaxError, Error);
|
||||
|
||||
function peg$padEnd(str, targetLength, padString) {
|
||||
padString = padString || " ";
|
||||
if (str.length > targetLength) { return str; }
|
||||
targetLength -= str.length;
|
||||
padString += padString.repeat(targetLength);
|
||||
return str + padString.slice(0, targetLength);
|
||||
}
|
||||
|
||||
peg$SyntaxError.prototype.format = function(sources) {
|
||||
var str = "Error: " + this.message;
|
||||
if (this.location) {
|
||||
var src = null;
|
||||
var k;
|
||||
for (k = 0; k < sources.length; k++) {
|
||||
if (sources[k].source === this.location.source) {
|
||||
src = sources[k].text.split(/\r\n|\n|\r/g);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var s = this.location.start;
|
||||
var loc = this.location.source + ":" + s.line + ":" + s.column;
|
||||
if (src) {
|
||||
var e = this.location.end;
|
||||
var filler = peg$padEnd("", s.line.toString().length, ' ');
|
||||
var line = src[s.line - 1];
|
||||
var last = s.line === e.line ? e.column : line.length + 1;
|
||||
var hatLen = (last - s.column) || 1;
|
||||
str += "\n --> " + loc + "\n"
|
||||
+ filler + " |\n"
|
||||
+ s.line + " | " + line + "\n"
|
||||
+ filler + " | " + peg$padEnd("", s.column - 1, ' ')
|
||||
+ peg$padEnd("", hatLen, "^");
|
||||
} else {
|
||||
str += "\n at " + loc;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
};
|
||||
|
||||
peg$SyntaxError.buildMessage = function(expected, found) {
|
||||
var DESCRIBE_EXPECTATION_FNS = {
|
||||
literal: function(expectation) {
|
||||
return "\"" + literalEscape(expectation.text) + "\"";
|
||||
},
|
||||
|
||||
class: function(expectation) {
|
||||
var escapedParts = expectation.parts.map(function(part) {
|
||||
return Array.isArray(part)
|
||||
? classEscape(part[0]) + "-" + classEscape(part[1])
|
||||
: classEscape(part);
|
||||
});
|
||||
|
||||
return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]";
|
||||
},
|
||||
|
||||
any: function() {
|
||||
return "any character";
|
||||
},
|
||||
|
||||
end: function() {
|
||||
return "end of input";
|
||||
},
|
||||
|
||||
other: function(expectation) {
|
||||
return expectation.description;
|
||||
}
|
||||
};
|
||||
|
||||
function hex(ch) {
|
||||
return ch.charCodeAt(0).toString(16).toUpperCase();
|
||||
}
|
||||
|
||||
function literalEscape(s) {
|
||||
return s
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/"/g, "\\\"")
|
||||
.replace(/\0/g, "\\0")
|
||||
.replace(/\t/g, "\\t")
|
||||
.replace(/\n/g, "\\n")
|
||||
.replace(/\r/g, "\\r")
|
||||
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
|
||||
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
|
||||
}
|
||||
|
||||
function classEscape(s) {
|
||||
return s
|
||||
.replace(/\\/g, "\\\\")
|
||||
.replace(/\]/g, "\\]")
|
||||
.replace(/\^/g, "\\^")
|
||||
.replace(/-/g, "\\-")
|
||||
.replace(/\0/g, "\\0")
|
||||
.replace(/\t/g, "\\t")
|
||||
.replace(/\n/g, "\\n")
|
||||
.replace(/\r/g, "\\r")
|
||||
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
|
||||
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
|
||||
}
|
||||
|
||||
function describeExpectation(expectation) {
|
||||
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
|
||||
}
|
||||
|
||||
function describeExpected(expected) {
|
||||
var descriptions = expected.map(describeExpectation);
|
||||
var i, j;
|
||||
|
||||
descriptions.sort();
|
||||
|
||||
if (descriptions.length > 0) {
|
||||
for (i = 1, j = 1; i < descriptions.length; i++) {
|
||||
if (descriptions[i - 1] !== descriptions[i]) {
|
||||
descriptions[j] = descriptions[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
descriptions.length = j;
|
||||
}
|
||||
|
||||
switch (descriptions.length) {
|
||||
case 1:
|
||||
return descriptions[0];
|
||||
|
||||
case 2:
|
||||
return descriptions[0] + " or " + descriptions[1];
|
||||
|
||||
default:
|
||||
return descriptions.slice(0, -1).join(", ")
|
||||
+ ", or "
|
||||
+ descriptions[descriptions.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
function describeFound(found) {
|
||||
return found ? "\"" + literalEscape(found) + "\"" : "end of input";
|
||||
}
|
||||
|
||||
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
|
||||
};
|
||||
|
||||
function peg$parse(input, options) {
|
||||
options = options !== undefined ? options : {};
|
||||
|
||||
var peg$FAILED = {};
|
||||
var peg$source = options.grammarSource;
|
||||
|
||||
var peg$startRuleFunctions = { start: peg$parsestart };
|
||||
var peg$startRuleFunction = peg$parsestart;
|
||||
|
||||
var peg$c0 = "#include";
|
||||
var peg$c1 = "'";
|
||||
var peg$c2 = "\"";
|
||||
var peg$c3 = "//";
|
||||
var peg$c4 = "/*";
|
||||
var peg$c5 = "*/";
|
||||
|
||||
var peg$r0 = /^[^']/;
|
||||
var peg$r1 = /^[^"]/;
|
||||
var peg$r2 = /^[^*]/;
|
||||
var peg$r3 = /^[ \t]/;
|
||||
var peg$r4 = /^[\n\r]/;
|
||||
var peg$r5 = /^[^\r\n]/;
|
||||
|
||||
var peg$e0 = peg$literalExpectation("#include", false);
|
||||
var peg$e1 = peg$otherExpectation("string");
|
||||
var peg$e2 = peg$literalExpectation("'", false);
|
||||
var peg$e3 = peg$classExpectation(["'"], true, false);
|
||||
var peg$e4 = peg$literalExpectation("\"", false);
|
||||
var peg$e5 = peg$classExpectation(["\""], true, false);
|
||||
var peg$e6 = peg$literalExpectation("//", false);
|
||||
var peg$e7 = peg$literalExpectation("/*", false);
|
||||
var peg$e8 = peg$classExpectation(["*"], true, false);
|
||||
var peg$e9 = peg$literalExpectation("*/", false);
|
||||
var peg$e10 = peg$otherExpectation("white space");
|
||||
var peg$e11 = peg$classExpectation([" ", "\t"], false, false);
|
||||
var peg$e12 = peg$otherExpectation("newline");
|
||||
var peg$e13 = peg$classExpectation(["\n", "\r"], false, false);
|
||||
var peg$e14 = peg$classExpectation(["\r", "\n"], true, false);
|
||||
|
||||
var peg$f0 = function(head, tail) {return [head, ...tail].filter( e => e != '');};
|
||||
var peg$f1 = function() {return [];};
|
||||
var peg$f2 = function(characters) {return characters.join('');};
|
||||
var peg$f3 = function(characters) {return characters.join('');};
|
||||
var peg$f4 = function() { return '';};
|
||||
var peg$f5 = function() { return '';};
|
||||
var peg$currPos = 0;
|
||||
var peg$savedPos = 0;
|
||||
var peg$posDetailsCache = [{ line: 1, column: 1 }];
|
||||
var peg$maxFailPos = 0;
|
||||
var peg$maxFailExpected = [];
|
||||
var peg$silentFails = 0;
|
||||
|
||||
var peg$resultsCache = {};
|
||||
|
||||
var peg$result;
|
||||
|
||||
if ("startRule" in options) {
|
||||
if (!(options.startRule in peg$startRuleFunctions)) {
|
||||
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
|
||||
}
|
||||
|
||||
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
|
||||
}
|
||||
|
||||
function text() {
|
||||
return input.substring(peg$savedPos, peg$currPos);
|
||||
}
|
||||
|
||||
function offset() {
|
||||
return peg$savedPos;
|
||||
}
|
||||
|
||||
function range() {
|
||||
return {
|
||||
source: peg$source,
|
||||
start: peg$savedPos,
|
||||
end: peg$currPos
|
||||
};
|
||||
}
|
||||
|
||||
function location() {
|
||||
return peg$computeLocation(peg$savedPos, peg$currPos);
|
||||
}
|
||||
|
||||
function expected(description, location) {
|
||||
location = location !== undefined
|
||||
? location
|
||||
: peg$computeLocation(peg$savedPos, peg$currPos);
|
||||
|
||||
throw peg$buildStructuredError(
|
||||
[peg$otherExpectation(description)],
|
||||
input.substring(peg$savedPos, peg$currPos),
|
||||
location
|
||||
);
|
||||
}
|
||||
|
||||
function error(message, location) {
|
||||
location = location !== undefined
|
||||
? location
|
||||
: peg$computeLocation(peg$savedPos, peg$currPos);
|
||||
|
||||
throw peg$buildSimpleError(message, location);
|
||||
}
|
||||
|
||||
function peg$literalExpectation(text, ignoreCase) {
|
||||
return { type: "literal", text: text, ignoreCase: ignoreCase };
|
||||
}
|
||||
|
||||
function peg$classExpectation(parts, inverted, ignoreCase) {
|
||||
return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
|
||||
}
|
||||
|
||||
function peg$anyExpectation() {
|
||||
return { type: "any" };
|
||||
}
|
||||
|
||||
function peg$endExpectation() {
|
||||
return { type: "end" };
|
||||
}
|
||||
|
||||
function peg$otherExpectation(description) {
|
||||
return { type: "other", description: description };
|
||||
}
|
||||
|
||||
function peg$computePosDetails(pos) {
|
||||
var details = peg$posDetailsCache[pos];
|
||||
var p;
|
||||
|
||||
if (details) {
|
||||
return details;
|
||||
} else {
|
||||
p = pos - 1;
|
||||
while (!peg$posDetailsCache[p]) {
|
||||
p--;
|
||||
}
|
||||
|
||||
details = peg$posDetailsCache[p];
|
||||
details = {
|
||||
line: details.line,
|
||||
column: details.column
|
||||
};
|
||||
|
||||
while (p < pos) {
|
||||
if (input.charCodeAt(p) === 10) {
|
||||
details.line++;
|
||||
details.column = 1;
|
||||
} else {
|
||||
details.column++;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
|
||||
peg$posDetailsCache[pos] = details;
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
||||
|
||||
function peg$computeLocation(startPos, endPos) {
|
||||
var startPosDetails = peg$computePosDetails(startPos);
|
||||
var endPosDetails = peg$computePosDetails(endPos);
|
||||
|
||||
return {
|
||||
source: peg$source,
|
||||
start: {
|
||||
offset: startPos,
|
||||
line: startPosDetails.line,
|
||||
column: startPosDetails.column
|
||||
},
|
||||
end: {
|
||||
offset: endPos,
|
||||
line: endPosDetails.line,
|
||||
column: endPosDetails.column
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function peg$fail(expected) {
|
||||
if (peg$currPos < peg$maxFailPos) { return; }
|
||||
|
||||
if (peg$currPos > peg$maxFailPos) {
|
||||
peg$maxFailPos = peg$currPos;
|
||||
peg$maxFailExpected = [];
|
||||
}
|
||||
|
||||
peg$maxFailExpected.push(expected);
|
||||
}
|
||||
|
||||
function peg$buildSimpleError(message, location) {
|
||||
return new peg$SyntaxError(message, null, null, location);
|
||||
}
|
||||
|
||||
function peg$buildStructuredError(expected, found, location) {
|
||||
return new peg$SyntaxError(
|
||||
peg$SyntaxError.buildMessage(expected, found),
|
||||
expected,
|
||||
found,
|
||||
location
|
||||
);
|
||||
}
|
||||
|
||||
function peg$parsestart() {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
var key = peg$currPos * 10 + 0;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parseincludes();
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
s3 = peg$parsenewLine();
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
s3 = peg$parsenewLine();
|
||||
}
|
||||
s3 = peg$parseignore();
|
||||
s0 = s1;
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parseincludes() {
|
||||
var s0, s1, s2, s3, s4, s5;
|
||||
|
||||
var key = peg$currPos * 10 + 1;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parseincludeStatement();
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
s3 = peg$currPos;
|
||||
s4 = [];
|
||||
s5 = peg$parsenewLine();
|
||||
if (s5 !== peg$FAILED) {
|
||||
while (s5 !== peg$FAILED) {
|
||||
s4.push(s5);
|
||||
s5 = peg$parsenewLine();
|
||||
}
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
}
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parseincludeStatement();
|
||||
if (s5 !== peg$FAILED) {
|
||||
s3 = s5;
|
||||
} else {
|
||||
peg$currPos = s3;
|
||||
s3 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s3;
|
||||
s3 = peg$FAILED;
|
||||
}
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
s3 = peg$currPos;
|
||||
s4 = [];
|
||||
s5 = peg$parsenewLine();
|
||||
if (s5 !== peg$FAILED) {
|
||||
while (s5 !== peg$FAILED) {
|
||||
s4.push(s5);
|
||||
s5 = peg$parsenewLine();
|
||||
}
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
}
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parseincludeStatement();
|
||||
if (s5 !== peg$FAILED) {
|
||||
s3 = s5;
|
||||
} else {
|
||||
peg$currPos = s3;
|
||||
s3 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s3;
|
||||
s3 = peg$FAILED;
|
||||
}
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f0(s1, s2);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
if (s0 === peg$FAILED) {
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parseignore();
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f1();
|
||||
s0 = s1;
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parseincludeStatement() {
|
||||
var s0, s1, s2, s3, s4, s5;
|
||||
|
||||
var key = peg$currPos * 10 + 2;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 8) === peg$c0) {
|
||||
s1 = peg$c0;
|
||||
peg$currPos += 8;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e0); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
s3 = peg$parse_();
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
s3 = peg$parse_();
|
||||
}
|
||||
s3 = peg$parsestring();
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = [];
|
||||
s5 = peg$parse_();
|
||||
while (s5 !== peg$FAILED) {
|
||||
s4.push(s5);
|
||||
s5 = peg$parse_();
|
||||
}
|
||||
s0 = s3;
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
if (s0 === peg$FAILED) {
|
||||
s0 = peg$parsecomment();
|
||||
if (s0 === peg$FAILED) {
|
||||
s0 = peg$parsedelimitedComment();
|
||||
}
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsestring() {
|
||||
var s0, s1, s2, s3, s4;
|
||||
|
||||
var key = peg$currPos * 10 + 3;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
peg$silentFails++;
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 39) {
|
||||
s2 = peg$c1;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e2); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = [];
|
||||
if (peg$r0.test(input.charAt(peg$currPos))) {
|
||||
s4 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
}
|
||||
while (s4 !== peg$FAILED) {
|
||||
s3.push(s4);
|
||||
if (peg$r0.test(input.charAt(peg$currPos))) {
|
||||
s4 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e3); }
|
||||
}
|
||||
}
|
||||
if (input.charCodeAt(peg$currPos) === 39) {
|
||||
s4 = peg$c1;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e2); }
|
||||
}
|
||||
if (s4 !== peg$FAILED) {
|
||||
s1 = s3;
|
||||
} else {
|
||||
peg$currPos = s1;
|
||||
s1 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s1;
|
||||
s1 = peg$FAILED;
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f2(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
if (s0 === peg$FAILED) {
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$currPos;
|
||||
if (input.charCodeAt(peg$currPos) === 34) {
|
||||
s2 = peg$c2;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s2 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
}
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = [];
|
||||
if (peg$r1.test(input.charAt(peg$currPos))) {
|
||||
s4 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
||||
}
|
||||
while (s4 !== peg$FAILED) {
|
||||
s3.push(s4);
|
||||
if (peg$r1.test(input.charAt(peg$currPos))) {
|
||||
s4 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
||||
}
|
||||
}
|
||||
if (input.charCodeAt(peg$currPos) === 34) {
|
||||
s4 = peg$c2;
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s4 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e4); }
|
||||
}
|
||||
if (s4 !== peg$FAILED) {
|
||||
s1 = s3;
|
||||
} else {
|
||||
peg$currPos = s1;
|
||||
s1 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s1;
|
||||
s1 = peg$FAILED;
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s1 = peg$f3(s1);
|
||||
}
|
||||
s0 = s1;
|
||||
}
|
||||
peg$silentFails--;
|
||||
if (s0 === peg$FAILED) {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e1); }
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parseignore() {
|
||||
var s0, s1;
|
||||
|
||||
var key = peg$currPos * 10 + 4;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = [];
|
||||
s1 = peg$parseany();
|
||||
if (s1 === peg$FAILED) {
|
||||
s1 = peg$parsenewLine();
|
||||
if (s1 === peg$FAILED) {
|
||||
s1 = peg$parse_();
|
||||
}
|
||||
}
|
||||
while (s1 !== peg$FAILED) {
|
||||
s0.push(s1);
|
||||
s1 = peg$parseany();
|
||||
if (s1 === peg$FAILED) {
|
||||
s1 = peg$parsenewLine();
|
||||
if (s1 === peg$FAILED) {
|
||||
s1 = peg$parse_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsecomment() {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
var key = peg$currPos * 10 + 5;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 2) === peg$c3) {
|
||||
s1 = peg$c3;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
s3 = peg$parseany();
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
s3 = peg$parseany();
|
||||
}
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f4();
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsedelimitedComment() {
|
||||
var s0, s1, s2, s3;
|
||||
|
||||
var key = peg$currPos * 10 + 6;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
s0 = peg$currPos;
|
||||
if (input.substr(peg$currPos, 2) === peg$c4) {
|
||||
s1 = peg$c4;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
||||
}
|
||||
if (s1 !== peg$FAILED) {
|
||||
s2 = [];
|
||||
if (peg$r2.test(input.charAt(peg$currPos))) {
|
||||
s3 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
}
|
||||
while (s3 !== peg$FAILED) {
|
||||
s2.push(s3);
|
||||
if (peg$r2.test(input.charAt(peg$currPos))) {
|
||||
s3 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
||||
}
|
||||
}
|
||||
if (input.substr(peg$currPos, 2) === peg$c5) {
|
||||
s3 = peg$c5;
|
||||
peg$currPos += 2;
|
||||
} else {
|
||||
s3 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
||||
}
|
||||
if (s3 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f5();
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parse_() {
|
||||
var s0, s1;
|
||||
|
||||
var key = peg$currPos * 10 + 7;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
peg$silentFails++;
|
||||
if (peg$r3.test(input.charAt(peg$currPos))) {
|
||||
s0 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
||||
}
|
||||
peg$silentFails--;
|
||||
if (s0 === peg$FAILED) {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parsenewLine() {
|
||||
var s0, s1;
|
||||
|
||||
var key = peg$currPos * 10 + 8;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
peg$silentFails++;
|
||||
if (peg$r4.test(input.charAt(peg$currPos))) {
|
||||
s0 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e13); }
|
||||
}
|
||||
peg$silentFails--;
|
||||
if (s0 === peg$FAILED) {
|
||||
s1 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e12); }
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
function peg$parseany() {
|
||||
var s0;
|
||||
|
||||
var key = peg$currPos * 10 + 9;
|
||||
var cached = peg$resultsCache[key];
|
||||
|
||||
if (cached) {
|
||||
peg$currPos = cached.nextPos;
|
||||
|
||||
return cached.result;
|
||||
}
|
||||
|
||||
if (peg$r5.test(input.charAt(peg$currPos))) {
|
||||
s0 = input.charAt(peg$currPos);
|
||||
peg$currPos++;
|
||||
} else {
|
||||
s0 = peg$FAILED;
|
||||
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
||||
}
|
||||
|
||||
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
|
||||
|
||||
return s0;
|
||||
}
|
||||
|
||||
peg$result = peg$startRuleFunction();
|
||||
|
||||
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
|
||||
return peg$result;
|
||||
} else {
|
||||
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
|
||||
peg$fail(peg$endExpectation());
|
||||
}
|
||||
|
||||
throw peg$buildStructuredError(
|
||||
peg$maxFailExpected,
|
||||
peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
|
||||
peg$maxFailPos < input.length
|
||||
? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
|
||||
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SyntaxError: peg$SyntaxError,
|
||||
parse: peg$parse
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
// Includes are at the start of the file, before any other code.
|
||||
// There might be some comments before or between the includes.
|
||||
// #include "string"
|
||||
// #include "string2"
|
||||
|
||||
|
||||
start
|
||||
= @includes newLine* ignore
|
||||
|
||||
includes
|
||||
= head:includeStatement tail:(newLine+ @includeStatement)*
|
||||
{return [head, ...tail].filter( e => e != '');}
|
||||
/ ignore
|
||||
{return [];}
|
||||
|
||||
includeStatement
|
||||
= '#include' _* @string _*
|
||||
/ comment
|
||||
/ delimitedComment
|
||||
|
||||
string 'string'
|
||||
= characters:("'" @([^'])* "'") {return characters.join('');}
|
||||
/ characters:('"' @([^"])* '"') {return characters.join('');}
|
||||
|
||||
ignore = (any / newLine / _)*
|
||||
|
||||
comment
|
||||
= '//'any*
|
||||
{ return '';}
|
||||
|
||||
delimitedComment
|
||||
= '/*' ([^*]*) '*/'
|
||||
{ return '';}
|
||||
|
||||
_ "white space"
|
||||
= [ \t]
|
||||
|
||||
newLine "newline"
|
||||
= [\n\r]
|
||||
|
||||
any = [^\r\n]
|
|
@ -0,0 +1,8 @@
|
|||
@module("./ReducerProject_IncludeParser.js") external parse__: string => array<string> = "parse"
|
||||
|
||||
let parseIncludes = (expr: string): array<string> =>
|
||||
try {
|
||||
parse__(expr)
|
||||
} catch {
|
||||
| Js.Exn.Error(_obj) => []
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
module ProjectItemT = ReducerProject_ProjectItem_T
|
||||
module Bindings = Reducer_Bindings
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
|
||||
type projectAccessors = {
|
||||
stdLib: Reducer_Bindings.t,
|
||||
environment: ExpressionT.environment,
|
||||
mutable continuation: ProjectItemT.continuationArgumentType,
|
||||
}
|
||||
|
||||
type t = projectAccessors
|
||||
|
||||
let identityAccessors: t = {
|
||||
continuation: Bindings.emptyBindings,
|
||||
stdLib: ReducerInterface_StdLib.internalStdLib,
|
||||
environment: InternalExpressionValue.defaultEnvironment,
|
||||
}
|
||||
|
||||
let identityAccessorsWithEnvironment = (environment): t => {
|
||||
continuation: Bindings.emptyBindings,
|
||||
stdLib: ReducerInterface_StdLib.internalStdLib,
|
||||
environment: environment,
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
// TODO: Use actual types instead of aliases in public functions
|
||||
// TODO: Use topological sorting to prevent unnecessary runs
|
||||
module Bindings = Reducer_Bindings
|
||||
module Continuation = ReducerInterface_Value_Continuation
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module ReducerFnT = ReducerProject_ReducerFn_T
|
||||
module T = ReducerProject_ProjectItem_T
|
||||
|
||||
type projectItem = T.projectItem
|
||||
type t = T.t
|
||||
|
||||
let emptyItem = T.ProjectItem({
|
||||
source: "",
|
||||
rawParse: None,
|
||||
expression: None,
|
||||
continuation: Bindings.emptyBindings,
|
||||
result: None,
|
||||
continues: [],
|
||||
includes: []->Ok,
|
||||
})
|
||||
// source -> rawParse -> includes -> expression -> continuation -> externalBindings -> result -> externalResult
|
||||
|
||||
let getSource = (T.ProjectItem(r)): T.sourceType => r.source
|
||||
let getRawParse = (T.ProjectItem(r)): T.rawParseType => r.rawParse
|
||||
let getExpression = (T.ProjectItem(r)): T.expressionType => r.expression
|
||||
let getContinuation = (T.ProjectItem(r)): T.continuationArgumentType => r.continuation
|
||||
let toExternalBindings = continuation =>
|
||||
continuation->InternalExpressionValue.nameSpaceToTypeScriptBindings
|
||||
let getResult = (T.ProjectItem(r)): T.resultType => r.result
|
||||
let getExternalResult = (T.ProjectItem(r)): T.externalResultType =>
|
||||
r.result->Belt.Option.map(opt =>
|
||||
opt->Belt.Result.map(value => value->InternalExpressionValue.toExternal)
|
||||
)
|
||||
|
||||
let getContinues = (T.ProjectItem(r)): T.continuesType => r.continues
|
||||
let getIncludes = (T.ProjectItem(r)): T.includesType => r.includes
|
||||
|
||||
let touchSource = (this: t): t => {
|
||||
let T.ProjectItem(r) = emptyItem
|
||||
T.ProjectItem({
|
||||
...r,
|
||||
includes: getIncludes(this),
|
||||
continues: getContinues(this),
|
||||
source: getSource(this),
|
||||
})
|
||||
}
|
||||
let touchRawParse = (this: t): t => {
|
||||
let T.ProjectItem(r) = emptyItem
|
||||
T.ProjectItem({
|
||||
...r,
|
||||
continues: getContinues(this),
|
||||
source: getSource(this),
|
||||
rawParse: getRawParse(this),
|
||||
includes: getIncludes(this),
|
||||
})
|
||||
}
|
||||
let touchExpression = (this: t): t => {
|
||||
let T.ProjectItem(r) = emptyItem
|
||||
T.ProjectItem({
|
||||
...r,
|
||||
continues: getContinues(this),
|
||||
source: getSource(this),
|
||||
rawParse: getRawParse(this),
|
||||
includes: getIncludes(this),
|
||||
expression: getExpression(this),
|
||||
})
|
||||
}
|
||||
|
||||
let setSource = (T.ProjectItem(r): t, source: T.sourceArgumentType): t =>
|
||||
T.ProjectItem({...r, source: source})->touchSource
|
||||
|
||||
let setRawParse = (T.ProjectItem(r): t, rawParse: T.rawParseArgumentType): t =>
|
||||
T.ProjectItem({...r, rawParse: Some(rawParse)})->touchRawParse
|
||||
|
||||
let setExpression = (T.ProjectItem(r): t, expression: T.expressionArgumentType): t =>
|
||||
T.ProjectItem({...r, expression: Some(expression)})->touchExpression
|
||||
|
||||
let setContinuation = (T.ProjectItem(r): t, continuation: T.continuationArgumentType): t => {
|
||||
T.ProjectItem({...r, continuation: continuation})
|
||||
}
|
||||
|
||||
let setResult = (T.ProjectItem(r): t, result: T.resultArgumentType): t => T.ProjectItem({
|
||||
...r,
|
||||
result: Some(result),
|
||||
})
|
||||
|
||||
let cleanResults = touchExpression
|
||||
|
||||
let clean = (this: t): t => {
|
||||
let T.ProjectItem(r) = emptyItem
|
||||
T.ProjectItem({
|
||||
...r,
|
||||
source: getSource(this),
|
||||
continuation: getContinuation(this),
|
||||
result: getResult(this),
|
||||
})
|
||||
}
|
||||
|
||||
let getImmediateDependencies = (this: t): T.includesType =>
|
||||
getIncludes(this)->Belt.Result.map(Js.Array2.concat(_, getContinues(this)))
|
||||
|
||||
let setContinues = (T.ProjectItem(r): t, continues: array<string>): t =>
|
||||
T.ProjectItem({...r, continues: continues})->touchSource
|
||||
let removeContinues = (T.ProjectItem(r): t): t => T.ProjectItem({...r, continues: []})->touchSource
|
||||
|
||||
let setIncludes = (T.ProjectItem(r): t, includes: T.includesType): t => T.ProjectItem({
|
||||
...r,
|
||||
includes: includes,
|
||||
})
|
||||
|
||||
//TODO: forward parse errors to the user
|
||||
let parseIncludes = (this: t): t =>
|
||||
setIncludes(this, getSource(this)->ReducerProject_ParseIncludes.parseIncludes->Ok)
|
||||
|
||||
let doRawParse = (this: t): T.rawParseArgumentType => this->getSource->Reducer_Peggy_Parse.parse
|
||||
|
||||
let rawParse = (this: t): t =>
|
||||
this->getRawParse->E.O2.defaultFn(() => doRawParse(this))->setRawParse(this, _)
|
||||
|
||||
let doBuildExpression = (this: t): T.expressionType =>
|
||||
this
|
||||
->getRawParse
|
||||
->Belt.Option.map(o => o->Belt.Result.map(r => r->Reducer_Peggy_ToExpression.fromNode))
|
||||
|
||||
let buildExpression = (this: t): t => {
|
||||
let withRawParse: t = this->rawParse
|
||||
switch withRawParse->getExpression {
|
||||
| Some(_) => withRawParse
|
||||
| None =>
|
||||
withRawParse
|
||||
->doBuildExpression
|
||||
->Belt.Option.map(setExpression(withRawParse, _))
|
||||
->E.O2.defaultFn(() => withRawParse)
|
||||
}
|
||||
}
|
||||
|
||||
let wrappedReducer = (
|
||||
rExpression: T.expressionArgumentType,
|
||||
aContinuation: T.continuation,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): T.resultArgumentType => {
|
||||
Belt.Result.flatMap(
|
||||
rExpression,
|
||||
Reducer_Expression.reduceExpressionInProject(_, aContinuation, accessors),
|
||||
)
|
||||
}
|
||||
|
||||
let doBuildResult = (
|
||||
this: t,
|
||||
aContinuation: T.continuation,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
): T.resultType =>
|
||||
this
|
||||
->getExpression
|
||||
->Belt.Option.map(
|
||||
Belt.Result.flatMap(
|
||||
_,
|
||||
Reducer_Expression.reduceExpressionInProject(_, aContinuation, accessors),
|
||||
),
|
||||
)
|
||||
|
||||
let buildResult = (this: t, aContinuation: T.continuation, accessors: ProjectAccessorsT.t): t => {
|
||||
let withExpression: t = this->buildExpression
|
||||
switch withExpression->getResult {
|
||||
| Some(_) => withExpression
|
||||
| None =>
|
||||
withExpression
|
||||
->doBuildResult(aContinuation, accessors)
|
||||
->Belt.Option.map(setResult(withExpression, _))
|
||||
->E.O2.defaultFn(() => withExpression)
|
||||
}
|
||||
}
|
||||
|
||||
let run = buildResult
|
|
@ -0,0 +1,39 @@
|
|||
module Parse = Reducer_Peggy_Parse
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
|
||||
open Reducer_ErrorValue
|
||||
|
||||
type sourceArgumentType = string
|
||||
type sourceType = string
|
||||
type rawParseArgumentType = result<Parse.node, errorValue>
|
||||
type rawParseType = option<rawParseArgumentType>
|
||||
type expressionArgumentType = result<ExpressionT.t, errorValue>
|
||||
type expressionType = option<expressionArgumentType>
|
||||
type continuation = InternalExpressionValue.nameSpace
|
||||
type continuationArgumentType = InternalExpressionValue.nameSpace
|
||||
type continuationType = option<continuationArgumentType>
|
||||
type continuationResultType = option<result<continuationArgumentType, errorValue>>
|
||||
type externalBindingsArgumentType = ExternalExpressionValue.record
|
||||
type externalBindingsType = option<externalBindingsArgumentType>
|
||||
type resultArgumentType = result<InternalExpressionValue.t, errorValue>
|
||||
type resultType = option<resultArgumentType>
|
||||
type externalResultArgumentType = result<ExternalExpressionValue.t, errorValue>
|
||||
type externalResultType = option<externalResultArgumentType>
|
||||
type continuesArgumentType = array<string>
|
||||
type continuesType = array<string>
|
||||
type includesArgumentType = string
|
||||
type includesType = result<array<string>, errorValue>
|
||||
|
||||
type projectItem =
|
||||
| ProjectItem({
|
||||
source: sourceType,
|
||||
rawParse: rawParseType,
|
||||
expression: expressionType,
|
||||
continuation: continuationArgumentType,
|
||||
result: resultType,
|
||||
continues: continuesType,
|
||||
includes: includesType,
|
||||
})
|
||||
|
||||
type t = projectItem
|
|
@ -0,0 +1,9 @@
|
|||
module ExpressionT = Reducer_Expression_T
|
||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
|
||||
type t = (
|
||||
ExpressionT.t,
|
||||
ExpressionT.bindings,
|
||||
ProjectAccessorsT.t,
|
||||
) => result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>
|
|
@ -0,0 +1,25 @@
|
|||
module ProjectItem = ReducerProject_ProjectItem
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
|
||||
type project = Object
|
||||
type t = project
|
||||
|
||||
module Private = {
|
||||
type internalProject = {
|
||||
"items": Belt.Map.String.t<ProjectItem.t>,
|
||||
"stdLib": Reducer_Bindings.t,
|
||||
"environment": ExpressionT.environment,
|
||||
}
|
||||
type t = internalProject
|
||||
|
||||
@set
|
||||
external setFieldItems: (t, Belt.Map.String.t<ProjectItem.t>) => unit = "items"
|
||||
@set
|
||||
external setFieldStdLib: (t, Reducer_Bindings.t) => unit = "stdLib"
|
||||
@set
|
||||
external setFieldEnvironment: (t, ExpressionT.environment) => unit = "stdLib"
|
||||
|
||||
external castFromInternalProject: t => project = "%identity"
|
||||
external castToInternalProject: project => t = "%identity"
|
||||
}
|
|
@ -34,17 +34,17 @@ type resultString = result<string, distributionError>
|
|||
@genType
|
||||
let makeSampleSetDist = SampleSetDist.make
|
||||
|
||||
@genType
|
||||
let evaluate = Reducer.evaluate
|
||||
// @genType
|
||||
// let evaluate = Reducer.evaluate
|
||||
|
||||
@genType
|
||||
let evaluateUsingOptions = Reducer.evaluateUsingOptions
|
||||
// @genType
|
||||
// let evaluateUsingOptions = Reducer.evaluateUsingOptions
|
||||
|
||||
@genType
|
||||
let parse = Reducer_Peggy_Parse.parse
|
||||
|
||||
@genType
|
||||
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
|
||||
// @genType
|
||||
// let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
|
||||
|
||||
@genType
|
||||
type externalBindings = Reducer.externalBindings
|
||||
|
@ -91,8 +91,8 @@ type environment = ReducerInterface_ExternalExpressionValue.environment
|
|||
@genType
|
||||
let defaultEnvironment = ReducerInterface_ExternalExpressionValue.defaultEnvironment
|
||||
|
||||
@genType
|
||||
let foreignFunctionInterface = Reducer.foreignFunctionInterface
|
||||
// @genType
|
||||
// let foreignFunctionInterface = Reducer.foreignFunctionInterface
|
||||
|
||||
@genType
|
||||
type declarationArg = Declaration.arg
|
||||
|
|
Loading…
Reference in New Issue
Block a user