partials compile. TODO tests
This commit is contained in:
parent
7ab69a1708
commit
b1e2b27cee
|
@ -7,8 +7,34 @@ open Expect
|
||||||
let expectParseToBe = (expr: string, answer: string) =>
|
let expectParseToBe = (expr: string, answer: string) =>
|
||||||
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
|
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let expectParsePartialToBe = (expr: string, answer: string) =>
|
||||||
|
Reducer.parse(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.evaluateWBindings(expr, bindings)->ExpressionValue.toStringResult->expect->toBe(answer)
|
Reducer.evaluateWBindings(expr, bindings)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
|
||||||
|
let testDescriptionParseToBe = (desc, expr, answer) =>
|
||||||
|
test(desc, () => expectParseToBe(expr, answer))
|
||||||
|
|
||||||
|
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
|
||||||
|
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
||||||
|
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
|
||||||
|
module MySkip = {
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
}
|
||||||
|
module MyOnly = {
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
|
||||||
|
describe("external bindings", () => {
|
||||||
|
MySkip.testEvalBindingsToBe(
|
||||||
|
"y=1; x+1",
|
||||||
|
list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
|
"Ok(2)",
|
||||||
|
)
|
||||||
|
MySkip.testEvalBindingsToBe(
|
||||||
|
// This will go away when we have a proper parser
|
||||||
|
// x+1 is an expression not a block!
|
||||||
|
// Bindings are done for blocks only
|
||||||
|
"x+1",
|
||||||
|
list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
|
"Error(JS Exception: Error: Undefined symbol x)",
|
||||||
|
)
|
||||||
|
MySkip.testEvalToBe("x=1; y=1", "???")
|
||||||
|
})
|
|
@ -1,18 +1,6 @@
|
||||||
open Jest
|
open Jest
|
||||||
open Reducer_TestHelpers
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
let testDescriptionParseToBe = (desc, expr, answer) =>
|
|
||||||
test(desc, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
|
||||||
|
|
||||||
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
|
||||||
|
|
||||||
let testEvalBindingsToBe = (expr, bindings, answer) =>
|
|
||||||
test(expr, () => expectEvalBindingsToBe(expr, bindings, answer))
|
|
||||||
|
|
||||||
describe("reducer using mathjs parse", () => {
|
describe("reducer using mathjs parse", () => {
|
||||||
// Test the MathJs parser compatibility
|
// Test the MathJs parser compatibility
|
||||||
// Those tests toString that there is a semantic mapping from MathJs to Expression
|
// Those tests toString that there is a semantic mapping from MathJs to Expression
|
||||||
|
@ -105,25 +93,6 @@ describe("eval", () => {
|
||||||
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", "Error(Expression expected)")
|
||||||
})
|
})
|
||||||
describe("external bindings", () => {
|
|
||||||
testEvalBindingsToBe(
|
|
||||||
"y=1; x+1",
|
|
||||||
list{("x", ExpressionValue.EvNumber(1.))}->Js.Dict.fromList,
|
|
||||||
"Ok(2)",
|
|
||||||
)
|
|
||||||
testEvalBindingsToBe(
|
|
||||||
// This will go away when we have a proper parser
|
|
||||||
// x+1 is an expression not a block!
|
|
||||||
// Bindings are done for blocks only
|
|
||||||
"x+1",
|
|
||||||
list{("x", ExpressionValue.EvNumber(1.))}->Js.Dict.fromList,
|
|
||||||
"Error(JS Exception: Error: Undefined symbol x)",
|
|
||||||
)
|
|
||||||
testEvalToBe(
|
|
||||||
"x=1; y=1",
|
|
||||||
"???",
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("test exceptions", () => {
|
describe("test exceptions", () => {
|
||||||
|
|
|
@ -9,4 +9,5 @@ type expressionValue = Reducer_Expression.expressionValue
|
||||||
type externalBindings = Expression.externalBindings
|
type externalBindings = Expression.externalBindings
|
||||||
let evaluate = Expression.eval
|
let evaluate = Expression.eval
|
||||||
let evaluateWBindings = Expression.evalWBindings
|
let evaluateWBindings = Expression.evalWBindings
|
||||||
|
let evaluatePartialWBindings = Expression.evalPartialWBindings
|
||||||
let parse = Expression.parse
|
let parse = Expression.parse
|
||||||
|
|
|
@ -12,5 +12,13 @@ type externalBindings = Expression.externalBindings
|
||||||
@genType
|
@genType
|
||||||
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>
|
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>
|
||||||
@genType
|
@genType
|
||||||
let evaluateWBindings: (string, externalBindings) => result<expressionValue, Reducer_ErrorValue.errorValue>
|
let evaluateWBindings: (
|
||||||
|
string,
|
||||||
|
externalBindings,
|
||||||
|
) => result<expressionValue, Reducer_ErrorValue.errorValue>
|
||||||
|
@genType
|
||||||
|
let evaluatePartialWBindings: (
|
||||||
|
string,
|
||||||
|
externalBindings,
|
||||||
|
) => result<externalBindings, Reducer_ErrorValue.errorValue>
|
||||||
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
||||||
|
|
|
@ -42,6 +42,9 @@ let parse = (mathJsCode: string): result<t, errorValue> =>
|
||||||
let parsePartial = (mathJsCode: string): result<t, errorValue> =>
|
let parsePartial = (mathJsCode: string): result<t, errorValue> =>
|
||||||
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode)
|
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
|
let defaultBindings: T.bindings = Belt.Map.String.empty
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -117,30 +120,60 @@ let evalWBindingsExpression_ = (aExpression, bindings): result<expressionValue,
|
||||||
reduceExpression(aExpression, bindings)
|
reduceExpression(aExpression, bindings)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Evaluates MathJs code via Reducer using bindings and answers the result
|
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.
|
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 evalWBindings_ = (codeText: string, bindings: T.bindings) => {
|
let evalPartialWBindings_ = (codeText: string, bindings: T.bindings) => {
|
||||||
parsePartial(codeText)->Result.flatMap(expression => expression->evalWBindingsExpression_(bindings))
|
parsePartial(codeText)->Result.flatMap(expression =>
|
||||||
|
expression->evalWBindingsExpression_(bindings)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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->evalWBindingsExpression_(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 eval = (codeText: string) => {
|
||||||
parse(codeText)->Result.flatMap(expression => expression->evalWBindingsExpression_(defaultBindings))
|
parse(codeText)->Result.flatMap(expression =>
|
||||||
|
expression->evalWBindingsExpression_(defaultBindings)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type externalBindings = Js.Dict.t<expressionValue>
|
type externalBindings = Js.Dict.t<expressionValue>
|
||||||
|
|
||||||
|
let externalBindingsToBindings = (externalBindings: externalBindings): T.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, T.EValue(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Evaluates code with external bindings. External bindings are a record of expression values.
|
Evaluates code with external bindings. External bindings are a record of expression values.
|
||||||
*/
|
*/
|
||||||
let evalWBindings = (code: string, externalBindings: externalBindings) => {
|
let evalWBindings = (code: string, externalBindings: externalBindings) => {
|
||||||
let keys = Js.Dict.keys(externalBindings)
|
let bindings = externalBindings -> externalBindingsToBindings
|
||||||
let bindings = keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
evalOuterWBindings_(code, bindings)
|
||||||
let value = Js.Dict.unsafeGet(externalBindings, key)
|
}
|
||||||
acc->Belt.Map.String.set(key, T.EValue(value))
|
|
||||||
})
|
/*
|
||||||
evalWBindings_(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 evalPartialWBindings = (code: string, externalBindings: externalBindings): result<externalBindings, 'e> => {
|
||||||
|
let bindings = externalBindings -> externalBindingsToBindings
|
||||||
|
let answer = evalPartialWBindings_(code, bindings)
|
||||||
|
answer->Result.flatMap(answer => switch answer {
|
||||||
|
| EvRecord(aRecord) => Ok(aRecord)
|
||||||
|
| _ => RETodo("TODO: External bindings must be returned")->Error
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,12 +130,11 @@ and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
|
||||||
rPreviousBindings->Result.flatMap(previousBindings => {
|
rPreviousBindings->Result.flatMap(previousBindings => {
|
||||||
let rStatement: result<expression, errorValue> = switch tagOrNode {
|
let rStatement: result<expression, errorValue> = switch tagOrNode {
|
||||||
| BlockNode(node) => fromNode(node)
|
| BlockNode(node) => fromNode(node)
|
||||||
| BlockTag(tag) => switch tag {
|
| BlockTag(tag) =>
|
||||||
| ImportVariablesStatement =>
|
switch tag {
|
||||||
passToFunction("$$importVariablesStatement", list{}->Ok)
|
| ImportVariablesStatement => passToFunction("$importVariablesStatement", list{}->Ok)
|
||||||
| ExportVariablesExpression =>
|
| ExportVariablesExpression => passToFunction("$exportVariablesExpression", list{}->Ok)
|
||||||
passToFunction("$$exportVariablesExpression", list{}->Ok)
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let bindName = if i == lastIndex {
|
let bindName = if i == lastIndex {
|
||||||
|
@ -178,3 +177,27 @@ let fromPartialNode = (mathJsNode: Parse.node): result<expression, errorValue> =
|
||||||
rFinalExpression
|
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 = Js.Array2.concatMany([BlockTag(ImportVariablesStatement)], [blocksOrTags])
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let casePartialExpression = (node: Parse.node) => {
|
||||||
|
let completed = Js.Array2.concatMany(
|
||||||
|
[BlockTag(ImportVariablesStatement)],
|
||||||
|
[[BlockNode(node)]],
|
||||||
|
)
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
||||||
|
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
|
||||||
|
| _ => casePartialExpression(mathJsNode)
|
||||||
|
}
|
||||||
|
rFinalExpression
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user