immutable bindings; test fixes
This commit is contained in:
parent
89397d3584
commit
065a7aeec0
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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)}"
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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",
|
||||
(),
|
||||
)
|
||||
|
|
|
@ -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", ())
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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)]",
|
||||
(),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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')})",
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -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)))")
|
||||
|
|
|
@ -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"),
|
||||
]
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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}")
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -12,4 +12,8 @@ export class SqRecord {
|
|||
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
|
||||
);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return RSRecord.toString(this._value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user