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 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)", "????")
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))
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.)",
)
})
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
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)
| REAssignmentExpected
| REDistributionError(DistributionTypes.error)
| REExpressionExpected
| REFunctionExpected(string)
| REJavaScriptExn(option<string>, option<string>) // 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"

View File

@ -29,8 +29,8 @@ let eLambda = (parameters: array<string>, 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

View File

@ -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<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 = (
internal: internalCode,
parameters: array<string>,
args: list<expressionValue>,
context: externalBindings,
lambdaValue: ExpressionValue.lambdaValue,
args,
environment,
reducer: ExpressionT.reducerFn,
): result<expressionValue, 'e> => {
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)
}

View File

@ -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}`