diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res index 69e20067..23b4eaba 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -1,17 +1,42 @@ open Jest open Reducer_TestHelpers -Skip.describe("function trics", () => { - testEvalToBe("f(x,y)=x(y); f(f)", "????") - testEvalToBe("f(x)=x(y); f(f)", "????") - testEvalToBe("f(x)=x; f(f)", "????") +describe("Arity check", () => { + testEvalToBe("f(x,y) = x + y; f(1,2)", "Ok(3)") + testEvalToBe( + "f(x,y) = x + y; f(1)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y) = x + y; f(1,2,3)", + "Error(2 arguments expected. Instead 3 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x+y; f(1,2,3,4)", + "Error(2 arguments expected. Instead 4 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x+y; f(1)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x(y); f(f)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))") + testEvalToBe( + "f(x,y)=x(y); f(z)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) +}) - testEvalToBe("f(x,y)=x(y); f(z)", "????") - testEvalToBe("f(x,y)=x(y); f(2)", "????") //prevent js error - testEvalToBe("f(x)=f(y)=2; f(2)", "????") //prevent multiple assignment - testEvalToBe("f(x)=x+1; g(x)=f(x)+1;g(2)", "????") //TODO: f is not found - testEvalToBe("y=2;g(x)=y+1;g(2)", "????") //TODO : y is not found - testEvalToBe("y=2;g(x)=inspect(y)+1", "????") //TODO : 666 - testEvalToBe("f(x,y)=x+y; f(1,2,3,4)", "????") //TODO : arity)) - testEvalToBe("f(x,y)=x+y; f(1)", "????") //TODO : arity)) -}) \ No newline at end of file +describe("function trics", () => { + testEvalToBe("f(x)=x(y); f(f)", "Error(y is not defined)") + testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))") + testEvalToBe("f(x)=x(y); f(z)", "Error(y is not defined)") + MySkip.testEvalToBe("f(x)=x(y); f(2)", "????") //prevent js error + MySkip.testEvalToBe("f(x)=f(y)=2; f(2)", "????") //prevent multiple assignment + MySkip.testEvalToBe("f(x)=x+1; g(x)=f(x)+1;g(2)", "????") //TODO: f is not found + MySkip.testEvalToBe("y=2;g(x)=y+1;g(2)", "????") //TODO : y is not found + MySkip.testEvalToBe("y=2;g(x)=inspect(y)+1", "????") //TODO : 666 +}) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 96b73fd2..b9eabe28 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -1,7 +1,9 @@ @genType type errorValue = + | REArityError(option, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting | REArrayIndexNotFound(string, int) | REAssignmentExpected + | REDistributionError(DistributionTypes.error) | REExpressionExpected | REFunctionExpected(string) | REJavaScriptExn(option, option) // Javascript Exception @@ -9,7 +11,6 @@ type errorValue = | RERecordPropertyNotFound(string, string) | RESymbolNotFound(string) | RESyntaxError(string) - | REDistributionError(DistributionTypes.error) | RETodo(string) // To do type t = errorValue @@ -17,6 +18,10 @@ type t = errorValue @genType let errorToString = err => switch err { + | REArityError(_oFnName, arity, usedArity) => + `${Js.String.make(arity)} arguments expected. Instead ${Js.String.make( + usedArity, + )} argument(s) were passed.` | REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}` | REAssignmentExpected => "Assignment expected" | REExpressionExpected => "Expression expected" 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 989d2810..3aa0fcba 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 @@ -29,8 +29,8 @@ let eLambda = (parameters: array, context, expr) => BExpressionValue.EvLambda({ parameters: parameters, context: context, - body: expr->castExpressionToInternalCode} - )->BExpressionT.EValue + 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_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index 675e3bc3..55931cc7 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -1,7 +1,9 @@ module Bindings = Reducer_Expression_Bindings +module ErrorValue = Reducer_ErrorValue module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T module ExpressionValue = ReducerInterface.ExpressionValue +module Result = Belt.Result type environment = ReducerInterface_ExpressionValue.environment type expression = ExpressionT.expression @@ -11,25 +13,48 @@ type internalCode = ReducerInterface_ExpressionValue.internalCode external castInternalCodeToExpression: internalCode => expression = "%identity" +let checkArity = (lambdaValue: ExpressionValue.lambdaValue, args: list) => { + let argsLength = Belt.List.length(args) + let parametersLength = Js.Array2.length(lambdaValue.parameters) + if argsLength !== parametersLength { + ErrorValue.REArityError(None, parametersLength, argsLength)->Error + } else { + args->Ok + } +} + +let checkIfReduced = (args: list) => + args->Belt.List.reduceReverse(Ok(list{}), (rAcc, arg) => + rAcc->Result.flatMap(acc => + switch arg { + | EvSymbol(symbol) => ErrorValue.RESymbolNotFound(symbol)->Error + | _ => list{arg, ...acc}->Ok + } + ) + ) + let applyParametersToLambda = ( - internal: internalCode, - parameters: array, - args: list, - context: externalBindings, + lambdaValue: ExpressionValue.lambdaValue, + args, environment, reducer: ExpressionT.reducerFn, ): result => { - let expr = castInternalCodeToExpression(internal) - let parameterList = parameters->Belt.List.fromArray - let zippedParameterList = parameterList->Belt.List.zip(args) - let bindings = Belt.List.reduce(zippedParameterList, context->Bindings.fromExternalBindings, ( - acc, - (variable, variableValue), - ) => acc->Belt.Map.String.set(variable, variableValue)) - let newExpression = ExpressionBuilder.eBlock(list{expr}) - reducer(newExpression, bindings, environment) + checkArity(lambdaValue, args)->Result.flatMap(args => + checkIfReduced(args)->Result.flatMap(args => { + let expr = castInternalCodeToExpression(lambdaValue.body) + let parameterList = lambdaValue.parameters->Belt.List.fromArray + let zippedParameterList = parameterList->Belt.List.zip(args) + let bindings = Belt.List.reduce( + zippedParameterList, + lambdaValue.context->Bindings.fromExternalBindings, + (acc, (variable, variableValue)) => acc->Belt.Map.String.set(variable, variableValue), + ) + let newExpression = ExpressionBuilder.eBlock(list{expr}) + reducer(newExpression, bindings, environment) + }) + ) } let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => { - applyParametersToLambda(lambdaValue.body, lambdaValue.parameters, args, lambdaValue.context, environment, reducer) + applyParametersToLambda(lambdaValue, args, environment, reducer) } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 4d6911af..5c9ee4b7 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -45,8 +45,7 @@ let rec toString = aValue => } | EvBool(aBool) => Js.String.make(aBool) | EvCall(fName) => `:${fName}` - | EvLambda(lambdaValue) => - `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)` + | EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)` | EvNumber(aNumber) => Js.String.make(aNumber) | EvString(aString) => `'${aString}'` | EvSymbol(aString) => `:${aString}`