From 6a3b35eb4aa366331a1fd6b413ca717b10a21cb2 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 2 May 2022 18:44:35 +0200 Subject: [PATCH] fixed function f not bound --- .../Reducer/Reducer_TestMacroHelpers.res | 5 +- .../Reducer/Reducer_functionTricks_test.res | 26 ++++-- .../Reducer_Dispatch_BuiltInMacros.res | 85 ++++++++++++++----- .../Reducer_Expression/Reducer_Expression.res | 10 +-- .../Reducer_ExpressionWithContext.res | 45 ++++++++++ .../Reducer_Expression_Bindings.res | 11 +-- .../Reducer_Expression_ExpressionBuilder.res | 8 +- .../Reducer_Expression_Macro.res | 13 ++- 8 files changed, 158 insertions(+), 45 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res index af88cac4..330f7da0 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res @@ -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) ) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res index e5808701..f134c309 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -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", () => { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index 2b6ef13f..c106c0ff 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -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 => { +): result => { 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 = 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, _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, 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 } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index c98da3a8..e1df1418 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -32,10 +32,9 @@ let parse = (mathJsCode: string): result => 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, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res new file mode 100644 index 00000000..dacd2462 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res @@ -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 => + 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) + } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res index 18d17af2..7c0c048a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res @@ -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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index 3aa0fcba..9c9f922e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -25,12 +25,18 @@ let eFunction = (fName: string, lispArgs: list): expression => { list{fn, ...lispArgs}->BExpressionT.EList } -let eLambda = (parameters: array, context, expr) => +let eLambda = ( + parameters: array, + 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 diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res index 5ac19502..23fb70f8 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res @@ -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 => +): result => 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("$$")