fixed function f not bound
This commit is contained in:
parent
5a0b436932
commit
6a3b35eb4a
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
@ -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", () => {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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("$$")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user