Merge branch 'overhaul' into project-component

This commit is contained in:
Sam Nolan 2022-09-26 13:35:12 +10:00
commit 64f9c9ddfc
144 changed files with 2943 additions and 5260 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@quri/squiggle-components", "name": "@quri/squiggle-components",
"version": "0.4.3", "version": "0.5.0-alpha.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@floating-ui/react-dom": "^1.0.0", "@floating-ui/react-dom": "^1.0.0",
@ -8,7 +8,7 @@
"@headlessui/react": "^1.7.2", "@headlessui/react": "^1.7.2",
"@heroicons/react": "^1.0.6", "@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.9.8", "@hookform/resolvers": "^2.9.8",
"@quri/squiggle-lang": "^0.4.2", "@quri/squiggle-lang": "^0.5.0-alpha.2",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"clsx": "^1.2.1", "clsx": "^1.2.1",

View File

@ -135,29 +135,6 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
{() => value.value.toString()} {() => value.value.toString()}
</VariableBox> </VariableBox>
); );
case SqValueTag.Symbol:
return (
<VariableBox value={value} heading="Symbol">
{() => (
<>
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
<span className="text-slate-600">{value.value}</span>
</>
)}
</VariableBox>
);
case SqValueTag.Call:
return (
<VariableBox value={value} heading="Call">
{() => value.value}
</VariableBox>
);
case SqValueTag.ArrayString:
return (
<VariableBox value={value} heading="Array String">
{() => value.value.map((r) => `"${r}"`).join(", ")}
</VariableBox>
);
case SqValueTag.Date: case SqValueTag.Date:
return ( return (
<VariableBox value={value} heading="Date"> <VariableBox value={value} heading="Date">
@ -242,24 +219,6 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
</VariableBox> </VariableBox>
); );
} }
case SqValueTag.Module: {
return (
<VariableList value={value} heading="Module">
{(_) =>
value.value
.entries()
.filter(([key, _]) => !key.match(/^(__result__)$/))
.map(([key, r]) => (
<ExpressionViewer
key={key}
value={r}
width={width !== undefined ? width - 20 : width}
/>
))
}
</VariableList>
);
}
case SqValueTag.Record: case SqValueTag.Record:
const plot = makePlot(value.value); const plot = makePlot(value.value);
if (plot) { if (plot) {

View File

@ -1,20 +0,0 @@
open Jest
open Expect
let makeTest = (~only=false, str, item1, item2) =>
only
? Only.test(str, () => expect(item1)->toEqual(item2))
: test(str, () => expect(item1)->toEqual(item2))
describe("Lodash", () =>
describe("Lodash", () => {
makeTest("min", Lodash.min([1, 3, 4]), 1)
makeTest("max", Lodash.max([1, 3, 4]), 4)
makeTest("uniq", Lodash.uniq([1, 3, 4, 4]), [1, 3, 4])
makeTest(
"countBy",
Lodash.countBy([1, 3, 4, 4], r => r),
Js.Dict.fromArray([("1", 1), ("3", 1), ("4", 2)]),
)
})
)

View File

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

View File

@ -1,146 +0,0 @@
open Jest
// open Expect
open Reducer_Expression_ExpressionBuilder
open Reducer_TestMacroHelpers
module ExpressionT = Reducer_Expression_T
let exampleExpression = eNumber(1.)
let exampleExpressionY = eSymbol("y")
let exampleStatementY = eLetStatement("y", eNumber(1.))
let exampleStatementX = eLetStatement("y", eSymbol("x"))
let exampleStatementZ = eLetStatement("z", eSymbol("y"))
// If it is not a macro then it is not expanded
testMacro([], exampleExpression, "Ok(1)")
describe("bindStatement", () => {
// A statement is bound by the bindings created by the previous statement
testMacro(
[],
eBindStatement(eBindings([]), exampleStatementY),
"Ok((:$_setBindings_$ @{} :y 1) context: @{})",
)
// Then it answers the bindings for the next statement when reduced
testMacroEval([], eBindStatement(eBindings([]), exampleStatementY), "Ok(@{y: 1})")
// Now let's feed a binding to see what happens
testMacro(
[],
eBindStatement(eBindings([("x", IEvNumber(2.))]), exampleStatementX),
"Ok((:$_setBindings_$ @{x: 2} :y 2) context: @{x: 2})",
)
// An expression does not return a binding, thus error
testMacro([], eBindStatement(eBindings([]), exampleExpression), "Assignment expected")
// When bindings from previous statement are missing the context is injected. This must be the first statement of a block
testMacro(
[("z", IEvNumber(99.))],
eBindStatementDefault(exampleStatementY),
"Ok((:$_setBindings_$ @{z: 99} :y 1) context: @{z: 99})",
)
})
describe("bindExpression", () => {
// x is simply bound in the expression
testMacro(
[],
eBindExpression(eBindings([("x", IEvNumber(2.))]), eSymbol("x")),
"Ok(2 context: @{x: 2})",
)
// When an let statement is the end expression then bindings are returned
testMacro(
[],
eBindExpression(eBindings([("x", IEvNumber(2.))]), exampleStatementY),
"Ok((:$_exportBindings_$ (:$_setBindings_$ @{x: 2} :y 1)) context: @{x: 2})",
)
// Now let's reduce that expression
testMacroEval(
[],
eBindExpression(eBindings([("x", IEvNumber(2.))]), exampleStatementY),
"Ok(@{x: 2,y: 1})",
)
// When bindings are missing the context is injected. This must be the first and last statement of a block
testMacroEval(
[("z", IEvNumber(99.))],
eBindExpressionDefault(exampleStatementY),
"Ok(@{y: 1,z: 99})",
)
})
describe("block", () => {
// Block with a single expression
testMacro([], eBlock(list{exampleExpression}), "Ok((:$$_bindExpression_$$ 1))")
testMacroEval([], eBlock(list{exampleExpression}), "Ok(1)")
// Block with a single statement
testMacro([], eBlock(list{exampleStatementY}), "Ok((:$$_bindExpression_$$ (:$_let_$ :y 1)))")
testMacroEval([], eBlock(list{exampleStatementY}), "Ok(@{y: 1})")
// Block with a statement and an expression
testMacro(
[],
eBlock(list{exampleStatementY, exampleExpressionY}),
"Ok((:$$_bindExpression_$$ (:$$_bindStatement_$$ (:$_let_$ :y 1)) :y))",
)
testMacroEval([], eBlock(list{exampleStatementY, exampleExpressionY}), "Ok(1)")
// Block with a statement and another statement
testMacro(
[],
eBlock(list{exampleStatementY, exampleStatementZ}),
"Ok((:$$_bindExpression_$$ (:$$_bindStatement_$$ (:$_let_$ :y 1)) (:$_let_$ :z :y)))",
)
testMacroEval([], eBlock(list{exampleStatementY, exampleStatementZ}), "Ok(@{y: 1,z: 1})")
// Block inside a block
testMacro([], eBlock(list{eBlock(list{exampleExpression})}), "Ok((:$$_bindExpression_$$ {1}))")
testMacroEval([], eBlock(list{eBlock(list{exampleExpression})}), "Ok(1)")
// Block assigned to a variable
testMacro(
[],
eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}),
"Ok((:$$_bindExpression_$$ (:$_let_$ :z {{:y}})))",
)
testMacroEval(
[],
eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}),
"Ok(@{z: :y})",
)
// Empty block
testMacro([], eBlock(list{}), "Ok(:undefined block)") //TODO: should be an error
// :$$_block_$$ (:$$_block_$$ (:$_let_$ :y (:add :x 1)) :y)"
testMacro(
[],
eBlock(list{
eBlock(list{
eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})),
eSymbol("y"),
}),
}),
"Ok((:$$_bindExpression_$$ {(:$_let_$ :y (:add :x 1)); :y}))",
)
testMacroEval(
[("x", IEvNumber(1.))],
eBlock(list{
eBlock(list{
eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})),
eSymbol("y"),
}),
}),
"Ok(2)",
)
})
describe("lambda", () => {
// assign a lambda to a variable
let lambdaExpression = eFunction("$$_lambda_$$", list{eArrayString(["y"]), exampleExpressionY})
testMacro([], lambdaExpression, "Ok(lambda(y=>internal code))")
// call a lambda
let callLambdaExpression = list{lambdaExpression, eNumber(1.)}->ExpressionT.EList
testMacro([], callLambdaExpression, "Ok(((:$$_lambda_$$ [y] :y) 1))")
testMacroEval([], callLambdaExpression, "Ok(1)")
// Parameters shadow the outer scope
testMacroEval([("y", IEvNumber(666.))], callLambdaExpression, "Ok(1)")
// When not shadowed by the parameters, the outer scope variables are available
let lambdaExpression = eFunction(
"$$_lambda_$$",
list{eArrayString(["z"]), eFunction("add", list{eSymbol("y"), eSymbol("z")})},
)
let callLambdaExpression = eList(list{lambdaExpression, eNumber(1.)})
testMacroEval([("y", IEvNumber(666.))], callLambdaExpression, "Ok(667)")
})

View File

@ -1,41 +0,0 @@
module ExpressionValue = ReducerInterface.InternalExpressionValue
module Expression = Reducer_Expression
open Jest
open Expect
let expectEvalToBe = (sourceCode: string, answer: string) =>
Expression.BackCompatible.evaluateString(sourceCode)
->ExpressionValue.toStringResult
->expect
->toBe(answer)
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
describe("builtin", () => {
// All MathJs operators and functions are available for string, number and boolean
// .e.g + - / * > >= < <= == /= not and or
// See https://mathjs.org/docs/expressions/syntax.html
// See https://mathjs.org/docs/reference/functions.html
testEval("-1", "Ok(-1)")
testEval("1-1", "Ok(0)")
testEval("2>1", "Ok(true)")
testEval("concat('a','b')", "Ok('ab')")
})
describe("builtin exception", () => {
//It's a pity that MathJs does not return error position
test("MathJs Exception", () =>
expectEvalToBe("testZadanga(1)", "Error(JS Exception: Error: Undefined function testZadanga)")
)
})
describe("error reporting from collection functions", () => {
testEval("arr=[1,2,3]; map(arr, {|x| x*2})", "Ok([2,4,6])")
testEval(
"arr = [normal(3,2)]; map(arr, zarathsuzaWasHere)",
"Error(zarathsuzaWasHere is not defined)",
)
// FIXME: returns "Error(Function not found: map(Array,Symbol))"
// Actually this error is correct but not informative
})

View File

@ -1,17 +0,0 @@
// Reducer_Helpers
module ErrorValue = Reducer_ErrorValue
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module Bindings = Reducer_Bindings
let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
switch iev {
| InternalExpressionValue.IEvBindings(nameSpace) =>
Bindings.removeOther(
nameSpace,
ReducerInterface.StdLib.internalStdLib,
)->InternalExpressionValue.IEvBindings
| value => value
}
}
let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal)

View File

@ -1,31 +0,0 @@
module MathJs = Reducer_MathJs
module ErrorValue = Reducer_ErrorValue
open Jest
open ExpectJs
describe("eval", () => {
test("Number", () => expect(MathJs.Eval.eval("1"))->toEqual(Ok(IEvNumber(1.))))
test("Number expr", () => expect(MathJs.Eval.eval("1-1"))->toEqual(Ok(IEvNumber(0.))))
test("String", () => expect(MathJs.Eval.eval("'hello'"))->toEqual(Ok(IEvString("hello"))))
test("String expr", () =>
expect(MathJs.Eval.eval("concat('hello ','world')"))->toEqual(Ok(IEvString("hello world")))
)
test("Boolean", () => expect(MathJs.Eval.eval("true"))->toEqual(Ok(IEvBool(true))))
test("Boolean expr", () => expect(MathJs.Eval.eval("2>1"))->toEqual(Ok(IEvBool(true))))
})
describe("errors", () => {
// All those errors propagete up and are returned by the resolver
test("unknown function", () =>
expect(MathJs.Eval.eval("testZadanga()"))->toEqual(
Error(ErrorValue.REJavaScriptExn(Some("Undefined function testZadanga"), Some("Error"))),
)
)
test("unknown answer type", () =>
expect(MathJs.Eval.eval("1+1i"))->toEqual(
Error(ErrorValue.RETodo("Unhandled MathJs literal type: object")),
)
)
})

View File

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

View File

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

View File

@ -1,6 +1,5 @@
module Expression = Reducer_Expression module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.InternalExpressionValue
module Parse = Reducer_Peggy_Parse module Parse = Reducer_Peggy_Parse
module Result = Belt.Result module Result = Belt.Result
module ToExpression = Reducer_Peggy_ToExpression module ToExpression = Reducer_Peggy_ToExpression
@ -24,8 +23,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
let a2 = let a2 =
rExpr rExpr
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr)) ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
->Reducer_Helpers.rRemoveDefaultsInternal ->Reducer_Value.toStringResultOkless
->ExpressionValue.toStringResultOkless
(a1, a2)->expect->toEqual((answer, v)) (a1, a2)->expect->toEqual((answer, v))
} }
} }

View File

@ -1,23 +1,12 @@
module Bindings = Reducer_Bindings
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Jest open Jest
open Reducer_Peggy_TestHelpers open Reducer_Peggy_TestHelpers
// Note: these tests aren't useful anymore since outer block macro got deleted.
// Probably can be removed or folded into other Peggy tests.
describe("Peggy Outer Block", () => { describe("Peggy Outer Block", () => {
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1", "1", ~v="1", ())
testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ()) testToExpression("x=1", "x = {1}", ~v="()", ())
testToExpression( testToExpression("x=1; y=2", "x = {1}; y = {2}", ~v="()", ())
"x=1; y=2", testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ())
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", testToExpression("x={a=1; a}; x", "x = {a = {1}; a}; x", ~v="1", ())
~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

@ -1,5 +1,4 @@
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Jest open Jest
open Reducer_Peggy_TestHelpers open Reducer_Peggy_TestHelpers
@ -7,160 +6,94 @@ open Reducer_Peggy_TestHelpers
describe("Peggy to Expression", () => { describe("Peggy to Expression", () => {
describe("literals operators parenthesis", () => { describe("literals operators parenthesis", () => {
// Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement // Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1", "1", ~v="1", ())
testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ()) testToExpression("'hello'", "'hello'", ~v="'hello'", ())
testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ()) testToExpression("true", "true", ~v="true", ())
testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("1+2", "(add)(1, 2)", ~v="3", ())
testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("add(1,2)", "(add)(1, 2)", ~v="3", ())
testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ()) testToExpression("(1)", "1", ())
testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ()) testToExpression("(1+2)", "(add)(1, 2)", ())
}) })
describe("unary", () => { describe("unary", () => {
testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ()) testToExpression("-1", "(unaryMinus)(1)", ~v="-1", ())
testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ()) testToExpression("!true", "(not)(true)", ~v="false", ())
testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ()) testToExpression("1 + -1", "(add)(1, (unaryMinus)(1))", ~v="0", ())
testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ()) testToExpression("-a[0]", "(unaryMinus)(($_atIndex_$)(a, 0))", ())
}) })
describe("multi-line", () => { describe("multi-line", () => {
testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ())
testToExpression( testToExpression("x=1; y=2", "x = {1}; y = {2}", ())
"x=1; y=2",
"{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
(),
)
}) })
describe("variables", () => { describe("variables", () => {
testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ()) testToExpression("x = 1", "x = {1}", ())
testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error testToExpression("x", "x", ~v="Error(x is not defined)", ()) //TODO: value should return error
testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ()) testToExpression("x = 1; x", "x = {1}; x", ~v="1", ())
}) })
describe("functions", () => { describe("functions", () => {
testToExpression( testToExpression("identity(x) = x", "identity = {|x| {x}}", ()) // Function definitions become lambda assignments
"identity(x) = x", testToExpression("identity(x)", "(identity)(x)", ()) // Note value returns error properly
"{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
(),
) // Function definitions become lambda assignments
testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly
testToExpression( testToExpression(
"f(x) = x> 2 ? 0 : 1; f(3)", "f(x) = x> 2 ? 0 : 1; f(3)",
"{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:$_endOfOuterBlock_$ () (:f 3))}", "f = {|x| {(larger)(x, 2) ? (0) : (1)}}; (f)(3)",
~v="0", ~v="0",
(), (),
) )
}) })
describe("arrays", () => { describe("arrays", () => {
testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) testToExpression("[]", "[]", ~v="[]", ())
testToExpression( testToExpression("[0, 1, 2]", "[0, 1, 2]", ~v="[0,1,2]", ())
"[0, 1, 2]", testToExpression("['hello', 'world']", "['hello', 'world']", ~v="['hello','world']", ())
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 0 1 2))}", testToExpression("([0,1,2])[1]", "($_atIndex_$)([0, 1, 2], 1)", ~v="1", ())
~v="[0,1,2]",
(),
)
testToExpression(
"['hello', 'world']",
"{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 'hello' 'world'))}",
~v="['hello','world']",
(),
)
testToExpression(
"([0,1,2])[1]",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ 0 1 2) 1))}",
~v="1",
(),
)
}) })
describe("records", () => { describe("records", () => {
testToExpression( testToExpression("{a: 1, b: 2}", "{'a': 1, 'b': 2}", ~v="{a: 1,b: 2}", ())
"{a: 1, b: 2}", testToExpression("{1+0: 1, 2+0: 2}", "{(add)(1, 0): 1, (add)(2, 0): 2}", ()) // key can be any expression
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}", testToExpression("record.property", "($_atIndex_$)(record, 'property')", ())
~v="{a: 1,b: 2}",
(),
)
testToExpression(
"{1+0: 1, 2+0: 2}",
"{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}",
(),
) // key can be any expression
testToExpression(
"record.property",
"{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
(),
)
testToExpression( testToExpression(
"record={property: 1}; record.property", "record={property: 1}; record.property",
"{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", "record = {{'property': 1}}; ($_atIndex_$)(record, 'property')",
~v="1", ~v="1",
(), (),
) )
}) })
describe("comments", () => { describe("comments", () => {
testToExpression("1 # This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1 # This is a line comment", "1", ~v="1", ())
testToExpression("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) testToExpression("1 // This is a line comment", "1", ~v="1", ())
testToExpression( testToExpression("1 /* This is a multi line comment */", "1", ~v="1", ())
"1 /* This is a multi line comment */", testToExpression("/* This is a multi line comment */ 1", "1", ~v="1", ())
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
testToExpression(
"/* This is a multi line comment */ 1",
"{(:$_endOfOuterBlock_$ () 1)}",
~v="1",
(),
)
}) })
describe("ternary operator", () => { describe("ternary operator", () => {
testToExpression( testToExpression("true ? 1 : 0", "true ? (1) : (0)", ~v="1", ())
"true ? 1 : 0", testToExpression("false ? 1 : 0", "false ? (1) : (0)", ~v="0", ())
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}", testToExpression("true ? 1 : false ? 2 : 0", "true ? (1) : (false ? (2) : (0))", ~v="1", ()) // nested ternary
~v="1", testToExpression("false ? 1 : false ? 2 : 0", "false ? (1) : (false ? (2) : (0))", ~v="0", ()) // nested ternary
(),
)
testToExpression(
"false ? 1 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}",
~v="0",
(),
)
testToExpression(
"true ? 1 : false ? 2 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}",
~v="1",
(),
) // nested ternary
testToExpression(
"false ? 1 : false ? 2 : 0",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}",
~v="0",
(),
) // nested ternary
describe("ternary bindings", () => { describe("ternary bindings", () => {
testToExpression( testToExpression(
// expression binding // expression binding
"f(a) = a > 5 ? 1 : 0; f(6)", "f(a) = a > 5 ? 1 : 0; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (:f 6))}", "f = {|a| {(larger)(a, 5) ? (1) : (0)}}; (f)(6)",
~v="1", ~v="1",
(), (),
) )
testToExpression( testToExpression(
// when true binding // when true binding
"f(a) = a > 5 ? a : 0; f(6)", "f(a) = a > 5 ? a : 0; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (:f 6))}", "f = {|a| {(larger)(a, 5) ? (a) : (0)}}; (f)(6)",
~v="6", ~v="6",
(), (),
) )
testToExpression( testToExpression(
// when false binding // when false binding
"f(a) = a < 5 ? 1 : a; f(6)", "f(a) = a < 5 ? 1 : a; f(6)",
"{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}", "f = {|a| {(smaller)(a, 5) ? (1) : (a)}}; (f)(6)",
~v="6", ~v="6",
(), (),
) )
@ -168,41 +101,23 @@ describe("Peggy to Expression", () => {
}) })
describe("if then else", () => { describe("if then else", () => {
testToExpression( testToExpression("if true then 2 else 3", "true ? ({2}) : ({3})", ())
"if true then 2 else 3", testToExpression("if true then {2} else {3}", "true ? ({2}) : ({3})", ())
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
(),
)
testToExpression(
"if true then {2} else {3}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
(),
)
testToExpression( testToExpression(
"if false then {2} else if false then {4} else {5}", "if false then {2} else if false then {4} else {5}",
"{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}", "false ? ({2}) : (false ? ({4}) : ({5}))",
(), (),
) //nested if ) //nested if
}) })
describe("pipe", () => { describe("pipe", () => {
testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("1 -> add(2)", "(add)(1, 2)", ~v="3", ())
testToExpression( testToExpression("-1 -> add(2)", "(add)((unaryMinus)(1), 2)", ~v="1", ()) // note that unary has higher priority naturally
"-1 -> add(2)", testToExpression("1 -> add(2) * 3", "(multiply)((add)(1, 2), 3)", ~v="9", ())
"{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}",
~v="1",
(),
) // note that unary has higher priority naturally
testToExpression(
"1 -> add(2) * 3",
"{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}",
~v="9",
(),
)
}) })
describe("elixir pipe", () => { describe("elixir pipe", () => {
testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) testToExpression("1 |> add(2)", "(add)(1, 2)", ~v="3", ())
}) })
// see testParse for priorities of to and credibleIntervalToDistribution // see testParse for priorities of to and credibleIntervalToDistribution
@ -212,44 +127,28 @@ describe("Peggy to Expression", () => {
// Like lambdas they have a local scope. // Like lambdas they have a local scope.
testToExpression( testToExpression(
"y=99; x={y=1; y}", "y=99; x={y=1; y}",
"{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}", "y = {99}; x = {y = {1}; y}",
// "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}",
(), (),
) )
}) })
describe("lambda", () => { describe("lambda", () => {
testToExpression( testToExpression("{|x| x}", "{|x| x}", ~v="lambda(x=>internal code)", ())
"{|x| x}", testToExpression("f={|x| x}", "f = {{|x| x}}", ())
"{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}", testToExpression("f(x)=x", "f = {|x| {x}}", ()) // Function definitions are lambda assignments
~v="lambda(x=>internal code)", testToExpression("f(x)=x ? 1 : 0", "f = {|x| {x ? (1) : (0)}}", ())
(),
)
testToExpression(
"f={|x| x}",
"{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}",
(),
)
testToExpression(
"f(x)=x",
"{(:$_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)})); (:$_endOfOuterBlock_$ () ())}",
(),
)
}) })
describe("module", () => { describe("module", () => {
// testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ()) // testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
// Only.test("stdlibrary", () => { // Only.test("stdlibrary", () => {
// ReducerInterface_StdLib.internalStdLib // SquiggleLibrary_StdLib.stdLib
// ->IEvBindings // ->IEvBindings
// ->InternalExpressionValue.toString // ->Reducer_Value.toString
// ->expect // ->expect
// ->toBe("") // ->toBe("")
// }) // })
testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ()) testToExpression("Math.pi", "Math.pi", ~v="3.141592653589793", ())
}) })
}) })

View File

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

View File

@ -1,7 +1,6 @@
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
open Jest open Jest
open Expect open Expect
@ -9,7 +8,7 @@ open Expect
let unwrapRecord = rValue => let unwrapRecord = rValue =>
rValue->Belt.Result.flatMap(value => rValue->Belt.Result.flatMap(value =>
switch value { switch value {
| InternalExpressionValue.IEvRecord(aRecord) => Ok(aRecord) | Reducer_T.IEvRecord(aRecord) => Ok(aRecord)
| _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error | _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error
} }
) )
@ -18,15 +17,11 @@ let expectParseToBe = (code: string, answer: string) =>
Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer) Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer)
let expectEvalToBe = (code: string, answer: string) => let expectEvalToBe = (code: string, answer: string) =>
Expression.BackCompatible.evaluateString(code) Expression.BackCompatible.evaluateString(code)->Reducer_Value.toStringResult->expect->toBe(answer)
->Reducer_Helpers.rRemoveDefaultsInternal
->InternalExpressionValue.toStringResult
->expect
->toBe(answer)
let expectEvalError = (code: string) => let expectEvalError = (code: string) =>
Expression.BackCompatible.evaluateString(code) Expression.BackCompatible.evaluateString(code)
->InternalExpressionValue.toStringResult ->Reducer_Value.toStringResult
->expect ->expect
->toMatch("Error\(") ->toMatch("Error\(")

View File

@ -1,89 +0,0 @@
open Jest
open Expect
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Expression = Reducer_Expression
module ExpressionWithContext = Reducer_ExpressionWithContext
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
module Macro = Reducer_Expression_Macro
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Expression_T
let testMacro_ = (
tester,
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedCode: string,
) => {
let bindings = Bindings.fromArray(bindArray)
tester(expr->T.toString, () =>
expr
->Macro.expandMacroCallRs(
bindings,
ProjectAccessorsT.identityAccessors,
Expression.reduceExpressionInProject,
)
->ExpressionWithContext.toStringResult
->expect
->toEqual(expectedCode)
)
}
let testMacroEval_ = (
tester,
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedValue: string,
) => {
let bindings = Bindings.fromArray(bindArray)
tester(expr->T.toString, () =>
expr
->Macro.doMacroCall(
bindings,
ProjectAccessorsT.identityAccessors,
Expression.reduceExpressionInProject,
)
->Ok
->InternalExpressionValue.toStringResult
->expect
->toEqual(expectedValue)
)
}
let testMacro = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(test, bindArray, expr, expectedValue)
module MySkip = {
let testMacro = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(Skip.test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(Skip.test, bindArray, expr, expectedValue)
}
module MyOnly = {
let testMacro = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedExpr: string,
) => testMacro_(Only.test, bindArray, expr, expectedExpr)
let testMacroEval = (
bindArray: array<(string, InternalExpressionValue.t)>,
expr: T.expression,
expectedValue: string,
) => testMacroEval_(Only.test, bindArray, expr, expectedValue)
}

View File

@ -1,52 +0,0 @@
module Expression = Reducer_Expression
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings
module T = Reducer_Type_T
module TypeCompile = Reducer_Type_Compile
open Jest
open Expect
let myIevEval = (aTypeSourceCode: string) =>
TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
let myIevEvalToString = (aTypeSourceCode: string) =>
myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
let myIevExpectEqual = (aTypeSourceCode, answer) =>
expect(myIevEvalToString(aTypeSourceCode))->toEqual(answer)
let myIevTest = (test, aTypeSourceCode, answer) =>
test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
let myTypeEval = (aTypeSourceCode: string) =>
TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
let myTypeExpectEqual = (aTypeSourceCode, answer) =>
expect(myTypeEvalToString(aTypeSourceCode))->toEqual(answer)
let myTypeTest = (test, aTypeSourceCode, answer) =>
test(aTypeSourceCode, () => myTypeExpectEqual(aTypeSourceCode, answer))
// | ItTypeIdentifier(string)
myTypeTest(test, "number", "number")
myTypeTest(test, "(number)", "number")
// | ItModifiedType({modifiedType: iType})
myIevTest(test, "number<-min(0)", "Ok({min: 0,typeIdentifier: #number,typeTag: 'typeIdentifier'})")
myTypeTest(test, "number<-min(0)", "number<-min(0)")
// | ItTypeOr({typeOr: array<iType>})
myTypeTest(test, "number | string", "(number | string)")
// | ItTypeFunction({inputs: array<iType>, output: iType})
myTypeTest(test, "number => number => number", "(number => number => number)")
// | ItTypeArray({element: iType})
myIevTest(test, "[number]", "Ok({element: #number,typeTag: 'typeArray'})")
myTypeTest(test, "[number]", "[number]")
// | ItTypeTuple({elements: array<iType>})
myTypeTest(test, "[number, string]", "[number, string]")
// | ItTypeRecord({properties: Belt.Map.String.t<iType>})
myIevTest(
test,
"{age: number, name: string}",
"Ok({properties: {age: #number,name: #string},typeTag: 'typeRecord'})",
)
myTypeTest(test, "{age: number, name: string}", "{age: number, name: string}")

View File

@ -1,42 +0,0 @@
module Bindings = Reducer_Bindings
module ErrorValue = Reducer_ErrorValue
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Type_T
module TypeChecker = Reducer_Type_TypeChecker
open Jest
open Expect
let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
'v,
ErrorValue.t,
> => {
let reducerFn = Expression.reduceExpressionInProject
let rResult =
Expression.BackCompatible.parse(sourceCode)->Belt.Result.map(expr =>
reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
)
rResult->Belt.Result.flatMap(result =>
switch result {
| IEvArray(args) => TypeChecker.checkArguments(aTypeSourceCode, args, reducerFn)
| _ => Js.Exn.raiseError("Arguments has to be an array")
}
)
}
let myCheckArguments = (aTypeSourceCode: string, sourceCode: string): string =>
switch checkArgumentsSourceCode(aTypeSourceCode, sourceCode) {
| Ok(_) => "Ok"
| Error(error) => ErrorValue.errorToString(error)
}
let myCheckArgumentsExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
expect(myCheckArguments(aTypeSourceCode, sourceCode))->toEqual(answer)
let myCheckArgumentsTest = (test, aTypeSourceCode, sourceCode, answer) =>
test(aTypeSourceCode, () => myCheckArgumentsExpectEqual(aTypeSourceCode, sourceCode, answer))
myCheckArgumentsTest(test, "number=>number=>number", "[1,2]", "Ok")

View File

@ -1,73 +0,0 @@
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
module ErrorValue = Reducer_ErrorValue
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
// In development, you are expected to use TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn).
// isTypeOfSourceCode is written to use strings instead of expression values.
let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
'v,
ErrorValue.t,
> => {
let reducerFn = Expression.reduceExpressionInProject
let rResult =
Expression.BackCompatible.parse(sourceCode)->Belt.Result.map(expr =>
reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
)
rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
}
let myTypeCheck = (aTypeSourceCode: string, sourceCode: string): string =>
switch isTypeOfSourceCode(aTypeSourceCode, sourceCode) {
| Ok(_) => "Ok"
| Error(error) => ErrorValue.errorToString(error)
}
let myTypeCheckExpectEqual = (aTypeSourceCode, sourceCode, answer) =>
expect(myTypeCheck(aTypeSourceCode, sourceCode))->toEqual(answer)
let myTypeCheckTest = (test, aTypeSourceCode, sourceCode, answer) =>
test(aTypeSourceCode, () => myTypeCheckExpectEqual(aTypeSourceCode, sourceCode, answer))
myTypeCheckTest(test, "number", "1", "Ok")
myTypeCheckTest(test, "number", "'2'", "Expected type: number but got: '2'")
myTypeCheckTest(test, "string", "3", "Expected type: string but got: 3")
myTypeCheckTest(test, "string", "'a'", "Ok")
myTypeCheckTest(test, "[number]", "[1,2,3]", "Ok")
myTypeCheckTest(test, "[number]", "['a','a','a']", "Expected type: number but got: 'a'")
myTypeCheckTest(test, "[number]", "[1,'a',3]", "Expected type: number but got: 'a'")
myTypeCheckTest(test, "[number, string]", "[1,'a']", "Ok")
myTypeCheckTest(test, "[number, string]", "[1, 2]", "Expected type: string but got: 2")
myTypeCheckTest(
test,
"[number, string, string]",
"[1,'a']",
"Expected type: [number, string, string] but got: [1,'a']",
)
myTypeCheckTest(
test,
"[number, string]",
"[1,'a', 3]",
"Expected type: [number, string] but got: [1,'a',3]",
)
myTypeCheckTest(test, "{age: number, name: string}", "{age: 1, name: 'a'}", "Ok")
myTypeCheckTest(
test,
"{age: number, name: string}",
"{age: 1, name: 'a', job: 'IT'}",
"Expected type: {age: number, name: string} but got: {age: 1,job: 'IT',name: 'a'}",
)
myTypeCheckTest(test, "number | string", "1", "Ok")
myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1")
myTypeCheckTest(test, "number<-min(10)", "10", "Ok")
myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0")
myTypeCheckTest(test, "any", "0", "Ok")
myTypeCheckTest(test, "any", "'a'", "Ok")

View File

@ -1,127 +0,0 @@
open Jest
open Expect
module DispatchT = Reducer_Dispatch_T
module Expression = Reducer_Expression
module ExpressionT = Reducer_Expression_T
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
// Let's build a function to replace switch statements
// 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, 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: ProjectReducerFnT.t): DispatchT.dispatchChainPiece => {
// Let's have a pure implementations
module Implementation = {
let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b)
let arrayConcat = (
a: Js.Array2.t<internalExpressionValue>,
b: Js.Array2.t<internalExpressionValue>,
): Js.Array2.t<internalExpressionValue> => Js.Array2.concat(a, b)
let plot = _r => "yey, plotted"
}
let extractStringString = args =>
switch args {
| [IEvString(a), IEvString(b)] => (a, b)
| _ => raise(Reducer_Exception.ImpossibleException("extractStringString developer error"))
}
let extractArrayArray = args =>
switch args {
| [IEvArray(a), IEvArray(b)] => (a, b)
| _ => raise(Reducer_Exception.ImpossibleException("extractArrayArray developer error"))
}
// Let's bridge the pure implementation to expression values
module Bridge = {
let stringConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
let (a, b) = extractStringString(args)
Implementation.stringConcat(a, b)->IEvString->Ok
}
let arrayConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
let (a, b) = extractArrayArray(args)
Implementation.arrayConcat(a, b)->IEvArray->Ok
}
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
| _ => raise(Reducer_Exception.ImpossibleException("plot developer error"))
}
}
}
// concat functions are to illustrate polymoprhism. And the plot function is to illustrate complex types
let jumpTable = [
(
"concat",
TypeCompile.fromTypeExpressionExn("string=>string=>string", reducer),
Bridge.stringConcat,
),
(
"concat",
TypeCompile.fromTypeExpressionExn("[any]=>[any]=>[any]", reducer),
Bridge.arrayConcat,
),
(
"plot",
TypeCompile.fromTypeExpressionExn(
// Nested complex types are available
// records {property: type}
// arrays [type]
// tuples [type, type]
// <- type contracts are available naturally and they become part of dispatching
// Here we are not enumerating the possibilities because type checking has a dedicated test
"{title: string, line: {width: number, color: string}}=>string",
reducer,
),
Bridge.plot,
),
]
//Here we are creating a dispatchChainPiece function that will do the actual dispatch from the jumpTable
Reducer_Dispatch_ChainPiece.makeFromTypes(jumpTable)
}
// And finally, let's write a library dispatch for our external library
// Exactly the same as the one used in real life
let _dispatch = (
call: functionCall,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
chain,
): result<internalExpressionValue, 'e> => {
let dispatchChainPiece = makeMyDispatchChainPiece(reducer)
dispatchChainPiece(call, accessors)->E.O2.defaultFn(() => chain(call, accessors, reducer))
}
// What is important about this implementation?
// A) Exactly the same function jump table can be used to create type guarded lambda functions
// Guarded lambda functions will be the basis of the next version of Squiggle
// B) Complicated recursive record types are not a problem.
describe("Type Dispatch", () => {
let reducerFn = Expression.reduceExpressionInProject
let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn)
test("stringConcat", () => {
let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")])
let result = dispatchChainPiece(call, ProjectAccessorsT.identityAccessors)
expect(result)->toEqual(Some(Ok(IEvString("helloworld"))))
})
})

View File

@ -0,0 +1,14 @@
open Jest
open Expect
describe("ExpressionValue", () => {
test("argsToString", () =>
expect([IEvNumber(1.), IEvString("a")]->Reducer_Value.argsToString)->toBe("1,'a'")
)
test("toStringFunctionCall", () =>
expect(("fn", [IEvNumber(1.), IEvString("a")])->Reducer_Value.toStringFunctionCall)->toBe(
"fn(1,'a')",
)
)
})

View File

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

View File

@ -2,17 +2,15 @@ open Jest
open Reducer_TestHelpers open Reducer_TestHelpers
describe("Parse function assignment", () => { describe("Parse function assignment", () => {
testParseToBe( testParseToBe("f(x)=x", "Ok(f = {|x| {x}})")
"f(x)=x", testParseToBe("f(x)=2*x", "Ok(f = {|x| {(multiply)(2, x)}})")
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})",
)
testParseToBe(
"f(x)=2*x",
"Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})",
)
//MathJs does not allow blocks in function definitions //MathJs does not allow blocks in function definitions
}) })
describe("Evaluate function assignment", () => { describe("Evaluate function assignment", () => {
testEvalToBe("f(x)=x; f(1)", "Ok(1)") testEvalToBe("f(x)=x; f(1)", "Ok(1)")
}) })
describe("Shadowing", () => {
testEvalToBe("x = 5; f(y) = x*y; x = 6; f(2)", "Ok(10)")
})

View File

@ -34,7 +34,7 @@ describe("symbol not defined", () => {
testEvalToBe("f(x)=x(y); f(f)", "Error(y is not defined)") testEvalToBe("f(x)=x(y); f(f)", "Error(y is not defined)")
testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))") testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))")
testEvalToBe("f(x)=x(y); f(z)", "Error(z is not defined)") testEvalToBe("f(x)=x(y); f(z)", "Error(z is not defined)")
testEvalToBe("f(x)=x(y); f(2)", "Error(2 is not a function)") testEvalToBe("f(x)=x(y); f(2)", "Error(y is not defined)")
testEvalToBe("f(x)=x(1); f(2)", "Error(2 is not a function)") testEvalToBe("f(x)=x(1); f(2)", "Error(2 is not a function)")
}) })
@ -46,10 +46,7 @@ describe("call and bindings", () => {
testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)") testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)")
testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)") 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)") testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)")
testParseToBe( testParseToBe("f=99; g(x)=f; g(2)", "Ok(f = {99}; g = {|x| {f}}; (g)(2))")
"f=99; g(x)=f; g(2)",
"Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:$_endOfOuterBlock_$ () (:g 2))})",
)
testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)") testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)")
testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)") testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)")
testEvalToBe("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; y=g(2); y", "Ok(4)")

View File

@ -5,3 +5,7 @@ Skip.describe("map reduce (sam)", () => {
testEvalToBe("addone(x)=x+1; map(2, addone)", "Error???") testEvalToBe("addone(x)=x+1; map(2, addone)", "Error???")
testEvalToBe("addone(x)=x+1; map(2, {x: addone})", "Error???") testEvalToBe("addone(x)=x+1; map(2, {x: addone})", "Error???")
}) })
describe("map", () => {
testEvalToBe("arr=[1,2,3]; map(arr, {|x| x*2})", "Ok([2,4,6])")
})

View File

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

View File

@ -2,19 +2,29 @@ open Jest
open Reducer_TestHelpers open Reducer_TestHelpers
describe("eval", () => { describe("eval", () => {
// All MathJs operators and functions are builtin for string, float and boolean
// .e.g + - / * > >= < <= == /= not and or
// See https://mathjs.org/docs/reference/functions.html
describe("expressions", () => { describe("expressions", () => {
testEvalToBe("1", "Ok(1)") testEvalToBe("1", "Ok(1)")
testEvalToBe("-1", "Ok(-1)")
testEvalToBe("1-1", "Ok(0)")
testEvalToBe("1+2", "Ok(3)") testEvalToBe("1+2", "Ok(3)")
testEvalToBe("(1+2)*3", "Ok(9)") testEvalToBe("(1+2)*3", "Ok(9)")
testEvalToBe("2>1", "Ok(true)") testEvalToBe("2>1", "Ok(true)")
testEvalToBe("concat('a ', 'b')", "Ok('a b')") testEvalToBe("concat('a ', 'b')", "Ok('a b')")
testEvalToBe("concat([3,4], [5,6,7])", "Ok([3,4,5,6,7])")
testEvalToBe("log(10)", "Ok(2.302585092994046)") testEvalToBe("log(10)", "Ok(2.302585092994046)")
testEvalToBe("cos(10)", "Ok(-0.8390715290764524)") testEvalToBe("Math.cos(10)", "Ok(-0.8390715290764524)")
// TODO more built ins // TODO more built ins
}) })
describe("missing function", () => {
testEvalToBe("testZadanga(1)", "Error(testZadanga is not defined)")
testEvalToBe(
"arr = [normal(3,2)]; map(arr, zarathsuzaWasHere)",
"Error(zarathsuzaWasHere is not defined)",
)
})
describe("arrays", () => { describe("arrays", () => {
test("empty array", () => expectEvalToBe("[]", "Ok([])")) test("empty array", () => expectEvalToBe("[]", "Ok([])"))
testEvalToBe("[1, 2, 3]", "Ok([1,2,3])") testEvalToBe("[1, 2, 3]", "Ok([1,2,3])")
@ -50,6 +60,10 @@ describe("eval", () => {
testEvalError("1; 1") testEvalError("1; 1")
testEvalToBe("x=1; x=1; x", "Ok(1)") testEvalToBe("x=1; x=1; x", "Ok(1)")
}) })
describe("blocks", () => {
testEvalToBe("x = { y = { z = 5; z * 2 }; y + 3 }; x", "Ok(13)")
})
}) })
describe("test exceptions", () => { describe("test exceptions", () => {

View File

@ -1,11 +0,0 @@
open ReducerInterface.InternalExpressionValue
open Jest
open Expect
describe("ExpressionValue", () => {
test("argsToString", () => expect([IEvNumber(1.), IEvString("a")]->argsToString)->toBe("1,'a'"))
test("toStringFunctionCall", () =>
expect(("fn", [IEvNumber(1.), IEvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
)
})

View File

@ -1,7 +1,5 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest open Jest
open Expect open Expect
@ -30,12 +28,11 @@ x=1`,
| Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
} }
}) })
let internalProject = project->Project.T.Private.castToInternalProject
test("past chain", () => { test("past chain", () => {
expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] expect(project->Project.getPastChain("main")) == ["common"]
}) })
test("import as variables", () => { test("import as variables", () => {
expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [] expect(project->Project.Private.getIncludesAsVariables("main")) == []
}) })
}) })
@ -67,20 +64,16 @@ x=1`,
} }
}) })
let internalProject = project->Project.T.Private.castToInternalProject
test("direct past chain", () => { test("direct past chain", () => {
expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] expect(project->Project.Private.getPastChain("main")) == ["common"]
}) })
test("direct includes", () => { test("direct includes", () => {
expect(Project.Private.getDirectIncludes(internalProject, "main")) == ["common"] expect(project->Project.Private.getDirectIncludes("main")) == ["common"]
}) })
test("include as variables", () => { test("include as variables", () => {
expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ expect(project->Project.Private.getIncludesAsVariables("main")) == [("myVariable", "myModule")]
("myVariable", "myModule"),
]
}) })
}) })
@ -109,13 +102,10 @@ x=1`,
| Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
} }
}) })
let internalProject = project->Project.T.Private.castToInternalProject
test("direct past chain", () => { test("direct past chain", () => {
expect(Project.getPastChain(project, "main")) == ["common", "common2"] expect(Project.getPastChain(project, "main")) == ["common", "common2"]
}) })
test("include as variables", () => { test("include as variables", () => {
expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ expect(project->Project.Private.getIncludesAsVariables("main")) == [("myVariable", "myModule")]
("myVariable", "myModule"),
]
}) })
}) })

View File

@ -1,5 +1,4 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
@ -7,29 +6,16 @@ open Jest
open Expect open Expect
open Expect.Operators open Expect.Operators
// test("", () => expect(1)->toBe(1))
let runFetchResult = (project, sourceId) => { let runFetchResult = (project, sourceId) => {
Project.run(project, sourceId) Project.run(project, sourceId)
Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult Project.getResult(project, sourceId)->Reducer_Value.toStringResult
} }
let runFetchFlatBindings = (project, sourceId) => { let runFetchFlatBindings = (project, sourceId) => {
Project.run(project, sourceId) Project.run(project, sourceId)
Project.getBindings(project, sourceId) Project.getBindings(project, sourceId)->Reducer_Value.toStringRecord
->Bindings.removeResult
->InternalExpressionValue.toStringBindings
} }
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", () => { test("test result true", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "main", "true") Project.setSource(project, "main", "true")
@ -51,7 +37,7 @@ test("test library", () => {
test("test bindings", () => { test("test bindings", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "variables", "myVariable=666") Project.setSource(project, "variables", "myVariable=666")
runFetchFlatBindings(project, "variables")->expect->toBe("@{myVariable: 666}") runFetchFlatBindings(project, "variables")->expect->toBe("{myVariable: 666}")
}) })
describe("project1", () => { describe("project1", () => {
@ -59,7 +45,6 @@ describe("project1", () => {
Project.setSource(project, "first", "x=1") Project.setSource(project, "first", "x=1")
Project.setSource(project, "main", "x") Project.setSource(project, "main", "x")
Project.setContinues(project, "main", ["first"]) Project.setContinues(project, "main", ["first"])
let internalProject = project->Project.T.Private.castToInternalProject
test("runOrder", () => { test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["first", "main"] expect(Project.getRunOrder(project)) == ["first", "main"]
@ -78,17 +63,17 @@ describe("project1", () => {
}) })
test("past chain first", () => { test("past chain first", () => {
expect(Project.Private.getPastChain(internalProject, "first")) == [] expect(ReducerProject.getPastChain(project, "first")) == []
}) })
test("past chain main", () => { test("past chain main", () => {
expect(Project.Private.getPastChain(internalProject, "main")) == ["first"] expect(ReducerProject.getPastChain(project, "main")) == ["first"]
}) })
test("test result", () => { test("test result", () => {
runFetchResult(project, "main")->expect->toBe("Ok(1)") runFetchResult(project, "main")->expect->toBe("Ok(1)")
}) })
test("test bindings", () => { test("test bindings", () => {
runFetchFlatBindings(project, "first")->expect->toBe("@{x: 1}") runFetchFlatBindings(project, "first")->expect->toBe("{x: 1}")
}) })
}) })
@ -98,7 +83,7 @@ describe("project2", () => {
Project.setContinues(project, "second", ["first"]) Project.setContinues(project, "second", ["first"])
Project.setSource(project, "first", "x=1") Project.setSource(project, "first", "x=1")
Project.setSource(project, "second", "y=2") Project.setSource(project, "second", "y=2")
Project.setSource(project, "main", "y") Project.setSource(project, "main", "z=3;y")
test("runOrder", () => { test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["first", "second", "main"] expect(Project.getRunOrder(project)) == ["first", "second", "main"]
@ -122,7 +107,8 @@ describe("project2", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)") runFetchResult(project, "main")->expect->toBe("Ok(2)")
}) })
test("test bindings", () => { test("test bindings", () => {
runFetchFlatBindings(project, "main")->expect->toBe("@{x: 1,y: 2}") // bindings from continues are not exposed!
runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}")
}) })
}) })
@ -152,7 +138,7 @@ describe("project with include", () => {
) )
Project.parseIncludes(project, "second") //The only way of setting includes Project.parseIncludes(project, "second") //The only way of setting includes
Project.setSource(project, "main", "y") Project.setSource(project, "main", "z=3; y")
test("runOrder", () => { test("runOrder", () => {
expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"] expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"]
@ -178,7 +164,8 @@ describe("project with include", () => {
runFetchResult(project, "main")->expect->toBe("Ok(2)") runFetchResult(project, "main")->expect->toBe("Ok(2)")
}) })
test("test bindings", () => { test("test bindings", () => {
runFetchFlatBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}") // bindings from continues are not exposed!
runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}")
}) })
}) })

View File

@ -1,5 +1,4 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
@ -16,13 +15,13 @@ Case "Running a single source".
/* Let's start with running a single source and getting Result as well as the Bindings /* Let's start with running a single source and getting Result as well as the Bindings
First you need to create a project. A project is a collection of sources. First you need to create a project. A project is a collection of sources.
Project takes care of the dependencies between the sources, correct compilation and run order. Project takes care of the dependencies between the sources, correct compilation and run order.
You can run any source in the project. It will be compiled and run if it is not already done else already existing results will be presented. You can run any source in the project. It will be compiled and run if it hasn't happened already; otherwise already existing results will be presented.
The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project. The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project.
In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source. In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source.
*/ */
let project = Project.createProject() let project = Project.createProject()
/* Every source has a name. This is used for debugging, dependencies and error messages. */ /* Every source has a name. This is used for debugging, dependencies and error messages. */
Project.setSource(project, "main", "1 + 2") project->Project.setSource("main", "1 + 2")
/* Let's run "main" source. */ /* Let's run "main" source. */
project->Project.run("main") project->Project.run("main")
/* Now you have a result for "main" source. /* Now you have a result for "main" source.
@ -46,27 +45,25 @@ Case "Running a single source".
Getting None means you have forgotten to run the source. Getting None means you have forgotten to run the source.
*/ */
let result = project->Project.getResult("main") let result = project->Project.getResult("main")
let bindings = project->Project.getBindings("main")->Bindings.removeResult let bindings = project->Project.getBindings("main")
/* Let's display the result and bindings */ /* Let's display the result and bindings */
( (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect ==
result->InternalExpressionValue.toStringResult, ("Ok(3)", "{}")
bindings->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(3)", "@{}")
/* You've got 3 with empty bindings. */ /* You've got 3 with empty bindings. */
}) })
test("run summary", () => { test("run summary", () => {
let project = Project.createProject() let project = Project.createProject()
Project.setSource(project, "main", "1 + 2") project->Project.setSource("main", "1 + 2")
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let bindings = Project.getBindings(project, "main")->Bindings.removeResult let bindings = project->Project.getBindings("main")
/* Now you have external bindings and external result. */ /* Now you have external bindings and external result. */
( (
result->InternalExpressionValue.toStringResult, result->Reducer_Value.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString, bindings->Reducer_T.IEvRecord->Reducer_Value.toString,
)->expect == ("Ok(3)", "@{}") )->expect == ("Ok(3)", "{}")
}) })
test("run with an environment", () => { test("run with an environment", () => {
@ -74,23 +71,21 @@ Case "Running a single source".
let project = Project.createProject() let project = Project.createProject()
/* Optional. Set your custom environment anytime before running */ /* Optional. Set your custom environment anytime before running */
Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment) project->Project.setEnvironment(Reducer_Context.defaultEnvironment)
Project.setSource(project, "main", "1 + 2") project->Project.setSource("main", "1 + 2")
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let _bindings = Project.getBindings(project, "main") let _bindings = project->Project.getBindings("main")
result->InternalExpressionValue.toStringResult->expect == "Ok(3)" result->Reducer_Value.toStringResult->expect == "Ok(3)"
}) })
test("shortcut", () => { test("shortcut", () => {
/* If you are running single source without includes and you don't need a custom environment, you can use the 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. */ /* Examples above was to prepare you for the multi source tutorial. */
let (result, bindings) = Project.evaluate("1+2") let (result, bindings) = Project.evaluate("1+2")
( (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect ==
result->InternalExpressionValue.toStringResult, ("Ok(3)", "{}")
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(3)", "@{}")
}) })
}) })
}) })

View File

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

View File

@ -1,5 +1,4 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
@ -16,8 +15,7 @@ Here we will finally proceed to a real life scenario. */
/* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */ /* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */
/* Everything happens inside a project, so let's have a project */ /* Everything happens inside a project, so let's have a project */
let project = Project.createProject() let project = Project.createProject()
Project.setSource( project->Project.setSource(
project,
"main", "main",
` `
#include "common" #include "common"
@ -25,10 +23,10 @@ Here we will finally proceed to a real life scenario. */
`, `,
) )
/* We need to parse includes after changing the source */ /* We need to parse includes after changing the source */
Project.parseIncludes(project, "main") project->Project.parseIncludes("main")
test("getDependencies", () => { test("getDependencies", () => {
/* Parse includes has set the dependencies */ /* Parse includes has set the dependencies */
Project.getDependencies(project, "main")->expect == ["common"] project->Project.getDependencies("main")->expect == ["common"]
/* If there were no includes than there would be no dependencies */ /* If there were no includes than there would be no dependencies */
/* However if there was a syntax error at includes then would be no dependencies also */ /* However if there was a syntax error at includes then would be no dependencies also */
/* Therefore looking at dependencies is not the right way to load includes */ /* Therefore looking at dependencies is not the right way to load includes */
@ -36,7 +34,7 @@ Here we will finally proceed to a real life scenario. */
}) })
test("getIncludes", () => { test("getIncludes", () => {
/* Parse includes has set the includes */ /* Parse includes has set the includes */
switch Project.getIncludes(project, "main") { switch project->Project.getIncludes("main") {
| Ok(includes) => includes->expect == ["common"] | Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail | Error(err) => err->Reducer_ErrorValue.errorToString->fail
} }
@ -50,7 +48,7 @@ Here we will finally proceed to a real life scenario. */
include or depend on the current source. include or depend on the current source.
But you don't need to use this to execute the projects. But you don't need to use this to execute the projects.
It is provided for completeness of information. */ It is provided for completeness of information. */
Project.getDependents(project, "main")->expect == [] project->Project.getDependents("main")->expect == []
/* Nothing is depending on or including main */ /* Nothing is depending on or including main */
}) })
@ -76,29 +74,29 @@ Here we will finally proceed to a real life scenario. */
/* let's recursively load the sources */ /* let's recursively load the sources */
let rec loadIncludesRecursively = (project, sourceName, visited) => { let rec loadIncludesRecursively = (project, sourceName, visited) => {
if Js.Array2.includes(visited, sourceName) { if visited->Js.Array2.includes(sourceName) {
/* Oh we have already visited this source. There is an include cycle */ /* Oh we have already visited this source. There is an include cycle */
"Cyclic include ${sourceName}"->Js.Exn.raiseError "Cyclic include ${sourceName}"->Js.Exn.raiseError
} else { } else {
let newVisited = Js.Array2.copy(visited) let newVisited = Js.Array2.copy(visited)
let _ = Js.Array2.push(newVisited, sourceName) let _ = newVisited->Js.Array2.push(sourceName)
/* Let's parse the includes and dive into them */ /* Let's parse the includes and dive into them */
Project.parseIncludes(project, sourceName) Project.parseIncludes(project, sourceName)
let rIncludes = Project.getIncludes(project, sourceName) let rIncludes = project->Project.getIncludes(sourceName)
switch rIncludes { switch rIncludes {
/* Maybe there is an include syntax error */ /* Maybe there is an include syntax error */
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError | Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
| Ok(includes) => | Ok(includes) =>
Belt.Array.forEach(includes, newIncludeName => { includes->Belt.Array.forEach(newIncludeName => {
/* We have got one of the new includes. /* We have got one of the new includes.
Let's load it and add it to the project */ Let's load it and add it to the project */
let newSource = loadSource(newIncludeName) let newSource = loadSource(newIncludeName)
Project.setSource(project, newIncludeName, newSource) project->Project.setSource(newIncludeName, newSource)
/* The new source is loaded and added to the project. */ /* The new source is loaded and added to the project. */
/* Of course the new source might have includes too. */ /* Of course the new source might have includes too. */
/* Let's recursively load them */ /* Let's recursively load them */
loadIncludesRecursively(project, newIncludeName, newVisited) project->loadIncludesRecursively(newIncludeName, newVisited)
}) })
} }
} }
@ -110,45 +108,44 @@ Here we will finally proceed to a real life scenario. */
let project = Project.createProject() let project = Project.createProject()
/* main includes source3 which includes source2 which includes source1 */ project->Project.setSource(
Project.setSource(
project,
"main", "main",
` `
#include "source1"
#include "source2"
#include "source3" #include "source3"
x+y+z a = x+y+z
b = doubleX
a
`, `,
) )
/* Setting source requires parsing and loading the includes recursively */ /* Setting source requires parsing and loading the includes recursively */
loadIncludesRecursively(project, "main", []) //No visited yet project->loadIncludesRecursively("main", []) // Not visited yet
/* Let's salt it more. Let's have another source in the project which also has includes */ /* Let's salt it more. Let's have another source in the project which also has includes */
/* doubleX includes source1 which is eventually included by main as well */ /* doubleX includes source1 which is eventually included by main as well */
Project.setSource( project->Project.setSource(
project,
"doubleX", "doubleX",
` `
#include "source1" #include "source1"
doubleX = x * 2 doubleX = x * 2
`, `,
) )
loadIncludesRecursively(project, "doubleX", []) project->loadIncludesRecursively("doubleX", [])
/* Remember, any time you set a source, you need to load includes recursively */ /* Remember, any time you set a source, you need to load includes recursively */
/* As doubleX is not included by main, it is not loaded recursively. /* As doubleX is not included by main, it is not loaded recursively.
So we link it to the project as a dependency */ So we link it to the project as a dependency */
Project.setContinues(project, "main", ["doubleX"]) project->Project.setContinues("main", ["doubleX"])
/* Let's run the project */ /* Let's run the project */
Project.runAll(project) project->Project.runAll
let result = Project.getResult(project, "main") let result = project->Project.getResult("main")
let bindings = Project.getBindings(project, "main") let bindings = project->Project.getBindings("main")
/* And see the result and bindings.. */ /* And see the result and bindings.. */
test("recursive includes", () => { test("recursive includes", () => {
( (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect ==
result->InternalExpressionValue.toStringResult, ("Ok(6)", "{a: 6,b: 2}")
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}")
/* Everything as expected */ /* Everything as expected */
}) })
}) })

View File

@ -1,7 +1,5 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest open Jest
open Expect open Expect
@ -30,7 +28,7 @@ describe("ReducerProject Tutorial", () => {
/* We can now run the project */ /* We can now run the project */
Project.runAll(project) Project.runAll(project)
let result = Project.getResult(project, "main") let result = Project.getResult(project, "main")
result->InternalExpressionValue.toStringResult->expect == "Ok(6)" result->Reducer_Value.toStringResult->expect == "Ok(6)"
}) })
}) })

View File

@ -1,5 +1,4 @@
@@warning("-44") @@warning("-44")
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
@ -32,7 +31,7 @@ describe("ReducerProject Tutorial", () => {
test("userResults", () => { test("userResults", () => {
let userResultsAsString = Belt.Array.map(userResults, aResult => let userResultsAsString = Belt.Array.map(userResults, aResult =>
aResult->InternalExpressionValue.toStringResult aResult->Reducer_Value.toStringResult
) )
userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"] userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"]
}) })

View File

@ -23,7 +23,7 @@ describe("eval on distribution functions", () => {
testEval("-normal(5,2)", "Ok(Normal(-5,2))") testEval("-normal(5,2)", "Ok(Normal(-5,2))")
}) })
describe("to", () => { describe("to", () => {
testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") testEval("5 to 2", "Error(Error: Low value must be less than high value.)")
testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))")
testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))") testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))")
}) })
@ -119,40 +119,28 @@ describe("eval on distribution functions", () => {
describe("parse on distribution functions", () => { describe("parse on distribution functions", () => {
describe("power", () => { describe("power", () => {
testParse( testParse("normal(5,2) ^ normal(5,1)", "Ok((pow)((normal)(5, 2), (normal)(5, 1)))")
"normal(5,2) ^ normal(5,1)", testParse("3 ^ normal(5,1)", "Ok((pow)(3, (normal)(5, 1)))")
"Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})", testParse("normal(5,2) ^ 3", "Ok((pow)((normal)(5, 2), 3))")
)
testParse("3 ^ normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:pow 3 (:normal 5 1)))})")
testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})")
}) })
describe("subtraction", () => { describe("subtraction", () => {
testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})") testParse("10 - normal(5,1)", "Ok((subtract)(10, (normal)(5, 1)))")
testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})") testParse("normal(5,1) - 10", "Ok((subtract)((normal)(5, 1), 10))")
}) })
describe("pointwise arithmetic expressions", () => { describe("pointwise arithmetic expressions", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
testParse( testParse(
~skip=true, ~skip=true,
"normal(5,2) .- normal(5,1)", "normal(5,2) .- normal(5,1)",
"Ok((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))", "Ok((:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1))))",
// TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})" // TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})"
) )
testParse( testParse("normal(5,2) .* normal(5,1)", "Ok((dotMultiply)((normal)(5, 2), (normal)(5, 1)))")
"normal(5,2) .* normal(5,1)", testParse("normal(5,2) ./ normal(5,1)", "Ok((dotDivide)((normal)(5, 2), (normal)(5, 1)))")
"Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (: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_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})",
)
testParse(
"normal(5,2) .^ normal(5,1)",
"Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})",
)
}) })
describe("equality", () => { describe("equality", () => {
testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})") testParse("5 == normal(5,2)", "Ok((equal)(5, (normal)(5, 2)))")
}) })
describe("pointwise adding two normals", () => { describe("pointwise adding two normals", () => {
testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")

View File

@ -3,11 +3,7 @@ open Expect
open Reducer_TestHelpers open Reducer_TestHelpers
let expectEvalToBeOk = (code: string) => let expectEvalToBeOk = (code: string) =>
Reducer_Expression.BackCompatible.evaluateString(code) Reducer_Expression.BackCompatible.evaluateString(code)->E.R.isOk->expect->toBe(true)
->Reducer_Helpers.rRemoveDefaultsInternal
->E.R.isOk
->expect
->toBe(true)
let registry = FunctionRegistry_Library.registry let registry = FunctionRegistry_Library.registry
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry)) let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
@ -15,7 +11,7 @@ let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
describe("FunctionRegistry Library", () => { describe("FunctionRegistry Library", () => {
describe("Regular tests", () => { describe("Regular tests", () => {
testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])") testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])")
testEvalToBe("make(3, 'HI')", "Error(Function not found: make(Number,String))") testEvalToBe("make(3, 'HI')", "Error(make is not defined)")
testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])") testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])")
testEvalToBe("List.first([3,5,8])", "Ok(3)") testEvalToBe("List.first([3,5,8])", "Ok(3)")
testEvalToBe("List.last([3,5,8])", "Ok(8)") testEvalToBe("List.last([3,5,8])", "Ok(8)")
@ -84,6 +80,13 @@ describe("FunctionRegistry Library", () => {
"SampleSet.toList(SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6]), SampleSet.fromList([6, 5, 4, 3, 2, 1])], {|x| x[0] > x[1] ? x[0] : x[1]}))", "SampleSet.toList(SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6]), SampleSet.fromList([6, 5, 4, 3, 2, 1])], {|x| x[0] > x[1] ? x[0] : x[1]}))",
"Ok([6,5,4,4,5,6])", "Ok([6,5,4,4,5,6])",
) )
testEvalToBe("Dict.merge({a: 1, b: 2}, {b: 3, c: 4, d: 5})", "Ok({a: 1,b: 3,c: 4,d: 5})")
testEvalToBe("Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}, {c: 5, e: 6}])", "Ok({a: 1,b: 2,c: 5,d: 4,e: 6})")
testEvalToBe("Dict.keys({a: 1, b: 2})", "Ok(['a','b'])")
testEvalToBe("Dict.values({a: 1, b: 2})", "Ok([1,2])")
testEvalToBe("Dict.toList({a: 1, b: 2})", "Ok([['a',1],['b',2]])")
testEvalToBe("Dict.fromList([['a', 1], ['b', 2]])", "Ok({a: 1,b: 2})")
}) })
describe("Fn auto-testing", () => { describe("Fn auto-testing", () => {
@ -102,7 +105,7 @@ describe("FunctionRegistry Library", () => {
let responseType = let responseType =
example example
->Reducer_Expression.BackCompatible.evaluateString ->Reducer_Expression.BackCompatible.evaluateString
->E.R2.fmap(ReducerInterface_InternalExpressionValue.valueToValueType) ->E.R2.fmap(Reducer_Value.valueToValueType)
let expectedOutputType = fn.output |> E.O.toExn("") let expectedOutputType = fn.output |> E.O.toExn("")
expect(responseType)->toEqual(Ok(expectedOutputType)) expect(responseType)->toEqual(Ok(expectedOutputType))
}, },

View File

@ -0,0 +1,80 @@
module Map: Benchmark_Helpers.BenchmarkTopic = {
let arraySize = 1000
let iterations = 300_000
let beltArray = () => {
let x = Belt.Array.make(arraySize, 0.)
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Belt.Array.map(v => v)
})
}
let jsArray2 = () => {
let x = Belt.Array.make(arraySize, 0.)
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Js.Array2.map(v => v)
})
}
let ocamlArray = () => {
let x = Belt.Array.make(arraySize, 0.)
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Array.map(v => v, _)
})
}
let runAll = () => {
Js.log(
`Mapping identity function over arrays of size ${arraySize->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`,
)
Benchmark_Helpers.measure("Belt.Array.map", beltArray)
Benchmark_Helpers.measure("Js.Array2.map", jsArray2)
Benchmark_Helpers.measure("Array.map", ocamlArray)
}
}
module Sort: Benchmark_Helpers.BenchmarkTopic = {
let arraySize = 1000
let iterations = 30000
let jsArray2 = () => {
let x = Belt.Array.make(arraySize, 0.)
let compare = (a: float, b: float) => {
if a < b {
-1
} else {
1
}
}
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Js.Array2.sortInPlaceWith(compare)
})
}
let jsArray2withOcamlCompare = () => {
let x = Belt.Array.make(arraySize, 0.)
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Js.Array2.sortInPlaceWith(Pervasives.compare)
})
}
let ocamlArray = () => {
let x = Belt.Array.make(arraySize, 0.)
Belt.Range.forEach(1, iterations, _ => {
let _ = x->Array.fast_sort(compare, _)
})
}
let runAll = () => {
Js.log(
`Sorting arrays of size ${arraySize->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`,
)
Benchmark_Helpers.measure("Js.Array2.sort", jsArray2)
Benchmark_Helpers.measure("Js.Array2.sort with Ocaml compare", jsArray2withOcamlCompare)
Benchmark_Helpers.measure("Array.fast_sort", ocamlArray)
}
}
Map.runAll()
Sort.runAll()

View File

@ -0,0 +1,11 @@
module type BenchmarkTopic = {
let runAll: unit => unit
}
let measure = (name: string, f: unit => unit) => {
let start = Js.Date.make()->Js.Date.valueOf
f()
let end = Js.Date.make()->Js.Date.valueOf
let duration = (end -. start) /. 1000.
Js.log2(duration, name)
}

View File

@ -0,0 +1,63 @@
module StringMap: Benchmark_Helpers.BenchmarkTopic = {
let size = 1000
let iterations = 10_000
let kv = Belt.Array.range(1, size)->Belt.Array.map(v => ("key" ++ v->Belt.Int.toString, v))
let beltMap = () => {
Belt.Range.forEach(1, iterations, _ => {
let m = Belt.Map.String.empty
let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => acc->Belt.Map.String.set(k, v))
})
}
let beltMutableMap = () => {
Belt.Range.forEach(1, iterations, _ => {
let m = Belt.MutableMap.String.make()
let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => {
acc->Belt.MutableMap.String.set(k, v)
acc
})
})
}
let beltHashMap = () => {
Belt.Range.forEach(1, iterations, _ => {
let m = Belt.HashMap.String.make(~hintSize=100)
let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => {
acc->Belt.HashMap.String.set(k, v)
acc
})
})
}
let jsDict = () => {
Belt.Range.forEach(1, iterations, _ => {
let m = Js.Dict.empty()
let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => {
acc->Js.Dict.set(k, v)
acc
})
})
}
let jsMap = () => {
Belt.Range.forEach(1, iterations, _ => {
let m = Js_map.make()
let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => acc->Js_map.set(k, v))
})
}
let runAll = () => {
Js.log(
`Filling a map with ("key{i}" => "i") key-value pairs, size ${size->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`,
)
Benchmark_Helpers.measure("Belt.Map.String", beltMap)
Benchmark_Helpers.measure("Belt.MutableMap.String", beltMutableMap)
Benchmark_Helpers.measure("Belt.HashMap.String", beltHashMap)
Benchmark_Helpers.measure("Js.Dict", jsDict)
Benchmark_Helpers.measure("Js.Map", jsMap)
}
}
let runAll = StringMap.runAll()

View File

@ -9,6 +9,11 @@
"dir": "__tests__", "dir": "__tests__",
"type": "dev", "type": "dev",
"subdirs": true "subdirs": true
},
{
"dir": "benchmark",
"type": "dev",
"subdirs": true
} }
], ],
"bsc-flags": ["-bs-super-errors", "-bs-no-version-header", "-bs-g"], "bsc-flags": ["-bs-super-errors", "-bs-no-version-header", "-bs-g"],
@ -21,7 +26,12 @@
"suffix": ".bs.js", "suffix": ".bs.js",
"namespace": true, "namespace": true,
"bs-dependencies": ["bisect_ppx"], "bs-dependencies": ["bisect_ppx"],
"bs-dev-dependencies": ["@glennsl/rescript-jest", "rescript-fast-check"], "bs-dev-dependencies": [
"@glennsl/rescript-jest",
"rescript-fast-check",
"rescript-js-map",
"rescript-js-iterator"
],
"gentypeconfig": { "gentypeconfig": {
"language": "typescript", "language": "typescript",
"module": "commonjs", "module": "commonjs",

View File

@ -10,5 +10,7 @@ module.exports = {
"/node_modules/", "/node_modules/",
".*Helpers.bs.js", ".*Helpers.bs.js",
".*Helpers.ts", ".*Helpers.ts",
".*Reducer_Type.*",
".*_type_test.bs.js",
], ],
}; };

View File

@ -1,6 +1,6 @@
{ {
"name": "@quri/squiggle-lang", "name": "@quri/squiggle-lang",
"version": "0.4.2", "version": "0.5.0-alpha.2",
"homepage": "https://squiggle-language.com", "homepage": "https://squiggle-language.com",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -66,6 +66,7 @@
"reanalyze": "^2.23.0", "reanalyze": "^2.23.0",
"rescript": "^9.1.4", "rescript": "^9.1.4",
"rescript-fast-check": "^1.1.1", "rescript-fast-check": "^1.1.1",
"rescript-js-map": "^1.1.0",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"ts-loader": "^9.4.0", "ts-loader": "^9.4.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",

View File

@ -13,7 +13,7 @@ export const measure = (cb, times = 1) => {
export const red = (str) => `\x1b[31m${str}\x1b[0m`; export const red = (str) => `\x1b[31m${str}\x1b[0m`;
export const green = (str) => `\x1b[32m${str}\x1b[0m`; export const green = (str) => `\x1b[32m${str}\x1b[0m`;
export const run = (src, { output, sampleCount }) => { export const run = (src, { output, sampleCount } = {}) => {
const project = SqProject.create(); const project = SqProject.create();
if (sampleCount) { if (sampleCount) {
project.setEnvironment({ project.setEnvironment({

View File

@ -7,4 +7,6 @@ if (!src) {
} }
console.log(`Running ${src}`); console.log(`Running ${src}`);
run(src); const sampleCount = process.env.SAMPLE_COUNT;
run(src, { output: true, sampleCount });

View File

@ -1,30 +0,0 @@
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
import { SqModuleValue, wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
export class SqModule {
constructor(
private _value: RSModuleValue.squiggleValue_Module,
public location: SqValueLocation
) {}
entries() {
return RSModuleValue.getKeyValuePairs(this._value).map(
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
);
}
asValue() {
return new SqModuleValue(
RSModuleValue.toSquiggleValue(this._value),
this.location
);
}
get(k: string) {
const v = RSModuleValue.get(this._value, k);
return v === undefined || v === null
? undefined
: wrapValue(v, this.location.extend(k));
}
}

View File

@ -1,4 +1,4 @@
import * as _ from "lodash"; import zipWith from "lodash/zipWith";
import { wrapDistribution } from "./SqDistribution"; import { wrapDistribution } from "./SqDistribution";
import * as RSPointSetDist from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution.gen"; import * as RSPointSetDist from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution.gen";
import { pointSetDistributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution_tag"; import { pointSetDistributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution_tag";
@ -16,7 +16,7 @@ const shapePoints = (
): SqPoint[] => { ): SqPoint[] => {
let xs = x.xyShape.xs; let xs = x.xyShape.xs;
let ys = x.xyShape.ys; let ys = x.xyShape.ys;
return _.zipWith(xs, ys, (x, y) => ({ x, y })); return zipWith(xs, ys, (x, y) => ({ x, y }));
}; };
export const wrapPointSetDist = (value: T) => { export const wrapPointSetDist = (value: T) => {

View File

@ -2,7 +2,7 @@ import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen"; import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen"; import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
import { SqError } from "./SqError"; import { SqError } from "./SqError";
import { SqModule } from "./SqModule"; import { SqRecord } from "./SqRecord";
import { wrapValue } from "./SqValue"; import { wrapValue } from "./SqValue";
import { resultMap2 } from "./types"; import { resultMap2 } from "./types";
import { SqValueLocation } from "./SqValueLocation"; import { SqValueLocation } from "./SqValueLocation";
@ -83,7 +83,7 @@ export class SqProject {
} }
getBindings(sourceId: string) { getBindings(sourceId: string) {
return new SqModule( return new SqRecord(
RSProject.getBindings(this._value, sourceId), RSProject.getBindings(this._value, sourceId),
new SqValueLocation(this, sourceId, { new SqValueLocation(this, sourceId, {
root: "bindings", root: "bindings",
@ -111,4 +111,8 @@ export class SqProject {
setEnvironment(environment: environment) { setEnvironment(environment: environment) {
RSProject.setEnvironment(this._value, environment); RSProject.setEnvironment(this._value, environment);
} }
getEnvironment(): environment {
return RSProject.getEnvironment(this._value);
}
} }

View File

@ -1,5 +1,5 @@
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen"; import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
import { wrapValue } from "./SqValue"; import { SqRecordValue, wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation"; import { SqValueLocation } from "./SqValueLocation";
type T = RSRecord.squiggleValue_Record; type T = RSRecord.squiggleValue_Record;
@ -12,4 +12,15 @@ export class SqRecord {
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
); );
} }
toString() {
return RSRecord.toString(this._value);
}
asValue() {
return new SqRecordValue(
RSRecord.toSquiggleValue(this._value),
this.location
);
}
} }

View File

@ -1,7 +0,0 @@
import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.gen";
type T = RSType.squiggleValue_Type;
export class SqType {
constructor(private _value: T) {}
}

View File

@ -3,11 +3,8 @@ import { squiggleValueTag as Tag } from "../rescript/ForTS/ForTS_SquiggleValue/F
import { wrapDistribution } from "./SqDistribution"; import { wrapDistribution } from "./SqDistribution";
import { SqLambda } from "./SqLambda"; import { SqLambda } from "./SqLambda";
import { SqLambdaDeclaration } from "./SqLambdaDeclaration"; import { SqLambdaDeclaration } from "./SqLambdaDeclaration";
import { SqModule } from "./SqModule";
import { SqRecord } from "./SqRecord"; import { SqRecord } from "./SqRecord";
import { SqArray } from "./SqArray"; import { SqArray } from "./SqArray";
import { SqType } from "./SqType";
import { SqProject } from "./SqProject";
import { SqValueLocation } from "./SqValueLocation"; import { SqValueLocation } from "./SqValueLocation";
export { Tag as SqValueTag }; export { Tag as SqValueTag };
@ -46,14 +43,6 @@ export class SqArrayValue extends SqAbstractValue {
} }
} }
export class SqArrayStringValue extends SqAbstractValue {
tag = Tag.ArrayString as const;
get value() {
return this.valueMethod(RSValue.getArrayString);
}
}
export class SqBoolValue extends SqAbstractValue { export class SqBoolValue extends SqAbstractValue {
tag = Tag.Bool as const; tag = Tag.Bool as const;
@ -62,14 +51,6 @@ export class SqBoolValue extends SqAbstractValue {
} }
} }
export class SqCallValue extends SqAbstractValue {
tag = Tag.Call as const;
get value() {
return this.valueMethod(RSValue.getCall);
}
}
export class SqDateValue extends SqAbstractValue { export class SqDateValue extends SqAbstractValue {
tag = Tag.Date as const; tag = Tag.Date as const;
@ -102,14 +83,6 @@ export class SqLambdaValue extends SqAbstractValue {
} }
} }
export class SqModuleValue extends SqAbstractValue {
tag = Tag.Module as const;
get value() {
return new SqModule(this.valueMethod(RSValue.getModule), this.location);
}
}
export class SqNumberValue extends SqAbstractValue { export class SqNumberValue extends SqAbstractValue {
tag = Tag.Number as const; tag = Tag.Number as const;
@ -134,14 +107,6 @@ export class SqStringValue extends SqAbstractValue {
} }
} }
export class SqSymbolValue extends SqAbstractValue {
tag = Tag.Symbol as const;
get value(): string {
return this.valueMethod(RSValue.getSymbol);
}
}
export class SqTimeDurationValue extends SqAbstractValue { export class SqTimeDurationValue extends SqAbstractValue {
tag = Tag.TimeDuration as const; tag = Tag.TimeDuration as const;
@ -150,22 +115,6 @@ export class SqTimeDurationValue extends SqAbstractValue {
} }
} }
export class SqTypeValue extends SqAbstractValue {
tag = Tag.Type as const;
get value() {
return new SqType(this.valueMethod(RSValue.getType));
}
}
export class SqTypeIdentifierValue extends SqAbstractValue {
tag = Tag.TypeIdentifier as const;
get value() {
return this.valueMethod(RSValue.getTypeIdentifier);
}
}
export class SqVoidValue extends SqAbstractValue { export class SqVoidValue extends SqAbstractValue {
tag = Tag.Void as const; tag = Tag.Void as const;
@ -176,21 +125,15 @@ export class SqVoidValue extends SqAbstractValue {
const tagToClass = { const tagToClass = {
[Tag.Array]: SqArrayValue, [Tag.Array]: SqArrayValue,
[Tag.ArrayString]: SqArrayStringValue,
[Tag.Bool]: SqBoolValue, [Tag.Bool]: SqBoolValue,
[Tag.Call]: SqCallValue,
[Tag.Date]: SqDateValue, [Tag.Date]: SqDateValue,
[Tag.Declaration]: SqDeclarationValue, [Tag.Declaration]: SqDeclarationValue,
[Tag.Distribution]: SqDistributionValue, [Tag.Distribution]: SqDistributionValue,
[Tag.Lambda]: SqLambdaValue, [Tag.Lambda]: SqLambdaValue,
[Tag.Module]: SqModuleValue,
[Tag.Number]: SqNumberValue, [Tag.Number]: SqNumberValue,
[Tag.Record]: SqRecordValue, [Tag.Record]: SqRecordValue,
[Tag.String]: SqStringValue, [Tag.String]: SqStringValue,
[Tag.Symbol]: SqSymbolValue,
[Tag.TimeDuration]: SqTimeDurationValue, [Tag.TimeDuration]: SqTimeDurationValue,
[Tag.Type]: SqTypeValue,
[Tag.TypeIdentifier]: SqTypeIdentifierValue,
[Tag.Void]: SqVoidValue, [Tag.Void]: SqVoidValue,
} as const; } as const;
@ -198,19 +141,13 @@ const tagToClass = {
// type SqValue = typeof tagToClass[keyof typeof tagToClass]; // type SqValue = typeof tagToClass[keyof typeof tagToClass];
export type SqValue = export type SqValue =
| SqArrayValue | SqArrayValue
| SqArrayStringValue
| SqBoolValue | SqBoolValue
| SqCallValue
| SqDateValue | SqDateValue
| SqDeclarationValue | SqDeclarationValue
| SqDistributionValue | SqDistributionValue
| SqLambdaValue | SqLambdaValue
| SqModuleValue
| SqNumberValue | SqNumberValue
| SqRecordValue | SqRecordValue
| SqStringValue | SqStringValue
| SqSymbolValue
| SqTimeDurationValue | SqTimeDurationValue
| SqTypeValue
| SqTypeIdentifierValue
| SqVoidValue; | SqVoidValue;

View File

@ -1,4 +1,4 @@
import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen"; import { environment } from "../rescript/ForTS/ForTS__Types.gen";
import { SqProject } from "./SqProject"; import { SqProject } from "./SqProject";
import { SqValue, SqValueTag } from "./SqValue"; import { SqValue, SqValueTag } from "./SqValue";
export { SqValueLocation } from "./SqValueLocation"; export { SqValueLocation } from "./SqValueLocation";

View File

@ -1,5 +1,6 @@
const pdfast = require("pdfast"); const pdfast = require("pdfast");
const _ = require("lodash"); const filter = require("lodash/filter");
const isFinite = require("lodash/isFinite");
const samplesToContinuousPdf = ( const samplesToContinuousPdf = (
samples, samples,
@ -8,12 +9,12 @@ const samplesToContinuousPdf = (
min = false, min = false,
max = false max = false
) => { ) => {
let _samples = _.filter(samples, _.isFinite); let _samples = filter(samples, isFinite);
if (_.isFinite(min)) { if (isFinite(min)) {
_samples = _.filter(_samples, (r) => r > min); _samples = filter(_samples, (r) => r > min);
} }
if (_.isFinite(max)) { if (isFinite(max)) {
_samples = _.filter(_samples, (r) => r < max); _samples = filter(_samples, (r) => r < max);
} }
// The pdf that's created from this function is not a pdf but a pmf. y values // The pdf that's created from this function is not a pdf but a pmf. y values

View File

@ -62,7 +62,7 @@ let toPointSetDist = (
~samplingInputs: SamplingInputs.samplingInputs, ~samplingInputs: SamplingInputs.samplingInputs,
(), (),
): Internals.Types.outputs => { ): Internals.Types.outputs => {
let samples = samples->Js.Array2.copy->Js.Array2.sortInPlaceWith(compare) let samples = samples->E.A.Floats.sort
let minDiscreteToKeep = MagicNumbers.ToPointSet.minDiscreteToKeep(samples) let minDiscreteToKeep = MagicNumbers.ToPointSet.minDiscreteToKeep(samples)
let (continuousPart, discretePart) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight( let (continuousPart, discretePart) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight(

View File

@ -0,0 +1,102 @@
open FunctionRegistry_Core
open FunctionRegistry_Helpers
let nameSpace = "" // no namespaced versions
type simpleDefinition = {
inputs: array<frType>,
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
}
let makeFnMany = (name: string, definitions: array<simpleDefinition>) =>
Function.make(
~name,
~nameSpace,
~requiresNamespace=false,
~definitions=definitions->Js.Array2.map(({inputs, fn}) =>
FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _) => fn(inputs), ())
),
(),
)
let makeFn = (
name: string,
inputs: array<frType>,
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
) => makeFnMany(name, [{inputs: inputs, fn: fn}])
let library = [
Make.ff2f(~name="add", ~fn=(x, y) => x +. y, ()), // infix + (see Reducer/Reducer_Peggy/helpers.ts)
Make.ff2f(~name="subtract", ~fn=(x, y) => x -. y, ()), // infix -
Make.ff2f(~name="multiply", ~fn=(x, y) => x *. y, ()), // infix *
Make.ff2f(~name="divide", ~fn=(x, y) => x /. y, ()), // infix /
Make.ff2f(~name="pow", ~fn=(x, y) => Js.Math.pow_float(~base=x, ~exp=y), ()), // infix ^
Make.ff2b(~name="equal", ~fn=(x, y) => x == y, ()), // infix == on numbers
Make.bb2b(~name="equal", ~fn=(x, y) => x == y, ()), // infix == on booleans
Make.ff2b(~name="unequal", ~fn=(x, y) => x != y, ()), // infix != on numbers
Make.ff2b(~name="unequal", ~fn=(x, y) => x != y, ()), // infix != on booleans
Make.ff2b(~name="smaller", ~fn=(x, y) => x < y, ()), // infix <
Make.ff2b(~name="smallerEq", ~fn=(x, y) => x <= y, ()), // infix <=
Make.ff2b(~name="larger", ~fn=(x, y) => x > y, ()), // infix >
Make.ff2b(~name="largerEq", ~fn=(x, y) => x >= y, ()), // infix >=
Make.bb2b(~name="or", ~fn=(x, y) => x || y, ()), // infix ||
Make.bb2b(~name="and", ~fn=(x, y) => x && y, ()), // infix &&
Make.f2f(~name="unaryMinus", ~fn=x => -.x, ()), // unary prefix -
makeFn("not", [FRTypeNumber], inputs => {
// unary prefix !
switch inputs {
| [IEvNumber(x)] => IEvBool(x != 0.)->Ok
| _ => Error(impossibleError)
}
}),
makeFn("not", [FRTypeBool], inputs => {
// unary prefix !
switch inputs {
| [IEvBool(x)] => IEvBool(!x)->Ok
| _ => Error(impossibleError)
}
}),
makeFn("concat", [FRTypeString, FRTypeString], inputs => {
switch inputs {
| [IEvString(a), IEvString(b)] => {
let answer = Js.String2.concat(a, b)
answer->Reducer_T.IEvString->Ok
}
| _ => Error(impossibleError)
}
}),
makeFn("concat", [FRTypeArray(FRTypeAny), FRTypeArray(FRTypeAny)], inputs => {
switch inputs {
| [IEvArray(originalA), IEvArray(b)] => {
let a = originalA->Js.Array2.copy
let _ = Js.Array2.pushMany(a, b)
a->Reducer_T.IEvArray->Ok
}
| _ => Error(impossibleError)
}
}),
makeFn("inspect", [FRTypeAny], inputs => {
switch inputs {
| [value] => {
Js.log(value->Reducer_Value.toString)
value->Ok
}
| _ => Error(impossibleError)
}
}),
makeFn("inspect", [FRTypeAny, FRTypeString], inputs => {
switch inputs {
| [value, IEvString(label)] => {
Js.log(`${label}: ${value->Reducer_Value.toString}`)
value->Ok
}
| _ => Error(impossibleError)
}
}),
makeFn("javascriptraise", [FRTypeAny], inputs => {
switch inputs {
| [msg] => Js.Exn.raiseError(msg->Reducer_Value.toString)
| _ => Error(impossibleError)
}
}),
]

View File

@ -8,11 +8,11 @@ let requiresNamespace = true
module Combinatorics = { module Combinatorics = {
module Helpers = { module Helpers = {
let laplace = ((successes, trials)) => (successes +. 1.0) /. (trials +. 2.0) let laplace = (successes, trials) => (successes +. 1.0) /. (trials +. 2.0)
let factorial = Stdlib.Math.factorial let factorial = Stdlib.Math.factorial
let choose = ((n, k)) => factorial(n) /. (factorial(n -. k) *. factorial(k)) let choose = (n, k) => factorial(n) /. (factorial(n -. k) *. factorial(k))
let pow = (base, exp) => Js.Math.pow_float(~base, ~exp) let pow = (base, exp) => Js.Math.pow_float(~base, ~exp)
let binomial = ((n, k, p)) => choose((n, k)) *. pow(p, k) *. pow(1.0 -. p, n -. k) let binomial = (n, k, p) => choose(n, k) *. pow(p, k) *. pow(1.0 -. p, n -. k)
} }
module Lib = { module Lib = {
let laplace = Function.make( let laplace = Function.make(
@ -69,15 +69,15 @@ module Integration = {
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point) let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall( let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
aLambda, aLambda,
list{pointAsInternalExpression}, [pointAsInternalExpression],
environment, environment,
reducer, reducer,
) )
let result = switch resultAsInternalExpression { let result = switch resultAsInternalExpression {
| IEvNumber(x) => Ok(x) | Reducer_T.IEvNumber(x) => Ok(x)
| _ => | _ =>
Error( Error(
"Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead", "Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther,
) )
} }
result result
@ -132,7 +132,7 @@ module Integration = {
| (Ok(yMin), Ok(yMax)) => { | (Ok(yMin), Ok(yMax)) => {
let result = let result =
(yMin +. yMax) *. weightForAnOuterPoint +. innerPointsSum *. weightForAnInnerPoint (yMin +. yMax) *. weightForAnOuterPoint +. innerPointsSum *. weightForAnInnerPoint
let wrappedResult = result->ReducerInterface_InternalExpressionValue.IEvNumber->Ok let wrappedResult = result->Reducer_T.IEvNumber->Ok
wrappedResult wrappedResult
} }
| (Error(b), _) => Error(b) | (Error(b), _) => Error(b)
@ -141,11 +141,11 @@ module Integration = {
resultWithOuterPoints resultWithOuterPoints
} }
| Error(b) => | Error(b) =>
Error( ("Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++
"Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++ "Original error: " ++
"Original error: " ++ b->Reducer_ErrorValue.errorToString)
b, ->Reducer_ErrorValue.REOther
) ->Error
} }
result result
} }
@ -165,10 +165,12 @@ module Integration = {
FnDefinition.make( FnDefinition.make(
~name="integrateFunctionBetweenWithNumIntegrationPoints", ~name="integrateFunctionBetweenWithNumIntegrationPoints",
~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(inputs, _, env, reducer) => { ~run=(inputs, env, reducer) => {
let result = switch inputs { let result = switch inputs {
| [_, _, _, IEvNumber(0.0)] => | [_, _, _, IEvNumber(0.0)] =>
Error("Integration error 4 in Danger.integrate: Increment can't be 0.") "Integration error 4 in Danger.integrate: Increment can't be 0."
->Reducer_ErrorValue.REOther
->Error
| [ | [
IEvLambda(aLambda), IEvLambda(aLambda),
IEvNumber(min), IEvNumber(min),
@ -185,7 +187,9 @@ module Integration = {
) )
| _ => | _ =>
Error( Error(
"Integration error 5 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))", Reducer_ErrorValue.REOther(
"Integration error 5 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))",
),
) )
} }
result result
@ -205,10 +209,12 @@ module Integration = {
FnDefinition.make( FnDefinition.make(
~name="integrateFunctionBetweenWithEpsilon", ~name="integrateFunctionBetweenWithEpsilon",
~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(inputs, _, env, reducer) => { ~run=(inputs, env, reducer) => {
let result = switch inputs { let result = switch inputs {
| [_, _, _, IEvNumber(0.0)] => | [_, _, _, IEvNumber(0.0)] =>
Error("Integration error in Danger.integrate: Increment can't be 0.") "Integration error in Danger.integrate: Increment can't be 0."
->Reducer_ErrorValue.REOther
->Error
| [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(epsilon)] => | [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(epsilon)] =>
Helpers.integrateFunctionBetweenWithNumIntegrationPoints( Helpers.integrateFunctionBetweenWithNumIntegrationPoints(
aLambda, aLambda,
@ -218,12 +224,13 @@ module Integration = {
env, env,
reducer, reducer,
)->E.R2.errMap(b => )->E.R2.errMap(b =>
"Integration error 7 in Danger.integrate. Something went wrong along the way: " ++ b ("Integration error 7 in Danger.integrate. Something went wrong along the way: " ++
b->Reducer_ErrorValue.errorToString)->Reducer_ErrorValue.REOther
) )
| _ => | _ =>
Error( "Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))"
"Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))", ->Reducer_ErrorValue.REOther
) ->Error
} }
result result
}, },
@ -239,16 +246,16 @@ module DiminishingReturns = {
module Helpers = { module Helpers = {
type diminishingReturnsAccumulatorInner = { type diminishingReturnsAccumulatorInner = {
optimalAllocations: array<float>, optimalAllocations: array<float>,
currentMarginalReturns: result<array<float>, string>, currentMarginalReturns: result<array<float>, errorValue>,
} }
let findBiggestElementIndex = xs => let findBiggestElementIndex = (xs: array<float>) =>
E.A.reducei(xs, 0, (acc, newElement, index) => { E.A.reducei(xs, 0, (acc, newElement, index) => {
switch newElement > xs[acc] { switch newElement > xs[acc] {
| true => index | true => index
| false => acc | false => acc
} }
}) })
type diminishingReturnsAccumulator = result<diminishingReturnsAccumulatorInner, string> type diminishingReturnsAccumulator = result<diminishingReturnsAccumulatorInner, errorValue>
// TODO: This is so complicated, it probably should be its own file. It might also make sense to have it work in Rescript directly, taking in a function rather than a reducer; then something else can wrap that function in the reducer/lambdas/environment. // TODO: This is so complicated, it probably should be its own file. It might also make sense to have it work in Rescript directly, taking in a function rather than a reducer; then something else can wrap that function in the reducer/lambdas/environment.
/* /*
The key idea for this function is that The key idea for this function is that
@ -283,19 +290,19 @@ module DiminishingReturns = {
) { ) {
| (false, _, _, _) => | (false, _, _, _) =>
Error( Error(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1.", "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."->Reducer_ErrorValue.REOther,
) )
| (_, false, _, _) => | (_, false, _, _) =>
Error( Error(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0.", "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."->Reducer_ErrorValue.REOther,
) )
| (_, _, false, _) => | (_, _, false, _) =>
Error( Error(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0.", "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."->Reducer_ErrorValue.REOther,
) )
| (_, _, _, false) => | (_, _, _, false) =>
Error( Error(
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount.", "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."->Reducer_ErrorValue.REOther,
) )
| (true, true, true, true) => { | (true, true, true, true) => {
let applyFunctionAtPoint = (lambda, point: float) => { let applyFunctionAtPoint = (lambda, point: float) => {
@ -303,15 +310,15 @@ module DiminishingReturns = {
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point) let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall( let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
lambda, lambda,
list{pointAsInternalExpression}, [pointAsInternalExpression],
environment, environment,
reducer, reducer,
) )
switch resultAsInternalExpression { switch resultAsInternalExpression {
| IEvNumber(x) => Ok(x) | Reducer_T.IEvNumber(x) => Ok(x)
| _ => | _ =>
Error( Error(
"Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead", "Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther,
) )
} }
} }
@ -396,16 +403,16 @@ module DiminishingReturns = {
FnDefinition.make( FnDefinition.make(
~name="optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions", ~name="optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions",
~inputs=[FRTypeArray(FRTypeLambda), FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeArray(FRTypeLambda), FRTypeNumber, FRTypeNumber],
~run=(inputs, _, environment, reducer) => ~run=(inputs, environment, reducer) =>
switch inputs { switch inputs {
| [IEvArray(innerlambdas), IEvNumber(funds), IEvNumber(approximateIncrement)] => { | [IEvArray(innerlambdas), IEvNumber(funds), IEvNumber(approximateIncrement)] => {
let individuallyWrappedLambdas = E.A.fmap(innerLambda => { let individuallyWrappedLambdas = E.A.fmap(innerLambda => {
switch innerLambda { switch innerLambda {
| ReducerInterface_InternalExpressionValue.IEvLambda(lambda) => Ok(lambda) | Reducer_T.IEvLambda(lambda) => Ok(lambda)
| _ => | _ =>
Error( "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. A member of the array wasn't a function"
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. A member of the array wasn't a function", ->Reducer_ErrorValue.REOther
) ->Error
} }
}, innerlambdas) }, innerlambdas)
let wrappedLambdas = E.A.R.firstErrorOrOpen(individuallyWrappedLambdas) let wrappedLambdas = E.A.R.firstErrorOrOpen(individuallyWrappedLambdas)
@ -424,7 +431,10 @@ module DiminishingReturns = {
} }
result result
} }
| _ => Error("Error in Danger.diminishingMarginalReturnsForTwoFunctions") | _ =>
"Error in Danger.diminishingMarginalReturnsForTwoFunctions"
->Reducer_ErrorValue.REOther
->Error
}, },
(), (),
), ),

View File

@ -0,0 +1,159 @@
open FunctionRegistry_Core
open FunctionRegistry_Helpers
let makeFn = (
name: string,
inputs: array<frType>,
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _) => fn(inputs), ())],
(),
)
let makeNumberToDurationFn = (name: string, fn: float => DateTime.Duration.t) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeNumber],
~run=(inputs, _, _) =>
switch inputs {
| [IEvNumber(t)] => IEvTimeDuration(fn(t))->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
let makeDurationToNumberFn = (name: string, fn: DateTime.Duration.t => float) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeTimeDuration],
~run=(inputs, _, _) =>
switch inputs {
| [IEvTimeDuration(t)] => IEvNumber(fn(t))->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
let library = [
makeFn("toString", [FRTypeDate], inputs =>
switch inputs {
| [IEvDate(t)] => IEvString(DateTime.Date.toString(t))->Ok
| _ => Error(impossibleError)
}
),
makeFn("makeDateFromYear", [FRTypeNumber], inputs =>
switch inputs {
| [IEvNumber(year)] =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => IEvDate(t)->Ok
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error
}
| _ => Error(impossibleError)
}
),
makeFn("dateFromNumber", [FRTypeNumber], inputs =>
switch inputs {
| [IEvNumber(f)] => IEvDate(DateTime.Date.fromFloat(f))->Ok
| _ => Error(impossibleError)
}
),
makeFn("toNumber", [FRTypeDate], inputs =>
switch inputs {
| [IEvDate(f)] => IEvNumber(DateTime.Date.toFloat(f))->Ok
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeDate, FRTypeDate], inputs =>
switch inputs {
| [IEvDate(d1), IEvDate(d2)] =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => IEvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeDate, FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvDate(d1), IEvTimeDuration(d2)] => IEvDate(DateTime.Date.subtractDuration(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("add", [FRTypeDate, FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvDate(d1), IEvTimeDuration(d2)] => IEvDate(DateTime.Date.addDuration(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("toString", [FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvTimeDuration(t)] => IEvString(DateTime.Duration.toString(t))->Ok
| _ => Error(impossibleError)
}
),
makeNumberToDurationFn("minutes", DateTime.Duration.fromMinutes),
makeNumberToDurationFn("fromUnit_minutes", DateTime.Duration.fromMinutes),
makeNumberToDurationFn("hours", DateTime.Duration.fromHours),
makeNumberToDurationFn("fromUnit_hours", DateTime.Duration.fromHours),
makeNumberToDurationFn("days", DateTime.Duration.fromDays),
makeNumberToDurationFn("fromUnit_days", DateTime.Duration.fromDays),
makeNumberToDurationFn("years", DateTime.Duration.fromYears),
makeNumberToDurationFn("fromUnit_years", DateTime.Duration.fromYears),
makeDurationToNumberFn("toMinutes", DateTime.Duration.toMinutes),
makeDurationToNumberFn("toHours", DateTime.Duration.toHours),
makeDurationToNumberFn("toDays", DateTime.Duration.toDays),
makeDurationToNumberFn("toYears", DateTime.Duration.toYears),
makeFn("add", [FRTypeTimeDuration, FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] =>
IEvTimeDuration(DateTime.Duration.add(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeTimeDuration, FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] =>
IEvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("multiply", [FRTypeTimeDuration, FRTypeNumber], inputs =>
switch inputs {
| [IEvTimeDuration(d1), IEvNumber(d2)] =>
IEvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("divide", [FRTypeTimeDuration, FRTypeNumber], inputs =>
switch inputs {
| [IEvTimeDuration(d1), IEvNumber(d2)] => IEvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("divide", [FRTypeTimeDuration, FRTypeTimeDuration], inputs =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] => IEvNumber(d1 /. d2)->Ok
| _ => Error(impossibleError)
}
),
]

View File

@ -4,23 +4,23 @@ open FunctionRegistry_Helpers
let nameSpace = "Dict" let nameSpace = "Dict"
module Internals = { module Internals = {
type t = ReducerInterface_InternalExpressionValue.map type t = Reducer_T.map
let keys = (a: t): internalExpressionValue => IEvArray( let keys = (a: t): Reducer_T.value => IEvArray(
Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString), Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString),
) )
let values = (a: t): internalExpressionValue => IEvArray(Belt.Map.String.valuesToArray(a)) let values = (a: t): Reducer_T.value => IEvArray(Belt.Map.String.valuesToArray(a))
let toList = (a: t): internalExpressionValue => let toList = (a: t): Reducer_T.value =>
Belt.Map.String.toArray(a) Belt.Map.String.toArray(a)
->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value])) ->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value]))
->Wrappers.evArray ->Wrappers.evArray
let fromList = (items: array<internalExpressionValue>): result<internalExpressionValue, string> => let fromList = (items: array<Reducer_T.value>): result<Reducer_T.value, errorValue> =>
items items
->E.A2.fmap(item => { ->E.A2.fmap(item => {
switch (item: internalExpressionValue) { switch (item: Reducer_T.value) {
| IEvArray([IEvString(string), value]) => (string, value)->Ok | IEvArray([IEvString(string), value]) => (string, value)->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
@ -29,12 +29,8 @@ module Internals = {
->E.R2.fmap(Belt.Map.String.fromArray) ->E.R2.fmap(Belt.Map.String.fromArray)
->E.R2.fmap(Wrappers.evRecord) ->E.R2.fmap(Wrappers.evRecord)
let merge = (a: t, b: t): internalExpressionValue => IEvRecord(
Belt.Map.String.merge(a, b, (_, _, c) => c),
)
//Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet. //Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet.
let mergeMany = (a: array<t>): internalExpressionValue => { let mergeMany = (a: array<t>): Reducer_T.value => {
let mergedValues = let mergedValues =
a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray
IEvRecord(mergedValues) IEvRecord(mergedValues)
@ -52,10 +48,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="merge", ~name="merge",
~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)], ~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)],
~run=(inputs, _, _, _) => { ~run=(inputs, _, _) => {
switch inputs { switch inputs {
| [IEvRecord(d1), IEvRecord(d2)] => Internals.merge(d1, d2)->Ok | [IEvRecord(d1), IEvRecord(d2)] => Internals.mergeMany([d1, d2])->Ok
| _ => Error(impossibleError) | _ => impossibleError->Error
} }
}, },
(), (),
@ -63,7 +59,7 @@ let library = [
], ],
(), (),
), ),
//TODO: Change to use new mergeMany() function.
Function.make( Function.make(
~name="mergeMany", ~name="mergeMany",
~nameSpace, ~nameSpace,
@ -74,13 +70,17 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="mergeMany", ~name="mergeMany",
~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))], ~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))],
~run=(_, inputs, _, _) => ~run=(inputs, _, _) => {
inputs switch inputs {
->Prepare.ToTypedArray.dicts | [IEvArray(dicts)] => {
->E.R2.fmap(E.Dict.concatMany) dicts->Belt.Array.map(dictValue => switch dictValue {
->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r))) | IEvRecord(dict) => dict
->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray) | _ => impossibleError->Reducer_ErrorValue.toException
->E.R2.fmap(Wrappers.evRecord), })->Internals.mergeMany->Ok
}
| _ => impossibleError->Error
}
},
(), (),
), ),
], ],
@ -96,7 +96,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="keys", ~name="keys",
~inputs=[FRTypeDict(FRTypeAny)], ~inputs=[FRTypeDict(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvRecord(d1)] => Internals.keys(d1)->Ok | [IEvRecord(d1)] => Internals.keys(d1)->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
@ -116,7 +116,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="values", ~name="values",
~inputs=[FRTypeDict(FRTypeAny)], ~inputs=[FRTypeDict(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvRecord(d1)] => Internals.values(d1)->Ok | [IEvRecord(d1)] => Internals.values(d1)->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
@ -136,7 +136,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="toList", ~name="toList",
~inputs=[FRTypeDict(FRTypeAny)], ~inputs=[FRTypeDict(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvRecord(dict)] => dict->Internals.toList->Ok | [IEvRecord(dict)] => dict->Internals.toList->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
@ -156,7 +156,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="fromList", ~name="fromList",
~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))], ~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))],
~run=(inputs, _, _, _) => { ~run=(inputs, _, _) => {
switch inputs { switch inputs {
| [IEvArray(items)] => Internals.fromList(items) | [IEvArray(items)] => Internals.fromList(items)
| _ => Error(impossibleError) | _ => Error(impossibleError)

View File

@ -4,7 +4,7 @@ let twoArgs = E.Tuple2.toFnCall
module DistributionCreation = { module DistributionCreation = {
let nameSpace = "Dist" let nameSpace = "Dist"
let output = ReducerInterface_InternalExpressionValue.EvtDistribution let output = Reducer_Value.EvtDistribution
let requiresNamespace = false let requiresNamespace = false
let fnMake = (~name, ~examples, ~definitions) => { let fnMake = (~name, ~examples, ~definitions) => {
@ -16,13 +16,14 @@ module DistributionCreation = {
r r
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env)) ->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(e => Reducer_ErrorValue.REOther(e))
let make = (name, fn) => { let make = (name, fn) => {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
~run=(_, inputs, accessors, _) => ~run=(inputs, env, _) =>
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=accessors.environment), inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
(), (),
) )
} }
@ -31,10 +32,8 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
~run=(_, inputs, accessors, _) => ~run=(inputs, env, _) =>
inputs inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))->process(~fn, ~env),
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(), (),
) )
} }
@ -43,10 +42,8 @@ module DistributionCreation = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
~run=(_, inputs, accessors, _) => ~run=(inputs, env, _) =>
inputs inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev"))->process(~fn, ~env),
->Prepare.ToValueTuple.Record.twoDistOrNumber
->process(~fn, ~env=accessors.environment),
(), (),
) )
} }
@ -57,13 +54,14 @@ module DistributionCreation = {
r r
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env)) ->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(e => Reducer_ErrorValue.REOther(e))
let make = (name, fn) => let make = (name, fn) =>
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeDistOrNumber], ~inputs=[FRTypeDistOrNumber],
~run=(_, inputs, accessors, _) => ~run=(inputs, env, _) =>
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=accessors.environment), inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
(), (),
) )
} }

View File

@ -7,23 +7,21 @@ module Declaration = {
("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))), ("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))),
]) ])
let fromExpressionValue = (e: frValue): result<internalExpressionValue, string> => { let fromExpressionValue = (e: Reducer_T.value): result<Reducer_T.value, string> => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) { switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e], ("fn", "inputs")) {
| Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => { | Ok([IEvLambda(lambda), IEvArray(inputs)]) => {
open FunctionRegistry_Helpers.Prepare open FunctionRegistry_Helpers.Prepare
let getMinMax = arg => let getMinMax = arg =>
ToValueArray.Record.toArgs([arg]) ToValueArray.Record.twoArgs([arg], ("min", "max"))
->E.R.bind(ToValueTuple.twoNumbers) ->E.R.bind(ToValueTuple.twoNumbers)
->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max)) ->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max))
inputs inputs
->E.A2.fmap(getMinMax) ->E.A2.fmap(getMinMax)
->E.A.R.firstErrorOrOpen ->E.A.R.firstErrorOrOpen
->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration( ->E.R2.fmap(args => Reducer_T.IEvDeclaration(Declaration.make(lambda, args)))
Declaration.make(lambda, args),
))
} }
| Error(r) => Error(r) | Error(r) => Error(r)
| Ok(_) => Error(FunctionRegistry_Helpers.impossibleError) | Ok(_) => Error(impossibleErrorString)
} }
} }
} }
@ -51,8 +49,8 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="declare", ~name="declare",
~inputs=[Declaration.frType], ~inputs=[Declaration.frType],
~run=(_, inputs, _, _) => { ~run=(inputs, _, _) => {
inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue) inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)->E.R2.errMap(wrapError)
}, },
(), (),
), ),

View File

@ -0,0 +1,410 @@
open FunctionRegistry_Core
module Old = {
module Helpers = {
let arithmeticMap = r =>
switch r {
| "add" => #Add
| "dotAdd" => #Add
| "subtract" => #Subtract
| "dotSubtract" => #Subtract
| "divide" => #Divide
| "log" => #Logarithm
| "dotDivide" => #Divide
| "pow" => #Power
| "dotPow" => #Power
| "multiply" => #Multiply
| "dotMultiply" => #Multiply
| _ => #Multiply
}
let catchAndConvertTwoArgsToDists = (args: array<Reducer_T.value>): option<(
DistributionTypes.genericDist,
DistributionTypes.genericDist,
)> =>
switch args {
| [IEvDistribution(a), IEvDistribution(b)] => Some((a, b))
| [IEvNumber(a), IEvDistribution(b)] => Some((GenericDist.fromFloat(a), b))
| [IEvDistribution(a), IEvNumber(b)] => Some((a, GenericDist.fromFloat(b)))
| _ => None
}
let toFloatFn = (
fnCall: DistributionTypes.DistributionOperation.toFloat,
dist: DistributionTypes.genericDist,
~env: GenericDist.env,
) => {
FromDist(#ToFloat(fnCall), dist)->DistributionOperation.run(~env)->Some
}
let toStringFn = (
fnCall: DistributionTypes.DistributionOperation.toString,
dist: DistributionTypes.genericDist,
~env: GenericDist.env,
) => {
FromDist(#ToString(fnCall), dist)->DistributionOperation.run(~env)->Some
}
let toBoolFn = (
fnCall: DistributionTypes.DistributionOperation.toBool,
dist: DistributionTypes.genericDist,
~env: GenericDist.env,
) => {
FromDist(#ToBool(fnCall), dist)->DistributionOperation.run(~env)->Some
}
let toDistFn = (
fnCall: DistributionTypes.DistributionOperation.toDist,
dist,
~env: GenericDist.env,
) => {
FromDist(#ToDist(fnCall), dist)->DistributionOperation.run(~env)->Some
}
let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: GenericDist.env) => {
FromDist(
#ToDistCombination(direction, arithmeticMap(arithmetic), #Dist(dist2)),
dist1,
)->DistributionOperation.run(~env)
}
let parseNumber = (args: Reducer_T.value): Belt.Result.t<float, string> =>
switch args {
| IEvNumber(x) => Ok(x)
| _ => Error("Not a number")
}
let parseNumberArray = (ags: array<Reducer_T.value>): Belt.Result.t<array<float>, string> =>
E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen
let parseDist = (args: Reducer_T.value): Belt.Result.t<DistributionTypes.genericDist, string> =>
switch args {
| IEvDistribution(x) => Ok(x)
| IEvNumber(x) => Ok(GenericDist.fromFloat(x))
| _ => Error("Not a distribution")
}
let parseDistributionArray = (ags: array<Reducer_T.value>): Belt.Result.t<
array<DistributionTypes.genericDist>,
string,
> => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen
let mixtureWithGivenWeights = (
distributions: array<DistributionTypes.genericDist>,
weights: array<float>,
~env: GenericDist.env,
): DistributionOperation.outputType =>
E.A.length(distributions) == E.A.length(weights)
? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env)
: GenDistError(
ArgumentError("Error, mixture call has different number of distributions and weights"),
)
let mixtureWithDefaultWeights = (
distributions: array<DistributionTypes.genericDist>,
~env: GenericDist.env,
): DistributionOperation.outputType => {
let length = E.A.length(distributions)
let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length))
mixtureWithGivenWeights(distributions, weights, ~env)
}
let mixture = (
args: array<Reducer_T.value>,
~env: GenericDist.env,
): DistributionOperation.outputType => {
let error = (err: string): DistributionOperation.outputType =>
err->DistributionTypes.ArgumentError->GenDistError
switch args {
| [IEvArray(distributions)] =>
switch parseDistributionArray(distributions) {
| Ok(distrs) => mixtureWithDefaultWeights(distrs, ~env)
| Error(err) => error(err)
}
| [IEvArray(distributions), IEvArray(weights)] =>
switch (parseDistributionArray(distributions), parseNumberArray(weights)) {
| (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts, ~env)
| (Error(err), Ok(_)) => error(err)
| (Ok(_), Error(err)) => error(err)
| (Error(err1), Error(err2)) => error(`${err1}|${err2}`)
}
| _ =>
switch E.A.last(args) {
| Some(IEvArray(b)) => {
let weights = parseNumberArray(b)
let distributions = parseDistributionArray(
Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1),
)
switch E.R.merge(distributions, weights) {
| Ok(d, w) => mixtureWithGivenWeights(d, w, ~env)
| Error(err) => error(err)
}
}
| Some(IEvNumber(_))
| Some(IEvDistribution(_)) =>
switch parseDistributionArray(args) {
| Ok(distributions) => mixtureWithDefaultWeights(distributions, ~env)
| Error(err) => error(err)
}
| _ => error("Last argument of mx must be array or distribution")
}
}
}
}
module SymbolicConstructors = {
let threeFloat = name =>
switch name {
| "triangular" => Ok(SymbolicDist.Triangular.make)
| _ => Error("Unreachable state")
}
let symbolicResultToOutput = (
symbolicResult: result<SymbolicDistTypes.symbolicDist, string>,
): option<DistributionOperation.outputType> =>
switch symbolicResult {
| Ok(r) => Some(Dist(Symbolic(r)))
| Error(r) => Some(GenDistError(OtherError(r)))
}
}
let dispatchToGenericOutput = (call: Reducer_Value.functionCall, env: GenericDist.env): option<
DistributionOperation.outputType,
> => {
let (fnName, args) = call
switch (fnName, args) {
| ("triangular" as fnName, [IEvNumber(f1), IEvNumber(f2), IEvNumber(f3)]) =>
SymbolicConstructors.threeFloat(fnName)
->E.R.bind(r => r(f1, f2, f3))
->SymbolicConstructors.symbolicResultToOutput
| ("sample", [IEvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env)
| ("sampleN", [IEvDistribution(dist), IEvNumber(n)]) =>
Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n))))
| (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [IEvDistribution(dist)]) => {
let fn = switch op {
| "mean" => #Mean
| "stdev" => #Stdev
| "variance" => #Variance
| "min" => #Min
| "max" => #Max
| "mode" => #Mode
| _ => #Mean
}
Helpers.toFloatFn(fn, dist, ~env)
}
| ("integralSum", [IEvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env)
| ("toString", [IEvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env)
| ("sparkline", [IEvDistribution(dist)]) =>
Helpers.toStringFn(ToSparkline(MagicNumbers.Environment.sparklineLength), dist, ~env)
| ("sparkline", [IEvDistribution(dist), IEvNumber(n)]) =>
Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist, ~env)
| ("exp", [IEvDistribution(a)]) =>
// https://mathjs.org/docs/reference/functions/exp.html
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"pow",
GenericDist.fromFloat(MagicNumbers.Math.e),
a,
~env,
)->Some
| ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env)
| ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env)
| ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env)
| ("scaleLog", [IEvDistribution(dist)]) =>
Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist, ~env)
| ("scaleLog10", [IEvDistribution(dist)]) =>
Helpers.toDistFn(Scale(#Logarithm, 10.0), dist, ~env)
| ("scaleLog", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toDistFn(Scale(#Logarithm, float), dist, ~env)
| ("scaleLogWithThreshold", [IEvDistribution(dist), IEvNumber(base), IEvNumber(eps)]) =>
Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist, ~env)
| ("scaleMultiply", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toDistFn(Scale(#Multiply, float), dist, ~env)
| ("scalePow", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toDistFn(Scale(#Power, float), dist, ~env)
| ("scaleExp", [IEvDistribution(dist)]) =>
Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist, ~env)
| ("cdf", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toFloatFn(#Cdf(float), dist, ~env)
| ("pdf", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toFloatFn(#Pdf(float), dist, ~env)
| ("inv", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toFloatFn(#Inv(float), dist, ~env)
| ("quantile", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toFloatFn(#Inv(float), dist, ~env)
| ("inspect", [IEvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist, ~env)
| ("truncateLeft", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toDistFn(Truncate(Some(float), None), dist, ~env)
| ("truncateRight", [IEvDistribution(dist), IEvNumber(float)]) =>
Helpers.toDistFn(Truncate(None, Some(float)), dist, ~env)
| ("truncate", [IEvDistribution(dist), IEvNumber(float1), IEvNumber(float2)]) =>
Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist, ~env)
| ("mx" | "mixture", args) => Helpers.mixture(args, ~env)->Some
| ("log", [IEvDistribution(a)]) =>
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"log",
a,
GenericDist.fromFloat(MagicNumbers.Math.e),
~env,
)->Some
| ("log10", [IEvDistribution(a)]) =>
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"log",
a,
GenericDist.fromFloat(10.0),
~env,
)->Some
| ("unaryMinus", [IEvDistribution(a)]) =>
Helpers.twoDiststoDistFn(
Algebraic(AsDefault),
"multiply",
a,
GenericDist.fromFloat(-1.0),
~env,
)->Some
| (
("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic,
[_, _] as args,
) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd, ~env)
)
| (
("dotAdd"
| "dotMultiply"
| "dotSubtract"
| "dotDivide"
| "dotPow") as arithmetic,
[_, _] as args,
) =>
Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) =>
Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd, ~env)
)
| ("dotExp", [IEvDistribution(a)]) =>
Helpers.twoDiststoDistFn(
Pointwise,
"dotPow",
GenericDist.fromFloat(MagicNumbers.Math.e),
a,
~env,
)->Some
| _ => None
}
}
let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
Reducer_T.value,
Reducer_ErrorValue.errorValue,
> =>
switch o {
| Dist(d) => Ok(Reducer_T.IEvDistribution(d))
| Float(d) => Ok(IEvNumber(d))
| String(d) => Ok(IEvString(d))
| Bool(d) => Ok(IEvBool(d))
| FloatArray(d) => Ok(IEvArray(d |> E.A.fmap(r => Reducer_T.IEvNumber(r))))
| GenDistError(err) => Error(REDistributionError(err))
}
let dispatch = (call: Reducer_Value.functionCall, environment) =>
switch dispatchToGenericOutput(call, environment) {
| Some(o) => genericOutputToReducerValue(o)
| None =>
Reducer_ErrorValue.REOther("Internal error in FR_GenericDist implementation")
->Reducer_ErrorValue.ErrorException
->raise
}
}
let makeProxyFn = (name: string, inputs: array<frType>) => {
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(
~name,
~inputs,
~run=(inputs, env, _) => Old.dispatch((name, inputs), env),
(),
),
],
(),
)
}
let makeOperationFns = (): array<function> => {
let ops = [
"add",
"multiply",
"subtract",
"divide",
"pow",
"log",
"dotAdd",
"dotMultiply",
"dotSubtract",
"dotDivide",
"dotPow",
]
let twoArgTypes = [
// can't use numeric+numeric, since number+number should be delegated to builtin arithmetics
[FRTypeDist, FRTypeNumber],
[FRTypeNumber, FRTypeDist],
[FRTypeDist, FRTypeDist],
]
ops->E.A2.fmap(op => twoArgTypes->E.A2.fmap(types => makeProxyFn(op, types)))->E.A.concatMany
}
// TODO - duplicates the switch above, should rewrite with standard FR APIs
let library = E.A.concatMany([
[
makeProxyFn("triangular", [FRTypeNumber, FRTypeNumber, FRTypeNumber]),
makeProxyFn("sample", [FRTypeDist]),
makeProxyFn("sampleN", [FRTypeDist, FRTypeNumber]),
makeProxyFn("mean", [FRTypeDist]),
makeProxyFn("stdev", [FRTypeDist]),
makeProxyFn("variance", [FRTypeDist]),
makeProxyFn("min", [FRTypeDist]),
makeProxyFn("max", [FRTypeDist]),
makeProxyFn("mode", [FRTypeDist]),
makeProxyFn("integralSum", [FRTypeDist]),
makeProxyFn("toString", [FRTypeDist]),
makeProxyFn("sparkline", [FRTypeDist]),
makeProxyFn("sparkline", [FRTypeDist, FRTypeNumber]),
makeProxyFn("exp", [FRTypeDist]),
makeProxyFn("normalize", [FRTypeDist]),
makeProxyFn("isNormalized", [FRTypeDist]),
makeProxyFn("toPointSet", [FRTypeDist]),
makeProxyFn("scaleLog", [FRTypeDist]),
makeProxyFn("scaleLog10", [FRTypeDist]),
makeProxyFn("scaleLog", [FRTypeDist, FRTypeNumber]),
makeProxyFn("scaleLogWithThreshold", [FRTypeDist, FRTypeNumber, FRTypeNumber]),
makeProxyFn("scaleMultiply", [FRTypeDist, FRTypeNumber]),
makeProxyFn("scalePow", [FRTypeDist, FRTypeNumber]),
makeProxyFn("scaleExp", [FRTypeDist]),
makeProxyFn("cdf", [FRTypeDist, FRTypeNumber]),
makeProxyFn("pdf", [FRTypeDist, FRTypeNumber]),
makeProxyFn("inv", [FRTypeDist, FRTypeNumber]),
makeProxyFn("quantile", [FRTypeDist, FRTypeNumber]),
makeProxyFn("inspect", [FRTypeDist]),
makeProxyFn("truncateLeft", [FRTypeDist, FRTypeNumber]),
makeProxyFn("truncateRight", [FRTypeDist, FRTypeNumber]),
makeProxyFn("truncate", [FRTypeDist, FRTypeNumber, FRTypeNumber]),
makeProxyFn("log", [FRTypeDist]),
makeProxyFn("log10", [FRTypeDist]),
makeProxyFn("unaryMinus", [FRTypeDist]),
makeProxyFn("dotExp", [FRTypeDist]),
],
makeOperationFns(),
])
// FIXME - impossible to implement with FR due to arbitrary parameters length;
let mxLambda = Reducer_Expression_Lambda.makeFFILambda((inputs, env, _) => {
switch Old.dispatch(("mx", inputs), env) {
| Ok(value) => value
| Error(e) => e->Reducer_ErrorValue.ErrorException->raise
}
})

View File

@ -1,6 +1,3 @@
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
open FunctionRegistry_Core open FunctionRegistry_Core
open FunctionRegistry_Helpers open FunctionRegistry_Helpers
@ -8,38 +5,32 @@ let nameSpace = "List"
let requiresNamespace = true let requiresNamespace = true
module Internals = { module Internals = {
let makeFromNumber = ( let makeFromNumber = (n: float, value: Reducer_T.value): Reducer_T.value => IEvArray(
n: float, Belt.Array.make(E.Float.toInt(n), value),
value: internalExpressionValue, )
): internalExpressionValue => IEvArray(Belt.Array.make(E.Float.toInt(n), value))
let upTo = (low: float, high: float): internalExpressionValue => IEvArray( let upTo = (low: float, high: float): Reducer_T.value => IEvArray(
E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber), E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber),
) )
let first = (v: array<internalExpressionValue>): result<internalExpressionValue, string> => let first = (v: array<Reducer_T.value>): result<Reducer_T.value, string> =>
v->E.A.first |> E.O.toResult("No first element") v->E.A.first |> E.O.toResult("No first element")
let last = (v: array<internalExpressionValue>): result<internalExpressionValue, string> => let last = (v: array<Reducer_T.value>): result<Reducer_T.value, string> =>
v->E.A.last |> E.O.toResult("No last element") v->E.A.last |> E.O.toResult("No last element")
let reverse = (array: array<internalExpressionValue>): internalExpressionValue => IEvArray( let reverse = (array: array<Reducer_T.value>): Reducer_T.value => IEvArray(
Belt.Array.reverse(array), Belt.Array.reverse(array),
) )
let map = ( let map = (
array: array<internalExpressionValue>, array: array<Reducer_T.value>,
accessors: ProjectAccessorsT.t,
eLambdaValue, eLambdaValue,
reducer: ProjectReducerFnT.t, env: Reducer_T.environment,
): ReducerInterface_InternalExpressionValue.t => { reducer: Reducer_T.reducerFn,
): Reducer_T.value => {
Belt.Array.map(array, elem => Belt.Array.map(array, elem =>
Reducer_Expression_Lambda.doLambdaCall( Reducer_Expression_Lambda.doLambdaCall(eLambdaValue, [elem], env, reducer)
eLambdaValue,
list{elem},
(accessors: ProjectAccessorsT.t),
(reducer: ProjectReducerFnT.t),
)
)->Wrappers.evArray )->Wrappers.evArray
} }
@ -47,11 +38,11 @@ module Internals = {
aValueArray, aValueArray,
initialValue, initialValue,
aLambdaValue, aLambdaValue,
accessors: ProjectAccessorsT.t, env: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => { ) => {
aValueArray->E.A.reduce(initialValue, (acc, elem) => aValueArray->E.A.reduce(initialValue, (acc, elem) =>
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer) Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
) )
} }
@ -59,27 +50,22 @@ module Internals = {
aValueArray, aValueArray,
initialValue, initialValue,
aLambdaValue, aLambdaValue,
accessors: ProjectAccessorsT.t, env: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => { ) => {
aValueArray->Belt.Array.reduceReverse(initialValue, (acc, elem) => aValueArray->Belt.Array.reduceReverse(initialValue, (acc, elem) =>
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer) Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
) )
} }
let filter = ( let filter = (
aValueArray, aValueArray,
aLambdaValue, aLambdaValue,
accessors: ProjectAccessorsT.t, env: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => { ) => {
Js.Array2.filter(aValueArray, elem => { Js.Array2.filter(aValueArray, elem => {
let result = Reducer_Expression_Lambda.doLambdaCall( let result = Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [elem], env, reducer)
aLambdaValue,
list{elem},
accessors,
reducer,
)
switch result { switch result {
| IEvBool(true) => true | IEvBool(true) => true
| _ => false | _ => false
@ -100,7 +86,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="make", ~name="make",
~inputs=[FRTypeNumber, FRTypeAny], ~inputs=[FRTypeNumber, FRTypeAny],
~run=(inputs, _, _, _) => { ~run=(inputs, _, _) => {
switch inputs { switch inputs {
| [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok | [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
@ -121,10 +107,11 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="upTo", ~name="upTo",
~inputs=[FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _, _) => ~run=(inputs, _, _) =>
inputs switch inputs {
->Prepare.ToValueTuple.twoNumbers | [IEvNumber(low), IEvNumber(high)] => Internals.upTo(low, high)->Ok
->E.R2.fmap(((low, high)) => Internals.upTo(low, high)), | _ => impossibleError->Error
},
(), (),
), ),
], ],
@ -139,9 +126,9 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="first", ~name="first",
~inputs=[FRTypeArray(FRTypeAny)], ~inputs=[FRTypeArray(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvArray(array)] => Internals.first(array) | [IEvArray(array)] => Internals.first(array)->E.R2.errMap(wrapError)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -158,9 +145,9 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="last", ~name="last",
~inputs=[FRTypeArray(FRTypeAny)], ~inputs=[FRTypeArray(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvArray(array)] => Internals.last(array) | [IEvArray(array)] => Internals.last(array)->E.R2.errMap(wrapError)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -178,7 +165,7 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="reverse", ~name="reverse",
~inputs=[FRTypeArray(FRTypeAny)], ~inputs=[FRTypeArray(FRTypeAny)],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvArray(array)] => Internals.reverse(array)->Ok | [IEvArray(array)] => Internals.reverse(array)->Ok
| _ => Error(impossibleError) | _ => Error(impossibleError)
@ -198,10 +185,9 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="map", ~name="map",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => ~run=(inputs, env, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), IEvLambda(lambda)] => | [IEvArray(array), IEvLambda(lambda)] => Ok(Internals.map(array, lambda, env, reducer))
Ok(Internals.map(array, accessors, lambda, reducer))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -218,10 +204,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="reduce", ~name="reduce",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => ~run=(inputs, env, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), initialValue, IEvLambda(lambda)] => | [IEvArray(array), initialValue, IEvLambda(lambda)] =>
Ok(Internals.reduce(array, initialValue, lambda, accessors, reducer)) Ok(Internals.reduce(array, initialValue, lambda, env, reducer))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -238,10 +224,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="reduceReverse", ~name="reduceReverse",
~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => ~run=(inputs, env, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), initialValue, IEvLambda(lambda)] => | [IEvArray(array), initialValue, IEvLambda(lambda)] =>
Ok(Internals.reduceReverse(array, initialValue, lambda, accessors, reducer)) Ok(Internals.reduceReverse(array, initialValue, lambda, env, reducer))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -258,10 +244,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="filter", ~name="filter",
~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => ~run=(inputs, env, reducer) =>
switch inputs { switch inputs {
| [IEvArray(array), IEvLambda(lambda)] => | [IEvArray(array), IEvLambda(lambda)] =>
Ok(Internals.filter(array, lambda, accessors, reducer)) Ok(Internals.filter(array, lambda, env, reducer))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),

View File

@ -0,0 +1,21 @@
open FunctionRegistry_Helpers
let library = [
// ported MathJS functions
// https://docs.google.com/spreadsheets/d/1bUK2RaBFg8aJHuzZcw9yXp8StCBH5If5sU2iRw1T_HY/edit
// TODO - implement the rest of useful stuff
Make.f2f(
~name="sqrt",
~nameSpace="Math",
~requiresNamespace=true,
~fn=x => Js.Math.pow_float(~base=x, ~exp=0.5),
(),
),
Make.f2f(~name="sin", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.sin, ()),
Make.f2f(~name="cos", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.cos, ()),
Make.f2f(~name="tan", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.tan, ()),
Make.f2f(~name="asin", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.asin, ()),
Make.f2f(~name="acos", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.acos, ()),
Make.f2f(~name="atan", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.atan, ()),
]

View File

@ -9,34 +9,24 @@ module ArrayNumberDist = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeArray(FRTypeNumber)], ~inputs=[FRTypeArray(FRTypeNumber)],
~run=(_, inputs, _, _) => ~run=(inputs, _, _) =>
Prepare.ToTypedArray.numbers(inputs) inputs
->Prepare.ToTypedArray.numbers
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r)) ->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
->E.R.bind(fn), ->E.R.bind(fn)
(), ->E.R2.errMap(wrapError),
)
}
let make2 = (name, fn) => {
FnDefinition.make(
~name,
~inputs=[FRTypeArray(FRTypeAny)],
~run=(_, inputs, _, _) =>
Prepare.ToTypedArray.numbers(inputs)
->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r))
->E.R.bind(fn),
(), (),
) )
} }
} }
let library = [ let library = [
Function.make( Make.f2f(
~name="floor", ~name="floor",
~nameSpace, ~nameSpace,
~requiresNamespace, ~requiresNamespace,
~output=EvtNumber,
~examples=[`floor(3.5)`], ~examples=[`floor(3.5)`],
~definitions=[DefineFn.Numbers.oneToOne("floor", Js.Math.floor_float)], ~fn=Js.Math.floor_float,
(), (),
), ),
Function.make( Function.make(

View File

@ -4,45 +4,47 @@ open FunctionRegistry_Helpers
let nameSpace = "PointSet" let nameSpace = "PointSet"
let requiresNamespace = true let requiresNamespace = true
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => { let inputsToDist = (inputs: array<Reducer_T.value>, xyShapeToPointSetDist) => {
let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA) // TODO - rewrite in more functional/functor-based style
let xyCoords = switch inputs {
array->E.R.bind(xyCoords => | [IEvArray(items)] => {
xyCoords items->Belt.Array.map(
->E.A2.fmap(xyCoord => item =>
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers) switch item {
) | IEvRecord(map) => {
->E.A.R.firstErrorOrOpen let xValue = map->Belt.Map.String.get("x")
) let yValue = map->Belt.Map.String.get("y")
let expressionValue = switch (xValue, yValue) {
xyCoords | (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y)
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString)) | _ => impossibleError->Reducer_ErrorValue.toException
->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution( }
PointSet(makeDist(r)), }
)) | _ => impossibleError->Reducer_ErrorValue.toException
expressionValue }
)->Ok->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist)))
}
| _ => impossibleError->Reducer_ErrorValue.toException
}
} }
module Internal = { module Internal = {
type t = PointSetDist.t type t = PointSetDist.t
let toType = (r): result< let toType = (r): result<Reducer_T.value, Reducer_ErrorValue.errorValue> =>
ReducerInterface_InternalExpressionValue.t,
Reducer_ErrorValue.errorValue,
> =>
switch r { switch r {
| Ok(r) => Ok(Wrappers.evDistribution(PointSet(r))) | Ok(r) => Ok(Wrappers.evDistribution(PointSet(r)))
| Error(err) => Error(REOperationError(err)) | Error(err) => Error(REOperationError(err))
} }
let doLambdaCall = (aLambdaValue, list, environment, reducer) => let doLambdaCall = (aLambdaValue, list, env, reducer) =>
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) { switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
| IEvNumber(f) => Ok(f) | Reducer_T.IEvNumber(f) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction) | _ => Error(Operation.SampleMapNeedsNtoNFunction)
} }
let mapY = (pointSetDist: t, aLambdaValue, env, reducer) => { let mapY = (pointSetDist: t, aLambdaValue, env, reducer) => {
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer) let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], env, reducer)
PointSetDist.T.mapYResult(~fn, pointSetDist)->toType PointSetDist.T.mapYResult(~fn, pointSetDist)->toType
} }
} }
@ -53,23 +55,23 @@ let library = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`PointSet.fromDist(normal(5,2))`], ~examples=[`PointSet.fromDist(normal(5,2))`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="fromDist", ~name="fromDist",
~inputs=[FRTypeDist], ~inputs=[FRTypeDist],
~run=(_, inputs, accessors, _) => ~run=(inputs, env, _) =>
switch inputs { switch inputs {
| [FRValueDist(dist)] => | [IEvDistribution(dist)] =>
GenericDist.toPointSet( GenericDist.toPointSet(
dist, dist,
~xyPointLength=accessors.environment.xyPointLength, ~xyPointLength=env.xyPointLength,
~sampleCount=accessors.environment.sampleCount, ~sampleCount=env.sampleCount,
(), (),
) )
->E.R2.fmap(Wrappers.pointSet) ->E.R2.fmap(Wrappers.pointSet)
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(_ => "") ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -82,15 +84,15 @@ let library = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`PointSet.mapY(mx(normal(5,2)), {|x| x + 1})`], ~examples=[`PointSet.mapY(mx(normal(5,2)), {|x| x + 1})`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="mapY", ~name="mapY",
~inputs=[FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeLambda],
~run=(inputs, _, env, reducer) => ~run=(inputs, env, reducer) =>
switch inputs { switch inputs {
| [IEvDistribution(PointSet(dist)), IEvLambda(lambda)] => | [IEvDistribution(PointSet(dist)), IEvLambda(lambda)] =>
Internal.mapY(dist, lambda, env, reducer)->E.R2.errMap(Reducer_ErrorValue.errorToString) Internal.mapY(dist, lambda, env, reducer)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -110,12 +112,13 @@ let library = [
{x: 3, y: 0.2} {x: 3, y: 0.2}
])`, ])`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="makeContinuous", ~name="makeContinuous",
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))], ~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
~run=(_, inputs, _, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))), ~run=(inputs, _, _) =>
inputsToDist(inputs, r => Continuous(Continuous.make(r)))->E.R2.errMap(wrapError),
(), (),
), ),
], ],
@ -133,12 +136,13 @@ let library = [
{x: 3, y: 0.2} {x: 3, y: 0.2}
])`, ])`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="makeDiscrete", ~name="makeDiscrete",
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))], ~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
~run=(_, inputs, _, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))), ~run=(inputs, _, _) =>
inputsToDist(inputs, r => Discrete(Discrete.make(r)))->E.R2.errMap(wrapError),
(), (),
), ),
], ],

View File

@ -1,5 +1,3 @@
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
// module ProjectReducerFnT = ReducerProject_ReducerFn_T
open FunctionRegistry_Core open FunctionRegistry_Core
open FunctionRegistry_Helpers open FunctionRegistry_Helpers
@ -12,51 +10,46 @@ module Internal = {
let doLambdaCall = ( let doLambdaCall = (
aLambdaValue, aLambdaValue,
list, list,
accessors: ProjectAccessorsT.t, env: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => ) =>
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) { switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
| IEvNumber(f) => Ok(f) | IEvNumber(f) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction) | _ => Error(Operation.SampleMapNeedsNtoNFunction)
} }
let toType = (r): result< let toType = (r): result<Reducer_T.value, Reducer_ErrorValue.errorValue> =>
ReducerInterface_InternalExpressionValue.t,
Reducer_ErrorValue.errorValue,
> =>
switch r { switch r {
| Ok(r) => Ok(Wrappers.evDistribution(SampleSet(r))) | Ok(r) => Ok(Wrappers.evDistribution(SampleSet(r)))
| Error(r) => Error(REDistributionError(SampleSetError(r))) | Error(r) => Error(REDistributionError(SampleSetError(r)))
} }
//TODO: I don't know why this seems to need at least one input //TODO: I don't know why this seems to need at least one input
let fromFn = (aLambdaValue, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => { let fromFn = (aLambdaValue, environment: Reducer_T.environment, reducer: Reducer_T.reducerFn) => {
let sampleCount = accessors.environment.sampleCount let sampleCount = environment.sampleCount
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer) let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen
} }
let map1 = (sampleSetDist: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { let map1 = (sampleSetDist: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer) let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
SampleSetDist.samplesMap(~fn, sampleSetDist)->toType SampleSetDist.samplesMap(~fn, sampleSetDist)->toType
} }
let map2 = (t1: t, t2: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { let map2 = (t1: t, t2: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
let fn = (a, b) => let fn = (a, b) =>
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, accessors, reducer) doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], environment, reducer)
SampleSetDist.map2(~fn, ~t1, ~t2)->toType SampleSetDist.map2(~fn, ~t1, ~t2)->toType
} }
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { let map3 = (t1: t, t2: t, t3: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
let fn = (a, b, c) => let fn = (a, b, c) =>
doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, accessors, reducer) doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], environment, reducer)
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
} }
let parseSampleSetArray = (arr: array<internalExpressionValue>): option< let parseSampleSetArray = (arr: array<Reducer_T.value>): option<array<SampleSetDist.t>> => {
array<SampleSetDist.t>, let parseSampleSet = (value: Reducer_T.value): option<SampleSetDist.t> =>
> => {
let parseSampleSet = (value: internalExpressionValue): option<SampleSetDist.t> =>
switch value { switch value {
| IEvDistribution(SampleSet(dist)) => Some(dist) | IEvDistribution(SampleSet(dist)) => Some(dist)
| _ => None | _ => None
@ -65,9 +58,9 @@ module Internal = {
} }
let mapN = ( let mapN = (
aValueArray: array<internalExpressionValue>, aValueArray: array<Reducer_T.value>,
aLambdaValue, aLambdaValue,
accessors: ProjectAccessorsT.t, environment: Reducer_T.environment,
reducer, reducer,
) => { ) => {
switch parseSampleSetArray(aValueArray) { switch parseSampleSetArray(aValueArray) {
@ -75,8 +68,8 @@ module Internal = {
let fn = a => let fn = a =>
doLambdaCall( doLambdaCall(
aLambdaValue, aLambdaValue,
list{IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))}, [IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))],
accessors, environment,
reducer, reducer,
) )
SampleSetDist.mapN(~fn, ~t1)->toType SampleSetDist.mapN(~fn, ~t1)->toType
@ -91,18 +84,18 @@ let libaryBase = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`SampleSet.fromDist(normal(5,2))`], ~examples=[`SampleSet.fromDist(normal(5,2))`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="fromDist", ~name="fromDist",
~inputs=[FRTypeDist], ~inputs=[FRTypeDist],
~run=(_, inputs, accessors: ProjectAccessorsT.t, _) => ~run=(inputs, environment, _) =>
switch inputs { switch inputs {
| [FRValueDist(dist)] => | [IEvDistribution(dist)] =>
GenericDist.toSampleSetDist(dist, accessors.environment.sampleCount) GenericDist.toSampleSetDist(dist, environment.sampleCount)
->E.R2.fmap(Wrappers.sampleSet) ->E.R2.fmap(Wrappers.sampleSet)
->E.R2.fmap(Wrappers.evDistribution) ->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(DistributionTypes.Error.toString) ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -115,17 +108,20 @@ let libaryBase = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5,3,2,3,1,1,3])`], ~examples=[`SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5,3,2,3,1,1,3])`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="fromList", ~name="fromList",
~inputs=[FRTypeArray(FRTypeNumber)], ~inputs=[FRTypeArray(FRTypeNumber)],
~run=(_, inputs, _, _) => { ~run=(inputs, _, _) => {
let sampleSet = let sampleSet =
Prepare.ToTypedArray.numbers(inputs) |> E.R2.bind(r => inputs->Prepare.ToTypedArray.numbers |> E.R2.bind(r =>
SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??") SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??")
) )
sampleSet->E.R2.fmap(Wrappers.sampleSet)->E.R2.fmap(Wrappers.evDistribution) sampleSet
->E.R2.fmap(Wrappers.sampleSet)
->E.R2.fmap(Wrappers.evDistribution)
->E.R2.errMap(wrapError)
}, },
(), (),
), ),
@ -137,12 +133,12 @@ let libaryBase = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`SampleSet.toList(SampleSet.fromDist(normal(5,2)))`], ~examples=[`SampleSet.toList(SampleSet.fromDist(normal(5,2)))`],
~output=ReducerInterface_InternalExpressionValue.EvtArray, ~output=Reducer_Value.EvtArray,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="toList", ~name="toList",
~inputs=[FRTypeDist], ~inputs=[FRTypeDist],
~run=(inputs, _, _, _) => ~run=(inputs, _, _) =>
switch inputs { switch inputs {
| [IEvDistribution(SampleSet(dist))] => | [IEvDistribution(SampleSet(dist))] =>
dist->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok dist->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok
@ -158,17 +154,17 @@ let libaryBase = [
~nameSpace, ~nameSpace,
~requiresNamespace=true, ~requiresNamespace=true,
~examples=[`SampleSet.fromFn({|| sample(normal(5,2))})`], ~examples=[`SampleSet.fromFn({|| sample(normal(5,2))})`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="fromFn", ~name="fromFn",
~inputs=[FRTypeLambda], ~inputs=[FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => ~run=(inputs, environment, reducer) =>
switch inputs { switch inputs {
| [IEvLambda(lambda)] => | [IEvLambda(lambda)] =>
switch Internal.fromFn(lambda, accessors, reducer) { switch Internal.fromFn(lambda, environment, reducer) {
| Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution) | Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution)
| Error(e) => Error(Operation.Error.toString(e)) | Error(e) => e->Reducer_ErrorValue.REOperationError->Error
} }
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
@ -182,15 +178,15 @@ let libaryBase = [
~nameSpace, ~nameSpace,
~requiresNamespace, ~requiresNamespace,
~examples=[`SampleSet.map(SampleSet.fromDist(normal(5,2)), {|x| x + 1})`], ~examples=[`SampleSet.map(SampleSet.fromDist(normal(5,2)), {|x| x + 1})`],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="map", ~name="map",
~inputs=[FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => ~run=(inputs, environment, reducer) =>
switch inputs { switch inputs {
| [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] => | [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] =>
Internal.map1(dist, lambda, accessors, reducer)->E.R2.errMap(_ => "") Internal.map1(dist, lambda, environment, reducer)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -205,19 +201,19 @@ let libaryBase = [
~examples=[ ~examples=[
`SampleSet.map2(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y| x + y})`, `SampleSet.map2(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y| x + y})`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="map2", ~name="map2",
~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => { ~run=(inputs, environment, reducer) => {
switch inputs { switch inputs {
| [ | [
IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist1)),
IEvDistribution(SampleSet(dist2)), IEvDistribution(SampleSet(dist2)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map2(dist1, dist2, lambda, accessors, reducer)->E.R2.errMap(_ => "") Internal.map2(dist1, dist2, lambda, environment, reducer)
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
}, },
@ -233,12 +229,12 @@ let libaryBase = [
~examples=[ ~examples=[
`SampleSet.map3(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y, z| max([x,y,z])})`, `SampleSet.map3(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y, z| max([x,y,z])})`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="map3", ~name="map3",
~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda], ~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => ~run=(inputs, environment, reducer) =>
switch inputs { switch inputs {
| [ | [
IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist1)),
@ -246,7 +242,7 @@ let libaryBase = [
IEvDistribution(SampleSet(dist3)), IEvDistribution(SampleSet(dist3)),
IEvLambda(lambda), IEvLambda(lambda),
] => ] =>
Internal.map3(dist1, dist2, dist3, lambda, accessors, reducer)->E.R2.errMap(_ => "") Internal.map3(dist1, dist2, dist3, lambda, environment, reducer)
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -261,17 +257,15 @@ let libaryBase = [
~examples=[ ~examples=[
`SampleSet.mapN([SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2))], {|x| max(x)})`, `SampleSet.mapN([SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2))], {|x| max(x)})`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="mapN", ~name="mapN",
~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda], ~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda],
~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => ~run=(inputs, environment, reducer) =>
switch inputs { switch inputs {
| [IEvArray(dists), IEvLambda(lambda)] => | [IEvArray(dists), IEvLambda(lambda)] =>
Internal.mapN(dists, lambda, accessors, reducer)->E.R2.errMap(_e => { Internal.mapN(dists, lambda, environment, reducer)
"AHHH doesn't work"
})
| _ => Error(impossibleError) | _ => Error(impossibleError)
}, },
(), (),
@ -286,7 +280,7 @@ module Comparison = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs, ~inputs,
~run=(inputs, _, _, _) => { ~run=(inputs, _, _) => {
run(inputs) run(inputs)
}, },
(), (),
@ -296,7 +290,9 @@ module Comparison = {
let wrapper = r => let wrapper = r =>
r r
->E.R2.fmap(r => r->Wrappers.sampleSet->Wrappers.evDistribution) ->E.R2.fmap(r => r->Wrappers.sampleSet->Wrappers.evDistribution)
->E.R2.errMap(SampleSetDist.Error.toString) ->E.R2.errMap(e =>
e->DistributionTypes.Error.sampleErrorToDistErr->Reducer_ErrorValue.REDistributionError
)
let mkBig = (name, withDist, withFloat) => let mkBig = (name, withDist, withFloat) =>
Function.make( Function.make(
@ -308,7 +304,7 @@ module Comparison = {
`SampleSet.${name}(SampleSet.fromDist(normal(5,2)), 3.0)`, `SampleSet.${name}(SampleSet.fromDist(normal(5,2)), 3.0)`,
`SampleSet.${name}(4.0, SampleSet.fromDist(normal(6,2)))`, `SampleSet.${name}(4.0, SampleSet.fromDist(normal(6,2)))`,
], ],
~output=ReducerInterface_InternalExpressionValue.EvtDistribution, ~output=Reducer_Value.EvtDistribution,
~definitions=[ ~definitions=[
template(name, [FRTypeDist, FRTypeDist], inputs => { template(name, [FRTypeDist, FRTypeDist], inputs => {
switch inputs { switch inputs {

View File

@ -6,7 +6,7 @@ let requiresNamespace = true
let runScoring = (estimate, answer, prior, env) => { let runScoring = (estimate, answer, prior, env) => {
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env) GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber) ->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
->E.R2.errMap(DistributionTypes.Error.toString) ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
} }
let library = [ let library = [
@ -30,17 +30,17 @@ let library = [
("prior", FRTypeDist), ("prior", FRTypeDist),
]), ]),
], ],
~run=(_, inputs, accessors, _) => { ~run=(inputs, environment, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) { switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs, ("estimate", "answer", "prior")) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) => | Ok([IEvDistribution(estimate), IEvDistribution(d), IEvDistribution(prior)]) =>
runScoring(estimate, Score_Dist(d), Some(prior), accessors.environment) runScoring(estimate, Score_Dist(d), Some(prior), environment)
| Ok([ | Ok([
FRValueDist(estimate), IEvDistribution(estimate),
FRValueDistOrNumber(FRValueNumber(d)), IEvNumber(d),
FRValueDist(prior), IEvDistribution(prior),
]) => ]) =>
runScoring(estimate, Score_Scalar(d), Some(prior), accessors.environment) runScoring(estimate, Score_Scalar(d), Some(prior), environment)
| Error(e) => Error(e) | Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
}, },
@ -49,13 +49,13 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="logScore", ~name="logScore",
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])], ~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
~run=(_, inputs, accessors, _) => { ~run=(inputs, environment, _) => {
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) { switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs, ("estimate", "answer")) {
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) => | Ok([IEvDistribution(estimate), IEvDistribution(d)]) =>
runScoring(estimate, Score_Dist(d), None, accessors.environment) runScoring(estimate, Score_Dist(d), None, environment)
| Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) => | Ok([IEvDistribution(estimate), IEvNumber(d)]) =>
runScoring(estimate, Score_Scalar(d), None, accessors.environment) runScoring(estimate, Score_Scalar(d), None, environment)
| Error(e) => Error(e) | Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
}, },
@ -74,10 +74,10 @@ let library = [
FnDefinition.make( FnDefinition.make(
~name="klDivergence", ~name="klDivergence",
~inputs=[FRTypeDist, FRTypeDist], ~inputs=[FRTypeDist, FRTypeDist],
~run=(_, inputs, accessors, _) => { ~run=(inputs, environment, _) => {
switch inputs { switch inputs {
| [FRValueDist(estimate), FRValueDist(d)] => | [IEvDistribution(estimate), IEvDistribution(d)] =>
runScoring(estimate, Score_Dist(d), None, accessors.environment) runScoring(estimate, Score_Dist(d), None, environment)
| _ => Error(FunctionRegistry_Helpers.impossibleError) | _ => Error(FunctionRegistry_Helpers.impossibleError)
} }
}, },

View File

@ -0,0 +1,34 @@
open FunctionRegistry_Core
let makeUnitFn = (name: string, multiplier: float) => {
Function.make(
~name="fromUnit_" ++ name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(
~name="fromUnit_" ++ name,
~inputs=[FRTypeNumber],
~run=(inputs, _, _) => {
switch inputs {
| [IEvNumber(f)] => IEvNumber(f *. multiplier)->Ok
| _ => FunctionRegistry_Helpers.impossibleError->Error
}
},
(),
),
],
(),
)
}
let library = [
makeUnitFn("n", 1E-9),
makeUnitFn("m", 1E-3),
makeUnitFn("k", 1E3),
makeUnitFn("M", 1E6),
makeUnitFn("B", 1E9),
makeUnitFn("G", 1E9),
makeUnitFn("T", 1E12),
makeUnitFn("P", 1E15),
]

View File

@ -8,7 +8,7 @@
type environment = ForTS_Distribution_Environment.environment //use type environment = ForTS_Distribution_Environment.environment //use
@genType @genType
let defaultEnvironment: environment = DistributionOperation.defaultEnv let defaultEnvironment: environment = Reducer_Context.defaultEnvironment
@module("./ForTS_Distribution_tag") @scope("distributionTag") @module("./ForTS_Distribution_tag") @scope("distributionTag")
external dtPointSet_: string = "PointSet" external dtPointSet_: string = "PointSet"

View File

@ -1,14 +1,14 @@
@genType type reducerProject = ReducerProject_T.t //re-export @genType type reducerProject = ReducerProject_T.project //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //use type squiggleValue_Record = ForTS_SquiggleValue.squiggleValue_Record //use
type environment = ForTS_Distribution_Environment.environment //use type environment = ForTS_Distribution_Environment.environment //use
module T = ReducerProject_T module T = ReducerProject_T
module Private = ReducerProject.Private module Private = ReducerProject
/* /*
PUBLIC FUNCTIONS PUBLIC FUNCTIONS
@ -35,35 +35,34 @@ A project has a public field tag with a constant value "reducerProject"
project = {tag: "reducerProject"} project = {tag: "reducerProject"}
*/ */
@genType @genType
let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject let createProject = (): reducerProject => Private.createProject()
/* /*
Answer all the source ids of all the sources in the project. Answer all the source ids of all the sources in the project.
*/ */
@genType @genType
let getSourceIds = (project: reducerProject): array<string> => let getSourceIds = (project: reducerProject): array<string> => project->Private.getSourceIds
project->T.Private.castToInternalProject->Private.getSourceIds
/* /*
Sets the source for a given source Id. Sets the source for a given source Id.
*/ */
@genType @genType
let setSource = (project: reducerProject, sourceId: string, value: string): unit => let setSource = (project: reducerProject, sourceId: string, value: string): unit =>
project->T.Private.castToInternalProject->Private.setSource(sourceId, value) project->Private.setSource(sourceId, value)
/* /*
Gets the source for a given source id. Gets the source for a given source id.
*/ */
@genType @genType
let getSource = (project: reducerProject, sourceId: string): option<string> => let getSource = (project: reducerProject, sourceId: string): option<string> =>
project->T.Private.castToInternalProject->Private.getSource(sourceId) project->Private.getSource(sourceId)
/* /*
Touches the source for a given source id. This and dependent, sources are set to be re-evaluated. Touches the source for a given source id. This and dependent, sources are set to be re-evaluated.
*/ */
@genType @genType
let touchSource = (project: reducerProject, sourceId: string): unit => let touchSource = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.touchSource(sourceId) project->Private.touchSource(sourceId)
/* /*
Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again. Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again.
@ -71,15 +70,13 @@ Cleans the compilation artifacts for a given source ID. The results stay untouch
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 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 @genType
let clean = (project: reducerProject, sourceId: string): unit => let clean = (project: reducerProject, sourceId: string): unit => project->Private.clean(sourceId)
project->T.Private.castToInternalProject->Private.clean(sourceId)
/* /*
Cleans all the compilation artifacts in all of the project Cleans all the compilation artifacts in all of the project
*/ */
@genType @genType
let cleanAll = (project: reducerProject): unit => let cleanAll = (project: reducerProject): unit => project->Private.cleanAll
project->T.Private.castToInternalProject->Private.cleanAll
/* /*
Cleans results. Compilation stays untouched to be able to re-run the source. Cleans results. Compilation stays untouched to be able to re-run the source.
@ -87,14 +84,13 @@ You would not do this if you were not trying to debug the source code.
*/ */
@genType @genType
let cleanResults = (project: reducerProject, sourceId: string): unit => let cleanResults = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.cleanResults(sourceId) project->Private.cleanResults(sourceId)
/* /*
Cleans all results. Compilations remains untouched to rerun the source. Cleans all results. Compilations remains untouched to rerun the source.
*/ */
@genType @genType
let cleanAllResults = (project: reducerProject): unit => let cleanAllResults = (project: reducerProject): unit => project->Private.cleanAllResults
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. To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned.
@ -103,19 +99,19 @@ To set the includes one first has to call "parseIncludes". The parsed includes o
let getIncludes = (project: reducerProject, sourceId: string): result< let getIncludes = (project: reducerProject, sourceId: string): result<
array<string>, array<string>,
reducerErrorValue, reducerErrorValue,
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId) > => project->Private.getIncludes(sourceId)
/* Other sources contributing to the global namespace of this source. */ /* Other sources contributing to the global namespace of this source. */
@genType @genType
let getPastChain = (project: reducerProject, sourceId: string): array<string> => let getPastChain = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getPastChain(sourceId) project->Private.getPastChain(sourceId)
/* /*
Answers the source codes after which this source code is continuing Answers the source codes after which this source code is continuing
*/ */
@genType @genType
let getContinues = (project: reducerProject, sourceId: string): array<string> => let getContinues = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getContinues(sourceId) project->Private.getContinues(sourceId)
/* /*
"continues" acts like hidden includes in the source. "continues" acts like hidden includes in the source.
@ -124,35 +120,34 @@ let getContinues = (project: reducerProject, sourceId: string): array<string> =>
*/ */
@genType @genType
let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit => let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit =>
project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues) project->Private.setContinues(sourceId, continues)
/* /*
This source depends on the array of sources returned. This source depends on the array of sources returned.
*/ */
@genType @genType
let getDependencies = (project: reducerProject, sourceId: string): array<string> => let getDependencies = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependencies(sourceId) project->Private.getDependencies(sourceId)
/* /*
The sources returned are dependent on this The sources returned are dependent on this
*/ */
@genType @genType
let getDependents = (project: reducerProject, sourceId: string): array<string> => let getDependents = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependents(sourceId) project->Private.getDependents(sourceId)
/* /*
Get the run order for the sources in the project. Get the run order for the sources in the project.
*/ */
@genType @genType
let getRunOrder = (project: reducerProject): array<string> => let getRunOrder = (project: reducerProject): array<string> => project->Private.getRunOrder
project->T.Private.castToInternalProject->Private.getRunOrder
/* /*
Get the run order to get the results of this specific source Get the run order to get the results of this specific source
*/ */
@genType @genType
let getRunOrderFor = (project: reducerProject, sourceId: string) => let getRunOrderFor = (project: reducerProject, sourceId: string) =>
project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId) project->Private.getRunOrderFor(sourceId)
/* /*
Parse includes so that you can load them before running. Parse includes so that you can load them before running.
@ -162,7 +157,7 @@ It is your responsibility to load the includes before running.
@genType @genType
let parseIncludes = (project: reducerProject, sourceId: string): unit => let parseIncludes = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.parseIncludes(sourceId) project->Private.parseIncludes(sourceId)
/* /*
Parse the source code if it is not done already. Parse the source code if it is not done already.
@ -171,28 +166,26 @@ You would need this function if you want to see the parse tree without running t
*/ */
@genType @genType
let rawParse = (project: reducerProject, sourceId: string): unit => let rawParse = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.rawParse(sourceId) project->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. 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 @genType
let run = (project: reducerProject, sourceId: string): unit => let run = (project: reducerProject, sourceId: string): unit => project->Private.run(sourceId)
project->T.Private.castToInternalProject->Private.run(sourceId)
/* /*
Runs all of the sources in a project. Their results and bindings will be available Runs all of the sources in a project. Their results and bindings will be available
*/ */
@genType @genType
let runAll = (project: reducerProject): unit => let runAll = (project: reducerProject): unit => project->Private.runAll
project->T.Private.castToInternalProject->Private.runAll
/* /*
Get the bindings after running this source fil. The bindings are local to the source Get the bindings after running this source fil. The bindings are local to the source
*/ */
@genType @genType
let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Module => let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Record =>
project->T.Private.castToInternalProject->Private.getBindings(sourceId) project->Private.getBindings(sourceId)->Reducer_Namespace.toMap
/* /*
Get the result after running this source file or the project Get the result after running this source file or the project
@ -201,7 +194,7 @@ Get the result after running this source file or the project
let getResult = (project: reducerProject, sourceId: string): result< let getResult = (project: reducerProject, sourceId: string): result<
squiggleValue, squiggleValue,
reducerErrorValue, reducerErrorValue,
> => project->T.Private.castToInternalProject->Private.getResult(sourceId) > => project->Private.getResult(sourceId)
/* /*
This is a convenience function to get the result of a single source without creating a project. This is a convenience function to get the result of a single source without creating a project.
@ -211,12 +204,15 @@ The source has to be include free
@genType @genType
let evaluate = (sourceCode: string): ( let evaluate = (sourceCode: string): (
result<squiggleValue, reducerErrorValue>, result<squiggleValue, reducerErrorValue>,
squiggleValue_Module, squiggleValue_Record,
) => Private.evaluate(sourceCode) ) => Private.evaluate(sourceCode)
@genType @genType
let setEnvironment = (project: reducerProject, environment: environment): unit => let setEnvironment = (project: reducerProject, environment: environment): unit =>
project->T.Private.castToInternalProject->Private.setEnvironment(environment) project->Private.setEnvironment(environment)
@genType
let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment
/* /*
Foreign function interface is intentionally demolished. Foreign function interface is intentionally demolished.

View File

@ -1,10 +1,8 @@
@genType type squiggleValue = ReducerInterface_InternalExpressionValue.t //re-export @genType type squiggleValue = Reducer_T.value //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
@genType type squiggleValue_Array = ReducerInterface_InternalExpressionValue.squiggleArray //re-export recursive type @genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type
@genType type squiggleValue_Module = ReducerInterface_InternalExpressionValue.nameSpace //re-export recursive type @genType type squiggleValue_Record = Reducer_T.map //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_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use
type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use
type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use
@ -14,15 +12,9 @@ type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //us
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArray_: string = "Array" external svtArray_: string = "Array"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtArrayString_: string = "ArrayString"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtBool_: string = "Bool" external svtBool_: string = "Bool"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtCall_: string = "Call"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtDate_: string = "Date" external svtDate_: string = "Date"
@ -35,9 +27,6 @@ external svtDistribution_: string = "Distribution"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtLambda_: string = "Lambda" external svtLambda_: string = "Lambda"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtModule_: string = "Module"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtNumber_: string = "Number" external svtNumber_: string = "Number"
@ -47,18 +36,9 @@ external svtRecord_: string = "Record"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtString_: string = "String" external svtString_: string = "String"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtSymbol_: string = "Symbol"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTimeDuration_: string = "TimeDuration" external svtTimeDuration_: string = "TimeDuration"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtType_: string = "Type"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtTypeIdentifier_: string = "TypeIdentifier"
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
external svtVoid_: string = "Void" external svtVoid_: string = "Void"
@ -71,33 +51,26 @@ external castEnum: string => squiggleValueTag = "%identity"
let getTag = (variant: squiggleValue): squiggleValueTag => let getTag = (variant: squiggleValue): squiggleValueTag =>
switch variant { switch variant {
| IEvArray(_) => svtArray_->castEnum | IEvArray(_) => svtArray_->castEnum
| IEvArrayString(_) => svtArrayString_->castEnum
| IEvBool(_) => svtBool_->castEnum | IEvBool(_) => svtBool_->castEnum
| IEvCall(_) => svtCall_->castEnum //Impossible
| IEvDate(_) => svtDate_->castEnum | IEvDate(_) => svtDate_->castEnum
| IEvDeclaration(_) => svtDeclaration_->castEnum | IEvDeclaration(_) => svtDeclaration_->castEnum
| IEvDistribution(_) => svtDistribution_->castEnum | IEvDistribution(_) => svtDistribution_->castEnum
| IEvLambda(_) => svtLambda_->castEnum | IEvLambda(_) => svtLambda_->castEnum
| IEvBindings(_) => svtModule_->castEnum //Impossible
| IEvNumber(_) => svtNumber_->castEnum | IEvNumber(_) => svtNumber_->castEnum
| IEvRecord(_) => svtRecord_->castEnum | IEvRecord(_) => svtRecord_->castEnum
| IEvString(_) => svtString_->castEnum | IEvString(_) => svtString_->castEnum
| IEvSymbol(_) => svtSymbol_->castEnum
| IEvTimeDuration(_) => svtTimeDuration_->castEnum | IEvTimeDuration(_) => svtTimeDuration_->castEnum
| IEvType(_) => svtType_->castEnum
| IEvTypeIdentifier(_) => svtTypeIdentifier_->castEnum
| IEvVoid => svtVoid_->castEnum | IEvVoid => svtVoid_->castEnum
} }
@genType @genType
let toString = (variant: squiggleValue) => let toString = (variant: squiggleValue) => Reducer_Value.toString(variant)
ReducerInterface_InternalExpressionValue.toString(variant)
// This is a useful method for unit tests. // This is a useful method for unit tests.
// Convert the result along with the error message to a string. // Convert the result along with the error message to a string.
@genType @genType
let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) => let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) =>
ReducerInterface_InternalExpressionValue.toStringResult(variantResult) Reducer_Value.toStringResult(variantResult)
@genType @genType
let getArray = (variant: squiggleValue): option<squiggleValue_Array> => let getArray = (variant: squiggleValue): option<squiggleValue_Array> =>
@ -107,13 +80,6 @@ let getArray = (variant: squiggleValue): option<squiggleValue_Array> =>
| _ => None | _ => None
} }
@genType
let getArrayString = (variant: squiggleValue): option<array<string>> =>
switch variant {
| IEvArrayString(value) => value->Some
| _ => None
}
@genType @genType
let getBool = (variant: squiggleValue): option<bool> => let getBool = (variant: squiggleValue): option<bool> =>
switch variant { switch variant {
@ -121,13 +87,6 @@ let getBool = (variant: squiggleValue): option<bool> =>
| _ => None | _ => None
} }
@genType
let getCall = (variant: squiggleValue): option<string> =>
switch variant {
| IEvCall(value) => value->Some
| _ => None
}
@genType @genType
let getDate = (variant: squiggleValue): option<Js.Date.t> => let getDate = (variant: squiggleValue): option<Js.Date.t> =>
switch variant { switch variant {
@ -156,13 +115,6 @@ let getLambda = (variant: squiggleValue): option<squiggleValue_Lambda> =>
| _ => None | _ => None
} }
@genType
let getModule = (variant: squiggleValue): option<squiggleValue_Module> =>
switch variant {
| IEvBindings(value) => value->Some
| _ => None
}
@genType @genType
let getNumber = (variant: squiggleValue): option<float> => let getNumber = (variant: squiggleValue): option<float> =>
switch variant { switch variant {
@ -184,30 +136,9 @@ let getString = (variant: squiggleValue): option<string> =>
| _ => None | _ => None
} }
@genType
let getSymbol = (variant: squiggleValue): option<string> =>
switch variant {
| IEvSymbol(value) => value->Some
| _ => None
}
@genType @genType
let getTimeDuration = (variant: squiggleValue): option<float> => let getTimeDuration = (variant: squiggleValue): option<float> =>
switch variant { switch variant {
| IEvTimeDuration(value) => value->Some | IEvTimeDuration(value) => value->Some
| _ => None | _ => 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

@ -2,9 +2,7 @@ type squiggleValue = ForTS_SquiggleValue.squiggleValue
@genType type squiggleValue_Array = ForTS_SquiggleValue.squiggleValue_Array //re-export recursive type @genType type squiggleValue_Array = ForTS_SquiggleValue.squiggleValue_Array //re-export recursive type
@genType @genType
let getValues = (v: squiggleValue_Array): array<squiggleValue> => let getValues = (v: squiggleValue_Array): array<squiggleValue> => Reducer_Value.arrayToValueArray(v)
ReducerInterface_InternalExpressionValue.arrayToValueArray(v)
@genType @genType
let toString = (v: squiggleValue_Array): string => let toString = (v: squiggleValue_Array): string => Reducer_Value.toStringArray(v)
ReducerInterface_InternalExpressionValue.toStringArray(v)

View File

@ -1,5 +1,4 @@
@genType type squiggleValue_Declaration = ReducerInterface_InternalExpressionValue.lambdaDeclaration //re-export @genType type squiggleValue_Declaration = Reducer_T.lambdaDeclaration //re-export
@genType @genType
let toString = (v: squiggleValue_Declaration): string => let toString = (v: squiggleValue_Declaration): string => Reducer_Value.toStringDeclaration(v)
ReducerInterface_InternalExpressionValue.toStringDeclaration(v)

View File

@ -1,5 +1,4 @@
@genType type squiggleValue_Distribution = ForTS_Distribution.distribution @genType type squiggleValue_Distribution = ForTS_Distribution.distribution
@genType @genType
let toString = (v: squiggleValue_Distribution): string => let toString = (v: squiggleValue_Distribution): string => Reducer_Value.toStringDistribution(v)
ReducerInterface_InternalExpressionValue.toStringDistribution(v)

View File

@ -1,8 +1,7 @@
@genType type squiggleValue_Lambda = ReducerInterface_InternalExpressionValue.lambdaValue //re-export @genType type squiggleValue_Lambda = Reducer_T.lambdaValue //re-export
@genType @genType
let toString = (v: squiggleValue_Lambda): string => let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringFunction(v)
ReducerInterface_InternalExpressionValue.toStringFunction(v)
@genType @genType
let parameters = (v: squiggleValue_Lambda): array<string> => { let parameters = (v: squiggleValue_Lambda): array<string> => {

View File

@ -1,16 +0,0 @@
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)
@genType
let toString = (v: squiggleValue_Module): string =>
ReducerInterface_InternalExpressionValue.toStringNameSpace(v)
@genType
let toSquiggleValue = (v: squiggleValue_Module): squiggleValue => IEvBindings(v)
@genType
let get = ReducerInterface_InternalExpressionValue.nameSpaceGet

View File

@ -3,7 +3,10 @@ type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
@genType @genType
let getKeyValuePairs = (value: squiggleValue_Record): array<(string, squiggleValue)> => let getKeyValuePairs = (value: squiggleValue_Record): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value) Reducer_Value.recordToKeyValuePairs(value)
@genType @genType
let toString = (v: squiggleValue_Record) => ReducerInterface_InternalExpressionValue.toStringMap(v) let toString = (v: squiggleValue_Record) => Reducer_Value.toStringMap(v)
@genType
let toSquiggleValue = (v: squiggleValue_Record): squiggleValue => IEvRecord(v)

View File

@ -1,10 +0,0 @@
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)
@genType
let toString = (value: squiggleValue_Type): string =>
ReducerInterface_InternalExpressionValue.toStringType(value)

View File

@ -1,19 +1,13 @@
export enum squiggleValueTag { export enum squiggleValueTag {
Array = "Array", Array = "Array",
ArrayString = "ArrayString",
Bool = "Bool", Bool = "Bool",
Call = "Call",
Date = "Date", Date = "Date",
Declaration = "Declaration", Declaration = "Declaration",
Distribution = "Distribution", Distribution = "Distribution",
Lambda = "Lambda", Lambda = "Lambda",
Module = "Module",
Number = "Number", Number = "Number",
Record = "Record", Record = "Record",
String = "String", String = "String",
Symbol = "Symbol",
TimeDuration = "TimeDuration", TimeDuration = "TimeDuration",
Type = "Type",
TypeIdentifier = "TypeIdentifier",
Void = "Void", Void = "Void",
} }

View File

@ -6,9 +6,7 @@
@genType type squiggleValue_Array = ForTS_SquiggleValue_Array.squiggleValue_Array //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_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //re-export
@genType type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //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_Record = ForTS_SquiggleValue_Record.squiggleValue_Record //re-export
@genType type squiggleValue_Type = ForTS_SquiggleValue_Type.squiggleValue_Type //re-export
/* Distribution related */ /* Distribution related */
@genType type squiggleValue_Distribution = ForTS_Distribution.distribution //re-export @genType type squiggleValue_Distribution = ForTS_Distribution.distribution //re-export

View File

@ -1,7 +1,5 @@
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T type internalExpressionValueType = Reducer_Value.internalExpressionValueType
module ProjectReducerFnT = ReducerProject_ReducerFn_T type errorValue = Reducer_ErrorValue.errorValue
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType
/* /*
Function Registry "Type". A type, without any other information. Function Registry "Type". A type, without any other information.
@ -9,7 +7,10 @@ type internalExpressionValueType = ReducerInterface_InternalExpressionValue.inte
*/ */
type rec frType = type rec frType =
| FRTypeNumber | FRTypeNumber
| FRTypeBool
| FRTypeNumeric | FRTypeNumeric
| FRTypeDate
| FRTypeTimeDuration
| FRTypeDistOrNumber | FRTypeDistOrNumber
| FRTypeDist | FRTypeDist
| FRTypeLambda | FRTypeLambda
@ -22,35 +23,16 @@ type rec frType =
and frTypeRecord = array<frTypeRecordParam> and frTypeRecord = array<frTypeRecordParam>
and frTypeRecordParam = (string, frType) and frTypeRecordParam = (string, frType)
/* type frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist)
Function Registry "Value". A type, with the information of that type.
Like, #Float(40.0)
*/
type rec frValue =
| FRValueNumber(float)
| FRValueDist(DistributionTypes.genericDist)
| FRValueArray(array<frValue>)
| FRValueDistOrNumber(frValueDistOrNumber)
| FRValueRecord(frValueRecord)
| FRValueLambda(ReducerInterface_InternalExpressionValue.lambdaValue)
| FRValueString(string)
| FRValueVariant(string)
| FRValueAny(frValue)
| FRValueDict(Js.Dict.t<frValue>)
and frValueRecord = array<frValueRecordParam>
and frValueRecordParam = (string, frValue)
and frValueDictParam = (string, frValue)
and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist)
type fnDefinition = { type fnDefinition = {
name: string, name: string,
inputs: array<frType>, inputs: array<frType>,
run: ( run: (
array<internalExpressionValue>, array<Reducer_T.value>,
array<frValue>, Reducer_T.environment,
ProjectAccessorsT.t, Reducer_T.reducerFn,
ProjectReducerFnT.t, ) => result<Reducer_T.value, errorValue>,
) => result<internalExpressionValue, string>,
} }
type function = { type function = {
@ -64,14 +46,14 @@ type function = {
isExperimental: bool, isExperimental: bool,
} }
type fnNameDict = Js.Dict.t<array<function>>
type registry = {functions: array<function>, fnNameDict: fnNameDict}
module FRType = { module FRType = {
type t = frType type t = frType
let rec toString = (t: t) => let rec toString = (t: t) =>
switch t { switch t {
| FRTypeNumber => "number" | FRTypeNumber => "number"
| FRTypeBool => "bool"
| FRTypeDate => "date"
| FRTypeTimeDuration => "duration"
| FRTypeNumeric => "numeric" | FRTypeNumeric => "numeric"
| FRTypeDist => "distribution" | FRTypeDist => "distribution"
| FRTypeDistOrNumber => "distribution|number" | FRTypeDistOrNumber => "distribution|number"
@ -87,284 +69,47 @@ module FRType = {
| FRTypeAny => `any` | FRTypeAny => `any`
} }
let rec toFrValue = (r: internalExpressionValue): option<frValue> => let rec matchWithValue = (t: t, r: Reducer_T.value): bool =>
switch r {
| IEvNumber(f) => Some(FRValueNumber(f))
| IEvString(f) => Some(FRValueString(f))
| IEvDistribution(f) => Some(FRValueDistOrNumber(FRValueDist(f)))
| IEvLambda(f) => Some(FRValueLambda(f))
| IEvArray(elements) =>
elements->E.A2.fmap(toFrValue)->E.A.O.openIfAllSome->E.O2.fmap(r => FRValueArray(r))
| IEvRecord(map) =>
Belt.Map.String.toArray(map)
->E.A2.fmap(((key, item)) => item->toFrValue->E.O2.fmap(o => (key, o)))
->E.A.O.openIfAllSome
->E.O2.fmap(r => FRValueRecord(r))
| _ => None
}
let rec matchWithExpressionValue = (t: t, r: internalExpressionValue): option<frValue> =>
switch (t, r) { switch (t, r) {
| (FRTypeAny, f) => toFrValue(f) | (FRTypeAny, _) => true
| (FRTypeString, IEvString(f)) => Some(FRValueString(f)) | (FRTypeString, IEvString(_)) => true
| (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f)) | (FRTypeNumber, IEvNumber(_)) => true
| (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f))) | (FRTypeBool, IEvBool(_)) => true
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) => | (FRTypeDate, IEvDate(_)) => true
Some(FRValueDistOrNumber(FRValueNumber(f))) | (FRTypeTimeDuration, IEvTimeDuration(_)) => true
| (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f))) | (FRTypeDistOrNumber, IEvNumber(_)) => true
| (FRTypeDist, IEvDistribution(f)) => Some(FRValueDist(f)) | (FRTypeDistOrNumber, IEvDistribution(_)) => true
| (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f)) | (FRTypeDist, IEvDistribution(_)) => true
| (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f)) | (FRTypeNumeric, IEvNumber(_)) => true
| (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f)) | (FRTypeNumeric, IEvDistribution(Symbolic(#Float(_)))) => true
| (FRTypeLambda, IEvLambda(_)) => true
| (FRTypeArray(intendedType), IEvArray(elements)) => { | (FRTypeArray(intendedType), IEvArray(elements)) => {
let el = elements->E.A2.fmap(matchWithExpressionValue(intendedType)) elements->Belt.Array.every(v => matchWithValue(intendedType, v))
E.A.O.openIfAllSome(el)->E.O2.fmap(r => FRValueArray(r))
} }
| (FRTypeDict(r), IEvRecord(map)) => | (FRTypeDict(r), IEvRecord(map)) =>
map map->Belt.Map.String.valuesToArray->Belt.Array.every(v => matchWithValue(r, v))
->Belt.Map.String.toArray
->E.A2.fmap(((key, item)) => matchWithExpressionValue(r, item)->E.O2.fmap(o => (key, o)))
->E.A.O.openIfAllSome
->E.O2.fmap(r => FRValueDict(Js.Dict.fromArray(r)))
| (FRTypeRecord(recordParams), IEvRecord(map)) => { | (FRTypeRecord(recordParams), IEvRecord(map)) => {
let getAndMatch = (name, input) => recordParams->Belt.Array.every(((name, input)) => {
Belt.Map.String.get(map, name)->E.O.bind(matchWithExpressionValue(input)) switch map->Belt.Map.String.get(name) {
//All names in the type must be present. If any are missing, the corresponding | Some(v) => matchWithValue(input, v)
//value will be None, and this function would return None. | None => false
let namesAndValues: array<option<(Js.Dict.key, frValue)>> = }
recordParams->E.A2.fmap(((name, input)) => })
getAndMatch(name, input)->E.O2.fmap(match => (name, match)) }
) | _ => false
namesAndValues->E.A.O.openIfAllSome->E.O2.fmap(r => FRValueRecord(r))
}
| _ => None
} }
let rec matchReverse = (e: frValue): internalExpressionValue => let matchWithValueArray = (inputs: array<t>, args: array<Reducer_T.value>): bool => {
switch e {
| FRValueNumber(f) => IEvNumber(f)
| FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n)
| FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n)
| FRValueDist(dist) => IEvDistribution(dist)
| FRValueArray(elements) => IEvArray(elements->E.A2.fmap(matchReverse))
| FRValueRecord(frValueRecord) => {
let map =
frValueRecord
->E.A2.fmap(((name, value)) => (name, matchReverse(value)))
->Belt.Map.String.fromArray
IEvRecord(map)
}
| FRValueDict(frValueRecord) => {
let map =
frValueRecord
->Js.Dict.entries
->E.A2.fmap(((name, value)) => (name, matchReverse(value)))
->Belt.Map.String.fromArray
IEvRecord(map)
}
| FRValueLambda(l) => IEvLambda(l)
| FRValueString(string) => IEvString(string)
| FRValueVariant(string) => IEvString(string)
| FRValueAny(f) => matchReverse(f)
}
let matchWithExpressionValueArray = (
inputs: array<t>,
args: array<internalExpressionValue>,
): option<array<frValue>> => {
let isSameLength = E.A.length(inputs) == E.A.length(args) let isSameLength = E.A.length(inputs) == E.A.length(args)
if !isSameLength { if !isSameLength {
None false
} else { } else {
E.A.zip(inputs, args) E.A.zip(inputs, args)
->E.A2.fmap(((input, arg)) => matchWithExpressionValue(input, arg)) ->Belt.Array.every(((input, arg)) => matchWithValue(input, arg))
->E.A.O.openIfAllSome
} }
} }
} }
/*
This module, Matcher, is fairly lengthy. However, only two functions from it
are meant to be used outside of it. These are findMatches and matchToDef in Matches.Registry.
The rest of it is just called from those two functions.
Update: This really should be completely re-done sometime, and tested. It works, but it's pretty messy. I'm sure
there are internal bugs, but the end functionality works, so I'm not too worried.
*/
module Matcher = {
module MatchSimple = {
type t = DifferentName | SameNameDifferentArguments | FullMatch
let isFullMatch = (match: t) =>
switch match {
| FullMatch => true
| _ => false
}
let isNameMatchOnly = (match: t) =>
switch match {
| SameNameDifferentArguments => true
| _ => false
}
}
module Match = {
type t<'a, 'b> = DifferentName | SameNameDifferentArguments('a) | FullMatch('b)
let isFullMatch = (match: t<'a, 'b>): bool =>
switch match {
| FullMatch(_) => true
| _ => false
}
let isNameMatchOnly = (match: t<'a, 'b>) =>
switch match {
| SameNameDifferentArguments(_) => true
| _ => false
}
}
module FnDefinition = {
let matchAssumingSameName = (f: fnDefinition, args: array<internalExpressionValue>) => {
switch FRType.matchWithExpressionValueArray(f.inputs, args) {
| Some(_) => MatchSimple.FullMatch
| None => MatchSimple.SameNameDifferentArguments
}
}
let match = (f: fnDefinition, fnName: string, args: array<internalExpressionValue>) => {
if f.name !== fnName {
MatchSimple.DifferentName
} else {
matchAssumingSameName(f, args)
}
}
}
module Function = {
type definitionId = int
type match = Match.t<array<definitionId>, definitionId>
let match = (
f: function,
nameSpace: option<string>,
fnName: string,
args: array<internalExpressionValue>,
): match => {
switch nameSpace {
| Some(ns) if ns !== f.nameSpace => Match.DifferentName
| _ => {
let matchedDefinition = () =>
E.A.getIndexBy(f.definitions, r =>
MatchSimple.isFullMatch(FnDefinition.match(r, fnName, args))
) |> E.O.fmap(r => Match.FullMatch(r))
let getMatchedNameOnlyDefinition = () => {
let nameMatchIndexes =
f.definitions
->E.A2.fmapi((index, r) =>
MatchSimple.isNameMatchOnly(FnDefinition.match(r, fnName, args))
? Some(index)
: None
)
->E.A.O.concatSomes
switch nameMatchIndexes {
| [] => None
| elements => Some(Match.SameNameDifferentArguments(elements))
}
}
E.A.O.firstSomeFnWithDefault(
[matchedDefinition, getMatchedNameOnlyDefinition],
Match.DifferentName,
)
}
}
}
}
module RegistryMatch = {
type match = {
nameSpace: string,
fnName: string,
inputIndex: int,
}
let makeMatch = (nameSpace: string, fnName: string, inputIndex: int) => {
nameSpace: nameSpace,
fnName: fnName,
inputIndex: inputIndex,
}
}
module Registry = {
let _findExactMatches = (
r: registry,
nameSpace: option<string>,
fnName: string,
args: array<internalExpressionValue>,
) => {
let functionMatchPairs =
r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args)))
let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match))
fullMatch->E.O.bind(((fn, match)) =>
switch match {
| FullMatch(index) => Some(RegistryMatch.makeMatch(fn.nameSpace, fn.name, index))
| _ => None
}
)
}
let _findNameMatches = (
r: registry,
nameSpace: option<string>,
fnName: string,
args: array<internalExpressionValue>,
) => {
let functionMatchPairs =
r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args)))
let getNameMatches =
functionMatchPairs
->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None)
->E.A.O.concatSomes
let matches =
getNameMatches
->E.A2.fmap(((fn, match)) =>
switch match {
| SameNameDifferentArguments(indexes) =>
indexes->E.A2.fmap(index => RegistryMatch.makeMatch(fn.nameSpace, fn.name, index))
| _ => []
}
)
->Belt.Array.concatMany
E.A.toNoneIfEmpty(matches)
}
let findMatches = (r: registry, fnName: string, args: array<internalExpressionValue>) => {
let fnNameInParts = Js.String.split(".", fnName)
let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0])
let nameSpace = E.A.length(fnNameInParts) > 1 ? Some(fnNameInParts[0]) : None
switch _findExactMatches(r, nameSpace, fnToSearch, args) {
| Some(r) => Match.FullMatch(r)
| None =>
switch _findNameMatches(r, nameSpace, fnToSearch, args) {
| Some(r) => Match.SameNameDifferentArguments(r)
| None => Match.DifferentName
}
}
}
let matchToDef = (
registry: registry,
{nameSpace, fnName, inputIndex}: RegistryMatch.match,
): option<fnDefinition> =>
registry.functions
->E.A.getBy(fn => {
nameSpace === fn.nameSpace && fnName === fn.name
})
->E.O.bind(fn => E.A.get(fn.definitions, inputIndex))
}
}
module FnDefinition = { module FnDefinition = {
type t = fnDefinition type t = fnDefinition
@ -373,24 +118,19 @@ module FnDefinition = {
t.name ++ `(${inputs})` t.name ++ `(${inputs})`
} }
let isMatch = (t: t, args: array<internalExpressionValue>) => { let isMatch = (t: t, args: array<Reducer_T.value>) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) FRType.matchWithValueArray(t.inputs, args)
switch argValues {
| Some(_) => true
| None => false
}
} }
let run = ( let run = (
t: t, t: t,
args: array<internalExpressionValue>, args: array<Reducer_T.value>,
accessors: ProjectAccessorsT.t, env: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => { ) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) switch t->isMatch(args) {
switch argValues { | true => t.run(args, env, reducer)
| Some(values) => t.run(args, values, accessors, reducer) | false => REOther("Incorrect Types")->Error
| None => Error("Incorrect Types")
} }
} }
@ -442,43 +182,41 @@ module Function = {
} }
} }
module NameSpace = {
type t = {name: string, functions: array<function>}
let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany
let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq
let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name)
}
module Registry = { module Registry = {
type fnNameDict = Belt.Map.String.t<array<fnDefinition>>
type registry = {functions: array<function>, fnNameDict: fnNameDict}
let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson) let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson)
let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany
let allExamplesWithFns = (r: registry) => let allExamplesWithFns = (r: registry) =>
r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany
let allNames = (r: registry) => r.fnNameDict->Belt.Map.String.keysToArray
let _buildFnNameDict = (r: array<function>): fnNameDict => { let _buildFnNameDict = (r: array<function>): fnNameDict => {
let allDefinitionsWithFns = // Three layers of reduce:
r // 1. functions
->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions))) // 2. definitions of each function
->E.A.concatMany // 3. name variations of each definition
let functionsWithFnNames = r->Belt.Array.reduce(Belt.Map.String.empty, (acc, fn) =>
allDefinitionsWithFns fn.definitions->Belt.Array.reduce(acc, (acc, def) => {
->E.A2.fmap(((fn, def)) => { let names =
let nameWithNamespace = `${fn.nameSpace}.${def.name}` [
let nameWithoutNamespace = def.name fn.nameSpace == "" ? [] : [`${fn.nameSpace}.${def.name}`],
fn.requiresNamespace fn.requiresNamespace ? [] : [def.name],
? [(nameWithNamespace, fn)] ]->E.A.concatMany
: [(nameWithNamespace, fn), (nameWithoutNamespace, fn)]
names->Belt.Array.reduce(acc, (acc, name) => {
switch acc->Belt.Map.String.get(name) {
| Some(fns) => {
let _ = fns->Js.Array2.push(def) // mutates the array, no need to update acc
acc
}
| None => acc->Belt.Map.String.set(name, [def])
}
})
}) })
->E.A.concatMany )
let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq
let cacheAsArray: array<(string, array<function>)> = uniqueNames->E.A2.fmap(uniqueName => {
let relevantItems =
E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap(
E.Tuple2.second,
)
(uniqueName, relevantItems)
})
cacheAsArray->Js.Dict.fromArray
} }
let make = (fns: array<function>): registry => { let make = (fns: array<function>): registry => {
@ -486,48 +224,31 @@ module Registry = {
{functions: fns, fnNameDict: dict} {functions: fns, fnNameDict: dict}
} }
/* let call = (
There's a (potential+minor) bug here: If a function definition is called outside of the calls
to the registry, then it's possible that there could be a match after the registry is
called. However, for now, we could just call the registry last.
*/
let _matchAndRun = (
~registry: registry,
~fnName: string,
~args: array<internalExpressionValue>,
~accessors: ProjectAccessorsT.t,
~reducer: ProjectReducerFnT.t,
) => {
let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([])
let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict}
let matchToDef = m => Matcher.Registry.matchToDef(registry, m)
let showNameMatchDefinitions = matches => {
let defs =
matches
->E.A2.fmap(matchToDef)
->E.A.O.concatSomes
->E.A2.fmap(FnDefinition.toString)
->E.A2.fmap(r => `[${r}]`)
->E.A2.joinWith("; ")
`There are function matches for ${fnName}(), but with different arguments: ${defs}`
}
switch Matcher.Registry.findMatches(modified, fnName, args) {
| Matcher.Match.FullMatch(match) =>
match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, accessors, reducer))
| SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m)))
| _ => None
}
}
let dispatch = (
registry, registry,
(fnName, args): ReducerInterface_InternalExpressionValue.functionCall, fnName: string,
accessors: ProjectAccessorsT.t, args: array<Reducer_T.value>,
reducer: ProjectReducerFnT.t, env: Reducer_T.environment,
) => { reducer: Reducer_T.reducerFn,
_matchAndRun(~registry, ~fnName, ~args, ~accessors, ~reducer)->E.O2.fmap( ): result<Reducer_T.value, errorValue> => {
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)), switch Belt.Map.String.get(registry.fnNameDict, fnName) {
) | Some(definitions) => {
let showNameMatchDefinitions = () => {
let defsString =
definitions
->E.A2.fmap(FnDefinition.toString)
->E.A2.fmap(r => `[${r}]`)
->E.A2.joinWith("; ")
`There are function matches for ${fnName}(), but with different arguments: ${defsString}`
}
let match = definitions->Js.Array2.find(def => def->FnDefinition.isMatch(args))
switch match {
| Some(def) => def->FnDefinition.run(args, env, reducer)
| None => REOther(showNameMatchDefinitions())->Error
}
}
| None => RESymbolNotFound(fnName)->Error
}
} }
} }

View File

@ -1,59 +1,65 @@
open FunctionRegistry_Core open FunctionRegistry_Core
open Reducer_T
let impossibleError = "Wrong inputs / Logically impossible" let impossibleErrorString = "Wrong inputs / Logically impossible"
let impossibleError: errorValue = impossibleErrorString->Reducer_ErrorValue.REOther
let wrapError = e => Reducer_ErrorValue.REOther(e)
module Wrappers = { module Wrappers = {
let symbolic = r => DistributionTypes.Symbolic(r) let symbolic = r => DistributionTypes.Symbolic(r)
let pointSet = r => DistributionTypes.PointSet(r) let pointSet = r => DistributionTypes.PointSet(r)
let sampleSet = r => DistributionTypes.SampleSet(r) let sampleSet = r => DistributionTypes.SampleSet(r)
let evDistribution = r => ReducerInterface_InternalExpressionValue.IEvDistribution(r) let evDistribution = r => Reducer_T.IEvDistribution(r)
let evNumber = r => ReducerInterface_InternalExpressionValue.IEvNumber(r) let evNumber = r => Reducer_T.IEvNumber(r)
let evArray = r => ReducerInterface_InternalExpressionValue.IEvArray(r) let evArray = r => Reducer_T.IEvArray(r)
let evRecord = r => ReducerInterface_InternalExpressionValue.IEvRecord(r) let evRecord = r => Reducer_T.IEvRecord(r)
let evString = r => ReducerInterface_InternalExpressionValue.IEvString(r) let evString = r => Reducer_T.IEvString(r)
let symbolicEvDistribution = r => r->DistributionTypes.Symbolic->evDistribution let symbolicEvDistribution = r => r->DistributionTypes.Symbolic->evDistribution
let evArrayOfEvNumber = xs => xs->Belt.Array.map(evNumber)->evArray let evArrayOfEvNumber = xs => xs->Belt.Array.map(evNumber)->evArray
} }
let getOrError = (a, g) => E.A.get(a, g) |> E.O.toResult(impossibleError) let getOrError = (a, g) => E.A.get(a, g) |> E.O.toResult(impossibleErrorString)
module Prepare = { module Prepare = {
type t = frValue type t = value
type ts = array<frValue> type ts = array<value>
type err = string type err = string
module ToValueArray = { module ToValueArray = {
module Record = { module Record = {
let twoArgs = (inputs: ts): result<ts, err> => let twoArgs = (inputs: ts, (arg1: string, arg2: string)): result<ts, err> =>
switch inputs { switch inputs {
| [FRValueRecord([(_, n1), (_, n2)])] => Ok([n1, n2]) | [IEvRecord(map)] => {
| _ => Error(impossibleError) let n1 = map->Belt.Map.String.getExn(arg1)
let n2 = map->Belt.Map.String.getExn(arg2)
Ok([n1, n2])
}
| _ => Error(impossibleErrorString)
} }
let threeArgs = (inputs: ts): result<ts, err> => let threeArgs = (inputs: ts, (arg1: string, arg2: string, arg3: string)): result<ts, err> =>
switch inputs { switch inputs {
| [FRValueRecord([(_, n1), (_, n2), (_, n3)])] => Ok([n1, n2, n3]) | [IEvRecord(map)] => {
| _ => Error(impossibleError) let n1 = map->Belt.Map.String.getExn(arg1)
let n2 = map->Belt.Map.String.getExn(arg2)
let n3 = map->Belt.Map.String.getExn(arg3)
Ok([n1, n2, n3])
} }
| _ => Error(impossibleErrorString)
let toArgs = (inputs: ts): result<ts, err> =>
switch inputs {
| [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok
| _ => Error(impossibleError)
} }
} }
module Array = { module Array = {
let openA = (inputs: t): result<ts, err> => let openA = (inputs: t): result<ts, err> =>
switch inputs { switch inputs {
| FRValueArray(n) => Ok(n) | IEvArray(n) => Ok(n)
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
} }
let arrayOfArrays = (inputs: t): result<array<ts>, err> => let arrayOfArrays = (inputs: t): result<array<ts>, err> =>
switch inputs { switch inputs {
| FRValueArray(n) => n->E.A2.fmap(openA)->E.A.R.firstErrorOrOpen | IEvArray(n) => n->E.A2.fmap(openA)->E.A.R.firstErrorOrOpen
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
} }
} }
} }
@ -61,8 +67,11 @@ module Prepare = {
module ToValueTuple = { module ToValueTuple = {
let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => { let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => {
switch values { switch values {
| [FRValueDistOrNumber(a1), FRValueDistOrNumber(a2)] => Ok(a1, a2) | [IEvDistribution(a1), IEvDistribution(a2)] => Ok(FRValueDist(a1), FRValueDist(a2))
| _ => Error(impossibleError) | [IEvDistribution(a1), IEvNumber(a2)] => Ok(FRValueDist(a1), FRValueNumber(a2))
| [IEvNumber(a1), IEvDistribution(a2)] => Ok(FRValueNumber(a1), FRValueDist(a2))
| [IEvNumber(a1), IEvNumber(a2)] => Ok(FRValueNumber(a1), FRValueNumber(a2))
| _ => Error(impossibleErrorString)
} }
} }
@ -71,67 +80,55 @@ module Prepare = {
err, err,
> => { > => {
switch values { switch values {
| [FRValueDist(a1), FRValueDist(a2)] => Ok(a1, a2) | [IEvDistribution(a1), IEvDistribution(a2)] => Ok(a1, a2)
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
} }
} }
let twoNumbers = (values: ts): result<(float, float), err> => { let twoNumbers = (values: ts): result<(float, float), err> => {
switch values { switch values {
| [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2) | [IEvNumber(a1), IEvNumber(a2)] => Ok(a1, a2)
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
} }
} }
let threeNumbers = (values: ts): result<(float, float, float), err> => { let threeNumbers = (values: ts): result<(float, float, float), err> => {
switch values { switch values {
| [FRValueNumber(a1), FRValueNumber(a2), FRValueNumber(a3)] => Ok(a1, a2, a3) | [IEvNumber(a1), IEvNumber(a2), IEvNumber(a3)] => Ok(a1, a2, a3)
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
} }
} }
let oneDistOrNumber = (values: ts): result<frValueDistOrNumber, err> => { let oneDistOrNumber = (values: ts): result<frValueDistOrNumber, err> => {
switch values { switch values {
| [FRValueDistOrNumber(a1)] => Ok(a1) | [IEvNumber(a1)] => FRValueNumber(a1)->Ok
| _ => Error(impossibleError) | [IEvDistribution(a2)] => FRValueDist(a2)->Ok
| _ => Error(impossibleErrorString)
} }
} }
module Record = { module Record = {
let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => let twoDistOrNumber = (values: ts, labels: (string, string)): result<(frValueDistOrNumber, frValueDistOrNumber), err> =>
values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber) values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDistOrNumber)
let twoDist = (values: ts): result< let twoDist = (values: ts, labels: (string, string)): result<
(DistributionTypes.genericDist, DistributionTypes.genericDist), (DistributionTypes.genericDist, DistributionTypes.genericDist),
err, err,
> => values->ToValueArray.Record.twoArgs->E.R.bind(twoDist) > => values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDist)
} }
} }
module ToArrayRecordPairs = { let oneNumber = (value: t): result<float, err> => {
let twoArgs = (input: t): result<array<ts>, err> => { switch value {
let array = input->ToValueArray.Array.openA | IEvNumber(a1) => Ok(a1)
let pairs = | _ => Error(impossibleErrorString)
array->E.R.bind(pairs =>
pairs
->E.A2.fmap(xyCoord => [xyCoord]->ToValueArray.Record.twoArgs)
->E.A.R.firstErrorOrOpen
)
pairs
} }
} }
let oneNumber = (values: t): result<float, err> => { let oneDict = (value: t): result<Reducer_T.map, err> => {
switch values { switch value {
| FRValueNumber(a1) => Ok(a1) | IEvRecord(a1) => Ok(a1)
| _ => Error(impossibleError) | _ => Error(impossibleErrorString)
}
}
let oneDict = (values: t): result<Js.Dict.t<frValue>, err> => {
switch values {
| FRValueDict(a1) => Ok(a1)
| _ => Error(impossibleError)
} }
} }
@ -142,7 +139,7 @@ module Prepare = {
inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openNumbers) inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openNumbers)
} }
let dicts = (inputs: ts): Belt.Result.t<array<Js.Dict.t<frValue>>, err> => { let dicts = (inputs: ts): Belt.Result.t<array<Reducer_T.map>, err> => {
let openDicts = (elements: array<t>) => elements->E.A2.fmap(oneDict)->E.A.R.firstErrorOrOpen let openDicts = (elements: array<t>) => elements->E.A2.fmap(oneDict)->E.A.R.firstErrorOrOpen
inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openDicts) inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openDicts)
} }
@ -223,12 +220,11 @@ module DefineFn = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeNumber], ~inputs=[FRTypeNumber],
~run=(_, inputs, _, _) => { ~run=(inputs, _, _) => {
inputs switch inputs {
->getOrError(0) | [IEvNumber(x)] => fn(x)->IEvNumber->Ok
->E.R.bind(Prepare.oneNumber) | _ => Error(impossibleError)
->E.R2.fmap(fn) }
->E.R2.fmap(Wrappers.evNumber)
}, },
(), (),
) )
@ -236,8 +232,11 @@ module DefineFn = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _, _) => { ~run=(inputs, _, _) => {
inputs->Prepare.ToValueTuple.twoNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) switch inputs {
| [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvNumber->Ok
| _ => Error(impossibleError)
}
}, },
(), (),
) )
@ -245,10 +244,153 @@ module DefineFn = {
FnDefinition.make( FnDefinition.make(
~name, ~name,
~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber], ~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _, _) => { ~run=(inputs, _, _) => {
inputs->Prepare.ToValueTuple.threeNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) switch inputs {
| [IEvNumber(x), IEvNumber(y), IEvNumber(z)] => fn(x, y, z)->IEvNumber->Ok
| _ => Error(impossibleError)
}
}, },
(), (),
) )
} }
} }
module Make = {
/*
Opinionated explanations for API choices here:
Q: Why such short names?
A: Because we have to type them a lot in definitions.
Q: Why not DefineFn.Numbers.oneToOne / DefineFn.Numbers.twoToOne / ...?
A: Because return values matter too, and we have many possible combinations: numbers to numbers, pairs of numbers to numbers, pair of numbers to bools.
Q: Does this approach scale?
A: It's good enough for most cases, and we can fall back on raw `Function.make` if necessary. We should figure out the better API powered by parameterized types, but it's hard (and might require PPX).
Q: What about `frValue` types?
A: I hope we'll get rid of them soon.
Q: What about polymorphic functions with multiple definitions? Why ~fn is not an array?
A: We often define the same function in multiple `FR_*` files, so that doesn't work well anyway. In 90%+ cases there's a single definition. And having to write `name` twice is annoying.
*/
let f2f = (
~name: string,
~fn: float => float,
~nameSpace="",
~requiresNamespace=false,
~examples=?,
(),
) => {
Function.make(
~name,
~nameSpace,
~requiresNamespace,
~examples=examples->E.O.default([], _),
~output=EvtNumber,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeNumber],
~run=(inputs, _, _) =>
switch inputs {
| [IEvNumber(x)] => fn(x)->IEvNumber->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
}
let ff2f = (
~name: string,
~fn: (float, float) => float,
~nameSpace="",
~requiresNamespace=false,
~examples=?,
(),
) => {
Function.make(
~name,
~nameSpace,
~requiresNamespace,
~examples=examples->E.O.default([], _),
~output=EvtNumber,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeNumber, FRTypeNumber],
~run=(inputs, _, _) =>
switch inputs {
| [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvNumber->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
}
let ff2b = (
~name: string,
~fn: (float, float) => bool,
~nameSpace="",
~requiresNamespace=false,
~examples=?,
(),
) => {
Function.make(
~name,
~nameSpace,
~requiresNamespace,
~examples=examples->E.O.default([], _),
~output=EvtBool,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeNumber, FRTypeNumber],
~run=(inputs, _, _) =>
switch inputs {
| [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvBool->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
}
let bb2b = (
~name: string,
~fn: (bool, bool) => bool,
~nameSpace="",
~requiresNamespace=false,
~examples=?,
(),
) => {
Function.make(
~name,
~nameSpace,
~requiresNamespace,
~examples=examples->E.O.default([], _),
~output=EvtBool,
~definitions=[
FnDefinition.make(
~name,
~inputs=[FRTypeBool, FRTypeBool],
~run=(inputs, _, _) =>
switch inputs {
| [IEvBool(x), IEvBool(y)] => fn(x, y)->IEvBool->Ok
| _ => Error(impossibleError)
},
(),
),
],
(),
)
}
}

View File

@ -1,4 +1,5 @@
let fnList = Belt.Array.concatMany([ let fnList = Belt.Array.concatMany([
FR_Builtin.library,
FR_Dict.library, FR_Dict.library,
FR_Dist.library, FR_Dist.library,
FR_Danger.library, FR_Danger.library,
@ -8,7 +9,16 @@ let fnList = Belt.Array.concatMany([
FR_Number.library, FR_Number.library,
FR_Pointset.library, FR_Pointset.library,
FR_Scoring.library, FR_Scoring.library,
FR_GenericDist.library,
FR_Units.library,
FR_Date.library,
FR_Math.library,
]) ])
let registry = FunctionRegistry_Core.Registry.make(fnList) let registry = FunctionRegistry_Core.Registry.make(fnList)
let dispatch = FunctionRegistry_Core.Registry.dispatch(registry) let call = FunctionRegistry_Core.Registry.call(registry)
let nonRegistryLambdas: array<(string, Reducer_T.lambdaValue)> = [
("mx", FR_GenericDist.mxLambda),
("mixture", FR_GenericDist.mxLambda),
]

View File

@ -9,38 +9,38 @@ The main interface is fairly constrained. Basically, write functions like the fo
~name="Normal", ~name="Normal",
~definitions=[ ~definitions=[
FnDefinition.make( FnDefinition.make(
~name="Normal", ~name="normal",
~definitions=[ ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
FnDefinition.make(~name="normal", ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=( ~run=(
inputs, inputs,
env, env,
) => ) =>
inputs inputs
->Prepare.ToValueTuple.twoDistOrNumber ->Prepare.ToValueTuple.twoDistOrNumber
->E.R.bind( ->E.R.bind(
Process.twoDistsOrNumbersToDistUsingSymbolicDist( Process.twoDistsOrNumbersToDistUsingSymbolicDist(
~fn=E.Tuple2.toFnCall(SymbolicDist.Normal.make), ~fn=E.Tuple2.toFnCall(SymbolicDist.Normal.make),
~env, ~env,
~values=_, ~values=_,
),
)
->E.R2.fmap(Wrappers.evDistribution)
), ),
], )
->E.R2.fmap(Wrappers.evDistribution)
) )
], ],
) )
``` ```
The Function name is just there for future documentation. The function defintions The Function name is just there for future documentation.
## Key Files ## Key Files
**FunctionRegistry_Core** **FunctionRegistry_Core**
Key types, internal functionality, and a `Registry` module with a `matchAndRun` function to call function definitions. Key types, internal functionality, and a `Registry` module with a `call` function to call function definitions.
**FunctionRegistry_Library** **FunctionRegistry_Library**
A list of all the Functions defined in the Function Registry. A list of all the Functions defined in the Function Registry.
The definition arrays are stored in `FR_*` modules, by convention.
**FunctionRegistry_Helpers** **FunctionRegistry_Helpers**
A list of helper functions for the FunctionRegistry_Library. A list of helper functions for the `FunctionRegistry_Library`.

View File

@ -0,0 +1,47 @@
/*
Bindings describe the entire set of bound variables accessible to the squiggle code.
Bindings objects are stored as linked lists of scopes:
{ localX: ..., localY: ... } <- { globalZ: ..., ... } <- { importedT: ..., ... } <- { stdlibFunction: ..., ... }
*/
type t = Reducer_T.bindings
let rec get = ({namespace, parent}: t, id: string) => {
switch namespace->Reducer_Namespace.get(id) {
| Some(v) => Some(v)
| None =>
switch parent {
| Some(p) => p->get(id)
| None => None
}
}
}
let set = ({namespace} as bindings: t, id: string, value): t => {
{
...bindings,
namespace: namespace->Reducer_Namespace.set(id, value),
}
}
let rec toString = ({namespace, parent}: t) => {
let pairs = namespace->Reducer_Namespace.toString
switch parent {
| Some(p) => `{${pairs}} / ${toString(p)}`
| None => `{${pairs}}`
}
}
let extend = (bindings: t): t => {namespace: Reducer_Namespace.make(), parent: bindings->Some}
let make = (): t => {namespace: Reducer_Namespace.make(), parent: None}
let removeResult = ({namespace} as bindings: t): t => {
...bindings,
namespace: namespace->Belt.Map.String.remove("__result__"),
}
let locals = ({namespace}: t): Reducer_T.namespace => namespace
let fromNamespace = (namespace: Reducer_Namespace.t): t => {namespace: namespace, parent: None}

View File

@ -1,189 +0,0 @@
// Only Bindings as the global module is supported
// Other module operations such as import export will be preprocessed jobs
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Reducer_ErrorValue
open ReducerInterface_InternalExpressionValue
let expressionValueToString = toString
type t = ReducerInterface_InternalExpressionValue.nameSpace
let typeAliasesKey = "_typeAliases_"
let typeReferencesKey = "_typeReferences_"
let getType = (NameSpace(container): t, id: string) => {
Belt.Map.String.get(container, typeAliasesKey)->Belt.Option.flatMap(aliases =>
switch aliases {
| IEvRecord(r) => Belt.Map.String.get(r, id)
| _ => None
}
)
}
let getTypeOf = (NameSpace(container): t, id: string) => {
Belt.Map.String.get(container, typeReferencesKey)->Belt.Option.flatMap(defs =>
switch defs {
| IEvRecord(r) => Belt.Map.String.get(r, id)
| _ => None
}
)
}
let getWithDefault = (NameSpace(container): t, id: string, default) =>
switch Belt.Map.String.get(container, id) {
| Some(v) => v
| None => default
}
let get = (NameSpace(container): t, id: string) => Belt.Map.String.get(container, id)
let emptyMap: map = Belt.Map.String.empty
let setTypeAlias = (NameSpace(container): t, id: string, value): t => {
let rValue = Belt.Map.String.getWithDefault(container, typeAliasesKey, IEvRecord(emptyMap))
let r = switch rValue {
| IEvRecord(r) => r
| _ => emptyMap
}
let r2 = Belt.Map.String.set(r, id, value)->IEvRecord
NameSpace(Belt.Map.String.set(container, typeAliasesKey, r2))
}
let setTypeOf = (NameSpace(container): t, id: string, value): t => {
let rValue = Belt.Map.String.getWithDefault(container, typeReferencesKey, IEvRecord(emptyMap))
let r = switch rValue {
| IEvRecord(r) => r
| _ => emptyMap
}
let r2 = Belt.Map.String.set(r, id, value)->IEvRecord
NameSpace(Belt.Map.String.set(container, typeReferencesKey, r2))
}
let set = (NameSpace(container): t, id: string, value): t => NameSpace(
Belt.Map.String.set(container, id, value),
)
let emptyModule: t = NameSpace(emptyMap)
let emptyBindings = emptyModule
let emptyNameSpace = emptyModule
// let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings
// let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings
let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace)
let fromExpressionValue = (aValue: internalExpressionValue): t =>
switch aValue {
| IEvBindings(nameSpace) => nameSpace
| _ => emptyModule
}
let fromArray = a => NameSpace(Belt.Map.String.fromArray(a))
let mergeFrom = (NameSpace(container): t, NameSpace(newContainer): t): t => {
NameSpace(
newContainer->Belt.Map.String.reduce(container, (container, key, value) =>
Belt.Map.String.set(container, key, value)
),
)
}
let removeOther = (NameSpace(container): t, NameSpace(otherContainer): t): t => {
let keys = Belt.Map.String.keysToArray(otherContainer)
NameSpace(
Belt.Map.String.keep(container, (key, _value) => {
let removeThis = Js.Array2.includes(keys, key)
!removeThis
}),
)
}
external castExpressionToInternalCode: ExpressionT.expressionOrFFI => internalCode = "%identity"
let eLambdaFFIValue = (ffiFn: ExpressionT.ffiFn) => {
IEvLambda({
parameters: [],
context: emptyModule,
body: FFI(ffiFn)->castExpressionToInternalCode,
})
}
let functionNotFoundError = (call: functionCall) =>
REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error
let functionNotFoundErrorFFIFn = (functionName: string): ExpressionT.ffiFn => {
(args: array<internalExpressionValue>, _environment: environment): result<
internalExpressionValue,
errorValue,
> => {
let call = (functionName, args)
functionNotFoundError(call)
}
}
let convertOptionToFfiFnReturningResult = (
myFunctionName: string,
myFunction: ExpressionT.optionFfiFnReturningResult,
): ExpressionT.ffiFn => {
(args: array<InternalExpressionValue.t>, environment) => {
myFunction(args, environment)->Belt.Option.getWithDefault(
functionNotFoundErrorFFIFn(myFunctionName)(args, environment),
)
}
}
let convertOptionToFfiFn = (
myFunctionName: string,
myFunction: ExpressionT.optionFfiFn,
): ExpressionT.ffiFn => {
(args: array<InternalExpressionValue.t>, environment) => {
myFunction(args, environment)
->Belt.Option.map(v => v->Ok)
->Belt.Option.getWithDefault(functionNotFoundErrorFFIFn(myFunctionName)(args, environment))
}
}
// -- Module definition
let define = (NameSpace(container): t, identifier: string, ev: internalExpressionValue): t => {
NameSpace(Belt.Map.String.set(container, identifier, ev))
}
let defineNumber = (nameSpace: t, identifier: string, value: float): t =>
nameSpace->define(identifier, IEvNumber(value))
let defineString = (nameSpace: t, identifier: string, value: string): t =>
nameSpace->define(identifier, IEvString(value))
let defineBool = (nameSpace: t, identifier: string, value: bool): t =>
nameSpace->define(identifier, IEvBool(value))
let defineModule = (nameSpace: t, identifier: string, value: t): t =>
nameSpace->define(identifier, toExpressionValue(value))
let defineFunction = (nameSpace: t, identifier: string, value: ExpressionT.optionFfiFn): t => {
nameSpace->define(identifier, convertOptionToFfiFn(identifier, value)->eLambdaFFIValue)
}
let defineFunctionReturningResult = (
nameSpace: t,
identifier: string,
value: ExpressionT.optionFfiFnReturningResult,
): t => {
nameSpace->define(
identifier,
convertOptionToFfiFnReturningResult(identifier, value)->eLambdaFFIValue,
)
}
let emptyStdLib: t = emptyModule->defineBool("_standardLibrary", true)
let chainTo = (nameSpace: t, previousNameSpaces: array<t>) => {
previousNameSpaces->Belt.Array.reduce(nameSpace, (topNameSpace, prevNameSpace) =>
mergeFrom(prevNameSpace, topNameSpace)
)
}
let removeResult = (NameSpace(container): t): t => {
container->Belt.Map.String.remove("__result__")->NameSpace
}

View File

@ -0,0 +1,12 @@
type t = Reducer_T.context
let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
{
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
environment: environment,
}
}
let createDefaultContext = (): t => createContext(SquiggleLibrary_StdLib.stdLib, defaultEnvironment)

View File

@ -1,2 +0,0 @@
module Builtin = Reducer_Dispatch_BuiltIn
module BuiltinMacros = Reducer_Dispatch_BuiltInMacros

View File

@ -1,218 +0,0 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module Continuation = ReducerInterface_Value_Continuation
module ExpressionT = Reducer_Expression_T
module ExternalLibrary = ReducerInterface.ExternalLibrary
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Lambda = Reducer_Expression_Lambda
module MathJs = Reducer_MathJs
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
/*
MathJs provides default implementations for built-ins
This is where all the expected built-ins like + = * / sin cos log ln etc are handled
DO NOT try to add external function mapping here!
*/
//TODO: pow to xor
exception TestRescriptException
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
| ("rescriptraise", _) => raise(TestRescriptException) // For Tests
| call => call->toStringFunctionCall->MathJs.Eval.eval
}
let constructRecord = arrayOfPairs => {
Belt.Array.map(arrayOfPairs, pairValue =>
switch pairValue {
| IEvArray([IEvString(key), valueValue]) => (key, valueValue)
| _ => ("wrong key type", pairValue->toStringWithType->IEvString)
}
)
->Belt.Map.String.fromArray
->IEvRecord
->Ok
}
let arrayAtIndex = (aValueArray: array<internalExpressionValue>, fIndex: float) =>
switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) {
| Some(value) => value->Ok
| None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error
}
let moduleAtIndex = (nameSpace: nameSpace, sIndex) =>
switch Bindings.get(nameSpace, sIndex) {
| Some(value) => value->Ok
| None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error
}
let recordAtIndex = (dict: Belt.Map.String.t<internalExpressionValue>, sIndex) =>
switch Belt.Map.String.get(dict, sIndex) {
| Some(value) => value->Ok
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
}
let doAddArray = (originalA, b) => {
let a = originalA->Js.Array2.copy
let _ = Js.Array2.pushMany(a, b)
a->IEvArray->Ok
}
let doAddString = (a, b) => {
let answer = Js.String2.concat(a, b)
answer->IEvString->Ok
}
let inspect = (value: internalExpressionValue) => {
Js.log(value->toString)
value->Ok
}
let inspectLabel = (value: internalExpressionValue, label: string) => {
Js.log(`${label}: ${value->toString}`)
value->Ok
}
let doSetBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) => {
Bindings.set(bindings, symbol, value)->IEvBindings->Ok
}
let doSetTypeAliasBindings = (
bindings: nameSpace,
symbol: string,
value: internalExpressionValue,
) => Bindings.setTypeAlias(bindings, symbol, value)->IEvBindings->Ok
let doSetTypeOfBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) =>
Bindings.setTypeOf(bindings, symbol, value)->IEvBindings->Ok
let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok
let doIdentity = (value: internalExpressionValue) => value->Ok
let doDumpBindings = (continuation: nameSpace, value: internalExpressionValue) => {
// let _ = Continuation.inspect(continuation, "doDumpBindings")
accessors.states.continuation = continuation->Bindings.set("__result__", value)
value->Ok
}
module SampleMap = {
let doLambdaCall = (aLambdaValue, list) =>
switch Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) {
| IEvNumber(f) => Ok(f)
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
}
let toType = r =>
switch r {
| Ok(r) => Ok(IEvDistribution(SampleSet(r)))
| Error(r) => Error(REDistributionError(SampleSetError(r)))
}
let parseSampleSetArray = (arr: array<internalExpressionValue>): option<
array<SampleSetDist.t>,
> => {
let parseSampleSet = (value: internalExpressionValue): option<SampleSetDist.t> =>
switch value {
| IEvDistribution(SampleSet(dist)) => Some(dist)
| _ => None
}
E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr))
}
let _mapN = (aValueArray: array<internalExpressionValue>, aLambdaValue) => {
switch parseSampleSetArray(aValueArray) {
| Some(t1) =>
let fn = a => doLambdaCall(aLambdaValue, list{IEvArray(E.A.fmap(x => IEvNumber(x), a))})
SampleSetDist.mapN(~fn, ~t1)->toType
| None =>
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString))
}
}
}
switch call {
| ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex)
| ("$_atIndex_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex)
| ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex)
| ("$_constructArray_$", args) => IEvArray(args)->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)]) =>
TypeBuilder.typeModifier_memberOf_update(typeRecord, IEvArray(arr))
| ("$_typeModifier_min_$", [IEvTypeIdentifier(typeIdentifier), value]) =>
TypeBuilder.typeModifier_min(IEvTypeIdentifier(typeIdentifier), value)
| ("$_typeModifier_min_$", [IEvType(typeRecord), value]) =>
TypeBuilder.typeModifier_min_update(typeRecord, value)
| ("$_typeModifier_max_$", [IEvTypeIdentifier(typeIdentifier), value]) =>
TypeBuilder.typeModifier_max(IEvTypeIdentifier(typeIdentifier), value)
| ("$_typeModifier_max_$", [IEvType(typeRecord), value]) =>
TypeBuilder.typeModifier_max_update(typeRecord, value)
| ("$_typeModifier_opaque_$", [IEvType(typeRecord)]) =>
TypeBuilder.typeModifier_opaque_update(typeRecord)
| ("$_typeOr_$", [IEvArray(arr)]) => TypeBuilder.typeOr(IEvArray(arr))
| ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr)
| ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems)
| ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem)
| ("$_typeRecord_$", [IEvRecord(propertyMap)]) => TypeBuilder.typeRecord(propertyMap)
| ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) =>
doAddArray(aValueArray, bValueArray)
| ("concat", [IEvString(aValueString), IEvString(bValueString)]) =>
doAddString(aValueString, bValueString)
| ("inspect", [value, IEvString(label)]) => inspectLabel(value, label)
| ("inspect", [value]) => inspect(value)
| (_, [IEvBool(_)])
| (_, [IEvNumber(_)])
| (_, [IEvString(_)])
| (_, [IEvBool(_), IEvBool(_)])
| (_, [IEvNumber(_), IEvNumber(_)])
| (_, [IEvString(_), IEvString(_)]) =>
callMathJs(call)
| call =>
Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error
}
}
/*
Reducer uses Result monad while reducing expressions
*/
let dispatch = (
call: functionCall,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): internalExpressionValue =>
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),
accessors,
reducer,
callInternal,
)->InternalExpressionValue.resultToValue
} catch {
| exn => Reducer_ErrorValue.fromException(exn)->Reducer_ErrorValue.toException
}

View File

@ -1,189 +0,0 @@
/*
Macros are like functions but instead of taking values as parameters,
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 ExpressionWithContext = Reducer_ExpressionWithContext
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
open Reducer_Expression_ExpressionBuilder
type expression = ExpressionT.expression
type expressionWithContext = ExpressionWithContext.expressionWithContext
let dispatchMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
accessors: ProjectAccessorsT.t,
reduceExpression: ProjectReducerFnT.t,
): expressionWithContext => {
let useExpressionToSetBindings = (bindingExpr: expression, accessors, statement, newCode) => {
let nameSpaceValue = reduceExpression(bindingExpr, bindings, accessors)
let newBindings = Bindings.fromExpressionValue(nameSpaceValue)
let boundStatement = BindingsReplacer.replaceSymbols(newBindings, statement)
ExpressionWithContext.withContext(newCode(newBindings->eModule, boundStatement), newBindings)
}
let correspondingSetBindingsFn = (fnName: string): string =>
switch fnName {
| "$_let_$" => "$_setBindings_$"
| "$_typeOf_$" => "$_setTypeOfBindings_$"
| "$_typeAlias_$" => "$_setTypeAliasBindings_$"
| "$_endOfOuterBlock_$" => "$_dumpBindings_$"
| _ => ""
}
let doBindStatement = (bindingExpr: expression, statement: expression, accessors) => {
let defaultStatement = ErrorValue.REAssignmentExpected
switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
let setBindingsFn = correspondingSetBindingsFn(callName)
if setBindingsFn !== "" {
useExpressionToSetBindings(bindingExpr, accessors, statement, (
newBindingsExpr,
boundStatement,
) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement}))
} else {
defaultStatement->Reducer_ErrorValue.toException
}
}
| _ => defaultStatement->Reducer_ErrorValue.toException
}
}
let doBindExpression = (
bindingExpr: expression,
statement: expression,
accessors,
): expressionWithContext => {
let defaultStatement = () =>
useExpressionToSetBindings(bindingExpr, accessors, statement, (
_newBindingsExpr,
boundStatement,
) => boundStatement)
switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => {
let setBindingsFn = correspondingSetBindingsFn(callName)
if setBindingsFn !== "" {
useExpressionToSetBindings(bindingExpr, accessors, statement, (
newBindingsExpr,
boundStatement,
) =>
eFunction(
"$_exportBindings_$",
list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})}, // expression returning bindings
)
)
} else {
defaultStatement()
}
}
| _ => defaultStatement()
}
}
let doBlock = (
exprs: list<expression>,
_bindings: ExpressionT.bindings,
_accessors,
): expressionWithContext => {
let exprsArray = Belt.List.toArray(exprs)
let maxIndex = Js.Array2.length(exprsArray) - 1
let newStatement = exprsArray->Js.Array2.reducei((acc, statement, index) =>
if index == 0 {
if index == maxIndex {
eBindExpressionDefault(statement)
} else {
eBindStatementDefault(statement)
}
} else if index == maxIndex {
eBindExpression(acc, statement)
} else {
eBindStatement(acc, statement)
}
, eSymbol("undefined block"))
ExpressionWithContext.noContext(newStatement)
}
let doLambdaDefinition = (
bindings: ExpressionT.bindings,
parameters: array<string>,
lambdaDefinition: ExpressionT.expression,
) => ExpressionWithContext.noContext(eLambda(parameters, bindings, lambdaDefinition))
let doTernary = (
condition: expression,
ifTrue: expression,
ifFalse: expression,
bindings: ExpressionT.bindings,
accessors,
): expressionWithContext => {
let blockCondition = ExpressionBuilder.eBlock(list{condition})
let conditionValue = reduceExpression(blockCondition, bindings, accessors)
switch conditionValue {
| InternalExpressionValue.IEvBool(false) => {
let ifFalseBlock = eBlock(list{ifFalse})
ExpressionWithContext.withContext(ifFalseBlock, bindings)
}
| InternalExpressionValue.IEvBool(true) => {
let ifTrueBlock = eBlock(list{ifTrue})
ExpressionWithContext.withContext(ifTrueBlock, bindings)
}
| _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.toException
}
}
let expandExpressionList = (
aList,
bindings: ExpressionT.bindings,
accessors,
): expressionWithContext =>
switch aList {
| list{
ExpressionT.EValue(IEvCall("$$_bindStatement_$$")),
bindingExpr: ExpressionT.expression,
statement,
} =>
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, accessors)
| list{
ExpressionT.EValue(IEvCall("$$_bindExpression_$$")),
bindingExpr: ExpressionT.expression,
expression,
} =>
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, accessors)
| list{ExpressionT.EValue(IEvCall("$$_block_$$")), ...exprs} =>
doBlock(exprs, bindings, accessors)
| list{
ExpressionT.EValue(IEvCall("$$_lambda_$$")),
ExpressionT.EValue(IEvArrayString(parameters)),
lambdaDefinition,
} =>
doLambdaDefinition(bindings, parameters, lambdaDefinition)
| list{ExpressionT.EValue(IEvCall("$$_ternary_$$")), condition, ifTrue, ifFalse} =>
doTernary(condition, ifTrue, ifFalse, bindings, accessors)
| _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))
}
switch macroExpression {
| EList(aList) => expandExpressionList(aList, bindings, accessors)
| _ => ExpressionWithContext.noContext(macroExpression)
}
}

View File

@ -1,7 +1,9 @@
// types are disabled until review and rewrite for 0.5 interpreter compatibility
/*
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module T = Reducer_Dispatch_T module T = Reducer_Dispatch_T
module TypeChecker = Reducer_Type_TypeChecker module TypeChecker = Reducer_Type_TypeChecker
open ReducerInterface_InternalExpressionValue open Reducer_Value
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
@ -21,3 +23,5 @@ let makeFromTypes = jumpTable => {
} }
dispatchChainPiece dispatchChainPiece
} }
*/

View File

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

View File

@ -1,108 +1,130 @@
module Bindings = Reducer_Bindings module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module BuiltIn = Reducer_Dispatch_BuiltIn
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module Extra = Reducer_Extra
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Lambda = Reducer_Expression_Lambda module Lambda = Reducer_Expression_Lambda
module Macro = Reducer_Expression_Macro
module MathJs = Reducer_MathJs
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module Result = Belt.Result module Result = Belt.Result
module T = Reducer_Expression_T module T = Reducer_T
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
type t = T.t
/* /*
Recursively evaluate/reduce the expression (Lisp AST/Lambda calculus) Recursively evaluate the expression
*/ */
let rec reduceExpressionInProject = ( let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
expression: t, // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`)
continuation: T.bindings,
accessors: ProjectAccessorsT.t,
): InternalExpressionValue.t => {
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression { switch expression {
| T.EValue(value) => value | T.EBlock(statements) => {
| T.EList(list) => let innerContext = {...context, bindings: context.bindings->Bindings.extend}
switch list { let (value, _) =
| list{EValue(IEvCall(fName)), ..._args} => statements->Belt.Array.reduce((T.IEvVoid, innerContext), ((_, currentContext), statement) =>
switch Macro.isMacroName(fName) { statement->evaluate(currentContext)
// A macro expands then reduces itself )
| true => Macro.doMacroCall(expression, continuation, accessors, reduceExpressionInProject) (value, context)
| false => reduceExpressionList(list, continuation, accessors)
}
| _ => reduceExpressionList(list, continuation, accessors)
} }
}
}
and reduceExpressionList = (
expressions: list<t>,
continuation: T.bindings,
accessors: ProjectAccessorsT.t,
): InternalExpressionValue.t => {
let acc: list<InternalExpressionValue.t> =
expressions->Belt.List.reduceReverse(list{}, (acc, each: t) =>
acc->Belt.List.add(each->reduceExpressionInProject(continuation, accessors))
)
acc->reduceValueList(accessors)
}
/* | T.EProgram(statements) => {
After reducing each level of expression(Lisp AST), we have a value list to evaluate // Js.log(`bindings: ${context.bindings->Bindings.locals->Reducer_Namespace.toString}`)
*/ let (value, finalContext) =
and reduceValueList = ( statements->Belt.Array.reduce((T.IEvVoid, context), ((_, currentContext), statement) =>
valueList: list<InternalExpressionValue.t>, statement->evaluate(currentContext)
accessors: ProjectAccessorsT.t, )
): InternalExpressionValue.t =>
switch valueList {
| list{IEvCall(fName), ...args} => {
let checkedArgs = switch fName {
| "$_setBindings_$" | "$_setTypeOfBindings_$" | "$_setTypeAliasBindings_$" => args
| _ => args->Lambda.checkIfReduced
}
(fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch( // Js.log(`bindings after: ${finalContext.bindings->Bindings.locals->Reducer_Namespace.toString}`)
accessors, (value, finalContext)
reduceExpressionInProject, }
| T.EArray(elements) => {
let value =
elements
->Belt.Array.map(element => {
let (value, _) = evaluate(element, context)
value
})
->T.IEvArray
(value, context)
}
| T.ERecord(pairs) => {
let value =
pairs
->Belt.Array.map(((eKey, eValue)) => {
let (key, _) = eKey->evaluate(context)
let keyString = switch key {
| IEvString(s) => s
| _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise
}
let (value, _) = eValue->evaluate(context)
(keyString, value)
})
->Belt.Map.String.fromArray
->T.IEvRecord
(value, context)
}
| T.EAssign(left, right) => {
let (result, _) = right->evaluate(context)
(
T.IEvVoid,
{
...context,
bindings: context.bindings->Bindings.set(left, result),
},
) )
} }
| list{IEvLambda(_)} =>
// TODO: remove on solving issue#558
valueList->Lambda.checkIfReduced->Belt.List.toArray->InternalExpressionValue.IEvArray
| list{IEvLambda(lambdaCall), ...args} =>
args
->Lambda.checkIfReduced
->Lambda.doLambdaCall(lambdaCall, _, accessors, reduceExpressionInProject)
| _ => valueList->Lambda.checkIfReduced->Belt.List.toArray->InternalExpressionValue.IEvArray
}
let reduceReturningBindings = ( | T.ESymbol(name) =>
expression: t, switch context.bindings->Bindings.get(name) {
continuation: T.bindings, | Some(v) => (v, context)
accessors: ProjectAccessorsT.t, | None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise
): (InternalExpressionValue.t, T.bindings) => { }
let states = accessors.states
let result = reduceExpressionInProject(expression, continuation, accessors) | T.EValue(value) => (value, context)
(result, states.continuation)
| T.ETernary(predicate, trueCase, falseCase) => {
let (predicateResult, _) = predicate->evaluate(context)
switch predicateResult {
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
| _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise
}
}
| T.ELambda(parameters, body) => (
Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda,
context,
)
| T.ECall(fn, args) => {
let (lambda, _) = fn->evaluate(context)
let argValues = Js.Array2.map(args, arg => {
let (argValue, _) = arg->evaluate(context)
argValue
})
switch lambda {
| T.IEvLambda(lambda) => (
Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate),
context,
)
| _ =>
RENotAFunction(lambda->Reducer_Value.toString)->Reducer_ErrorValue.ErrorException->raise
}
}
}
} }
module BackCompatible = { module BackCompatible = {
// Those methods are used to support the existing tests // Those methods are used to support the existing tests
// If they are used outside limited testing context, error location reporting will fail // If they are used outside limited testing context, error location reporting will fail
let parse = (peggyCode: string): result<t, errorValue> => let parse = (peggyCode: string): result<T.expression, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
let evaluate = (expression: t): result<InternalExpressionValue.t, errorValue> => { let evaluate = (expression: T.expression): result<T.value, errorValue> => {
let accessors = ProjectAccessorsT.identityAccessors let context = Reducer_Context.createDefaultContext()
try { try {
expression->reduceExpressionInProject(accessors.stdLib, accessors)->Ok let (value, _) = expression->evaluate(context)
value->Ok
} catch { } catch {
| exn => Reducer_ErrorValue.fromException(exn)->Error | exn => Reducer_ErrorValue.fromException(exn)->Error
} }
} }
let evaluateString = (peggyCode: string): result<InternalExpressionValue.t, errorValue> => let evaluateString = (peggyCode: string): result<T.value, errorValue> =>
parse(peggyCode)->Result.flatMap(evaluate) parse(peggyCode)->Result.flatMap(evaluate)
} }

View File

@ -1,62 +0,0 @@
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
type bindings = ExpressionT.bindings
type context = bindings
type environment = InternalExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t
type expressionWithContext =
| ExpressionWithContext(expression, context)
| ExpressionNoContext(expression)
type t = expressionWithContext
let callReducer = (
expressionWithContext: expressionWithContext,
bindings: bindings,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): internalExpressionValue => {
switch expressionWithContext {
| ExpressionNoContext(expr) =>
// Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`)
reducer(expr, bindings, accessors)
| ExpressionWithContext(expr, context) =>
// Js.log(`callReducer: context ${Bindings.toString(context)} expr ${ExpressionT.toString(expr)}`)
reducer(expr, context, accessors)
}
}
let withContext = (expression, context) => ExpressionWithContext(expression, context)
let noContext = expression => ExpressionNoContext(expression)
let toString = expressionWithContext =>
switch expressionWithContext {
| ExpressionNoContext(expr) => ExpressionT.toString(expr)
| ExpressionWithContext(expr, context) =>
`${ExpressionT.toString(expr)} context: ${context
->Bindings.toExpressionValue
->InternalExpressionValue.toString}`
}
let toStringResult = rExpressionWithContext =>
switch rExpressionWithContext {
| Ok(expressionWithContext) => `Ok(${toString(expressionWithContext)})`
| Error(errorValue) => ErrorValue.errorToString(errorValue)
}
let resultToValue = (rExpressionWithContext: result<t, errorValue>): t => {
switch rExpressionWithContext {
| Ok(expressionWithContext) => expressionWithContext
| Error(errorValue) => ErrorValue.toException(errorValue)
}
}

View File

@ -1,45 +0,0 @@
module ErrorValue = Reducer_ErrorValue
module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Bindings = Reducer_Bindings
type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression
type internalExpressionValue = InternalExpressionValue.t
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")
let rec replaceSymbols = (bindings: ExpressionT.bindings, expression: expression): expression =>
switch expression {
| ExpressionT.EValue(value) => replaceSymbolOnValue(bindings, value)->ExpressionT.EValue
| ExpressionT.EList(list) =>
switch list {
| list{EValue(IEvCall(fName)), ..._args} =>
switch isMacroName(fName) {
// A macro reduces itself so we dont dive in it
| true => expression
| false => replaceSymbolsOnExpressionList(bindings, list)
}
| _ => replaceSymbolsOnExpressionList(bindings, list)
}
}
and replaceSymbolsOnExpressionList = (bindings, list) => {
let racc =
list->Belt.List.reduceReverse(list{}, (acc, each: expression) =>
replaceSymbols(bindings, each)->Belt.List.add(acc, _)
)
ExpressionT.EList(racc)
}
and replaceSymbolOnValue = (bindings, evValue: internalExpressionValue) =>
switch evValue {
| IEvSymbol(symbol) => Bindings.getWithDefault(bindings, symbol, evValue)
| IEvCall(symbol) => Bindings.getWithDefault(bindings, symbol, evValue)->checkIfCallable
| _ => evValue
}
and checkIfCallable = (evValue: internalExpressionValue) =>
switch evValue {
| IEvCall(_) | IEvLambda(_) => evValue
| _ =>
ErrorValue.RENotAFunction(InternalExpressionValue.toString(evValue))->ErrorValue.toException
}

View File

@ -1,86 +1,43 @@
module BBindingsReplacer = Reducer_Expression_BindingsReplacer
module BErrorValue = Reducer_ErrorValue module BErrorValue = Reducer_ErrorValue
module BExpressionT = Reducer_Expression_T module T = Reducer_T
module BInternalExpressionValue = ReducerInterface_InternalExpressionValue
module BBindings = Reducer_Bindings
type errorValue = BErrorValue.errorValue type errorValue = BErrorValue.errorValue
type expression = BExpressionT.expression type expression = Reducer_T.expression
type expressionOrFFI = BExpressionT.expressionOrFFI
type ffiFn = BExpressionT.ffiFn
type internalCode = ReducerInterface_InternalExpressionValue.internalCode
let eArray = anArray => anArray->BInternalExpressionValue.IEvArray->BExpressionT.EValue let eArray = (anArray: array<T.expression>) => anArray->T.EArray
let eArrayString = anArray => anArray->BInternalExpressionValue.IEvArrayString->BExpressionT.EValue let eBool = aBool => aBool->T.IEvBool->T.EValue
let eBindings = (anArray: array<(string, BInternalExpressionValue.t)>) => let eCall = (fn: expression, args: array<expression>): expression => T.ECall(fn, args)
anArray->BBindings.fromArray->BBindings.toExpressionValue->BExpressionT.EValue
let eBool = aBool => aBool->BInternalExpressionValue.IEvBool->BExpressionT.EValue let eLambda = (parameters: array<string>, expr: expression) => T.ELambda(parameters, expr)
let eCall = (name: string): expression => let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
name->BInternalExpressionValue.IEvCall->BExpressionT.EValue
let eFunction = (fName: string, lispArgs: list<expression>): expression => { let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord
let fn = fName->eCall
list{fn, ...lispArgs}->BExpressionT.EList
}
let eLambda = ( let eString = aString => aString->T.IEvString->T.EValue
parameters: array<string>,
context: BInternalExpressionValue.nameSpace,
expr: expression,
) => {
BInternalExpressionValue.IEvLambda({
parameters: parameters,
context: context,
body: NotFFI(expr)->BBindings.castExpressionToInternalCode,
})->BExpressionT.EValue
}
let eLambdaFFI = (ffiFn: ffiFn) => { let eSymbol = (name: string): expression => T.ESymbol(name)
ffiFn->BBindings.eLambdaFFIValue->BExpressionT.EValue
}
let eNumber = aNumber => aNumber->BInternalExpressionValue.IEvNumber->BExpressionT.EValue let eBlock = (exprs: array<expression>): expression => T.EBlock(exprs)
let eRecord = aMap => aMap->BInternalExpressionValue.IEvRecord->BExpressionT.EValue let eProgram = (exprs: array<expression>): expression => T.EProgram(exprs)
let eString = aString => aString->BInternalExpressionValue.IEvString->BExpressionT.EValue let eLetStatement = (symbol: string, valueExpression: expression): expression => T.EAssign(
symbol,
valueExpression,
)
let eSymbol = (name: string): expression => let eTernary = (
name->BInternalExpressionValue.IEvSymbol->BExpressionT.EValue predicate: expression,
trueCase: expression,
falseCase: expression,
): expression => T.ETernary(predicate, trueCase, falseCase)
let eList = (list: list<expression>): expression => list->BExpressionT.EList let eIdentifier = (name: string): expression => name->T.ESymbol
let eBlock = (exprs: list<expression>): expression => eFunction("$$_block_$$", exprs) // let eTypeIdentifier = (name: string): expression =>
// name->T.IEvTypeIdentifier->T.EValue
let eModule = (nameSpace: BInternalExpressionValue.nameSpace): expression => let eVoid: expression = T.IEvVoid->T.EValue
nameSpace->BInternalExpressionValue.IEvBindings->BExpressionT.EValue
let eLetStatement = (symbol: string, valueExpression: expression): expression =>
eFunction("$_let_$", list{eSymbol(symbol), valueExpression})
let eBindStatement = (bindingExpr: expression, letStatement: expression): expression =>
eFunction("$$_bindStatement_$$", list{bindingExpr, letStatement})
let eBindStatementDefault = (letStatement: expression): expression =>
eFunction("$$_bindStatement_$$", list{letStatement})
let eBindExpression = (bindingExpr: expression, expression: expression): expression =>
eFunction("$$_bindExpression_$$", list{bindingExpr, expression})
let eBindExpressionDefault = (expression: expression): expression =>
eFunction("$$_bindExpression_$$", list{expression})
let eTernary = (truth: expression, trueCase: expression, falseCase: expression): expression =>
eFunction("$$_ternary_$$", list{truth, trueCase, falseCase})
let eIdentifier = (name: string): expression =>
name->BInternalExpressionValue.IEvSymbol->BExpressionT.EValue
let eTypeIdentifier = (name: string): expression =>
name->BInternalExpressionValue.IEvTypeIdentifier->BExpressionT.EValue
let eVoid: expression = BInternalExpressionValue.IEvVoid->BExpressionT.EValue

View File

@ -1,99 +1,60 @@
module Bindings = Reducer_Bindings
module BindingsReplacer = Reducer_Expression_BindingsReplacer
module ErrorValue = Reducer_ErrorValue module ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectReducerFnT = ReducerProject_ReducerFn_T
module Result = Belt.Result
type expression = ExpressionT.expression
type expressionOrFFI = ExpressionT.expressionOrFFI
type internalExpressionValue = ReducerInterface_InternalExpressionValue.t
type internalCode = ReducerInterface_InternalExpressionValue.internalCode
external castInternalCodeToExpression: internalCode => expressionOrFFI = "%identity"
let checkArity = (
lambdaValue: ExpressionValue.lambdaValue,
args: list<internalExpressionValue>,
) => {
let reallyCheck = {
let argsLength = Belt.List.length(args)
let parametersLength = Js.Array2.length(lambdaValue.parameters)
if argsLength !== parametersLength {
ErrorValue.REArityError(None, parametersLength, argsLength)->ErrorValue.toException
} else {
args
}
}
let exprOrFFI = castInternalCodeToExpression(lambdaValue.body)
switch exprOrFFI {
| NotFFI(_) => reallyCheck
| FFI(_) => args
}
}
let checkIfReduced = (args: list<internalExpressionValue>) =>
args->Belt.List.reduceReverse(list{}, (acc, arg) =>
switch arg {
| IEvSymbol(symbol) => ErrorValue.RESymbolNotFound(symbol)->ErrorValue.toException
| _ => list{arg, ...acc}
}
)
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, (
acc,
(variable, variableValue),
) => acc->Bindings.set(variable, variableValue))
let newExpression = ExpressionBuilder.eBlock(list{expr})
reducer(newExpression, bindings, accessors)
}
let caseFFI = (ffiFn: ExpressionT.ffiFn, args, accessors: ProjectAccessorsT.t) => {
switch ffiFn(args->Belt.List.toArray, accessors.environment) {
| Ok(value) => value
| Error(value) => value->ErrorValue.toException
}
}
let applyParametersToLambda = (
lambdaValue: ExpressionValue.lambdaValue,
args,
accessors: ProjectAccessorsT.t,
reducer: ProjectReducerFnT.t,
): internalExpressionValue => {
let args = checkArity(lambdaValue, args)->checkIfReduced
let exprOrFFI = castInternalCodeToExpression(lambdaValue.body)
switch exprOrFFI {
| NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, accessors, reducer)
| FFI(ffiFn) => caseFFI(ffiFn, args, accessors)
}
}
let doLambdaCall = ( let doLambdaCall = (
lambdaValue: ExpressionValue.lambdaValue, lambdaValue: Reducer_T.lambdaValue,
args, args,
accessors: ProjectAccessorsT.t, environment: Reducer_T.environment,
reducer: ProjectReducerFnT.t, reducer: Reducer_T.reducerFn,
) => applyParametersToLambda(lambdaValue, args, accessors, reducer) ): Reducer_T.value => {
lambdaValue.body(args, environment, reducer)
let foreignFunctionInterface = ( }
lambdaValue: ExpressionValue.lambdaValue,
argArray: array<internalExpressionValue>, let makeLambda = (
accessors: ProjectAccessorsT.t, parameters: array<string>,
reducer: ProjectReducerFnT.t, bindings: Reducer_T.bindings,
): internalExpressionValue => { body: Reducer_T.expression,
let args = argArray->Belt.List.fromArray ): Reducer_T.lambdaValue => {
applyParametersToLambda(lambdaValue, args, accessors, reducer) // TODO - clone bindings to avoid later redefinitions affecting lambdas?
// Note: with this implementation, FFI lambdas (created by other methods than calling `makeLambda`) are allowed to violate the rules, pollute the bindings, etc.
// Not sure yet if that's a bug or a feature.
// FunctionRegistry functions are unaffected by this, their API is too limited.
let lambda = (
arguments: array<Reducer_T.value>,
environment: Reducer_T.environment,
reducer: Reducer_T.reducerFn,
) => {
let argsLength = arguments->Js.Array2.length
let parametersLength = parameters->Js.Array2.length
if argsLength !== parametersLength {
ErrorValue.REArityError(None, parametersLength, argsLength)->ErrorValue.ErrorException->raise
}
let localBindings = bindings->Reducer_Bindings.extend
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
currentBindings,
parameter,
index,
) => {
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
})
let (value, _) = reducer(
body,
{bindings: localBindingsWithParameters, environment: environment},
)
value
}
{
// context: bindings,
body: lambda,
parameters: parameters,
}
}
let makeFFILambda = (body: Reducer_T.lambdaBody): Reducer_T.lambdaValue => {
body: body,
parameters: ["..."],
} }

View File

@ -1,43 +0,0 @@
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
type internalExpressionValue = InternalExpressionValue.t
type expressionWithContext = ExpressionWithContext.expressionWithContext
let expandMacroCallRs = (
macroExpression: expression,
bindings: ExpressionT.bindings,
accessors: ProjectAccessorsT.t,
reduceExpression: ProjectReducerFnT.t,
): result<expressionWithContext, 'e> =>
try {
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression,
bindings,
accessors,
reduceExpression,
)->Ok
} catch {
| exn => Reducer_ErrorValue.fromException(exn)->Error
}
let doMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
accessors: ProjectAccessorsT.t,
reduceExpression: ProjectReducerFnT.t,
): internalExpressionValue =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression,
bindings,
(accessors: ProjectAccessorsT.t),
(reduceExpression: ProjectReducerFnT.t),
)->ExpressionWithContext.callReducer(bindings, accessors, reduceExpression)
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")

View File

@ -1,46 +1,31 @@
/* /*
An expression is a Lisp AST. An expression is either a primitive value or a list of expressions. An expression is an intermediate representation of a Squiggle code.
In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is Expressions are evaluated by `Reducer_Expression.evaluate` function.
apply e1, e2 -> apply e3 -> ... -> apply eN
This is Lisp semantics. It holds true in both eager and lazy evaluations.
A Lisp AST contains only expressions/primitive values to apply to their left.
The act of defining the semantics of a functional language is to write it in terms of Lisp AST.
*/ */
module Extra = Reducer_Extra type t = Reducer_T.expression
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
type internalExpressionValue = InternalExpressionValue.t let commaJoin = values => values->Reducer_Extra_Array.intersperse(", ")->Js.String.concatMany("")
type environment = ReducerInterface_InternalExpressionValue.environment let semicolonJoin = values =>
values->Reducer_Extra_Array.intersperse("; ")->Js.String.concatMany("")
type rec expression =
| EList(list<expression>) // A list to map-reduce
| 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,
environment,
) => result<internalExpressionValue, Reducer_ErrorValue.errorValue>
/* /*
Converts the expression to String Converts the expression to String
*/ */
let rec toString = expression => let rec toString = (expression: t) =>
switch expression { switch expression {
| EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) => | EBlock(statements) =>
`{${Belt.List.map(statements, aValue => toString(aValue)) `{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}`
->Extra.List.intersperse("; ") | EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin
->Belt.List.toArray | EArray(aList) => `[${Js.Array2.map(aList, aValue => toString(aValue))->commaJoin}]`
->Js.String.concatMany("")}}` | ERecord(map) =>
| EList(aList) => `{${map->Belt.Array.map(((key, value)) => `${key->toString}: ${value->toString}`)->commaJoin}}`
`(${Belt.List.map(aList, aValue => toString(aValue)) | ESymbol(name) => name
->Extra.List.intersperse(" ") | ETernary(predicate, trueCase, falseCase) =>
->Belt.List.toArray `${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
->Js.String.concatMany("")})` | EAssign(name, value) => `${name} = ${value->toString}`
| EValue(aValue) => InternalExpressionValue.toString(aValue) | ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})`
| ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}`
| EValue(aValue) => Reducer_Value.toString(aValue)
} }
let toStringResult = codeResult => let toStringResult = codeResult =>
@ -55,34 +40,19 @@ let toStringResultOkless = codeResult =>
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
} }
let inspect = (expr: expression): expression => { let inspect = (expr: t): t => {
Js.log(toString(expr)) Js.log(toString(expr))
expr expr
} }
let inspectResult = (r: result<expression, Reducer_ErrorValue.errorValue>): result< let inspectResult = (r: result<t, Reducer_ErrorValue.errorValue>): result<
expression, t,
Reducer_ErrorValue.errorValue, Reducer_ErrorValue.errorValue,
> => { > => {
Js.log(toStringResult(r)) Js.log(toStringResult(r))
r r
} }
type ffiFn = (
array<internalExpressionValue>,
environment,
) => result<internalExpressionValue, Reducer_ErrorValue.errorValue>
type optionFfiFn = (array<internalExpressionValue>, environment) => option<internalExpressionValue>
type optionFfiFnReturningResult = (
array<internalExpressionValue>,
environment,
) => option<result<internalExpressionValue, Reducer_ErrorValue.errorValue>>
type expressionOrFFI =
| NotFFI(expression)
| FFI(ffiFn)
let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t => let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t =>
switch rExpression { switch rExpression {
| Ok(expression) => expression | Ok(expression) => expression

View File

@ -1 +0,0 @@
module Gate = Reducer_Js_Gate

Some files were not shown because too many files have changed in this diff Show More