fixed function f not bound

This commit is contained in:
Umur Ozkul 2022-05-02 18:44:35 +02:00
parent 5a0b436932
commit 6a3b35eb4a
8 changed files with 158 additions and 45 deletions

View File

@ -1,10 +1,11 @@
open Jest
open Expect
module Macro = Reducer_Expression_Macro
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_ = (
@ -21,7 +22,7 @@ let testMacro_ = (
ExpressionValue.defaultEnvironment,
Expression.reduceExpression,
)
->T.toStringResult
->ExpressionWithContext.toStringResult
->expect
->toEqual(expectedCode)
)

View File

@ -39,15 +39,27 @@ describe("symbol not defined", () => {
})
Only.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(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)", "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)})") //TODO: f is not found
MyOnly.testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2)", "????") //TODO: f is not found
MySkip.testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "????") //TODO: f is not found
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)")
})
Skip.describe("function trics", () => {

View File

@ -6,19 +6,22 @@
module Bindings = Reducer_Expression_Bindings
module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext
module Result = Belt.Result
open Reducer_Expression_ExpressionBuilder
type expression = ExpressionT.expression
type environment = ExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue
type expressionWithContext = ExpressionWithContext.expressionWithContext
let dispatchMacroCall = (
macroExpression: expression,
bindings: ExpressionT.bindings,
environment,
reduceExpression: ExpressionT.reducerFn,
): result<expression, errorValue> => {
): result<expressionWithContext, errorValue> => {
let doBindStatement = (bindingExpr: expression, statement: expression, environment) =>
switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
@ -26,11 +29,23 @@ let dispatchMacroCall = (
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
let newBindings = Bindings.fromValue(externalBindingsValue)
// 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 =>
eFunction(
"$setBindings",
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
ExpressionWithContext.withContext(
eFunction(
"$setBindings",
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
),
newBindings,
)
)
})
@ -38,7 +53,10 @@ let dispatchMacroCall = (
| _ => REAssignmentExpected->Error
}
let doBindExpression = (bindingExpr: expression, statement: expression, environment) =>
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result<
expressionWithContext,
errorValue,
> =>
switch statement {
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => {
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
@ -47,35 +65,49 @@ let dispatchMacroCall = (
let newBindings = Bindings.fromValue(externalBindingsValue)
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
rNewStatement->Result.map(newStatement =>
eFunction(
"$exportBindings",
list{
eFunction(
"$setBindings",
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
),
},
ExpressionWithContext.withContext(
eFunction(
"$exportBindings",
list{
eFunction(
"$setBindings",
list{
newBindings->Bindings.toExternalBindings->eRecord,
symbolExpr,
newStatement,
},
),
},
),
newBindings,
)
)
})
}
| _ => {
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
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
rNewStatement->Result.map(newStatement =>
ExpressionWithContext.withContext(newStatement, newBindings)
)
})
}
}
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
expression,
expressionWithContext,
errorValue,
> => {
let exprsArray = Belt.List.toArray(exprs)
let maxIndex = Js.Array2.length(exprsArray) - 1
exprsArray->Js.Array2.reducei((acc, statement, index) =>
let newStatement = exprsArray->Js.Array2.reducei((acc, statement, index) =>
if index == 0 {
if index == maxIndex {
eBindExpressionDefault(statement)
@ -87,16 +119,23 @@ let dispatchMacroCall = (
} else {
eBindStatement(acc, statement)
}
, eSymbol("undefined block"))->Ok
, eSymbol("undefined block"))
ExpressionWithContext.noContext(newStatement)->Ok
}
let doLambdaDefinition = (
bindings: ExpressionT.bindings,
parameters: array<string>,
lambdaDefinition: ExpressionT.expression,
) => eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition)->Ok
) =>
ExpressionWithContext.noContext(
eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition),
)->Ok
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment) =>
let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result<
expressionWithContext,
errorValue,
> =>
switch aList {
| list{
ExpressionT.EValue(EvCall("$$bindStatement")),
@ -123,11 +162,11 @@ let dispatchMacroCall = (
lambdaDefinition,
} =>
doLambdaDefinition(bindings, parameters, lambdaDefinition)
| _ => ExpressionT.EList(aList)->Ok
| _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok
}
switch macroExpression {
| EList(aList) => expandExpressionList(aList, bindings, environment)
| _ => macroExpression->Ok
| _ => ExpressionWithContext.noContext(macroExpression)->Ok
}
}

View File

@ -32,10 +32,9 @@ let parse = (mathJsCode: string): result<t, errorValue> =>
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
expressionValue,
'e,
> =>
{
Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression {
> => {
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression {
| T.EValue(value) => value->Ok
| T.EList(list) =>
switch list {
@ -47,7 +46,8 @@ let rec reduceExpression = (expression: t, bindings: T.bindings, environment: en
}
| _ => reduceExpressionList(list, bindings, environment)
}
}}
}
}
and reduceExpressionList = (
expressions: list<t>,

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

@ -68,10 +68,8 @@ and replaceSymbolsOnExpressionList = (bindings, list) => {
}
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
| 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) =>
@ -80,5 +78,8 @@ and checkIfCallable = (evValue: expressionValue) =>
| _ => ErrorValue.RENotAFunction(ExpressionValue.toString(evValue))->Error
}
let toString = (bindings: ExpressionT.bindings) =>
let toString = (bindings: ExpressionT.bindings) =>
bindings->toExternalBindings->ExpressionValue.EvRecord->ExpressionValue.toString
let externalBindingsToString = (externalBindings: externalBindings) =>
externalBindings->ExpressionValue.EvRecord->ExpressionValue.toString

View File

@ -25,12 +25,18 @@ let eFunction = (fName: string, lispArgs: list<expression>): expression => {
list{fn, ...lispArgs}->BExpressionT.EList
}
let eLambda = (parameters: array<string>, context, expr) =>
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

View File

@ -1,17 +1,19 @@
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<expression, 'e> =>
): result<expressionWithContext, 'e> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression,
bindings,
@ -30,6 +32,13 @@ let doMacroCall = (
bindings,
environment,
reduceExpression,
)->Result.flatMap(expression => reduceExpression(expression, bindings, environment))
)->Result.flatMap(expressionWithContext =>
ExpressionWithContext.callReducer(
expressionWithContext,
bindings,
environment,
reduceExpression,
)
)
let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")