Attempt of refactor for Bindings and Paramaters

This commit is contained in:
Ozzie Gooen 2022-04-25 16:26:23 -04:00
parent 7d4e3072b8
commit d4025e7f02
6 changed files with 83 additions and 68 deletions

View File

@ -20,49 +20,11 @@ let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings
expression,
errorValue,
> => {
let getParameters = (bindings: ExpressionT.bindings): array<string> => {
let eParameters = Belt.Map.String.getWithDefault(bindings, "$parameters", EParameters([]))
switch eParameters {
| EParameters(parameters) => parameters
| _ => []
}
}
let putParameters = (
bindings: ExpressionT.bindings,
parameters: array<string>,
): ExpressionT.bindings =>
Belt.Map.String.set(bindings, "$parameters", ExpressionT.EParameters(parameters))
let answerBindingIfNotParameter = (aSymbol, defaultExpression, parameters, bindings) =>
switch Js.Array2.some(parameters, a => a == aSymbol) {
| true => defaultExpression->Ok // We cannot bind the parameters with global values
| false =>
switch bindings->Belt.Map.String.get(aSymbol) {
| Some(boundExpression) => boundExpression->Ok
| None => RESymbolNotFound(aSymbol)->Error
}
}
let answerCallBindingIfNotParameter = (aSymbol, defaultExpression, parameters, bindings) =>
switch Js.Array2.some(parameters, a => a == aSymbol) {
| true => defaultExpression->Ok // We cannot bind the parameters with global values
| false =>
switch bindings->Belt.Map.String.get(aSymbol) {
| Some(boundExpression) => boundExpression->Ok
| None => defaultExpression->Ok
}
}
switch expression {
| ExpressionT.EValue(EvSymbol(aSymbol)) => {
let parameters = getParameters(bindings)
answerBindingIfNotParameter(aSymbol, expression, parameters, bindings)
}
| ExpressionT.EValue(EvCall(aSymbol)) => {
let parameters = getParameters(bindings)
answerCallBindingIfNotParameter(aSymbol, expression, parameters, bindings)
}
| ExpressionT.EValue(EvSymbol(aSymbol)) =>
ExpressionT.Bindings.getResultIfNotInParameters(bindings, aSymbol)->E.O2.default(Ok(expression))
| ExpressionT.EValue(EvCall(aSymbol)) =>
ExpressionT.Bindings.getIfNotInParameters(bindings, aSymbol)->E.O2.default(expression)->Ok
| ExpressionT.EValue(_) => expression->Ok
| ExpressionT.EBindings(_) => expression->Ok
| ExpressionT.EParameters(_) => expression->Ok
@ -71,9 +33,7 @@ let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings
ExpressionT.EParameters(parameters),
expr,
}) => {
let oldParameters = getParameters(bindings)
let newParameters = oldParameters->Js.Array2.concat(parameters)
let newBindings = putParameters(bindings, newParameters)
let newBindings = ExpressionT.Bindings.Parameters.setArray(bindings, parameters)
let rNewExpr = replaceSymbols(expr, newBindings)
rNewExpr->Result.flatMap(newExpr =>
ExpressionT.EList(list{
@ -119,7 +79,7 @@ let dispatchMacroCall = (
let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue))
rNewExpression->Result.map(newExpression =>
Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
ExpressionT.Bindings.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
)
}
| _ => REAssignmentExpected->Error

View File

@ -6,11 +6,14 @@ module Result = Belt.Result
type errorValue = ErrorValue.errorValue
type expression = ExpressionT.expression
let passToFunction = (fName: string, lispArgs: list<expression>): expression => {
let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue
let fn = fName->toEvCallValue
list{fn, ...lispArgs}->ExpressionT.EList
}
let toEvCallValue = (name: string): expression => ExpressionT.EValue(ExpressionValue.EvCall(name))
let toEvSymbolValue = (name: string): expression =>
name->ExpressionValue.EvSymbol->ExpressionT.EValue
let toEvSymbolValue = (name: string): expression => ExpressionT.EValue(
ExpressionValue.EvSymbol(name),
)
//From Ozzie: I think this could really use a more descriptive name.
let passToFunction = (fName: string, lispArgs: list<expression>): expression => {
let fn = toEvCallValue(fName)
ExpressionT.EList(list{fn, ...lispArgs})
}

View File

@ -8,9 +8,60 @@ open ReducerInterface.ExpressionValue
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 kind of statements return bindings; for internal use only
| EParameters(array<string>) // for $defun; for internal use only
and bindings = Belt.Map.String.t<expression>
module Bindings = {
type t = bindings
let get = (t: t, str: string) => Belt.Map.String.get(t, str)
let set = (t: t, key: string, value: expression): t => {
Belt.Map.String.set(t, key, value)
}
let getResult = (t: t, str: string): result<expression, Reducer_ErrorValue.errorValue> =>
get(t, str) |> E.O.toResult(Reducer_ErrorValue.RESymbolNotFound(str))
module Parameters = {
let parametersKey = "$parameters"
let default = EParameters([])
let get = (t: t): array<string> => {
let expressionParameters = Belt.Map.String.getWithDefault(t, parametersKey, default)
switch expressionParameters {
| EParameters(parameters) => parameters
| _ => []
}
}
let has = (t: t, s: string): bool => E.A.has(get(t), s)
let setArray = (t: t, newParameters: array<string>): t => {
let oldParameters = get(t)
let updatedParameters = Js.Array2.concat(oldParameters, newParameters)
Belt.Map.String.set(t, parametersKey, EParameters(updatedParameters))
}
}
// We cannot bind the parameters with global values
let getIfNotInParameters = (t: t, aSymbol: string):option<expression> => {
if Parameters.has(t, aSymbol) {
None
} else {
get(t, aSymbol)
}
}
// We cannot bind the parameters with global values
let getResultIfNotInParameters = (t: t, aSymbol: string) => {
if Parameters.has(t, aSymbol) {
None
} else {
Some(getResult(t, aSymbol))
}
}
}

View File

@ -1,4 +1,4 @@
module Builder = Reducer_Expression_Builder
module ExpressionBuilder = Reducer_Expression_Builder
module ErrorValue = Reducer_ErrorValue
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
@ -31,7 +31,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
let caseFunctionNode = fNode => {
let rLispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
rLispArgs->Result.flatMap(lispArgs =>
Builder.passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)->Ok
ExpressionBuilder.passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)->Ok
)
}
@ -53,7 +53,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
)
)
rargs->Result.flatMap(args =>
Builder.passToFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok
ExpressionBuilder.passToFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok
) // $constructRecord gets a single argument: List of key-value paiers
}
@ -78,27 +78,27 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
let caseAccessorNode = (objectNode, indexNode) => {
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
fromNode(objectNode)->Result.flatMap(objectCode =>
Builder.passToFunction("$atIndex", list{objectCode, indexCode})->Ok
ExpressionBuilder.passToFunction("$atIndex", list{objectCode, indexCode})->Ok
)
})
}
let caseAssignmentNode = aNode => {
let symbol = aNode["object"]["name"]->Builder.toEvSymbolValue
let symbol = aNode["object"]["name"]->ExpressionBuilder.toEvSymbolValue
let rValueExpression = fromNode(aNode["value"])
rValueExpression->Result.flatMap(valueExpression =>
Builder.passToFunction("$let", list{symbol, valueExpression})->Ok
ExpressionBuilder.passToFunction("$let", list{symbol, valueExpression})->Ok
)
}
let caseFunctionAssignmentNode = faNode => {
let symbol = faNode["name"]->Builder.toEvSymbolValue
let symbol = faNode["name"]->ExpressionBuilder.toEvSymbolValue
let rValueExpression = fromNode(faNode["expr"])
rValueExpression->Result.flatMap(valueExpression => {
let lispParams = faNode["params"]->ExpressionT.EParameters
let lambda = Builder.passToFunction("$lambda", list{lispParams, valueExpression})
Builder.passToFunction("$let", list{symbol, lambda})->Ok
let lambda = ExpressionBuilder.passToFunction("$lambda", list{lispParams, valueExpression})
ExpressionBuilder.passToFunction("$let", list{symbol, lambda})->Ok
})
}
@ -111,7 +111,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
| MjArrayNode(aNode) => caseArrayNode(aNode)
| MjAssignmentNode(aNode) => caseAssignmentNode(aNode)
| MjSymbolNode(sNode) => {
let expr: expression = Builder.toEvSymbolValue(sNode["name"])
let expr: expression = ExpressionBuilder.toEvSymbolValue(sNode["name"])
let rExpr: result<expression, errorValue> = expr->Ok
rExpr
}
@ -128,7 +128,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
rFinalExpression
})
and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
let initialBindings = Builder.passToFunction("$$bindings", list{})->Ok
let initialBindings = ExpressionBuilder.passToFunction("$$bindings", list{})->Ok
let lastIndex = Belt.Array.length(tagOrNodes) - 1
tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => {
rPreviousBindings->Result.flatMap(previousBindings => {
@ -137,9 +137,9 @@ and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
| BlockTag(tag) =>
switch tag {
| ImportVariablesStatement =>
Builder.passToFunction("$importVariablesStatement", list{})->Ok
ExpressionBuilder.passToFunction("$importVariablesStatement", list{})->Ok
| ExportVariablesExpression =>
Builder.passToFunction("$exportVariablesExpression", list{})->Ok
ExpressionBuilder.passToFunction("$exportVariablesExpression", list{})->Ok
}
}
@ -150,7 +150,7 @@ and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
}
rStatement->Result.flatMap((statement: expression) => {
Builder.passToFunction(bindName, list{previousBindings, statement})->Ok
ExpressionBuilder.passToFunction(bindName, list{previousBindings, statement})->Ok
})
})
})

View File

@ -83,4 +83,4 @@ let toStringResultRecord = x =>
switch x {
| Ok(a) => `Ok(${toStringRecord(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
}
}

View File

@ -338,6 +338,7 @@ module A = {
let getBy = Belt.Array.getBy
let last = a => get(a, length(a) - 1)
let first = get(_, 0)
let has = (r, el) => Belt.Array.some(r, (x) => x == el)
let hasBy = (r, fn) => Belt.Array.getBy(r, fn) |> O.isSome
let fold_left = Array.fold_left
let fold_right = Array.fold_right