format, rename, simplify
This commit is contained in:
parent
cb550f34a5
commit
2040ab52b1
|
@ -1,12 +1,10 @@
|
||||||
module CT = Reducer.CodeTree
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
module CTV = Reducer.Extension.CodeTreeValue
|
|
||||||
module JsG = Reducer.Js.Gate
|
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
let expectEvalToBe = (expr: string, answer: string) =>
|
let expectEvalToBe = (expr: string, answer: string) =>
|
||||||
Reducer.eval(expr) -> CTV.showResult -> expect -> toBe(answer)
|
Reducer.eval(expr)->ExpressionValue.showResult->expect->toBe(answer)
|
||||||
|
|
||||||
describe("builtin", () => {
|
describe("builtin", () => {
|
||||||
// All MathJs operators and functions are available for string, number and boolean
|
// All MathJs operators and functions are available for string, number and boolean
|
||||||
|
@ -21,5 +19,7 @@ describe("builtin", () => {
|
||||||
|
|
||||||
describe("builtin exception", () => {
|
describe("builtin exception", () => {
|
||||||
//It's a pity that MathJs does not return error position
|
//It's a pity that MathJs does not return error position
|
||||||
test("MathJs Exception", () => expectEvalToBe( "testZadanga()", "Error(JS Exception: Error: Undefined function testZadanga)"))
|
test("MathJs Exception", () =>
|
||||||
|
expectEvalToBe("testZadanga()", "Error(JS Exception: Error: Undefined function testZadanga)")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
module CTV = Reducer.Extension.CodeTreeValue
|
open ReducerInterface.ExpressionValue
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
describe("CodeTreeValue", () => {
|
describe("ExpressionValue", () => {
|
||||||
test("showArgs", () =>
|
test("showArgs", () => expect([EvNumber(1.), EvString("a")]->showArgs)->toBe("1, 'a'"))
|
||||||
expect([CTV.CtvNumber(1.), CTV.CtvString("a")]->CTV.showArgs)
|
|
||||||
->toBe("1, 'a'")
|
|
||||||
)
|
|
||||||
|
|
||||||
test("showFunctionCall", () =>
|
test("showFunctionCall", () =>
|
||||||
expect( ("fn", [CTV.CtvNumber(1.), CTV.CtvString("a")])->CTV.showFunctionCall )
|
expect(("fn", [EvNumber(1.), EvString("a")])->showFunctionCall)->toBe("fn(1, 'a')")
|
||||||
->toBe("fn(1, 'a')")
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
module CTV = Reducer.Extension.CodeTreeValue
|
open ReducerInterface.ExpressionValue
|
||||||
module ME = Reducer.MathJs.Eval
|
module ME = Reducer.MathJs.Eval
|
||||||
module Rerr = Reducer.Error
|
module ErrorValue = Reducer.Error
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open ExpectJs
|
open ExpectJs
|
||||||
|
|
||||||
describe("eval", () => {
|
describe("eval", () => {
|
||||||
test("Number", () => expect(ME.eval("1"))
|
test("Number", () => expect(ME.eval("1"))->toEqual(Ok(EvNumber(1.))))
|
||||||
-> toEqual(Ok(CTV.CtvNumber(1.))))
|
test("Number expr", () => expect(ME.eval("1-1"))->toEqual(Ok(EvNumber(0.))))
|
||||||
test("Number expr", () => expect(ME.eval("1-1"))
|
test("String", () => expect(ME.eval("'hello'"))->toEqual(Ok(EvString("hello"))))
|
||||||
-> toEqual(Ok(CTV.CtvNumber(0.))))
|
test("String expr", () =>
|
||||||
test("String", () => expect(ME.eval("'hello'"))
|
expect(ME.eval("concat('hello ','world')"))->toEqual(Ok(EvString("hello world")))
|
||||||
-> toEqual(Ok(CTV.CtvString("hello"))))
|
)
|
||||||
test("String expr", () => expect(ME.eval("concat('hello ','world')"))
|
test("Boolean", () => expect(ME.eval("true"))->toEqual(Ok(EvBool(true))))
|
||||||
-> toEqual(Ok(CTV.CtvString("hello world"))))
|
test("Boolean expr", () => expect(ME.eval("2>1"))->toEqual(Ok(EvBool(true))))
|
||||||
test("Boolean", () => expect(ME.eval("true"))
|
|
||||||
-> toEqual(Ok(CTV.CtvBool(true))))
|
|
||||||
test("Boolean expr", () => expect(ME.eval("2>1"))
|
|
||||||
-> toEqual(Ok(CTV.CtvBool(true))))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("errors", () => {
|
describe("errors", () => {
|
||||||
// All those errors propagete up and are returned by the resolver
|
// All those errors propagete up and are returned by the resolver
|
||||||
test("unknown function", () => expect(ME.eval("testZadanga()"))
|
test("unknown function", () =>
|
||||||
-> toEqual(Error(Rerr.RerrJs(Some("Undefined function testZadanga"), Some("Error")))))
|
expect(ME.eval("testZadanga()"))->toEqual(
|
||||||
|
Error(ErrorValue.REJs(Some("Undefined function testZadanga"), Some("Error"))),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
test("unknown answer type", () => expect(ME.eval("1+1i"))
|
test("unknown answer type", () =>
|
||||||
-> toEqual(Error(Rerr.RerrTodo("Unhandled MathJs literal type: object"))))
|
expect(ME.eval("1+1i"))->toEqual(
|
||||||
|
Error(ErrorValue.RETodo("Unhandled MathJs literal type: object")),
|
||||||
|
)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
module MJ=Reducer.MathJs.Parse
|
module Parse = Reducer.MathJs.Parse
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
let expectParseToBe = (expr, answer) =>
|
let expectParseToBe = (expr, answer) =>
|
||||||
MJ.parse(expr) -> Result.flatMap(MJ.castNodeType) -> MJ.showResult
|
Parse.parse(expr)->Result.flatMap(Parse.castNodeType)->Parse.showResult->expect->toBe(answer)
|
||||||
-> expect -> toBe(answer)
|
|
||||||
|
|
||||||
describe("MathJs parse", () => {
|
describe("MathJs parse", () => {
|
||||||
|
|
||||||
describe("literals operators paranthesis", () => {
|
describe("literals operators paranthesis", () => {
|
||||||
test("1", () => expectParseToBe("1", "1"))
|
test("1", () => expectParseToBe("1", "1"))
|
||||||
test("'hello'", () => expectParseToBe("'hello'", "'hello'"))
|
test("'hello'", () => expectParseToBe("'hello'", "'hello'"))
|
||||||
|
@ -33,8 +31,7 @@ describe("MathJs parse", () => {
|
||||||
describe("arrays", () => {
|
describe("arrays", () => {
|
||||||
test("empty", () => expectParseToBe("[]", "[]"))
|
test("empty", () => expectParseToBe("[]", "[]"))
|
||||||
test("define", () => expectParseToBe("[0, 1, 2]", "[0, 1, 2]"))
|
test("define", () => expectParseToBe("[0, 1, 2]", "[0, 1, 2]"))
|
||||||
test("define with strings", () =>
|
test("define with strings", () => expectParseToBe("['hello', 'world']", "['hello', 'world']"))
|
||||||
expectParseToBe("['hello', 'world']", "['hello', 'world']"))
|
|
||||||
Skip.test("range", () => expectParseToBe("range(0, 4)", "range(0, 4)"))
|
Skip.test("range", () => expectParseToBe("range(0, 4)", "range(0, 4)"))
|
||||||
test("index", () => expectParseToBe("([0,1,2])[1]", "([0, 1, 2])[1]"))
|
test("index", () => expectParseToBe("([0,1,2])[1]", "([0, 1, 2])[1]"))
|
||||||
})
|
})
|
||||||
|
@ -51,5 +48,4 @@ describe("MathJs parse", () => {
|
||||||
describe("if statement", () => {
|
describe("if statement", () => {
|
||||||
Skip.test("define", () => expectParseToBe("if (true) { 1 } else { 0 }", "???"))
|
Skip.test("define", () => expectParseToBe("if (true) { 1 } else { 0 }", "???"))
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
module CT = Reducer.CodeTree
|
module Expression = Reducer.Expression
|
||||||
module CTV = Reducer.Extension.CodeTreeValue
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Expect
|
||||||
|
|
||||||
let expectParseToBe = (expr: string, answer: string) =>
|
let expectParseToBe = (expr: string, answer: string) =>
|
||||||
Reducer.parse(expr) -> CT.showResult -> expect -> toBe(answer)
|
Reducer.parse(expr)->Expression.showResult->expect->toBe(answer)
|
||||||
|
|
||||||
let expectEvalToBe = (expr: string, answer: string) =>
|
let expectEvalToBe = (expr: string, answer: string) =>
|
||||||
Reducer.eval(expr) -> CTV.showResult -> expect -> toBe(answer)
|
Reducer.eval(expr)->ExpressionValue.showResult->expect->toBe(answer)
|
||||||
|
|
||||||
// Current configuration does not ignore this file so we have to have a test
|
// Current configuration does not ignore this file so we have to have a test
|
||||||
test("test helpers", () => expect(1)->toBe(1))
|
test("test helpers", () => expect(1)->toBe(1))
|
||||||
|
|
|
@ -3,7 +3,7 @@ open Reducer_TestHelpers
|
||||||
|
|
||||||
describe("reducer using mathjs parse", () => {
|
describe("reducer using mathjs parse", () => {
|
||||||
// Test the MathJs parser compatibility
|
// Test the MathJs parser compatibility
|
||||||
// Those tests show that there is a semantic mapping from MathJs to CodeTree
|
// Those tests show that there is a semantic mapping from MathJs to Expression
|
||||||
// Reducer.parse is called by Reducer.eval
|
// Reducer.parse is called by Reducer.eval
|
||||||
// See https://mathjs.org/docs/expressions/syntax.html
|
// See https://mathjs.org/docs/expressions/syntax.html
|
||||||
// See https://mathjs.org/docs/reference/functions.html
|
// See https://mathjs.org/docs/reference/functions.html
|
||||||
|
@ -23,14 +23,19 @@ describe("reducer using mathjs parse", () => {
|
||||||
// [1,2,3] becomes (1 2 3)
|
// [1,2,3] becomes (1 2 3)
|
||||||
test("empty", () => expectParseToBe("[]", "Ok(())"))
|
test("empty", () => expectParseToBe("[]", "Ok(())"))
|
||||||
test("[1, 2, 3]", () => expectParseToBe("[1, 2, 3]", "Ok((1 2 3))"))
|
test("[1, 2, 3]", () => expectParseToBe("[1, 2, 3]", "Ok((1 2 3))"))
|
||||||
test("['hello', 'world']", () =>
|
test("['hello', 'world']", () => expectParseToBe("['hello', 'world']", "Ok(('hello' 'world'))"))
|
||||||
expectParseToBe( "['hello', 'world']", "Ok(('hello' 'world'))"))
|
|
||||||
test("index", () => expectParseToBe("([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))"))
|
test("index", () => expectParseToBe("([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))"))
|
||||||
})
|
})
|
||||||
describe("records", () => {
|
describe("records", () => {
|
||||||
test("define", () => expectParseToBe("{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))"))
|
test("define", () =>
|
||||||
|
expectParseToBe("{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))")
|
||||||
|
)
|
||||||
test("use", () =>
|
test("use", () =>
|
||||||
expectParseToBe("{a: 1, b: 2}.a", "Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))"))
|
expectParseToBe(
|
||||||
|
"{a: 1, b: 2}.a",
|
||||||
|
"Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))",
|
||||||
|
)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -54,23 +59,23 @@ describe("eval", () => {
|
||||||
test("[1, 2, 3]", () => expectEvalToBe("[1, 2, 3]", "Ok([1, 2, 3])"))
|
test("[1, 2, 3]", () => expectEvalToBe("[1, 2, 3]", "Ok([1, 2, 3])"))
|
||||||
test("['hello', 'world']", () => expectEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])"))
|
test("['hello', 'world']", () => expectEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])"))
|
||||||
test("index", () => expectEvalToBe("([0,1,2])[1]", "Ok(1)"))
|
test("index", () => expectEvalToBe("([0,1,2])[1]", "Ok(1)"))
|
||||||
test("index not found", ()
|
test("index not found", () =>
|
||||||
=> expectEvalToBe("([0,1,2])[10]", "Error(Array index not found: 10)"))
|
expectEvalToBe("([0,1,2])[10]", "Error(Array index not found: 10)")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
describe("records", () => {
|
describe("records", () => {
|
||||||
test("define", () =>
|
test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})"))
|
||||||
expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})"))
|
test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)"))
|
||||||
test("index", () =>
|
test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
|
||||||
expectEvalToBe("{a: 1}.a", "Ok(1)"))
|
|
||||||
test("index not found", () =>
|
|
||||||
expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)"))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("test exceptions", () => {
|
describe("test exceptions", () => {
|
||||||
test("javascript exception", () =>
|
test("javascript exception", () =>
|
||||||
expectEvalToBe( "jsraise('div by 0')", "Error(JS Exception: Error: 'div by 0')"))
|
expectEvalToBe("jsraise('div by 0')", "Error(JS Exception: Error: 'div by 0')")
|
||||||
|
)
|
||||||
|
|
||||||
test("rescript exception", () =>
|
test("rescript exception", () =>
|
||||||
expectEvalToBe( "resraise()", "Error(TODO: unhandled rescript exception)"))
|
expectEvalToBe("resraise()", "Error(TODO: unhandled rescript exception)")
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
To interface your library there only 2 files to be modified:
|
To interface your library there only 2 files to be modified:
|
||||||
- Reducer/Reducer_Extension/Reducer_Extension_CodeTreeValue.res
|
|
||||||
|
- Reducer/ReducerInterface/ReducerInterface_ExpressionValue.res
|
||||||
|
|
||||||
This is where your additional types are referred for the dispatcher.
|
This is where your additional types are referred for the dispatcher.
|
||||||
|
|
||||||
- Reducer/Reducer_Extension/Reducer_ReducerLibrary.res
|
- Reducer/ReducerInterface/ReducerInterface_ExternalLibrary.res
|
||||||
|
|
||||||
This is where dispatching to your library is done. If the dispatcher becomes beastly then feel free to divide it into submodules.
|
This is where dispatching to your library is done. If the dispatcher becomes beastly then feel free to divide it into submodules.
|
||||||
|
|
||||||
The Reducer is built to use different external libraries as well as different external parsers. Both external parsers and external libraries are plugins.
|
The Reducer is built to use different external libraries as well as different external parsers. Both external parsers and external libraries are plugins.
|
||||||
|
|
||||||
And finally try using Reducer.eval to how your extentions look:
|
And finally try using Reducer.eval to how your extentions look:
|
||||||
|
|
||||||
```rescript
|
```rescript
|
||||||
test("1+2", () => expectEvalToBe( "1+2", "Ok(3)"))
|
test("1+2", () => expectEvalToBe( "1+2", "Ok(3)"))
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
module CodeTree = Reducer_CodeTree
|
|
||||||
module Dispatch = Reducer_Dispatch
|
module Dispatch = Reducer_Dispatch
|
||||||
module Error = Reducer_Error
|
module Error = Reducer_ErrorValue
|
||||||
module Extension = Reducer_Extension
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module Extra = Reducer_Extra
|
||||||
module Js = Reducer_Js
|
module Js = Reducer_Js
|
||||||
module Etra = Reducer_Extra
|
|
||||||
module MathJs = Reducer_MathJs
|
module MathJs = Reducer_MathJs
|
||||||
|
|
||||||
let eval = CodeTree.eval
|
let eval = Expression.eval
|
||||||
let parse = CodeTree.parse
|
let parse = Expression.parse
|
||||||
|
|
9
packages/squiggle-lang/src/rescript/Reducer/Reducer.resi
Normal file
9
packages/squiggle-lang/src/rescript/Reducer/Reducer.resi
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module Dispatch = Reducer_Dispatch
|
||||||
|
module Error = Reducer_ErrorValue
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module Extra = Reducer_Extra
|
||||||
|
module Js = Reducer_Js
|
||||||
|
module MathJs = Reducer_MathJs
|
||||||
|
let eval: string => result<Expression.expressionValue, ErrorValue.errorValue>
|
||||||
|
let parse: string => result<Expression.expression, ErrorValue.errorValue>
|
|
@ -1,91 +0,0 @@
|
||||||
module BuiltIn = Reducer_Dispatch_BuiltIn
|
|
||||||
module T = Reducer_CodeTree_T
|
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
|
||||||
module MJ = Reducer_MathJs_Parse
|
|
||||||
module MJT = Reducer_MathJs_ToCodeTree
|
|
||||||
module RLE = Reducer_Extra_List
|
|
||||||
module Rerr = Reducer_Error
|
|
||||||
module Result = Belt.Result
|
|
||||||
|
|
||||||
type codeTree = T.codeTree
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
type reducerError = Rerr.reducerError
|
|
||||||
|
|
||||||
/*
|
|
||||||
Shows the Lisp Code as text lisp code
|
|
||||||
*/
|
|
||||||
let rec show = codeTree => switch codeTree {
|
|
||||||
| T.CtList(aList) => `(${(Belt.List.map(aList, aValue => show(aValue))
|
|
||||||
-> RLE.interperse(" ")
|
|
||||||
-> Belt.List.toArray -> Js.String.concatMany(""))})`
|
|
||||||
| CtValue(aValue) => CTV.show(aValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
let showResult = (codeResult) => switch codeResult {
|
|
||||||
| Ok(a) => `Ok(${show(a)})`
|
|
||||||
| Error(m) => `Error(${Js.String.make(m)})`
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Converts a MathJs code to Lisp Code
|
|
||||||
*/
|
|
||||||
let parse_ = (expr: string, parser, converter): result<codeTree, reducerError> =>
|
|
||||||
expr -> parser -> Result.flatMap(node => converter(node))
|
|
||||||
|
|
||||||
let parse = (mathJsCode: string): result<codeTree, reducerError> =>
|
|
||||||
mathJsCode -> parse_( MJ.parse, MJT.fromNode )
|
|
||||||
|
|
||||||
module MapString = Belt.Map.String
|
|
||||||
type bindings = MapString.t<unit>
|
|
||||||
let defaultBindings: bindings = MapString.fromArray([])
|
|
||||||
// TODO Define bindings for function execution context
|
|
||||||
|
|
||||||
/*
|
|
||||||
After reducing each level of code tree, we have a value list to evaluate
|
|
||||||
*/
|
|
||||||
let reduceValueList = (valueList: list<codeTreeValue>): result<codeTreeValue, 'e> =>
|
|
||||||
switch valueList {
|
|
||||||
| list{CtvSymbol(fName), ...args} =>
|
|
||||||
(fName, args->Belt.List.toArray) -> BuiltIn.dispatch
|
|
||||||
| _ =>
|
|
||||||
valueList -> Belt.List.toArray -> CTV.CtvArray -> Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Recursively evaluate/reduce the code tree
|
|
||||||
*/
|
|
||||||
let rec reduceCodeTree = (codeTree: codeTree, bindings): result<codeTreeValue, 'e> =>
|
|
||||||
switch codeTree {
|
|
||||||
| T.CtValue( value ) => value -> Ok
|
|
||||||
| T.CtList( list ) => {
|
|
||||||
let racc: result<list<codeTreeValue>, 'e> = list -> Belt.List.reduceReverse(
|
|
||||||
|
|
||||||
Ok(list{}),
|
|
||||||
(racc, each: codeTree) => racc->Result.flatMap( acc => {
|
|
||||||
|
|
||||||
each
|
|
||||||
-> reduceCodeTree(bindings)
|
|
||||||
-> Result.flatMap( newNode => {
|
|
||||||
acc->Belt.List.add(newNode)->Ok
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
)
|
|
||||||
racc -> Result.flatMap( acc => acc->reduceValueList )}
|
|
||||||
}
|
|
||||||
|
|
||||||
let evalWBindingsCodeTree = (aCodeTree, bindings): result<codeTreeValue, 'e> =>
|
|
||||||
reduceCodeTree(aCodeTree, bindings)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Evaluates MathJs code via Lisp using bindings and answers the result
|
|
||||||
*/
|
|
||||||
let evalWBindings = (codeText:string, bindings: bindings) => {
|
|
||||||
parse(codeText) -> Result.flatMap(code => code -> evalWBindingsCodeTree(bindings))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Evaluates MathJs code via Lisp and answers the result
|
|
||||||
*/
|
|
||||||
let eval = (code: string) => evalWBindings(code, defaultBindings)
|
|
|
@ -1,7 +0,0 @@
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
|
||||||
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
|
|
||||||
type rec codeTree =
|
|
||||||
| CtList(list<codeTree>) // A list to map-reduce
|
|
||||||
| CtValue(codeTreeValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
|
|
|
@ -1,72 +1,72 @@
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
module ExternalLibrary = ReducerInterface.ExternalLibrary
|
||||||
module Lib = Reducer_Extension.ReducerLibrary
|
module MathJs = Reducer_MathJs
|
||||||
module ME = Reducer_MathJs.Eval
|
open ReducerInterface.ExpressionValue
|
||||||
module Rerr = Reducer_Error
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MathJs provides default implementations for builtins
|
MathJs provides default implementations for builtins
|
||||||
This is where all the expected builtins like + = * / sin cos log ln etc are handled
|
This is where all the expected builtins like + = * / sin cos log ln etc are handled
|
||||||
DO NOT try to add external function mapping here!
|
DO NOT try to add external function mapping here!
|
||||||
*/
|
*/
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
type reducerError = Rerr.reducerError
|
|
||||||
|
|
||||||
exception TestRescriptException
|
exception TestRescriptException
|
||||||
|
|
||||||
let callInternal = (call: CTV.functionCall): result<'b, reducerError> =>{
|
let callInternal = (call: functionCall): result<'b, errorValue> => {
|
||||||
|
let callMathJs = (call: functionCall): result<'b, errorValue> =>
|
||||||
let callMathJs = (call: CTV.functionCall): result<'b, reducerError> =>
|
|
||||||
switch call {
|
switch call {
|
||||||
| ("jsraise", [msg]) => Js.Exn.raiseError(CTV.show(msg)) // For Tests
|
| ("jsraise", [msg]) => Js.Exn.raiseError(show(msg)) // For Tests
|
||||||
| ("resraise", _) => raise(TestRescriptException) // For Tests
|
| ("resraise", _) => raise(TestRescriptException) // For Tests
|
||||||
| call => call->CTV.showFunctionCall-> ME.eval
|
| call => call->showFunctionCall->MathJs.Eval.eval
|
||||||
}
|
}
|
||||||
|
|
||||||
let constructRecord = arrayOfPairs => {
|
let constructRecord = arrayOfPairs => {
|
||||||
Belt.Array.map(arrayOfPairs, pairValue => {
|
Belt.Array.map(arrayOfPairs, pairValue => {
|
||||||
switch pairValue {
|
switch pairValue {
|
||||||
| CTV.CtvArray([CTV.CtvString(key), valueValue]) =>
|
| EvArray([EvString(key), valueValue]) => (key, valueValue)
|
||||||
(key, valueValue)
|
| _ => ("wrong key type", pairValue->showWithType->EvString)
|
||||||
| _ => ("wrong key type", pairValue->CTV.showWithType->CTV.CtvString)}
|
}
|
||||||
}) -> Js.Dict.fromArray -> CTV.CtvRecord -> Ok
|
})
|
||||||
|
->Js.Dict.fromArray
|
||||||
|
->EvRecord
|
||||||
|
->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
let arrayAtIndex = (aValueArray: array<codeTreeValue>, fIndex: float) =>
|
let arrayAtIndex = (aValueArray: array<expressionValue>, 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 => Rerr.RerrArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex)) -> Error
|
| None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
let recordAtIndex = (dict: Js.Dict.t<codeTreeValue>, sIndex) =>
|
let recordAtIndex = (dict: Js.Dict.t<expressionValue>, sIndex) =>
|
||||||
switch (Js.Dict.get(dict, sIndex)) {
|
switch Js.Dict.get(dict, sIndex) {
|
||||||
| Some(value) => value->Ok
|
| Some(value) => value->Ok
|
||||||
| None => Rerr.RerrRecordPropertyNotFound("Record property not found", sIndex) -> Error
|
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
// | ("$constructRecord", pairArray)
|
// | ("$constructRecord", pairArray)
|
||||||
// | ("$atIndex", [CTV.CtvArray(anArray), CTV.CtvNumber(fIndex)]) => arrayAtIndex(anArray, fIndex)
|
// | ("$atIndex", [EvArray(anArray), EvNumber(fIndex)]) => arrayAtIndex(anArray, fIndex)
|
||||||
// | ("$atIndex", [CTV.CtvRecord(aRecord), CTV.CtvString(sIndex)]) => recordAtIndex(aRecord, sIndex)
|
// | ("$atIndex", [EvRecord(aRecord), EvString(sIndex)]) => recordAtIndex(aRecord, sIndex)
|
||||||
| ("$constructRecord", [CTV.CtvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
| ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs)
|
||||||
| ("$atIndex", [CTV.CtvArray(aValueArray), CTV.CtvArray([CTV.CtvNumber(fIndex)])]) =>
|
| ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) =>
|
||||||
arrayAtIndex(aValueArray, fIndex)
|
arrayAtIndex(aValueArray, fIndex)
|
||||||
| ("$atIndex", [CTV.CtvRecord(dict), CTV.CtvArray([CTV.CtvString(sIndex)])]) => recordAtIndex(dict, sIndex)
|
| ("$atIndex", [EvRecord(dict), EvArray([EvString(sIndex)])]) => recordAtIndex(dict, sIndex)
|
||||||
| ("$atIndex", [obj, index]) => (CTV.showWithType(obj) ++ "??~~~~" ++ CTV.showWithType(index))->CTV.CtvString->Ok
|
| ("$atIndex", [obj, index]) =>
|
||||||
|
(showWithType(obj) ++ "??~~~~" ++ showWithType(index))->EvString->Ok
|
||||||
| call => callMathJs(call)
|
| call => callMathJs(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Lisp engine uses Result monad while reducing expressions
|
Lisp engine uses Result monad while reducing expressions
|
||||||
*/
|
*/
|
||||||
let dispatch = (call: CTV.functionCall): result<codeTreeValue, reducerError> =>
|
let dispatch = (call: functionCall): result<expressionValue, errorValue> =>
|
||||||
try {
|
try {
|
||||||
let (fn, args) = call
|
let (fn, args) = call
|
||||||
// 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
|
||||||
Lib.dispatch((Js.String.make(fn), args), callInternal)
|
ExternalLibrary.dispatch((Js.String.make(fn), args), callInternal)
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) => REJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
RerrJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
| _ => RETodo("unhandled rescript exception")->Error
|
||||||
| _ => RerrTodo("unhandled rescript exception")->Error
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
type reducerError =
|
|
||||||
| RerrFunctionExpected( string )
|
|
||||||
| RerrJs(option<string>, option<string>) // Javascript Exception
|
|
||||||
| RerrTodo(string) // To do
|
|
||||||
| RerrUnexecutedCode( string )
|
|
||||||
| RerrArrayIndexNotFound(string, int)
|
|
||||||
| RerrRecordPropertyNotFound(string, string)
|
|
||||||
|
|
||||||
let showError = (err) => switch err {
|
|
||||||
| RerrTodo( msg ) => `TODO: ${msg}`
|
|
||||||
| RerrJs( omsg, oname ) => {
|
|
||||||
let answer = "JS Exception:"
|
|
||||||
let answer = switch oname {
|
|
||||||
| Some(name) => `${answer} ${name}`
|
|
||||||
| _ => answer
|
|
||||||
}
|
|
||||||
let answer = switch omsg {
|
|
||||||
| Some(msg) => `${answer}: ${msg}`
|
|
||||||
| _ => answer
|
|
||||||
}
|
|
||||||
answer
|
|
||||||
}
|
|
||||||
| RerrArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
|
||||||
| RerrRecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
|
||||||
| RerrUnexecutedCode( codeString ) => `Unexecuted code remaining: ${codeString}`
|
|
||||||
| RerrFunctionExpected( msg ) => `Function expected: ${msg}`
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
type errorValue =
|
||||||
|
| REArrayIndexNotFound(string, int)
|
||||||
|
| REFunctionExpected(string)
|
||||||
|
| REJs(option<string>, option<string>) // Javascript Exception
|
||||||
|
| RERecordPropertyNotFound(string, string)
|
||||||
|
| RETodo(string) // To do
|
||||||
|
|
||||||
|
let showError = err =>
|
||||||
|
switch err {
|
||||||
|
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
||||||
|
| REFunctionExpected(msg) => `Function expected: ${msg}`
|
||||||
|
| REJs(omsg, oname) => {
|
||||||
|
let answer = "JS Exception:"
|
||||||
|
let answer = switch oname {
|
||||||
|
| Some(name) => `${answer} ${name}`
|
||||||
|
| _ => answer
|
||||||
|
}
|
||||||
|
let answer = switch omsg {
|
||||||
|
| Some(msg) => `${answer}: ${msg}`
|
||||||
|
| _ => answer
|
||||||
|
}
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
||||||
|
| RETodo(msg) => `TODO: ${msg}`
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
module BuiltIn = Reducer_Dispatch_BuiltIn
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module Extra = Reducer_Extra
|
||||||
|
module MathJs = Reducer_MathJs
|
||||||
|
module Result = Belt.Result
|
||||||
|
module T = Reducer_Expression_T
|
||||||
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
|
type expression = T.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
|
||||||
|
/*
|
||||||
|
Shows the Lisp Code as text lisp code
|
||||||
|
*/
|
||||||
|
let rec show = expression =>
|
||||||
|
switch expression {
|
||||||
|
| T.EList(aList) =>
|
||||||
|
`(${Belt.List.map(aList, aValue => show(aValue))
|
||||||
|
->Extra.List.interperse(" ")
|
||||||
|
->Belt.List.toArray
|
||||||
|
->Js.String.concatMany("")})`
|
||||||
|
| EValue(aValue) => ExpressionValue.show(aValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
let showResult = codeResult =>
|
||||||
|
switch codeResult {
|
||||||
|
| Ok(a) => `Ok(${show(a)})`
|
||||||
|
| Error(m) => `Error(${Js.String.make(m)})`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converts a MathJs code to Lisp Code
|
||||||
|
*/
|
||||||
|
let parse_ = (expr: string, parser, converter): result<expression, errorValue> =>
|
||||||
|
expr->parser->Result.flatMap(node => converter(node))
|
||||||
|
|
||||||
|
let parse = (mathJsCode: string): result<expression, errorValue> =>
|
||||||
|
mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode)
|
||||||
|
|
||||||
|
module MapString = Belt.Map.String
|
||||||
|
type bindings = MapString.t<unit>
|
||||||
|
let defaultBindings: bindings = MapString.fromArray([])
|
||||||
|
// TODO Define bindings for function execution context
|
||||||
|
|
||||||
|
/*
|
||||||
|
After reducing each level of code tree, we have a value list to evaluate
|
||||||
|
*/
|
||||||
|
let reduceValueList = (valueList: list<expressionValue>): result<expressionValue, 'e> =>
|
||||||
|
switch valueList {
|
||||||
|
| list{EvSymbol(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch
|
||||||
|
| _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Recursively evaluate/reduce the code tree
|
||||||
|
*/
|
||||||
|
let rec reduceExpression = (expression: expression, bindings): result<expressionValue, 'e> =>
|
||||||
|
switch expression {
|
||||||
|
| T.EValue(value) => value->Ok
|
||||||
|
| T.EList(list) => {
|
||||||
|
let racc: result<list<expressionValue>, 'e> = list->Belt.List.reduceReverse(Ok(list{}), (
|
||||||
|
racc,
|
||||||
|
each: expression,
|
||||||
|
) =>
|
||||||
|
racc->Result.flatMap(acc => {
|
||||||
|
each
|
||||||
|
->reduceExpression(bindings)
|
||||||
|
->Result.flatMap(newNode => {
|
||||||
|
acc->Belt.List.add(newNode)->Ok
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
racc->Result.flatMap(acc => acc->reduceValueList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let evalWBindingsExpression = (aExpression, bindings): result<expressionValue, 'e> =>
|
||||||
|
reduceExpression(aExpression, bindings)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Evaluates MathJs code via Lisp using bindings and answers the result
|
||||||
|
*/
|
||||||
|
let evalWBindings = (codeText: string, bindings: bindings) => {
|
||||||
|
parse(codeText)->Result.flatMap(code => code->evalWBindingsExpression(bindings))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Evaluates MathJs code via Lisp and answers the result
|
||||||
|
*/
|
||||||
|
let eval = (code: string) => evalWBindings(code, defaultBindings)
|
|
@ -0,0 +1,5 @@
|
||||||
|
open ReducerInterface.ExpressionValue
|
||||||
|
|
||||||
|
type rec expression =
|
||||||
|
| EList(list<expression>) // A list to map-reduce
|
||||||
|
| EValue(expressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible
|
|
@ -1,2 +0,0 @@
|
||||||
module CodeTreeValue = Reducer_Extension_CodeTreeValue
|
|
||||||
module ReducerLibrary = Reducer_Extension_ReducerLibrary
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
Irreducible values. Reducer does not know about those. Only used for external calls
|
|
||||||
This is a configuration to to make external calls of those types
|
|
||||||
*/
|
|
||||||
module AE = Reducer_Extra_Array
|
|
||||||
module Rerr = Reducer_Error
|
|
||||||
|
|
||||||
type rec codeTreeValue =
|
|
||||||
| CtvBool(bool)
|
|
||||||
| CtvNumber(float)
|
|
||||||
| CtvString(string)
|
|
||||||
| CtvSymbol(string)
|
|
||||||
| CtvArray(array<codeTreeValue>)
|
|
||||||
| CtvRecord(Js.Dict.t<codeTreeValue>)
|
|
||||||
|
|
||||||
type functionCall = (string, array<codeTreeValue>)
|
|
||||||
|
|
||||||
let rec show = aValue => switch aValue {
|
|
||||||
| CtvBool( aBool ) => Js.String.make( aBool )
|
|
||||||
| CtvNumber( aNumber ) => Js.String.make( aNumber )
|
|
||||||
| CtvString( aString ) => `'${aString}'`
|
|
||||||
| CtvSymbol( aString ) => `:${aString}`
|
|
||||||
| CtvArray( anArray ) => {
|
|
||||||
let args = anArray
|
|
||||||
-> Belt.Array.map(each => show(each))
|
|
||||||
-> AE.interperse(", ")
|
|
||||||
-> Js.String.concatMany("")
|
|
||||||
`[${args}]`}
|
|
||||||
| CtvRecord( aRecord ) => {
|
|
||||||
let pairs = aRecord
|
|
||||||
-> Js.Dict.entries
|
|
||||||
-> Belt.Array.map( ((eachKey, eachValue)) => `${eachKey}: ${show(eachValue)}` )
|
|
||||||
-> AE.interperse(", ")
|
|
||||||
-> Js.String.concatMany("")
|
|
||||||
`{${pairs}}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let showWithType = aValue => switch aValue {
|
|
||||||
| CtvBool( _ ) => `Bool::${show(aValue)}`
|
|
||||||
| CtvNumber( _ ) => `Number::${show(aValue)}`
|
|
||||||
| CtvString( _ ) => `String::${show(aValue)}`
|
|
||||||
| CtvSymbol( _ ) => `Symbol::${show(aValue)}`
|
|
||||||
| CtvArray( _ ) => `Array::${show(aValue)}`
|
|
||||||
| CtvRecord( _ ) => `Record::${show(aValue)}`
|
|
||||||
}
|
|
||||||
|
|
||||||
let showArgs = (args: array<codeTreeValue>): string => {
|
|
||||||
args
|
|
||||||
-> Belt.Array.map(arg => arg->show)
|
|
||||||
-> AE.interperse(", ")
|
|
||||||
-> Js.String.concatMany("") }
|
|
||||||
|
|
||||||
let showFunctionCall = ((fn, args)): string => `${fn}(${ showArgs(args) })`
|
|
||||||
|
|
||||||
let showResult = (x) => switch x {
|
|
||||||
| Ok(a) => `Ok(${ show(a) })`
|
|
||||||
| Error(m) => `Error(${Rerr.showError(m)})`
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Insert seperator between the elements of an array
|
Insert seperator between the elements of an array
|
||||||
*/
|
*/
|
||||||
module LE = Reducer_Extra_List
|
module ExtraList = Reducer_Extra_List
|
||||||
|
|
||||||
let interperse = (anArray, seperator) =>
|
let interperse = (anArray, seperator) =>
|
||||||
anArray -> Belt.List.fromArray -> LE.interperse(seperator) -> Belt.List.toArray
|
anArray->Belt.List.fromArray->ExtraList.interperse(seperator)->Belt.List.toArray
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
Insert seperator between the elements of a list
|
Insert seperator between the elements of a list
|
||||||
*/
|
*/
|
||||||
let rec interperse = (aList, seperator) => switch aList {
|
let rec interperse = (aList, seperator) =>
|
||||||
|
switch aList {
|
||||||
| list{} => list{}
|
| list{} => list{}
|
||||||
| list{a} => list{a}
|
| list{a} => list{a}
|
||||||
| list{a, ...rest} => list{a, seperator, ...interperse(rest, seperator)}
|
| list{a, ...rest} => list{a, seperator, ...interperse(rest, seperator)}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
open ReducerInterface.ExpressionValue
|
||||||
module Rerr = Reducer_Error
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
type reducerError = Rerr.reducerError
|
|
||||||
|
|
||||||
external castBool: unit => bool = "%identity"
|
external castBool: unit => bool = "%identity"
|
||||||
external castNumber: unit => float = "%identity"
|
external castNumber: unit => float = "%identity"
|
||||||
|
@ -11,11 +8,11 @@ external castString: unit => string = "%identity"
|
||||||
/*
|
/*
|
||||||
As JavaScript returns us any type, we need to type check and cast type propertype before using it
|
As JavaScript returns us any type, we need to type check and cast type propertype before using it
|
||||||
*/
|
*/
|
||||||
let jsToCtv = (jsValue): result<codeTreeValue, reducerError> => {
|
let jsToEv = (jsValue): result<expressionValue, errorValue> => {
|
||||||
switch Js.typeof(jsValue) {
|
switch Js.typeof(jsValue) {
|
||||||
| "boolean" => jsValue -> castBool -> CTV.CtvBool -> Ok
|
| "boolean" => jsValue->castBool->EvBool->Ok
|
||||||
| "number" => jsValue -> castNumber -> CTV.CtvNumber -> Ok
|
| "number" => jsValue->castNumber->EvNumber->Ok
|
||||||
| "string" => jsValue -> castString -> CTV.CtvString -> Ok
|
| "string" => jsValue->castString->EvString->Ok
|
||||||
| other => Rerr.RerrTodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`) -> Error
|
| other => RETodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`)->Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
module Eval = Reducer_MathJs_Eval
|
module Eval = Reducer_MathJs_Eval
|
||||||
module Parse = Reducer_MathJs_Parse
|
module Parse = Reducer_MathJs_Parse
|
||||||
|
module ToExpression = Reducer_MathJs_ToExpression
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
module JavaScript = Reducer_Js
|
||||||
module JsG = Reducer_Js_Gate
|
open ReducerInterface.ExpressionValue
|
||||||
module Rerr = Reducer_Error
|
open Reducer_ErrorValue
|
||||||
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
type reducerError = Rerr.reducerError
|
|
||||||
|
|
||||||
@module("mathjs") external dummy_: string => unit = "evaluate"
|
@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 dummy1_ = dummy_ //Deceive the compiler to make the import although we wont make a call from rescript. Otherwise the optimizer deletes the import
|
||||||
|
|
||||||
type answer = {
|
type answer = {"value": unit}
|
||||||
"value": unit
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The result has to be delivered in an object so that we can type cast.
|
The result has to be delivered in an object so that we can type cast.
|
||||||
|
@ -22,12 +17,11 @@ let eval__ = %raw(`function (expr) { return {value: Mathjs.evaluate(expr)}; }`)
|
||||||
/*
|
/*
|
||||||
Call MathJs evaluate and return as a variant
|
Call MathJs evaluate and return as a variant
|
||||||
*/
|
*/
|
||||||
let eval = (expr: string): result<codeTreeValue, reducerError> => {
|
let eval = (expr: string): result<expressionValue, errorValue> => {
|
||||||
try {
|
try {
|
||||||
let answer = eval__(expr)
|
let answer = eval__(expr)
|
||||||
answer["value"]->JsG.jsToCtv
|
answer["value"]->JavaScript.Gate.jsToEv
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) => REJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
RerrJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,63 +2,27 @@
|
||||||
MathJs Nodes
|
MathJs Nodes
|
||||||
We make MathJs Nodes strong-typed
|
We make MathJs Nodes strong-typed
|
||||||
*/
|
*/
|
||||||
module AE = Reducer_Extra_Array
|
module Extra = Reducer_Extra
|
||||||
module JsG = Reducer_Js_Gate
|
open Reducer_ErrorValue
|
||||||
module Rerr = Reducer_Error
|
|
||||||
|
|
||||||
type reducerError = Rerr.reducerError
|
type node = {"type": string, "isNode": bool, "comment": string}
|
||||||
|
type arrayNode = {...node, "items": array<node>}
|
||||||
type node = {
|
|
||||||
"type": string,
|
|
||||||
"isNode": bool,
|
|
||||||
"comment": string
|
|
||||||
}
|
|
||||||
type arrayNode = {
|
|
||||||
...node,
|
|
||||||
"items": array<node>
|
|
||||||
}
|
|
||||||
//assignmentNode
|
//assignmentNode
|
||||||
//blockNode
|
//blockNode
|
||||||
//conditionalNode
|
//conditionalNode
|
||||||
type constantNode = {
|
type constantNode = {...node, "value": unit}
|
||||||
...node,
|
|
||||||
"value": unit
|
|
||||||
}
|
|
||||||
//functionAssignmentNode
|
//functionAssignmentNode
|
||||||
type functionNode = {
|
type functionNode = {...node, "fn": string, "args": array<node>}
|
||||||
...node,
|
type indexNode = {...node, "dimensions": array<node>}
|
||||||
"fn": string,
|
type objectNode = {...node, "properties": Js.Dict.t<node>}
|
||||||
"args": array<node>
|
type accessorNode = {...node, "object": node, "index": indexNode}
|
||||||
}
|
type operatorNode = {...functionNode, "op": string}
|
||||||
type indexNode = {
|
|
||||||
...node,
|
|
||||||
"dimensions": array<node>
|
|
||||||
}
|
|
||||||
type objectNode = {
|
|
||||||
...node,
|
|
||||||
"properties": Js.Dict.t<node>
|
|
||||||
}
|
|
||||||
type accessorNode = {
|
|
||||||
...node,
|
|
||||||
"object": node,
|
|
||||||
"index": indexNode
|
|
||||||
}
|
|
||||||
type operatorNode = {
|
|
||||||
...functionNode,
|
|
||||||
"op": string,
|
|
||||||
}
|
|
||||||
|
|
||||||
//parenthesisNode
|
//parenthesisNode
|
||||||
type parenthesisNode = {
|
type parenthesisNode = {...node, "content": node}
|
||||||
...node,
|
|
||||||
"content": node
|
|
||||||
}
|
|
||||||
//rangeNode
|
//rangeNode
|
||||||
//relationalNode
|
//relationalNode
|
||||||
type symbolNode = {
|
type symbolNode = {...node, "name": string}
|
||||||
...node,
|
|
||||||
"name": string
|
|
||||||
}
|
|
||||||
|
|
||||||
external castAccessorNode: node => accessorNode = "%identity"
|
external castAccessorNode: node => accessorNode = "%identity"
|
||||||
external castArrayNode: node => arrayNode = "%identity"
|
external castArrayNode: node => arrayNode = "%identity"
|
||||||
|
@ -76,12 +40,11 @@ external castSymbolNode: node => symbolNode = "%identity"
|
||||||
*/
|
*/
|
||||||
@module("mathjs") external parse__: string => node = "parse"
|
@module("mathjs") external parse__: string => node = "parse"
|
||||||
|
|
||||||
let parse = (expr: string): result<node, reducerError> =>
|
let parse = (expr: string): result<node, errorValue> =>
|
||||||
try {
|
try {
|
||||||
Ok(parse__(expr))
|
Ok(parse__(expr))
|
||||||
} catch {
|
} catch {
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) => REJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
||||||
RerrJs(Js.Exn.message(obj), Js.Exn.name(obj))->Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type mjNode =
|
type mjNode =
|
||||||
|
@ -95,7 +58,8 @@ type mjNode =
|
||||||
| MjParenthesisNode(parenthesisNode)
|
| MjParenthesisNode(parenthesisNode)
|
||||||
| MjSymbolNode(symbolNode)
|
| MjSymbolNode(symbolNode)
|
||||||
|
|
||||||
let castNodeType = (node: node) => switch node["type"] {
|
let castNodeType = (node: node) =>
|
||||||
|
switch node["type"] {
|
||||||
| "AccessorNode" => node->castAccessorNode->MjAccessorNode->Ok
|
| "AccessorNode" => node->castAccessorNode->MjAccessorNode->Ok
|
||||||
| "ArrayNode" => node->castArrayNode->MjArrayNode->Ok
|
| "ArrayNode" => node->castArrayNode->MjArrayNode->Ok
|
||||||
| "ConstantNode" => node->castConstantNode->MjConstantNode->Ok
|
| "ConstantNode" => node->castConstantNode->MjConstantNode->Ok
|
||||||
|
@ -105,11 +69,12 @@ let castNodeType = (node: node) => switch node["type"] {
|
||||||
| "OperatorNode" => node->castOperatorNode->MjOperatorNode->Ok
|
| "OperatorNode" => node->castOperatorNode->MjOperatorNode->Ok
|
||||||
| "ParenthesisNode" => node->castParenthesisNode->MjParenthesisNode->Ok
|
| "ParenthesisNode" => node->castParenthesisNode->MjParenthesisNode->Ok
|
||||||
| "SymbolNode" => node->castSymbolNode->MjSymbolNode->Ok
|
| "SymbolNode" => node->castSymbolNode->MjSymbolNode->Ok
|
||||||
| _ => Rerr.RerrTodo(`Argg, unhandled MathJsNode: ${node["type"]}`)-> Error
|
| _ => RETodo(`Argg, unhandled MathJsNode: ${node["type"]}`)->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
let rec show = (mjNode: mjNode): string => {
|
let rec show = (mjNode: mjNode): string => {
|
||||||
let showValue = (a: 'a): string => if (Js.typeof(a) == "string") {
|
let showValue = (a: 'a): string =>
|
||||||
|
if Js.typeof(a) == "string" {
|
||||||
`'${Js.String.make(a)}'`
|
`'${Js.String.make(a)}'`
|
||||||
} else {
|
} else {
|
||||||
Js.String.make(a)
|
Js.String.make(a)
|
||||||
|
@ -118,25 +83,24 @@ let rec show = (mjNode: mjNode): string => {
|
||||||
let showNodeArray = (nodeArray: array<node>): string =>
|
let showNodeArray = (nodeArray: array<node>): string =>
|
||||||
nodeArray
|
nodeArray
|
||||||
->Belt.Array.map(a => showMathJsNode(a))
|
->Belt.Array.map(a => showMathJsNode(a))
|
||||||
-> AE.interperse(", ")
|
->Extra.Array.interperse(", ")
|
||||||
->Js.String.concatMany("")
|
->Js.String.concatMany("")
|
||||||
|
|
||||||
let showFunctionNode = (fnode: functionNode): string =>
|
let showFunctionNode = (fnode: functionNode): string =>
|
||||||
`${fnode["fn"]}(${fnode["args"]->showNodeArray})`
|
`${fnode["fn"]}(${fnode["args"]->showNodeArray})`
|
||||||
|
|
||||||
let showObjectEntry = ( (key: string, value: node) ): string =>
|
let showObjectEntry = ((key: string, value: node)): string => `${key}: ${value->showMathJsNode}`
|
||||||
`${key}: ${value->showMathJsNode}`
|
|
||||||
|
|
||||||
let showObjectNode = (oNode: objectNode): string =>
|
let showObjectNode = (oNode: objectNode): string =>
|
||||||
`{${oNode["properties"]
|
`{${oNode["properties"]
|
||||||
->Js.Dict.entries
|
->Js.Dict.entries
|
||||||
->Belt.Array.map(entry => entry->showObjectEntry)
|
->Belt.Array.map(entry => entry->showObjectEntry)
|
||||||
->AE.interperse(", ")->Js.String.concatMany("")
|
->Extra.Array.interperse(", ")
|
||||||
}}`
|
->Js.String.concatMany("")}}`
|
||||||
|
|
||||||
let showIndexNode = (iNode: indexNode): string =>
|
let showIndexNode = (iNode: indexNode): string =>
|
||||||
iNode["dimensions"]
|
iNode["dimensions"]
|
||||||
-> Belt.Array.map( each => `${showResult(each->castNodeType)}`)
|
->Belt.Array.map(each => showResult(each->castNodeType))
|
||||||
->Js.String.concatMany("")
|
->Js.String.concatMany("")
|
||||||
|
|
||||||
switch mjNode {
|
switch mjNode {
|
||||||
|
@ -149,10 +113,11 @@ let rec show = (mjNode: mjNode): string => {
|
||||||
| MjOperatorNode(opNode) => opNode->castOperatorNodeToFunctionNode->showFunctionNode
|
| MjOperatorNode(opNode) => opNode->castOperatorNodeToFunctionNode->showFunctionNode
|
||||||
| MjParenthesisNode(pNode) => `(${showMathJsNode(pNode["content"])})`
|
| MjParenthesisNode(pNode) => `(${showMathJsNode(pNode["content"])})`
|
||||||
| MjSymbolNode(sNode) => sNode["name"]
|
| MjSymbolNode(sNode) => sNode["name"]
|
||||||
}}
|
}
|
||||||
and let showResult = (rmjnode: result<mjNode, reducerError>): string =>
|
}
|
||||||
|
and showResult = (rmjnode: result<mjNode, errorValue>): string =>
|
||||||
switch rmjnode {
|
switch rmjnode {
|
||||||
| Error(e) => Rerr.showError(e)
|
| Error(e) => showError(e)
|
||||||
| Ok(mjNode) => show(mjNode)
|
| Ok(mjNode) => show(mjNode)
|
||||||
}
|
}
|
||||||
and let showMathJsNode = (node) => node -> castNodeType -> showResult
|
and showMathJsNode = node => node->castNodeType->showResult
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
module CTT = Reducer_CodeTree_T
|
|
||||||
module CTV = Reducer_Extension.CodeTreeValue
|
|
||||||
module JsG = Reducer_Js_Gate
|
|
||||||
module MJ = Reducer_MathJs_Parse
|
|
||||||
module Rerr = Reducer_Error
|
|
||||||
module Result = Belt.Result
|
|
||||||
|
|
||||||
type codeTree = CTT.codeTree
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
|
||||||
type reducerError = Rerr.reducerError
|
|
||||||
|
|
||||||
let rec fromNode =
|
|
||||||
(mjnode: MJ.node): result<codeTree, reducerError> =>
|
|
||||||
MJ.castNodeType(mjnode) -> Result.flatMap(typedMjNode => {
|
|
||||||
|
|
||||||
let fromNodeList = (nodeList: list<MJ.node>): result<list<codeTree>, 'e> =>
|
|
||||||
Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) =>
|
|
||||||
racc -> Result.flatMap(
|
|
||||||
acc => fromNode(currNode) -> Result.map(
|
|
||||||
currCode => list{currCode, ...acc})))
|
|
||||||
|
|
||||||
let caseFunctionNode = (fNode) => {
|
|
||||||
let fn = fNode["fn"] -> CTV.CtvSymbol -> CTT.CtValue
|
|
||||||
let lispArgs = fNode["args"] -> Belt.List.fromArray -> fromNodeList
|
|
||||||
lispArgs -> Result.map(
|
|
||||||
argsCode => list{fn, ...argsCode} -> CTT.CtList )
|
|
||||||
}
|
|
||||||
|
|
||||||
let caseObjectNode = oNode => {
|
|
||||||
|
|
||||||
let fromObjectEntries = ( entryList ) => {
|
|
||||||
let rargs = Belt.List.reduceReverse(
|
|
||||||
entryList,
|
|
||||||
Ok(list{}),
|
|
||||||
(racc, (key: string, value: MJ.node))
|
|
||||||
=>
|
|
||||||
racc
|
|
||||||
-> Result.flatMap( acc =>
|
|
||||||
fromNode(value) -> Result.map(valueCodeTree => {
|
|
||||||
let entryCode = list{key->CTV.CtvString->CTT.CtValue, valueCodeTree}
|
|
||||||
-> CTT.CtList
|
|
||||||
list{entryCode, ...acc}})))
|
|
||||||
let lispName = "$constructRecord" -> CTV.CtvSymbol -> CTT.CtValue
|
|
||||||
rargs -> Result.map(args => list{lispName, CTT.CtList(args)} -> CTT.CtList)
|
|
||||||
}
|
|
||||||
|
|
||||||
oNode["properties"]
|
|
||||||
-> Js.Dict.entries
|
|
||||||
-> Belt.List.fromArray
|
|
||||||
-> fromObjectEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
let caseIndexNode = iNode => {
|
|
||||||
let rpropertyCodeList = Belt.List.reduceReverse(
|
|
||||||
iNode["dimensions"]->Belt.List.fromArray,
|
|
||||||
Ok(list{}),
|
|
||||||
(racc, currentPropertyMjNode)
|
|
||||||
=>
|
|
||||||
racc -> Result.flatMap( acc =>
|
|
||||||
fromNode(currentPropertyMjNode)
|
|
||||||
-> Result.map( propertyCode =>
|
|
||||||
list{ propertyCode, ...acc} )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
rpropertyCodeList -> Result.map(
|
|
||||||
propertyCodeList => CTT.CtList(propertyCodeList))
|
|
||||||
}
|
|
||||||
|
|
||||||
let caseAccessorNode = ( objectNode, indexNode ) => {
|
|
||||||
let fn = "$atIndex" -> CTV.CtvSymbol -> CTT.CtValue
|
|
||||||
|
|
||||||
caseIndexNode( indexNode ) -> Result.flatMap(
|
|
||||||
indexCode => {
|
|
||||||
fromNode( objectNode ) -> Result.map(
|
|
||||||
objectCode => list{fn, objectCode, indexCode} -> CTT.CtList )
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch typedMjNode {
|
|
||||||
| MjArrayNode(aNode) =>
|
|
||||||
aNode["items"]
|
|
||||||
-> Belt.List.fromArray
|
|
||||||
-> fromNodeList
|
|
||||||
-> Result.map(list => CTT.CtList(list))
|
|
||||||
| MjConstantNode(cNode) =>
|
|
||||||
cNode["value"]-> JsG.jsToCtv -> Result.map( v => v->CTT.CtValue)
|
|
||||||
| MjFunctionNode(fNode) => fNode
|
|
||||||
-> caseFunctionNode
|
|
||||||
| MjOperatorNode(opNode) => opNode
|
|
||||||
-> MJ.castOperatorNodeToFunctionNode -> caseFunctionNode
|
|
||||||
| MjParenthesisNode(pNode) => pNode["content"] -> fromNode
|
|
||||||
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
|
|
||||||
| MjObjectNode(oNode) => caseObjectNode(oNode)
|
|
||||||
| MjSymbolNode(sNode) =>
|
|
||||||
sNode["name"]-> CTV.CtvSymbol -> CTT.CtValue -> Ok
|
|
||||||
| MjIndexNode(iNode) => caseIndexNode(iNode)
|
|
||||||
}})
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
module ExtressionT = Reducer_Expression_T
|
||||||
|
module JavaScript = Reducer_Js
|
||||||
|
module Parse = Reducer_MathJs_Parse
|
||||||
|
module Result = Belt.Result
|
||||||
|
|
||||||
|
type expression = ExtressionT.expression
|
||||||
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
type errorValue = ErrorValue.errorValue
|
||||||
|
|
||||||
|
let rec fromNode = (mjnode: Parse.node): result<expression, errorValue> =>
|
||||||
|
Parse.castNodeType(mjnode)->Result.flatMap(typedMjNode => {
|
||||||
|
let fromNodeList = (nodeList: list<Parse.node>): result<list<expression>, 'e> =>
|
||||||
|
Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) =>
|
||||||
|
racc->Result.flatMap(acc =>
|
||||||
|
fromNode(currNode)->Result.map(currCode => list{currCode, ...acc})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let caseFunctionNode = fNode => {
|
||||||
|
let fn = fNode["fn"]->ExpressionValue.EvSymbol->ExtressionT.EValue
|
||||||
|
let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList
|
||||||
|
lispArgs->Result.map(argsCode => list{fn, ...argsCode}->ExtressionT.EList)
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseObjectNode = oNode => {
|
||||||
|
let fromObjectEntries = entryList => {
|
||||||
|
let rargs = Belt.List.reduceReverse(entryList, Ok(list{}), (
|
||||||
|
racc,
|
||||||
|
(key: string, value: Parse.node),
|
||||||
|
) =>
|
||||||
|
racc->Result.flatMap(acc =>
|
||||||
|
fromNode(value)->Result.map(valueExpression => {
|
||||||
|
let entryCode =
|
||||||
|
list{
|
||||||
|
key->ExpressionValue.EvString->ExtressionT.EValue,
|
||||||
|
valueExpression,
|
||||||
|
}->ExtressionT.EList
|
||||||
|
list{entryCode, ...acc}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
let lispName = "$constructRecord"->ExpressionValue.EvSymbol->ExtressionT.EValue
|
||||||
|
rargs->Result.map(args => list{lispName, ExtressionT.EList(args)}->ExtressionT.EList)
|
||||||
|
}
|
||||||
|
|
||||||
|
oNode["properties"]->Js.Dict.entries->Belt.List.fromArray->fromObjectEntries
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseIndexNode = iNode => {
|
||||||
|
let rpropertyCodeList = Belt.List.reduceReverse(
|
||||||
|
iNode["dimensions"]->Belt.List.fromArray,
|
||||||
|
Ok(list{}),
|
||||||
|
(racc, currentPropertyMjNode) =>
|
||||||
|
racc->Result.flatMap(acc =>
|
||||||
|
fromNode(currentPropertyMjNode)->Result.map(propertyCode => list{propertyCode, ...acc})
|
||||||
|
),
|
||||||
|
)
|
||||||
|
rpropertyCodeList->Result.map(propertyCodeList => ExtressionT.EList(propertyCodeList))
|
||||||
|
}
|
||||||
|
|
||||||
|
let caseAccessorNode = (objectNode, indexNode) => {
|
||||||
|
let fn = "$atIndex"->ExpressionValue.EvSymbol->ExtressionT.EValue
|
||||||
|
|
||||||
|
caseIndexNode(indexNode)->Result.flatMap(indexCode => {
|
||||||
|
fromNode(objectNode)->Result.map(objectCode =>
|
||||||
|
list{fn, objectCode, indexCode}->ExtressionT.EList
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typedMjNode {
|
||||||
|
| MjArrayNode(aNode) =>
|
||||||
|
aNode["items"]->Belt.List.fromArray->fromNodeList->Result.map(list => ExtressionT.EList(list))
|
||||||
|
| MjConstantNode(cNode) =>
|
||||||
|
cNode["value"]->JavaScript.Gate.jsToEv->Result.map(v => v->ExtressionT.EValue)
|
||||||
|
| MjFunctionNode(fNode) => fNode->caseFunctionNode
|
||||||
|
| MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode
|
||||||
|
| MjParenthesisNode(pNode) => pNode["content"]->fromNode
|
||||||
|
| MjAccessorNode(aNode) => caseAccessorNode(aNode["object"], aNode["index"])
|
||||||
|
| MjObjectNode(oNode) => caseObjectNode(oNode)
|
||||||
|
| MjSymbolNode(sNode) => sNode["name"]->ExpressionValue.EvSymbol->ExtressionT.EValue->Ok
|
||||||
|
| MjIndexNode(iNode) => caseIndexNode(iNode)
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,2 @@
|
||||||
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
|
module ExternalLibrary = ReducerInterface_ExternalLibrary
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
Irreducible values. Reducer does not know about those. Only used for external calls
|
||||||
|
This is a configuration to to make external calls of those types
|
||||||
|
*/
|
||||||
|
module AE = Reducer_Extra_Array
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
|
|
||||||
|
type rec expressionValue =
|
||||||
|
| EvBool(bool)
|
||||||
|
| EvNumber(float)
|
||||||
|
| EvString(string)
|
||||||
|
| EvSymbol(string)
|
||||||
|
| EvArray(array<expressionValue>)
|
||||||
|
| EvRecord(Js.Dict.t<expressionValue>)
|
||||||
|
|
||||||
|
type functionCall = (string, array<expressionValue>)
|
||||||
|
|
||||||
|
let rec show = aValue =>
|
||||||
|
switch aValue {
|
||||||
|
| EvBool(aBool) => Js.String.make(aBool)
|
||||||
|
| EvNumber(aNumber) => Js.String.make(aNumber)
|
||||||
|
| EvString(aString) => `'${aString}'`
|
||||||
|
| EvSymbol(aString) => `:${aString}`
|
||||||
|
| EvArray(anArray) => {
|
||||||
|
let args =
|
||||||
|
anArray->Belt.Array.map(each => show(each))->AE.interperse(", ")->Js.String.concatMany("")
|
||||||
|
`[${args}]`
|
||||||
|
}
|
||||||
|
| EvRecord(aRecord) => {
|
||||||
|
let pairs =
|
||||||
|
aRecord
|
||||||
|
->Js.Dict.entries
|
||||||
|
->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${show(eachValue)}`)
|
||||||
|
->AE.interperse(", ")
|
||||||
|
->Js.String.concatMany("")
|
||||||
|
`{${pairs}}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let showWithType = aValue =>
|
||||||
|
switch aValue {
|
||||||
|
| EvBool(_) => `Bool::${show(aValue)}`
|
||||||
|
| EvNumber(_) => `Number::${show(aValue)}`
|
||||||
|
| EvString(_) => `String::${show(aValue)}`
|
||||||
|
| EvSymbol(_) => `Symbol::${show(aValue)}`
|
||||||
|
| EvArray(_) => `Array::${show(aValue)}`
|
||||||
|
| EvRecord(_) => `Record::${show(aValue)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
let showArgs = (args: array<expressionValue>): string => {
|
||||||
|
args->Belt.Array.map(arg => arg->show)->AE.interperse(", ")->Js.String.concatMany("")
|
||||||
|
}
|
||||||
|
|
||||||
|
let showFunctionCall = ((fn, args)): string => `${fn}(${showArgs(args)})`
|
||||||
|
|
||||||
|
let showResult = x =>
|
||||||
|
switch x {
|
||||||
|
| Ok(a) => `Ok(${show(a)})`
|
||||||
|
| Error(m) => `Error(${ErrorValue.showError(m)})`
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
module CTV = Reducer_Extension_CodeTreeValue
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
|
|
||||||
type codeTreeValue = CTV.codeTreeValue
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
|
|
||||||
module Sample = { // In real life real libraries should be somewhere else
|
module Sample = {
|
||||||
|
// 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
|
||||||
*/
|
*/
|
||||||
|
@ -12,9 +13,9 @@ module Sample = { // In real life real libraries should be somewhere else
|
||||||
/*
|
/*
|
||||||
Map external calls of Reducer
|
Map external calls of Reducer
|
||||||
*/
|
*/
|
||||||
let dispatch = (call: CTV.functionCall, chain): result<codeTreeValue, 'e> => switch call {
|
let dispatch = (call: ExpressionValue.functionCall, chain): result<expressionValue, 'e> =>
|
||||||
|
switch call {
|
||||||
| ("add", [CtvNumber(a), CtvNumber(b)]) => Sample.customAdd(a, b) -> CtvNumber -> Ok
|
| ("add", [EvNumber(a), EvNumber(b)]) => Sample.customAdd(a, b)->EvNumber->Ok
|
||||||
|
|
||||||
| call => chain(call)
|
| call => chain(call)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user