variables and statements

format only reducer

reformat lint

multi-line test

spelling

multi-line semantic mapping

todo multi-line eval

multi-line tests todo

change context to bindings

simplify tests

rename exception test methods

bindings is an expression value

make bindings callable

reformat

Emphasize the nature of Lisp AST

Initial definition of macros

make functions private

fixed functionNode type casting

macro call skeleton

sort ReducerInterface

fix test

macros skeleton

bindings is not a value

assignment semantics

let semantics defined

format

reformat

reformat

TODO function calls and list hd variables are confused

reformat

tmp

works

reformat

reformat

add test

reformat

add test
This commit is contained in:
Umur Ozkul 2022-04-08 14:14:37 +02:00
parent 1d550353c9
commit 660c0c70ae
12 changed files with 288 additions and 103 deletions

View File

@ -6,15 +6,18 @@ open Expect
let expectEvalToBe = (expr: string, answer: string) =>
Reducer.eval(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
let testEval = (expr, answer) =>
test(expr, () => expectEvalToBe(expr, answer))
describe("builtin", () => {
// All MathJs operators and functions are available for string, number and boolean
// .e.g + - / * > >= < <= == /= not and or
// See https://mathjs.org/docs/expressions/syntax.html
// See https://mathjs.org/docs/reference/functions.html
test("-1", () => expectEvalToBe("-1", "Ok(-1)"))
test("1-1", () => expectEvalToBe("1-1", "Ok(0)"))
test("2>1", () => expectEvalToBe("2>1", "Ok(true)"))
test("concat('a','b')", () => expectEvalToBe("concat('a','b')", "Ok('ab')"))
testEval("-1", "Ok(-1)")
testEval("1-1", "Ok(0)")
testEval("2>1", "Ok(true)")
testEval("concat('a','b')", "Ok('ab')")
})
describe("builtin exception", () => {

View File

@ -11,9 +11,11 @@ let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)
let testDescParse = (desc, expr, answer) => test(desc, () => expectParseToBe(expr, answer))
let skipTestParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
module MySkip = {
let testParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
let skipDescTestParse = (desc, expr, answer) => Skip.test(desc, () => expectParseToBe(expr, answer))
let testDescParse = (desc, expr, answer) => Skip.test(desc, () => expectParseToBe(expr, answer))
}
describe("MathJs parse", () => {
describe("literals operators paranthesis", () => {
@ -26,6 +28,10 @@ describe("MathJs parse", () => {
testParse("(1+2)", "(add(1, 2))")
})
describe("multi-line", () => {
testParse("1; 2", "{1; 2}")
})
describe("variables", () => {
testParse("x = 1", "x = 1")
testParse("x", "x")
@ -33,16 +39,16 @@ describe("MathJs parse", () => {
})
describe("functions", () => {
skipTestParse("identity(x) = x", "???")
skipTestParse("identity(x)", "???")
MySkip.testParse("identity(x) = x", "???")
MySkip.testParse("identity(x)", "???")
})
describe("arrays", () => {
test("empty", () => expectParseToBe("[]", "[]"))
test("define", () => expectParseToBe("[0, 1, 2]", "[0, 1, 2]"))
test("define with strings", () => expectParseToBe("['hello', 'world']", "['hello', 'world']"))
skipTestParse("range(0, 4)", "range(0, 4)")
test("index", () => expectParseToBe("([0,1,2])[1]", "([0, 1, 2])[1]"))
testDescParse("empty", "[]", "[]")
testDescParse("define", "[0, 1, 2]", "[0, 1, 2]")
testDescParse("define with strings", "['hello', 'world']", "['hello', 'world']")
MySkip.testParse("range(0, 4)", "range(0, 4)")
testDescParse("index", "([0,1,2])[1]", "([0, 1, 2])[1]")
})
describe("records", () => {
@ -51,10 +57,10 @@ describe("MathJs parse", () => {
})
describe("comments", () => {
skipDescTestParse("define", "# This is a comment", "???")
MySkip.testDescParse("define", "# This is a comment", "???")
})
describe("if statement", () => {
skipDescTestParse("define", "if (true) { 1 } else { 0 }", "???")
describe("if statement", () => { // TODO Tertiary operator instead
MySkip.testDescParse("define", "if (true) { 1 } else { 0 }", "???")
})
})

View File

@ -42,6 +42,15 @@ describe("reducer using mathjs parse", () => {
"Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))",
)
})
describe("multi-line", () => {
testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))")
testParseToBe("1+1; 2+1", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))")
})
describe("assignment", () => {
testParseToBe("x=1; x", "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)))")
})
})
describe("eval", () => {
@ -71,13 +80,26 @@ describe("eval", () => {
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
})
describe("multi-line", () => {
testEvalToBe("1; 2", "Error(Assignment expected)")
testEvalToBe("1+1; 2+1", "Error(Assignment expected)")
})
describe("assignment", () => {
testEvalToBe("x=1; x", "Ok(1)")
testEvalToBe("x=1+1; x+1", "Ok(3)")
testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
testEvalToBe("1; x=1", "Error(Assignment expected)")
testEvalToBe("1; 1", "Error(Assignment expected)")
testEvalToBe("x=1; x=1", "Error(Expression expected)")
})
})
describe("test exceptions", () => {
testDescEvalToBe(
"javascript exception",
"jsraise('div by 0')",
"javascriptraise('div by 0')",
"Error(JS Exception: Error: 'div by 0')",
)
testDescEvalToBe("rescript exception", "resraise()", "Error(TODO: unhandled rescript exception)")
testDescEvalToBe("rescript exception", "rescriptraise()", "Error(TODO: unhandled rescript exception)")
})

View File

@ -12,6 +12,7 @@
"test:watch": "jest --watchAll",
"coverage": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test; bisect-ppx-report html",
"rescript:format": "find . -type f \\( -name '*.res' -o -name '*.resi' \\) -exec sh -c 'bsc -format {} | sponge {}' \\;",
"reducer:format": "find src/rescript/Reducer src/rescript/ReducerInterface -type f \\( -name '*.res' -o -name '*.resi' \\) -exec sh -c 'bsc -format {} | sponge {}' \\;",
"all": "yarn build && yarn bundle && yarn test"
},
"keywords": [

View File

@ -14,8 +14,8 @@ exception TestRescriptException
let callInternal = (call: functionCall): result<'b, errorValue> => {
let callMathJs = (call: functionCall): result<'b, errorValue> =>
switch call {
| ("jsraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
| ("resraise", _) => raise(TestRescriptException) // For Tests
| ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests
| ("rescriptraise", _) => raise(TestRescriptException) // For Tests
| call => call->toStringFunctionCall->MathJs.Eval.eval
}

View File

@ -1,8 +1,12 @@
type errorValue =
| REArrayIndexNotFound(string, int)
| REAssignmentExpected
| REExpressionExpected
| REFunctionExpected(string)
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception
| REMacroNotFound(string)
| RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string)
| RESyntaxError(string)
| RETodo(string) // To do
@ -11,6 +15,8 @@ type t = errorValue
let errorToString = err =>
switch err {
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
| REAssignmentExpected => "Assignment expected"
| REExpressionExpected => "Expression expected"
| REFunctionExpected(msg) => `Function expected: ${msg}`
| REJavaScriptExn(omsg, oname) => {
let answer = "JS Exception:"
@ -24,7 +30,9 @@ let errorToString = err =>
}
answer
}
| REMacroNotFound(macro) => `Macro not found: ${macro}`
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
| RESyntaxError(desc) => `Syntax Error: ${desc}`
| RETodo(msg) => `TODO: ${msg}`
}

View File

@ -15,6 +15,7 @@ type t = expression
*/
let rec toString = expression =>
switch expression {
| T.EBindings(bindings) => "$$bound"
| T.EList(aList) =>
`(${Belt.List.map(aList, aValue => toString(aValue))
->Extra.List.interperse(" ")
@ -38,50 +39,137 @@ let parse_ = (expr: string, parser, converter): result<t, errorValue> =>
let parse = (mathJsCode: string): result<t, errorValue> =>
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
module MapString = Belt.Map.String
type bindings = MapString.t<unit>
let defaultBindings: bindings = MapString.fromArray([])
// TODO Define bindings for function execution context
let defaultBindings: T.bindings = Belt.Map.String.empty
/*
After reducing each level of code tree, we have a value list to evaluate
Recursively evaluate/reduce the expression (Lisp AST)
*/
let reduceValueList = (valueList: list<expressionValue>): result<expressionValue, 'e> =>
switch valueList {
| list{EvSymbol(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
}
/*
Recursively evaluate/reduce the code tree
*/
let rec reduceExpression = (expression: t, bindings): result<expressionValue, 'e> =>
switch expression {
| T.EValue(value) => value->Ok
| T.EList(list) => {
let racc: result<list<expressionValue>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
racc,
each: expression,
) =>
racc->Result.flatMap(acc => {
each
->reduceExpression(bindings)
->Result.flatMap(newNode => {
acc->Belt.List.add(newNode)->Ok
})
})
)
racc->Result.flatMap(acc => acc->reduceValueList)
let rec reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
/*
After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/
let reduceValueList = (valueList: list<expressionValue>): result<expressionValue, 'e> =>
switch valueList {
| list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
}
/*
Macros are like functions but instead of taking values as parameters,
they take expressions as parameters and return a new expression.
Macros are used to define language building blocks. They are like Lisp macros.
*/
let doMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> => {
let dispatchMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> => {
let rec replaceSymbols = (expression: t, bindings: T.bindings): result<t, errorValue> =>
switch expression {
| T.EValue(EvSymbol(aSymbol)) =>
switch bindings->Belt.Map.String.get(aSymbol) {
| Some(boundExpression) => boundExpression->Ok
| None => RESymbolNotFound(aSymbol)->Error
}
| T.EValue(_) => expression->Ok
| T.EBindings(_) => expression->Ok
| T.EList(list) =>
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
})
})
)
->Result.map(acc => acc->T.EList)
}
let doBindStatement = (statement: t, bindings: T.bindings) => {
switch statement {
| T.EList(list{T.EValue(EvCall("$let")), T.EValue(EvSymbol(aSymbol)), expression}) => {
let rNewExpression = replaceSymbols(expression, bindings)
rNewExpression->Result.map(newExpression =>
Belt.Map.String.set(bindings, aSymbol, newExpression)->T.EBindings
)
}
| _ => REAssignmentExpected->Error
}
}
let doBindExpression = (expression: t, bindings: T.bindings) => {
switch expression {
| T.EList(list{T.EValue(EvCall("$let")), ..._}) => REExpressionExpected->Error
| _ => replaceSymbols(expression, bindings)
}
}
switch list {
| list{T.EValue(EvCall("$$bindings"))} => bindings->EBindings->Ok
| list{T.EValue(EvCall("$$bindStatement")), T.EBindings(bindings), statement} =>
doBindStatement(statement, bindings)
| list{T.EValue(EvCall("$$bindExpression")), T.EBindings(bindings), expression} =>
doBindExpression(expression, bindings)
| _ => list->T.EList->Ok
}
}
list->dispatchMacroCall(bindings)
}
let rec seekMacros = (expression: t, bindings: T.bindings): result<t, 'e> =>
switch expression {
| T.EValue(value) => expression->Ok
| T.EList(list) => {
let racc: result<list<t>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
racc,
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> =>
switch expression {
| T.EValue(value) => value->Ok
| T.EList(list) => {
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)
}
}
let rExpandedExpression: result<t, 'e> = expression->seekMacros(bindings)
rExpandedExpression->Result.flatMap(expandedExpression =>
expandedExpression->reduceExpandedExpression
)
}
let evalWBindingsExpression = (aExpression, bindings): result<expressionValue, 'e> =>
reduceExpression(aExpression, bindings)
/*
Evaluates MathJs code via Lisp using bindings and answers the result
*/
let evalWBindings = (codeText: string, bindings: bindings) => {
let evalWBindings = (codeText: string, bindings: T.bindings) => {
parse(codeText)->Result.flatMap(code => code->evalWBindingsExpression(bindings))
}

View File

@ -1,16 +0,0 @@
module Result = Belt.Result
module T = Reducer_Expression_T
type expression = T.expression
type expressionValue = ReducerInterface.ExpressionValue.expressionValue
type t = expression
let toString: T.expression => Js.String.t
let toStringResult: result<T.expression, 'a> => string
let parse: string => result<expression, Reducer_ErrorValue.t>
module MapString = Belt.Map.String
type bindings = MapString.t<unit>
let defaultBindings: bindings
let reduceValueList: list<expressionValue> => result<expressionValue, Reducer_ErrorValue.t>
let reduceExpression: (expression, 'a) => result<expressionValue, Reducer_ErrorValue.t>
let evalWBindingsExpression: (expression, 'a) => result<expressionValue, Reducer_ErrorValue.t>
let evalWBindings: (string, bindings) => Result.t<expressionValue, Reducer_ErrorValue.t>
let eval: string => Result.t<expressionValue, Reducer_ErrorValue.t>

View File

@ -1,5 +1,15 @@
open ReducerInterface.ExpressionValue
/*
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
apply e1, e2 -> apply e3 -> ... -> apply eN
This is Lisp semantics. It holds true in both eager and lazy evaluations.
A Lisp AST contains only expressions/primitive values to apply to their left.
The act of defining the semantics of a functional language is to write it in terms of Lisp AST.
*/
type rec expression =
| EList(list<expression>) // A list to map-reduce
| 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<expression>

View File

@ -12,17 +12,16 @@ type blockNode = {...node, "blocks": array<block>}
//conditionalNode
type constantNode = {...node, "value": unit}
//functionAssignmentNode
type functionNode = {...node, "fn": string, "args": array<node>}
type indexNode = {...node, "dimensions": array<node>}
type objectNode = {...node, "properties": Js.Dict.t<node>}
type accessorNode = {...node, "object": node, "index": indexNode}
type operatorNode = {...functionNode, "op": string}
type accessorNode = {...node, "object": node, "index": indexNode, "name": string}
//parenthesisNode
type parenthesisNode = {...node, "content": node}
//rangeNode
//relationalNode
type symbolNode = {...node, "name": string}
type functionNode = {...node, "fn": unit, "args": array<node>}
type operatorNode = {...functionNode, "op": string}
type assignmentNode = {...node, "object": symbolNode, "value": node}
type assignmentNodeWAccessor = {...node, "object": accessorNode, "value": node}
type assignmentNodeWIndex = {...assignmentNodeWAccessor, "index": Js.null<indexNode>}
@ -93,6 +92,18 @@ let castNodeType = (node: node) => {
}
}
external unitAsSymbolNode: unit => symbolNode = "%identity"
external unitAsString: unit => string = "%identity"
let nameOfFunctionNode = (fNode: functionNode): string => {
let name = fNode["fn"]
if Js.typeof(name) == "string" {
name->unitAsString
} else {
(name->unitAsSymbolNode)["name"]
}
}
let rec toString = (mathJsNode: mathJsNode): string => {
let toStringValue = (a: 'a): string =>
if Js.typeof(a) == "string" {
@ -108,7 +119,7 @@ let rec toString = (mathJsNode: mathJsNode): string => {
->Js.String.concatMany("")
let toStringFunctionNode = (fnode: functionNode): string =>
`${fnode["fn"]}(${fnode["args"]->toStringNodeArray})`
`${fnode->nameOfFunctionNode}(${fnode["args"]->toStringNodeArray})`
let toStringObjectEntry = ((key: string, value: node)): string =>
`${key}: ${value->toStringMathJsNode}`

View File

@ -1,11 +1,11 @@
module ErrorValue = Reducer_ErrorValue
module ExpressionValue = ReducerInterface.ExpressionValue
module ExtressionT = Reducer_Expression_T
module ExpressionT = Reducer_Expression_T
module JavaScript = Reducer_Js
module Parse = Reducer_MathJs_Parse
module Result = Belt.Result
type expression = ExtressionT.expression
type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue
type errorValue = ErrorValue.errorValue
@ -18,10 +18,19 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
)
)
let castFunctionNode = fNode => {
let fn = fNode["fn"]->ExpressionValue.EvSymbol->ExtressionT.EValue
let toEvCallValue = (name: string): expression =>
name->ExpressionValue.EvCall->ExpressionT.EValue
let toEvSymbolValue = (name: string): expression =>
name->ExpressionValue.EvSymbol->ExpressionT.EValue
let passToFunction = (fName: string, rLispArgs): result<expression, errorValue> => {
let fn = fName->toEvCallValue
rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok)
}
let caseFunctionNode = fNode => {
let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
lispArgs->Result.map(argsCode => list{fn, ...argsCode}->ExtressionT.EList)
passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
}
let caseObjectNode = oNode => {
@ -34,15 +43,16 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
fromNode(value)->Result.map(valueExpression => {
let entryCode =
list{
key->ExpressionValue.EvString->ExtressionT.EValue,
key->ExpressionValue.EvString->ExpressionT.EValue,
valueExpression,
}->ExtressionT.EList
}->ExpressionT.EList
list{entryCode, ...acc}
})
)
)
let lispName = "$constructRecord"->ExpressionValue.EvSymbol->ExtressionT.EValue
rargs->Result.map(args => list{lispName, ExtressionT.EList(args)}->ExtressionT.EList)
rargs->Result.flatMap(args =>
passToFunction("$constructRecord", list{ExpressionT.EList(args)}->Ok)
) // $consturctRecord gets a single argument: List of key-value paiers
}
oNode["properties"]->Js.Dict.entries->Belt.List.fromArray->fromObjectEntries
@ -60,30 +70,69 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
})
),
)
rpropertyCodeList->Result.map(propertyCodeList => ExtressionT.EList(propertyCodeList))
rpropertyCodeList->Result.map(propertyCodeList => ExpressionT.EList(propertyCodeList))
}
let caseAccessorNode = (objectNode, indexNode) => {
let fn = "$atIndex"->ExpressionValue.EvSymbol->ExtressionT.EValue
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
fromNode(objectNode)->Result.map(objectCode =>
list{fn, objectCode, indexCode}->ExtressionT.EList
fromNode(objectNode)->Result.flatMap(objectCode =>
passToFunction("$atIndex", list{objectCode, indexCode}->Ok)
)
})
}
switch typedMathJsNode {
| MjArrayNode(aNode) =>
aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExtressionT.EList(list))
| MjConstantNode(cNode) =>
cNode["value"]->JavaScript.Gate.jsToEv->Result.map(v => v->ExtressionT.EValue)
| MjFunctionNode(fNode) => fNode->castFunctionNode
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->castFunctionNode
| MjParenthesisNode(pNode) => pNode["content"]->fromNode
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
| MjObjectNode(oNode) => caseObjectNode(oNode)
| MjSymbolNode(sNode) => sNode["name"]->ExpressionValue.EvSymbol->ExtressionT.EValue->Ok
| MjIndexNode(iNode) => caseIndexNode(iNode)
let caseAssignmentNode = aNode => {
let symbol = aNode["object"]["name"]->toEvSymbolValue
let rValueExpression = fromNode(aNode["value"])
rValueExpression->Result.flatMap(valueExpression => {
let lispArgs = list{symbol, valueExpression}->Ok
passToFunction("$let", lispArgs)
})
}
let caseArrayNode = aNode => {
aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExpressionT.EList(list))
}
let caseBlockNode = (bNode): result<expression, errorValue> => {
let blocks = bNode["blocks"]
let initialBindings = passToFunction("$$bindings", list{}->Ok)
let lastIndex = Belt.Array.length(blocks) - 1
blocks->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, block, i) => {
rPreviousBindings->Result.flatMap(previousBindings => {
let node = block["node"]
let rStatement: result<expression, errorValue> = node->fromNode
let bindName = if i == lastIndex {
"$$bindExpression"
} else {
"$$bindStatement"
}
rStatement->Result.flatMap((statement: expression) => {
let lispArgs = list{previousBindings, statement}->Ok
passToFunction(bindName, lispArgs)
})
})
})
}
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
| MjArrayNode(aNode) => caseArrayNode(aNode)
| MjAssignmentNode(aNode) => caseAssignmentNode(aNode)
| MjSymbolNode(sNode) => {
let expr: expression = toEvSymbolValue(sNode["name"])
let rExpr: result<expression, errorValue> = expr->Ok
rExpr
}
| MjBlockNode(bNode) => caseBlockNode(bNode)
// | MjBlockNode(bNode) => "statement"->toEvSymbolValue->Ok
| MjConstantNode(cNode) =>
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
| MjFunctionNode(fNode) => fNode->caseFunctionNode
| MjIndexNode(iNode) => caseIndexNode(iNode)
| MjObjectNode(oNode) => caseObjectNode(oNode)
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode
| MjParenthesisNode(pNode) => pNode["content"]->fromNode
}
rFinalExpression
})

View File

@ -6,19 +6,21 @@ module Extra_Array = Reducer_Extra_Array
module ErrorValue = Reducer_ErrorValue
type rec expressionValue =
| EvArray(array<expressionValue>)
| EvBool(bool)
| EvCall(string) // External function call
| EvDistribution(GenericDist_Types.genericDist)
| EvNumber(float)
| EvRecord(Js.Dict.t<expressionValue>)
| EvString(string)
| EvSymbol(string)
| EvArray(array<expressionValue>)
| EvRecord(Js.Dict.t<expressionValue>)
| EvDistribution(GenericDist_Types.genericDist)
type functionCall = (string, array<expressionValue>)
let rec toString = aValue =>
switch aValue {
| EvBool(aBool) => Js.String.make(aBool)
| EvCall(fName) => `:${fName}`
| EvNumber(aNumber) => Js.String.make(aNumber)
| EvString(aString) => `'${aString}'`
| EvSymbol(aString) => `:${aString}`
@ -39,12 +41,13 @@ let rec toString = aValue =>
->Js.String.concatMany("")
`{${pairs}}`
}
| EvDistribution(dist) => `${GenericDist.toString(dist)}`
| EvDistribution(dist) => GenericDist.toString(dist)
}
let toStringWithType = aValue =>
switch aValue {
| EvBool(_) => `Bool::${toString(aValue)}`
| EvCall(_) => `Call::${toString(aValue)}`
| EvNumber(_) => `Number::${toString(aValue)}`
| EvString(_) => `String::${toString(aValue)}`
| EvSymbol(_) => `Symbol::${toString(aValue)}`