eval partial tested

This commit is contained in:
Umur Ozkul 2022-04-21 22:56:06 +02:00
parent a02bc27852
commit 5038e2c691
3 changed files with 73 additions and 50 deletions

View File

@ -14,6 +14,9 @@ describe("Parse for Bindings", () => {
"y = x+1; y",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
)
})
describe("Parse for Bindings", () => {
testParsePartialToBe(
"x",
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
@ -50,8 +53,12 @@ describe("Eval with Bindings", () => {
)
})
/*
Partial code is a partial code fragment that is cut out from a larger code.
Therefore it does not end with an expression.
*/
Only.describe("Eval Partial", () => {
MyOnly.testEvalPartialBindingsToBe(
testEvalPartialBindingsToBe(
// A partial cannot end with an expression
"x",
list{("x", ExpressionValue.EvNumber(1.))},
@ -60,17 +67,17 @@ Only.describe("Eval Partial", () => {
testEvalPartialBindingsToBe(
"y=x",
list{("x", ExpressionValue.EvNumber(1.))},
"????",
"Ok({x: 1, y: 1})",
)
testEvalBindingsToBe(
testEvalPartialBindingsToBe(
"y=x+1",
list{("x", ExpressionValue.EvNumber(1.))},
"????",
"Ok({x: 1, y: 2})",
)
testEvalBindingsToBe(
MyOnly.testEvalPartialBindingsToBe(
"y = x+1; z = y",
list{("x", ExpressionValue.EvNumber(1.))},
"????",
"Ok({x: 1, y: 2, z: 2})",
)
})

View File

@ -1,14 +1,26 @@
/*
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.
*/
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
module ExpressionT = Reducer_Expression_T
open Reducer_ErrorValue
type expression = ExpressionT.expression
let dispatchMacroCall = (list: list<expression>, bindings: ExpressionT.bindings): result<
type reducerFn = (
expression,
'e,
> => {
ExpressionT.bindings,
) => result<ExpressionValue.expressionValue, errorValue>
let dispatchMacroCall = (
list: list<expression>,
bindings: ExpressionT.bindings,
reduceExpression: reducerFn,
): result<expression, 'e> => {
let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result<
expression,
errorValue,
@ -40,9 +52,16 @@ let dispatchMacroCall = (list: list<expression>, bindings: ExpressionT.bindings)
| ExpressionT.EList(list{
ExpressionT.EValue(EvCall("$let")),
ExpressionT.EValue(EvSymbol(aSymbol)),
expression,
expressionToReduce,
}) => {
let rNewExpression = replaceSymbols(expression, bindings)
let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings)
let rNewValue =
rNewExpressionToReduce->Result.flatMap(newExpressionToReduce =>
reduceExpression(newExpressionToReduce, bindings)
)
let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue))
rNewExpression->Result.map(newExpression =>
Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
)
@ -51,36 +70,38 @@ let dispatchMacroCall = (list: list<expression>, bindings: ExpressionT.bindings)
}
}
let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) => {
switch expression {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) =>
REExpressionExpected->Error
| _ => replaceSymbols(expression, bindings)
}
}
let doExportVariableExpression = (bindings: ExpressionT.bindings) => {
let emptyDictionary: Js.Dict.t<ExpressionValue.expressionValue> = Js.Dict.empty()
let reducedBindings = bindings->Belt.Map.String.keep (
(key, value) =>
let reducedBindings = bindings->Belt.Map.String.keep((key, value) =>
switch value {
| ExpressionT.EValue(_) => true
| _ => false
}
)
let externalBindings = reducedBindings->Belt.Map.String.reduce(
emptyDictionary,
(acc, key, expressionValue) => {
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
}
)
Js.Dict.set(acc, key, value)
acc
})
externalBindings->EvRecord->ExpressionT.EValue->Ok
}
let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) =>
switch expression {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) =>
REExpressionExpected->Error
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) =>
doExportVariableExpression(bindings)
| _ => replaceSymbols(expression, bindings)
}
switch list {
| list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok
@ -90,11 +111,6 @@ let dispatchMacroCall = (list: list<expression>, bindings: ExpressionT.bindings)
statement,
} =>
doBindStatement(statement, bindings)
| list{
ExpressionT.EValue(EvCall("$$bindExpression")),
ExpressionT.EBindings(bindings),
ExpressionT.EList(list{EValue(EvCall("$exportVariableExpression")), ..._}),
} => doExportVariableExpression(bindings)
| list{
ExpressionT.EValue(EvCall("$$bindExpression")),
ExpressionT.EBindings(bindings),

View File

@ -50,7 +50,15 @@ let defaultBindings: T.bindings = Belt.Map.String.empty
/*
Recursively evaluate/reduce the expression (Lisp AST)
*/
let reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
let rec reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
/*
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> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, reduceExpression)
/*
After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/
@ -60,14 +68,6 @@ let reduceExpression = (expression: t, bindings: T.bindings): result<expressionV
| _ => 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> =>
list->Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(bindings)
let rec seekMacros = (expression: t, bindings: T.bindings): result<t, 'e> =>
switch expression {
| T.EValue(_value) => expression->Ok