Added FunctionRegistry test and got simple polymorphism working

This commit is contained in:
Ozzie Gooen 2022-07-14 22:14:23 -07:00
parent 3f09f5f307
commit 8839f1d9c7
4 changed files with 123 additions and 7 deletions

View File

@ -0,0 +1,82 @@
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ExpressionT = Reducer_Expression_T
module Module = Reducer_Module
module Bindings = Reducer_Module
module ErrorValue = Reducer_ErrorValue
open Jest
open Expect
// ----------------------
// --- Start of Module File
// ----------------------
module FooImplementation = {
open FunctionRegistry_Core
open FunctionRegistry_Helpers
let library = [
Function.make(
~name="add",
~nameSpace="Foo",
~definitions=[
FnDefinition.make(
~requiresNamespace=false,
~name="add",
~inputs=[FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _) =>
switch inputs {
| [FRValueNumber(a), FRValueNumber(b)] => Ok(Wrappers.evNumber(a +. b))
| _ => Error("False")
},
(),
),
FnDefinition.make(
~requiresNamespace=true,
~name="add",
~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber],
~run=(_, inputs, _) =>
switch inputs {
| [FRValueNumber(a), FRValueNumber(b), FRValueNumber(c)] =>
Ok(Wrappers.evNumber(a +. b +. c))
| _ => Error("False")
},
(),
),
],
(),
),
]
}
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
previousBindings->FunctionRegistry_Core.Registry.makeModules(FooImplementation.library)
let stdLibWithFoo = Bindings.emptyBindings->makeBindings
let evalWithFoo = sourceCode =>
Reducer_Expression.parse(sourceCode)->Belt.Result.flatMap(expr =>
Reducer_Expression.reduceExpression(
expr,
stdLibWithFoo,
InternalExpressionValue.defaultEnvironment,
)
)
let evalToStringResultWithFoo = sourceCode =>
evalWithFoo(sourceCode)->InternalExpressionValue.toStringResult
describe("Module", () => {
test("add(1,2)", () => {
let result = evalToStringResultWithFoo("Foo.add(1,2)")
expect(result)->toEqual("Ok(3)")
})
test("add(1,2,3)", () => {
let result = evalToStringResultWithFoo("Foo.add(1,2,3)")
expect(result)->toEqual("Ok(6)")
})
test("add(1,2,3,5)", () => {
let result = evalToStringResultWithFoo("Foo.add(1,2,3,5)")
expect(result)->toEqual("Ok(6)")
})
})

View File

@ -15,6 +15,7 @@
"start": "rescript build -w -with-deps",
"clean": "rescript clean && rm -rf dist",
"test:reducer": "jest __tests__/Reducer*/",
"test:reducer2": "jest __tests__/FunctionRegistry_test",
"benchmark": "ts-node benchmark/conversion_tests.ts",
"test": "jest",
"test:ts": "jest __tests__/TS/",

View File

@ -328,6 +328,14 @@ module FnDefinition = {
t.name ++ `(${inputs})`
}
let isMatch = (t: t, args: array<internalExpressionValue>) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
switch argValues {
| Some(values) => true
| None => false
}
}
let run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
switch argValues {
@ -387,6 +395,32 @@ module Function = {
}
}
module NameSpace = {
type t = {name: string, functions: array<function>}
let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany
let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq
let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name)
//todo: It could be good to set a warning if two definitions are both valid, but I don't expect this often.
let nameFfiFn = (t: t, name: string): Reducer_Expression_T.optionFfiFn => {
(args, environment) => {
let definitions =
nameToDefinitions(t, name)->E.A2.fmap((def, ()) =>
FnDefinition.isMatch(def, args)
? FnDefinition.run(def, args, environment) |> E.R.toOption
: None
)
E.A.O.firstSomeFn(definitions)
}
}
let toModule = (t: t): Reducer_Module.t =>
E.A.reduce(uniqueFnNames(t), Reducer_Module.emptyStdLib, (acc, uniqueName) => {
let relevantDefinitions = nameFfiFn(t, uniqueName)
acc->Reducer_Module.defineFunction(uniqueName, relevantDefinitions)
})
}
module Registry = {
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
let definitionsWithFunctions = (r: registry) =>
@ -428,13 +462,11 @@ module Registry = {
let makeModules = (prevBindings: Reducer_Module.t, t: registry): Reducer_Module.t => {
let nameSpaces = allNamespaces(t)
let nameSpaceBindings = nameSpaces->E.A2.fmap(nameSpace => {
let definitions =
t->definitionsWithFunctions->E.A2.filter(((_, fn)) => fn.nameSpace === nameSpace)
let newModule = E.A.reduce(definitions, Reducer_Module.emptyStdLib, (acc, (def, _)) => {
acc->Reducer_Module.defineFunction(def.name, FnDefinition.toFfiFn(def))
})
(nameSpace, newModule)
let foo: NameSpace.t = {
name: nameSpace,
functions: t->E.A2.filter(r => r.nameSpace == nameSpace),
}
(nameSpace, NameSpace.toModule(foo))
})
E.A.reduce(nameSpaceBindings, prevBindings, (acc, (name, fn)) =>
acc->Reducer_Module.defineModule(name, fn)

View File

@ -19,6 +19,7 @@ let mathBindings: Bindings.t =
->E.A2.fmap(((name, v)) => (name, ReducerInterface_InternalExpressionValue.IEvNumber(v)))
->Bindings.fromArray
//TODO: This should be in a different place.
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
previousBindings
->Bindings.defineModule("Math", mathBindings)