From 2e8e71bbd0b4198db1a4dba71a6f6e890af1cc12 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 25 Jul 2022 16:34:33 +0200 Subject: [PATCH] project test remove setResult parse end of outerblock test end of outer block compiles testing bindings tested todo chain bindings topological sort --- .../Reducer_Dispatch_BuiltIn_test.res | 10 +- .../Reducer_Peggy_Parse_test.res | 517 ++++++---- .../Reducer_Peggy_Parse_type_test.res | 36 +- .../Reducer_Peggy_TestHelpers.res | 8 +- ...cer_Peggy_ToExpression_outerBlock_test.res | 23 + .../Reducer_Peggy_ToExpression_test.res | 178 ++-- .../Reducer_Peggy_ToExpression_type_test.res | 52 +- .../Reducer_Peggy_ToExpression_void_test.res | 17 +- .../__tests__/Reducer/Reducer_TestHelpers.res | 27 +- .../Reducer/Reducer_TestMacroHelpers.res | 14 +- .../Reducer_Type_Compile_test.res | 4 +- ...educer_Type_TypeChecker_arguments_test.res | 11 +- .../Reducer_Type_TypeChecker_test.res | 7 +- .../Reducer/Reducer_externalBindings_test.res | 14 - .../Reducer_functionAssignment_test.res | 10 +- .../Reducer/Reducer_functionTricks_test.res | 27 +- .../Reducer/Reducer_ternaryOperator_test.res | 5 +- .../__tests__/Reducer/Reducer_test.res | 2 +- .../ReducerInterface_Distribution_test.res | 32 +- .../ReducerProject/ReducerProject_test.res | 179 ++++ ...leLibrary_FunctionRegistryLibrary_test.res | 10 +- .../FunctionRegistry_Core.res | 24 +- .../FunctionRegistry/Library/FR_Dist.res | 20 +- .../FunctionRegistry/Library/FR_List.res | 72 +- .../FunctionRegistry/Library/FR_Pointset.res | 6 +- .../FunctionRegistry/Library/FR_Sampleset.res | 68 +- .../FunctionRegistry/Library/FR_Scoring.res | 16 +- .../src/rescript/Reducer/Reducer.res | 26 +- .../src/rescript/Reducer/Reducer.resi | 52 +- .../Reducer_Bindings/Reducer_Bindings.res | 1 + .../Reducer_Dispatch_BuiltIn.res | 38 +- .../Reducer_Dispatch_BuiltInMacros.res | 51 +- .../rescript/Reducer/Reducer_ErrorValue.res | 2 +- .../Reducer_Expression/Reducer_Expression.res | 140 ++- .../Reducer_ExpressionWithContext.res | 13 +- .../Reducer_Expression_Lambda.res | 41 +- .../Reducer_Expression_Macro.res | 23 +- .../Reducer_Expression_T.res | 2 + .../Reducer_Peggy_GeneratedParser.peggy | 19 +- .../Reducer_Type/Reducer_Type_Compile.res | 47 +- .../Reducer_Type/Reducer_Type_TypeChecker.res | 6 +- ...ducerInterface_ExternalExpressionValue.res | 12 + .../ReducerInterface_ExternalLibrary.res | 18 +- ...ducerInterface_InternalExpressionValue.res | 6 + .../ReducerInterface_StdLib.res | 3 +- .../ReducerInterface_Value_Continuation.res | 28 + .../ReducerProject/ReducerProject.res | 396 ++++++++ .../ReducerProject_IncludeParser.js | 915 ++++++++++++++++++ .../ReducerProject_IncludeParser.peggy | 41 + .../ReducerProject_ParseIncludes.res | 8 + .../ReducerProject_ProjectAccessors_T.res | 24 + .../ReducerProject_ProjectItem.res | 176 ++++ .../ReducerProject_ProjectItem_T.res | 39 + .../ReducerProject_ReducerFn_T.res | 9 + .../ReducerProject/ReducerProject_T.res | 25 + .../src/rescript/TypescriptInterface.res | 16 +- 56 files changed, 2831 insertions(+), 735 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res delete mode 100644 packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res create mode 100644 packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.js create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.peggy create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ParseIncludes.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res create mode 100644 packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res index f3c92f78..4932af3b 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res @@ -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)) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res index f17fe9aa..80093afb 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res @@ -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", + "{(::$_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 { - 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))}", ) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_type_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_type_test.res index 4f7dabdc..010486be 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_type_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_type_test.res @@ -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_$ () ())}", ) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res index b434fdb9..303f66ee 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res @@ -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)) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res new file mode 100644 index 00000000..951005c2 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res @@ -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", + (), + ) +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res index b8a81f25..3b20fafc 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res @@ -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", ()) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_type_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_type_test.res index 9849adb0..b0cfb676 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_type_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_type_test.res @@ -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]}", (), ) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res index a106a188..f46d387f 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res @@ -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)}", (), ) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res index 736c9c2f..d32457e6 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res @@ -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)) } diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res index ffd5d964..752cc9c7 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res @@ -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 diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res index 71f90373..55ed36bd 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res @@ -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) => diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res index 07da15bb..29fa8249 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res @@ -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 { diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res index efd9bb18..67de7941 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res @@ -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)) } diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res deleted file mode 100644 index bb1a4d35..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res +++ /dev/null @@ -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})", - ) -}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res index 30725cfe..a2abb85d 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res @@ -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 }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res index 68614f89..9b111354 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -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)})") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index 22fa8328..c86f4bca 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -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", () => { diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res index dc403e7d..f70aa934 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res @@ -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)") }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 3083b71b..4adb363f 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -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)))") diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res new file mode 100644 index 00000000..cea3d777 --- /dev/null +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res @@ -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 diff --git a/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res index 2418b4d8..dd23d994 100644 --- a/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res +++ b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res @@ -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)) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index 4b27c221..edce5ac2 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -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, array, - GenericDist.env, - Reducer_Expression_T.reducerFn, + ProjectAccessorsT.t, + ProjectReducerFnT.t, ) => result, } @@ -382,12 +384,12 @@ module FnDefinition = { let run = ( t: t, args: array, - 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, - ~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)), ) } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res index 9e10577b..23619187 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res @@ -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), (), ) } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res index 762fe31b..e0b83d18 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res @@ -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, environment, eLambdaValue, reducer): result< - ReducerInterface_InternalExpressionValue.t, - Reducer_ErrorValue.errorValue, - > => { + let map = ( + array: array, + accessors: ProjectAccessorsT.t, + eLambdaValue, + reducer: ProjectReducerFnT.t, + ): result => { 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) }, (), diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res index 4f4e1731..441a7450 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res @@ -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) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res index 27b870ee..b42aaa89 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res @@ -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, aLambdaValue, env, reducer) => { + let mapN = ( + aValueArray: array, + 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) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res index 972501e9..d45b1a81 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res @@ -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) } }, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res index bf8c89f9..ca0f63f3 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res @@ -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, - 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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi index bd0c43fb..8ac63abf 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi @@ -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, - ~externalBindings: option< - QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings, - >, - string, -) => result -@genType -let evaluatePartialUsingExternalBindings: ( - string, - QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings, - QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment, -) => result -@genType -let evaluate: string => result +// @genType +// let evaluateUsingOptions: ( +// ~environment: option, +// ~externalBindings: option< +// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings, +// >, +// string, +// ) => result +// @genType +// let evaluatePartialUsingExternalBindings: ( +// string, +// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings, +// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment, +// ) => result +// @genType +// let evaluate: string => result -let parse: string => result +// let parse: string => result -@genType -let foreignFunctionInterface: ( - QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.lambdaValue, - array, - QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment, -) => result +// @genType +// let foreignFunctionInterface: ( +// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.lambdaValue, +// array, +// QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment, +// ) => result @genType let defaultEnvironment: environment -@genType -let defaultExternalBindings: externalBindings +// @genType +// let defaultExternalBindings: externalBindings diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res index 28175d7a..3daeb98d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res @@ -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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res index a2ca204a..b1ae2e41 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res @@ -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 => 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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index c4c76ce3..978ca399 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -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 => { - 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, _bindings: ExpressionT.bindings, _environment): result< + let doBlock = (exprs: list, _bindings: ExpressionT.bindings, _accessors): result< expressionWithContext, errorValue, > => { @@ -130,10 +133,10 @@ let dispatchMacroCall = ( ifTrue: expression, ifFalse: expression, bindings: ExpressionT.bindings, - environment, + accessors, ): result => { 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 } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 211c9f53..d908bf93 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -3,7 +3,7 @@ type location @genType type errorValue = - | REArityError(option, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting + | REArityError(option, int, int) | REArrayIndexNotFound(string, int) | REAssignmentExpected | REDistributionError(DistributionTypes.error) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index 6de4f74a..3e476403 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -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 => - 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 => { // 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, - bindings: T.bindings, - environment: environment, -): result => { + continuation: T.bindings, + accessors: ProjectAccessorsT.t, +): result => { let racc: result< - list, + list, '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, environment): result< - internalExpressionValue, - 'e, -> => +and reduceValueList = ( + valueList: list, + accessors: ProjectAccessorsT.t, +): result => switch valueList { | list{IEvCall(fName), ...args} => { let rCheckedArgs = switch fName { @@ -81,7 +74,10 @@ and reduceValueList = (valueList: list, 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, 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, environment): r ) } -let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result< - internalExpressionValue, - 'e, -> => reduceExpression(aExpression, bindings, environment) - -let evaluateUsingOptions = ( - ~environment: option, - ~externalBindings: option, - code: string, -): result => { - 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, 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 => { - evaluateUsingOptions(~environment=None, ~externalBindings=None, code) -} -let evaluatePartialUsingExternalBindings = ( - code: string, - externalBindings: ReducerInterface_ExternalExpressionValue.externalBindings, - environment: ReducerInterface_ExternalExpressionValue.environment, -): result => { - 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 => + peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) + + let evaluate = (expression: t): result => { + let accessors = ProjectAccessorsT.identityAccessors + expression->reduceExpressionInProject(accessors.stdLib, accessors) } + + let evaluateString = (peggyCode: string): result => + parse(peggyCode)->Result.flatMap(evaluate) + + let evaluateAsExternal = (expression: t): result => + { + 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) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res index 44059e2b..808a2dcd 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res @@ -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 => { 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) } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index 59779484..587d80be 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -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) => ) ) -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 => { 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, - environment: ExpressionValue.environment, - reducer: ExpressionT.reducerFn, + accessors: ProjectAccessorsT.t, + reducer: ProjectReducerFnT.t, ): result => { let args = argArray->Belt.List.fromArray - applyParametersToLambda(lambdaValue, args, environment, reducer) + applyParametersToLambda(lambdaValue, args, accessors, reducer) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res index 2598a9ed..003d3170 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res @@ -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 => 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 => 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("$$") diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res index c9739be3..61f723df 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res @@ -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, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index 15837da4..ec490c42 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res index 2119ee62..ab0ff74e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res @@ -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 => { 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 => { - 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) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res index e4336df5..42704fd1 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res @@ -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 = let isTypeOf = ( typeExpressionSourceCode: string, aValue: InternalExpressionValue.t, - reducerFn: ExpressionT.reducerFn, + reducerFn: ProjectReducerFnT.t, ): result => { switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { | Ok(anIType) => @@ -152,7 +154,7 @@ let checkITypeArguments = (anIType: T.iType, args: array, - reducerFn: ExpressionT.reducerFn, + reducerFn: ProjectReducerFnT.t, ): result => { switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { | Ok(anIType) => diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res index d613ceb8..185fb351 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res @@ -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 diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index 7ae6ace9..89528a32 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -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 => { 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)) } /* diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res index 86718b64..b5799067 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res @@ -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): string => switch codeResult { | Ok(a) => toString(a) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res index ec6c4fd4..8beec882 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res @@ -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 diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res new file mode 100644 index 00000000..af1c16c9 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res @@ -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 +} diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res new file mode 100644 index 00000000..8a78c795 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res @@ -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 => 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, list) + 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 => { + 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 => { + let runOrder = getRunOrderFor(this, sourceId) + + let _ = Js.Array2.pop(runOrder) + runOrder + } + + let getDependents = (this: t, sourceId: string): array => { + 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 => + 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): 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 => + 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 => + 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 => + 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 => + 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): 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 => + this->T.Private.castToInternalProject->Private.getDependencies(sourceId) + +// Get source ids depending on a given source id. +let getDependents = (this: t, sourceId: string): array => + 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, +> => 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, + 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) +} diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.js b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.js new file mode 100644 index 00000000..adc6e598 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.js @@ -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 +}; diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.peggy b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.peggy new file mode 100644 index 00000000..25d69b93 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_IncludeParser.peggy @@ -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] diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ParseIncludes.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ParseIncludes.res new file mode 100644 index 00000000..347234a4 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ParseIncludes.res @@ -0,0 +1,8 @@ +@module("./ReducerProject_IncludeParser.js") external parse__: string => array = "parse" + +let parseIncludes = (expr: string): array => + try { + parse__(expr) + } catch { + | Js.Exn.Error(_obj) => [] + } diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res new file mode 100644 index 00000000..d3becba9 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res @@ -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, +} diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res new file mode 100644 index 00000000..c07cebad --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res @@ -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): 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 diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res new file mode 100644 index 00000000..5d0846cf --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res @@ -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 +type rawParseType = option +type expressionArgumentType = result +type expressionType = option +type continuation = InternalExpressionValue.nameSpace +type continuationArgumentType = InternalExpressionValue.nameSpace +type continuationType = option +type continuationResultType = option> +type externalBindingsArgumentType = ExternalExpressionValue.record +type externalBindingsType = option +type resultArgumentType = result +type resultType = option +type externalResultArgumentType = result +type externalResultType = option +type continuesArgumentType = array +type continuesType = array +type includesArgumentType = string +type includesType = result, errorValue> + +type projectItem = + | ProjectItem({ + source: sourceType, + rawParse: rawParseType, + expression: expressionType, + continuation: continuationArgumentType, + result: resultType, + continues: continuesType, + includes: includesType, + }) + +type t = projectItem diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res new file mode 100644 index 00000000..0b401fcb --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res @@ -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 diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res new file mode 100644 index 00000000..9acaa74e --- /dev/null +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res @@ -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, + "stdLib": Reducer_Bindings.t, + "environment": ExpressionT.environment, + } + type t = internalProject + + @set + external setFieldItems: (t, Belt.Map.String.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" +} diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index a1f5afe6..64629731 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -34,17 +34,17 @@ type resultString = result @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