arity error

This commit is contained in:
Umur Ozkul 2022-05-02 12:55:28 +02:00
parent 806ff93983
commit c68138e5f6
5 changed files with 86 additions and 32 deletions

View File

@ -1,17 +1,42 @@
open Jest open Jest
open Reducer_TestHelpers open Reducer_TestHelpers
Skip.describe("function trics", () => { describe("Arity check", () => {
testEvalToBe("f(x,y)=x(y); f(f)", "????") testEvalToBe("f(x,y) = x + y; f(1,2)", "Ok(3)")
testEvalToBe("f(x)=x(y); f(f)", "????") testEvalToBe(
testEvalToBe("f(x)=x; f(f)", "????") "f(x,y) = x + y; f(1)",
"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(
testEvalToBe("f(x)=f(y)=2; f(2)", "????") //prevent multiple assignment "f(x,y) = x + y; f(1,2,3)",
testEvalToBe("f(x)=x+1; g(x)=f(x)+1;g(2)", "????") //TODO: f is not found "Error(2 arguments expected. Instead 3 argument(s) were passed.)",
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(
testEvalToBe("f(x,y)=x+y; f(1,2,3,4)", "????") //TODO : arity)) "f(x,y)=x+y; f(1,2,3,4)",
testEvalToBe("f(x,y)=x+y; f(1)", "????") //TODO : arity)) "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.)",
)
})
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
}) })

View File

@ -1,7 +1,9 @@
@genType @genType
type errorValue = type errorValue =
| REArityError(option<string>, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting
| REArrayIndexNotFound(string, int) | REArrayIndexNotFound(string, int)
| REAssignmentExpected | REAssignmentExpected
| REDistributionError(DistributionTypes.error)
| REExpressionExpected | REExpressionExpected
| REFunctionExpected(string) | REFunctionExpected(string)
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception | REJavaScriptExn(option<string>, option<string>) // Javascript Exception
@ -9,7 +11,6 @@ type errorValue =
| RERecordPropertyNotFound(string, string) | RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string) | RESymbolNotFound(string)
| RESyntaxError(string) | RESyntaxError(string)
| REDistributionError(DistributionTypes.error)
| RETodo(string) // To do | RETodo(string) // To do
type t = errorValue type t = errorValue
@ -17,6 +18,10 @@ type t = errorValue
@genType @genType
let errorToString = err => let errorToString = err =>
switch 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)}` | REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
| REAssignmentExpected => "Assignment expected" | REAssignmentExpected => "Assignment expected"
| REExpressionExpected => "Expression expected" | REExpressionExpected => "Expression expected"

View File

@ -29,8 +29,8 @@ let eLambda = (parameters: array<string>, context, expr) =>
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,7 +1,9 @@
module Bindings = Reducer_Expression_Bindings module Bindings = Reducer_Expression_Bindings
module ErrorValue = Reducer_ErrorValue
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionT = Reducer_Expression_T module ExpressionT = Reducer_Expression_T
module ExpressionValue = ReducerInterface.ExpressionValue module ExpressionValue = ReducerInterface.ExpressionValue
module Result = Belt.Result
type environment = ReducerInterface_ExpressionValue.environment type environment = ReducerInterface_ExpressionValue.environment
type expression = ExpressionT.expression type expression = ExpressionT.expression
@ -11,25 +13,48 @@ type internalCode = ReducerInterface_ExpressionValue.internalCode
external castInternalCodeToExpression: internalCode => expression = "%identity" external castInternalCodeToExpression: internalCode => expression = "%identity"
let checkArity = (lambdaValue: ExpressionValue.lambdaValue, args: list<expressionValue>) => {
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<expressionValue>) =>
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 = ( let applyParametersToLambda = (
internal: internalCode, lambdaValue: ExpressionValue.lambdaValue,
parameters: array<string>, args,
args: list<expressionValue>,
context: externalBindings,
environment, environment,
reducer: ExpressionT.reducerFn, reducer: ExpressionT.reducerFn,
): result<expressionValue, 'e> => { ): result<expressionValue, 'e> => {
let expr = castInternalCodeToExpression(internal) checkArity(lambdaValue, args)->Result.flatMap(args =>
let parameterList = parameters->Belt.List.fromArray 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 zippedParameterList = parameterList->Belt.List.zip(args)
let bindings = Belt.List.reduce(zippedParameterList, context->Bindings.fromExternalBindings, ( let bindings = Belt.List.reduce(
acc, zippedParameterList,
(variable, variableValue), lambdaValue.context->Bindings.fromExternalBindings,
) => acc->Belt.Map.String.set(variable, variableValue)) (acc, (variable, variableValue)) => acc->Belt.Map.String.set(variable, variableValue),
)
let newExpression = ExpressionBuilder.eBlock(list{expr}) let newExpression = ExpressionBuilder.eBlock(list{expr})
reducer(newExpression, bindings, environment) reducer(newExpression, bindings, environment)
})
)
} }
let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => { let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => {
applyParametersToLambda(lambdaValue.body, lambdaValue.parameters, args, lambdaValue.context, environment, reducer) applyParametersToLambda(lambdaValue, args, environment, reducer)
} }

View File

@ -45,8 +45,7 @@ let rec toString = aValue =>
} }
| EvBool(aBool) => Js.String.make(aBool) | EvBool(aBool) => Js.String.make(aBool)
| EvCall(fName) => `:${fName}` | EvCall(fName) => `:${fName}`
| EvLambda(lambdaValue) => | EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
`lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
| EvNumber(aNumber) => Js.String.make(aNumber) | EvNumber(aNumber) => Js.String.make(aNumber)
| EvString(aString) => `'${aString}'` | EvString(aString) => `'${aString}'`
| EvSymbol(aString) => `:${aString}` | EvSymbol(aString) => `:${aString}`