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:
Umur Ozkul 2022-07-25 16:34:33 +02:00
parent f1f274127b
commit 2e8e71bbd0
56 changed files with 2831 additions and 735 deletions

View File

@ -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))

View File

@ -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))}",
)
})

View File

@ -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_$ () ())}",
)
})
})

View File

@ -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))

View File

@ -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",
(),
)
})

View File

@ -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", ())
})
})

View File

@ -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]}",
(),
)
})

View File

@ -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)}",
(),
)
})

View File

@ -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))
}

View File

@ -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

View File

@ -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) =>

View File

@ -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 {

View File

@ -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))
}

View File

@ -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})",
)
})

View File

@ -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
})

View File

@ -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)})")

View File

@ -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", () => {

View File

@ -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)")
})
})

View File

@ -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)))")

View File

@ -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

View File

@ -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))

View File

@ -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)),
)
}

View File

@ -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),
(),
)
}

View File

@ -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)
},
(),

View File

@ -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)

View File

@ -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)

View File

@ -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)
}
},

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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("$$")

View File

@ -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,

View File

@ -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

View File

@ -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)
}

View File

@ -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) =>

View File

@ -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

View File

@ -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))
}
/*

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
};

View File

@ -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]

View File

@ -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) => []
}

View File

@ -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,
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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"
}

View File

@ -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