diff --git a/packages/squiggle-lang/__tests__/FunctionRegistry_test.res b/packages/squiggle-lang/__tests__/FunctionRegistry_test.res new file mode 100644 index 00000000..e976f7b1 --- /dev/null +++ b/packages/squiggle-lang/__tests__/FunctionRegistry_test.res @@ -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)") + }) +}) \ No newline at end of file diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index b2067205..ba6449ce 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -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/", diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index f18f493f..e0ef4441 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -328,6 +328,14 @@ module FnDefinition = { t.name ++ `(${inputs})` } + let isMatch = (t: t, args: array) => { + let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) + switch argValues { + | Some(values) => true + | None => false + } + } + let run = (t: t, args: array, 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} + 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) diff --git a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res index 436ed1fe..b7b925ed 100644 --- a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res +++ b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res @@ -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)