From 065a7aeec01e492aca700ed0669593c4db011956 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 18 Sep 2022 02:19:08 +0400 Subject: [PATCH] immutable bindings; test fixes --- .../Reducer_Bindings_test.res | 43 ++- .../__tests__/Reducer/Reducer_Helpers.res | 10 +- .../Reducer/Reducer_Namespace_test.res | 57 ++++ .../Reducer_Peggy_Parse_test.res | 265 +++++++++--------- ...cer_Peggy_ToExpression_outerBlock_test.res | 10 +- .../Reducer_Peggy_ToExpression_test.res | 103 +++---- .../Reducer/Reducer_constructArray_test.res | 6 +- .../Reducer_functionAssignment_test.res | 4 +- .../Reducer/Reducer_ternaryOperator_test.res | 2 +- .../ReducerInterface_Distribution_test.res | 20 +- .../ReducerProject_includes_test.res | 218 +++++++------- .../ReducerProject/ReducerProject_test.res | 27 +- .../ReducerProject_tutorial_1_test.res | 40 +-- ...cerProject_tutorial_2_multisource_test.res | 59 ++-- ...educerProject_tutorial_3_includes_test.res | 52 ++-- packages/squiggle-lang/bsconfig.json | 10 +- packages/squiggle-lang/scripts/run-file.js | 2 +- packages/squiggle-lang/src/js/SqRecord.ts | 4 + .../FunctionRegistry_Core.res | 5 + .../FunctionRegistry/Library/FR_Builtin.res | 22 +- .../Reducer_Bindings/Reducer_Bindings.res | 26 +- .../src/rescript/Reducer/Reducer_Context.res | 2 +- .../Reducer_Dispatch_BuiltIn.res | 34 --- .../Reducer_Expression/Reducer_Expression.res | 93 +++--- .../Reducer_Expression_Lambda.res | 9 +- .../Reducer_Expression_T.res | 46 +-- .../rescript/Reducer/Reducer_Namespace.res | 20 +- .../src/rescript/Reducer/Reducer_T.res | 4 +- .../ReducerInterface_StdLib.res | 15 +- .../ReducerProject/ReducerProject.res | 18 +- .../ReducerProject_ProjectItem.res | 4 +- .../ReducerProject/ReducerProject_T.res | 3 +- 32 files changed, 637 insertions(+), 596 deletions(-) create mode 100644 packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res index 8b9f2064..f43944d4 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res @@ -1,27 +1,46 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Bindings = Reducer_Bindings +module Namespace = Reducer_Namespace open Jest open Expect open Expect.Operators -describe("Name Space", () => { +describe("Bindings", () => { let value = Reducer_T.IEvNumber(1967.0) - let nameSpace = Bindings.makeEmptyBindings()->Bindings.set("value", value) + let bindings = Bindings.make()->Bindings.set("value", value) test("get", () => { - expect(Bindings.get(nameSpace, "value")) == Some(value) + expect(bindings->Bindings.get("value")) == Some(value) }) - test("chain and get", () => { - let mainNameSpace = Bindings.makeEmptyBindings()->Bindings.chainTo([nameSpace]) - expect(Bindings.get(mainNameSpace, "value")) == Some(value) + test("get nonexisting value", () => { + expect(bindings->Bindings.get("nosuchvalue")) == None }) - test("chain and set", () => { - let mainNameSpace0 = Bindings.makeEmptyBindings()->Bindings.chainTo([nameSpace]) - let mainNameSpace = - mainNameSpace0->Bindings.set("value", Reducer_T.IEvNumber(1968.0)) - expect(Bindings.get(mainNameSpace, "value")) == Some(Reducer_T.IEvNumber(1968.0)) + test("get on extended", () => { + expect(bindings->Bindings.extend->Bindings.get("value")) == Some(value) + }) + + test("locals", () => { + expect(bindings->Bindings.locals->Namespace.get("value")) == Some(value) + }) + + test("locals on extendeed", () => { + expect(bindings->Bindings.extend->Bindings.locals->Namespace.get("value")) == None + }) + + describe("extend", () => { + let value2 = Reducer_T.IEvNumber(5.) + let extendedBindings = bindings + ->Bindings.extend + ->Bindings.set("value", value2) + + test("get on extended", () => { + expect(extendedBindings->Bindings.get("value")) == Some(value2) + }) + + test("get on original", () => { + expect(bindings->Bindings.get("value")) == Some(value) + }) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res index 706e4c8e..4bbeff17 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res @@ -3,15 +3,7 @@ module ErrorValue = Reducer_ErrorValue module InternalExpressionValue = ReducerInterface.InternalExpressionValue let removeDefaultsInternal = (iev: InternalExpressionValue.t) => { - Not_found->raise - // switch iev { - // | Reducer_T.IEvBindings(nameSpace) => - // Reducer_Bindings.removeOther( - // nameSpace, - // ReducerInterface.StdLib.internalStdLib, - // )->Reducer_T.IEvBindings - // | value => value - // } + iev // TODO - cleanup, noop } let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res new file mode 100644 index 00000000..64401e32 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res @@ -0,0 +1,57 @@ +@@warning("-44") +module Namespace = Reducer_Namespace + +open Jest +open Expect +open Expect.Operators + +let makeValue = (v: float) => v->Reducer_T.IEvNumber + +describe("Namespace", () => { + let value = makeValue(5.) + let v2 = makeValue(2.) + let ns = Namespace.make()->Namespace.set("value", value) + + test("get", () => { + expect(ns->Namespace.get("value")) == Some(value) + }) + + test("get nonexisting value", () => { + expect(ns->Namespace.get("nosuchvalue")) == None + }) + + test("set", () => { + let ns2 = ns->Namespace.set("v2", v2) + expect(ns2->Namespace.get("v2")) == Some(v2) + }) + + test("immutable", () => { + let _ = ns->Namespace.set("v2", Reducer_T.IEvNumber(2.)) + expect(ns->Namespace.get("v2")) == None + }) + + describe("merge many", () => { + let x1 = makeValue(10.) + let x2 = makeValue(20.) + let x3 = makeValue(30.) + let x4 = makeValue(40.) + let ns1 = Namespace.make() + ->Namespace.set("x1", x1) + ->Namespace.set("x2", x2) + let ns2 = Namespace.make() + ->Namespace.set("x3", x3) + ->Namespace.set("x4", x4) + + let nsMerged = Namespace.mergeMany([ns, ns1, ns2]) + + test("merge many 1", () => { + expect(nsMerged->Namespace.get("x1")) == Some(x1) + }) + test("merge many 2", () => { + expect(nsMerged->Namespace.get("x4")) == Some(x4) + }) + test("merge many 3", () => { + expect(nsMerged->Namespace.get("value")) == Some(value) + }) + }) +}) 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 3440c3e0..416e0178 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,346 +3,345 @@ open Reducer_Peggy_TestHelpers describe("Peggy parse", () => { describe("float", () => { - 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)}") + 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}") }) describe("literals operators parenthesis", () => { - // Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement - 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))}") + 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)}") }) describe("unary", () => { - 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)))}") + 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))}") }) describe("multiplicative", () => { - 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", "{(: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", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}", + "{(:add (:multiply 1 2) (:multiply 3 4))}", ) testParse( "1 * 2 - 3 * 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}", + "{(:subtract (:multiply 1 2) (:multiply 3 4))}", ) testParse( "1 * 2 .+ 3 * 4", - "{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}", + "{(:dotAdd (:multiply 1 2) (:multiply 3 4))}", ) testParse( "1 * 2 .- 3 * 4", - "{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}", + "{(:dotSubtract (:multiply 1 2) (:multiply 3 4))}", ) testParse( "1 * 2 + 3 .* 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}", + "{(:add (:multiply 1 2) (:dotMultiply 3 4))}", ) testParse( "1 * 2 + 3 / 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}", + "{(:add (:multiply 1 2) (:divide 3 4))}", ) testParse( "1 * 2 + 3 ./ 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}", + "{(:add (:multiply 1 2) (:dotDivide 3 4))}", ) testParse( "1 * 2 - 3 .* 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}", + "{(:subtract (:multiply 1 2) (:dotMultiply 3 4))}", ) testParse( "1 * 2 - 3 / 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}", + "{(:subtract (:multiply 1 2) (:divide 3 4))}", ) testParse( "1 * 2 - 3 ./ 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}", + "{(:subtract (:multiply 1 2) (:dotDivide 3 4))}", ) testParse( "1 * 2 - 3 * 4^5", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5))))}", + "{(:subtract (:multiply 1 2) (:multiply 3 (:pow 4 5)))}", ) testParse( "1 * 2 - 3 * 4^5^6", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6))))}", + "{(:subtract (:multiply 1 2) (:multiply 3 (:pow (:pow 4 5) 6)))}", ) testParse( "1 * -a[-2]", - "{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}", + "{(:multiply 1 (:unaryMinus (:$_atIndex_$ :a (:unaryMinus 2))))}", ) }) describe("multi-line", () => { - testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}") - testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}") + testParse("x=1; 2", "{:x = {1}; 2}") + testParse("x=1; y=2", "{:x = {1}; :y = {2}}") }) describe("variables", () => { - testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}") - testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") - testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}") + testParse("x = 1", "{:x = {1}}") + testParse("x", "{:x}") + testParse("x = 1; x", "{:x = {1}; :x}") }) describe("functions", () => { - testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments - testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}") + testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments + testParse("identity(x)", "{(:identity :x)}") }) describe("arrays", () => { - testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$))}") - testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 0 1 2))}") + testParse("[]", "{[]}") + testParse("[0, 1, 2]", "{[0; 1; 2]}") testParse( "['hello', 'world']", - "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 'hello' 'world'))}", + "{['hello'; 'world']}", ) testParse( "([0,1,2])[1]", - "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ 0 1 2) 1))}", + "{(:$_atIndex_$ [0; 1; 2] 1)}", ) }) describe("records", () => { testParse( "{a: 1, b: 2}", - "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}", + "{{'a': 1, 'b': 2}}", ) testParse( "{1+0: 1, 2+0: 2}", - "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2)))}", + "{{(:add 1 0): 1, (:add 2 0): 2}}", ) // key can be any expression - testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}") + testParse("record.property", "{(:$_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)", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::b 1))))}") - testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}") + testParse("a==!b(1)", "{(:equal :a (:not (:b 1)))}") + testParse("a==!b[1]", "{(:equal :a (:not (:$_atIndex_$ :b 1)))}") testParse( "a==!b.one", - "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}", + "{(:equal :a (:not (:$_atIndex_$ :b 'one')))}", ) }) describe("comments", () => { - 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("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( ` /* This is a multi line comment */ 1`, - "{(::$_endOfOuterBlock_$ () 1)}", + "{1}", ) }) describe("ternary operator", () => { - testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}") + testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}") testParse( "false ? 2 : false ? 4 : 5", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}", + "{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}", ) // nested ternary }) describe("if then else", () => { testParse( "if true then 2 else 3", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}", + "{(::$$_ternary_$$ true {2} {3})}", ) testParse( "if false then {2} else {3}", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} {3}))}", + "{(::$$_ternary_$$ false {2} {3})}", ) testParse( "if false then {2} else if false then {4} else {5}", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5})))}", + "{(::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5}))}", ) //nested if }) describe("logical", () => { - testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}") - testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}") - testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison - testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}") - testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}") - testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}") - testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}") + testParse("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", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}", + "{(:or (:and :a (:unequal :b :c)) :d)}", ) testParse( "a && !(b==c) || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}", + "{(:or (:and :a (:not (:equal :b :c))) :d)}", ) testParse( "a && b>=c || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}", + "{(:or (:and :a (:largerEq :b :c)) :d)}", ) testParse( "a && !(b>=c) || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}", + "{(:or (:and :a (:not (:largerEq :b :c))) :d)}", ) testParse( "a && b<=c || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smallerEq :b :c)) :d))}", + "{(:or (:and :a (:smallerEq :b :c)) :d)}", ) - testParse("a && b>c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::larger :b :c)) :d))}") + testParse("a && b>c || d", "{(:or (:and :a (:larger :b :c)) :d)}") testParse( "a && b { - testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") - testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}") + testParse("1 -> add(2)", "{(:add 1 2)}") + testParse("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}") testParse( "-a[1] -> add(2)", - "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::$_atIndex_$ :a 1)) 2))}", + "{(: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("-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", - "{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}", + "{(:multiply (:subtract 1 2) 3)}", ) }) describe("elixir pipe", () => { //handled together with -> so there is no need for seperate tests - testParse("1 |> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") + testParse("1 |> add(2)", "{(:add 1 2)}") }) describe("to", () => { - testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}") + testParse("1 to 2", "{(:credibleIntervalToDistribution 1 2)}") testParse( "-1 to -2", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}", + "{(:credibleIntervalToDistribution (:unaryMinus 1) (:unaryMinus 2))}", ) // lower than unary testParse( "a[1] to a[2]", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2)))}", + "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 1) (:$_atIndex_$ :a 2))}", ) // lower than post testParse( "a.p1 to a.p2", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}", + "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 'p1') (:$_atIndex_$ :a 'p2'))}", ) // lower than post testParse( "1 to 2 + 3", - "{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}", + "{(:add (:credibleIntervalToDistribution 1 2) 3)}", ) // higher than binary operators testParse( "1->add(2) to 3->add(4) -> add(4)", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4)))}", + "{(: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}; (::$_endOfOuterBlock_$ () :x)}") + testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; :x}") }) describe("lambda", () => { - 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("{|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)}}; (::$_endOfOuterBlock_$ () ())}", + "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}", ) // 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}; (::$_endOfOuterBlock_$ () :z)}", + "{:myadd = {|:x,:y| {(:add :x :y)}}; :z = {:myadd}; :z}", ) testParse( "myadd(x,y)=x+y; z=[myadd]; z", - "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ :myadd)}; (::$_endOfOuterBlock_$ () :z)}", + "{:myadd = {|:x,:y| {(:add :x :y)}}; :z = {[:myadd]}; :z}", ) testParse( "myaddd(x,y)=x+y; z={x: myaddd}; z", - "{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; (::$_endOfOuterBlock_$ () :z)}", + "{:myaddd = {|:x,:y| {(:add :x :y)}}; :z = {{'x': :myaddd}}; :z}", ) - testParse("f({|x| x+1})", "{(::$_endOfOuterBlock_$ () (::f {|:x| {(::add :x 1)}}))}") + testParse("f({|x| x+1})", "{(:f {|:x| (:add :x 1)})}") testParse( "map(arr, {|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}", + "{(:map :arr {|:x| (:add :x 1)})}", ) testParse( "map([1,2,3], {|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ 1 2 3) {|:x| {(::add :x 1)}}))}", + "{(:map [1; 2; 3] {|:x| (:add :x 1)})}", ) testParse( "[1,2,3]->map({|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ 1 2 3) {|:x| {(::add :x 1)}}))}", + "{(:map [1; 2; 3] {|:x| (:add :x 1)})}", ) }) describe("unit", () => { - testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}") - testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}") - testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}") + testParse("1m", "{(:fromUnit_m 1)}") + testParse("1M", "{(:fromUnit_M 1)}") + testParse("1m+2cm", "{(:add (:fromUnit_m 1) (:fromUnit_cm 2))}") }) describe("Module", () => { - testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") - testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}") + testParse("x", "{:x}") + testParse("Math.pi", "{:Math.pi}") }) }) @@ -351,19 +350,19 @@ describe("parsing new line", () => { ` a + b`, - "{(::$_endOfOuterBlock_$ () (::add :a :b))}", + "{(:add :a :b)}", ) testParse( ` x= 1`, - "{:x = {1}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}}", ) testParse( ` x=1 y=2`, - "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}; :y = {2}}", ) testParse( ` @@ -371,7 +370,7 @@ describe("parsing new line", () => { y=2; y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -379,7 +378,7 @@ describe("parsing new line", () => { y=2 y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -388,7 +387,7 @@ describe("parsing new line", () => { y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -396,7 +395,7 @@ describe("parsing new line", () => { y=2 z=3 `, - "{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}; :y = {2}; :z = {3}}", ) testParse( ` @@ -407,7 +406,7 @@ describe("parsing new line", () => { x+y+z } `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; (::$_endOfOuterBlock_$ () ())}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}}", ) testParse( ` @@ -420,7 +419,7 @@ describe("parsing new line", () => { g=f+4 g `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () :g)}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}; :g = {(:add :f 4)}; :g}" ) testParse( ` @@ -442,7 +441,7 @@ describe("parsing new line", () => { p -> q `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () (::q (::p (::h :g))))}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}; :g = {(:add :f 4)}; (:q (:p (:h :g)))}" ) testParse( ` @@ -451,7 +450,7 @@ describe("parsing new line", () => { c |> d `, - "{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}", + "{(:d (:c (:b :a)))}", ) testParse( ` @@ -461,6 +460,6 @@ describe("parsing new line", () => { d + e `, - "{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}", + "{(:add (:d (:c (:b :a))) :e)}" ) }) 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 index 951005c2..bcc99bfb 100644 --- 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 @@ -5,18 +5,18 @@ 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("1", "1", ~v="1", ()) + testToExpression("x=1", "x = {1}", ~v="()", ()) testToExpression( "x=1; y=2", - "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", + "x = {1}; y = {2}", ~v="()", (), ) - testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) + testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ()) testToExpression( "x={a=1; a}; x", - "{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}", + "x = {a = {1}; a}; 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 eefa2294..d2e64393 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,69 +7,69 @@ 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", "{(:$_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))}", ()) + 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)", ()) }) describe("unary", () => { - 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)))}", ()) + 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))", ()) }) describe("multi-line", () => { - testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) + testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ()) testToExpression( "x=1; y=2", - "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", + "x = {1}; y = {2}", (), ) }) describe("variables", () => { - 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", ()) + testToExpression("x = 1", "x = {1}", ()) + testToExpression("x", "x", ~v="Error(x is not defined)", ()) //TODO: value should return error + testToExpression("x = 1; x", "x = {1}; x", ~v="1", ()) }) describe("functions", () => { testToExpression( "identity(x) = x", - "{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", + "identity = {|x| {x}}", (), ) // Function definitions become lambda assignments - testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly + testToExpression("identity(x)", "(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)})); (:$_endOfOuterBlock_$ () (:f 3))}", + "f = {|x| {(larger)(x, 2) ? (0) : (1)}}; (f)(3)", ~v="0", (), ) }) describe("arrays", () => { - testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) + testToExpression("[]", "[]", ~v="[]", ()) testToExpression( "[0, 1, 2]", - "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 0 1 2))}", + "[0, 1, 2]", ~v="[0,1,2]", (), ) testToExpression( "['hello', 'world']", - "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 'hello' 'world'))}", + "['hello', 'world']", ~v="['hello','world']", (), ) testToExpression( "([0,1,2])[1]", - "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ 0 1 2) 1))}", + "($_atIndex_$)([0, 1, 2], 1)", ~v="1", (), ) @@ -78,40 +78,40 @@ describe("Peggy to Expression", () => { describe("records", () => { testToExpression( "{a: 1, b: 2}", - "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}", + "{'a': 1, 'b': 2}", ~v="{a: 1,b: 2}", (), ) testToExpression( "{1+0: 1, 2+0: 2}", - "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}", + "{(add)(1, 0): 1, (add)(2, 0): 2}", (), ) // key can be any expression testToExpression( "record.property", - "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", + "($_atIndex_$)(record, 'property')", (), ) testToExpression( "record={property: 1}; record.property", - "{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", + "record = {{'property': 1}}; ($_atIndex_$)(record, 'property')", ~v="1", (), ) }) describe("comments", () => { - 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 line comment", "1", ~v="1", ()) + testToExpression("1 // This is a line comment", "1", ~v="1", ()) testToExpression( "1 /* This is a multi line comment */", - "{(:$_endOfOuterBlock_$ () 1)}", + "1", ~v="1", (), ) testToExpression( "/* This is a multi line comment */ 1", - "{(:$_endOfOuterBlock_$ () 1)}", + "1", ~v="1", (), ) @@ -120,25 +120,25 @@ describe("Peggy to Expression", () => { describe("ternary operator", () => { testToExpression( "true ? 1 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}", + "true ? (1) : (0)", ~v="1", (), ) testToExpression( "false ? 1 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}", + "false ? (1) : (0)", ~v="0", (), ) testToExpression( "true ? 1 : false ? 2 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}", + "true ? (1) : (false ? (2) : (0))", ~v="1", (), ) // nested ternary testToExpression( "false ? 1 : false ? 2 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}", + "false ? (1) : (false ? (2) : (0))", ~v="0", (), ) // nested ternary @@ -146,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)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(larger)(a, 5) ? (1) : (0)}}; (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)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(larger)(a, 5) ? (a) : (0)}}; (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)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(smaller)(a, 5) ? (1) : (a)}}; (f)(6)", ~v="6", (), ) @@ -170,39 +170,39 @@ describe("Peggy to Expression", () => { describe("if then else", () => { testToExpression( "if true then 2 else 3", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", + "true ? ({2}) : ({3})", (), ) testToExpression( "if true then {2} else {3}", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", + "true ? ({2}) : ({3})", (), ) testToExpression( "if false then {2} else if false then {4} else {5}", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}", + "false ? ({2}) : (false ? ({4}) : ({5}))", (), ) //nested if }) describe("pipe", () => { - testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) + testToExpression("1 -> add(2)", "(add)(1, 2)", ~v="3", ()) testToExpression( "-1 -> add(2)", - "{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}", + "(add)((unaryMinus)(1), 2)", ~v="1", (), ) // note that unary has higher priority naturally testToExpression( "1 -> add(2) * 3", - "{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}", + "(multiply)((add)(1, 2), 3)", ~v="9", (), ) }) describe("elixir pipe", () => { - testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) + testToExpression("1 |> add(2)", "(add)(1, 2)", ~v="3", ()) }) // see testParse for priorities of to and credibleIntervalToDistribution @@ -212,7 +212,8 @@ 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}); (:$_endOfOuterBlock_$ () ())}", + "y = {99}; x = {y = {1}; y}", + // "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}", (), ) }) @@ -220,23 +221,23 @@ describe("Peggy to Expression", () => { describe("lambda", () => { testToExpression( "{|x| x}", - "{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}", + "{|x| x}", ~v="lambda(x=>internal code)", (), ) testToExpression( "f={|x| x}", - "{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}", + "f = {{|x| x}}", (), ) testToExpression( "f(x)=x", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", + "f = {|x| {x}}", (), ) // Function definitions are lambda assignments testToExpression( "f(x)=x ? 1 : 0", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}", + "f = {|x| {x ? (1) : (0)}}", (), ) }) @@ -250,6 +251,6 @@ describe("Peggy to Expression", () => { // ->expect // ->toBe("") // }) - testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ()) + testToExpression("Math.pi", "Math.pi", ~v="3.141592653589793", ()) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res index 205d4883..55ffff27 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res @@ -2,11 +2,11 @@ open Jest open Reducer_Peggy_TestHelpers describe("Construct Array", () => { - testToExpression("[1,2]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 1 2))}", ~v="[1,2]", ()) - testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) + testToExpression("[1,2]", "[1, 2]", ~v="[1,2]", ()) + testToExpression("[]", "[]", ~v="[]", ()) testToExpression( "f(x)=x; g(x)=x; [f, g]", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_let_$ :g (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () (:$_constructArray_$ :f :g))}", + "f = {|x| {x}}; g = {|x| {x}}; [f, g]", ~v="[lambda(x=>internal code),lambda(x=>internal code)]", (), ) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res index a2abb85d..52f8329b 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res @@ -4,11 +4,11 @@ open Reducer_TestHelpers describe("Parse function assignment", () => { testParseToBe( "f(x)=x", - "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})", + "Ok(f = {|x| {x}})" ) testParseToBe( "f(x)=2*x", - "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})", + "Ok(f = {|x| {(multiply)(2, x)}})" ) //MathJs does not allow blocks in function definitions }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index c86f4bca..c1af1d59 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -4,7 +4,7 @@ open Reducer_TestHelpers describe("Parse ternary operator", () => { testParseToBe( "true ? 'YES' : 'NO'", - "Ok({(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 'YES' 'NO'))})", + "Ok({(:$$_ternary_$$ true 'YES' 'NO')})", ) }) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index c8207131..330767df 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -121,38 +121,38 @@ describe("parse on distribution functions", () => { describe("power", () => { testParse( "normal(5,2) ^ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})", + "Ok((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))})") + testParse("3 ^ normal(5,1)", "Ok((pow)(3, (normal)(5, 1)))") + testParse("normal(5,2) ^ 3", "Ok((pow)((normal)(5, 2), 3))") }) describe("subtraction", () => { - testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})") - testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})") + testParse("10 - normal(5,1)", "Ok((subtract)(10, (normal)(5, 1)))") + testParse("normal(5,1) - 10", "Ok((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((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))", + "Ok((:$$_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({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})", + "Ok((dotMultiply)((normal)(5, 2), (normal)(5, 1)))", ) testParse( "normal(5,2) ./ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})", + "Ok((dotDivide)((normal)(5, 2), (normal)(5, 1)))", ) testParse( "normal(5,2) .^ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})", + "Ok((dotPow)((normal)(5, 2), (normal)(5, 1)))", ) }) describe("equality", () => { - testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})") + testParse("5 == normal(5,2)", "Ok((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_includes_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res index 471ac55b..3097d74a 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res @@ -1,121 +1,117 @@ -// @@warning("-44") -// module InternalExpressionValue = ReducerInterface_InternalExpressionValue -// module Project = ForTS_ReducerProject -// module Bindings = Reducer_Bindings +@@warning("-44") +module InternalExpressionValue = ReducerInterface_InternalExpressionValue +module Project = ForTS_ReducerProject +module Bindings = Reducer_Bindings -// open Jest -// open Expect -// open Expect.Operators +open Jest +open Expect +open Expect.Operators -// describe("Parse includes", () => { -// let project = Project.createProject() -// Project.setSource( -// project, -// "main", -// ` -// #include 'common' -// x=1`, -// ) -// Project.parseIncludes(project, "main") -// test("dependencies", () => { -// expect(Project.getDependencies(project, "main")) == ["common"] -// }) -// test("dependents", () => { -// expect(Project.getDependents(project, "main")) == [] -// }) -// test("getIncludes", () => { -// let mainIncludes = Project.getIncludes(project, "main") -// switch mainIncludes { -// | Ok(includes) => expect(includes) == ["common"] -// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) -// } -// }) -// let internalProject = project->Project.T.Private.castToInternalProject -// test("past chain", () => { -// expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] -// }) -// test("import as variables", () => { -// expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [] -// }) -// }) +describe("Parse includes", () => { + let project = Project.createProject() + Project.setSource( + project, + "main", + ` +#include 'common' +x=1`, + ) + Project.parseIncludes(project, "main") + test("dependencies", () => { + expect(Project.getDependencies(project, "main")) == ["common"] + }) + test("dependents", () => { + expect(Project.getDependents(project, "main")) == [] + }) + test("getIncludes", () => { + let mainIncludes = Project.getIncludes(project, "main") + switch mainIncludes { + | Ok(includes) => expect(includes) == ["common"] + | Error(error) => fail(error->Reducer_ErrorValue.errorToString) + } + }) + test("past chain", () => { + expect(project->Project.getPastChain("main")) == ["common"] + }) + test("import as variables", () => { + expect(project->Project.Private.getIncludesAsVariables("main")) == [] + }) +}) -// describe("Parse includes", () => { -// let project = Project.createProject() -// Project.setSource( -// project, -// "main", -// ` -// #include 'common' -// #include 'myModule' as myVariable -// x=1`, -// ) -// Project.parseIncludes(project, "main") +describe("Parse includes", () => { + let project = Project.createProject() + Project.setSource( + project, + "main", + ` +#include 'common' +#include 'myModule' as myVariable +x=1`, + ) + Project.parseIncludes(project, "main") -// test("dependencies", () => { -// expect(Project.getDependencies(project, "main")) == ["common", "myModule"] -// }) + test("dependencies", () => { + expect(Project.getDependencies(project, "main")) == ["common", "myModule"] + }) -// test("dependents", () => { -// expect(Project.getDependents(project, "main")) == [] -// }) + test("dependents", () => { + expect(Project.getDependents(project, "main")) == [] + }) -// test("getIncludes", () => { -// let mainIncludes = Project.getIncludes(project, "main") -// switch mainIncludes { -// | Ok(includes) => expect(includes) == ["common", "myModule"] -// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) -// } -// }) + test("getIncludes", () => { + let mainIncludes = Project.getIncludes(project, "main") + switch mainIncludes { + | Ok(includes) => expect(includes) == ["common", "myModule"] + | Error(error) => fail(error->Reducer_ErrorValue.errorToString) + } + }) -// let internalProject = project->Project.T.Private.castToInternalProject + test("direct past chain", () => { + expect(project->Project.Private.getPastChain("main")) == ["common"] + }) -// test("direct past chain", () => { -// expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] -// }) + test("direct includes", () => { + expect(project->Project.Private.getDirectIncludes("main")) == ["common"] + }) -// test("direct includes", () => { -// expect(Project.Private.getDirectIncludes(internalProject, "main")) == ["common"] -// }) + test("include as variables", () => { + expect(project->Project.Private.getIncludesAsVariables("main")) == [ + ("myVariable", "myModule"), + ] + }) +}) -// test("include as variables", () => { -// expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ -// ("myVariable", "myModule"), -// ] -// }) -// }) - -// describe("Parse multiple direct includes", () => { -// let project = Project.createProject() -// Project.setSource( -// project, -// "main", -// ` -// #include 'common' -// #include 'common2' -// #include 'myModule' as myVariable -// x=1`, -// ) -// Project.parseIncludes(project, "main") -// test("dependencies", () => { -// expect(Project.getDependencies(project, "main")) == ["common", "common2", "myModule"] -// }) -// test("dependents", () => { -// expect(Project.getDependents(project, "main")) == [] -// }) -// test("getIncludes", () => { -// let mainIncludes = Project.getIncludes(project, "main") -// switch mainIncludes { -// | Ok(includes) => expect(includes) == ["common", "common2", "myModule"] -// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) -// } -// }) -// let internalProject = project->Project.T.Private.castToInternalProject -// test("direct past chain", () => { -// expect(Project.getPastChain(project, "main")) == ["common", "common2"] -// }) -// test("include as variables", () => { -// expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ -// ("myVariable", "myModule"), -// ] -// }) -// }) +describe("Parse multiple direct includes", () => { + let project = Project.createProject() + Project.setSource( + project, + "main", + ` +#include 'common' +#include 'common2' +#include 'myModule' as myVariable +x=1`, + ) + Project.parseIncludes(project, "main") + test("dependencies", () => { + expect(Project.getDependencies(project, "main")) == ["common", "common2", "myModule"] + }) + test("dependents", () => { + expect(Project.getDependents(project, "main")) == [] + }) + test("getIncludes", () => { + let mainIncludes = Project.getIncludes(project, "main") + switch mainIncludes { + | Ok(includes) => expect(includes) == ["common", "common2", "myModule"] + | Error(error) => fail(error->Reducer_ErrorValue.errorToString) + } + }) + test("direct past chain", () => { + expect(Project.getPastChain(project, "main")) == ["common", "common2"] + }) + test("include as variables", () => { + expect(project->Project.Private.getIncludesAsVariables("main")) == [ + ("myVariable", "myModule"), + ] + }) +}) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res index 1bfe8c36..ccda4b4f 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res @@ -7,8 +7,6 @@ open Jest open Expect open Expect.Operators -// test("", () => expect(1)->toBe(1)) - let runFetchResult = (project, sourceId) => { Project.run(project, sourceId) Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult @@ -17,18 +15,9 @@ let runFetchResult = (project, sourceId) => { let runFetchFlatBindings = (project, sourceId) => { Project.run(project, sourceId) Project.getBindings(project, sourceId) - ->Bindings.removeResult - ->InternalExpressionValue.toStringBindings + ->InternalExpressionValue.toStringRecord } -test("setting continuation", () => { - let project = Project.createProject() - let sampleBindings = Bindings.makeEmptyBindings()->Bindings.set("test", IEvVoid) - ReducerProject.setContinuation(project, "main", sampleBindings) - let answer = ReducerProject.getContinuation(project, "main") - expect(answer)->toBe(sampleBindings) -}) - test("test result true", () => { let project = Project.createProject() Project.setSource(project, "main", "true") @@ -50,7 +39,7 @@ test("test library", () => { test("test bindings", () => { let project = Project.createProject() Project.setSource(project, "variables", "myVariable=666") - runFetchFlatBindings(project, "variables")->expect->toBe("@{myVariable: 666}") + runFetchFlatBindings(project, "variables")->expect->toBe("{myVariable: 666}") }) describe("project1", () => { @@ -86,7 +75,7 @@ describe("project1", () => { runFetchResult(project, "main")->expect->toBe("Ok(1)") }) test("test bindings", () => { - runFetchFlatBindings(project, "first")->expect->toBe("@{x: 1}") + runFetchFlatBindings(project, "first")->expect->toBe("{x: 1}") }) }) @@ -96,7 +85,7 @@ describe("project2", () => { Project.setContinues(project, "second", ["first"]) Project.setSource(project, "first", "x=1") Project.setSource(project, "second", "y=2") - Project.setSource(project, "main", "y") + Project.setSource(project, "main", "z=3;y") test("runOrder", () => { expect(Project.getRunOrder(project)) == ["first", "second", "main"] @@ -120,7 +109,8 @@ describe("project2", () => { runFetchResult(project, "main")->expect->toBe("Ok(2)") }) test("test bindings", () => { - runFetchFlatBindings(project, "main")->expect->toBe("@{x: 1,y: 2}") + // bindings from continues are not exposed! + runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}") }) }) @@ -150,7 +140,7 @@ describe("project with include", () => { ) Project.parseIncludes(project, "second") //The only way of setting includes - Project.setSource(project, "main", "y") + Project.setSource(project, "main", "z=3; y") test("runOrder", () => { expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"] @@ -176,7 +166,8 @@ describe("project with include", () => { runFetchResult(project, "main")->expect->toBe("Ok(2)") }) test("test bindings", () => { - runFetchFlatBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}") + // bindings from continues are not exposed! + runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}") }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res index 59579c80..13cab7aa 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res @@ -16,13 +16,13 @@ Case "Running a single source". /* Let's start with running a single source and getting Result as well as the Bindings First you need to create a project. A project is a collection of sources. Project takes care of the dependencies between the sources, correct compilation and run order. - You can run any source in the project. It will be compiled and run if it is not already done else already existing results will be presented. + You can run any source in the project. It will be compiled and run if it hasn't happened already; otherwise already existing results will be presented. The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project. In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source. */ let project = Project.createProject() /* Every source has a name. This is used for debugging, dependencies and error messages. */ - Project.setSource(project, "main", "1 + 2") + project->Project.setSource("main", "1 + 2") /* Let's run "main" source. */ project->Project.run("main") /* Now you have a result for "main" source. @@ -46,27 +46,27 @@ Case "Running a single source". Getting None means you have forgotten to run the source. */ let result = project->Project.getResult("main") - let bindings = project->Project.getBindings("main")->Bindings.removeResult + let bindings = project->Project.getBindings("main") /* Let's display the result and bindings */ ( result->InternalExpressionValue.toStringResult, - bindings->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(3)", "@{}") + bindings->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(3)", "{}") /* You've got 3 with empty bindings. */ }) test("run summary", () => { let project = Project.createProject() - Project.setSource(project, "main", "1 + 2") - Project.runAll(project) - let result = Project.getResult(project, "main") - let bindings = Project.getBindings(project, "main")->Bindings.removeResult + project->Project.setSource("main", "1 + 2") + project->Project.runAll + let result = project->Project.getResult("main") + let bindings = project->Project.getBindings("main") /* Now you have external bindings and external result. */ ( result->InternalExpressionValue.toStringResult, - bindings->Reducer_T.IEvBindings->InternalExpressionValue.toString, - )->expect == ("Ok(3)", "@{}") + bindings->Reducer_T.IEvRecord->InternalExpressionValue.toString, + )->expect == ("Ok(3)", "{}") }) test("run with an environment", () => { @@ -74,12 +74,12 @@ Case "Running a single source". let project = Project.createProject() /* Optional. Set your custom environment anytime before running */ - Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment) + project->Project.setEnvironment(InternalExpressionValue.defaultEnvironment) - Project.setSource(project, "main", "1 + 2") - Project.runAll(project) - let result = Project.getResult(project, "main") - let _bindings = Project.getBindings(project, "main") + project->Project.setSource("main", "1 + 2") + project->Project.runAll + let result = project->Project.getResult("main") + let _bindings = project->Project.getBindings("main") result->InternalExpressionValue.toStringResult->expect == "Ok(3)" }) @@ -89,12 +89,16 @@ Case "Running a single source". let (result, bindings) = Project.evaluate("1+2") ( result->InternalExpressionValue.toStringResult, - bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(3)", "@{}") + bindings->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(3)", "{}") }) }) }) +// s1: { x = 1 } / { stdlib } +// s2 (deps=[s1]): { y = 2 } / { { x = 1 } + stdlib } +// s3 (deps=[s2]): { z = 3 } / { { y = 2 } + stdlib } + //TODO multiple sources //TODO multiple sources with includes. Introduction to includes //TODO multiple sources with multi level includes. Cycle detection diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res index 80c73c9e..d50ec544 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res @@ -14,27 +14,27 @@ describe("ReducerProject Tutorial", () => { test("Chaining", () => { let project = Project.createProject() /* This time let's add 3 sources and chain them together */ - Project.setSource(project, "source1", "x=1") + project->Project.setSource("source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source2", "y=x+1") /* To run, source2 depends on source1 */ - Project.setContinues(project, "source2", ["source1"]) + project->Project.setContinues("source2", ["source1"]) - Project.setSource(project, "source3", "z=3") + project->Project.setSource("source3", "z=y+1") /* To run, source3 depends on source2 */ - Project.setContinues(project, "source3", ["source2"]) + project->Project.setContinues("source3", ["source2"]) /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") ( result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + bindings3->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(())", "{z: 3}") }) test("Depending", () => { @@ -43,24 +43,24 @@ describe("ReducerProject Tutorial", () => { let project = Project.createProject() /* This time source1 and source2 are not depending on anything */ - Project.setSource(project, "source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source1", "x=1") + project->Project.setSource("source2", "y=2") - Project.setSource(project, "source3", "z=3") + project->Project.setSource("source3", "z=x+y") /* To run, source3 depends on source1 and source3 together */ - Project.setContinues(project, "source3", ["source1", "source2"]) + project->Project.setContinues("source3", ["source1", "source2"]) /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") ( result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + bindings3->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(())", "{z: 3}") }) test("Intro to including", () => { @@ -70,33 +70,32 @@ describe("ReducerProject Tutorial", () => { let project = Project.createProject() /* This time source1 and source2 are not depending on anything */ - Project.setSource(project, "source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source1", "x=1") + project->Project.setSource("source2", "y=2") - Project.setSource( - project, + project->Project.setSource( "source3", ` #include "source1" #include "source2" - z=3`, + z=x+y`, ) /* We need to parse the includes to set the dependencies */ - Project.parseIncludes(project, "source3") + project->Project.parseIncludes("source3") /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 This time you are getting all the variables because we are including the other sources Behind the scenes parseIncludes is setting the dependencies */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") ( result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + bindings3->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(())", "{z: 3}") /* Doing it like this is too verbose for a storybook But I hope you have seen the relation of setContinues and parseIncludes */ diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res index 12c14468..8e584576 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res @@ -16,8 +16,7 @@ Here we will finally proceed to a real life scenario. */ /* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */ /* Everything happens inside a project, so let's have a project */ let project = Project.createProject() - Project.setSource( - project, + project->Project.setSource( "main", ` #include "common" @@ -25,10 +24,10 @@ Here we will finally proceed to a real life scenario. */ `, ) /* We need to parse includes after changing the source */ - Project.parseIncludes(project, "main") + project->Project.parseIncludes("main") test("getDependencies", () => { /* Parse includes has set the dependencies */ - Project.getDependencies(project, "main")->expect == ["common"] + project->Project.getDependencies("main")->expect == ["common"] /* If there were no includes than there would be no dependencies */ /* However if there was a syntax error at includes then would be no dependencies also */ /* Therefore looking at dependencies is not the right way to load includes */ @@ -36,7 +35,7 @@ Here we will finally proceed to a real life scenario. */ }) test("getIncludes", () => { /* Parse includes has set the includes */ - switch Project.getIncludes(project, "main") { + switch project->Project.getIncludes("main") { | Ok(includes) => includes->expect == ["common"] | Error(err) => err->Reducer_ErrorValue.errorToString->fail } @@ -50,7 +49,7 @@ Here we will finally proceed to a real life scenario. */ include or depend on the current source. But you don't need to use this to execute the projects. It is provided for completeness of information. */ - Project.getDependents(project, "main")->expect == [] + project->Project.getDependents("main")->expect == [] /* Nothing is depending on or including main */ }) @@ -76,29 +75,29 @@ Here we will finally proceed to a real life scenario. */ /* let's recursively load the sources */ let rec loadIncludesRecursively = (project, sourceName, visited) => { - if Js.Array2.includes(visited, sourceName) { + if visited->Js.Array2.includes(sourceName) { /* Oh we have already visited this source. There is an include cycle */ "Cyclic include ${sourceName}"->Js.Exn.raiseError } else { let newVisited = Js.Array2.copy(visited) - let _ = Js.Array2.push(newVisited, sourceName) + let _ = newVisited->Js.Array2.push(sourceName) /* Let's parse the includes and dive into them */ Project.parseIncludes(project, sourceName) - let rIncludes = Project.getIncludes(project, sourceName) + let rIncludes = project->Project.getIncludes(sourceName) switch rIncludes { /* Maybe there is an include syntax error */ | Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError | Ok(includes) => - Belt.Array.forEach(includes, newIncludeName => { + includes->Belt.Array.forEach(newIncludeName => { /* We have got one of the new includes. Let's load it and add it to the project */ let newSource = loadSource(newIncludeName) - Project.setSource(project, newIncludeName, newSource) + project->Project.setSource(newIncludeName, newSource) /* The new source is loaded and added to the project. */ /* Of course the new source might have includes too. */ /* Let's recursively load them */ - loadIncludesRecursively(project, newIncludeName, newVisited) + project->loadIncludesRecursively(newIncludeName, newVisited) }) } } @@ -110,45 +109,46 @@ Here we will finally proceed to a real life scenario. */ let project = Project.createProject() - /* main includes source3 which includes source2 which includes source1 */ - Project.setSource( - project, + project->Project.setSource( "main", ` + #include "source1" + #include "source2" #include "source3" - x+y+z + a = x+y+z + b = doubleX + a `, ) /* Setting source requires parsing and loading the includes recursively */ - loadIncludesRecursively(project, "main", []) //No visited yet + project->loadIncludesRecursively("main", []) // Not visited yet /* Let's salt it more. Let's have another source in the project which also has includes */ /* doubleX includes source1 which is eventually included by main as well */ - Project.setSource( - project, + project->Project.setSource( "doubleX", ` #include "source1" doubleX = x * 2 `, ) - loadIncludesRecursively(project, "doubleX", []) + project->loadIncludesRecursively("doubleX", []) /* Remember, any time you set a source, you need to load includes recursively */ /* As doubleX is not included by main, it is not loaded recursively. So we link it to the project as a dependency */ - Project.setContinues(project, "main", ["doubleX"]) + project->Project.setContinues("main", ["doubleX"]) /* Let's run the project */ - Project.runAll(project) - let result = Project.getResult(project, "main") - let bindings = Project.getBindings(project, "main") + project->Project.runAll + let result = project->Project.getResult("main") + let bindings = project->Project.getBindings("main") /* And see the result and bindings.. */ test("recursive includes", () => { ( result->InternalExpressionValue.toStringResult, - bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}") + bindings->InternalExpressionValue.toStringRecord, + )->expect == ("Ok(6)", "{a: 6,b: 2}") /* Everything as expected */ }) }) diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index d9c352a5..a05df290 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -5,11 +5,11 @@ "dir": "src/rescript", "subdirs": true }, - // { - // "dir": "__tests__", - // "type": "dev", - // "subdirs": true - // }, + { + "dir": "__tests__", + "type": "dev", + "subdirs": true + }, { "dir": "benchmark", "type": "dev", diff --git a/packages/squiggle-lang/scripts/run-file.js b/packages/squiggle-lang/scripts/run-file.js index fb09aa23..f697f909 100755 --- a/packages/squiggle-lang/scripts/run-file.js +++ b/packages/squiggle-lang/scripts/run-file.js @@ -35,4 +35,4 @@ const result = project.getResult("a"); console.log("Result:", result.tag, result.value.toString()); const bindings = project.getBindings("a"); -console.log("Bindings:", bindings.asValue().toString()); +console.log("Bindings:", bindings.toString()); diff --git a/packages/squiggle-lang/src/js/SqRecord.ts b/packages/squiggle-lang/src/js/SqRecord.ts index 85eac76d..6ce3823e 100644 --- a/packages/squiggle-lang/src/js/SqRecord.ts +++ b/packages/squiggle-lang/src/js/SqRecord.ts @@ -12,4 +12,8 @@ export class SqRecord { ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const ); } + + toString() { + return RSRecord.toString(this._value); + } } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index d76a8cbc..96f5b0a8 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -8,6 +8,7 @@ type errorValue = Reducer_ErrorValue.errorValue */ type rec frType = | FRTypeNumber + | FRTypeBool | FRTypeNumeric | FRTypeDistOrNumber | FRTypeDist @@ -27,6 +28,7 @@ and frTypeRecordParam = (string, frType) */ type rec frValue = | FRValueNumber(float) + | FRValueBool(bool) | FRValueDist(DistributionTypes.genericDist) | FRValueArray(array) | FRValueDistOrNumber(frValueDistOrNumber) @@ -71,6 +73,7 @@ module FRType = { let rec toString = (t: t) => switch t { | FRTypeNumber => "number" + | FRTypeBool => "bool" | FRTypeNumeric => "numeric" | FRTypeDist => "distribution" | FRTypeDistOrNumber => "distribution|number" @@ -107,6 +110,7 @@ module FRType = { | (FRTypeAny, f) => toFrValue(f) | (FRTypeString, IEvString(f)) => Some(FRValueString(f)) | (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f)) + | (FRTypeBool, IEvBool(f)) => Some(FRValueBool(f)) | (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f))) | (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueDistOrNumber(FRValueNumber(f))) @@ -142,6 +146,7 @@ module FRType = { let rec matchReverse = (e: frValue): internalExpressionValue => switch e { | FRValueNumber(f) => IEvNumber(f) + | FRValueBool(f) => IEvBool(f) | FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n) | FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n) | FRValueDist(dist) => IEvDistribution(dist) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res index 3bc610a4..f5f7c818 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res @@ -72,5 +72,25 @@ let library = [ | _ => Error(impossibleError) } } - ) + ), + makeFn( + "not", + [FRTypeNumber], + inputs => { + switch inputs { + | [IEvNumber(x)] => IEvBool(x != 0.)->Ok + | _ => Error(impossibleError) + } + } + ), + makeFn( + "not", + [FRTypeBool], + inputs => { + switch inputs { + | [IEvBool(x)] => IEvBool(!x)->Ok + | _ => Error(impossibleError) + } + } + ), ] 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 ddf250dc..c00dbe5c 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 @@ -18,15 +18,11 @@ let rec get = ({ namespace, parent }: t, id: string) => { } } -let getWithDefault = (namespace: t, id: string, default) => - switch namespace->get(id) { - | Some(v) => Some(v) - | None => default - } - let set = ({ namespace } as bindings: t, id: string, value): t => { - let _ = namespace->Reducer_Namespace.set(id, value) - bindings + { + ...bindings, + namespace: namespace->Reducer_Namespace.set(id, value), + } } let rec toString = ({ namespace, parent }: t) => { @@ -43,8 +39,8 @@ let extend = (bindings: t): t => { namespace: Reducer_Namespace.make(), parent: let make = (): t => { namespace: Reducer_Namespace.make(), parent: None } let removeResult = ({ namespace } as bindings: t): t => { - namespace->Belt.MutableMap.String.remove("__result__") - bindings + ...bindings, + namespace: namespace->Belt.Map.String.remove("__result__"), } let locals = ({ namespace }: t): Reducer_T.namespace => namespace @@ -92,16 +88,6 @@ let fromNamespace = (namespace: Reducer_Namespace.t): t => { namespace, parent: // NameSpace(Belt.Map.String.set(container, typeReferencesKey, r2)) // } -// let removeOther = (NameSpace(container): t, NameSpace(otherContainer): t): t => { -// let keys = Belt.Map.String.keysToArray(otherContainer) -// NameSpace( -// Belt.Map.String.keep(container, (key, _value) => { -// let removeThis = Js.Array2.includes(keys, key) -// !removeThis -// }), -// ) -// } - // let functionNotFoundError = (call: functionCall) => // REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res index 60adcb1e..f9b8a2c8 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res @@ -2,7 +2,7 @@ type t = Reducer_T.context let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => { { - bindings: stdLib->Reducer_Bindings.fromNamespace, + bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend, environment: environment, } } 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 6d032523..62ec6cff 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 @@ -34,36 +34,6 @@ let callInternal = ( | call => call->IEV.toStringFunctionCall->MathJs.Eval.eval } - // let constructRecord = arrayOfPairs => { - // Belt.Array.map(arrayOfPairs, pairValue => - // switch pairValue { - // | Reducer_T.IEvArray([IEvString(key), valueValue]) => (key, valueValue) - // | _ => ("wrong key type", pairValue->IEV.toStringWithType->IEvString) - // } - // ) - // ->Belt.Map.String.fromArray - // ->Reducer_T.IEvRecord - // ->Ok - // } - - // let arrayAtIndex = (aValueArray: array, fIndex: float) => - // switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { - // | Some(value) => value->Ok - // | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error - // } - - // let moduleAtIndex = (nameSpace: Reducer_T.nameSpace, sIndex) => - // switch Bindings.get(nameSpace, sIndex) { - // | Some(value) => value->Ok - // | None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error - // } - - // let recordAtIndex = (dict: Belt.Map.String.t, sIndex) => - // switch Belt.Map.String.get(dict, sIndex) { - // | Some(value) => value->Ok - // | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error - // } - let doAddArray = (originalA, b) => { let a = originalA->Js.Array2.copy let _ = Js.Array2.pushMany(a, b) @@ -84,10 +54,6 @@ let callInternal = ( value->Ok } - // let doSetBindings = (bindings: Reducer_T.nameSpace, symbol: string, value: Reducer_T.value) => { - // Bindings.set(bindings, symbol, value)->IEvBindings->Ok - // } - // let doSetTypeAliasBindings = ( // bindings: nameSpace, // symbol: string, 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 c0e623bc..bfaf993a 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 @@ -8,55 +8,74 @@ type errorValue = Reducer_ErrorValue.errorValue /* Recursively evaluate the expression */ -let rec evaluate: T.reducerFn = (expression, context) => { +let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`) switch expression { | T.EBlock(statements) => { let innerContext = {...context, bindings: context.bindings->Bindings.extend} - statements->Js.Array2.reduce((_, statement) => statement->evaluate(innerContext), T.IEvVoid) + let (value, _) = statements->Belt.Array.reduce( + (T.IEvVoid, innerContext), + ((_, currentContext), statement) => statement->evaluate(currentContext) + ) + (value, context) // inner context can be dropped } | T.EProgram(statements) => { - // Js.log(`bindings: ${context.bindings->Reducer_Bindings.toString}`) - let res = - statements->Js.Array2.reduce((_, statement) => statement->evaluate(context), T.IEvVoid) + // Js.log(`bindings: ${context.bindings->Bindings.locals->Reducer_Namespace.toString}`) + let (value, finalContext) = statements->Belt.Array.reduce( + (T.IEvVoid, context), + ((_, currentContext), statement) => statement->evaluate(currentContext)) - // Js.log(`bindings after: ${context.bindings->Reducer_Bindings.toString}`) - res + // Js.log(`bindings after: ${finalContext.bindings->Bindings.locals->Reducer_Namespace.toString}`) + (value, finalContext) } - | T.EArray(elements) => elements->Js.Array2.map(element => evaluate(element, context))->T.IEvArray + | T.EArray(elements) => { + let value = elements->Belt.Array.map(element => { + let (value, _) = evaluate(element, context) + value + })->T.IEvArray + (value, context) + } - | T.ERecord(pairs) => - pairs - ->Js.Array2.map(((eKey, eValue)) => { - let key = eKey->evaluate(context) - let keyString = switch key { - | IEvString(s) => s - | _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise - } - let value = eValue->evaluate(context) - (keyString, value) - }) - ->Belt.Map.String.fromArray - ->IEvRecord + | T.ERecord(pairs) => { + let value = + pairs + ->Belt.Array.map(((eKey, eValue)) => { + let (key, _) = eKey->evaluate(context) + let keyString = switch key { + | IEvString(s) => s + | _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise + } + let (value, _) = eValue->evaluate(context) + (keyString, value) + }) + ->Belt.Map.String.fromArray + ->T.IEvRecord + (value, context) + } | T.EAssign(left, right) => { - let result = right->evaluate(context) - let _ = context.bindings->Bindings.set(left, result) - T.IEvVoid + let (result, _) = right->evaluate(context) + ( + T.IEvVoid, + { + ...context, + bindings: context.bindings->Bindings.set(left, result), + } + ) } | T.ESymbol(name) => switch context.bindings->Bindings.get(name) { - | Some(v) => v + | Some(v) => (v, context) | None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise } - | T.EValue(value) => value + | T.EValue(value) => (value, context) | T.ETernary(predicate, trueCase, falseCase) => { - let predicateResult = predicate->evaluate(context) + let (predicateResult, _) = predicate->evaluate(context) switch predicateResult { | T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context) | _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise @@ -64,13 +83,16 @@ let rec evaluate: T.reducerFn = (expression, context) => { } | T.ELambda(parameters, body) => - Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda + (Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda, context) | T.ECall(fn, args) => { - let lambda = fn->evaluate(context) - let argValues = Js.Array2.map(args, arg => arg->evaluate(context)) + let (lambda, _) = fn->evaluate(context) + let argValues = Js.Array2.map(args, arg => { + let (argValue, _) = arg->evaluate(context) + argValue + }) switch lambda { - | T.IEvLambda(lambda) => Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate) + | T.IEvLambda(lambda) => (Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate), context) | _ => REExpectedType("Lambda", "")->Reducer_ErrorValue.ErrorException->raise } } @@ -80,18 +102,19 @@ let rec evaluate: T.reducerFn = (expression, context) => { 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 => + let parse = (peggyCode: string): result => peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) - let evaluate = (expression: Reducer_T.expression): result => { + let evaluate = (expression: T.expression): result => { let context = Reducer_Context.createDefaultContext() try { - expression->evaluate(context)->Ok + let (value, _) = expression->evaluate(context) + value->Ok } catch { | exn => Reducer_ErrorValue.fromException(exn)->Error } } - let evaluateString = (peggyCode: string): result => + let evaluateString = (peggyCode: string): result => parse(peggyCode)->Result.flatMap(evaluate) } 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 49ca0cde..f6cc4ffa 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 @@ -34,11 +34,14 @@ let makeLambda = ( } let localBindings = bindings->Reducer_Bindings.extend - parameters->Js.Array2.forEachi((parameter, index) => { - let _ = localBindings->Reducer_Bindings.set(parameter, arguments[index]) + let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex( + localBindings, + (currentBindings, parameter, index) => { + currentBindings->Reducer_Bindings.set(parameter, arguments[index]) }) - reducer(body, {bindings: localBindings, environment: environment}) + let (value, _) = reducer(body, {bindings: localBindingsWithParameters, environment: environment}) + value } { 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 91fb3b1c..1aa64246 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 @@ -1,36 +1,23 @@ /* - An expression is a Lisp AST. An expression is either a primitive value or a list of expressions. - In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is - apply e1, e2 -> apply e3 -> ... -> apply eN - This is Lisp semantics. It holds true in both eager and lazy evaluations. - A Lisp AST contains only expressions/primitive values to apply to their left. - The act of defining the semantics of a functional language is to write it in terms of Lisp AST. + An expression is an intermediate representation of a Squiggle code. + Expressions are evaluated by `Reducer_Expression.evaluate` function. */ -module Extra = Reducer_Extra module InternalExpressionValue = ReducerInterface_InternalExpressionValue -type internalExpressionValue = Reducer_T.value -type environment = Reducer_T.environment - -type expression = Reducer_T.expression - -type t = expression - -type context = Reducer_T.context - -type reducerFn = Reducer_T.reducerFn +type t = Reducer_T.expression let commaJoin = values => values->Reducer_Extra_Array.intersperse(", ")->Js.String.concatMany("") +let semicolonJoin = values => values->Reducer_Extra_Array.intersperse("; ")->Js.String.concatMany("") /* Converts the expression to String */ -let rec toString = (expression: expression) => +let rec toString = (expression: t) => switch expression { - | EBlock(statements) => `{${Js.Array2.map(statements, aValue => toString(aValue))->commaJoin}}` - | EProgram(statements) => `<${Js.Array2.map(statements, aValue => toString(aValue))->commaJoin}>` + | EBlock(statements) => `{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}` + | EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin | EArray(aList) => `[${Js.Array2.map(aList, aValue => toString(aValue))->commaJoin}]` - | ERecord(map) => `{${map->Belt.Array.map(((key, value)) => `${key->toString}: ${value->toString}`)->Js.Array2.toString}}` + | ERecord(map) => `{${map->Belt.Array.map(((key, value)) => `${key->toString}: ${value->toString}`)->commaJoin}}` | ESymbol(name) => name | ETernary(predicate, trueCase, falseCase) => `${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})` @@ -52,30 +39,19 @@ let toStringResultOkless = codeResult => | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` } -let inspect = (expr: expression): expression => { +let inspect = (expr: t): t => { Js.log(toString(expr)) expr } -let inspectResult = (r: result): result< - expression, +let inspectResult = (r: result): result< + t, Reducer_ErrorValue.errorValue, > => { Js.log(toStringResult(r)) r } -type ffiFn = ( - array, - environment, -) => result - -type optionFfiFn = (array, environment) => option -type optionFfiFnReturningResult = ( - array, - environment, -) => option> - let resultToValue = (rExpression: result): t => switch rExpression { | Ok(expression) => expression diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res index 581f765b..7a95f58d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res @@ -1,21 +1,21 @@ type t = Reducer_T.namespace -let make = (): t => Belt.MutableMap.String.make() +let make = (): t => Belt.Map.String.empty let get = (namespace: t, id: string): option => - namespace->Belt.MutableMap.String.get(id) + namespace->Belt.Map.String.get(id) let set = (namespace: t, id: string, value): t => { - namespace->Belt.MutableMap.String.set(id, value) - namespace + namespace->Belt.Map.String.set(id, value) } let mergeFrom = (from: t, to: t): t => { - to->Belt.MutableMap.String.reduce(from, (namespace, key, value) => { + to->Belt.Map.String.reduce(from, (namespace, key, value) => { if key != "__result__" { - namespace->Belt.MutableMap.String.set(key, value) + namespace->set(key, value) + } else { + namespace } - namespace }) } @@ -24,15 +24,15 @@ let mergeMany = (namespaces: array): t => let toString = (namespace: t) => namespace - ->Belt.MutableMap.String.toArray + ->Belt.Map.String.toArray ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${eachValue->ReducerInterface_InternalExpressionValue.toString}`) ->Js.Array2.toString let fromArray = (a): t => - Belt.MutableMap.String.fromArray(a) + Belt.Map.String.fromArray(a) let toMap = (namespace: t): Reducer_T.map => - namespace->Belt.MutableMap.String.toArray->Belt.Map.String.fromArray + namespace let toRecord = (namespace: t): Reducer_T.value => namespace->toMap->IEvRecord diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res index 08f0b74a..e7fe07f3 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res @@ -38,7 +38,7 @@ and expression = | ELambda(array, expression) | EValue(value) -and namespace = Belt.MutableMap.String.t +and namespace = Belt.Map.String.t and bindings = { namespace: namespace, parent: option, @@ -49,4 +49,4 @@ and context = { environment: environment, } -and reducerFn = (expression, context) => value +and reducerFn = (expression, context) => (value, context) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res index 6b757b9a..c3ebf5e3 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res @@ -6,7 +6,7 @@ let internalStdLib: Reducer_T.namespace = { ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Math.make()) ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Versions.make()) - let _ = res->Reducer_Namespace.set( + let res = res->Reducer_Namespace.set( "$_atIndex_$", Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => { switch inputs { @@ -29,9 +29,10 @@ let internalStdLib: Reducer_T.namespace = { })->Reducer_T.IEvLambda, ) - FunctionRegistry_Library.nonRegistryLambdas->Js.Array2.forEach( - ((name, lambda)) => { - let _ = res->Reducer_Namespace.set(name, lambda->Reducer_T.IEvLambda) + let res = FunctionRegistry_Library.nonRegistryLambdas->Belt.Array.reduce( + res, + (cur, (name, lambda)) => { + cur->Reducer_Namespace.set(name, lambda->Reducer_T.IEvLambda) } ) @@ -59,10 +60,10 @@ let internalStdLib: Reducer_T.namespace = { // [ ] | (_, [IEvNumber(_), IEvNumber(_)]) // [ ] | (_, [IEvString(_), IEvString(_)]) => callMathJs(call) - FunctionRegistry_Library.registry.fnNameDict + let res = FunctionRegistry_Library.registry.fnNameDict ->Js.Dict.keys - ->Js.Array2.forEach(name => { - let _ = res->Reducer_Namespace.set( + ->Belt.Array.reduce(res, (cur, name) => { + cur->Reducer_Namespace.set( name, Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => { switch FunctionRegistry_Library.call(name, arguments, environment, reducer) { diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res index ea888d71..cfbd6ec9 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res @@ -194,17 +194,17 @@ let tryRunWithResult = ( sourceId: string, rPrevResult: ProjectItem.T.resultArgumentType, ): ProjectItem.T.resultArgumentType => { - switch getResultOption(project, sourceId) { + switch project->getResultOption(sourceId) { | Some(result) => result // already ran | None => switch rPrevResult { | Error(error) => { - setResult(project, sourceId, Error(error)) + project->setResult(sourceId, Error(error)) Error(error) } | Ok(_prevResult) => { - doLinkAndRun(project, sourceId) - getResultOption(project, sourceId)->Belt.Option.getWithDefault(rPrevResult) + project->doLinkAndRun(sourceId) + project->getResultOption(sourceId)->Belt.Option.getWithDefault(rPrevResult) } } } @@ -214,7 +214,7 @@ let runAll = (project: t): unit => { let runOrder = Topology.getRunOrder(project) let initialState = Ok(Reducer_T.IEvVoid) let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => - tryRunWithResult(project, currId, currState) + project->tryRunWithResult(currId, currState) ) } @@ -222,17 +222,17 @@ let run = (project: t, sourceId: string): unit => { let runOrder = Topology.getRunOrderFor(project, sourceId) let initialState = Ok(Reducer_T.IEvVoid) let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => - tryRunWithResult(project, currId, currState) + project->tryRunWithResult(currId, currState) ) } let evaluate = (sourceCode: string) => { let project = createProject() - setSource(project, "main", sourceCode) - runAll(project) + project->setSource("main", sourceCode) + project->runAll ( - getResultOption(project, "main")->Belt.Option.getWithDefault(Reducer_T.IEvVoid->Ok), + project->getResultOption("main")->Belt.Option.getWithDefault(Reducer_T.IEvVoid->Ok), project->getBindings("main")->Reducer_Namespace.toMap, ) } diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res index 271b3ac9..e51f7d4b 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res @@ -174,8 +174,8 @@ let doRun = (this: t, context: Reducer_T.context): t => switch this->getExpression { | Some(expressionResult) => switch expressionResult { | Ok(expression) => try { - let result = Reducer_Expression.evaluate(expression, context) - this->setResult(result->Ok)->setContinuation(context.bindings->Reducer_Bindings.locals) + let (result, contextAfterEvaluation) = Reducer_Expression.evaluate(expression, context) + this->setResult(result->Ok)->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals) } catch { | Reducer_ErrorValue.ErrorException(e) => this->failRun(e) | _ => this->failRun(RETodo("unhandled rescript exception")) diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res index 8d53e7fe..0eac623a 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res @@ -1,11 +1,10 @@ module ProjectItem = ReducerProject_ProjectItem -module ExpressionT = Reducer_Expression_T @genType.opaque type project = { items: Belt.MutableMap.String.t, mutable stdLib: Reducer_Namespace.t, - mutable environment: ExpressionT.environment, + mutable environment: Reducer_T.environment, mutable previousRunOrder: array, } type t = project