Merge remote-tracking branch 'origin/develop' into log-score-attempt

This commit is contained in:
Quinn Dougherty 2022-05-03 10:17:17 -04:00
commit 6c815a62ff
40 changed files with 1360 additions and 614 deletions

View File

@ -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>
);
} }
}; };

View File

@ -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)")
})

View File

@ -0,0 +1,6 @@
open Jest
open Expect
test("dummy", () => {
expect(true)->toBe(true)
})

View File

@ -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

View File

@ -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 }", "???")
}) })
}) })

View File

@ -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)
)
} }

View File

@ -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)
}

View File

@ -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)")
})

View File

@ -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})",
) // )
}) // })

View File

@ -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)")
})

View File

@ -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
})

View File

@ -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})")
}) })
}) })

View File

@ -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)))")

View File

@ -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')")
) )
}) })

View File

@ -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",

View File

@ -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;

View File

@ -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;

View File

@ -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) };
} }
} }

View File

@ -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)

View File

@ -4,6 +4,9 @@ type env = {
xyPointLength: int, xyPointLength: int,
} }
@genType
let defaultEnv: env
open DistributionTypes open DistributionTypes
@genType @genType

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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
} }
} }

View File

@ -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}`

View File

@ -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
}
} }

View File

@ -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)
}

View File

@ -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

View File

@ -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})

View File

@ -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)
}

View File

@ -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("$$")

View File

@ -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
}

View File

@ -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
} }
}

View File

@ -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

View File

@ -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
})
}

View File

@ -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

View File

@ -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.

View File

@ -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)
} }

View File

@ -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>>

View File

@ -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