Merge remote-tracking branch 'origin/develop' into log-score-attempt
This commit is contained in:
commit
6c815a62ff
|
@ -141,6 +141,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
||||||
expression={r}
|
expression={r}
|
||||||
width={width !== undefined ? width - 20 : width}
|
width={width !== undefined ? width - 20 : width}
|
||||||
height={50}
|
height={50}
|
||||||
|
showTypes={showTypes}
|
||||||
|
showControls={showControls}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
|
@ -155,11 +157,25 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
||||||
expression={r}
|
expression={r}
|
||||||
width={width !== undefined ? width - 20 : width}
|
width={width !== undefined ? width - 20 : width}
|
||||||
height={50}
|
height={50}
|
||||||
|
showTypes={showTypes}
|
||||||
|
showControls={showControls}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
|
case "arraystring":
|
||||||
|
return (
|
||||||
|
<VariableBox heading="Array String" showTypes={showTypes}>
|
||||||
|
{expression.value.map((r) => `"${r}"`)}
|
||||||
|
</VariableBox>
|
||||||
|
);
|
||||||
|
case "lambda":
|
||||||
|
return (
|
||||||
|
<ErrorBox heading="No Viewer">
|
||||||
|
There is no viewer currently available for function types.
|
||||||
|
</ErrorBox>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
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))")
|
||||||
|
// 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", EvNumber(2.))]), exampleStatementX),
|
||||||
|
"Ok((:$setBindings {x: 2} :y 2))",
|
||||||
|
)
|
||||||
|
// An expression does not return a binding, thus error
|
||||||
|
testMacro([], eBindStatement(eBindings([]), exampleExpression), "Error(Assignment expected)")
|
||||||
|
// When bindings from previous statement are missing the context is injected. This must be the first statement of a block
|
||||||
|
testMacro(
|
||||||
|
[("z", EvNumber(99.))],
|
||||||
|
eBindStatementDefault(exampleStatementY),
|
||||||
|
"Ok((:$setBindings {z: 99} :y 1))",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("bindExpression", () => {
|
||||||
|
// x is simply bound in the expression
|
||||||
|
testMacro([], eBindExpression(eBindings([("x", EvNumber(2.))]), eSymbol("x")), "Ok(2)")
|
||||||
|
// When an let statement is the end expression then bindings are returned
|
||||||
|
testMacro(
|
||||||
|
[],
|
||||||
|
eBindExpression(eBindings([("x", EvNumber(2.))]), exampleStatementY),
|
||||||
|
"Ok((:$exportBindings (:$setBindings {x: 2} :y 1)))",
|
||||||
|
)
|
||||||
|
// Now let's reduce that expression
|
||||||
|
testMacroEval(
|
||||||
|
[],
|
||||||
|
eBindExpression(eBindings([("x", EvNumber(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", EvNumber(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 (:$$block 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 (:$$block (:$$block :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 (:$$block (:$let :y (:add :x 1)) :y)))",
|
||||||
|
)
|
||||||
|
MyOnly.testMacroEval(
|
||||||
|
[("x", EvNumber(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))")
|
||||||
|
// 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", EvNumber(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", EvNumber(666.))], callLambdaExpression, "Ok(667)")
|
||||||
|
})
|
|
@ -0,0 +1,6 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
test("dummy", () => {
|
||||||
|
expect(true)->toBe(true)
|
||||||
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
open ReducerInterface.ExpressionValue
|
open ReducerInterface.ExpressionValue
|
||||||
module MathJs = Reducer.MathJs
|
module MathJs = Reducer_MathJs
|
||||||
module ErrorValue = Reducer.ErrorValue
|
module ErrorValue = Reducer.ErrorValue
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module Parse = Reducer.MathJs.Parse
|
module Parse = Reducer_MathJs.Parse
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
|
@ -18,8 +18,14 @@ module MySkip = {
|
||||||
Skip.test(desc, () => expectParseToBe(expr, answer))
|
Skip.test(desc, () => expectParseToBe(expr, answer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module MyOnly = {
|
||||||
|
let testParse = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testDescriptionParse = (desc, expr, answer) =>
|
||||||
|
Only.test(desc, () => expectParseToBe(expr, answer))
|
||||||
|
}
|
||||||
|
|
||||||
describe("MathJs parse", () => {
|
describe("MathJs parse", () => {
|
||||||
describe("literals operators paranthesis", () => {
|
describe("literals operators parenthesis", () => {
|
||||||
testParse("1", "1")
|
testParse("1", "1")
|
||||||
testParse("'hello'", "'hello'")
|
testParse("'hello'", "'hello'")
|
||||||
testParse("true", "true")
|
testParse("true", "true")
|
||||||
|
@ -40,15 +46,15 @@ describe("MathJs parse", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("functions", () => {
|
describe("functions", () => {
|
||||||
MySkip.testParse("identity(x) = x", "???")
|
testParse("identity(x) = x", "identity = (x) => x")
|
||||||
MySkip.testParse("identity(x)", "???")
|
testParse("identity(x)", "identity(x)")
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("arrays", () => {
|
describe("arrays", () => {
|
||||||
testDescriptionParse("empty", "[]", "[]")
|
testDescriptionParse("empty", "[]", "[]")
|
||||||
testDescriptionParse("define", "[0, 1, 2]", "[0, 1, 2]")
|
testDescriptionParse("define", "[0, 1, 2]", "[0, 1, 2]")
|
||||||
testDescriptionParse("define with strings", "['hello', 'world']", "['hello', 'world']")
|
testDescriptionParse("define with strings", "['hello', 'world']", "['hello', 'world']")
|
||||||
MySkip.testParse("range(0, 4)", "range(0, 4)")
|
testParse("range(0, 4)", "range(0, 4)")
|
||||||
testDescriptionParse("index", "([0,1,2])[1]", "([0, 1, 2])[1]")
|
testDescriptionParse("index", "([0,1,2])[1]", "([0, 1, 2])[1]")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -58,11 +64,6 @@ describe("MathJs parse", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("comments", () => {
|
describe("comments", () => {
|
||||||
MySkip.testDescriptionParse("define", "# This is a comment", "???")
|
testDescriptionParse("define", "1 # This is a comment", "1")
|
||||||
})
|
|
||||||
|
|
||||||
describe("if statement", () => {
|
|
||||||
// TODO Tertiary operator instead
|
|
||||||
MySkip.testDescriptionParse("define", "if (true) { 1 } else { 0 }", "???")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,40 +1,31 @@
|
||||||
module Expression = Reducer.Expression
|
module ExpressionT = Reducer_Expression_T
|
||||||
module ExpressionValue = ReducerInterface.ExpressionValue
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
|
let unwrapRecord = rValue =>
|
||||||
|
rValue->Belt.Result.flatMap(value =>
|
||||||
|
switch value {
|
||||||
|
| ExpressionValue.EvRecord(aRecord) => Ok(aRecord)
|
||||||
|
| _ => ErrorValue.RETodo("TODO: External bindings must be returned")->Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
let expectParseToBe = (expr: string, answer: string) =>
|
let expectParseToBe = (expr: string, answer: string) =>
|
||||||
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
|
Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
let expectParseOuterToBe = (expr: string, answer: string) =>
|
|
||||||
Reducer.parseOuter(expr)->Expression.toStringResult->expect->toBe(answer)
|
|
||||||
|
|
||||||
let expectParsePartialToBe = (expr: string, answer: string) =>
|
|
||||||
Reducer.parsePartial(expr)->Expression.toStringResult->expect->toBe(answer)
|
|
||||||
|
|
||||||
let expectEvalToBe = (expr: string, answer: string) =>
|
let expectEvalToBe = (expr: string, answer: string) =>
|
||||||
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
|
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
|
||||||
Reducer.evaluateUsingExternalBindings(expr, bindings)
|
Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
|
||||||
->ExpressionValue.toStringResult
|
->ExpressionValue.toStringResult
|
||||||
->expect
|
->expect
|
||||||
->toBe(answer)
|
->toBe(answer)
|
||||||
|
|
||||||
let expectEvalPartialBindingsToBe = (
|
|
||||||
expr: string,
|
|
||||||
bindings: Reducer.externalBindings,
|
|
||||||
answer: string,
|
|
||||||
) =>
|
|
||||||
Reducer.evaluatePartialUsingExternalBindings(expr, bindings)
|
|
||||||
->ExpressionValue.toStringResultRecord
|
|
||||||
->expect
|
|
||||||
->toBe(answer)
|
|
||||||
|
|
||||||
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||||
let testParseOuterToBe = (expr, answer) => test(expr, () => expectParseOuterToBe(expr, answer))
|
|
||||||
let testParsePartialToBe = (expr, answer) => test(expr, () => expectParsePartialToBe(expr, answer))
|
|
||||||
let testDescriptionParseToBe = (desc, expr, answer) =>
|
let testDescriptionParseToBe = (desc, expr, answer) =>
|
||||||
test(desc, () => expectParseToBe(expr, answer))
|
test(desc, () => expectParseToBe(expr, answer))
|
||||||
|
|
||||||
|
@ -42,34 +33,16 @@ let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answe
|
||||||
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
||||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
|
||||||
test(expr, () => expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
|
||||||
|
|
||||||
module MySkip = {
|
module MySkip = {
|
||||||
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
||||||
let testParseOuterToBe = (expr, answer) =>
|
|
||||||
Skip.test(expr, () => expectParseOuterToBe(expr, answer))
|
|
||||||
let testParsePartialToBe = (expr, answer) =>
|
|
||||||
Skip.test(expr, () => expectParsePartialToBe(expr, answer))
|
|
||||||
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
|
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
|
||||||
Skip.test(expr, () =>
|
|
||||||
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
module MyOnly = {
|
module MyOnly = {
|
||||||
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||||
let testParseOuterToBe = (expr, answer) =>
|
|
||||||
Only.test(expr, () => expectParseOuterToBe(expr, answer))
|
|
||||||
let testParsePartialToBe = (expr, answer) =>
|
|
||||||
Only.test(expr, () => expectParsePartialToBe(expr, answer))
|
|
||||||
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
|
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
|
||||||
Only.test(expr, () =>
|
|
||||||
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
|
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||||
|
module Macro = Reducer_Expression_Macro
|
||||||
|
module T = Reducer_Expression_T
|
||||||
|
|
||||||
|
let testMacro_ = (
|
||||||
|
tester,
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedCode: string,
|
||||||
|
) => {
|
||||||
|
let bindings = Belt.Map.String.fromArray(bindArray)
|
||||||
|
tester(expr->T.toString, () =>
|
||||||
|
expr
|
||||||
|
->Macro.expandMacroCall(
|
||||||
|
bindings,
|
||||||
|
ExpressionValue.defaultEnvironment,
|
||||||
|
Expression.reduceExpression,
|
||||||
|
)
|
||||||
|
->ExpressionWithContext.toStringResult
|
||||||
|
->expect
|
||||||
|
->toEqual(expectedCode)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let testMacroEval_ = (
|
||||||
|
tester,
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedValue: string,
|
||||||
|
) => {
|
||||||
|
let bindings = Belt.Map.String.fromArray(bindArray)
|
||||||
|
tester(expr->T.toString, () =>
|
||||||
|
expr
|
||||||
|
->Macro.doMacroCall(bindings, ExpressionValue.defaultEnvironment, Expression.reduceExpression)
|
||||||
|
->ExpressionValue.toStringResult
|
||||||
|
->expect
|
||||||
|
->toEqual(expectedValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let testMacro = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedExpr: string,
|
||||||
|
) => testMacro_(test, bindArray, expr, expectedExpr)
|
||||||
|
let testMacroEval = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedValue: string,
|
||||||
|
) => testMacroEval_(test, bindArray, expr, expectedValue)
|
||||||
|
|
||||||
|
module MySkip = {
|
||||||
|
let testMacro = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedExpr: string,
|
||||||
|
) => testMacro_(Skip.test, bindArray, expr, expectedExpr)
|
||||||
|
let testMacroEval = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedValue: string,
|
||||||
|
) => testMacroEval_(Skip.test, bindArray, expr, expectedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
module MyOnly = {
|
||||||
|
let testMacro = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedExpr: string,
|
||||||
|
) => testMacro_(Only.test, bindArray, expr, expectedExpr)
|
||||||
|
let testMacroEval = (
|
||||||
|
bindArray: array<(string, ExpressionValue.expressionValue)>,
|
||||||
|
expr: T.expression,
|
||||||
|
expectedValue: string,
|
||||||
|
) => testMacroEval_(Only.test, bindArray, expr, expectedValue)
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
/*
|
||||||
|
You can wrap around any expression with inspect(expr) to log the value of that expression.
|
||||||
|
This is useful for debugging. inspect(expr) returns the value of expr, but also prints it out.
|
||||||
|
|
||||||
|
There is a second version of inspect that takes a label, which will print out the label and the value.
|
||||||
|
|
||||||
|
inspectPerformace(expr, label) will print out the value of expr, the label, and the time it took to evaluate expr.
|
||||||
|
*/
|
||||||
|
describe("Debugging", () => {
|
||||||
|
testEvalToBe("inspect(1)", "Ok(1)")
|
||||||
|
testEvalToBe("inspect(1, \"one\")", "Ok(1)")
|
||||||
|
testEvalToBe("inspectPerformance(1, \"one\")", "Ok(1)")
|
||||||
|
})
|
|
@ -1,60 +1,63 @@
|
||||||
|
// TODO: Reimplement with usual parse
|
||||||
open Jest
|
open Jest
|
||||||
open Reducer_TestHelpers
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
describe("Parse for Bindings", () => {
|
// describe("Parse for Bindings", () => {
|
||||||
testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))")
|
// testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))")
|
||||||
testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))")
|
// testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))")
|
||||||
testParseOuterToBe(
|
// testParseOuterToBe(
|
||||||
"y = x+1; y",
|
// "y = x+1; y",
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
|
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
|
||||||
)
|
// )
|
||||||
})
|
// })
|
||||||
|
|
||||||
describe("Parse Partial", () => {
|
// describe("Parse Partial", () => {
|
||||||
testParsePartialToBe(
|
// testParsePartialToBe(
|
||||||
"x",
|
// "x",
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
|
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
|
||||||
)
|
// )
|
||||||
testParsePartialToBe(
|
// testParsePartialToBe(
|
||||||
"y=x",
|
// "y=x",
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))",
|
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))",
|
||||||
)
|
// )
|
||||||
testParsePartialToBe(
|
// testParsePartialToBe(
|
||||||
"y=x+1",
|
// "y=x+1",
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))",
|
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))",
|
||||||
)
|
// )
|
||||||
testParsePartialToBe(
|
// testParsePartialToBe(
|
||||||
"y = x+1; z = y",
|
// "y = x+1; z = y",
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))",
|
// "Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))",
|
||||||
)
|
// )
|
||||||
})
|
// })
|
||||||
|
|
||||||
describe("Eval with Bindings", () => {
|
describe("Eval with Bindings", () => {
|
||||||
testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)")
|
testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)")
|
||||||
testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||||
|
testParseToBe("y = x+1; y", "Ok((:$$block (:$$block (:$let :y (:add :x 1)) :y)))")
|
||||||
testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||||
|
testEvalBindingsToBe("y = x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 2})")
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Partial code is a partial code fragment that is cut out from a larger code.
|
Partial code is a partial code fragment that is cut out from a larger code.
|
||||||
Therefore it does not end with an expression.
|
Therefore it does not end with an expression.
|
||||||
*/
|
*/
|
||||||
describe("Eval Partial", () => {
|
// describe("Eval Partial", () => {
|
||||||
testEvalPartialBindingsToBe(
|
// testEvalPartialBindingsToBe(
|
||||||
// A partial cannot end with an expression
|
// // A partial cannot end with an expression
|
||||||
"x",
|
// "x",
|
||||||
list{("x", ExpressionValue.EvNumber(1.))},
|
// list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
"Error(Assignment expected)",
|
// "Error(Assignment expected)",
|
||||||
)
|
// )
|
||||||
testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1, y: 1})")
|
// testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 1})")
|
||||||
testEvalPartialBindingsToBe(
|
// testEvalPartialBindingsToBe(
|
||||||
"y=x+1",
|
// "y=x+1",
|
||||||
list{("x", ExpressionValue.EvNumber(1.))},
|
// list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
"Ok({x: 1, y: 2})",
|
// "Ok({x: 1,y: 2})",
|
||||||
)
|
// )
|
||||||
testEvalPartialBindingsToBe(
|
// testEvalPartialBindingsToBe(
|
||||||
"y = x+1; z = y",
|
// "y = x+1; z = y",
|
||||||
list{("x", ExpressionValue.EvNumber(1.))},
|
// list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
"Ok({x: 1, y: 2, z: 2})",
|
// "Ok({x: 1,y: 2,z: 2})",
|
||||||
)
|
// )
|
||||||
})
|
// })
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
describe("Parse function assignment", () => {
|
||||||
|
testParseToBe("f(x)=x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block :x)))))")
|
||||||
|
testParseToBe("f(x)=2*x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block (:multiply 2 :x))))))")
|
||||||
|
//MathJs does not allow blocks in function definitions
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Evaluate function assignment", () => {
|
||||||
|
testEvalToBe("f(x)=x; f(1)", "Ok(1)")
|
||||||
|
})
|
|
@ -0,0 +1,77 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
describe("Arity check", () => {
|
||||||
|
testEvalToBe("f(x,y) = x + y; f(1,2)", "Ok(3)")
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y) = x + y; f(1)",
|
||||||
|
"Error(2 arguments expected. Instead 1 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y) = x + y; f(1,2,3)",
|
||||||
|
"Error(2 arguments expected. Instead 3 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y)=x+y; f(1,2,3,4)",
|
||||||
|
"Error(2 arguments expected. Instead 4 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y)=x+y; f(1)",
|
||||||
|
"Error(2 arguments expected. Instead 1 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y)=x(y); f(f)",
|
||||||
|
"Error(2 arguments expected. Instead 1 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))")
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x,y)=x(y); f(z)",
|
||||||
|
"Error(2 arguments expected. Instead 1 argument(s) were passed.)",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("symbol 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(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(1); f(2)", "Error(2 is not a function)")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("call and bindings", () => {
|
||||||
|
testEvalToBe("f(x)=x+1", "Ok({f: lambda(x=>internal code)})")
|
||||||
|
testEvalToBe("f(x)=x+1; f(1)", "Ok(2)")
|
||||||
|
testEvalToBe("f=1;y=2", "Ok({f: 1,y: 2})")
|
||||||
|
testEvalToBe("f(x)=x+1; y=f(1)", "Ok({f: lambda(x=>internal code),y: 2})")
|
||||||
|
testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)")
|
||||||
|
testEvalToBe("f(x)=x+1; y=f(1); z=f(1)", "Ok({f: lambda(x=>internal code),y: 2,z: 2})")
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x)=x+1; g(x)=f(x)+1",
|
||||||
|
"Ok({f: lambda(x=>internal code),g: lambda(x=>internal code)})",
|
||||||
|
)
|
||||||
|
testParseToBe(
|
||||||
|
"f=99; g(x)=f; g(2)",
|
||||||
|
"Ok((:$$block (:$$block (:$let :f 99) (:$let :g (:$$lambda [x] (:$$block :f))) (:g 2))))",
|
||||||
|
)
|
||||||
|
testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)")
|
||||||
|
testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)")
|
||||||
|
testEvalToBe(
|
||||||
|
"f(x)=x+1; g(x)=f(x)+1; y=g(2)",
|
||||||
|
"Ok({f: lambda(x=>internal code),g: lambda(x=>internal code),y: 4})",
|
||||||
|
)
|
||||||
|
testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("function trics", () => {
|
||||||
|
testParseToBe(
|
||||||
|
"f(x)=f(y)=2; f(2)",
|
||||||
|
"Ok((:$$block (:$$block (:$let :f (:$$lambda [x] (:$$block (:$let :f (:$$lambda [y] (:$$block 2)))))) (:f 2))))",
|
||||||
|
)
|
||||||
|
testEvalToBe("f(x)=f(y)=2; f(2)", "Ok({f: lambda(y=>internal code),x: 2})")
|
||||||
|
testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)")
|
||||||
|
testEvalToBe("y=2;g(x)=inspect(y)+1", "Ok({g: lambda(x=>internal code),y: 2})")
|
||||||
|
MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout?
|
||||||
|
MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters
|
||||||
|
MySkip.testEvalToBe("myadd(x,y)=x+y; z=[add]; z[0](3,2)", "????") //TODO: to fix with new parser
|
||||||
|
MySkip.testEvalToBe("myaddd(x,y)=x+y; z={x: add}; z.x(3,2)", "????") //TODO: to fix with new parser
|
||||||
|
})
|
|
@ -10,46 +10,39 @@ describe("reducer using mathjs parse", () => {
|
||||||
// Those tests toString that we are converting mathjs parse tree to what we need
|
// Those tests toString that we are converting mathjs parse tree to what we need
|
||||||
|
|
||||||
describe("expressions", () => {
|
describe("expressions", () => {
|
||||||
testParseToBe("1", "Ok(1)")
|
testParseToBe("1", "Ok((:$$block 1))")
|
||||||
testParseToBe("(1)", "Ok(1)")
|
testParseToBe("(1)", "Ok((:$$block 1))")
|
||||||
testParseToBe("1+2", "Ok((:add 1 2))")
|
testParseToBe("1+2", "Ok((:$$block (:add 1 2)))")
|
||||||
testParseToBe("1+2", "Ok((:add 1 2))")
|
testParseToBe("1+2*3", "Ok((:$$block (:add 1 (:multiply 2 3))))")
|
||||||
testParseToBe("1+2", "Ok((:add 1 2))")
|
|
||||||
testParseToBe("1+2*3", "Ok((:add 1 (:multiply 2 3)))")
|
|
||||||
})
|
})
|
||||||
describe("arrays", () => {
|
describe("arrays", () => {
|
||||||
//Note. () is a empty list in Lisp
|
//Note. () is a empty list in Lisp
|
||||||
// The only builtin structure in Lisp is list. There are no arrays
|
// The only builtin structure in Lisp is list. There are no arrays
|
||||||
// [1,2,3] becomes (1 2 3)
|
// [1,2,3] becomes (1 2 3)
|
||||||
testDescriptionParseToBe("empty", "[]", "Ok(())")
|
testDescriptionParseToBe("empty", "[]", "Ok((:$$block ()))")
|
||||||
testParseToBe("[1, 2, 3]", "Ok((1 2 3))")
|
testParseToBe("[1, 2, 3]", "Ok((:$$block (1 2 3)))")
|
||||||
testParseToBe("['hello', 'world']", "Ok(('hello' 'world'))")
|
testParseToBe("['hello', 'world']", "Ok((:$$block ('hello' 'world')))")
|
||||||
testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))")
|
testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$$block (:$atIndex (0 1 2) (1))))")
|
||||||
})
|
})
|
||||||
describe("records", () => {
|
describe("records", () => {
|
||||||
testDescriptionParseToBe("define", "{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))")
|
testDescriptionParseToBe(
|
||||||
|
"define",
|
||||||
|
"{a: 1, b: 2}",
|
||||||
|
"Ok((:$$block (:$constructRecord (('a' 1) ('b' 2)))))",
|
||||||
|
)
|
||||||
testDescriptionParseToBe(
|
testDescriptionParseToBe(
|
||||||
"use",
|
"use",
|
||||||
"{a: 1, b: 2}.a",
|
"{a: 1, b: 2}.a",
|
||||||
"Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))",
|
"Ok((:$$block (:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a'))))",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
describe("multi-line", () => {
|
describe("multi-line", () => {
|
||||||
testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))")
|
testParseToBe("1; 2", "Ok((:$$block (:$$block 1 2)))")
|
||||||
testParseToBe(
|
testParseToBe("1+1; 2+1", "Ok((:$$block (:$$block (:add 1 1) (:add 2 1))))")
|
||||||
"1+1; 2+1",
|
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))",
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
describe("assignment", () => {
|
describe("assignment", () => {
|
||||||
testParseToBe(
|
testParseToBe("x=1; x", "Ok((:$$block (:$$block (:$let :x 1) :x)))")
|
||||||
"x=1; x",
|
testParseToBe("x=1+1; x+1", "Ok((:$$block (:$$block (:$let :x (:add 1 1)) (:add :x 1))))")
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))",
|
|
||||||
)
|
|
||||||
testParseToBe(
|
|
||||||
"x=1+1; x+1",
|
|
||||||
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))",
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -70,13 +63,13 @@ describe("eval", () => {
|
||||||
})
|
})
|
||||||
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])")
|
||||||
testEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])")
|
testEvalToBe("['hello', 'world']", "Ok(['hello','world'])")
|
||||||
testEvalToBe("([0,1,2])[1]", "Ok(1)")
|
testEvalToBe("([0,1,2])[1]", "Ok(1)")
|
||||||
testDescriptionEvalToBe("index not found", "([0,1,2])[10]", "Error(Array index not found: 10)")
|
testDescriptionEvalToBe("index not found", "([0,1,2])[10]", "Error(Array index not found: 10)")
|
||||||
})
|
})
|
||||||
describe("records", () => {
|
describe("records", () => {
|
||||||
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})"))
|
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1,b: 2})"))
|
||||||
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
|
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
|
||||||
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
|
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
|
||||||
})
|
})
|
||||||
|
@ -91,7 +84,7 @@ describe("eval", () => {
|
||||||
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
|
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
|
||||||
testEvalToBe("1; x=1", "Error(Assignment expected)")
|
testEvalToBe("1; x=1", "Error(Assignment expected)")
|
||||||
testEvalToBe("1; 1", "Error(Assignment expected)")
|
testEvalToBe("1; 1", "Error(Assignment expected)")
|
||||||
testEvalToBe("x=1; x=1", "Error(Expression expected)")
|
testEvalToBe("x=1; x=1", "Ok({x: 1})")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -119,27 +119,34 @@ describe("eval on distribution functions", () => {
|
||||||
|
|
||||||
describe("parse on distribution functions", () => {
|
describe("parse on distribution functions", () => {
|
||||||
describe("power", () => {
|
describe("power", () => {
|
||||||
testParse("normal(5,2) ^ normal(5,1)", "Ok((:pow (:normal 5 2) (:normal 5 1)))")
|
testParse("normal(5,2) ^ normal(5,1)", "Ok((:$$block (:pow (:normal 5 2) (:normal 5 1))))")
|
||||||
testParse("3 ^ normal(5,1)", "Ok((:pow 3 (:normal 5 1)))")
|
testParse("3 ^ normal(5,1)", "Ok((:$$block (:pow 3 (:normal 5 1))))")
|
||||||
testParse("normal(5,2) ^ 3", "Ok((:pow (:normal 5 2) 3))")
|
testParse("normal(5,2) ^ 3", "Ok((:$$block (:pow (:normal 5 2) 3)))")
|
||||||
})
|
})
|
||||||
describe("subtraction", () => {
|
describe("subtraction", () => {
|
||||||
testParse("10 - normal(5,1)", "Ok((:subtract 10 (:normal 5 1)))")
|
testParse("10 - normal(5,1)", "Ok((:$$block (:subtract 10 (:normal 5 1))))")
|
||||||
testParse("normal(5,1) - 10", "Ok((:subtract (:normal 5 1) 10))")
|
testParse("normal(5,1) - 10", "Ok((:$$block (: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((:dotSubtract (:normal 5 2) (:normal 5 1)))",
|
"Ok((:$$block (:dotSubtract (:normal 5 2) (:normal 5 1))))",
|
||||||
|
// TODO: !!! returns "Ok((:$$block (:dotPow (:normal 5 2) (:normal 5 1))))"
|
||||||
)
|
)
|
||||||
testParse("normal(5,2) .* normal(5,1)", "Ok((:dotMultiply (:normal 5 2) (:normal 5 1)))")
|
testParse(
|
||||||
testParse("normal(5,2) ./ normal(5,1)", "Ok((:dotDivide (:normal 5 2) (:normal 5 1)))")
|
"normal(5,2) .* normal(5,1)",
|
||||||
testParse("normal(5,2) .^ normal(5,1)", "Ok((:dotPow (:normal 5 2) (:normal 5 1)))")
|
"Ok((:$$block (:dotMultiply (:normal 5 2) (:normal 5 1))))",
|
||||||
|
)
|
||||||
|
testParse(
|
||||||
|
"normal(5,2) ./ normal(5,1)",
|
||||||
|
"Ok((:$$block (:dotDivide (:normal 5 2) (:normal 5 1))))",
|
||||||
|
)
|
||||||
|
testParse("normal(5,2) .^ normal(5,1)", "Ok((:$$block (:dotPow (:normal 5 2) (:normal 5 1))))")
|
||||||
})
|
})
|
||||||
describe("equality", () => {
|
describe("equality", () => {
|
||||||
testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))")
|
testParse("5 == normal(5,2)", "Ok((:$$block (: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)))")
|
||||||
|
|
|
@ -3,9 +3,9 @@ open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
describe("ExpressionValue", () => {
|
describe("ExpressionValue", () => {
|
||||||
test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1, 'a'"))
|
test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1,'a'"))
|
||||||
|
|
||||||
test("toStringFunctionCall", () =>
|
test("toStringFunctionCall", () =>
|
||||||
expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1, 'a')")
|
expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
"homepage": "https://squiggle-language.com",
|
"homepage": "https://squiggle-language.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rescript build -with-deps && tsc",
|
"build": "yarn build:rescript && yarn build:typescript",
|
||||||
|
"build:rescript": "rescript build -with-deps",
|
||||||
|
"build:typescript": "tsc",
|
||||||
"bundle": "webpack",
|
"bundle": "webpack",
|
||||||
"start": "rescript build -w -with-deps",
|
"start": "rescript build -w -with-deps",
|
||||||
"clean": "rescript clean && rm -r dist",
|
"clean": "rescript clean && rm -r dist",
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
genericDist,
|
genericDist,
|
||||||
continuousShape,
|
continuousShape,
|
||||||
discreteShape,
|
discreteShape,
|
||||||
samplingParams,
|
environment,
|
||||||
distributionError,
|
distributionError,
|
||||||
toPointSet,
|
toPointSet,
|
||||||
distributionErrorToString,
|
distributionErrorToString,
|
||||||
|
@ -51,9 +51,9 @@ export type shape = {
|
||||||
|
|
||||||
export class Distribution {
|
export class Distribution {
|
||||||
t: genericDist;
|
t: genericDist;
|
||||||
env: samplingParams;
|
env: environment;
|
||||||
|
|
||||||
constructor(t: genericDist, env: samplingParams) {
|
constructor(t: genericDist, env: environment) {
|
||||||
this.t = t;
|
this.t = t;
|
||||||
this.env = env;
|
this.env = env;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import * as _ from "lodash";
|
import * as _ from "lodash";
|
||||||
import {
|
import {
|
||||||
samplingParams,
|
samplingParams,
|
||||||
evaluateUsingExternalBindings,
|
environment,
|
||||||
|
defaultEnvironment,
|
||||||
evaluatePartialUsingExternalBindings,
|
evaluatePartialUsingExternalBindings,
|
||||||
|
evaluateUsingOptions,
|
||||||
externalBindings,
|
externalBindings,
|
||||||
expressionValue,
|
expressionValue,
|
||||||
errorValue,
|
errorValue,
|
||||||
|
@ -39,33 +41,34 @@ export let defaultSamplingInputs: samplingParams = {
|
||||||
export function run(
|
export function run(
|
||||||
squiggleString: string,
|
squiggleString: string,
|
||||||
bindings?: externalBindings,
|
bindings?: externalBindings,
|
||||||
samplingInputs?: samplingParams,
|
environment?: environment,
|
||||||
imports?: jsImports
|
imports?: jsImports
|
||||||
): result<squiggleExpression, errorValue> {
|
): result<squiggleExpression, errorValue> {
|
||||||
let b = bindings ? bindings : defaultBindings;
|
let b = bindings ? bindings : defaultBindings;
|
||||||
let i = imports ? imports : defaultImports;
|
let i = imports ? imports : defaultImports;
|
||||||
let si: samplingParams = samplingInputs
|
let e = environment ? environment : defaultEnvironment;
|
||||||
? samplingInputs
|
let res: result<expressionValue, errorValue> = evaluateUsingOptions(
|
||||||
: defaultSamplingInputs;
|
{ externalBindings: mergeImports(b, i), environment: e },
|
||||||
|
squiggleString
|
||||||
let result: result<expressionValue, errorValue> =
|
);
|
||||||
evaluateUsingExternalBindings(squiggleString, mergeImports(b, i));
|
return resultMap(res, (x) => createTsExport(x, e));
|
||||||
return resultMap(result, (x) => createTsExport(x, si));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Partial. A partial is a block of code that doesn't return a value
|
// Run Partial. A partial is a block of code that doesn't return a value
|
||||||
export function runPartial(
|
export function runPartial(
|
||||||
squiggleString: string,
|
squiggleString: string,
|
||||||
bindings?: externalBindings,
|
bindings?: externalBindings,
|
||||||
_samplingInputs?: samplingParams,
|
environment?: environment,
|
||||||
imports?: jsImports
|
imports?: jsImports
|
||||||
): result<externalBindings, errorValue> {
|
): result<externalBindings, errorValue> {
|
||||||
let b = bindings ? bindings : defaultBindings;
|
let b = bindings ? bindings : defaultBindings;
|
||||||
let i = imports ? imports : defaultImports;
|
let i = imports ? imports : defaultImports;
|
||||||
|
let e = environment ? environment : defaultEnvironment;
|
||||||
|
|
||||||
return evaluatePartialUsingExternalBindings(
|
return evaluatePartialUsingExternalBindings(
|
||||||
squiggleString,
|
squiggleString,
|
||||||
mergeImports(b, i)
|
mergeImports(b, i),
|
||||||
|
e
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +92,7 @@ export let defaultBindings: externalBindings = {};
|
||||||
|
|
||||||
function createTsExport(
|
function createTsExport(
|
||||||
x: expressionValue,
|
x: expressionValue,
|
||||||
sampEnv: samplingParams
|
environment: environment
|
||||||
): squiggleExpression {
|
): squiggleExpression {
|
||||||
switch (x.tag) {
|
switch (x.tag) {
|
||||||
case "EvArray":
|
case "EvArray":
|
||||||
|
@ -108,7 +111,10 @@ function createTsExport(
|
||||||
return tag(
|
return tag(
|
||||||
"record",
|
"record",
|
||||||
_.mapValues(arrayItem.value, (recordValue: unknown) =>
|
_.mapValues(arrayItem.value, (recordValue: unknown) =>
|
||||||
convertRawToTypescript(recordValue as rescriptExport, sampEnv)
|
convertRawToTypescript(
|
||||||
|
recordValue as rescriptExport,
|
||||||
|
environment
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
case "EvArray":
|
case "EvArray":
|
||||||
|
@ -116,20 +122,24 @@ function createTsExport(
|
||||||
return tag(
|
return tag(
|
||||||
"array",
|
"array",
|
||||||
y.map((childArrayItem) =>
|
y.map((childArrayItem) =>
|
||||||
convertRawToTypescript(childArrayItem, sampEnv)
|
convertRawToTypescript(childArrayItem, environment)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return createTsExport(arrayItem, sampEnv);
|
return createTsExport(arrayItem, environment);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
case "EvArrayString":
|
||||||
|
return tag("arraystring", x.value);
|
||||||
case "EvBool":
|
case "EvBool":
|
||||||
return tag("boolean", x.value);
|
return tag("boolean", x.value);
|
||||||
case "EvCall":
|
case "EvCall":
|
||||||
return tag("call", x.value);
|
return tag("call", x.value);
|
||||||
|
case "EvLambda":
|
||||||
|
return tag("lambda", x.value);
|
||||||
case "EvDistribution":
|
case "EvDistribution":
|
||||||
return tag("distribution", new Distribution(x.value, sampEnv));
|
return tag("distribution", new Distribution(x.value, environment));
|
||||||
case "EvNumber":
|
case "EvNumber":
|
||||||
return tag("number", x.value);
|
return tag("number", x.value);
|
||||||
case "EvRecord":
|
case "EvRecord":
|
||||||
|
@ -137,7 +147,7 @@ function createTsExport(
|
||||||
let result: tagged<"record", { [key: string]: squiggleExpression }> = tag(
|
let result: tagged<"record", { [key: string]: squiggleExpression }> = tag(
|
||||||
"record",
|
"record",
|
||||||
_.mapValues(x.value, (x: unknown) =>
|
_.mapValues(x.value, (x: unknown) =>
|
||||||
convertRawToTypescript(x as rescriptExport, sampEnv)
|
convertRawToTypescript(x as rescriptExport, environment)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -3,10 +3,11 @@ import {
|
||||||
mixedShape,
|
mixedShape,
|
||||||
sampleSetDist,
|
sampleSetDist,
|
||||||
genericDist,
|
genericDist,
|
||||||
samplingParams,
|
environment,
|
||||||
symbolicDist,
|
symbolicDist,
|
||||||
discreteShape,
|
discreteShape,
|
||||||
continuousShape,
|
continuousShape,
|
||||||
|
lambdaValue,
|
||||||
} from "../rescript/TypescriptInterface.gen";
|
} from "../rescript/TypescriptInterface.gen";
|
||||||
import { Distribution } from "./distribution";
|
import { Distribution } from "./distribution";
|
||||||
import { tagged, tag } from "./types";
|
import { tagged, tag } from "./types";
|
||||||
|
@ -19,31 +20,39 @@ export type rescriptExport =
|
||||||
_0: rescriptExport[];
|
_0: rescriptExport[];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 1; // EvBool
|
TAG: 1; // EvString
|
||||||
|
_0: string[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
TAG: 2; // EvBool
|
||||||
_0: boolean;
|
_0: boolean;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 2; // EvCall
|
TAG: 3; // EvCall
|
||||||
_0: string;
|
_0: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 3; // EvDistribution
|
TAG: 4; // EvDistribution
|
||||||
_0: rescriptDist;
|
_0: rescriptDist;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 4; // EvNumber
|
TAG: 5; // EvLambda
|
||||||
|
_0: lambdaValue;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
TAG: 6; // EvNumber
|
||||||
_0: number;
|
_0: number;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 5; // EvRecord
|
TAG: 7; // EvRecord
|
||||||
_0: { [key: string]: rescriptExport };
|
_0: { [key: string]: rescriptExport };
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 6; // EvString
|
TAG: 8; // EvString
|
||||||
_0: string;
|
_0: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
TAG: 7; // EvSymbol
|
TAG: 9; // EvSymbol
|
||||||
_0: string;
|
_0: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,7 +79,9 @@ export type squiggleExpression =
|
||||||
| tagged<"symbol", string>
|
| tagged<"symbol", string>
|
||||||
| tagged<"string", string>
|
| tagged<"string", string>
|
||||||
| tagged<"call", string>
|
| tagged<"call", string>
|
||||||
|
| tagged<"lambda", lambdaValue>
|
||||||
| tagged<"array", squiggleExpression[]>
|
| tagged<"array", squiggleExpression[]>
|
||||||
|
| tagged<"arraystring", string[]>
|
||||||
| tagged<"boolean", boolean>
|
| tagged<"boolean", boolean>
|
||||||
| tagged<"distribution", Distribution>
|
| tagged<"distribution", Distribution>
|
||||||
| tagged<"number", number>
|
| tagged<"number", number>
|
||||||
|
@ -78,36 +89,40 @@ export type squiggleExpression =
|
||||||
|
|
||||||
export function convertRawToTypescript(
|
export function convertRawToTypescript(
|
||||||
result: rescriptExport,
|
result: rescriptExport,
|
||||||
sampEnv: samplingParams
|
environment: environment
|
||||||
): squiggleExpression {
|
): squiggleExpression {
|
||||||
switch (result.TAG) {
|
switch (result.TAG) {
|
||||||
case 0: // EvArray
|
case 0: // EvArray
|
||||||
return tag(
|
return tag(
|
||||||
"array",
|
"array",
|
||||||
result._0.map((x) => convertRawToTypescript(x, sampEnv))
|
result._0.map((x) => convertRawToTypescript(x, environment))
|
||||||
);
|
);
|
||||||
case 1: // EvBool
|
case 1: // EvArrayString
|
||||||
|
return tag("arraystring", result._0);
|
||||||
|
case 2: // EvBool
|
||||||
return tag("boolean", result._0);
|
return tag("boolean", result._0);
|
||||||
case 2: // EvCall
|
case 3: // EvCall
|
||||||
return tag("call", result._0);
|
return tag("call", result._0);
|
||||||
case 3: // EvDistribution
|
case 4: // EvDistribution
|
||||||
return tag(
|
return tag(
|
||||||
"distribution",
|
"distribution",
|
||||||
new Distribution(
|
new Distribution(
|
||||||
convertRawDistributionToGenericDist(result._0),
|
convertRawDistributionToGenericDist(result._0),
|
||||||
sampEnv
|
environment
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
case 4: // EvNumber
|
case 5: // EvDistribution
|
||||||
|
return tag("lambda", result._0);
|
||||||
|
case 6: // EvNumber
|
||||||
return tag("number", result._0);
|
return tag("number", result._0);
|
||||||
case 5: // EvRecord
|
case 7: // EvRecord
|
||||||
return tag(
|
return tag(
|
||||||
"record",
|
"record",
|
||||||
_.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv))
|
_.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
|
||||||
);
|
);
|
||||||
case 6: // EvString
|
case 8: // EvString
|
||||||
return tag("string", result._0);
|
return tag("string", result._0);
|
||||||
case 7: // EvSymbol
|
case 9: // EvSymbol
|
||||||
return tag("symbol", result._0);
|
return tag("symbol", result._0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,15 +156,15 @@ export type jsValue =
|
||||||
|
|
||||||
export function jsValueToBinding(value: jsValue): rescriptExport {
|
export function jsValueToBinding(value: jsValue): rescriptExport {
|
||||||
if (typeof value === "boolean") {
|
if (typeof value === "boolean") {
|
||||||
return { TAG: 1, _0: value as boolean };
|
return { TAG: 2, _0: value as boolean };
|
||||||
} else if (typeof value === "string") {
|
} else if (typeof value === "string") {
|
||||||
return { TAG: 6, _0: value as string };
|
return { TAG: 8, _0: value as string };
|
||||||
} else if (typeof value === "number") {
|
} else if (typeof value === "number") {
|
||||||
return { TAG: 4, _0: value as number };
|
return { TAG: 6, _0: value as number };
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
return { TAG: 0, _0: value.map(jsValueToBinding) };
|
return { TAG: 0, _0: value.map(jsValueToBinding) };
|
||||||
} else {
|
} else {
|
||||||
// Record
|
// Record
|
||||||
return { TAG: 5, _0: _.mapValues(value, jsValueToBinding) };
|
return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,11 @@ type env = {
|
||||||
xyPointLength: int,
|
xyPointLength: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultEnv = {
|
||||||
|
sampleCount: 10000,
|
||||||
|
xyPointLength: 10000,
|
||||||
|
}
|
||||||
|
|
||||||
type outputType =
|
type outputType =
|
||||||
| Dist(genericDist)
|
| Dist(genericDist)
|
||||||
| Float(float)
|
| Float(float)
|
||||||
|
|
|
@ -4,6 +4,9 @@ type env = {
|
||||||
xyPointLength: int,
|
xyPointLength: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let defaultEnv: env
|
||||||
|
|
||||||
open DistributionTypes
|
open DistributionTypes
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
module Dispatch = Reducer_Dispatch
|
|
||||||
module ErrorValue = Reducer_ErrorValue
|
module ErrorValue = Reducer_ErrorValue
|
||||||
module Expression = Reducer_Expression
|
module Expression = Reducer_Expression
|
||||||
module Extra = Reducer_Extra
|
|
||||||
module Js = Reducer_Js
|
|
||||||
module MathJs = Reducer_MathJs
|
|
||||||
|
|
||||||
type expressionValue = Reducer_Expression.expressionValue
|
type environment = ReducerInterface_ExpressionValue.environment
|
||||||
type externalBindings = Expression.externalBindings
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
let evaluate = Expression.eval
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
let evaluateUsingExternalBindings = Expression.evalUsingExternalBindings
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
let evaluatePartialUsingExternalBindings = Expression.evalPartialUsingExternalBindings
|
let evaluate = Expression.evaluate
|
||||||
|
let evaluateUsingOptions = Expression.evaluateUsingOptions
|
||||||
|
let evaluatePartialUsingExternalBindings = Expression.evaluatePartialUsingExternalBindings
|
||||||
let parse = Expression.parse
|
let parse = Expression.parse
|
||||||
let parseOuter = Expression.parseOuter
|
|
||||||
let parsePartial = Expression.parsePartial
|
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
module Dispatch = Reducer_Dispatch
|
|
||||||
module ErrorValue = Reducer_ErrorValue
|
module ErrorValue = Reducer_ErrorValue
|
||||||
module Expression = Reducer_Expression
|
module Expression = Reducer_Expression
|
||||||
module Extra = Reducer_Extra
|
|
||||||
module Js = Reducer_Js
|
|
||||||
module MathJs = Reducer_MathJs
|
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type environment = ReducerInterface_ExpressionValue.environment
|
||||||
|
@genType
|
||||||
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
@genType
|
@genType
|
||||||
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
@genType
|
@genType
|
||||||
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>
|
let evaluateUsingOptions: (
|
||||||
@genType
|
~environment: option<QuriSquiggleLang.ReducerInterface_ExpressionValue.environment>,
|
||||||
let evaluateUsingExternalBindings: (
|
~externalBindings: option<QuriSquiggleLang.ReducerInterface_ExpressionValue.externalBindings>,
|
||||||
string,
|
string,
|
||||||
externalBindings,
|
) => result<expressionValue, errorValue>
|
||||||
) => result<expressionValue, Reducer_ErrorValue.errorValue>
|
|
||||||
@genType
|
@genType
|
||||||
let evaluatePartialUsingExternalBindings: (
|
let evaluatePartialUsingExternalBindings: (
|
||||||
string,
|
string,
|
||||||
externalBindings,
|
QuriSquiggleLang.ReducerInterface_ExpressionValue.externalBindings,
|
||||||
) => result<externalBindings, Reducer_ErrorValue.errorValue>
|
QuriSquiggleLang.ReducerInterface_ExpressionValue.environment,
|
||||||
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
) => result<externalBindings, errorValue>
|
||||||
let parseOuter: string => result<Expression.expression, ErrorValue.errorValue>
|
@genType
|
||||||
let parsePartial: string => result<Expression.expression, ErrorValue.errorValue>
|
let evaluate: string => result<expressionValue, errorValue>
|
||||||
|
|
||||||
|
let parse: string => result<Expression.expression, errorValue>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
open ReducerInterface.ExpressionValue
|
open ReducerInterface.ExpressionValue
|
||||||
open Reducer_ErrorValue
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ open Reducer_ErrorValue
|
||||||
|
|
||||||
exception TestRescriptException
|
exception TestRescriptException
|
||||||
|
|
||||||
let callInternal = (call: functionCall): result<'b, errorValue> => {
|
let callInternal = (call: functionCall, _environment): result<'b, errorValue> => {
|
||||||
let callMathJs = (call: functionCall): result<'b, errorValue> =>
|
let callMathJs = (call: functionCall): result<'b, errorValue> =>
|
||||||
switch call {
|
switch call {
|
||||||
| ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
|
| ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
|
||||||
|
@ -20,12 +21,12 @@ let callInternal = (call: functionCall): result<'b, errorValue> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let constructRecord = arrayOfPairs => {
|
let constructRecord = arrayOfPairs => {
|
||||||
Belt.Array.map(arrayOfPairs, pairValue => {
|
Belt.Array.map(arrayOfPairs, pairValue =>
|
||||||
switch pairValue {
|
switch pairValue {
|
||||||
| EvArray([EvString(key), valueValue]) => (key, valueValue)
|
| EvArray([EvString(key), valueValue]) => (key, valueValue)
|
||||||
| _ => ("wrong key type", pairValue->toStringWithType->EvString)
|
| _ => ("wrong key type", pairValue->toStringWithType->EvString)
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
->Js.Dict.fromArray
|
->Js.Dict.fromArray
|
||||||
->EvRecord
|
->EvRecord
|
||||||
->Ok
|
->Ok
|
||||||
|
@ -43,16 +44,58 @@ let callInternal = (call: functionCall): result<'b, errorValue> => {
|
||||||
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
|
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let inspect = (value: expressionValue) => {
|
||||||
|
Js.log(value->toString)
|
||||||
|
value->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let inspectLabel = (value: expressionValue, label: string) => {
|
||||||
|
Js.log(`${label}: ${value->toString}`)
|
||||||
|
value->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: This function is cancelled. The related issue is
|
||||||
|
https://github.com/webpack/webpack/issues/13435
|
||||||
|
*/
|
||||||
|
let inspectPerformance = (value: expressionValue, label: string) => {
|
||||||
|
// let _ = %raw("{performance} = require('perf_hooks')")
|
||||||
|
// let start = %raw(`performance.now()`)
|
||||||
|
// let finish = %raw(`performance.now()`)
|
||||||
|
// let performance = finish - start
|
||||||
|
// Js.log(`${label}: ${value->toString} performance: ${Js.String.make(performance)}ms`)
|
||||||
|
// TODO find a way of failing the hook gracefully, also needs a block parameter
|
||||||
|
Js.log(`${label}: ${value->toString}`)
|
||||||
|
value->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let doSetBindings = (
|
||||||
|
externalBindings: externalBindings,
|
||||||
|
symbol: string,
|
||||||
|
value: expressionValue,
|
||||||
|
) => {
|
||||||
|
Bindings.fromExternalBindings(externalBindings)
|
||||||
|
->Belt.Map.String.set(symbol, value)
|
||||||
|
->Bindings.toExternalBindings
|
||||||
|
->EvRecord
|
||||||
|
->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
// | ("$constructRecord", pairArray)
|
|
||||||
// | ("$atIndex", [EvArray(anArray), EvNumber(fIndex)]) => arrayAtIndex(anArray, fIndex)
|
|
||||||
// | ("$atIndex", [EvRecord(aRecord), EvString(sIndex)]) => recordAtIndex(aRecord, sIndex)
|
|
||||||
| ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
|
||||||
| ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) =>
|
| ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) =>
|
||||||
arrayAtIndex(aValueArray, fIndex)
|
arrayAtIndex(aValueArray, fIndex)
|
||||||
| ("$atIndex", [EvRecord(dict), EvArray([EvString(sIndex)])]) => recordAtIndex(dict, sIndex)
|
| ("$atIndex", [EvRecord(dict), EvArray([EvString(sIndex)])]) => recordAtIndex(dict, sIndex)
|
||||||
| ("$atIndex", [obj, index]) =>
|
| ("$atIndex", [obj, index]) =>
|
||||||
(toStringWithType(obj) ++ "??~~~~" ++ toStringWithType(index))->EvString->Ok
|
(toStringWithType(obj) ++ "??~~~~" ++ toStringWithType(index))->EvString->Ok
|
||||||
|
| ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
||||||
|
| ("inspect", [value, EvString(label)]) => inspectLabel(value, label)
|
||||||
|
| ("inspect", [value]) => inspect(value)
|
||||||
|
| ("inspectPerformance", [value, EvString(label)]) => inspectPerformance(value, label)
|
||||||
|
| ("$setBindings", [EvRecord(externalBindings), EvSymbol(symbol), value]) =>
|
||||||
|
doSetBindings(externalBindings, symbol, value)
|
||||||
|
| ("$exportBindings", [EvRecord(externalBindings)]) => doExportBindings(externalBindings)
|
||||||
| call => callMathJs(call)
|
| call => callMathJs(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,12 +103,12 @@ let callInternal = (call: functionCall): result<'b, errorValue> => {
|
||||||
/*
|
/*
|
||||||
Reducer uses Result monad while reducing expressions
|
Reducer uses Result monad while reducing expressions
|
||||||
*/
|
*/
|
||||||
let dispatch = (call: functionCall): result<expressionValue, errorValue> =>
|
let dispatch = (call: functionCall, environment): result<expressionValue, errorValue> =>
|
||||||
try {
|
try {
|
||||||
let (fn, args) = call
|
let (fn, args) = call
|
||||||
// There is a bug that prevents string match in patterns
|
// There is a bug that prevents string match in patterns
|
||||||
// So we have to recreate a copy of the string
|
// So we have to recreate a copy of the string
|
||||||
ExternalLibrary.dispatch((Js.String.make(fn), args), callInternal)
|
ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternal)
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
| Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
| _ => RETodo("unhandled rescript exception")->Error
|
| _ => RETodo("unhandled rescript exception")->Error
|
||||||
|
|
|
@ -3,120 +3,170 @@
|
||||||
they take expressions as parameters and return a new expression.
|
they take expressions as parameters and return a new expression.
|
||||||
Macros are used to define language building blocks. They are like Lisp macros.
|
Macros are used to define language building blocks. They are like Lisp macros.
|
||||||
*/
|
*/
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module ExpressionValue = ReducerInterface.ExpressionValue
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
|
open Reducer_Expression_ExpressionBuilder
|
||||||
|
|
||||||
open Reducer_ErrorValue
|
type environment = ExpressionValue.environment
|
||||||
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
type expression = ExpressionT.expression
|
type expression = ExpressionT.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
type reducerFn = (
|
type expressionWithContext = ExpressionWithContext.expressionWithContext
|
||||||
expression,
|
|
||||||
ExpressionT.bindings,
|
|
||||||
) => result<ExpressionValue.expressionValue, errorValue>
|
|
||||||
|
|
||||||
let dispatchMacroCall = (
|
let dispatchMacroCall = (
|
||||||
list: list<expression>,
|
macroExpression: expression,
|
||||||
bindings: ExpressionT.bindings,
|
bindings: ExpressionT.bindings,
|
||||||
reduceExpression: reducerFn,
|
environment,
|
||||||
): result<expression, 'e> => {
|
reduceExpression: ExpressionT.reducerFn,
|
||||||
let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result<
|
): result<expressionWithContext, errorValue> => {
|
||||||
expression,
|
let doBindStatement = (bindingExpr: expression, statement: expression, environment) =>
|
||||||
errorValue,
|
|
||||||
> =>
|
|
||||||
switch expression {
|
|
||||||
| ExpressionT.EValue(EvSymbol(aSymbol)) =>
|
|
||||||
switch bindings->Belt.Map.String.get(aSymbol) {
|
|
||||||
| Some(boundExpression) => boundExpression->Ok
|
|
||||||
| None => RESymbolNotFound(aSymbol)->Error
|
|
||||||
}
|
|
||||||
| ExpressionT.EValue(_) => expression->Ok
|
|
||||||
| ExpressionT.EBindings(_) => expression->Ok
|
|
||||||
| ExpressionT.EList(list) => {
|
|
||||||
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
|
|
||||||
racc->Result.flatMap(acc => {
|
|
||||||
each
|
|
||||||
->replaceSymbols(bindings)
|
|
||||||
->Result.flatMap(newNode => {
|
|
||||||
acc->Belt.List.add(newNode)->Ok
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
racc->Result.map(acc => acc->ExpressionT.EList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let doBindStatement = (statement: expression, bindings: ExpressionT.bindings) => {
|
|
||||||
switch statement {
|
switch statement {
|
||||||
| ExpressionT.EList(list{
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
|
||||||
ExpressionT.EValue(EvCall("$let")),
|
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
||||||
ExpressionT.EValue(EvSymbol(aSymbol)),
|
|
||||||
expressionToReduce,
|
|
||||||
}) => {
|
|
||||||
let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings)
|
|
||||||
|
|
||||||
let rNewValue =
|
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
||||||
rNewExpressionToReduce->Result.flatMap(newExpressionToReduce =>
|
let newBindings = Bindings.fromValue(externalBindingsValue)
|
||||||
reduceExpression(newExpressionToReduce, bindings)
|
|
||||||
|
// Js.log(
|
||||||
|
// `bindStatement ${Bindings.toString(newBindings)}<==${ExpressionT.toString(
|
||||||
|
// bindingExpr,
|
||||||
|
// )} statement: $let ${ExpressionT.toString(symbolExpr)}=${ExpressionT.toString(
|
||||||
|
// statement,
|
||||||
|
// )}`,
|
||||||
|
// )
|
||||||
|
|
||||||
|
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
||||||
|
rNewStatement->Result.map(newStatement =>
|
||||||
|
ExpressionWithContext.withContext(
|
||||||
|
eFunction(
|
||||||
|
"$setBindings",
|
||||||
|
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
|
||||||
|
),
|
||||||
|
newBindings,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
})
|
||||||
let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue))
|
|
||||||
rNewExpression->Result.map(newExpression =>
|
|
||||||
Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
| _ => REAssignmentExpected->Error
|
| _ => REAssignmentExpected->Error
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let doExportVariableExpression = (bindings: ExpressionT.bindings) => {
|
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result<
|
||||||
let emptyDictionary: Js.Dict.t<ExpressionValue.expressionValue> = Js.Dict.empty()
|
expressionWithContext,
|
||||||
let reducedBindings = bindings->Belt.Map.String.keep((_key, value) =>
|
errorValue,
|
||||||
switch value {
|
> =>
|
||||||
| ExpressionT.EValue(_) => true
|
switch statement {
|
||||||
| _ => false
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
|
||||||
}
|
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
||||||
)
|
|
||||||
let externalBindings = reducedBindings->Belt.Map.String.reduce(emptyDictionary, (
|
|
||||||
acc,
|
|
||||||
key,
|
|
||||||
expressionValue,
|
|
||||||
) => {
|
|
||||||
let value = switch expressionValue {
|
|
||||||
| EValue(aValue) => aValue
|
|
||||||
| _ => EvSymbol("internal")
|
|
||||||
}
|
|
||||||
Js.Dict.set(acc, key, value)
|
|
||||||
acc
|
|
||||||
})
|
|
||||||
externalBindings->ExpressionValue.EvRecord->ExpressionT.EValue->Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) =>
|
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
||||||
switch expression {
|
let newBindings = Bindings.fromValue(externalBindingsValue)
|
||||||
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) =>
|
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
||||||
REExpressionExpected->Error
|
rNewStatement->Result.map(newStatement =>
|
||||||
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) =>
|
ExpressionWithContext.withContext(
|
||||||
doExportVariableExpression(bindings)
|
eFunction(
|
||||||
| _ => replaceSymbols(expression, bindings)
|
"$exportBindings",
|
||||||
|
list{
|
||||||
|
eFunction(
|
||||||
|
"$setBindings",
|
||||||
|
list{
|
||||||
|
newBindings->Bindings.toExternalBindings->eRecord,
|
||||||
|
symbolExpr,
|
||||||
|
newStatement,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
newBindings,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
| _ => {
|
||||||
|
let rExternalBindingsValue: result<expressionValue, errorValue> = reduceExpression(
|
||||||
|
bindingExpr,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
)
|
||||||
|
|
||||||
|
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
||||||
|
let newBindings = Bindings.fromValue(externalBindingsValue)
|
||||||
|
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
||||||
|
rNewStatement->Result.map(newStatement =>
|
||||||
|
ExpressionWithContext.withContext(newStatement, newBindings)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch list {
|
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
|
||||||
| list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok
|
expressionWithContext,
|
||||||
|
errorValue,
|
||||||
|
> => {
|
||||||
|
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)->Ok
|
||||||
|
}
|
||||||
|
|
||||||
| list{
|
let doLambdaDefinition = (
|
||||||
ExpressionT.EValue(EvCall("$$bindStatement")),
|
bindings: ExpressionT.bindings,
|
||||||
ExpressionT.EBindings(bindings),
|
parameters: array<string>,
|
||||||
statement,
|
lambdaDefinition: ExpressionT.expression,
|
||||||
} =>
|
) =>
|
||||||
doBindStatement(statement, bindings)
|
ExpressionWithContext.noContext(
|
||||||
| list{
|
eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition),
|
||||||
ExpressionT.EValue(EvCall("$$bindExpression")),
|
)->Ok
|
||||||
ExpressionT.EBindings(bindings),
|
|
||||||
expression,
|
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result<
|
||||||
} =>
|
expressionWithContext,
|
||||||
doBindExpression(expression, bindings)
|
errorValue,
|
||||||
| _ => list->ExpressionT.EList->Ok
|
> =>
|
||||||
|
switch aList {
|
||||||
|
| list{
|
||||||
|
ExpressionT.EValue(EvCall("$$bindStatement")),
|
||||||
|
bindingExpr: ExpressionT.expression,
|
||||||
|
statement,
|
||||||
|
} =>
|
||||||
|
doBindStatement(bindingExpr, statement, environment)
|
||||||
|
| list{ExpressionT.EValue(EvCall("$$bindStatement")), statement} =>
|
||||||
|
// bindings of the context are used when there is no binding expression
|
||||||
|
doBindStatement(eRecord(Bindings.toExternalBindings(bindings)), statement, environment)
|
||||||
|
| list{
|
||||||
|
ExpressionT.EValue(EvCall("$$bindExpression")),
|
||||||
|
bindingExpr: ExpressionT.expression,
|
||||||
|
expression,
|
||||||
|
} =>
|
||||||
|
doBindExpression(bindingExpr, expression, environment)
|
||||||
|
| list{ExpressionT.EValue(EvCall("$$bindExpression")), expression} =>
|
||||||
|
// bindings of the context are used when there is no binding expression
|
||||||
|
doBindExpression(eRecord(Bindings.toExternalBindings(bindings)), expression, environment)
|
||||||
|
| list{ExpressionT.EValue(EvCall("$$block")), ...exprs} => doBlock(exprs, bindings, environment)
|
||||||
|
| list{
|
||||||
|
ExpressionT.EValue(EvCall("$$lambda")),
|
||||||
|
ExpressionT.EValue(EvArrayString(parameters)),
|
||||||
|
lambdaDefinition,
|
||||||
|
} =>
|
||||||
|
doLambdaDefinition(bindings, parameters, lambdaDefinition)
|
||||||
|
| _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
switch macroExpression {
|
||||||
|
| EList(aList) => expandExpressionList(aList, bindings, environment)
|
||||||
|
| _ => ExpressionWithContext.noContext(macroExpression)->Ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
@genType
|
@genType
|
||||||
type errorValue =
|
type errorValue =
|
||||||
|
| REArityError(option<string>, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting
|
||||||
| REArrayIndexNotFound(string, int)
|
| REArrayIndexNotFound(string, int)
|
||||||
| REAssignmentExpected
|
| REAssignmentExpected
|
||||||
|
| REDistributionError(DistributionTypes.error)
|
||||||
| REExpressionExpected
|
| REExpressionExpected
|
||||||
| REFunctionExpected(string)
|
| REFunctionExpected(string)
|
||||||
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception
|
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception
|
||||||
| REMacroNotFound(string)
|
| REMacroNotFound(string)
|
||||||
|
| RENotAFunction(string)
|
||||||
| RERecordPropertyNotFound(string, string)
|
| RERecordPropertyNotFound(string, string)
|
||||||
| RESymbolNotFound(string)
|
| RESymbolNotFound(string)
|
||||||
| RESyntaxError(string)
|
| RESyntaxError(string)
|
||||||
| REDistributionError(DistributionTypes.error)
|
|
||||||
| RETodo(string) // To do
|
| RETodo(string) // To do
|
||||||
|
|
||||||
type t = errorValue
|
type t = errorValue
|
||||||
|
@ -17,6 +19,10 @@ type t = errorValue
|
||||||
@genType
|
@genType
|
||||||
let errorToString = err =>
|
let errorToString = err =>
|
||||||
switch err {
|
switch err {
|
||||||
|
| REArityError(_oFnName, arity, usedArity) =>
|
||||||
|
`${Js.String.make(arity)} arguments expected. Instead ${Js.String.make(
|
||||||
|
usedArity,
|
||||||
|
)} argument(s) were passed.`
|
||||||
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
||||||
| REAssignmentExpected => "Assignment expected"
|
| REAssignmentExpected => "Assignment expected"
|
||||||
| REExpressionExpected => "Expression expected"
|
| REExpressionExpected => "Expression expected"
|
||||||
|
@ -35,6 +41,7 @@ let errorToString = err =>
|
||||||
answer
|
answer
|
||||||
}
|
}
|
||||||
| REMacroNotFound(macro) => `Macro not found: ${macro}`
|
| REMacroNotFound(macro) => `Macro not found: ${macro}`
|
||||||
|
| RENotAFunction(valueString) => `${valueString} is not a function`
|
||||||
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
||||||
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
||||||
| RESyntaxError(desc) => `Syntax Error: ${desc}`
|
| RESyntaxError(desc) => `Syntax Error: ${desc}`
|
||||||
|
|
|
@ -1,35 +1,22 @@
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
module BuiltIn = Reducer_Dispatch_BuiltIn
|
module BuiltIn = Reducer_Dispatch_BuiltIn
|
||||||
|
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
module ExpressionValue = ReducerInterface.ExpressionValue
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
module Extra = Reducer_Extra
|
module Extra = Reducer_Extra
|
||||||
|
module Lambda = Reducer_Expression_Lambda
|
||||||
|
module Macro = Reducer_Expression_Macro
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module T = Reducer_Expression_T
|
module T = Reducer_Expression_T
|
||||||
open Reducer_ErrorValue
|
|
||||||
|
|
||||||
|
type environment = ReducerInterface_ExpressionValue.environment
|
||||||
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
type expression = T.expression
|
type expression = T.expression
|
||||||
type expressionValue = ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
type internalCode = ReducerInterface_ExpressionValue.internalCode
|
||||||
type t = expression
|
type t = expression
|
||||||
|
|
||||||
/*
|
|
||||||
Shows the expression as text of expression
|
|
||||||
*/
|
|
||||||
let rec toString = expression =>
|
|
||||||
switch expression {
|
|
||||||
| T.EBindings(_) => "$$bound"
|
|
||||||
| T.EList(aList) =>
|
|
||||||
`(${Belt.List.map(aList, aValue => toString(aValue))
|
|
||||||
->Extra.List.interperse(" ")
|
|
||||||
->Belt.List.toArray
|
|
||||||
->Js.String.concatMany("")})`
|
|
||||||
| EValue(aValue) => ExpressionValue.toString(aValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
let toStringResult = codeResult =>
|
|
||||||
switch codeResult {
|
|
||||||
| Ok(a) => `Ok(${toString(a)})`
|
|
||||||
| Error(m) => `Error(${Js.String.make(m)})`
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Converts a MathJs code to expression
|
Converts a MathJs code to expression
|
||||||
*/
|
*/
|
||||||
|
@ -39,148 +26,115 @@ let parse_ = (expr: string, parser, converter): result<t, errorValue> =>
|
||||||
let parse = (mathJsCode: string): result<t, errorValue> =>
|
let parse = (mathJsCode: string): result<t, errorValue> =>
|
||||||
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
|
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
|
||||||
|
|
||||||
let parsePartial = (mathJsCode: string): result<t, errorValue> =>
|
|
||||||
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode)
|
|
||||||
|
|
||||||
let parseOuter = (mathJsCode: string): result<t, errorValue> =>
|
|
||||||
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromOuterNode)
|
|
||||||
|
|
||||||
let defaultBindings: T.bindings = Belt.Map.String.empty
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Recursively evaluate/reduce the expression (Lisp AST)
|
Recursively evaluate/reduce the expression (Lisp AST)
|
||||||
*/
|
*/
|
||||||
let rec reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
|
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
|
||||||
/*
|
expressionValue,
|
||||||
Macros are like functions but instead of taking values as parameters,
|
'e,
|
||||||
they take expressions as parameters and return a new expression.
|
> => {
|
||||||
Macros are used to define language building blocks. They are like Lisp macros.
|
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
|
||||||
*/
|
switch expression {
|
||||||
let doMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> =>
|
| T.EValue(value) => value->Ok
|
||||||
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, reduceExpression)
|
| T.EList(list) =>
|
||||||
|
switch list {
|
||||||
|
| list{EValue(EvCall(fName)), ..._args} =>
|
||||||
|
switch Macro.isMacroName(fName) {
|
||||||
|
// A macro expands then reduces itself
|
||||||
|
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression)
|
||||||
|
| false => reduceExpressionList(list, bindings, environment)
|
||||||
|
}
|
||||||
|
| _ => reduceExpressionList(list, bindings, environment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
and reduceExpressionList = (
|
||||||
|
expressions: list<t>,
|
||||||
|
bindings: T.bindings,
|
||||||
|
environment: environment,
|
||||||
|
): result<expressionValue, 'e> => {
|
||||||
|
let racc: result<list<expressionValue>, 'e> = expressions->Belt.List.reduceReverse(Ok(list{}), (
|
||||||
|
racc,
|
||||||
|
each: expression,
|
||||||
|
) =>
|
||||||
|
racc->Result.flatMap(acc => {
|
||||||
|
each
|
||||||
|
->reduceExpression(bindings, environment)
|
||||||
|
->Result.map(newNode => {
|
||||||
|
acc->Belt.List.add(newNode)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
racc->Result.flatMap(acc => acc->reduceValueList(environment))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
After reducing each level of expression(Lisp AST), we have a value list to evaluate
|
After reducing each level of expression(Lisp AST), we have a value list to evaluate
|
||||||
*/
|
*/
|
||||||
let reduceValueList = (valueList: list<expressionValue>): result<expressionValue, 'e> =>
|
and reduceValueList = (valueList: list<expressionValue>, environment): result<
|
||||||
switch valueList {
|
expressionValue,
|
||||||
| list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch
|
'e,
|
||||||
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
|
> =>
|
||||||
}
|
switch valueList {
|
||||||
|
| list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment)
|
||||||
|
|
||||||
let rec seekMacros = (expression: t, bindings: T.bindings): result<t, 'e> =>
|
| list{EvLambda(lamdaCall), ...args} =>
|
||||||
switch expression {
|
Lambda.doLambdaCall(lamdaCall, args, environment, reduceExpression)
|
||||||
| T.EValue(_value) => expression->Ok
|
| _ =>
|
||||||
| T.EBindings(_value) => expression->Ok
|
valueList
|
||||||
| T.EList(list) => {
|
->Lambda.checkIfReduced
|
||||||
let racc: result<list<t>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
|
->Result.flatMap(reducedValueList =>
|
||||||
racc,
|
reducedValueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
|
||||||
each: expression,
|
)
|
||||||
) =>
|
}
|
||||||
racc->Result.flatMap(acc => {
|
|
||||||
each
|
|
||||||
->seekMacros(bindings)
|
|
||||||
->Result.flatMap(newNode => {
|
|
||||||
acc->Belt.List.add(newNode)->Ok
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
racc->Result.flatMap(acc => acc->doMacroCall(bindings))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rec reduceExpandedExpression = (expression: t): result<expressionValue, 'e> =>
|
let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result<
|
||||||
switch expression {
|
expressionValue,
|
||||||
| T.EValue(value) => value->Ok
|
'e,
|
||||||
| T.EList(list) => {
|
> => reduceExpression(aExpression, bindings, environment)
|
||||||
let racc: result<list<expressionValue>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
|
|
||||||
racc,
|
|
||||||
each: expression,
|
|
||||||
) =>
|
|
||||||
racc->Result.flatMap(acc => {
|
|
||||||
each
|
|
||||||
->reduceExpandedExpression
|
|
||||||
->Result.flatMap(newNode => {
|
|
||||||
acc->Belt.List.add(newNode)->Ok
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
racc->Result.flatMap(acc => acc->reduceValueList)
|
|
||||||
}
|
|
||||||
| EBindings(_bindings) => RETodo("Error: Bindings cannot be reduced to values")->Error
|
|
||||||
}
|
|
||||||
|
|
||||||
let rExpandedExpression: result<t, 'e> = expression->seekMacros(bindings)
|
let evaluateUsingOptions = (
|
||||||
rExpandedExpression->Result.flatMap(expandedExpression =>
|
~environment: option<ReducerInterface_ExpressionValue.environment>,
|
||||||
expandedExpression->reduceExpandedExpression
|
~externalBindings: option<ReducerInterface_ExpressionValue.externalBindings>,
|
||||||
)
|
code: string,
|
||||||
}
|
): result<expressionValue, errorValue> => {
|
||||||
|
let anEnvironment = switch environment {
|
||||||
|
| Some(env) => env
|
||||||
|
| None => ReducerInterface_ExpressionValue.defaultEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
let evalUsingExternalBindingsExpression_ = (aExpression, bindings): result<expressionValue, 'e> =>
|
let anExternalBindings = switch externalBindings {
|
||||||
reduceExpression(aExpression, bindings)
|
| Some(bindings) => bindings
|
||||||
|
| None => ReducerInterface_ExpressionValue.defaultExternalBindings
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
let bindings = anExternalBindings->Bindings.fromExternalBindings
|
||||||
Evaluates MathJs code via Reducer using bindings and answers the result.
|
|
||||||
When bindings are used, the code is a partial code as if it is cut from a larger code.
|
|
||||||
Therefore all statements are assignments.
|
|
||||||
*/
|
|
||||||
let evalPartialUsingExternalBindings_ = (codeText: string, bindings: T.bindings) => {
|
|
||||||
parsePartial(codeText)->Result.flatMap(expression =>
|
|
||||||
expression->evalUsingExternalBindingsExpression_(bindings)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
parse(code)->Result.flatMap(expr => evalUsingBindingsExpression_(expr, bindings, anEnvironment))
|
||||||
Evaluates MathJs code via Reducer using bindings and answers the result.
|
|
||||||
When bindings are used, the code is a partial code as if it is cut from a larger code.
|
|
||||||
Therefore all statments are assignments.
|
|
||||||
*/
|
|
||||||
let evalOuterWBindings_ = (codeText: string, bindings: T.bindings) => {
|
|
||||||
parseOuter(codeText)->Result.flatMap(expression =>
|
|
||||||
expression->evalUsingExternalBindingsExpression_(bindings)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Evaluates MathJs code and bindings via Reducer and answers the result
|
Evaluates MathJs code and bindings via Reducer and answers the result
|
||||||
*/
|
*/
|
||||||
let eval = (codeText: string) => {
|
let evaluate = (code: string): result<expressionValue, errorValue> => {
|
||||||
parse(codeText)->Result.flatMap(expression =>
|
evaluateUsingOptions(~environment=None, ~externalBindings=None, code)
|
||||||
expression->evalUsingExternalBindingsExpression_(defaultBindings)
|
}
|
||||||
)
|
let eval = evaluate
|
||||||
}
|
let evaluatePartialUsingExternalBindings = (
|
||||||
|
code: string,
|
||||||
type externalBindings = ReducerInterface.ExpressionValue.externalBindings //Js.Dict.t<expressionValue>
|
externalBindings: ReducerInterface_ExpressionValue.externalBindings,
|
||||||
|
environment: ReducerInterface_ExpressionValue.environment,
|
||||||
let externalBindingsToBindings = (externalBindings: externalBindings): T.bindings => {
|
): result<externalBindings, errorValue> => {
|
||||||
let keys = Js.Dict.keys(externalBindings)
|
let rAnswer = evaluateUsingOptions(
|
||||||
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
~environment=Some(environment),
|
||||||
let value = Js.Dict.unsafeGet(externalBindings, key)
|
~externalBindings=Some(externalBindings),
|
||||||
acc->Belt.Map.String.set(key, T.EValue(value))
|
code,
|
||||||
})
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Evaluates code with external bindings. External bindings are a record of expression values.
|
|
||||||
*/
|
|
||||||
let evalUsingExternalBindings = (code: string, externalBindings: externalBindings) => {
|
|
||||||
let bindings = externalBindings->externalBindingsToBindings
|
|
||||||
evalOuterWBindings_(code, bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Evaluates code with external bindings. External bindings are a record of expression values.
|
|
||||||
The code is a partial code as if it is cut from a larger code. Therefore all statments are assignments.
|
|
||||||
*/
|
|
||||||
let evalPartialUsingExternalBindings = (code: string, externalBindings: externalBindings): result<
|
|
||||||
externalBindings,
|
|
||||||
'e,
|
|
||||||
> => {
|
|
||||||
let bindings = externalBindings->externalBindingsToBindings
|
|
||||||
let answer = evalPartialUsingExternalBindings_(code, bindings)
|
|
||||||
answer->Result.flatMap(answer =>
|
|
||||||
switch answer {
|
|
||||||
| EvRecord(aRecord) => Ok(aRecord)
|
|
||||||
| _ => RETodo("TODO: External bindings must be returned")->Error
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
switch rAnswer {
|
||||||
|
| Ok(EvRecord(externalBindings)) => Ok(externalBindings)
|
||||||
|
| Ok(_) =>
|
||||||
|
Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`))
|
||||||
|
| Error(err) => err->Error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type bindings = ExpressionT.bindings
|
||||||
|
type context = bindings
|
||||||
|
type environment = ExpressionValue.environment
|
||||||
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
|
type expression = ExpressionT.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
type reducerFn = ExpressionT.reducerFn
|
||||||
|
|
||||||
|
type expressionWithContext =
|
||||||
|
| ExpressionWithContext(expression, context)
|
||||||
|
| ExpressionNoContext(expression)
|
||||||
|
|
||||||
|
let callReducer = (
|
||||||
|
expressionWithContext: expressionWithContext,
|
||||||
|
bindings: bindings,
|
||||||
|
environment: environment,
|
||||||
|
reducer: reducerFn,
|
||||||
|
): result<expressionValue, errorValue> =>
|
||||||
|
switch expressionWithContext {
|
||||||
|
| ExpressionNoContext(expr) => reducer(expr, bindings, environment)
|
||||||
|
| ExpressionWithContext(expr, context) => reducer(expr, context, environment)
|
||||||
|
}
|
||||||
|
|
||||||
|
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: ${Bindings.toString(context)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
let toStringResult = rExpressionWithContext =>
|
||||||
|
switch rExpressionWithContext {
|
||||||
|
| Ok(expressionWithContext) => `Ok(${toString(expressionWithContext)})`
|
||||||
|
| Error(errorValue) => ErrorValue.errorToString(errorValue)
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
|
type expression = ExpressionT.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
|
||||||
|
let defaultBindings: ExpressionT.bindings = Belt.Map.String.empty
|
||||||
|
|
||||||
|
let fromExternalBindings = (externalBindings: externalBindings): ExpressionT.bindings => {
|
||||||
|
let keys = Js.Dict.keys(externalBindings)
|
||||||
|
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
||||||
|
let value = Js.Dict.unsafeGet(externalBindings, key)
|
||||||
|
acc->Belt.Map.String.set(key, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => {
|
||||||
|
let keys = Belt.Map.String.keysToArray(bindings)
|
||||||
|
keys->Belt.Array.reduce(Js.Dict.empty(), (acc, key) => {
|
||||||
|
let value = bindings->Belt.Map.String.getExn(key)
|
||||||
|
Js.Dict.set(acc, key, value)
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromValue = (aValue: expressionValue) =>
|
||||||
|
switch aValue {
|
||||||
|
| EvRecord(externalBindings) => fromExternalBindings(externalBindings)
|
||||||
|
| _ => defaultBindings
|
||||||
|
}
|
||||||
|
|
||||||
|
let externalFromArray = anArray => Js.Dict.fromArray(anArray)
|
||||||
|
|
||||||
|
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")
|
||||||
|
|
||||||
|
let rec replaceSymbols = (bindings: ExpressionT.bindings, expression: expression): result<
|
||||||
|
expression,
|
||||||
|
errorValue,
|
||||||
|
> =>
|
||||||
|
switch expression {
|
||||||
|
| ExpressionT.EValue(value) =>
|
||||||
|
replaceSymbolOnValue(bindings, value)->Result.map(evValue => evValue->ExpressionT.EValue)
|
||||||
|
| ExpressionT.EList(list) =>
|
||||||
|
switch list {
|
||||||
|
| list{EValue(EvCall(fName)), ..._args} =>
|
||||||
|
switch isMacroName(fName) {
|
||||||
|
// A macro reduces itself so we dont dive in it
|
||||||
|
| true => expression->Ok
|
||||||
|
| false => replaceSymbolsOnExpressionList(bindings, list)
|
||||||
|
}
|
||||||
|
| _ => replaceSymbolsOnExpressionList(bindings, list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
and replaceSymbolsOnExpressionList = (bindings, list) => {
|
||||||
|
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
|
||||||
|
racc->Result.flatMap(acc => {
|
||||||
|
replaceSymbols(bindings, each)->Result.flatMap(newNode => {
|
||||||
|
acc->Belt.List.add(newNode)->Ok
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
racc->Result.map(acc => acc->ExpressionT.EList)
|
||||||
|
}
|
||||||
|
and replaceSymbolOnValue = (bindings, evValue: expressionValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| EvSymbol(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->Ok
|
||||||
|
| EvCall(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->checkIfCallable
|
||||||
|
| _ => evValue->Ok
|
||||||
|
}
|
||||||
|
and checkIfCallable = (evValue: expressionValue) =>
|
||||||
|
switch evValue {
|
||||||
|
| EvCall(_) | EvLambda(_) => evValue->Ok
|
||||||
|
| _ => ErrorValue.RENotAFunction(ExpressionValue.toString(evValue))->Error
|
||||||
|
}
|
||||||
|
|
||||||
|
let toString = (bindings: ExpressionT.bindings) =>
|
||||||
|
bindings->toExternalBindings->ExpressionValue.EvRecord->ExpressionValue.toString
|
||||||
|
|
||||||
|
let externalBindingsToString = (externalBindings: externalBindings) =>
|
||||||
|
externalBindings->ExpressionValue.EvRecord->ExpressionValue.toString
|
|
@ -0,0 +1,66 @@
|
||||||
|
module BBindings = Reducer_Expression_Bindings
|
||||||
|
module BErrorValue = Reducer_ErrorValue
|
||||||
|
module BExpressionT = Reducer_Expression_T
|
||||||
|
module BExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
|
||||||
|
type errorValue = BErrorValue.errorValue
|
||||||
|
type expression = BExpressionT.expression
|
||||||
|
type internalCode = ReducerInterface_ExpressionValue.internalCode
|
||||||
|
|
||||||
|
external castExpressionToInternalCode: expression => internalCode = "%identity"
|
||||||
|
|
||||||
|
let eArray = anArray => anArray->BExpressionValue.EvArray->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eArrayString = anArray => anArray->BExpressionValue.EvArrayString->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eBindings = (anArray: array<(string, BExpressionValue.expressionValue)>) =>
|
||||||
|
anArray->Js.Dict.fromArray->EvRecord->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eBool = aBool => aBool->BExpressionValue.EvBool->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eCall = (name: string): expression => name->BExpressionValue.EvCall->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eFunction = (fName: string, lispArgs: list<expression>): expression => {
|
||||||
|
let fn = fName->eCall
|
||||||
|
list{fn, ...lispArgs}->BExpressionT.EList
|
||||||
|
}
|
||||||
|
|
||||||
|
let eLambda = (
|
||||||
|
parameters: array<string>,
|
||||||
|
context: BExpressionValue.externalBindings,
|
||||||
|
expr: expression,
|
||||||
|
) => {
|
||||||
|
// Js.log(`eLambda context ${BBindings.externalBindingsToString(context)}`)
|
||||||
|
BExpressionValue.EvLambda({
|
||||||
|
parameters: parameters,
|
||||||
|
context: context,
|
||||||
|
body: expr->castExpressionToInternalCode,
|
||||||
|
})->BExpressionT.EValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let eNumber = aNumber => aNumber->BExpressionValue.EvNumber->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eRecord = aRecord => aRecord->BExpressionValue.EvRecord->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eString = aString => aString->BExpressionValue.EvString->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eSymbol = (name: string): expression => name->BExpressionValue.EvSymbol->BExpressionT.EValue
|
||||||
|
|
||||||
|
let eList = (list: list<expression>): expression => list->BExpressionT.EList
|
||||||
|
|
||||||
|
let eBlock = (exprs: list<expression>): expression => eFunction("$$block", exprs)
|
||||||
|
|
||||||
|
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})
|
|
@ -0,0 +1,60 @@
|
||||||
|
module Bindings = Reducer_Expression_Bindings
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type environment = ReducerInterface_ExpressionValue.environment
|
||||||
|
type expression = ExpressionT.expression
|
||||||
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
type internalCode = ReducerInterface_ExpressionValue.internalCode
|
||||||
|
|
||||||
|
external castInternalCodeToExpression: internalCode => expression = "%identity"
|
||||||
|
|
||||||
|
let checkArity = (lambdaValue: ExpressionValue.lambdaValue, args: list<expressionValue>) => {
|
||||||
|
let argsLength = Belt.List.length(args)
|
||||||
|
let parametersLength = Js.Array2.length(lambdaValue.parameters)
|
||||||
|
if argsLength !== parametersLength {
|
||||||
|
ErrorValue.REArityError(None, parametersLength, argsLength)->Error
|
||||||
|
} else {
|
||||||
|
args->Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkIfReduced = (args: list<expressionValue>) =>
|
||||||
|
args->Belt.List.reduceReverse(Ok(list{}), (rAcc, arg) =>
|
||||||
|
rAcc->Result.flatMap(acc =>
|
||||||
|
switch arg {
|
||||||
|
| EvSymbol(symbol) => ErrorValue.RESymbolNotFound(symbol)->Error
|
||||||
|
| _ => list{arg, ...acc}->Ok
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let applyParametersToLambda = (
|
||||||
|
lambdaValue: ExpressionValue.lambdaValue,
|
||||||
|
args,
|
||||||
|
environment,
|
||||||
|
reducer: ExpressionT.reducerFn,
|
||||||
|
): result<expressionValue, 'e> => {
|
||||||
|
checkArity(lambdaValue, args)->Result.flatMap(args =>
|
||||||
|
checkIfReduced(args)->Result.flatMap(args => {
|
||||||
|
let expr = castInternalCodeToExpression(lambdaValue.body)
|
||||||
|
let parameterList = lambdaValue.parameters->Belt.List.fromArray
|
||||||
|
let zippedParameterList = parameterList->Belt.List.zip(args)
|
||||||
|
let bindings = Belt.List.reduce(
|
||||||
|
zippedParameterList,
|
||||||
|
lambdaValue.context->Bindings.fromExternalBindings,
|
||||||
|
(acc, (variable, variableValue)) => acc->Belt.Map.String.set(variable, variableValue),
|
||||||
|
)
|
||||||
|
let newExpression = ExpressionBuilder.eBlock(list{expr})
|
||||||
|
reducer(newExpression, bindings, environment)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => {
|
||||||
|
applyParametersToLambda(lambdaValue, args, environment, reducer)
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module ExpressionWithContext = Reducer_ExpressionWithContext
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type environment = ExpressionValue.environment
|
||||||
|
type expression = ExpressionT.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
type expressionWithContext = ExpressionWithContext.expressionWithContext
|
||||||
|
|
||||||
|
let expandMacroCall = (
|
||||||
|
macroExpression: expression,
|
||||||
|
bindings: ExpressionT.bindings,
|
||||||
|
environment: environment,
|
||||||
|
reduceExpression: ExpressionT.reducerFn,
|
||||||
|
): result<expressionWithContext, 'e> =>
|
||||||
|
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
|
||||||
|
macroExpression,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
reduceExpression,
|
||||||
|
)
|
||||||
|
|
||||||
|
let doMacroCall = (
|
||||||
|
macroExpression: expression,
|
||||||
|
bindings: ExpressionT.bindings,
|
||||||
|
environment: environment,
|
||||||
|
reduceExpression: ExpressionT.reducerFn,
|
||||||
|
): result<expressionValue, 'e> =>
|
||||||
|
expandMacroCall(
|
||||||
|
macroExpression,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
reduceExpression,
|
||||||
|
)->Result.flatMap(expressionWithContext =>
|
||||||
|
ExpressionWithContext.callReducer(
|
||||||
|
expressionWithContext,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
reduceExpression,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")
|
|
@ -1,5 +1,3 @@
|
||||||
open ReducerInterface.ExpressionValue
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An expression is a Lisp AST. An expression is either a primitive value or a list of expressions.
|
An expression is a Lisp AST. An expression is either a primitive value or a list of expressions.
|
||||||
In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is
|
In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is
|
||||||
|
@ -8,8 +6,51 @@ open ReducerInterface.ExpressionValue
|
||||||
A Lisp AST contains only expressions/primitive values to apply to their left.
|
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.
|
The act of defining the semantics of a functional language is to write it in terms of Lisp AST.
|
||||||
*/
|
*/
|
||||||
|
module Extra = Reducer_Extra
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
type environment = ExpressionValue.environment
|
||||||
|
|
||||||
type rec expression =
|
type rec expression =
|
||||||
| EList(list<expression>) // A list to map-reduce
|
| EList(list<expression>) // A list to map-reduce
|
||||||
| EValue(expressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
|
| EValue(expressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
|
||||||
| EBindings(bindings) // let/def kind of statements return bindings
|
and bindings = Belt.Map.String.t<expressionValue>
|
||||||
and bindings = Belt.Map.String.t<expression>
|
|
||||||
|
type reducerFn = (
|
||||||
|
expression,
|
||||||
|
bindings,
|
||||||
|
environment,
|
||||||
|
) => result<expressionValue, Reducer_ErrorValue.errorValue>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts the expression to String
|
||||||
|
*/
|
||||||
|
let rec toString = expression =>
|
||||||
|
switch expression {
|
||||||
|
| EList(aList) =>
|
||||||
|
`(${Belt.List.map(aList, aValue => toString(aValue))
|
||||||
|
->Extra.List.interperse(" ")
|
||||||
|
->Belt.List.toArray
|
||||||
|
->Js.String.concatMany("")})`
|
||||||
|
| EValue(aValue) => ExpressionValue.toString(aValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
let toStringResult = codeResult =>
|
||||||
|
switch codeResult {
|
||||||
|
| Ok(a) => `Ok(${toString(a)})`
|
||||||
|
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
|
||||||
|
}
|
||||||
|
|
||||||
|
let inspect = (expr: expression): expression => {
|
||||||
|
Js.log(toString(expr))
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
|
let inspectResult = (r: result<expression, Reducer_ErrorValue.errorValue>): result<
|
||||||
|
expression,
|
||||||
|
Reducer_ErrorValue.errorValue,
|
||||||
|
> => {
|
||||||
|
Js.log(toStringResult(r))
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
|
@ -8,11 +8,10 @@ external castString: unit => string = "%identity"
|
||||||
/*
|
/*
|
||||||
As JavaScript returns us any type, we need to type check and cast type propertype before using it
|
As JavaScript returns us any type, we need to type check and cast type propertype before using it
|
||||||
*/
|
*/
|
||||||
let jsToEv = (jsValue): result<expressionValue, errorValue> => {
|
let jsToEv = (jsValue): result<expressionValue, errorValue> =>
|
||||||
switch Js.typeof(jsValue) {
|
switch Js.typeof(jsValue) {
|
||||||
| "boolean" => jsValue->castBool->EvBool->Ok
|
| "boolean" => jsValue->castBool->EvBool->Ok
|
||||||
| "number" => jsValue->castNumber->EvNumber->Ok
|
| "number" => jsValue->castNumber->EvNumber->Ok
|
||||||
| "string" => jsValue->castString->EvString->Ok
|
| "string" => jsValue->castString->EvString->Ok
|
||||||
| other => RETodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`)->Error
|
| other => RETodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`)->Error
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -11,11 +11,10 @@ type block = {"node": node}
|
||||||
type blockNode = {...node, "blocks": array<block>}
|
type blockNode = {...node, "blocks": array<block>}
|
||||||
//conditionalNode
|
//conditionalNode
|
||||||
type constantNode = {...node, "value": unit}
|
type constantNode = {...node, "value": unit}
|
||||||
//functionAssignmentNode
|
type functionAssignmentNode = {...node, "name": string, "params": array<string>, "expr": node}
|
||||||
type indexNode = {...node, "dimensions": array<node>}
|
type indexNode = {...node, "dimensions": array<node>}
|
||||||
type objectNode = {...node, "properties": Js.Dict.t<node>}
|
type objectNode = {...node, "properties": Js.Dict.t<node>}
|
||||||
type accessorNode = {...node, "object": node, "index": indexNode, "name": string}
|
type accessorNode = {...node, "object": node, "index": indexNode, "name": string}
|
||||||
|
|
||||||
type parenthesisNode = {...node, "content": node}
|
type parenthesisNode = {...node, "content": node}
|
||||||
//rangeNode
|
//rangeNode
|
||||||
//relationalNode
|
//relationalNode
|
||||||
|
@ -33,6 +32,7 @@ external castAssignmentNodeWAccessor: node => assignmentNodeWAccessor = "%identi
|
||||||
external castAssignmentNodeWIndex: node => assignmentNodeWIndex = "%identity"
|
external castAssignmentNodeWIndex: node => assignmentNodeWIndex = "%identity"
|
||||||
external castBlockNode: node => blockNode = "%identity"
|
external castBlockNode: node => blockNode = "%identity"
|
||||||
external castConstantNode: node => constantNode = "%identity"
|
external castConstantNode: node => constantNode = "%identity"
|
||||||
|
external castFunctionAssignmentNode: node => functionAssignmentNode = "%identity"
|
||||||
external castFunctionNode: node => functionNode = "%identity"
|
external castFunctionNode: node => functionNode = "%identity"
|
||||||
external castIndexNode: node => indexNode = "%identity"
|
external castIndexNode: node => indexNode = "%identity"
|
||||||
external castObjectNode: node => objectNode = "%identity"
|
external castObjectNode: node => objectNode = "%identity"
|
||||||
|
@ -59,6 +59,7 @@ type mathJsNode =
|
||||||
| MjAssignmentNode(assignmentNode)
|
| MjAssignmentNode(assignmentNode)
|
||||||
| MjBlockNode(blockNode)
|
| MjBlockNode(blockNode)
|
||||||
| MjConstantNode(constantNode)
|
| MjConstantNode(constantNode)
|
||||||
|
| MjFunctionAssignmentNode(functionAssignmentNode)
|
||||||
| MjFunctionNode(functionNode)
|
| MjFunctionNode(functionNode)
|
||||||
| MjIndexNode(indexNode)
|
| MjIndexNode(indexNode)
|
||||||
| MjObjectNode(objectNode)
|
| MjObjectNode(objectNode)
|
||||||
|
@ -82,6 +83,7 @@ let castNodeType = (node: node) => {
|
||||||
| "AssignmentNode" => node->decideAssignmentNode
|
| "AssignmentNode" => node->decideAssignmentNode
|
||||||
| "BlockNode" => node->castBlockNode->MjBlockNode->Ok
|
| "BlockNode" => node->castBlockNode->MjBlockNode->Ok
|
||||||
| "ConstantNode" => node->castConstantNode->MjConstantNode->Ok
|
| "ConstantNode" => node->castConstantNode->MjConstantNode->Ok
|
||||||
|
| "FunctionAssignmentNode" => node->castFunctionAssignmentNode->MjFunctionAssignmentNode->Ok
|
||||||
| "FunctionNode" => node->castFunctionNode->MjFunctionNode->Ok
|
| "FunctionNode" => node->castFunctionNode->MjFunctionNode->Ok
|
||||||
| "IndexNode" => node->castIndexNode->MjIndexNode->Ok
|
| "IndexNode" => node->castIndexNode->MjIndexNode->Ok
|
||||||
| "ObjectNode" => node->castObjectNode->MjObjectNode->Ok
|
| "ObjectNode" => node->castObjectNode->MjObjectNode->Ok
|
||||||
|
@ -118,6 +120,10 @@ let rec toString = (mathJsNode: mathJsNode): string => {
|
||||||
->Extra.Array.interperse(", ")
|
->Extra.Array.interperse(", ")
|
||||||
->Js.String.concatMany("")
|
->Js.String.concatMany("")
|
||||||
|
|
||||||
|
let toStringFunctionAssignmentNode = (faNode: functionAssignmentNode): string => {
|
||||||
|
let paramNames = Js.Array2.toString(faNode["params"])
|
||||||
|
`${faNode["name"]} = (${paramNames}) => ${toStringMathJsNode(faNode["expr"])}`
|
||||||
|
}
|
||||||
let toStringFunctionNode = (fnode: functionNode): string =>
|
let toStringFunctionNode = (fnode: functionNode): string =>
|
||||||
`${fnode->nameOfFunctionNode}(${fnode["args"]->toStringNodeArray})`
|
`${fnode->nameOfFunctionNode}(${fnode["args"]->toStringNodeArray})`
|
||||||
|
|
||||||
|
@ -152,6 +158,7 @@ let rec toString = (mathJsNode: mathJsNode): string => {
|
||||||
`${aNode["object"]->toStringSymbolNode} = ${aNode["value"]->toStringMathJsNode}`
|
`${aNode["object"]->toStringSymbolNode} = ${aNode["value"]->toStringMathJsNode}`
|
||||||
| MjBlockNode(bNode) => `{${bNode["blocks"]->toStringBlocks}}`
|
| MjBlockNode(bNode) => `{${bNode["blocks"]->toStringBlocks}}`
|
||||||
| MjConstantNode(cNode) => cNode["value"]->toStringValue
|
| MjConstantNode(cNode) => cNode["value"]->toStringValue
|
||||||
|
| MjFunctionAssignmentNode(faNode) => faNode->toStringFunctionAssignmentNode
|
||||||
| MjFunctionNode(fNode) => fNode->toStringFunctionNode
|
| MjFunctionNode(fNode) => fNode->toStringFunctionNode
|
||||||
| MjIndexNode(iNode) => iNode->toStringIndexNode
|
| MjIndexNode(iNode) => iNode->toStringIndexNode
|
||||||
| MjObjectNode(oNode) => oNode->toStringObjectNode
|
| MjObjectNode(oNode) => oNode->toStringObjectNode
|
||||||
|
|
|
@ -1,45 +1,35 @@
|
||||||
|
/* * WARNING. DO NOT EDIT, BEAUTIFY, COMMENT ON OR REFACTOR THIS CODE.
|
||||||
|
We will stop using MathJs parser and
|
||||||
|
this whole file will go to trash
|
||||||
|
**/
|
||||||
module ErrorValue = Reducer_ErrorValue
|
module ErrorValue = Reducer_ErrorValue
|
||||||
module ExpressionValue = ReducerInterface.ExpressionValue
|
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
module JavaScript = Reducer_Js
|
module JavaScript = Reducer_Js
|
||||||
module Parse = Reducer_MathJs_Parse
|
module Parse = Reducer_MathJs_Parse
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type errorValue = ErrorValue.errorValue
|
||||||
type expression = ExpressionT.expression
|
type expression = ExpressionT.expression
|
||||||
type expressionValue = ExpressionValue.expressionValue
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
type errorValue = ErrorValue.errorValue
|
|
||||||
|
|
||||||
let passToFunction = (fName: string, rLispArgs): result<expression, errorValue> => {
|
let blockToNode = block => block["node"]
|
||||||
let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue
|
|
||||||
|
|
||||||
let fn = fName->toEvCallValue
|
let rec fromInnerNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
type blockTag =
|
|
||||||
| ImportVariablesStatement
|
|
||||||
| ExportVariablesExpression
|
|
||||||
type tagOrNode =
|
|
||||||
| BlockTag(blockTag)
|
|
||||||
| BlockNode(Parse.node)
|
|
||||||
|
|
||||||
let toTagOrNode = block => BlockNode(block["node"])
|
|
||||||
|
|
||||||
let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
|
||||||
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
||||||
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
|
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
|
||||||
Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) =>
|
Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) =>
|
||||||
racc->Result.flatMap(acc =>
|
racc->Result.flatMap(acc =>
|
||||||
fromNode(currNode)->Result.map(currCode => list{currCode, ...acc})
|
fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let toEvSymbolValue = (name: string): expression =>
|
|
||||||
name->ExpressionValue.EvSymbol->ExpressionT.EValue
|
|
||||||
|
|
||||||
let caseFunctionNode = fNode => {
|
let caseFunctionNode = fNode => {
|
||||||
let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
|
let rLispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
|
||||||
passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
|
rLispArgs->Result.map(lispArgs =>
|
||||||
|
ExpressionBuilder.eFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let caseObjectNode = oNode => {
|
let caseObjectNode = oNode => {
|
||||||
|
@ -49,19 +39,16 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
(key: string, value: Parse.node),
|
(key: string, value: Parse.node),
|
||||||
) =>
|
) =>
|
||||||
racc->Result.flatMap(acc =>
|
racc->Result.flatMap(acc =>
|
||||||
fromNode(value)->Result.map(valueExpression => {
|
fromInnerNode(value)->Result.map(valueExpression => {
|
||||||
let entryCode =
|
let entryCode =
|
||||||
list{
|
list{ExpressionBuilder.eString(key), valueExpression}->ExpressionT.EList
|
||||||
key->ExpressionValue.EvString->ExpressionT.EValue,
|
|
||||||
valueExpression,
|
|
||||||
}->ExpressionT.EList
|
|
||||||
list{entryCode, ...acc}
|
list{entryCode, ...acc}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
rargs->Result.flatMap(args =>
|
rargs->Result.flatMap(args =>
|
||||||
passToFunction("$constructRecord", list{ExpressionT.EList(args)}->Ok)
|
ExpressionBuilder.eFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok
|
||||||
) // $consturctRecord gets a single argument: List of key-value paiers
|
) // $constructRecord gets a single argument: List of key-value paiers
|
||||||
}
|
}
|
||||||
|
|
||||||
oNode["properties"]->Js.Dict.entries->Belt.List.fromArray->fromObjectEntries
|
oNode["properties"]->Js.Dict.entries->Belt.List.fromArray->fromObjectEntries
|
||||||
|
@ -73,7 +60,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
Ok(list{}),
|
Ok(list{}),
|
||||||
(racc, currentPropertyMathJsNode) =>
|
(racc, currentPropertyMathJsNode) =>
|
||||||
racc->Result.flatMap(acc =>
|
racc->Result.flatMap(acc =>
|
||||||
fromNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{
|
fromInnerNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{
|
||||||
propertyCode,
|
propertyCode,
|
||||||
...acc,
|
...acc,
|
||||||
})
|
})
|
||||||
|
@ -84,18 +71,41 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
|
|
||||||
let caseAccessorNode = (objectNode, indexNode) => {
|
let caseAccessorNode = (objectNode, indexNode) => {
|
||||||
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
|
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
|
||||||
fromNode(objectNode)->Result.flatMap(objectCode =>
|
fromInnerNode(objectNode)->Result.flatMap(objectCode =>
|
||||||
passToFunction("$atIndex", list{objectCode, indexCode}->Ok)
|
ExpressionBuilder.eFunction("$atIndex", list{objectCode, indexCode})->Ok
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let caseBlock = (nodesArray: array<Parse.node>): result<expression, errorValue> => {
|
||||||
|
let rStatements: result<list<expression>, 'a> =
|
||||||
|
nodesArray
|
||||||
|
->Belt.List.fromArray
|
||||||
|
->Belt.List.reduceReverse(Ok(list{}), (racc, currNode) =>
|
||||||
|
racc->Result.flatMap(acc =>
|
||||||
|
fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
rStatements->Result.map(statements => ExpressionBuilder.eBlock(statements))
|
||||||
|
}
|
||||||
|
|
||||||
let caseAssignmentNode = aNode => {
|
let caseAssignmentNode = aNode => {
|
||||||
let symbol = aNode["object"]["name"]->toEvSymbolValue
|
let symbolName = aNode["object"]["name"]
|
||||||
let rValueExpression = fromNode(aNode["value"])
|
let rValueExpression = fromInnerNode(aNode["value"])
|
||||||
|
rValueExpression->Result.map(valueExpression =>
|
||||||
|
ExpressionBuilder.eLetStatement(symbolName, valueExpression)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseFunctionAssignmentNode = faNode => {
|
||||||
|
let symbol = faNode["name"]->ExpressionBuilder.eSymbol
|
||||||
|
let rValueExpression = fromInnerNode(faNode["expr"])
|
||||||
|
|
||||||
rValueExpression->Result.flatMap(valueExpression => {
|
rValueExpression->Result.flatMap(valueExpression => {
|
||||||
let lispArgs = list{symbol, valueExpression}->Ok
|
let lispParams = ExpressionBuilder.eArrayString(faNode["params"])
|
||||||
passToFunction("$let", lispArgs)
|
let valueBlock = ExpressionBuilder.eBlock(list{valueExpression})
|
||||||
|
let lambda = ExpressionBuilder.eFunction("$$lambda", list{lispParams, valueBlock})
|
||||||
|
ExpressionBuilder.eFunction("$let", list{symbol, lambda})->Ok
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,88 +118,22 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
| MjArrayNode(aNode) => caseArrayNode(aNode)
|
| MjArrayNode(aNode) => caseArrayNode(aNode)
|
||||||
| MjAssignmentNode(aNode) => caseAssignmentNode(aNode)
|
| MjAssignmentNode(aNode) => caseAssignmentNode(aNode)
|
||||||
| MjSymbolNode(sNode) => {
|
| MjSymbolNode(sNode) => {
|
||||||
let expr: expression = toEvSymbolValue(sNode["name"])
|
let expr: expression = ExpressionBuilder.eSymbol(sNode["name"])
|
||||||
let rExpr: result<expression, errorValue> = expr->Ok
|
let rExpr: result<expression, errorValue> = expr->Ok
|
||||||
rExpr
|
rExpr
|
||||||
}
|
}
|
||||||
| MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes
|
| MjBlockNode(bNode) => bNode["blocks"]->Js.Array2.map(blockToNode)->caseBlock
|
||||||
| MjConstantNode(cNode) =>
|
| MjConstantNode(cNode) =>
|
||||||
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
|
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
|
||||||
|
| MjFunctionAssignmentNode(faNode) => caseFunctionAssignmentNode(faNode)
|
||||||
| MjFunctionNode(fNode) => fNode->caseFunctionNode
|
| MjFunctionNode(fNode) => fNode->caseFunctionNode
|
||||||
| MjIndexNode(iNode) => caseIndexNode(iNode)
|
| MjIndexNode(iNode) => caseIndexNode(iNode)
|
||||||
| MjObjectNode(oNode) => caseObjectNode(oNode)
|
| MjObjectNode(oNode) => caseObjectNode(oNode)
|
||||||
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode
|
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode
|
||||||
| MjParenthesisNode(pNode) => pNode["content"]->fromNode
|
| MjParenthesisNode(pNode) => pNode["content"]->fromInnerNode
|
||||||
}
|
}
|
||||||
rFinalExpression
|
rFinalExpression
|
||||||
})
|
})
|
||||||
and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
|
|
||||||
let initialBindings = passToFunction("$$bindings", list{}->Ok)
|
|
||||||
let lastIndex = Belt.Array.length(tagOrNodes) - 1
|
|
||||||
tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => {
|
|
||||||
rPreviousBindings->Result.flatMap(previousBindings => {
|
|
||||||
let rStatement: result<expression, errorValue> = switch tagOrNode {
|
|
||||||
| BlockNode(node) => fromNode(node)
|
|
||||||
| BlockTag(tag) =>
|
|
||||||
switch tag {
|
|
||||||
| ImportVariablesStatement => passToFunction("$importVariablesStatement", list{}->Ok)
|
|
||||||
| ExportVariablesExpression => passToFunction("$exportVariablesExpression", list{}->Ok)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bindName = if i == lastIndex {
|
let fromNode = (node: Parse.node): result<expression, errorValue> =>
|
||||||
"$$bindExpression"
|
fromInnerNode(node)->Result.map(expr => ExpressionBuilder.eBlock(list{expr}))
|
||||||
} else {
|
|
||||||
"$$bindStatement"
|
|
||||||
}
|
|
||||||
|
|
||||||
rStatement->Result.flatMap((statement: expression) => {
|
|
||||||
let lispArgs = list{previousBindings, statement}->Ok
|
|
||||||
passToFunction(bindName, lispArgs)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let fromPartialNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
|
|
||||||
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
|
||||||
let casePartialBlockNode = (bNode: Parse.blockNode) => {
|
|
||||||
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
|
|
||||||
let completed = Js.Array2.concat(blocksOrTags, [BlockTag(ExportVariablesExpression)])
|
|
||||||
completed->caseTagOrNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
let casePartialExpression = (node: Parse.node) => {
|
|
||||||
let completed = [BlockNode(node), BlockTag(ExportVariablesExpression)]
|
|
||||||
|
|
||||||
completed->caseTagOrNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
|
||||||
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
|
|
||||||
| _ => casePartialExpression(mathJsNode)
|
|
||||||
}
|
|
||||||
rFinalExpression
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let fromOuterNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
|
|
||||||
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
|
||||||
let casePartialBlockNode = (bNode: Parse.blockNode) => {
|
|
||||||
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
|
|
||||||
let completed = blocksOrTags
|
|
||||||
completed->caseTagOrNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
let casePartialExpression = (node: Parse.node) => {
|
|
||||||
let completed = [BlockNode(node)]
|
|
||||||
completed->caseTagOrNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
|
||||||
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
|
|
||||||
| _ => casePartialExpression(mathJsNode)
|
|
||||||
}
|
|
||||||
rFinalExpression
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,37 +5,50 @@
|
||||||
module Extra_Array = Reducer_Extra_Array
|
module Extra_Array = Reducer_Extra_Array
|
||||||
module ErrorValue = Reducer_ErrorValue
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
|
||||||
|
@genType.opaque
|
||||||
|
type internalCode = Object
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type rec expressionValue =
|
type rec expressionValue =
|
||||||
| EvArray(array<expressionValue>)
|
| EvArray(array<expressionValue>)
|
||||||
|
| EvArrayString(array<string>)
|
||||||
| EvBool(bool)
|
| EvBool(bool)
|
||||||
| EvCall(string) // External function call
|
| EvCall(string) // External function call
|
||||||
| EvDistribution(DistributionTypes.genericDist)
|
| EvDistribution(DistributionTypes.genericDist)
|
||||||
|
| EvLambda(lambdaValue)
|
||||||
| EvNumber(float)
|
| EvNumber(float)
|
||||||
| EvRecord(Js.Dict.t<expressionValue>)
|
| EvRecord(record)
|
||||||
| EvString(string)
|
| EvString(string)
|
||||||
| EvSymbol(string)
|
| EvSymbol(string)
|
||||||
|
and record = Js.Dict.t<expressionValue>
|
||||||
|
and externalBindings = record
|
||||||
|
and lambdaValue = {
|
||||||
|
parameters: array<string>,
|
||||||
|
context: externalBindings,
|
||||||
|
body: internalCode,
|
||||||
|
}
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type externalBindings = Js.Dict.t<expressionValue>
|
let defaultExternalBindings: externalBindings = Js.Dict.empty()
|
||||||
|
|
||||||
type functionCall = (string, array<expressionValue>)
|
type functionCall = (string, array<expressionValue>)
|
||||||
|
|
||||||
let rec toString = aValue =>
|
let rec toString = aValue =>
|
||||||
switch aValue {
|
switch aValue {
|
||||||
|
| EvArray(anArray) => {
|
||||||
|
let args = anArray->Js.Array2.map(each => toString(each))->Js.Array2.toString
|
||||||
|
`[${args}]`
|
||||||
|
}
|
||||||
|
| EvArrayString(anArray) => {
|
||||||
|
let args = anArray->Js.Array2.toString
|
||||||
|
`[${args}]`
|
||||||
|
}
|
||||||
| EvBool(aBool) => Js.String.make(aBool)
|
| EvBool(aBool) => Js.String.make(aBool)
|
||||||
| EvCall(fName) => `:${fName}`
|
| EvCall(fName) => `:${fName}`
|
||||||
|
| EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
|
||||||
| EvNumber(aNumber) => Js.String.make(aNumber)
|
| EvNumber(aNumber) => Js.String.make(aNumber)
|
||||||
| EvString(aString) => `'${aString}'`
|
| EvString(aString) => `'${aString}'`
|
||||||
| EvSymbol(aString) => `:${aString}`
|
| EvSymbol(aString) => `:${aString}`
|
||||||
| EvArray(anArray) => {
|
|
||||||
let args =
|
|
||||||
anArray
|
|
||||||
->Belt.Array.map(each => toString(each))
|
|
||||||
->Extra_Array.interperse(", ")
|
|
||||||
->Js.String.concatMany("")
|
|
||||||
`[${args}]`
|
|
||||||
}
|
|
||||||
| EvRecord(aRecord) => aRecord->toStringRecord
|
| EvRecord(aRecord) => aRecord->toStringRecord
|
||||||
| EvDistribution(dist) => GenericDist.toString(dist)
|
| EvDistribution(dist) => GenericDist.toString(dist)
|
||||||
}
|
}
|
||||||
|
@ -43,26 +56,27 @@ and toStringRecord = aRecord => {
|
||||||
let pairs =
|
let pairs =
|
||||||
aRecord
|
aRecord
|
||||||
->Js.Dict.entries
|
->Js.Dict.entries
|
||||||
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
|
->Js.Array2.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
|
||||||
->Extra_Array.interperse(", ")
|
->Js.Array2.toString
|
||||||
->Js.String.concatMany("")
|
|
||||||
`{${pairs}}`
|
`{${pairs}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
let toStringWithType = aValue =>
|
let toStringWithType = aValue =>
|
||||||
switch aValue {
|
switch aValue {
|
||||||
|
| EvArray(_) => `Array::${toString(aValue)}`
|
||||||
|
| EvArrayString(_) => `ArrayString::${toString(aValue)}`
|
||||||
| EvBool(_) => `Bool::${toString(aValue)}`
|
| EvBool(_) => `Bool::${toString(aValue)}`
|
||||||
| EvCall(_) => `Call::${toString(aValue)}`
|
| EvCall(_) => `Call::${toString(aValue)}`
|
||||||
|
| EvDistribution(_) => `Distribution::${toString(aValue)}`
|
||||||
|
| EvLambda(_) => `Lambda::${toString(aValue)}`
|
||||||
| EvNumber(_) => `Number::${toString(aValue)}`
|
| EvNumber(_) => `Number::${toString(aValue)}`
|
||||||
|
| EvRecord(_) => `Record::${toString(aValue)}`
|
||||||
| EvString(_) => `String::${toString(aValue)}`
|
| EvString(_) => `String::${toString(aValue)}`
|
||||||
| EvSymbol(_) => `Symbol::${toString(aValue)}`
|
| EvSymbol(_) => `Symbol::${toString(aValue)}`
|
||||||
| EvArray(_) => `Array::${toString(aValue)}`
|
|
||||||
| EvRecord(_) => `Record::${toString(aValue)}`
|
|
||||||
| EvDistribution(_) => `Distribution::${toString(aValue)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let argsToString = (args: array<expressionValue>): string => {
|
let argsToString = (args: array<expressionValue>): string => {
|
||||||
args->Belt.Array.map(arg => arg->toString)->Extra_Array.interperse(", ")->Js.String.concatMany("")
|
args->Js.Array2.map(arg => arg->toString)->Js.Array2.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})`
|
let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})`
|
||||||
|
@ -78,3 +92,9 @@ let toStringResultRecord = x =>
|
||||||
| Ok(a) => `Ok(${toStringRecord(a)})`
|
| Ok(a) => `Ok(${toStringRecord(a)})`
|
||||||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type environment = DistributionOperation.env
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let defaultEnvironment: environment = DistributionOperation.defaultEnv
|
||||||
|
|
|
@ -14,8 +14,13 @@ type expressionValue = ExpressionValue.expressionValue
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let dispatch = (call: ExpressionValue.functionCall, chain): result<expressionValue, 'e> =>
|
let dispatch = (call: ExpressionValue.functionCall, environment, chain): result<
|
||||||
ReducerInterface_GenericDistribution.dispatch(call) |> E.O.default(chain(call))
|
expressionValue,
|
||||||
|
'e,
|
||||||
|
> =>
|
||||||
|
ReducerInterface_GenericDistribution.dispatch(call, environment) |> E.O.default(
|
||||||
|
chain(call, environment),
|
||||||
|
)
|
||||||
/*
|
/*
|
||||||
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
module ExpressionValue = ReducerInterface_ExpressionValue
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
|
||||||
let runGenericOperation = DistributionOperation.run(
|
let defaultEnv: DistributionOperation.env = {
|
||||||
~env={
|
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
||||||
sampleCount: MagicNumbers.Environment.defaultSampleCount,
|
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
||||||
xyPointLength: MagicNumbers.Environment.defaultXYPointLength,
|
}
|
||||||
},
|
|
||||||
)
|
let runGenericOperation = DistributionOperation.run(~env=defaultEnv)
|
||||||
|
|
||||||
module Helpers = {
|
module Helpers = {
|
||||||
let arithmeticMap = r =>
|
let arithmeticMap = r =>
|
||||||
|
@ -28,14 +28,13 @@ module Helpers = {
|
||||||
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
|
let catchAndConvertTwoArgsToDists = (args: array<expressionValue>): option<(
|
||||||
DistributionTypes.genericDist,
|
DistributionTypes.genericDist,
|
||||||
DistributionTypes.genericDist,
|
DistributionTypes.genericDist,
|
||||||
)> => {
|
)> =>
|
||||||
switch args {
|
switch args {
|
||||||
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
|
| [EvDistribution(a), EvDistribution(b)] => Some((a, b))
|
||||||
| [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b))
|
| [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b))
|
||||||
| [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b)))
|
| [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b)))
|
||||||
| _ => None
|
| _ => None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let toFloatFn = (
|
let toFloatFn = (
|
||||||
fnCall: DistributionTypes.DistributionOperation.toFloat,
|
fnCall: DistributionTypes.DistributionOperation.toFloat,
|
||||||
|
@ -120,7 +119,7 @@ module Helpers = {
|
||||||
mixtureWithGivenWeights(distributions, weights)
|
mixtureWithGivenWeights(distributions, weights)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mixture = (args: array<expressionValue>): DistributionOperation.outputType => {
|
let mixture = (args: array<expressionValue>): DistributionOperation.outputType =>
|
||||||
switch E.A.last(args) {
|
switch E.A.last(args) {
|
||||||
| Some(EvArray(b)) => {
|
| Some(EvArray(b)) => {
|
||||||
let weights = parseNumberArray(b)
|
let weights = parseNumberArray(b)
|
||||||
|
@ -140,7 +139,6 @@ module Helpers = {
|
||||||
}
|
}
|
||||||
| _ => GenDistError(ArgumentError("Last argument of mx must be array or distribution"))
|
| _ => GenDistError(ArgumentError("Last argument of mx must be array or distribution"))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module SymbolicConstructors = {
|
module SymbolicConstructors = {
|
||||||
|
@ -176,7 +174,7 @@ module SymbolicConstructors = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option<
|
let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment): option<
|
||||||
DistributionOperation.outputType,
|
DistributionOperation.outputType,
|
||||||
> => {
|
> => {
|
||||||
let (fnName, args) = call
|
let (fnName, args) = call
|
||||||
|
@ -297,6 +295,6 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
|
||||||
| GenDistError(err) => Error(REDistributionError(err))
|
| GenDistError(err) => Error(REDistributionError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
let dispatch = call => {
|
let dispatch = (call, environment) => {
|
||||||
dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue)
|
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
let dispatch: ReducerInterface_ExpressionValue.functionCall => option<
|
let defaultEnv: DistributionOperation.env
|
||||||
result<ReducerInterface_ExpressionValue.expressionValue, Reducer_ErrorValue.errorValue>,
|
let dispatch: (
|
||||||
>
|
ReducerInterface_ExpressionValue.functionCall,
|
||||||
|
ReducerInterface_ExpressionValue.environment,
|
||||||
|
) => option<result<ReducerInterface_ExpressionValue.expressionValue, Reducer_ErrorValue.errorValue>>
|
||||||
|
|
|
@ -38,7 +38,7 @@ let makeSampleSetDist = SampleSetDist.make
|
||||||
let evaluate = Reducer.evaluate
|
let evaluate = Reducer.evaluate
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings
|
let evaluateUsingOptions = Reducer.evaluateUsingOptions
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
|
let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings
|
||||||
|
@ -49,6 +49,9 @@ type externalBindings = Reducer.externalBindings
|
||||||
@genType
|
@genType
|
||||||
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type recordEV = ReducerInterface_ExpressionValue.record
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type errorValue = Reducer_ErrorValue.errorValue
|
type errorValue = Reducer_ErrorValue.errorValue
|
||||||
|
|
||||||
|
@ -69,3 +72,15 @@ let errorValueToString = Reducer_ErrorValue.errorToString
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let distributionErrorToString = DistributionTypes.Error.toString
|
let distributionErrorToString = DistributionTypes.Error.toString
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type lambdaValue = ReducerInterface_ExpressionValue.lambdaValue
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let defaultSamplingEnv = ReducerInterface_GenericDistribution.defaultEnv
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type environment = ReducerInterface_ExpressionValue.environment
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment
|
||||||
|
|
Loading…
Reference in New Issue
Block a user