conver the rest of dispatch functions (Date, Duration)

This commit is contained in:
Vyacheslav Matyukhin 2022-09-18 18:33:20 +04:00
parent bbc1bee313
commit 6aa2280543
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
15 changed files with 256 additions and 105 deletions

View File

@ -111,4 +111,8 @@ export class SqProject {
setEnvironment(environment: environment) {
RSProject.setEnvironment(this._value, environment);
}
getEnvironment(): environment {
return RSProject.getEnvironment(this._value);
}
}

View File

@ -211,6 +211,10 @@ let evaluate = (sourceCode: string): (
let setEnvironment = (project: reducerProject, environment: environment): unit =>
project->Private.setEnvironment(environment)
@genType
let getEnvironment = (project: reducerProject): environment =>
project->Private.getEnvironment
/*
Foreign function interface is intentionally demolished.
There is another way to do that: Umur.

View File

@ -10,6 +10,8 @@ type rec frType =
| FRTypeNumber
| FRTypeBool
| FRTypeNumeric
| FRTypeDate
| FRTypeTimeDuration
| FRTypeDistOrNumber
| FRTypeDist
| FRTypeLambda
@ -29,6 +31,8 @@ and frTypeRecordParam = (string, frType)
type rec frValue =
| FRValueNumber(float)
| FRValueBool(bool)
| FRValueDate(Js.Date.t)
| FRValueTimeDuration(float)
| FRValueDist(DistributionTypes.genericDist)
| FRValueArray(array<frValue>)
| FRValueDistOrNumber(frValueDistOrNumber)
@ -65,15 +69,14 @@ type function = {
isExperimental: bool,
}
type fnNameDict = Js.Dict.t<array<fnDefinition>>
type registry = {functions: array<function>, fnNameDict: fnNameDict}
module FRType = {
type t = frType
let rec toString = (t: t) =>
switch t {
| FRTypeNumber => "number"
| FRTypeBool => "bool"
| FRTypeDate => "date"
| FRTypeTimeDuration => "duration"
| FRTypeNumeric => "numeric"
| FRTypeDist => "distribution"
| FRTypeDistOrNumber => "distribution|number"
@ -90,6 +93,7 @@ module FRType = {
}
let rec toFrValue = (r: internalExpressionValue): option<frValue> =>
// not all value variants are supported, but it's not important (we'll probably deprecate frValues soon anyway)
switch r {
| IEvNumber(f) => Some(FRValueNumber(f))
| IEvString(f) => Some(FRValueString(f))
@ -111,6 +115,8 @@ module FRType = {
| (FRTypeString, IEvString(f)) => Some(FRValueString(f))
| (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f))
| (FRTypeBool, IEvBool(f)) => Some(FRValueBool(f))
| (FRTypeDate, IEvDate(f)) => Some(FRValueDate(f))
| (FRTypeTimeDuration, IEvTimeDuration(f)) => Some(FRValueTimeDuration(f))
| (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f)))
| (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) =>
Some(FRValueDistOrNumber(FRValueNumber(f)))
@ -147,6 +153,8 @@ module FRType = {
switch e {
| FRValueNumber(f) => IEvNumber(f)
| FRValueBool(f) => IEvBool(f)
| FRValueDate(f) => IEvDate(f)
| FRValueTimeDuration(f) => IEvTimeDuration(f)
| FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n)
| FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n)
| FRValueDist(dist) => IEvDistribution(dist)
@ -265,33 +273,44 @@ module Function = {
}
module Registry = {
type fnNameDict = Belt.Map.String.t<array<fnDefinition>>
type registry = {functions: array<function>, fnNameDict: fnNameDict}
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
let allExamplesWithFns = (r: registry) =>
r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany
let allNames = (r: registry) => r.fnNameDict->Belt.Map.String.keysToArray
let _buildFnNameDict = (r: array<function>): fnNameDict => {
// 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 => {
// Three layers of reduce:
// 1. functions
// 2. definitions of each function
// 3. name variations of each definition
r->Belt.Array.reduce(
Belt.Map.String.empty,
(acc, fn) =>
fn.definitions->Belt.Array.reduce(
acc,
(acc, def) => {
let names =
[
fn.nameSpace == "" ? [] : [`${fn.nameSpace}.${def.name}`],
fn.requiresNamespace ? [] : [def.name],
]->E.A.concatMany
names->Js.Array2.forEach(name => {
switch res->Js.Dict.get(name) {
names->Belt.Array.reduce(acc, (acc, name) => {
switch acc->Belt.Map.String.get(name) {
| Some(fns) => {
let _ = fns->Js.Array2.push(def)
let _ = fns->Js.Array2.push(def) // mutates the array, no need to update acc
acc
}
| None => res->Js.Dict.set(name, [def])
| None => acc->Belt.Map.String.set(name, [def])
}
})
})
)
res
}
let make = (fns: array<function>): registry => {
@ -306,7 +325,7 @@ module Registry = {
env: Reducer_T.environment,
reducer: Reducer_T.reducerFn,
): result<internalExpressionValue, errorValue> => {
switch Js.Dict.get(registry.fnNameDict, fnName) {
switch Belt.Map.String.get(registry.fnNameDict, fnName) {
| Some(definitions) => {
let showNameMatchDefinitions = () => {
let defsString =

View File

@ -11,6 +11,8 @@ let fnList = Belt.Array.concatMany([
FR_Scoring.library,
FR_GenericDist.library,
FR_Units.library,
FR_Date.library,
FR_Mathjs.library,
])
let registry = FunctionRegistry_Core.Registry.make(fnList)

View File

@ -51,8 +51,20 @@ let makeBinaryCmpFn = (name: string, fn: (float, float) => bool) => {
)
}
let makeBinaryBooleanFn = (name: string, fn: (bool, bool) => bool) => {
makeFn(
name,
[FRTypeBool, FRTypeBool],
inputs => {
switch inputs {
| [IEvBool(x), IEvBool(y)] => fn(x, y)->IEvBool->Ok
| _ => Error(impossibleError)
}
}
)
}
let library = [
// TODO - other MathJS
makeBinaryFn("add", (x, y) => x +. y),
makeBinaryFn("subtract", (x, y) => x -. y),
makeBinaryFn("multiply", (x, y) => x *. y),
@ -63,6 +75,8 @@ let library = [
makeBinaryCmpFn("smallerEq", (x, y) => x <= y),
makeBinaryCmpFn("larger", (x, y) => x > y),
makeBinaryCmpFn("largerEq", (x, y) => x >= y),
makeBinaryBooleanFn("or", (x, y) => x || y),
makeBinaryBooleanFn("and", (x, y) => x && y),
makeFn(
"unaryMinus",
[FRTypeNumber],

View File

@ -0,0 +1,155 @@
open FunctionRegistry_Core
open FunctionRegistry_Helpers
let makeFn = (
name: string,
inputs: array<frType>,
fn: array<internalExpressionValue> => result<internalExpressionValue, errorValue>
) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _, _) => fn(inputs), ())
],
()
)
let makeNumberToDurationFn = (
name: string,
fn: float => DateTime.Duration.t,
) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(~name, ~inputs=[FRTypeNumber], ~run=(inputs, _, _, _) =>
switch inputs {
| [IEvNumber(t)] => IEvTimeDuration(fn(t))->Ok
| _ => Error(impossibleError)
}, ())
],
()
)
let makeDurationToNumberFn = (
name: string,
fn: DateTime.Duration.t => float,
) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(~name, ~inputs=[FRTypeTimeDuration], ~run=(inputs, _, _, _) =>
switch inputs {
| [IEvTimeDuration(t)] => IEvNumber(fn(t))->Ok
| _ => Error(impossibleError)
}, ())
],
()
)
let library = [
makeFn("toString", [FRTypeDate], (inputs) =>
switch inputs {
| [IEvDate(t)] => IEvString(DateTime.Date.toString(t))->Ok
| _ => Error(impossibleError)
}
),
makeFn("makeDateFromYear", [FRTypeNumber], (inputs) =>
switch inputs {
| [IEvNumber(year)] =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => IEvDate(t)->Ok
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error
}
| _ => Error(impossibleError)
}
),
makeFn("dateFromNumber", [FRTypeNumber], (inputs) =>
switch inputs {
| [IEvNumber(f)] => IEvDate(DateTime.Date.fromFloat(f))->Ok
| _ => Error(impossibleError)
}
),
makeFn("toNumber", [FRTypeDate], (inputs) =>
switch inputs {
| [IEvDate(f)] => IEvNumber(DateTime.Date.toFloat(f))->Ok
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeDate, FRTypeDate], (inputs) =>
switch inputs {
| [IEvDate(d1), IEvDate(d2)] => switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => IEvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeDate, FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvDate(d1), IEvTimeDuration(d2)] =>
IEvDate(DateTime.Date.subtractDuration(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("add", [FRTypeDate, FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvDate(d1), IEvTimeDuration(d2)] =>
IEvDate(DateTime.Date.addDuration(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("toString", [FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvTimeDuration(t)] => IEvString(DateTime.Duration.toString(t))->Ok
| _ => Error(impossibleError)
}
),
makeNumberToDurationFn("minutes", DateTime.Duration.fromMinutes),
makeNumberToDurationFn("fromUnit_minutes", DateTime.Duration.fromMinutes),
makeNumberToDurationFn("hours", DateTime.Duration.fromHours),
makeNumberToDurationFn("fromUnit_hours", DateTime.Duration.fromHours),
makeNumberToDurationFn("days", DateTime.Duration.fromDays),
makeNumberToDurationFn("fromUnit_days", DateTime.Duration.fromDays),
makeNumberToDurationFn("years", DateTime.Duration.fromYears),
makeNumberToDurationFn("fromUnit_years", DateTime.Duration.fromYears),
makeDurationToNumberFn("toMinutes", DateTime.Duration.toMinutes),
makeDurationToNumberFn("toHours", DateTime.Duration.toHours),
makeDurationToNumberFn("toDays", DateTime.Duration.toDays),
makeDurationToNumberFn("toYears", DateTime.Duration.toYears),
makeFn("add", [FRTypeTimeDuration, FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] => IEvTimeDuration(DateTime.Duration.add(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("subtract", [FRTypeTimeDuration, FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] => IEvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("multiply", [FRTypeTimeDuration, FRTypeNumber], (inputs) =>
switch inputs {
| [IEvTimeDuration(d1), IEvNumber(d2)] => IEvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("divide", [FRTypeTimeDuration, FRTypeNumber], (inputs) =>
switch inputs {
| [IEvTimeDuration(d1), IEvNumber(d2)] => IEvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok
| _ => Error(impossibleError)
}
),
makeFn("divide", [FRTypeTimeDuration, FRTypeTimeDuration], (inputs) =>
switch inputs {
| [IEvTimeDuration(d1), IEvTimeDuration(d2)] => IEvNumber(d1 /. d2)->Ok
| _ => Error(impossibleError)
}
),
]

View File

@ -310,8 +310,6 @@ module Old = {
| String(d) => Ok(IEvString(d))
| Bool(d) => Ok(IEvBool(d))
| FloatArray(d) => Ok(IEvArray(d |> E.A.fmap(r => Reducer_T.IEvNumber(r))))
// // FIXME - can't propagate error objects through FunctionRegistry
// | GenDistError(err) => Error(REDistributionError(err))
| GenDistError(err) => Error(REDistributionError(err))
}
@ -370,8 +368,7 @@ let library = E.A.concatMany([
makeProxyFn("max", [FRTypeDist]),
makeProxyFn("mode", [FRTypeDist]),
makeProxyFn("integralSum", [FRTypeDist]),
// // FIXME: doesn't work with Js.Dict in FunctionRegistry_Core
// makeProxyFn("toString", [FRTypeDist]),
makeProxyFn("toString", [FRTypeDist]),
makeProxyFn("sparkline", [FRTypeDist]),
makeProxyFn("sparkline", [FRTypeDist, FRTypeNumber]),
makeProxyFn("exp", [FRTypeDist]),

View File

@ -0,0 +1,38 @@
open FunctionRegistry_Core
open FunctionRegistry_Helpers
// FIXME - copy-pasted (see FR_Date.res and others)
let makeFn = (
name: string,
inputs: array<frType>,
fn: array<internalExpressionValue> => result<internalExpressionValue, errorValue>
) =>
Function.make(
~name,
~nameSpace="",
~requiresNamespace=false,
~definitions=[
FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _, _) => fn(inputs), ())
],
()
)
@module("mathjs") external dummy_: string => unit = "evaluate"
let dummy1_ = dummy_ //Deceive the compiler to make the import although we wont make a call from rescript. Otherwise the optimizer deletes the import
let mathjsFactorial: float => 'a = %raw(`function (expr) { return Mathjs.factorial(expr); }`)
let library = [
// TODO - other MathJS
// https://mathjs.org/docs/reference/functions.html
makeFn(
"factorial",
[FRTypeNumber],
inputs => {
switch inputs {
| [IEvNumber(x)] => mathjsFactorial(x)->Reducer_Js_Gate.jsToIEv
| _ => Error(impossibleError)
}
}
),
]

View File

@ -93,7 +93,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
})
switch lambda {
| T.IEvLambda(lambda) => (Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate), context)
| _ => REExpectedType("Lambda", "")->Reducer_ErrorValue.ErrorException->raise
| _ => RENotAFunction(lambda->ReducerInterface_InternalExpressionValue.toString)->Reducer_ErrorValue.ErrorException->raise
}
}
}

View File

@ -1,27 +0,0 @@
module T = Reducer_T
let dispatch = (
call: ReducerInterface_InternalExpressionValue.functionCall,
_: GenericDist.env,
): option<result<T.value, Reducer_ErrorValue.errorValue>> => {
switch call {
| ("toString", [IEvDate(t)]) => T.IEvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [IEvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => T.IEvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [IEvNumber(f)]) => T.IEvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [IEvDate(f)]) => T.IEvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [IEvDate(d1), IEvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => T.IEvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [IEvDate(d1), IEvTimeDuration(d2)]) =>
T.IEvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [IEvDate(d1), IEvTimeDuration(d2)]) =>
T.IEvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}

View File

@ -1,36 +0,0 @@
module IEV = ReducerInterface_InternalExpressionValue
module T = Reducer_T
type internalExpressionValue = IEV.t
let dispatch = (call: IEV.functionCall, _: T.environment): option<
result<Reducer_T.value, Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [IEvTimeDuration(t)]) => T.IEvString(DateTime.Duration.toString(t))->Ok->Some
| ("minutes", [IEvNumber(f)]) => T.IEvTimeDuration(DateTime.Duration.fromMinutes(f))->Ok->Some
| ("fromUnit_minutes", [IEvNumber(f)]) =>
T.IEvTimeDuration(DateTime.Duration.fromMinutes(f))->Ok->Some
| ("hours", [IEvNumber(f)]) => T.IEvTimeDuration(DateTime.Duration.fromHours(f))->Ok->Some
| ("fromUnit_hours", [IEvNumber(f)]) =>
T.IEvTimeDuration(DateTime.Duration.fromHours(f))->Ok->Some
| ("days", [IEvNumber(f)]) => T.IEvTimeDuration(DateTime.Duration.fromDays(f))->Ok->Some
| ("fromUnit_days", [IEvNumber(f)]) => T.IEvTimeDuration(DateTime.Duration.fromDays(f))->Ok->Some
| ("years", [IEvNumber(f)]) => T.IEvTimeDuration(DateTime.Duration.fromYears(f))->Ok->Some
| ("fromUnit_years", [IEvNumber(f)]) =>
T.IEvTimeDuration(DateTime.Duration.fromYears(f))->Ok->Some
| ("toHours", [IEvTimeDuration(f)]) => T.IEvNumber(DateTime.Duration.toHours(f))->Ok->Some
| ("toMinutes", [IEvTimeDuration(f)]) => T.IEvNumber(DateTime.Duration.toMinutes(f))->Ok->Some
| ("toDays", [IEvTimeDuration(f)]) => T.IEvNumber(DateTime.Duration.toDays(f))->Ok->Some
| ("toYears", [IEvTimeDuration(f)]) => T.IEvNumber(DateTime.Duration.toYears(f))->Ok->Some
| ("add", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) =>
T.IEvTimeDuration(DateTime.Duration.add(d1, d2))->Ok->Some
| ("subtract", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) =>
T.IEvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok->Some
| ("multiply", [IEvTimeDuration(d1), IEvNumber(d2)]) =>
T.IEvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok->Some
| ("divide", [IEvTimeDuration(d1), IEvNumber(d2)]) =>
T.IEvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok->Some
| ("divide", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) => T.IEvNumber(d1 /. d2)->Ok->Some
| _ => None
}
}

View File

@ -11,8 +11,8 @@ let dispatch = (
): result<Reducer_T.value, 'e> => {
E.A.O.firstSomeFn([
// () => ReducerInterface_GenericDistribution.dispatch(call, environment),
() => ReducerInterface_Date.dispatch(call, environment),
() => ReducerInterface_Duration.dispatch(call, environment),
// () => ReducerInterface_Date.dispatch(call, environment),
// () => ReducerInterface_Duration.dispatch(call, environment),
// () => ReducerInterface_Number.dispatch(call, environment),
// () => FunctionRegistry_Library.dispatch(call, environment, reducer),
])->E.O2.defaultFn(() => chain(call, environment, reducer))

View File

@ -60,8 +60,8 @@ let internalStdLib: Reducer_T.namespace = {
// [ ] | (_, [IEvNumber(_), IEvNumber(_)])
// [ ] | (_, [IEvString(_), IEvString(_)]) => callMathJs(call)
let res = FunctionRegistry_Library.registry.fnNameDict
->Js.Dict.keys
let res = FunctionRegistry_Library.registry
->FunctionRegistry_Core.Registry.allNames
->Belt.Array.reduce(res, (cur, name) => {
cur->Reducer_Namespace.set(
name,

View File

@ -1,10 +0,0 @@
// @module("./MathjsWrapper.js")
// external parseMathExt: string => Js.Json.t = "parseMath"
// let parseMath = (str: string): result<Js.Json.t, string> =>
// switch parseMathExt(str) {
// | exception Js.Exn.Error(err) => Error(Js.Exn.message(err) |> E.O.default("MathJS Parse Error"))
// | exception _ => Error("MathJS Parse Error")
// | j => Ok(j)
// }

View File

@ -1,9 +0,0 @@
const math = require("mathjs");
function parseMath(f) {
return JSON.parse(JSON.stringify(math.parse(f)));
}
module.exports = {
parseMath,
};