immutable bindings; test fixes

This commit is contained in:
Vyacheslav Matyukhin 2022-09-18 02:19:08 +04:00
parent 89397d3584
commit 065a7aeec0
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
32 changed files with 637 additions and 596 deletions

View File

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

View File

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

View File

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

View File

@ -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<c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b :c)) :d))}",
"{(:or (:and :a (:smaller :b :c)) :d)}",
)
testParse(
"a && b<c[i] || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d))}",
"{(:or (:and :a (:smaller :b (:$_atIndex_$ :c :i))) :d)}",
)
testParse(
"a && b<c.i || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d))}",
"{(:or (:and :a (:smaller :b (:$_atIndex_$ :c 'i'))) :d)}",
)
testParse(
"a && b<c(i) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::c :i))) :d))}",
"{(:or (:and :a (:smaller :b (:c :i))) :d)}",
)
testParse(
"a && b<1+2 || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 2))) :d))}",
"{(:or (:and :a (:smaller :b (:add 1 2))) :d)}",
)
testParse(
"a && b<1+2*3 || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d))}",
"{(:or (:and :a (:smaller :b (:add 1 (:multiply 2 3)))) :d)}",
)
testParse(
"a && b<1+2*-3+4 || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d))}",
"{(:or (:and :a (:smaller :b (:add (:add 1 (:multiply 2 (:unaryMinus 3))) 4))) :d)}",
)
testParse(
"a && b<1+2*3 || d ? true : false",
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false))}",
"{(::$$_ternary_$$ (:or (:and :a (:smaller :b (:add 1 (:multiply 2 3)))) :d) true false)}",
)
})
describe("pipe", () => {
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)}"
)
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,4 +12,8 @@ export class SqRecord {
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
);
}
toString() {
return RSRecord.toString(this._value);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Reducer_T.value>, 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<Reducer_T.value>, 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,

View File

@ -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) =>
| T.ERecord(pairs) => {
let value =
pairs
->Js.Array2.map(((eKey, eValue)) => {
let key = eKey->evaluate(context)
->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)
let (value, _) = eValue->evaluate(context)
(keyString, value)
})
->Belt.Map.String.fromArray
->IEvRecord
->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<Reducer_T.expression, errorValue> =>
let parse = (peggyCode: string): result<T.expression, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
let evaluate = (expression: Reducer_T.expression): result<Reducer_T.value, errorValue> => {
let evaluate = (expression: T.expression): result<T.value, errorValue> => {
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<Reducer_T.value, errorValue> =>
let evaluateString = (peggyCode: string): result<T.value, errorValue> =>
parse(peggyCode)->Result.flatMap(evaluate)
}

View File

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

View File

@ -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<expression, Reducer_ErrorValue.errorValue>): result<
expression,
let inspectResult = (r: result<t, Reducer_ErrorValue.errorValue>): result<
t,
Reducer_ErrorValue.errorValue,
> => {
Js.log(toStringResult(r))
r
}
type ffiFn = (
array<internalExpressionValue>,
environment,
) => result<internalExpressionValue, Reducer_ErrorValue.errorValue>
type optionFfiFn = (array<internalExpressionValue>, environment) => option<internalExpressionValue>
type optionFfiFnReturningResult = (
array<internalExpressionValue>,
environment,
) => option<result<internalExpressionValue, Reducer_ErrorValue.errorValue>>
let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t =>
switch rExpression {
| Ok(expression) => expression

View File

@ -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<Reducer_T.value> =>
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
}
})
}
@ -24,15 +24,15 @@ let mergeMany = (namespaces: array<t>): 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

View File

@ -38,7 +38,7 @@ and expression =
| ELambda(array<string>, expression)
| EValue(value)
and namespace = Belt.MutableMap.String.t<value>
and namespace = Belt.Map.String.t<value>
and bindings = {
namespace: namespace,
parent: option<bindings>,
@ -49,4 +49,4 @@ and context = {
environment: environment,
}
and reducerFn = (expression, context) => value
and reducerFn = (expression, context) => (value, context)

View File

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

View File

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

View File

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

View File

@ -1,11 +1,10 @@
module ProjectItem = ReducerProject_ProjectItem
module ExpressionT = Reducer_Expression_T
@genType.opaque
type project = {
items: Belt.MutableMap.String.t<ProjectItem.t>,
mutable stdLib: Reducer_Namespace.t,
mutable environment: ExpressionT.environment,
mutable environment: Reducer_T.environment,
mutable previousRunOrder: array<string>,
}
type t = project