From 6dc1cea045584b916caf7fc7a445995f1b281239 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Mon, 12 Sep 2022 03:07:34 +0400 Subject: [PATCH] more WIP --- .../FunctionRegistry_Core.res | 291 +++--------------- .../FunctionRegistry_Library.res | 2 +- .../FunctionRegistry/Library/FR_Builtin.res | 22 ++ .../Reducer_Peggy_GeneratedParser.peggy | 2 +- .../Reducer_Peggy/Reducer_Peggy_Parse.res | 4 +- .../Reducer_Peggy_ToExpression.res | 2 +- .../ReducerInterface_ExternalLibrary.res | 2 +- .../ReducerInterface_StdLib.res | 11 +- 8 files changed, 72 insertions(+), 264 deletions(-) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index e217cad9..145c9a60 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -62,7 +62,7 @@ type function = { isExperimental: bool, } -type fnNameDict = Js.Dict.t> +type fnNameDict = Js.Dict.t> type registry = {functions: array, fnNameDict: fnNameDict} module FRType = { @@ -181,188 +181,6 @@ module FRType = { } } -/* - This module, Matcher, is fairly lengthy. However, only two functions from it - are meant to be used outside of it. These are findMatches and matchToDef in Matches.Registry. - The rest of it is just called from those two functions. - - Update: This really should be completely re-done sometime, and tested. It works, but it's pretty messy. I'm sure - there are internal bugs, but the end functionality works, so I'm not too worried. -*/ -module Matcher = { - module MatchSimple = { - type t = DifferentName | SameNameDifferentArguments | FullMatch - - let isFullMatch = (match: t) => - switch match { - | FullMatch => true - | _ => false - } - - let isNameMatchOnly = (match: t) => - switch match { - | SameNameDifferentArguments => true - | _ => false - } - } - - module Match = { - type t<'a, 'b> = DifferentName | SameNameDifferentArguments('a) | FullMatch('b) - - let isFullMatch = (match: t<'a, 'b>): bool => - switch match { - | FullMatch(_) => true - | _ => false - } - - let isNameMatchOnly = (match: t<'a, 'b>) => - switch match { - | SameNameDifferentArguments(_) => true - | _ => false - } - } - - module FnDefinition = { - let matchAssumingSameName = (f: fnDefinition, args: array) => { - switch FRType.matchWithExpressionValueArray(f.inputs, args) { - | Some(_) => MatchSimple.FullMatch - | None => MatchSimple.SameNameDifferentArguments - } - } - - let match = (f: fnDefinition, fnName: string, args: array) => { - if f.name !== fnName { - MatchSimple.DifferentName - } else { - matchAssumingSameName(f, args) - } - } - } - - module Function = { - type definitionId = int - type match = Match.t, definitionId> - - let match = ( - f: function, - nameSpace: option, - fnName: string, - args: array, - ): match => { - switch nameSpace { - | Some(ns) if ns !== f.nameSpace => Match.DifferentName - | _ => { - let matchedDefinition = () => - E.A.getIndexBy(f.definitions, r => - MatchSimple.isFullMatch(FnDefinition.match(r, fnName, args)) - ) |> E.O.fmap(r => Match.FullMatch(r)) - let getMatchedNameOnlyDefinition = () => { - let nameMatchIndexes = - f.definitions - ->E.A2.fmapi((index, r) => - MatchSimple.isNameMatchOnly(FnDefinition.match(r, fnName, args)) - ? Some(index) - : None - ) - ->E.A.O.concatSomes - switch nameMatchIndexes { - | [] => None - | elements => Some(Match.SameNameDifferentArguments(elements)) - } - } - - E.A.O.firstSomeFnWithDefault( - [matchedDefinition, getMatchedNameOnlyDefinition], - Match.DifferentName, - ) - } - } - } - } - - module RegistryMatch = { - type match = { - nameSpace: string, - fnName: string, - inputIndex: int, - } - let makeMatch = (nameSpace: string, fnName: string, inputIndex: int) => { - nameSpace: nameSpace, - fnName: fnName, - inputIndex: inputIndex, - } - } - - module Registry = { - let _findExactMatches = ( - r: registry, - nameSpace: option, - fnName: string, - args: array, - ) => { - let functionMatchPairs = - r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args))) - let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match)) - fullMatch->E.O.bind(((fn, match)) => - switch match { - | FullMatch(index) => Some(RegistryMatch.makeMatch(fn.nameSpace, fn.name, index)) - | _ => None - } - ) - } - - let _findNameMatches = ( - r: registry, - nameSpace: option, - fnName: string, - args: array, - ) => { - let functionMatchPairs = - r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args))) - let getNameMatches = - functionMatchPairs - ->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None) - ->E.A.O.concatSomes - let matches = - getNameMatches - ->E.A2.fmap(((fn, match)) => - switch match { - | SameNameDifferentArguments(indexes) => - indexes->E.A2.fmap(index => RegistryMatch.makeMatch(fn.nameSpace, fn.name, index)) - | _ => [] - } - ) - ->Belt.Array.concatMany - E.A.toNoneIfEmpty(matches) - } - - let findMatches = (r: registry, fnName: string, args: array) => { - let fnNameInParts = Js.String.split(".", fnName) - let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0]) - let nameSpace = E.A.length(fnNameInParts) > 1 ? Some(fnNameInParts[0]) : None - - switch _findExactMatches(r, nameSpace, fnToSearch, args) { - | Some(r) => Match.FullMatch(r) - | None => - switch _findNameMatches(r, nameSpace, fnToSearch, args) { - | Some(r) => Match.SameNameDifferentArguments(r) - | None => Match.DifferentName - } - } - } - - let matchToDef = ( - registry: registry, - {nameSpace, fnName, inputIndex}: RegistryMatch.match, - ): option => - registry.functions - ->E.A.getBy(fn => { - nameSpace === fn.nameSpace && fnName === fn.name - }) - ->E.O.bind(fn => E.A.get(fn.definitions, inputIndex)) - } -} - module FnDefinition = { type t = fnDefinition @@ -440,13 +258,6 @@ 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) -} - module Registry = { let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson) let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany @@ -454,29 +265,24 @@ module Registry = { r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany let _buildFnNameDict = (r: array): fnNameDict => { - let allDefinitionsWithFns = - r - ->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions))) - ->E.A.concatMany - let functionsWithFnNames = - allDefinitionsWithFns - ->E.A2.fmap(((fn, def)) => { + // Sorry for the imperative style of this. But it's much easier/less buggy than the previous version. + let res: fnNameDict = Js.Dict.empty() + r->Js.Array2.forEach(fn => + fn.definitions->Js.Array2.forEach(def => { let nameWithNamespace = `${fn.nameSpace}.${def.name}` let nameWithoutNamespace = def.name - fn.requiresNamespace - ? [(nameWithNamespace, fn)] - : [(nameWithNamespace, fn), (nameWithoutNamespace, fn)] + let names = fn.requiresNamespace ? [nameWithNamespace] : [nameWithNamespace, nameWithoutNamespace] + names->Js.Array2.forEach(name => { + switch res->Js.Dict.get(name) { + | Some(fns) => { + let _ = fns->Js.Array2.push(def) + } + | None => res->Js.Dict.set(name, [def]) + } + }) }) - ->E.A.concatMany - let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq - let cacheAsArray: array<(string, array)> = uniqueNames->E.A2.fmap(uniqueName => { - let relevantItems = - E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap( - E.Tuple2.second, - ) - (uniqueName, relevantItems) - }) - cacheAsArray->Js.Dict.fromArray + ) + res } let make = (fns: array): registry => { @@ -484,48 +290,33 @@ module Registry = { {functions: fns, fnNameDict: dict} } - /* - There's a (potential+minor) bug here: If a function definition is called outside of the calls - to the registry, then it's possible that there could be a match after the registry is - called. However, for now, we could just call the registry last. - */ - let _matchAndRun = ( - ~registry: registry, - ~fnName: string, - ~args: array, - ~env: Reducer_T.environment, - ~reducer: Reducer_T.reducerFn, - ) => { - let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([]) - let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict} - let matchToDef = m => Matcher.Registry.matchToDef(registry, m) - let showNameMatchDefinitions = matches => { - let defs = - matches - ->E.A2.fmap(matchToDef) - ->E.A.O.concatSomes - ->E.A2.fmap(FnDefinition.toString) - ->E.A2.fmap(r => `[${r}]`) - ->E.A2.joinWith("; ") - `There are function matches for ${fnName}(), but with different arguments: ${defs}` - } - - switch Matcher.Registry.findMatches(modified, fnName, args) { - | Matcher.Match.FullMatch(match) => - match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, env, reducer)) - | SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m))) - | _ => None - } - } - - let dispatch = ( + let call = ( registry, - (fnName, args): ReducerInterface_InternalExpressionValue.functionCall, + fnName: string, + args: array, env: Reducer_T.environment, reducer: Reducer_T.reducerFn, - ) => { - _matchAndRun(~registry, ~fnName, ~args, ~env, ~reducer)->E.O2.fmap( - E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)), - ) + ): result => { + switch Js.Dict.get(registry.fnNameDict, fnName) { + | Some(definitions) => { + let showNameMatchDefinitions = () => { + let defsString = + definitions + ->E.A2.fmap(FnDefinition.toString) + ->E.A2.fmap(r => `[${r}]`) + ->E.A2.joinWith("; ") + `There are function matches for ${fnName}(), but with different arguments: ${defsString}` + } + + let match = definitions->Js.Array2.find(def => def->FnDefinition.isMatch(args)) + switch match { + | Some(def) => def->FnDefinition.run(args, env, reducer)->E.R2.errMap(e => Reducer_ErrorValue.REOther(e)) + | None => { + Reducer_ErrorValue.REOther(showNameMatchDefinitions())->Error + } + } + } + | None => Reducer_ErrorValue.RESymbolNotFound(fnName)->Error + } } } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index d8c2c04b..0d54dee3 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -12,4 +12,4 @@ let fnList = Belt.Array.concatMany([ ]) let registry = FunctionRegistry_Core.Registry.make(fnList) -let dispatch = FunctionRegistry_Core.Registry.dispatch(registry) +let call = FunctionRegistry_Core.Registry.call(registry) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res index 6bced75d..da064f94 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res @@ -45,6 +45,28 @@ let library = [ }, () ), + FnDefinition.make( + ~name="add", + ~inputs=[FRTypeNumber, FRTypeNumber], + ~run=(inputs, _, _, _) => { + switch inputs { + | [IEvNumber(x), IEvNumber(y)] => IEvNumber(x+.y)->Ok + | _ => Error(impossibleError) + } + }, + () + ), + FnDefinition.make( + ~name="add", + ~inputs=[FRTypeNumber, FRTypeNumber], + ~run=(inputs, _, _, _) => { + switch inputs { + | [IEvNumber(x), IEvNumber(y)] => IEvNumber(x+.y)->Ok + | _ => Error(impossibleError) + } + }, + () + ), ], (), ), diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index 4beba469..469fcba1 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -264,7 +264,7 @@ lambda { statements.push(finalExpression) return h.nodeLambda(args, h.nodeBlock(statements)) } / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}' - { return h.nodeLambda(args, h.nodeBlock([finalExpression])) } + { return h.nodeLambda(args, finalExpression) } arrayConstructor 'array' = '[' _nl ']' diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res index 58bfbb70..ed8d8f18 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res @@ -29,7 +29,7 @@ type nodeIdentifier = {...node, "value": string} type nodeInteger = {...node, "value": int} type nodeKeyValue = {...node, "key": node, "value": node} type nodeRecord = {...node, "elements": array} -type nodeLambda = {...node, "args": array, "body": nodeBlock} +type nodeLambda = {...node, "args": array, "body": node} type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node} type nodeModuleIdentifier = {...node, "value": string} type nodeString = {...node, "value": string} @@ -122,7 +122,7 @@ let rec pgToString = (peggyNode: peggyNode): string => { | PgNodeInteger(node) => node["value"]->Js.String.make | PgNodeKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"]) | PgNodeLambda(node) => - "{|" ++ node["args"]->argsToString ++ "| " ++ pgToString(PgNodeBlock(node["body"])) ++ "}" + "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}" | PgNodeLetStatement(node) => pgToString(PgNodeIdentifier(node["variable"])) ++ " = " ++ toString(node["value"]) | PgNodeModuleIdentifier(node) => `@${node["value"]}` diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res index c1823f5c..cf8ad010 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res @@ -15,7 +15,7 @@ let rec fromNode = (node: Parse.node): expression => { let args = nodeLambda["args"] ->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"]) - let body = nodeLambda["body"]->caseBlock + let body = nodeLambda["body"]->fromNode ExpressionBuilder.eLambda(args, body) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index f60c4f95..0c9703d7 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -14,7 +14,7 @@ let dispatch = ( () => ReducerInterface_Date.dispatch(call, environment), () => ReducerInterface_Duration.dispatch(call, environment), () => ReducerInterface_Number.dispatch(call, environment), - () => FunctionRegistry_Library.dispatch(call, environment, reducer), + // () => FunctionRegistry_Library.dispatch(call, environment, reducer), ])->E.O2.defaultFn(() => chain(call, environment, reducer)) } diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res index 2e356984..21f40d72 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res @@ -63,14 +63,9 @@ let internalStdLib: Reducer_Bindings.t = { (name) => { let _ = res->Reducer_Bindings.set(name, Reducer_Expression_Lambda.makeFFILambda( (arguments, environment, reducer) => { - switch FunctionRegistry_Library.dispatch((name, arguments), environment, reducer) { - | Some(result) => { - switch result { - | Ok(value) => value - | Error(error) => error->Reducer_ErrorValue.ErrorException->raise - } - } - | None => Reducer_ErrorValue.RESymbolNotFound("Not found in registry")->Reducer_ErrorValue.ErrorException->raise + switch FunctionRegistry_Library.call(name, arguments, environment, reducer) { + | Ok(value) => value + | Error(error) => error->Reducer_ErrorValue.ErrorException->raise } } )->Reducer_T.IEvLambda)