Merge pull request #333 from quantified-uncertainty/reducer-dev
Bindings in and Out
This commit is contained in:
commit
d6e4f77624
|
@ -7,5 +7,66 @@ open Expect
|
||||||
let expectParseToBe = (expr: string, answer: string) =>
|
let expectParseToBe = (expr: string, answer: string) =>
|
||||||
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
|
Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let expectParseOuterToBe = (expr: string, answer: string) =>
|
||||||
|
Reducer.parseOuter(expr)->Expression.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let expectParsePartialToBe = (expr: string, answer: string) =>
|
||||||
|
Reducer.parsePartial(expr)->Expression.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
let expectEvalToBe = (expr: string, answer: string) =>
|
let expectEvalToBe = (expr: string, answer: string) =>
|
||||||
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
|
||||||
|
Reducer.evaluateUsingExternalBindings(expr, bindings)->ExpressionValue.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let expectEvalPartialBindingsToBe = (
|
||||||
|
expr: string,
|
||||||
|
bindings: Reducer.externalBindings,
|
||||||
|
answer: string,
|
||||||
|
) =>
|
||||||
|
Reducer.evaluatePartialUsingExternalBindings(expr, bindings)
|
||||||
|
->ExpressionValue.toStringResultRecord
|
||||||
|
->expect
|
||||||
|
->toBe(answer)
|
||||||
|
|
||||||
|
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testParseOuterToBe = (expr, answer) => test(expr, () => expectParseOuterToBe(expr, answer))
|
||||||
|
let testParsePartialToBe = (expr, answer) => test(expr, () => expectParsePartialToBe(expr, answer))
|
||||||
|
let testDescriptionParseToBe = (desc, expr, answer) =>
|
||||||
|
test(desc, () => expectParseToBe(expr, answer))
|
||||||
|
|
||||||
|
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
test(expr, () => expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
|
||||||
|
module MySkip = {
|
||||||
|
let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testParseOuterToBe = (expr, answer) =>
|
||||||
|
Skip.test(expr, () => expectParseOuterToBe(expr, answer))
|
||||||
|
let testParsePartialToBe = (expr, answer) =>
|
||||||
|
Skip.test(expr, () => expectParsePartialToBe(expr, answer))
|
||||||
|
let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Skip.test(expr, () =>
|
||||||
|
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
module MyOnly = {
|
||||||
|
let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testParseOuterToBe = (expr, answer) =>
|
||||||
|
Only.test(expr, () => expectParseOuterToBe(expr, answer))
|
||||||
|
let testParsePartialToBe = (expr, answer) =>
|
||||||
|
Only.test(expr, () => expectParsePartialToBe(expr, answer))
|
||||||
|
let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
|
||||||
|
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
|
||||||
|
let testEvalPartialBindingsToBe = (expr, bindingsList, answer) =>
|
||||||
|
Only.test(expr, () =>
|
||||||
|
expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
describe("Parse for Bindings", () => {
|
||||||
|
testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))")
|
||||||
|
testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))")
|
||||||
|
testParseOuterToBe(
|
||||||
|
"y = x+1; y",
|
||||||
|
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Parse Partial", () => {
|
||||||
|
testParsePartialToBe(
|
||||||
|
"x",
|
||||||
|
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))",
|
||||||
|
)
|
||||||
|
testParsePartialToBe(
|
||||||
|
"y=x",
|
||||||
|
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))",
|
||||||
|
)
|
||||||
|
testParsePartialToBe(
|
||||||
|
"y=x+1",
|
||||||
|
"Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))",
|
||||||
|
)
|
||||||
|
testParsePartialToBe(
|
||||||
|
"y = x+1; z = y",
|
||||||
|
"Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Eval with Bindings", () => {
|
||||||
|
testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)")
|
||||||
|
testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||||
|
testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)")
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
Partial code is a partial code fragment that is cut out from a larger code.
|
||||||
|
Therefore it does not end with an expression.
|
||||||
|
*/
|
||||||
|
describe("Eval Partial", () => {
|
||||||
|
testEvalPartialBindingsToBe(
|
||||||
|
// A partial cannot end with an expression
|
||||||
|
"x",
|
||||||
|
list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
|
"Error(Assignment expected)",
|
||||||
|
)
|
||||||
|
testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1, y: 1})")
|
||||||
|
testEvalPartialBindingsToBe(
|
||||||
|
"y=x+1",
|
||||||
|
list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
|
"Ok({x: 1, y: 2})",
|
||||||
|
)
|
||||||
|
testEvalPartialBindingsToBe(
|
||||||
|
"y = x+1; z = y",
|
||||||
|
list{("x", ExpressionValue.EvNumber(1.))},
|
||||||
|
"Ok({x: 1, y: 2, z: 2})",
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,12 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
|
Skip.describe("Parse ternary operator", () => {
|
||||||
|
testParseToBe("true ? 'YES' : 'NO'", "Ok('YES')")
|
||||||
|
testParseToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
|
})
|
||||||
|
|
||||||
|
Skip.describe("Evaluate ternary operator", () => {
|
||||||
|
testEvalToBe("true ? 'YES' : 'NO'", "Ok('YES')")
|
||||||
|
testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')")
|
||||||
|
})
|
|
@ -1,15 +1,6 @@
|
||||||
open Jest
|
open Jest
|
||||||
open Reducer_TestHelpers
|
open Reducer_TestHelpers
|
||||||
|
|
||||||
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
let testDescriptionParseToBe = (desc, expr, answer) =>
|
|
||||||
test(desc, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
|
|
||||||
|
|
||||||
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
|
|
||||||
|
|
||||||
describe("reducer using mathjs parse", () => {
|
describe("reducer using mathjs parse", () => {
|
||||||
// Test the MathJs parser compatibility
|
// Test the MathJs parser compatibility
|
||||||
// Those tests toString that there is a semantic mapping from MathJs to Expression
|
// Those tests toString that there is a semantic mapping from MathJs to Expression
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"test:rescript": "jest --modulePathIgnorePatterns=__tests__/TS/*",
|
"test:rescript": "jest --modulePathIgnorePatterns=__tests__/TS/*",
|
||||||
"test:watch": "jest --watchAll",
|
"test:watch": "jest --watchAll",
|
||||||
"coverage:rescript": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report html",
|
"coverage:rescript": "rm -f *.coverage; yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report html",
|
||||||
"coverage:ts": "nyc --reporter=lcov yarn test:ts",
|
"coverage:ts": "yarn clean; yarn build; nyc --reporter=lcov yarn test:ts",
|
||||||
"coverage:rescript:ci": "yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report send-to Codecov",
|
"coverage:rescript:ci": "yarn clean; BISECT_ENABLE=yes yarn build; yarn test:rescript; bisect-ppx-report send-to Codecov",
|
||||||
"coverage:ts:ci": "yarn coverage:ts && codecov",
|
"coverage:ts:ci": "yarn coverage:ts && codecov",
|
||||||
"lint:rescript": "./lint.sh",
|
"lint:rescript": "./lint.sh",
|
||||||
|
@ -48,6 +48,7 @@
|
||||||
"gentype": "^4.3.0",
|
"gentype": "^4.3.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"moduleserve": "0.9.1",
|
"moduleserve": "0.9.1",
|
||||||
|
"reanalyze": "^2.19.0",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^27.1.4",
|
||||||
"ts-loader": "^9.2.8",
|
"ts-loader": "^9.2.8",
|
||||||
|
|
|
@ -6,5 +6,10 @@ module Js = Reducer_Js
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
|
|
||||||
type expressionValue = Reducer_Expression.expressionValue
|
type expressionValue = Reducer_Expression.expressionValue
|
||||||
|
type externalBindings = Expression.externalBindings
|
||||||
let evaluate = Expression.eval
|
let evaluate = Expression.eval
|
||||||
|
let evaluateUsingExternalBindings = Expression.evalUsingExternalBindings
|
||||||
|
let evaluatePartialUsingExternalBindings = Expression.evalPartialUsingExternalBindings
|
||||||
let parse = Expression.parse
|
let parse = Expression.parse
|
||||||
|
let parseOuter = Expression.parseOuter
|
||||||
|
let parsePartial = Expression.parsePartial
|
||||||
|
|
|
@ -7,7 +7,20 @@ module MathJs = Reducer_MathJs
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
|
||||||
|
@genType
|
||||||
|
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
@genType
|
@genType
|
||||||
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>
|
let evaluate: string => result<expressionValue, Reducer_ErrorValue.errorValue>
|
||||||
|
@genType
|
||||||
|
let evaluateUsingExternalBindings: (
|
||||||
|
string,
|
||||||
|
externalBindings,
|
||||||
|
) => result<expressionValue, Reducer_ErrorValue.errorValue>
|
||||||
|
@genType
|
||||||
|
let evaluatePartialUsingExternalBindings: (
|
||||||
|
string,
|
||||||
|
externalBindings,
|
||||||
|
) => result<externalBindings, Reducer_ErrorValue.errorValue>
|
||||||
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
||||||
|
let parseOuter: string => result<Expression.expression, ErrorValue.errorValue>
|
||||||
|
let parsePartial: string => result<Expression.expression, ErrorValue.errorValue>
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
module Builtin = Reducer_Dispatch_BuiltIn
|
module Builtin = Reducer_Dispatch_BuiltIn
|
||||||
|
module BuiltinMacros = Reducer_Dispatch_BuiltInMacros
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
Macros are like functions but instead of taking values as parameters,
|
||||||
|
they take expressions as parameters and return a new expression.
|
||||||
|
Macros are used to define language building blocks. They are like Lisp macros.
|
||||||
|
*/
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
|
type expression = ExpressionT.expression
|
||||||
|
|
||||||
|
type reducerFn = (
|
||||||
|
expression,
|
||||||
|
ExpressionT.bindings,
|
||||||
|
) => result<ExpressionValue.expressionValue, errorValue>
|
||||||
|
|
||||||
|
let dispatchMacroCall = (
|
||||||
|
list: list<expression>,
|
||||||
|
bindings: ExpressionT.bindings,
|
||||||
|
reduceExpression: reducerFn,
|
||||||
|
): result<expression, 'e> => {
|
||||||
|
let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result<
|
||||||
|
expression,
|
||||||
|
errorValue,
|
||||||
|
> =>
|
||||||
|
switch expression {
|
||||||
|
| ExpressionT.EValue(EvSymbol(aSymbol)) =>
|
||||||
|
switch bindings->Belt.Map.String.get(aSymbol) {
|
||||||
|
| Some(boundExpression) => boundExpression->Ok
|
||||||
|
| None => RESymbolNotFound(aSymbol)->Error
|
||||||
|
}
|
||||||
|
| ExpressionT.EValue(_) => expression->Ok
|
||||||
|
| ExpressionT.EBindings(_) => expression->Ok
|
||||||
|
| ExpressionT.EList(list) => {
|
||||||
|
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
|
||||||
|
racc->Result.flatMap(acc => {
|
||||||
|
each
|
||||||
|
->replaceSymbols(bindings)
|
||||||
|
->Result.flatMap(newNode => {
|
||||||
|
acc->Belt.List.add(newNode)->Ok
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
racc->Result.map(acc => acc->ExpressionT.EList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let doBindStatement = (statement: expression, bindings: ExpressionT.bindings) => {
|
||||||
|
switch statement {
|
||||||
|
| ExpressionT.EList(list{
|
||||||
|
ExpressionT.EValue(EvCall("$let")),
|
||||||
|
ExpressionT.EValue(EvSymbol(aSymbol)),
|
||||||
|
expressionToReduce,
|
||||||
|
}) => {
|
||||||
|
let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings)
|
||||||
|
|
||||||
|
let rNewValue =
|
||||||
|
rNewExpressionToReduce->Result.flatMap(newExpressionToReduce =>
|
||||||
|
reduceExpression(newExpressionToReduce, bindings)
|
||||||
|
)
|
||||||
|
|
||||||
|
let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue))
|
||||||
|
rNewExpression->Result.map(newExpression =>
|
||||||
|
Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings
|
||||||
|
)
|
||||||
|
}
|
||||||
|
| _ => REAssignmentExpected->Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let doExportVariableExpression = (bindings: ExpressionT.bindings) => {
|
||||||
|
let emptyDictionary: Js.Dict.t<ExpressionValue.expressionValue> = Js.Dict.empty()
|
||||||
|
let reducedBindings = bindings->Belt.Map.String.keep((_key, value) =>
|
||||||
|
switch value {
|
||||||
|
| ExpressionT.EValue(_) => true
|
||||||
|
| _ => false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let externalBindings = reducedBindings->Belt.Map.String.reduce(emptyDictionary, (
|
||||||
|
acc,
|
||||||
|
key,
|
||||||
|
expressionValue,
|
||||||
|
) => {
|
||||||
|
let value = switch expressionValue {
|
||||||
|
| EValue(aValue) => aValue
|
||||||
|
| _ => EvSymbol("internal")
|
||||||
|
}
|
||||||
|
Js.Dict.set(acc, key, value)
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
externalBindings->EvRecord->ExpressionT.EValue->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) =>
|
||||||
|
switch expression {
|
||||||
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) =>
|
||||||
|
REExpressionExpected->Error
|
||||||
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) =>
|
||||||
|
doExportVariableExpression(bindings)
|
||||||
|
| _ => replaceSymbols(expression, bindings)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch list {
|
||||||
|
| list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok
|
||||||
|
|
||||||
|
| list{
|
||||||
|
ExpressionT.EValue(EvCall("$$bindStatement")),
|
||||||
|
ExpressionT.EBindings(bindings),
|
||||||
|
statement,
|
||||||
|
} =>
|
||||||
|
doBindStatement(statement, bindings)
|
||||||
|
| list{
|
||||||
|
ExpressionT.EValue(EvCall("$$bindExpression")),
|
||||||
|
ExpressionT.EBindings(bindings),
|
||||||
|
expression,
|
||||||
|
} =>
|
||||||
|
doBindExpression(expression, bindings)
|
||||||
|
| _ => list->ExpressionT.EList->Ok
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,12 +39,26 @@ let parse_ = (expr: string, parser, converter): result<t, errorValue> =>
|
||||||
let parse = (mathJsCode: string): result<t, errorValue> =>
|
let parse = (mathJsCode: string): result<t, errorValue> =>
|
||||||
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
|
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
|
||||||
|
|
||||||
|
let parsePartial = (mathJsCode: string): result<t, errorValue> =>
|
||||||
|
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode)
|
||||||
|
|
||||||
|
let parseOuter = (mathJsCode: string): result<t, errorValue> =>
|
||||||
|
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromOuterNode)
|
||||||
|
|
||||||
let defaultBindings: T.bindings = Belt.Map.String.empty
|
let defaultBindings: T.bindings = Belt.Map.String.empty
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Recursively evaluate/reduce the expression (Lisp AST)
|
Recursively evaluate/reduce the expression (Lisp AST)
|
||||||
*/
|
*/
|
||||||
let reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
|
let rec reduceExpression = (expression: t, bindings: T.bindings): result<expressionValue, 'e> => {
|
||||||
|
/*
|
||||||
|
Macros are like functions but instead of taking values as parameters,
|
||||||
|
they take expressions as parameters and return a new expression.
|
||||||
|
Macros are used to define language building blocks. They are like Lisp macros.
|
||||||
|
*/
|
||||||
|
let doMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> =>
|
||||||
|
Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, reduceExpression)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
After reducing each level of expression(Lisp AST), we have a value list to evaluate
|
After reducing each level of expression(Lisp AST), we have a value list to evaluate
|
||||||
*/
|
*/
|
||||||
|
@ -54,72 +68,10 @@ let reduceExpression = (expression: t, bindings: T.bindings): result<expressionV
|
||||||
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
|
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Macros are like functions but instead of taking values as parameters,
|
|
||||||
they take expressions as parameters and return a new expression.
|
|
||||||
Macros are used to define language building blocks. They are like Lisp macros.
|
|
||||||
*/
|
|
||||||
let doMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> => {
|
|
||||||
let dispatchMacroCall = (list: list<t>, bindings: T.bindings): result<t, 'e> => {
|
|
||||||
let rec replaceSymbols = (expression: t, bindings: T.bindings): result<t, errorValue> =>
|
|
||||||
switch expression {
|
|
||||||
| T.EValue(EvSymbol(aSymbol)) =>
|
|
||||||
switch bindings->Belt.Map.String.get(aSymbol) {
|
|
||||||
| Some(boundExpression) => boundExpression->Ok
|
|
||||||
| None => RESymbolNotFound(aSymbol)->Error
|
|
||||||
}
|
|
||||||
| T.EValue(_) => expression->Ok
|
|
||||||
| T.EBindings(_) => expression->Ok
|
|
||||||
| T.EList(list) => {
|
|
||||||
let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) =>
|
|
||||||
racc->Result.flatMap(acc => {
|
|
||||||
each
|
|
||||||
->replaceSymbols(bindings)
|
|
||||||
->Result.flatMap(newNode => {
|
|
||||||
acc->Belt.List.add(newNode)->Ok
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
racc->Result.map(acc => acc->T.EList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let doBindStatement = (statement: t, bindings: T.bindings) => {
|
|
||||||
switch statement {
|
|
||||||
| T.EList(list{T.EValue(EvCall("$let")), T.EValue(EvSymbol(aSymbol)), expression}) => {
|
|
||||||
let rNewExpression = replaceSymbols(expression, bindings)
|
|
||||||
rNewExpression->Result.map(newExpression =>
|
|
||||||
Belt.Map.String.set(bindings, aSymbol, newExpression)->T.EBindings
|
|
||||||
)
|
|
||||||
}
|
|
||||||
| _ => REAssignmentExpected->Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let doBindExpression = (expression: t, bindings: T.bindings) => {
|
|
||||||
switch expression {
|
|
||||||
| T.EList(list{T.EValue(EvCall("$let")), ..._}) => REExpressionExpected->Error
|
|
||||||
| _ => replaceSymbols(expression, bindings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch list {
|
|
||||||
| list{T.EValue(EvCall("$$bindings"))} => bindings->T.EBindings->Ok
|
|
||||||
|
|
||||||
| list{T.EValue(EvCall("$$bindStatement")), T.EBindings(bindings), statement} =>
|
|
||||||
doBindStatement(statement, bindings)
|
|
||||||
| list{T.EValue(EvCall("$$bindExpression")), T.EBindings(bindings), expression} =>
|
|
||||||
doBindExpression(expression, bindings)
|
|
||||||
| _ => list->T.EList->Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list->dispatchMacroCall(bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
let rec seekMacros = (expression: t, bindings: T.bindings): result<t, 'e> =>
|
let rec seekMacros = (expression: t, bindings: T.bindings): result<t, 'e> =>
|
||||||
switch expression {
|
switch expression {
|
||||||
| T.EValue(_) => expression->Ok
|
| T.EValue(_value) => expression->Ok
|
||||||
|
| T.EBindings(_value) => expression->Ok
|
||||||
| T.EList(list) => {
|
| T.EList(list) => {
|
||||||
let racc: result<list<t>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
|
let racc: result<list<t>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
|
||||||
racc,
|
racc,
|
||||||
|
@ -135,7 +87,6 @@ let reduceExpression = (expression: t, bindings: T.bindings): result<expressionV
|
||||||
)
|
)
|
||||||
racc->Result.flatMap(acc => acc->doMacroCall(bindings))
|
racc->Result.flatMap(acc => acc->doMacroCall(bindings))
|
||||||
}
|
}
|
||||||
| T.EBindings(bindings) => T.EBindings(bindings)->Ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let rec reduceExpandedExpression = (expression: t): result<expressionValue, 'e> =>
|
let rec reduceExpandedExpression = (expression: t): result<expressionValue, 'e> =>
|
||||||
|
@ -156,7 +107,7 @@ let reduceExpression = (expression: t, bindings: T.bindings): result<expressionV
|
||||||
)
|
)
|
||||||
racc->Result.flatMap(acc => acc->reduceValueList)
|
racc->Result.flatMap(acc => acc->reduceValueList)
|
||||||
}
|
}
|
||||||
| T.EBindings(_) => RETodo("Cannot return bindings")->Error
|
| EBindings(_bindings) => RETodo("Error: Bindings cannot be reduced to values")->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
let rExpandedExpression: result<t, 'e> = expression->seekMacros(bindings)
|
let rExpandedExpression: result<t, 'e> = expression->seekMacros(bindings)
|
||||||
|
@ -165,17 +116,69 @@ let reduceExpression = (expression: t, bindings: T.bindings): result<expressionV
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let evalWBindingsExpression = (aExpression, bindings): result<expressionValue, 'e> =>
|
let evalUsingExternalBindingsExpression_ = (aExpression, bindings): result<expressionValue, 'e> =>
|
||||||
reduceExpression(aExpression, bindings)
|
reduceExpression(aExpression, bindings)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Evaluates MathJs code via Reducer using bindings and answers the result
|
Evaluates MathJs code via Reducer using bindings and answers the result.
|
||||||
|
When bindings are used, the code is a partial code as if it is cut from a larger code.
|
||||||
|
Therefore all statements are assignments.
|
||||||
*/
|
*/
|
||||||
let evalWBindings = (codeText: string, bindings: T.bindings) => {
|
let evalPartialUsingExternalBindings_ = (codeText: string, bindings: T.bindings) => {
|
||||||
parse(codeText)->Result.flatMap(code => code->evalWBindingsExpression(bindings))
|
parsePartial(codeText)->Result.flatMap(expression =>
|
||||||
|
expression->evalUsingExternalBindingsExpression_(bindings)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Evaluates MathJs code via Reducer and answers the result
|
Evaluates MathJs code via Reducer using bindings and answers the result.
|
||||||
|
When bindings are used, the code is a partial code as if it is cut from a larger code.
|
||||||
|
Therefore all statments are assignments.
|
||||||
*/
|
*/
|
||||||
let eval = (code: string) => evalWBindings(code, defaultBindings)
|
let evalOuterWBindings_ = (codeText: string, bindings: T.bindings) => {
|
||||||
|
parseOuter(codeText)->Result.flatMap(expression => expression->evalUsingExternalBindingsExpression_(bindings))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Evaluates MathJs code and bindings via Reducer and answers the result
|
||||||
|
*/
|
||||||
|
let eval = (codeText: string) => {
|
||||||
|
parse(codeText)->Result.flatMap(expression =>
|
||||||
|
expression->evalUsingExternalBindingsExpression_(defaultBindings)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type externalBindings = ReducerInterface.ExpressionValue.externalBindings //Js.Dict.t<expressionValue>
|
||||||
|
|
||||||
|
let externalBindingsToBindings = (externalBindings: externalBindings): T.bindings => {
|
||||||
|
let keys = Js.Dict.keys(externalBindings)
|
||||||
|
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
||||||
|
let value = Js.Dict.unsafeGet(externalBindings, key)
|
||||||
|
acc->Belt.Map.String.set(key, T.EValue(value))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Evaluates code with external bindings. External bindings are a record of expression values.
|
||||||
|
*/
|
||||||
|
let evalUsingExternalBindings = (code: string, externalBindings: externalBindings) => {
|
||||||
|
let bindings = externalBindings->externalBindingsToBindings
|
||||||
|
evalOuterWBindings_(code, bindings)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Evaluates code with external bindings. External bindings are a record of expression values.
|
||||||
|
The code is a partial code as if it is cut from a larger code. Therefore all statments are assignments.
|
||||||
|
*/
|
||||||
|
let evalPartialUsingExternalBindings = (code: string, externalBindings: externalBindings): result<
|
||||||
|
externalBindings,
|
||||||
|
'e,
|
||||||
|
> => {
|
||||||
|
let bindings = externalBindings->externalBindingsToBindings
|
||||||
|
let answer = evalPartialUsingExternalBindings_(code, bindings)
|
||||||
|
answer->Result.flatMap(answer =>
|
||||||
|
switch answer {
|
||||||
|
| EvRecord(aRecord) => Ok(aRecord)
|
||||||
|
| _ => RETodo("TODO: External bindings must be returned")->Error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,22 @@ type expression = ExpressionT.expression
|
||||||
type expressionValue = ExpressionValue.expressionValue
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
type errorValue = ErrorValue.errorValue
|
type errorValue = ErrorValue.errorValue
|
||||||
|
|
||||||
|
let passToFunction = (fName: string, rLispArgs): result<expression, errorValue> => {
|
||||||
|
let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue
|
||||||
|
|
||||||
|
let fn = fName->toEvCallValue
|
||||||
|
rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
type blockTag =
|
||||||
|
| ImportVariablesStatement
|
||||||
|
| ExportVariablesExpression
|
||||||
|
type tagOrNode =
|
||||||
|
| BlockTag(blockTag)
|
||||||
|
| BlockNode(Parse.node)
|
||||||
|
|
||||||
|
let toTagOrNode = block => BlockNode(block["node"])
|
||||||
|
|
||||||
let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
||||||
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
|
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
|
||||||
|
@ -18,16 +34,9 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
let toEvCallValue = (name: string): expression =>
|
|
||||||
name->ExpressionValue.EvCall->ExpressionT.EValue
|
|
||||||
let toEvSymbolValue = (name: string): expression =>
|
let toEvSymbolValue = (name: string): expression =>
|
||||||
name->ExpressionValue.EvSymbol->ExpressionT.EValue
|
name->ExpressionValue.EvSymbol->ExpressionT.EValue
|
||||||
|
|
||||||
let passToFunction = (fName: string, rLispArgs): result<expression, errorValue> => {
|
|
||||||
let fn = fName->toEvCallValue
|
|
||||||
rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
let caseFunctionNode = fNode => {
|
let caseFunctionNode = fNode => {
|
||||||
let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
|
let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
|
||||||
passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
|
passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs)
|
||||||
|
@ -94,27 +103,6 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExpressionT.EList(list))
|
aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExpressionT.EList(list))
|
||||||
}
|
}
|
||||||
|
|
||||||
let caseBlockNode = (bNode): result<expression, errorValue> => {
|
|
||||||
let blocks = bNode["blocks"]
|
|
||||||
let initialBindings = passToFunction("$$bindings", list{}->Ok)
|
|
||||||
let lastIndex = Belt.Array.length(blocks) - 1
|
|
||||||
blocks->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, block, i) => {
|
|
||||||
rPreviousBindings->Result.flatMap(previousBindings => {
|
|
||||||
let node = block["node"]
|
|
||||||
let rStatement: result<expression, errorValue> = node->fromNode
|
|
||||||
let bindName = if i == lastIndex {
|
|
||||||
"$$bindExpression"
|
|
||||||
} else {
|
|
||||||
"$$bindStatement"
|
|
||||||
}
|
|
||||||
rStatement->Result.flatMap((statement: expression) => {
|
|
||||||
let lispArgs = list{previousBindings, statement}->Ok
|
|
||||||
passToFunction(bindName, lispArgs)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
||||||
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
|
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
|
||||||
| MjArrayNode(aNode) => caseArrayNode(aNode)
|
| MjArrayNode(aNode) => caseArrayNode(aNode)
|
||||||
|
@ -124,8 +112,7 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
let rExpr: result<expression, errorValue> = expr->Ok
|
let rExpr: result<expression, errorValue> = expr->Ok
|
||||||
rExpr
|
rExpr
|
||||||
}
|
}
|
||||||
| MjBlockNode(bNode) => caseBlockNode(bNode)
|
| MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes
|
||||||
// | MjBlockNode(bNode) => "statement"->toEvSymbolValue->Ok
|
|
||||||
| MjConstantNode(cNode) =>
|
| MjConstantNode(cNode) =>
|
||||||
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
|
cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok)
|
||||||
| MjFunctionNode(fNode) => fNode->caseFunctionNode
|
| MjFunctionNode(fNode) => fNode->caseFunctionNode
|
||||||
|
@ -136,3 +123,73 @@ let rec fromNode = (mathJsNode: Parse.node): result<expression, errorValue> =>
|
||||||
}
|
}
|
||||||
rFinalExpression
|
rFinalExpression
|
||||||
})
|
})
|
||||||
|
and caseTagOrNodes = (tagOrNodes): result<expression, errorValue> => {
|
||||||
|
let initialBindings = passToFunction("$$bindings", list{}->Ok)
|
||||||
|
let lastIndex = Belt.Array.length(tagOrNodes) - 1
|
||||||
|
tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => {
|
||||||
|
rPreviousBindings->Result.flatMap(previousBindings => {
|
||||||
|
let rStatement: result<expression, errorValue> = switch tagOrNode {
|
||||||
|
| BlockNode(node) => fromNode(node)
|
||||||
|
| BlockTag(tag) =>
|
||||||
|
switch tag {
|
||||||
|
| ImportVariablesStatement => passToFunction("$importVariablesStatement", list{}->Ok)
|
||||||
|
| ExportVariablesExpression => passToFunction("$exportVariablesExpression", list{}->Ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bindName = if i == lastIndex {
|
||||||
|
"$$bindExpression"
|
||||||
|
} else {
|
||||||
|
"$$bindStatement"
|
||||||
|
}
|
||||||
|
|
||||||
|
rStatement->Result.flatMap((statement: expression) => {
|
||||||
|
let lispArgs = list{previousBindings, statement}->Ok
|
||||||
|
passToFunction(bindName, lispArgs)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromPartialNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
|
||||||
|
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
||||||
|
let casePartialBlockNode = (bNode: Parse.blockNode) => {
|
||||||
|
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
|
||||||
|
let completed = Js.Array2.concat(blocksOrTags, [BlockTag(ExportVariablesExpression)])
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let casePartialExpression = (node: Parse.node) => {
|
||||||
|
let completed = [BlockNode(node), BlockTag(ExportVariablesExpression)]
|
||||||
|
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
||||||
|
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
|
||||||
|
| _ => casePartialExpression(mathJsNode)
|
||||||
|
}
|
||||||
|
rFinalExpression
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromOuterNode = (mathJsNode: Parse.node): result<expression, errorValue> => {
|
||||||
|
Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => {
|
||||||
|
let casePartialBlockNode = (bNode: Parse.blockNode) => {
|
||||||
|
let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode)
|
||||||
|
let completed = blocksOrTags
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let casePartialExpression = (node: Parse.node) => {
|
||||||
|
let completed = [BlockNode(node)]
|
||||||
|
completed->caseTagOrNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
let rFinalExpression: result<expression, errorValue> = switch typedMathJsNode {
|
||||||
|
| MjBlockNode(bNode) => casePartialBlockNode(bNode)
|
||||||
|
| _ => casePartialExpression(mathJsNode)
|
||||||
|
}
|
||||||
|
rFinalExpression
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ type rec expressionValue =
|
||||||
| EvString(string)
|
| EvString(string)
|
||||||
| EvSymbol(string)
|
| EvSymbol(string)
|
||||||
|
|
||||||
|
@genType
|
||||||
|
type externalBindings = Js.Dict.t<expressionValue>
|
||||||
|
|
||||||
type functionCall = (string, array<expressionValue>)
|
type functionCall = (string, array<expressionValue>)
|
||||||
|
|
||||||
let rec toString = aValue =>
|
let rec toString = aValue =>
|
||||||
|
@ -33,17 +36,18 @@ let rec toString = aValue =>
|
||||||
->Js.String.concatMany("")
|
->Js.String.concatMany("")
|
||||||
`[${args}]`
|
`[${args}]`
|
||||||
}
|
}
|
||||||
| EvRecord(aRecord) => {
|
| EvRecord(aRecord) => aRecord->toStringRecord
|
||||||
let pairs =
|
|
||||||
aRecord
|
|
||||||
->Js.Dict.entries
|
|
||||||
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
|
|
||||||
->Extra_Array.interperse(", ")
|
|
||||||
->Js.String.concatMany("")
|
|
||||||
`{${pairs}}`
|
|
||||||
}
|
|
||||||
| EvDistribution(dist) => GenericDist.toString(dist)
|
| EvDistribution(dist) => GenericDist.toString(dist)
|
||||||
}
|
}
|
||||||
|
and toStringRecord = aRecord => {
|
||||||
|
let pairs =
|
||||||
|
aRecord
|
||||||
|
->Js.Dict.entries
|
||||||
|
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`)
|
||||||
|
->Extra_Array.interperse(", ")
|
||||||
|
->Js.String.concatMany("")
|
||||||
|
`{${pairs}}`
|
||||||
|
}
|
||||||
|
|
||||||
let toStringWithType = aValue =>
|
let toStringWithType = aValue =>
|
||||||
switch aValue {
|
switch aValue {
|
||||||
|
@ -68,3 +72,9 @@ let toStringResult = x =>
|
||||||
| Ok(a) => `Ok(${toString(a)})`
|
| Ok(a) => `Ok(${toString(a)})`
|
||||||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toStringResultRecord = x =>
|
||||||
|
switch x {
|
||||||
|
| Ok(a) => `Ok(${toStringRecord(a)})`
|
||||||
|
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||||
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
|
|
||||||
type expressionValue = ExpressionValue.expressionValue
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
|
||||||
module Sample = {
|
// module Sample = {
|
||||||
// In real life real libraries should be somewhere else
|
// // In real life real libraries should be somewhere else
|
||||||
/*
|
// /*
|
||||||
For an example of mapping polymorphic custom functions. To be deleted after real integration
|
// For an example of mapping polymorphic custom functions. To be deleted after real integration
|
||||||
*/
|
// */
|
||||||
let customAdd = (a: float, b: float): float => {a +. b}
|
// let customAdd = (a: float, b: float): float => {a +. b}
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
|
|
|
@ -14639,6 +14639,11 @@ reading-time@^1.5.0:
|
||||||
resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb"
|
resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb"
|
||||||
integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==
|
integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==
|
||||||
|
|
||||||
|
reanalyze@^2.19.0:
|
||||||
|
version "2.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/reanalyze/-/reanalyze-2.19.0.tgz#b34b16bd7249a07dc931bb3cc58410247bb36a03"
|
||||||
|
integrity sha512-ycVr19BH6b2J70BmG6nUIzrFWiBNLUCmip6D2NEPLpnJimF6wagE+BIksI6vcTCovqWjgNMKhFCbZKwm+5/8VQ==
|
||||||
|
|
||||||
rechoir@^0.6.2:
|
rechoir@^0.6.2:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user