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 Expect
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
let expectEvalToBe = (sourceCode: string, answer: string) =>
Expression.BackCompatible.evaluateString(sourceCode)
->ExpressionValue.toStringResult
->expect
->toBe(answer)
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))

View File

@ -1,6 +1,5 @@
// Reducer_Helpers
module ErrorValue = Reducer_ErrorValue
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
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 rRemoveDefaultsExternal = r => Belt.Result.map(r, removeDefaultsExternal)

View File

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

View File

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

View File

@ -3,77 +3,83 @@ open Reducer_Peggy_TestHelpers
describe("Peggy parse type", () => {
describe("type of", () => {
testParse("p: number", "{(::$_typeOf_$ :p #number)}")
testParse("p: number", "{(::$_typeOf_$ :p #number); (::$_endOfOuterBlock_$ () ())}")
})
describe("type alias", () => {
testParse("type index=number", "{(::$_typeAlias_$ #index #number)}")
testParse(
"type index=number",
"{(::$_typeAlias_$ #index #number); (::$_endOfOuterBlock_$ () ())}",
)
})
describe("type or", () => {
testParse(
"answer: number|string",
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string))))}",
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string)))); (::$_endOfOuterBlock_$ () ())}",
)
})
describe("type function", () => {
testParse(
"f: number=>number=>number",
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number)))); (::$_endOfOuterBlock_$ () ())}",
)
})
describe("high priority contract", () => {
testParse(
"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(
"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", () => {
testParse(
"answer: number | string $ opaque",
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
)
})
describe("type array", () => {
testParse("answer: [number]", "{(::$_typeOf_$ :answer (::$_typeArray_$ #number))}")
testParse(
"answer: [number]",
"{(::$_typeOf_$ :answer (::$_typeArray_$ #number)); (::$_endOfOuterBlock_$ () ())}",
)
})
describe("type record", () => {
testParse(
"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", () => {
testParse(
"answer: Age(number)",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number))))}",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number)))); (::$_endOfOuterBlock_$ () ())}",
)
testParse(
"answer: Complex(number, number)",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number))))}",
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number)))); (::$_endOfOuterBlock_$ () ())}",
)
testParse(
"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(
"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", () => {
//$ is introduced to avoid parenthesis
testParse(
"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", () => {
testParse(
"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 {
let a2 =
rExpr
->Result.flatMap(expr =>
Expression.reduceExpression(
expr,
ReducerInterface_StdLib.internalStdLib,
ExpressionValue.defaultEnvironment,
)
)
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
->Reducer_Helpers.rRemoveDefaultsInternal
->ExpressionValue.toStringResultOkless
(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("literals operators parenthesis", () => {
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
testToExpression("1", "{1}", ~v="1", ())
testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
testToExpression("true", "{true}", ~v="true", ())
testToExpression("1+2", "{(:add 1 2)}", ~v="3", ())
testToExpression("add(1,2)", "{(:add 1 2)}", ~v="3", ())
testToExpression("(1)", "{1}", ())
testToExpression("(1+2)", "{(:add 1 2)}", ())
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ())
testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ())
testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ())
testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ())
})
describe("unary", () => {
testToExpression("-1", "{(:unaryMinus 1)}", ~v="-1", ())
testToExpression("!true", "{(:not true)}", ~v="false", ())
testToExpression("1 + -1", "{(:add 1 (:unaryMinus 1))}", ~v="0", ())
testToExpression("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}", ())
testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ())
testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ())
testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ())
testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ())
})
describe("multi-line", () => {
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); 2}", ~v="2", ())
testToExpression("x=1; y=2", "{(:$_let_$ :x {1}); (:$_let_$ :y {2})}", ~v="@{x: 1,y: 2}", ())
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
testToExpression(
"x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
(),
)
})
describe("variables", () => {
testToExpression("x = 1", "{(:$_let_$ :x {1})}", ~v="@{x: 1}", ())
testToExpression("x", "{:x}", ~v=":x", ()) //TODO: value should return error
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); :x}", ~v="1", ())
testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ())
testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ())
})
describe("functions", () => {
testToExpression(
"identity(x) = x",
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x}))}",
~v="@{identity: lambda(x=>internal code)}",
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
(),
) // 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(
"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",
(),
)
})
describe("arrays", () => {
testToExpression("[]", "{(:$_constructArray_$ ())}", ~v="[]", ())
testToExpression("[0, 1, 2]", "{(:$_constructArray_$ (0 1 2))}", ~v="[0,1,2]", ())
testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ()))}", ~v="[]", ())
testToExpression(
"[0, 1, 2]",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ (0 1 2)))}",
~v="[0,1,2]",
(),
)
testToExpression(
"['hello', 'world']",
"{(:$_constructArray_$ ('hello' 'world'))}",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ('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", () => {
testToExpression(
"{a: 1, b: 2}",
"{(:$_constructRecord_$ (('a' 1) ('b' 2)))}",
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}",
~v="{a: 1,b: 2}",
(),
)
testToExpression(
"{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
testToExpression("record.property", "{(:$_atIndex_$ :record 'property')}", ())
testToExpression(
"record.property",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
(),
)
testToExpression(
"record={property: 1}; record.property",
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_atIndex_$ :record 'property')}",
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
~v="1",
(),
)
})
describe("comments", () => {
testToExpression("1 # This is a line comment", "{1}", ~v="1", ())
testToExpression("1 // This is a line comment", "{1}", ~v="1", ())
testToExpression("1 /* This is a multi line comment */", "{1}", ~v="1", ())
testToExpression("/* This is a multi line comment */ 1", "{1}", ~v="1", ())
testToExpression("1 # This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
testToExpression(
"1 /* This is a multi line comment */",
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
testToExpression(
"/* This is a multi line comment */ 1",
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
})
describe("ternary operator", () => {
testToExpression("true ? 1 : 0", "{(:$$_ternary_$$ true 1 0)}", ~v="1", ())
testToExpression("false ? 1 : 0", "{(:$$_ternary_$$ false 1 0)}", ~v="0", ())
testToExpression(
"true ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}",
~v="1",
(),
)
testToExpression(
"false ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}",
~v="0",
(),
)
testToExpression(
"true ? 1 : false ? 2 : 0",
"{(:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0))}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}",
~v="1",
(),
) // nested ternary
testToExpression(
"false ? 1 : false ? 2 : 0",
"{(:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0))}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}",
~v="0",
(),
) // nested ternary
@ -109,21 +146,21 @@ describe("Peggy to Expression", () => {
testToExpression(
// expression binding
"f(a) = a > 5 ? 1 : 0; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:f 6)}",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
~v="1",
(),
)
testToExpression(
// when true binding
"f(a) = a > 5 ? a : 0; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:f 6)}",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
~v="6",
(),
)
testToExpression(
// when false binding
"f(a) = a < 5 ? 1 : a; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:f 6)}",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}",
~v="6",
(),
)
@ -131,23 +168,41 @@ describe("Peggy to Expression", () => {
})
describe("if then else", () => {
testToExpression("if true then 2 else 3", "{(:$$_ternary_$$ true {2} {3})}", ())
testToExpression("if true then {2} else {3}", "{(:$$_ternary_$$ true {2} {3})}", ())
testToExpression(
"if true then 2 else 3",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
(),
)
testToExpression(
"if true then {2} else {3}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
(),
)
testToExpression(
"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
})
describe("pipe", () => {
testToExpression("1 -> add(2)", "{(:add 1 2)}", ~v="3", ())
testToExpression("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}", ~v="1", ()) // note that unary has higher priority naturally
testToExpression("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}", ~v="9", ())
testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
testToExpression(
"-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", () => {
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
@ -157,30 +212,31 @@ describe("Peggy to Expression", () => {
// Like lambdas they have a local scope.
testToExpression(
"y=99; x={y=1; y}",
"{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y})}",
~v="@{x: 1,y: 99}",
"{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}",
(),
)
})
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(
"f={|x| x}",
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})})}",
~v="@{f: lambda(x=>internal code)}",
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}",
(),
)
testToExpression(
"f(x)=x",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))}",
~v="@{f: lambda(x=>internal code)}",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
(),
) // Function definitions are lambda assignments
testToExpression(
"f(x)=x ? 1 : 0",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)}))}",
~v="@{f: lambda(x=>internal code)}",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}",
(),
)
})
@ -194,6 +250,6 @@ describe("Peggy to Expression", () => {
// ->expect
// ->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", () => {
testToExpression(
"p: number",
"{(:$_typeOf_$ :p #number)}",
~v="@{_typeReferences_: {p: #number}}",
"{(:$_typeOf_$ :p #number); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {p: #number}}",
(),
)
})
describe("type alias", () => {
testToExpression(
"type index=number",
"{(:$_typeAlias_$ #index #number)}",
~v="@{_typeAliases_: {index: #number}}",
"{(:$_typeAlias_$ #index #number); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeAliases_: {index: #number}}",
(),
)
})
describe("type or", () => {
testToExpression(
"answer: number|string|distribution",
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution))))}",
~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution)))); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
(),
)
})
describe("type function", () => {
testToExpression(
"f: number=>number=>number",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number))))}",
~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number)))); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
(),
)
testToExpression(
"f: number=>number",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number))))}",
~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number)))); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
(),
)
})
describe("high priority contract", () => {
testToExpression(
"answer: number<-min(1)<-max(100)|string",
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
"{(:$_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'}}}",
(),
)
testToExpression(
"answer: number<-memberOf([1,3,5])",
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5))))}",
~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5)))); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(),
)
testToExpression(
"answer: number<-min(1)",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1))}",
~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1)); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(),
)
testToExpression(
"answer: number<-max(10)",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10))}",
~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10)); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(),
)
testToExpression(
"answer: number<-min(1)<-max(10)",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10))}",
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10)); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(),
)
testToExpression(
"answer: number<-max(10)<-min(1)",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1))}",
~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1)); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
(),
)
})
describe("low priority contract", () => {
testToExpression(
"answer: number | string $ opaque",
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string))))); (:$_endOfOuterBlock_$ () ())}",
// ~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
(),
)
})
describe("squiggle expressions in type contracts", () => {
testToExpression(
"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)))}",
~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
"{(:$_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]}",
(),
)
})

View File

@ -3,18 +3,23 @@ open Reducer_Peggy_TestHelpers
describe("Peggy void", () => {
//literal
testToExpression("()", "{()}", ~v="()", ())
testToExpression("()", "{(:$_endOfOuterBlock_$ () ())}", ~v="()", ())
testToExpression(
"fn()=1",
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1}))}",
~v="@{fn: lambda(_=>internal code)}",
"{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () ())}",
// ~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(
"fn(a)=(); call fn(1)",
"{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)})}",
~v="@{_: (),fn: lambda(a=>internal code)}",
"{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)}); (:$_endOfOuterBlock_$ () ())}",
// ~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 Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
open Jest
open Expect
@ -8,30 +9,26 @@ open Expect
let unwrapRecord = rValue =>
rValue->Belt.Result.flatMap(value =>
switch value {
| ExternalExpressionValue.EvRecord(aRecord) => Ok(aRecord)
| _ => ErrorValue.RETodo("TODO: External bindings must be returned")->Error
| InternalExpressionValue.IEvRecord(aRecord) => Ok(aRecord)
| _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error
}
)
let expectParseToBe = (expr: string, answer: string) =>
Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer)
let expectParseToBe = (code: string, answer: string) =>
Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer)
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.evaluate(expr)
->Reducer_Helpers.rRemoveDefaultsExternal
->ExternalExpressionValue.toStringResult
let expectEvalToBe = (code: string, answer: string) =>
Expression.BackCompatible.evaluateString(code)
->Reducer_Helpers.rRemoveDefaultsInternal
->InternalExpressionValue.toStringResult
->expect
->toBe(answer)
let expectEvalError = (expr: string) =>
Reducer.evaluate(expr)->ExternalExpressionValue.toStringResult->expect->toMatch("Error\(")
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
->Reducer_Helpers.rRemoveDefaultsExternal
->ExternalExpressionValue.toStringResult
let expectEvalError = (code: string) =>
Expression.BackCompatible.evaluateString(code)
->InternalExpressionValue.toStringResult
->expect
->toBe(answer)
->toMatch("Error\(")
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
let testDescriptionParseToBe = (desc, expr, answer) =>
@ -40,18 +37,12 @@ let testDescriptionParseToBe = (desc, expr, answer) =>
let testEvalError = expr => test(expr, () => expectEvalError(expr))
let testEvalToBe = (expr, answer) => test(expr, () => 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 = {
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(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 = {
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(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 Expect
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Expression = Reducer_Expression
// module ExpressionValue = ReducerInterface.ExpressionValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module Macro = Reducer_Expression_Macro
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Expression_T
module Bindings = Reducer_Bindings
let testMacro_ = (
tester,
@ -21,8 +21,8 @@ let testMacro_ = (
expr
->Macro.expandMacroCall(
bindings,
InternalExpressionValue.defaultEnvironment,
Expression.reduceExpression,
ProjectAccessorsT.identityAccessors,
Expression.reduceExpressionInProject,
)
->ExpressionWithContext.toStringResult
->expect
@ -41,8 +41,8 @@ let testMacroEval_ = (
expr
->Macro.doMacroCall(
bindings,
InternalExpressionValue.defaultEnvironment,
Expression.reduceExpression,
ProjectAccessorsT.identityAccessors,
Expression.reduceExpressionInProject,
)
->InternalExpressionValue.toStringResult
->expect

View File

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

View File

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

View File

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

View File

@ -4,8 +4,11 @@ open Expect
module DispatchT = Reducer_Dispatch_T
module Expression = Reducer_Expression
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 TypeCompile = Reducer_Type_Compile
open ReducerInterface_InternalExpressionValue
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.
// Otherwise we return None so that the call chain can continue.
// 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.
// 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.
let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispatchChainPiece => {
let makeMyDispatchChainPiece = (reducer: ProjectReducerFnT.t): DispatchT.dispatchChainPiece => {
// Let's have a pure implementations
module Implementation = {
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
module Bridge = {
let stringConcat: DispatchT.genericIEvFunction = (args, _environment) => {
let stringConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
let (a, b) = extractStringString(args)
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)
Implementation.arrayConcat(a, b)->IEvArray->Ok
}
let plot: DispatchT.genericIEvFunction = (args, _environment) => {
let plot: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
switch args {
// 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
@ -98,12 +102,12 @@ let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispa
// Exactly the same as the one used in real life
let _dispatch = (
call: functionCall,
environment,
reducer: Reducer_Expression_T.reducerFn,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
chain,
): result<internalExpressionValue, 'e> => {
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?
@ -112,12 +116,12 @@ let _dispatch = (
// B) Complicated recursive record types are not a problem.
describe("Type Dispatch", () => {
let reducerFn = Expression.reduceExpression
let reducerFn = Expression.reduceExpressionInProject
let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn)
test("stringConcat", () => {
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"))))
})
})

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
describe("Parse function assignment", () => {
testParseToBe("f(x)=x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))})")
testParseToBe("f(x)=2*x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)}))})")
testParseToBe(
"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
})

View File

@ -39,33 +39,27 @@ describe("symbol not defined", () => {
})
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=1;y=2", "Ok(@{f: 1,y: 2})")
testEvalToBe("f(x)=x+1; y=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2})")
testEvalToBe("f=1;y=2", "Ok(())")
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); z=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2,z: 2})")
testEvalToBe(
"f(x)=x+1; g(x)=f(x)+1",
"Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code)})",
)
testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)")
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)")
testParseToBe(
"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(x)=x; g(x)=f(x); g(2)", "Ok(2)")
testEvalToBe(
"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; y=g(2); y", "Ok(4)")
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)")
})
describe("function tricks", () => {
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)=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+x; f(1,2)", "????") // TODO: Duplicate parameters
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", () => {
testEvalToBe(
"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]", "Ok(())")
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("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
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", () => {

View File

@ -48,7 +48,7 @@ describe("eval", () => {
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
testEvalError("1; x=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("power", () => {
testParse("normal(5,2) ^ normal(5,1)", "Ok({(:pow (:normal 5 2) (:normal 5 1))})")
testParse("3 ^ normal(5,1)", "Ok({(:pow 3 (:normal 5 1))})")
testParse("normal(5,2) ^ 3", "Ok({(:pow (:normal 5 2) 3)})")
testParse(
"normal(5,2) ^ normal(5,1)",
"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", () => {
testParse("10 - normal(5,1)", "Ok({(:subtract 10 (:normal 5 1))})")
testParse("normal(5,1) - 10", "Ok({(:subtract (:normal 5 1) 10)})")
testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})")
testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})")
})
describe("pointwise arithmetic expressions", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse(
~skip=true,
"normal(5,2) .- normal(5,1)",
"Ok((:$$_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))})"
)
testParse("normal(5,2) .* normal(5,1)", "Ok({(:dotMultiply (:normal 5 2) (:normal 5 1))})")
testParse("normal(5,2) ./ normal(5,1)", "Ok({(:dotDivide (:normal 5 2) (:normal 5 1))})")
testParse("normal(5,2) .^ normal(5,1)", "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})")
testParse(
"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", () => {
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", () => {
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 Expect
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", () =>
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 Reducer_TestHelpers
let expectEvalToBeOk = (expr: string) =>
Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
let expectEvalToBeOk = (code: string) =>
Reducer_Expression.BackCompatible.evaluateString(code)
->Reducer_Helpers.rRemoveDefaultsInternal
->E.R.isOk
->expect
->toBe(true)
let registry = FunctionRegistry_Library.registry
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
@ -88,8 +92,8 @@ describe("FunctionRegistry Library", () => {
((fn, example)) => {
let responseType =
example
->Reducer.evaluate
->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
->Reducer_Expression.BackCompatible.evaluateString
->E.R2.fmap(ReducerInterface_InternalExpressionValue.valueToValueType)
let expectedOutputType = fn.output |> E.O.toExn("")
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 type {
environment,
@ -44,162 +56,164 @@ export let defaultSamplingInputs: environment = {
xyPointLength: 10000,
};
export function run(
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));
}
/* Umur: All the functions below are invalid. ForTS_Reducer project is the new way to do this. */
// Run Partial. A partial is a block of code that doesn't return a value
export function runPartial(
squiggleString: string,
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 run(
// 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));
// }
return evaluatePartialUsingExternalBindings(
squiggleString,
mergeImportsWithBindings(b, i),
e
);
}
// // Run Partial. A partial is a block of code that doesn't return a value
// export function runPartial(
// squiggleString: string,
// 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(
fn: lambdaValue,
args: jsValue[],
environment?: environment
): 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));
}
// return evaluatePartialUsingExternalBindings(
// squiggleString,
// mergeImportsWithBindings(b, i),
// e
// );
// }
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 function runForeign(
// fn: lambdaValue,
// args: jsValue[],
// environment?: environment
// ): 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));
// }
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 = {};
export let defaultBindings: externalBindings = {};
// type jsImports = { [key: string]: jsValue };
export function mergeBindings(
allBindings: externalBindings[]
): externalBindings {
return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
}
// export let defaultImports: jsImports = {};
// export let defaultBindings: externalBindings = {};
function createTsExport(
x: expressionValue,
environment: environment
): squiggleExpression {
switch (x) {
case "EvVoid":
return tag("void", "");
default: {
switch (x.tag) {
case "EvArray":
// genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
// format, leaving it as the raw values. This converts the raw values
// directly into typescript values.
//
// The casting here is because genType is about the types of the returned
// values, claiming they are fully recursive when that's not actually the
// case
return tag(
"array",
x.value.map(
(arrayItem): squiggleExpression =>
convertRawToTypescript(
arrayItem as unknown as rescriptExport,
environment
)
)
);
case "EvArrayString":
return tag("arraystring", x.value);
case "EvBool":
return tag("boolean", x.value);
case "EvCall":
return tag("call", x.value);
case "EvLambda":
return tag("lambda", x.value);
case "EvDistribution":
return tag("distribution", new Distribution(x.value, environment));
case "EvNumber":
return tag("number", x.value);
case "EvRecord":
// genType doesn't support records, so we have to do the raw conversion ourself
let result: tagged<"record", { [key: string]: squiggleExpression }> =
tag(
"record",
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, environment)
)
);
return result;
case "EvString":
return tag("string", x.value);
case "EvSymbol":
return tag("symbol", x.value);
case "EvDate":
return tag("date", x.value);
case "EvTimeDuration":
return tag("timeDuration", x.value);
case "EvDeclaration":
return tag("lambdaDeclaration", x.value);
case "EvTypeIdentifier":
return tag("typeIdentifier", x.value);
case "EvType":
let typeResult: tagged<
"type",
{ [key: string]: squiggleExpression }
> = tag(
"type",
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, environment)
)
);
return typeResult;
case "EvModule":
let moduleResult: tagged<
"module",
{ [key: string]: squiggleExpression }
> = tag(
"module",
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, environment)
)
);
return moduleResult;
}
}
}
}
// export function mergeBindings(
// allBindings: externalBindings[]
// ): externalBindings {
// return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
// }
// function createTsExport(
// x: expressionValue,
// environment: environment
// ): squiggleExpression {
// switch (x) {
// case "EvVoid":
// return tag("void", "");
// default: {
// switch (x.tag) {
// case "EvArray":
// // genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
// // format, leaving it as the raw values. This converts the raw values
// // directly into typescript values.
// //
// // The casting here is because genType is about the types of the returned
// // values, claiming they are fully recursive when that's not actually the
// // case
// return tag(
// "array",
// x.value.map(
// (arrayItem): squiggleExpression =>
// convertRawToTypescript(
// arrayItem as unknown as rescriptExport,
// environment
// )
// )
// );
// case "EvArrayString":
// return tag("arraystring", x.value);
// case "EvBool":
// return tag("boolean", x.value);
// case "EvCall":
// return tag("call", x.value);
// case "EvLambda":
// return tag("lambda", x.value);
// case "EvDistribution":
// return tag("distribution", new Distribution(x.value, environment));
// case "EvNumber":
// return tag("number", x.value);
// case "EvRecord":
// // genType doesn't support records, so we have to do the raw conversion ourself
// let result: tagged<"record", { [key: string]: squiggleExpression }> =
// tag(
// "record",
// _.mapValues(x.value, (x: unknown) =>
// convertRawToTypescript(x as rescriptExport, environment)
// )
// );
// return result;
// case "EvString":
// return tag("string", x.value);
// case "EvSymbol":
// return tag("symbol", x.value);
// case "EvDate":
// return tag("date", x.value);
// case "EvTimeDuration":
// return tag("timeDuration", x.value);
// case "EvDeclaration":
// return tag("lambdaDeclaration", x.value);
// case "EvTypeIdentifier":
// return tag("typeIdentifier", x.value);
// case "EvType":
// let typeResult: tagged<
// "type",
// { [key: string]: squiggleExpression }
// > = tag(
// "type",
// _.mapValues(x.value, (x: unknown) =>
// convertRawToTypescript(x as rescriptExport, environment)
// )
// );
// return typeResult;
// case "EvModule":
// let moduleResult: tagged<
// "module",
// { [key: string]: squiggleExpression }
// > = 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 {
expressionValue,
mixedShape,
sampleSetDist,
genericDist,
environment,
symbolicDist,
discreteShape,
continuousShape,
lambdaValue,
lambdaDeclaration,
declarationArg,
} from "../rescript/TypescriptInterface.gen";
import { Distribution } from "./distribution";
import { tagged, tag } from "./types";
/**
Umur: Delete this file! There is nothing left to see here.
**/
// import * as _ from "lodash";
// import type {
// // expressionValue,
// mixedShape,
// sampleSetDist,
// genericDist,
// // environment,
// symbolicDist,
// discreteShape,
// continuousShape,
// // lambdaValue,
// // 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
// Raw rescript types.
export type rescriptExport =
| 0 // EvVoid
| {
TAG: 0; // EvArray
_0: rescriptExport[];
}
| {
TAG: 1; // EvString
_0: string[];
}
| {
TAG: 2; // EvBool
_0: boolean;
}
| {
TAG: 3; // EvCall
_0: string;
}
| {
TAG: 4; // EvDistribution
_0: rescriptDist;
}
| {
TAG: 5; // EvLambda
_0: lambdaValue;
}
| {
TAG: 6; // EvNumber
_0: number;
}
| {
TAG: 7; // EvRecord
_0: { [key: string]: rescriptExport };
}
| {
TAG: 8; // EvString
_0: string;
}
| {
TAG: 9; // EvSymbol
_0: string;
}
| {
TAG: 10; // EvDate
_0: Date;
}
| {
TAG: 11; // EvTimeDuration
_0: number;
}
| {
TAG: 12; // EvDeclaration
_0: rescriptLambdaDeclaration;
}
| {
TAG: 13; // EvTypeIdentifier
_0: string;
}
| {
TAG: 14; // EvModule
_0: { [key: string]: rescriptExport };
};
// Umur: Rescript expression values are opaque!
// export type rescriptExport =
// | 0 // EvVoid
// | {
// TAG: 0; // EvArray
// _0: rescriptExport[];
// }
// | {
// TAG: 1; // EvString
// _0: string[];
// }
// | {
// TAG: 2; // EvBool
// _0: boolean;
// }
// | {
// TAG: 3; // EvCall
// _0: string;
// }
// | {
// TAG: 4; // EvDistribution
// _0: rescriptDist;
// }
// | {
// TAG: 5; // EvLambda
// _0: lambdaValue;
// }
// | {
// TAG: 6; // EvNumber
// _0: number;
// }
// | {
// TAG: 7; // EvRecord
// _0: { [key: string]: rescriptExport };
// }
// | {
// TAG: 8; // EvString
// _0: string;
// }
// | {
// TAG: 9; // EvSymbol
// _0: string;
// }
// | {
// TAG: 10; // EvDate
// _0: Date;
// }
// | {
// TAG: 11; // EvTimeDuration
// _0: number;
// }
// | {
// TAG: 12; // EvDeclaration
// _0: rescriptLambdaDeclaration;
// }
// | {
// TAG: 13; // EvTypeIdentifier
// _0: string;
// }
// | {
// TAG: 14; // EvModule
// _0: { [key: string]: rescriptExport };
// };
type rescriptDist =
| { TAG: 0; _0: rescriptPointSetDist }
| { TAG: 1; _0: sampleSetDist }
| { TAG: 2; _0: symbolicDist };
// Umur: opaque type
// type rescriptDist =
// | { TAG: 0; _0: rescriptPointSetDist }
// | { TAG: 1; _0: sampleSetDist }
// | { TAG: 2; _0: symbolicDist };
type rescriptPointSetDist =
| {
TAG: 0; // Mixed
_0: mixedShape;
}
| {
TAG: 1; // Discrete
_0: discreteShape;
}
| {
TAG: 2; // ContinuousShape
_0: continuousShape;
};
// Umur: opaque type, no conversion
// type rescriptPointSetDist =
// | {
// TAG: 0; // Mixed
// _0: mixedShape;
// }
// | {
// TAG: 1; // Discrete
// _0: discreteShape;
// }
// | {
// TAG: 2; // ContinuousShape
// _0: continuousShape;
// };
type rescriptLambdaDeclaration = {
readonly fn: lambdaValue;
readonly args: rescriptDeclarationArg[];
};
// type rescriptLambdaDeclaration = {
// readonly fn: lambdaValue;
// readonly args: rescriptDeclarationArg[];
// };
type rescriptDeclarationArg =
| {
TAG: 0; // Float
min: number;
max: number;
}
| {
TAG: 1; // Date
min: Date;
max: Date;
};
// type rescriptDeclarationArg =
// | {
// TAG: 0; // Float
// min: number;
// max: number;
// }
// | {
// TAG: 1; // Date
// min: Date;
// max: Date;
// };
export type squiggleExpression =
| tagged<"symbol", string>
| tagged<"string", string>
| tagged<"call", string>
| tagged<"lambda", lambdaValue>
| tagged<"array", squiggleExpression[]>
| tagged<"arraystring", string[]>
| tagged<"boolean", boolean>
| tagged<"distribution", Distribution>
| tagged<"number", number>
| tagged<"date", Date>
| tagged<"timeDuration", number>
| tagged<"lambdaDeclaration", lambdaDeclaration>
| tagged<"record", { [key: string]: squiggleExpression }>
| tagged<"type", { [key: string]: squiggleExpression }>
| tagged<"typeIdentifier", string>
| tagged<"module", { [key: string]: squiggleExpression }>
| tagged<"void", string>;
// Umur: Squiggle expressions are opaque!
// export type squiggleExpression =
// | tagged<"symbol", string>
// | tagged<"string", string>
// | tagged<"call", string>
// | tagged<"lambda", lambdaValue>
// | tagged<"array", squiggleExpression[]>
// | tagged<"arraystring", string[]>
// | tagged<"boolean", boolean>
// | tagged<"distribution", Distribution>
// | tagged<"number", number>
// | tagged<"date", Date>
// | tagged<"timeDuration", number>
// | tagged<"lambdaDeclaration", lambdaDeclaration>
// | tagged<"record", { [key: string]: squiggleExpression }>
// | tagged<"type", { [key: string]: squiggleExpression }>
// | tagged<"typeIdentifier", string>
// | tagged<"module", { [key: string]: squiggleExpression }>
// | tagged<"void", string>;
export { lambdaValue };
// export { lambdaValue };
export function convertRawToTypescript(
result: rescriptExport,
environment: environment
): squiggleExpression {
if (typeof result === "number") {
// EvVoid
return tag("void", "");
}
switch (result.TAG) {
case 0: // EvArray
return tag(
"array",
result._0.map((x) => convertRawToTypescript(x, environment))
);
case 1: // EvArrayString
return tag("arraystring", result._0);
case 2: // EvBool
return tag("boolean", result._0);
case 3: // EvCall
return tag("call", result._0);
case 4: // EvDistribution
return tag(
"distribution",
new Distribution(
convertRawDistributionToGenericDist(result._0),
environment
)
);
case 5: // EvDistribution
return tag("lambda", result._0);
case 6: // EvNumber
return tag("number", result._0);
case 7: // EvRecord
return tag(
"record",
_.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
);
case 8: // EvString
return tag("string", result._0);
case 9: // EvSymbol
return tag("symbol", result._0);
case 10: // EvDate
return tag("date", result._0);
case 11: // EvTimeDuration
return tag("number", result._0);
case 12: // EvDeclaration
return tag("lambdaDeclaration", {
fn: result._0.fn,
args: result._0.args.map(convertDeclaration),
});
case 13: // EvSymbol
return tag("typeIdentifier", result._0);
case 14: // EvModule
return tag(
"module",
_.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
);
}
}
// Umur: Opaque type no conversion!
// export function convertRawToTypescript(
// result: rescriptExport,
// environment: environment
// ): squiggleExpression {
// if (typeof result === "number") {
// // EvVoid
// return tag("void", "");
// }
// switch (result.TAG) {
// case 0: // EvArray
// return tag(
// "array",
// result._0.map((x) => convertRawToTypescript(x, environment))
// );
// case 1: // EvArrayString
// return tag("arraystring", result._0);
// case 2: // EvBool
// return tag("boolean", result._0);
// case 3: // EvCall
// return tag("call", result._0);
// case 4: // EvDistribution
// return tag(
// "distribution",
// new Distribution(
// convertRawDistributionToGenericDist(result._0),
// environment
// )
// );
// case 5: // EvDistribution
// return tag("lambda", result._0);
// case 6: // EvNumber
// return tag("number", result._0);
// case 7: // EvRecord
// return tag(
// "record",
// _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
// );
// case 8: // EvString
// return tag("string", result._0);
// case 9: // EvSymbol
// return tag("symbol", result._0);
// case 10: // EvDate
// return tag("date", result._0);
// case 11: // EvTimeDuration
// return tag("number", result._0);
// case 12: // EvDeclaration
// return tag("lambdaDeclaration", {
// fn: result._0.fn,
// args: result._0.args.map(convertDeclaration),
// });
// case 13: // EvSymbol
// return tag("typeIdentifier", result._0);
// case 14: // EvModule
// return tag(
// "module",
// _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
// );
// }
// }
function convertDeclaration(
declarationArg: rescriptDeclarationArg
): declarationArg {
switch (declarationArg.TAG) {
case 0: // Float
return tag("Float", { min: declarationArg.min, max: declarationArg.max });
case 1: // Date
return tag("Date", { min: declarationArg.min, max: declarationArg.max });
}
}
// function convertDeclaration(
// declarationArg: rescriptDeclarationArg
// ): declarationArg {
// switch (declarationArg.TAG) {
// case 0: // Float
// return tag("Float", { min: declarationArg.min, max: declarationArg.max });
// case 1: // Date
// return tag("Date", { min: declarationArg.min, max: declarationArg.max });
// }
// }
function convertRawDistributionToGenericDist(
result: rescriptDist
): genericDist {
switch (result.TAG) {
case 0: // Point Set Dist
switch (result._0.TAG) {
case 0: // Mixed
return tag("PointSet", tag("Mixed", result._0._0));
case 1: // Discrete
return tag("PointSet", tag("Discrete", 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 2: // Symbolic Dist
return tag("Symbolic", result._0);
}
}
// Umur: opaque type no conversion!
// function convertRawDistributionToGenericDist(
// result: rescriptDist
// ): genericDist {
// switch (result.TAG) {
// case 0: // Point Set Dist
// switch (result._0.TAG) {
// case 0: // Mixed
// return tag("PointSet", tag("Mixed", result._0._0));
// case 1: // Discrete
// return tag("PointSet", tag("Discrete", 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 2: // Symbolic Dist
// return tag("Symbolic", result._0);
// }
// }
export type jsValue =
| string
| number
| jsValue[]
| { [key: string]: jsValue }
| boolean;
// export type jsValue =
// | string
// | number
// | jsValue[]
// | { [key: string]: jsValue }
// | boolean;
export function jsValueToBinding(value: jsValue): rescriptExport {
if (typeof value === "boolean") {
return { TAG: 2, _0: value as boolean };
} else if (typeof value === "string") {
return { TAG: 8, _0: value as string };
} else if (typeof value === "number") {
return { TAG: 6, _0: value as number };
} else if (Array.isArray(value)) {
return { TAG: 0, _0: value.map(jsValueToBinding) };
} else {
// Record
return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
}
}
// export function jsValueToBinding(value: jsValue): rescriptExport {
// if (typeof value === "boolean") {
// return { TAG: 2, _0: value as boolean };
// } else if (typeof value === "string") {
// return { TAG: 8, _0: value as string };
// } else if (typeof value === "number") {
// return { TAG: 6, _0: value as number };
// } else if (Array.isArray(value)) {
// return { TAG: 0, _0: value.map(jsValueToBinding) };
// } else {
// // Record
// return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
// }
// }
export function jsValueToExpressionValue(value: jsValue): expressionValue {
if (typeof value === "boolean") {
return { tag: "EvBool", value: value as boolean };
} else if (typeof value === "string") {
return { tag: "EvString", value: value as string };
} else if (typeof value === "number") {
return { tag: "EvNumber", value: value as number };
} else if (Array.isArray(value)) {
return { tag: "EvArray", value: value.map(jsValueToExpressionValue) };
} else {
// Record
return {
tag: "EvRecord",
value: _.mapValues(value, jsValueToExpressionValue),
};
}
}
// export function jsValueToExpressionValue(value: jsValue): expressionValue {
// if (typeof value === "boolean") {
// return { tag: "EvBool", value: value as boolean };
// } else if (typeof value === "string") {
// return { tag: "EvString", value: value as string };
// } else if (typeof value === "number") {
// return { tag: "EvNumber", value: value as number };
// } else if (Array.isArray(value)) {
// return { tag: "EvArray", value: value.map(jsValueToExpressionValue) };
// } else {
// // Record
// return {
// tag: "EvRecord",
// value: _.mapValues(value, jsValueToExpressionValue),
// };
// }
// }

View File

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

View File

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

View File

@ -47,7 +47,7 @@ type pointSetDistMonad<'a, 'b, 'c> =
| Discrete('b)
| Continuous('c)
@genType
@genType.opaque
type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape>
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 internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
@ -46,8 +48,8 @@ type fnDefinition = {
run: (
array<internalExpressionValue>,
array<frValue>,
GenericDist.env,
Reducer_Expression_T.reducerFn,
ProjectAccessorsT.t,
ProjectReducerFnT.t,
) => result<internalExpressionValue, string>,
}
@ -382,12 +384,12 @@ module FnDefinition = {
let run = (
t: t,
args: array<internalExpressionValue>,
env: GenericDist.env,
reducer: Reducer_Expression_T.reducerFn,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
switch argValues {
| Some(values) => t.run(args, values, env, reducer)
| Some(values) => t.run(args, values, accessors, reducer)
| None => Error("Incorrect Types")
}
}
@ -493,8 +495,8 @@ module Registry = {
~registry: registry,
~fnName: string,
~args: array<internalExpressionValue>,
~env: GenericDist.env,
~reducer: Reducer_Expression_T.reducerFn,
~accessors: ProjectAccessorsT.t,
~reducer: ProjectReducerFnT.t,
) => {
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
@ -512,7 +514,7 @@ module Registry = {
switch Matcher.Registry.findMatches(modified, fnName, args) {
| 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)))
| _ => None
}
@ -521,10 +523,10 @@ module Registry = {
let dispatch = (
registry,
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall,
env,
reducer: Reducer_Expression_T.reducerFn,
accessors: ProjectAccessorsT.t,
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)),
)
}

View File

@ -21,8 +21,8 @@ module DistributionCreation = {
FnDefinition.make(
~name,
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
~run=(_, inputs, env, _) =>
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
~run=(_, inputs, accessors, _) =>
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=accessors.environment),
(),
)
}
@ -31,8 +31,10 @@ module DistributionCreation = {
FnDefinition.make(
~name,
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) =>
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
~run=(_, inputs, accessors, _) =>
inputs
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(),
)
}
@ -41,8 +43,10 @@ module DistributionCreation = {
FnDefinition.make(
~name,
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) =>
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber->process(~fn, ~env),
~run=(_, inputs, accessors, _) =>
inputs
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(),
)
}
@ -58,8 +62,8 @@ module DistributionCreation = {
FnDefinition.make(
~name,
~inputs=[FRTypeDistOrNumber],
~run=(_, inputs, env, _) =>
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
~run=(_, inputs, accessors, _) =>
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_Helpers
@ -24,17 +27,19 @@ module Internals = {
Belt.Array.reverse(array),
)
let map = (array: array<internalExpressionValue>, environment, eLambdaValue, reducer): result<
ReducerInterface_InternalExpressionValue.t,
Reducer_ErrorValue.errorValue,
> => {
let map = (
array: array<internalExpressionValue>,
accessors: ProjectAccessorsT.t,
eLambdaValue,
reducer: ProjectReducerFnT.t,
): result<ReducerInterface_InternalExpressionValue.t, Reducer_ErrorValue.errorValue> => {
let rMappedList = array->E.A.reduceReverse(Ok(list{}), (rAcc, elem) =>
rAcc->E.R.bind(_, acc => {
let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
eLambdaValue,
list{elem},
environment,
reducer,
(accessors: ProjectAccessorsT.t),
(reducer: ProjectReducerFnT.t),
)
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)
}
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) =>
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) =>
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) =>
rAcc->E.R.bind(_, acc => {
let rNewElem = Reducer_Expression_Lambda.doLambdaCall(
aLambdaValue,
list{elem},
environment,
accessors,
reducer,
)
rNewElem->E.R2.fmap(newElem => {
@ -189,10 +211,10 @@ let library = [
FnDefinition.make(
~name="map",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, env, reducer) =>
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs {
| [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)
},
(),
@ -209,10 +231,12 @@ let library = [
FnDefinition.make(
~name="reduce",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, env, reducer) =>
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) =>
switch inputs {
| [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)
},
(),
@ -229,12 +253,16 @@ let library = [
FnDefinition.make(
~name="reduceReverse",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, env, reducer) =>
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
switch inputs {
| [IEvArray(array), initialValue, IEvLambda(lambda)] =>
Internals.reduceReverse(array, initialValue, lambda, env, reducer)->E.R2.errMap(_ =>
"Error!"
)
Internals.reduceReverse(
array,
initialValue,
lambda,
accessors,
reducer,
)->E.R2.errMap(_ => "Error!")
| _ => Error(impossibleError)
},
(),
@ -251,10 +279,10 @@ let library = [
FnDefinition.make(
~name="filter",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, env, reducer) =>
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) =>
switch inputs {
| [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)
},
(),

View File

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

View File

@ -30,16 +30,16 @@ let library = [
("prior", FRTypeDist),
]),
],
~run=(_, inputs, env, _) => {
~run=(_, inputs, accessors, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) {
| 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([
FRValueDist(estimate),
FRValueDistOrNumber(FRValueNumber(d)),
FRValueDist(prior),
]) =>
runScoring(estimate, Score_Scalar(d), Some(prior), env)
runScoring(estimate, Score_Scalar(d), Some(prior), accessors.environment)
| Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError)
}
@ -49,12 +49,12 @@ let library = [
FnDefinition.make(
~name="logScore",
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
~run=(_, inputs, env, _) => {
~run=(_, inputs, accessors, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) {
| 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))]) =>
runScoring(estimate, Score_Scalar(d), None, env)
runScoring(estimate, Score_Scalar(d), None, accessors.environment)
| Error(e) => Error(e)
| _ => Error(FunctionRegistry_Helpers.impossibleError)
}
@ -74,10 +74,10 @@ let library = [
FnDefinition.make(
~name="klDivergence",
~inputs=[FRTypeDist, FRTypeDist],
~run=(_, inputs, env, _) => {
~run=(_, inputs, accessors, _) => {
switch inputs {
| [FRValueDist(estimate), FRValueDist(d)] =>
runScoring(estimate, Score_Dist(d), None, env)
runScoring(estimate, Score_Dist(d), None, accessors.environment)
| _ => 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 emptyBindings = emptyModule
let emptyNameSpace = emptyModule
let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
// let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
// let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace)
let fromExpressionValue = (aValue: internalExpressionValue): t =>

View File

@ -1,11 +1,15 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Continuation = ReducerInterface_Value_Continuation
module ExpressionT = Reducer_Expression_T
module ExternalLibrary = ReducerInterface.ExternalLibrary
module Lambda = Reducer_Expression_Lambda
module MathJs = Reducer_MathJs
module Bindings = Reducer_Bindings
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result
module TypeBuilder = Reducer_Type_TypeBuilder
open ReducerInterface_InternalExpressionValue
open Reducer_ErrorValue
@ -19,10 +23,11 @@ open Reducer_ErrorValue
exception TestRescriptException
let callInternal = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result<
'b,
errorValue,
> => {
let callInternal = (
call: functionCall,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): result<'b, errorValue> => {
let callMathJs = (call: functionCall): result<'b, errorValue> =>
switch call {
| ("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 doIdentity = (value: internalExpressionValue) => value->Ok
let doDumpBindings = (continuation: nameSpace, value: internalExpressionValue) => {
// let _ = Continuation.inspect(continuation, "doDumpBindings")
accessors.states.continuation = continuation
value->Ok
}
module SampleMap = {
let doLambdaCall = (aLambdaValue, list) =>
switch Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) {
switch Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
| Ok(IEvNumber(f)) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
}
@ -137,12 +150,14 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
| ("$_constructArray_$", [IEvArray(aValueArray)]) => IEvArray(aValueArray)->Ok
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
| ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace)
| ("$_exportBindings_$", [evValue]) => doIdentity(evValue)
| ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
doSetBindings(nameSpace, symbol, value)
| ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) =>
doSetTypeAliasBindings(nameSpace, symbol, value)
| ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) =>
doSetTypeOfBindings(nameSpace, symbol, value)
| ("$_dumpBindings_$", [IEvBindings(nameSpace), _, evValue]) => doDumpBindings(nameSpace, evValue)
| ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) =>
TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), 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
*/
let dispatch = (call: functionCall, environment, reducer: ExpressionT.reducerFn): result<
internalExpressionValue,
errorValue,
> =>
let dispatch = (
call: functionCall,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, errorValue> =>
try {
let (fn, args) = call
// There is a bug that prevents string match in patterns
// 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 {
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
| _ => RETodo("unhandled rescript exception")->Error

View File

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

View File

@ -1,20 +1,22 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
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
type dispatchChainPiece = (
InternalExpressionValue.functionCall,
InternalExpressionValue.environment,
ProjectAccessorsT.t,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
type dispatchChainPieceWithReducer = (
InternalExpressionValue.functionCall,
InternalExpressionValue.environment,
ExpressionT.reducerFn,
ProjectAccessorsT.t,
ProjectReducerFnT.t,
) => option<result<InternalExpressionValue.t, Reducer_ErrorValue.errorValue>>
// This is a switch statement case implementation: get the arguments and compute the result
type genericIEvFunction = (
array<InternalExpressionValue.t>,
InternalExpressionValue.environment,
ProjectAccessorsT.t,
) => 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")
type location
type syntaxErrorLocation
@genType
@genType.opaque
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)
| REAssignmentExpected
| REDistributionError(DistributionTypes.error)
@ -17,13 +18,13 @@ type errorValue =
| REOperationError(Operation.operationError)
| RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string)
| RESyntaxError(string, option<location>)
| RESyntaxError(string, option<syntaxErrorLocation>)
| RETodo(string) // To do
| REUnitNotFound(string)
| RENeedToRun
type t = errorValue
@genType
let errorToString = err =>
switch err {
| REArityError(_oFnName, arity, usedArity) =>
@ -57,4 +58,5 @@ let errorToString = err =>
| RETodo(msg) => `TODO: ${msg}`
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
| 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 BuiltIn = Reducer_Dispatch_BuiltIn
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
@ -6,30 +7,21 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Lambda = Reducer_Expression_Lambda
module Macro = Reducer_Expression_Macro
module MathJs = Reducer_MathJs
module Bindings = Reducer_Bindings
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module Result = Belt.Result
module T = Reducer_Expression_T
type environment = InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = T.expression
type internalExpressionValue = InternalExpressionValue.t
type externalExpressionValue = ReducerInterface_ExternalExpressionValue.t
type t = expression
type t = T.t
/*
Converts a Squigle code to expression
Recursively evaluate/reduce the expression (Lisp AST/Lambda calculus)
*/
let parse = (peggyCode: string): result<t, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
/*
Recursively evaluate/reduce the expression (Lisp AST)
*/
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
internalExpressionValue,
'e,
> => {
let rec reduceExpressionInProject = (
expression: t,
continuation: T.bindings,
accessors: ProjectAccessorsT.t,
): result<InternalExpressionValue.t, 'e> => {
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression {
| T.EValue(value) => value->Ok
@ -38,41 +30,40 @@ let rec reduceExpression = (expression: t, bindings: T.bindings, environment: en
| list{EValue(IEvCall(fName)), ..._args} =>
switch Macro.isMacroName(fName) {
// A macro expands then reduces itself
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression)
| false => reduceExpressionList(list, bindings, environment)
| true => Macro.doMacroCall(expression, continuation, accessors, reduceExpressionInProject)
| false => reduceExpressionList(list, continuation, accessors)
}
| _ => reduceExpressionList(list, bindings, environment)
| _ => reduceExpressionList(list, continuation, accessors)
}
}
}
and reduceExpressionList = (
expressions: list<t>,
bindings: T.bindings,
environment: environment,
): result<internalExpressionValue, 'e> => {
continuation: T.bindings,
accessors: ProjectAccessorsT.t,
): result<InternalExpressionValue.t, 'e> => {
let racc: result<
list<internalExpressionValue>,
list<InternalExpressionValue.t>,
'e,
> = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
> = expressions->Belt.List.reduceReverse(Ok(list{}), (racc, each: t) =>
racc->Result.flatMap(acc => {
each
->reduceExpression(bindings, environment)
->reduceExpressionInProject(continuation, accessors)
->Result.map(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
*/
and reduceValueList = (valueList: list<internalExpressionValue>, environment): result<
internalExpressionValue,
'e,
> =>
and reduceValueList = (
valueList: list<InternalExpressionValue.t>,
accessors: ProjectAccessorsT.t,
): result<InternalExpressionValue.t, 'e> =>
switch valueList {
| list{IEvCall(fName), ...args} => {
let rCheckedArgs = switch fName {
@ -81,7 +72,10 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
}
rCheckedArgs->Result.flatMap(checkedArgs =>
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(environment, reduceExpression)
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch(
accessors,
reduceExpressionInProject,
)
)
}
| list{IEvLambda(_)} =>
@ -91,11 +85,11 @@ and reduceValueList = (valueList: list<internalExpressionValue>, environment): r
->Result.flatMap(reducedValueList =>
reducedValueList->Belt.List.toArray->InternalExpressionValue.IEvArray->Ok
)
| list{IEvLambda(lamdaCall), ...args} =>
| list{IEvLambda(lambdaCall), ...args} =>
args
->Lambda.checkIfReduced
->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<
internalExpressionValue,
'e,
> => reduceExpression(aExpression, bindings, environment)
let evaluateUsingOptions = (
~environment: option<ReducerInterface_ExternalExpressionValue.environment>,
~externalBindings: option<ReducerInterface_ExternalExpressionValue.externalBindings>,
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)
let reduceReturningBindings = (
expression: t,
continuation: T.bindings,
accessors: ProjectAccessorsT.t,
): (result<InternalExpressionValue.t, 'e>, T.bindings) => {
let states = accessors.states
let result = reduceExpressionInProject(expression, continuation, accessors)
(result, states.continuation)
}
/*
IEvaluates Squiggle code and bindings via Reducer and answers the result
*/
let evaluate = (code: string): result<externalExpressionValue, errorValue> => {
evaluateUsingOptions(~environment=None, ~externalBindings=None, code)
}
let evaluatePartialUsingExternalBindings = (
code: string,
externalBindings: ReducerInterface_ExternalExpressionValue.externalBindings,
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
module BackCompatible = {
// Those methods are used to support the existing tests
// If they are used outside limited testing context, error location reporting will fail
let parse = (peggyCode: string): result<t, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
let evaluate = (expression: t): result<InternalExpressionValue.t, errorValue> => {
let accessors = ProjectAccessorsT.identityAccessors
expression->reduceExpressionInProject(accessors.stdLib, accessors)
}
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 ErrorValue = Reducer_ErrorValue
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result
module Bindings = Reducer_Bindings
type bindings = ExpressionT.bindings
type context = bindings
@ -11,7 +13,6 @@ type environment = InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t
type reducerFn = ExpressionT.reducerFn
type expressionWithContext =
| ExpressionWithContext(expression, context)
@ -20,16 +21,16 @@ type expressionWithContext =
let callReducer = (
expressionWithContext: expressionWithContext,
bindings: bindings,
environment: environment,
reducer: reducerFn,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, errorValue> => {
switch expressionWithContext {
| ExpressionNoContext(expr) =>
// Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`)
reducer(expr, bindings, environment)
reducer(expr, bindings, accessors)
| ExpressionWithContext(expr, context) =>
// 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 expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t
type externalBindings = ReducerInterface_ExternalExpressionValue.externalBindings
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 ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result
type environment = ReducerInterface_InternalExpressionValue.environment
type expression = ExpressionT.expression
type expressionOrFFI = ExpressionT.expressionOrFFI
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 zippedParameterList = parameterList->Belt.List.zip(args)
let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, (
@ -52,39 +59,43 @@ let caseNotFFI = (lambdaValue: ExpressionValue.lambdaValue, expr, args, environm
(variable, variableValue),
) => acc->Bindings.set(variable, variableValue))
let newExpression = ExpressionBuilder.eBlock(list{expr})
reducer(newExpression, bindings, environment)
reducer(newExpression, bindings, accessors)
}
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, environment) => {
ffiFn(args->Belt.List.toArray, environment)
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, accessors: ProjectAccessorsT.t) => {
ffiFn(args->Belt.List.toArray, accessors.environment)
}
let applyParametersToLambda = (
lambdaValue: ExpressionValue.lambdaValue,
args,
environment,
reducer: ExpressionT.reducerFn,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, 'e> => {
checkArity(lambdaValue, args)->Result.flatMap(args =>
checkIfReduced(args)->Result.flatMap(args => {
let exprOrFFI = castInternalCodeToExpression(lambdaValue.body)
switch exprOrFFI {
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, environment, reducer)
| FFI(ffiFn) => caseFFI(ffiFn, args, environment)
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, accessors, reducer)
| FFI(ffiFn) => caseFFI(ffiFn, args, accessors)
}
})
)
}
let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) =>
applyParametersToLambda(lambdaValue, args, environment, reducer)
let doLambdaCall = (
lambdaValue: ExpressionValue.lambdaValue,
args,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
) => applyParametersToLambda(lambdaValue, args, accessors, reducer)
let foreignFunctionInterface = (
lambdaValue: ExpressionValue.lambdaValue,
argArray: array<internalExpressionValue>,
environment: ExpressionValue.environment,
reducer: ExpressionT.reducerFn,
): result<internalExpressionValue, 'e> => {
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): result<internalExpressionValue, Reducer_ErrorValue.errorValue> => {
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 ExpressionWithContext = Reducer_ExpressionWithContext
module Result = Belt.Result
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
type environment = InternalExpressionValue.environment
type expression = ExpressionT.expression
@ -11,34 +13,29 @@ type expressionWithContext = ExpressionWithContext.expressionWithContext
let expandMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
environment: environment,
reduceExpression: ExpressionT.reducerFn,
accessors: ProjectAccessorsT.t,
reduceExpression: ProjectReducerFnT.t,
): result<expressionWithContext, 'e> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression,
bindings,
environment,
accessors,
reduceExpression,
)
let doMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
environment: environment,
reduceExpression: ExpressionT.reducerFn,
accessors: ProjectAccessorsT.t,
reduceExpression: ProjectReducerFnT.t,
): result<internalExpressionValue, 'e> =>
expandMacroCall(
macroExpression,
bindings,
environment,
reduceExpression,
(accessors: ProjectAccessorsT.t),
(reduceExpression: ProjectReducerFnT.t),
)->Result.flatMap(expressionWithContext =>
ExpressionWithContext.callReducer(
expressionWithContext,
bindings,
environment,
reduceExpression,
)
ExpressionWithContext.callReducer(expressionWithContext, bindings, accessors, reduceExpression)
)
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
and bindings = InternalExpressionValue.nameSpace
type t = expression
type reducerFn = (
expression,
bindings,

View File

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

View File

@ -5,13 +5,12 @@ type node = {"type": string}
@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"
let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.location =>
let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.syntaxErrorLocation =>
castWithLocation(error)["location"]
@genType
let parse = (expr: string): result<node, errorValue> =>
try {
Ok(parse__(expr))

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,11 @@
module ErrorValue = Reducer_ErrorValue
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module Extra_Array = Reducer_Extra_Array
type internalCode = ExternalExpressionValue.internalCode
type environment = ExternalExpressionValue.environment
type internalCode = Object
type environment = GenericDist.env
let defaultEnvironment = ExternalExpressionValue.defaultEnvironment
let defaultEnvironment: environment = DistributionOperation.defaultEnv
@genType.opaque
type rec t =
| IEvArray(array<t>) // FIXME: Convert to MapInt
| IEvArrayString(array<string>)
@ -24,42 +24,21 @@ type rec t =
| IEvType(map)
| IEvTypeIdentifier(string)
| IEvVoid
and map = Belt.Map.String.t<t>
and nameSpace = NameSpace(Belt.Map.String.t<t>)
@genType.opaque and squiggleArray = array<t>
@genType.opaque and map = Belt.Map.String.t<t>
@genType.opaque and nameSpace = NameSpace(Belt.Map.String.t<t>)
@genType.opaque
and lambdaValue = {
parameters: array<string>,
context: nameSpace,
body: internalCode,
}
and lambdaDeclaration = Declaration.declaration<lambdaValue>
@genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue>
type internalExpressionValue = 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 =>
switch aValue {
| IEvArray(anArray) => {
@ -132,6 +111,12 @@ let toStringResult = x =>
| 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 =>
switch codeResult {
| Ok(a) => toString(a)
@ -140,7 +125,7 @@ let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): strin
let toStringResultRecord = x =>
switch x {
| Ok(a) => `Ok(${ExternalExpressionValue.toStringRecord(a)})`
| Ok(a) => `Ok(${toStringMap(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
}
@ -188,27 +173,6 @@ let valueToValueType = value =>
| 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 (fn, args) = functionCall
CallSignature(fn, args->Js.Array2.map(valueToValueType))
@ -240,70 +204,18 @@ let functionCallSignatureToString = (functionCallSignature: functionCallSignatur
`${fn}(${args->Js.Array2.map(valueTypeToString)->Js.Array2.toString})`
}
let rec toExternal = (iev: t): ExternalExpressionValue.t => {
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 arrayToValueArray = (arr: array<t>): array<t> => arr
let rec toInternal = (ev: ExternalExpressionValue.t): t => {
switch ev {
| EvArray(v) => v->Belt.Array.map(e => toInternal(e))->IEvArray
| EvArrayString(v) => IEvArrayString(v)
| EvBool(v) => IEvBool(v)
| EvCall(v) => IEvCall(v)
| EvDate(v) => IEvDate(v)
| EvDeclaration(v) => {
let fn = lambdaValueToInternal(v.fn)
let args = v.args
IEvDeclaration({fn: fn, args: args})
}
| 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
}
let recordToKeyValuePairs = (record: map): array<(string, t)> => record->Belt.Map.String.toArray
// let nameSpaceToTypeScriptBindings = (
// nameSpace: nameSpace,
// ) => {
// let NameSpace(container) = nameSpace
// Belt.Map.String.map(container, e => e->Belt.Map.String.toArray->Js.Dict.fromArray)
// }
let nameSpaceToKeyValuePairs = (nameSpace: nameSpace): array<(string, t)> => {
let NameSpace(container) = nameSpace
container->Belt.Map.String.toArray
}
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
let internalStdLib =
let internalStdLib: Bindings.t =
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.
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.
*/
@genType
type samplingParams = GenericDist.env
// For backwards compatibility:
//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
type genericDist = DistributionTypes.genericDist
let makeSampleSetDist: array<float> => result<
sampleSetDist,
SampleSetDist.sampleSetError,
> = SampleSetDist.make
//TODO: ForTS Interface module candid
@genType
type sampleSetDist = SampleSetDist.t
@genType
type symbolicDist = SymbolicDistTypes.symbolicDist
@genType
type distributionError = DistributionTypes.error
@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
let toPointSet: (
squiggleValue_Distribution,
~xyPointLength: int,
~sampleCount: int,
~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?,
unit,
) => result<PointSetTypes.pointSetDist, distributionError> = GenericDist.toPointSet
@genType
type mixedShape = PointSetTypes.mixedShape
@ -71,31 +45,14 @@ type discreteShape = PointSetTypes.discreteShape
type continuousShape = PointSetTypes.continuousShape
@genType
let errorValueToString = Reducer_ErrorValue.errorToString
let distributionErrorToString = ForTS_Distribution_Error.toString
@genType
let distributionErrorToString = DistributionTypes.Error.toString
let defaultSamplingEnv = ForTS_Distribution.defaultEnvironment
@genType
type lambdaValue = ReducerInterface_ExternalExpressionValue.lambdaValue
// Umur: opaqe types
// @genType
// type declarationArg = Declaration.arg
@genType
type lambdaDeclaration = ReducerInterface_ExternalExpressionValue.lambdaDeclaration
@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>
// @genType
// type declaration<'a> = Declaration.declaration<'a>