This commit is contained in:
Vyacheslav Matyukhin 2022-09-11 23:56:07 +04:00
parent f2dccd4f1e
commit fea89abff9
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
10 changed files with 175 additions and 36 deletions

View File

@ -1,4 +1,5 @@
let fnList = Belt.Array.concatMany([ let fnList = Belt.Array.concatMany([
FR_Builtin.library,
FR_Dict.library, FR_Dict.library,
FR_Dist.library, FR_Dist.library,
FR_Danger.library, FR_Danger.library,

View File

@ -0,0 +1,51 @@
open FunctionRegistry_Core
open FunctionRegistry_Helpers
let nameSpace = "Builtin"
type simpleDefinition = {
inputs: array<frType>,
fn: (array<internalExpressionValue>) => result<internalExpressionValue, string>,
}
let makeFnMany = (name: string, definitions: array<simpleDefinition>) =>
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<frType>, fn: (array<internalExpressionValue>) => result<internalExpressionValue, string>) =>
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)
}
},
()
),
],
(),
),
]

View File

@ -45,11 +45,11 @@ let callInternal = (
->Ok ->Ok
} }
let arrayAtIndex = (aValueArray: array<Reducer_T.value>, fIndex: float) => // let arrayAtIndex = (aValueArray: array<Reducer_T.value>, fIndex: float) =>
switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { // switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) {
| Some(value) => value->Ok // | Some(value) => value->Ok
| None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error // | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error
} // }
let moduleAtIndex = (nameSpace: Reducer_T.nameSpace, sIndex) => let moduleAtIndex = (nameSpace: Reducer_T.nameSpace, sIndex) =>
switch Bindings.get(nameSpace, sIndex) { switch Bindings.get(nameSpace, sIndex) {
@ -57,11 +57,11 @@ let callInternal = (
| None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error | None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error
} }
let recordAtIndex = (dict: Belt.Map.String.t<Reducer_T.value>, sIndex) => // let recordAtIndex = (dict: Belt.Map.String.t<Reducer_T.value>, sIndex) =>
switch Belt.Map.String.get(dict, sIndex) { // switch Belt.Map.String.get(dict, sIndex) {
| Some(value) => value->Ok // | Some(value) => value->Ok
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error // | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
} // }
let doAddArray = (originalA, b) => { let doAddArray = (originalA, b) => {
let a = originalA->Js.Array2.copy let a = originalA->Js.Array2.copy
@ -107,10 +107,10 @@ let callInternal = (
// } // }
switch call { 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_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex)
| ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex) // | ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex)
| ("$_constructArray_$", args) => IEvArray(args)->Ok // | ("$_constructArray_$", args) => IEvArray(args)->Ok
| ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) | ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
// | ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace) // | ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace)
// | ("$_exportBindings_$", [evValue]) => doIdentity(evValue) // | ("$_exportBindings_$", [evValue]) => doIdentity(evValue)
@ -167,19 +167,13 @@ let dispatch = (
): Reducer_T.value => ): Reducer_T.value =>
try { try {
let (fn, args) = call 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 // There is a bug that prevents string match in patterns
// So we have to recreate a copy of the string // So we have to recreate a copy of the string
switch ExternalLibrary.dispatch((Js.String.make(fn), args), env, reducer, callInternal) { switch ExternalLibrary.dispatch((Js.String.make(fn), args), env, reducer, callInternal) {
| Ok(v) => v | Ok(v) => v
| Error(e) => raise(ErrorException(e)) | Error(e) => raise(ErrorException(e))
} }
}
} catch { } catch {
| ErrorException(e) => raise(ErrorException(e)) | ErrorException(e) => raise(ErrorException(e))
| Js.Exn.Error(obj) => | Js.Exn.Error(obj) =>

View File

@ -19,7 +19,7 @@ let rec evaluate: T.reducerFn = (
| T.EBlock(statements) => { | T.EBlock(statements) => {
let innerContext = {...context, bindings: context.bindings->Bindings.extend} let innerContext = {...context, bindings: context.bindings->Bindings.extend}
statements->Js.Array2.reduce( statements->Js.Array2.reduce(
(acc, statement) => statement->evaluate(innerContext), (_, statement) => statement->evaluate(innerContext),
T.IEvVoid T.IEvVoid
) )
} }
@ -27,7 +27,7 @@ let rec evaluate: T.reducerFn = (
| T.EProgram(statements) => { | T.EProgram(statements) => {
// Js.log(`bindings: ${context.bindings->Reducer_Bindings.toString}`) // Js.log(`bindings: ${context.bindings->Reducer_Bindings.toString}`)
let res = statements->Js.Array2.reduce( let res = statements->Js.Array2.reduce(
(acc, statement) => statement->evaluate(context), (_, statement) => statement->evaluate(context),
T.IEvVoid T.IEvVoid
) )
// Js.log(`bindings after: ${context.bindings->Reducer_Bindings.toString}`) // Js.log(`bindings after: ${context.bindings->Reducer_Bindings.toString}`)
@ -37,8 +37,16 @@ let rec evaluate: T.reducerFn = (
| T.EArray(elements) => | T.EArray(elements) =>
elements->Js.Array2.map(element => evaluate(element, context))->T.IEvArray elements->Js.Array2.map(element => evaluate(element, context))->T.IEvArray
| T.ERecord(map) => | T.ERecord(pairs) =>
RETodo("TODO")->ErrorException->raise 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) => { | T.EAssign(left, right) => {
let result = right->evaluate(context) let result = right->evaluate(context)

View File

@ -24,7 +24,7 @@ let eLambda = (
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue 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 let eString = aString => aString->T.IEvString->T.EValue

View File

@ -21,12 +21,14 @@ let parse = (expr: string): result<node, errorValue> =>
type nodeBlock = {...node, "statements": array<node>} type nodeBlock = {...node, "statements": array<node>}
type nodeProgram = {...node, "statements": array<node>} type nodeProgram = {...node, "statements": array<node>}
type nodeArray = {...node, "elements": array<node>}
type nodeBoolean = {...node, "value": bool} type nodeBoolean = {...node, "value": bool}
type nodeCall = {...node, "fn": node, "args": array<node>} type nodeCall = {...node, "fn": node, "args": array<node>}
type nodeFloat = {...node, "value": float} type nodeFloat = {...node, "value": float}
type nodeIdentifier = {...node, "value": string} type nodeIdentifier = {...node, "value": string}
type nodeInteger = {...node, "value": int} type nodeInteger = {...node, "value": int}
type nodeKeyValue = {...node, "key": node, "value": node} type nodeKeyValue = {...node, "key": node, "value": node}
type nodeRecord = {...node, "elements": array<nodeKeyValue>}
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": nodeBlock} type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": nodeBlock}
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node} type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
type nodeModuleIdentifier = {...node, "value": string} type nodeModuleIdentifier = {...node, "value": string}
@ -37,7 +39,9 @@ type nodeVoid = node
type peggyNode = type peggyNode =
| PgNodeBlock(nodeBlock) | PgNodeBlock(nodeBlock)
| PgNodeProgram(nodeBlock) | PgNodeProgram(nodeProgram)
| PgNodeArray(nodeArray)
| PgNodeRecord(nodeRecord)
| PgNodeBoolean(nodeBoolean) | PgNodeBoolean(nodeBoolean)
| PgNodeFloat(nodeFloat) | PgNodeFloat(nodeFloat)
| PgNodeCall(nodeCall) | PgNodeCall(nodeCall)
@ -54,6 +58,8 @@ type peggyNode =
external castNodeBlock: node => nodeBlock = "%identity" external castNodeBlock: node => nodeBlock = "%identity"
external castNodeProgram: node => nodeProgram = "%identity" external castNodeProgram: node => nodeProgram = "%identity"
external castNodeArray: node => nodeArray = "%identity"
external castNodeRecord: node => nodeRecord = "%identity"
external castNodeBoolean: node => nodeBoolean = "%identity" external castNodeBoolean: node => nodeBoolean = "%identity"
external castNodeCall: node => nodeCall = "%identity" external castNodeCall: node => nodeCall = "%identity"
external castNodeFloat: node => nodeFloat = "%identity" external castNodeFloat: node => nodeFloat = "%identity"
@ -73,6 +79,8 @@ let castNodeType = (node: node) =>
switch node["type"] { switch node["type"] {
| "Block" => node->castNodeBlock->PgNodeBlock | "Block" => node->castNodeBlock->PgNodeBlock
| "Program" => node->castNodeBlock->PgNodeProgram | "Program" => node->castNodeBlock->PgNodeProgram
| "Array" => node->castNodeArray->PgNodeArray
| "Record" => node->castNodeRecord->PgNodeRecord
| "Boolean" => node->castNodeBoolean->PgNodeBoolean | "Boolean" => node->castNodeBoolean->PgNodeBoolean
| "Call" => node->castNodeCall->PgNodeCall | "Call" => node->castNodeCall->PgNodeCall
| "Float" => node->castNodeFloat->PgNodeFloat | "Float" => node->castNodeFloat->PgNodeFloat
@ -96,10 +104,17 @@ let rec pgToString = (peggyNode: peggyNode): string => {
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string => let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
let pgNodesToStringUsingSeparator = (nodes: array<peggyNode>, separator: string): string =>
nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
switch peggyNode { switch peggyNode {
| PgNodeBlock(node) | PgNodeBlock(node)
| PgNodeProgram(node) | PgNodeProgram(node)
=> "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" => "{" ++ 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 | PgNodeBoolean(node) => node["value"]->Js.String.make
| PgNodeCall(node) => "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")" | PgNodeCall(node) => "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")"
| PgNodeFloat(node) => node["value"]->Js.String.make | PgNodeFloat(node) => node["value"]->Js.String.make

View File

@ -18,11 +18,22 @@ let rec fromNode = (node: Parse.node): expression => {
let body = nodeLambda["body"]->caseBlock let body = nodeLambda["body"]->caseBlock
ExpressionBuilder.eLambda(args, body) 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) { switch Parse.castNodeType(node) {
| PgNodeBlock(nodeBlock) => caseBlock(nodeBlock) | PgNodeBlock(nodeBlock) => caseBlock(nodeBlock)
| PgNodeProgram(nodeProgram) => caseProgram(nodeProgram) | PgNodeProgram(nodeProgram) => caseProgram(nodeProgram)
| PgNodeArray(nodeArray) => ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode))
| PgNodeRecord(nodeRecord) => caseRecord(nodeRecord)
| PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"]) | PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"])
| PgNodeCall(nodeCall) => ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode)) | PgNodeCall(nodeCall) => ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode))
| PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"]) | PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"])

View File

@ -45,6 +45,16 @@ type NodeProgram = {
statements: AnyPeggyNode[]; statements: AnyPeggyNode[];
}; };
type NodeArray = {
type: "Array";
elements: AnyPeggyNode[];
};
type NodeRecord = {
type: "Record";
elements: NodeKeyValue[];
};
type NodeCall = { type NodeCall = {
type: "Call"; type: "Call";
fn: AnyPeggyNode; fn: AnyPeggyNode;
@ -103,6 +113,8 @@ type NodeBoolean = {
}; };
export type AnyPeggyNode = export type AnyPeggyNode =
| NodeArray
| NodeRecord
| NodeBlock | NodeBlock
| NodeProgram | NodeProgram
| NodeCall | NodeCall
@ -124,11 +136,11 @@ export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) {
} }
} }
export function constructArray(elems: AnyPeggyNode[]) { export function constructArray(elements: AnyPeggyNode[]) {
return makeFunctionCall("$_constructArray_$", elems); return { type: "Array", elements };
} }
export function constructRecord(elems: AnyPeggyNode[]) { export function constructRecord(elements: AnyPeggyNode[]) {
return makeFunctionCall("$_constructRecord_$", elems); return { type: "Record", elements };
} }
export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock { export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock {

View File

@ -34,7 +34,7 @@ and expression =
| EBlock(array<expression>) | EBlock(array<expression>)
| EProgram(array<expression>) // 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. | EProgram(array<expression>) // 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<expression>) | EArray(array<expression>)
| ERecord(Belt.Map.String.t<expression>) | ERecord(array<(expression, expression)>)
| ESymbol(string) | ESymbol(string)
| ETernary(expression, expression, expression) | ETernary(expression, expression, expression)
| EAssign(string, expression) | EAssign(string, expression)

View File

@ -1,3 +1,5 @@
exception ErrorException = Reducer_ErrorValue.ErrorException
let internalStdLib: Reducer_Bindings.t = { let internalStdLib: Reducer_Bindings.t = {
let res = Reducer_Bindings.makeEmptyBindings() let res = Reducer_Bindings.makeEmptyBindings()
->SquiggleLibrary_Math.makeBindings ->SquiggleLibrary_Math.makeBindings
@ -12,6 +14,51 @@ let internalStdLib: Reducer_Bindings.t = {
} }
)->Reducer_T.IEvLambda) )->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( FunctionRegistry_Library.registry.fnNameDict->Js.Dict.keys->Js.Array2.forEach(
(name) => { (name) => {
let _ = res->Reducer_Bindings.set(name, Reducer_Expression_Lambda.makeFFILambda( let _ = res->Reducer_Bindings.set(name, Reducer_Expression_Lambda.makeFFILambda(