Merge pull request #968 from quantified-uncertainty/reducer-project

Reducer Project - API Revamp
This commit is contained in:
Ozzie Gooen 2022-08-25 08:31:56 -07:00 committed by GitHub
commit 05299c78e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 4399 additions and 1514 deletions

View File

@ -1,10 +1,14 @@
module ExpressionValue = ReducerInterface.ExternalExpressionValue module ExpressionValue = ReducerInterface.InternalExpressionValue
module Expression = Reducer_Expression
open Jest open Jest
open Expect open Expect
let expectEvalToBe = (expr: string, answer: string) => let expectEvalToBe = (sourceCode: string, answer: string) =>
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer) Expression.BackCompatible.evaluateString(sourceCode)
->ExpressionValue.toStringResult
->expect
->toBe(answer)
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer)) let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))

View File

@ -1,6 +1,5 @@
// Reducer_Helpers // Reducer_Helpers
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
@ -15,8 +14,4 @@ let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
} }
} }
let removeDefaultsExternal = (ev: ExternalExpressionValue.t): ExternalExpressionValue.t =>
ev->InternalExpressionValue.toInternal->removeDefaultsInternal->InternalExpressionValue.toExternal
let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal) let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal)
let rRemoveDefaultsExternal = r => Belt.Result.map(r, removeDefaultsExternal)

View File

@ -1,5 +1,5 @@
module MathJs = Reducer_MathJs module MathJs = Reducer_MathJs
module ErrorValue = Reducer.ErrorValue module ErrorValue = Reducer_ErrorValue
open Jest open Jest
open ExpectJs open ExpectJs

View File

@ -3,359 +3,464 @@ open Reducer_Peggy_TestHelpers
describe("Peggy parse", () => { describe("Peggy parse", () => {
describe("float", () => { describe("float", () => {
testParse("1.", "{1}") testParse("1.", "{(::$_endOfOuterBlock_$ () 1)}")
testParse("1.1", "{1.1}") testParse("1.1", "{(::$_endOfOuterBlock_$ () 1.1)}")
testParse(".1", "{0.1}") testParse(".1", "{(::$_endOfOuterBlock_$ () 0.1)}")
testParse("0.1", "{0.1}") testParse("0.1", "{(::$_endOfOuterBlock_$ () 0.1)}")
testParse("1e1", "{10}") testParse("1e1", "{(::$_endOfOuterBlock_$ () 10)}")
testParse("1e-1", "{0.1}") testParse("1e-1", "{(::$_endOfOuterBlock_$ () 0.1)}")
testParse(".1e1", "{1}") testParse(".1e1", "{(::$_endOfOuterBlock_$ () 1)}")
testParse("0.1e1", "{1}") testParse("0.1e1", "{(::$_endOfOuterBlock_$ () 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 // 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", "{(::unaryMinus 1)}") testParse("-1", "{(::$_endOfOuterBlock_$ () (::unaryMinus 1))}")
testParse("!true", "{(::not true)}") testParse("!true", "{(::$_endOfOuterBlock_$ () (::not true))}")
testParse("1 + -1", "{(::add 1 (::unaryMinus 1))}") testParse("1 + -1", "{(::$_endOfOuterBlock_$ () (::add 1 (::unaryMinus 1)))}")
testParse("-a[0]", "{(::unaryMinus (::$_atIndex_$ :a 0))}") testParse("-a[0]", "{(::$_endOfOuterBlock_$ () (::unaryMinus (::$_atIndex_$ :a 0)))}")
testParse("!a[0]", "{(::not (::$_atIndex_$ :a 0))}") testParse("!a[0]", "{(::$_endOfOuterBlock_$ () (::not (::$_atIndex_$ :a 0)))}")
}) })
describe("multiplicative", () => { describe("multiplicative", () => {
testParse("1 * 2", "{(::multiply 1 2)}") testParse("1 * 2", "{(::$_endOfOuterBlock_$ () (::multiply 1 2))}")
testParse("1 / 2", "{(::divide 1 2)}") testParse("1 / 2", "{(::$_endOfOuterBlock_$ () (::divide 1 2))}")
testParse("1 * 2 * 3", "{(::multiply (::multiply 1 2) 3)}") testParse("1 * 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::multiply 1 2) 3))}")
testParse("1 * 2 / 3", "{(::divide (::multiply 1 2) 3)}") testParse("1 * 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::multiply 1 2) 3))}")
testParse("1 / 2 * 3", "{(::multiply (::divide 1 2) 3)}") testParse("1 / 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::divide 1 2) 3))}")
testParse("1 / 2 / 3", "{(::divide (::divide 1 2) 3)}") testParse("1 / 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::divide 1 2) 3))}")
testParse("1 * 2 + 3 * 4", "{(::add (::multiply 1 2) (::multiply 3 4))}") testParse(
testParse("1 * 2 - 3 * 4", "{(::subtract (::multiply 1 2) (::multiply 3 4))}") "1 * 2 + 3 * 4",
testParse("1 * 2 .+ 3 * 4", "{(::dotAdd (::multiply 1 2) (::multiply 3 4))}") "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}",
testParse("1 * 2 .- 3 * 4", "{(::dotSubtract (::multiply 1 2) (::multiply 3 4))}") )
testParse("1 * 2 + 3 .* 4", "{(::add (::multiply 1 2) (::dotMultiply 3 4))}") testParse(
testParse("1 * 2 + 3 / 4", "{(::add (::multiply 1 2) (::divide 3 4))}") "1 * 2 - 3 * 4",
testParse("1 * 2 + 3 ./ 4", "{(::add (::multiply 1 2) (::dotDivide 3 4))}") "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}",
testParse("1 * 2 - 3 .* 4", "{(::subtract (::multiply 1 2) (::dotMultiply 3 4))}") )
testParse("1 * 2 - 3 / 4", "{(::subtract (::multiply 1 2) (::divide 3 4))}") testParse(
testParse("1 * 2 - 3 ./ 4", "{(::subtract (::multiply 1 2) (::dotDivide 3 4))}") "1 * 2 .+ 3 * 4",
testParse("1 * 2 - 3 * 4^5", "{(::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5)))}") "{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}",
)
testParse(
"1 * 2 .- 3 * 4",
"{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}",
)
testParse(
"1 * 2 + 3 .* 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}",
)
testParse(
"1 * 2 + 3 / 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}",
)
testParse(
"1 * 2 + 3 ./ 4",
"{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}",
)
testParse(
"1 * 2 - 3 .* 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}",
)
testParse(
"1 * 2 - 3 / 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}",
)
testParse(
"1 * 2 - 3 ./ 4",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}",
)
testParse(
"1 * 2 - 3 * 4^5",
"{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5))))}",
)
testParse( testParse(
"1 * 2 - 3 * 4^5^6", "1 * 2 - 3 * 4^5^6",
"{(::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6)))}", "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6))))}",
)
testParse(
"1 * -a[-2]",
"{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}",
) )
testParse("1 * -a[-2]", "{(::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2))))}")
}) })
describe("multi-line", () => { describe("multi-line", () => {
testParse("x=1; 2", "{:x = {1}; 2}") testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}")
testParse("x=1; y=2", "{:x = {1}; :y = {2}}") testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}")
}) })
describe("variables", () => { describe("variables", () => {
testParse("x = 1", "{:x = {1}}") testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}")
testParse("x", "{:x}") testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
testParse("x = 1; x", "{:x = {1}; :x}") testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}")
}) })
describe("functions", () => { describe("functions", () => {
testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments
testParse("identity(x)", "{(::identity :x)}") testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}")
}) })
describe("arrays", () => { describe("arrays", () => {
testParse("[]", "{(::$_constructArray_$ ())}") testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ()))}")
testParse("[0, 1, 2]", "{(::$_constructArray_$ (0 1 2))}") testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ (0 1 2)))}")
testParse("['hello', 'world']", "{(::$_constructArray_$ ('hello' 'world'))}") testParse(
testParse("([0,1,2])[1]", "{(::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1)}") "['hello', 'world']",
"{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ('hello' 'world')))}",
)
testParse(
"([0,1,2])[1]",
"{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1))}",
)
}) })
describe("records", () => { describe("records", () => {
testParse("{a: 1, b: 2}", "{(::$_constructRecord_$ ('a': 1 'b': 2))}") testParse(
testParse("{1+0: 1, 2+0: 2}", "{(::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2))}") // key can be any expression "{a: 1, b: 2}",
testParse("record.property", "{(::$_atIndex_$ :record 'property')}") "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}",
)
testParse(
"{1+0: 1, 2+0: 2}",
"{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2)))}",
) // key can be any expression
testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}")
}) })
describe("post operators", () => { 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)", "{(::equal :a (::not (::b 1)))}") testParse("a==!b(1)", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::b 1))))}")
testParse("a==!b[1]", "{(::equal :a (::not (::$_atIndex_$ :b 1)))}") testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}")
testParse("a==!b.one", "{(::equal :a (::not (::$_atIndex_$ :b 'one')))}") testParse(
"a==!b.one",
"{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}",
)
}) })
describe("comments", () => { describe("comments", () => {
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 line comment", "{(::$_endOfOuterBlock_$ () 1)}")
testParse("1 /* This is a multi line comment */", "{1}") testParse("1 /* This is a multi line comment */", "{(::$_endOfOuterBlock_$ () 1)}")
testParse("/* This is a multi line comment */ 1", "{1}") testParse("/* This is a multi line comment */ 1", "{(::$_endOfOuterBlock_$ () 1)}")
testParse( testParse(
` `
/* This is /* This is
a multi line a multi line
comment */ comment */
1`, 1`,
"{1}", "{(::$_endOfOuterBlock_$ () 1)}",
) )
}) })
describe("ternary operator", () => { describe("ternary operator", () => {
testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}") testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}")
testParse( testParse(
"false ? 2 : false ? 4 : 5", "false ? 2 : false ? 4 : 5",
"{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}",
) // nested ternary ) // nested ternary
}) })
describe("if then else", () => { describe("if then else", () => {
testParse("if true then 2 else 3", "{(::$$_ternary_$$ true {2} {3})}") testParse(
testParse("if false then {2} else {3}", "{(::$$_ternary_$$ false {2} {3})}") "if true then 2 else 3",
"{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}",
)
testParse(
"if false then {2} else {3}",
"{(::$_endOfOuterBlock_$ () (::$$_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}",
"{(::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5}))}", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5})))}",
) //nested if ) //nested if
}) })
describe("logical", () => { describe("logical", () => {
testParse("true || false", "{(::or true false)}") testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}")
testParse("true && false", "{(::and true false)}") testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}")
testParse("a * b + c", "{(::add (::multiply :a :b) :c)}") // for comparison testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison
testParse("a && b || c", "{(::or (::and :a :b) :c)}") testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}")
testParse("a && b || c && d", "{(::or (::and :a :b) (::and :c :d))}") testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}")
testParse("a && !b || c", "{(::or (::and :a (::not :b)) :c)}") testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}")
testParse("a && b==c || d", "{(::or (::and :a (::equal :b :c)) :d)}") testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}")
testParse("a && b!=c || d", "{(::or (::and :a (::unequal :b :c)) :d)}") testParse(
testParse("a && !(b==c) || d", "{(::or (::and :a (::not (::equal :b :c))) :d)}") "a && b!=c || d",
testParse("a && b>=c || d", "{(::or (::and :a (::largerEq :b :c)) :d)}") "{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}",
testParse("a && !(b>=c) || d", "{(::or (::and :a (::not (::largerEq :b :c))) :d)}") )
testParse("a && b<=c || d", "{(::or (::and :a (::smallerEq :b :c)) :d)}") testParse(
testParse("a && b>c || d", "{(::or (::and :a (::larger :b :c)) :d)}") "a && !(b==c) || d",
testParse("a && b<c || d", "{(::or (::and :a (::smaller :b :c)) :d)}") "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}",
testParse("a && b<c[i] || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d)}") )
testParse("a && b<c.i || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d)}") testParse(
testParse("a && b<c(i) || d", "{(::or (::and :a (::smaller :b (::c :i))) :d)}") "a && b>=c || d",
testParse("a && b<1+2 || d", "{(::or (::and :a (::smaller :b (::add 1 2))) :d)}") "{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}",
)
testParse(
"a && !(b>=c) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}",
)
testParse(
"a && b<=c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smallerEq :b :c)) :d))}",
)
testParse("a && b>c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::larger :b :c)) :d))}")
testParse(
"a && b<c || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b :c)) :d))}",
)
testParse(
"a && b<c[i] || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d))}",
)
testParse(
"a && b<c.i || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d))}",
)
testParse(
"a && b<c(i) || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::c :i))) :d))}",
)
testParse(
"a && b<1+2 || d",
"{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 2))) :d))}",
)
testParse( testParse(
"a && b<1+2*3 || d", "a && b<1+2*3 || d",
"{(::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d)}", "{(::$_endOfOuterBlock_$ () (::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",
"{(::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d)}", "{(::$_endOfOuterBlock_$ () (::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",
"{(::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false)}", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false))}",
) )
}) })
describe("pipe", () => { describe("pipe", () => {
testParse("1 -> add(2)", "{(::add 1 2)}") testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
testParse("-1 -> add(2)", "{(::add (::unaryMinus 1) 2)}") testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}")
testParse("-a[1] -> add(2)", "{(::add (::unaryMinus (::$_atIndex_$ :a 1)) 2)}") testParse(
testParse("-f(1) -> add(2)", "{(::add (::unaryMinus (::f 1)) 2)}") "-a[1] -> add(2)",
testParse("1 + 2 -> add(3)", "{(::add 1 (::add 2 3))}") "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::$_atIndex_$ :a 1)) 2))}",
testParse("1 -> add(2) * 3", "{(::multiply (::add 1 2) 3)}") )
testParse("1 -> subtract(2)", "{(::subtract 1 2)}") testParse("-f(1) -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::f 1)) 2))}")
testParse("-1 -> subtract(2)", "{(::subtract (::unaryMinus 1) 2)}") testParse("1 + 2 -> add(3)", "{(::$_endOfOuterBlock_$ () (::add 1 (::add 2 3)))}")
testParse("1 -> subtract(2) * 3", "{(::multiply (::subtract 1 2) 3)}") testParse("1 -> add(2) * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::add 1 2) 3))}")
testParse("1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract 1 2))}")
testParse("-1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract (::unaryMinus 1) 2))}")
testParse(
"1 -> subtract(2) * 3",
"{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}",
)
}) })
describe("elixir pipe", () => { 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)", "{(::add 1 2)}") testParse("1 |> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
}) })
describe("to", () => { describe("to", () => {
testParse("1 to 2", "{(::credibleIntervalToDistribution 1 2)}") testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}")
testParse("-1 to -2", "{(::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2))}") // lower than unary testParse(
"-1 to -2",
"{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}",
) // lower than unary
testParse( testParse(
"a[1] to a[2]", "a[1] to a[2]",
"{(::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2))}", "{(::$_endOfOuterBlock_$ () (::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",
"{(::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2'))}", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}",
) // lower than post ) // lower than post
testParse("1 to 2 + 3", "{(::add (::credibleIntervalToDistribution 1 2) 3)}") // higher than binary operators testParse(
"1 to 2 + 3",
"{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}",
) // higher than binary operators
testParse( testParse(
"1->add(2) to 3->add(4) -> add(4)", "1->add(2) to 3->add(4) -> add(4)",
"{(::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4))}", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4)))}",
) // lower than chain ) // 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}; :x}") testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; (::$_endOfOuterBlock_$ () :x)}")
}) })
describe("lambda", () => { describe("lambda", () => {
testParse("{|x| x}", "{{|:x| {:x}}}") testParse("{|x| x}", "{(::$_endOfOuterBlock_$ () {|:x| {:x}})}")
testParse("f={|x| x}", "{:f = {{|:x| {:x}}}}") testParse("f={|x| x}", "{:f = {{|:x| {:x}}}; (::$_endOfOuterBlock_$ () ())}")
testParse("f(x)=x", "{:f = {|:x| {:x}}}") // Function definitions are lambda assignments testParse("f(x)=x", "{:f = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions are lambda assignments
testParse("f(x)=x ? 1 : 0", "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}") // Function definitions are lambda assignments testParse(
"f(x)=x ? 1 : 0",
"{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}; (::$_endOfOuterBlock_$ () ())}",
) // Function definitions are lambda assignments
}) })
describe("Using lambda as value", () => { 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}; :z}", "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; (::$_endOfOuterBlock_$ () :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))}; :z}", "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ (:myadd))}; (::$_endOfOuterBlock_$ () :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))}; :z}", "{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; (::$_endOfOuterBlock_$ () :z)}",
)
testParse("f({|x| x+1})", "{(::$_endOfOuterBlock_$ () (::f {|:x| {(::add :x 1)}}))}")
testParse(
"map(arr, {|x| x+1})",
"{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}",
) )
testParse("f({|x| x+1})", "{(::f {|:x| {(::add :x 1)}})}")
testParse("map(arr, {|x| x+1})", "{(::map :arr {|:x| {(::add :x 1)}})}")
testParse( testParse(
"map([1,2,3], {|x| x+1})", "map([1,2,3], {|x| x+1})",
"{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}", "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
) )
testParse( testParse(
"[1,2,3]->map({|x| x+1})", "[1,2,3]->map({|x| x+1})",
"{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}", "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
) )
}) })
describe("unit", () => { describe("unit", () => {
testParse("1m", "{(::fromUnit_m 1)}") testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}")
testParse("1M", "{(::fromUnit_M 1)}") testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}")
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}") testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}")
}) })
describe("Module", () => { describe("Module", () => {
testParse("x", "{:x}") testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
testParse("Math.pi", "{:Math.pi}") testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}")
}) })
}) })
describe("parsing new line", () => { describe("parsing new line", () => {
testParse( testParse(
` `
a + a +
b`, b`,
"{(::add :a :b)}", "{(::$_endOfOuterBlock_$ () (::add :a :b))}",
) )
testParse( testParse(
` `
x= x=
1`, 1`,
"{:x = {1}}", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
` `
x=1 x=1
y=2`, y=2`,
"{:x = {1}; :y = {2}}", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
` `
x={ x={
y=2; y=2;
y } y }
x`, x`,
"{:x = {:y = {2}; :y}; :x}", "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
) )
testParse( testParse(
` `
x={ x={
y=2 y=2
y } y }
x`, x`,
"{:x = {:y = {2}; :y}; :x}", "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
) )
testParse( testParse(
` `
x={ x={
y=2 y=2
y y
} }
x`, x`,
"{:x = {:y = {2}; :y}; :x}", "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
) )
testParse( testParse(
` `
x=1
y=2
z=3
`,
"{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}",
)
testParse(
`
f={
x=1 x=1
y=2 y=2
z=3 z=3
`, x+y+z
"{:x = {1}; :y = {2}; :z = {3}}", }
`,
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
` `
f={ f={
x=1 x=1
y=2 y=2
z=3 z=3
x+y+z x+y+z
}
g=f+4
g
`,
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () :g)}",
)
testParse(
`
f =
{
x=1; //x
y=2 //y
z=
3
x+
y+
z
} }
`, g =
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}}", f +
4
g ->
h ->
p ->
q
`,
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () (::q (::p (::h :g))))}",
) )
testParse( testParse(
` `
f={ a |>
x=1 b |>
y=2 c |>
z=3 d
x+y+z `,
} "{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}",
g=f+4
g
`,
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; :g}",
) )
testParse( testParse(
` `
f = a |>
{ b |>
x=1; //x c |>
y=2 //y d +
z= e
3 `,
x+ "{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}",
y+
z
}
g =
f +
4
g ->
h ->
p ->
q
`,
"{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::q (::p (::h :g)))}",
)
testParse(
`
a |>
b |>
c |>
d
`,
"{(::d (::c (::b :a)))}",
)
testParse(
`
a |>
b |>
c |>
d +
e
`,
"{(::add (::d (::c (::b :a))) :e)}",
) )
}) })

View File

@ -3,77 +3,83 @@ open Reducer_Peggy_TestHelpers
describe("Peggy parse type", () => { describe("Peggy parse type", () => {
describe("type of", () => { describe("type of", () => {
testParse("p: number", "{(::$_typeOf_$ :p #number)}") testParse("p: number", "{(::$_typeOf_$ :p #number); (::$_endOfOuterBlock_$ () ())}")
}) })
describe("type alias", () => { describe("type alias", () => {
testParse("type index=number", "{(::$_typeAlias_$ #index #number)}") testParse(
"type index=number",
"{(::$_typeAlias_$ #index #number); (::$_endOfOuterBlock_$ () ())}",
)
}) })
describe("type or", () => { describe("type or", () => {
testParse( testParse(
"answer: number|string", "answer: number|string",
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string))))}", "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string)))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("type function", () => { describe("type function", () => {
testParse( testParse(
"f: number=>number=>number", "f: number=>number=>number",
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}", "{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number)))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("high priority contract", () => { describe("high priority contract", () => {
testParse( testParse(
"answer: number<-min<-max(100)|string", "answer: number<-min<-max(100)|string",
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}", "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string)))); (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
"answer: number<-memberOf([1,3,5])", "answer: number<-memberOf([1,3,5])",
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}", "{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5)))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("low priority contract", () => { describe("low priority contract", () => {
testParse( testParse(
"answer: number | string $ opaque", "answer: number | string $ opaque",
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}", "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("type array", () => { describe("type array", () => {
testParse("answer: [number]", "{(::$_typeOf_$ :answer (::$_typeArray_$ #number))}") testParse(
"answer: [number]",
"{(::$_typeOf_$ :answer (::$_typeArray_$ #number)); (::$_endOfOuterBlock_$ () ())}",
)
}) })
describe("type record", () => { describe("type record", () => {
testParse( testParse(
"answer: {a: number, b: string}", "answer: {a: number, b: string}",
"{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string))))}", "{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string)))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("type constructor", () => { describe("type constructor", () => {
testParse( testParse(
"answer: Age(number)", "answer: Age(number)",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number))))}", "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number)))); (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
"answer: Complex(number, number)", "answer: Complex(number, number)",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number))))}", "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number)))); (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
"answer: Person({age: number, name: string})", "answer: Person({age: number, name: string})",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string)))))))}", "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string))))))); (::$_endOfOuterBlock_$ () ())}",
) )
testParse( testParse(
"weekend: Saturday | Sunday", "weekend: Saturday | Sunday",
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}", "{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ())))))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("type parenthesis", () => { describe("type parenthesis", () => {
//$ is introduced to avoid parenthesis //$ is introduced to avoid parenthesis
testParse( testParse(
"answer: (number|string)<-opaque", "answer: (number|string)<-opaque",
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}", "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
describe("squiggle expressions in type contracts", () => { describe("squiggle expressions in type contracts", () => {
testParse( testParse(
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))", "odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}", "{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2))); (::$_endOfOuterBlock_$ () ())}",
) )
}) })
}) })

View File

@ -23,13 +23,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
} else { } else {
let a2 = let a2 =
rExpr rExpr
->Result.flatMap(expr => ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
Expression.reduceExpression(
expr,
ReducerInterface_StdLib.internalStdLib,
ExpressionValue.defaultEnvironment,
)
)
->Reducer_Helpers.rRemoveDefaultsInternal ->Reducer_Helpers.rRemoveDefaultsInternal
->ExpressionValue.toStringResultOkless ->ExpressionValue.toStringResultOkless
(a1, a2)->expect->toEqual((answer, v)) (a1, a2)->expect->toEqual((answer, v))

View File

@ -0,0 +1,23 @@
module Bindings = Reducer_Bindings
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Jest
open Reducer_Peggy_TestHelpers
describe("Peggy Outer Block", () => {
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ())
testToExpression(
"x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
~v="()",
(),
)
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
testToExpression(
"x={a=1; a}; x",
"{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}",
~v="1",
(),
)
})

View File

@ -7,101 +7,138 @@ 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", "{1}", ~v="1", ()) testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ()) testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ())
testToExpression("true", "{true}", ~v="true", ()) testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ())
testToExpression("1+2", "{(:add 1 2)}", ~v="3", ()) testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression("add(1,2)", "{(:add 1 2)}", ~v="3", ()) testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression("(1)", "{1}", ()) testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ())
testToExpression("(1+2)", "{(:add 1 2)}", ()) testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ())
}) })
describe("unary", () => { describe("unary", () => {
testToExpression("-1", "{(:unaryMinus 1)}", ~v="-1", ()) testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ())
testToExpression("!true", "{(:not true)}", ~v="false", ()) testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ())
testToExpression("1 + -1", "{(:add 1 (:unaryMinus 1))}", ~v="0", ()) testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ())
testToExpression("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}", ()) testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ())
}) })
describe("multi-line", () => { describe("multi-line", () => {
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); 2}", ~v="2", ()) testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
testToExpression("x=1; y=2", "{(:$_let_$ :x {1}); (:$_let_$ :y {2})}", ~v="@{x: 1,y: 2}", ()) testToExpression(
"x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
(),
)
}) })
describe("variables", () => { describe("variables", () => {
testToExpression("x = 1", "{(:$_let_$ :x {1})}", ~v="@{x: 1}", ()) testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ())
testToExpression("x", "{:x}", ~v=":x", ()) //TODO: value should return error testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); :x}", ~v="1", ()) testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ())
}) })
describe("functions", () => { describe("functions", () => {
testToExpression( testToExpression(
"identity(x) = x", "identity(x) = x",
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x}))}", "{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
~v="@{identity: lambda(x=>internal code)}",
(), (),
) // Function definitions become lambda assignments ) // Function definitions become lambda assignments
testToExpression("identity(x)", "{(:identity :x)}", ()) // Note value returns error properly testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly
testToExpression( 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)})); (:f 3)}", "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:$_endOfOuterBlock_$ () (:f 3))}",
~v="0", ~v="0",
(), (),
) )
}) })
describe("arrays", () => { describe("arrays", () => {
testToExpression("[]", "{(:$_constructArray_$ ())}", ~v="[]", ()) testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ()))}", ~v="[]", ())
testToExpression("[0, 1, 2]", "{(:$_constructArray_$ (0 1 2))}", ~v="[0,1,2]", ()) testToExpression(
"[0, 1, 2]",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ (0 1 2)))}",
~v="[0,1,2]",
(),
)
testToExpression( testToExpression(
"['hello', 'world']", "['hello', 'world']",
"{(:$_constructArray_$ ('hello' 'world'))}", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ('hello' 'world')))}",
~v="['hello','world']", ~v="['hello','world']",
(), (),
) )
testToExpression("([0,1,2])[1]", "{(:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1)}", ~v="1", ()) testToExpression(
"([0,1,2])[1]",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1))}",
~v="1",
(),
)
}) })
describe("records", () => { describe("records", () => {
testToExpression( testToExpression(
"{a: 1, b: 2}", "{a: 1, b: 2}",
"{(:$_constructRecord_$ (('a' 1) ('b' 2)))}", "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('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}",
"{(:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2)))}", "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}",
(), (),
) // key can be any expression ) // key can be any expression
testToExpression("record.property", "{(:$_atIndex_$ :record 'property')}", ()) testToExpression(
"record.property",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
(),
)
testToExpression( testToExpression(
"record={property: 1}; record.property", "record={property: 1}; record.property",
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_atIndex_$ :record 'property')}", "{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
~v="1", ~v="1",
(), (),
) )
}) })
describe("comments", () => { describe("comments", () => {
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("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression("1 /* This is a multi line comment */", "{1}", ~v="1", ()) testToExpression(
testToExpression("/* This is a multi line comment */ 1", "{1}", ~v="1", ()) "1 /* This is a multi line comment */",
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
testToExpression(
"/* This is a multi line comment */ 1",
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
}) })
describe("ternary operator", () => { describe("ternary operator", () => {
testToExpression("true ? 1 : 0", "{(:$$_ternary_$$ true 1 0)}", ~v="1", ()) testToExpression(
testToExpression("false ? 1 : 0", "{(:$$_ternary_$$ false 1 0)}", ~v="0", ()) "true ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}",
~v="1",
(),
)
testToExpression(
"false ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}",
~v="0",
(),
)
testToExpression( testToExpression(
"true ? 1 : false ? 2 : 0", "true ? 1 : false ? 2 : 0",
"{(:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0))}", "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}",
~v="1", ~v="1",
(), (),
) // nested ternary ) // nested ternary
testToExpression( testToExpression(
"false ? 1 : false ? 2 : 0", "false ? 1 : false ? 2 : 0",
"{(:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0))}", "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}",
~v="0", ~v="0",
(), (),
) // nested ternary ) // nested ternary
@ -109,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)})); (:f 6)}", "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (: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)})); (:f 6)}", "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (: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)})); (:f 6)}", "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}",
~v="6", ~v="6",
(), (),
) )
@ -131,23 +168,41 @@ describe("Peggy to Expression", () => {
}) })
describe("if then else", () => { describe("if then else", () => {
testToExpression("if true then 2 else 3", "{(:$$_ternary_$$ true {2} {3})}", ()) testToExpression(
testToExpression("if true then {2} else {3}", "{(:$$_ternary_$$ true {2} {3})}", ()) "if true then 2 else 3",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
(),
)
testToExpression(
"if true then {2} else {3}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ 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}",
"{(:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5}))}", "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}",
(), (),
) //nested if ) //nested if
}) })
describe("pipe", () => { describe("pipe", () => {
testToExpression("1 -> add(2)", "{(:add 1 2)}", ~v="3", ()) testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}", ~v="1", ()) // note that unary has higher priority naturally testToExpression(
testToExpression("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}", ~v="9", ()) "-1 -> add(2)",
"{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}",
~v="1",
(),
) // note that unary has higher priority naturally
testToExpression(
"1 -> add(2) * 3",
"{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}",
~v="9",
(),
)
}) })
describe("elixir pipe", () => { describe("elixir pipe", () => {
testToExpression("1 |> add(2)", "{(:add 1 2)}", ~v="3", ()) testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
}) })
// see testParse for priorities of to and credibleIntervalToDistribution // see testParse for priorities of to and credibleIntervalToDistribution
@ -157,30 +212,31 @@ 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})}", "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}",
~v="@{x: 1,y: 99}",
(), (),
) )
}) })
describe("lambda", () => { describe("lambda", () => {
testToExpression("{|x| x}", "{(:$$_lambda_$$ [x] {:x})}", ~v="lambda(x=>internal code)", ()) testToExpression(
"{|x| x}",
"{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}",
~v="lambda(x=>internal code)",
(),
)
testToExpression( testToExpression(
"f={|x| x}", "f={|x| x}",
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})})}", "{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}",
~v="@{f: lambda(x=>internal code)}",
(), (),
) )
testToExpression( testToExpression(
"f(x)=x", "f(x)=x",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))}", "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
~v="@{f: lambda(x=>internal code)}",
(), (),
) // 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)}))}", "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}",
~v="@{f: lambda(x=>internal code)}",
(), (),
) )
}) })
@ -188,12 +244,12 @@ describe("Peggy to Expression", () => {
describe("module", () => { describe("module", () => {
// testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ()) // testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
// Only.test("stdlibrary", () => { // Only.test("stdlibrary", () => {
// ReducerInterface_StdLib.internalStdLib // ReducerInterface_StdLib.internalStdLib
// ->IEvBindings // ->IEvBindings
// ->InternalExpressionValue.toString // ->InternalExpressionValue.toString
// ->expect // ->expect
// ->toBe("") // ->toBe("")
// }) // })
testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ()) testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ())
}) })
}) })

View File

@ -5,92 +5,92 @@ describe("Peggy Types to Expression", () => {
describe("type of", () => { describe("type of", () => {
testToExpression( testToExpression(
"p: number", "p: number",
"{(:$_typeOf_$ :p #number)}", "{(:$_typeOf_$ :p #number); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {p: #number}}", // ~v="@{_typeReferences_: {p: #number}}",
(), (),
) )
}) })
describe("type alias", () => { describe("type alias", () => {
testToExpression( testToExpression(
"type index=number", "type index=number",
"{(:$_typeAlias_$ #index #number)}", "{(:$_typeAlias_$ #index #number); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeAliases_: {index: #number}}", // ~v="@{_typeAliases_: {index: #number}}",
(), (),
) )
}) })
describe("type or", () => { describe("type or", () => {
testToExpression( testToExpression(
"answer: number|string|distribution", "answer: number|string|distribution",
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution))))}", "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution)))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}", // ~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
(), (),
) )
}) })
describe("type function", () => { describe("type function", () => {
testToExpression( testToExpression(
"f: number=>number=>number", "f: number=>number=>number",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number))))}", "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number)))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}", // ~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
(), (),
) )
testToExpression( testToExpression(
"f: number=>number", "f: number=>number",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number))))}", "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number)))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}", // ~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
(), (),
) )
}) })
describe("high priority contract", () => { describe("high priority contract", () => {
testToExpression( testToExpression(
"answer: number<-min(1)<-max(100)|string", "answer: number<-min(1)<-max(100)|string",
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}", "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string)))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}", // ~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
(), (),
) )
testToExpression( testToExpression(
"answer: number<-memberOf([1,3,5])", "answer: number<-memberOf([1,3,5])",
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5))))}", "{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5)))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}", // ~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(), (),
) )
testToExpression( testToExpression(
"answer: number<-min(1)", "answer: number<-min(1)",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1))}", "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1)); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}", // ~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(), (),
) )
testToExpression( testToExpression(
"answer: number<-max(10)", "answer: number<-max(10)",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10))}", "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10)); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}", // ~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(), (),
) )
testToExpression( testToExpression(
"answer: number<-min(1)<-max(10)", "answer: number<-min(1)<-max(10)",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10))}", "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10)); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}", // ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(), (),
) )
testToExpression( testToExpression(
"answer: number<-max(10)<-min(1)", "answer: number<-max(10)<-min(1)",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1))}", "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1)); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}", // ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(), (),
) )
}) })
describe("low priority contract", () => { describe("low priority contract", () => {
testToExpression( testToExpression(
"answer: number | string $ opaque", "answer: number | string $ opaque",
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}", "{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string))))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}", // ~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
(), (),
) )
}) })
describe("squiggle expressions in type contracts", () => { describe("squiggle expressions in type contracts", () => {
testToExpression( testToExpression(
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))", "odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}", "{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2))); (:$_endOfOuterBlock_$ () ())}",
~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}", // ~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
(), (),
) )
}) })

View File

@ -3,18 +3,23 @@ open Reducer_Peggy_TestHelpers
describe("Peggy void", () => { describe("Peggy void", () => {
//literal //literal
testToExpression("()", "{()}", ~v="()", ()) testToExpression("()", "{(:$_endOfOuterBlock_$ () ())}", ~v="()", ())
testToExpression( testToExpression(
"fn()=1", "fn()=1",
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1}))}", "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () ())}",
~v="@{fn: lambda(_=>internal code)}", // ~v="@{fn: lambda(_=>internal code)}",
(),
)
testToExpression(
"fn()=1; fn()",
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () (:fn ()))}",
~v="1",
(), (),
) )
testToExpression("fn()=1; fn()", "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:fn ())}", ~v="1", ())
testToExpression( testToExpression(
"fn(a)=(); call fn(1)", "fn(a)=(); call fn(1)",
"{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)})}", "{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)}); (:$_endOfOuterBlock_$ () ())}",
~v="@{_: (),fn: lambda(a=>internal code)}", // ~v="@{_: (),fn: lambda(a=>internal code)}",
(), (),
) )
}) })

View File

@ -1,6 +1,7 @@
module ExpressionT = Reducer_Expression_T
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
open Jest open Jest
open Expect open Expect
@ -8,30 +9,26 @@ open Expect
let unwrapRecord = rValue => let unwrapRecord = rValue =>
rValue->Belt.Result.flatMap(value => rValue->Belt.Result.flatMap(value =>
switch value { switch value {
| ExternalExpressionValue.EvRecord(aRecord) => Ok(aRecord) | InternalExpressionValue.IEvRecord(aRecord) => Ok(aRecord)
| _ => ErrorValue.RETodo("TODO: External bindings must be returned")->Error | _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error
} }
) )
let expectParseToBe = (expr: string, answer: string) => let expectParseToBe = (code: string, answer: string) =>
Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer) Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer)
let expectEvalToBe = (expr: string, answer: string) => let expectEvalToBe = (code: string, answer: string) =>
Reducer.evaluate(expr) Expression.BackCompatible.evaluateString(code)
->Reducer_Helpers.rRemoveDefaultsExternal ->Reducer_Helpers.rRemoveDefaultsInternal
->ExternalExpressionValue.toStringResult ->InternalExpressionValue.toStringResult
->expect ->expect
->toBe(answer) ->toBe(answer)
let expectEvalError = (expr: string) => let expectEvalError = (code: string) =>
Reducer.evaluate(expr)->ExternalExpressionValue.toStringResult->expect->toMatch("Error\(") Expression.BackCompatible.evaluateString(code)
->InternalExpressionValue.toStringResult
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
->Reducer_Helpers.rRemoveDefaultsExternal
->ExternalExpressionValue.toStringResult
->expect ->expect
->toBe(answer) ->toMatch("Error\(")
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)) let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
let testDescriptionParseToBe = (desc, expr, answer) => let testDescriptionParseToBe = (desc, expr, answer) =>
@ -40,18 +37,12 @@ let testDescriptionParseToBe = (desc, expr, answer) =>
let testEvalError = expr => test(expr, () => expectEvalError(expr)) let testEvalError = expr => test(expr, () => expectEvalError(expr))
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer)) let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer)) let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
module MySkip = { module MySkip = {
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer)) let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer)) let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
} }
module MyOnly = { module MyOnly = {
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer)) let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer)) let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
} }

View File

@ -1,14 +1,14 @@
open Jest open Jest
open Expect open Expect
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Expression = Reducer_Expression module Expression = Reducer_Expression
// module ExpressionValue = ReducerInterface.ExpressionValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext module ExpressionWithContext = Reducer_ExpressionWithContext
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module Macro = Reducer_Expression_Macro module Macro = Reducer_Expression_Macro
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Expression_T module T = Reducer_Expression_T
module Bindings = Reducer_Bindings
let testMacro_ = ( let testMacro_ = (
tester, tester,
@ -21,8 +21,8 @@ let testMacro_ = (
expr expr
->Macro.expandMacroCall( ->Macro.expandMacroCall(
bindings, bindings,
InternalExpressionValue.defaultEnvironment, ProjectAccessorsT.identityAccessors,
Expression.reduceExpression, Expression.reduceExpressionInProject,
) )
->ExpressionWithContext.toStringResult ->ExpressionWithContext.toStringResult
->expect ->expect
@ -41,8 +41,8 @@ let testMacroEval_ = (
expr expr
->Macro.doMacroCall( ->Macro.doMacroCall(
bindings, bindings,
InternalExpressionValue.defaultEnvironment, ProjectAccessorsT.identityAccessors,
Expression.reduceExpression, Expression.reduceExpressionInProject,
) )
->InternalExpressionValue.toStringResult ->InternalExpressionValue.toStringResult
->expect ->expect

View File

@ -8,7 +8,7 @@ open Jest
open Expect open Expect
let myIevEval = (aTypeSourceCode: string) => let myIevEval = (aTypeSourceCode: string) =>
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpression) TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
let myIevEvalToString = (aTypeSourceCode: string) => let myIevEvalToString = (aTypeSourceCode: string) =>
myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
@ -19,7 +19,7 @@ let myIevTest = (test, aTypeSourceCode, answer) =>
test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer)) test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
let myTypeEval = (aTypeSourceCode: string) => let myTypeEval = (aTypeSourceCode: string) =>
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpression) TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
let myTypeExpectEqual = (aTypeSourceCode, answer) => let myTypeExpectEqual = (aTypeSourceCode, answer) =>

View File

@ -1,8 +1,9 @@
module Bindings = Reducer_Bindings
module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ErrorValue = Reducer_ErrorValue
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Type_T module T = Reducer_Type_T
module TypeChecker = Reducer_Type_TypeChecker module TypeChecker = Reducer_Type_TypeChecker
@ -13,10 +14,10 @@ let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): re
'v, 'v,
ErrorValue.t, ErrorValue.t,
> => { > => {
let reducerFn = Expression.reduceExpression let reducerFn = Expression.reduceExpressionInProject
let rResult = let rResult =
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr => Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment) reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
) )
rResult->Belt.Result.flatMap(result => rResult->Belt.Result.flatMap(result =>
switch result { switch result {

View File

@ -5,6 +5,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
module T = Reducer_Type_T module T = Reducer_Type_T
module TypeChecker = Reducer_Type_TypeChecker module TypeChecker = Reducer_Type_TypeChecker
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
open Jest open Jest
open Expect open Expect
@ -16,10 +17,10 @@ let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
'v, 'v,
ErrorValue.t, ErrorValue.t,
> => { > => {
let reducerFn = Expression.reduceExpression let reducerFn = Expression.reduceExpressionInProject
let rResult = let rResult =
Reducer.parse(sourceCode)->Belt.Result.flatMap(expr => Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment) reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
) )
rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn)) rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
} }

View File

@ -4,8 +4,11 @@ open Expect
module DispatchT = Reducer_Dispatch_T module DispatchT = Reducer_Dispatch_T
module Expression = Reducer_Expression module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module TypeCompile = Reducer_Type_Compile module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module TypeChecker = Reducer_Type_TypeChecker module TypeChecker = Reducer_Type_TypeChecker
module TypeCompile = Reducer_Type_Compile
open ReducerInterface_InternalExpressionValue open ReducerInterface_InternalExpressionValue
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
@ -14,13 +17,14 @@ type errorValue = Reducer_ErrorValue.errorValue
// In dispatchChainPiece, we execute an return the result of execution if there is a type match. // In dispatchChainPiece, we execute an return the result of execution if there is a type match.
// Otherwise we return None so that the call chain can continue. // Otherwise we return None so that the call chain can continue.
// So we want to build a function like // So we want to build a function like
// dispatchChainPiece = (call: functionCall, environment): option<result<internalExpressionValue, errorValue>> // dispatchChainPiece = (call: functionCall, accessors): option<result<internalExpressionValue, errorValue>>
// Use accessors.environment to get the environment finally.
// Now lets make the dispatchChainPiece itself. // Now lets make the dispatchChainPiece itself.
// Note that I am not passing the reducer to the dispatchChainPiece as an argument because it is in the context anyway. // Note that I am not passing the reducer to the dispatchChainPiece as an argument because it is in the context anyway.
// Keep in mind that reducerFn is necessary for map/reduce so dispatchChainPiece should have a reducerFn in context. // Keep in mind that reducerFn is necessary for map/reduce so dispatchChainPiece should have a reducerFn in context.
let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispatchChainPiece => { let makeMyDispatchChainPiece = (reducer: ProjectReducerFnT.t): DispatchT.dispatchChainPiece => {
// Let's have a pure implementations // Let's have a pure implementations
module Implementation = { module Implementation = {
let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b) let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b)
@ -45,15 +49,15 @@ let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispa
// Let's bridge the pure implementation to expression values // Let's bridge the pure implementation to expression values
module Bridge = { module Bridge = {
let stringConcat: DispatchT.genericIEvFunction = (args, _environment) => { let stringConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
let (a, b) = extractStringString(args) let (a, b) = extractStringString(args)
Implementation.stringConcat(a, b)->IEvString->Ok Implementation.stringConcat(a, b)->IEvString->Ok
} }
let arrayConcat: DispatchT.genericIEvFunction = (args, _environment) => { let arrayConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
let (a, b) = extractArrayArray(args) let (a, b) = extractArrayArray(args)
Implementation.arrayConcat(a, b)->IEvArray->Ok Implementation.arrayConcat(a, b)->IEvArray->Ok
} }
let plot: DispatchT.genericIEvFunction = (args, _environment) => { let plot: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
switch args { switch args {
// Just assume that we are doing the business of extracting and converting the deep record // Just assume that we are doing the business of extracting and converting the deep record
| [IEvRecord(_)] => Implementation.plot({"title": "This is a plot"})->IEvString->Ok | [IEvRecord(_)] => Implementation.plot({"title": "This is a plot"})->IEvString->Ok
@ -98,12 +102,12 @@ let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispa
// Exactly the same as the one used in real life // Exactly the same as the one used in real life
let _dispatch = ( let _dispatch = (
call: functionCall, call: functionCall,
environment, accessors: ProjectAccessorsT.t,
reducer: Reducer_Expression_T.reducerFn, reducer: ProjectReducerFnT.t,
chain, chain,
): result<internalExpressionValue, 'e> => { ): result<internalExpressionValue, 'e> => {
let dispatchChainPiece = makeMyDispatchChainPiece(reducer) let dispatchChainPiece = makeMyDispatchChainPiece(reducer)
dispatchChainPiece(call, environment)->E.O2.defaultFn(() => chain(call, environment, reducer)) dispatchChainPiece(call, accessors)->E.O2.defaultFn(() => chain(call, accessors, reducer))
} }
// What is important about this implementation? // What is important about this implementation?
@ -112,12 +116,12 @@ let _dispatch = (
// B) Complicated recursive record types are not a problem. // B) Complicated recursive record types are not a problem.
describe("Type Dispatch", () => { describe("Type Dispatch", () => {
let reducerFn = Expression.reduceExpression let reducerFn = Expression.reduceExpressionInProject
let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn) let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn)
test("stringConcat", () => { test("stringConcat", () => {
let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")]) let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")])
let result = dispatchChainPiece(call, defaultEnvironment) let result = dispatchChainPiece(call, ProjectAccessorsT.identityAccessors)
expect(result)->toEqual(Some(Ok(IEvString("helloworld")))) expect(result)->toEqual(Some(Ok(IEvString("helloworld"))))
}) })
}) })

View File

@ -1,14 +0,0 @@
open Jest
open Reducer_TestHelpers
describe("Eval with Bindings", () => {
testEvalBindingsToBe("x", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(1)")
testEvalBindingsToBe("x+1", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
testParseToBe("y = x+1; y", "Ok({(:$_let_$ :y {(:add :x 1)}); :y})")
testEvalBindingsToBe("y = x+1; y", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
testEvalBindingsToBe(
"y = x+1",
list{("x", ExternalExpressionValue.EvNumber(1.))},
"Ok(@{x: 1,y: 2})",
)
})

View File

@ -2,8 +2,14 @@ open Jest
open Reducer_TestHelpers open Reducer_TestHelpers
describe("Parse function assignment", () => { describe("Parse function assignment", () => {
testParseToBe("f(x)=x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))})") testParseToBe(
testParseToBe("f(x)=2*x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)}))})") "f(x)=x",
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})",
)
testParseToBe(
"f(x)=2*x",
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})",
)
//MathJs does not allow blocks in function definitions //MathJs does not allow blocks in function definitions
}) })

View File

@ -39,33 +39,27 @@ describe("symbol not defined", () => {
}) })
describe("call and bindings", () => { describe("call and bindings", () => {
testEvalToBe("f(x)=x+1", "Ok(@{f: lambda(x=>internal code)})") testEvalToBe("f(x)=x+1; f(0)", "Ok(1)")
testEvalToBe("f(x)=x+1; f(1)", "Ok(2)") testEvalToBe("f(x)=x+1; f(1)", "Ok(2)")
testEvalToBe("f=1;y=2", "Ok(@{f: 1,y: 2})") testEvalToBe("f=1;y=2", "Ok(())")
testEvalToBe("f(x)=x+1; y=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2})") testEvalToBe("f(x)=x+1; y=f(1); y", "Ok(2)")
testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)") testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)")
testEvalToBe("f(x)=x+1; y=f(1); z=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2,z: 2})") testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)")
testEvalToBe( testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)")
"f(x)=x+1; g(x)=f(x)+1",
"Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code)})",
)
testParseToBe( testParseToBe(
"f=99; g(x)=f; g(2)", "f=99; g(x)=f; g(2)",
"Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:g 2)})", "Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:$_endOfOuterBlock_$ () (:g 2))})",
) )
testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)") testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)")
testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)") testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)")
testEvalToBe( testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2); y", "Ok(4)")
"f(x)=x+1; g(x)=f(x)+1; y=g(2)",
"Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code),y: 4})",
)
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)") testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)")
}) })
describe("function tricks", () => { describe("function tricks", () => {
testEvalError("f(x)=f(y)=2; f(2)") //Error because chain assignment is not allowed testEvalError("f(x)=f(y)=2; f(2)") //Error because chain assignment is not allowed
testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)") testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)")
testEvalToBe("y=2;g(x)=inspect(y)+1", "Ok(@{g: lambda(x=>internal code),y: 2})") testEvalToBe("y=2;g(x)=inspect(y)+1;y", "Ok(2)")
MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout? MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout?
MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters
testEvalToBe("myadd(x,y)=x+y; z=myadd; z", "Ok(lambda(x,y=>internal code))") testEvalToBe("myadd(x,y)=x+y; z=myadd; z", "Ok(lambda(x,y=>internal code))")
@ -73,10 +67,7 @@ describe("function tricks", () => {
}) })
describe("lambda in structures", () => { describe("lambda in structures", () => {
testEvalToBe( testEvalToBe("myadd(x,y)=x+y; z=[myadd]", "Ok(())")
"myadd(x,y)=x+y; z=[myadd]",
"Ok(@{myadd: lambda(x,y=>internal code),z: [lambda(x,y=>internal code)]})",
)
testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0]", "Ok(lambda(x,y=>internal code))") testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0]", "Ok(lambda(x,y=>internal code))")
testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0](3,2)", "Ok(5)") testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0](3,2)", "Ok(5)")
testEvalToBe("myaddd(x,y)=x+y; z={x: myaddd}; z", "Ok({x: lambda(x,y=>internal code)})") testEvalToBe("myaddd(x,y)=x+y; z={x: myaddd}; z", "Ok({x: lambda(x,y=>internal code)})")

View File

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

View File

@ -48,7 +48,7 @@ describe("eval", () => {
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)") testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
testEvalError("1; x=1") testEvalError("1; x=1")
testEvalError("1; 1") testEvalError("1; 1")
testEvalToBe("x=1; x=1", "Ok(@{x: 1})") testEvalToBe("x=1; x=1; x", "Ok(1)")
}) })
}) })

View File

@ -118,28 +118,40 @@ describe("eval on distribution functions", () => {
describe("parse on distribution functions", () => { describe("parse on distribution functions", () => {
describe("power", () => { describe("power", () => {
testParse("normal(5,2) ^ normal(5,1)", "Ok({(:pow (:normal 5 2) (:normal 5 1))})") testParse(
testParse("3 ^ normal(5,1)", "Ok({(:pow 3 (:normal 5 1))})") "normal(5,2) ^ normal(5,1)",
testParse("normal(5,2) ^ 3", "Ok({(:pow (:normal 5 2) 3)})") "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})",
)
testParse("3 ^ normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:pow 3 (:normal 5 1)))})")
testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})")
}) })
describe("subtraction", () => { describe("subtraction", () => {
testParse("10 - normal(5,1)", "Ok({(:subtract 10 (:normal 5 1))})") testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})")
testParse("normal(5,1) - 10", "Ok({(:subtract (:normal 5 1) 10)})") testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (: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((:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1))))", "Ok((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))",
// TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})" // TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})"
) )
testParse("normal(5,2) .* normal(5,1)", "Ok({(:dotMultiply (:normal 5 2) (:normal 5 1))})") testParse(
testParse("normal(5,2) ./ normal(5,1)", "Ok({(:dotDivide (:normal 5 2) (:normal 5 1))})") "normal(5,2) .* normal(5,1)",
testParse("normal(5,2) .^ normal(5,1)", "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})") "Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})",
)
testParse(
"normal(5,2) ./ normal(5,1)",
"Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})",
)
testParse(
"normal(5,2) .^ normal(5,1)",
"Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})",
)
}) })
describe("equality", () => { describe("equality", () => {
testParse("5 == normal(5,2)", "Ok({(:equal 5 (:normal 5 2))})") testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})")
}) })
describe("pointwise adding two normals", () => { 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,11 +1,11 @@
open ReducerInterface.ExternalExpressionValue open ReducerInterface.InternalExpressionValue
open Jest open Jest
open Expect open Expect
describe("ExpressionValue", () => { describe("ExpressionValue", () => {
test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1,'a'")) test("argsToString", () => expect([IEvNumber(1.), IEvString("a")]->argsToString)->toBe("1,'a'"))
test("toStringFunctionCall", () => test("toStringFunctionCall", () =>
expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')") expect(("fn", [IEvNumber(1.), IEvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
) )
}) })

View File

@ -0,0 +1,33 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
Only.describe("Parse includes", () => {
let project = Project.createProject()
Project.setSource(
project,
"main",
`
#include 'common'
x=1`,
)
Project.parseIncludes(project, "main")
test("dependencies", () => {
expect(Project.getDependencies(project, "main")) == ["common"]
})
test("dependents", () => {
expect(Project.getDependents(project, "main")) == []
})
test("getIncludes", () => {
let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes {
| Ok(includes) => expect(includes) == ["common"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
}
})
})

View File

@ -0,0 +1,174 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
// test("", () => expect(1)->toBe(1))
let runFetchResult = (project, sourceId) => {
Project.run(project, sourceId)
Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult
}
let runFetchBindings = (project, sourceId) => {
Project.run(project, sourceId)
Project.getBindings(project, sourceId)
->InternalExpressionValue.IEvBindings
->InternalExpressionValue.toString
}
test("setting continuation", () => {
let project = Project.createProject()
let privateProject = project->Project.T.Private.castToInternalProject
let sampleBindings = Bindings.emptyBindings->Bindings.set("test", IEvVoid)
Project.Private.setContinuation(privateProject, "main", sampleBindings)
let answer = Project.Private.getContinuation(privateProject, "main")
expect(answer)->toBe(sampleBindings)
})
test("test result true", () => {
let project = Project.createProject()
Project.setSource(project, "main", "true")
runFetchResult(project, "main")->expect->toBe("Ok(true)")
})
test("test result false", () => {
let project = Project.createProject()
Project.setSource(project, "main", "false")
runFetchResult(project, "main")->expect->toBe("Ok(false)")
})
test("test library", () => {
let project = Project.createProject()
Project.setSource(project, "main", "x=Math.pi; x")
runFetchResult(project, "main")->expect->toBe("Ok(3.141592653589793)")
})
test("test bindings", () => {
let project = Project.createProject()
Project.setSource(project, "variables", "myVariable=666")
runFetchBindings(project, "variables")->expect->toBe("@{myVariable: 666}")
})
describe("project1", () => {
let project = Project.createProject()
Project.setSource(project, "first", "x=1")
Project.setSource(project, "main", "x")
Project.setContinues(project, "main", ["first"])
test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["first", "main"]
})
test("dependents first", () => {
expect(Project.getDependents(project, "first")) == ["main"]
})
test("dependencies first", () => {
expect(Project.getDependencies(project, "first")) == []
})
test("dependents main", () => {
expect(Project.getDependents(project, "main")) == []
})
test("dependencies main", () => {
expect(Project.getDependencies(project, "main")) == ["first"]
})
test("test result", () => {
runFetchResult(project, "main")->expect->toBe("Ok(1)")
})
test("test bindings", () => {
runFetchBindings(project, "main")->expect->toBe("@{x: 1}")
})
})
describe("project2", () => {
let project = Project.createProject()
Project.setContinues(project, "main", ["second"])
Project.setContinues(project, "second", ["first"])
Project.setSource(project, "first", "x=1")
Project.setSource(project, "second", "y=2")
Project.setSource(project, "main", "y")
test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["first", "second", "main"]
})
test("runOrderFor", () => {
expect(Project.getRunOrderFor(project, "first")) == ["first"]
})
test("dependencies first", () => {
expect(Project.getDependencies(project, "first")) == []
})
test("dependents first", () => {
expect(Project.getDependents(project, "first")) == ["second", "main"]
})
test("dependents main", () => {
expect(Project.getDependents(project, "main")) == []
})
test("dependencies main", () => {
expect(Project.getDependencies(project, "main")) == ["first", "second"]
})
test("test result", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)")
})
test("test bindings", () => {
runFetchBindings(project, "main")->expect->toBe("@{x: 1,y: 2}")
})
})
describe("project with include", () => {
let project = Project.createProject()
Project.setContinues(project, "main", ["second"])
Project.setContinues(project, "second", ["first"])
Project.setSource(
project,
"first",
`
#include 'common'
x=1`,
)
Project.parseIncludes(project, "first")
Project.parseIncludes(project, "first") //The only way of setting includes
//Don't forget to parse includes after changing the source
Project.setSource(project, "common", "common=0")
Project.setSource(
project,
"second",
`
#include 'common'
y=2`,
)
Project.parseIncludes(project, "second") //The only way of setting includes
Project.setSource(project, "main", "y")
test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"]
})
test("runOrderFor", () => {
expect(Project.getRunOrderFor(project, "first")) == ["common", "first"]
})
test("dependencies first", () => {
expect(Project.getDependencies(project, "first")) == ["common"]
})
test("dependents first", () => {
expect(Project.getDependents(project, "first")) == ["second", "main"]
})
test("dependents main", () => {
expect(Project.getDependents(project, "main")) == []
})
test("dependencies main", () => {
expect(Project.getDependencies(project, "main")) == ["common", "first", "second"]
})
test("test result", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)")
})
test("test bindings", () => {
runFetchBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}")
})
})

View File

@ -0,0 +1,109 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
describe("ReducerProject Tutorial", () => {
describe("Single source", () => {
/*
Case "Running a single source".
*/
test("run", () => {
/* Let's start with running a single source and getting Result as well as the Bindings
First you need to create a project. A project is a collection of sources.
Project takes care of the dependencies between the sources, correct compilation and run order.
You can run any source in the project. It will be compiled and run if it is not already done else already existing results will be presented.
The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project.
In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source.
*/
let project = Project.createProject()
/* Every source has a name. This is used for debugging, dependencies and error messages. */
Project.setSource(project, "main", "1 + 2")
/* Let's run "main" source. */
project->Project.run("main")
/* Now you have a result for "main" source.
Running one by one is necessary for UI to navigate among the sources and to see the results by source.
And you're free to run any source you want.
You will look at the results of this source and you don't want to run the others if not required.
*/
/* However, you could also run the whole project.
If you have all the sources, you can always run the whole project.
Dependencies and recompiling on demand will be taken care of by the project.
*/
project->Project.runAll
/* Either with run or runAll you executed the project.
You can get the result of a specific source by calling getResult for that source.
You can get the bindings of a specific source by calling getBindings for that source.
If there is any runtime error, getResult will return the error.
Note that getResult returns None if the source has not been run.
Getting None means you have forgotten to run the source.
*/
let result = project->Project.getResult("main")
let bindings = project->Project.getBindings("main")
/* Let's display the result and bindings */
(
result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
/* You've got 3 with empty bindings. */
})
test("run summary", () => {
let project = Project.createProject()
Project.setSource(project, "main", "1 + 2")
Project.runAll(project)
let result = Project.getResult(project, "main")
let bindings = Project.getBindings(project, "main")
/* Now you have external bindings and external result. */
(
result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
})
test("run with an environment", () => {
/* Running the source code like above allows you to set a custom environment */
let project = Project.createProject()
/* Optional. Set your custom environment anytime before running */
Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment)
Project.setSource(project, "main", "1 + 2")
Project.runAll(project)
let result = Project.getResult(project, "main")
let _bindings = Project.getBindings(project, "main")
result->InternalExpressionValue.toStringResult->expect == "Ok(3)"
})
test("shortcut", () => {
/* If you are running single source without includes and you don't need a custom environment, you can use the shortcut. */
/* Examples above was to prepare you for the multi source tutorial. */
let (result, bindings) = Project.evaluate("1+2")
(
result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
})
})
})
//TODO multiple sources
//TODO multiple sources with includes. Introduction to includes
//TODO multiple sources with multi level includes. Cycle detection
//TODO
//TODO: Implement a runOrder consideration - clean results based on run order.
//TODO: runOrder vs setSource/touchSource
//TODO: Advanced details: (below)
//TODO runOrder. includes vs continues. Run order based reexecution
//TODO: dependents and reexecution
//TODO: dependencies and reexecution
//TODO: cleanAllResults clean
//TODO: cleanAll clean

View File

@ -0,0 +1,112 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
describe("ReducerProject Tutorial", () => {
describe("Multi source", () => {
/*
Case "Running multiple sources" */
test("Chaining", () => {
let project = Project.createProject()
/* This time let's add 3 sources and chain them together */
Project.setSource(project, "source1", "x=1")
Project.setSource(project, "source2", "y=2")
/* To run, source2 depends on source1 */
Project.setContinues(project, "source2", ["source1"])
Project.setSource(project, "source3", "z=3")
/* To run, source3 depends on source2 */
Project.setContinues(project, "source3", ["source2"])
/* Now we can run the project */
Project.runAll(project)
/* And let's check the result and bindings of source3 */
let result3 = Project.getResult(project, "source3")
let bindings3 = Project.getBindings(project, "source3")
(
result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
})
test("Depending", () => {
/* Instead of chaining the sources, we could have a dependency tree */
/* The point here is that any source can depend on multiple sources */
let project = Project.createProject()
/* This time source1 and source2 are not depending on anything */
Project.setSource(project, "source1", "x=1")
Project.setSource(project, "source2", "y=2")
Project.setSource(project, "source3", "z=3")
/* To run, source3 depends on source1 and source3 together */
Project.setContinues(project, "source3", ["source1", "source2"])
/* Now we can run the project */
Project.runAll(project)
/* And let's check the result and bindings of source3 */
let result3 = Project.getResult(project, "source3")
let bindings3 = Project.getBindings(project, "source3")
(
result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
})
test("Intro to including", () => {
/* Though it would not be practical for a storybook,
let's write the same project above with includes.
You will see that parsing includes is setting the dependencies the same way as before. */
let project = Project.createProject()
/* This time source1 and source2 are not depending on anything */
Project.setSource(project, "source1", "x=1")
Project.setSource(project, "source2", "y=2")
Project.setSource(
project,
"source3",
`
#include "source1"
#include "source2"
z=3`,
)
/* We need to parse the includes to set the dependencies */
Project.parseIncludes(project, "source3")
/* Now we can run the project */
Project.runAll(project)
/* And let's check the result and bindings of source3
This time you are getting all the variables because we are including the other sources
Behind the scenes parseIncludes is setting the dependencies */
let result3 = Project.getResult(project, "source3")
let bindings3 = Project.getBindings(project, "source3")
(
result3->InternalExpressionValue.toStringResult,
bindings3->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
/*
Doing it like this is too verbose for a storybook
But I hope you have seen the relation of setContinues and parseIncludes */
/*
Dealing with includes needs more.
- There are parse errors
- There are cyclic includes
- And the depended source1 and source2 is not already there in the project
- If you knew the includes before hand there would not be point of the include directive.
More on those on the next section. */
})
})
})

View File

@ -0,0 +1,156 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
describe("ReducerProject Tutorial", () => {
/* Case: Includes
In the previous tutorial we have set the similarity between setContinues and parseIncludes.
Here we will finally proceed to a real life scenario. */
describe("parseIncludes", () => {
/* Here we investigate the details about parseIncludes, before setting up a real life senario in the next section. */
/* Everything happens inside a project, so let's have a project */
let project = Project.createProject()
Project.setSource(
project,
"main",
`
#include "common"
x=1
`,
)
/* We need to parse includes after changing the source */
Project.parseIncludes(project, "main")
test("getDependencies", () => {
/* Parse includes has set the dependencies */
Project.getDependencies(project, "main")->expect == ["common"]
/* If there were no includes than there would be no dependencies */
/* However if there was a syntax error at includes then would be no dependencies also */
/* Therefore looking at dependencies is not the right way to load includes */
/* getDependencies does not distinguish between setContinues or parseIncludes */
})
test("getIncludes", () => {
/* Parse includes has set the includes */
switch Project.getIncludes(project, "main") {
| Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail
}
/* If the includes cannot be parsed then you get a syntax error.
Otherwise you get the includes.
If there is no syntax error then you can load that file and use setSource to add it to the project.
And so on recursively... */
})
test("getDependents", () => {
/* For any reason, you are able to query what other sources
include or depend on the current source.
But you don't need to use this to execute the projects.
It is provided for completeness of information. */
Project.getDependents(project, "main")->expect == []
/* Nothing is depending on or including main */
})
describe("Real Like", () => {
/* Now let's look at recursive and possibly cyclic includes */
/* There is no function provided to load the include files.
Because we have no idea if will it be an ordinary function or will it use promises.
Therefore one has to write a function to load sources recursively and and setSources
while checking for dependencies */
/* Let's make a dummy loader */
let loadSource = (sourceName: string) =>
switch sourceName {
| "source1" => "x=1"
| "source2" => `
#include "source1"
y=2`
| "source3" => `
#include "source2"
z=3`
| _ => `source ${sourceName} not found`->Js.Exn.raiseError
}
/* let's recursively load the sources */
let rec loadIncludesRecursively = (project, sourceName, visited) => {
if Js.Array2.includes(visited, sourceName) {
/* Oh we have already visited this source. There is an include cycle */
"Cyclic include ${sourceName}"->Js.Exn.raiseError
} else {
let newVisited = Js.Array2.copy(visited)
let _ = Js.Array2.push(newVisited, sourceName)
/* Let's parse the includes and dive into them */
Project.parseIncludes(project, sourceName)
let rIncludes = Project.getIncludes(project, sourceName)
switch rIncludes {
/* Maybe there is an include syntax error */
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
| Ok(includes) =>
Belt.Array.forEach(includes, newIncludeName => {
/* We have got one of the new includes.
Let's load it and add it to the project */
let newSource = loadSource(newIncludeName)
Project.setSource(project, newIncludeName, newSource)
/* The new source is loaded and added to the project. */
/* Of course the new source might have includes too. */
/* Let's recursively load them */
loadIncludesRecursively(project, newIncludeName, newVisited)
})
}
}
}
/* As we have a fake source loader and a recursive include handler,
We can not set up a real project */
/* * Here starts our real life project! * */
let project = Project.createProject()
/* main includes source3 which includes source2 which includes source1 */
Project.setSource(
project,
"main",
`
#include "source3"
x+y+z
`,
)
/* Setting source requires parsing and loading the includes recursively */
loadIncludesRecursively(project, "main", []) //No visited yet
/* Let's salt it more. Let's have another source in the project which also has includes */
/* doubleX includes source1 which is eventually included by main as well */
Project.setSource(
project,
"doubleX",
`
#include "source1"
doubleX = x * 2
`,
)
loadIncludesRecursively(project, "doubleX", [])
/* Remember, any time you set a source, you need to load includes recursively */
/* As doubleX is not included by main, it is not loaded recursively.
So we link it to the project as a dependency */
Project.setContinues(project, "main", ["doubleX"])
/* Let's run the project */
Project.runAll(project)
let result = Project.getResult(project, "main")
let bindings = Project.getBindings(project, "main")
/* And see the result and bindings.. */
test("recursive includes", () => {
(
result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}")
/* Everything as expected */
})
})
})
})

View File

@ -0,0 +1,39 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
describe("ReducerProject Tutorial", () => {
/* Let's build a project that depends on values from the UI */
let project = Project.createProject()
Project.setSource(project, "main", "x+y+z")
/* x, y and z is not defined in the project but they has to come from the user */
test("Injecting user values", () => {
/* User has input the values */
let x = 1
let y = 2
let z = 3
/* Then we construct a source code to define those values */
let userCode = `
x = ${x->Js.Int.toString}
y = ${y->Js.Int.toString}
z = ${z->Js.Int.toString}
`
/* We inject the user code into the project */
Project.setSource(project, "userCode", userCode)
/* "main" is depending on the user code */
Project.setContinues(project, "main", ["userCode"])
/* We can now run the project */
Project.runAll(project)
let result = Project.getResult(project, "main")
result->InternalExpressionValue.toStringResult->expect == "Ok(6)"
})
})
/* Note that this is not final version of the project */
/* In the future, for safety, we will provide a way to inject values instead of a source code */
/* But time is limited for now... */

View File

@ -0,0 +1,39 @@
@@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
open Expect
open Expect.Operators
describe("ReducerProject Tutorial", () => {
/* Let's build a project to provide a function. */
/* But we will call that function on an array of user input. */
let project = Project.createProject()
Project.setSource(project, "library", "double(x) = x * 2")
/* userCode is not here yet but its dependency is fixed. So we can set it once and for all */
Project.setContinues(project, "userCode", ["library"])
let userValues = [1, 2, 3, 4, 5]
let userResults = Belt.Array.map(userValues, aUserValue => {
let userCode = `double(${aUserValue->Js.Int.toString})`
/* Put the constructed source in the project */
/* We have already set that it depends on "library" */
Project.setSource(project, "userCode", userCode)
/* Run the project */
Project.runAll(project)
/* Get the result */
Project.getResult(project, "userCode")
/* I have to remind you that the "library" is run only once and for all.
The library is not run for each user value. */
})
test("userResults", () => {
let userResultsAsString = Belt.Array.map(userResults, aResult =>
aResult->InternalExpressionValue.toStringResult
)
userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"]
})
})

View File

@ -2,8 +2,12 @@ open Jest
open Expect open Expect
open Reducer_TestHelpers open Reducer_TestHelpers
let expectEvalToBeOk = (expr: string) => let expectEvalToBeOk = (code: string) =>
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true) Reducer_Expression.BackCompatible.evaluateString(code)
->Reducer_Helpers.rRemoveDefaultsInternal
->E.R.isOk
->expect
->toBe(true)
let registry = FunctionRegistry_Library.registry let registry = FunctionRegistry_Library.registry
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry)) let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
@ -88,8 +92,8 @@ describe("FunctionRegistry Library", () => {
((fn, example)) => { ((fn, example)) => {
let responseType = let responseType =
example example
->Reducer.evaluate ->Reducer_Expression.BackCompatible.evaluateString
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType) ->E.R2.fmap(ReducerInterface_InternalExpressionValue.valueToValueType)
let expectedOutputType = fn.output |> E.O.toExn("") let expectedOutputType = fn.output |> E.O.toExn("")
expect(responseType)->toEqual(Ok(expectedOutputType)) expect(responseType)->toEqual(Ok(expectedOutputType))
}, },

View File

@ -1,3 +1,15 @@
/* Some of the types have moved to ForTS__Types.
Needs to be reimported here if necessary and distribution related
We only need distribution related extras for back compatibility. Umur
Instead of a global function namespace we should use modules under ForTS directly maybe renaming them for ease.
.e.g. Project.run(project)
.e.g. Distribution.makeSampleSetDist
*/
import * as _ from "lodash"; import * as _ from "lodash";
import type { import type {
environment, environment,
@ -44,162 +56,164 @@ export let defaultSamplingInputs: environment = {
xyPointLength: 10000, xyPointLength: 10000,
}; };
export function run( /* Umur: All the functions below are invalid. ForTS_Reducer project is the new way to do this. */
squiggleString: string,
bindings?: externalBindings,
environment?: environment,
imports?: jsImports
): result<squiggleExpression, errorValue> {
let b = bindings ? bindings : defaultBindings;
let i = imports ? imports : defaultImports;
let e = environment ? environment : defaultEnvironment;
let res: result<expressionValue, errorValue> = evaluateUsingOptions(
{ externalBindings: mergeImportsWithBindings(b, i), environment: e },
squiggleString
);
return resultMap(res, (x) => createTsExport(x, e));
}
// Run Partial. A partial is a block of code that doesn't return a value // export function run(
export function runPartial( // squiggleString: string,
squiggleString: string, // bindings?: externalBindings,
bindings?: externalBindings, // environment?: environment,
environment?: environment, // imports?: jsImports
imports?: jsImports // ): result<squiggleExpression, errorValue> {
): result<externalBindings, errorValue> { // let b = bindings ? bindings : defaultBindings;
let b = bindings ? bindings : defaultBindings; // let i = imports ? imports : defaultImports;
let i = imports ? imports : defaultImports; // let e = environment ? environment : defaultEnvironment;
let e = environment ? environment : defaultEnvironment; // let res: result<expressionValue, errorValue> = evaluateUsingOptions(
// { externalBindings: mergeImportsWithBindings(b, i), environment: e },
// squiggleString
// );
// return resultMap(res, (x) => createTsExport(x, e));
// }
return evaluatePartialUsingExternalBindings( // // Run Partial. A partial is a block of code that doesn't return a value
squiggleString, // export function runPartial(
mergeImportsWithBindings(b, i), // squiggleString: string,
e // bindings?: externalBindings,
); // environment?: environment,
} // imports?: jsImports
// ): result<externalBindings, errorValue> {
// let b = bindings ? bindings : defaultBindings;
// let i = imports ? imports : defaultImports;
// let e = environment ? environment : defaultEnvironment;
export function runForeign( // return evaluatePartialUsingExternalBindings(
fn: lambdaValue, // squiggleString,
args: jsValue[], // mergeImportsWithBindings(b, i),
environment?: environment // e
): result<squiggleExpression, errorValue> { // );
let e = environment ? environment : defaultEnvironment; // }
let res: result<expressionValue, errorValue> = foreignFunctionInterface(
fn,
args.map(jsValueToExpressionValue),
e
);
return resultMap(res, (x) => createTsExport(x, e));
}
function mergeImportsWithBindings( // export function runForeign(
bindings: externalBindings, // fn: lambdaValue,
imports: jsImports // args: jsValue[],
): externalBindings { // environment?: environment
let transformedImports = Object.fromEntries( // ): result<squiggleExpression, errorValue> {
Object.entries(imports).map(([key, value]) => [ // let e = environment ? environment : defaultEnvironment;
"$" + key, // let res: result<expressionValue, errorValue> = foreignFunctionInterface(
jsValueToBinding(value), // fn,
]) // args.map(jsValueToExpressionValue),
); // e
return _.merge(bindings, transformedImports); // );
} // return resultMap(res, (x) => createTsExport(x, e));
// }
type jsImports = { [key: string]: jsValue }; // function mergeImportsWithBindings(
// bindings: externalBindings,
// imports: jsImports
// ): externalBindings {
// let transformedImports = Object.fromEntries(
// Object.entries(imports).map(([key, value]) => [
// "$" + key,
// jsValueToBinding(value),
// ])
// );
// return _.merge(bindings, transformedImports);
// }
export let defaultImports: jsImports = {}; // type jsImports = { [key: string]: jsValue };
export let defaultBindings: externalBindings = {};
export function mergeBindings( // export let defaultImports: jsImports = {};
allBindings: externalBindings[] // export let defaultBindings: externalBindings = {};
): externalBindings {
return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
}
function createTsExport( // export function mergeBindings(
x: expressionValue, // allBindings: externalBindings[]
environment: environment // ): externalBindings {
): squiggleExpression { // return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
switch (x) { // }
case "EvVoid":
return tag("void", ""); // function createTsExport(
default: { // x: expressionValue,
switch (x.tag) { // environment: environment
case "EvArray": // ): squiggleExpression {
// genType doesn't convert anything more than 2 layers down into {tag: x, value: x} // switch (x) {
// format, leaving it as the raw values. This converts the raw values // case "EvVoid":
// directly into typescript values. // return tag("void", "");
// // default: {
// The casting here is because genType is about the types of the returned // switch (x.tag) {
// values, claiming they are fully recursive when that's not actually the // case "EvArray":
// case // // genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
return tag( // // format, leaving it as the raw values. This converts the raw values
"array", // // directly into typescript values.
x.value.map( // //
(arrayItem): squiggleExpression => // // The casting here is because genType is about the types of the returned
convertRawToTypescript( // // values, claiming they are fully recursive when that's not actually the
arrayItem as unknown as rescriptExport, // // case
environment // return tag(
) // "array",
) // x.value.map(
); // (arrayItem): squiggleExpression =>
case "EvArrayString": // convertRawToTypescript(
return tag("arraystring", x.value); // arrayItem as unknown as rescriptExport,
case "EvBool": // environment
return tag("boolean", x.value); // )
case "EvCall": // )
return tag("call", x.value); // );
case "EvLambda": // case "EvArrayString":
return tag("lambda", x.value); // return tag("arraystring", x.value);
case "EvDistribution": // case "EvBool":
return tag("distribution", new Distribution(x.value, environment)); // return tag("boolean", x.value);
case "EvNumber": // case "EvCall":
return tag("number", x.value); // return tag("call", x.value);
case "EvRecord": // case "EvLambda":
// genType doesn't support records, so we have to do the raw conversion ourself // return tag("lambda", x.value);
let result: tagged<"record", { [key: string]: squiggleExpression }> = // case "EvDistribution":
tag( // return tag("distribution", new Distribution(x.value, environment));
"record", // case "EvNumber":
_.mapValues(x.value, (x: unknown) => // return tag("number", x.value);
convertRawToTypescript(x as rescriptExport, environment) // case "EvRecord":
) // // genType doesn't support records, so we have to do the raw conversion ourself
); // let result: tagged<"record", { [key: string]: squiggleExpression }> =
return result; // tag(
case "EvString": // "record",
return tag("string", x.value); // _.mapValues(x.value, (x: unknown) =>
case "EvSymbol": // convertRawToTypescript(x as rescriptExport, environment)
return tag("symbol", x.value); // )
case "EvDate": // );
return tag("date", x.value); // return result;
case "EvTimeDuration": // case "EvString":
return tag("timeDuration", x.value); // return tag("string", x.value);
case "EvDeclaration": // case "EvSymbol":
return tag("lambdaDeclaration", x.value); // return tag("symbol", x.value);
case "EvTypeIdentifier": // case "EvDate":
return tag("typeIdentifier", x.value); // return tag("date", x.value);
case "EvType": // case "EvTimeDuration":
let typeResult: tagged< // return tag("timeDuration", x.value);
"type", // case "EvDeclaration":
{ [key: string]: squiggleExpression } // return tag("lambdaDeclaration", x.value);
> = tag( // case "EvTypeIdentifier":
"type", // return tag("typeIdentifier", x.value);
_.mapValues(x.value, (x: unknown) => // case "EvType":
convertRawToTypescript(x as rescriptExport, environment) // let typeResult: tagged<
) // "type",
); // { [key: string]: squiggleExpression }
return typeResult; // > = tag(
case "EvModule": // "type",
let moduleResult: tagged< // _.mapValues(x.value, (x: unknown) =>
"module", // convertRawToTypescript(x as rescriptExport, environment)
{ [key: string]: squiggleExpression } // )
> = tag( // );
"module", // return typeResult;
_.mapValues(x.value, (x: unknown) => // case "EvModule":
convertRawToTypescript(x as rescriptExport, environment) // let moduleResult: tagged<
) // "module",
); // { [key: string]: squiggleExpression }
return moduleResult; // > = tag(
} // "module",
} // _.mapValues(x.value, (x: unknown) =>
} // convertRawToTypescript(x as rescriptExport, environment)
} // )
// );
// return moduleResult;
// }
// }
// }
// }

View File

@ -1,269 +1,279 @@
import * as _ from "lodash"; /**
import type { Umur: Delete this file! There is nothing left to see here.
expressionValue, **/
mixedShape,
sampleSetDist, // import * as _ from "lodash";
genericDist, // import type {
environment, // // expressionValue,
symbolicDist, // mixedShape,
discreteShape, // sampleSetDist,
continuousShape, // genericDist,
lambdaValue, // // environment,
lambdaDeclaration, // symbolicDist,
declarationArg, // discreteShape,
} from "../rescript/TypescriptInterface.gen"; // continuousShape,
import { Distribution } from "./distribution"; // // lambdaValue,
import { tagged, tag } from "./types"; // // lambdaDeclaration,
// // declarationArg,
// } from "../rescript/TypescriptInterface.gen";
// import { Distribution } from "./distribution";
// import { tagged, tag } from "./types";
// This file is here to compensate for genType not fully recursively converting types // This file is here to compensate for genType not fully recursively converting types
// Raw rescript types. // Raw rescript types.
export type rescriptExport = // Umur: Rescript expression values are opaque!
| 0 // EvVoid // export type rescriptExport =
| { // | 0 // EvVoid
TAG: 0; // EvArray // | {
_0: rescriptExport[]; // TAG: 0; // EvArray
} // _0: rescriptExport[];
| { // }
TAG: 1; // EvString // | {
_0: string[]; // TAG: 1; // EvString
} // _0: string[];
| { // }
TAG: 2; // EvBool // | {
_0: boolean; // TAG: 2; // EvBool
} // _0: boolean;
| { // }
TAG: 3; // EvCall // | {
_0: string; // TAG: 3; // EvCall
} // _0: string;
| { // }
TAG: 4; // EvDistribution // | {
_0: rescriptDist; // TAG: 4; // EvDistribution
} // _0: rescriptDist;
| { // }
TAG: 5; // EvLambda // | {
_0: lambdaValue; // TAG: 5; // EvLambda
} // _0: lambdaValue;
| { // }
TAG: 6; // EvNumber // | {
_0: number; // TAG: 6; // EvNumber
} // _0: number;
| { // }
TAG: 7; // EvRecord // | {
_0: { [key: string]: rescriptExport }; // TAG: 7; // EvRecord
} // _0: { [key: string]: rescriptExport };
| { // }
TAG: 8; // EvString // | {
_0: string; // TAG: 8; // EvString
} // _0: string;
| { // }
TAG: 9; // EvSymbol // | {
_0: string; // TAG: 9; // EvSymbol
} // _0: string;
| { // }
TAG: 10; // EvDate // | {
_0: Date; // TAG: 10; // EvDate
} // _0: Date;
| { // }
TAG: 11; // EvTimeDuration // | {
_0: number; // TAG: 11; // EvTimeDuration
} // _0: number;
| { // }
TAG: 12; // EvDeclaration // | {
_0: rescriptLambdaDeclaration; // TAG: 12; // EvDeclaration
} // _0: rescriptLambdaDeclaration;
| { // }
TAG: 13; // EvTypeIdentifier // | {
_0: string; // TAG: 13; // EvTypeIdentifier
} // _0: string;
| { // }
TAG: 14; // EvModule // | {
_0: { [key: string]: rescriptExport }; // TAG: 14; // EvModule
}; // _0: { [key: string]: rescriptExport };
// };
type rescriptDist = // Umur: opaque type
| { TAG: 0; _0: rescriptPointSetDist } // type rescriptDist =
| { TAG: 1; _0: sampleSetDist } // | { TAG: 0; _0: rescriptPointSetDist }
| { TAG: 2; _0: symbolicDist }; // | { TAG: 1; _0: sampleSetDist }
// | { TAG: 2; _0: symbolicDist };
type rescriptPointSetDist = // Umur: opaque type, no conversion
| { // type rescriptPointSetDist =
TAG: 0; // Mixed // | {
_0: mixedShape; // TAG: 0; // Mixed
} // _0: mixedShape;
| { // }
TAG: 1; // Discrete // | {
_0: discreteShape; // TAG: 1; // Discrete
} // _0: discreteShape;
| { // }
TAG: 2; // ContinuousShape // | {
_0: continuousShape; // TAG: 2; // ContinuousShape
}; // _0: continuousShape;
// };
type rescriptLambdaDeclaration = { // type rescriptLambdaDeclaration = {
readonly fn: lambdaValue; // readonly fn: lambdaValue;
readonly args: rescriptDeclarationArg[]; // readonly args: rescriptDeclarationArg[];
}; // };
type rescriptDeclarationArg = // type rescriptDeclarationArg =
| { // | {
TAG: 0; // Float // TAG: 0; // Float
min: number; // min: number;
max: number; // max: number;
} // }
| { // | {
TAG: 1; // Date // TAG: 1; // Date
min: Date; // min: Date;
max: Date; // max: Date;
}; // };
export type squiggleExpression = // Umur: Squiggle expressions are opaque!
| tagged<"symbol", string> // export type squiggleExpression =
| tagged<"string", string> // | tagged<"symbol", string>
| tagged<"call", string> // | tagged<"string", string>
| tagged<"lambda", lambdaValue> // | tagged<"call", string>
| tagged<"array", squiggleExpression[]> // | tagged<"lambda", lambdaValue>
| tagged<"arraystring", string[]> // | tagged<"array", squiggleExpression[]>
| tagged<"boolean", boolean> // | tagged<"arraystring", string[]>
| tagged<"distribution", Distribution> // | tagged<"boolean", boolean>
| tagged<"number", number> // | tagged<"distribution", Distribution>
| tagged<"date", Date> // | tagged<"number", number>
| tagged<"timeDuration", number> // | tagged<"date", Date>
| tagged<"lambdaDeclaration", lambdaDeclaration> // | tagged<"timeDuration", number>
| tagged<"record", { [key: string]: squiggleExpression }> // | tagged<"lambdaDeclaration", lambdaDeclaration>
| tagged<"type", { [key: string]: squiggleExpression }> // | tagged<"record", { [key: string]: squiggleExpression }>
| tagged<"typeIdentifier", string> // | tagged<"type", { [key: string]: squiggleExpression }>
| tagged<"module", { [key: string]: squiggleExpression }> // | tagged<"typeIdentifier", string>
| tagged<"void", string>; // | tagged<"module", { [key: string]: squiggleExpression }>
// | tagged<"void", string>;
export { lambdaValue }; // export { lambdaValue };
export function convertRawToTypescript( // Umur: Opaque type no conversion!
result: rescriptExport, // export function convertRawToTypescript(
environment: environment // result: rescriptExport,
): squiggleExpression { // environment: environment
if (typeof result === "number") { // ): squiggleExpression {
// EvVoid // if (typeof result === "number") {
return tag("void", ""); // // EvVoid
} // return tag("void", "");
switch (result.TAG) { // }
case 0: // EvArray // switch (result.TAG) {
return tag( // case 0: // EvArray
"array", // return tag(
result._0.map((x) => convertRawToTypescript(x, environment)) // "array",
); // result._0.map((x) => convertRawToTypescript(x, environment))
case 1: // EvArrayString // );
return tag("arraystring", result._0); // case 1: // EvArrayString
case 2: // EvBool // return tag("arraystring", result._0);
return tag("boolean", result._0); // case 2: // EvBool
case 3: // EvCall // return tag("boolean", result._0);
return tag("call", result._0); // case 3: // EvCall
case 4: // EvDistribution // return tag("call", result._0);
return tag( // case 4: // EvDistribution
"distribution", // return tag(
new Distribution( // "distribution",
convertRawDistributionToGenericDist(result._0), // new Distribution(
environment // convertRawDistributionToGenericDist(result._0),
) // environment
); // )
case 5: // EvDistribution // );
return tag("lambda", result._0); // case 5: // EvDistribution
case 6: // EvNumber // return tag("lambda", result._0);
return tag("number", result._0); // case 6: // EvNumber
case 7: // EvRecord // return tag("number", result._0);
return tag( // case 7: // EvRecord
"record", // return tag(
_.mapValues(result._0, (x) => convertRawToTypescript(x, environment)) // "record",
); // _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
case 8: // EvString // );
return tag("string", result._0); // case 8: // EvString
case 9: // EvSymbol // return tag("string", result._0);
return tag("symbol", result._0); // case 9: // EvSymbol
case 10: // EvDate // return tag("symbol", result._0);
return tag("date", result._0); // case 10: // EvDate
case 11: // EvTimeDuration // return tag("date", result._0);
return tag("number", result._0); // case 11: // EvTimeDuration
case 12: // EvDeclaration // return tag("number", result._0);
return tag("lambdaDeclaration", { // case 12: // EvDeclaration
fn: result._0.fn, // return tag("lambdaDeclaration", {
args: result._0.args.map(convertDeclaration), // fn: result._0.fn,
}); // args: result._0.args.map(convertDeclaration),
case 13: // EvSymbol // });
return tag("typeIdentifier", result._0); // case 13: // EvSymbol
case 14: // EvModule // return tag("typeIdentifier", result._0);
return tag( // case 14: // EvModule
"module", // return tag(
_.mapValues(result._0, (x) => convertRawToTypescript(x, environment)) // "module",
); // _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
} // );
} // }
// }
function convertDeclaration( // function convertDeclaration(
declarationArg: rescriptDeclarationArg // declarationArg: rescriptDeclarationArg
): declarationArg { // ): declarationArg {
switch (declarationArg.TAG) { // switch (declarationArg.TAG) {
case 0: // Float // case 0: // Float
return tag("Float", { min: declarationArg.min, max: declarationArg.max }); // return tag("Float", { min: declarationArg.min, max: declarationArg.max });
case 1: // Date // case 1: // Date
return tag("Date", { min: declarationArg.min, max: declarationArg.max }); // return tag("Date", { min: declarationArg.min, max: declarationArg.max });
} // }
} // }
function convertRawDistributionToGenericDist( // Umur: opaque type no conversion!
result: rescriptDist // function convertRawDistributionToGenericDist(
): genericDist { // result: rescriptDist
switch (result.TAG) { // ): genericDist {
case 0: // Point Set Dist // switch (result.TAG) {
switch (result._0.TAG) { // case 0: // Point Set Dist
case 0: // Mixed // switch (result._0.TAG) {
return tag("PointSet", tag("Mixed", result._0._0)); // case 0: // Mixed
case 1: // Discrete // return tag("PointSet", tag("Mixed", result._0._0));
return tag("PointSet", tag("Discrete", result._0._0)); // case 1: // Discrete
case 2: // Continuous // return tag("PointSet", tag("Discrete", result._0._0));
return tag("PointSet", tag("Continuous", result._0._0)); // case 2: // Continuous
} // return tag("PointSet", tag("Continuous", result._0._0));
case 1: // Sample Set Dist // }
return tag("SampleSet", result._0); // case 1: // Sample Set Dist
case 2: // Symbolic Dist // return tag("SampleSet", result._0);
return tag("Symbolic", result._0); // case 2: // Symbolic Dist
} // return tag("Symbolic", result._0);
} // }
// }
export type jsValue = // export type jsValue =
| string // | string
| number // | number
| jsValue[] // | jsValue[]
| { [key: string]: jsValue } // | { [key: string]: jsValue }
| boolean; // | boolean;
export function jsValueToBinding(value: jsValue): rescriptExport { // export function jsValueToBinding(value: jsValue): rescriptExport {
if (typeof value === "boolean") { // if (typeof value === "boolean") {
return { TAG: 2, _0: value as boolean }; // return { TAG: 2, _0: value as boolean };
} else if (typeof value === "string") { // } else if (typeof value === "string") {
return { TAG: 8, _0: value as string }; // return { TAG: 8, _0: value as string };
} else if (typeof value === "number") { // } else if (typeof value === "number") {
return { TAG: 6, _0: value as number }; // return { TAG: 6, _0: value as number };
} else if (Array.isArray(value)) { // } else if (Array.isArray(value)) {
return { TAG: 0, _0: value.map(jsValueToBinding) }; // return { TAG: 0, _0: value.map(jsValueToBinding) };
} else { // } else {
// Record // // Record
return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) }; // return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
} // }
} // }
export function jsValueToExpressionValue(value: jsValue): expressionValue { // export function jsValueToExpressionValue(value: jsValue): expressionValue {
if (typeof value === "boolean") { // if (typeof value === "boolean") {
return { tag: "EvBool", value: value as boolean }; // return { tag: "EvBool", value: value as boolean };
} else if (typeof value === "string") { // } else if (typeof value === "string") {
return { tag: "EvString", value: value as string }; // return { tag: "EvString", value: value as string };
} else if (typeof value === "number") { // } else if (typeof value === "number") {
return { tag: "EvNumber", value: value as number }; // return { tag: "EvNumber", value: value as number };
} else if (Array.isArray(value)) { // } else if (Array.isArray(value)) {
return { tag: "EvArray", value: value.map(jsValueToExpressionValue) }; // return { tag: "EvArray", value: value.map(jsValueToExpressionValue) };
} else { // } else {
// Record // // Record
return { // return {
tag: "EvRecord", // tag: "EvRecord",
value: _.mapValues(value, jsValueToExpressionValue), // value: _.mapValues(value, jsValueToExpressionValue),
}; // };
} // }
} // }

View File

@ -1,4 +1,5 @@
@genType //FIXME accessor methods or not opaque?
@genType.opaque
type genericDist = type genericDist =
| PointSet(PointSetTypes.pointSetDist) | PointSet(PointSetTypes.pointSetDist)
| SampleSet(SampleSetDist.t) | SampleSet(SampleSetDist.t)
@ -6,7 +7,7 @@ type genericDist =
type asAlgebraicCombinationStrategy = AsDefault | AsSymbolic | AsMonteCarlo | AsConvolution type asAlgebraicCombinationStrategy = AsDefault | AsSymbolic | AsMonteCarlo | AsConvolution
@genType @genType.opaque
type error = type error =
| NotYetImplemented | NotYetImplemented
| Unreachable | Unreachable
@ -27,7 +28,6 @@ module Error = {
let fromString = (s: string): t => OtherError(s) let fromString = (s: string): t => OtherError(s)
@genType
let toString = (err: error): string => let toString = (err: error): string =>
switch err { switch err {
| NotYetImplemented => "Function not yet implemented" | NotYetImplemented => "Function not yet implemented"

View File

@ -1,4 +1,5 @@
//TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res. //TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res.
type t = DistributionTypes.genericDist type t = DistributionTypes.genericDist
type error = DistributionTypes.error type error = DistributionTypes.error
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error> type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>

View File

@ -47,7 +47,7 @@ type pointSetDistMonad<'a, 'b, 'c> =
| Discrete('b) | Discrete('b)
| Continuous('c) | Continuous('c)
@genType @genType.opaque
type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape> type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape>
module ShapeMonad = { module ShapeMonad = {

View File

@ -0,0 +1,57 @@
// Genetic Distribution happens to be abstract distribution
@genType type distribution = DistributionTypes.genericDist
@genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution
@genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution
@genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution
type environment = ForTS_Distribution_Environment.environment //use
@genType
let defaultEnvironment: environment = DistributionOperation.defaultEnv
@module("ForTS_Distribution_tag") @scope("distributionTag")
external dtPointSet_: int = "DtPointSet"
@module("ForTS_Distribution_tag") @scope("distributionTag")
external dtSampleSet_: int = "DtSampleSet"
@module("ForTS_Distribution_tag") @scope("distributionTag")
external dtSymbolic_: int = "DtSymbolic"
@genType.import("./ForTS_Distribution_tag")
type distributionTag
external castEnum: int => distributionTag = "%identity"
// type genericDist =
// | PointSet(PointSetTypes.pointSetDist)
// | SampleSet(SampleSetDist.t)
// | Symbolic(SymbolicDistTypes.symbolicDist)
@genType
let getTag = (variant: distribution): distributionTag =>
switch variant {
| PointSet(_) => dtPointSet_->castEnum
| SampleSet(_) => dtSampleSet_->castEnum
| Symbolic(_) => dtSymbolic_->castEnum
}
@genType
let getPointSet = (variant: distribution): option<pointSetDistribution> =>
switch variant {
| PointSet(dist) => dist->Some
| _ => None
}
@genType
let getSampleSet = (variant: distribution): option<sampleSetDistribution> =>
switch variant {
| SampleSet(dist) => dist->Some
| _ => None
}
@genType
let getSymbolic = (variant: distribution): option<symbolicDistribution> =>
switch variant {
| Symbolic(dist) => dist->Some
| _ => None
}

View File

@ -0,0 +1 @@
@genType type environment = GenericDist.env //re-export

View File

@ -0,0 +1,4 @@
@genType type distributionError = DistributionTypes.error
@genType
let toString = (e: distributionError) => DistributionTypes.Error.toString(e)

View File

@ -0,0 +1,44 @@
@genType type pointSetDistribution = PointSetTypes.pointSetDist
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstMixed_: int = "PstMixed"
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstDiscrete_: int = "PstDiscrete"
@module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstContinuous_: int = "PstContinuous"
@genType.import("./ForTS_Distribution_PointSetDistribution_tag")
type pointSetDistributionTag
external castEnum: int => pointSetDistributionTag = "%identity"
@genType
let getTag = (variant: pointSetDistribution): pointSetDistributionTag =>
switch variant {
| Mixed(_) => pstMixed_->castEnum
| Discrete(_) => pstDiscrete_->castEnum
| Continuous(_) => pstContinuous_->castEnum
}
@genType
let getMixed = (variant: pointSetDistribution): 'd =>
switch variant {
| Mixed(mixed) => mixed->Some
| _ => None
}
@genType
let getDiscrete = (variant: pointSetDistribution): 'd =>
switch variant {
| Discrete(discrete) => discrete->Some
| _ => None
}
@genType
let getContinues = (variant: pointSetDistribution): 'd =>
switch variant {
| Continuous(continuous) => continuous->Some
| _ => None
}

View File

@ -0,0 +1,15 @@
"use strict";
exports.__esModule = true;
exports.pointSetDistributionTag = void 0;
var pointSetDistributionTag;
(function (pointSetDistributionTag) {
pointSetDistributionTag[(pointSetDistributionTag["PstMixed"] = 0)] =
"PstMixed";
pointSetDistributionTag[(pointSetDistributionTag["PstDiscrete"] = 1)] =
"PstDiscrete";
pointSetDistributionTag[(pointSetDistributionTag["PstContinuous"] = 2)] =
"PstContinuous";
})(
(pointSetDistributionTag =
exports.pointSetDistributionTag || (exports.pointSetDistributionTag = {}))
);

View File

@ -0,0 +1,5 @@
export enum pointSetDistributionTag {
PstMixed,
PstDiscrete,
PstContinuous,
}

View File

@ -0,0 +1 @@
@genType type sampleSetDistribution = SampleSetDist.t

View File

@ -0,0 +1 @@
@genType type symbolicDistribution = SymbolicDistTypes.symbolicDist

View File

@ -0,0 +1,5 @@
export enum distributionTag {
DtPointSet,
DtSampleSet,
DtSymbolic,
}

View File

@ -0,0 +1,235 @@
@genType type reducerProject = ReducerProject_T.t //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //use
type environment = ForTS_Distribution_Environment.environment //use
module T = ReducerProject_T
module Private = ReducerProject.Private
/*
PUBLIC FUNCTIONS
*/
/*
A project links and runs sources that continue or include each other.
Creates a new project to hold the sources, executables, bindings, and other data.
The new project runs the sources according to their topological sorting because of the includes and continues.
Any source can include or continue other sources. "Therefore, the project is a graph data structure."
The difference between including and continuing is that includes are stated inside the source code while continues are stated in the project.
To run a group of source codes and get results/bindings, the necessary methods are
- setSource
- setContinues
- parseIncludes
- run or runAll
- getExternalBindings
- getExternalResult
A project has a public field tag with a constant value "reducerProject"
project = {tag: "reducerProject"}
*/
@genType
let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject
/*
Answer all the source ids of all the sources in the project.
*/
@genType
let getSourceIds = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getSourceIds
/*
Sets the source for a given source Id.
*/
@genType
let setSource = (project: reducerProject, sourceId: string, value: string): unit =>
project->T.Private.castToInternalProject->Private.setSource(sourceId, value)
/*
Gets the source for a given source id.
*/
@genType
let getSource = (project: reducerProject, sourceId: string): option<string> =>
project->T.Private.castToInternalProject->Private.getSource(sourceId)
/*
Touches the source for a given source id. This and dependent, sources are set to be re-evaluated.
*/
@genType
let touchSource = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.touchSource(sourceId)
/*
Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again.
Normally, you would never need the compilation artifacts again as the results with the same sources would never change. However, they are needed in case of any debugging reruns
*/
@genType
let clean = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.clean(sourceId)
/*
Cleans all the compilation artifacts in all of the project
*/
@genType
let cleanAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAll
/*
Cleans results. Compilation stays untouched to be able to re-run the source.
You would not do this if you were not trying to debug the source code.
*/
@genType
let cleanResults = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.cleanResults(sourceId)
/*
Cleans all results. Compilations remains untouched to rerun the source.
*/
@genType
let cleanAllResults = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAllResults
/*
To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned.
*/
@genType
let getIncludes = (project: reducerProject, sourceId: string): result<
array<string>,
reducerErrorValue,
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId)
/*
Answers the source codes after which this source code is continuing
*/
@genType
let getContinues = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getContinues(sourceId)
/*
"continues" acts like hidden includes in the source.
It is used to define a continuation that is not visible in the source code.
You can chain source codes on the web interface for example
*/
@genType
let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit =>
project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues)
/*
This source depends on the array of sources returned.
*/
@genType
let getDependencies = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependencies(sourceId)
/*
The sources returned are dependent on this
*/
@genType
let getDependents = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependents(sourceId)
/*
Get the run order for the sources in the project.
*/
@genType
let getRunOrder = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getRunOrder
/*
Get the run order to get the results of this specific source
*/
@genType
let getRunOrderFor = (project: reducerProject, sourceId: string) =>
project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId)
/*
Parse includes so that you can load them before running.
Load includes by calling getIncludes which returns the includes that have been parsed.
It is your responsibility to load the includes before running.
*/
@genType
let parseIncludes = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.parseIncludes(sourceId)
/*
Parse the source code if it is not done already.
Use getRawParse to get the parse tree.
You would need this function if you want to see the parse tree without running the source code.
*/
@genType
let rawParse = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.rawParse(sourceId)
/*
Runs a specific source code if it is not done already. The code is parsed if it is not already done. It runs the dependencies if it is not already done.
*/
@genType
let run = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.run(sourceId)
/*
Runs all of the sources in a project. Their results and bindings will be available
*/
@genType
let runAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.runAll
/*
Get the bindings after running this source file or the project
*/
@genType
let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Module =>
project->T.Private.castToInternalProject->Private.getBindings(sourceId)
/*
Get the result after running this source file or the project
*/
@genType
let getResult = (project: reducerProject, sourceId: string): result<
squiggleValue,
reducerErrorValue,
> => project->T.Private.castToInternalProject->Private.getResult(sourceId)
/*
This is a convenience function to get the result of a single source without creating a project.
However, without a project, you cannot handle include directives.
The source has to be include free
*/
@genType
let evaluate = (sourceCode: string): (
result<squiggleValue, reducerErrorValue>,
squiggleValue_Module,
) => Private.evaluate(sourceCode)
@genType
let setEnvironment = (project: reducerProject, environment: environment): unit =>
project->T.Private.castToInternalProject->Private.setEnvironment(environment)
/*
Foreign function interface is intentionally demolished.
There is another way to do that: Umur.
Also there is no more conversion from javascript to squiggle values currently.
If the conversion to the new project is too difficult, I can add it later.
*/
// let foreignFunctionInterface = (
// lambdaValue: squiggleValue_Lambda,
// argArray: array<squiggleValue>,
// environment: environment,
// ): result<squiggleValue, reducerErrorValue> => {
// let accessors = ReducerProject_ProjectAccessors_T.identityAccessorsWithEnvironment(environment)
// Reducer_Expression_Lambda.foreignFunctionInterface(
// lambdaValue,
// argArray,
// accessors,
// Reducer_Expression.reduceExpressionInProject,
// )
// }

View File

@ -0,0 +1,12 @@
@genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias
@genType type syntaxErrorLocation = Reducer_ErrorValue.syntaxErrorLocation //alias
@genType
let toString = (e: reducerErrorValue): string => Reducer_ErrorValue.errorToString(e)
@genType
let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> =>
switch e {
| RESyntaxError(_, optionalLocation) => optionalLocation
| _ => None
}

View File

@ -0,0 +1,9 @@
export type result<a, b> =
| {
tag: "Ok";
value: a;
}
| {
tag: "Error";
value: b;
};

View File

@ -0,0 +1,213 @@
@genType type squiggleValue = ReducerInterface_InternalExpressionValue.t //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
@genType type squiggleValue_Array = ReducerInterface_InternalExpressionValue.squiggleArray //re-export recursive type
@genType type squiggleValue_Module = ReducerInterface_InternalExpressionValue.nameSpace //re-export recursive type
@genType type squiggleValue_Record = ReducerInterface_InternalExpressionValue.map //re-export recursive type
@genType type squiggleValue_Type = ReducerInterface_InternalExpressionValue.map //re-export recursive type
type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use
type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use
type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use
// Return values are kept as they are if they are JavaScript types.
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArray_: string = "SvtArray"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArrayString_: string = "SvtArrayString"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtBool_: string = "SvtBool"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtCall_: string = "SvtCall"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDate_: string = "SvtDate"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDeclaration_: string = "SvtDeclaration"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDistribution_: string = "SvtDistribution"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtLambda_: string = "SvtLambda"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtModule_: string = "SvtModule"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtNumber_: string = "SvtNumber"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtRecord_: string = "SvtRecord"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtString_: string = "SvtString"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtSymbol_: string = "SvtSymbol"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTimeDuration_: string = "SvtTimeDuration"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtType_: string = "SvtType"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTypeIdentifier_: string = "SvtUndefined"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtVoid_: string = "SvtVoid"
@genType.import("./ForTS_SquiggleValue_tag")
type squiggleValueTag
external castEnum: string => squiggleValueTag = "%identity"
@genType
let getTag = (variant: squiggleValue): squiggleValueTag =>
switch variant {
| IEvArray(_) => svtArray_->castEnum
| IEvArrayString(_) => svtArrayString_->castEnum
| IEvBool(_) => svtBool_->castEnum
| IEvCall(_) => svtCall_->castEnum //Impossible
| IEvDate(_) => svtDate_->castEnum
| IEvDeclaration(_) => svtDeclaration_->castEnum
| IEvDistribution(_) => svtDistribution_->castEnum
| IEvLambda(_) => svtLambda_->castEnum
| IEvBindings(_) => svtModule_->castEnum //Impossible
| IEvNumber(_) => svtNumber_->castEnum
| IEvRecord(_) => svtRecord_->castEnum
| IEvString(_) => svtString_->castEnum
| IEvSymbol(_) => svtSymbol_->castEnum
| IEvTimeDuration(_) => svtTimeDuration_->castEnum
| IEvType(_) => svtType_->castEnum
| IEvTypeIdentifier(_) => svtTypeIdentifier_->castEnum
| IEvVoid => svtVoid_->castEnum
}
@genType
let toString = (variant: squiggleValue) =>
ReducerInterface_InternalExpressionValue.toString(variant)
// This is a useful method for unit tests.
// Convert the result along with the error message to a string.
@genType
let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) =>
ReducerInterface_InternalExpressionValue.toStringResult(variantResult)
@genType
let getArray = (variant: squiggleValue): option<squiggleValue_Array> =>
//FIXME: Convert
switch variant {
| IEvArray(arrayLike) => arrayLike->Some
| _ => None
}
@genType
let getArrayString = (variant: squiggleValue): option<array<string>> =>
switch variant {
| IEvArrayString(value) => value->Some
| _ => None
}
@genType
let getBool = (variant: squiggleValue): option<bool> =>
switch variant {
| IEvBool(value) => value->Some
| _ => None
}
@genType
let getCall = (variant: squiggleValue): option<string> =>
switch variant {
| IEvCall(value) => value->Some
| _ => None
}
@genType
let getDate = (variant: squiggleValue): option<Js.Date.t> =>
switch variant {
| IEvDate(value) => value->Some
| _ => None
}
@genType
let getDeclaration = (variant: squiggleValue): option<squiggleValue_Declaration> =>
switch variant {
| IEvDeclaration(value) => value->Some
| _ => None
}
@genType
let getDistribution = (variant: squiggleValue): option<squiggleValue_Distribution> =>
switch variant {
| IEvDistribution(value) => value->Some
| _ => None
}
@genType
let getLambda = (variant: squiggleValue): option<squiggleValue_Lambda> =>
switch variant {
| IEvLambda(value) => value->Some
| _ => None
}
@genType
let getModule = (variant: squiggleValue): option<squiggleValue_Module> =>
switch variant {
| IEvBindings(value) => value->Some
| _ => None
}
@genType
let getNumber = (variant: squiggleValue): option<float> =>
switch variant {
| IEvNumber(value) => value->Some
| _ => None
}
@genType
let getRecord = (variant: squiggleValue): option<squiggleValue_Record> =>
switch variant {
| IEvRecord(value) => value->Some
| _ => None
}
@genType
let getString = (variant: squiggleValue): option<string> =>
switch variant {
| IEvString(value) => value->Some
| _ => None
}
@genType
let getSymbol = (variant: squiggleValue): option<string> =>
switch variant {
| IEvSymbol(value) => value->Some
| _ => None
}
@genType
let getTimeDuration = (variant: squiggleValue): option<float> =>
switch variant {
| IEvTimeDuration(value) => value->Some
| _ => None
}
@genType
let getType = (variant: squiggleValue): option<squiggleValue_Type> =>
switch variant {
| IEvType(value) => value->Some
| _ => None
}
@genType
let getTypeIdentifier = (variant: squiggleValue): option<string> =>
switch variant {
| IEvTypeIdentifier(value) => value->Some
| _ => None
}

View File

@ -0,0 +1,6 @@
type squiggleValue = ForTS_SquiggleValue.squiggleValue
@genType type squiggleValue_Array = ForTS_SquiggleValue.squiggleValue_Array //re-export recursive type
@genType
let getValues = (v: squiggleValue_Array): array<squiggleValue> =>
ReducerInterface_InternalExpressionValue.arrayToValueArray(v)

View File

@ -0,0 +1 @@
@genType type squiggleValue_Declaration = ReducerInterface_InternalExpressionValue.lambdaDeclaration //re-export

View File

@ -0,0 +1 @@
@genType type squiggleValue_Distribution = ForTS_Distribution.distribution

View File

@ -0,0 +1 @@
@genType type squiggleValue_Lambda = ReducerInterface_InternalExpressionValue.lambdaValue //re-export

View File

@ -0,0 +1,6 @@
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
@genType type squiggleValue_Module = ForTS_SquiggleValue.squiggleValue_Module //re-export recursive type
@genType
let getKeyValuePairs = (v: squiggleValue_Module): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.nameSpaceToKeyValuePairs(v)

View File

@ -0,0 +1,6 @@
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
@genType type squiggleValue_Record = ForTS_SquiggleValue.squiggleValue_Record //re-export recursive type
@genType
let getKeyValuePairs = (value: squiggleValue_Record): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value)

View File

@ -0,0 +1,6 @@
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
@genType type squiggleValue_Type = ForTS_SquiggleValue.squiggleValue_Type //re-export recursive type
@genType
let getKeyValuePairs = (value: squiggleValue_Type): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value)

View File

@ -0,0 +1,19 @@
export enum squiggleValueTag {
SvtArray = "Array",
SvtArrayString = "ArrayString",
SvtBool = "Bool",
SvtCall = "Call",
SvtDate = "Date",
SvtDeclaration = "Declaration",
SvtDistribution = "Distribution",
SvtLambda = "Lambda",
SvtModule = "Module",
SvtNumber = "Number",
SvtRecord = "Record",
SvtString = "String",
SvtSymbol = "Symbol",
SvtTimeDuration = "TimeDuration",
SvtType = "Type",
SvtTypeIdentifier = "TypeIdentifier",
SvtVoid = "Void",
}

View File

@ -0,0 +1,21 @@
@genType type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //re-export
@genType type syntaxErrorLocation = ForTS_Reducer_ErrorValue.syntaxErrorLocation //re-export
@genType type reducerProject = ForTS_ReducerProject.reducerProject //re-export
@genType type squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export
@genType type squiggleValue_Array = ForTS_SquiggleValue_Array.squiggleValue_Array //re-export
@genType type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //re-export
@genType type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //re-export
@genType type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //re-export
@genType type squiggleValue_Record = ForTS_SquiggleValue_Record.squiggleValue_Record //re-export
@genType type squiggleValue_Type = ForTS_SquiggleValue_Type.squiggleValue_Type //re-export
/* Distribution related */
@genType type squiggleValue_Distribution = ForTS_Distribution.distribution //re-export
@genType type distribution = squiggleValue_Distribution //candid
@genType type distributionError = ForTS_Distribution_Error.distributionError //re-export
@genType type environment = ForTS_Distribution_Environment.environment //re-export
@genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution //re-export
@genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution //re-export
@genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution //re-export

View File

@ -1,3 +1,5 @@
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
@ -46,8 +48,8 @@ type fnDefinition = {
run: ( run: (
array<internalExpressionValue>, array<internalExpressionValue>,
array<frValue>, array<frValue>,
GenericDist.env, ProjectAccessorsT.t,
Reducer_Expression_T.reducerFn, ProjectReducerFnT.t,
) => result<internalExpressionValue, string>, ) => result<internalExpressionValue, string>,
} }
@ -382,12 +384,12 @@ module FnDefinition = {
let run = ( let run = (
t: t, t: t,
args: array<internalExpressionValue>, args: array<internalExpressionValue>,
env: GenericDist.env, accessors: ProjectAccessorsT.t,
reducer: Reducer_Expression_T.reducerFn, reducer: ProjectReducerFnT.t,
) => { ) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
switch argValues { switch argValues {
| Some(values) => t.run(args, values, env, reducer) | Some(values) => t.run(args, values, accessors, reducer)
| None => Error("Incorrect Types") | None => Error("Incorrect Types")
} }
} }
@ -493,8 +495,8 @@ module Registry = {
~registry: registry, ~registry: registry,
~fnName: string, ~fnName: string,
~args: array<internalExpressionValue>, ~args: array<internalExpressionValue>,
~env: GenericDist.env, ~accessors: ProjectAccessorsT.t,
~reducer: Reducer_Expression_T.reducerFn, ~reducer: ProjectReducerFnT.t,
) => { ) => {
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([]) let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict} let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
@ -512,7 +514,7 @@ module Registry = {
switch Matcher.Registry.findMatches(modified, fnName, args) { switch Matcher.Registry.findMatches(modified, fnName, args) {
| Matcher.Match.FullMatch(match) => | Matcher.Match.FullMatch(match) =>
match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env, reducer)) match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, accessors, reducer))
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m))) | SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
| _ => None | _ => None
} }
@ -521,10 +523,10 @@ module Registry = {
let dispatch = ( let dispatch = (
registry, registry,
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall, (fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
env, accessors: ProjectAccessorsT.t,
reducer: Reducer_Expression_T.reducerFn, reducer: ProjectReducerFnT.t,
) => { ) => {
_matchAndRun(~registry, ~fnName, ~args, ~env, ~reducer)->E.O2.fmap( _matchAndRun(~registry, ~fnName, ~args, ~accessors, ~reducer)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)), E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
) )
} }

View File

@ -21,8 +21,8 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors, _) =>
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env), inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=accessors.environment),
(), (),
) )
} }
@ -31,8 +31,10 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors, _) =>
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env), inputs
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(), (),
) )
} }
@ -41,8 +43,10 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors, _) =>
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env), inputs
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(), (),
) )
} }
@ -58,8 +62,8 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeDistOrNumber], ~inputs=[FRTypeDistOrNumber],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors, _) =>
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env), inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=accessors.environment),
(), (),
) )
} }

View File

@ -1,3 +1,6 @@
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
open FunctionRegistry_Core open FunctionRegistry_Core
open FunctionRegistry_Helpers open FunctionRegistry_Helpers
@ -24,17 +27,19 @@ module Internals = {
Belt.Array.reverse(array), Belt.Array.reverse(array),
) )
let map = (array: array<internalExpressionValue>, environment, eLambdaValue, reducer): result< let map = (
ReducerInterface_InternalExpressionValue.t, array: array<internalExpressionValue>,
Reducer_ErrorValue.errorValue, accessors: ProjectAccessorsT.t,
> => { eLambdaValue,
reducer: ProjectReducerFnT.t,
): result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue> => {
let rMappedList = array->E.A.reduceReverse(Ok(list{}), (rAcc, elem) => let rMappedList = array->E.A.reduceReverse(Ok(list{}), (rAcc, elem) =>
rAcc->E.R.bind(_, acc => { rAcc->E.R.bind(_, acc => {
let rNewElem = Reducer_Expression_Lambda.doLambdaCall( let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
eLambdaValue, eLambdaValue,
list{elem}, list{elem},
environment, (accessors: ProjectAccessorsT.t),
reducer, (reducer: ProjectReducerFnT.t),
) )
rNewElem->E.R2.fmap(newElem => list{newElem, ...acc}) rNewElem->E.R2.fmap(newElem => list{newElem, ...acc})
}) })
@ -42,29 +47,46 @@ module Internals = {
rMappedList->E.R2.fmap(mappedList => mappedList->Belt.List.toArray->Wrappers.evArray) rMappedList->E.R2.fmap(mappedList => mappedList->Belt.List.toArray->Wrappers.evArray)
} }
let reduce = (aValueArray, initialValue, aLambdaValue, environment, reducer) => { let reduce = (
aValueArray,
initialValue,
aLambdaValue,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => {
aValueArray->E.A.reduce(Ok(initialValue), (rAcc, elem) => aValueArray->E.A.reduce(Ok(initialValue), (rAcc, elem) =>
rAcc->E.R.bind(_, acc => rAcc->E.R.bind(_, acc =>
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer) Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer)
) )
) )
} }
let reduceReverse = (aValueArray, initialValue, aLambdaValue, environment, reducer) => { let reduceReverse = (
aValueArray,
initialValue,
aLambdaValue,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => {
aValueArray->Belt.Array.reduceReverse(Ok(initialValue), (rAcc, elem) => aValueArray->Belt.Array.reduceReverse(Ok(initialValue), (rAcc, elem) =>
rAcc->Belt.Result.flatMap(acc => rAcc->Belt.Result.flatMap(acc =>
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, environment, reducer) Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer)
) )
) )
} }
let filter = (aValueArray, aLambdaValue, environment, reducer) => { let filter = (
aValueArray,
aLambdaValue,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => {
let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) => let rMappedList = aValueArray->Belt.Array.reduceReverse(Ok(list{}), (rAcc, elem) =>
rAcc->E.R.bind(_, acc => { rAcc->E.R.bind(_, acc => {
let rNewElem = Reducer_Expression_Lambda.doLambdaCall( let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
aLambdaValue, aLambdaValue,
list{elem}, list{elem},
environment, accessors,
reducer, reducer,
) )
rNewElem->E.R2.fmap(newElem => { rNewElem->E.R2.fmap(newElem => {
@ -189,10 +211,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="map", ~name="map",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), IEvLambda(lambda)] => | [IEvArray(array), IEvLambda(lambda)] =>
Internals.map(array, env, lambda, reducer)->E.R2.errMap(_ => "Error!") Internals.map(array, accessors, lambda, reducer)->E.R2.errMap(_ => "Error!")
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -209,10 +231,12 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="reduce", ~name="reduce",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), initialValue, IEvLambda(lambda)] => | [IEvArray(array), initialValue, IEvLambda(lambda)] =>
Internals.reduce(array, initialValue, lambda, env, reducer)->E.R2.errMap(_ => "Error!") Internals.reduce(array, initialValue, lambda, accessors, reducer)->E.R2.errMap(_ =>
"Error!"
)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -229,12 +253,16 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="reduceReverse", ~name="reduceReverse",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
switch inputs { switch inputs {
| [IEvArray(array), initialValue, IEvLambda(lambda)] => | [IEvArray(array), initialValue, IEvLambda(lambda)] =>
Internals.reduceReverse(array, initialValue, lambda, env, reducer)->E.R2.errMap(_ => Internals.reduceReverse(
"Error!" array,
) initialValue,
lambda,
accessors,
reducer,
)->E.R2.errMap(_ => "Error!")
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -251,10 +279,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="filter", ~name="filter",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
switch inputs { switch inputs {
| [IEvArray(array), IEvLambda(lambda)] => | [IEvArray(array), IEvLambda(lambda)] =>
Internals.filter(array, lambda, env, reducer)->E.R2.errMap(_ => "Error!") Internals.filter(array, lambda, accessors, reducer)->E.R2.errMap(_ => "Error!")
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),

View File

@ -58,13 +58,13 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="fromDist", ~name="fromDist",
~inputs=[FRTypeDist], ~inputs=[FRTypeDist],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors, _) =>
switch inputs { switch inputs {
| [FRValueDist(dist)] => | [FRValueDist(dist)] =>
GenericDist.toPointSet( GenericDist.toPointSet(
dist, dist,
~xyPointLength=env.xyPointLength, ~xyPointLength=accessors.environment.xyPointLength,
~sampleCount=env.sampleCount, ~sampleCount=accessors.environment.sampleCount,
(), (),
) )
->E.R2.fmap(Wrappers.pointSet) ->E.R2.fmap(Wrappers.pointSet)

View File

@ -1,3 +1,5 @@
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
open FunctionRegistry_Core open FunctionRegistry_Core
open FunctionRegistry_Helpers open FunctionRegistry_Helpers
@ -6,8 +8,14 @@ let requiresNamespace = true
module Internal = { module Internal = {
type t = SampleSetDist.t type t = SampleSetDist.t
let doLambdaCall = (aLambdaValue, list, environment, reducer) =>
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) { let doLambdaCall = (
aLambdaValue,
list,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) =>
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
| Ok(IEvNumber(f)) => Ok(f) | Ok(IEvNumber(f)) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction) | _ => Error(Operation.SampleMapNeedsNtoNFunction)
} }
@ -22,29 +30,26 @@ module Internal = {
} }
//TODO: I don't know why this seems to need at least one input //TODO: I don't know why this seems to need at least one input
let fromFn = ( let fromFn = (aLambdaValue, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => {
aLambdaValue, let sampleCount = accessors.environment.sampleCount
env: ReducerInterface_InternalExpressionValue.environment, let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer)
reducer,
) => {
let sampleCount = env.sampleCount
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer)
Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen
} }
let map1 = (sampleSetDist: t, aLambdaValue, env, reducer) => { let map1 = (sampleSetDist: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer) let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer)
SampleSetDist.samplesMap(~fn, sampleSetDist)->toType SampleSetDist.samplesMap(~fn, sampleSetDist)->toType
} }
let map2 = (t1: t, t2: t, aLambdaValue, env, reducer) => { let map2 = (t1: t, t2: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
let fn = (a, b) => doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, env, reducer) let fn = (a, b) =>
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, accessors, reducer)
SampleSetDist.map2(~fn, ~t1, ~t2)->toType SampleSetDist.map2(~fn, ~t1, ~t2)->toType
} }
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, env, reducer) => { let map3 = (t1: t, t2: t, t3: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => {
let fn = (a, b, c) => let fn = (a, b, c) =>
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, env, reducer) doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, accessors, reducer)
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
} }
@ -59,14 +64,19 @@ module Internal = {
E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr)) E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr))
} }
let mapN = (aValueArray: array<internalExpressionValue>, aLambdaValue, env, reducer) => { let mapN = (
aValueArray: array<internalExpressionValue>,
aLambdaValue,
accessors: ProjectAccessorsT.t,
reducer,
) => {
switch parseSampleSetArray(aValueArray) { switch parseSampleSetArray(aValueArray) {
| Some(t1) => | Some(t1) =>
let fn = a => let fn = a =>
doLambdaCall( doLambdaCall(
aLambdaValue, aLambdaValue,
list{IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))}, list{IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))},
env, accessors,
reducer, reducer,
) )
SampleSetDist.mapN(~fn, ~t1)->toType SampleSetDist.mapN(~fn, ~t1)->toType
@ -86,10 +96,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="fromDist", ~name="fromDist",
~inputs=[FRTypeDist], ~inputs=[FRTypeDist],
~run=(_, inputs, env, _) => ~run=(_, inputs, accessors: ProjectAccessorsT.t, _) =>
switch inputs { switch inputs {
| [FRValueDist(dist)] => | [FRValueDist(dist)] =>
GenericDist.toSampleSetDist(dist, env.sampleCount) GenericDist.toSampleSetDist(dist, accessors.environment.sampleCount)
->E.R2.fmap(Wrappers.sampleSet) ->E.R2.fmap(Wrappers.sampleSet)
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(DistributionTypes.Error.toString) ->E.R2.errMap(DistributionTypes.Error.toString)
@ -153,10 +163,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="fromFn", ~name="fromFn",
~inputs=[FRTypeLambda], ~inputs=[FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
switch inputs { switch inputs {
| [IEvLambda(lambda)] => | [IEvLambda(lambda)] =>
switch Internal.fromFn(lambda, env, reducer) { switch Internal.fromFn(lambda, accessors, reducer) {
| Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution) | Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution)
| Error(e) => Error(Operation.Error.toString(e)) | Error(e) => Error(Operation.Error.toString(e))
} }
@ -177,10 +187,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="map", ~name="map",
~inputs=[FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs { switch inputs {
| [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] => | [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] =>
Internal.map1(dist, lambda, env, reducer)->E.R2.errMap(Reducer_ErrorValue.errorToString) Internal.map1(dist, lambda, accessors, reducer)->E.R2.errMap(_ => "")
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -200,16 +210,14 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="map2", ~name="map2",
~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda],
~run=(inputs, _, env, reducer) => { ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => {
switch inputs { switch inputs {
| [ | [
IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist1)),
IEvDistribution(SampleSet(dist2)), IEvDistribution(SampleSet(dist2)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map2(dist1, dist2, lambda, env, reducer)->E.R2.errMap( Internal.map2(dist1, dist2, lambda, accessors, reducer)->E.R2.errMap(_ => "")
Reducer_ErrorValue.errorToString,
)
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
}, },
@ -230,7 +238,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="map3", ~name="map3",
~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs { switch inputs {
| [ | [
IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist1)),
@ -238,9 +246,7 @@ let library = [
IEvDistribution(SampleSet(dist3)), IEvDistribution(SampleSet(dist3)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map3(dist1, dist2, dist3, lambda, env, reducer)->E.R2.errMap( Internal.map3(dist1, dist2, dist3, lambda, accessors, reducer)->E.R2.errMap(_ => "")
Reducer_ErrorValue.errorToString,
)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -260,12 +266,12 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="mapN", ~name="mapN",
~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs { switch inputs {
| [IEvArray(dists), IEvLambda(lambda)] => | [IEvArray(dists), IEvLambda(lambda)] =>
Internal.mapN(dists, lambda, env, reducer)->E.R2.errMap( Internal.mapN(dists, lambda, accessors, reducer)->E.R2.errMap(_e => {
Reducer_ErrorValue.errorToString, "AHHH doesn't work"
) })
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),

View File

@ -30,16 +30,16 @@ let library = [
("prior", FRTypeDist), ("prior", FRTypeDist),
]), ]),
], ],
~run=(_, inputs, env, _) => { ~run=(_, inputs, accessors, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) { switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) => | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) =>
runScoring(estimate, Score_Dist(d), Some(prior), env) runScoring(estimate, Score_Dist(d), Some(prior), accessors.environment)
| Ok([ | Ok([
FRValueDist(estimate), FRValueDist(estimate),
FRValueDistOrNumber(FRValueNumber(d)), FRValueDistOrNumber(FRValueNumber(d)),
FRValueDist(prior), FRValueDist(prior),
]) => ]) =>
runScoring(estimate, Score_Scalar(d), Some(prior), env) runScoring(estimate, Score_Scalar(d), Some(prior), accessors.environment)
| Error(e) => Error(e) | Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
@ -49,12 +49,12 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="logScore", ~name="logScore",
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) => { ~run=(_, inputs, accessors, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) { switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) => | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) =>
runScoring(estimate, Score_Dist(d), None, env) runScoring(estimate, Score_Dist(d), None, accessors.environment)
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) => | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) =>
runScoring(estimate, Score_Scalar(d), None, env) runScoring(estimate, Score_Scalar(d), None, accessors.environment)
| Error(e) => Error(e) | Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
@ -74,10 +74,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="klDivergence", ~name="klDivergence",
~inputs=[FRTypeDist, FRTypeDist], ~inputs=[FRTypeDist, FRTypeDist],
~run=(_, inputs, env, _) => { ~run=(_, inputs, accessors, _) => {
switch inputs { switch inputs {
| [FRValueDist(estimate), FRValueDist(d)] => | [FRValueDist(estimate), FRValueDist(d)] =>
runScoring(estimate, Score_Dist(d), None, env) runScoring(estimate, Score_Dist(d), None, accessors.environment)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
}, },

View File

@ -1,35 +0,0 @@
module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Lambda = Reducer_Expression_Lambda
type environment = ReducerInterface_InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expressionValue = ExternalExpressionValue.t
type externalBindings = ReducerInterface_ExternalExpressionValue.externalBindings
type lambdaValue = ExternalExpressionValue.lambdaValue
let evaluate = Expression.evaluate
let evaluateUsingOptions = Expression.evaluateUsingOptions
let evaluatePartialUsingExternalBindings = Expression.evaluatePartialUsingExternalBindings
let parse = Expression.parse
let foreignFunctionInterface = (
lambdaValue: ExternalExpressionValue.lambdaValue,
argArray: array<expressionValue>,
environment: ExternalExpressionValue.environment,
) => {
let internallambdaValue = InternalExpressionValue.lambdaValueToInternal(lambdaValue)
let internalArgArray = argArray->Js.Array2.map(InternalExpressionValue.toInternal)
Lambda.foreignFunctionInterface(
internallambdaValue,
internalArgArray,
environment,
Expression.reduceExpression,
)->Belt.Result.map(InternalExpressionValue.toExternal)
}
let defaultEnvironment = ExternalExpressionValue.defaultEnvironment
let defaultExternalBindings = ReducerInterface_StdLib.externalStdLib

View File

@ -1,45 +0,0 @@
module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression
@genType
type environment = ReducerInterface_ExternalExpressionValue.environment
@genType
type errorValue = Reducer_ErrorValue.errorValue
@genType
type expressionValue = ReducerInterface_ExternalExpressionValue.t
@genType
type externalBindings = ReducerInterface_ExternalExpressionValue.externalBindings
@genType
type lambdaValue = ReducerInterface_ExternalExpressionValue.lambdaValue
@genType
let evaluateUsingOptions: (
~environment: option<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment>,
~externalBindings: option<
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
>,
string,
) => result<expressionValue, errorValue>
@genType
let evaluatePartialUsingExternalBindings: (
string,
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.externalBindings,
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
) => result<externalBindings, errorValue>
@genType
let evaluate: string => result<expressionValue, errorValue>
let parse: string => result<Expression.expression, errorValue>
@genType
let foreignFunctionInterface: (
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.lambdaValue,
array<QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.t>,
QuriSquiggleLang.ReducerInterface_ExternalExpressionValue.environment,
) => result<expressionValue, errorValue>
@genType
let defaultEnvironment: environment
@genType
let defaultExternalBindings: externalBindings

View File

@ -74,9 +74,10 @@ let set = (nameSpace: t, id: string, value): t => {
let emptyModule: t = NameSpace(emptyMap) let emptyModule: t = NameSpace(emptyMap)
let emptyBindings = emptyModule let emptyBindings = emptyModule
let emptyNameSpace = emptyModule
let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings // let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings // let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace) let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace)
let fromExpressionValue = (aValue: internalExpressionValue): t => let fromExpressionValue = (aValue: internalExpressionValue): t =>

View File

@ -1,11 +1,15 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Continuation = ReducerInterface_Value_Continuation
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ExternalLibrary = ReducerInterface.ExternalLibrary module ExternalLibrary = ReducerInterface.ExternalLibrary
module Lambda = Reducer_Expression_Lambda module Lambda = Reducer_Expression_Lambda
module MathJs = Reducer_MathJs module MathJs = Reducer_MathJs
module Bindings = Reducer_Bindings module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result module Result = Belt.Result
module TypeBuilder = Reducer_Type_TypeBuilder module TypeBuilder = Reducer_Type_TypeBuilder
open ReducerInterface_InternalExpressionValue open ReducerInterface_InternalExpressionValue
open Reducer_ErrorValue open Reducer_ErrorValue
@ -19,10 +23,11 @@ open Reducer_ErrorValue
exception TestRescriptException exception TestRescriptException
let callInternal = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result< let callInternal = (
'b, call: functionCall,
errorValue, accessors: ProjectAccessorsT.t,
> => { reducer: ProjectReducerFnT.t,
): result<'b, errorValue> => {
let callMathJs = (call: functionCall): result<'b, errorValue> => let callMathJs = (call: functionCall): result<'b, errorValue> =>
switch call { switch call {
| ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests | ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
@ -95,9 +100,17 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok
let doIdentity = (value: internalExpressionValue) => value->Ok
let doDumpBindings = (continuation: nameSpace, value: internalExpressionValue) => {
// let _ = Continuation.inspect(continuation, "doDumpBindings")
accessors.states.continuation = continuation
value->Ok
}
module SampleMap = { module SampleMap = {
let doLambdaCall = (aLambdaValue, list) => let doLambdaCall = (aLambdaValue, list) =>
switch Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) { switch Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
| Ok(IEvNumber(f)) => Ok(f) | Ok(IEvNumber(f)) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction) | _ => Error(Operation.SampleMapNeedsNtoNFunction)
} }
@ -137,12 +150,14 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
| ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok | ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) | ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
| ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace) | ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace)
| ("$_exportBindings_$", [evValue]) => doIdentity(evValue)
| ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) => | ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
doSetBindings(nameSpace, symbol, value) doSetBindings(nameSpace, symbol, value)
| ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) => | ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) =>
doSetTypeAliasBindings(nameSpace, symbol, value) doSetTypeAliasBindings(nameSpace, symbol, value)
| ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) => | ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
doSetTypeOfBindings(nameSpace, symbol, value) doSetTypeOfBindings(nameSpace, symbol, value)
| ("$_dumpBindings_$", [IEvBindings(nameSpace), _, evValue]) => doDumpBindings(nameSpace, evValue)
| ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) => | ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) =>
TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr)) TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr))
| ("$_typeModifier_memberOf_$", [IEvType(typeRecord), IEvArray(arr)]) => | ("$_typeModifier_memberOf_$", [IEvType(typeRecord), IEvArray(arr)]) =>
@ -182,15 +197,16 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
/* /*
Reducer uses Result monad while reducing expressions Reducer uses Result monad while reducing expressions
*/ */
let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result< let dispatch = (
internalExpressionValue, call: functionCall,
errorValue, accessors: ProjectAccessorsT.t,
> => reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, errorValue> =>
try { try {
let (fn, args) = call let (fn, args) = call
// There is a bug that prevents string match in patterns // There is a bug that prevents string match in patterns
// So we have to recreate a copy of the string // So we have to recreate a copy of the string
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, reducer, callInternal) ExternalLibrary.dispatch((Js.String.make(fn), args), accessors, reducer, callInternal)
} catch { } catch {
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error | Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
| _ => RETodo("unhandled rescript exception")->Error | _ => RETodo("unhandled rescript exception")->Error

View File

@ -3,17 +3,19 @@
they take expressions as parameters and return a new expression. they take expressions as parameters and return a new expression.
Macros are used to define language building blocks. They are like Lisp macros. Macros are used to define language building blocks. They are like Lisp macros.
*/ */
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext module ExpressionWithContext = Reducer_ExpressionWithContext
module Bindings = Reducer_Bindings module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result module Result = Belt.Result
open Reducer_Expression_ExpressionBuilder open Reducer_Expression_ExpressionBuilder
type environment = InternalExpressionValue.environment
type errorValue = ErrorValue.errorValue type errorValue = ErrorValue.errorValue
type expression = ExpressionT.expression type expression = ExpressionT.expression
type expressionWithContext = ExpressionWithContext.expressionWithContext type expressionWithContext = ExpressionWithContext.expressionWithContext
@ -21,11 +23,11 @@ type expressionWithContext = ExpressionWithContext.expressionWithContext
let dispatchMacroCall = ( let dispatchMacroCall = (
macroExpression: expression, macroExpression: expression,
bindings: ExpressionT.bindings, bindings: ExpressionT.bindings,
environment, accessors: ProjectAccessorsT.t,
reduceExpression: ExpressionT.reducerFn, reduceExpression: ProjectReducerFnT.t,
): result<expressionWithContext, errorValue> => { ): result<expressionWithContext, errorValue> => {
let useExpressionToSetBindings = (bindingExpr: expression, environment, statement, newCode) => { let useExpressionToSetBindings = (bindingExpr: expression, accessors, statement, newCode) => {
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment) let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, accessors)
rExternalBindingsValue->Result.flatMap(nameSpaceValue => { rExternalBindingsValue->Result.flatMap(nameSpaceValue => {
let newBindings = Bindings.fromExpressionValue(nameSpaceValue) let newBindings = Bindings.fromExpressionValue(nameSpaceValue)
@ -45,16 +47,17 @@ let dispatchMacroCall = (
| "$_let_$" => "$_setBindings_$" | "$_let_$" => "$_setBindings_$"
| "$_typeOf_$" => "$_setTypeOfBindings_$" | "$_typeOf_$" => "$_setTypeOfBindings_$"
| "$_typeAlias_$" => "$_setTypeAliasBindings_$" | "$_typeAlias_$" => "$_setTypeAliasBindings_$"
| "$_endOfOuterBlock_$" => "$_dumpBindings_$"
| _ => "" | _ => ""
} }
let doBindStatement = (bindingExpr: expression, statement: expression, environment) => { let doBindStatement = (bindingExpr: expression, statement: expression, accessors) => {
let defaultStatement = ErrorValue.REAssignmentExpected->Error let defaultStatement = ErrorValue.REAssignmentExpected->Error
switch statement { switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => { | ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
let setBindingsFn = correspondingSetBindingsFn(callName) let setBindingsFn = correspondingSetBindingsFn(callName)
if setBindingsFn !== "" { if setBindingsFn !== "" {
useExpressionToSetBindings(bindingExpr, environment, statement, ( useExpressionToSetBindings(bindingExpr, accessors, statement, (
newBindingsExpr, newBindingsExpr,
boundStatement, boundStatement,
) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})) ) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement}))
@ -66,12 +69,12 @@ let dispatchMacroCall = (
} }
} }
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result< let doBindExpression = (bindingExpr: expression, statement: expression, accessors): result<
expressionWithContext, expressionWithContext,
errorValue, errorValue,
> => { > => {
let defaultStatement = () => let defaultStatement = () =>
useExpressionToSetBindings(bindingExpr, environment, statement, ( useExpressionToSetBindings(bindingExpr, accessors, statement, (
_newBindingsExpr, _newBindingsExpr,
boundStatement, boundStatement,
) => boundStatement) ) => boundStatement)
@ -80,13 +83,13 @@ let dispatchMacroCall = (
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => { | ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
let setBindingsFn = correspondingSetBindingsFn(callName) let setBindingsFn = correspondingSetBindingsFn(callName)
if setBindingsFn !== "" { if setBindingsFn !== "" {
useExpressionToSetBindings(bindingExpr, environment, statement, ( useExpressionToSetBindings(bindingExpr, accessors, statement, (
newBindingsExpr, newBindingsExpr,
boundStatement, boundStatement,
) => ) =>
eFunction( eFunction(
"$_exportBindings_$", "$_exportBindings_$",
list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})}, list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})}, // expression returning bindings
) )
) )
} else { } else {
@ -97,7 +100,7 @@ let dispatchMacroCall = (
} }
} }
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result< let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _accessors): result<
expressionWithContext, expressionWithContext,
errorValue, errorValue,
> => { > => {
@ -130,10 +133,10 @@ let dispatchMacroCall = (
ifTrue: expression, ifTrue: expression,
ifFalse: expression, ifFalse: expression,
bindings: ExpressionT.bindings, bindings: ExpressionT.bindings,
environment, accessors,
): result<expressionWithContext, errorValue> => { ): result<expressionWithContext, errorValue> => {
let blockCondition = ExpressionBuilder.eBlock(list{condition}) let blockCondition = ExpressionBuilder.eBlock(list{condition})
let rCondition = reduceExpression(blockCondition, bindings, environment) let rCondition = reduceExpression(blockCondition, bindings, accessors)
rCondition->Result.flatMap(conditionValue => rCondition->Result.flatMap(conditionValue =>
switch conditionValue { switch conditionValue {
| InternalExpressionValue.IEvBool(false) => { | InternalExpressionValue.IEvBool(false) => {
@ -149,7 +152,7 @@ let dispatchMacroCall = (
) )
} }
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result< let expandExpressionList = (aList, bindings: ExpressionT.bindings, accessors): result<
expressionWithContext, expressionWithContext,
errorValue, errorValue,
> => > =>
@ -159,21 +162,21 @@ let dispatchMacroCall = (
bindingExpr: ExpressionT.expression, bindingExpr: ExpressionT.expression,
statement, statement,
} => } =>
doBindStatement(bindingExpr, statement, environment) doBindStatement(bindingExpr, statement, accessors)
| list{ExpressionT.EValue(IEvCall("$$_bindStatement_$$")), statement} => | list{ExpressionT.EValue(IEvCall("$$_bindStatement_$$")), statement} =>
// bindings of the context are used when there is no binding expression // bindings of the context are used when there is no binding expression
doBindStatement(eModule(bindings), statement, environment) doBindStatement(eModule(bindings), statement, accessors)
| list{ | list{
ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), ExpressionT.EValue(IEvCall("$$_bindExpression_$$")),
bindingExpr: ExpressionT.expression, bindingExpr: ExpressionT.expression,
expression, expression,
} => } =>
doBindExpression(bindingExpr, expression, environment) doBindExpression(bindingExpr, expression, accessors)
| list{ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), expression} => | list{ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), expression} =>
// bindings of the context are used when there is no binding expression // bindings of the context are used when there is no binding expression
doBindExpression(eModule(bindings), expression, environment) doBindExpression(eModule(bindings), expression, accessors)
| list{ExpressionT.EValue(IEvCall("$$_block_$$")), ...exprs} => | list{ExpressionT.EValue(IEvCall("$$_block_$$")), ...exprs} =>
doBlock(exprs, bindings, environment) doBlock(exprs, bindings, accessors)
| list{ | list{
ExpressionT.EValue(IEvCall("$$_lambda_$$")), ExpressionT.EValue(IEvCall("$$_lambda_$$")),
ExpressionT.EValue(IEvArrayString(parameters)), ExpressionT.EValue(IEvArrayString(parameters)),
@ -181,12 +184,12 @@ let dispatchMacroCall = (
} => } =>
doLambdaDefinition(bindings, parameters, lambdaDefinition) doLambdaDefinition(bindings, parameters, lambdaDefinition)
| list{ExpressionT.EValue(IEvCall("$$_ternary_$$")), condition, ifTrue, ifFalse} => | list{ExpressionT.EValue(IEvCall("$$_ternary_$$")), condition, ifTrue, ifFalse} =>
doTernary(condition, ifTrue, ifFalse, bindings, environment) doTernary(condition, ifTrue, ifFalse, bindings, accessors)
| _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok | _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok
} }
switch macroExpression { switch macroExpression {
| EList(aList) => expandExpressionList(aList, bindings, environment) | EList(aList) => expandExpressionList(aList, bindings, accessors)
| _ => ExpressionWithContext.noContext(macroExpression)->Ok | _ => ExpressionWithContext.noContext(macroExpression)->Ok
} }
} }

View File

@ -1,17 +1,21 @@
module TypeChecker = Reducer_Type_TypeChecker module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Dispatch_T module T = Reducer_Dispatch_T
module TypeChecker = Reducer_Type_TypeChecker
open ReducerInterface_InternalExpressionValue open ReducerInterface_InternalExpressionValue
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
let makeFromTypes = jumpTable => { let makeFromTypes = jumpTable => {
let dispatchChainPiece: T.dispatchChainPiece = ((fnName, fnArgs): functionCall, environment) => { let dispatchChainPiece: T.dispatchChainPiece = (
(fnName, fnArgs): functionCall,
accessors: ProjectAccessorsT.t,
) => {
let jumpTableEntry = jumpTable->Js.Array2.find(elem => { let jumpTableEntry = jumpTable->Js.Array2.find(elem => {
let (candidName, candidType, _) = elem let (candidName, candidType, _) = elem
candidName == fnName && TypeChecker.checkITypeArgumentsBool(candidType, fnArgs) candidName == fnName && TypeChecker.checkITypeArgumentsBool(candidType, fnArgs)
}) })
switch jumpTableEntry { switch jumpTableEntry {
| Some((_, _, bridgeFn)) => bridgeFn(fnArgs, environment)->Some | Some((_, _, bridgeFn)) => bridgeFn(fnArgs, accessors)->Some
| _ => None | _ => None
} }
} }

View File

@ -1,20 +1,22 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
// Each piece of the dispatch chain computes the result or returns None so that the chain can continue // Each piece of the dispatch chain computes the result or returns None so that the chain can continue
type dispatchChainPiece = ( type dispatchChainPiece = (
InternalExpressionValue.functionCall, InternalExpressionValue.functionCall,
InternalExpressionValue.environment, ProjectAccessorsT.t,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>> ) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
type dispatchChainPieceWithReducer = ( type dispatchChainPieceWithReducer = (
InternalExpressionValue.functionCall, InternalExpressionValue.functionCall,
InternalExpressionValue.environment, ProjectAccessorsT.t,
ExpressionT.reducerFn, ProjectReducerFnT.t,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>> ) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
// This is a switch statement case implementation: get the arguments and compute the result // This is a switch statement case implementation: get the arguments and compute the result
type genericIEvFunction = ( type genericIEvFunction = (
array<InternalExpressionValue.t>, array<InternalExpressionValue.t>,
InternalExpressionValue.environment, ProjectAccessorsT.t,
) => result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue> ) => result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>

View File

@ -1,9 +1,10 @@
//TODO: Do not export here but in ForTS__Types
@gentype.import("peggy") @genType.as("LocationRange") @gentype.import("peggy") @genType.as("LocationRange")
type location type syntaxErrorLocation
@genType @genType.opaque
type errorValue = type errorValue =
| REArityError(option<string>, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting | REArityError(option<string>, int, int)
| REArrayIndexNotFound(string, int) | REArrayIndexNotFound(string, int)
| REAssignmentExpected | REAssignmentExpected
| REDistributionError(DistributionTypes.error) | REDistributionError(DistributionTypes.error)
@ -17,13 +18,13 @@ type errorValue =
| REOperationError(Operation.operationError) | REOperationError(Operation.operationError)
| RERecordPropertyNotFound(string, string) | RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string) | RESymbolNotFound(string)
| RESyntaxError(string, option<location>) | RESyntaxError(string, option<syntaxErrorLocation>)
| RETodo(string) // To do | RETodo(string) // To do
| REUnitNotFound(string) | REUnitNotFound(string)
| RENeedToRun
type t = errorValue type t = errorValue
@genType
let errorToString = err => let errorToString = err =>
switch err { switch err {
| REArityError(_oFnName, arity, usedArity) => | REArityError(_oFnName, arity, usedArity) =>
@ -57,4 +58,5 @@ let errorToString = err =>
| RETodo(msg) => `TODO: ${msg}` | RETodo(msg) => `TODO: ${msg}`
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}` | REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
| REUnitNotFound(unitName) => `Unit not found: ${unitName}` | REUnitNotFound(unitName) => `Unit not found: ${unitName}`
| RENeedToRun => "Need to run"
} }

View File

@ -1,3 +1,4 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module BuiltIn = Reducer_Dispatch_BuiltIn module BuiltIn = Reducer_Dispatch_BuiltIn
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
@ -6,30 +7,21 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Lambda = Reducer_Expression_Lambda module Lambda = Reducer_Expression_Lambda
module Macro = Reducer_Expression_Macro module Macro = Reducer_Expression_Macro
module MathJs = Reducer_MathJs module MathJs = Reducer_MathJs
module Bindings = Reducer_Bindings module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module Result = Belt.Result module Result = Belt.Result
module T = Reducer_Expression_T module T = Reducer_Expression_T
type environment = InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
type expression = T.expression type t = T.t
type internalExpressionValue = InternalExpressionValue.t
type externalExpressionValue = ReducerInterface_ExternalExpressionValue.t
type t = expression
/* /*
Converts a Squigle code to expression Recursively evaluate/reduce the expression (Lisp AST/Lambda calculus)
*/ */
let parse = (peggyCode: string): result<t, errorValue> => let rec reduceExpressionInProject = (
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) expression: t,
continuation: T.bindings,
/* accessors: ProjectAccessorsT.t,
Recursively evaluate/reduce the expression (Lisp AST) ): result<InternalExpressionValue.t, 'e> => {
*/
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
internalExpressionValue,
'e,
> => {
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`) // Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression { switch expression {
| T.EValue(value) => value->Ok | T.EValue(value) => value->Ok
@ -38,41 +30,40 @@ let rec reduceExpression = (expression: t, bindings: T.bindings, environment: en
| list{EValue(IEvCall(fName)), ..._args} => | list{EValue(IEvCall(fName)), ..._args} =>
switch Macro.isMacroName(fName) { switch Macro.isMacroName(fName) {
// A macro expands then reduces itself // A macro expands then reduces itself
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression) | true => Macro.doMacroCall(expression, continuation, accessors, reduceExpressionInProject)
| false => reduceExpressionList(list, bindings, environment) | false => reduceExpressionList(list, continuation, accessors)
} }
| _ => reduceExpressionList(list, bindings, environment) | _ => reduceExpressionList(list, continuation, accessors)
} }
} }
} }
and reduceExpressionList = ( and reduceExpressionList = (
expressions: list<t>, expressions: list<t>,
bindings: T.bindings, continuation: T.bindings,
environment: environment, accessors: ProjectAccessorsT.t,
): result<internalExpressionValue, 'e> => { ): result<InternalExpressionValue.t, 'e> => {
let racc: result< let racc: result<
list<internalExpressionValue>, list<InternalExpressionValue.t>,
'e, 'e,
> = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) => > = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: t) =>
racc->Result.flatMap(acc => { racc->Result.flatMap(acc => {
each each
->reduceExpression(bindings, environment) ->reduceExpressionInProject(continuation, accessors)
->Result.map(newNode => { ->Result.map(newNode => {
acc->Belt.List.add(newNode) acc->Belt.List.add(newNode)
}) })
}) })
) )
racc->Result.flatMap(acc => acc->reduceValueList(environment)) racc->Result.flatMap(acc => acc->reduceValueList(accessors))
} }
/* /*
After reducing each level of expression(Lisp AST), we have a value list to evaluate After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/ */
and reduceValueList = (valueList: list<internalExpressionValue>, environment): result< and reduceValueList = (
internalExpressionValue, valueList: list<InternalExpressionValue.t>,
'e, accessors: ProjectAccessorsT.t,
> => ): result<InternalExpressionValue.t, 'e> =>
switch valueList { switch valueList {
| list{IEvCall(fName), ...args} => { | list{IEvCall(fName), ...args} => {
let rCheckedArgs = switch fName { let rCheckedArgs = switch fName {
@ -81,7 +72,10 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
} }
rCheckedArgs->Result.flatMap(checkedArgs => rCheckedArgs->Result.flatMap(checkedArgs =>
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(environment, reduceExpression) (fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(
accessors,
reduceExpressionInProject,
)
) )
} }
| list{IEvLambda(_)} => | list{IEvLambda(_)} =>
@ -91,11 +85,11 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
->Result.flatMap(reducedValueList => ->Result.flatMap(reducedValueList =>
reducedValueList->Belt.List.toArray->InternalExpressionValue.IEvArray->Ok reducedValueList->Belt.List.toArray->InternalExpressionValue.IEvArray->Ok
) )
| list{IEvLambda(lamdaCall), ...args} => | list{IEvLambda(lambdaCall), ...args} =>
args args
->Lambda.checkIfReduced ->Lambda.checkIfReduced
->Result.flatMap(checkedArgs => ->Result.flatMap(checkedArgs =>
Lambda.doLambdaCall(lamdaCall, checkedArgs, environment, reduceExpression) Lambda.doLambdaCall(lambdaCall, checkedArgs, accessors, reduceExpressionInProject)
) )
| _ => | _ =>
@ -106,53 +100,27 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
) )
} }
let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result< let reduceReturningBindings = (
internalExpressionValue, expression: t,
'e, continuation: T.bindings,
> => reduceExpression(aExpression, bindings, environment) accessors: ProjectAccessorsT.t,
): (result<InternalExpressionValue.t, 'e>, T.bindings) => {
let evaluateUsingOptions = ( let states = accessors.states
~environment: option<ReducerInterface_ExternalExpressionValue.environment>, let result = reduceExpressionInProject(expression, continuation, accessors)
~externalBindings: option<ReducerInterface_ExternalExpressionValue.externalBindings>, (result, states.continuation)
code: string,
): result<externalExpressionValue, errorValue> => {
let anEnvironment = Belt.Option.getWithDefault(
environment,
ReducerInterface_ExternalExpressionValue.defaultEnvironment,
)
let mergedBindings: InternalExpressionValue.nameSpace = Bindings.merge(
ReducerInterface_StdLib.internalStdLib,
Belt.Option.map(externalBindings, Bindings.fromTypeScriptBindings)->Belt.Option.getWithDefault(
Bindings.emptyModule,
),
)
parse(code)
->Result.flatMap(expr => evalUsingBindingsExpression_(expr, mergedBindings, anEnvironment))
->Result.map(ReducerInterface_InternalExpressionValue.toExternal)
} }
/* module BackCompatible = {
IEvaluates Squiggle code and bindings via Reducer and answers the result // Those methods are used to support the existing tests
*/ // If they are used outside limited testing context, error location reporting will fail
let evaluate = (code: string): result<externalExpressionValue, errorValue> => { let parse = (peggyCode: string): result<t, errorValue> =>
evaluateUsingOptions(~environment=None, ~externalBindings=None, code) peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
}
let evaluatePartialUsingExternalBindings = ( let evaluate = (expression: t): result<InternalExpressionValue.t, errorValue> => {
code: string, let accessors = ProjectAccessorsT.identityAccessors
externalBindings: ReducerInterface_ExternalExpressionValue.externalBindings, expression->reduceExpressionInProject(accessors.stdLib, accessors)
environment: ReducerInterface_ExternalExpressionValue.environment,
): result<ReducerInterface_ExternalExpressionValue.externalBindings, errorValue> => {
let rAnswer = evaluateUsingOptions(
~environment=Some(environment),
~externalBindings=Some(externalBindings),
code,
)
switch rAnswer {
| Ok(EvModule(externalBindings)) => Ok(externalBindings)
| Ok(_) =>
Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`, None))
| Error(err) => err->Error
} }
let evaluateString = (peggyCode: string): result<InternalExpressionValue.t, errorValue> =>
parse(peggyCode)->Result.flatMap(evaluate)
} }

View File

@ -1,9 +1,11 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result module Result = Belt.Result
module Bindings = Reducer_Bindings
type bindings = ExpressionT.bindings type bindings = ExpressionT.bindings
type context = bindings type context = bindings
@ -11,7 +13,6 @@ type environment = InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression type expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t type internalExpressionValue = InternalExpressionValue.t
type reducerFn = ExpressionT.reducerFn
type expressionWithContext = type expressionWithContext =
| ExpressionWithContext(expression, context) | ExpressionWithContext(expression, context)
@ -20,16 +21,16 @@ type expressionWithContext =
let callReducer = ( let callReducer = (
expressionWithContext: expressionWithContext, expressionWithContext: expressionWithContext,
bindings: bindings, bindings: bindings,
environment: environment, accessors: ProjectAccessorsT.t,
reducer: reducerFn, reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, errorValue> => { ): result<internalExpressionValue, errorValue> => {
switch expressionWithContext { switch expressionWithContext {
| ExpressionNoContext(expr) => | ExpressionNoContext(expr) =>
// Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`) // Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`)
reducer(expr, bindings, environment) reducer(expr, bindings, accessors)
| ExpressionWithContext(expr, context) => | ExpressionWithContext(expr, context) =>
// Js.log(`callReducer: context ${Bindings.toString(context)} expr ${ExpressionT.toString(expr)}`) // Js.log(`callReducer: context ${Bindings.toString(context)} expr ${ExpressionT.toString(expr)}`)
reducer(expr, context, environment) reducer(expr, context, accessors)
} }
} }

View File

@ -7,7 +7,6 @@ module Bindings = Reducer_Bindings
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression type expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t type internalExpressionValue = InternalExpressionValue.t
type externalBindings = ReducerInterface_ExternalExpressionValue.externalBindings
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$") let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")

View File

@ -1,12 +1,13 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer module BindingsReplacer = Reducer_Expression_BindingsReplacer
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface_InternalExpressionValue module ExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result module Result = Belt.Result
type environment = ReducerInterface_InternalExpressionValue.environment
type expression = ExpressionT.expression type expression = ExpressionT.expression
type expressionOrFFI = ExpressionT.expressionOrFFI type expressionOrFFI = ExpressionT.expressionOrFFI
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
@ -44,7 +45,13 @@ let checkIfReduced = (args: list<internalExpressionValue>) =>
) )
) )
let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environment, reducer) => { let caseNotFFI = (
lambdaValue: ExpressionValue.lambdaValue,
expr,
args,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => {
let parameterList = lambdaValue.parameters->Belt.List.fromArray let parameterList = lambdaValue.parameters->Belt.List.fromArray
let zippedParameterList = parameterList->Belt.List.zip(args) let zippedParameterList = parameterList->Belt.List.zip(args)
let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, ( let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, (
@ -52,39 +59,43 @@ let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environm
(variable, variableValue), (variable, variableValue),
) => acc->Bindings.set(variable, variableValue)) ) => acc->Bindings.set(variable, variableValue))
let newExpression = ExpressionBuilder.eBlock(list{expr}) let newExpression = ExpressionBuilder.eBlock(list{expr})
reducer(newExpression, bindings, environment) reducer(newExpression, bindings, accessors)
} }
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, environment) => { let caseFFI = (ffiFn: ExpressionT.ffiFn, args, accessors: ProjectAccessorsT.t) => {
ffiFn(args->Belt.List.toArray, environment) ffiFn(args->Belt.List.toArray, accessors.environment)
} }
let applyParametersToLambda = ( let applyParametersToLambda = (
lambdaValue: ExpressionValue.lambdaValue, lambdaValue: ExpressionValue.lambdaValue,
args, args,
environment, accessors: ProjectAccessorsT.t,
reducer: ExpressionT.reducerFn, reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, 'e> => { ): result<internalExpressionValue, 'e> => {
checkArity(lambdaValue, args)->Result.flatMap(args => checkArity(lambdaValue, args)->Result.flatMap(args =>
checkIfReduced(args)->Result.flatMap(args => { checkIfReduced(args)->Result.flatMap(args => {
let exprOrFFI = castInternalCodeToExpression(lambdaValue.body) let exprOrFFI = castInternalCodeToExpression(lambdaValue.body)
switch exprOrFFI { switch exprOrFFI {
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, environment, reducer) | NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, accessors, reducer)
| FFI(ffiFn) => caseFFI(ffiFn, args, environment) | FFI(ffiFn) => caseFFI(ffiFn, args, accessors)
} }
}) })
) )
} }
let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => let doLambdaCall = (
applyParametersToLambda(lambdaValue, args, environment, reducer) lambdaValue: ExpressionValue.lambdaValue,
args,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => applyParametersToLambda(lambdaValue, args, accessors, reducer)
let foreignFunctionInterface = ( let foreignFunctionInterface = (
lambdaValue: ExpressionValue.lambdaValue, lambdaValue: ExpressionValue.lambdaValue,
argArray: array<internalExpressionValue>, argArray: array<internalExpressionValue>,
environment: ExpressionValue.environment, accessors: ProjectAccessorsT.t,
reducer: ExpressionT.reducerFn, reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, 'e> => { ): result<internalExpressionValue, Reducer_ErrorValue.errorValue> => {
let args = argArray->Belt.List.fromArray let args = argArray->Belt.List.fromArray
applyParametersToLambda(lambdaValue, args, environment, reducer) applyParametersToLambda(lambdaValue, args, accessors, reducer)
} }

View File

@ -2,6 +2,8 @@ module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext module ExpressionWithContext = Reducer_ExpressionWithContext
module Result = Belt.Result module Result = Belt.Result
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
type environment = InternalExpressionValue.environment type environment = InternalExpressionValue.environment
type expression = ExpressionT.expression type expression = ExpressionT.expression
@ -11,34 +13,29 @@ type expressionWithContext = ExpressionWithContext.expressionWithContext
let expandMacroCall = ( let expandMacroCall = (
macroExpression: expression, macroExpression: expression,
bindings: ExpressionT.bindings, bindings: ExpressionT.bindings,
environment: environment, accessors: ProjectAccessorsT.t,
reduceExpression: ExpressionT.reducerFn, reduceExpression: ProjectReducerFnT.t,
): result<expressionWithContext, 'e> => ): result<expressionWithContext, 'e> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall( Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression, macroExpression,
bindings, bindings,
environment, accessors,
reduceExpression, reduceExpression,
) )
let doMacroCall = ( let doMacroCall = (
macroExpression: expression, macroExpression: expression,
bindings: ExpressionT.bindings, bindings: ExpressionT.bindings,
environment: environment, accessors: ProjectAccessorsT.t,
reduceExpression: ExpressionT.reducerFn, reduceExpression: ProjectReducerFnT.t,
): result<internalExpressionValue, 'e> => ): result<internalExpressionValue, 'e> =>
expandMacroCall( expandMacroCall(
macroExpression, macroExpression,
bindings, bindings,
environment, (accessors: ProjectAccessorsT.t),
reduceExpression, (reduceExpression: ProjectReducerFnT.t),
)->Result.flatMap(expressionWithContext => )->Result.flatMap(expressionWithContext =>
ExpressionWithContext.callReducer( ExpressionWithContext.callReducer(expressionWithContext, bindings, accessors, reduceExpression)
expressionWithContext,
bindings,
environment,
reduceExpression,
)
) )
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$") let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")

View File

@ -17,6 +17,8 @@ type rec expression =
| EValue(internalExpressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible | EValue(internalExpressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
and bindings = InternalExpressionValue.nameSpace and bindings = InternalExpressionValue.nameSpace
type t = expression
type reducerFn = ( type reducerFn = (
expression, expression,
bindings, bindings,

View File

@ -9,12 +9,25 @@ start
zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
// { return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])}
// {return [h.nodeVoid()];}
outerBlock outerBlock
= statements:array_statements finalExpression: (statementSeparator @expression)? = statements:array_statements finalExpression: (statementSeparator @expression)?
{ if (finalExpression != null) { statements.push(finalExpression) } { if (finalExpression != null)
return h.nodeBlock(statements) } {
var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]);
statements.push(newFinalExpression);
}
else
{
var newFinalStatement = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), h.nodeVoid()]);
statements.push(newFinalStatement);
}
return h.nodeBlock(statements) }
/ finalExpression: expression / finalExpression: expression
{ return h.nodeBlock([finalExpression])} {
var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]);
return h.nodeBlock([newFinalExpression])}
innerBlockOrExpression innerBlockOrExpression
= quotedInnerBlock = quotedInnerBlock

View File

@ -5,13 +5,12 @@ type node = {"type": string}
@module("./Reducer_Peggy_GeneratedParser.js") external parse__: string => node = "parse" @module("./Reducer_Peggy_GeneratedParser.js") external parse__: string => node = "parse"
type withLocation = {"location": Reducer_ErrorValue.location} type withLocation = {"location": Reducer_ErrorValue.syntaxErrorLocation}
external castWithLocation: Js.Exn.t => withLocation = "%identity" external castWithLocation: Js.Exn.t => withLocation = "%identity"
let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.location => let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.syntaxErrorLocation =>
castWithLocation(error)["location"] castWithLocation(error)["location"]
@genType
let parse = (expr: string): result<node, errorValue> => let parse = (expr: string): result<node, errorValue> =>
try { try {
Ok(parse__(expr)) Ok(parse__(expr))

View File

@ -1,47 +1,44 @@
module Bindings = Reducer_Bindings
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module T = Reducer_Type_T module T = Reducer_Type_T
let ievFromTypeExpression = ( let ievFromTypeExpression = (
typeExpressionSourceCode: string, typeExpressionSourceCode: string,
reducerFn: ExpressionT.reducerFn, reducerFn: ProjectReducerFnT.t,
): result<InternalExpressionValue.t, ErrorValue.t> => { ): result<InternalExpressionValue.t, ErrorValue.t> => {
let sIndex = "compiled" let sIndex = "compiled"
let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}` let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}`
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr => { Reducer_Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr => {
let rContext = reducerFn( let accessors = ProjectAccessorsT.identityAccessors
expr, let result = reducerFn(expr, Bindings.emptyBindings, accessors)
Bindings.emptyBindings, let nameSpace = accessors.states.continuation
InternalExpressionValue.defaultEnvironment,
) switch result {
Belt.Result.map(rContext, context => | Ok(_) =>
switch context { switch Bindings.getType(nameSpace, sIndex) {
| IEvBindings(nameSpace) => | Some(value) => value->Ok
switch Bindings.getType(nameSpace, sIndex) { | None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
| Some(value) => value
| None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none"))
}
| _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-raise"))
} }
) | err => err
}
}) })
} }
let fromTypeExpression = ( let fromTypeExpression = (typeExpressionSourceCode: string, reducerFn: ProjectReducerFnT.t): result<
typeExpressionSourceCode: string, T.t,
reducerFn: ExpressionT.reducerFn, ErrorValue.t,
): result<T.t, ErrorValue.t> => { > => {
ievFromTypeExpression( ievFromTypeExpression(typeExpressionSourceCode, reducerFn)->Belt.Result.map(T.fromIEvValue)
(typeExpressionSourceCode: string),
(reducerFn: ExpressionT.reducerFn),
)->Belt.Result.map(T.fromIEvValue)
} }
let fromTypeExpressionExn = ( let fromTypeExpressionExn = (
typeExpressionSourceCode: string, typeExpressionSourceCode: string,
reducerFn: ExpressionT.reducerFn, reducerFn: ProjectReducerFnT.t,
): T.t => ): T.t =>
switch fromTypeExpression(typeExpressionSourceCode, reducerFn) { switch fromTypeExpression(typeExpressionSourceCode, reducerFn) {
| Ok(value) => value | Ok(value) => value

View File

@ -1,5 +1,7 @@
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module T = Reducer_Type_T module T = Reducer_Type_T
module TypeContracts = Reducer_Type_Contracts module TypeContracts = Reducer_Type_Contracts
open InternalExpressionValue open InternalExpressionValue
@ -129,7 +131,7 @@ let rec isITypeOf = (anIType: T.iType, aValue): result<bool, T.typeErrorValue> =
let isTypeOf = ( let isTypeOf = (
typeExpressionSourceCode: string, typeExpressionSourceCode: string,
aValue: InternalExpressionValue.t, aValue: InternalExpressionValue.t,
reducerFn: ExpressionT.reducerFn, reducerFn: ProjectReducerFnT.t,
): result<InternalExpressionValue.t, ErrorValue.t> => { ): result<InternalExpressionValue.t, ErrorValue.t> => {
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
| Ok(anIType) => | Ok(anIType) =>
@ -164,7 +166,7 @@ let checkITypeArgumentsBool = (anIType: T.iType, args: array<InternalExpressionV
let checkArguments = ( let checkArguments = (
typeExpressionSourceCode: string, typeExpressionSourceCode: string,
args: array<InternalExpressionValue.t>, args: array<InternalExpressionValue.t>,
reducerFn: ExpressionT.reducerFn, reducerFn: ProjectReducerFnT.t,
): result<InternalExpressionValue.t, ErrorValue.t> => { ): result<InternalExpressionValue.t, ErrorValue.t> => {
switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) {
| Ok(anIType) => | Ok(anIType) =>

View File

@ -1,4 +1,3 @@
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module ExternalLibrary = ReducerInterface_ExternalLibrary module ExternalLibrary = ReducerInterface_ExternalLibrary
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module StdLib = ReducerInterface_StdLib module StdLib = ReducerInterface_StdLib

View File

@ -1,99 +0,0 @@
/*
Irreducible values. Reducer does not know about those. Only used for external calls
This is a configuration to to make external calls of those types
*/
module Extra_Array = Reducer_Extra_Array
module ErrorValue = Reducer_ErrorValue
@genType.opaque
type internalCode = Object
@genType.opaque
type hiddenNameSpace = Object
@genType
type rec externalExpressionValue =
| EvArray(array<externalExpressionValue>)
| EvArrayString(array<string>)
| EvBool(bool)
| EvCall(string) // External function call
| EvDistribution(DistributionTypes.genericDist)
| EvLambda(lambdaValue)
| EvNumber(float)
| EvRecord(record)
| EvString(string)
| EvSymbol(string)
| EvDate(Js.Date.t)
| EvTimeDuration(float)
| EvDeclaration(lambdaDeclaration)
| EvTypeIdentifier(string)
| EvModule(record)
| EvType(record)
| EvVoid
and record = Js.Dict.t<externalExpressionValue>
and lambdaValue = {
parameters: array<string>,
context: hiddenNameSpace,
body: internalCode,
}
and lambdaDeclaration = Declaration.declaration<lambdaValue>
@genType
type externalBindings = record
@genType
type t = externalExpressionValue
type functionCall = (string, array<externalExpressionValue>)
let rec toString = aValue =>
switch aValue {
| EvArray(anArray) => {
let args = anArray->Js.Array2.map(each => toString(each))->Js.Array2.toString
`[${args}]`
}
| EvArrayString(anArray) => {
let args = anArray->Js.Array2.toString
`[${args}]`
}
| EvBool(aBool) => Js.String.make(aBool)
| EvCall(fName) => `:${fName}`
| EvDate(date) => DateTime.Date.toString(date)
| EvDeclaration(d) => Declaration.toString(d, r => toString(EvLambda(r)))
| EvDistribution(dist) => GenericDist.toString(dist)
| EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
| EvModule(m) => `@${m->toStringRecord}`
| EvNumber(aNumber) => Js.String.make(aNumber)
| EvRecord(aRecord) => aRecord->toStringRecord
| EvString(aString) => `'${aString}'`
| EvSymbol(aString) => `:${aString}`
| EvTimeDuration(t) => DateTime.Duration.toString(t)
| EvType(t) => `type${t->toStringRecord}`
| EvTypeIdentifier(id) => `#${id}`
| EvVoid => `()`
}
and toStringRecord = aRecord => {
let pairs =
aRecord
->Js.Dict.entries
->Js.Array2.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
->Js.Array2.toString
`{${pairs}}`
}
let argsToString = (args: array<externalExpressionValue>): string => {
args->Js.Array2.map(arg => arg->toString)->Js.Array2.toString
}
let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})`
let toStringResult = x =>
switch x {
| Ok(a) => `Ok(${toString(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
}
@genType
type environment = GenericDist.env
@genType
let defaultEnvironment: environment = DistributionOperation.defaultEnv

View File

@ -1,4 +1,6 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
type internalExpressionValue = InternalExpressionValue.t type internalExpressionValue = InternalExpressionValue.t
/* /*
@ -6,17 +8,17 @@ type internalExpressionValue = InternalExpressionValue.t
*/ */
let dispatch = ( let dispatch = (
call: InternalExpressionValue.functionCall, call: InternalExpressionValue.functionCall,
environment, accessors: ProjectAccessorsT.t,
reducer: Reducer_Expression_T.reducerFn, reducer: ProjectReducerFnT.t,
chain, chain,
): result<internalExpressionValue, 'e> => { ): result<internalExpressionValue, 'e> => {
E.A.O.firstSomeFn([ E.A.O.firstSomeFn([
() => ReducerInterface_GenericDistribution.dispatch(call, environment), () => ReducerInterface_GenericDistribution.dispatch(call, accessors.environment),
() => ReducerInterface_Date.dispatch(call, environment), () => ReducerInterface_Date.dispatch(call, accessors.environment),
() => ReducerInterface_Duration.dispatch(call, environment), () => ReducerInterface_Duration.dispatch(call, accessors.environment),
() => ReducerInterface_Number.dispatch(call, environment), () => ReducerInterface_Number.dispatch(call, accessors.environment),
() => FunctionRegistry_Library.dispatch(call, environment, reducer), () => FunctionRegistry_Library.dispatch(call, accessors, reducer),
])->E.O2.defaultFn(() => chain(call, environment, reducer)) ])->E.O2.defaultFn(() => chain(call, accessors, reducer))
} }
/* /*

View File

@ -1,4 +1,4 @@
let dispatch: ( let dispatch: (
ReducerInterface_InternalExpressionValue.functionCall, ReducerInterface_InternalExpressionValue.functionCall,
ReducerInterface_ExternalExpressionValue.environment, ReducerInterface_InternalExpressionValue.environment,
) => option<result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue>> ) => option<result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>

View File

@ -1,11 +1,11 @@
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module Extra_Array = Reducer_Extra_Array module Extra_Array = Reducer_Extra_Array
type internalCode = ExternalExpressionValue.internalCode type internalCode = Object
type environment = ExternalExpressionValue.environment type environment = GenericDist.env
let defaultEnvironment = ExternalExpressionValue.defaultEnvironment let defaultEnvironment: environment = DistributionOperation.defaultEnv
@genType.opaque
type rec t = type rec t =
| IEvArray(array<t>) // FIXME: Convert to MapInt | IEvArray(array<t>) // FIXME: Convert to MapInt
| IEvArrayString(array<string>) | IEvArrayString(array<string>)
@ -24,42 +24,21 @@ type rec t =
| IEvType(map) | IEvType(map)
| IEvTypeIdentifier(string) | IEvTypeIdentifier(string)
| IEvVoid | IEvVoid
and map = Belt.Map.String.t<t> @genType.opaque and squiggleArray = array<t>
and nameSpace = NameSpace(Belt.Map.String.t<t>) @genType.opaque and map = Belt.Map.String.t<t>
@genType.opaque and nameSpace = NameSpace(Belt.Map.String.t<t>)
@genType.opaque
and lambdaValue = { and lambdaValue = {
parameters: array<string>, parameters: array<string>,
context: nameSpace, context: nameSpace,
body: internalCode, body: internalCode,
} }
and lambdaDeclaration = Declaration.declaration<lambdaValue> @genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue>
type internalExpressionValue = t type internalExpressionValue = t
type functionCall = (string, array<t>) type functionCall = (string, array<t>)
module Internal = {
module NameSpace = {
external castNameSpaceToHidden: nameSpace => ExternalExpressionValue.hiddenNameSpace =
"%identity"
external castHiddenToNameSpace: ExternalExpressionValue.hiddenNameSpace => nameSpace =
"%identity"
}
module Lambda = {
let toInternal = (v: ExternalExpressionValue.lambdaValue): lambdaValue => {
let p = v.parameters
let c = v.context->NameSpace.castHiddenToNameSpace
let b = v.body
{parameters: p, context: c, body: b}
}
and toExternal = (v: lambdaValue): ExternalExpressionValue.lambdaValue => {
let p = v.parameters
let c = v.context->NameSpace.castNameSpaceToHidden
let b = v.body
{parameters: p, context: c, body: b}
}
}
}
let rec toString = aValue => let rec toString = aValue =>
switch aValue { switch aValue {
| IEvArray(anArray) => { | IEvArray(anArray) => {
@ -132,6 +111,12 @@ let toStringResult = x =>
| Error(m) => `Error(${ErrorValue.errorToString(m)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})`
} }
let toStringOptionResult = x =>
switch x {
| Some(a) => toStringResult(a)
| None => "None"
}
let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string => let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string =>
switch codeResult { switch codeResult {
| Ok(a) => toString(a) | Ok(a) => toString(a)
@ -140,7 +125,7 @@ let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): strin
let toStringResultRecord = x => let toStringResultRecord = x =>
switch x { switch x {
| Ok(a) => `Ok(${ExternalExpressionValue.toStringRecord(a)})` | Ok(a) => `Ok(${toStringMap(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})`
} }
@ -188,27 +173,6 @@ let valueToValueType = value =>
| IEvVoid => EvtVoid | IEvVoid => EvtVoid
} }
let externalValueToValueType = (value: ExternalExpressionValue.t) =>
switch value {
| EvArray(_) => EvtArray
| EvArrayString(_) => EvtArrayString
| EvBool(_) => EvtBool
| EvCall(_) => EvtCall
| EvDate(_) => EvtDate
| EvDeclaration(_) => EvtDeclaration
| EvDistribution(_) => EvtDistribution
| EvLambda(_) => EvtLambda
| EvModule(_) => EvtModule
| EvNumber(_) => EvtNumber
| EvRecord(_) => EvtRecord
| EvString(_) => EvtString
| EvSymbol(_) => EvtSymbol
| EvTimeDuration(_) => EvtTimeDuration
| EvType(_) => EvtType
| EvTypeIdentifier(_) => EvtTypeIdentifier
| EvVoid => EvtVoid
}
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => { let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
let (fn, args) = functionCall let (fn, args) = functionCall
CallSignature(fn, args->Js.Array2.map(valueToValueType)) CallSignature(fn, args->Js.Array2.map(valueToValueType))
@ -240,70 +204,18 @@ let functionCallSignatureToString = (functionCallSignature: functionCallSignatur
`${fn}(${args->Js.Array2.map(valueTypeToString)->Js.Array2.toString})` `${fn}(${args->Js.Array2.map(valueTypeToString)->Js.Array2.toString})`
} }
let rec toExternal = (iev: t): ExternalExpressionValue.t => { let arrayToValueArray = (arr: array<t>): array<t> => arr
switch iev {
| IEvArray(v) => v->Belt.Array.map(e => toExternal(e))->EvArray
| IEvArrayString(v) => EvArrayString(v)
| IEvBool(v) => EvBool(v)
| IEvCall(v) => EvCall(v)
| IEvDeclaration(v) => {
let fn = lambdaValueToExternal(v.fn)
let args = v.args
EvDeclaration({fn: fn, args: args})
}
| IEvDistribution(v) => EvDistribution(v)
| IEvLambda(v) => EvLambda(lambdaValueToExternal(v))
| IEvNumber(v) => EvNumber(v)
| IEvRecord(v) => v->mapToExternal->EvRecord
| IEvString(v) => EvString(v)
| IEvSymbol(v) => EvSymbol(v)
| IEvDate(v) => EvDate(v)
| IEvTimeDuration(v) => EvTimeDuration(v)
| IEvType(v) => v->mapToExternal->EvType
| IEvTypeIdentifier(v) => EvTypeIdentifier(v)
| IEvBindings(v) => v->nameSpaceToTypeScriptBindings->EvModule
| IEvVoid => EvVoid
}
}
and mapToExternal = v =>
v->Belt.Map.String.map(e => toExternal(e))->Belt.Map.String.toArray->Js.Dict.fromArray
and lambdaValueToExternal = Internal.Lambda.toExternal
and nameSpaceToTypeScriptBindings = (
nameSpace: nameSpace,
): ReducerInterface_ExternalExpressionValue.externalBindings => {
let NameSpace(container) = nameSpace
Belt.Map.String.map(container, e => toExternal(e))->Belt.Map.String.toArray->Js.Dict.fromArray
}
let rec toInternal = (ev: ExternalExpressionValue.t): t => { let recordToKeyValuePairs = (record: map): array<(string, t)> => record->Belt.Map.String.toArray
switch ev {
| EvArray(v) => v->Belt.Array.map(e => toInternal(e))->IEvArray // let nameSpaceToTypeScriptBindings = (
| EvArrayString(v) => IEvArrayString(v) // nameSpace: nameSpace,
| EvBool(v) => IEvBool(v) // ) => {
| EvCall(v) => IEvCall(v) // let NameSpace(container) = nameSpace
| EvDate(v) => IEvDate(v) // Belt.Map.String.map(container, e => e->Belt.Map.String.toArray->Js.Dict.fromArray)
| EvDeclaration(v) => { // }
let fn = lambdaValueToInternal(v.fn)
let args = v.args let nameSpaceToKeyValuePairs = (nameSpace: nameSpace): array<(string, t)> => {
IEvDeclaration({fn: fn, args: args}) let NameSpace(container) = nameSpace
} container->Belt.Map.String.toArray
| EvDistribution(v) => IEvDistribution(v)
| EvLambda(v) => IEvLambda(lambdaValueToInternal(v))
| EvModule(v) => v->nameSpaceFromTypeScriptBindings->IEvBindings
| EvNumber(v) => IEvNumber(v)
| EvRecord(v) => v->recordToInternal->IEvRecord
| EvString(v) => IEvString(v)
| EvSymbol(v) => IEvSymbol(v)
| EvTimeDuration(v) => IEvTimeDuration(v)
| EvType(v) => v->recordToInternal->IEvType
| EvTypeIdentifier(v) => IEvTypeIdentifier(v)
| EvVoid => IEvVoid
}
} }
and recordToInternal = v =>
v->Js.Dict.entries->Belt.Map.String.fromArray->Belt.Map.String.map(e => toInternal(e))
and lambdaValueToInternal = Internal.Lambda.toInternal
and nameSpaceFromTypeScriptBindings = (
r: ReducerInterface_ExternalExpressionValue.externalBindings,
): nameSpace =>
r->Js.Dict.entries->Belt.Map.String.fromArray->Belt.Map.String.map(e => toInternal(e))->NameSpace

View File

@ -1,7 +1,4 @@
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
let internalStdLib = let internalStdLib: Bindings.t =
Bindings.emptyBindings->SquiggleLibrary_Math.makeBindings->SquiggleLibrary_Versions.makeBindings Bindings.emptyBindings->SquiggleLibrary_Math.makeBindings->SquiggleLibrary_Versions.makeBindings
@genType
let externalStdLib = internalStdLib->Bindings.toTypeScriptBindings

View File

@ -0,0 +1,28 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
type t = InternalExpressionValue.nameSpace
let toValue = nameSpace => InternalExpressionValue.IEvBindings(nameSpace)
let toString = nameSpace => InternalExpressionValue.toString(toValue(nameSpace))
let toStringResult = rNameSpace =>
Belt.Result.map(rNameSpace, toValue(_))->InternalExpressionValue.toStringResult
let toStringOptionResult = orNameSpace =>
Belt.Option.map(
orNameSpace,
Belt.Result.map(_, toValue(_)),
)->InternalExpressionValue.toStringOptionResult
let inspect = (nameSpace, label: string) => Js.log(`${label}: ${toString(nameSpace)}`)
let inspectOption = (oNameSpace, label: string) =>
switch oNameSpace {
| Some(nameSpace) => inspect(nameSpace, label)
| None => Js.log(`${label}: None`)
}
let minus = (NameSpace(thisContainer): t, NameSpace(thatContainer): t) => {
Belt.Map.String.removeMany(
thisContainer,
Belt.Map.String.keysToArray(thatContainer),
)->InternalExpressionValue.NameSpace
}

View File

@ -0,0 +1,208 @@
// TODO: Auto clean project based on topology
module Bindings = Reducer_Bindings
module Continuation = ReducerInterface_Value_Continuation
module ErrorValue = Reducer_ErrorValue
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectItem = ReducerProject_ProjectItem
module T = ReducerProject_T
module Topology = ReducerProject_Topology
type t = T.t
module Private = {
type internalProject = T.Private.t
type t = T.Private.t
let getSourceIds = T.Private.getSourceIds
let getItem = T.Private.getItem
let getDependents = Topology.getDependents
let getDependencies = Topology.getDependencies
let getRunOrder = Topology.getRunOrder
let getRunOrderFor = Topology.getRunOrderFor
let createProject = () => {
let project: t = {
"tag": "reducerProject",
"items": Belt.Map.String.empty,
"stdLib": ReducerInterface_StdLib.internalStdLib,
"environment": InternalExpressionValue.defaultEnvironment,
}
project
}
let rec touchSource = (project: t, sourceId: string): unit => {
let item = project->getItem(sourceId)
let newItem = ProjectItem.touchSource(item)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
touchDependents(project, sourceId)
}
and touchDependents = (project: t, sourceId: string): unit => {
let _ = getDependents(project, sourceId)->Belt.Array.forEach(_, touchSource(project, _))
}
let getSource = (project: t, sourceId: string): option<string> =>
Belt.Map.String.get(project["items"], sourceId)->Belt.Option.map(ProjectItem.getSource)
let setSource = (project: t, sourceId: string, value: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.setSource(value)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
touchDependents(project, sourceId)
}
let clean = (project: t, sourceId: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.clean
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let cleanAll = (project: t): unit =>
getSourceIds(project)->Belt.Array.forEach(sourceId => clean(project, sourceId))
let cleanResults = (project: t, sourceId: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.cleanResults
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let cleanAllResults = (project: t): unit =>
getSourceIds(project)->Belt.Array.forEach(sourceId => cleanResults(project, sourceId))
let getIncludes = (project: t, sourceId: string): ProjectItem.T.includesType =>
project->getItem(sourceId)->ProjectItem.getIncludes
let setContinues = (project: t, sourceId: string, continues: array<string>): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.setContinues(continues)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
touchSource(project, sourceId)
}
let getContinues = (project: t, sourceId: string): array<string> =>
ProjectItem.getContinues(project->getItem(sourceId))
let removeContinues = (project: t, sourceId: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.removeContinues
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
touchSource(project, sourceId)
}
let getContinuation = (project: t, sourceId: string): ProjectItem.T.continuationArgumentType =>
project->getItem(sourceId)->ProjectItem.getContinuation
let setContinuation = (
project: t,
sourceId: string,
continuation: ProjectItem.T.continuationArgumentType,
): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.setContinuation(continuation)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType =>
project->getItem(sourceId)->ProjectItem.getResult
let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType =>
switch getResultOption(project, sourceId) {
| None => RENeedToRun->Error
| Some(result) => result
}
let setResult = (project: t, sourceId: string, value: ProjectItem.T.resultArgumentType): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.setResult(value)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let parseIncludes = (project: t, sourceId: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.parseIncludes
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let rawParse = (project: t, sourceId): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.rawParse
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let getStdLib = (project: t): Reducer_Bindings.t => project["stdLib"]
let setStdLib = (project: t, value: Reducer_Bindings.t): unit =>
T.Private.setFieldStdLib(project, value)
let getEnvironment = (project: t): InternalExpressionValue.environment => project["environment"]
let setEnvironment = (project: t, value: InternalExpressionValue.environment): unit =>
T.Private.setFieldEnvironment(project, value)
let getBindings = (project: t, sourceId: string): ProjectItem.T.bindingsArgumentType => {
let those = project->getContinuation(sourceId)
let these = project->getStdLib
let ofUser = Continuation.minus(those, these)
ofUser
}
let buildProjectAccessors = (project: t): ProjectAccessorsT.t => {
states: {continuation: Bindings.emptyBindings},
stdLib: getStdLib(project),
environment: getEnvironment(project),
}
let doRunWithContinuation = (
project: t,
sourceId: string,
continuation: ProjectItem.T.continuation,
): unit => {
let accessors = buildProjectAccessors(project)
let states = accessors.states
let newItem = project->getItem(sourceId)->ProjectItem.run(continuation, accessors)
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
setContinuation(project, sourceId, states.continuation)
}
type runState = (ProjectItem.T.resultArgumentType, ProjectItem.T.continuation)
let tryRunWithContinuation = (
project: t,
sourceId: string,
(rPrevResult: ProjectItem.T.resultArgumentType, continuation: ProjectItem.T.continuation),
): (ProjectItem.T.resultArgumentType, ProjectItem.T.continuation) => {
switch getResultOption(project, sourceId) {
| Some(result) => (result, getContinuation(project, sourceId)) // already ran
| None =>
switch rPrevResult {
| Error(error) => {
setResult(project, sourceId, Error(error))
(Error(error), continuation)
}
| Ok(_prevResult) => {
doRunWithContinuation(project, sourceId, continuation)
(
getResultOption(project, sourceId)->Belt.Option.getWithDefault(rPrevResult),
getContinuation(project, sourceId),
)
}
}
}
}
let runAll = (project: t): unit => {
let runOrder = Topology.getRunOrder(project)
let initialState = (Ok(InternalExpressionValue.IEvVoid), getStdLib(project))
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) =>
tryRunWithContinuation(project, currId, currState)
)
}
let run = (project: t, sourceId: string): unit => {
let runOrder = Topology.getRunOrderFor(project, sourceId)
let initialState = (Ok(InternalExpressionValue.IEvVoid), getStdLib(project))
let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) =>
tryRunWithContinuation(project, currId, currState)
)
}
let evaluate = (sourceCode: string) => {
let project = createProject()
setSource(project, "main", sourceCode)
runAll(project)
let those = project->getContinuation("main")
let these = project->getStdLib
let ofUser = Continuation.minus(those, these)
(getResultOption(project, "main")->Belt.Option.getWithDefault(IEvVoid->Ok), ofUser)
}
}

View File

@ -0,0 +1,945 @@
// Generated by Peggy 2.0.1.
//
// https://peggyjs.org/
"use strict";
function peg$subclass(child, parent) {
function C() { this.constructor = child; }
C.prototype = parent.prototype;
child.prototype = new C();
}
function peg$SyntaxError(message, expected, found, location) {
var self = Error.call(this, message);
// istanbul ignore next Check is a necessary evil to support older environments
if (Object.setPrototypeOf) {
Object.setPrototypeOf(self, peg$SyntaxError.prototype);
}
self.expected = expected;
self.found = found;
self.location = location;
self.name = "SyntaxError";
return self;
}
peg$subclass(peg$SyntaxError, Error);
function peg$padEnd(str, targetLength, padString) {
padString = padString || " ";
if (str.length > targetLength) { return str; }
targetLength -= str.length;
padString += padString.repeat(targetLength);
return str + padString.slice(0, targetLength);
}
peg$SyntaxError.prototype.format = function(sources) {
var str = "Error: " + this.message;
if (this.location) {
var src = null;
var k;
for (k = 0; k < sources.length; k++) {
if (sources[k].source === this.location.source) {
src = sources[k].text.split(/\r\n|\n|\r/g);
break;
}
}
var s = this.location.start;
var loc = this.location.source + ":" + s.line + ":" + s.column;
if (src) {
var e = this.location.end;
var filler = peg$padEnd("", s.line.toString().length, ' ');
var line = src[s.line - 1];
var last = s.line === e.line ? e.column : line.length + 1;
var hatLen = (last - s.column) || 1;
str += "\n --> " + loc + "\n"
+ filler + " |\n"
+ s.line + " | " + line + "\n"
+ filler + " | " + peg$padEnd("", s.column - 1, ' ')
+ peg$padEnd("", hatLen, "^");
} else {
str += "\n at " + loc;
}
}
return str;
};
peg$SyntaxError.buildMessage = function(expected, found) {
var DESCRIBE_EXPECTATION_FNS = {
literal: function(expectation) {
return "\"" + literalEscape(expectation.text) + "\"";
},
class: function(expectation) {
var escapedParts = expectation.parts.map(function(part) {
return Array.isArray(part)
? classEscape(part[0]) + "-" + classEscape(part[1])
: classEscape(part);
});
return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]";
},
any: function() {
return "any character";
},
end: function() {
return "end of input";
},
other: function(expectation) {
return expectation.description;
}
};
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
function literalEscape(s) {
return s
.replace(/\\/g, "\\\\")
.replace(/"/g, "\\\"")
.replace(/\0/g, "\\0")
.replace(/\t/g, "\\t")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
}
function classEscape(s) {
return s
.replace(/\\/g, "\\\\")
.replace(/\]/g, "\\]")
.replace(/\^/g, "\\^")
.replace(/-/g, "\\-")
.replace(/\0/g, "\\0")
.replace(/\t/g, "\\t")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
}
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected) {
var descriptions = expected.map(describeExpectation);
var i, j;
descriptions.sort();
if (descriptions.length > 0) {
for (i = 1, j = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + " or " + descriptions[1];
default:
return descriptions.slice(0, -1).join(", ")
+ ", or "
+ descriptions[descriptions.length - 1];
}
}
function describeFound(found) {
return found ? "\"" + literalEscape(found) + "\"" : "end of input";
}
return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found.";
};
function peg$parse(input, options) {
options = options !== undefined ? options : {};
var peg$FAILED = {};
var peg$source = options.grammarSource;
var peg$startRuleFunctions = { start: peg$parsestart };
var peg$startRuleFunction = peg$parsestart;
var peg$c0 = "#include";
var peg$c1 = "'";
var peg$c2 = "\"";
var peg$c3 = "//";
var peg$c4 = "/*";
var peg$c5 = "*/";
var peg$r0 = /^[^']/;
var peg$r1 = /^[^"]/;
var peg$r2 = /^[^*]/;
var peg$r3 = /^[ \t]/;
var peg$r4 = /^[\n\r]/;
var peg$r5 = /^[^\r\n]/;
var peg$e0 = peg$literalExpectation("#include", false);
var peg$e1 = peg$otherExpectation("string");
var peg$e2 = peg$literalExpectation("'", false);
var peg$e3 = peg$classExpectation(["'"], true, false);
var peg$e4 = peg$literalExpectation("\"", false);
var peg$e5 = peg$classExpectation(["\""], true, false);
var peg$e6 = peg$literalExpectation("//", false);
var peg$e7 = peg$literalExpectation("/*", false);
var peg$e8 = peg$classExpectation(["*"], true, false);
var peg$e9 = peg$literalExpectation("*/", false);
var peg$e10 = peg$otherExpectation("white space");
var peg$e11 = peg$classExpectation([" ", "\t"], false, false);
var peg$e12 = peg$otherExpectation("newline");
var peg$e13 = peg$classExpectation(["\n", "\r"], false, false);
var peg$e14 = peg$classExpectation(["\r", "\n"], true, false);
var peg$f0 = function(head, tail) {return [head, ...tail].filter( e => e != '');};
var peg$f1 = function() {return [];};
var peg$f2 = function(characters) {return characters.join('');};
var peg$f3 = function(characters) {return characters.join('');};
var peg$f4 = function() { return '';};
var peg$f5 = function() { return '';};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
var peg$maxFailPos = 0;
var peg$maxFailExpected = [];
var peg$silentFails = 0;
var peg$resultsCache = {};
var peg$result;
if ("startRule" in options) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function offset() {
return peg$savedPos;
}
function range() {
return {
source: peg$source,
start: peg$savedPos,
end: peg$currPos
};
}
function location() {
return peg$computeLocation(peg$savedPos, peg$currPos);
}
function expected(description, location) {
location = location !== undefined
? location
: peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildStructuredError(
[peg$otherExpectation(description)],
input.substring(peg$savedPos, peg$currPos),
location
);
}
function error(message, location) {
location = location !== undefined
? location
: peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildSimpleError(message, location);
}
function peg$literalExpectation(text, ignoreCase) {
return { type: "literal", text: text, ignoreCase: ignoreCase };
}
function peg$classExpectation(parts, inverted, ignoreCase) {
return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase };
}
function peg$anyExpectation() {
return { type: "any" };
}
function peg$endExpectation() {
return { type: "end" };
}
function peg$otherExpectation(description) {
return { type: "other", description: description };
}
function peg$computePosDetails(pos) {
var details = peg$posDetailsCache[pos];
var p;
if (details) {
return details;
} else {
p = pos - 1;
while (!peg$posDetailsCache[p]) {
p--;
}
details = peg$posDetailsCache[p];
details = {
line: details.line,
column: details.column
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
function peg$computeLocation(startPos, endPos) {
var startPosDetails = peg$computePosDetails(startPos);
var endPosDetails = peg$computePosDetails(endPos);
return {
source: peg$source,
start: {
offset: startPos,
line: startPosDetails.line,
column: startPosDetails.column
},
end: {
offset: endPos,
line: endPosDetails.line,
column: endPosDetails.column
}
};
}
function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) { return; }
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected);
}
function peg$buildSimpleError(message, location) {
return new peg$SyntaxError(message, null, null, location);
}
function peg$buildStructuredError(expected, found, location) {
return new peg$SyntaxError(
peg$SyntaxError.buildMessage(expected, found),
expected,
found,
location
);
}
function peg$parsestart() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 10 + 0;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parsenewLine();
if (s2 === peg$FAILED) {
s2 = peg$parse_();
if (s2 === peg$FAILED) {
s2 = peg$parsecomment();
if (s2 === peg$FAILED) {
s2 = peg$parsedelimitedComment();
}
}
}
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parsenewLine();
if (s2 === peg$FAILED) {
s2 = peg$parse_();
if (s2 === peg$FAILED) {
s2 = peg$parsecomment();
if (s2 === peg$FAILED) {
s2 = peg$parsedelimitedComment();
}
}
}
}
s2 = peg$parseincludes();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parsenewLine();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parsenewLine();
}
s4 = peg$parseignore();
s0 = s2;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseincludes() {
var s0, s1, s2, s3, s4, s5;
var key = peg$currPos * 10 + 1;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parseincludeStatement();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$currPos;
s4 = [];
s5 = peg$parsenewLine();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parsenewLine();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseincludeStatement();
if (s5 !== peg$FAILED) {
s3 = s5;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$currPos;
s4 = [];
s5 = peg$parsenewLine();
if (s5 !== peg$FAILED) {
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parsenewLine();
}
} else {
s4 = peg$FAILED;
}
if (s4 !== peg$FAILED) {
s5 = peg$parseincludeStatement();
if (s5 !== peg$FAILED) {
s3 = s5;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
}
peg$savedPos = s0;
s0 = peg$f0(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseignore();
peg$savedPos = s0;
s1 = peg$f1();
s0 = s1;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseincludeStatement() {
var s0, s1, s2, s3, s4, s5, s6;
var key = peg$currPos * 10 + 2;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
if (input.substr(peg$currPos, 8) === peg$c0) {
s2 = peg$c0;
peg$currPos += 8;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e0); }
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parse_();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
s4 = peg$parsestring();
if (s4 !== peg$FAILED) {
s5 = [];
s6 = peg$parse_();
while (s6 !== peg$FAILED) {
s5.push(s6);
s6 = peg$parse_();
}
s0 = s4;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parsecomment();
if (s0 === peg$FAILED) {
s0 = peg$parsedelimitedComment();
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsestring() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 10 + 3;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
peg$silentFails++;
s0 = peg$currPos;
s1 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 39) {
s2 = peg$c1;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e2); }
}
if (s2 !== peg$FAILED) {
s3 = [];
if (peg$r0.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e3); }
}
while (s4 !== peg$FAILED) {
s3.push(s4);
if (peg$r0.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e3); }
}
}
if (input.charCodeAt(peg$currPos) === 39) {
s4 = peg$c1;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e2); }
}
if (s4 !== peg$FAILED) {
s1 = s3;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f2(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 34) {
s2 = peg$c2;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e4); }
}
if (s2 !== peg$FAILED) {
s3 = [];
if (peg$r1.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e5); }
}
while (s4 !== peg$FAILED) {
s3.push(s4);
if (peg$r1.test(input.charAt(peg$currPos))) {
s4 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e5); }
}
}
if (input.charCodeAt(peg$currPos) === 34) {
s4 = peg$c2;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e4); }
}
if (s4 !== peg$FAILED) {
s1 = s3;
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
} else {
peg$currPos = s1;
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f3(s1);
}
s0 = s1;
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e1); }
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseignore() {
var s0, s1;
var key = peg$currPos * 10 + 4;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = [];
s1 = peg$parseany();
if (s1 === peg$FAILED) {
s1 = peg$parsenewLine();
if (s1 === peg$FAILED) {
s1 = peg$parse_();
}
}
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = peg$parseany();
if (s1 === peg$FAILED) {
s1 = peg$parsenewLine();
if (s1 === peg$FAILED) {
s1 = peg$parse_();
}
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsecomment() {
var s0, s1, s2, s3;
var key = peg$currPos * 10 + 5;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c3) {
s1 = peg$c3;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e6); }
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parseany();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parseany();
}
peg$savedPos = s0;
s0 = peg$f4();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsedelimitedComment() {
var s0, s1, s2, s3;
var key = peg$currPos * 10 + 6;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c4) {
s1 = peg$c4;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e7); }
}
if (s1 !== peg$FAILED) {
s2 = [];
if (peg$r2.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e8); }
}
while (s3 !== peg$FAILED) {
s2.push(s3);
if (peg$r2.test(input.charAt(peg$currPos))) {
s3 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e8); }
}
}
if (input.substr(peg$currPos, 2) === peg$c5) {
s3 = peg$c5;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e9); }
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f5();
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parse_() {
var s0, s1;
var key = peg$currPos * 10 + 7;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
peg$silentFails++;
if (peg$r3.test(input.charAt(peg$currPos))) {
s0 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e11); }
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e10); }
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsenewLine() {
var s0, s1;
var key = peg$currPos * 10 + 8;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
peg$silentFails++;
if (peg$r4.test(input.charAt(peg$currPos))) {
s0 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e13); }
}
peg$silentFails--;
if (s0 === peg$FAILED) {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e12); }
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseany() {
var s0;
var key = peg$currPos * 10 + 9;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
if (peg$r5.test(input.charAt(peg$currPos))) {
s0 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e14); }
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
peg$result = peg$startRuleFunction();
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
throw peg$buildStructuredError(
peg$maxFailExpected,
peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
peg$maxFailPos < input.length
? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1)
: peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
}
module.exports = {
SyntaxError: peg$SyntaxError,
parse: peg$parse
};

View File

@ -0,0 +1,40 @@
// Includes are at the start of the file, before any other code.
// There might be some comments before or between the includes.
// #include "string"
// #include "string2"
start
= (newLine/_/comment/delimitedComment)* @includes newLine* ignore
includes
= head:includeStatement tail:(newLine+ @includeStatement)*
{return [head, ...tail].filter( e => e != '');}
/ ignore
{return [];}
includeStatement
= _* '#include' _* @string _*
/ comment
/ delimitedComment
string 'string'
= characters:("'" @([^'])* "'") {return characters.join('');}
/ characters:('"' @([^"])* '"') {return characters.join('');}
ignore = (any / newLine / _)*
comment
= '//'any*
{ return '';}
delimitedComment
= '/*' ([^*]*) '*/'
{ return '';}
_ "white space"
= [ \t]
newLine "newline"
= [\n\r]
any = [^\r\n]

View File

@ -0,0 +1,15 @@
@module("./ReducerProject_IncludeParser.js") external parse__: string => array<string> = "parse"
let parseIncludes = (expr: string): result<array<string>, Reducer_ErrorValue.errorValue> =>
try {
let answer = parse__(expr)
// let logEntry = answer->Js.Array2.joinWith(",")
// `parseIncludes: ${logEntry} for expr: ${expr}`->Js.log
answer->Ok
} catch {
| Js.Exn.Error(obj) =>
RESyntaxError(
Belt.Option.getExn(Js.Exn.message(obj)),
Reducer_Peggy_Parse.syntaxErrorToLocation(obj)->Some,
)->Error
}

View File

@ -0,0 +1,40 @@
module ProjectItemT = ReducerProject_ProjectItem_T
module Bindings = Reducer_Bindings
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
type states = {mutable continuation: ProjectItemT.continuationArgumentType}
type projectAccessors = {
stdLib: Reducer_Bindings.t,
environment: ExpressionT.environment,
states: states,
}
type t = projectAccessors
let identityAccessors: t = {
// We need the states at the end of the runtime.
// Accessors can be modified but states will stay as the same pointer
states: {
continuation: Bindings.emptyBindings,
},
stdLib: ReducerInterface_StdLib.internalStdLib,
environment: InternalExpressionValue.defaultEnvironment,
}
let identityAccessorsWithEnvironment = (environment): t => {
states: {
continuation: Bindings.emptyBindings,
},
stdLib: ReducerInterface_StdLib.internalStdLib,
environment: environment,
}
// to support change of environment in runtime
let setEnvironment = (this: t, environment: ExpressionT.environment): t => {
{
...this,
environment: environment,
}
}

View File

@ -0,0 +1,170 @@
// TODO: Use actual types instead of aliases in public functions
// TODO: Use topological sorting to prevent unnecessary runs
module Bindings = Reducer_Bindings
module Continuation = ReducerInterface_Value_Continuation
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ReducerFnT = ReducerProject_ReducerFn_T
module T = ReducerProject_ProjectItem_T
type projectItem = T.projectItem
type t = T.t
let emptyItem = T.ProjectItem({
source: "",
rawParse: None,
expression: None,
continuation: Bindings.emptyBindings,
result: None,
continues: [],
includes: []->Ok,
})
// source -> rawParse -> includes -> expression -> continuation -> result
let getSource = (T.ProjectItem(r)): T.sourceType => r.source
let getRawParse = (T.ProjectItem(r)): T.rawParseType => r.rawParse
let getExpression = (T.ProjectItem(r)): T.expressionType => r.expression
let getContinuation = (T.ProjectItem(r)): T.continuationArgumentType => r.continuation
let getResult = (T.ProjectItem(r)): T.resultType => r.result
let getContinues = (T.ProjectItem(r)): T.continuesType => r.continues
let getIncludes = (T.ProjectItem(r)): T.includesType => r.includes
let touchSource = (this: t): t => {
let T.ProjectItem(r) = emptyItem
T.ProjectItem({
...r,
includes: getIncludes(this),
continues: getContinues(this),
source: getSource(this),
})
}
let touchRawParse = (this: t): t => {
let T.ProjectItem(r) = emptyItem
T.ProjectItem({
...r,
continues: getContinues(this),
source: getSource(this),
rawParse: getRawParse(this),
includes: getIncludes(this),
})
}
let touchExpression = (this: t): t => {
let T.ProjectItem(r) = emptyItem
T.ProjectItem({
...r,
continues: getContinues(this),
source: getSource(this),
rawParse: getRawParse(this),
includes: getIncludes(this),
expression: getExpression(this),
})
}
let setSource = (T.ProjectItem(r): t, source: T.sourceArgumentType): t =>
T.ProjectItem({...r, source: source})->touchSource
let setRawParse = (T.ProjectItem(r): t, rawParse: T.rawParseArgumentType): t =>
T.ProjectItem({...r, rawParse: Some(rawParse)})->touchRawParse
let setExpression = (T.ProjectItem(r): t, expression: T.expressionArgumentType): t =>
T.ProjectItem({...r, expression: Some(expression)})->touchExpression
let setContinuation = (T.ProjectItem(r): t, continuation: T.continuationArgumentType): t => {
T.ProjectItem({...r, continuation: continuation})
}
let setResult = (T.ProjectItem(r): t, result: T.resultArgumentType): t => T.ProjectItem({
...r,
result: Some(result),
})
let cleanResults = touchExpression
let clean = (this: t): t => {
let T.ProjectItem(r) = emptyItem
T.ProjectItem({
...r,
source: getSource(this),
continuation: getContinuation(this),
result: getResult(this),
})
}
let getImmediateDependencies = (this: t): T.includesType =>
getIncludes(this)->Belt.Result.map(Js.Array2.concat(_, getContinues(this)))
let setContinues = (T.ProjectItem(r): t, continues: array<string>): t =>
T.ProjectItem({...r, continues: continues})->touchSource
let removeContinues = (T.ProjectItem(r): t): t => T.ProjectItem({...r, continues: []})->touchSource
let setIncludes = (T.ProjectItem(r): t, includes: T.includesType): t => T.ProjectItem({
...r,
includes: includes,
})
//TODO: forward parse errors to the user
let parseIncludes = (this: t): t =>
setIncludes(this, getSource(this)->ReducerProject_ParseIncludes.parseIncludes)
let doRawParse = (this: t): T.rawParseArgumentType => this->getSource->Reducer_Peggy_Parse.parse
let rawParse = (this: t): t =>
this->getRawParse->E.O2.defaultFn(() => doRawParse(this))->setRawParse(this, _)
let doBuildExpression = (this: t): T.expressionType =>
this
->getRawParse
->Belt.Option.map(o => o->Belt.Result.map(r => r->Reducer_Peggy_ToExpression.fromNode))
let buildExpression = (this: t): t => {
let withRawParse: t = this->rawParse
switch withRawParse->getExpression {
| Some(_) => withRawParse
| None =>
withRawParse
->doBuildExpression
->Belt.Option.map(setExpression(withRawParse, _))
->E.O2.defaultFn(() => withRawParse)
}
}
let wrappedReducer = (
rExpression: T.expressionArgumentType,
aContinuation: T.continuation,
accessors: ProjectAccessorsT.t,
): T.resultArgumentType => {
Belt.Result.flatMap(
rExpression,
Reducer_Expression.reduceExpressionInProject(_, aContinuation, accessors),
)
}
let doBuildResult = (
this: t,
aContinuation: T.continuation,
accessors: ProjectAccessorsT.t,
): T.resultType =>
this
->getExpression
->Belt.Option.map(
Belt.Result.flatMap(
_,
Reducer_Expression.reduceExpressionInProject(_, aContinuation, accessors),
),
)
let buildResult = (this: t, aContinuation: T.continuation, accessors: ProjectAccessorsT.t): t => {
let withExpression: t = this->buildExpression
switch withExpression->getResult {
| Some(_) => withExpression
| None =>
withExpression
->doBuildResult(aContinuation, accessors)
->Belt.Option.map(setResult(withExpression, _))
->E.O2.defaultFn(() => withExpression)
}
}
let run = buildResult

View File

@ -0,0 +1,36 @@
module Parse = Reducer_Peggy_Parse
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Reducer_ErrorValue
type sourceArgumentType = string
type sourceType = string
type rawParseArgumentType = result<Parse.node, errorValue>
type rawParseType = option<rawParseArgumentType>
type expressionArgumentType = result<ExpressionT.t, errorValue>
type expressionType = option<expressionArgumentType>
type continuation = InternalExpressionValue.nameSpace
type continuationArgumentType = InternalExpressionValue.nameSpace
type continuationType = option<continuationArgumentType>
type continuationResultType = option<result<continuationArgumentType, errorValue>>
type bindingsArgumentType = InternalExpressionValue.nameSpace
type bindingsType = option<bindingsArgumentType>
type resultArgumentType = result<InternalExpressionValue.t, errorValue>
type resultType = option<resultArgumentType>
type continuesArgumentType = array<string>
type continuesType = array<string>
type includesArgumentType = string
type includesType = result<array<string>, errorValue>
type projectItem =
| ProjectItem({
source: sourceType,
rawParse: rawParseType,
expression: expressionType,
continuation: continuationArgumentType,
result: resultType,
continues: continuesType,
includes: includesType,
})
type t = projectItem

View File

@ -0,0 +1,9 @@
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
type t = (
ExpressionT.t,
ExpressionT.bindings,
ProjectAccessorsT.t,
) => result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>

View File

@ -0,0 +1,34 @@
module ProjectItem = ReducerProject_ProjectItem
module ExpressionT = Reducer_Expression_T
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
@genType.opaque
type project = {"tag": string}
//re-export
@genType
type t = project
module Private = {
type internalProject = {
"tag": string,
"items": Belt.Map.String.t<ProjectItem.t>,
"stdLib": Reducer_Bindings.t,
"environment": ExpressionT.environment,
}
type t = internalProject
@set
external setFieldItems: (t, Belt.Map.String.t<ProjectItem.t>) => unit = "items"
@set
external setFieldStdLib: (t, Reducer_Bindings.t) => unit = "stdLib"
@set
external setFieldEnvironment: (t, ExpressionT.environment) => unit = "stdLib"
external castFromInternalProject: t => project = "%identity"
external castToInternalProject: project => t = "%identity"
let getSourceIds = (this: t): array<string> => Belt.Map.String.keysToArray(this["items"])
let getItem = (this: t, sourceId: string) =>
Belt.Map.String.getWithDefault(this["items"], sourceId, ProjectItem.emptyItem)
}

View File

@ -0,0 +1,72 @@
module ProjectItem = ReducerProject_ProjectItem
module T = ReducerProject_T
type t = T.Private.t
let getSourceIds = T.Private.getSourceIds
let getItem = T.Private.getItem
let getImmediateDependencies = (this: t, sourceId: string): ProjectItem.T.includesType =>
getItem(this, sourceId)->ProjectItem.getImmediateDependencies
type topologicalSortState = (Belt.Map.String.t<bool>, list<string>)
let rec topologicalSortUtil = (
this: t,
sourceId: string,
state: topologicalSortState,
): topologicalSortState => {
let dependencies = getImmediateDependencies(this, sourceId)->Belt.Result.getWithDefault([])
let (visited, stack) = state
let myVisited = Belt.Map.String.set(visited, sourceId, true)
let (newVisited, newStack) = dependencies->Belt.Array.reduce((myVisited, stack), (
(currVisited, currStack),
dependency,
) => {
if !Belt.Map.String.getWithDefault(currVisited, dependency, false) {
topologicalSortUtil(this, dependency, (currVisited, currStack))
} else {
(currVisited, currStack)
}
})
(newVisited, list{sourceId, ...newStack})
}
let getTopologicalSort = (this: t): array<string> => {
let (_visited, stack) = getSourceIds(this)->Belt.Array.reduce((Belt.Map.String.empty, list{}), (
(currVisited, currStack),
currId,
) =>
if !Belt.Map.String.getWithDefault(currVisited, currId, false) {
topologicalSortUtil(this, currId, (currVisited, currStack))
} else {
(currVisited, currStack)
}
)
Belt.List.reverse(stack)->Belt.List.toArray
}
let getTopologicalSortFor = (this: t, sourceId) => {
let runOrder = getTopologicalSort(this)
let index = runOrder->Js.Array2.indexOf(sourceId)
let after = Belt.Array.sliceToEnd(runOrder, index + 1)
let before = Js.Array2.slice(runOrder, ~start=0, ~end_=index + 1)
(before, after)
}
let getRunOrder = getTopologicalSort
let getRunOrderFor = (this: t, sourceId: string) => {
let (runOrder, _) = getTopologicalSortFor(this, sourceId)
runOrder
}
let getDependencies = (this: t, sourceId: string): array<string> => {
let runOrder = getRunOrderFor(this, sourceId)
let _ = Js.Array2.pop(runOrder)
runOrder
}
let getDependents = (this: t, sourceId: string): array<string> => {
let (_, dependents) = getTopologicalSortFor(this, sourceId)
dependents
}

View File

@ -1,3 +1,4 @@
open ForTS__Types
/* /*
This is meant as a file to contain @genType declarations as needed for Typescript. This is meant as a file to contain @genType declarations as needed for Typescript.
I would ultimately want to have all @genType declarations here, vs. other files, but I would ultimately want to have all @genType declarations here, vs. other files, but
@ -7,59 +8,32 @@ would be preferable.
The below few seem to work fine. In the future there's definitely more work to do here. The below few seem to work fine. In the future there's definitely more work to do here.
*/ */
@genType // For backwards compatibility:
type samplingParams = GenericDist.env //Alternatives if one wants to keep the old habits
@genType type samplingParams = environment
@genType type squiggleValue_Dist = squiggleValue_Distribution //alternative
@genType type genericDist = squiggleValue_Distribution //alternative
@genType type sampleSetDist = sampleSetDistribution //alternative
@genType type symbolicDist = symbolicDistribution //alternative
@genType type resultDist = result<distribution, distributionError> //alternative
@genType type resultFloat = result<float, distributionError> //alternative
@genType type resultString = result<string, distributionError> //alternative
@genType @genType
type genericDist = DistributionTypes.genericDist let makeSampleSetDist: array<float> => result<
sampleSetDist,
SampleSetDist.sampleSetError,
> = SampleSetDist.make
//TODO: ForTS Interface module candid
@genType @genType
type sampleSetDist = SampleSetDist.t let toPointSet: (
squiggleValue_Distribution,
@genType ~xyPointLength: int,
type symbolicDist = SymbolicDistTypes.symbolicDist ~sampleCount: int,
~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?,
@genType unit,
type distributionError = DistributionTypes.error ) => result<PointSetTypes.pointSetDist, distributionError> = GenericDist.toPointSet
@genType
type resultDist = result<genericDist, distributionError>
@genType
type resultFloat = result<float, distributionError>
@genType
type resultString = result<string, distributionError>
@genType
let makeSampleSetDist = SampleSetDist.make
@genType
let evaluate = Reducer.evaluate
@genType
let evaluateUsingOptions = Reducer.evaluateUsingOptions
@genType
let parse = Reducer_Peggy_Parse.parse
@genType
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
@genType
type externalBindings = Reducer.externalBindings
@genType
type expressionValue = ReducerInterface_ExternalExpressionValue.t
@genType
type recordEV = ReducerInterface_ExternalExpressionValue.record
@genType
type errorValue = Reducer_ErrorValue.errorValue
@genType
let toPointSet = GenericDist.toPointSet
@genType @genType
type mixedShape = PointSetTypes.mixedShape type mixedShape = PointSetTypes.mixedShape
@ -71,31 +45,14 @@ type discreteShape = PointSetTypes.discreteShape
type continuousShape = PointSetTypes.continuousShape type continuousShape = PointSetTypes.continuousShape
@genType @genType
let errorValueToString = Reducer_ErrorValue.errorToString let distributionErrorToString = ForTS_Distribution_Error.toString
@genType @genType
let distributionErrorToString = DistributionTypes.Error.toString let defaultSamplingEnv = ForTS_Distribution.defaultEnvironment
@genType // Umur: opaqe types
type lambdaValue = ReducerInterface_ExternalExpressionValue.lambdaValue // @genType
// type declarationArg = Declaration.arg
@genType // @genType
type lambdaDeclaration = ReducerInterface_ExternalExpressionValue.lambdaDeclaration // type declaration<'a> = Declaration.declaration<'a>
@genType
let defaultSamplingEnv = DistributionOperation.defaultEnv
@genType
type environment = ReducerInterface_ExternalExpressionValue.environment
@genType
let defaultEnvironment = ReducerInterface_ExternalExpressionValue.defaultEnvironment
@genType
let foreignFunctionInterface = Reducer.foreignFunctionInterface
@genType
type declarationArg = Declaration.arg
@genType
type declaration<'a> = Declaration.declaration<'a>