Added FunctionRegistry test and got simple polymorphism working
This commit is contained in:
parent
3f09f5f307
commit
8839f1d9c7
82
packages/squiggle-lang/__tests__/FunctionRegistry_test.res
Normal file
82
packages/squiggle-lang/__tests__/FunctionRegistry_test.res
Normal 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)")
|
||||||
|
})
|
||||||
|
})
|
|
@ -15,6 +15,7 @@
|
||||||
"start": "rescript build -w -with-deps",
|
"start": "rescript build -w -with-deps",
|
||||||
"clean": "rescript clean && rm -rf dist",
|
"clean": "rescript clean && rm -rf dist",
|
||||||
"test:reducer": "jest __tests__/Reducer*/",
|
"test:reducer": "jest __tests__/Reducer*/",
|
||||||
|
"test:reducer2": "jest __tests__/FunctionRegistry_test",
|
||||||
"benchmark": "ts-node benchmark/conversion_tests.ts",
|
"benchmark": "ts-node benchmark/conversion_tests.ts",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:ts": "jest __tests__/TS/",
|
"test:ts": "jest __tests__/TS/",
|
||||||
|
|
|
@ -328,6 +328,14 @@ module FnDefinition = {
|
||||||
t.name ++ `(${inputs})`
|
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 run = (t: t, args: array<internalExpressionValue>, env: GenericDist.env) => {
|
||||||
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
let argValues = FRType.matchWithExpressionValueArray(t.inputs, args)
|
||||||
switch argValues {
|
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 = {
|
module Registry = {
|
||||||
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
|
let toJson = (r: registry) => r->E.A2.fmap(Function.toJson)
|
||||||
let definitionsWithFunctions = (r: registry) =>
|
let definitionsWithFunctions = (r: registry) =>
|
||||||
|
@ -428,13 +462,11 @@ module Registry = {
|
||||||
let makeModules = (prevBindings: Reducer_Module.t, t: registry): Reducer_Module.t => {
|
let makeModules = (prevBindings: Reducer_Module.t, t: registry): Reducer_Module.t => {
|
||||||
let nameSpaces = allNamespaces(t)
|
let nameSpaces = allNamespaces(t)
|
||||||
let nameSpaceBindings = nameSpaces->E.A2.fmap(nameSpace => {
|
let nameSpaceBindings = nameSpaces->E.A2.fmap(nameSpace => {
|
||||||
let definitions =
|
let foo: NameSpace.t = {
|
||||||
t->definitionsWithFunctions->E.A2.filter(((_, fn)) => fn.nameSpace === nameSpace)
|
name: nameSpace,
|
||||||
|
functions: t->E.A2.filter(r => r.nameSpace == nameSpace),
|
||||||
let newModule = E.A.reduce(definitions, Reducer_Module.emptyStdLib, (acc, (def, _)) => {
|
}
|
||||||
acc->Reducer_Module.defineFunction(def.name, FnDefinition.toFfiFn(def))
|
(nameSpace, NameSpace.toModule(foo))
|
||||||
})
|
|
||||||
(nameSpace, newModule)
|
|
||||||
})
|
})
|
||||||
E.A.reduce(nameSpaceBindings, prevBindings, (acc, (name, fn)) =>
|
E.A.reduce(nameSpaceBindings, prevBindings, (acc, (name, fn)) =>
|
||||||
acc->Reducer_Module.defineModule(name, fn)
|
acc->Reducer_Module.defineModule(name, fn)
|
||||||
|
|
|
@ -19,6 +19,7 @@ let mathBindings: Bindings.t =
|
||||||
->E.A2.fmap(((name, v)) => (name, ReducerInterface_InternalExpressionValue.IEvNumber(v)))
|
->E.A2.fmap(((name, v)) => (name, ReducerInterface_InternalExpressionValue.IEvNumber(v)))
|
||||||
->Bindings.fromArray
|
->Bindings.fromArray
|
||||||
|
|
||||||
|
//TODO: This should be in a different place.
|
||||||
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
|
let makeBindings = (previousBindings: Bindings.t): Bindings.t =>
|
||||||
previousBindings
|
previousBindings
|
||||||
->Bindings.defineModule("Math", mathBindings)
|
->Bindings.defineModule("Math", mathBindings)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user