immutable bindings; test fixes

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

View File

@ -1,27 +1,46 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
module Namespace = Reducer_Namespace
open Jest open Jest
open Expect open Expect
open Expect.Operators open Expect.Operators
describe("Name Space", () => { describe("Bindings", () => {
let value = Reducer_T.IEvNumber(1967.0) 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", () => { test("get", () => {
expect(Bindings.get(nameSpace, "value")) == Some(value) expect(bindings->Bindings.get("value")) == Some(value)
}) })
test("chain and get", () => { test("get nonexisting value", () => {
let mainNameSpace = Bindings.makeEmptyBindings()->Bindings.chainTo([nameSpace]) expect(bindings->Bindings.get("nosuchvalue")) == None
expect(Bindings.get(mainNameSpace, "value")) == Some(value)
}) })
test("chain and set", () => { test("get on extended", () => {
let mainNameSpace0 = Bindings.makeEmptyBindings()->Bindings.chainTo([nameSpace]) expect(bindings->Bindings.extend->Bindings.get("value")) == Some(value)
let mainNameSpace = })
mainNameSpace0->Bindings.set("value", Reducer_T.IEvNumber(1968.0))
expect(Bindings.get(mainNameSpace, "value")) == Some(Reducer_T.IEvNumber(1968.0)) test("locals", () => {
expect(bindings->Bindings.locals->Namespace.get("value")) == Some(value)
})
test("locals on extendeed", () => {
expect(bindings->Bindings.extend->Bindings.locals->Namespace.get("value")) == None
})
describe("extend", () => {
let value2 = Reducer_T.IEvNumber(5.)
let extendedBindings = bindings
->Bindings.extend
->Bindings.set("value", value2)
test("get on extended", () => {
expect(extendedBindings->Bindings.get("value")) == Some(value2)
})
test("get on original", () => {
expect(bindings->Bindings.get("value")) == Some(value)
})
}) })
}) })

View File

@ -3,15 +3,7 @@ module ErrorValue = Reducer_ErrorValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue module InternalExpressionValue = ReducerInterface.InternalExpressionValue
let removeDefaultsInternal = (iev: InternalExpressionValue.t) => { let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
Not_found->raise iev // TODO - cleanup, noop
// switch iev {
// | Reducer_T.IEvBindings(nameSpace) =>
// Reducer_Bindings.removeOther(
// nameSpace,
// ReducerInterface.StdLib.internalStdLib,
// )->Reducer_T.IEvBindings
// | value => value
// }
} }
let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal) let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal)

View File

@ -0,0 +1,57 @@
@@warning("-44")
module Namespace = Reducer_Namespace
open Jest
open Expect
open Expect.Operators
let makeValue = (v: float) => v->Reducer_T.IEvNumber
describe("Namespace", () => {
let value = makeValue(5.)
let v2 = makeValue(2.)
let ns = Namespace.make()->Namespace.set("value", value)
test("get", () => {
expect(ns->Namespace.get("value")) == Some(value)
})
test("get nonexisting value", () => {
expect(ns->Namespace.get("nosuchvalue")) == None
})
test("set", () => {
let ns2 = ns->Namespace.set("v2", v2)
expect(ns2->Namespace.get("v2")) == Some(v2)
})
test("immutable", () => {
let _ = ns->Namespace.set("v2", Reducer_T.IEvNumber(2.))
expect(ns->Namespace.get("v2")) == None
})
describe("merge many", () => {
let x1 = makeValue(10.)
let x2 = makeValue(20.)
let x3 = makeValue(30.)
let x4 = makeValue(40.)
let ns1 = Namespace.make()
->Namespace.set("x1", x1)
->Namespace.set("x2", x2)
let ns2 = Namespace.make()
->Namespace.set("x3", x3)
->Namespace.set("x4", x4)
let nsMerged = Namespace.mergeMany([ns, ns1, ns2])
test("merge many 1", () => {
expect(nsMerged->Namespace.get("x1")) == Some(x1)
})
test("merge many 2", () => {
expect(nsMerged->Namespace.get("x4")) == Some(x4)
})
test("merge many 3", () => {
expect(nsMerged->Namespace.get("value")) == Some(value)
})
})
})

View File

@ -3,346 +3,345 @@ open Reducer_Peggy_TestHelpers
describe("Peggy parse", () => { describe("Peggy parse", () => {
describe("float", () => { describe("float", () => {
testParse("1.", "{(::$_endOfOuterBlock_$ () 1)}") testParse("1.", "{1}")
testParse("1.1", "{(::$_endOfOuterBlock_$ () 1.1)}") testParse("1.1", "{1.1}")
testParse(".1", "{(::$_endOfOuterBlock_$ () 0.1)}") testParse(".1", "{0.1}")
testParse("0.1", "{(::$_endOfOuterBlock_$ () 0.1)}") testParse("0.1", "{0.1}")
testParse("1e1", "{(::$_endOfOuterBlock_$ () 10)}") testParse("1e1", "{10}")
testParse("1e-1", "{(::$_endOfOuterBlock_$ () 0.1)}") testParse("1e-1", "{0.1}")
testParse(".1e1", "{(::$_endOfOuterBlock_$ () 1)}") testParse(".1e1", "{1}")
testParse("0.1e1", "{(::$_endOfOuterBlock_$ () 1)}") testParse("0.1e1", "{1}")
}) })
describe("literals operators parenthesis", () => { describe("literals operators parenthesis", () => {
// Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement testParse("1", "{1}")
testParse("1", "{(::$_endOfOuterBlock_$ () 1)}") testParse("'hello'", "{'hello'}")
testParse("'hello'", "{(::$_endOfOuterBlock_$ () 'hello')}") testParse("true", "{true}")
testParse("true", "{(::$_endOfOuterBlock_$ () true)}") testParse("1+2", "{(:add 1 2)}")
testParse("1+2", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") testParse("add(1,2)", "{(:add 1 2)}")
testParse("add(1,2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") testParse("(1)", "{1}")
testParse("(1)", "{(::$_endOfOuterBlock_$ () 1)}") testParse("(1+2)", "{(:add 1 2)}")
testParse("(1+2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
}) })
describe("unary", () => { describe("unary", () => {
testParse("-1", "{(::$_endOfOuterBlock_$ () (::unaryMinus 1))}") testParse("-1", "{(:unaryMinus 1)}")
testParse("!true", "{(::$_endOfOuterBlock_$ () (::not true))}") testParse("!true", "{(:not true)}")
testParse("1 + -1", "{(::$_endOfOuterBlock_$ () (::add 1 (::unaryMinus 1)))}") testParse("1 + -1", "{(:add 1 (:unaryMinus 1))}")
testParse("-a[0]", "{(::$_endOfOuterBlock_$ () (::unaryMinus (::$_atIndex_$ :a 0)))}") testParse("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}")
testParse("!a[0]", "{(::$_endOfOuterBlock_$ () (::not (::$_atIndex_$ :a 0)))}") testParse("!a[0]", "{(:not (:$_atIndex_$ :a 0))}")
}) })
describe("multiplicative", () => { describe("multiplicative", () => {
testParse("1 * 2", "{(::$_endOfOuterBlock_$ () (::multiply 1 2))}") testParse("1 * 2", "{(:multiply 1 2)}")
testParse("1 / 2", "{(::$_endOfOuterBlock_$ () (::divide 1 2))}") testParse("1 / 2", "{(:divide 1 2)}")
testParse("1 * 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::multiply 1 2) 3))}") testParse("1 * 2 * 3", "{(:multiply (:multiply 1 2) 3)}")
testParse("1 * 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::multiply 1 2) 3))}") testParse("1 * 2 / 3", "{(:divide (:multiply 1 2) 3)}")
testParse("1 / 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::divide 1 2) 3))}") testParse("1 / 2 * 3", "{(:multiply (:divide 1 2) 3)}")
testParse("1 / 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::divide 1 2) 3))}") testParse("1 / 2 / 3", "{(:divide (:divide 1 2) 3)}")
testParse( testParse(
"1 * 2 + 3 * 4", "1 * 2 + 3 * 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}", "{(:add (:multiply 1 2) (:multiply 3 4))}",
) )
testParse( testParse(
"1 * 2 - 3 * 4", "1 * 2 - 3 * 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}", "{(:subtract (:multiply 1 2) (:multiply 3 4))}",
) )
testParse( testParse(
"1 * 2 .+ 3 * 4", "1 * 2 .+ 3 * 4",
"{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}", "{(:dotAdd (:multiply 1 2) (:multiply 3 4))}",
) )
testParse( testParse(
"1 * 2 .- 3 * 4", "1 * 2 .- 3 * 4",
"{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}", "{(:dotSubtract (:multiply 1 2) (:multiply 3 4))}",
) )
testParse( testParse(
"1 * 2 + 3 .* 4", "1 * 2 + 3 .* 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}", "{(:add (:multiply 1 2) (:dotMultiply 3 4))}",
) )
testParse( testParse(
"1 * 2 + 3 / 4", "1 * 2 + 3 / 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}", "{(:add (:multiply 1 2) (:divide 3 4))}",
) )
testParse( testParse(
"1 * 2 + 3 ./ 4", "1 * 2 + 3 ./ 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}", "{(:add (:multiply 1 2) (:dotDivide 3 4))}",
) )
testParse( testParse(
"1 * 2 - 3 .* 4", "1 * 2 - 3 .* 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}", "{(:subtract (:multiply 1 2) (:dotMultiply 3 4))}",
) )
testParse( testParse(
"1 * 2 - 3 / 4", "1 * 2 - 3 / 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}", "{(:subtract (:multiply 1 2) (:divide 3 4))}",
) )
testParse( testParse(
"1 * 2 - 3 ./ 4", "1 * 2 - 3 ./ 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}", "{(:subtract (:multiply 1 2) (:dotDivide 3 4))}",
) )
testParse( testParse(
"1 * 2 - 3 * 4^5", "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( testParse(
"1 * 2 - 3 * 4^5^6", "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( testParse(
"1 * -a[-2]", "1 * -a[-2]",
"{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}", "{(:multiply 1 (:unaryMinus (:$_atIndex_$ :a (:unaryMinus 2))))}",
) )
}) })
describe("multi-line", () => { describe("multi-line", () => {
testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}") testParse("x=1; 2", "{:x = {1}; 2}")
testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}") testParse("x=1; y=2", "{:x = {1}; :y = {2}}")
}) })
describe("variables", () => { describe("variables", () => {
testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}") testParse("x = 1", "{:x = {1}}")
testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") testParse("x", "{:x}")
testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}") testParse("x = 1; x", "{:x = {1}; :x}")
}) })
describe("functions", () => { describe("functions", () => {
testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments
testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}") testParse("identity(x)", "{(:identity :x)}")
}) })
describe("arrays", () => { describe("arrays", () => {
testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$))}") testParse("[]", "{[]}")
testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 0 1 2))}") testParse("[0, 1, 2]", "{[0; 1; 2]}")
testParse( testParse(
"['hello', 'world']", "['hello', 'world']",
"{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 'hello' 'world'))}", "{['hello'; 'world']}",
) )
testParse( testParse(
"([0,1,2])[1]", "([0,1,2])[1]",
"{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ 0 1 2) 1))}", "{(:$_atIndex_$ [0; 1; 2] 1)}",
) )
}) })
describe("records", () => { describe("records", () => {
testParse( testParse(
"{a: 1, b: 2}", "{a: 1, b: 2}",
"{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}", "{{'a': 1, 'b': 2}}",
) )
testParse( testParse(
"{1+0: 1, 2+0: 2}", "{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 ) // key can be any expression
testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}") testParse("record.property", "{(:$_atIndex_$ :record 'property')}")
}) })
describe("post operators", () => { describe("post operators", () => {
//function call, array and record access are post operators with higher priority than unary 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)", "{(:equal :a (:not (:b 1)))}")
testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}") testParse("a==!b[1]", "{(:equal :a (:not (:$_atIndex_$ :b 1)))}")
testParse( testParse(
"a==!b.one", "a==!b.one",
"{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}", "{(:equal :a (:not (:$_atIndex_$ :b 'one')))}",
) )
}) })
describe("comments", () => { describe("comments", () => {
testParse("1 # This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}") testParse("1 # This is a line comment", "{1}")
testParse("1 // This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}") testParse("1 // This is a line comment", "{1}")
testParse("1 /* This is a multi line comment */", "{(::$_endOfOuterBlock_$ () 1)}") testParse("1 /* This is a multi line comment */", "{1}")
testParse("/* This is a multi line comment */ 1", "{(::$_endOfOuterBlock_$ () 1)}") testParse("/* This is a multi line comment */ 1", "{1}")
testParse( testParse(
` `
/* This is /* This is
a multi line a multi line
comment */ comment */
1`, 1`,
"{(::$_endOfOuterBlock_$ () 1)}", "{1}",
) )
}) })
describe("ternary operator", () => { describe("ternary operator", () => {
testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}") testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}")
testParse( testParse(
"false ? 2 : false ? 4 : 5", "false ? 2 : false ? 4 : 5",
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}", "{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}",
) // nested ternary ) // nested ternary
}) })
describe("if then else", () => { describe("if then else", () => {
testParse( testParse(
"if true then 2 else 3", "if true then 2 else 3",
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}", "{(::$$_ternary_$$ true {2} {3})}",
) )
testParse( testParse(
"if false then {2} else {3}", "if false then {2} else {3}",
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} {3}))}", "{(::$$_ternary_$$ false {2} {3})}",
) )
testParse( testParse(
"if false then {2} else if false then {4} else {5}", "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 ) //nested if
}) })
describe("logical", () => { describe("logical", () => {
testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}") testParse("true || false", "{(:or true false)}")
testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}") testParse("true && false", "{(:and true false)}")
testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison testParse("a * b + c", "{(:add (:multiply :a :b) :c)}") // for comparison
testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}") testParse("a && b || c", "{(:or (:and :a :b) :c)}")
testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}") testParse("a && b || c && d", "{(:or (:and :a :b) (:and :c :d))}")
testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}") testParse("a && !b || c", "{(:or (:and :a (:not :b)) :c)}")
testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}") testParse("a && b==c || d", "{(:or (:and :a (:equal :b :c)) :d)}")
testParse( testParse(
"a && b!=c || d", "a && b!=c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}", "{(:or (:and :a (:unequal :b :c)) :d)}",
) )
testParse( testParse(
"a && !(b==c) || d", "a && !(b==c) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}", "{(:or (:and :a (:not (:equal :b :c))) :d)}",
) )
testParse( testParse(
"a && b>=c || d", "a && b>=c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}", "{(:or (:and :a (:largerEq :b :c)) :d)}",
) )
testParse( testParse(
"a && !(b>=c) || d", "a && !(b>=c) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}", "{(:or (:and :a (:not (:largerEq :b :c))) :d)}",
) )
testParse( testParse(
"a && b<=c || d", "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( testParse(
"a && b<c || d", "a && b<c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b :c)) :d))}", "{(:or (:and :a (:smaller :b :c)) :d)}",
) )
testParse( testParse(
"a && b<c[i] || d", "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( testParse(
"a && b<c.i || d", "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( testParse(
"a && b<c(i) || d", "a && b<c(i) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::c :i))) :d))}", "{(:or (:and :a (:smaller :b (:c :i))) :d)}",
) )
testParse( testParse(
"a && b<1+2 || d", "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( testParse(
"a && b<1+2*3 || d", "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( testParse(
"a && b<1+2*-3+4 || d", "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( testParse(
"a && b<1+2*3 || d ? true : false", "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", () => { describe("pipe", () => {
testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") testParse("1 -> add(2)", "{(:add 1 2)}")
testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}") testParse("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}")
testParse( testParse(
"-a[1] -> add(2)", "-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("-f(1) -> add(2)", "{(:add (:unaryMinus (:f 1)) 2)}")
testParse("1 + 2 -> add(3)", "{(::$_endOfOuterBlock_$ () (::add 1 (::add 2 3)))}") testParse("1 + 2 -> add(3)", "{(:add 1 (:add 2 3))}")
testParse("1 -> add(2) * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::add 1 2) 3))}") testParse("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}")
testParse("1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract 1 2))}") testParse("1 -> subtract(2)", "{(:subtract 1 2)}")
testParse("-1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract (::unaryMinus 1) 2))}") testParse("-1 -> subtract(2)", "{(:subtract (:unaryMinus 1) 2)}")
testParse( testParse(
"1 -> subtract(2) * 3", "1 -> subtract(2) * 3",
"{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}", "{(:multiply (:subtract 1 2) 3)}",
) )
}) })
describe("elixir pipe", () => { describe("elixir pipe", () => {
//handled together with -> so there is no need for seperate tests //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", () => { describe("to", () => {
testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}") testParse("1 to 2", "{(:credibleIntervalToDistribution 1 2)}")
testParse( testParse(
"-1 to -2", "-1 to -2",
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}", "{(:credibleIntervalToDistribution (:unaryMinus 1) (:unaryMinus 2))}",
) // lower than unary ) // lower than unary
testParse( testParse(
"a[1] to a[2]", "a[1] to a[2]",
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2)))}", "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 1) (:$_atIndex_$ :a 2))}",
) // lower than post ) // lower than post
testParse( testParse(
"a.p1 to a.p2", "a.p1 to a.p2",
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}", "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 'p1') (:$_atIndex_$ :a 'p2'))}",
) // lower than post ) // lower than post
testParse( testParse(
"1 to 2 + 3", "1 to 2 + 3",
"{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}", "{(:add (:credibleIntervalToDistribution 1 2) 3)}",
) // higher than binary operators ) // higher than binary operators
testParse( testParse(
"1->add(2) to 3->add(4) -> add(4)", "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 ) // lower than chain
}) })
describe("inner block", () => { describe("inner block", () => {
// inner blocks are 0 argument lambdas. They can be used whenever a value is required. // inner blocks are 0 argument lambdas. They can be used whenever a value is required.
// Like lambdas they have a local scope. // 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", () => { describe("lambda", () => {
testParse("{|x| x}", "{(::$_endOfOuterBlock_$ () {|:x| {:x}})}") testParse("{|x| x}", "{{|:x| :x}}")
testParse("f={|x| x}", "{:f = {{|:x| {:x}}}; (::$_endOfOuterBlock_$ () ())}") testParse("f={|x| x}", "{:f = {{|:x| :x}}}")
testParse("f(x)=x", "{:f = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions are lambda assignments testParse("f(x)=x", "{:f = {|:x| {:x}}}") // Function definitions are lambda assignments
testParse( testParse(
"f(x)=x ? 1 : 0", "f(x)=x ? 1 : 0",
"{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}; (::$_endOfOuterBlock_$ () ())}", "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}",
) // Function definitions are lambda assignments ) // Function definitions are lambda assignments
}) })
describe("Using lambda as value", () => { describe("Using lambda as value", () => {
testParse( testParse(
"myadd(x,y)=x+y; z=myadd; z", "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( testParse(
"myadd(x,y)=x+y; z=[myadd]; z", "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( testParse(
"myaddd(x,y)=x+y; z={x: myaddd}; z", "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( testParse(
"map(arr, {|x| x+1})", "map(arr, {|x| x+1})",
"{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}", "{(:map :arr {|:x| (:add :x 1)})}",
) )
testParse( testParse(
"map([1,2,3], {|x| x+1})", "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( testParse(
"[1,2,3]->map({|x| x+1})", "[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", () => { describe("unit", () => {
testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}") testParse("1m", "{(:fromUnit_m 1)}")
testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}") testParse("1M", "{(:fromUnit_M 1)}")
testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}") testParse("1m+2cm", "{(:add (:fromUnit_m 1) (:fromUnit_cm 2))}")
}) })
describe("Module", () => { describe("Module", () => {
testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") testParse("x", "{:x}")
testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}") testParse("Math.pi", "{:Math.pi}")
}) })
}) })
@ -351,19 +350,19 @@ describe("parsing new line", () => {
` `
a + a +
b`, b`,
"{(::$_endOfOuterBlock_$ () (::add :a :b))}", "{(:add :a :b)}",
) )
testParse( testParse(
` `
x= x=
1`, 1`,
"{:x = {1}; (::$_endOfOuterBlock_$ () ())}", "{:x = {1}}",
) )
testParse( testParse(
` `
x=1 x=1
y=2`, y=2`,
"{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}", "{:x = {1}; :y = {2}}",
) )
testParse( testParse(
` `
@ -371,7 +370,7 @@ describe("parsing new line", () => {
y=2; y=2;
y } y }
x`, x`,
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", "{:x = {:y = {2}; :y}; :x}",
) )
testParse( testParse(
` `
@ -379,7 +378,7 @@ describe("parsing new line", () => {
y=2 y=2
y } y }
x`, x`,
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", "{:x = {:y = {2}; :y}; :x}",
) )
testParse( testParse(
` `
@ -388,7 +387,7 @@ describe("parsing new line", () => {
y y
} }
x`, x`,
"{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", "{:x = {:y = {2}; :y}; :x}",
) )
testParse( testParse(
` `
@ -396,7 +395,7 @@ describe("parsing new line", () => {
y=2 y=2
z=3 z=3
`, `,
"{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}", "{:x = {1}; :y = {2}; :z = {3}}",
) )
testParse( testParse(
` `
@ -407,7 +406,7 @@ describe("parsing new line", () => {
x+y+z 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( testParse(
` `
@ -420,7 +419,7 @@ describe("parsing new line", () => {
g=f+4 g=f+4
g 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( testParse(
` `
@ -442,7 +441,7 @@ describe("parsing new line", () => {
p -> p ->
q 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( testParse(
` `
@ -451,7 +450,7 @@ describe("parsing new line", () => {
c |> c |>
d d
`, `,
"{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}", "{(:d (:c (:b :a)))}",
) )
testParse( testParse(
` `
@ -461,6 +460,6 @@ describe("parsing new line", () => {
d + d +
e e
`, `,
"{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}", "{(:add (:d (:c (:b :a))) :e)}"
) )
}) })

View File

@ -5,18 +5,18 @@ open Jest
open Reducer_Peggy_TestHelpers open Reducer_Peggy_TestHelpers
describe("Peggy Outer Block", () => { describe("Peggy Outer Block", () => {
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1", "1", ~v="1", ())
testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ()) testToExpression("x=1", "x = {1}", ~v="()", ())
testToExpression( testToExpression(
"x=1; y=2", "x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", "x = {1}; y = {2}",
~v="()", ~v="()",
(), (),
) )
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ())
testToExpression( testToExpression(
"x={a=1; a}; x", "x={a=1; a}; x",
"{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}", "x = {a = {1}; a}; x",
~v="1", ~v="1",
(), (),
) )

View File

@ -7,69 +7,69 @@ open Reducer_Peggy_TestHelpers
describe("Peggy to Expression", () => { describe("Peggy to Expression", () => {
describe("literals operators parenthesis", () => { describe("literals operators parenthesis", () => {
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement // Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1", "1", ~v="1", ())
testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ()) testToExpression("'hello'", "'hello'", ~v="'hello'", ())
testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ()) testToExpression("true", "true", ~v="true", ())
testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("1+2", "(add)(1, 2)", ~v="3", ())
testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("add(1,2)", "(add)(1, 2)", ~v="3", ())
testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ()) testToExpression("(1)", "1", ())
testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ()) testToExpression("(1+2)", "(add)(1, 2)", ())
}) })
describe("unary", () => { describe("unary", () => {
testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ()) testToExpression("-1", "(unaryMinus)(1)", ~v="-1", ())
testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ()) testToExpression("!true", "(not)(true)", ~v="false", ())
testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ()) testToExpression("1 + -1", "(add)(1, (unaryMinus)(1))", ~v="0", ())
testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ()) testToExpression("-a[0]", "(unaryMinus)(($_atIndex_$)(a, 0))", ())
}) })
describe("multi-line", () => { describe("multi-line", () => {
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ())
testToExpression( testToExpression(
"x=1; y=2", "x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", "x = {1}; y = {2}",
(), (),
) )
}) })
describe("variables", () => { describe("variables", () => {
testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ()) testToExpression("x = 1", "x = {1}", ())
testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error testToExpression("x", "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", "x = {1}; x", ~v="1", ())
}) })
describe("functions", () => { describe("functions", () => {
testToExpression( testToExpression(
"identity(x) = x", "identity(x) = x",
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", "identity = {|x| {x}}",
(), (),
) // Function definitions become lambda assignments ) // 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( testToExpression(
"f(x) = x> 2 ? 0 : 1; f(3)", "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", ~v="0",
(), (),
) )
}) })
describe("arrays", () => { describe("arrays", () => {
testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) testToExpression("[]", "[]", ~v="[]", ())
testToExpression( testToExpression(
"[0, 1, 2]", "[0, 1, 2]",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 0 1 2))}", "[0, 1, 2]",
~v="[0,1,2]", ~v="[0,1,2]",
(), (),
) )
testToExpression( testToExpression(
"['hello', 'world']", "['hello', 'world']",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 'hello' 'world'))}", "['hello', 'world']",
~v="['hello','world']", ~v="['hello','world']",
(), (),
) )
testToExpression( testToExpression(
"([0,1,2])[1]", "([0,1,2])[1]",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ 0 1 2) 1))}", "($_atIndex_$)([0, 1, 2], 1)",
~v="1", ~v="1",
(), (),
) )
@ -78,40 +78,40 @@ describe("Peggy to Expression", () => {
describe("records", () => { describe("records", () => {
testToExpression( testToExpression(
"{a: 1, b: 2}", "{a: 1, b: 2}",
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}", "{'a': 1, 'b': 2}",
~v="{a: 1,b: 2}", ~v="{a: 1,b: 2}",
(), (),
) )
testToExpression( testToExpression(
"{1+0: 1, 2+0: 2}", "{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 ) // key can be any expression
testToExpression( testToExpression(
"record.property", "record.property",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", "($_atIndex_$)(record, 'property')",
(), (),
) )
testToExpression( testToExpression(
"record={property: 1}; record.property", "record={property: 1}; record.property",
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", "record = {{'property': 1}}; ($_atIndex_$)(record, 'property')",
~v="1", ~v="1",
(), (),
) )
}) })
describe("comments", () => { describe("comments", () => {
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", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1 // This is a line comment", "1", ~v="1", ())
testToExpression( testToExpression(
"1 /* This is a multi line comment */", "1 /* This is a multi line comment */",
"{(:$_endOfOuterBlock_$ () 1)}", "1",
~v="1", ~v="1",
(), (),
) )
testToExpression( testToExpression(
"/* This is a multi line comment */ 1", "/* This is a multi line comment */ 1",
"{(:$_endOfOuterBlock_$ () 1)}", "1",
~v="1", ~v="1",
(), (),
) )
@ -120,25 +120,25 @@ describe("Peggy to Expression", () => {
describe("ternary operator", () => { describe("ternary operator", () => {
testToExpression( testToExpression(
"true ? 1 : 0", "true ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}", "true ? (1) : (0)",
~v="1", ~v="1",
(), (),
) )
testToExpression( testToExpression(
"false ? 1 : 0", "false ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}", "false ? (1) : (0)",
~v="0", ~v="0",
(), (),
) )
testToExpression( testToExpression(
"true ? 1 : false ? 2 : 0", "true ? 1 : false ? 2 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}", "true ? (1) : (false ? (2) : (0))",
~v="1", ~v="1",
(), (),
) // nested ternary ) // nested ternary
testToExpression( testToExpression(
"false ? 1 : false ? 2 : 0", "false ? 1 : false ? 2 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}", "false ? (1) : (false ? (2) : (0))",
~v="0", ~v="0",
(), (),
) // nested ternary ) // nested ternary
@ -146,21 +146,21 @@ describe("Peggy to Expression", () => {
testToExpression( testToExpression(
// expression binding // expression binding
"f(a) = a > 5 ? 1 : 0; f(6)", "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", ~v="1",
(), (),
) )
testToExpression( testToExpression(
// when true binding // when true binding
"f(a) = a > 5 ? a : 0; f(6)", "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", ~v="6",
(), (),
) )
testToExpression( testToExpression(
// when false binding // when false binding
"f(a) = a < 5 ? 1 : a; f(6)", "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", ~v="6",
(), (),
) )
@ -170,39 +170,39 @@ describe("Peggy to Expression", () => {
describe("if then else", () => { describe("if then else", () => {
testToExpression( testToExpression(
"if true then 2 else 3", "if true then 2 else 3",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", "true ? ({2}) : ({3})",
(), (),
) )
testToExpression( testToExpression(
"if true then {2} else {3}", "if true then {2} else {3}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", "true ? ({2}) : ({3})",
(), (),
) )
testToExpression( testToExpression(
"if false then {2} else if false then {4} else {5}", "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 ) //nested if
}) })
describe("pipe", () => { describe("pipe", () => {
testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("1 -> add(2)", "(add)(1, 2)", ~v="3", ())
testToExpression( testToExpression(
"-1 -> add(2)", "-1 -> add(2)",
"{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}", "(add)((unaryMinus)(1), 2)",
~v="1", ~v="1",
(), (),
) // note that unary has higher priority naturally ) // note that unary has higher priority naturally
testToExpression( testToExpression(
"1 -> add(2) * 3", "1 -> add(2) * 3",
"{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}", "(multiply)((add)(1, 2), 3)",
~v="9", ~v="9",
(), (),
) )
}) })
describe("elixir pipe", () => { 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 // see testParse for priorities of to and credibleIntervalToDistribution
@ -212,7 +212,8 @@ describe("Peggy to Expression", () => {
// Like lambdas they have a local scope. // Like lambdas they have a local scope.
testToExpression( testToExpression(
"y=99; x={y=1; y}", "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", () => { describe("lambda", () => {
testToExpression( testToExpression(
"{|x| x}", "{|x| x}",
"{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}", "{|x| x}",
~v="lambda(x=>internal code)", ~v="lambda(x=>internal code)",
(), (),
) )
testToExpression( testToExpression(
"f={|x| x}", "f={|x| x}",
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}", "f = {{|x| x}}",
(), (),
) )
testToExpression( testToExpression(
"f(x)=x", "f(x)=x",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", "f = {|x| {x}}",
(), (),
) // Function definitions are lambda assignments ) // Function definitions are lambda assignments
testToExpression( testToExpression(
"f(x)=x ? 1 : 0", "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 // ->expect
// ->toBe("") // ->toBe("")
// }) // })
testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ()) testToExpression("Math.pi", "Math.pi", ~v="3.141592653589793", ())
}) })
}) })

View File

@ -2,11 +2,11 @@ open Jest
open Reducer_Peggy_TestHelpers open Reducer_Peggy_TestHelpers
describe("Construct Array", () => { describe("Construct Array", () => {
testToExpression("[1,2]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 1 2))}", ~v="[1,2]", ()) testToExpression("[1,2]", "[1, 2]", ~v="[1,2]", ())
testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) testToExpression("[]", "[]", ~v="[]", ())
testToExpression( testToExpression(
"f(x)=x; g(x)=x; [f, g]", "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)]", ~v="[lambda(x=>internal code),lambda(x=>internal code)]",
(), (),
) )

View File

@ -4,11 +4,11 @@ open Reducer_TestHelpers
describe("Parse function assignment", () => { describe("Parse function assignment", () => {
testParseToBe( testParseToBe(
"f(x)=x", "f(x)=x",
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})", "Ok(f = {|x| {x}})"
) )
testParseToBe( testParseToBe(
"f(x)=2*x", "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 //MathJs does not allow blocks in function definitions
}) })

View File

@ -4,7 +4,7 @@ open Reducer_TestHelpers
describe("Parse ternary operator", () => { describe("Parse ternary operator", () => {
testParseToBe( testParseToBe(
"true ? 'YES' : 'NO'", "true ? 'YES' : 'NO'",
"Ok({(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 'YES' 'NO'))})", "Ok({(:$$_ternary_$$ true 'YES' 'NO')})",
) )
}) })

View File

@ -121,38 +121,38 @@ describe("parse on distribution functions", () => {
describe("power", () => { describe("power", () => {
testParse( testParse(
"normal(5,2) ^ normal(5,1)", "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("3 ^ normal(5,1)", "Ok((pow)(3, (normal)(5, 1)))")
testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})") testParse("normal(5,2) ^ 3", "Ok((pow)((normal)(5, 2), 3))")
}) })
describe("subtraction", () => { describe("subtraction", () => {
testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})") testParse("10 - normal(5,1)", "Ok((subtract)(10, (normal)(5, 1)))")
testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})") testParse("normal(5,1) - 10", "Ok((subtract)((normal)(5, 1), 10))")
}) })
describe("pointwise arithmetic expressions", () => { 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((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse( testParse(
~skip=true, ~skip=true,
"normal(5,2) .- normal(5,1)", "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))})" // TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})"
) )
testParse( testParse(
"normal(5,2) .* normal(5,1)", "normal(5,2) .* normal(5,1)",
"Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})", "Ok((dotMultiply)((normal)(5, 2), (normal)(5, 1)))",
) )
testParse( testParse(
"normal(5,2) ./ normal(5,1)", "normal(5,2) ./ normal(5,1)",
"Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})", "Ok((dotDivide)((normal)(5, 2), (normal)(5, 1)))",
) )
testParse( testParse(
"normal(5,2) .^ normal(5,1)", "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", () => { 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", () => { describe("pointwise adding two normals", () => {
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((:dotAdd (:normal 5 2) (:normal 5 1)))")

View File

@ -1,121 +1,117 @@
// @@warning("-44") @@warning("-44")
// module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
// module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
// module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
// open Jest open Jest
// open Expect open Expect
// open Expect.Operators open Expect.Operators
// describe("Parse includes", () => { describe("Parse includes", () => {
// let project = Project.createProject() let project = Project.createProject()
// Project.setSource( Project.setSource(
// project, project,
// "main", "main",
// ` `
// #include 'common' #include 'common'
// x=1`, x=1`,
// ) )
// Project.parseIncludes(project, "main") Project.parseIncludes(project, "main")
// test("dependencies", () => { test("dependencies", () => {
// expect(Project.getDependencies(project, "main")) == ["common"] expect(Project.getDependencies(project, "main")) == ["common"]
// }) })
// test("dependents", () => { test("dependents", () => {
// expect(Project.getDependents(project, "main")) == [] expect(Project.getDependents(project, "main")) == []
// }) })
// test("getIncludes", () => { test("getIncludes", () => {
// let mainIncludes = Project.getIncludes(project, "main") let mainIncludes = Project.getIncludes(project, "main")
// switch mainIncludes { switch mainIncludes {
// | Ok(includes) => expect(includes) == ["common"] | Ok(includes) => expect(includes) == ["common"]
// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
// } }
// }) })
// let internalProject = project->Project.T.Private.castToInternalProject test("past chain", () => {
// test("past chain", () => { expect(project->Project.getPastChain("main")) == ["common"]
// expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] })
// }) test("import as variables", () => {
// test("import as variables", () => { expect(project->Project.Private.getIncludesAsVariables("main")) == []
// expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [] })
// }) })
// })
// describe("Parse includes", () => { describe("Parse includes", () => {
// let project = Project.createProject() let project = Project.createProject()
// Project.setSource( Project.setSource(
// project, project,
// "main", "main",
// ` `
// #include 'common' #include 'common'
// #include 'myModule' as myVariable #include 'myModule' as myVariable
// x=1`, x=1`,
// ) )
// Project.parseIncludes(project, "main") Project.parseIncludes(project, "main")
// test("dependencies", () => { test("dependencies", () => {
// expect(Project.getDependencies(project, "main")) == ["common", "myModule"] expect(Project.getDependencies(project, "main")) == ["common", "myModule"]
// }) })
// test("dependents", () => { test("dependents", () => {
// expect(Project.getDependents(project, "main")) == [] expect(Project.getDependents(project, "main")) == []
// }) })
// test("getIncludes", () => { test("getIncludes", () => {
// let mainIncludes = Project.getIncludes(project, "main") let mainIncludes = Project.getIncludes(project, "main")
// switch mainIncludes { switch mainIncludes {
// | Ok(includes) => expect(includes) == ["common", "myModule"] | Ok(includes) => expect(includes) == ["common", "myModule"]
// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) | 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", () => { test("direct includes", () => {
// expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] expect(project->Project.Private.getDirectIncludes("main")) == ["common"]
// }) })
// test("direct includes", () => { test("include as variables", () => {
// expect(Project.Private.getDirectIncludes(internalProject, "main")) == ["common"] expect(project->Project.Private.getIncludesAsVariables("main")) == [
// }) ("myVariable", "myModule"),
]
})
})
// test("include as variables", () => { describe("Parse multiple direct includes", () => {
// expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ let project = Project.createProject()
// ("myVariable", "myModule"), Project.setSource(
// ] project,
// }) "main",
// }) `
#include 'common'
// describe("Parse multiple direct includes", () => { #include 'common2'
// let project = Project.createProject() #include 'myModule' as myVariable
// Project.setSource( x=1`,
// project, )
// "main", Project.parseIncludes(project, "main")
// ` test("dependencies", () => {
// #include 'common' expect(Project.getDependencies(project, "main")) == ["common", "common2", "myModule"]
// #include 'common2' })
// #include 'myModule' as myVariable test("dependents", () => {
// x=1`, expect(Project.getDependents(project, "main")) == []
// ) })
// Project.parseIncludes(project, "main") test("getIncludes", () => {
// test("dependencies", () => { let mainIncludes = Project.getIncludes(project, "main")
// expect(Project.getDependencies(project, "main")) == ["common", "common2", "myModule"] switch mainIncludes {
// }) | Ok(includes) => expect(includes) == ["common", "common2", "myModule"]
// test("dependents", () => { | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
// expect(Project.getDependents(project, "main")) == [] }
// }) })
// test("getIncludes", () => { test("direct past chain", () => {
// let mainIncludes = Project.getIncludes(project, "main") expect(Project.getPastChain(project, "main")) == ["common", "common2"]
// switch mainIncludes { })
// | Ok(includes) => expect(includes) == ["common", "common2", "myModule"] test("include as variables", () => {
// | Error(error) => fail(error->Reducer_ErrorValue.errorToString) expect(project->Project.Private.getIncludesAsVariables("main")) == [
// } ("myVariable", "myModule"),
// }) ]
// 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"),
// ]
// })
// })

View File

@ -7,8 +7,6 @@ open Jest
open Expect open Expect
open Expect.Operators open Expect.Operators
// test("", () => expect(1)->toBe(1))
let runFetchResult = (project, sourceId) => { let runFetchResult = (project, sourceId) => {
Project.run(project, sourceId) Project.run(project, sourceId)
Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult
@ -17,18 +15,9 @@ let runFetchResult = (project, sourceId) => {
let runFetchFlatBindings = (project, sourceId) => { let runFetchFlatBindings = (project, sourceId) => {
Project.run(project, sourceId) Project.run(project, sourceId)
Project.getBindings(project, sourceId) Project.getBindings(project, sourceId)
->Bindings.removeResult ->InternalExpressionValue.toStringRecord
->InternalExpressionValue.toStringBindings
} }
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", () => { test("test result true", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "main", "true") Project.setSource(project, "main", "true")
@ -50,7 +39,7 @@ test("test library", () => {
test("test bindings", () => { test("test bindings", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "variables", "myVariable=666") Project.setSource(project, "variables", "myVariable=666")
runFetchFlatBindings(project, "variables")->expect->toBe("@{myVariable: 666}") runFetchFlatBindings(project, "variables")->expect->toBe("{myVariable: 666}")
}) })
describe("project1", () => { describe("project1", () => {
@ -86,7 +75,7 @@ describe("project1", () => {
runFetchResult(project, "main")->expect->toBe("Ok(1)") runFetchResult(project, "main")->expect->toBe("Ok(1)")
}) })
test("test bindings", () => { 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.setContinues(project, "second", ["first"])
Project.setSource(project, "first", "x=1") Project.setSource(project, "first", "x=1")
Project.setSource(project, "second", "y=2") Project.setSource(project, "second", "y=2")
Project.setSource(project, "main", "y") Project.setSource(project, "main", "z=3;y")
test("runOrder", () => { test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["first", "second", "main"] expect(Project.getRunOrder(project)) == ["first", "second", "main"]
@ -120,7 +109,8 @@ describe("project2", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)") runFetchResult(project, "main")->expect->toBe("Ok(2)")
}) })
test("test bindings", () => { 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.parseIncludes(project, "second") //The only way of setting includes
Project.setSource(project, "main", "y") Project.setSource(project, "main", "z=3; y")
test("runOrder", () => { test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"] expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"]
@ -176,7 +166,8 @@ describe("project with include", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)") runFetchResult(project, "main")->expect->toBe("Ok(2)")
}) })
test("test bindings", () => { test("test bindings", () => {
runFetchFlatBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}") // bindings from continues are not exposed!
runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}")
}) })
}) })

View File

@ -16,13 +16,13 @@ Case "Running a single source".
/* Let's start with running a single source and getting Result as well as the Bindings /* 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. 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. 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. 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. 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() let project = Project.createProject()
/* Every source has a name. This is used for debugging, dependencies and error messages. */ /* 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. */ /* Let's run "main" source. */
project->Project.run("main") project->Project.run("main")
/* Now you have a result for "main" source. /* 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. Getting None means you have forgotten to run the source.
*/ */
let result = project->Project.getResult("main") 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 */ /* Let's display the result and bindings */
( (
result->InternalExpressionValue.toStringResult, result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.toStringBindings, bindings->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(3)", "@{}") )->expect == ("Ok(3)", "{}")
/* You've got 3 with empty bindings. */ /* You've got 3 with empty bindings. */
}) })
test("run summary", () => { test("run summary", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "main", "1 + 2") project->Project.setSource("main", "1 + 2")
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let bindings = Project.getBindings(project, "main")->Bindings.removeResult let bindings = project->Project.getBindings("main")
/* Now you have external bindings and external result. */ /* Now you have external bindings and external result. */
( (
result->InternalExpressionValue.toStringResult, result->InternalExpressionValue.toStringResult,
bindings->Reducer_T.IEvBindings->InternalExpressionValue.toString, bindings->Reducer_T.IEvRecord->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}") )->expect == ("Ok(3)", "{}")
}) })
test("run with an environment", () => { test("run with an environment", () => {
@ -74,12 +74,12 @@ Case "Running a single source".
let project = Project.createProject() let project = Project.createProject()
/* Optional. Set your custom environment anytime before running */ /* 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->Project.setSource("main", "1 + 2")
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let _bindings = Project.getBindings(project, "main") let _bindings = project->Project.getBindings("main")
result->InternalExpressionValue.toStringResult->expect == "Ok(3)" result->InternalExpressionValue.toStringResult->expect == "Ok(3)"
}) })
@ -89,12 +89,16 @@ Case "Running a single source".
let (result, bindings) = Project.evaluate("1+2") let (result, bindings) = Project.evaluate("1+2")
( (
result->InternalExpressionValue.toStringResult, result->InternalExpressionValue.toStringResult,
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, bindings->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(3)", "@{}") )->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
//TODO multiple sources with includes. Introduction to includes //TODO multiple sources with includes. Introduction to includes
//TODO multiple sources with multi level includes. Cycle detection //TODO multiple sources with multi level includes. Cycle detection

View File

@ -14,27 +14,27 @@ describe("ReducerProject Tutorial", () => {
test("Chaining", () => { test("Chaining", () => {
let project = Project.createProject() let project = Project.createProject()
/* This time let's add 3 sources and chain them together */ /* 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 */ /* 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 */ /* To run, source3 depends on source2 */
Project.setContinues(project, "source3", ["source2"]) project->Project.setContinues("source3", ["source2"])
/* Now we can run the project */ /* Now we can run the project */
Project.runAll(project) project->Project.runAll
/* And let's check the result and bindings of source3 */ /* And let's check the result and bindings of source3 */
let result3 = Project.getResult(project, "source3") let result3 = project->Project.getResult("source3")
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult let bindings3 = project->Project.getBindings("source3")
( (
result3->InternalExpressionValue.toStringResult, result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.toStringBindings, bindings3->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") )->expect == ("Ok(())", "{z: 3}")
}) })
test("Depending", () => { test("Depending", () => {
@ -43,24 +43,24 @@ describe("ReducerProject Tutorial", () => {
let project = Project.createProject() let project = Project.createProject()
/* This time source1 and source2 are not depending on anything */ /* This time source1 and source2 are not depending on anything */
Project.setSource(project, "source1", "x=1") project->Project.setSource("source1", "x=1")
Project.setSource(project, "source2", "y=2") 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 */ /* 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 */ /* Now we can run the project */
Project.runAll(project) project->Project.runAll
/* And let's check the result and bindings of source3 */ /* And let's check the result and bindings of source3 */
let result3 = Project.getResult(project, "source3") let result3 = project->Project.getResult("source3")
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult let bindings3 = project->Project.getBindings("source3")
( (
result3->InternalExpressionValue.toStringResult, result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.toStringBindings, bindings3->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") )->expect == ("Ok(())", "{z: 3}")
}) })
test("Intro to including", () => { test("Intro to including", () => {
@ -70,33 +70,32 @@ describe("ReducerProject Tutorial", () => {
let project = Project.createProject() let project = Project.createProject()
/* This time source1 and source2 are not depending on anything */ /* This time source1 and source2 are not depending on anything */
Project.setSource(project, "source1", "x=1") project->Project.setSource("source1", "x=1")
Project.setSource(project, "source2", "y=2") project->Project.setSource("source2", "y=2")
Project.setSource( project->Project.setSource(
project,
"source3", "source3",
` `
#include "source1" #include "source1"
#include "source2" #include "source2"
z=3`, z=x+y`,
) )
/* We need to parse the includes to set the dependencies */ /* We need to parse the includes to set the dependencies */
Project.parseIncludes(project, "source3") project->Project.parseIncludes("source3")
/* Now we can run the project */ /* Now we can run the project */
Project.runAll(project) project->Project.runAll
/* And let's check the result and bindings of source3 /* 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 This time you are getting all the variables because we are including the other sources
Behind the scenes parseIncludes is setting the dependencies */ Behind the scenes parseIncludes is setting the dependencies */
let result3 = Project.getResult(project, "source3") let result3 = project->Project.getResult("source3")
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult let bindings3 = project->Project.getBindings("source3")
( (
result3->InternalExpressionValue.toStringResult, result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.toStringBindings, bindings3->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") )->expect == ("Ok(())", "{z: 3}")
/* /*
Doing it like this is too verbose for a storybook Doing it like this is too verbose for a storybook
But I hope you have seen the relation of setContinues and parseIncludes */ But I hope you have seen the relation of setContinues and parseIncludes */

View File

@ -16,8 +16,7 @@ Here we will finally proceed to a real life scenario. */
/* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */ /* 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 */ /* Everything happens inside a project, so let's have a project */
let project = Project.createProject() let project = Project.createProject()
Project.setSource( project->Project.setSource(
project,
"main", "main",
` `
#include "common" #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 */ /* We need to parse includes after changing the source */
Project.parseIncludes(project, "main") project->Project.parseIncludes("main")
test("getDependencies", () => { test("getDependencies", () => {
/* Parse includes has set the dependencies */ /* 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 */ /* 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 */ /* 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 */ /* 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", () => { test("getIncludes", () => {
/* Parse includes has set the includes */ /* Parse includes has set the includes */
switch Project.getIncludes(project, "main") { switch project->Project.getIncludes("main") {
| Ok(includes) => includes->expect == ["common"] | Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail | 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. include or depend on the current source.
But you don't need to use this to execute the projects. But you don't need to use this to execute the projects.
It is provided for completeness of information. */ It is provided for completeness of information. */
Project.getDependents(project, "main")->expect == [] project->Project.getDependents("main")->expect == []
/* Nothing is depending on or including main */ /* 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's recursively load the sources */
let rec loadIncludesRecursively = (project, sourceName, visited) => { 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 */ /* Oh we have already visited this source. There is an include cycle */
"Cyclic include ${sourceName}"->Js.Exn.raiseError "Cyclic include ${sourceName}"->Js.Exn.raiseError
} else { } else {
let newVisited = Js.Array2.copy(visited) 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 */ /* Let's parse the includes and dive into them */
Project.parseIncludes(project, sourceName) Project.parseIncludes(project, sourceName)
let rIncludes = Project.getIncludes(project, sourceName) let rIncludes = project->Project.getIncludes(sourceName)
switch rIncludes { switch rIncludes {
/* Maybe there is an include syntax error */ /* Maybe there is an include syntax error */
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError | Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
| Ok(includes) => | Ok(includes) =>
Belt.Array.forEach(includes, newIncludeName => { includes->Belt.Array.forEach(newIncludeName => {
/* We have got one of the new includes. /* We have got one of the new includes.
Let's load it and add it to the project */ Let's load it and add it to the project */
let newSource = loadSource(newIncludeName) let newSource = loadSource(newIncludeName)
Project.setSource(project, newIncludeName, newSource) project->Project.setSource(newIncludeName, newSource)
/* The new source is loaded and added to the project. */ /* The new source is loaded and added to the project. */
/* Of course the new source might have includes too. */ /* Of course the new source might have includes too. */
/* Let's recursively load them */ /* 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() let project = Project.createProject()
/* main includes source3 which includes source2 which includes source1 */ project->Project.setSource(
Project.setSource(
project,
"main", "main",
` `
#include "source1"
#include "source2"
#include "source3" #include "source3"
x+y+z a = x+y+z
b = doubleX
a
`, `,
) )
/* Setting source requires parsing and loading the includes recursively */ /* 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 */ /* 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 */ /* doubleX includes source1 which is eventually included by main as well */
Project.setSource( project->Project.setSource(
project,
"doubleX", "doubleX",
` `
#include "source1" #include "source1"
doubleX = x * 2 doubleX = x * 2
`, `,
) )
loadIncludesRecursively(project, "doubleX", []) project->loadIncludesRecursively("doubleX", [])
/* Remember, any time you set a source, you need to load includes recursively */ /* 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. /* As doubleX is not included by main, it is not loaded recursively.
So we link it to the project as a dependency */ 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 */ /* Let's run the project */
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let bindings = Project.getBindings(project, "main") let bindings = project->Project.getBindings("main")
/* And see the result and bindings.. */ /* And see the result and bindings.. */
test("recursive includes", () => { test("recursive includes", () => {
( (
result->InternalExpressionValue.toStringResult, result->InternalExpressionValue.toStringResult,
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, bindings->InternalExpressionValue.toStringRecord,
)->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}") )->expect == ("Ok(6)", "{a: 6,b: 2}")
/* Everything as expected */ /* Everything as expected */
}) })
}) })

View File

@ -5,11 +5,11 @@
"dir": "src/rescript", "dir": "src/rescript",
"subdirs": true "subdirs": true
}, },
// { {
// "dir": "__tests__", "dir": "__tests__",
// "type": "dev", "type": "dev",
// "subdirs": true "subdirs": true
// }, },
{ {
"dir": "benchmark", "dir": "benchmark",
"type": "dev", "type": "dev",

View File

@ -35,4 +35,4 @@ const result = project.getResult("a");
console.log("Result:", result.tag, result.value.toString()); console.log("Result:", result.tag, result.value.toString());
const bindings = project.getBindings("a"); const bindings = project.getBindings("a");
console.log("Bindings:", bindings.asValue().toString()); console.log("Bindings:", bindings.toString());

View File

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

View File

@ -8,6 +8,7 @@ type errorValue = Reducer_ErrorValue.errorValue
*/ */
type rec frType = type rec frType =
| FRTypeNumber | FRTypeNumber
| FRTypeBool
| FRTypeNumeric | FRTypeNumeric
| FRTypeDistOrNumber | FRTypeDistOrNumber
| FRTypeDist | FRTypeDist
@ -27,6 +28,7 @@ and frTypeRecordParam = (string, frType)
*/ */
type rec frValue = type rec frValue =
| FRValueNumber(float) | FRValueNumber(float)
| FRValueBool(bool)
| FRValueDist(DistributionTypes.genericDist) | FRValueDist(DistributionTypes.genericDist)
| FRValueArray(array<frValue>) | FRValueArray(array<frValue>)
| FRValueDistOrNumber(frValueDistOrNumber) | FRValueDistOrNumber(frValueDistOrNumber)
@ -71,6 +73,7 @@ module FRType = {
let rec toString = (t: t) => let rec toString = (t: t) =>
switch t { switch t {
| FRTypeNumber => "number" | FRTypeNumber => "number"
| FRTypeBool => "bool"
| FRTypeNumeric => "numeric" | FRTypeNumeric => "numeric"
| FRTypeDist => "distribution" | FRTypeDist => "distribution"
| FRTypeDistOrNumber => "distribution|number" | FRTypeDistOrNumber => "distribution|number"
@ -107,6 +110,7 @@ module FRType = {
| (FRTypeAny, f) => toFrValue(f) | (FRTypeAny, f) => toFrValue(f)
| (FRTypeString, IEvString(f)) => Some(FRValueString(f)) | (FRTypeString, IEvString(f)) => Some(FRValueString(f))
| (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f)) | (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f))
| (FRTypeBool, IEvBool(f)) => Some(FRValueBool(f))
| (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f))) | (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f)))
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) => | (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) =>
Some(FRValueDistOrNumber(FRValueNumber(f))) Some(FRValueDistOrNumber(FRValueNumber(f)))
@ -142,6 +146,7 @@ module FRType = {
let rec matchReverse = (e: frValue): internalExpressionValue => let rec matchReverse = (e: frValue): internalExpressionValue =>
switch e { switch e {
| FRValueNumber(f) => IEvNumber(f) | FRValueNumber(f) => IEvNumber(f)
| FRValueBool(f) => IEvBool(f)
| FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n) | FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n)
| FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n) | FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n)
| FRValueDist(dist) => IEvDistribution(dist) | FRValueDist(dist) => IEvDistribution(dist)

View File

@ -72,5 +72,25 @@ let library = [
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
} }
) ),
makeFn(
"not",
[FRTypeNumber],
inputs => {
switch inputs {
| [IEvNumber(x)] => IEvBool(x != 0.)->Ok
| _ => Error(impossibleError)
}
}
),
makeFn(
"not",
[FRTypeBool],
inputs => {
switch inputs {
| [IEvBool(x)] => IEvBool(!x)->Ok
| _ => Error(impossibleError)
}
}
),
] ]

View File

@ -18,15 +18,11 @@ let rec get = ({ namespace, parent }: t, id: string) => {
} }
} }
let getWithDefault = (namespace: t, id: string, default) =>
switch namespace->get(id) {
| Some(v) => Some(v)
| None => default
}
let set = ({ namespace } as bindings: t, id: string, value): t => { let 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) => { 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 make = (): t => { namespace: Reducer_Namespace.make(), parent: None }
let removeResult = ({ namespace } as bindings: t): t => { 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 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)) // 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) => // let functionNotFoundError = (call: functionCall) =>
// REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error // REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error

View File

@ -2,7 +2,7 @@ type t = Reducer_T.context
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => { 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, environment: environment,
} }
} }

View File

@ -34,36 +34,6 @@ let callInternal = (
| call => call->IEV.toStringFunctionCall->MathJs.Eval.eval | 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 doAddArray = (originalA, b) => {
let a = originalA->Js.Array2.copy let a = originalA->Js.Array2.copy
let _ = Js.Array2.pushMany(a, b) let _ = Js.Array2.pushMany(a, b)
@ -84,10 +54,6 @@ let callInternal = (
value->Ok value->Ok
} }
// let doSetBindings = (bindings: Reducer_T.nameSpace, symbol: string, value: Reducer_T.value) => {
// Bindings.set(bindings, symbol, value)->IEvBindings->Ok
// }
// let doSetTypeAliasBindings = ( // let doSetTypeAliasBindings = (
// bindings: nameSpace, // bindings: nameSpace,
// symbol: string, // symbol: string,

View File

@ -8,55 +8,74 @@ type errorValue = Reducer_ErrorValue.errorValue
/* /*
Recursively evaluate the expression 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}`) // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`)
switch expression { switch expression {
| T.EBlock(statements) => { | T.EBlock(statements) => {
let innerContext = {...context, bindings: context.bindings->Bindings.extend} 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) => { | T.EProgram(statements) => {
// Js.log(`bindings: ${context.bindings->Reducer_Bindings.toString}`) // Js.log(`bindings: ${context.bindings->Bindings.locals->Reducer_Namespace.toString}`)
let res = let (value, finalContext) = statements->Belt.Array.reduce(
statements->Js.Array2.reduce((_, statement) => statement->evaluate(context), T.IEvVoid) (T.IEvVoid, context),
((_, currentContext), statement) => statement->evaluate(currentContext))
// Js.log(`bindings after: ${context.bindings->Reducer_Bindings.toString}`) // Js.log(`bindings after: ${finalContext.bindings->Bindings.locals->Reducer_Namespace.toString}`)
res (value, finalContext)
} }
| T.EArray(elements) => elements->Js.Array2.map(element => evaluate(element, context))->T.IEvArray | T.EArray(elements) => {
let value = elements->Belt.Array.map(element => {
let (value, _) = evaluate(element, context)
value
})->T.IEvArray
(value, context)
}
| T.ERecord(pairs) => | T.ERecord(pairs) => {
pairs let value =
->Js.Array2.map(((eKey, eValue)) => { pairs
let key = eKey->evaluate(context) ->Belt.Array.map(((eKey, eValue)) => {
let keyString = switch key { let (key, _) = eKey->evaluate(context)
| IEvString(s) => s let keyString = switch key {
| _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise | IEvString(s) => s
} | _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise
let value = eValue->evaluate(context) }
(keyString, value) let (value, _) = eValue->evaluate(context)
}) (keyString, value)
->Belt.Map.String.fromArray })
->IEvRecord ->Belt.Map.String.fromArray
->T.IEvRecord
(value, context)
}
| T.EAssign(left, right) => { | T.EAssign(left, right) => {
let result = right->evaluate(context) let (result, _) = right->evaluate(context)
let _ = context.bindings->Bindings.set(left, result) (
T.IEvVoid T.IEvVoid,
{
...context,
bindings: context.bindings->Bindings.set(left, result),
}
)
} }
| T.ESymbol(name) => | T.ESymbol(name) =>
switch context.bindings->Bindings.get(name) { switch context.bindings->Bindings.get(name) {
| Some(v) => v | Some(v) => (v, context)
| None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise | None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise
} }
| T.EValue(value) => value | T.EValue(value) => (value, context)
| T.ETernary(predicate, trueCase, falseCase) => { | T.ETernary(predicate, trueCase, falseCase) => {
let predicateResult = predicate->evaluate(context) let (predicateResult, _) = predicate->evaluate(context)
switch predicateResult { switch predicateResult {
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context) | T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
| _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise | _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise
@ -64,13 +83,16 @@ let rec evaluate: T.reducerFn = (expression, context) => {
} }
| T.ELambda(parameters, body) => | 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) => { | T.ECall(fn, args) => {
let lambda = fn->evaluate(context) let (lambda, _) = fn->evaluate(context)
let argValues = Js.Array2.map(args, arg => arg->evaluate(context)) let argValues = Js.Array2.map(args, arg => {
let (argValue, _) = arg->evaluate(context)
argValue
})
switch lambda { 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 | _ => REExpectedType("Lambda", "")->Reducer_ErrorValue.ErrorException->raise
} }
} }
@ -80,18 +102,19 @@ let rec evaluate: T.reducerFn = (expression, context) => {
module BackCompatible = { module BackCompatible = {
// Those methods are used to support the existing tests // Those methods are used to support the existing tests
// If they are used outside limited testing context, error location reporting will fail // 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) 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() let context = Reducer_Context.createDefaultContext()
try { try {
expression->evaluate(context)->Ok let (value, _) = expression->evaluate(context)
value->Ok
} catch { } catch {
| exn => Reducer_ErrorValue.fromException(exn)->Error | 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) parse(peggyCode)->Result.flatMap(evaluate)
} }

View File

@ -34,11 +34,14 @@ let makeLambda = (
} }
let localBindings = bindings->Reducer_Bindings.extend let localBindings = bindings->Reducer_Bindings.extend
parameters->Js.Array2.forEachi((parameter, index) => { let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(
let _ = localBindings->Reducer_Bindings.set(parameter, arguments[index]) localBindings,
(currentBindings, parameter, index) => {
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
}) })
reducer(body, {bindings: localBindings, environment: environment}) let (value, _) = reducer(body, {bindings: localBindingsWithParameters, environment: environment})
value
} }
{ {

View File

@ -1,36 +1,23 @@
/* /*
An expression is a Lisp AST. An expression is either a primitive value or a list of expressions. An expression is an intermediate representation of a Squiggle code.
In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is Expressions are evaluated by `Reducer_Expression.evaluate` function.
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.
*/ */
module Extra = Reducer_Extra
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
type internalExpressionValue = Reducer_T.value type t = Reducer_T.expression
type environment = Reducer_T.environment
type expression = Reducer_T.expression
type t = expression
type context = Reducer_T.context
type reducerFn = Reducer_T.reducerFn
let commaJoin = values => values->Reducer_Extra_Array.intersperse(", ")->Js.String.concatMany("") 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 Converts the expression to String
*/ */
let rec toString = (expression: expression) => let rec toString = (expression: t) =>
switch expression { switch expression {
| EBlock(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))->commaJoin}>` | EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin
| EArray(aList) => `[${Js.Array2.map(aList, aValue => toString(aValue))->commaJoin}]` | 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 | ESymbol(name) => name
| ETernary(predicate, trueCase, falseCase) => | ETernary(predicate, trueCase, falseCase) =>
`${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})` `${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
@ -52,30 +39,19 @@ let toStringResultOkless = codeResult =>
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
} }
let inspect = (expr: expression): expression => { let inspect = (expr: t): t => {
Js.log(toString(expr)) Js.log(toString(expr))
expr expr
} }
let inspectResult = (r: result<expression, Reducer_ErrorValue.errorValue>): result< let inspectResult = (r: result<t, Reducer_ErrorValue.errorValue>): result<
expression, t,
Reducer_ErrorValue.errorValue, Reducer_ErrorValue.errorValue,
> => { > => {
Js.log(toStringResult(r)) Js.log(toStringResult(r))
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 => let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t =>
switch rExpression { switch rExpression {
| Ok(expression) => expression | Ok(expression) => expression

View File

@ -1,21 +1,21 @@
type t = Reducer_T.namespace 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> => 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 => { let set = (namespace: t, id: string, value): t => {
namespace->Belt.MutableMap.String.set(id, value) namespace->Belt.Map.String.set(id, value)
namespace
} }
let mergeFrom = (from: t, to: t): t => { 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__" { 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) => let toString = (namespace: t) =>
namespace namespace
->Belt.MutableMap.String.toArray ->Belt.Map.String.toArray
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${eachValue->ReducerInterface_InternalExpressionValue.toString}`) ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${eachValue->ReducerInterface_InternalExpressionValue.toString}`)
->Js.Array2.toString ->Js.Array2.toString
let fromArray = (a): t => let fromArray = (a): t =>
Belt.MutableMap.String.fromArray(a) Belt.Map.String.fromArray(a)
let toMap = (namespace: t): Reducer_T.map => let toMap = (namespace: t): Reducer_T.map =>
namespace->Belt.MutableMap.String.toArray->Belt.Map.String.fromArray namespace
let toRecord = (namespace: t): Reducer_T.value => let toRecord = (namespace: t): Reducer_T.value =>
namespace->toMap->IEvRecord namespace->toMap->IEvRecord

View File

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

View File

@ -6,7 +6,7 @@ let internalStdLib: Reducer_T.namespace = {
->Reducer_Namespace.mergeFrom(SquiggleLibrary_Math.make()) ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Math.make())
->Reducer_Namespace.mergeFrom(SquiggleLibrary_Versions.make()) ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Versions.make())
let _ = res->Reducer_Namespace.set( let res = res->Reducer_Namespace.set(
"$_atIndex_$", "$_atIndex_$",
Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => { Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => {
switch inputs { switch inputs {
@ -29,9 +29,10 @@ let internalStdLib: Reducer_T.namespace = {
})->Reducer_T.IEvLambda, })->Reducer_T.IEvLambda,
) )
FunctionRegistry_Library.nonRegistryLambdas->Js.Array2.forEach( let res = FunctionRegistry_Library.nonRegistryLambdas->Belt.Array.reduce(
((name, lambda)) => { res,
let _ = res->Reducer_Namespace.set(name, lambda->Reducer_T.IEvLambda) (cur, (name, lambda)) => {
cur->Reducer_Namespace.set(name, lambda->Reducer_T.IEvLambda)
} }
) )
@ -59,10 +60,10 @@ let internalStdLib: Reducer_T.namespace = {
// [ ] | (_, [IEvNumber(_), IEvNumber(_)]) // [ ] | (_, [IEvNumber(_), IEvNumber(_)])
// [ ] | (_, [IEvString(_), IEvString(_)]) => callMathJs(call) // [ ] | (_, [IEvString(_), IEvString(_)]) => callMathJs(call)
FunctionRegistry_Library.registry.fnNameDict let res = FunctionRegistry_Library.registry.fnNameDict
->Js.Dict.keys ->Js.Dict.keys
->Js.Array2.forEach(name => { ->Belt.Array.reduce(res, (cur, name) => {
let _ = res->Reducer_Namespace.set( cur->Reducer_Namespace.set(
name, name,
Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => { Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => {
switch FunctionRegistry_Library.call(name, arguments, environment, reducer) { switch FunctionRegistry_Library.call(name, arguments, environment, reducer) {

View File

@ -194,17 +194,17 @@ let tryRunWithResult = (
sourceId: string, sourceId: string,
rPrevResult: ProjectItem.T.resultArgumentType, rPrevResult: ProjectItem.T.resultArgumentType,
): ProjectItem.T.resultArgumentType => { ): ProjectItem.T.resultArgumentType => {
switch getResultOption(project, sourceId) { switch project->getResultOption(sourceId) {
| Some(result) => result // already ran | Some(result) => result // already ran
| None => | None =>
switch rPrevResult { switch rPrevResult {
| Error(error) => { | Error(error) => {
setResult(project, sourceId, Error(error)) project->setResult(sourceId, Error(error))
Error(error) Error(error)
} }
| Ok(_prevResult) => { | Ok(_prevResult) => {
doLinkAndRun(project, sourceId) project->doLinkAndRun(sourceId)
getResultOption(project, sourceId)->Belt.Option.getWithDefault(rPrevResult) project->getResultOption(sourceId)->Belt.Option.getWithDefault(rPrevResult)
} }
} }
} }
@ -214,7 +214,7 @@ let runAll = (project: t): unit => {
let runOrder = Topology.getRunOrder(project) let runOrder = Topology.getRunOrder(project)
let initialState = Ok(Reducer_T.IEvVoid) let initialState = Ok(Reducer_T.IEvVoid)
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => 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 runOrder = Topology.getRunOrderFor(project, sourceId)
let initialState = Ok(Reducer_T.IEvVoid) let initialState = Ok(Reducer_T.IEvVoid)
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) =>
tryRunWithResult(project, currId, currState) project->tryRunWithResult(currId, currState)
) )
} }
let evaluate = (sourceCode: string) => { let evaluate = (sourceCode: string) => {
let project = createProject() let project = createProject()
setSource(project, "main", sourceCode) project->setSource("main", sourceCode)
runAll(project) 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, project->getBindings("main")->Reducer_Namespace.toMap,
) )
} }

View File

@ -174,8 +174,8 @@ let doRun = (this: t, context: Reducer_T.context): t =>
switch this->getExpression { switch this->getExpression {
| Some(expressionResult) => switch expressionResult { | Some(expressionResult) => switch expressionResult {
| Ok(expression) => try { | Ok(expression) => try {
let result = Reducer_Expression.evaluate(expression, context) let (result, contextAfterEvaluation) = Reducer_Expression.evaluate(expression, context)
this->setResult(result->Ok)->setContinuation(context.bindings->Reducer_Bindings.locals) this->setResult(result->Ok)->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals)
} catch { } catch {
| Reducer_ErrorValue.ErrorException(e) => this->failRun(e) | Reducer_ErrorValue.ErrorException(e) => this->failRun(e)
| _ => this->failRun(RETodo("unhandled rescript exception")) | _ => this->failRun(RETodo("unhandled rescript exception"))

View File

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