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 Jest
open Expect open Expect
module Macro = Reducer_Expression_Macro
module Bindings = Reducer_Expression_Bindings module Bindings = Reducer_Expression_Bindings
module Expression = Reducer_Expression module Expression = Reducer_Expression
module ExpressionValue = ReducerInterface_ExpressionValue module ExpressionValue = ReducerInterface_ExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext
module Macro = Reducer_Expression_Macro
module T = Reducer_Expression_T module T = Reducer_Expression_T
let testMacro_ = ( let testMacro_ = (
@ -21,7 +22,7 @@ let testMacro_ = (
ExpressionValue.defaultEnvironment, ExpressionValue.defaultEnvironment,
Expression.reduceExpression, Expression.reduceExpression,
) )
->T.toStringResult ->ExpressionWithContext.toStringResult
->expect ->expect
->toEqual(expectedCode) ->toEqual(expectedCode)
) )

View File

@ -45,9 +45,21 @@ Only.describe("call and bindings", () => {
testEvalToBe("f(x)=x+1; y=f(1)", "Ok({f: lambda(x=>internal code),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); 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; 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 testEvalToBe(
MyOnly.testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2)", "????") //TODO: f is not found "f(x)=x+1; g(x)=f(x)+1",
MySkip.testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "????") //TODO: f is not found "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", () => { Skip.describe("function trics", () => {

View File

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

View File

@ -32,9 +32,8 @@ let parse = (mathJsCode: string): result<t, errorValue> =>
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result< let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
expressionValue, expressionValue,
'e, 'e,
> => > => {
{ // Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression { switch expression {
| T.EValue(value) => value->Ok | T.EValue(value) => value->Ok
| T.EList(list) => | T.EList(list) =>
@ -47,7 +46,8 @@ let rec reduceExpression = (expression: t, bindings: T.bindings, environment: en
} }
| _ => reduceExpressionList(list, bindings, environment) | _ => reduceExpressionList(list, bindings, environment)
} }
}} }
}
and reduceExpressionList = ( and reduceExpressionList = (
expressions: list<t>, 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) => and replaceSymbolOnValue = (bindings, evValue: expressionValue) =>
switch evValue { switch evValue {
| EvSymbol(symbol) => | EvSymbol(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->Ok
Belt.Map.String.getWithDefault(bindings, symbol, evValue)->Ok | EvCall(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->checkIfCallable
| EvCall(symbol) =>
Belt.Map.String.getWithDefault(bindings, symbol, evValue)->checkIfCallable
| _ => evValue->Ok | _ => evValue->Ok
} }
and checkIfCallable = (evValue: expressionValue) => and checkIfCallable = (evValue: expressionValue) =>
@ -82,3 +80,6 @@ and checkIfCallable = (evValue: expressionValue) =>
let toString = (bindings: ExpressionT.bindings) => let toString = (bindings: ExpressionT.bindings) =>
bindings->toExternalBindings->ExpressionValue.EvRecord->ExpressionValue.toString 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 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({ BExpressionValue.EvLambda({
parameters: parameters, parameters: parameters,
context: context, context: context,
body: expr->castExpressionToInternalCode, body: expr->castExpressionToInternalCode,
})->BExpressionT.EValue })->BExpressionT.EValue
}
let eNumber = aNumber => aNumber->BExpressionValue.EvNumber->BExpressionT.EValue let eNumber = aNumber => aNumber->BExpressionValue.EvNumber->BExpressionT.EValue

View File

@ -1,17 +1,19 @@
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue module ExpressionValue = ReducerInterface.ExpressionValue
module ExpressionWithContext = Reducer_ExpressionWithContext
module Result = Belt.Result module Result = Belt.Result
type environment = ExpressionValue.environment type environment = ExpressionValue.environment
type expression = ExpressionT.expression type expression = ExpressionT.expression
type expressionValue = ExpressionValue.expressionValue type expressionValue = ExpressionValue.expressionValue
type expressionWithContext = ExpressionWithContext.expressionWithContext
let expandMacroCall = ( let expandMacroCall = (
macroExpression: expression, macroExpression: expression,
bindings: ExpressionT.bindings, bindings: ExpressionT.bindings,
environment: environment, environment: environment,
reduceExpression: ExpressionT.reducerFn, reduceExpression: ExpressionT.reducerFn,
): result<expression, 'e> => ): result<expressionWithContext, 'e> =>
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall( Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(
macroExpression, macroExpression,
bindings, bindings,
@ -30,6 +32,13 @@ let doMacroCall = (
bindings, bindings,
environment, environment,
reduceExpression, 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("$$") let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$")