From fea89abff927aab1cc52a213f0036ca8cf0ab35c Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 11 Sep 2022 23:56:07 +0400 Subject: [PATCH] more WIP --- .../FunctionRegistry_Library.res | 1 + .../FunctionRegistry/Library/FR_Builtin.res | 51 +++++++++++++++++++ .../Reducer_Dispatch_BuiltIn.res | 44 +++++++--------- .../Reducer_Expression/Reducer_Expression.res | 16 ++++-- .../Reducer_Expression_ExpressionBuilder.res | 2 +- .../Reducer_Peggy/Reducer_Peggy_Parse.res | 17 ++++++- .../Reducer_Peggy_ToExpression.res | 11 ++++ .../rescript/Reducer/Reducer_Peggy/helpers.ts | 20 ++++++-- .../src/rescript/Reducer/Reducer_T.res | 2 +- .../ReducerInterface_StdLib.res | 47 +++++++++++++++++ 10 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index 3714b0b7..d8c2c04b 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -1,4 +1,5 @@ let fnList = Belt.Array.concatMany([ + FR_Builtin.library, FR_Dict.library, FR_Dist.library, FR_Danger.library, diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res new file mode 100644 index 00000000..6bced75d --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Builtin.res @@ -0,0 +1,51 @@ +open FunctionRegistry_Core +open FunctionRegistry_Helpers + +let nameSpace = "Builtin" + +type simpleDefinition = { + inputs: array, + fn: (array) => result, +} + +let makeFnMany = (name: string, definitions: array) => + Function.make( + ~name=name, + ~nameSpace, + ~requiresNamespace=false, + ~definitions=definitions->Js.Array2.map( + ({ inputs, fn }) => FnDefinition.make( + ~name=name, + ~inputs=inputs, + ~run=(inputs, _, _, _) => fn(inputs), + () + ) + ), + (), + ) + +let makeFn = (name: string, inputs: array, fn: (array) => result) => + makeFnMany(name, [{ inputs, fn }]) + +let library = [ +// TODO - other MathJS + Function.make( + ~name="add", + ~nameSpace, + ~requiresNamespace=false, + ~definitions=[ + 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_Dispatch/Reducer_Dispatch_BuiltIn.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res index 1ae512db..72c8aa79 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res @@ -45,11 +45,11 @@ let callInternal = ( ->Ok } - let arrayAtIndex = (aValueArray: array, fIndex: float) => - switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { - | Some(value) => value->Ok - | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error - } + // let arrayAtIndex = (aValueArray: array, fIndex: float) => + // switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { + // | Some(value) => value->Ok + // | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error + // } let moduleAtIndex = (nameSpace: Reducer_T.nameSpace, sIndex) => switch Bindings.get(nameSpace, sIndex) { @@ -57,11 +57,11 @@ let callInternal = ( | None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error } - let recordAtIndex = (dict: Belt.Map.String.t, sIndex) => - switch Belt.Map.String.get(dict, sIndex) { - | Some(value) => value->Ok - | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error - } + // let recordAtIndex = (dict: Belt.Map.String.t, sIndex) => + // switch Belt.Map.String.get(dict, sIndex) { + // | Some(value) => value->Ok + // | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error + // } let doAddArray = (originalA, b) => { let a = originalA->Js.Array2.copy @@ -107,10 +107,10 @@ let callInternal = ( // } switch call { - | ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex) + // | ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex) | ("$_atIndex_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex) - | ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex) - | ("$_constructArray_$", args) => IEvArray(args)->Ok + // | ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex) + // | ("$_constructArray_$", args) => IEvArray(args)->Ok | ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) // | ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace) // | ("$_exportBindings_$", [evValue]) => doIdentity(evValue) @@ -167,18 +167,12 @@ let dispatch = ( ): Reducer_T.value => try { let (fn, args) = call - if fn->Js.String2.startsWith("$") { - switch callInternal((fn, args), env, reducer) { - | Ok(v) => v - | Error(e) => raise(ErrorException(e)) - } - } else { - // There is a bug that prevents string match in patterns - // So we have to recreate a copy of the string - switch ExternalLibrary.dispatch((Js.String.make(fn), args), env, reducer, callInternal) { - | Ok(v) => v - | Error(e) => raise(ErrorException(e)) - } + + // There is a bug that prevents string match in patterns + // So we have to recreate a copy of the string + switch ExternalLibrary.dispatch((Js.String.make(fn), args), env, reducer, callInternal) { + | Ok(v) => v + | Error(e) => raise(ErrorException(e)) } } catch { | ErrorException(e) => raise(ErrorException(e)) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index 62a1f200..ecf6c29a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -19,7 +19,7 @@ let rec evaluate: T.reducerFn = ( | T.EBlock(statements) => { let innerContext = {...context, bindings: context.bindings->Bindings.extend} statements->Js.Array2.reduce( - (acc, statement) => statement->evaluate(innerContext), + (_, statement) => statement->evaluate(innerContext), T.IEvVoid ) } @@ -27,7 +27,7 @@ let rec evaluate: T.reducerFn = ( | T.EProgram(statements) => { // Js.log(`bindings: ${context.bindings->Reducer_Bindings.toString}`) let res = statements->Js.Array2.reduce( - (acc, statement) => statement->evaluate(context), + (_, statement) => statement->evaluate(context), T.IEvVoid ) // Js.log(`bindings after: ${context.bindings->Reducer_Bindings.toString}`) @@ -37,8 +37,16 @@ let rec evaluate: T.reducerFn = ( | T.EArray(elements) => elements->Js.Array2.map(element => evaluate(element, context))->T.IEvArray - | T.ERecord(map) => - RETodo("TODO")->ErrorException->raise + | T.ERecord(pairs) => + pairs->Js.Array2.map(((eKey, eValue)) => { + let key = eKey->evaluate(context) + let keyString = switch key { + | IEvString(s) => s + | _ => REOther("Record keys must be strings")->ErrorException->raise + } + let value = eValue->evaluate(context) + (keyString, value) + })->Belt.Map.String.fromArray->IEvRecord | T.EAssign(left, right) => { let result = right->evaluate(context) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index f45125c7..b11baa6e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -24,7 +24,7 @@ let eLambda = ( let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue -let eRecord = aMap => aMap->T.IEvRecord->T.EValue +let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord let eString = aString => aString->T.IEvString->T.EValue 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 b5099c36..58bfbb70 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 @@ -21,12 +21,14 @@ let parse = (expr: string): result => type nodeBlock = {...node, "statements": array} type nodeProgram = {...node, "statements": array} +type nodeArray = {...node, "elements": array} type nodeBoolean = {...node, "value": bool} type nodeCall = {...node, "fn": node, "args": array} type nodeFloat = {...node, "value": float} 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 nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node} type nodeModuleIdentifier = {...node, "value": string} @@ -37,7 +39,9 @@ type nodeVoid = node type peggyNode = | PgNodeBlock(nodeBlock) - | PgNodeProgram(nodeBlock) + | PgNodeProgram(nodeProgram) + | PgNodeArray(nodeArray) + | PgNodeRecord(nodeRecord) | PgNodeBoolean(nodeBoolean) | PgNodeFloat(nodeFloat) | PgNodeCall(nodeCall) @@ -54,6 +58,8 @@ type peggyNode = external castNodeBlock: node => nodeBlock = "%identity" external castNodeProgram: node => nodeProgram = "%identity" +external castNodeArray: node => nodeArray = "%identity" +external castNodeRecord: node => nodeRecord = "%identity" external castNodeBoolean: node => nodeBoolean = "%identity" external castNodeCall: node => nodeCall = "%identity" external castNodeFloat: node => nodeFloat = "%identity" @@ -73,6 +79,8 @@ let castNodeType = (node: node) => switch node["type"] { | "Block" => node->castNodeBlock->PgNodeBlock | "Program" => node->castNodeBlock->PgNodeProgram + | "Array" => node->castNodeArray->PgNodeArray + | "Record" => node->castNodeRecord->PgNodeRecord | "Boolean" => node->castNodeBoolean->PgNodeBoolean | "Call" => node->castNodeCall->PgNodeCall | "Float" => node->castNodeFloat->PgNodeFloat @@ -96,10 +104,17 @@ let rec pgToString = (peggyNode: peggyNode): string => { let nodesToStringUsingSeparator = (nodes: array, separator: string): string => nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") + let pgNodesToStringUsingSeparator = (nodes: array, separator: string): string => + nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") + switch peggyNode { | PgNodeBlock(node) | PgNodeProgram(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" + | PgNodeArray(node) + => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]" + | PgNodeRecord(node) + => "{" ++ node["elements"]->Js.Array2.map(element => PgNodeKeyValue(element))->pgNodesToStringUsingSeparator(", ") ++ "}" | PgNodeBoolean(node) => node["value"]->Js.String.make | PgNodeCall(node) => "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")" | PgNodeFloat(node) => node["value"]->Js.String.make 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 3b997440..c1823f5c 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 @@ -18,11 +18,22 @@ let rec fromNode = (node: Parse.node): expression => { let body = nodeLambda["body"]->caseBlock ExpressionBuilder.eLambda(args, body) + + } + + let caseRecord = (nodeRecord): expression => { + nodeRecord["elements"] + ->Js.Array2.map( + keyValueNode => (keyValueNode["key"]->fromNode, keyValueNode["value"]->fromNode) + ) + ->ExpressionBuilder.eRecord } switch Parse.castNodeType(node) { | PgNodeBlock(nodeBlock) => caseBlock(nodeBlock) | PgNodeProgram(nodeProgram) => caseProgram(nodeProgram) + | PgNodeArray(nodeArray) => ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode)) + | PgNodeRecord(nodeRecord) => caseRecord(nodeRecord) | PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"]) | PgNodeCall(nodeCall) => ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode)) | PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"]) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts index ebf6dd2e..4dd66205 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts @@ -45,6 +45,16 @@ type NodeProgram = { statements: AnyPeggyNode[]; }; +type NodeArray = { + type: "Array"; + elements: AnyPeggyNode[]; +}; + +type NodeRecord = { + type: "Record"; + elements: NodeKeyValue[]; +}; + type NodeCall = { type: "Call"; fn: AnyPeggyNode; @@ -103,6 +113,8 @@ type NodeBoolean = { }; export type AnyPeggyNode = + | NodeArray + | NodeRecord | NodeBlock | NodeProgram | NodeCall @@ -124,11 +136,11 @@ export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) { } } -export function constructArray(elems: AnyPeggyNode[]) { - return makeFunctionCall("$_constructArray_$", elems); +export function constructArray(elements: AnyPeggyNode[]) { + return { type: "Array", elements }; } -export function constructRecord(elems: AnyPeggyNode[]) { - return makeFunctionCall("$_constructRecord_$", elems); +export function constructRecord(elements: AnyPeggyNode[]) { + return { type: "Record", elements }; } export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res index a70e0183..a207f69c 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res @@ -34,7 +34,7 @@ and expression = | EBlock(array) | EProgram(array) // programs are similar to blocks, but don't create an inner scope. there can be only one program at the top level of the expression. | EArray(array) - | ERecord(Belt.Map.String.t) + | ERecord(array<(expression, expression)>) | ESymbol(string) | ETernary(expression, expression, expression) | EAssign(string, expression) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res index 81daff42..2e356984 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res @@ -1,3 +1,5 @@ +exception ErrorException = Reducer_ErrorValue.ErrorException + let internalStdLib: Reducer_Bindings.t = { let res = Reducer_Bindings.makeEmptyBindings() ->SquiggleLibrary_Math.makeBindings @@ -12,6 +14,51 @@ let internalStdLib: Reducer_Bindings.t = { } )->Reducer_T.IEvLambda) + let _ = res->Reducer_Bindings.set("$_atIndex_$", Reducer_Expression_Lambda.makeFFILambda( + (inputs, _, _) => { + switch inputs { + | [IEvArray(aValueArray), IEvNumber(fIndex)] => { + switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { + | Some(value) => value + | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->ErrorException->raise + } + } + | [IEvRecord(dict), IEvString(sIndex)] => { + switch Belt.Map.String.get(dict, sIndex) { + | Some(value) => value + | None => RERecordPropertyNotFound("Record index not found", sIndex)->ErrorException->raise + } + } + | _ => REOther("Trying to access key on wrong value")->ErrorException->raise + } + } + )->Reducer_T.IEvLambda) + + // TODO: + // () => ReducerInterface_GenericDistribution.dispatch(call, environment), + // () => ReducerInterface_Date.dispatch(call, environment), + // () => ReducerInterface_Duration.dispatch(call, environment), + // () => ReducerInterface_Number.dispatch(call, environment), + + // Reducer_Dispatch_BuiltIn: + + // [x] | ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex) + // [ ] | ("$_atIndex_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex) + // [x] | ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex) + // [ ] | ("$_constructArray_$", args) => IEvArray(args)->Ok + // [ ] | ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) + // [ ] | ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) => doAddArray(aValueArray, bValueArray) + // [ ] | ("concat", [IEvString(aValueString), IEvString(bValueString)]) => doAddString(aValueString, bValueString) + // [ ] | ("inspect", [value, IEvString(label)]) => inspectLabel(value, label) + // [ ] | ("inspect", [value]) => inspect(value) + // [ ] | (_, [IEvBool(_)]) + // [ ] | (_, [IEvNumber(_)]) + // [ ] | (_, [IEvString(_)]) + // [ ] | (_, [IEvBool(_), IEvBool(_)]) + // [ ] | (_, [IEvNumber(_), IEvNumber(_)]) + // [ ] | (_, [IEvString(_), IEvString(_)]) => callMathJs(call) + + FunctionRegistry_Library.registry.fnNameDict->Js.Dict.keys->Js.Array2.forEach( (name) => { let _ = res->Reducer_Bindings.set(name, Reducer_Expression_Lambda.makeFFILambda(