diff --git a/packages/components/package.json b/packages/components/package.json index 2b72013f..12c56fff 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@quri/squiggle-components", - "version": "0.4.3", + "version": "0.5.0-alpha.2", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^1.0.0", @@ -8,7 +8,7 @@ "@headlessui/react": "^1.7.2", "@heroicons/react": "^1.0.6", "@hookform/resolvers": "^2.9.8", - "@quri/squiggle-lang": "^0.4.2", + "@quri/squiggle-lang": "^0.5.0-alpha.2", "@react-hook/size": "^2.1.2", "clsx": "^1.2.1", "framer-motion": "^7.4.0", diff --git a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx index 1aa106c8..cb40c509 100644 --- a/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx +++ b/packages/components/src/components/SquiggleViewer/ExpressionViewer.tsx @@ -135,29 +135,6 @@ export const ExpressionViewer: React.FC = ({ value, width }) => { {() => value.value.toString()} ); - case SqValueTag.Symbol: - return ( - - {() => ( - <> - Undefined Symbol: - {value.value} - - )} - - ); - case SqValueTag.Call: - return ( - - {() => value.value} - - ); - case SqValueTag.ArrayString: - return ( - - {() => value.value.map((r) => `"${r}"`).join(", ")} - - ); case SqValueTag.Date: return ( @@ -242,24 +219,6 @@ export const ExpressionViewer: React.FC = ({ value, width }) => { ); } - case SqValueTag.Module: { - return ( - - {(_) => - value.value - .entries() - .filter(([key, _]) => !key.match(/^(__result__)$/)) - .map(([key, r]) => ( - - )) - } - - ); - } case SqValueTag.Record: const plot = makePlot(value.value); if (plot) { @@ -339,7 +298,9 @@ export const ExpressionViewer: React.FC = ({ value, width }) => { {() => (
No display for type: {" "} - {value.tag} + + {(value as { tag: string }).tag} +
)} diff --git a/packages/components/src/stories/SquigglePartial.stories.mdx b/packages/components/src/stories/SquigglePartial.stories.mdx deleted file mode 100644 index 3305586b..00000000 --- a/packages/components/src/stories/SquigglePartial.stories.mdx +++ /dev/null @@ -1,51 +0,0 @@ -import { SquigglePartial, SquiggleEditor } from "../components/SquiggleEditor"; -import { useState } from "react"; -import { Canvas, Meta, Story, Props } from "@storybook/addon-docs"; - - - -export const Template = (props) => ; - -# Squiggle Partial - -A Squiggle Partial is an editor that does not return a graph to the user, but -instead returns bindings that can be used by further Squiggle Editors. - - - - {Template.bind({})} - - - - - - {(props) => { - let [bindings, setBindings] = useState({}); - return ( - <> - - - - ); - }} - - diff --git a/packages/squiggle-lang/__tests__/Lodash_test.res b/packages/squiggle-lang/__tests__/Lodash_test.res deleted file mode 100644 index f0782019..00000000 --- a/packages/squiggle-lang/__tests__/Lodash_test.res +++ /dev/null @@ -1,20 +0,0 @@ -open Jest -open Expect - -let makeTest = (~only=false, str, item1, item2) => - only - ? Only.test(str, () => expect(item1)->toEqual(item2)) - : test(str, () => expect(item1)->toEqual(item2)) - -describe("Lodash", () => - describe("Lodash", () => { - makeTest("min", Lodash.min([1, 3, 4]), 1) - makeTest("max", Lodash.max([1, 3, 4]), 4) - makeTest("uniq", Lodash.uniq([1, 3, 4, 4]), [1, 3, 4]) - makeTest( - "countBy", - Lodash.countBy([1, 3, 4, 4], r => r), - Js.Dict.fromArray([("1", 1), ("3", 1), ("4", 2)]), - ) - }) -) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res index 9079acfa..c386d343 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Bindings/Reducer_Bindings_test.res @@ -1,27 +1,44 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Bindings = Reducer_Bindings +module Namespace = Reducer_Namespace open Jest open Expect open Expect.Operators -describe("Name Space", () => { - let value = InternalExpressionValue.IEvNumber(1967.0) - let nameSpace = Bindings.emptyNameSpace->Bindings.set("value", value) +describe("Bindings", () => { + let value = Reducer_T.IEvNumber(1967.0) + let bindings = Bindings.make()->Bindings.set("value", value) test("get", () => { - expect(Bindings.get(nameSpace, "value")) == Some(value) + expect(bindings->Bindings.get("value")) == Some(value) }) - test("chain and get", () => { - let mainNameSpace = Bindings.emptyNameSpace->Bindings.chainTo([nameSpace]) - expect(Bindings.get(mainNameSpace, "value")) == Some(value) + test("get nonexisting value", () => { + expect(bindings->Bindings.get("nosuchvalue")) == None }) - test("chain and set", () => { - let mainNameSpace0 = Bindings.emptyNameSpace->Bindings.chainTo([nameSpace]) - let mainNameSpace = - mainNameSpace0->Bindings.set("value", InternalExpressionValue.IEvNumber(1968.0)) - expect(Bindings.get(mainNameSpace, "value")) == Some(InternalExpressionValue.IEvNumber(1968.0)) + test("get on extended", () => { + expect(bindings->Bindings.extend->Bindings.get("value")) == Some(value) + }) + + test("locals", () => { + expect(bindings->Bindings.locals->Namespace.get("value")) == Some(value) + }) + + test("locals on extendeed", () => { + expect(bindings->Bindings.extend->Bindings.locals->Namespace.get("value")) == None + }) + + describe("extend", () => { + let value2 = Reducer_T.IEvNumber(5.) + let extendedBindings = bindings->Bindings.extend->Bindings.set("value", value2) + + test("get on extended", () => { + expect(extendedBindings->Bindings.get("value")) == Some(value2) + }) + + test("get on original", () => { + expect(bindings->Bindings.get("value")) == Some(value) + }) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros_test.res deleted file mode 100644 index 84659ed7..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros_test.res +++ /dev/null @@ -1,146 +0,0 @@ -open Jest -// open Expect - -open Reducer_Expression_ExpressionBuilder -open Reducer_TestMacroHelpers -module ExpressionT = Reducer_Expression_T - -let exampleExpression = eNumber(1.) -let exampleExpressionY = eSymbol("y") -let exampleStatementY = eLetStatement("y", eNumber(1.)) -let exampleStatementX = eLetStatement("y", eSymbol("x")) -let exampleStatementZ = eLetStatement("z", eSymbol("y")) - -// If it is not a macro then it is not expanded -testMacro([], exampleExpression, "Ok(1)") - -describe("bindStatement", () => { - // A statement is bound by the bindings created by the previous statement - testMacro( - [], - eBindStatement(eBindings([]), exampleStatementY), - "Ok((:$_setBindings_$ @{} :y 1) context: @{})", - ) - // Then it answers the bindings for the next statement when reduced - testMacroEval([], eBindStatement(eBindings([]), exampleStatementY), "Ok(@{y: 1})") - // Now let's feed a binding to see what happens - testMacro( - [], - eBindStatement(eBindings([("x", IEvNumber(2.))]), exampleStatementX), - "Ok((:$_setBindings_$ @{x: 2} :y 2) context: @{x: 2})", - ) - // An expression does not return a binding, thus error - testMacro([], eBindStatement(eBindings([]), exampleExpression), "Assignment expected") - // When bindings from previous statement are missing the context is injected. This must be the first statement of a block - testMacro( - [("z", IEvNumber(99.))], - eBindStatementDefault(exampleStatementY), - "Ok((:$_setBindings_$ @{z: 99} :y 1) context: @{z: 99})", - ) -}) - -describe("bindExpression", () => { - // x is simply bound in the expression - testMacro( - [], - eBindExpression(eBindings([("x", IEvNumber(2.))]), eSymbol("x")), - "Ok(2 context: @{x: 2})", - ) - // When an let statement is the end expression then bindings are returned - testMacro( - [], - eBindExpression(eBindings([("x", IEvNumber(2.))]), exampleStatementY), - "Ok((:$_exportBindings_$ (:$_setBindings_$ @{x: 2} :y 1)) context: @{x: 2})", - ) - // Now let's reduce that expression - testMacroEval( - [], - eBindExpression(eBindings([("x", IEvNumber(2.))]), exampleStatementY), - "Ok(@{x: 2,y: 1})", - ) - // When bindings are missing the context is injected. This must be the first and last statement of a block - testMacroEval( - [("z", IEvNumber(99.))], - eBindExpressionDefault(exampleStatementY), - "Ok(@{y: 1,z: 99})", - ) -}) - -describe("block", () => { - // Block with a single expression - testMacro([], eBlock(list{exampleExpression}), "Ok((:$$_bindExpression_$$ 1))") - testMacroEval([], eBlock(list{exampleExpression}), "Ok(1)") - // Block with a single statement - testMacro([], eBlock(list{exampleStatementY}), "Ok((:$$_bindExpression_$$ (:$_let_$ :y 1)))") - testMacroEval([], eBlock(list{exampleStatementY}), "Ok(@{y: 1})") - // Block with a statement and an expression - testMacro( - [], - eBlock(list{exampleStatementY, exampleExpressionY}), - "Ok((:$$_bindExpression_$$ (:$$_bindStatement_$$ (:$_let_$ :y 1)) :y))", - ) - testMacroEval([], eBlock(list{exampleStatementY, exampleExpressionY}), "Ok(1)") - // Block with a statement and another statement - testMacro( - [], - eBlock(list{exampleStatementY, exampleStatementZ}), - "Ok((:$$_bindExpression_$$ (:$$_bindStatement_$$ (:$_let_$ :y 1)) (:$_let_$ :z :y)))", - ) - testMacroEval([], eBlock(list{exampleStatementY, exampleStatementZ}), "Ok(@{y: 1,z: 1})") - // Block inside a block - testMacro([], eBlock(list{eBlock(list{exampleExpression})}), "Ok((:$$_bindExpression_$$ {1}))") - testMacroEval([], eBlock(list{eBlock(list{exampleExpression})}), "Ok(1)") - // Block assigned to a variable - testMacro( - [], - eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}), - "Ok((:$$_bindExpression_$$ (:$_let_$ :z {{:y}})))", - ) - testMacroEval( - [], - eBlock(list{eLetStatement("z", eBlock(list{eBlock(list{exampleExpressionY})}))}), - "Ok(@{z: :y})", - ) - // Empty block - testMacro([], eBlock(list{}), "Ok(:undefined block)") //TODO: should be an error - // :$$_block_$$ (:$$_block_$$ (:$_let_$ :y (:add :x 1)) :y)" - testMacro( - [], - eBlock(list{ - eBlock(list{ - eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})), - eSymbol("y"), - }), - }), - "Ok((:$$_bindExpression_$$ {(:$_let_$ :y (:add :x 1)); :y}))", - ) - testMacroEval( - [("x", IEvNumber(1.))], - eBlock(list{ - eBlock(list{ - eLetStatement("y", eFunction("add", list{eSymbol("x"), eNumber(1.)})), - eSymbol("y"), - }), - }), - "Ok(2)", - ) -}) - -describe("lambda", () => { - // assign a lambda to a variable - let lambdaExpression = eFunction("$$_lambda_$$", list{eArrayString(["y"]), exampleExpressionY}) - testMacro([], lambdaExpression, "Ok(lambda(y=>internal code))") - // call a lambda - let callLambdaExpression = list{lambdaExpression, eNumber(1.)}->ExpressionT.EList - testMacro([], callLambdaExpression, "Ok(((:$$_lambda_$$ [y] :y) 1))") - testMacroEval([], callLambdaExpression, "Ok(1)") - // Parameters shadow the outer scope - testMacroEval([("y", IEvNumber(666.))], callLambdaExpression, "Ok(1)") - // When not shadowed by the parameters, the outer scope variables are available - let lambdaExpression = eFunction( - "$$_lambda_$$", - list{eArrayString(["z"]), eFunction("add", list{eSymbol("y"), eSymbol("z")})}, - ) - let callLambdaExpression = eList(list{lambdaExpression, eNumber(1.)}) - testMacroEval([("y", IEvNumber(666.))], callLambdaExpression, "Ok(667)") -}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res deleted file mode 100644 index 4932af3b..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn_test.res +++ /dev/null @@ -1,41 +0,0 @@ -module ExpressionValue = ReducerInterface.InternalExpressionValue -module Expression = Reducer_Expression - -open Jest -open Expect - -let expectEvalToBe = (sourceCode: string, answer: string) => - Expression.BackCompatible.evaluateString(sourceCode) - ->ExpressionValue.toStringResult - ->expect - ->toBe(answer) - -let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer)) - -describe("builtin", () => { - // All MathJs operators and functions are available for string, number and boolean - // .e.g + - / * > >= < <= == /= not and or - // See https://mathjs.org/docs/expressions/syntax.html - // See https://mathjs.org/docs/reference/functions.html - testEval("-1", "Ok(-1)") - testEval("1-1", "Ok(0)") - testEval("2>1", "Ok(true)") - testEval("concat('a','b')", "Ok('ab')") -}) - -describe("builtin exception", () => { - //It's a pity that MathJs does not return error position - test("MathJs Exception", () => - expectEvalToBe("testZadanga(1)", "Error(JS Exception: Error: Undefined function testZadanga)") - ) -}) - -describe("error reporting from collection functions", () => { - testEval("arr=[1,2,3]; map(arr, {|x| x*2})", "Ok([2,4,6])") - testEval( - "arr = [normal(3,2)]; map(arr, zarathsuzaWasHere)", - "Error(zarathsuzaWasHere is not defined)", - ) - // FIXME: returns "Error(Function not found: map(Array,Symbol))" - // Actually this error is correct but not informative -}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res deleted file mode 100644 index e915bfc1..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Helpers.res +++ /dev/null @@ -1,17 +0,0 @@ -// Reducer_Helpers -module ErrorValue = Reducer_ErrorValue -module InternalExpressionValue = ReducerInterface.InternalExpressionValue -module Bindings = Reducer_Bindings - -let removeDefaultsInternal = (iev: InternalExpressionValue.t) => { - switch iev { - | InternalExpressionValue.IEvBindings(nameSpace) => - Bindings.removeOther( - nameSpace, - ReducerInterface.StdLib.internalStdLib, - )->InternalExpressionValue.IEvBindings - | value => value - } -} - -let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res deleted file mode 100644 index e56c04d7..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res +++ /dev/null @@ -1,31 +0,0 @@ -module MathJs = Reducer_MathJs -module ErrorValue = Reducer_ErrorValue - -open Jest -open ExpectJs - -describe("eval", () => { - test("Number", () => expect(MathJs.Eval.eval("1"))->toEqual(Ok(IEvNumber(1.)))) - test("Number expr", () => expect(MathJs.Eval.eval("1-1"))->toEqual(Ok(IEvNumber(0.)))) - test("String", () => expect(MathJs.Eval.eval("'hello'"))->toEqual(Ok(IEvString("hello")))) - test("String expr", () => - expect(MathJs.Eval.eval("concat('hello ','world')"))->toEqual(Ok(IEvString("hello world"))) - ) - test("Boolean", () => expect(MathJs.Eval.eval("true"))->toEqual(Ok(IEvBool(true)))) - test("Boolean expr", () => expect(MathJs.Eval.eval("2>1"))->toEqual(Ok(IEvBool(true)))) -}) - -describe("errors", () => { - // All those errors propagete up and are returned by the resolver - test("unknown function", () => - expect(MathJs.Eval.eval("testZadanga()"))->toEqual( - Error(ErrorValue.REJavaScriptExn(Some("Undefined function testZadanga"), Some("Error"))), - ) - ) - - test("unknown answer type", () => - expect(MathJs.Eval.eval("1+1i"))->toEqual( - Error(ErrorValue.RETodo("Unhandled MathJs literal type: object")), - ) - ) -}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res new file mode 100644 index 00000000..e745dc64 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Namespace_test.res @@ -0,0 +1,53 @@ +@@warning("-44") +module Namespace = Reducer_Namespace + +open Jest +open Expect +open Expect.Operators + +let makeValue = (v: float) => v->Reducer_T.IEvNumber + +describe("Namespace", () => { + let value = makeValue(5.) + let v2 = makeValue(2.) + let ns = Namespace.make()->Namespace.set("value", value) + + test("get", () => { + expect(ns->Namespace.get("value")) == Some(value) + }) + + test("get nonexisting value", () => { + expect(ns->Namespace.get("nosuchvalue")) == None + }) + + test("set", () => { + let ns2 = ns->Namespace.set("v2", v2) + expect(ns2->Namespace.get("v2")) == Some(v2) + }) + + test("immutable", () => { + let _ = ns->Namespace.set("v2", Reducer_T.IEvNumber(2.)) + expect(ns->Namespace.get("v2")) == None + }) + + describe("merge many", () => { + let x1 = makeValue(10.) + let x2 = makeValue(20.) + let x3 = makeValue(30.) + let x4 = makeValue(40.) + let ns1 = Namespace.make()->Namespace.set("x1", x1)->Namespace.set("x2", x2) + let ns2 = Namespace.make()->Namespace.set("x3", x3)->Namespace.set("x4", x4) + + let nsMerged = Namespace.mergeMany([ns, ns1, ns2]) + + test("merge many 1", () => { + expect(nsMerged->Namespace.get("x1")) == Some(x1) + }) + test("merge many 2", () => { + expect(nsMerged->Namespace.get("x4")) == Some(x4) + }) + test("merge many 3", () => { + expect(nsMerged->Namespace.get("value")) == Some(value) + }) + }) +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res index 3440c3e0..6c60c187 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_Parse_test.res @@ -3,346 +3,231 @@ open Reducer_Peggy_TestHelpers describe("Peggy parse", () => { describe("float", () => { - testParse("1.", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("1.1", "{(::$_endOfOuterBlock_$ () 1.1)}") - testParse(".1", "{(::$_endOfOuterBlock_$ () 0.1)}") - testParse("0.1", "{(::$_endOfOuterBlock_$ () 0.1)}") - testParse("1e1", "{(::$_endOfOuterBlock_$ () 10)}") - testParse("1e-1", "{(::$_endOfOuterBlock_$ () 0.1)}") - testParse(".1e1", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("0.1e1", "{(::$_endOfOuterBlock_$ () 1)}") + testParse("1.", "{1}") + testParse("1.1", "{1.1}") + testParse(".1", "{0.1}") + testParse("0.1", "{0.1}") + testParse("1e1", "{10}") + testParse("1e-1", "{0.1}") + testParse(".1e1", "{1}") + testParse("0.1e1", "{1}") }) describe("literals operators parenthesis", () => { - // Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement - testParse("1", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("'hello'", "{(::$_endOfOuterBlock_$ () 'hello')}") - testParse("true", "{(::$_endOfOuterBlock_$ () true)}") - testParse("1+2", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") - testParse("add(1,2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") - testParse("(1)", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("(1+2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") + testParse("1", "{1}") + testParse("'hello'", "{'hello'}") + testParse("true", "{true}") + testParse("1+2", "{(:add 1 2)}") + testParse("add(1,2)", "{(:add 1 2)}") + testParse("(1)", "{1}") + testParse("(1+2)", "{(:add 1 2)}") }) describe("unary", () => { - testParse("-1", "{(::$_endOfOuterBlock_$ () (::unaryMinus 1))}") - testParse("!true", "{(::$_endOfOuterBlock_$ () (::not true))}") - testParse("1 + -1", "{(::$_endOfOuterBlock_$ () (::add 1 (::unaryMinus 1)))}") - testParse("-a[0]", "{(::$_endOfOuterBlock_$ () (::unaryMinus (::$_atIndex_$ :a 0)))}") - testParse("!a[0]", "{(::$_endOfOuterBlock_$ () (::not (::$_atIndex_$ :a 0)))}") + testParse("-1", "{(:unaryMinus 1)}") + testParse("!true", "{(:not true)}") + testParse("1 + -1", "{(:add 1 (:unaryMinus 1))}") + testParse("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}") + testParse("!a[0]", "{(:not (:$_atIndex_$ :a 0))}") }) describe("multiplicative", () => { - testParse("1 * 2", "{(::$_endOfOuterBlock_$ () (::multiply 1 2))}") - testParse("1 / 2", "{(::$_endOfOuterBlock_$ () (::divide 1 2))}") - testParse("1 * 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::multiply 1 2) 3))}") - testParse("1 * 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::multiply 1 2) 3))}") - testParse("1 / 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::divide 1 2) 3))}") - testParse("1 / 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::divide 1 2) 3))}") - testParse( - "1 * 2 + 3 * 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}", - ) - testParse( - "1 * 2 - 3 * 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}", - ) - testParse( - "1 * 2 .+ 3 * 4", - "{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}", - ) - testParse( - "1 * 2 .- 3 * 4", - "{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}", - ) - testParse( - "1 * 2 + 3 .* 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}", - ) - testParse( - "1 * 2 + 3 / 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}", - ) - testParse( - "1 * 2 + 3 ./ 4", - "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}", - ) - testParse( - "1 * 2 - 3 .* 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}", - ) - testParse( - "1 * 2 - 3 / 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}", - ) - testParse( - "1 * 2 - 3 ./ 4", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}", - ) - testParse( - "1 * 2 - 3 * 4^5", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5))))}", - ) + testParse("1 * 2", "{(:multiply 1 2)}") + testParse("1 / 2", "{(:divide 1 2)}") + testParse("1 * 2 * 3", "{(:multiply (:multiply 1 2) 3)}") + testParse("1 * 2 / 3", "{(:divide (:multiply 1 2) 3)}") + testParse("1 / 2 * 3", "{(:multiply (:divide 1 2) 3)}") + testParse("1 / 2 / 3", "{(:divide (:divide 1 2) 3)}") + testParse("1 * 2 + 3 * 4", "{(:add (:multiply 1 2) (:multiply 3 4))}") + testParse("1 * 2 - 3 * 4", "{(:subtract (:multiply 1 2) (:multiply 3 4))}") + testParse("1 * 2 .+ 3 * 4", "{(:dotAdd (:multiply 1 2) (:multiply 3 4))}") + testParse("1 * 2 .- 3 * 4", "{(:dotSubtract (:multiply 1 2) (:multiply 3 4))}") + testParse("1 * 2 + 3 .* 4", "{(:add (:multiply 1 2) (:dotMultiply 3 4))}") + testParse("1 * 2 + 3 / 4", "{(:add (:multiply 1 2) (:divide 3 4))}") + testParse("1 * 2 + 3 ./ 4", "{(:add (:multiply 1 2) (:dotDivide 3 4))}") + testParse("1 * 2 - 3 .* 4", "{(:subtract (:multiply 1 2) (:dotMultiply 3 4))}") + testParse("1 * 2 - 3 / 4", "{(:subtract (:multiply 1 2) (:divide 3 4))}") + testParse("1 * 2 - 3 ./ 4", "{(:subtract (:multiply 1 2) (:dotDivide 3 4))}") + testParse("1 * 2 - 3 * 4^5", "{(:subtract (:multiply 1 2) (:multiply 3 (:pow 4 5)))}") testParse( "1 * 2 - 3 * 4^5^6", - "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6))))}", - ) - testParse( - "1 * -a[-2]", - "{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}", + "{(:subtract (:multiply 1 2) (:multiply 3 (:pow (:pow 4 5) 6)))}", ) + testParse("1 * -a[-2]", "{(:multiply 1 (:unaryMinus (:$_atIndex_$ :a (:unaryMinus 2))))}") }) describe("multi-line", () => { - testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}") - testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}") + testParse("x=1; 2", "{:x = {1}; 2}") + testParse("x=1; y=2", "{:x = {1}; :y = {2}}") }) describe("variables", () => { - testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}") - testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") - testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}") + testParse("x = 1", "{:x = {1}}") + testParse("x", "{:x}") + testParse("x = 1; x", "{:x = {1}; :x}") }) describe("functions", () => { - testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments - testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}") + testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments + testParse("identity(x)", "{(:identity :x)}") }) describe("arrays", () => { - testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$))}") - testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 0 1 2))}") - testParse( - "['hello', 'world']", - "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ 'hello' 'world'))}", - ) - testParse( - "([0,1,2])[1]", - "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ 0 1 2) 1))}", - ) + testParse("[]", "{[]}") + testParse("[0, 1, 2]", "{[0; 1; 2]}") + testParse("['hello', 'world']", "{['hello'; 'world']}") + testParse("([0,1,2])[1]", "{(:$_atIndex_$ [0; 1; 2] 1)}") }) describe("records", () => { - testParse( - "{a: 1, b: 2}", - "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}", - ) - testParse( - "{1+0: 1, 2+0: 2}", - "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2)))}", - ) // key can be any expression - testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}") + testParse("{a: 1, b: 2}", "{{'a': 1, 'b': 2}}") + testParse("{1+0: 1, 2+0: 2}", "{{(:add 1 0): 1, (:add 2 0): 2}}") // key can be any expression + testParse("record.property", "{(:$_atIndex_$ :record 'property')}") }) describe("post operators", () => { //function call, array and record access are post operators with higher priority than unary operators - testParse("a==!b(1)", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::b 1))))}") - testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}") - testParse( - "a==!b.one", - "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}", - ) + testParse("a==!b(1)", "{(:equal :a (:not (:b 1)))}") + testParse("a==!b[1]", "{(:equal :a (:not (:$_atIndex_$ :b 1)))}") + testParse("a==!b.one", "{(:equal :a (:not (:$_atIndex_$ :b 'one')))}") }) describe("comments", () => { - testParse("1 # This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("1 // This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("1 /* This is a multi line comment */", "{(::$_endOfOuterBlock_$ () 1)}") - testParse("/* This is a multi line comment */ 1", "{(::$_endOfOuterBlock_$ () 1)}") + testParse("1 # This is a line comment", "{1}") + testParse("1 // This is a line comment", "{1}") + testParse("1 /* This is a multi line comment */", "{1}") + testParse("/* This is a multi line comment */ 1", "{1}") testParse( ` /* This is a multi line comment */ 1`, - "{(::$_endOfOuterBlock_$ () 1)}", + "{1}", ) }) describe("ternary operator", () => { - testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}") + testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}") testParse( "false ? 2 : false ? 4 : 5", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}", + "{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}", ) // nested ternary }) describe("if then else", () => { - testParse( - "if true then 2 else 3", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}", - ) - testParse( - "if false then {2} else {3}", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} {3}))}", - ) + testParse("if true then 2 else 3", "{(::$$_ternary_$$ true {2} {3})}") + testParse("if false then {2} else {3}", "{(::$$_ternary_$$ false {2} {3})}") testParse( "if false then {2} else if false then {4} else {5}", - "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5})))}", + "{(::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5}))}", ) //nested if }) describe("logical", () => { - testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}") - testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}") - testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison - testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}") - testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}") - testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}") - testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}") - testParse( - "a && b!=c || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}", - ) - testParse( - "a && !(b==c) || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}", - ) - testParse( - "a && b>=c || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}", - ) - testParse( - "a && !(b>=c) || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}", - ) - testParse( - "a && b<=c || d", - "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smallerEq :b :c)) :d))}", - ) - testParse("a && b>c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::larger :b :c)) :d))}") - testParse( - "a && b=c || d", "{(:or (:and :a (:largerEq :b :c)) :d)}") + testParse("a && !(b>=c) || d", "{(:or (:and :a (:not (:largerEq :b :c))) :d)}") + testParse("a && b<=c || d", "{(:or (:and :a (:smallerEq :b :c)) :d)}") + testParse("a && b>c || d", "{(:or (:and :a (:larger :b :c)) :d)}") + testParse("a && b { - testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") - testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}") - testParse( - "-a[1] -> add(2)", - "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::$_atIndex_$ :a 1)) 2))}", - ) - testParse("-f(1) -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::f 1)) 2))}") - testParse("1 + 2 -> add(3)", "{(::$_endOfOuterBlock_$ () (::add 1 (::add 2 3)))}") - testParse("1 -> add(2) * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::add 1 2) 3))}") - testParse("1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract 1 2))}") - testParse("-1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract (::unaryMinus 1) 2))}") - testParse( - "1 -> subtract(2) * 3", - "{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}", - ) + testParse("1 -> add(2)", "{(:add 1 2)}") + testParse("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}") + testParse("-a[1] -> add(2)", "{(:add (:unaryMinus (:$_atIndex_$ :a 1)) 2)}") + testParse("-f(1) -> add(2)", "{(:add (:unaryMinus (:f 1)) 2)}") + testParse("1 + 2 -> add(3)", "{(:add 1 (:add 2 3))}") + testParse("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}") + testParse("1 -> subtract(2)", "{(:subtract 1 2)}") + testParse("-1 -> subtract(2)", "{(:subtract (:unaryMinus 1) 2)}") + testParse("1 -> subtract(2) * 3", "{(:multiply (:subtract 1 2) 3)}") }) describe("elixir pipe", () => { //handled together with -> so there is no need for seperate tests - testParse("1 |> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}") + testParse("1 |> add(2)", "{(:add 1 2)}") }) describe("to", () => { - testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}") - testParse( - "-1 to -2", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}", - ) // lower than unary + testParse("1 to 2", "{(:credibleIntervalToDistribution 1 2)}") + testParse("-1 to -2", "{(:credibleIntervalToDistribution (:unaryMinus 1) (:unaryMinus 2))}") // lower than unary testParse( "a[1] to a[2]", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2)))}", + "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 1) (:$_atIndex_$ :a 2))}", ) // lower than post testParse( "a.p1 to a.p2", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}", + "{(:credibleIntervalToDistribution (:$_atIndex_$ :a 'p1') (:$_atIndex_$ :a 'p2'))}", ) // lower than post - testParse( - "1 to 2 + 3", - "{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}", - ) // higher than binary operators + testParse("1 to 2 + 3", "{(:add (:credibleIntervalToDistribution 1 2) 3)}") // higher than binary operators testParse( "1->add(2) to 3->add(4) -> add(4)", - "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4)))}", + "{(:credibleIntervalToDistribution (:add 1 2) (:add (:add 3 4) 4))}", ) // lower than chain }) describe("inner block", () => { // inner blocks are 0 argument lambdas. They can be used whenever a value is required. // Like lambdas they have a local scope. - testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; (::$_endOfOuterBlock_$ () :x)}") + testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; :x}") }) describe("lambda", () => { - testParse("{|x| x}", "{(::$_endOfOuterBlock_$ () {|:x| {:x}})}") - testParse("f={|x| x}", "{:f = {{|:x| {:x}}}; (::$_endOfOuterBlock_$ () ())}") - testParse("f(x)=x", "{:f = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions are lambda assignments - testParse( - "f(x)=x ? 1 : 0", - "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}; (::$_endOfOuterBlock_$ () ())}", - ) // Function definitions are lambda assignments + testParse("{|x| x}", "{{|:x| :x}}") + testParse("f={|x| x}", "{:f = {{|:x| :x}}}") + testParse("f(x)=x", "{:f = {|:x| {:x}}}") // Function definitions are lambda assignments + testParse("f(x)=x ? 1 : 0", "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}") // Function definitions are lambda assignments }) describe("Using lambda as value", () => { testParse( "myadd(x,y)=x+y; z=myadd; z", - "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; (::$_endOfOuterBlock_$ () :z)}", + "{:myadd = {|:x,:y| {(:add :x :y)}}; :z = {:myadd}; :z}", ) testParse( "myadd(x,y)=x+y; z=[myadd]; z", - "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ :myadd)}; (::$_endOfOuterBlock_$ () :z)}", + "{:myadd = {|:x,:y| {(:add :x :y)}}; :z = {[:myadd]}; :z}", ) testParse( "myaddd(x,y)=x+y; z={x: myaddd}; z", - "{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; (::$_endOfOuterBlock_$ () :z)}", - ) - testParse("f({|x| x+1})", "{(::$_endOfOuterBlock_$ () (::f {|:x| {(::add :x 1)}}))}") - testParse( - "map(arr, {|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}", - ) - testParse( - "map([1,2,3], {|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ 1 2 3) {|:x| {(::add :x 1)}}))}", - ) - testParse( - "[1,2,3]->map({|x| x+1})", - "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ 1 2 3) {|:x| {(::add :x 1)}}))}", + "{:myaddd = {|:x,:y| {(:add :x :y)}}; :z = {{'x': :myaddd}}; :z}", ) + testParse("f({|x| x+1})", "{(:f {|:x| (:add :x 1)})}") + testParse("map(arr, {|x| x+1})", "{(:map :arr {|:x| (:add :x 1)})}") + testParse("map([1,2,3], {|x| x+1})", "{(:map [1; 2; 3] {|:x| (:add :x 1)})}") + testParse("[1,2,3]->map({|x| x+1})", "{(:map [1; 2; 3] {|:x| (:add :x 1)})}") }) describe("unit", () => { - testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}") - testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}") - testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}") + testParse("1m", "{(:fromUnit_m 1)}") + testParse("1M", "{(:fromUnit_M 1)}") + testParse("1m+2cm", "{(:add (:fromUnit_m 1) (:fromUnit_cm 2))}") }) describe("Module", () => { - testParse("x", "{(::$_endOfOuterBlock_$ () :x)}") - testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}") + testParse("x", "{:x}") + testParse("Math.pi", "{:Math.pi}") }) }) @@ -351,19 +236,19 @@ describe("parsing new line", () => { ` a + b`, - "{(::$_endOfOuterBlock_$ () (::add :a :b))}", + "{(:add :a :b)}", ) testParse( ` x= 1`, - "{:x = {1}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}}", ) testParse( ` x=1 y=2`, - "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}; :y = {2}}", ) testParse( ` @@ -371,7 +256,7 @@ describe("parsing new line", () => { y=2; y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -379,7 +264,7 @@ describe("parsing new line", () => { y=2 y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -388,7 +273,7 @@ describe("parsing new line", () => { y } x`, - "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}", + "{:x = {:y = {2}; :y}; :x}", ) testParse( ` @@ -396,7 +281,7 @@ describe("parsing new line", () => { y=2 z=3 `, - "{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}", + "{:x = {1}; :y = {2}; :z = {3}}", ) testParse( ` @@ -407,7 +292,7 @@ describe("parsing new line", () => { x+y+z } `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; (::$_endOfOuterBlock_$ () ())}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}}", ) testParse( ` @@ -420,7 +305,7 @@ describe("parsing new line", () => { g=f+4 g `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () :g)}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}; :g = {(:add :f 4)}; :g}", ) testParse( ` @@ -442,7 +327,7 @@ describe("parsing new line", () => { p -> q `, - "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () (::q (::p (::h :g))))}", + "{:f = {:x = {1}; :y = {2}; :z = {3}; (:add (:add :x :y) :z)}; :g = {(:add :f 4)}; (:q (:p (:h :g)))}", ) testParse( ` @@ -451,7 +336,7 @@ describe("parsing new line", () => { c |> d `, - "{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}", + "{(:d (:c (:b :a)))}", ) testParse( ` @@ -461,6 +346,6 @@ describe("parsing new line", () => { d + e `, - "{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}", + "{(:add (:d (:c (:b :a))) :e)}", ) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res index 303f66ee..0dadae86 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res @@ -1,6 +1,5 @@ module Expression = Reducer_Expression module ExpressionT = Reducer_Expression_T -module ExpressionValue = ReducerInterface.InternalExpressionValue module Parse = Reducer_Peggy_Parse module Result = Belt.Result module ToExpression = Reducer_Peggy_ToExpression @@ -24,8 +23,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => { let a2 = rExpr ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr)) - ->Reducer_Helpers.rRemoveDefaultsInternal - ->ExpressionValue.toStringResultOkless + ->Reducer_Value.toStringResultOkless (a1, a2)->expect->toEqual((answer, v)) } } diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res index 951005c2..80f93fdc 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_outerBlock_test.res @@ -1,23 +1,12 @@ -module Bindings = Reducer_Bindings -module InternalExpressionValue = ReducerInterface_InternalExpressionValue - open Jest open Reducer_Peggy_TestHelpers +// Note: these tests aren't useful anymore since outer block macro got deleted. +// Probably can be removed or folded into other Peggy tests. describe("Peggy Outer Block", () => { - testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) - testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ()) - testToExpression( - "x=1; y=2", - "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", - ~v="()", - (), - ) - testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) - testToExpression( - "x={a=1; a}; x", - "{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}", - ~v="1", - (), - ) + testToExpression("1", "1", ~v="1", ()) + testToExpression("x=1", "x = {1}", ~v="()", ()) + testToExpression("x=1; y=2", "x = {1}; y = {2}", ~v="()", ()) + testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ()) + testToExpression("x={a=1; a}; x", "x = {a = {1}; a}; x", ~v="1", ()) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res index eefa2294..cab84681 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_test.res @@ -1,5 +1,4 @@ module Bindings = Reducer_Bindings -module InternalExpressionValue = ReducerInterface_InternalExpressionValue open Jest open Reducer_Peggy_TestHelpers @@ -7,160 +6,94 @@ open Reducer_Peggy_TestHelpers describe("Peggy to Expression", () => { describe("literals operators parenthesis", () => { // Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement - testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) - testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ()) - testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ()) - testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) - testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) - testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ()) - testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ()) + testToExpression("1", "1", ~v="1", ()) + testToExpression("'hello'", "'hello'", ~v="'hello'", ()) + testToExpression("true", "true", ~v="true", ()) + testToExpression("1+2", "(add)(1, 2)", ~v="3", ()) + testToExpression("add(1,2)", "(add)(1, 2)", ~v="3", ()) + testToExpression("(1)", "1", ()) + testToExpression("(1+2)", "(add)(1, 2)", ()) }) describe("unary", () => { - testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ()) - testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ()) - testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ()) - testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ()) + testToExpression("-1", "(unaryMinus)(1)", ~v="-1", ()) + testToExpression("!true", "(not)(true)", ~v="false", ()) + testToExpression("1 + -1", "(add)(1, (unaryMinus)(1))", ~v="0", ()) + testToExpression("-a[0]", "(unaryMinus)(($_atIndex_$)(a, 0))", ()) }) describe("multi-line", () => { - testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ()) - testToExpression( - "x=1; y=2", - "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}", - (), - ) + testToExpression("x=1; 2", "x = {1}; 2", ~v="2", ()) + testToExpression("x=1; y=2", "x = {1}; y = {2}", ()) }) describe("variables", () => { - testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ()) - testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error - testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ()) + testToExpression("x = 1", "x = {1}", ()) + testToExpression("x", "x", ~v="Error(x is not defined)", ()) //TODO: value should return error + testToExpression("x = 1; x", "x = {1}; x", ~v="1", ()) }) describe("functions", () => { - testToExpression( - "identity(x) = x", - "{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", - (), - ) // Function definitions become lambda assignments - testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly + testToExpression("identity(x) = x", "identity = {|x| {x}}", ()) // Function definitions become lambda assignments + testToExpression("identity(x)", "(identity)(x)", ()) // Note value returns error properly testToExpression( "f(x) = x> 2 ? 0 : 1; f(3)", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:$_endOfOuterBlock_$ () (:f 3))}", + "f = {|x| {(larger)(x, 2) ? (0) : (1)}}; (f)(3)", ~v="0", (), ) }) describe("arrays", () => { - testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) - testToExpression( - "[0, 1, 2]", - "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 0 1 2))}", - ~v="[0,1,2]", - (), - ) - testToExpression( - "['hello', 'world']", - "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 'hello' 'world'))}", - ~v="['hello','world']", - (), - ) - testToExpression( - "([0,1,2])[1]", - "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ 0 1 2) 1))}", - ~v="1", - (), - ) + testToExpression("[]", "[]", ~v="[]", ()) + testToExpression("[0, 1, 2]", "[0, 1, 2]", ~v="[0,1,2]", ()) + testToExpression("['hello', 'world']", "['hello', 'world']", ~v="['hello','world']", ()) + testToExpression("([0,1,2])[1]", "($_atIndex_$)([0, 1, 2], 1)", ~v="1", ()) }) describe("records", () => { - testToExpression( - "{a: 1, b: 2}", - "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}", - ~v="{a: 1,b: 2}", - (), - ) - testToExpression( - "{1+0: 1, 2+0: 2}", - "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}", - (), - ) // key can be any expression - testToExpression( - "record.property", - "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", - (), - ) + testToExpression("{a: 1, b: 2}", "{'a': 1, 'b': 2}", ~v="{a: 1,b: 2}", ()) + testToExpression("{1+0: 1, 2+0: 2}", "{(add)(1, 0): 1, (add)(2, 0): 2}", ()) // key can be any expression + testToExpression("record.property", "($_atIndex_$)(record, 'property')", ()) testToExpression( "record={property: 1}; record.property", - "{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}", + "record = {{'property': 1}}; ($_atIndex_$)(record, 'property')", ~v="1", (), ) }) describe("comments", () => { - testToExpression("1 # This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) - testToExpression("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ()) - testToExpression( - "1 /* This is a multi line comment */", - "{(:$_endOfOuterBlock_$ () 1)}", - ~v="1", - (), - ) - testToExpression( - "/* This is a multi line comment */ 1", - "{(:$_endOfOuterBlock_$ () 1)}", - ~v="1", - (), - ) + testToExpression("1 # This is a line comment", "1", ~v="1", ()) + testToExpression("1 // This is a line comment", "1", ~v="1", ()) + testToExpression("1 /* This is a multi line comment */", "1", ~v="1", ()) + testToExpression("/* This is a multi line comment */ 1", "1", ~v="1", ()) }) describe("ternary operator", () => { - testToExpression( - "true ? 1 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}", - ~v="1", - (), - ) - testToExpression( - "false ? 1 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}", - ~v="0", - (), - ) - testToExpression( - "true ? 1 : false ? 2 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}", - ~v="1", - (), - ) // nested ternary - testToExpression( - "false ? 1 : false ? 2 : 0", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}", - ~v="0", - (), - ) // nested ternary + testToExpression("true ? 1 : 0", "true ? (1) : (0)", ~v="1", ()) + testToExpression("false ? 1 : 0", "false ? (1) : (0)", ~v="0", ()) + testToExpression("true ? 1 : false ? 2 : 0", "true ? (1) : (false ? (2) : (0))", ~v="1", ()) // nested ternary + testToExpression("false ? 1 : false ? 2 : 0", "false ? (1) : (false ? (2) : (0))", ~v="0", ()) // nested ternary describe("ternary bindings", () => { testToExpression( // expression binding "f(a) = a > 5 ? 1 : 0; f(6)", - "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(larger)(a, 5) ? (1) : (0)}}; (f)(6)", ~v="1", (), ) testToExpression( // when true binding "f(a) = a > 5 ? a : 0; f(6)", - "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(larger)(a, 5) ? (a) : (0)}}; (f)(6)", ~v="6", (), ) testToExpression( // when false binding "f(a) = a < 5 ? 1 : a; f(6)", - "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}", + "f = {|a| {(smaller)(a, 5) ? (1) : (a)}}; (f)(6)", ~v="6", (), ) @@ -168,41 +101,23 @@ describe("Peggy to Expression", () => { }) describe("if then else", () => { - testToExpression( - "if true then 2 else 3", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", - (), - ) - testToExpression( - "if true then {2} else {3}", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}", - (), - ) + testToExpression("if true then 2 else 3", "true ? ({2}) : ({3})", ()) + testToExpression("if true then {2} else {3}", "true ? ({2}) : ({3})", ()) testToExpression( "if false then {2} else if false then {4} else {5}", - "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}", + "false ? ({2}) : (false ? ({4}) : ({5}))", (), ) //nested if }) describe("pipe", () => { - testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) - testToExpression( - "-1 -> add(2)", - "{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}", - ~v="1", - (), - ) // note that unary has higher priority naturally - testToExpression( - "1 -> add(2) * 3", - "{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}", - ~v="9", - (), - ) + testToExpression("1 -> add(2)", "(add)(1, 2)", ~v="3", ()) + testToExpression("-1 -> add(2)", "(add)((unaryMinus)(1), 2)", ~v="1", ()) // note that unary has higher priority naturally + testToExpression("1 -> add(2) * 3", "(multiply)((add)(1, 2), 3)", ~v="9", ()) }) describe("elixir pipe", () => { - testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ()) + testToExpression("1 |> add(2)", "(add)(1, 2)", ~v="3", ()) }) // see testParse for priorities of to and credibleIntervalToDistribution @@ -212,44 +127,28 @@ describe("Peggy to Expression", () => { // Like lambdas they have a local scope. testToExpression( "y=99; x={y=1; y}", - "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}", + "y = {99}; x = {y = {1}; y}", + // "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}", (), ) }) describe("lambda", () => { - testToExpression( - "{|x| x}", - "{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}", - ~v="lambda(x=>internal code)", - (), - ) - testToExpression( - "f={|x| x}", - "{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}", - (), - ) - testToExpression( - "f(x)=x", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}", - (), - ) // Function definitions are lambda assignments - testToExpression( - "f(x)=x ? 1 : 0", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}", - (), - ) + testToExpression("{|x| x}", "{|x| x}", ~v="lambda(x=>internal code)", ()) + testToExpression("f={|x| x}", "f = {{|x| x}}", ()) + testToExpression("f(x)=x", "f = {|x| {x}}", ()) // Function definitions are lambda assignments + testToExpression("f(x)=x ? 1 : 0", "f = {|x| {x ? (1) : (0)}}", ()) }) describe("module", () => { // testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ()) // Only.test("stdlibrary", () => { - // ReducerInterface_StdLib.internalStdLib + // SquiggleLibrary_StdLib.stdLib // ->IEvBindings - // ->InternalExpressionValue.toString + // ->Reducer_Value.toString // ->expect // ->toBe("") // }) - testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ()) + testToExpression("Math.pi", "Math.pi", ~v="3.141592653589793", ()) }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res index f46d387f..465789da 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res @@ -3,22 +3,17 @@ open Reducer_Peggy_TestHelpers describe("Peggy void", () => { //literal - testToExpression("()", "{(:$_endOfOuterBlock_$ () ())}", ~v="()", ()) + testToExpression("()", "()", ~v="()", ()) testToExpression( "fn()=1", - "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () ())}", + "fn = {|_| {1}}", // ~v="@{fn: lambda(_=>internal code)}", (), ) - testToExpression( - "fn()=1; fn()", - "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () (:fn ()))}", - ~v="1", - (), - ) + testToExpression("fn()=1; fn()", "fn = {|_| {1}}; (fn)(())", ~v="1", ()) testToExpression( "fn(a)=(); call fn(1)", - "{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)}); (:$_endOfOuterBlock_$ () ())}", + "fn = {|a| {()}}; _ = {(fn)(1)}", // ~v="@{_: (),fn: lambda(a=>internal code)}", (), ) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res index 34a21573..43de902e 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res @@ -1,7 +1,6 @@ module ErrorValue = Reducer_ErrorValue module Expression = Reducer_Expression module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface.InternalExpressionValue open Jest open Expect @@ -9,7 +8,7 @@ open Expect let unwrapRecord = rValue => rValue->Belt.Result.flatMap(value => switch value { - | InternalExpressionValue.IEvRecord(aRecord) => Ok(aRecord) + | Reducer_T.IEvRecord(aRecord) => Ok(aRecord) | _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error } ) @@ -18,15 +17,11 @@ let expectParseToBe = (code: string, answer: string) => Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer) let expectEvalToBe = (code: string, answer: string) => - Expression.BackCompatible.evaluateString(code) - ->Reducer_Helpers.rRemoveDefaultsInternal - ->InternalExpressionValue.toStringResult - ->expect - ->toBe(answer) + Expression.BackCompatible.evaluateString(code)->Reducer_Value.toStringResult->expect->toBe(answer) let expectEvalError = (code: string) => Expression.BackCompatible.evaluateString(code) - ->InternalExpressionValue.toStringResult + ->Reducer_Value.toStringResult ->expect ->toMatch("Error\(") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res deleted file mode 100644 index 329f30bc..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res +++ /dev/null @@ -1,89 +0,0 @@ -open Jest -open Expect - -module Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer -module Expression = Reducer_Expression -module ExpressionWithContext = Reducer_ExpressionWithContext -module InternalExpressionValue = ReducerInterface.InternalExpressionValue -module Macro = Reducer_Expression_Macro -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module T = Reducer_Expression_T - -let testMacro_ = ( - tester, - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedCode: string, -) => { - let bindings = Bindings.fromArray(bindArray) - tester(expr->T.toString, () => - expr - ->Macro.expandMacroCallRs( - bindings, - ProjectAccessorsT.identityAccessors, - Expression.reduceExpressionInProject, - ) - ->ExpressionWithContext.toStringResult - ->expect - ->toEqual(expectedCode) - ) -} - -let testMacroEval_ = ( - tester, - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedValue: string, -) => { - let bindings = Bindings.fromArray(bindArray) - tester(expr->T.toString, () => - expr - ->Macro.doMacroCall( - bindings, - ProjectAccessorsT.identityAccessors, - Expression.reduceExpressionInProject, - ) - ->Ok - ->InternalExpressionValue.toStringResult - ->expect - ->toEqual(expectedValue) - ) -} - -let testMacro = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedExpr: string, -) => testMacro_(test, bindArray, expr, expectedExpr) -let testMacroEval = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedValue: string, -) => testMacroEval_(test, bindArray, expr, expectedValue) - -module MySkip = { - let testMacro = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedExpr: string, - ) => testMacro_(Skip.test, bindArray, expr, expectedExpr) - let testMacroEval = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedValue: string, - ) => testMacroEval_(Skip.test, bindArray, expr, expectedValue) -} - -module MyOnly = { - let testMacro = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedExpr: string, - ) => testMacro_(Only.test, bindArray, expr, expectedExpr) - let testMacroEval = ( - bindArray: array<(string, InternalExpressionValue.t)>, - expr: T.expression, - expectedValue: string, - ) => testMacroEval_(Only.test, bindArray, expr, expectedValue) -} diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res deleted file mode 100644 index 55ed36bd..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_Compile_test.res +++ /dev/null @@ -1,52 +0,0 @@ -module Expression = Reducer_Expression -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module Bindings = Reducer_Bindings -module T = Reducer_Type_T -module TypeCompile = Reducer_Type_Compile - -open Jest -open Expect - -let myIevEval = (aTypeSourceCode: string) => - TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject) -let myIevEvalToString = (aTypeSourceCode: string) => - myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult - -let myIevExpectEqual = (aTypeSourceCode, answer) => - expect(myIevEvalToString(aTypeSourceCode))->toEqual(answer) - -let myIevTest = (test, aTypeSourceCode, answer) => - test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer)) - -let myTypeEval = (aTypeSourceCode: string) => - TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject) -let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult - -let myTypeExpectEqual = (aTypeSourceCode, answer) => - expect(myTypeEvalToString(aTypeSourceCode))->toEqual(answer) - -let myTypeTest = (test, aTypeSourceCode, answer) => - test(aTypeSourceCode, () => myTypeExpectEqual(aTypeSourceCode, answer)) - -// | ItTypeIdentifier(string) -myTypeTest(test, "number", "number") -myTypeTest(test, "(number)", "number") -// | ItModifiedType({modifiedType: iType}) -myIevTest(test, "number<-min(0)", "Ok({min: 0,typeIdentifier: #number,typeTag: 'typeIdentifier'})") -myTypeTest(test, "number<-min(0)", "number<-min(0)") -// | ItTypeOr({typeOr: array}) -myTypeTest(test, "number | string", "(number | string)") -// | ItTypeFunction({inputs: array, output: iType}) -myTypeTest(test, "number => number => number", "(number => number => number)") -// | ItTypeArray({element: iType}) -myIevTest(test, "[number]", "Ok({element: #number,typeTag: 'typeArray'})") -myTypeTest(test, "[number]", "[number]") -// | ItTypeTuple({elements: array}) -myTypeTest(test, "[number, string]", "[number, string]") -// | ItTypeRecord({properties: Belt.Map.String.t}) -myIevTest( - test, - "{age: number, name: string}", - "Ok({properties: {age: #number,name: #string},typeTag: 'typeRecord'})", -) -myTypeTest(test, "{age: number, name: string}", "{age: number, name: string}") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res deleted file mode 100644 index 957024cb..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_arguments_test.res +++ /dev/null @@ -1,42 +0,0 @@ -module Bindings = Reducer_Bindings -module ErrorValue = Reducer_ErrorValue -module Expression = Reducer_Expression -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module T = Reducer_Type_T -module TypeChecker = Reducer_Type_TypeChecker - -open Jest -open Expect - -let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): result< - 'v, - ErrorValue.t, -> => { - let reducerFn = Expression.reduceExpressionInProject - let rResult = - Expression.BackCompatible.parse(sourceCode)->Belt.Result.map(expr => - reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors) - ) - rResult->Belt.Result.flatMap(result => - switch result { - | IEvArray(args) => TypeChecker.checkArguments(aTypeSourceCode, args, reducerFn) - | _ => Js.Exn.raiseError("Arguments has to be an array") - } - ) -} - -let myCheckArguments = (aTypeSourceCode: string, sourceCode: string): string => - switch checkArgumentsSourceCode(aTypeSourceCode, sourceCode) { - | Ok(_) => "Ok" - | Error(error) => ErrorValue.errorToString(error) - } - -let myCheckArgumentsExpectEqual = (aTypeSourceCode, sourceCode, answer) => - expect(myCheckArguments(aTypeSourceCode, sourceCode))->toEqual(answer) - -let myCheckArgumentsTest = (test, aTypeSourceCode, sourceCode, answer) => - test(aTypeSourceCode, () => myCheckArgumentsExpectEqual(aTypeSourceCode, sourceCode, answer)) - -myCheckArgumentsTest(test, "number=>number=>number", "[1,2]", "Ok") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res deleted file mode 100644 index d5726d5d..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_TypeChecker_test.res +++ /dev/null @@ -1,73 +0,0 @@ -module Expression = Reducer_Expression -module ExpressionT = Reducer_Expression_T -module ErrorValue = Reducer_ErrorValue -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module Bindings = Reducer_Bindings -module T = Reducer_Type_T -module TypeChecker = Reducer_Type_TypeChecker -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T - -open Jest -open Expect - -// In development, you are expected to use TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn). -// isTypeOfSourceCode is written to use strings instead of expression values. - -let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result< - 'v, - ErrorValue.t, -> => { - let reducerFn = Expression.reduceExpressionInProject - let rResult = - Expression.BackCompatible.parse(sourceCode)->Belt.Result.map(expr => - reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors) - ) - rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn)) -} - -let myTypeCheck = (aTypeSourceCode: string, sourceCode: string): string => - switch isTypeOfSourceCode(aTypeSourceCode, sourceCode) { - | Ok(_) => "Ok" - | Error(error) => ErrorValue.errorToString(error) - } - -let myTypeCheckExpectEqual = (aTypeSourceCode, sourceCode, answer) => - expect(myTypeCheck(aTypeSourceCode, sourceCode))->toEqual(answer) - -let myTypeCheckTest = (test, aTypeSourceCode, sourceCode, answer) => - test(aTypeSourceCode, () => myTypeCheckExpectEqual(aTypeSourceCode, sourceCode, answer)) - -myTypeCheckTest(test, "number", "1", "Ok") -myTypeCheckTest(test, "number", "'2'", "Expected type: number but got: '2'") -myTypeCheckTest(test, "string", "3", "Expected type: string but got: 3") -myTypeCheckTest(test, "string", "'a'", "Ok") -myTypeCheckTest(test, "[number]", "[1,2,3]", "Ok") -myTypeCheckTest(test, "[number]", "['a','a','a']", "Expected type: number but got: 'a'") -myTypeCheckTest(test, "[number]", "[1,'a',3]", "Expected type: number but got: 'a'") -myTypeCheckTest(test, "[number, string]", "[1,'a']", "Ok") -myTypeCheckTest(test, "[number, string]", "[1, 2]", "Expected type: string but got: 2") -myTypeCheckTest( - test, - "[number, string, string]", - "[1,'a']", - "Expected type: [number, string, string] but got: [1,'a']", -) -myTypeCheckTest( - test, - "[number, string]", - "[1,'a', 3]", - "Expected type: [number, string] but got: [1,'a',3]", -) -myTypeCheckTest(test, "{age: number, name: string}", "{age: 1, name: 'a'}", "Ok") -myTypeCheckTest( - test, - "{age: number, name: string}", - "{age: 1, name: 'a', job: 'IT'}", - "Expected type: {age: number, name: string} but got: {age: 1,job: 'IT',name: 'a'}", -) -myTypeCheckTest(test, "number | string", "1", "Ok") -myTypeCheckTest(test, "date | string", "1", "Expected type: (date | string) but got: 1") -myTypeCheckTest(test, "number<-min(10)", "10", "Ok") -myTypeCheckTest(test, "number<-min(10)", "0", "Expected type: number<-min(10) but got: 0") -myTypeCheckTest(test, "any", "0", "Ok") -myTypeCheckTest(test, "any", "'a'", "Ok") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_switch_replacement_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_switch_replacement_test.res deleted file mode 100644 index 9dbe08c5..00000000 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Type/Reducer_Type_switch_replacement_test.res +++ /dev/null @@ -1,127 +0,0 @@ -open Jest -open Expect - -module DispatchT = Reducer_Dispatch_T -module Expression = Reducer_Expression -module ExpressionT = Reducer_Expression_T -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module TypeChecker = Reducer_Type_TypeChecker -module TypeCompile = Reducer_Type_Compile - -open ReducerInterface_InternalExpressionValue - -type errorValue = Reducer_ErrorValue.errorValue - -// Let's build a function to replace switch statements -// In dispatchChainPiece, we execute an return the result of execution if there is a type match. -// Otherwise we return None so that the call chain can continue. -// So we want to build a function like -// dispatchChainPiece = (call: functionCall, accessors): option> -// Use accessors.environment to get the environment finally. - -// Now lets make the dispatchChainPiece itself. -// Note that I am not passing the reducer to the dispatchChainPiece as an argument because it is in the context anyway. -// Keep in mind that reducerFn is necessary for map/reduce so dispatchChainPiece should have a reducerFn in context. - -let makeMyDispatchChainPiece = (reducer: ProjectReducerFnT.t): DispatchT.dispatchChainPiece => { - // Let's have a pure implementations - module Implementation = { - let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b) - let arrayConcat = ( - a: Js.Array2.t, - b: Js.Array2.t, - ): Js.Array2.t => Js.Array2.concat(a, b) - let plot = _r => "yey, plotted" - } - - let extractStringString = args => - switch args { - | [IEvString(a), IEvString(b)] => (a, b) - | _ => raise(Reducer_Exception.ImpossibleException("extractStringString developer error")) - } - - let extractArrayArray = args => - switch args { - | [IEvArray(a), IEvArray(b)] => (a, b) - | _ => raise(Reducer_Exception.ImpossibleException("extractArrayArray developer error")) - } - - // Let's bridge the pure implementation to expression values - module Bridge = { - let stringConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => { - let (a, b) = extractStringString(args) - Implementation.stringConcat(a, b)->IEvString->Ok - } - let arrayConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => { - let (a, b) = extractArrayArray(args) - Implementation.arrayConcat(a, b)->IEvArray->Ok - } - let plot: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => { - switch args { - // Just assume that we are doing the business of extracting and converting the deep record - | [IEvRecord(_)] => Implementation.plot({"title": "This is a plot"})->IEvString->Ok - | _ => raise(Reducer_Exception.ImpossibleException("plot developer error")) - } - } - } - - // concat functions are to illustrate polymoprhism. And the plot function is to illustrate complex types - let jumpTable = [ - ( - "concat", - TypeCompile.fromTypeExpressionExn("string=>string=>string", reducer), - Bridge.stringConcat, - ), - ( - "concat", - TypeCompile.fromTypeExpressionExn("[any]=>[any]=>[any]", reducer), - Bridge.arrayConcat, - ), - ( - "plot", - TypeCompile.fromTypeExpressionExn( - // Nested complex types are available - // records {property: type} - // arrays [type] - // tuples [type, type] - // <- type contracts are available naturally and they become part of dispatching - // Here we are not enumerating the possibilities because type checking has a dedicated test - "{title: string, line: {width: number, color: string}}=>string", - reducer, - ), - Bridge.plot, - ), - ] - - //Here we are creating a dispatchChainPiece function that will do the actual dispatch from the jumpTable - Reducer_Dispatch_ChainPiece.makeFromTypes(jumpTable) -} - -// And finally, let's write a library dispatch for our external library -// Exactly the same as the one used in real life -let _dispatch = ( - call: functionCall, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, - chain, -): result => { - let dispatchChainPiece = makeMyDispatchChainPiece(reducer) - dispatchChainPiece(call, accessors)->E.O2.defaultFn(() => chain(call, accessors, reducer)) -} - -// What is important about this implementation? -// A) Exactly the same function jump table can be used to create type guarded lambda functions -// Guarded lambda functions will be the basis of the next version of Squiggle -// B) Complicated recursive record types are not a problem. - -describe("Type Dispatch", () => { - let reducerFn = Expression.reduceExpressionInProject - let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn) - test("stringConcat", () => { - let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")]) - - let result = dispatchChainPiece(call, ProjectAccessorsT.identityAccessors) - expect(result)->toEqual(Some(Ok(IEvString("helloworld")))) - }) -}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Value_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Value_test.res new file mode 100644 index 00000000..9b1558c1 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Value_test.res @@ -0,0 +1,14 @@ +open Jest +open Expect + +describe("ExpressionValue", () => { + test("argsToString", () => + expect([IEvNumber(1.), IEvString("a")]->Reducer_Value.argsToString)->toBe("1,'a'") + ) + + test("toStringFunctionCall", () => + expect(("fn", [IEvNumber(1.), IEvString("a")])->Reducer_Value.toStringFunctionCall)->toBe( + "fn(1,'a')", + ) + ) +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res index 205d4883..55ffff27 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_constructArray_test.res @@ -2,11 +2,11 @@ open Jest open Reducer_Peggy_TestHelpers describe("Construct Array", () => { - testToExpression("[1,2]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ 1 2))}", ~v="[1,2]", ()) - testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$))}", ~v="[]", ()) + testToExpression("[1,2]", "[1, 2]", ~v="[1,2]", ()) + testToExpression("[]", "[]", ~v="[]", ()) testToExpression( "f(x)=x; g(x)=x; [f, g]", - "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_let_$ :g (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () (:$_constructArray_$ :f :g))}", + "f = {|x| {x}}; g = {|x| {x}}; [f, g]", ~v="[lambda(x=>internal code),lambda(x=>internal code)]", (), ) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res index a2abb85d..95f9b4c8 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res @@ -2,17 +2,15 @@ open Jest open Reducer_TestHelpers describe("Parse function assignment", () => { - testParseToBe( - "f(x)=x", - "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})", - ) - testParseToBe( - "f(x)=2*x", - "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})", - ) + testParseToBe("f(x)=x", "Ok(f = {|x| {x}})") + testParseToBe("f(x)=2*x", "Ok(f = {|x| {(multiply)(2, x)}})") //MathJs does not allow blocks in function definitions }) describe("Evaluate function assignment", () => { testEvalToBe("f(x)=x; f(1)", "Ok(1)") }) + +describe("Shadowing", () => { + testEvalToBe("x = 5; f(y) = x*y; x = 6; f(2)", "Ok(10)") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res index 9b111354..14f7f380 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -34,7 +34,7 @@ describe("symbol not defined", () => { testEvalToBe("f(x)=x(y); f(f)", "Error(y is not defined)") testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))") testEvalToBe("f(x)=x(y); f(z)", "Error(z is not defined)") - testEvalToBe("f(x)=x(y); f(2)", "Error(2 is not a function)") + testEvalToBe("f(x)=x(y); f(2)", "Error(y is not defined)") testEvalToBe("f(x)=x(1); f(2)", "Error(2 is not a function)") }) @@ -46,10 +46,7 @@ describe("call and bindings", () => { testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)") testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)") testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)") - testParseToBe( - "f=99; g(x)=f; g(2)", - "Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:$_endOfOuterBlock_$ () (:g 2))})", - ) + testParseToBe("f=99; g(x)=f; g(2)", "Ok(f = {99}; g = {|x| {f}}; (g)(2))") testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)") testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)") testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2); y", "Ok(4)") diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res index f89173d5..d75004d5 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_mapReduce_test.res @@ -5,3 +5,7 @@ Skip.describe("map reduce (sam)", () => { testEvalToBe("addone(x)=x+1; map(2, addone)", "Error???") testEvalToBe("addone(x)=x+1; map(2, {x: addone})", "Error???") }) + +describe("map", () => { + testEvalToBe("arr=[1,2,3]; map(arr, {|x| x*2})", "Ok([2,4,6])") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index c86f4bca..e157ccbf 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -2,10 +2,7 @@ open Jest open Reducer_TestHelpers describe("Parse ternary operator", () => { - testParseToBe( - "true ? 'YES' : 'NO'", - "Ok({(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 'YES' 'NO'))})", - ) + testParseToBe("true ? 'YES' : 'NO'", "Ok(true ? ('YES') : ('NO'))") }) describe("Evaluate ternary operator", () => { diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res index f6b9ad77..49180135 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res @@ -2,19 +2,29 @@ open Jest open Reducer_TestHelpers describe("eval", () => { - // All MathJs operators and functions are builtin for string, float and boolean - // .e.g + - / * > >= < <= == /= not and or - // See https://mathjs.org/docs/reference/functions.html describe("expressions", () => { testEvalToBe("1", "Ok(1)") + testEvalToBe("-1", "Ok(-1)") + testEvalToBe("1-1", "Ok(0)") testEvalToBe("1+2", "Ok(3)") testEvalToBe("(1+2)*3", "Ok(9)") testEvalToBe("2>1", "Ok(true)") testEvalToBe("concat('a ', 'b')", "Ok('a b')") + testEvalToBe("concat([3,4], [5,6,7])", "Ok([3,4,5,6,7])") testEvalToBe("log(10)", "Ok(2.302585092994046)") - testEvalToBe("cos(10)", "Ok(-0.8390715290764524)") + testEvalToBe("Math.cos(10)", "Ok(-0.8390715290764524)") // TODO more built ins }) + + describe("missing function", () => { + testEvalToBe("testZadanga(1)", "Error(testZadanga is not defined)") + + testEvalToBe( + "arr = [normal(3,2)]; map(arr, zarathsuzaWasHere)", + "Error(zarathsuzaWasHere is not defined)", + ) + }) + describe("arrays", () => { test("empty array", () => expectEvalToBe("[]", "Ok([])")) testEvalToBe("[1, 2, 3]", "Ok([1,2,3])") @@ -50,6 +60,10 @@ describe("eval", () => { testEvalError("1; 1") testEvalToBe("x=1; x=1; x", "Ok(1)") }) + + describe("blocks", () => { + testEvalToBe("x = { y = { z = 5; z * 2 }; y + 3 }; x", "Ok(13)") + }) }) describe("test exceptions", () => { diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res deleted file mode 100644 index 71fd9f61..00000000 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res +++ /dev/null @@ -1,11 +0,0 @@ -open ReducerInterface.InternalExpressionValue -open Jest -open Expect - -describe("ExpressionValue", () => { - test("argsToString", () => expect([IEvNumber(1.), IEvString("a")]->argsToString)->toBe("1,'a'")) - - test("toStringFunctionCall", () => - expect(("fn", [IEvNumber(1.), IEvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')") - ) -}) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res index d2dbafb3..07660044 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_includes_test.res @@ -1,7 +1,5 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject -module Bindings = Reducer_Bindings open Jest open Expect @@ -30,12 +28,11 @@ x=1`, | Error(error) => fail(error->Reducer_ErrorValue.errorToString) } }) - let internalProject = project->Project.T.Private.castToInternalProject test("past chain", () => { - expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] + expect(project->Project.getPastChain("main")) == ["common"] }) test("import as variables", () => { - expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [] + expect(project->Project.Private.getIncludesAsVariables("main")) == [] }) }) @@ -67,20 +64,16 @@ x=1`, } }) - let internalProject = project->Project.T.Private.castToInternalProject - test("direct past chain", () => { - expect(Project.Private.getPastChain(internalProject, "main")) == ["common"] + expect(project->Project.Private.getPastChain("main")) == ["common"] }) test("direct includes", () => { - expect(Project.Private.getDirectIncludes(internalProject, "main")) == ["common"] + expect(project->Project.Private.getDirectIncludes("main")) == ["common"] }) test("include as variables", () => { - expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ - ("myVariable", "myModule"), - ] + expect(project->Project.Private.getIncludesAsVariables("main")) == [("myVariable", "myModule")] }) }) @@ -109,13 +102,10 @@ x=1`, | Error(error) => fail(error->Reducer_ErrorValue.errorToString) } }) - let internalProject = project->Project.T.Private.castToInternalProject test("direct past chain", () => { expect(Project.getPastChain(project, "main")) == ["common", "common2"] }) test("include as variables", () => { - expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [ - ("myVariable", "myModule"), - ] + expect(project->Project.Private.getIncludesAsVariables("main")) == [("myVariable", "myModule")] }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res index 1dd215b9..aee917a3 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_test.res @@ -1,5 +1,4 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject module Bindings = Reducer_Bindings @@ -7,29 +6,16 @@ open Jest open Expect open Expect.Operators -// test("", () => expect(1)->toBe(1)) - let runFetchResult = (project, sourceId) => { Project.run(project, sourceId) - Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult + Project.getResult(project, sourceId)->Reducer_Value.toStringResult } let runFetchFlatBindings = (project, sourceId) => { Project.run(project, sourceId) - Project.getBindings(project, sourceId) - ->Bindings.removeResult - ->InternalExpressionValue.toStringBindings + Project.getBindings(project, sourceId)->Reducer_Value.toStringRecord } -test("setting continuation", () => { - let project = Project.createProject() - let privateProject = project->Project.T.Private.castToInternalProject - let sampleBindings = Bindings.emptyBindings->Bindings.set("test", IEvVoid) - Project.Private.setContinuation(privateProject, "main", sampleBindings) - let answer = Project.Private.getContinuation(privateProject, "main") - expect(answer)->toBe(sampleBindings) -}) - test("test result true", () => { let project = Project.createProject() Project.setSource(project, "main", "true") @@ -51,7 +37,7 @@ test("test library", () => { test("test bindings", () => { let project = Project.createProject() Project.setSource(project, "variables", "myVariable=666") - runFetchFlatBindings(project, "variables")->expect->toBe("@{myVariable: 666}") + runFetchFlatBindings(project, "variables")->expect->toBe("{myVariable: 666}") }) describe("project1", () => { @@ -59,7 +45,6 @@ describe("project1", () => { Project.setSource(project, "first", "x=1") Project.setSource(project, "main", "x") Project.setContinues(project, "main", ["first"]) - let internalProject = project->Project.T.Private.castToInternalProject test("runOrder", () => { expect(Project.getRunOrder(project)) == ["first", "main"] @@ -78,17 +63,17 @@ describe("project1", () => { }) test("past chain first", () => { - expect(Project.Private.getPastChain(internalProject, "first")) == [] + expect(ReducerProject.getPastChain(project, "first")) == [] }) test("past chain main", () => { - expect(Project.Private.getPastChain(internalProject, "main")) == ["first"] + expect(ReducerProject.getPastChain(project, "main")) == ["first"] }) test("test result", () => { runFetchResult(project, "main")->expect->toBe("Ok(1)") }) test("test bindings", () => { - runFetchFlatBindings(project, "first")->expect->toBe("@{x: 1}") + runFetchFlatBindings(project, "first")->expect->toBe("{x: 1}") }) }) @@ -98,7 +83,7 @@ describe("project2", () => { Project.setContinues(project, "second", ["first"]) Project.setSource(project, "first", "x=1") Project.setSource(project, "second", "y=2") - Project.setSource(project, "main", "y") + Project.setSource(project, "main", "z=3;y") test("runOrder", () => { expect(Project.getRunOrder(project)) == ["first", "second", "main"] @@ -122,7 +107,8 @@ describe("project2", () => { runFetchResult(project, "main")->expect->toBe("Ok(2)") }) test("test bindings", () => { - runFetchFlatBindings(project, "main")->expect->toBe("@{x: 1,y: 2}") + // bindings from continues are not exposed! + runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}") }) }) @@ -152,7 +138,7 @@ describe("project with include", () => { ) Project.parseIncludes(project, "second") //The only way of setting includes - Project.setSource(project, "main", "y") + Project.setSource(project, "main", "z=3; y") test("runOrder", () => { expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"] @@ -178,7 +164,8 @@ describe("project with include", () => { runFetchResult(project, "main")->expect->toBe("Ok(2)") }) test("test bindings", () => { - runFetchFlatBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}") + // bindings from continues are not exposed! + runFetchFlatBindings(project, "main")->expect->toBe("{z: 3}") }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res index 92a4932f..3c67fe4e 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_1_test.res @@ -1,5 +1,4 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject module Bindings = Reducer_Bindings @@ -16,13 +15,13 @@ Case "Running a single source". /* Let's start with running a single source and getting Result as well as the Bindings First you need to create a project. A project is a collection of sources. Project takes care of the dependencies between the sources, correct compilation and run order. - You can run any source in the project. It will be compiled and run if it is not already done else already existing results will be presented. + You can run any source in the project. It will be compiled and run if it hasn't happened already; otherwise already existing results will be presented. The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project. In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source. */ let project = Project.createProject() /* Every source has a name. This is used for debugging, dependencies and error messages. */ - Project.setSource(project, "main", "1 + 2") + project->Project.setSource("main", "1 + 2") /* Let's run "main" source. */ project->Project.run("main") /* Now you have a result for "main" source. @@ -46,27 +45,25 @@ Case "Running a single source". Getting None means you have forgotten to run the source. */ let result = project->Project.getResult("main") - let bindings = project->Project.getBindings("main")->Bindings.removeResult + let bindings = project->Project.getBindings("main") /* Let's display the result and bindings */ - ( - result->InternalExpressionValue.toStringResult, - bindings->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(3)", "@{}") + (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect == + ("Ok(3)", "{}") /* You've got 3 with empty bindings. */ }) test("run summary", () => { let project = Project.createProject() - Project.setSource(project, "main", "1 + 2") - Project.runAll(project) - let result = Project.getResult(project, "main") - let bindings = Project.getBindings(project, "main")->Bindings.removeResult + project->Project.setSource("main", "1 + 2") + project->Project.runAll + let result = project->Project.getResult("main") + let bindings = project->Project.getBindings("main") /* Now you have external bindings and external result. */ ( - result->InternalExpressionValue.toStringResult, - bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString, - )->expect == ("Ok(3)", "@{}") + result->Reducer_Value.toStringResult, + bindings->Reducer_T.IEvRecord->Reducer_Value.toString, + )->expect == ("Ok(3)", "{}") }) test("run with an environment", () => { @@ -74,23 +71,21 @@ Case "Running a single source". let project = Project.createProject() /* Optional. Set your custom environment anytime before running */ - Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment) + project->Project.setEnvironment(Reducer_Context.defaultEnvironment) - Project.setSource(project, "main", "1 + 2") - Project.runAll(project) - let result = Project.getResult(project, "main") - let _bindings = Project.getBindings(project, "main") - result->InternalExpressionValue.toStringResult->expect == "Ok(3)" + project->Project.setSource("main", "1 + 2") + project->Project.runAll + let result = project->Project.getResult("main") + let _bindings = project->Project.getBindings("main") + result->Reducer_Value.toStringResult->expect == "Ok(3)" }) test("shortcut", () => { /* If you are running single source without includes and you don't need a custom environment, you can use the shortcut. */ /* Examples above was to prepare you for the multi source tutorial. */ let (result, bindings) = Project.evaluate("1+2") - ( - result->InternalExpressionValue.toStringResult, - bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(3)", "@{}") + (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect == + ("Ok(3)", "{}") }) }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res index 80c73c9e..a31b9811 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_2_multisource_test.res @@ -1,5 +1,4 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject module Bindings = Reducer_Bindings @@ -14,27 +13,25 @@ describe("ReducerProject Tutorial", () => { test("Chaining", () => { let project = Project.createProject() /* This time let's add 3 sources and chain them together */ - Project.setSource(project, "source1", "x=1") + project->Project.setSource("source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source2", "y=x+1") /* To run, source2 depends on source1 */ - Project.setContinues(project, "source2", ["source1"]) + project->Project.setContinues("source2", ["source1"]) - Project.setSource(project, "source3", "z=3") + project->Project.setSource("source3", "z=y+1") /* To run, source3 depends on source2 */ - Project.setContinues(project, "source3", ["source2"]) + project->Project.setContinues("source3", ["source2"]) /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") - ( - result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + (result3->Reducer_Value.toStringResult, bindings3->Reducer_Value.toStringRecord)->expect == + ("Ok(())", "{z: 3}") }) test("Depending", () => { @@ -43,24 +40,22 @@ describe("ReducerProject Tutorial", () => { let project = Project.createProject() /* This time source1 and source2 are not depending on anything */ - Project.setSource(project, "source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source1", "x=1") + project->Project.setSource("source2", "y=2") - Project.setSource(project, "source3", "z=3") + project->Project.setSource("source3", "z=x+y") /* To run, source3 depends on source1 and source3 together */ - Project.setContinues(project, "source3", ["source1", "source2"]) + project->Project.setContinues("source3", ["source1", "source2"]) /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") - ( - result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + (result3->Reducer_Value.toStringResult, bindings3->Reducer_Value.toStringRecord)->expect == + ("Ok(())", "{z: 3}") }) test("Intro to including", () => { @@ -70,33 +65,30 @@ describe("ReducerProject Tutorial", () => { let project = Project.createProject() /* This time source1 and source2 are not depending on anything */ - Project.setSource(project, "source1", "x=1") - Project.setSource(project, "source2", "y=2") + project->Project.setSource("source1", "x=1") + project->Project.setSource("source2", "y=2") - Project.setSource( - project, + project->Project.setSource( "source3", ` #include "source1" #include "source2" - z=3`, + z=x+y`, ) /* We need to parse the includes to set the dependencies */ - Project.parseIncludes(project, "source3") + project->Project.parseIncludes("source3") /* Now we can run the project */ - Project.runAll(project) + project->Project.runAll /* And let's check the result and bindings of source3 This time you are getting all the variables because we are including the other sources Behind the scenes parseIncludes is setting the dependencies */ - let result3 = Project.getResult(project, "source3") - let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult + let result3 = project->Project.getResult("source3") + let bindings3 = project->Project.getBindings("source3") - ( - result3->InternalExpressionValue.toStringResult, - bindings3->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}") + (result3->Reducer_Value.toStringResult, bindings3->Reducer_Value.toStringRecord)->expect == + ("Ok(())", "{z: 3}") /* Doing it like this is too verbose for a storybook But I hope you have seen the relation of setContinues and parseIncludes */ diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res index 12c14468..2c63dfff 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_3_includes_test.res @@ -1,5 +1,4 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject module Bindings = Reducer_Bindings @@ -16,8 +15,7 @@ Here we will finally proceed to a real life scenario. */ /* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */ /* Everything happens inside a project, so let's have a project */ let project = Project.createProject() - Project.setSource( - project, + project->Project.setSource( "main", ` #include "common" @@ -25,10 +23,10 @@ Here we will finally proceed to a real life scenario. */ `, ) /* We need to parse includes after changing the source */ - Project.parseIncludes(project, "main") + project->Project.parseIncludes("main") test("getDependencies", () => { /* Parse includes has set the dependencies */ - Project.getDependencies(project, "main")->expect == ["common"] + project->Project.getDependencies("main")->expect == ["common"] /* If there were no includes than there would be no dependencies */ /* However if there was a syntax error at includes then would be no dependencies also */ /* Therefore looking at dependencies is not the right way to load includes */ @@ -36,7 +34,7 @@ Here we will finally proceed to a real life scenario. */ }) test("getIncludes", () => { /* Parse includes has set the includes */ - switch Project.getIncludes(project, "main") { + switch project->Project.getIncludes("main") { | Ok(includes) => includes->expect == ["common"] | Error(err) => err->Reducer_ErrorValue.errorToString->fail } @@ -50,7 +48,7 @@ Here we will finally proceed to a real life scenario. */ include or depend on the current source. But you don't need to use this to execute the projects. It is provided for completeness of information. */ - Project.getDependents(project, "main")->expect == [] + project->Project.getDependents("main")->expect == [] /* Nothing is depending on or including main */ }) @@ -76,29 +74,29 @@ Here we will finally proceed to a real life scenario. */ /* let's recursively load the sources */ let rec loadIncludesRecursively = (project, sourceName, visited) => { - if Js.Array2.includes(visited, sourceName) { + if visited->Js.Array2.includes(sourceName) { /* Oh we have already visited this source. There is an include cycle */ "Cyclic include ${sourceName}"->Js.Exn.raiseError } else { let newVisited = Js.Array2.copy(visited) - let _ = Js.Array2.push(newVisited, sourceName) + let _ = newVisited->Js.Array2.push(sourceName) /* Let's parse the includes and dive into them */ Project.parseIncludes(project, sourceName) - let rIncludes = Project.getIncludes(project, sourceName) + let rIncludes = project->Project.getIncludes(sourceName) switch rIncludes { /* Maybe there is an include syntax error */ | Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError | Ok(includes) => - Belt.Array.forEach(includes, newIncludeName => { + includes->Belt.Array.forEach(newIncludeName => { /* We have got one of the new includes. Let's load it and add it to the project */ let newSource = loadSource(newIncludeName) - Project.setSource(project, newIncludeName, newSource) + project->Project.setSource(newIncludeName, newSource) /* The new source is loaded and added to the project. */ /* Of course the new source might have includes too. */ /* Let's recursively load them */ - loadIncludesRecursively(project, newIncludeName, newVisited) + project->loadIncludesRecursively(newIncludeName, newVisited) }) } } @@ -110,45 +108,44 @@ Here we will finally proceed to a real life scenario. */ let project = Project.createProject() - /* main includes source3 which includes source2 which includes source1 */ - Project.setSource( - project, + project->Project.setSource( "main", ` + #include "source1" + #include "source2" #include "source3" - x+y+z + a = x+y+z + b = doubleX + a `, ) /* Setting source requires parsing and loading the includes recursively */ - loadIncludesRecursively(project, "main", []) //No visited yet + project->loadIncludesRecursively("main", []) // Not visited yet /* Let's salt it more. Let's have another source in the project which also has includes */ /* doubleX includes source1 which is eventually included by main as well */ - Project.setSource( - project, + project->Project.setSource( "doubleX", ` #include "source1" doubleX = x * 2 `, ) - loadIncludesRecursively(project, "doubleX", []) + project->loadIncludesRecursively("doubleX", []) /* Remember, any time you set a source, you need to load includes recursively */ /* As doubleX is not included by main, it is not loaded recursively. So we link it to the project as a dependency */ - Project.setContinues(project, "main", ["doubleX"]) + project->Project.setContinues("main", ["doubleX"]) /* Let's run the project */ - Project.runAll(project) - let result = Project.getResult(project, "main") - let bindings = Project.getBindings(project, "main") + project->Project.runAll + let result = project->Project.getResult("main") + let bindings = project->Project.getBindings("main") /* And see the result and bindings.. */ test("recursive includes", () => { - ( - result->InternalExpressionValue.toStringResult, - bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings, - )->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}") + (result->Reducer_Value.toStringResult, bindings->Reducer_Value.toStringRecord)->expect == + ("Ok(6)", "{a: 6,b: 2}") /* Everything as expected */ }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_4_injecting_user_values.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_4_injecting_user_values.res index 38d655c1..507e12cc 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_4_injecting_user_values.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_4_injecting_user_values.res @@ -1,7 +1,5 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject -module Bindings = Reducer_Bindings open Jest open Expect @@ -30,7 +28,7 @@ describe("ReducerProject Tutorial", () => { /* We can now run the project */ Project.runAll(project) let result = Project.getResult(project, "main") - result->InternalExpressionValue.toStringResult->expect == "Ok(6)" + result->Reducer_Value.toStringResult->expect == "Ok(6)" }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_5_calling_functions.res b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_5_calling_functions.res index 7e2471b5..35643c77 100644 --- a/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_5_calling_functions.res +++ b/packages/squiggle-lang/__tests__/ReducerProject/ReducerProject_tutorial_5_calling_functions.res @@ -1,5 +1,4 @@ @@warning("-44") -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Project = ForTS_ReducerProject module Bindings = Reducer_Bindings @@ -32,7 +31,7 @@ describe("ReducerProject Tutorial", () => { test("userResults", () => { let userResultsAsString = Belt.Array.map(userResults, aResult => - aResult->InternalExpressionValue.toStringResult + aResult->Reducer_Value.toStringResult ) userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"] }) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Distribution_test.res similarity index 82% rename from packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res rename to packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Distribution_test.res index 46ed4c42..7328bcc1 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_Distribution_test.res @@ -23,7 +23,7 @@ describe("eval on distribution functions", () => { testEval("-normal(5,2)", "Ok(Normal(-5,2))") }) describe("to", () => { - testEval("5 to 2", "Error(TODO: Low value must be less than high value.)") + testEval("5 to 2", "Error(Error: Low value must be less than high value.)") testEval("to(2,5)", "Ok(Lognormal(1.1512925464970227,0.27853260523016377))") testEval("to(-2,2)", "Ok(Normal(0,1.2159136638235384))") }) @@ -120,40 +120,28 @@ describe("eval on distribution functions", () => { describe("parse on distribution functions", () => { describe("power", () => { - testParse( - "normal(5,2) ^ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})", - ) - testParse("3 ^ normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:pow 3 (:normal 5 1)))})") - testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})") + testParse("normal(5,2) ^ normal(5,1)", "Ok((pow)((normal)(5, 2), (normal)(5, 1)))") + testParse("3 ^ normal(5,1)", "Ok((pow)(3, (normal)(5, 1)))") + testParse("normal(5,2) ^ 3", "Ok((pow)((normal)(5, 2), 3))") }) describe("subtraction", () => { - testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})") - testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})") + testParse("10 - normal(5,1)", "Ok((subtract)(10, (normal)(5, 1)))") + testParse("normal(5,1) - 10", "Ok((subtract)((normal)(5, 1), 10))") }) describe("pointwise arithmetic expressions", () => { testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") testParse( ~skip=true, "normal(5,2) .- normal(5,1)", - "Ok((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))", + "Ok((:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1))))", // TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})" ) - testParse( - "normal(5,2) .* normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})", - ) - testParse( - "normal(5,2) ./ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})", - ) - testParse( - "normal(5,2) .^ normal(5,1)", - "Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})", - ) + testParse("normal(5,2) .* normal(5,1)", "Ok((dotMultiply)((normal)(5, 2), (normal)(5, 1)))") + testParse("normal(5,2) ./ normal(5,1)", "Ok((dotDivide)((normal)(5, 2), (normal)(5, 1)))") + testParse("normal(5,2) .^ normal(5,1)", "Ok((dotPow)((normal)(5, 2), (normal)(5, 1)))") }) describe("equality", () => { - testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})") + testParse("5 == normal(5,2)", "Ok((equal)(5, (normal)(5, 2)))") }) describe("pointwise adding two normals", () => { testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))") diff --git a/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res index 831eb271..af2c3d7a 100644 --- a/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res +++ b/packages/squiggle-lang/__tests__/SquiggleLibrary/SquiggleLibrary_FunctionRegistryLibrary_test.res @@ -3,11 +3,7 @@ open Expect open Reducer_TestHelpers let expectEvalToBeOk = (code: string) => - Reducer_Expression.BackCompatible.evaluateString(code) - ->Reducer_Helpers.rRemoveDefaultsInternal - ->E.R.isOk - ->expect - ->toBe(true) + Reducer_Expression.BackCompatible.evaluateString(code)->E.R.isOk->expect->toBe(true) let registry = FunctionRegistry_Library.registry let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry)) @@ -15,7 +11,7 @@ let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry)) describe("FunctionRegistry Library", () => { describe("Regular tests", () => { testEvalToBe("List.make(3, 'HI')", "Ok(['HI','HI','HI'])") - testEvalToBe("make(3, 'HI')", "Error(Function not found: make(Number,String))") + testEvalToBe("make(3, 'HI')", "Error(make is not defined)") testEvalToBe("List.upTo(1,3)", "Ok([1,2,3])") testEvalToBe("List.first([3,5,8])", "Ok(3)") testEvalToBe("List.last([3,5,8])", "Ok(8)") @@ -84,6 +80,16 @@ describe("FunctionRegistry Library", () => { "SampleSet.toList(SampleSet.mapN([SampleSet.fromList([1,2,3,4,5,6]), SampleSet.fromList([6, 5, 4, 3, 2, 1])], {|x| x[0] > x[1] ? x[0] : x[1]}))", "Ok([6,5,4,4,5,6])", ) + + testEvalToBe("Dict.merge({a: 1, b: 2}, {b: 3, c: 4, d: 5})", "Ok({a: 1,b: 3,c: 4,d: 5})") + testEvalToBe( + "Dict.mergeMany([{a: 1, b: 2}, {c: 3, d: 4}, {c: 5, e: 6}])", + "Ok({a: 1,b: 2,c: 5,d: 4,e: 6})", + ) + testEvalToBe("Dict.keys({a: 1, b: 2})", "Ok(['a','b'])") + testEvalToBe("Dict.values({a: 1, b: 2})", "Ok([1,2])") + testEvalToBe("Dict.toList({a: 1, b: 2})", "Ok([['a',1],['b',2]])") + testEvalToBe("Dict.fromList([['a', 1], ['b', 2]])", "Ok({a: 1,b: 2})") }) describe("Fn auto-testing", () => { @@ -102,7 +108,7 @@ describe("FunctionRegistry Library", () => { let responseType = example ->Reducer_Expression.BackCompatible.evaluateString - ->E.R2.fmap(ReducerInterface_InternalExpressionValue.valueToValueType) + ->E.R2.fmap(Reducer_Value.valueToValueType) let expectedOutputType = fn.output |> E.O.toExn("") expect(responseType)->toEqual(Ok(expectedOutputType)) }, diff --git a/packages/squiggle-lang/benchmark/Benchmark_Array.res b/packages/squiggle-lang/benchmark/Benchmark_Array.res new file mode 100644 index 00000000..268fea01 --- /dev/null +++ b/packages/squiggle-lang/benchmark/Benchmark_Array.res @@ -0,0 +1,80 @@ +module Map: Benchmark_Helpers.BenchmarkTopic = { + let arraySize = 1000 + let iterations = 300_000 + + let beltArray = () => { + let x = Belt.Array.make(arraySize, 0.) + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Belt.Array.map(v => v) + }) + } + + let jsArray2 = () => { + let x = Belt.Array.make(arraySize, 0.) + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Js.Array2.map(v => v) + }) + } + + let ocamlArray = () => { + let x = Belt.Array.make(arraySize, 0.) + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Array.map(v => v, _) + }) + } + + let runAll = () => { + Js.log( + `Mapping identity function over arrays of size ${arraySize->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`, + ) + Benchmark_Helpers.measure("Belt.Array.map", beltArray) + Benchmark_Helpers.measure("Js.Array2.map", jsArray2) + Benchmark_Helpers.measure("Array.map", ocamlArray) + } +} + +module Sort: Benchmark_Helpers.BenchmarkTopic = { + let arraySize = 1000 + let iterations = 30000 + + let jsArray2 = () => { + let x = Belt.Array.make(arraySize, 0.) + let compare = (a: float, b: float) => { + if a < b { + -1 + } else { + 1 + } + } + + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Js.Array2.sortInPlaceWith(compare) + }) + } + + let jsArray2withOcamlCompare = () => { + let x = Belt.Array.make(arraySize, 0.) + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Js.Array2.sortInPlaceWith(Pervasives.compare) + }) + } + + let ocamlArray = () => { + let x = Belt.Array.make(arraySize, 0.) + Belt.Range.forEach(1, iterations, _ => { + let _ = x->Array.fast_sort(compare, _) + }) + } + + let runAll = () => { + Js.log( + `Sorting arrays of size ${arraySize->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`, + ) + Benchmark_Helpers.measure("Js.Array2.sort", jsArray2) + Benchmark_Helpers.measure("Js.Array2.sort with Ocaml compare", jsArray2withOcamlCompare) + Benchmark_Helpers.measure("Array.fast_sort", ocamlArray) + } +} + +Map.runAll() +Sort.runAll() diff --git a/packages/squiggle-lang/benchmark/Benchmark_Helpers.res b/packages/squiggle-lang/benchmark/Benchmark_Helpers.res new file mode 100644 index 00000000..e6365bea --- /dev/null +++ b/packages/squiggle-lang/benchmark/Benchmark_Helpers.res @@ -0,0 +1,11 @@ +module type BenchmarkTopic = { + let runAll: unit => unit +} + +let measure = (name: string, f: unit => unit) => { + let start = Js.Date.make()->Js.Date.valueOf + f() + let end = Js.Date.make()->Js.Date.valueOf + let duration = (end -. start) /. 1000. + Js.log2(duration, name) +} diff --git a/packages/squiggle-lang/benchmark/Benchmark_Map.res b/packages/squiggle-lang/benchmark/Benchmark_Map.res new file mode 100644 index 00000000..ee9ae51b --- /dev/null +++ b/packages/squiggle-lang/benchmark/Benchmark_Map.res @@ -0,0 +1,63 @@ +module StringMap: Benchmark_Helpers.BenchmarkTopic = { + let size = 1000 + let iterations = 10_000 + + let kv = Belt.Array.range(1, size)->Belt.Array.map(v => ("key" ++ v->Belt.Int.toString, v)) + + let beltMap = () => { + Belt.Range.forEach(1, iterations, _ => { + let m = Belt.Map.String.empty + let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => acc->Belt.Map.String.set(k, v)) + }) + } + + let beltMutableMap = () => { + Belt.Range.forEach(1, iterations, _ => { + let m = Belt.MutableMap.String.make() + let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => { + acc->Belt.MutableMap.String.set(k, v) + acc + }) + }) + } + + let beltHashMap = () => { + Belt.Range.forEach(1, iterations, _ => { + let m = Belt.HashMap.String.make(~hintSize=100) + let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => { + acc->Belt.HashMap.String.set(k, v) + acc + }) + }) + } + + let jsDict = () => { + Belt.Range.forEach(1, iterations, _ => { + let m = Js.Dict.empty() + let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => { + acc->Js.Dict.set(k, v) + acc + }) + }) + } + + let jsMap = () => { + Belt.Range.forEach(1, iterations, _ => { + let m = Js_map.make() + let _ = Belt.Array.reduce(kv, m, (acc, (k, v)) => acc->Js_map.set(k, v)) + }) + } + + let runAll = () => { + Js.log( + `Filling a map with ("key{i}" => "i") key-value pairs, size ${size->Js.Int.toString} (${iterations->Js.Int.toString} iterations)`, + ) + Benchmark_Helpers.measure("Belt.Map.String", beltMap) + Benchmark_Helpers.measure("Belt.MutableMap.String", beltMutableMap) + Benchmark_Helpers.measure("Belt.HashMap.String", beltHashMap) + Benchmark_Helpers.measure("Js.Dict", jsDict) + Benchmark_Helpers.measure("Js.Map", jsMap) + } +} + +let runAll = StringMap.runAll() diff --git a/packages/squiggle-lang/bsconfig.json b/packages/squiggle-lang/bsconfig.json index 1fa663ac..a05df290 100644 --- a/packages/squiggle-lang/bsconfig.json +++ b/packages/squiggle-lang/bsconfig.json @@ -9,6 +9,11 @@ "dir": "__tests__", "type": "dev", "subdirs": true + }, + { + "dir": "benchmark", + "type": "dev", + "subdirs": true } ], "bsc-flags": ["-bs-super-errors", "-bs-no-version-header", "-bs-g"], @@ -21,7 +26,12 @@ "suffix": ".bs.js", "namespace": true, "bs-dependencies": ["bisect_ppx"], - "bs-dev-dependencies": ["@glennsl/rescript-jest", "rescript-fast-check"], + "bs-dev-dependencies": [ + "@glennsl/rescript-jest", + "rescript-fast-check", + "rescript-js-map", + "rescript-js-iterator" + ], "gentypeconfig": { "language": "typescript", "module": "commonjs", diff --git a/packages/squiggle-lang/jest.config.js b/packages/squiggle-lang/jest.config.js index 71babb3c..6fe2b2f4 100644 --- a/packages/squiggle-lang/jest.config.js +++ b/packages/squiggle-lang/jest.config.js @@ -10,5 +10,7 @@ module.exports = { "/node_modules/", ".*Helpers.bs.js", ".*Helpers.ts", + ".*Reducer_Type.*", + ".*_type_test.bs.js", ], }; diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 8974856b..8eb97346 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -1,6 +1,6 @@ { "name": "@quri/squiggle-lang", - "version": "0.4.2", + "version": "0.5.0-alpha.2", "homepage": "https://squiggle-language.com", "license": "MIT", "scripts": { @@ -66,6 +66,7 @@ "reanalyze": "^2.23.0", "rescript": "^9.1.4", "rescript-fast-check": "^1.1.1", + "rescript-js-map": "^1.1.0", "ts-jest": "^27.1.4", "ts-loader": "^9.4.1", "ts-node": "^10.9.1", diff --git a/packages/squiggle-lang/scripts/lib.mjs b/packages/squiggle-lang/scripts/lib.mjs index ffa1d84f..6f778769 100644 --- a/packages/squiggle-lang/scripts/lib.mjs +++ b/packages/squiggle-lang/scripts/lib.mjs @@ -13,7 +13,7 @@ export const measure = (cb, times = 1) => { export const red = (str) => `\x1b[31m${str}\x1b[0m`; export const green = (str) => `\x1b[32m${str}\x1b[0m`; -export const run = (src, { output, sampleCount }) => { +export const run = (src, { output, sampleCount } = {}) => { const project = SqProject.create(); if (sampleCount) { project.setEnvironment({ diff --git a/packages/squiggle-lang/scripts/run.mjs b/packages/squiggle-lang/scripts/run.mjs index bc17314b..6dde265f 100755 --- a/packages/squiggle-lang/scripts/run.mjs +++ b/packages/squiggle-lang/scripts/run.mjs @@ -7,4 +7,6 @@ if (!src) { } console.log(`Running ${src}`); -run(src); +const sampleCount = process.env.SAMPLE_COUNT; + +run(src, { output: true, sampleCount }); diff --git a/packages/squiggle-lang/src/js/SqModule.ts b/packages/squiggle-lang/src/js/SqModule.ts deleted file mode 100644 index c50d3be3..00000000 --- a/packages/squiggle-lang/src/js/SqModule.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen"; -import { SqModuleValue, wrapValue } from "./SqValue"; -import { SqValueLocation } from "./SqValueLocation"; - -export class SqModule { - constructor( - private _value: RSModuleValue.squiggleValue_Module, - public location: SqValueLocation - ) {} - - entries() { - return RSModuleValue.getKeyValuePairs(this._value).map( - ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const - ); - } - - asValue() { - return new SqModuleValue( - RSModuleValue.toSquiggleValue(this._value), - this.location - ); - } - - get(k: string) { - const v = RSModuleValue.get(this._value, k); - return v === undefined || v === null - ? undefined - : wrapValue(v, this.location.extend(k)); - } -} diff --git a/packages/squiggle-lang/src/js/SqPointSetDist.ts b/packages/squiggle-lang/src/js/SqPointSetDist.ts index 524c9319..19a6b178 100644 --- a/packages/squiggle-lang/src/js/SqPointSetDist.ts +++ b/packages/squiggle-lang/src/js/SqPointSetDist.ts @@ -1,4 +1,4 @@ -import * as _ from "lodash"; +import zipWith from "lodash/zipWith"; import { wrapDistribution } from "./SqDistribution"; import * as RSPointSetDist from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution.gen"; import { pointSetDistributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution_tag"; @@ -16,7 +16,7 @@ const shapePoints = ( ): SqPoint[] => { let xs = x.xyShape.xs; let ys = x.xyShape.ys; - return _.zipWith(xs, ys, (x, y) => ({ x, y })); + return zipWith(xs, ys, (x, y) => ({ x, y })); }; export const wrapPointSetDist = (value: T) => { diff --git a/packages/squiggle-lang/src/js/SqProject.ts b/packages/squiggle-lang/src/js/SqProject.ts index 4c2dea7c..9e79b564 100644 --- a/packages/squiggle-lang/src/js/SqProject.ts +++ b/packages/squiggle-lang/src/js/SqProject.ts @@ -2,7 +2,7 @@ import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen"; import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen"; import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen"; import { SqError } from "./SqError"; -import { SqModule } from "./SqModule"; +import { SqRecord } from "./SqRecord"; import { wrapValue } from "./SqValue"; import { resultMap2 } from "./types"; import { SqValueLocation } from "./SqValueLocation"; @@ -83,7 +83,7 @@ export class SqProject { } getBindings(sourceId: string) { - return new SqModule( + return new SqRecord( RSProject.getBindings(this._value, sourceId), new SqValueLocation(this, sourceId, { root: "bindings", @@ -111,4 +111,8 @@ export class SqProject { setEnvironment(environment: environment) { RSProject.setEnvironment(this._value, environment); } + + getEnvironment(): environment { + return RSProject.getEnvironment(this._value); + } } diff --git a/packages/squiggle-lang/src/js/SqRecord.ts b/packages/squiggle-lang/src/js/SqRecord.ts index 85eac76d..27949220 100644 --- a/packages/squiggle-lang/src/js/SqRecord.ts +++ b/packages/squiggle-lang/src/js/SqRecord.ts @@ -1,5 +1,5 @@ import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen"; -import { wrapValue } from "./SqValue"; +import { SqRecordValue, wrapValue } from "./SqValue"; import { SqValueLocation } from "./SqValueLocation"; type T = RSRecord.squiggleValue_Record; @@ -12,4 +12,15 @@ export class SqRecord { ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const ); } + + toString() { + return RSRecord.toString(this._value); + } + + asValue() { + return new SqRecordValue( + RSRecord.toSquiggleValue(this._value), + this.location + ); + } } diff --git a/packages/squiggle-lang/src/js/SqType.ts b/packages/squiggle-lang/src/js/SqType.ts deleted file mode 100644 index 45fe4c9d..00000000 --- a/packages/squiggle-lang/src/js/SqType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.gen"; - -type T = RSType.squiggleValue_Type; - -export class SqType { - constructor(private _value: T) {} -} diff --git a/packages/squiggle-lang/src/js/SqValue.ts b/packages/squiggle-lang/src/js/SqValue.ts index abb49ae8..ef2cfca6 100644 --- a/packages/squiggle-lang/src/js/SqValue.ts +++ b/packages/squiggle-lang/src/js/SqValue.ts @@ -3,11 +3,8 @@ import { squiggleValueTag as Tag } from "../rescript/ForTS/ForTS_SquiggleValue/F import { wrapDistribution } from "./SqDistribution"; import { SqLambda } from "./SqLambda"; import { SqLambdaDeclaration } from "./SqLambdaDeclaration"; -import { SqModule } from "./SqModule"; import { SqRecord } from "./SqRecord"; import { SqArray } from "./SqArray"; -import { SqType } from "./SqType"; -import { SqProject } from "./SqProject"; import { SqValueLocation } from "./SqValueLocation"; export { Tag as SqValueTag }; @@ -46,14 +43,6 @@ export class SqArrayValue extends SqAbstractValue { } } -export class SqArrayStringValue extends SqAbstractValue { - tag = Tag.ArrayString as const; - - get value() { - return this.valueMethod(RSValue.getArrayString); - } -} - export class SqBoolValue extends SqAbstractValue { tag = Tag.Bool as const; @@ -62,14 +51,6 @@ export class SqBoolValue extends SqAbstractValue { } } -export class SqCallValue extends SqAbstractValue { - tag = Tag.Call as const; - - get value() { - return this.valueMethod(RSValue.getCall); - } -} - export class SqDateValue extends SqAbstractValue { tag = Tag.Date as const; @@ -102,14 +83,6 @@ export class SqLambdaValue extends SqAbstractValue { } } -export class SqModuleValue extends SqAbstractValue { - tag = Tag.Module as const; - - get value() { - return new SqModule(this.valueMethod(RSValue.getModule), this.location); - } -} - export class SqNumberValue extends SqAbstractValue { tag = Tag.Number as const; @@ -134,14 +107,6 @@ export class SqStringValue extends SqAbstractValue { } } -export class SqSymbolValue extends SqAbstractValue { - tag = Tag.Symbol as const; - - get value(): string { - return this.valueMethod(RSValue.getSymbol); - } -} - export class SqTimeDurationValue extends SqAbstractValue { tag = Tag.TimeDuration as const; @@ -150,22 +115,6 @@ export class SqTimeDurationValue extends SqAbstractValue { } } -export class SqTypeValue extends SqAbstractValue { - tag = Tag.Type as const; - - get value() { - return new SqType(this.valueMethod(RSValue.getType)); - } -} - -export class SqTypeIdentifierValue extends SqAbstractValue { - tag = Tag.TypeIdentifier as const; - - get value() { - return this.valueMethod(RSValue.getTypeIdentifier); - } -} - export class SqVoidValue extends SqAbstractValue { tag = Tag.Void as const; @@ -176,21 +125,15 @@ export class SqVoidValue extends SqAbstractValue { const tagToClass = { [Tag.Array]: SqArrayValue, - [Tag.ArrayString]: SqArrayStringValue, [Tag.Bool]: SqBoolValue, - [Tag.Call]: SqCallValue, [Tag.Date]: SqDateValue, [Tag.Declaration]: SqDeclarationValue, [Tag.Distribution]: SqDistributionValue, [Tag.Lambda]: SqLambdaValue, - [Tag.Module]: SqModuleValue, [Tag.Number]: SqNumberValue, [Tag.Record]: SqRecordValue, [Tag.String]: SqStringValue, - [Tag.Symbol]: SqSymbolValue, [Tag.TimeDuration]: SqTimeDurationValue, - [Tag.Type]: SqTypeValue, - [Tag.TypeIdentifier]: SqTypeIdentifierValue, [Tag.Void]: SqVoidValue, } as const; @@ -198,19 +141,13 @@ const tagToClass = { // type SqValue = typeof tagToClass[keyof typeof tagToClass]; export type SqValue = | SqArrayValue - | SqArrayStringValue | SqBoolValue - | SqCallValue | SqDateValue | SqDeclarationValue | SqDistributionValue | SqLambdaValue - | SqModuleValue | SqNumberValue | SqRecordValue | SqStringValue - | SqSymbolValue | SqTimeDurationValue - | SqTypeValue - | SqTypeIdentifierValue | SqVoidValue; diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index bb443fac..aac26bd5 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,4 +1,4 @@ -import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen"; +import { environment } from "../rescript/ForTS/ForTS__Types.gen"; import { SqProject } from "./SqProject"; import { SqValue, SqValueTag } from "./SqValue"; export { SqValueLocation } from "./SqValueLocation"; diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js index 9cc75b39..e9592745 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/KdeLibrary.js @@ -1,5 +1,6 @@ const pdfast = require("pdfast"); -const _ = require("lodash"); +const filter = require("lodash/filter"); +const isFinite = require("lodash/isFinite"); const samplesToContinuousPdf = ( samples, @@ -8,12 +9,12 @@ const samplesToContinuousPdf = ( min = false, max = false ) => { - let _samples = _.filter(samples, _.isFinite); - if (_.isFinite(min)) { - _samples = _.filter(_samples, (r) => r > min); + let _samples = filter(samples, isFinite); + if (isFinite(min)) { + _samples = filter(_samples, (r) => r > min); } - if (_.isFinite(max)) { - _samples = _.filter(_samples, (r) => r < max); + if (isFinite(max)) { + _samples = filter(_samples, (r) => r < max); } // The pdf that's created from this function is not a pdf but a pmf. y values diff --git a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res index 81bfc760..4cf2b868 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res +++ b/packages/squiggle-lang/src/rescript/Distributions/SampleSetDist/SampleSetDist_ToPointSet.res @@ -62,7 +62,7 @@ let toPointSetDist = ( ~samplingInputs: SamplingInputs.samplingInputs, (), ): Internals.Types.outputs => { - let samples = samples->Js.Array2.copy->Js.Array2.sortInPlaceWith(compare) + let samples = samples->E.A.Floats.sort let minDiscreteToKeep = MagicNumbers.ToPointSet.minDiscreteToKeep(samples) let (continuousPart, discretePart) = E.A.Floats.Sorted.splitContinuousAndDiscreteForMinWeight( diff --git a/packages/squiggle-lang/src/rescript/FR/FR_Builtin.res b/packages/squiggle-lang/src/rescript/FR/FR_Builtin.res new file mode 100644 index 00000000..84e6a50f --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_Builtin.res @@ -0,0 +1,102 @@ +open FunctionRegistry_Core +open FunctionRegistry_Helpers + +let nameSpace = "" // no namespaced versions + +type simpleDefinition = { + inputs: array, + fn: array => result, +} + +let makeFnMany = (name: string, definitions: array) => + Function.make( + ~name, + ~nameSpace, + ~requiresNamespace=false, + ~definitions=definitions->Js.Array2.map(({inputs, fn}) => + FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _) => fn(inputs), ()) + ), + (), + ) + +let makeFn = ( + name: string, + inputs: array, + fn: array => result, +) => makeFnMany(name, [{inputs: inputs, fn: fn}]) + +let library = [ + Make.ff2f(~name="add", ~fn=(x, y) => x +. y, ()), // infix + (see Reducer/Reducer_Peggy/helpers.ts) + Make.ff2f(~name="subtract", ~fn=(x, y) => x -. y, ()), // infix - + Make.ff2f(~name="multiply", ~fn=(x, y) => x *. y, ()), // infix * + Make.ff2f(~name="divide", ~fn=(x, y) => x /. y, ()), // infix / + Make.ff2f(~name="pow", ~fn=(x, y) => Js.Math.pow_float(~base=x, ~exp=y), ()), // infix ^ + Make.ff2b(~name="equal", ~fn=(x, y) => x == y, ()), // infix == on numbers + Make.bb2b(~name="equal", ~fn=(x, y) => x == y, ()), // infix == on booleans + Make.ff2b(~name="unequal", ~fn=(x, y) => x != y, ()), // infix != on numbers + Make.ff2b(~name="unequal", ~fn=(x, y) => x != y, ()), // infix != on booleans + Make.ff2b(~name="smaller", ~fn=(x, y) => x < y, ()), // infix < + Make.ff2b(~name="smallerEq", ~fn=(x, y) => x <= y, ()), // infix <= + Make.ff2b(~name="larger", ~fn=(x, y) => x > y, ()), // infix > + Make.ff2b(~name="largerEq", ~fn=(x, y) => x >= y, ()), // infix >= + Make.bb2b(~name="or", ~fn=(x, y) => x || y, ()), // infix || + Make.bb2b(~name="and", ~fn=(x, y) => x && y, ()), // infix && + Make.f2f(~name="unaryMinus", ~fn=x => -.x, ()), // unary prefix - + makeFn("not", [FRTypeNumber], inputs => { + // unary prefix ! + switch inputs { + | [IEvNumber(x)] => IEvBool(x != 0.)->Ok + | _ => Error(impossibleError) + } + }), + makeFn("not", [FRTypeBool], inputs => { + // unary prefix ! + switch inputs { + | [IEvBool(x)] => IEvBool(!x)->Ok + | _ => Error(impossibleError) + } + }), + makeFn("concat", [FRTypeString, FRTypeString], inputs => { + switch inputs { + | [IEvString(a), IEvString(b)] => { + let answer = Js.String2.concat(a, b) + answer->Reducer_T.IEvString->Ok + } + | _ => Error(impossibleError) + } + }), + makeFn("concat", [FRTypeArray(FRTypeAny), FRTypeArray(FRTypeAny)], inputs => { + switch inputs { + | [IEvArray(originalA), IEvArray(b)] => { + let a = originalA->Js.Array2.copy + let _ = Js.Array2.pushMany(a, b) + a->Reducer_T.IEvArray->Ok + } + | _ => Error(impossibleError) + } + }), + makeFn("inspect", [FRTypeAny], inputs => { + switch inputs { + | [value] => { + Js.log(value->Reducer_Value.toString) + value->Ok + } + | _ => Error(impossibleError) + } + }), + makeFn("inspect", [FRTypeAny, FRTypeString], inputs => { + switch inputs { + | [value, IEvString(label)] => { + Js.log(`${label}: ${value->Reducer_Value.toString}`) + value->Ok + } + | _ => Error(impossibleError) + } + }), + makeFn("javascriptraise", [FRTypeAny], inputs => { + switch inputs { + | [msg] => Js.Exn.raiseError(msg->Reducer_Value.toString) + | _ => Error(impossibleError) + } + }), +] diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Danger.res b/packages/squiggle-lang/src/rescript/FR/FR_Danger.res similarity index 86% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Danger.res rename to packages/squiggle-lang/src/rescript/FR/FR_Danger.res index 2fcfcdaf..2d60f556 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Danger.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Danger.res @@ -8,11 +8,11 @@ let requiresNamespace = true module Combinatorics = { module Helpers = { - let laplace = ((successes, trials)) => (successes +. 1.0) /. (trials +. 2.0) + let laplace = (successes, trials) => (successes +. 1.0) /. (trials +. 2.0) let factorial = Stdlib.Math.factorial - let choose = ((n, k)) => factorial(n) /. (factorial(n -. k) *. factorial(k)) + let choose = (n, k) => factorial(n) /. (factorial(n -. k) *. factorial(k)) let pow = (base, exp) => Js.Math.pow_float(~base, ~exp) - let binomial = ((n, k, p)) => choose((n, k)) *. pow(p, k) *. pow(1.0 -. p, n -. k) + let binomial = (n, k, p) => choose(n, k) *. pow(p, k) *. pow(1.0 -. p, n -. k) } module Lib = { let laplace = Function.make( @@ -69,15 +69,15 @@ module Integration = { let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point) let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall( aLambda, - list{pointAsInternalExpression}, + [pointAsInternalExpression], environment, reducer, ) let result = switch resultAsInternalExpression { - | IEvNumber(x) => Ok(x) + | Reducer_T.IEvNumber(x) => Ok(x) | _ => Error( - "Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead", + "Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther, ) } result @@ -132,7 +132,7 @@ module Integration = { | (Ok(yMin), Ok(yMax)) => { let result = (yMin +. yMax) *. weightForAnOuterPoint +. innerPointsSum *. weightForAnInnerPoint - let wrappedResult = result->ReducerInterface_InternalExpressionValue.IEvNumber->Ok + let wrappedResult = result->Reducer_T.IEvNumber->Ok wrappedResult } | (Error(b), _) => Error(b) @@ -141,11 +141,11 @@ module Integration = { resultWithOuterPoints } | Error(b) => - Error( - "Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++ - "Original error: " ++ - b, - ) + ("Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++ + "Original error: " ++ + b->Reducer_ErrorValue.errorToString) + ->Reducer_ErrorValue.REOther + ->Error } result } @@ -165,10 +165,12 @@ module Integration = { FnDefinition.make( ~name="integrateFunctionBetweenWithNumIntegrationPoints", ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], - ~run=(inputs, _, env, reducer) => { + ~run=(inputs, env, reducer) => { let result = switch inputs { | [_, _, _, IEvNumber(0.0)] => - Error("Integration error 4 in Danger.integrate: Increment can't be 0.") + "Integration error 4 in Danger.integrate: Increment can't be 0." + ->Reducer_ErrorValue.REOther + ->Error | [ IEvLambda(aLambda), IEvNumber(min), @@ -185,7 +187,9 @@ module Integration = { ) | _ => Error( - "Integration error 5 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))", + Reducer_ErrorValue.REOther( + "Integration error 5 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))", + ), ) } result @@ -205,10 +209,12 @@ module Integration = { FnDefinition.make( ~name="integrateFunctionBetweenWithEpsilon", ~inputs=[FRTypeLambda, FRTypeNumber, FRTypeNumber, FRTypeNumber], - ~run=(inputs, _, env, reducer) => { + ~run=(inputs, env, reducer) => { let result = switch inputs { | [_, _, _, IEvNumber(0.0)] => - Error("Integration error in Danger.integrate: Increment can't be 0.") + "Integration error in Danger.integrate: Increment can't be 0." + ->Reducer_ErrorValue.REOther + ->Error | [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(epsilon)] => Helpers.integrateFunctionBetweenWithNumIntegrationPoints( aLambda, @@ -218,12 +224,13 @@ module Integration = { env, reducer, )->E.R2.errMap(b => - "Integration error 7 in Danger.integrate. Something went wrong along the way: " ++ b + ("Integration error 7 in Danger.integrate. Something went wrong along the way: " ++ + b->Reducer_ErrorValue.errorToString)->Reducer_ErrorValue.REOther ) | _ => - Error( - "Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))", - ) + "Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))" + ->Reducer_ErrorValue.REOther + ->Error } result }, @@ -239,16 +246,16 @@ module DiminishingReturns = { module Helpers = { type diminishingReturnsAccumulatorInner = { optimalAllocations: array, - currentMarginalReturns: result, string>, + currentMarginalReturns: result, errorValue>, } - let findBiggestElementIndex = xs => + let findBiggestElementIndex = (xs: array) => E.A.reducei(xs, 0, (acc, newElement, index) => { switch newElement > xs[acc] { | true => index | false => acc } }) - type diminishingReturnsAccumulator = result + type diminishingReturnsAccumulator = result // TODO: This is so complicated, it probably should be its own file. It might also make sense to have it work in Rescript directly, taking in a function rather than a reducer; then something else can wrap that function in the reducer/lambdas/environment. /* The key idea for this function is that @@ -283,19 +290,19 @@ module DiminishingReturns = { ) { | (false, _, _, _) => Error( - "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1.", + "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."->Reducer_ErrorValue.REOther, ) | (_, false, _, _) => Error( - "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0.", + "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."->Reducer_ErrorValue.REOther, ) | (_, _, false, _) => Error( - "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0.", + "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."->Reducer_ErrorValue.REOther, ) | (_, _, _, false) => Error( - "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount.", + "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."->Reducer_ErrorValue.REOther, ) | (true, true, true, true) => { let applyFunctionAtPoint = (lambda, point: float) => { @@ -303,15 +310,15 @@ module DiminishingReturns = { let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point) let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall( lambda, - list{pointAsInternalExpression}, + [pointAsInternalExpression], environment, reducer, ) switch resultAsInternalExpression { - | IEvNumber(x) => Ok(x) + | Reducer_T.IEvNumber(x) => Ok(x) | _ => Error( - "Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead", + "Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther, ) } } @@ -396,16 +403,16 @@ module DiminishingReturns = { FnDefinition.make( ~name="optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions", ~inputs=[FRTypeArray(FRTypeLambda), FRTypeNumber, FRTypeNumber], - ~run=(inputs, _, environment, reducer) => + ~run=(inputs, environment, reducer) => switch inputs { | [IEvArray(innerlambdas), IEvNumber(funds), IEvNumber(approximateIncrement)] => { let individuallyWrappedLambdas = E.A.fmap(innerLambda => { switch innerLambda { - | ReducerInterface_InternalExpressionValue.IEvLambda(lambda) => Ok(lambda) + | Reducer_T.IEvLambda(lambda) => Ok(lambda) | _ => - Error( - "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. A member of the array wasn't a function", - ) + "Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. A member of the array wasn't a function" + ->Reducer_ErrorValue.REOther + ->Error } }, innerlambdas) let wrappedLambdas = E.A.R.firstErrorOrOpen(individuallyWrappedLambdas) @@ -424,7 +431,10 @@ module DiminishingReturns = { } result } - | _ => Error("Error in Danger.diminishingMarginalReturnsForTwoFunctions") + | _ => + "Error in Danger.diminishingMarginalReturnsForTwoFunctions" + ->Reducer_ErrorValue.REOther + ->Error }, (), ), diff --git a/packages/squiggle-lang/src/rescript/FR/FR_Date.res b/packages/squiggle-lang/src/rescript/FR/FR_Date.res new file mode 100644 index 00000000..c9732980 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_Date.res @@ -0,0 +1,159 @@ +open FunctionRegistry_Core +open FunctionRegistry_Helpers + +let makeFn = ( + name: string, + inputs: array, + fn: array => result, +) => + Function.make( + ~name, + ~nameSpace="", + ~requiresNamespace=false, + ~definitions=[FnDefinition.make(~name, ~inputs, ~run=(inputs, _, _) => fn(inputs), ())], + (), + ) + +let makeNumberToDurationFn = (name: string, fn: float => DateTime.Duration.t) => + Function.make( + ~name, + ~nameSpace="", + ~requiresNamespace=false, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeNumber], + ~run=(inputs, _, _) => + switch inputs { + | [IEvNumber(t)] => IEvTimeDuration(fn(t))->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + +let makeDurationToNumberFn = (name: string, fn: DateTime.Duration.t => float) => + Function.make( + ~name, + ~nameSpace="", + ~requiresNamespace=false, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeTimeDuration], + ~run=(inputs, _, _) => + switch inputs { + | [IEvTimeDuration(t)] => IEvNumber(fn(t))->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + +let library = [ + makeFn("toString", [FRTypeDate], inputs => + switch inputs { + | [IEvDate(t)] => IEvString(DateTime.Date.toString(t))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("makeDateFromYear", [FRTypeNumber], inputs => + switch inputs { + | [IEvNumber(year)] => + switch DateTime.Date.makeFromYear(year) { + | Ok(t) => IEvDate(t)->Ok + | Error(e) => Reducer_ErrorValue.RETodo(e)->Error + } + | _ => Error(impossibleError) + } + ), + makeFn("dateFromNumber", [FRTypeNumber], inputs => + switch inputs { + | [IEvNumber(f)] => IEvDate(DateTime.Date.fromFloat(f))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("toNumber", [FRTypeDate], inputs => + switch inputs { + | [IEvDate(f)] => IEvNumber(DateTime.Date.toFloat(f))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("subtract", [FRTypeDate, FRTypeDate], inputs => + switch inputs { + | [IEvDate(d1), IEvDate(d2)] => + switch DateTime.Date.subtract(d1, d2) { + | Ok(d) => IEvTimeDuration(d)->Ok + | Error(e) => Error(RETodo(e)) + } + | _ => Error(impossibleError) + } + ), + makeFn("subtract", [FRTypeDate, FRTypeTimeDuration], inputs => + switch inputs { + | [IEvDate(d1), IEvTimeDuration(d2)] => IEvDate(DateTime.Date.subtractDuration(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("add", [FRTypeDate, FRTypeTimeDuration], inputs => + switch inputs { + | [IEvDate(d1), IEvTimeDuration(d2)] => IEvDate(DateTime.Date.addDuration(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("toString", [FRTypeTimeDuration], inputs => + switch inputs { + | [IEvTimeDuration(t)] => IEvString(DateTime.Duration.toString(t))->Ok + | _ => Error(impossibleError) + } + ), + makeNumberToDurationFn("minutes", DateTime.Duration.fromMinutes), + makeNumberToDurationFn("fromUnit_minutes", DateTime.Duration.fromMinutes), + makeNumberToDurationFn("hours", DateTime.Duration.fromHours), + makeNumberToDurationFn("fromUnit_hours", DateTime.Duration.fromHours), + makeNumberToDurationFn("days", DateTime.Duration.fromDays), + makeNumberToDurationFn("fromUnit_days", DateTime.Duration.fromDays), + makeNumberToDurationFn("years", DateTime.Duration.fromYears), + makeNumberToDurationFn("fromUnit_years", DateTime.Duration.fromYears), + makeDurationToNumberFn("toMinutes", DateTime.Duration.toMinutes), + makeDurationToNumberFn("toHours", DateTime.Duration.toHours), + makeDurationToNumberFn("toDays", DateTime.Duration.toDays), + makeDurationToNumberFn("toYears", DateTime.Duration.toYears), + makeFn("add", [FRTypeTimeDuration, FRTypeTimeDuration], inputs => + switch inputs { + | [IEvTimeDuration(d1), IEvTimeDuration(d2)] => + IEvTimeDuration(DateTime.Duration.add(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("subtract", [FRTypeTimeDuration, FRTypeTimeDuration], inputs => + switch inputs { + | [IEvTimeDuration(d1), IEvTimeDuration(d2)] => + IEvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("multiply", [FRTypeTimeDuration, FRTypeNumber], inputs => + switch inputs { + | [IEvTimeDuration(d1), IEvNumber(d2)] => + IEvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("divide", [FRTypeTimeDuration, FRTypeNumber], inputs => + switch inputs { + | [IEvTimeDuration(d1), IEvNumber(d2)] => IEvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok + | _ => Error(impossibleError) + } + ), + makeFn("divide", [FRTypeTimeDuration, FRTypeTimeDuration], inputs => + switch inputs { + | [IEvTimeDuration(d1), IEvTimeDuration(d2)] => IEvNumber(d1 /. d2)->Ok + | _ => Error(impossibleError) + } + ), +] diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dict.res b/packages/squiggle-lang/src/rescript/FR/FR_Dict.res similarity index 72% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dict.res rename to packages/squiggle-lang/src/rescript/FR/FR_Dict.res index 0c85bbe1..77ca49a7 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dict.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Dict.res @@ -4,23 +4,23 @@ open FunctionRegistry_Helpers let nameSpace = "Dict" module Internals = { - type t = ReducerInterface_InternalExpressionValue.map + type t = Reducer_T.map - let keys = (a: t): internalExpressionValue => IEvArray( + let keys = (a: t): Reducer_T.value => IEvArray( Belt.Map.String.keysToArray(a)->E.A2.fmap(Wrappers.evString), ) - let values = (a: t): internalExpressionValue => IEvArray(Belt.Map.String.valuesToArray(a)) + let values = (a: t): Reducer_T.value => IEvArray(Belt.Map.String.valuesToArray(a)) - let toList = (a: t): internalExpressionValue => + let toList = (a: t): Reducer_T.value => Belt.Map.String.toArray(a) ->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value])) ->Wrappers.evArray - let fromList = (items: array): result => + let fromList = (items: array): result => items ->E.A2.fmap(item => { - switch (item: internalExpressionValue) { + switch (item: Reducer_T.value) { | IEvArray([IEvString(string), value]) => (string, value)->Ok | _ => Error(impossibleError) } @@ -29,12 +29,8 @@ module Internals = { ->E.R2.fmap(Belt.Map.String.fromArray) ->E.R2.fmap(Wrappers.evRecord) - let merge = (a: t, b: t): internalExpressionValue => IEvRecord( - Belt.Map.String.merge(a, b, (_, _, c) => c), - ) - //Belt.Map.String has a function for mergeMany, but I couldn't understand how to use it yet. - let mergeMany = (a: array): internalExpressionValue => { + let mergeMany = (a: array): Reducer_T.value => { let mergedValues = a->E.A2.fmap(Belt.Map.String.toArray)->Belt.Array.concatMany->Belt.Map.String.fromArray IEvRecord(mergedValues) @@ -52,10 +48,10 @@ let library = [ FnDefinition.make( ~name="merge", ~inputs=[FRTypeDict(FRTypeAny), FRTypeDict(FRTypeAny)], - ~run=(inputs, _, _, _) => { + ~run=(inputs, _, _) => { switch inputs { - | [IEvRecord(d1), IEvRecord(d2)] => Internals.merge(d1, d2)->Ok - | _ => Error(impossibleError) + | [IEvRecord(d1), IEvRecord(d2)] => Internals.mergeMany([d1, d2])->Ok + | _ => impossibleError->Error } }, (), @@ -63,7 +59,6 @@ let library = [ ], (), ), - //TODO: Change to use new mergeMany() function. Function.make( ~name="mergeMany", ~nameSpace, @@ -74,13 +69,21 @@ let library = [ FnDefinition.make( ~name="mergeMany", ~inputs=[FRTypeArray(FRTypeDict(FRTypeAny))], - ~run=(_, inputs, _, _) => - inputs - ->Prepare.ToTypedArray.dicts - ->E.R2.fmap(E.Dict.concatMany) - ->E.R2.fmap(Js.Dict.map((. r) => FunctionRegistry_Core.FRType.matchReverse(r))) - ->E.R2.fmap(r => r->Js.Dict.entries->Belt.Map.String.fromArray) - ->E.R2.fmap(Wrappers.evRecord), + ~run=(inputs, _, _) => { + switch inputs { + | [IEvArray(dicts)] => + dicts + ->Belt.Array.map(dictValue => + switch dictValue { + | IEvRecord(dict) => dict + | _ => impossibleError->Reducer_ErrorValue.toException + } + ) + ->Internals.mergeMany + ->Ok + | _ => impossibleError->Error + } + }, (), ), ], @@ -96,7 +99,7 @@ let library = [ FnDefinition.make( ~name="keys", ~inputs=[FRTypeDict(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { | [IEvRecord(d1)] => Internals.keys(d1)->Ok | _ => Error(impossibleError) @@ -116,7 +119,7 @@ let library = [ FnDefinition.make( ~name="values", ~inputs=[FRTypeDict(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { | [IEvRecord(d1)] => Internals.values(d1)->Ok | _ => Error(impossibleError) @@ -136,7 +139,7 @@ let library = [ FnDefinition.make( ~name="toList", ~inputs=[FRTypeDict(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { | [IEvRecord(dict)] => dict->Internals.toList->Ok | _ => Error(impossibleError) @@ -156,7 +159,7 @@ let library = [ FnDefinition.make( ~name="fromList", ~inputs=[FRTypeArray(FRTypeArray(FRTypeAny))], - ~run=(inputs, _, _, _) => { + ~run=(inputs, _, _) => { switch inputs { | [IEvArray(items)] => Internals.fromList(items) | _ => Error(impossibleError) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res b/packages/squiggle-lang/src/rescript/FR/FR_Dist.res similarity index 86% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res rename to packages/squiggle-lang/src/rescript/FR/FR_Dist.res index 23619187..97befcd4 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Dist.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Dist.res @@ -4,7 +4,7 @@ let twoArgs = E.Tuple2.toFnCall module DistributionCreation = { let nameSpace = "Dist" - let output = ReducerInterface_InternalExpressionValue.EvtDistribution + let output = Reducer_Value.EvtDistribution let requiresNamespace = false let fnMake = (~name, ~examples, ~definitions) => { @@ -16,13 +16,13 @@ module DistributionCreation = { r ->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env)) ->E.R2.fmap(Wrappers.evDistribution) + ->E.R2.errMap(e => Reducer_ErrorValue.REOther(e)) let make = (name, fn) => { FnDefinition.make( ~name, ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], - ~run=(_, inputs, accessors, _) => - inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=accessors.environment), + ~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env), (), ) } @@ -31,10 +31,8 @@ module DistributionCreation = { FnDefinition.make( ~name, ~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])], - ~run=(_, inputs, accessors, _) => - inputs - ->Prepare.ToValueTuple.Record.twoDistOrNumber - ->process(~fn, ~env=accessors.environment), + ~run=(inputs, env, _) => + inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))->process(~fn, ~env), (), ) } @@ -43,10 +41,10 @@ module DistributionCreation = { FnDefinition.make( ~name, ~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])], - ~run=(_, inputs, accessors, _) => + ~run=(inputs, env, _) => inputs - ->Prepare.ToValueTuple.Record.twoDistOrNumber - ->process(~fn, ~env=accessors.environment), + ->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev")) + ->process(~fn, ~env), (), ) } @@ -57,13 +55,13 @@ module DistributionCreation = { r ->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env)) ->E.R2.fmap(Wrappers.evDistribution) + ->E.R2.errMap(e => Reducer_ErrorValue.REOther(e)) let make = (name, fn) => FnDefinition.make( ~name, ~inputs=[FRTypeDistOrNumber], - ~run=(_, inputs, accessors, _) => - inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=accessors.environment), + ~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env), (), ) } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Fn.res b/packages/squiggle-lang/src/rescript/FR/FR_Fn.res similarity index 75% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Fn.res rename to packages/squiggle-lang/src/rescript/FR/FR_Fn.res index ee2ef343..66d20c1f 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Fn.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Fn.res @@ -7,23 +7,21 @@ module Declaration = { ("inputs", FRTypeArray(FRTypeRecord([("min", FRTypeNumber), ("max", FRTypeNumber)]))), ]) - let fromExpressionValue = (e: frValue): result => { - switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e]) { - | Ok([FRValueLambda(lambda), FRValueArray(inputs)]) => { + let fromExpressionValue = (e: Reducer_T.value): result => { + switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs([e], ("fn", "inputs")) { + | Ok([IEvLambda(lambda), IEvArray(inputs)]) => { open FunctionRegistry_Helpers.Prepare let getMinMax = arg => - ToValueArray.Record.toArgs([arg]) + ToValueArray.Record.twoArgs([arg], ("min", "max")) ->E.R.bind(ToValueTuple.twoNumbers) ->E.R2.fmap(((min, max)) => Declaration.ContinuousFloatArg.make(min, max)) inputs ->E.A2.fmap(getMinMax) ->E.A.R.firstErrorOrOpen - ->E.R2.fmap(args => ReducerInterface_InternalExpressionValue.IEvDeclaration( - Declaration.make(lambda, args), - )) + ->E.R2.fmap(args => Reducer_T.IEvDeclaration(Declaration.make(lambda, args))) } | Error(r) => Error(r) - | Ok(_) => Error(FunctionRegistry_Helpers.impossibleError) + | Ok(_) => Error(impossibleErrorString) } } } @@ -51,8 +49,8 @@ let library = [ FnDefinition.make( ~name="declare", ~inputs=[Declaration.frType], - ~run=(_, inputs, _, _) => { - inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue) + ~run=(inputs, _, _) => { + inputs->getOrError(0)->E.R.bind(Declaration.fromExpressionValue)->E.R2.errMap(wrapError) }, (), ), diff --git a/packages/squiggle-lang/src/rescript/FR/FR_GenericDist.res b/packages/squiggle-lang/src/rescript/FR/FR_GenericDist.res new file mode 100644 index 00000000..6f98d132 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_GenericDist.res @@ -0,0 +1,410 @@ +open FunctionRegistry_Core + +module Old = { + module Helpers = { + let arithmeticMap = r => + switch r { + | "add" => #Add + | "dotAdd" => #Add + | "subtract" => #Subtract + | "dotSubtract" => #Subtract + | "divide" => #Divide + | "log" => #Logarithm + | "dotDivide" => #Divide + | "pow" => #Power + | "dotPow" => #Power + | "multiply" => #Multiply + | "dotMultiply" => #Multiply + | _ => #Multiply + } + + let catchAndConvertTwoArgsToDists = (args: array): option<( + DistributionTypes.genericDist, + DistributionTypes.genericDist, + )> => + switch args { + | [IEvDistribution(a), IEvDistribution(b)] => Some((a, b)) + | [IEvNumber(a), IEvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) + | [IEvDistribution(a), IEvNumber(b)] => Some((a, GenericDist.fromFloat(b))) + | _ => None + } + + let toFloatFn = ( + fnCall: DistributionTypes.DistributionOperation.toFloat, + dist: DistributionTypes.genericDist, + ~env: GenericDist.env, + ) => { + FromDist(#ToFloat(fnCall), dist)->DistributionOperation.run(~env)->Some + } + + let toStringFn = ( + fnCall: DistributionTypes.DistributionOperation.toString, + dist: DistributionTypes.genericDist, + ~env: GenericDist.env, + ) => { + FromDist(#ToString(fnCall), dist)->DistributionOperation.run(~env)->Some + } + + let toBoolFn = ( + fnCall: DistributionTypes.DistributionOperation.toBool, + dist: DistributionTypes.genericDist, + ~env: GenericDist.env, + ) => { + FromDist(#ToBool(fnCall), dist)->DistributionOperation.run(~env)->Some + } + + let toDistFn = ( + fnCall: DistributionTypes.DistributionOperation.toDist, + dist, + ~env: GenericDist.env, + ) => { + FromDist(#ToDist(fnCall), dist)->DistributionOperation.run(~env)->Some + } + + let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: GenericDist.env) => { + FromDist( + #ToDistCombination(direction, arithmeticMap(arithmetic), #Dist(dist2)), + dist1, + )->DistributionOperation.run(~env) + } + + let parseNumber = (args: Reducer_T.value): Belt.Result.t => + switch args { + | IEvNumber(x) => Ok(x) + | _ => Error("Not a number") + } + + let parseNumberArray = (ags: array): Belt.Result.t, string> => + E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen + + let parseDist = (args: Reducer_T.value): Belt.Result.t => + switch args { + | IEvDistribution(x) => Ok(x) + | IEvNumber(x) => Ok(GenericDist.fromFloat(x)) + | _ => Error("Not a distribution") + } + + let parseDistributionArray = (ags: array): Belt.Result.t< + array, + string, + > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen + + let mixtureWithGivenWeights = ( + distributions: array, + weights: array, + ~env: GenericDist.env, + ): DistributionOperation.outputType => + E.A.length(distributions) == E.A.length(weights) + ? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env) + : GenDistError( + ArgumentError("Error, mixture call has different number of distributions and weights"), + ) + + let mixtureWithDefaultWeights = ( + distributions: array, + ~env: GenericDist.env, + ): DistributionOperation.outputType => { + let length = E.A.length(distributions) + let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) + mixtureWithGivenWeights(distributions, weights, ~env) + } + + let mixture = ( + args: array, + ~env: GenericDist.env, + ): DistributionOperation.outputType => { + let error = (err: string): DistributionOperation.outputType => + err->DistributionTypes.ArgumentError->GenDistError + switch args { + | [IEvArray(distributions)] => + switch parseDistributionArray(distributions) { + | Ok(distrs) => mixtureWithDefaultWeights(distrs, ~env) + | Error(err) => error(err) + } + | [IEvArray(distributions), IEvArray(weights)] => + switch (parseDistributionArray(distributions), parseNumberArray(weights)) { + | (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts, ~env) + | (Error(err), Ok(_)) => error(err) + | (Ok(_), Error(err)) => error(err) + | (Error(err1), Error(err2)) => error(`${err1}|${err2}`) + } + | _ => + switch E.A.last(args) { + | Some(IEvArray(b)) => { + let weights = parseNumberArray(b) + let distributions = parseDistributionArray( + Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1), + ) + switch E.R.merge(distributions, weights) { + | Ok(d, w) => mixtureWithGivenWeights(d, w, ~env) + | Error(err) => error(err) + } + } + | Some(IEvNumber(_)) + | Some(IEvDistribution(_)) => + switch parseDistributionArray(args) { + | Ok(distributions) => mixtureWithDefaultWeights(distributions, ~env) + | Error(err) => error(err) + } + | _ => error("Last argument of mx must be array or distribution") + } + } + } + } + + module SymbolicConstructors = { + let threeFloat = name => + switch name { + | "triangular" => Ok(SymbolicDist.Triangular.make) + | _ => Error("Unreachable state") + } + + let symbolicResultToOutput = ( + symbolicResult: result, + ): option => + switch symbolicResult { + | Ok(r) => Some(Dist(Symbolic(r))) + | Error(r) => Some(GenDistError(OtherError(r))) + } + } + + let dispatchToGenericOutput = (call: Reducer_Value.functionCall, env: GenericDist.env): option< + DistributionOperation.outputType, + > => { + let (fnName, args) = call + switch (fnName, args) { + | ("triangular" as fnName, [IEvNumber(f1), IEvNumber(f2), IEvNumber(f3)]) => + SymbolicConstructors.threeFloat(fnName) + ->E.R.bind(r => r(f1, f2, f3)) + ->SymbolicConstructors.symbolicResultToOutput + | ("sample", [IEvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env) + | ("sampleN", [IEvDistribution(dist), IEvNumber(n)]) => + Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n)))) + | (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [IEvDistribution(dist)]) => { + let fn = switch op { + | "mean" => #Mean + | "stdev" => #Stdev + | "variance" => #Variance + | "min" => #Min + | "max" => #Max + | "mode" => #Mode + | _ => #Mean + } + Helpers.toFloatFn(fn, dist, ~env) + } + | ("integralSum", [IEvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env) + | ("toString", [IEvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env) + | ("sparkline", [IEvDistribution(dist)]) => + Helpers.toStringFn(ToSparkline(MagicNumbers.Environment.sparklineLength), dist, ~env) + | ("sparkline", [IEvDistribution(dist), IEvNumber(n)]) => + Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist, ~env) + | ("exp", [IEvDistribution(a)]) => + // https://mathjs.org/docs/reference/functions/exp.html + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "pow", + GenericDist.fromFloat(MagicNumbers.Math.e), + a, + ~env, + )->Some + | ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env) + | ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env) + | ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env) + | ("scaleLog", [IEvDistribution(dist)]) => + Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist, ~env) + | ("scaleLog10", [IEvDistribution(dist)]) => + Helpers.toDistFn(Scale(#Logarithm, 10.0), dist, ~env) + | ("scaleLog", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toDistFn(Scale(#Logarithm, float), dist, ~env) + | ("scaleLogWithThreshold", [IEvDistribution(dist), IEvNumber(base), IEvNumber(eps)]) => + Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist, ~env) + | ("scaleMultiply", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toDistFn(Scale(#Multiply, float), dist, ~env) + | ("scalePow", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toDistFn(Scale(#Power, float), dist, ~env) + | ("scaleExp", [IEvDistribution(dist)]) => + Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist, ~env) + | ("cdf", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toFloatFn(#Cdf(float), dist, ~env) + | ("pdf", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toFloatFn(#Pdf(float), dist, ~env) + | ("inv", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toFloatFn(#Inv(float), dist, ~env) + | ("quantile", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toFloatFn(#Inv(float), dist, ~env) + | ("inspect", [IEvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist, ~env) + | ("truncateLeft", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toDistFn(Truncate(Some(float), None), dist, ~env) + | ("truncateRight", [IEvDistribution(dist), IEvNumber(float)]) => + Helpers.toDistFn(Truncate(None, Some(float)), dist, ~env) + | ("truncate", [IEvDistribution(dist), IEvNumber(float1), IEvNumber(float2)]) => + Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist, ~env) + | ("mx" | "mixture", args) => Helpers.mixture(args, ~env)->Some + | ("log", [IEvDistribution(a)]) => + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "log", + a, + GenericDist.fromFloat(MagicNumbers.Math.e), + ~env, + )->Some + | ("log10", [IEvDistribution(a)]) => + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "log", + a, + GenericDist.fromFloat(10.0), + ~env, + )->Some + | ("unaryMinus", [IEvDistribution(a)]) => + Helpers.twoDiststoDistFn( + Algebraic(AsDefault), + "multiply", + a, + GenericDist.fromFloat(-1.0), + ~env, + )->Some + | ( + ("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, + [_, _] as args, + ) => + Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => + Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd, ~env) + ) + | ( + ("dotAdd" + | "dotMultiply" + | "dotSubtract" + | "dotDivide" + | "dotPow") as arithmetic, + [_, _] as args, + ) => + Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => + Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd, ~env) + ) + | ("dotExp", [IEvDistribution(a)]) => + Helpers.twoDiststoDistFn( + Pointwise, + "dotPow", + GenericDist.fromFloat(MagicNumbers.Math.e), + a, + ~env, + )->Some + | _ => None + } + } + + let genericOutputToReducerValue = (o: DistributionOperation.outputType): result< + Reducer_T.value, + Reducer_ErrorValue.errorValue, + > => + switch o { + | Dist(d) => Ok(Reducer_T.IEvDistribution(d)) + | Float(d) => Ok(IEvNumber(d)) + | String(d) => Ok(IEvString(d)) + | Bool(d) => Ok(IEvBool(d)) + | FloatArray(d) => Ok(IEvArray(d |> E.A.fmap(r => Reducer_T.IEvNumber(r)))) + | GenDistError(err) => Error(REDistributionError(err)) + } + + let dispatch = (call: Reducer_Value.functionCall, environment) => + switch dispatchToGenericOutput(call, environment) { + | Some(o) => genericOutputToReducerValue(o) + | None => + Reducer_ErrorValue.REOther("Internal error in FR_GenericDist implementation") + ->Reducer_ErrorValue.ErrorException + ->raise + } +} + +let makeProxyFn = (name: string, inputs: array) => { + Function.make( + ~name, + ~nameSpace="", + ~requiresNamespace=false, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs, + ~run=(inputs, env, _) => Old.dispatch((name, inputs), env), + (), + ), + ], + (), + ) +} + +let makeOperationFns = (): array => { + let ops = [ + "add", + "multiply", + "subtract", + "divide", + "pow", + "log", + "dotAdd", + "dotMultiply", + "dotSubtract", + "dotDivide", + "dotPow", + ] + let twoArgTypes = [ + // can't use numeric+numeric, since number+number should be delegated to builtin arithmetics + [FRTypeDist, FRTypeNumber], + [FRTypeNumber, FRTypeDist], + [FRTypeDist, FRTypeDist], + ] + + ops->E.A2.fmap(op => twoArgTypes->E.A2.fmap(types => makeProxyFn(op, types)))->E.A.concatMany +} + +// TODO - duplicates the switch above, should rewrite with standard FR APIs +let library = E.A.concatMany([ + [ + makeProxyFn("triangular", [FRTypeNumber, FRTypeNumber, FRTypeNumber]), + makeProxyFn("sample", [FRTypeDist]), + makeProxyFn("sampleN", [FRTypeDist, FRTypeNumber]), + makeProxyFn("mean", [FRTypeDist]), + makeProxyFn("stdev", [FRTypeDist]), + makeProxyFn("variance", [FRTypeDist]), + makeProxyFn("min", [FRTypeDist]), + makeProxyFn("max", [FRTypeDist]), + makeProxyFn("mode", [FRTypeDist]), + makeProxyFn("integralSum", [FRTypeDist]), + makeProxyFn("toString", [FRTypeDist]), + makeProxyFn("sparkline", [FRTypeDist]), + makeProxyFn("sparkline", [FRTypeDist, FRTypeNumber]), + makeProxyFn("exp", [FRTypeDist]), + makeProxyFn("normalize", [FRTypeDist]), + makeProxyFn("isNormalized", [FRTypeDist]), + makeProxyFn("toPointSet", [FRTypeDist]), + makeProxyFn("scaleLog", [FRTypeDist]), + makeProxyFn("scaleLog10", [FRTypeDist]), + makeProxyFn("scaleLog", [FRTypeDist, FRTypeNumber]), + makeProxyFn("scaleLogWithThreshold", [FRTypeDist, FRTypeNumber, FRTypeNumber]), + makeProxyFn("scaleMultiply", [FRTypeDist, FRTypeNumber]), + makeProxyFn("scalePow", [FRTypeDist, FRTypeNumber]), + makeProxyFn("scaleExp", [FRTypeDist]), + makeProxyFn("cdf", [FRTypeDist, FRTypeNumber]), + makeProxyFn("pdf", [FRTypeDist, FRTypeNumber]), + makeProxyFn("inv", [FRTypeDist, FRTypeNumber]), + makeProxyFn("quantile", [FRTypeDist, FRTypeNumber]), + makeProxyFn("inspect", [FRTypeDist]), + makeProxyFn("truncateLeft", [FRTypeDist, FRTypeNumber]), + makeProxyFn("truncateRight", [FRTypeDist, FRTypeNumber]), + makeProxyFn("truncate", [FRTypeDist, FRTypeNumber, FRTypeNumber]), + makeProxyFn("log", [FRTypeDist]), + makeProxyFn("log10", [FRTypeDist]), + makeProxyFn("unaryMinus", [FRTypeDist]), + makeProxyFn("dotExp", [FRTypeDist]), + ], + makeOperationFns(), +]) + +// FIXME - impossible to implement with FR due to arbitrary parameters length; +let mxLambda = Reducer_Expression_Lambda.makeFFILambda((inputs, env, _) => { + switch Old.dispatch(("mx", inputs), env) { + | Ok(value) => value + | Error(e) => e->Reducer_ErrorValue.ErrorException->raise + } +}) diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res b/packages/squiggle-lang/src/rescript/FR/FR_List.res similarity index 65% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res rename to packages/squiggle-lang/src/rescript/FR/FR_List.res index c174272b..21106978 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_List.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_List.res @@ -1,6 +1,3 @@ -// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -// module ProjectReducerFnT = ReducerProject_ReducerFn_T - open FunctionRegistry_Core open FunctionRegistry_Helpers @@ -8,38 +5,32 @@ let nameSpace = "List" let requiresNamespace = true module Internals = { - let makeFromNumber = ( - n: float, - value: internalExpressionValue, - ): internalExpressionValue => IEvArray(Belt.Array.make(E.Float.toInt(n), value)) + let makeFromNumber = (n: float, value: Reducer_T.value): Reducer_T.value => IEvArray( + Belt.Array.make(E.Float.toInt(n), value), + ) - let upTo = (low: float, high: float): internalExpressionValue => IEvArray( + let upTo = (low: float, high: float): Reducer_T.value => IEvArray( E.A.Floats.range(low, high, (high -. low +. 1.0)->E.Float.toInt)->E.A2.fmap(Wrappers.evNumber), ) - let first = (v: array): result => + let first = (v: array): result => v->E.A.first |> E.O.toResult("No first element") - let last = (v: array): result => + let last = (v: array): result => v->E.A.last |> E.O.toResult("No last element") - let reverse = (array: array): internalExpressionValue => IEvArray( + let reverse = (array: array): Reducer_T.value => IEvArray( Belt.Array.reverse(array), ) let map = ( - array: array, - accessors: ProjectAccessorsT.t, + array: array, eLambdaValue, - reducer: ProjectReducerFnT.t, - ): ReducerInterface_InternalExpressionValue.t => { + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, + ): Reducer_T.value => { Belt.Array.map(array, elem => - Reducer_Expression_Lambda.doLambdaCall( - eLambdaValue, - list{elem}, - (accessors: ProjectAccessorsT.t), - (reducer: ProjectReducerFnT.t), - ) + Reducer_Expression_Lambda.doLambdaCall(eLambdaValue, [elem], env, reducer) )->Wrappers.evArray } @@ -47,11 +38,11 @@ module Internals = { aValueArray, initialValue, aLambdaValue, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, ) => { aValueArray->E.A.reduce(initialValue, (acc, elem) => - Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer) + Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer) ) } @@ -59,27 +50,22 @@ module Internals = { aValueArray, initialValue, aLambdaValue, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, ) => { aValueArray->Belt.Array.reduceReverse(initialValue, (acc, elem) => - Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list{acc, elem}, accessors, reducer) + Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer) ) } let filter = ( aValueArray, aLambdaValue, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, ) => { Js.Array2.filter(aValueArray, elem => { - let result = Reducer_Expression_Lambda.doLambdaCall( - aLambdaValue, - list{elem}, - accessors, - reducer, - ) + let result = Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [elem], env, reducer) switch result { | IEvBool(true) => true | _ => false @@ -100,7 +86,7 @@ let library = [ FnDefinition.make( ~name="make", ~inputs=[FRTypeNumber, FRTypeAny], - ~run=(inputs, _, _, _) => { + ~run=(inputs, _, _) => { switch inputs { | [IEvNumber(number), value] => Internals.makeFromNumber(number, value)->Ok | _ => Error(impossibleError) @@ -121,10 +107,11 @@ let library = [ FnDefinition.make( ~name="upTo", ~inputs=[FRTypeNumber, FRTypeNumber], - ~run=(_, inputs, _, _) => - inputs - ->Prepare.ToValueTuple.twoNumbers - ->E.R2.fmap(((low, high)) => Internals.upTo(low, high)), + ~run=(inputs, _, _) => + switch inputs { + | [IEvNumber(low), IEvNumber(high)] => Internals.upTo(low, high)->Ok + | _ => impossibleError->Error + }, (), ), ], @@ -139,9 +126,9 @@ let library = [ FnDefinition.make( ~name="first", ~inputs=[FRTypeArray(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { - | [IEvArray(array)] => Internals.first(array) + | [IEvArray(array)] => Internals.first(array)->E.R2.errMap(wrapError) | _ => Error(impossibleError) }, (), @@ -158,9 +145,9 @@ let library = [ FnDefinition.make( ~name="last", ~inputs=[FRTypeArray(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { - | [IEvArray(array)] => Internals.last(array) + | [IEvArray(array)] => Internals.last(array)->E.R2.errMap(wrapError) | _ => Error(impossibleError) }, (), @@ -178,7 +165,7 @@ let library = [ FnDefinition.make( ~name="reverse", ~inputs=[FRTypeArray(FRTypeAny)], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { | [IEvArray(array)] => Internals.reverse(array)->Ok | _ => Error(impossibleError) @@ -198,10 +185,9 @@ let library = [ FnDefinition.make( ~name="map", ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => + ~run=(inputs, env, reducer) => switch inputs { - | [IEvArray(array), IEvLambda(lambda)] => - Ok(Internals.map(array, accessors, lambda, reducer)) + | [IEvArray(array), IEvLambda(lambda)] => Ok(Internals.map(array, lambda, env, reducer)) | _ => Error(impossibleError) }, (), @@ -218,10 +204,10 @@ let library = [ FnDefinition.make( ~name="reduce", ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => + ~run=(inputs, env, reducer) => switch inputs { | [IEvArray(array), initialValue, IEvLambda(lambda)] => - Ok(Internals.reduce(array, initialValue, lambda, accessors, reducer)) + Ok(Internals.reduce(array, initialValue, lambda, env, reducer)) | _ => Error(impossibleError) }, (), @@ -238,10 +224,10 @@ let library = [ FnDefinition.make( ~name="reduceReverse", ~inputs=[FRTypeArray(FRTypeAny), FRTypeAny, FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => + ~run=(inputs, env, reducer) => switch inputs { | [IEvArray(array), initialValue, IEvLambda(lambda)] => - Ok(Internals.reduceReverse(array, initialValue, lambda, accessors, reducer)) + Ok(Internals.reduceReverse(array, initialValue, lambda, env, reducer)) | _ => Error(impossibleError) }, (), @@ -258,10 +244,10 @@ let library = [ FnDefinition.make( ~name="filter", ~inputs=[FRTypeArray(FRTypeAny), FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => + ~run=(inputs, env, reducer) => switch inputs { | [IEvArray(array), IEvLambda(lambda)] => - Ok(Internals.filter(array, lambda, accessors, reducer)) + Ok(Internals.filter(array, lambda, env, reducer)) | _ => Error(impossibleError) }, (), diff --git a/packages/squiggle-lang/src/rescript/FR/FR_Math.res b/packages/squiggle-lang/src/rescript/FR/FR_Math.res new file mode 100644 index 00000000..5d98485b --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_Math.res @@ -0,0 +1,21 @@ +open FunctionRegistry_Helpers + +let library = [ + // ported MathJS functions + // https://docs.google.com/spreadsheets/d/1bUK2RaBFg8aJHuzZcw9yXp8StCBH5If5sU2iRw1T_HY/edit + // TODO - implement the rest of useful stuff + + Make.f2f( + ~name="sqrt", + ~nameSpace="Math", + ~requiresNamespace=true, + ~fn=x => Js.Math.pow_float(~base=x, ~exp=0.5), + (), + ), + Make.f2f(~name="sin", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.sin, ()), + Make.f2f(~name="cos", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.cos, ()), + Make.f2f(~name="tan", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.tan, ()), + Make.f2f(~name="asin", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.asin, ()), + Make.f2f(~name="acos", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.acos, ()), + Make.f2f(~name="atan", ~nameSpace="Math", ~requiresNamespace=true, ~fn=Js.Math.atan, ()), +] diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Number.res b/packages/squiggle-lang/src/rescript/FR/FR_Number.res similarity index 90% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Number.res rename to packages/squiggle-lang/src/rescript/FR/FR_Number.res index 6305855d..336a3f2b 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Number.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Number.res @@ -9,34 +9,24 @@ module ArrayNumberDist = { FnDefinition.make( ~name, ~inputs=[FRTypeArray(FRTypeNumber)], - ~run=(_, inputs, _, _) => - Prepare.ToTypedArray.numbers(inputs) + ~run=(inputs, _, _) => + inputs + ->Prepare.ToTypedArray.numbers ->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r)) - ->E.R.bind(fn), - (), - ) - } - let make2 = (name, fn) => { - FnDefinition.make( - ~name, - ~inputs=[FRTypeArray(FRTypeAny)], - ~run=(_, inputs, _, _) => - Prepare.ToTypedArray.numbers(inputs) - ->E.R.bind(r => E.A.length(r) === 0 ? Error("List is empty") : Ok(r)) - ->E.R.bind(fn), + ->E.R.bind(fn) + ->E.R2.errMap(wrapError), (), ) } } let library = [ - Function.make( + Make.f2f( ~name="floor", ~nameSpace, ~requiresNamespace, - ~output=EvtNumber, ~examples=[`floor(3.5)`], - ~definitions=[DefineFn.Numbers.oneToOne("floor", Js.Math.floor_float)], + ~fn=Js.Math.floor_float, (), ), Function.make( diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res b/packages/squiggle-lang/src/rescript/FR/FR_Pointset.res similarity index 58% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res rename to packages/squiggle-lang/src/rescript/FR/FR_Pointset.res index ccc9a4a4..a12164bd 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Pointset.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Pointset.res @@ -4,45 +4,48 @@ open FunctionRegistry_Helpers let nameSpace = "PointSet" let requiresNamespace = true -let inputsTodist = (inputs: array, makeDist) => { - let array = inputs->getOrError(0)->E.R.bind(Prepare.ToValueArray.Array.openA) - let xyCoords = - array->E.R.bind(xyCoords => - xyCoords - ->E.A2.fmap(xyCoord => - [xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers) - ) - ->E.A.R.firstErrorOrOpen +let inputsToDist = (inputs: array, xyShapeToPointSetDist) => { + // TODO - rewrite in more functional/functor-based style + switch inputs { + | [IEvArray(items)] => + items + ->Belt.Array.map(item => + switch item { + | IEvRecord(map) => { + let xValue = map->Belt.Map.String.get("x") + let yValue = map->Belt.Map.String.get("y") + switch (xValue, yValue) { + | (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y) + | _ => impossibleError->Reducer_ErrorValue.toException + } + } + | _ => impossibleError->Reducer_ErrorValue.toException + } ) - let expressionValue = - xyCoords + ->Ok ->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString)) - ->E.R2.fmap(r => ReducerInterface_InternalExpressionValue.IEvDistribution( - PointSet(makeDist(r)), - )) - expressionValue + ->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist))) + | _ => impossibleError->Reducer_ErrorValue.toException + } } module Internal = { type t = PointSetDist.t - let toType = (r): result< - ReducerInterface_InternalExpressionValue.t, - Reducer_ErrorValue.errorValue, - > => + let toType = (r): result => switch r { | Ok(r) => Ok(Wrappers.evDistribution(PointSet(r))) | Error(err) => Error(REOperationError(err)) } - let doLambdaCall = (aLambdaValue, list, environment, reducer) => - switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, environment, reducer) { - | IEvNumber(f) => Ok(f) + let doLambdaCall = (aLambdaValue, list, env, reducer) => + switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) { + | Reducer_T.IEvNumber(f) => Ok(f) | _ => Error(Operation.SampleMapNeedsNtoNFunction) } let mapY = (pointSetDist: t, aLambdaValue, env, reducer) => { - let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, env, reducer) + let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], env, reducer) PointSetDist.T.mapYResult(~fn, pointSetDist)->toType } } @@ -53,23 +56,23 @@ let library = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`PointSet.fromDist(normal(5,2))`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="fromDist", ~inputs=[FRTypeDist], - ~run=(_, inputs, accessors, _) => + ~run=(inputs, env, _) => switch inputs { - | [FRValueDist(dist)] => + | [IEvDistribution(dist)] => GenericDist.toPointSet( dist, - ~xyPointLength=accessors.environment.xyPointLength, - ~sampleCount=accessors.environment.sampleCount, + ~xyPointLength=env.xyPointLength, + ~sampleCount=env.sampleCount, (), ) ->E.R2.fmap(Wrappers.pointSet) ->E.R2.fmap(Wrappers.evDistribution) - ->E.R2.errMap(_ => "") + ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e)) | _ => Error(impossibleError) }, (), @@ -82,15 +85,15 @@ let library = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`PointSet.mapY(mx(normal(5,2)), {|x| x + 1})`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="mapY", ~inputs=[FRTypeDist, FRTypeLambda], - ~run=(inputs, _, env, reducer) => + ~run=(inputs, env, reducer) => switch inputs { | [IEvDistribution(PointSet(dist)), IEvLambda(lambda)] => - Internal.mapY(dist, lambda, env, reducer)->E.R2.errMap(Reducer_ErrorValue.errorToString) + Internal.mapY(dist, lambda, env, reducer) | _ => Error(impossibleError) }, (), @@ -110,12 +113,13 @@ let library = [ {x: 3, y: 0.2} ])`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="makeContinuous", ~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))], - ~run=(_, inputs, _, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))), + ~run=(inputs, _, _) => + inputsToDist(inputs, r => Continuous(Continuous.make(r)))->E.R2.errMap(wrapError), (), ), ], @@ -133,12 +137,13 @@ let library = [ {x: 3, y: 0.2} ])`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="makeDiscrete", ~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))], - ~run=(_, inputs, _, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))), + ~run=(inputs, _, _) => + inputsToDist(inputs, r => Discrete(Discrete.make(r)))->E.R2.errMap(wrapError), (), ), ], diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res b/packages/squiggle-lang/src/rescript/FR/FR_Sampleset.res similarity index 66% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res rename to packages/squiggle-lang/src/rescript/FR/FR_Sampleset.res index 7d724a0f..9263bf29 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Sampleset.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Sampleset.res @@ -1,5 +1,3 @@ -// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -// module ProjectReducerFnT = ReducerProject_ReducerFn_T open FunctionRegistry_Core open FunctionRegistry_Helpers @@ -12,51 +10,46 @@ module Internal = { let doLambdaCall = ( aLambdaValue, list, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, ) => - switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) { + switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) { | IEvNumber(f) => Ok(f) | _ => Error(Operation.SampleMapNeedsNtoNFunction) } - let toType = (r): result< - ReducerInterface_InternalExpressionValue.t, - Reducer_ErrorValue.errorValue, - > => + let toType = (r): result => switch r { | Ok(r) => Ok(Wrappers.evDistribution(SampleSet(r))) | Error(r) => Error(REDistributionError(SampleSetError(r))) } //TODO: I don't know why this seems to need at least one input - let fromFn = (aLambdaValue, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => { - let sampleCount = accessors.environment.sampleCount - let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer) + let fromFn = (aLambdaValue, environment: Reducer_T.environment, reducer: Reducer_T.reducerFn) => { + let sampleCount = environment.sampleCount + let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer) Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen } - let map1 = (sampleSetDist: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { - let fn = r => doLambdaCall(aLambdaValue, list{IEvNumber(r)}, accessors, reducer) + let map1 = (sampleSetDist: t, aLambdaValue, environment: Reducer_T.environment, reducer) => { + let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer) SampleSetDist.samplesMap(~fn, sampleSetDist)->toType } - let map2 = (t1: t, t2: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { + let map2 = (t1: t, t2: t, aLambdaValue, environment: Reducer_T.environment, reducer) => { let fn = (a, b) => - doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b)}, accessors, reducer) + doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], environment, reducer) SampleSetDist.map2(~fn, ~t1, ~t2)->toType } - let map3 = (t1: t, t2: t, t3: t, aLambdaValue, accessors: ProjectAccessorsT.t, reducer) => { + let map3 = (t1: t, t2: t, t3: t, aLambdaValue, environment: Reducer_T.environment, reducer) => { let fn = (a, b, c) => - doLambdaCall(aLambdaValue, list{IEvNumber(a), IEvNumber(b), IEvNumber(c)}, accessors, reducer) + doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], environment, reducer) SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType } - let parseSampleSetArray = (arr: array): option< - array, - > => { - let parseSampleSet = (value: internalExpressionValue): option => + let parseSampleSetArray = (arr: array): option> => { + let parseSampleSet = (value: Reducer_T.value): option => switch value { | IEvDistribution(SampleSet(dist)) => Some(dist) | _ => None @@ -65,9 +58,9 @@ module Internal = { } let mapN = ( - aValueArray: array, + aValueArray: array, aLambdaValue, - accessors: ProjectAccessorsT.t, + environment: Reducer_T.environment, reducer, ) => { switch parseSampleSetArray(aValueArray) { @@ -75,8 +68,8 @@ module Internal = { let fn = a => doLambdaCall( aLambdaValue, - list{IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))}, - accessors, + [IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))], + environment, reducer, ) SampleSetDist.mapN(~fn, ~t1)->toType @@ -91,18 +84,18 @@ let libaryBase = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`SampleSet.fromDist(normal(5,2))`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="fromDist", ~inputs=[FRTypeDist], - ~run=(_, inputs, accessors: ProjectAccessorsT.t, _) => + ~run=(inputs, environment, _) => switch inputs { - | [FRValueDist(dist)] => - GenericDist.toSampleSetDist(dist, accessors.environment.sampleCount) + | [IEvDistribution(dist)] => + GenericDist.toSampleSetDist(dist, environment.sampleCount) ->E.R2.fmap(Wrappers.sampleSet) ->E.R2.fmap(Wrappers.evDistribution) - ->E.R2.errMap(DistributionTypes.Error.toString) + ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e)) | _ => Error(impossibleError) }, (), @@ -115,17 +108,19 @@ let libaryBase = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`SampleSet.fromList([3,5,2,3,5,2,3,5,2,3,3,5,3,2,3,1,1,3])`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="fromList", ~inputs=[FRTypeArray(FRTypeNumber)], - ~run=(_, inputs, _, _) => { + ~run=(inputs, _, _) => { let sampleSet = - Prepare.ToTypedArray.numbers(inputs) |> E.R2.bind(r => - SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??") - ) - sampleSet->E.R2.fmap(Wrappers.sampleSet)->E.R2.fmap(Wrappers.evDistribution) + inputs->Prepare.ToTypedArray.numbers + |> E.R2.bind(r => SampleSetDist.make(r)->E.R2.errMap(_ => "AM I HERE? WHYERE AMI??")) + sampleSet + ->E.R2.fmap(Wrappers.sampleSet) + ->E.R2.fmap(Wrappers.evDistribution) + ->E.R2.errMap(wrapError) }, (), ), @@ -137,12 +132,12 @@ let libaryBase = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`SampleSet.toList(SampleSet.fromDist(normal(5,2)))`], - ~output=ReducerInterface_InternalExpressionValue.EvtArray, + ~output=Reducer_Value.EvtArray, ~definitions=[ FnDefinition.make( ~name="toList", ~inputs=[FRTypeDist], - ~run=(inputs, _, _, _) => + ~run=(inputs, _, _) => switch inputs { | [IEvDistribution(SampleSet(dist))] => dist->E.A2.fmap(Wrappers.evNumber)->Wrappers.evArray->Ok @@ -158,17 +153,17 @@ let libaryBase = [ ~nameSpace, ~requiresNamespace=true, ~examples=[`SampleSet.fromFn({|| sample(normal(5,2))})`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="fromFn", ~inputs=[FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer: ProjectReducerFnT.t) => + ~run=(inputs, environment, reducer) => switch inputs { | [IEvLambda(lambda)] => - switch Internal.fromFn(lambda, accessors, reducer) { + switch Internal.fromFn(lambda, environment, reducer) { | Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution) - | Error(e) => Error(Operation.Error.toString(e)) + | Error(e) => e->Reducer_ErrorValue.REOperationError->Error } | _ => Error(impossibleError) }, @@ -182,15 +177,15 @@ let libaryBase = [ ~nameSpace, ~requiresNamespace, ~examples=[`SampleSet.map(SampleSet.fromDist(normal(5,2)), {|x| x + 1})`], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="map", ~inputs=[FRTypeDist, FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => + ~run=(inputs, environment, reducer) => switch inputs { | [IEvDistribution(SampleSet(dist)), IEvLambda(lambda)] => - Internal.map1(dist, lambda, accessors, reducer)->E.R2.errMap(_ => "") + Internal.map1(dist, lambda, environment, reducer) | _ => Error(impossibleError) }, (), @@ -205,19 +200,19 @@ let libaryBase = [ ~examples=[ `SampleSet.map2(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y| x + y})`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="map2", ~inputs=[FRTypeDist, FRTypeDist, FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => { + ~run=(inputs, environment, reducer) => { switch inputs { | [ IEvDistribution(SampleSet(dist1)), IEvDistribution(SampleSet(dist2)), IEvLambda(lambda), ] => - Internal.map2(dist1, dist2, lambda, accessors, reducer)->E.R2.errMap(_ => "") + Internal.map2(dist1, dist2, lambda, environment, reducer) | _ => Error(impossibleError) } }, @@ -233,12 +228,12 @@ let libaryBase = [ ~examples=[ `SampleSet.map3(SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), {|x, y, z| max([x,y,z])})`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="map3", ~inputs=[FRTypeDist, FRTypeDist, FRTypeDist, FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => + ~run=(inputs, environment, reducer) => switch inputs { | [ IEvDistribution(SampleSet(dist1)), @@ -246,7 +241,7 @@ let libaryBase = [ IEvDistribution(SampleSet(dist3)), IEvLambda(lambda), ] => - Internal.map3(dist1, dist2, dist3, lambda, accessors, reducer)->E.R2.errMap(_ => "") + Internal.map3(dist1, dist2, dist3, lambda, environment, reducer) | _ => Error(impossibleError) }, (), @@ -261,17 +256,15 @@ let libaryBase = [ ~examples=[ `SampleSet.mapN([SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2)), SampleSet.fromDist(normal(5,2))], {|x| max(x)})`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ FnDefinition.make( ~name="mapN", ~inputs=[FRTypeArray(FRTypeDist), FRTypeLambda], - ~run=(inputs, _, accessors: ProjectAccessorsT.t, reducer) => + ~run=(inputs, environment, reducer) => switch inputs { | [IEvArray(dists), IEvLambda(lambda)] => - Internal.mapN(dists, lambda, accessors, reducer)->E.R2.errMap(_e => { - "AHHH doesn't work" - }) + Internal.mapN(dists, lambda, environment, reducer) | _ => Error(impossibleError) }, (), @@ -286,7 +279,7 @@ module Comparison = { FnDefinition.make( ~name, ~inputs, - ~run=(inputs, _, _, _) => { + ~run=(inputs, _, _) => { run(inputs) }, (), @@ -296,7 +289,9 @@ module Comparison = { let wrapper = r => r ->E.R2.fmap(r => r->Wrappers.sampleSet->Wrappers.evDistribution) - ->E.R2.errMap(SampleSetDist.Error.toString) + ->E.R2.errMap(e => + e->DistributionTypes.Error.sampleErrorToDistErr->Reducer_ErrorValue.REDistributionError + ) let mkBig = (name, withDist, withFloat) => Function.make( @@ -308,7 +303,7 @@ module Comparison = { `SampleSet.${name}(SampleSet.fromDist(normal(5,2)), 3.0)`, `SampleSet.${name}(4.0, SampleSet.fromDist(normal(6,2)))`, ], - ~output=ReducerInterface_InternalExpressionValue.EvtDistribution, + ~output=Reducer_Value.EvtDistribution, ~definitions=[ template(name, [FRTypeDist, FRTypeDist], inputs => { switch inputs { diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res b/packages/squiggle-lang/src/rescript/FR/FR_Scoring.res similarity index 59% rename from packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res rename to packages/squiggle-lang/src/rescript/FR/FR_Scoring.res index d45b1a81..5b780da5 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/Library/FR_Scoring.res +++ b/packages/squiggle-lang/src/rescript/FR/FR_Scoring.res @@ -6,7 +6,7 @@ let requiresNamespace = true let runScoring = (estimate, answer, prior, env) => { GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env) ->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber) - ->E.R2.errMap(DistributionTypes.Error.toString) + ->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e)) } let library = [ @@ -30,17 +30,16 @@ let library = [ ("prior", FRTypeDist), ]), ], - ~run=(_, inputs, accessors, _) => { - switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(inputs) { - | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d)), FRValueDist(prior)]) => - runScoring(estimate, Score_Dist(d), Some(prior), accessors.environment) - | Ok([ - FRValueDist(estimate), - FRValueDistOrNumber(FRValueNumber(d)), - FRValueDist(prior), - ]) => - runScoring(estimate, Score_Scalar(d), Some(prior), accessors.environment) - | Error(e) => Error(e) + ~run=(inputs, environment, _) => { + switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs( + inputs, + ("estimate", "answer", "prior"), + ) { + | Ok([IEvDistribution(estimate), IEvDistribution(d), IEvDistribution(prior)]) => + runScoring(estimate, Score_Dist(d), Some(prior), environment) + | Ok([IEvDistribution(estimate), IEvNumber(d), IEvDistribution(prior)]) => + runScoring(estimate, Score_Scalar(d), Some(prior), environment) + | Error(e) => Error(e->FunctionRegistry_Helpers.wrapError) | _ => Error(FunctionRegistry_Helpers.impossibleError) } }, @@ -49,13 +48,16 @@ let library = [ FnDefinition.make( ~name="logScore", ~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])], - ~run=(_, inputs, accessors, _) => { - switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(inputs) { - | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueDist(d))]) => - runScoring(estimate, Score_Dist(d), None, accessors.environment) - | Ok([FRValueDist(estimate), FRValueDistOrNumber(FRValueNumber(d))]) => - runScoring(estimate, Score_Scalar(d), None, accessors.environment) - | Error(e) => Error(e) + ~run=(inputs, environment, _) => { + switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs( + inputs, + ("estimate", "answer"), + ) { + | Ok([IEvDistribution(estimate), IEvDistribution(d)]) => + runScoring(estimate, Score_Dist(d), None, environment) + | Ok([IEvDistribution(estimate), IEvNumber(d)]) => + runScoring(estimate, Score_Scalar(d), None, environment) + | Error(e) => Error(e->FunctionRegistry_Helpers.wrapError) | _ => Error(FunctionRegistry_Helpers.impossibleError) } }, @@ -74,10 +76,10 @@ let library = [ FnDefinition.make( ~name="klDivergence", ~inputs=[FRTypeDist, FRTypeDist], - ~run=(_, inputs, accessors, _) => { + ~run=(inputs, environment, _) => { switch inputs { - | [FRValueDist(estimate), FRValueDist(d)] => - runScoring(estimate, Score_Dist(d), None, accessors.environment) + | [IEvDistribution(estimate), IEvDistribution(d)] => + runScoring(estimate, Score_Dist(d), None, environment) | _ => Error(FunctionRegistry_Helpers.impossibleError) } }, diff --git a/packages/squiggle-lang/src/rescript/FR/FR_Units.res b/packages/squiggle-lang/src/rescript/FR/FR_Units.res new file mode 100644 index 00000000..8934b5d8 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/FR/FR_Units.res @@ -0,0 +1,34 @@ +open FunctionRegistry_Core + +let makeUnitFn = (name: string, multiplier: float) => { + Function.make( + ~name="fromUnit_" ++ name, + ~nameSpace="", + ~requiresNamespace=false, + ~definitions=[ + FnDefinition.make( + ~name="fromUnit_" ++ name, + ~inputs=[FRTypeNumber], + ~run=(inputs, _, _) => { + switch inputs { + | [IEvNumber(f)] => IEvNumber(f *. multiplier)->Ok + | _ => FunctionRegistry_Helpers.impossibleError->Error + } + }, + (), + ), + ], + (), + ) +} + +let library = [ + makeUnitFn("n", 1E-9), + makeUnitFn("m", 1E-3), + makeUnitFn("k", 1E3), + makeUnitFn("M", 1E6), + makeUnitFn("B", 1E9), + makeUnitFn("G", 1E9), + makeUnitFn("T", 1E12), + makeUnitFn("P", 1E15), +] diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_Distribution/ForTS_Distribution.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_Distribution/ForTS_Distribution.res index 80824ba9..6701b580 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_Distribution/ForTS_Distribution.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_Distribution/ForTS_Distribution.res @@ -8,7 +8,7 @@ type environment = ForTS_Distribution_Environment.environment //use @genType -let defaultEnvironment: environment = DistributionOperation.defaultEnv +let defaultEnvironment: environment = Reducer_Context.defaultEnvironment @module("./ForTS_Distribution_tag") @scope("distributionTag") external dtPointSet_: string = "PointSet" diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res index 18838899..ac9c167c 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res @@ -1,14 +1,14 @@ -@genType type reducerProject = ReducerProject_T.t //re-export +@genType type reducerProject = ReducerProject_T.project //re-export type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use type squiggleValue = ForTS_SquiggleValue.squiggleValue //use -type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //use +type squiggleValue_Record = ForTS_SquiggleValue.squiggleValue_Record //use type environment = ForTS_Distribution_Environment.environment //use module T = ReducerProject_T -module Private = ReducerProject.Private +module Private = ReducerProject /* PUBLIC FUNCTIONS @@ -35,35 +35,34 @@ A project has a public field tag with a constant value "reducerProject" project = {tag: "reducerProject"} */ @genType -let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject +let createProject = (): reducerProject => Private.createProject() /* Answer all the source ids of all the sources in the project. */ @genType -let getSourceIds = (project: reducerProject): array => - project->T.Private.castToInternalProject->Private.getSourceIds +let getSourceIds = (project: reducerProject): array => project->Private.getSourceIds /* Sets the source for a given source Id. */ @genType let setSource = (project: reducerProject, sourceId: string, value: string): unit => - project->T.Private.castToInternalProject->Private.setSource(sourceId, value) + project->Private.setSource(sourceId, value) /* Gets the source for a given source id. */ @genType let getSource = (project: reducerProject, sourceId: string): option => - project->T.Private.castToInternalProject->Private.getSource(sourceId) + project->Private.getSource(sourceId) /* Touches the source for a given source id. This and dependent, sources are set to be re-evaluated. */ @genType let touchSource = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.touchSource(sourceId) + project->Private.touchSource(sourceId) /* Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again. @@ -71,15 +70,13 @@ Cleans the compilation artifacts for a given source ID. The results stay untouch Normally, you would never need the compilation artifacts again as the results with the same sources would never change. However, they are needed in case of any debugging reruns */ @genType -let clean = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.clean(sourceId) +let clean = (project: reducerProject, sourceId: string): unit => project->Private.clean(sourceId) /* Cleans all the compilation artifacts in all of the project */ @genType -let cleanAll = (project: reducerProject): unit => - project->T.Private.castToInternalProject->Private.cleanAll +let cleanAll = (project: reducerProject): unit => project->Private.cleanAll /* Cleans results. Compilation stays untouched to be able to re-run the source. @@ -87,14 +84,13 @@ You would not do this if you were not trying to debug the source code. */ @genType let cleanResults = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.cleanResults(sourceId) + project->Private.cleanResults(sourceId) /* Cleans all results. Compilations remains untouched to rerun the source. */ @genType -let cleanAllResults = (project: reducerProject): unit => - project->T.Private.castToInternalProject->Private.cleanAllResults +let cleanAllResults = (project: reducerProject): unit => project->Private.cleanAllResults /* To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned. @@ -103,19 +99,19 @@ To set the includes one first has to call "parseIncludes". The parsed includes o let getIncludes = (project: reducerProject, sourceId: string): result< array, reducerErrorValue, -> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId) +> => project->Private.getIncludes(sourceId) /* Other sources contributing to the global namespace of this source. */ @genType let getPastChain = (project: reducerProject, sourceId: string): array => - project->T.Private.castToInternalProject->Private.getPastChain(sourceId) + project->Private.getPastChain(sourceId) /* Answers the source codes after which this source code is continuing */ @genType let getContinues = (project: reducerProject, sourceId: string): array => - project->T.Private.castToInternalProject->Private.getContinues(sourceId) + project->Private.getContinues(sourceId) /* "continues" acts like hidden includes in the source. @@ -124,35 +120,34 @@ let getContinues = (project: reducerProject, sourceId: string): array => */ @genType let setContinues = (project: reducerProject, sourceId: string, continues: array): unit => - project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues) + project->Private.setContinues(sourceId, continues) /* This source depends on the array of sources returned. */ @genType let getDependencies = (project: reducerProject, sourceId: string): array => - project->T.Private.castToInternalProject->Private.getDependencies(sourceId) + project->Private.getDependencies(sourceId) /* The sources returned are dependent on this */ @genType let getDependents = (project: reducerProject, sourceId: string): array => - project->T.Private.castToInternalProject->Private.getDependents(sourceId) + project->Private.getDependents(sourceId) /* Get the run order for the sources in the project. */ @genType -let getRunOrder = (project: reducerProject): array => - project->T.Private.castToInternalProject->Private.getRunOrder +let getRunOrder = (project: reducerProject): array => project->Private.getRunOrder /* Get the run order to get the results of this specific source */ @genType let getRunOrderFor = (project: reducerProject, sourceId: string) => - project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId) + project->Private.getRunOrderFor(sourceId) /* Parse includes so that you can load them before running. @@ -162,7 +157,7 @@ It is your responsibility to load the includes before running. @genType let parseIncludes = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.parseIncludes(sourceId) + project->Private.parseIncludes(sourceId) /* Parse the source code if it is not done already. @@ -171,28 +166,26 @@ You would need this function if you want to see the parse tree without running t */ @genType let rawParse = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.rawParse(sourceId) + project->Private.rawParse(sourceId) /* Runs a specific source code if it is not done already. The code is parsed if it is not already done. It runs the dependencies if it is not already done. */ @genType -let run = (project: reducerProject, sourceId: string): unit => - project->T.Private.castToInternalProject->Private.run(sourceId) +let run = (project: reducerProject, sourceId: string): unit => project->Private.run(sourceId) /* Runs all of the sources in a project. Their results and bindings will be available */ @genType -let runAll = (project: reducerProject): unit => - project->T.Private.castToInternalProject->Private.runAll +let runAll = (project: reducerProject): unit => project->Private.runAll /* Get the bindings after running this source fil. The bindings are local to the source */ @genType -let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Module => - project->T.Private.castToInternalProject->Private.getBindings(sourceId) +let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Record => + project->Private.getBindings(sourceId)->Reducer_Namespace.toMap /* Get the result after running this source file or the project @@ -201,7 +194,7 @@ Get the result after running this source file or the project let getResult = (project: reducerProject, sourceId: string): result< squiggleValue, reducerErrorValue, -> => project->T.Private.castToInternalProject->Private.getResult(sourceId) +> => project->Private.getResult(sourceId) /* This is a convenience function to get the result of a single source without creating a project. @@ -211,12 +204,15 @@ The source has to be include free @genType let evaluate = (sourceCode: string): ( result, - squiggleValue_Module, + squiggleValue_Record, ) => Private.evaluate(sourceCode) @genType let setEnvironment = (project: reducerProject, environment: environment): unit => - project->T.Private.castToInternalProject->Private.setEnvironment(environment) + project->Private.setEnvironment(environment) + +@genType +let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment /* Foreign function interface is intentionally demolished. diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res index 692ed3e6..8331e776 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res @@ -1,10 +1,8 @@ -@genType type squiggleValue = ReducerInterface_InternalExpressionValue.t //re-export +@genType type squiggleValue = Reducer_T.value //re-export type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use -@genType type squiggleValue_Array = ReducerInterface_InternalExpressionValue.squiggleArray //re-export recursive type -@genType type squiggleValue_Module = ReducerInterface_InternalExpressionValue.nameSpace //re-export recursive type -@genType type squiggleValue_Record = ReducerInterface_InternalExpressionValue.map //re-export recursive type -@genType type squiggleValue_Type = ReducerInterface_InternalExpressionValue.map //re-export recursive type +@genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type +@genType type squiggleValue_Record = Reducer_T.map //re-export recursive type type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use @@ -14,15 +12,9 @@ type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //us @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtArray_: string = "Array" -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtArrayString_: string = "ArrayString" - @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtBool_: string = "Bool" -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtCall_: string = "Call" - @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtDate_: string = "Date" @@ -35,9 +27,6 @@ external svtDistribution_: string = "Distribution" @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtLambda_: string = "Lambda" -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtModule_: string = "Module" - @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtNumber_: string = "Number" @@ -47,18 +36,9 @@ external svtRecord_: string = "Record" @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtString_: string = "String" -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtSymbol_: string = "Symbol" - @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtTimeDuration_: string = "TimeDuration" -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtType_: string = "Type" - -@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") -external svtTypeIdentifier_: string = "TypeIdentifier" - @module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag") external svtVoid_: string = "Void" @@ -71,33 +51,26 @@ external castEnum: string => squiggleValueTag = "%identity" let getTag = (variant: squiggleValue): squiggleValueTag => switch variant { | IEvArray(_) => svtArray_->castEnum - | IEvArrayString(_) => svtArrayString_->castEnum | IEvBool(_) => svtBool_->castEnum - | IEvCall(_) => svtCall_->castEnum //Impossible | IEvDate(_) => svtDate_->castEnum | IEvDeclaration(_) => svtDeclaration_->castEnum | IEvDistribution(_) => svtDistribution_->castEnum | IEvLambda(_) => svtLambda_->castEnum - | IEvBindings(_) => svtModule_->castEnum //Impossible | IEvNumber(_) => svtNumber_->castEnum | IEvRecord(_) => svtRecord_->castEnum | IEvString(_) => svtString_->castEnum - | IEvSymbol(_) => svtSymbol_->castEnum | IEvTimeDuration(_) => svtTimeDuration_->castEnum - | IEvType(_) => svtType_->castEnum - | IEvTypeIdentifier(_) => svtTypeIdentifier_->castEnum | IEvVoid => svtVoid_->castEnum } @genType -let toString = (variant: squiggleValue) => - ReducerInterface_InternalExpressionValue.toString(variant) +let toString = (variant: squiggleValue) => Reducer_Value.toString(variant) // This is a useful method for unit tests. // Convert the result along with the error message to a string. @genType let toStringResult = (variantResult: result) => - ReducerInterface_InternalExpressionValue.toStringResult(variantResult) + Reducer_Value.toStringResult(variantResult) @genType let getArray = (variant: squiggleValue): option => @@ -107,13 +80,6 @@ let getArray = (variant: squiggleValue): option => | _ => None } -@genType -let getArrayString = (variant: squiggleValue): option> => - switch variant { - | IEvArrayString(value) => value->Some - | _ => None - } - @genType let getBool = (variant: squiggleValue): option => switch variant { @@ -121,13 +87,6 @@ let getBool = (variant: squiggleValue): option => | _ => None } -@genType -let getCall = (variant: squiggleValue): option => - switch variant { - | IEvCall(value) => value->Some - | _ => None - } - @genType let getDate = (variant: squiggleValue): option => switch variant { @@ -156,13 +115,6 @@ let getLambda = (variant: squiggleValue): option => | _ => None } -@genType -let getModule = (variant: squiggleValue): option => - switch variant { - | IEvBindings(value) => value->Some - | _ => None - } - @genType let getNumber = (variant: squiggleValue): option => switch variant { @@ -184,30 +136,9 @@ let getString = (variant: squiggleValue): option => | _ => None } -@genType -let getSymbol = (variant: squiggleValue): option => - switch variant { - | IEvSymbol(value) => value->Some - | _ => None - } - @genType let getTimeDuration = (variant: squiggleValue): option => switch variant { | IEvTimeDuration(value) => value->Some | _ => None } - -@genType -let getType = (variant: squiggleValue): option => - switch variant { - | IEvType(value) => value->Some - | _ => None - } - -@genType -let getTypeIdentifier = (variant: squiggleValue): option => - switch variant { - | IEvTypeIdentifier(value) => value->Some - | _ => None - } diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.res index cfe128e7..51854580 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.res @@ -2,9 +2,7 @@ type squiggleValue = ForTS_SquiggleValue.squiggleValue @genType type squiggleValue_Array = ForTS_SquiggleValue.squiggleValue_Array //re-export recursive type @genType -let getValues = (v: squiggleValue_Array): array => - ReducerInterface_InternalExpressionValue.arrayToValueArray(v) +let getValues = (v: squiggleValue_Array): array => Reducer_Value.arrayToValueArray(v) @genType -let toString = (v: squiggleValue_Array): string => - ReducerInterface_InternalExpressionValue.toStringArray(v) +let toString = (v: squiggleValue_Array): string => Reducer_Value.toStringArray(v) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.res index 7fe342f8..996668de 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.res @@ -1,5 +1,4 @@ -@genType type squiggleValue_Declaration = ReducerInterface_InternalExpressionValue.lambdaDeclaration //re-export +@genType type squiggleValue_Declaration = Reducer_T.lambdaDeclaration //re-export @genType -let toString = (v: squiggleValue_Declaration): string => - ReducerInterface_InternalExpressionValue.toStringDeclaration(v) +let toString = (v: squiggleValue_Declaration): string => Reducer_Value.toStringDeclaration(v) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Distribution.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Distribution.res index c346701c..ff3e45d8 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Distribution.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Distribution.res @@ -1,5 +1,4 @@ @genType type squiggleValue_Distribution = ForTS_Distribution.distribution @genType -let toString = (v: squiggleValue_Distribution): string => - ReducerInterface_InternalExpressionValue.toStringDistribution(v) +let toString = (v: squiggleValue_Distribution): string => Reducer_Value.toStringDistribution(v) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.res index 9f176576..a9793e87 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.res @@ -1,8 +1,7 @@ -@genType type squiggleValue_Lambda = ReducerInterface_InternalExpressionValue.lambdaValue //re-export +@genType type squiggleValue_Lambda = Reducer_T.lambdaValue //re-export @genType -let toString = (v: squiggleValue_Lambda): string => - ReducerInterface_InternalExpressionValue.toStringFunction(v) +let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringFunction(v) @genType let parameters = (v: squiggleValue_Lambda): array => { diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.res deleted file mode 100644 index e76acbc8..00000000 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.res +++ /dev/null @@ -1,16 +0,0 @@ -type squiggleValue = ForTS_SquiggleValue.squiggleValue //use -@genType type squiggleValue_Module = ForTS_SquiggleValue.squiggleValue_Module //re-export recursive type - -@genType -let getKeyValuePairs = (v: squiggleValue_Module): array<(string, squiggleValue)> => - ReducerInterface_InternalExpressionValue.nameSpaceToKeyValuePairs(v) - -@genType -let toString = (v: squiggleValue_Module): string => - ReducerInterface_InternalExpressionValue.toStringNameSpace(v) - -@genType -let toSquiggleValue = (v: squiggleValue_Module): squiggleValue => IEvBindings(v) - -@genType -let get = ReducerInterface_InternalExpressionValue.nameSpaceGet diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.res index 340abcfc..2f0d034a 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.res @@ -3,7 +3,10 @@ type squiggleValue = ForTS_SquiggleValue.squiggleValue //use @genType let getKeyValuePairs = (value: squiggleValue_Record): array<(string, squiggleValue)> => - ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value) + Reducer_Value.recordToKeyValuePairs(value) @genType -let toString = (v: squiggleValue_Record) => ReducerInterface_InternalExpressionValue.toStringMap(v) +let toString = (v: squiggleValue_Record) => Reducer_Value.toStringMap(v) + +@genType +let toSquiggleValue = (v: squiggleValue_Record): squiggleValue => IEvRecord(v) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.res deleted file mode 100644 index 1399dcf4..00000000 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.res +++ /dev/null @@ -1,10 +0,0 @@ -type squiggleValue = ForTS_SquiggleValue.squiggleValue //use -@genType type squiggleValue_Type = ForTS_SquiggleValue.squiggleValue_Type //re-export recursive type - -@genType -let getKeyValuePairs = (value: squiggleValue_Type): array<(string, squiggleValue)> => - ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value) - -@genType -let toString = (value: squiggleValue_Type): string => - ReducerInterface_InternalExpressionValue.toStringType(value) diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts index a54b38e0..11d82e6d 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag.ts @@ -1,19 +1,13 @@ export enum squiggleValueTag { Array = "Array", - ArrayString = "ArrayString", Bool = "Bool", - Call = "Call", Date = "Date", Declaration = "Declaration", Distribution = "Distribution", Lambda = "Lambda", - Module = "Module", Number = "Number", Record = "Record", String = "String", - Symbol = "Symbol", TimeDuration = "TimeDuration", - Type = "Type", - TypeIdentifier = "TypeIdentifier", Void = "Void", } diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res index b97c93e0..1cd74827 100644 --- a/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res +++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res @@ -6,9 +6,7 @@ @genType type squiggleValue_Array = ForTS_SquiggleValue_Array.squiggleValue_Array //re-export @genType type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //re-export @genType type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //re-export -@genType type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //re-export @genType type squiggleValue_Record = ForTS_SquiggleValue_Record.squiggleValue_Record //re-export -@genType type squiggleValue_Type = ForTS_SquiggleValue_Type.squiggleValue_Type //re-export /* Distribution related */ @genType type squiggleValue_Distribution = ForTS_Distribution.distribution //re-export diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res index edce5ac2..9683147f 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Core.res @@ -1,7 +1,5 @@ -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -type internalExpressionValue = ReducerInterface_InternalExpressionValue.t -type internalExpressionValueType = ReducerInterface_InternalExpressionValue.internalExpressionValueType +type internalExpressionValueType = Reducer_Value.internalExpressionValueType +type errorValue = Reducer_ErrorValue.errorValue /* Function Registry "Type". A type, without any other information. @@ -9,7 +7,10 @@ type internalExpressionValueType = ReducerInterface_InternalExpressionValue.inte */ type rec frType = | FRTypeNumber + | FRTypeBool | FRTypeNumeric + | FRTypeDate + | FRTypeTimeDuration | FRTypeDistOrNumber | FRTypeDist | FRTypeLambda @@ -22,35 +23,16 @@ type rec frType = and frTypeRecord = array and frTypeRecordParam = (string, frType) -/* - Function Registry "Value". A type, with the information of that type. - Like, #Float(40.0) -*/ -type rec frValue = - | FRValueNumber(float) - | FRValueDist(DistributionTypes.genericDist) - | FRValueArray(array) - | FRValueDistOrNumber(frValueDistOrNumber) - | FRValueRecord(frValueRecord) - | FRValueLambda(ReducerInterface_InternalExpressionValue.lambdaValue) - | FRValueString(string) - | FRValueVariant(string) - | FRValueAny(frValue) - | FRValueDict(Js.Dict.t) -and frValueRecord = array -and frValueRecordParam = (string, frValue) -and frValueDictParam = (string, frValue) -and frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist) +type frValueDistOrNumber = FRValueNumber(float) | FRValueDist(DistributionTypes.genericDist) type fnDefinition = { name: string, inputs: array, run: ( - array, - array, - ProjectAccessorsT.t, - ProjectReducerFnT.t, - ) => result, + array, + Reducer_T.environment, + Reducer_T.reducerFn, + ) => result, } type function = { @@ -64,14 +46,14 @@ type function = { isExperimental: bool, } -type fnNameDict = Js.Dict.t> -type registry = {functions: array, fnNameDict: fnNameDict} - module FRType = { type t = frType let rec toString = (t: t) => switch t { | FRTypeNumber => "number" + | FRTypeBool => "bool" + | FRTypeDate => "date" + | FRTypeTimeDuration => "duration" | FRTypeNumeric => "numeric" | FRTypeDist => "distribution" | FRTypeDistOrNumber => "distribution|number" @@ -87,284 +69,44 @@ module FRType = { | FRTypeAny => `any` } - let rec toFrValue = (r: internalExpressionValue): option => - switch r { - | IEvNumber(f) => Some(FRValueNumber(f)) - | IEvString(f) => Some(FRValueString(f)) - | IEvDistribution(f) => Some(FRValueDistOrNumber(FRValueDist(f))) - | IEvLambda(f) => Some(FRValueLambda(f)) - | IEvArray(elements) => - elements->E.A2.fmap(toFrValue)->E.A.O.openIfAllSome->E.O2.fmap(r => FRValueArray(r)) - | IEvRecord(map) => - Belt.Map.String.toArray(map) - ->E.A2.fmap(((key, item)) => item->toFrValue->E.O2.fmap(o => (key, o))) - ->E.A.O.openIfAllSome - ->E.O2.fmap(r => FRValueRecord(r)) - | _ => None - } - - let rec matchWithExpressionValue = (t: t, r: internalExpressionValue): option => + let rec matchWithValue = (t: t, r: Reducer_T.value): bool => switch (t, r) { - | (FRTypeAny, f) => toFrValue(f) - | (FRTypeString, IEvString(f)) => Some(FRValueString(f)) - | (FRTypeNumber, IEvNumber(f)) => Some(FRValueNumber(f)) - | (FRTypeDistOrNumber, IEvNumber(f)) => Some(FRValueDistOrNumber(FRValueNumber(f))) - | (FRTypeDistOrNumber, IEvDistribution(Symbolic(#Float(f)))) => - Some(FRValueDistOrNumber(FRValueNumber(f))) - | (FRTypeDistOrNumber, IEvDistribution(f)) => Some(FRValueDistOrNumber(FRValueDist(f))) - | (FRTypeDist, IEvDistribution(f)) => Some(FRValueDist(f)) - | (FRTypeNumeric, IEvNumber(f)) => Some(FRValueNumber(f)) - | (FRTypeNumeric, IEvDistribution(Symbolic(#Float(f)))) => Some(FRValueNumber(f)) - | (FRTypeLambda, IEvLambda(f)) => Some(FRValueLambda(f)) - | (FRTypeArray(intendedType), IEvArray(elements)) => { - let el = elements->E.A2.fmap(matchWithExpressionValue(intendedType)) - E.A.O.openIfAllSome(el)->E.O2.fmap(r => FRValueArray(r)) - } + | (FRTypeAny, _) => true + | (FRTypeString, IEvString(_)) => true + | (FRTypeNumber, IEvNumber(_)) => true + | (FRTypeBool, IEvBool(_)) => true + | (FRTypeDate, IEvDate(_)) => true + | (FRTypeTimeDuration, IEvTimeDuration(_)) => true + | (FRTypeDistOrNumber, IEvNumber(_)) => true + | (FRTypeDistOrNumber, IEvDistribution(_)) => true + | (FRTypeDist, IEvDistribution(_)) => true + | (FRTypeNumeric, IEvNumber(_)) => true + | (FRTypeNumeric, IEvDistribution(Symbolic(#Float(_)))) => true + | (FRTypeLambda, IEvLambda(_)) => true + | (FRTypeArray(intendedType), IEvArray(elements)) => + elements->Belt.Array.every(v => matchWithValue(intendedType, v)) | (FRTypeDict(r), IEvRecord(map)) => - map - ->Belt.Map.String.toArray - ->E.A2.fmap(((key, item)) => matchWithExpressionValue(r, item)->E.O2.fmap(o => (key, o))) - ->E.A.O.openIfAllSome - ->E.O2.fmap(r => FRValueDict(Js.Dict.fromArray(r))) - | (FRTypeRecord(recordParams), IEvRecord(map)) => { - let getAndMatch = (name, input) => - Belt.Map.String.get(map, name)->E.O.bind(matchWithExpressionValue(input)) - //All names in the type must be present. If any are missing, the corresponding - //value will be None, and this function would return None. - let namesAndValues: array> = - recordParams->E.A2.fmap(((name, input)) => - getAndMatch(name, input)->E.O2.fmap(match => (name, match)) - ) - namesAndValues->E.A.O.openIfAllSome->E.O2.fmap(r => FRValueRecord(r)) - } - | _ => None + map->Belt.Map.String.valuesToArray->Belt.Array.every(v => matchWithValue(r, v)) + | (FRTypeRecord(recordParams), IEvRecord(map)) => + recordParams->Belt.Array.every(((name, input)) => { + switch map->Belt.Map.String.get(name) { + | Some(v) => matchWithValue(input, v) + | None => false + } + }) + | _ => false } - let rec matchReverse = (e: frValue): internalExpressionValue => - switch e { - | FRValueNumber(f) => IEvNumber(f) - | FRValueDistOrNumber(FRValueNumber(n)) => IEvNumber(n) - | FRValueDistOrNumber(FRValueDist(n)) => IEvDistribution(n) - | FRValueDist(dist) => IEvDistribution(dist) - | FRValueArray(elements) => IEvArray(elements->E.A2.fmap(matchReverse)) - | FRValueRecord(frValueRecord) => { - let map = - frValueRecord - ->E.A2.fmap(((name, value)) => (name, matchReverse(value))) - ->Belt.Map.String.fromArray - IEvRecord(map) - } - | FRValueDict(frValueRecord) => { - let map = - frValueRecord - ->Js.Dict.entries - ->E.A2.fmap(((name, value)) => (name, matchReverse(value))) - ->Belt.Map.String.fromArray - IEvRecord(map) - } - | FRValueLambda(l) => IEvLambda(l) - | FRValueString(string) => IEvString(string) - | FRValueVariant(string) => IEvString(string) - | FRValueAny(f) => matchReverse(f) - } - - let matchWithExpressionValueArray = ( - inputs: array, - args: array, - ): option> => { + let matchWithValueArray = (inputs: array, args: array): bool => { let isSameLength = E.A.length(inputs) == E.A.length(args) if !isSameLength { - None + false } else { - E.A.zip(inputs, args) - ->E.A2.fmap(((input, arg)) => matchWithExpressionValue(input, arg)) - ->E.A.O.openIfAllSome + E.A.zip(inputs, args)->Belt.Array.every(((input, arg)) => matchWithValue(input, arg)) } } } -/* - This module, Matcher, is fairly lengthy. However, only two functions from it - are meant to be used outside of it. These are findMatches and matchToDef in Matches.Registry. - The rest of it is just called from those two functions. - - Update: This really should be completely re-done sometime, and tested. It works, but it's pretty messy. I'm sure - there are internal bugs, but the end functionality works, so I'm not too worried. -*/ -module Matcher = { - module MatchSimple = { - type t = DifferentName | SameNameDifferentArguments | FullMatch - - let isFullMatch = (match: t) => - switch match { - | FullMatch => true - | _ => false - } - - let isNameMatchOnly = (match: t) => - switch match { - | SameNameDifferentArguments => true - | _ => false - } - } - - module Match = { - type t<'a, 'b> = DifferentName | SameNameDifferentArguments('a) | FullMatch('b) - - let isFullMatch = (match: t<'a, 'b>): bool => - switch match { - | FullMatch(_) => true - | _ => false - } - - let isNameMatchOnly = (match: t<'a, 'b>) => - switch match { - | SameNameDifferentArguments(_) => true - | _ => false - } - } - - module FnDefinition = { - let matchAssumingSameName = (f: fnDefinition, args: array) => { - switch FRType.matchWithExpressionValueArray(f.inputs, args) { - | Some(_) => MatchSimple.FullMatch - | None => MatchSimple.SameNameDifferentArguments - } - } - - let match = (f: fnDefinition, fnName: string, args: array) => { - if f.name !== fnName { - MatchSimple.DifferentName - } else { - matchAssumingSameName(f, args) - } - } - } - - module Function = { - type definitionId = int - type match = Match.t, definitionId> - - let match = ( - f: function, - nameSpace: option, - fnName: string, - args: array, - ): match => { - switch nameSpace { - | Some(ns) if ns !== f.nameSpace => Match.DifferentName - | _ => { - let matchedDefinition = () => - E.A.getIndexBy(f.definitions, r => - MatchSimple.isFullMatch(FnDefinition.match(r, fnName, args)) - ) |> E.O.fmap(r => Match.FullMatch(r)) - let getMatchedNameOnlyDefinition = () => { - let nameMatchIndexes = - f.definitions - ->E.A2.fmapi((index, r) => - MatchSimple.isNameMatchOnly(FnDefinition.match(r, fnName, args)) - ? Some(index) - : None - ) - ->E.A.O.concatSomes - switch nameMatchIndexes { - | [] => None - | elements => Some(Match.SameNameDifferentArguments(elements)) - } - } - - E.A.O.firstSomeFnWithDefault( - [matchedDefinition, getMatchedNameOnlyDefinition], - Match.DifferentName, - ) - } - } - } - } - - module RegistryMatch = { - type match = { - nameSpace: string, - fnName: string, - inputIndex: int, - } - let makeMatch = (nameSpace: string, fnName: string, inputIndex: int) => { - nameSpace: nameSpace, - fnName: fnName, - inputIndex: inputIndex, - } - } - - module Registry = { - let _findExactMatches = ( - r: registry, - nameSpace: option, - fnName: string, - args: array, - ) => { - let functionMatchPairs = - r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args))) - let fullMatch = functionMatchPairs->E.A.getBy(((_, match)) => Match.isFullMatch(match)) - fullMatch->E.O.bind(((fn, match)) => - switch match { - | FullMatch(index) => Some(RegistryMatch.makeMatch(fn.nameSpace, fn.name, index)) - | _ => None - } - ) - } - - let _findNameMatches = ( - r: registry, - nameSpace: option, - fnName: string, - args: array, - ) => { - let functionMatchPairs = - r.functions->E.A2.fmap(l => (l, Function.match(l, nameSpace, fnName, args))) - let getNameMatches = - functionMatchPairs - ->E.A2.fmap(((fn, match)) => Match.isNameMatchOnly(match) ? Some((fn, match)) : None) - ->E.A.O.concatSomes - let matches = - getNameMatches - ->E.A2.fmap(((fn, match)) => - switch match { - | SameNameDifferentArguments(indexes) => - indexes->E.A2.fmap(index => RegistryMatch.makeMatch(fn.nameSpace, fn.name, index)) - | _ => [] - } - ) - ->Belt.Array.concatMany - E.A.toNoneIfEmpty(matches) - } - - let findMatches = (r: registry, fnName: string, args: array) => { - let fnNameInParts = Js.String.split(".", fnName) - let fnToSearch = E.A.get(fnNameInParts, 1) |> E.O.default(fnNameInParts[0]) - let nameSpace = E.A.length(fnNameInParts) > 1 ? Some(fnNameInParts[0]) : None - - switch _findExactMatches(r, nameSpace, fnToSearch, args) { - | Some(r) => Match.FullMatch(r) - | None => - switch _findNameMatches(r, nameSpace, fnToSearch, args) { - | Some(r) => Match.SameNameDifferentArguments(r) - | None => Match.DifferentName - } - } - } - - let matchToDef = ( - registry: registry, - {nameSpace, fnName, inputIndex}: RegistryMatch.match, - ): option => - registry.functions - ->E.A.getBy(fn => { - nameSpace === fn.nameSpace && fnName === fn.name - }) - ->E.O.bind(fn => E.A.get(fn.definitions, inputIndex)) - } -} - module FnDefinition = { type t = fnDefinition @@ -373,24 +115,19 @@ module FnDefinition = { t.name ++ `(${inputs})` } - let isMatch = (t: t, args: array) => { - let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) - switch argValues { - | Some(_) => true - | None => false - } + let isMatch = (t: t, args: array) => { + FRType.matchWithValueArray(t.inputs, args) } let run = ( t: t, - args: array, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, + args: array, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, ) => { - let argValues = FRType.matchWithExpressionValueArray(t.inputs, args) - switch argValues { - | Some(values) => t.run(args, values, accessors, reducer) - | None => Error("Incorrect Types") + switch t->isMatch(args) { + | true => t.run(args, env, reducer) + | false => REOther("Incorrect Types")->Error } } @@ -442,43 +179,41 @@ module Function = { } } -module NameSpace = { - type t = {name: string, functions: array} - let definitions = (t: t) => t.functions->E.A2.fmap(f => f.definitions)->E.A.concatMany - let uniqueFnNames = (t: t) => definitions(t)->E.A2.fmap(r => r.name)->E.A.uniq - let nameToDefinitions = (t: t, name: string) => definitions(t)->E.A2.filter(r => r.name == name) -} - module Registry = { + type fnNameDict = Belt.Map.String.t> + type registry = {functions: array, fnNameDict: fnNameDict} + let toJson = (r: registry) => r.functions->E.A2.fmap(Function.toJson) let allExamples = (r: registry) => r.functions->E.A2.fmap(r => r.examples)->E.A.concatMany let allExamplesWithFns = (r: registry) => r.functions->E.A2.fmap(fn => fn.examples->E.A2.fmap(example => (fn, example)))->E.A.concatMany + let allNames = (r: registry) => r.fnNameDict->Belt.Map.String.keysToArray + let _buildFnNameDict = (r: array): fnNameDict => { - let allDefinitionsWithFns = - r - ->E.A2.fmap(fn => fn.definitions->E.A2.fmap(definitions => (fn, definitions))) - ->E.A.concatMany - let functionsWithFnNames = - allDefinitionsWithFns - ->E.A2.fmap(((fn, def)) => { - let nameWithNamespace = `${fn.nameSpace}.${def.name}` - let nameWithoutNamespace = def.name - fn.requiresNamespace - ? [(nameWithNamespace, fn)] - : [(nameWithNamespace, fn), (nameWithoutNamespace, fn)] + // Three layers of reduce: + // 1. functions + // 2. definitions of each function + // 3. name variations of each definition + r->Belt.Array.reduce(Belt.Map.String.empty, (acc, fn) => + fn.definitions->Belt.Array.reduce(acc, (acc, def) => { + let names = + [ + fn.nameSpace == "" ? [] : [`${fn.nameSpace}.${def.name}`], + fn.requiresNamespace ? [] : [def.name], + ]->E.A.concatMany + + names->Belt.Array.reduce(acc, (acc, name) => { + switch acc->Belt.Map.String.get(name) { + | Some(fns) => { + let _ = fns->Js.Array2.push(def) // mutates the array, no need to update acc + acc + } + | None => acc->Belt.Map.String.set(name, [def]) + } + }) }) - ->E.A.concatMany - let uniqueNames = functionsWithFnNames->E.A2.fmap(((name, _)) => name)->E.A.uniq - let cacheAsArray: array<(string, array)> = uniqueNames->E.A2.fmap(uniqueName => { - let relevantItems = - E.A2.filter(functionsWithFnNames, ((defName, _)) => defName == uniqueName)->E.A2.fmap( - E.Tuple2.second, - ) - (uniqueName, relevantItems) - }) - cacheAsArray->Js.Dict.fromArray + ) } let make = (fns: array): registry => { @@ -486,48 +221,31 @@ module Registry = { {functions: fns, fnNameDict: dict} } - /* - There's a (potential+minor) bug here: If a function definition is called outside of the calls - to the registry, then it's possible that there could be a match after the registry is - called. However, for now, we could just call the registry last. - */ - let _matchAndRun = ( - ~registry: registry, - ~fnName: string, - ~args: array, - ~accessors: ProjectAccessorsT.t, - ~reducer: ProjectReducerFnT.t, - ) => { - let relevantFunctions = Js.Dict.get(registry.fnNameDict, fnName) |> E.O.default([]) - let modified = {functions: relevantFunctions, fnNameDict: registry.fnNameDict} - let matchToDef = m => Matcher.Registry.matchToDef(registry, m) - let showNameMatchDefinitions = matches => { - let defs = - matches - ->E.A2.fmap(matchToDef) - ->E.A.O.concatSomes - ->E.A2.fmap(FnDefinition.toString) - ->E.A2.fmap(r => `[${r}]`) - ->E.A2.joinWith("; ") - `There are function matches for ${fnName}(), but with different arguments: ${defs}` - } - - switch Matcher.Registry.findMatches(modified, fnName, args) { - | Matcher.Match.FullMatch(match) => - match->matchToDef->E.O2.fmap(FnDefinition.run(_, args, accessors, reducer)) - | SameNameDifferentArguments(m) => Some(Error(showNameMatchDefinitions(m))) - | _ => None - } - } - - let dispatch = ( + let call = ( registry, - (fnName, args): ReducerInterface_InternalExpressionValue.functionCall, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, - ) => { - _matchAndRun(~registry, ~fnName, ~args, ~accessors, ~reducer)->E.O2.fmap( - E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)), - ) + fnName: string, + args: array, + env: Reducer_T.environment, + reducer: Reducer_T.reducerFn, + ): result => { + switch Belt.Map.String.get(registry.fnNameDict, fnName) { + | Some(definitions) => { + let showNameMatchDefinitions = () => { + let defsString = + definitions + ->E.A2.fmap(FnDefinition.toString) + ->E.A2.fmap(r => `[${r}]`) + ->E.A2.joinWith("; ") + `There are function matches for ${fnName}(), but with different arguments: ${defsString}` + } + + let match = definitions->Js.Array2.find(def => def->FnDefinition.isMatch(args)) + switch match { + | Some(def) => def->FnDefinition.run(args, env, reducer) + | None => REOther(showNameMatchDefinitions())->Error + } + } + | None => RESymbolNotFound(fnName)->Error + } } } diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res index 3fce1b54..9a7f04db 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Helpers.res @@ -1,59 +1,65 @@ open FunctionRegistry_Core +open Reducer_T -let impossibleError = "Wrong inputs / Logically impossible" +let impossibleErrorString = "Wrong inputs / Logically impossible" +let impossibleError: errorValue = impossibleErrorString->Reducer_ErrorValue.REOther +let wrapError = e => Reducer_ErrorValue.REOther(e) module Wrappers = { let symbolic = r => DistributionTypes.Symbolic(r) let pointSet = r => DistributionTypes.PointSet(r) let sampleSet = r => DistributionTypes.SampleSet(r) - let evDistribution = r => ReducerInterface_InternalExpressionValue.IEvDistribution(r) - let evNumber = r => ReducerInterface_InternalExpressionValue.IEvNumber(r) - let evArray = r => ReducerInterface_InternalExpressionValue.IEvArray(r) - let evRecord = r => ReducerInterface_InternalExpressionValue.IEvRecord(r) - let evString = r => ReducerInterface_InternalExpressionValue.IEvString(r) + let evDistribution = r => Reducer_T.IEvDistribution(r) + let evNumber = r => Reducer_T.IEvNumber(r) + let evArray = r => Reducer_T.IEvArray(r) + let evRecord = r => Reducer_T.IEvRecord(r) + let evString = r => Reducer_T.IEvString(r) let symbolicEvDistribution = r => r->DistributionTypes.Symbolic->evDistribution let evArrayOfEvNumber = xs => xs->Belt.Array.map(evNumber)->evArray } -let getOrError = (a, g) => E.A.get(a, g) |> E.O.toResult(impossibleError) +let getOrError = (a, g) => E.A.get(a, g) |> E.O.toResult(impossibleErrorString) module Prepare = { - type t = frValue - type ts = array + type t = value + type ts = array type err = string module ToValueArray = { module Record = { - let twoArgs = (inputs: ts): result => + let twoArgs = (inputs: ts, (arg1: string, arg2: string)): result => switch inputs { - | [FRValueRecord([(_, n1), (_, n2)])] => Ok([n1, n2]) - | _ => Error(impossibleError) + | [IEvRecord(map)] => { + let n1 = map->Belt.Map.String.getExn(arg1) + let n2 = map->Belt.Map.String.getExn(arg2) + Ok([n1, n2]) + } + | _ => Error(impossibleErrorString) } - let threeArgs = (inputs: ts): result => + let threeArgs = (inputs: ts, (arg1: string, arg2: string, arg3: string)): result => switch inputs { - | [FRValueRecord([(_, n1), (_, n2), (_, n3)])] => Ok([n1, n2, n3]) - | _ => Error(impossibleError) - } - - let toArgs = (inputs: ts): result => - switch inputs { - | [FRValueRecord(args)] => args->E.A2.fmap(((_, b)) => b)->Ok - | _ => Error(impossibleError) + | [IEvRecord(map)] => { + let n1 = map->Belt.Map.String.getExn(arg1) + let n2 = map->Belt.Map.String.getExn(arg2) + let n3 = map->Belt.Map.String.getExn(arg3) + Ok([n1, n2, n3]) + } + | _ => Error(impossibleErrorString) } } module Array = { let openA = (inputs: t): result => switch inputs { - | FRValueArray(n) => Ok(n) - | _ => Error(impossibleError) + | IEvArray(n) => Ok(n) + | _ => Error(impossibleErrorString) } let arrayOfArrays = (inputs: t): result, err> => switch inputs { - | FRValueArray(n) => n->E.A2.fmap(openA)->E.A.R.firstErrorOrOpen - | _ => Error(impossibleError) + | IEvArray(n) => n->E.A2.fmap(openA)->E.A.R.firstErrorOrOpen + | _ => Error(impossibleErrorString) } } } @@ -61,8 +67,11 @@ module Prepare = { module ToValueTuple = { let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => { switch values { - | [FRValueDistOrNumber(a1), FRValueDistOrNumber(a2)] => Ok(a1, a2) - | _ => Error(impossibleError) + | [IEvDistribution(a1), IEvDistribution(a2)] => Ok(FRValueDist(a1), FRValueDist(a2)) + | [IEvDistribution(a1), IEvNumber(a2)] => Ok(FRValueDist(a1), FRValueNumber(a2)) + | [IEvNumber(a1), IEvDistribution(a2)] => Ok(FRValueNumber(a1), FRValueDist(a2)) + | [IEvNumber(a1), IEvNumber(a2)] => Ok(FRValueNumber(a1), FRValueNumber(a2)) + | _ => Error(impossibleErrorString) } } @@ -71,67 +80,57 @@ module Prepare = { err, > => { switch values { - | [FRValueDist(a1), FRValueDist(a2)] => Ok(a1, a2) - | _ => Error(impossibleError) + | [IEvDistribution(a1), IEvDistribution(a2)] => Ok(a1, a2) + | _ => Error(impossibleErrorString) } } let twoNumbers = (values: ts): result<(float, float), err> => { switch values { - | [FRValueNumber(a1), FRValueNumber(a2)] => Ok(a1, a2) - | _ => Error(impossibleError) + | [IEvNumber(a1), IEvNumber(a2)] => Ok(a1, a2) + | _ => Error(impossibleErrorString) } } let threeNumbers = (values: ts): result<(float, float, float), err> => { switch values { - | [FRValueNumber(a1), FRValueNumber(a2), FRValueNumber(a3)] => Ok(a1, a2, a3) - | _ => Error(impossibleError) + | [IEvNumber(a1), IEvNumber(a2), IEvNumber(a3)] => Ok(a1, a2, a3) + | _ => Error(impossibleErrorString) } } let oneDistOrNumber = (values: ts): result => { switch values { - | [FRValueDistOrNumber(a1)] => Ok(a1) - | _ => Error(impossibleError) + | [IEvNumber(a1)] => FRValueNumber(a1)->Ok + | [IEvDistribution(a2)] => FRValueDist(a2)->Ok + | _ => Error(impossibleErrorString) } } module Record = { - let twoDistOrNumber = (values: ts): result<(frValueDistOrNumber, frValueDistOrNumber), err> => - values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber) + let twoDistOrNumber = (values: ts, labels: (string, string)): result< + (frValueDistOrNumber, frValueDistOrNumber), + err, + > => values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDistOrNumber) - let twoDist = (values: ts): result< + let twoDist = (values: ts, labels: (string, string)): result< (DistributionTypes.genericDist, DistributionTypes.genericDist), err, - > => values->ToValueArray.Record.twoArgs->E.R.bind(twoDist) + > => values->ToValueArray.Record.twoArgs(labels)->E.R.bind(twoDist) } } - module ToArrayRecordPairs = { - let twoArgs = (input: t): result, err> => { - let array = input->ToValueArray.Array.openA - let pairs = - array->E.R.bind(pairs => - pairs - ->E.A2.fmap(xyCoord => [xyCoord]->ToValueArray.Record.twoArgs) - ->E.A.R.firstErrorOrOpen - ) - pairs + let oneNumber = (value: t): result => { + switch value { + | IEvNumber(a1) => Ok(a1) + | _ => Error(impossibleErrorString) } } - let oneNumber = (values: t): result => { - switch values { - | FRValueNumber(a1) => Ok(a1) - | _ => Error(impossibleError) - } - } - - let oneDict = (values: t): result, err> => { - switch values { - | FRValueDict(a1) => Ok(a1) - | _ => Error(impossibleError) + let oneDict = (value: t): result => { + switch value { + | IEvRecord(a1) => Ok(a1) + | _ => Error(impossibleErrorString) } } @@ -142,7 +141,7 @@ module Prepare = { inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openNumbers) } - let dicts = (inputs: ts): Belt.Result.t>, err> => { + let dicts = (inputs: ts): Belt.Result.t, err> => { let openDicts = (elements: array) => elements->E.A2.fmap(oneDict)->E.A.R.firstErrorOrOpen inputs->getOrError(0)->E.R.bind(ToValueArray.Array.openA)->E.R.bind(openDicts) } @@ -223,12 +222,11 @@ module DefineFn = { FnDefinition.make( ~name, ~inputs=[FRTypeNumber], - ~run=(_, inputs, _, _) => { - inputs - ->getOrError(0) - ->E.R.bind(Prepare.oneNumber) - ->E.R2.fmap(fn) - ->E.R2.fmap(Wrappers.evNumber) + ~run=(inputs, _, _) => { + switch inputs { + | [IEvNumber(x)] => fn(x)->IEvNumber->Ok + | _ => Error(impossibleError) + } }, (), ) @@ -236,8 +234,11 @@ module DefineFn = { FnDefinition.make( ~name, ~inputs=[FRTypeNumber, FRTypeNumber], - ~run=(_, inputs, _, _) => { - inputs->Prepare.ToValueTuple.twoNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) + ~run=(inputs, _, _) => { + switch inputs { + | [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvNumber->Ok + | _ => Error(impossibleError) + } }, (), ) @@ -245,10 +246,153 @@ module DefineFn = { FnDefinition.make( ~name, ~inputs=[FRTypeNumber, FRTypeNumber, FRTypeNumber], - ~run=(_, inputs, _, _) => { - inputs->Prepare.ToValueTuple.threeNumbers->E.R2.fmap(fn)->E.R2.fmap(Wrappers.evNumber) + ~run=(inputs, _, _) => { + switch inputs { + | [IEvNumber(x), IEvNumber(y), IEvNumber(z)] => fn(x, y, z)->IEvNumber->Ok + | _ => Error(impossibleError) + } }, (), ) } } + +module Make = { + /* + Opinionated explanations for API choices here: + + Q: Why such short names? + A: Because we have to type them a lot in definitions. + + Q: Why not DefineFn.Numbers.oneToOne / DefineFn.Numbers.twoToOne / ...? + A: Because return values matter too, and we have many possible combinations: numbers to numbers, pairs of numbers to numbers, pair of numbers to bools. + + Q: Does this approach scale? + A: It's good enough for most cases, and we can fall back on raw `Function.make` if necessary. We should figure out the better API powered by parameterized types, but it's hard (and might require PPX). + + Q: What about `frValue` types? + A: I hope we'll get rid of them soon. + + Q: What about polymorphic functions with multiple definitions? Why ~fn is not an array? + A: We often define the same function in multiple `FR_*` files, so that doesn't work well anyway. In 90%+ cases there's a single definition. And having to write `name` twice is annoying. + */ + let f2f = ( + ~name: string, + ~fn: float => float, + ~nameSpace="", + ~requiresNamespace=false, + ~examples=?, + (), + ) => { + Function.make( + ~name, + ~nameSpace, + ~requiresNamespace, + ~examples=examples->E.O.default([], _), + ~output=EvtNumber, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeNumber], + ~run=(inputs, _, _) => + switch inputs { + | [IEvNumber(x)] => fn(x)->IEvNumber->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + } + + let ff2f = ( + ~name: string, + ~fn: (float, float) => float, + ~nameSpace="", + ~requiresNamespace=false, + ~examples=?, + (), + ) => { + Function.make( + ~name, + ~nameSpace, + ~requiresNamespace, + ~examples=examples->E.O.default([], _), + ~output=EvtNumber, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeNumber, FRTypeNumber], + ~run=(inputs, _, _) => + switch inputs { + | [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvNumber->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + } + + let ff2b = ( + ~name: string, + ~fn: (float, float) => bool, + ~nameSpace="", + ~requiresNamespace=false, + ~examples=?, + (), + ) => { + Function.make( + ~name, + ~nameSpace, + ~requiresNamespace, + ~examples=examples->E.O.default([], _), + ~output=EvtBool, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeNumber, FRTypeNumber], + ~run=(inputs, _, _) => + switch inputs { + | [IEvNumber(x), IEvNumber(y)] => fn(x, y)->IEvBool->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + } + + let bb2b = ( + ~name: string, + ~fn: (bool, bool) => bool, + ~nameSpace="", + ~requiresNamespace=false, + ~examples=?, + (), + ) => { + Function.make( + ~name, + ~nameSpace, + ~requiresNamespace, + ~examples=examples->E.O.default([], _), + ~output=EvtBool, + ~definitions=[ + FnDefinition.make( + ~name, + ~inputs=[FRTypeBool, FRTypeBool], + ~run=(inputs, _, _) => + switch inputs { + | [IEvBool(x), IEvBool(y)] => fn(x, y)->IEvBool->Ok + | _ => Error(impossibleError) + }, + (), + ), + ], + (), + ) + } +} diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res index 3714b0b7..7dc7aa0b 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/FunctionRegistry_Library.res @@ -1,4 +1,5 @@ let fnList = Belt.Array.concatMany([ + FR_Builtin.library, FR_Dict.library, FR_Dist.library, FR_Danger.library, @@ -8,7 +9,16 @@ let fnList = Belt.Array.concatMany([ FR_Number.library, FR_Pointset.library, FR_Scoring.library, + FR_GenericDist.library, + FR_Units.library, + FR_Date.library, + FR_Math.library, ]) let registry = FunctionRegistry_Core.Registry.make(fnList) -let dispatch = FunctionRegistry_Core.Registry.dispatch(registry) +let call = FunctionRegistry_Core.Registry.call(registry) + +let nonRegistryLambdas: array<(string, Reducer_T.lambdaValue)> = [ + ("mx", FR_GenericDist.mxLambda), + ("mixture", FR_GenericDist.mxLambda), +] diff --git a/packages/squiggle-lang/src/rescript/FunctionRegistry/README.md b/packages/squiggle-lang/src/rescript/FunctionRegistry/README.md index e974b189..38bf1858 100644 --- a/packages/squiggle-lang/src/rescript/FunctionRegistry/README.md +++ b/packages/squiggle-lang/src/rescript/FunctionRegistry/README.md @@ -9,38 +9,38 @@ The main interface is fairly constrained. Basically, write functions like the fo ~name="Normal", ~definitions=[ FnDefinition.make( - ~name="Normal", - ~definitions=[ - FnDefinition.make(~name="normal", ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], ~run=( - inputs, - env, - ) => - inputs - ->Prepare.ToValueTuple.twoDistOrNumber - ->E.R.bind( - Process.twoDistsOrNumbersToDistUsingSymbolicDist( - ~fn=E.Tuple2.toFnCall(SymbolicDist.Normal.make), - ~env, - ~values=_, - ), - ) - ->E.R2.fmap(Wrappers.evDistribution) + ~name="normal", + ~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber], + ~run=( + inputs, + env, + ) => + inputs + ->Prepare.ToValueTuple.twoDistOrNumber + ->E.R.bind( + Process.twoDistsOrNumbersToDistUsingSymbolicDist( + ~fn=E.Tuple2.toFnCall(SymbolicDist.Normal.make), + ~env, + ~values=_, ), - ], + ) + ->E.R2.fmap(Wrappers.evDistribution) ) ], ) ``` -The Function name is just there for future documentation. The function defintions +The Function name is just there for future documentation. ## Key Files **FunctionRegistry_Core** -Key types, internal functionality, and a `Registry` module with a `matchAndRun` function to call function definitions. +Key types, internal functionality, and a `Registry` module with a `call` function to call function definitions. **FunctionRegistry_Library** A list of all the Functions defined in the Function Registry. +The definition arrays are stored in `FR_*` modules, by convention. + **FunctionRegistry_Helpers** -A list of helper functions for the FunctionRegistry_Library. +A list of helper functions for the `FunctionRegistry_Library`. diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings.res new file mode 100644 index 00000000..5c0fbc59 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings.res @@ -0,0 +1,47 @@ +/* + Bindings describe the entire set of bound variables accessible to the squiggle code. + Bindings objects are stored as linked lists of scopes: + { localX: ..., localY: ... } <- { globalZ: ..., ... } <- { importedT: ..., ... } <- { stdlibFunction: ..., ... } +*/ + +type t = Reducer_T.bindings + +let rec get = ({namespace, parent}: t, id: string) => { + switch namespace->Reducer_Namespace.get(id) { + | Some(v) => Some(v) + | None => + switch parent { + | Some(p) => p->get(id) + | None => None + } + } +} + +let set = ({namespace} as bindings: t, id: string, value): t => { + { + ...bindings, + namespace: namespace->Reducer_Namespace.set(id, value), + } +} + +let rec toString = ({namespace, parent}: t) => { + let pairs = namespace->Reducer_Namespace.toString + + switch parent { + | Some(p) => `{${pairs}} / ${toString(p)}` + | None => `{${pairs}}` + } +} + +let extend = (bindings: t): t => {namespace: Reducer_Namespace.make(), parent: bindings->Some} + +let make = (): t => {namespace: Reducer_Namespace.make(), parent: None} + +let removeResult = ({namespace} as bindings: t): t => { + ...bindings, + namespace: namespace->Belt.Map.String.remove("__result__"), +} + +let locals = ({namespace}: t): Reducer_T.namespace => namespace + +let fromNamespace = (namespace: Reducer_Namespace.t): t => {namespace: namespace, parent: None} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res deleted file mode 100644 index 68bc6c3a..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Bindings/Reducer_Bindings.res +++ /dev/null @@ -1,189 +0,0 @@ -// Only Bindings as the global module is supported -// Other module operations such as import export will be preprocessed jobs - -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -open Reducer_ErrorValue -open ReducerInterface_InternalExpressionValue - -let expressionValueToString = toString - -type t = ReducerInterface_InternalExpressionValue.nameSpace - -let typeAliasesKey = "_typeAliases_" -let typeReferencesKey = "_typeReferences_" - -let getType = (NameSpace(container): t, id: string) => { - Belt.Map.String.get(container, typeAliasesKey)->Belt.Option.flatMap(aliases => - switch aliases { - | IEvRecord(r) => Belt.Map.String.get(r, id) - | _ => None - } - ) -} - -let getTypeOf = (NameSpace(container): t, id: string) => { - Belt.Map.String.get(container, typeReferencesKey)->Belt.Option.flatMap(defs => - switch defs { - | IEvRecord(r) => Belt.Map.String.get(r, id) - | _ => None - } - ) -} - -let getWithDefault = (NameSpace(container): t, id: string, default) => - switch Belt.Map.String.get(container, id) { - | Some(v) => v - | None => default - } - -let get = (NameSpace(container): t, id: string) => Belt.Map.String.get(container, id) - -let emptyMap: map = Belt.Map.String.empty - -let setTypeAlias = (NameSpace(container): t, id: string, value): t => { - let rValue = Belt.Map.String.getWithDefault(container, typeAliasesKey, IEvRecord(emptyMap)) - let r = switch rValue { - | IEvRecord(r) => r - | _ => emptyMap - } - let r2 = Belt.Map.String.set(r, id, value)->IEvRecord - NameSpace(Belt.Map.String.set(container, typeAliasesKey, r2)) -} - -let setTypeOf = (NameSpace(container): t, id: string, value): t => { - let rValue = Belt.Map.String.getWithDefault(container, typeReferencesKey, IEvRecord(emptyMap)) - let r = switch rValue { - | IEvRecord(r) => r - | _ => emptyMap - } - let r2 = Belt.Map.String.set(r, id, value)->IEvRecord - NameSpace(Belt.Map.String.set(container, typeReferencesKey, r2)) -} - -let set = (NameSpace(container): t, id: string, value): t => NameSpace( - Belt.Map.String.set(container, id, value), -) - -let emptyModule: t = NameSpace(emptyMap) -let emptyBindings = emptyModule -let emptyNameSpace = emptyModule - -// let fromTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceFromTypeScriptBindings -// let toTypeScriptBindings = ReducerInterface_InternalExpressionValue.nameSpaceToTypeScriptBindings - -let toExpressionValue = (nameSpace: t): internalExpressionValue => IEvBindings(nameSpace) -let fromExpressionValue = (aValue: internalExpressionValue): t => - switch aValue { - | IEvBindings(nameSpace) => nameSpace - | _ => emptyModule - } - -let fromArray = a => NameSpace(Belt.Map.String.fromArray(a)) - -let mergeFrom = (NameSpace(container): t, NameSpace(newContainer): t): t => { - NameSpace( - newContainer->Belt.Map.String.reduce(container, (container, key, value) => - Belt.Map.String.set(container, key, value) - ), - ) -} - -let removeOther = (NameSpace(container): t, NameSpace(otherContainer): t): t => { - let keys = Belt.Map.String.keysToArray(otherContainer) - NameSpace( - Belt.Map.String.keep(container, (key, _value) => { - let removeThis = Js.Array2.includes(keys, key) - !removeThis - }), - ) -} - -external castExpressionToInternalCode: ExpressionT.expressionOrFFI => internalCode = "%identity" - -let eLambdaFFIValue = (ffiFn: ExpressionT.ffiFn) => { - IEvLambda({ - parameters: [], - context: emptyModule, - body: FFI(ffiFn)->castExpressionToInternalCode, - }) -} - -let functionNotFoundError = (call: functionCall) => - REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)->Error - -let functionNotFoundErrorFFIFn = (functionName: string): ExpressionT.ffiFn => { - (args: array, _environment: environment): result< - internalExpressionValue, - errorValue, - > => { - let call = (functionName, args) - functionNotFoundError(call) - } -} - -let convertOptionToFfiFnReturningResult = ( - myFunctionName: string, - myFunction: ExpressionT.optionFfiFnReturningResult, -): ExpressionT.ffiFn => { - (args: array, environment) => { - myFunction(args, environment)->Belt.Option.getWithDefault( - functionNotFoundErrorFFIFn(myFunctionName)(args, environment), - ) - } -} - -let convertOptionToFfiFn = ( - myFunctionName: string, - myFunction: ExpressionT.optionFfiFn, -): ExpressionT.ffiFn => { - (args: array, environment) => { - myFunction(args, environment) - ->Belt.Option.map(v => v->Ok) - ->Belt.Option.getWithDefault(functionNotFoundErrorFFIFn(myFunctionName)(args, environment)) - } -} - -// -- Module definition -let define = (NameSpace(container): t, identifier: string, ev: internalExpressionValue): t => { - NameSpace(Belt.Map.String.set(container, identifier, ev)) -} - -let defineNumber = (nameSpace: t, identifier: string, value: float): t => - nameSpace->define(identifier, IEvNumber(value)) - -let defineString = (nameSpace: t, identifier: string, value: string): t => - nameSpace->define(identifier, IEvString(value)) - -let defineBool = (nameSpace: t, identifier: string, value: bool): t => - nameSpace->define(identifier, IEvBool(value)) - -let defineModule = (nameSpace: t, identifier: string, value: t): t => - nameSpace->define(identifier, toExpressionValue(value)) - -let defineFunction = (nameSpace: t, identifier: string, value: ExpressionT.optionFfiFn): t => { - nameSpace->define(identifier, convertOptionToFfiFn(identifier, value)->eLambdaFFIValue) -} - -let defineFunctionReturningResult = ( - nameSpace: t, - identifier: string, - value: ExpressionT.optionFfiFnReturningResult, -): t => { - nameSpace->define( - identifier, - convertOptionToFfiFnReturningResult(identifier, value)->eLambdaFFIValue, - ) -} - -let emptyStdLib: t = emptyModule->defineBool("_standardLibrary", true) - -let chainTo = (nameSpace: t, previousNameSpaces: array) => { - previousNameSpaces->Belt.Array.reduce(nameSpace, (topNameSpace, prevNameSpace) => - mergeFrom(prevNameSpace, topNameSpace) - ) -} - -let removeResult = (NameSpace(container): t): t => { - container->Belt.Map.String.remove("__result__")->NameSpace -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res new file mode 100644 index 00000000..c45994bb --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res @@ -0,0 +1,12 @@ +type t = Reducer_T.context + +let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv + +let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => { + { + bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend, + environment: environment, + } +} + +let createDefaultContext = (): t => createContext(SquiggleLibrary_StdLib.stdLib, defaultEnvironment) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res deleted file mode 100644 index 83d3eca2..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch.res +++ /dev/null @@ -1,2 +0,0 @@ -module Builtin = Reducer_Dispatch_BuiltIn -module BuiltinMacros = Reducer_Dispatch_BuiltInMacros diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res deleted file mode 100644 index 985558e7..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltIn.res +++ /dev/null @@ -1,218 +0,0 @@ -module Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer -module Continuation = ReducerInterface_Value_Continuation -module ExpressionT = Reducer_Expression_T -module ExternalLibrary = ReducerInterface.ExternalLibrary -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module Lambda = Reducer_Expression_Lambda -module MathJs = Reducer_MathJs -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module Result = Belt.Result -module TypeBuilder = Reducer_Type_TypeBuilder - -open ReducerInterface_InternalExpressionValue -open Reducer_ErrorValue - -/* - MathJs provides default implementations for built-ins - This is where all the expected built-ins like + = * / sin cos log ln etc are handled - DO NOT try to add external function mapping here! -*/ - -//TODO: pow to xor - -exception TestRescriptException - -let callInternal = ( - call: functionCall, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -): result<'b, errorValue> => { - let callMathJs = (call: functionCall): result<'b, errorValue> => - switch call { - | ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests - | ("rescriptraise", _) => raise(TestRescriptException) // For Tests - | call => call->toStringFunctionCall->MathJs.Eval.eval - } - - let constructRecord = arrayOfPairs => { - Belt.Array.map(arrayOfPairs, pairValue => - switch pairValue { - | IEvArray([IEvString(key), valueValue]) => (key, valueValue) - | _ => ("wrong key type", pairValue->toStringWithType->IEvString) - } - ) - ->Belt.Map.String.fromArray - ->IEvRecord - ->Ok - } - - let arrayAtIndex = (aValueArray: array, fIndex: float) => - switch Belt.Array.get(aValueArray, Belt.Int.fromFloat(fIndex)) { - | Some(value) => value->Ok - | None => REArrayIndexNotFound("Array index not found", Belt.Int.fromFloat(fIndex))->Error - } - - let moduleAtIndex = (nameSpace: nameSpace, sIndex) => - switch Bindings.get(nameSpace, sIndex) { - | Some(value) => value->Ok - | None => RERecordPropertyNotFound("Bindings property not found", sIndex)->Error - } - - let recordAtIndex = (dict: Belt.Map.String.t, sIndex) => - switch Belt.Map.String.get(dict, sIndex) { - | Some(value) => value->Ok - | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error - } - - let doAddArray = (originalA, b) => { - let a = originalA->Js.Array2.copy - let _ = Js.Array2.pushMany(a, b) - a->IEvArray->Ok - } - let doAddString = (a, b) => { - let answer = Js.String2.concat(a, b) - answer->IEvString->Ok - } - - let inspect = (value: internalExpressionValue) => { - Js.log(value->toString) - value->Ok - } - - let inspectLabel = (value: internalExpressionValue, label: string) => { - Js.log(`${label}: ${value->toString}`) - value->Ok - } - - let doSetBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) => { - Bindings.set(bindings, symbol, value)->IEvBindings->Ok - } - - let doSetTypeAliasBindings = ( - bindings: nameSpace, - symbol: string, - value: internalExpressionValue, - ) => Bindings.setTypeAlias(bindings, symbol, value)->IEvBindings->Ok - - let doSetTypeOfBindings = (bindings: nameSpace, symbol: string, value: internalExpressionValue) => - Bindings.setTypeOf(bindings, symbol, value)->IEvBindings->Ok - - let doExportBindings = (bindings: nameSpace) => bindings->Bindings.toExpressionValue->Ok - - let doIdentity = (value: internalExpressionValue) => value->Ok - - let doDumpBindings = (continuation: nameSpace, value: internalExpressionValue) => { - // let _ = Continuation.inspect(continuation, "doDumpBindings") - accessors.states.continuation = continuation->Bindings.set("__result__", value) - value->Ok - } - - module SampleMap = { - let doLambdaCall = (aLambdaValue, list) => - switch Lambda.doLambdaCall(aLambdaValue, list, accessors, reducer) { - | IEvNumber(f) => Ok(f) - | _ => Error(Operation.SampleMapNeedsNtoNFunction) - } - - let toType = r => - switch r { - | Ok(r) => Ok(IEvDistribution(SampleSet(r))) - | Error(r) => Error(REDistributionError(SampleSetError(r))) - } - - let parseSampleSetArray = (arr: array): option< - array, - > => { - let parseSampleSet = (value: internalExpressionValue): option => - switch value { - | IEvDistribution(SampleSet(dist)) => Some(dist) - | _ => None - } - E.A.O.openIfAllSome(E.A.fmap(parseSampleSet, arr)) - } - - let _mapN = (aValueArray: array, aLambdaValue) => { - switch parseSampleSetArray(aValueArray) { - | Some(t1) => - let fn = a => doLambdaCall(aLambdaValue, list{IEvArray(E.A.fmap(x => IEvNumber(x), a))}) - SampleSetDist.mapN(~fn, ~t1)->toType - | None => - Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) - } - } - } - - switch call { - | ("$_atIndex_$", [IEvArray(aValueArray), IEvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex) - | ("$_atIndex_$", [IEvBindings(dict), IEvString(sIndex)]) => moduleAtIndex(dict, sIndex) - | ("$_atIndex_$", [IEvRecord(dict), IEvString(sIndex)]) => recordAtIndex(dict, sIndex) - | ("$_constructArray_$", args) => IEvArray(args)->Ok - | ("$_constructRecord_$", [IEvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) - | ("$_exportBindings_$", [IEvBindings(nameSpace)]) => doExportBindings(nameSpace) - | ("$_exportBindings_$", [evValue]) => doIdentity(evValue) - | ("$_setBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) => - doSetBindings(nameSpace, symbol, value) - | ("$_setTypeAliasBindings_$", [IEvBindings(nameSpace), IEvTypeIdentifier(symbol), value]) => - doSetTypeAliasBindings(nameSpace, symbol, value) - | ("$_setTypeOfBindings_$", [IEvBindings(nameSpace), IEvSymbol(symbol), value]) => - doSetTypeOfBindings(nameSpace, symbol, value) - | ("$_dumpBindings_$", [IEvBindings(nameSpace), _, evValue]) => doDumpBindings(nameSpace, evValue) - | ("$_typeModifier_memberOf_$", [IEvTypeIdentifier(typeIdentifier), IEvArray(arr)]) => - TypeBuilder.typeModifier_memberOf(IEvTypeIdentifier(typeIdentifier), IEvArray(arr)) - | ("$_typeModifier_memberOf_$", [IEvType(typeRecord), IEvArray(arr)]) => - TypeBuilder.typeModifier_memberOf_update(typeRecord, IEvArray(arr)) - | ("$_typeModifier_min_$", [IEvTypeIdentifier(typeIdentifier), value]) => - TypeBuilder.typeModifier_min(IEvTypeIdentifier(typeIdentifier), value) - | ("$_typeModifier_min_$", [IEvType(typeRecord), value]) => - TypeBuilder.typeModifier_min_update(typeRecord, value) - | ("$_typeModifier_max_$", [IEvTypeIdentifier(typeIdentifier), value]) => - TypeBuilder.typeModifier_max(IEvTypeIdentifier(typeIdentifier), value) - | ("$_typeModifier_max_$", [IEvType(typeRecord), value]) => - TypeBuilder.typeModifier_max_update(typeRecord, value) - | ("$_typeModifier_opaque_$", [IEvType(typeRecord)]) => - TypeBuilder.typeModifier_opaque_update(typeRecord) - | ("$_typeOr_$", [IEvArray(arr)]) => TypeBuilder.typeOr(IEvArray(arr)) - | ("$_typeFunction_$", [IEvArray(arr)]) => TypeBuilder.typeFunction(arr) - | ("$_typeTuple_$", [IEvArray(elems)]) => TypeBuilder.typeTuple(elems) - | ("$_typeArray_$", [elem]) => TypeBuilder.typeArray(elem) - | ("$_typeRecord_$", [IEvRecord(propertyMap)]) => TypeBuilder.typeRecord(propertyMap) - | ("concat", [IEvArray(aValueArray), IEvArray(bValueArray)]) => - doAddArray(aValueArray, bValueArray) - | ("concat", [IEvString(aValueString), IEvString(bValueString)]) => - doAddString(aValueString, bValueString) - | ("inspect", [value, IEvString(label)]) => inspectLabel(value, label) - | ("inspect", [value]) => inspect(value) - | (_, [IEvBool(_)]) - | (_, [IEvNumber(_)]) - | (_, [IEvString(_)]) - | (_, [IEvBool(_), IEvBool(_)]) - | (_, [IEvNumber(_), IEvNumber(_)]) - | (_, [IEvString(_), IEvString(_)]) => - callMathJs(call) - | call => - Error(REFunctionNotFound(call->functionCallToCallSignature->functionCallSignatureToString)) // Report full type signature as error - } -} -/* - Reducer uses Result monad while reducing expressions -*/ -let dispatch = ( - call: functionCall, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -): internalExpressionValue => - try { - let (fn, args) = call - // There is a bug that prevents string match in patterns - // So we have to recreate a copy of the string - ExternalLibrary.dispatch( - (Js.String.make(fn), args), - accessors, - reducer, - callInternal, - )->InternalExpressionValue.resultToValue - } catch { - | exn => Reducer_ErrorValue.fromException(exn)->Reducer_ErrorValue.toException - } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res deleted file mode 100644 index 73b7bbd1..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ /dev/null @@ -1,189 +0,0 @@ -/* - 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 Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer -module ErrorValue = Reducer_ErrorValue -module ExpressionBuilder = Reducer_Expression_ExpressionBuilder -module ExpressionT = Reducer_Expression_T -module ExpressionWithContext = Reducer_ExpressionWithContext -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T - -open Reducer_Expression_ExpressionBuilder - -type expression = ExpressionT.expression -type expressionWithContext = ExpressionWithContext.expressionWithContext - -let dispatchMacroCall = ( - macroExpression: expression, - bindings: ExpressionT.bindings, - accessors: ProjectAccessorsT.t, - reduceExpression: ProjectReducerFnT.t, -): expressionWithContext => { - let useExpressionToSetBindings = (bindingExpr: expression, accessors, statement, newCode) => { - let nameSpaceValue = reduceExpression(bindingExpr, bindings, accessors) - - let newBindings = Bindings.fromExpressionValue(nameSpaceValue) - - let boundStatement = BindingsReplacer.replaceSymbols(newBindings, statement) - - ExpressionWithContext.withContext(newCode(newBindings->eModule, boundStatement), newBindings) - } - - let correspondingSetBindingsFn = (fnName: string): string => - switch fnName { - | "$_let_$" => "$_setBindings_$" - | "$_typeOf_$" => "$_setTypeOfBindings_$" - | "$_typeAlias_$" => "$_setTypeAliasBindings_$" - | "$_endOfOuterBlock_$" => "$_dumpBindings_$" - | _ => "" - } - - let doBindStatement = (bindingExpr: expression, statement: expression, accessors) => { - let defaultStatement = ErrorValue.REAssignmentExpected - switch statement { - | ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => { - let setBindingsFn = correspondingSetBindingsFn(callName) - if setBindingsFn !== "" { - useExpressionToSetBindings(bindingExpr, accessors, statement, ( - newBindingsExpr, - boundStatement, - ) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})) - } else { - defaultStatement->Reducer_ErrorValue.toException - } - } - | _ => defaultStatement->Reducer_ErrorValue.toException - } - } - - let doBindExpression = ( - bindingExpr: expression, - statement: expression, - accessors, - ): expressionWithContext => { - let defaultStatement = () => - useExpressionToSetBindings(bindingExpr, accessors, statement, ( - _newBindingsExpr, - boundStatement, - ) => boundStatement) - - switch statement { - | ExpressionT.EList(list{ExpressionT.EValue(IEvCall(callName)), symbolExpr, statement}) => { - let setBindingsFn = correspondingSetBindingsFn(callName) - if setBindingsFn !== "" { - useExpressionToSetBindings(bindingExpr, accessors, statement, ( - newBindingsExpr, - boundStatement, - ) => - eFunction( - "$_exportBindings_$", - list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})}, // expression returning bindings - ) - ) - } else { - defaultStatement() - } - } - | _ => defaultStatement() - } - } - - let doBlock = ( - exprs: list, - _bindings: ExpressionT.bindings, - _accessors, - ): expressionWithContext => { - let exprsArray = Belt.List.toArray(exprs) - let maxIndex = Js.Array2.length(exprsArray) - 1 - let newStatement = exprsArray->Js.Array2.reducei((acc, statement, index) => - if index == 0 { - if index == maxIndex { - eBindExpressionDefault(statement) - } else { - eBindStatementDefault(statement) - } - } else if index == maxIndex { - eBindExpression(acc, statement) - } else { - eBindStatement(acc, statement) - } - , eSymbol("undefined block")) - ExpressionWithContext.noContext(newStatement) - } - - let doLambdaDefinition = ( - bindings: ExpressionT.bindings, - parameters: array, - lambdaDefinition: ExpressionT.expression, - ) => ExpressionWithContext.noContext(eLambda(parameters, bindings, lambdaDefinition)) - - let doTernary = ( - condition: expression, - ifTrue: expression, - ifFalse: expression, - bindings: ExpressionT.bindings, - accessors, - ): expressionWithContext => { - let blockCondition = ExpressionBuilder.eBlock(list{condition}) - let conditionValue = reduceExpression(blockCondition, bindings, accessors) - - switch conditionValue { - | InternalExpressionValue.IEvBool(false) => { - let ifFalseBlock = eBlock(list{ifFalse}) - ExpressionWithContext.withContext(ifFalseBlock, bindings) - } - | InternalExpressionValue.IEvBool(true) => { - let ifTrueBlock = eBlock(list{ifTrue}) - ExpressionWithContext.withContext(ifTrueBlock, bindings) - } - | _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.toException - } - } - - let expandExpressionList = ( - aList, - bindings: ExpressionT.bindings, - accessors, - ): expressionWithContext => - switch aList { - | list{ - ExpressionT.EValue(IEvCall("$$_bindStatement_$$")), - bindingExpr: ExpressionT.expression, - statement, - } => - doBindStatement(bindingExpr, statement, accessors) - | list{ExpressionT.EValue(IEvCall("$$_bindStatement_$$")), statement} => - // bindings of the context are used when there is no binding expression - doBindStatement(eModule(bindings), statement, accessors) - | list{ - ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), - bindingExpr: ExpressionT.expression, - expression, - } => - doBindExpression(bindingExpr, expression, accessors) - | list{ExpressionT.EValue(IEvCall("$$_bindExpression_$$")), expression} => - // bindings of the context are used when there is no binding expression - doBindExpression(eModule(bindings), expression, accessors) - | list{ExpressionT.EValue(IEvCall("$$_block_$$")), ...exprs} => - doBlock(exprs, bindings, accessors) - | list{ - ExpressionT.EValue(IEvCall("$$_lambda_$$")), - ExpressionT.EValue(IEvArrayString(parameters)), - lambdaDefinition, - } => - doLambdaDefinition(bindings, parameters, lambdaDefinition) - | list{ExpressionT.EValue(IEvCall("$$_ternary_$$")), condition, ifTrue, ifFalse} => - doTernary(condition, ifTrue, ifFalse, bindings, accessors) - | _ => ExpressionWithContext.noContext(ExpressionT.EList(aList)) - } - - switch macroExpression { - | EList(aList) => expandExpressionList(aList, bindings, accessors) - | _ => ExpressionWithContext.noContext(macroExpression) - } -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_ChainPiece.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_ChainPiece.res index 310f4879..e545bb2d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_ChainPiece.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_ChainPiece.res @@ -1,7 +1,9 @@ +// types are disabled until review and rewrite for 0.5 interpreter compatibility +/* module ProjectAccessorsT = ReducerProject_ProjectAccessors_T module T = Reducer_Dispatch_T module TypeChecker = Reducer_Type_TypeChecker -open ReducerInterface_InternalExpressionValue +open Reducer_Value type errorValue = Reducer_ErrorValue.errorValue @@ -21,3 +23,5 @@ let makeFromTypes = jumpTable => { } dispatchChainPiece } + +*/ diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_T.res index 68b8f789..0d852783 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_T.res @@ -1,22 +1,21 @@ -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ExpressionT = Reducer_Expression_T -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T +// module ExpressionT = Reducer_Expression_T +// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -// Each piece of the dispatch chain computes the result or returns None so that the chain can continue -type dispatchChainPiece = ( - InternalExpressionValue.functionCall, - ProjectAccessorsT.t, -) => option> +// // Each piece of the dispatch chain computes the result or returns None so that the chain can continue +// type dispatchChainPiece = ( +// Reducer_Value.functionCall, +// ProjectAccessorsT.t, +// ) => option> -type dispatchChainPieceWithReducer = ( - InternalExpressionValue.functionCall, - ProjectAccessorsT.t, - ProjectReducerFnT.t, -) => option> +// type dispatchChainPieceWithReducer = ( +// Reducer_Value.functionCall, +// ProjectAccessorsT.t, +// Reducer_T.reducerFn, +// ) => option> + +// // This is a switch statement case implementation: get the arguments and compute the result +// type genericIEvFunction = ( +// array, +// ProjectAccessorsT.t, +// ) => result -// This is a switch statement case implementation: get the arguments and compute the result -type genericIEvFunction = ( - array, - ProjectAccessorsT.t, -) => result diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index e3fdf168..c28e121e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -1,108 +1,130 @@ module Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer -module BuiltIn = Reducer_Dispatch_BuiltIn -module ExpressionBuilder = Reducer_Expression_ExpressionBuilder -module Extra = Reducer_Extra -module InternalExpressionValue = ReducerInterface_InternalExpressionValue module Lambda = Reducer_Expression_Lambda -module Macro = Reducer_Expression_Macro -module MathJs = Reducer_MathJs -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T module Result = Belt.Result -module T = Reducer_Expression_T +module T = Reducer_T type errorValue = Reducer_ErrorValue.errorValue -type t = T.t /* - Recursively evaluate/reduce the expression (Lisp AST/Lambda calculus) + Recursively evaluate the expression */ -let rec reduceExpressionInProject = ( - expression: t, - continuation: T.bindings, - accessors: ProjectAccessorsT.t, -): InternalExpressionValue.t => { - // Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`) +let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { + // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`) switch expression { - | T.EValue(value) => value - | T.EList(list) => - switch list { - | list{EValue(IEvCall(fName)), ..._args} => - switch Macro.isMacroName(fName) { - // A macro expands then reduces itself - | true => Macro.doMacroCall(expression, continuation, accessors, reduceExpressionInProject) - | false => reduceExpressionList(list, continuation, accessors) - } - | _ => reduceExpressionList(list, continuation, accessors) + | T.EBlock(statements) => { + let innerContext = {...context, bindings: context.bindings->Bindings.extend} + let (value, _) = + statements->Belt.Array.reduce((T.IEvVoid, innerContext), ((_, currentContext), statement) => + statement->evaluate(currentContext) + ) + (value, context) } - } -} -and reduceExpressionList = ( - expressions: list, - continuation: T.bindings, - accessors: ProjectAccessorsT.t, -): InternalExpressionValue.t => { - let acc: list = - expressions->Belt.List.reduceReverse(list{}, (acc, each: t) => - acc->Belt.List.add(each->reduceExpressionInProject(continuation, accessors)) - ) - acc->reduceValueList(accessors) -} -/* - After reducing each level of expression(Lisp AST), we have a value list to evaluate - */ -and reduceValueList = ( - valueList: list, - accessors: ProjectAccessorsT.t, -): InternalExpressionValue.t => - switch valueList { - | list{IEvCall(fName), ...args} => { - let checkedArgs = switch fName { - | "$_setBindings_$" | "$_setTypeOfBindings_$" | "$_setTypeAliasBindings_$" => args - | _ => args->Lambda.checkIfReduced - } + | T.EProgram(statements) => { + // Js.log(`bindings: ${context.bindings->Bindings.locals->Reducer_Namespace.toString}`) + let (value, finalContext) = + statements->Belt.Array.reduce((T.IEvVoid, context), ((_, currentContext), statement) => + statement->evaluate(currentContext) + ) - (fName, checkedArgs->Belt.List.toArray)->BuiltIn.dispatch( - accessors, - reduceExpressionInProject, + // Js.log(`bindings after: ${finalContext.bindings->Bindings.locals->Reducer_Namespace.toString}`) + (value, finalContext) + } + + | T.EArray(elements) => { + let value = + elements + ->Belt.Array.map(element => { + let (value, _) = evaluate(element, context) + value + }) + ->T.IEvArray + (value, context) + } + + | T.ERecord(pairs) => { + let value = + pairs + ->Belt.Array.map(((eKey, eValue)) => { + let (key, _) = eKey->evaluate(context) + let keyString = switch key { + | IEvString(s) => s + | _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise + } + let (value, _) = eValue->evaluate(context) + (keyString, value) + }) + ->Belt.Map.String.fromArray + ->T.IEvRecord + (value, context) + } + + | T.EAssign(left, right) => { + let (result, _) = right->evaluate(context) + ( + T.IEvVoid, + { + ...context, + bindings: context.bindings->Bindings.set(left, result), + }, ) } - | list{IEvLambda(_)} => - // TODO: remove on solving issue#558 - valueList->Lambda.checkIfReduced->Belt.List.toArray->InternalExpressionValue.IEvArray - | list{IEvLambda(lambdaCall), ...args} => - args - ->Lambda.checkIfReduced - ->Lambda.doLambdaCall(lambdaCall, _, accessors, reduceExpressionInProject) - | _ => valueList->Lambda.checkIfReduced->Belt.List.toArray->InternalExpressionValue.IEvArray - } -let reduceReturningBindings = ( - expression: t, - continuation: T.bindings, - accessors: ProjectAccessorsT.t, -): (InternalExpressionValue.t, T.bindings) => { - let states = accessors.states - let result = reduceExpressionInProject(expression, continuation, accessors) - (result, states.continuation) + | T.ESymbol(name) => + switch context.bindings->Bindings.get(name) { + | Some(v) => (v, context) + | None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise + } + + | T.EValue(value) => (value, context) + + | T.ETernary(predicate, trueCase, falseCase) => { + let (predicateResult, _) = predicate->evaluate(context) + switch predicateResult { + | T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context) + | _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise + } + } + + | T.ELambda(parameters, body) => ( + Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda, + context, + ) + + | T.ECall(fn, args) => { + let (lambda, _) = fn->evaluate(context) + let argValues = Js.Array2.map(args, arg => { + let (argValue, _) = arg->evaluate(context) + argValue + }) + switch lambda { + | T.IEvLambda(lambda) => ( + Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate), + context, + ) + | _ => + RENotAFunction(lambda->Reducer_Value.toString)->Reducer_ErrorValue.ErrorException->raise + } + } + } } module BackCompatible = { // Those methods are used to support the existing tests // If they are used outside limited testing context, error location reporting will fail - let parse = (peggyCode: string): result => + let parse = (peggyCode: string): result => peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) - let evaluate = (expression: t): result => { - let accessors = ProjectAccessorsT.identityAccessors + let evaluate = (expression: T.expression): result => { + let context = Reducer_Context.createDefaultContext() try { - expression->reduceExpressionInProject(accessors.stdLib, accessors)->Ok + let (value, _) = expression->evaluate(context) + value->Ok } catch { | exn => Reducer_ErrorValue.fromException(exn)->Error } } - let evaluateString = (peggyCode: string): result => + let evaluateString = (peggyCode: string): result => parse(peggyCode)->Result.flatMap(evaluate) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res deleted file mode 100644 index 539585b4..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res +++ /dev/null @@ -1,62 +0,0 @@ -module Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer -module ErrorValue = Reducer_ErrorValue -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module Result = Belt.Result - -type bindings = ExpressionT.bindings -type context = bindings -type environment = InternalExpressionValue.environment -type errorValue = Reducer_ErrorValue.errorValue -type expression = ExpressionT.expression -type internalExpressionValue = InternalExpressionValue.t - -type expressionWithContext = - | ExpressionWithContext(expression, context) - | ExpressionNoContext(expression) - -type t = expressionWithContext - -let callReducer = ( - expressionWithContext: expressionWithContext, - bindings: bindings, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -): internalExpressionValue => { - switch expressionWithContext { - | ExpressionNoContext(expr) => - // Js.log(`callReducer: bindings ${Bindings.toString(bindings)} expr ${ExpressionT.toString(expr)}`) - reducer(expr, bindings, accessors) - | ExpressionWithContext(expr, context) => - // Js.log(`callReducer: context ${Bindings.toString(context)} expr ${ExpressionT.toString(expr)}`) - reducer(expr, context, accessors) - } -} - -let withContext = (expression, context) => ExpressionWithContext(expression, context) -let noContext = expression => ExpressionNoContext(expression) - -let toString = expressionWithContext => - switch expressionWithContext { - | ExpressionNoContext(expr) => ExpressionT.toString(expr) - | ExpressionWithContext(expr, context) => - `${ExpressionT.toString(expr)} context: ${context - ->Bindings.toExpressionValue - ->InternalExpressionValue.toString}` - } - -let toStringResult = rExpressionWithContext => - switch rExpressionWithContext { - | Ok(expressionWithContext) => `Ok(${toString(expressionWithContext)})` - | Error(errorValue) => ErrorValue.errorToString(errorValue) - } - -let resultToValue = (rExpressionWithContext: result): t => { - switch rExpressionWithContext { - | Ok(expressionWithContext) => expressionWithContext - | Error(errorValue) => ErrorValue.toException(errorValue) - } -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res deleted file mode 100644 index f1bf0135..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_BindingsReplacer.res +++ /dev/null @@ -1,45 +0,0 @@ -module ErrorValue = Reducer_ErrorValue -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module Bindings = Reducer_Bindings - -type errorValue = Reducer_ErrorValue.errorValue -type expression = ExpressionT.expression -type internalExpressionValue = InternalExpressionValue.t - -let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$") - -let rec replaceSymbols = (bindings: ExpressionT.bindings, expression: expression): expression => - switch expression { - | ExpressionT.EValue(value) => replaceSymbolOnValue(bindings, value)->ExpressionT.EValue - | ExpressionT.EList(list) => - switch list { - | list{EValue(IEvCall(fName)), ..._args} => - switch isMacroName(fName) { - // A macro reduces itself so we dont dive in it - | true => expression - | false => replaceSymbolsOnExpressionList(bindings, list) - } - | _ => replaceSymbolsOnExpressionList(bindings, list) - } - } - -and replaceSymbolsOnExpressionList = (bindings, list) => { - let racc = - list->Belt.List.reduceReverse(list{}, (acc, each: expression) => - replaceSymbols(bindings, each)->Belt.List.add(acc, _) - ) - ExpressionT.EList(racc) -} -and replaceSymbolOnValue = (bindings, evValue: internalExpressionValue) => - switch evValue { - | IEvSymbol(symbol) => Bindings.getWithDefault(bindings, symbol, evValue) - | IEvCall(symbol) => Bindings.getWithDefault(bindings, symbol, evValue)->checkIfCallable - | _ => evValue - } -and checkIfCallable = (evValue: internalExpressionValue) => - switch evValue { - | IEvCall(_) | IEvLambda(_) => evValue - | _ => - ErrorValue.RENotAFunction(InternalExpressionValue.toString(evValue))->ErrorValue.toException - } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index 9cc25725..9f1ed5fb 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -1,86 +1,43 @@ -module BBindingsReplacer = Reducer_Expression_BindingsReplacer module BErrorValue = Reducer_ErrorValue -module BExpressionT = Reducer_Expression_T -module BInternalExpressionValue = ReducerInterface_InternalExpressionValue -module BBindings = Reducer_Bindings +module T = Reducer_T type errorValue = BErrorValue.errorValue -type expression = BExpressionT.expression -type expressionOrFFI = BExpressionT.expressionOrFFI -type ffiFn = BExpressionT.ffiFn -type internalCode = ReducerInterface_InternalExpressionValue.internalCode +type expression = Reducer_T.expression -let eArray = anArray => anArray->BInternalExpressionValue.IEvArray->BExpressionT.EValue +let eArray = (anArray: array) => anArray->T.EArray -let eArrayString = anArray => anArray->BInternalExpressionValue.IEvArrayString->BExpressionT.EValue +let eBool = aBool => aBool->T.IEvBool->T.EValue -let eBindings = (anArray: array<(string, BInternalExpressionValue.t)>) => - anArray->BBindings.fromArray->BBindings.toExpressionValue->BExpressionT.EValue +let eCall = (fn: expression, args: array): expression => T.ECall(fn, args) -let eBool = aBool => aBool->BInternalExpressionValue.IEvBool->BExpressionT.EValue +let eLambda = (parameters: array, expr: expression) => T.ELambda(parameters, expr) -let eCall = (name: string): expression => - name->BInternalExpressionValue.IEvCall->BExpressionT.EValue +let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue -let eFunction = (fName: string, lispArgs: list): expression => { - let fn = fName->eCall - list{fn, ...lispArgs}->BExpressionT.EList -} +let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord -let eLambda = ( - parameters: array, - context: BInternalExpressionValue.nameSpace, - expr: expression, -) => { - BInternalExpressionValue.IEvLambda({ - parameters: parameters, - context: context, - body: NotFFI(expr)->BBindings.castExpressionToInternalCode, - })->BExpressionT.EValue -} +let eString = aString => aString->T.IEvString->T.EValue -let eLambdaFFI = (ffiFn: ffiFn) => { - ffiFn->BBindings.eLambdaFFIValue->BExpressionT.EValue -} +let eSymbol = (name: string): expression => T.ESymbol(name) -let eNumber = aNumber => aNumber->BInternalExpressionValue.IEvNumber->BExpressionT.EValue +let eBlock = (exprs: array): expression => T.EBlock(exprs) -let eRecord = aMap => aMap->BInternalExpressionValue.IEvRecord->BExpressionT.EValue +let eProgram = (exprs: array): expression => T.EProgram(exprs) -let eString = aString => aString->BInternalExpressionValue.IEvString->BExpressionT.EValue +let eLetStatement = (symbol: string, valueExpression: expression): expression => T.EAssign( + symbol, + valueExpression, +) -let eSymbol = (name: string): expression => - name->BInternalExpressionValue.IEvSymbol->BExpressionT.EValue +let eTernary = ( + predicate: expression, + trueCase: expression, + falseCase: expression, +): expression => T.ETernary(predicate, trueCase, falseCase) -let eList = (list: list): expression => list->BExpressionT.EList +let eIdentifier = (name: string): expression => name->T.ESymbol -let eBlock = (exprs: list): expression => eFunction("$$_block_$$", exprs) +// let eTypeIdentifier = (name: string): expression => +// name->T.IEvTypeIdentifier->T.EValue -let eModule = (nameSpace: BInternalExpressionValue.nameSpace): expression => - nameSpace->BInternalExpressionValue.IEvBindings->BExpressionT.EValue - -let eLetStatement = (symbol: string, valueExpression: expression): expression => - eFunction("$_let_$", list{eSymbol(symbol), valueExpression}) - -let eBindStatement = (bindingExpr: expression, letStatement: expression): expression => - eFunction("$$_bindStatement_$$", list{bindingExpr, letStatement}) - -let eBindStatementDefault = (letStatement: expression): expression => - eFunction("$$_bindStatement_$$", list{letStatement}) - -let eBindExpression = (bindingExpr: expression, expression: expression): expression => - eFunction("$$_bindExpression_$$", list{bindingExpr, expression}) - -let eBindExpressionDefault = (expression: expression): expression => - eFunction("$$_bindExpression_$$", list{expression}) - -let eTernary = (truth: expression, trueCase: expression, falseCase: expression): expression => - eFunction("$$_ternary_$$", list{truth, trueCase, falseCase}) - -let eIdentifier = (name: string): expression => - name->BInternalExpressionValue.IEvSymbol->BExpressionT.EValue - -let eTypeIdentifier = (name: string): expression => - name->BInternalExpressionValue.IEvTypeIdentifier->BExpressionT.EValue - -let eVoid: expression = BInternalExpressionValue.IEvVoid->BExpressionT.EValue +let eVoid: expression = T.IEvVoid->T.EValue diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res index aa0745ae..e9a85a1a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -1,99 +1,60 @@ -module Bindings = Reducer_Bindings -module BindingsReplacer = Reducer_Expression_BindingsReplacer module ErrorValue = Reducer_ErrorValue -module ExpressionBuilder = Reducer_Expression_ExpressionBuilder -module ExpressionT = Reducer_Expression_T -module ExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module Result = Belt.Result - -type expression = ExpressionT.expression -type expressionOrFFI = ExpressionT.expressionOrFFI -type internalExpressionValue = ReducerInterface_InternalExpressionValue.t -type internalCode = ReducerInterface_InternalExpressionValue.internalCode - -external castInternalCodeToExpression: internalCode => expressionOrFFI = "%identity" - -let checkArity = ( - lambdaValue: ExpressionValue.lambdaValue, - args: list, -) => { - let reallyCheck = { - let argsLength = Belt.List.length(args) - let parametersLength = Js.Array2.length(lambdaValue.parameters) - if argsLength !== parametersLength { - ErrorValue.REArityError(None, parametersLength, argsLength)->ErrorValue.toException - } else { - args - } - } - let exprOrFFI = castInternalCodeToExpression(lambdaValue.body) - switch exprOrFFI { - | NotFFI(_) => reallyCheck - | FFI(_) => args - } -} - -let checkIfReduced = (args: list) => - args->Belt.List.reduceReverse(list{}, (acc, arg) => - switch arg { - | IEvSymbol(symbol) => ErrorValue.RESymbolNotFound(symbol)->ErrorValue.toException - | _ => list{arg, ...acc} - } - ) - -let caseNotFFI = ( - lambdaValue: ExpressionValue.lambdaValue, - expr, - args, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -) => { - let parameterList = lambdaValue.parameters->Belt.List.fromArray - let zippedParameterList = parameterList->Belt.List.zip(args) - let bindings = Belt.List.reduce(zippedParameterList, lambdaValue.context, ( - acc, - (variable, variableValue), - ) => acc->Bindings.set(variable, variableValue)) - let newExpression = ExpressionBuilder.eBlock(list{expr}) - reducer(newExpression, bindings, accessors) -} - -let caseFFI = (ffiFn: ExpressionT.ffiFn, args, accessors: ProjectAccessorsT.t) => { - switch ffiFn(args->Belt.List.toArray, accessors.environment) { - | Ok(value) => value - | Error(value) => value->ErrorValue.toException - } -} - -let applyParametersToLambda = ( - lambdaValue: ExpressionValue.lambdaValue, - args, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -): internalExpressionValue => { - let args = checkArity(lambdaValue, args)->checkIfReduced - let exprOrFFI = castInternalCodeToExpression(lambdaValue.body) - switch exprOrFFI { - | NotFFI(expr) => caseNotFFI(lambdaValue, expr, args, accessors, reducer) - | FFI(ffiFn) => caseFFI(ffiFn, args, accessors) - } -} let doLambdaCall = ( - lambdaValue: ExpressionValue.lambdaValue, + lambdaValue: Reducer_T.lambdaValue, args, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -) => applyParametersToLambda(lambdaValue, args, accessors, reducer) - -let foreignFunctionInterface = ( - lambdaValue: ExpressionValue.lambdaValue, - argArray: array, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, -): internalExpressionValue => { - let args = argArray->Belt.List.fromArray - applyParametersToLambda(lambdaValue, args, accessors, reducer) + environment: Reducer_T.environment, + reducer: Reducer_T.reducerFn, +): Reducer_T.value => { + lambdaValue.body(args, environment, reducer) +} + +let makeLambda = ( + parameters: array, + bindings: Reducer_T.bindings, + body: Reducer_T.expression, +): Reducer_T.lambdaValue => { + // TODO - clone bindings to avoid later redefinitions affecting lambdas? + + // Note: with this implementation, FFI lambdas (created by other methods than calling `makeLambda`) are allowed to violate the rules, pollute the bindings, etc. + // Not sure yet if that's a bug or a feature. + // FunctionRegistry functions are unaffected by this, their API is too limited. + + let lambda = ( + arguments: array, + environment: Reducer_T.environment, + reducer: Reducer_T.reducerFn, + ) => { + let argsLength = arguments->Js.Array2.length + let parametersLength = parameters->Js.Array2.length + if argsLength !== parametersLength { + ErrorValue.REArityError(None, parametersLength, argsLength)->ErrorValue.ErrorException->raise + } + + let localBindings = bindings->Reducer_Bindings.extend + let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, ( + currentBindings, + parameter, + index, + ) => { + currentBindings->Reducer_Bindings.set(parameter, arguments[index]) + }) + + let (value, _) = reducer( + body, + {bindings: localBindingsWithParameters, environment: environment}, + ) + value + } + + { + // context: bindings, + body: lambda, + parameters: parameters, + } +} + +let makeFFILambda = (body: Reducer_T.lambdaBody): Reducer_T.lambdaValue => { + body: body, + parameters: ["..."], } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res deleted file mode 100644 index 11a7cb0b..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res +++ /dev/null @@ -1,43 +0,0 @@ -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ExpressionWithContext = Reducer_ExpressionWithContext -module Result = Belt.Result -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T - -type environment = InternalExpressionValue.environment -type expression = ExpressionT.expression -type internalExpressionValue = InternalExpressionValue.t -type expressionWithContext = ExpressionWithContext.expressionWithContext - -let expandMacroCallRs = ( - macroExpression: expression, - bindings: ExpressionT.bindings, - accessors: ProjectAccessorsT.t, - reduceExpression: ProjectReducerFnT.t, -): result => - try { - Reducer_Dispatch_BuiltInMacros.dispatchMacroCall( - macroExpression, - bindings, - accessors, - reduceExpression, - )->Ok - } catch { - | exn => Reducer_ErrorValue.fromException(exn)->Error - } - -let doMacroCall = ( - macroExpression: expression, - bindings: ExpressionT.bindings, - accessors: ProjectAccessorsT.t, - reduceExpression: ProjectReducerFnT.t, -): internalExpressionValue => - Reducer_Dispatch_BuiltInMacros.dispatchMacroCall( - macroExpression, - bindings, - (accessors: ProjectAccessorsT.t), - (reduceExpression: ProjectReducerFnT.t), - )->ExpressionWithContext.callReducer(bindings, accessors, reduceExpression) - -let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$") diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res index be2755fe..71995c0a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res @@ -1,46 +1,31 @@ /* - An expression is a Lisp AST. An expression is either a primitive value or a list of expressions. - In the case of a list of expressions (e1, e2, e3, ...eN), the semantic is - apply e1, e2 -> apply e3 -> ... -> apply eN - This is Lisp semantics. It holds true in both eager and lazy evaluations. - A Lisp AST contains only expressions/primitive values to apply to their left. - The act of defining the semantics of a functional language is to write it in terms of Lisp AST. + An expression is an intermediate representation of a Squiggle code. + Expressions are evaluated by `Reducer_Expression.evaluate` function. */ -module Extra = Reducer_Extra -module InternalExpressionValue = ReducerInterface_InternalExpressionValue +type t = Reducer_T.expression -type internalExpressionValue = InternalExpressionValue.t -type environment = ReducerInterface_InternalExpressionValue.environment - -type rec expression = - | EList(list) // A list to map-reduce - | EValue(internalExpressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible -and bindings = InternalExpressionValue.nameSpace - -type t = expression - -type reducerFn = ( - expression, - bindings, - environment, -) => result +let commaJoin = values => values->Reducer_Extra_Array.intersperse(", ")->Js.String.concatMany("") +let semicolonJoin = values => + values->Reducer_Extra_Array.intersperse("; ")->Js.String.concatMany("") /* Converts the expression to String */ -let rec toString = expression => +let rec toString = (expression: t) => switch expression { - | EList(list{EValue(IEvCall("$$_block_$$")), ...statements}) => - `{${Belt.List.map(statements, aValue => toString(aValue)) - ->Extra.List.intersperse("; ") - ->Belt.List.toArray - ->Js.String.concatMany("")}}` - | EList(aList) => - `(${Belt.List.map(aList, aValue => toString(aValue)) - ->Extra.List.intersperse(" ") - ->Belt.List.toArray - ->Js.String.concatMany("")})` - | EValue(aValue) => InternalExpressionValue.toString(aValue) + | EBlock(statements) => + `{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}` + | EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin + | EArray(aList) => `[${Js.Array2.map(aList, aValue => toString(aValue))->commaJoin}]` + | ERecord(map) => + `{${map->Belt.Array.map(((key, value)) => `${key->toString}: ${value->toString}`)->commaJoin}}` + | ESymbol(name) => name + | ETernary(predicate, trueCase, falseCase) => + `${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})` + | EAssign(name, value) => `${name} = ${value->toString}` + | ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})` + | ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}` + | EValue(aValue) => Reducer_Value.toString(aValue) } let toStringResult = codeResult => @@ -55,34 +40,19 @@ let toStringResultOkless = codeResult => | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` } -let inspect = (expr: expression): expression => { +let inspect = (expr: t): t => { Js.log(toString(expr)) expr } -let inspectResult = (r: result): result< - expression, +let inspectResult = (r: result): result< + t, Reducer_ErrorValue.errorValue, > => { Js.log(toStringResult(r)) r } -type ffiFn = ( - array, - environment, -) => result - -type optionFfiFn = (array, environment) => option -type optionFfiFnReturningResult = ( - array, - environment, -) => option> - -type expressionOrFFI = - | NotFFI(expression) - | FFI(ffiFn) - let resultToValue = (rExpression: result): t => switch rExpression { | Ok(expression) => expression diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js.res deleted file mode 100644 index 8e7bd1f2..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js.res +++ /dev/null @@ -1 +0,0 @@ -module Gate = Reducer_Js_Gate diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js_Gate.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js_Gate.res deleted file mode 100644 index d7a267e2..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Js/Reducer_Js_Gate.res +++ /dev/null @@ -1,17 +0,0 @@ -open ReducerInterface_InternalExpressionValue -open Reducer_ErrorValue - -external castBool: unit => bool = "%identity" -external castNumber: unit => float = "%identity" -external castString: unit => string = "%identity" - -/* - As JavaScript returns us any type, we need to type check and cast type propertype before using it -*/ -let jsToIEv = (jsValue): result => - switch Js.typeof(jsValue) { - | "boolean" => jsValue->castBool->IEvBool->Ok - | "number" => jsValue->castNumber->IEvNumber->Ok - | "string" => jsValue->castString->IEvString->Ok - | other => RETodo(`Unhandled MathJs literal type: ${Js.String.make(other)}`)->Error - } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs.res deleted file mode 100644 index 640f010a..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs.res +++ /dev/null @@ -1 +0,0 @@ -module Eval = Reducer_MathJs_Eval diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res deleted file mode 100644 index 30cb2014..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res +++ /dev/null @@ -1,27 +0,0 @@ -module JavaScript = Reducer_Js -open ReducerInterface_InternalExpressionValue -open Reducer_ErrorValue - -@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 - -type answer = {"value": unit} - -/* - The result has to be delivered in an object so that we can type cast. - Rescript cannot type cast on basic values passed on their own. - This is why we call evalua inside Javascript and wrap the result in an Object - */ -let eval__: string => 'a = %raw(`function (expr) { return {value: Mathjs.evaluate(expr)}; }`) - -/* - Call MathJs evaluate and return as a variant -*/ -let eval = (expr: string): result => { - try { - let answer = eval__(expr) - answer["value"]->JavaScript.Gate.jsToIEv - } catch { - | Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error - } -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res new file mode 100644 index 00000000..e2757f60 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Namespace.res @@ -0,0 +1,34 @@ +/* + Namespace is a flat mapping of names to Squiggle values. + The full context of variables accessible to Squiggle is called "bindings"; see Reducer_Bindings module for details on it. +*/ +type t = Reducer_T.namespace + +let make = (): t => Belt.Map.String.empty + +let get = (namespace: t, id: string): option => namespace->Belt.Map.String.get(id) + +let set = (namespace: t, id: string, value): t => { + namespace->Belt.Map.String.set(id, value) +} + +let mergeFrom = (from: t, to: t): t => { + to->Belt.Map.String.reduce(from, (namespace, key, value) => { + namespace->set(key, value) + }) +} + +let mergeMany = (namespaces: array): t => + Belt.Array.reduce(namespaces, make(), (acc, ns) => acc->mergeFrom(ns)) + +let toString = (namespace: t) => + namespace + ->Belt.Map.String.toArray + ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${eachValue->Reducer_Value.toString}`) + ->Js.Array2.toString + +let fromArray = (a): t => Belt.Map.String.fromArray(a) + +let toMap = (namespace: t): Reducer_T.map => namespace + +let toRecord = (namespace: t): Reducer_T.value => namespace->toMap->IEvRecord diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index 646dae5a..4d8cc135 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -9,25 +9,12 @@ start zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda -// { return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])} -// {return [h.nodeVoid()];} outerBlock = statements:array_statements finalExpression: (statementSeparator @expression)? - { if (finalExpression != null) - { - var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]); - statements.push(newFinalExpression); - } - else - { - var newFinalStatement = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), h.nodeVoid()]); - statements.push(newFinalStatement); - } - return h.nodeBlock(statements) } + { if (finalExpression) statements.push(finalExpression) + return h.nodeProgram(statements) } / finalExpression: expression - { - var newFinalExpression = h.makeFunctionCall('$_endOfOuterBlock_$', [h.nodeVoid(), finalExpression]); - return h.nodeBlock([newFinalExpression])} + { return h.nodeProgram([finalExpression]) } innerBlockOrExpression = quotedInnerBlock @@ -36,7 +23,7 @@ innerBlockOrExpression quotedInnerBlock = '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' - { statements.push(finalExpression) + { if (finalExpression) statements.push(finalExpression) return h.nodeBlock(statements) } / '{' _nl finalExpression: expression _nl '}' { return h.nodeBlock([finalExpression]) } @@ -50,7 +37,6 @@ array_statements statement = letStatement / defunStatement - / typeStatement / voidStatement voidStatement @@ -168,7 +154,7 @@ chainFunctionCall unary = unaryOperator:unaryOperator _nl right:(unary/postOperator) - { return h.apply(h.unaryToFunction[unaryOperator], right)} + { return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right])} / postOperator unaryOperator "unary operator" @@ -247,7 +233,7 @@ number = number:(float / integer) unit:unitIdentifier? if (unit === null) { return number } else - { return h.apply('fromUnit_'+unit.value, number) + { return h.makeFunctionCall('fromUnit_'+unit.value, [number]) } } @@ -277,7 +263,7 @@ lambda { statements.push(finalExpression) return h.nodeLambda(args, h.nodeBlock(statements)) } / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}' - { return h.nodeLambda(args, h.nodeBlock([finalExpression])) } + { return h.nodeLambda(args, finalExpression) } arrayConstructor 'array' = '[' _nl ']' @@ -336,81 +322,3 @@ statementSeparator 'statement separator' newLine "newline" = [\n\r] - -// Types - -noArguments = ('(' _nl ')' )? - -typeIdentifier 'type identifier' - = ([a-z]+[_a-z0-9]i*) {return h.nodeTypeIdentifier(text())} - -typeConstructorIdentifier 'type constructor identifier' - = ([A-Z]+[_a-z0-9]i*) {return h.nodeTypeIdentifier(text())} - -typeExpression = typePostModifierExpression - -typePostModifierExpression = head:typeOr tail:(_ '$' _nl @typeModifier)* - { - return tail.reduce((result, element) => { - return h.makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args]) - }, head) - } - -typeOr = head:typeFunction tail:(_ '|' _nl @typeFunction)* - { return tail.length === 0 ? head : h.apply('$_typeOr_$', h.constructArray([head, ...tail])); } - -typeFunction = head:typeModifierExpression tail:(_ '=>' _nl @typeModifierExpression)* - { return tail.length === 0 ? head : h.apply( '$_typeFunction_$', h.constructArray([head, ...tail])); } - -typeModifierExpression = head:basicType tail:(_ '<-' _nl @typeModifier)* - { - return tail.reduce((result, element) => { - return h.makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args]) - }, head) - } - - typeModifier - = modifier:identifier _ '(' _nl args:array_elements _nl ')' - { return {modifier: modifier, args: args}; } - / modifier:identifier _ noArguments - { return {modifier: modifier, args: []}; } - -basicType = typeConstructor / typeArray / typeTuple / typeRecord / typeInParanthesis / typeIdentifier - -typeArray = '[' _nl elem:typeExpression _nl ']' - {return h.apply('$_typeArray_$', elem)} - -typeTuple = '[' _nl elems:array_typeTupleArguments _nl ']' - { return h.apply('$_typeTuple_$', h.constructArray(elems))} - - array_typeTupleArguments - = head:typeExpression tail:(_ ',' _nl @typeExpression)* - { return [head, ...tail]; } - -typeRecord = '{' _nl elems:array_typeRecordArguments _nl '}' - { return h.apply('$_typeRecord_$', h.constructRecord(elems)); } - - array_typeRecordArguments - = head:typeKeyValuePair tail:(_ ',' _nl @typeKeyValuePair)* - { return [head, ...tail]; } - - typeKeyValuePair - = key:identifier _ ':' _nl value:typeExpression - { return h.nodeKeyValue(key, value)} - -typeConstructor - = constructor:typeConstructorIdentifier _ '(' _nl args:array_types _nl ')' - { return h.makeFunctionCall('$_typeConstructor_$', [constructor, h.constructArray(args)]); } - / constructor:typeConstructorIdentifier _ noArguments - { return h.makeFunctionCall('$_typeConstructor_$', [constructor, h.constructArray([])]); } - - array_types = head:typeExpression tail:(_ ',' _nl @typeExpression)* - { return [head, ...tail]; } - -typeStatement = typeAliasStatement / typeOfStatement -typeAliasStatement = 'type' __nl typeIdentifier:typeIdentifier _nl '=' _nl typeExpression:typeExpression - { return h.makeFunctionCall('$_typeAlias_$', [typeIdentifier, typeExpression])} -typeOfStatement = identifier:identifier _ ':' _nl typeExpression:typeExpression - { return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])} - -typeInParanthesis = '(' _nl typeExpression:typeExpression _nl ')' {return typeExpression} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res index 8ecff48c..9c59c504 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res @@ -20,27 +20,31 @@ let parse = (expr: string): result => } type nodeBlock = {...node, "statements": array} +type nodeProgram = {...node, "statements": array} +type nodeArray = {...node, "elements": array} type nodeBoolean = {...node, "value": bool} -type nodeCallIdentifier = {...node, "value": string} -type nodeExpression = {...node, "nodes": array} +type nodeCall = {...node, "fn": node, "args": array} type nodeFloat = {...node, "value": float} type nodeIdentifier = {...node, "value": string} type nodeInteger = {...node, "value": int} type nodeKeyValue = {...node, "key": node, "value": node} -type nodeLambda = {...node, "args": array, "body": nodeBlock} +type nodeRecord = {...node, "elements": array} +type nodeLambda = {...node, "args": array, "body": node} type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node} type nodeModuleIdentifier = {...node, "value": string} type nodeString = {...node, "value": string} type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node} -type nodeTypeIdentifier = {...node, "value": string} +// type nodeTypeIdentifier = {...node, "value": string} type nodeVoid = node type peggyNode = | PgNodeBlock(nodeBlock) + | PgNodeProgram(nodeProgram) + | PgNodeArray(nodeArray) + | PgNodeRecord(nodeRecord) | PgNodeBoolean(nodeBoolean) - | PgNodeCallIdentifier(nodeCallIdentifier) - | PgNodeExpression(nodeExpression) | PgNodeFloat(nodeFloat) + | PgNodeCall(nodeCall) | PgNodeIdentifier(nodeIdentifier) | PgNodeInteger(nodeInteger) | PgNodeKeyValue(nodeKeyValue) @@ -49,13 +53,15 @@ type peggyNode = | PgNodeModuleIdentifier(nodeModuleIdentifier) | PgNodeString(nodeString) | PgNodeTernary(nodeTernary) - | PgNodeTypeIdentifier(nodeTypeIdentifier) + // | PgNodeTypeIdentifier(nodeTypeIdentifier) | PgNodeVoid(nodeVoid) external castNodeBlock: node => nodeBlock = "%identity" +external castNodeProgram: node => nodeProgram = "%identity" +external castNodeArray: node => nodeArray = "%identity" +external castNodeRecord: node => nodeRecord = "%identity" external castNodeBoolean: node => nodeBoolean = "%identity" -external castNodeCallIdentifier: node => nodeCallIdentifier = "%identity" -external castNodeExpression: node => nodeExpression = "%identity" +external castNodeCall: node => nodeCall = "%identity" external castNodeFloat: node => nodeFloat = "%identity" external castNodeIdentifier: node => nodeIdentifier = "%identity" external castNodeInteger: node => nodeInteger = "%identity" @@ -65,16 +71,18 @@ external castNodeLetStatement: node => nodeLetStatement = "%identity" external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity" external castNodeString: node => nodeString = "%identity" external castNodeTernary: node => nodeTernary = "%identity" -external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity" +// external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity" external castNodeVoid: node => nodeVoid = "%identity" exception UnsupportedPeggyNodeType(string) // This should never happen; programming error let castNodeType = (node: node) => switch node["type"] { | "Block" => node->castNodeBlock->PgNodeBlock + | "Program" => node->castNodeBlock->PgNodeProgram + | "Array" => node->castNodeArray->PgNodeArray + | "Record" => node->castNodeRecord->PgNodeRecord | "Boolean" => node->castNodeBoolean->PgNodeBoolean - | "CallIdentifier" => node->castNodeCallIdentifier->PgNodeCallIdentifier - | "Expression" => node->castNodeExpression->PgNodeExpression + | "Call" => node->castNodeCall->PgNodeCall | "Float" => node->castNodeFloat->PgNodeFloat | "Identifier" => node->castNodeIdentifier->PgNodeIdentifier | "Integer" => node->castNodeInteger->PgNodeInteger @@ -84,7 +92,7 @@ let castNodeType = (node: node) => | "ModuleIdentifier" => node->castNodeModuleIdentifier->PgNodeModuleIdentifier | "String" => node->castNodeString->PgNodeString | "Ternary" => node->castNodeTernary->PgNodeTernary - | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier + // | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier | "Void" => node->castNodeVoid->PgNodeVoid | _ => raise(UnsupportedPeggyNodeType(node["type"])) } @@ -96,17 +104,28 @@ let rec pgToString = (peggyNode: peggyNode): string => { let nodesToStringUsingSeparator = (nodes: array, separator: string): string => nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") + let pgNodesToStringUsingSeparator = (nodes: array, separator: string): string => + nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") + switch peggyNode { - | PgNodeBlock(node) => "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" + | PgNodeBlock(node) + | PgNodeProgram(node) => + "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" + | PgNodeArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]" + | PgNodeRecord(node) => + "{" ++ + node["elements"] + ->Js.Array2.map(element => PgNodeKeyValue(element)) + ->pgNodesToStringUsingSeparator(", ") ++ "}" | PgNodeBoolean(node) => node["value"]->Js.String.make - | PgNodeCallIdentifier(node) => `::${Js.String.make(node["value"])}` // This is an identifier also but for function names - | PgNodeExpression(node) => "(" ++ node["nodes"]->nodesToStringUsingSeparator(" ") ++ ")" + | PgNodeCall(node) => + "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")" | PgNodeFloat(node) => node["value"]->Js.String.make | PgNodeIdentifier(node) => `:${node["value"]}` | PgNodeInteger(node) => node["value"]->Js.String.make | PgNodeKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"]) | PgNodeLambda(node) => - "{|" ++ node["args"]->argsToString ++ "| " ++ pgToString(PgNodeBlock(node["body"])) ++ "}" + "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}" | PgNodeLetStatement(node) => pgToString(PgNodeIdentifier(node["variable"])) ++ " = " ++ toString(node["value"]) | PgNodeModuleIdentifier(node) => `@${node["value"]}` @@ -118,7 +137,7 @@ let rec pgToString = (peggyNode: peggyNode): string => { toString(node["trueExpression"]) ++ " " ++ toString(node["falseExpression"]) ++ ")" - | PgNodeTypeIdentifier(node) => `#${node["value"]}` + // | PgNodeTypeIdentifier(node) => `#${node["value"]}` | PgNodeVoid(_node) => "()" } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res index 73247a8e..ef8ed75b 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res @@ -2,32 +2,46 @@ module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T module Parse = Reducer_Peggy_Parse -type expression = ExpressionT.expression +type expression = Reducer_T.expression let rec fromNode = (node: Parse.node): expression => { let caseBlock = nodeBlock => - ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode)->Belt.List.fromArray) + ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode)) + + let caseProgram = nodeProgram => + ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(fromNode)) let caseLambda = (nodeLambda: Parse.nodeLambda): expression => { let args = - nodeLambda["args"] - ->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"]) - ->ExpressionBuilder.eArrayString - let body = nodeLambda["body"]->caseBlock - ExpressionBuilder.eFunction("$$_lambda_$$", list{args, body}) + nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"]) + let body = nodeLambda["body"]->fromNode + + ExpressionBuilder.eLambda(args, body) + } + + let caseRecord = (nodeRecord): expression => { + nodeRecord["elements"] + ->Js.Array2.map(keyValueNode => ( + keyValueNode["key"]->fromNode, + keyValueNode["value"]->fromNode, + )) + ->ExpressionBuilder.eRecord } switch Parse.castNodeType(node) { | PgNodeBlock(nodeBlock) => caseBlock(nodeBlock) + | PgNodeProgram(nodeProgram) => caseProgram(nodeProgram) + | PgNodeArray(nodeArray) => + ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode)) + | PgNodeRecord(nodeRecord) => caseRecord(nodeRecord) | PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"]) - | PgNodeCallIdentifier(nodeCallIdentifier) => ExpressionBuilder.eCall(nodeCallIdentifier["value"]) - | PgNodeExpression(nodeExpression) => - ExpressionT.EList(nodeExpression["nodes"]->Js.Array2.map(fromNode)->Belt.List.fromArray) + | PgNodeCall(nodeCall) => + ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode)) | PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"]) | PgNodeIdentifier(nodeIdentifier) => ExpressionBuilder.eSymbol(nodeIdentifier["value"]) | PgNodeInteger(nodeInteger) => ExpressionBuilder.eNumber(Belt.Int.toFloat(nodeInteger["value"])) | PgNodeKeyValue(nodeKeyValue) => - ExpressionT.EList(list{fromNode(nodeKeyValue["key"]), fromNode(nodeKeyValue["value"])}) + ExpressionBuilder.eArray([fromNode(nodeKeyValue["key"]), fromNode(nodeKeyValue["value"])]) | PgNodeLambda(nodeLambda) => caseLambda(nodeLambda) | PgNodeLetStatement(nodeLetStatement) => ExpressionBuilder.eLetStatement( @@ -38,16 +52,13 @@ let rec fromNode = (node: Parse.node): expression => { ExpressionBuilder.eIdentifier(nodeModuleIdentifier["value"]) | PgNodeString(nodeString) => ExpressionBuilder.eString(nodeString["value"]) | PgNodeTernary(nodeTernary) => - ExpressionBuilder.eFunction( - "$$_ternary_$$", - list{ - fromNode(nodeTernary["condition"]), - fromNode(nodeTernary["trueExpression"]), - fromNode(nodeTernary["falseExpression"]), - }, + ExpressionBuilder.eTernary( + fromNode(nodeTernary["condition"]), + fromNode(nodeTernary["trueExpression"]), + fromNode(nodeTernary["falseExpression"]), ) - | PgNodeTypeIdentifier(nodeTypeIdentifier) => - ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"]) + // | PgNodeTypeIdentifier(nodeTypeIdentifier) => + // ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"]) | PgNodeVoid(_) => ExpressionBuilder.eVoid } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts index 394796f5..f24fd819 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts @@ -1,8 +1,8 @@ import { LocationRange } from "peggy"; export const toFunction = { + "+": "add", "-": "subtract", - "->": "pipe", "!=": "unequal", ".-": "dotSubtract", ".*": "dotMultiply", @@ -13,7 +13,6 @@ export const toFunction = { "/": "divide", "&&": "and", "^": "pow", // or xor - "+": "add", "<": "smaller", "<=": "smallerEq", "==": "equal", @@ -40,9 +39,25 @@ type NodeBlock = { statements: AnyPeggyNode[]; }; -type NodeExpression = { - type: "Expression"; - nodes: AnyPeggyNode[]; +type NodeProgram = { + type: "Program"; + statements: AnyPeggyNode[]; +}; + +type NodeArray = { + type: "Array"; + elements: AnyPeggyNode[]; +}; + +type NodeRecord = { + type: "Record"; + elements: NodeKeyValue[]; +}; + +type NodeCall = { + type: "Call"; + fn: AnyPeggyNode; + args: AnyPeggyNode[]; }; type NodeFloat = { @@ -58,12 +73,6 @@ type NodeInteger = { type NodeIdentifier = { type: "Identifier"; value: string; - location: LocationRange; -}; - -type NodeCallIdentifier = { - type: "CallIdentifier"; - value: string; }; type NodeLetStatement = { @@ -103,12 +112,14 @@ type NodeBoolean = { }; export type AnyPeggyNode = + | NodeArray + | NodeRecord | NodeBlock - | NodeExpression + | NodeProgram + | NodeCall | NodeFloat | NodeInteger | NodeIdentifier - | NodeCallIdentifier | NodeLetStatement | NodeLambda | NodeTernary @@ -118,51 +129,36 @@ export type AnyPeggyNode = export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) { if (fn === "$$_applyAll_$$") { - // Any list of values is applied from left to right anyway. - // Like in Haskell and Lisp. - // So we remove the redundant $$_applyAll_$$. - if (args[0].type === "Identifier") { - args[0] = { - ...args[0], - type: "CallIdentifier", - }; - } - return nodeExpression(args); + return nodeCall(args[0], args.splice(1)); } else { - return nodeExpression([nodeCallIndentifier(fn), ...args]); + return nodeCall(nodeIdentifier(fn), args); } } -export function apply(fn: string, arg: AnyPeggyNode) { - return makeFunctionCall(fn, [arg]); +export function constructArray(elements: AnyPeggyNode[]) { + return { type: "Array", elements }; } -export function constructArray(elems: AnyPeggyNode[]) { - return makeFunctionCall("$_constructArray_$", elems); -} -export function constructRecord(elems: AnyPeggyNode[]) { - return apply("$_constructRecord_$", nodeExpression(elems)); +export function constructRecord(elements: AnyPeggyNode[]) { + return { type: "Record", elements }; } export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock { return { type: "Block", statements }; } +export function nodeProgram(statements: AnyPeggyNode[]): NodeProgram { + return { type: "Program", statements }; +} export function nodeBoolean(value: boolean): NodeBoolean { return { type: "Boolean", value }; } -export function nodeCallIndentifier(value: string): NodeCallIdentifier { - return { type: "CallIdentifier", value }; -} -export function nodeExpression(args: AnyPeggyNode[]): NodeExpression { - return { type: "Expression", nodes: args }; +export function nodeCall(fn: AnyPeggyNode, args: AnyPeggyNode[]): NodeCall { + return { type: "Call", fn, args }; } export function nodeFloat(value: number): NodeFloat { return { type: "Float", value }; } -export function nodeIdentifier( - value: string, - location: LocationRange -): NodeIdentifier { - return { type: "Identifier", value, location }; +export function nodeIdentifier(value: string): NodeIdentifier { + return { type: "Identifier", value }; } export function nodeInteger(value: number): NodeInteger { return { type: "Integer", value }; diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res new file mode 100644 index 00000000..85546905 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res @@ -0,0 +1,49 @@ +type environment = GenericDist.env + +@genType.opaque +type rec value = + | IEvArray(arrayValue) + | IEvBool(bool) + | IEvDate(Js.Date.t) + | IEvDeclaration(lambdaDeclaration) + | IEvDistribution(DistributionTypes.genericDist) + | IEvLambda(lambdaValue) + | IEvNumber(float) + | IEvRecord(map) + | IEvString(string) + | IEvTimeDuration(float) + | IEvVoid +@genType.opaque and arrayValue = array +@genType.opaque and map = Belt.Map.String.t +and lambdaBody = (array, environment, reducerFn) => value +@genType.opaque +and lambdaValue = { + parameters: array, + body: lambdaBody, +} +@genType.opaque and lambdaDeclaration = Declaration.declaration +and expression = + | EBlock(array) + // programs are similar to blocks, but don't create an inner scope. there can be only one program at the top level of the expression. + | EProgram(array) + | EArray(array) + | ERecord(array<(expression, expression)>) + | ESymbol(string) + | ETernary(expression, expression, expression) + | EAssign(string, expression) + | ECall(expression, array) + | ELambda(array, expression) + | EValue(value) + +and namespace = Belt.Map.String.t +and bindings = { + namespace: namespace, + parent: option, +} + +and context = { + bindings: bindings, + environment: environment, +} + +and reducerFn = (expression, context) => (value, context) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res deleted file mode 100644 index b2e0be9b..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Compile.res +++ /dev/null @@ -1,42 +0,0 @@ -module Bindings = Reducer_Bindings -module ErrorValue = Reducer_ErrorValue -module Expression = Reducer_Expression -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module T = Reducer_Type_T - -let ievFromTypeExpression = ( - typeExpressionSourceCode: string, - reducerFn: ProjectReducerFnT.t, -): result => { - let sIndex = "compiled" - let sourceCode = `type ${sIndex}=${typeExpressionSourceCode}` - Reducer_Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr => { - let accessors = ProjectAccessorsT.identityAccessors - let _result = reducerFn(expr, Bindings.emptyBindings, accessors) - let nameSpace = accessors.states.continuation - - switch Bindings.getType(nameSpace, sIndex) { - | Some(value) => value->Ok - | None => raise(Reducer_Exception.ImpossibleException("Reducer_Type_Compile-none")) - } - }) -} - -let fromTypeExpression = (typeExpressionSourceCode: string, reducerFn: ProjectReducerFnT.t): result< - T.t, - ErrorValue.t, -> => { - ievFromTypeExpression(typeExpressionSourceCode, reducerFn)->Belt.Result.map(T.fromIEvValue) -} - -let fromTypeExpressionExn = ( - typeExpressionSourceCode: string, - reducerFn: ProjectReducerFnT.t, -): T.t => - switch fromTypeExpression(typeExpressionSourceCode, reducerFn) { - | Ok(value) => value - | _ => `Cannot compile ${typeExpressionSourceCode}`->Reducer_Exception.ImpossibleException->raise - } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Contracts.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Contracts.res deleted file mode 100644 index 7b68f178..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_Contracts.res +++ /dev/null @@ -1,53 +0,0 @@ -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module T = Reducer_Type_T - -let isMin = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => { - let pair = (modifierArg, aValue) - switch pair { - | (IEvNumber(a), IEvNumber(b)) => a <= b - | _ => false - } -} - -let isMax = (modifierArg: InternalExpressionValue.t, aValue: InternalExpressionValue.t): bool => { - let pair = (modifierArg, aValue) - switch pair { - | (IEvNumber(a), IEvNumber(b)) => a >= b - | _ => false - } -} - -let isMemberOf = ( - modifierArg: InternalExpressionValue.t, - aValue: InternalExpressionValue.t, -): bool => { - let pair = (modifierArg, aValue) - switch pair { - | (ievA, IEvArray(b)) => Js.Array2.includes(b, ievA) - | _ => false - } -} - -let checkModifier = ( - key: string, - modifierArg: InternalExpressionValue.t, - aValue: InternalExpressionValue.t, -): bool => - switch key { - | "min" => isMin(modifierArg, aValue) - | "max" => isMax(modifierArg, aValue) - | "isMemberOf" => isMemberOf(modifierArg, aValue) - | _ => false - } - -let checkModifiers = ( - contracts: Belt.Map.String.t, - aValue: InternalExpressionValue.t, -): bool => { - contracts->Belt.Map.String.reduce(true, (acc, key, modifierArg) => - switch acc { - | true => checkModifier(key, modifierArg, aValue) - | _ => acc - } - ) -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res deleted file mode 100644 index 511fe815..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_T.res +++ /dev/null @@ -1,119 +0,0 @@ -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -open InternalExpressionValue - -type rec iType = - | ItTypeIdentifier(string) - | ItModifiedType({modifiedType: iType, contracts: Belt.Map.String.t}) - | ItTypeOr({typeOr: array}) - | ItTypeFunction({inputs: array, output: iType}) - | ItTypeArray({element: iType}) - | ItTypeTuple({elements: array}) - | ItTypeRecord({properties: Belt.Map.String.t}) - -type t = iType -type typeErrorValue = TypeMismatch(t, InternalExpressionValue.t) - -let rec toString = (t: t): string => { - switch t { - | ItTypeIdentifier(s) => s - | ItModifiedType({modifiedType, contracts}) => - `${toString(modifiedType)}${contracts->Belt.Map.String.reduce("", (acc, k, v) => - Js.String2.concatMany(acc, ["<-", k, "(", InternalExpressionValue.toString(v), ")"]) - )}` - | ItTypeOr({typeOr}) => `(${Js.Array2.map(typeOr, toString)->Js.Array2.joinWith(" | ")})` - | ItTypeFunction({inputs, output}) => - `(${inputs->Js.Array2.map(toString)->Js.Array2.joinWith(" => ")} => ${toString(output)})` - | ItTypeArray({element}) => `[${toString(element)}]` - | ItTypeTuple({elements}) => `[${Js.Array2.map(elements, toString)->Js.Array2.joinWith(", ")}]` - | ItTypeRecord({properties}) => - `{${properties - ->Belt.Map.String.toArray - ->Js.Array2.map(((k, v)) => Js.String2.concatMany(k, [": ", toString(v)])) - ->Js.Array2.joinWith(", ")}}` - } -} - -let toStringResult = (rt: result) => - switch rt { - | Ok(t) => toString(t) - | Error(e) => ErrorValue.errorToString(e) - } - -let rec fromTypeMap = typeMap => { - let default = IEvString("") - let evTypeTag: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "typeTag", - default, - ) - let evTypeIdentifier: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "typeIdentifier", - default, - ) - let evTypeOr: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "typeOr", - default, - ) - let evInputs: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "inputs", - default, - ) - let evOutput: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "output", - default, - ) - let evElement: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "element", - default, - ) - let evElements: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "elements", - default, - ) - let evProperties: InternalExpressionValue.t = Belt.Map.String.getWithDefault( - typeMap, - "properties", - default, - ) - - let contracts = - typeMap->Belt.Map.String.keep((k, _v) => ["min", "max", "memberOf"]->Js.Array2.includes(k)) - - let makeIt = switch evTypeTag { - | IEvString("typeIdentifier") => fromIEvValue(evTypeIdentifier) - | IEvString("typeOr") => ItTypeOr({typeOr: fromIEvArray(evTypeOr)}) - | IEvString("typeFunction") => - ItTypeFunction({inputs: fromIEvArray(evInputs), output: fromIEvValue(evOutput)}) - | IEvString("typeArray") => ItTypeArray({element: fromIEvValue(evElement)}) - | IEvString("typeTuple") => ItTypeTuple({elements: fromIEvArray(evElements)}) - | IEvString("typeRecord") => ItTypeRecord({properties: fromIEvRecord(evProperties)}) - | _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-evTypeTag")) - } - - Belt.Map.String.isEmpty(contracts) - ? makeIt - : ItModifiedType({modifiedType: makeIt, contracts: contracts}) -} - -and fromIEvValue = (ievValue: InternalExpressionValue.t): iType => - switch ievValue { - | IEvTypeIdentifier(typeIdentifier) => ItTypeIdentifier({typeIdentifier}) - | IEvType(typeMap) => fromTypeMap(typeMap) - | _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievValue")) - } -and fromIEvArray = (ievArray: InternalExpressionValue.t) => - switch ievArray { - | IEvArray(array) => array->Belt.Array.map(fromIEvValue) - | _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievArray")) - } -and fromIEvRecord = (ievRecord: InternalExpressionValue.t) => - switch ievRecord { - | IEvRecord(record) => record->Belt.Map.String.map(fromIEvValue) - | _ => raise(Reducer_Exception.ImpossibleException("Reducer_Type_T-ievRecord")) - } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res deleted file mode 100644 index d3906a38..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeBuilder.res +++ /dev/null @@ -1,80 +0,0 @@ -open ReducerInterface_InternalExpressionValue - -let typeModifier_memberOf = (aType, anArray) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("memberOf", anArray)->IEvType->Ok -} - -let typeModifier_memberOf_update = (aRecord, anArray) => { - aRecord->Belt.Map.String.set("memberOf", anArray)->IEvType->Ok -} - -let typeModifier_min = (aType, value) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("min", value)->IEvType->Ok -} - -let typeModifier_min_update = (aRecord, value) => { - aRecord->Belt.Map.String.set("min", value)->IEvType->Ok -} - -let typeModifier_max = (aType, value) => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeIdentifier")), - ("typeIdentifier", aType), - ]) - newRecord->Belt.Map.String.set("max", value)->IEvType->Ok -} - -let typeModifier_max_update = (aRecord, value) => - aRecord->Belt.Map.String.set("max", value)->IEvType->Ok - -let typeModifier_opaque_update = aRecord => - aRecord->Belt.Map.String.set("opaque", IEvBool(true))->IEvType->Ok - -let typeOr = evArray => { - let newRecord = Belt.Map.String.fromArray([("typeTag", IEvString("typeOr")), ("typeOr", evArray)]) - newRecord->IEvType->Ok -} - -let typeFunction = anArray => { - let output = Belt.Array.getUnsafe(anArray, Js.Array2.length(anArray) - 1) - let inputs = Js.Array2.slice(anArray, ~start=0, ~end_=-1) - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeFunction")), - ("inputs", IEvArray(inputs)), - ("output", output), - ]) - newRecord->IEvType->Ok -} - -let typeArray = element => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeArray")), - ("element", element), - ]) - newRecord->IEvType->Ok -} - -let typeTuple = anArray => { - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeTuple")), - ("elements", IEvArray(anArray)), - ]) - newRecord->IEvType->Ok -} - -let typeRecord = propertyMap => { - let newProperties = propertyMap->IEvRecord - let newRecord = Belt.Map.String.fromArray([ - ("typeTag", IEvString("typeRecord")), - ("properties", newProperties), - ]) - newRecord->IEvType->Ok -} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res deleted file mode 100644 index c9152e3c..00000000 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Type/Reducer_Type_TypeChecker.res +++ /dev/null @@ -1,182 +0,0 @@ -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -module T = Reducer_Type_T -module TypeContracts = Reducer_Type_Contracts -open InternalExpressionValue - -let rec isITypeOf = (anIType: T.iType, aValue): result => { - let caseTypeIdentifier = (anUpperTypeName, aValue) => { - let aTypeName = anUpperTypeName->Js.String2.toLowerCase - switch aTypeName { - | "any" => Ok(true) - | _ => { - let valueTypeName = aValue->valueToValueType->valueTypeToString->Js.String2.toLowerCase - switch aTypeName == valueTypeName { - | true => Ok(true) - | false => T.TypeMismatch(anIType, aValue)->Error - } - } - } - } - - let caseRecord = (anIType, propertyMap: Belt.Map.String.t, evValue) => - switch evValue { - | IEvRecord(aRecord) => - if ( - Js.Array2.length(propertyMap->Belt.Map.String.keysToArray) == - Js.Array2.length(aRecord->Belt.Map.String.keysToArray) - ) { - Belt.Map.String.reduce(propertyMap, Ok(true), (acc, property, propertyType) => { - Belt.Result.flatMap(acc, _ => - switch Belt.Map.String.get(aRecord, property) { - | Some(propertyValue) => isITypeOf(propertyType, propertyValue) - | None => T.TypeMismatch(anIType, evValue)->Error - } - ) - }) - } else { - T.TypeMismatch(anIType, evValue)->Error - } - - | _ => T.TypeMismatch(anIType, evValue)->Error - } - - let caseArray = (anIType, elementType, evValue) => - switch evValue { - | IEvArray(anArray) => - Belt.Array.reduce(anArray, Ok(true), (acc, element) => - Belt.Result.flatMap(acc, _ => - switch isITypeOf(elementType, element) { - | Ok(_) => Ok(true) - | Error(error) => error->Error - } - ) - ) - | _ => T.TypeMismatch(anIType, evValue)->Error - } - - let caseTuple = (anIType, elementTypes, evValue) => - switch evValue { - | IEvArray(anArray) => - if Js.Array2.length(elementTypes) == Js.Array2.length(anArray) { - let zipped = Belt.Array.zip(elementTypes, anArray) - Belt.Array.reduce(zipped, Ok(true), (acc, (elementType, element)) => - switch acc { - | Ok(_) => - switch isITypeOf(elementType, element) { - | Ok(_) => acc - | Error(error) => Error(error) - } - | _ => acc - } - ) - } else { - T.TypeMismatch(anIType, evValue)->Error - } - | _ => T.TypeMismatch(anIType, evValue)->Error - } - - let caseOr = (anIType, anITypeArray, evValue) => - switch Belt.Array.reduce(anITypeArray, Ok(false), (acc, anIType) => - Belt.Result.flatMap(acc, _ => - switch acc { - | Ok(false) => - switch isITypeOf(anIType, evValue) { - | Ok(_) => Ok(true) - | Error(_) => acc - } - | _ => acc - } - ) - ) { - | Ok(true) => Ok(true) - | Ok(false) => T.TypeMismatch(anIType, evValue)->Error - | Error(error) => Error(error) - } - - let caseModifiedType = ( - anIType: T.iType, - modifiedType: T.iType, - contracts: Belt.Map.String.t, - aValue: InternalExpressionValue.t, - ) => { - isITypeOf(modifiedType, aValue)->Belt.Result.flatMap(_result => { - if TypeContracts.checkModifiers(contracts, aValue) { - Ok(true) - } else { - T.TypeMismatch(anIType, aValue)->Error - } - }) - } - - switch anIType { - | ItTypeIdentifier(name) => caseTypeIdentifier(name, aValue) - | ItModifiedType({modifiedType, contracts}) => - caseModifiedType(anIType, modifiedType, contracts, aValue) //{modifiedType: iType, contracts: Belt.Map.String.t} - | ItTypeOr({typeOr}) => caseOr(anIType, typeOr, aValue) - | ItTypeFunction(_) => - raise( - Reducer_Exception.ImpossibleException( - "Reducer_TypeChecker-functions are without a type at the moment", - ), - ) - | ItTypeArray({element}) => caseArray(anIType, element, aValue) - | ItTypeTuple({elements}) => caseTuple(anIType, elements, aValue) - | ItTypeRecord({properties}) => caseRecord(anIType, properties, aValue) - } -} - -let isTypeOf = ( - typeExpressionSourceCode: string, - aValue: InternalExpressionValue.t, - reducerFn: ProjectReducerFnT.t, -): result => { - switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { - | Ok(anIType) => - switch isITypeOf(anIType, aValue) { - | Ok(_) => Ok(aValue) - | Error(T.TypeMismatch(anIType, evValue)) => - Error( - ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString), - ) - } - | Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch - } -} - -let checkITypeArguments = (anIType: T.iType, args: array): result< - bool, - T.typeErrorValue, -> => { - switch anIType { - | T.ItTypeFunction({inputs}) => isITypeOf(T.ItTypeTuple({elements: inputs}), args->IEvArray) - | _ => T.TypeMismatch(anIType, args->IEvArray)->Error - } -} - -let checkITypeArgumentsBool = (anIType: T.iType, args: array): bool => { - switch checkITypeArguments(anIType, args) { - | Ok(_) => true - | _ => false - } -} - -let checkArguments = ( - typeExpressionSourceCode: string, - args: array, - reducerFn: ProjectReducerFnT.t, -): result => { - switch typeExpressionSourceCode->Reducer_Type_Compile.fromTypeExpression(reducerFn) { - | Ok(anIType) => - switch checkITypeArguments(anIType, args) { - | Ok(_) => Ok(args->IEvArray) - | Error(T.TypeMismatch(anIType, evValue)) => - Error( - ErrorValue.REExpectedType(anIType->T.toString, evValue->InternalExpressionValue.toString), - ) - } - | Error(error) => Error(error) // Directly propagating - err => err - causes type mismatch - } -} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res similarity index 59% rename from packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res rename to packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res index 5531cbdf..cd1eefef 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res @@ -1,51 +1,18 @@ +// deprecated, use Reducer_T instead +// (value methods should be moved to Reducer_Value.res) + module ErrorValue = Reducer_ErrorValue -module Extra_Array = Reducer_Extra_Array -type internalCode = Object type environment = GenericDist.env +module T = Reducer_T -let defaultEnvironment: environment = DistributionOperation.defaultEnv - -@genType.opaque -type rec t = - | IEvArray(array) // FIXME: Convert to MapInt - | IEvArrayString(array) - | IEvBool(bool) - | IEvCall(string) // External function call - | IEvDate(Js.Date.t) - | IEvDeclaration(lambdaDeclaration) - | IEvDistribution(DistributionTypes.genericDist) - | IEvLambda(lambdaValue) - | IEvBindings(nameSpace) - | IEvNumber(float) - | IEvRecord(map) - | IEvString(string) - | IEvSymbol(string) - | IEvTimeDuration(float) - | IEvType(map) - | IEvTypeIdentifier(string) - | IEvVoid -@genType.opaque and squiggleArray = array -@genType.opaque and map = Belt.Map.String.t -@genType.opaque and nameSpace = NameSpace(Belt.Map.String.t) -@genType.opaque -and lambdaValue = { - parameters: array, - context: nameSpace, - body: internalCode, -} -@genType.opaque and lambdaDeclaration = Declaration.declaration - -type internalExpressionValue = t +type t = Reducer_T.value type functionCall = (string, array) -let rec toString = aValue => +let rec toString = (aValue: T.value) => switch aValue { | IEvArray(anArray) => toStringArray(anArray) - | IEvArrayString(anArray) => toStringArrayString(anArray) - | IEvBindings(m) => toStringBindings(m) | IEvBool(aBool) => toStringBool(aBool) - | IEvCall(fName) => toStringCall(fName) | IEvDate(date) => toStringDate(date) | IEvDeclaration(d) => toStringDeclaration(d) | IEvDistribution(dist) => toStringDistribution(dist) @@ -53,36 +20,27 @@ let rec toString = aValue => | IEvNumber(aNumber) => toStringNumber(aNumber) | IEvRecord(aMap) => aMap->toStringRecord | IEvString(aString) => toStringString(aString) - | IEvSymbol(aString) => toStringSymbol(aString) | IEvTimeDuration(t) => toStringTimeDuration(t) - | IEvType(aMap) => toStringType(aMap) - | IEvTypeIdentifier(id) => toStringTypeIdentifier(id) | IEvVoid => toStringVoid } and toStringArray = anArray => { let args = anArray->Js.Array2.map(each => toString(each))->Js.Array2.toString `[${args}]` } -and toStringArrayString = anArray => { - let args = anArray->Js.Array2.toString - `[${args}]` -} -and toStringBindings = m => `@${m->toStringNameSpace}` and toStringBool = aBool => Js.String.make(aBool) and toStringCall = fName => `:${fName}` and toStringDate = date => DateTime.Date.toString(date) and toStringDeclaration = d => Declaration.toString(d, r => toString(IEvLambda(r))) and toStringDistribution = dist => GenericDist.toString(dist) -and toStringLambda = lambdaValue => +and toStringLambda = (lambdaValue: T.lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)` -and toStringFunction = lambdaValue => `function(${Js.Array2.toString(lambdaValue.parameters)})` +and toStringFunction = (lambdaValue: T.lambdaValue) => + `function(${Js.Array2.toString(lambdaValue.parameters)})` and toStringNumber = aNumber => Js.String.make(aNumber) and toStringRecord = aMap => aMap->toStringMap and toStringString = aString => `'${aString}'` and toStringSymbol = aString => `:${aString}` and toStringTimeDuration = t => DateTime.Duration.toString(t) -and toStringType = aMap => aMap->toStringMap -and toStringTypeIdentifier = id => `#${id}` and toStringVoid = `()` and toStringMap = aMap => { @@ -93,29 +51,19 @@ and toStringMap = aMap => { ->Js.Array2.toString `{${pairs}}` } -and toStringNameSpace = nameSpace => { - let NameSpace(container) = nameSpace - container->toStringMap -} -let toStringWithType = aValue => +let toStringWithType = (aValue: T.value) => switch aValue { | IEvArray(_) => `Array::${toString(aValue)}` - | IEvArrayString(_) => `ArrayString::${toString(aValue)}` | IEvBool(_) => `Bool::${toString(aValue)}` - | IEvCall(_) => `Call::${toString(aValue)}` | IEvDate(_) => `Date::${toString(aValue)}` | IEvDeclaration(_) => `Declaration::${toString(aValue)}` | IEvDistribution(_) => `Distribution::${toString(aValue)}` | IEvLambda(_) => `Lambda::${toString(aValue)}` - | IEvBindings(_) => `Bindings::${toString(aValue)}` | IEvNumber(_) => `Number::${toString(aValue)}` | IEvRecord(_) => `Record::${toString(aValue)}` | IEvString(_) => `String::${toString(aValue)}` - | IEvSymbol(_) => `Symbol::${toString(aValue)}` | IEvTimeDuration(_) => `Date::${toString(aValue)}` - | IEvType(_) => `Type::${toString(aValue)}` - | IEvTypeIdentifier(_) => `TypeIdentifier::${toString(aValue)}` | IEvVoid => `Void` } @@ -151,45 +99,33 @@ let toStringResultRecord = x => type internalExpressionValueType = | EvtArray - | EvtArrayString | EvtBool - | EvtCall | EvtDate | EvtDeclaration | EvtDistribution | EvtLambda - | EvtModule | EvtNumber | EvtRecord | EvtString - | EvtSymbol | EvtTimeDuration - | EvtType - | EvtTypeIdentifier | EvtVoid type functionCallSignature = CallSignature(string, array) type functionDefinitionSignature = FunctionDefinitionSignature(functionCallSignature, internalExpressionValueType) -let valueToValueType = value => +let valueToValueType = (value: T.value) => switch value { | IEvArray(_) => EvtArray - | IEvArrayString(_) => EvtArrayString | IEvBool(_) => EvtBool - | IEvCall(_) => EvtCall | IEvDate(_) => EvtDate | IEvDeclaration(_) => EvtDeclaration | IEvDistribution(_) => EvtDistribution | IEvLambda(_) => EvtLambda - | IEvBindings(_) => EvtModule | IEvNumber(_) => EvtNumber | IEvRecord(_) => EvtRecord | IEvString(_) => EvtString - | IEvSymbol(_) => EvtSymbol | IEvTimeDuration(_) => EvtTimeDuration - | IEvType(_) => EvtType - | IEvTypeIdentifier(_) => EvtTypeIdentifier | IEvVoid => EvtVoid } @@ -201,21 +137,15 @@ let functionCallToCallSignature = (functionCall: functionCall): functionCallSign let valueTypeToString = (valueType: internalExpressionValueType): string => switch valueType { | EvtArray => `Array` - | EvtArrayString => `ArrayString` | EvtBool => `Bool` - | EvtCall => `Call` | EvtDate => `Date` | EvtDeclaration => `Declaration` | EvtDistribution => `Distribution` | EvtLambda => `Lambda` - | EvtModule => `Module` | EvtNumber => `Number` | EvtRecord => `Record` | EvtString => `String` - | EvtSymbol => `Symbol` | EvtTimeDuration => `Duration` - | EvtType => `Type` - | EvtTypeIdentifier => `TypeIdentifier` | EvtVoid => `Void` } @@ -226,27 +156,10 @@ let functionCallSignatureToString = (functionCallSignature: functionCallSignatur let arrayToValueArray = (arr: array): array => arr -let recordToKeyValuePairs = (record: map): array<(string, t)> => record->Belt.Map.String.toArray - -// let nameSpaceToTypeScriptBindings = ( -// nameSpace: nameSpace, -// ) => { -// let NameSpace(container) = nameSpace -// Belt.Map.String.map(container, e => e->Belt.Map.String.toArray->Js.Dict.fromArray) -// } - -let nameSpaceToKeyValuePairs = (nameSpace: nameSpace): array<(string, t)> => { - let NameSpace(container) = nameSpace - container->Belt.Map.String.toArray -} - -let nameSpaceGet = (nameSpace: nameSpace, key: string): option => { - let NameSpace(container) = nameSpace - container->Belt.Map.String.get(key) -} - let resultToValue = (rExpression: result): t => switch rExpression { | Ok(expression) => expression | Error(errorValue) => Reducer_ErrorValue.toException(errorValue) } + +let recordToKeyValuePairs = (record: T.map): array<(string, t)> => record->Belt.Map.String.toArray diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface.res deleted file mode 100644 index d19b93b3..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface.res +++ /dev/null @@ -1,3 +0,0 @@ -module ExternalLibrary = ReducerInterface_ExternalLibrary -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module StdLib = ReducerInterface_StdLib diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Date.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Date.res deleted file mode 100644 index 3396d0f3..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Date.res +++ /dev/null @@ -1,27 +0,0 @@ -module IEV = ReducerInterface_InternalExpressionValue -type internalExpressionValue = IEV.t - -let dispatch = (call: IEV.functionCall, _: GenericDist.env): option< - result, -> => { - switch call { - | ("toString", [IEvDate(t)]) => IEV.IEvString(DateTime.Date.toString(t))->Ok->Some - | ("makeDateFromYear", [IEvNumber(year)]) => - switch DateTime.Date.makeFromYear(year) { - | Ok(t) => IEV.IEvDate(t)->Ok->Some - | Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some - } - | ("dateFromNumber", [IEvNumber(f)]) => IEV.IEvDate(DateTime.Date.fromFloat(f))->Ok->Some - | ("toNumber", [IEvDate(f)]) => IEV.IEvNumber(DateTime.Date.toFloat(f))->Ok->Some - | ("subtract", [IEvDate(d1), IEvDate(d2)]) => - switch DateTime.Date.subtract(d1, d2) { - | Ok(d) => IEV.IEvTimeDuration(d)->Ok - | Error(e) => Error(RETodo(e)) - }->Some - | ("subtract", [IEvDate(d1), IEvTimeDuration(d2)]) => - IEV.IEvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some - | ("add", [IEvDate(d1), IEvTimeDuration(d2)]) => - IEV.IEvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some - | _ => None - } -} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Duration.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Duration.res deleted file mode 100644 index f9e06de4..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Duration.res +++ /dev/null @@ -1,36 +0,0 @@ -module IEV = ReducerInterface_InternalExpressionValue -type internalExpressionValue = IEV.t - -let dispatch = (call: IEV.functionCall, _: GenericDist.env): option< - result, -> => { - switch call { - | ("toString", [IEvTimeDuration(t)]) => IEV.IEvString(DateTime.Duration.toString(t))->Ok->Some - | ("minutes", [IEvNumber(f)]) => IEV.IEvTimeDuration(DateTime.Duration.fromMinutes(f))->Ok->Some - | ("fromUnit_minutes", [IEvNumber(f)]) => - IEV.IEvTimeDuration(DateTime.Duration.fromMinutes(f))->Ok->Some - | ("hours", [IEvNumber(f)]) => IEV.IEvTimeDuration(DateTime.Duration.fromHours(f))->Ok->Some - | ("fromUnit_hours", [IEvNumber(f)]) => - IEV.IEvTimeDuration(DateTime.Duration.fromHours(f))->Ok->Some - | ("days", [IEvNumber(f)]) => IEV.IEvTimeDuration(DateTime.Duration.fromDays(f))->Ok->Some - | ("fromUnit_days", [IEvNumber(f)]) => - IEV.IEvTimeDuration(DateTime.Duration.fromDays(f))->Ok->Some - | ("years", [IEvNumber(f)]) => IEV.IEvTimeDuration(DateTime.Duration.fromYears(f))->Ok->Some - | ("fromUnit_years", [IEvNumber(f)]) => - IEV.IEvTimeDuration(DateTime.Duration.fromYears(f))->Ok->Some - | ("toHours", [IEvTimeDuration(f)]) => IEV.IEvNumber(DateTime.Duration.toHours(f))->Ok->Some - | ("toMinutes", [IEvTimeDuration(f)]) => IEV.IEvNumber(DateTime.Duration.toMinutes(f))->Ok->Some - | ("toDays", [IEvTimeDuration(f)]) => IEV.IEvNumber(DateTime.Duration.toDays(f))->Ok->Some - | ("toYears", [IEvTimeDuration(f)]) => IEV.IEvNumber(DateTime.Duration.toYears(f))->Ok->Some - | ("add", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) => - IEV.IEvTimeDuration(DateTime.Duration.add(d1, d2))->Ok->Some - | ("subtract", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) => - IEV.IEvTimeDuration(DateTime.Duration.subtract(d1, d2))->Ok->Some - | ("multiply", [IEvTimeDuration(d1), IEvNumber(d2)]) => - IEV.IEvTimeDuration(DateTime.Duration.multiply(d1, d2))->Ok->Some - | ("divide", [IEvTimeDuration(d1), IEvNumber(d2)]) => - IEV.IEvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok->Some - | ("divide", [IEvTimeDuration(d1), IEvTimeDuration(d2)]) => IEV.IEvNumber(d1 /. d2)->Ok->Some - | _ => None - } -} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res deleted file mode 100644 index 89528a32..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ /dev/null @@ -1,39 +0,0 @@ -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ProjectReducerFnT = ReducerProject_ReducerFn_T -type internalExpressionValue = InternalExpressionValue.t - -/* - Map external calls of Reducer -*/ -let dispatch = ( - call: InternalExpressionValue.functionCall, - accessors: ProjectAccessorsT.t, - reducer: ProjectReducerFnT.t, - chain, -): result => { - E.A.O.firstSomeFn([ - () => ReducerInterface_GenericDistribution.dispatch(call, accessors.environment), - () => ReducerInterface_Date.dispatch(call, accessors.environment), - () => ReducerInterface_Duration.dispatch(call, accessors.environment), - () => ReducerInterface_Number.dispatch(call, accessors.environment), - () => FunctionRegistry_Library.dispatch(call, accessors, reducer), - ])->E.O2.defaultFn(() => chain(call, accessors, reducer)) -} - -/* -If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. - -The final chain(call) invokes the builtin default functions of the interpreter. - -Via chain(call), all MathJs operators and functions are available for string, number , boolean, array and record - .e.g + - / * > >= < <= == /= not and or sin cos log ln concat, etc. - -// See https://mathjs.org/docs/expressions/syntax.html -// See https://mathjs.org/docs/reference/functions.html - -Remember from the users point of view, there are no different modules: -// "doSth( constructorType1 )" -// "doSth( constructorType2 )" -doSth gets dispatched to the correct module because of the type signature. You get function and operator abstraction for free. You don't need to combine different implementations into one type. That would be duplicating the repsonsibility of the dispatcher. -*/ diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res deleted file mode 100644 index addd4388..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ /dev/null @@ -1,310 +0,0 @@ -module IEV = ReducerInterface_InternalExpressionValue -type internalExpressionValue = IEV.t - -module Helpers = { - let arithmeticMap = r => - switch r { - | "add" => #Add - | "dotAdd" => #Add - | "subtract" => #Subtract - | "dotSubtract" => #Subtract - | "divide" => #Divide - | "log" => #Logarithm - | "dotDivide" => #Divide - | "pow" => #Power - | "dotPow" => #Power - | "multiply" => #Multiply - | "dotMultiply" => #Multiply - | _ => #Multiply - } - - let catchAndConvertTwoArgsToDists = (args: array): option<( - DistributionTypes.genericDist, - DistributionTypes.genericDist, - )> => - switch args { - | [IEvDistribution(a), IEvDistribution(b)] => Some((a, b)) - | [IEvNumber(a), IEvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) - | [IEvDistribution(a), IEvNumber(b)] => Some((a, GenericDist.fromFloat(b))) - | _ => None - } - - let toFloatFn = ( - fnCall: DistributionTypes.DistributionOperation.toFloat, - dist: DistributionTypes.genericDist, - ~env: GenericDist.env, - ) => { - FromDist(#ToFloat(fnCall), dist)->DistributionOperation.run(~env)->Some - } - - let toStringFn = ( - fnCall: DistributionTypes.DistributionOperation.toString, - dist: DistributionTypes.genericDist, - ~env: GenericDist.env, - ) => { - FromDist(#ToString(fnCall), dist)->DistributionOperation.run(~env)->Some - } - - let toBoolFn = ( - fnCall: DistributionTypes.DistributionOperation.toBool, - dist: DistributionTypes.genericDist, - ~env: GenericDist.env, - ) => { - FromDist(#ToBool(fnCall), dist)->DistributionOperation.run(~env)->Some - } - - let toDistFn = ( - fnCall: DistributionTypes.DistributionOperation.toDist, - dist, - ~env: GenericDist.env, - ) => { - FromDist(#ToDist(fnCall), dist)->DistributionOperation.run(~env)->Some - } - - let twoDiststoDistFn = (direction, arithmetic, dist1, dist2, ~env: GenericDist.env) => { - FromDist( - #ToDistCombination(direction, arithmeticMap(arithmetic), #Dist(dist2)), - dist1, - )->DistributionOperation.run(~env) - } - - let parseNumber = (args: internalExpressionValue): Belt.Result.t => - switch args { - | IEvNumber(x) => Ok(x) - | _ => Error("Not a number") - } - - let parseNumberArray = (ags: array): Belt.Result.t< - array, - string, - > => E.A.fmap(parseNumber, ags) |> E.A.R.firstErrorOrOpen - - let parseDist = (args: internalExpressionValue): Belt.Result.t< - DistributionTypes.genericDist, - string, - > => - switch args { - | IEvDistribution(x) => Ok(x) - | IEvNumber(x) => Ok(GenericDist.fromFloat(x)) - | _ => Error("Not a distribution") - } - - let parseDistributionArray = (ags: array): Belt.Result.t< - array, - string, - > => E.A.fmap(parseDist, ags) |> E.A.R.firstErrorOrOpen - - let mixtureWithGivenWeights = ( - distributions: array, - weights: array, - ~env: GenericDist.env, - ): DistributionOperation.outputType => - E.A.length(distributions) == E.A.length(weights) - ? Mixture(Belt.Array.zip(distributions, weights))->DistributionOperation.run(~env) - : GenDistError( - ArgumentError("Error, mixture call has different number of distributions and weights"), - ) - - let mixtureWithDefaultWeights = ( - distributions: array, - ~env: GenericDist.env, - ): DistributionOperation.outputType => { - let length = E.A.length(distributions) - let weights = Belt.Array.make(length, 1.0 /. Belt.Int.toFloat(length)) - mixtureWithGivenWeights(distributions, weights, ~env) - } - - let mixture = ( - args: array, - ~env: GenericDist.env, - ): DistributionOperation.outputType => { - let error = (err: string): DistributionOperation.outputType => - err->DistributionTypes.ArgumentError->GenDistError - switch args { - | [IEvArray(distributions)] => - switch parseDistributionArray(distributions) { - | Ok(distrs) => mixtureWithDefaultWeights(distrs, ~env) - | Error(err) => error(err) - } - | [IEvArray(distributions), IEvArray(weights)] => - switch (parseDistributionArray(distributions), parseNumberArray(weights)) { - | (Ok(distrs), Ok(wghts)) => mixtureWithGivenWeights(distrs, wghts, ~env) - | (Error(err), Ok(_)) => error(err) - | (Ok(_), Error(err)) => error(err) - | (Error(err1), Error(err2)) => error(`${err1}|${err2}`) - } - | _ => - switch E.A.last(args) { - | Some(IEvArray(b)) => { - let weights = parseNumberArray(b) - let distributions = parseDistributionArray( - Belt.Array.slice(args, ~offset=0, ~len=E.A.length(args) - 1), - ) - switch E.R.merge(distributions, weights) { - | Ok(d, w) => mixtureWithGivenWeights(d, w, ~env) - | Error(err) => error(err) - } - } - | Some(IEvNumber(_)) - | Some(IEvDistribution(_)) => - switch parseDistributionArray(args) { - | Ok(distributions) => mixtureWithDefaultWeights(distributions, ~env) - | Error(err) => error(err) - } - | _ => error("Last argument of mx must be array or distribution") - } - } - } -} - -module SymbolicConstructors = { - let threeFloat = name => - switch name { - | "triangular" => Ok(SymbolicDist.Triangular.make) - | _ => Error("Unreachable state") - } - - let symbolicResultToOutput = ( - symbolicResult: result, - ): option => - switch symbolicResult { - | Ok(r) => Some(Dist(Symbolic(r))) - | Error(r) => Some(GenDistError(OtherError(r))) - } -} - -let dispatchToGenericOutput = (call: IEV.functionCall, env: GenericDist.env): option< - DistributionOperation.outputType, -> => { - let (fnName, args) = call - switch (fnName, args) { - | ("triangular" as fnName, [IEvNumber(f1), IEvNumber(f2), IEvNumber(f3)]) => - SymbolicConstructors.threeFloat(fnName) - ->E.R.bind(r => r(f1, f2, f3)) - ->SymbolicConstructors.symbolicResultToOutput - | ("sample", [IEvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env) - | ("sampleN", [IEvDistribution(dist), IEvNumber(n)]) => - Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n)))) - | (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [IEvDistribution(dist)]) => { - let fn = switch op { - | "mean" => #Mean - | "stdev" => #Stdev - | "variance" => #Variance - | "min" => #Min - | "max" => #Max - | "mode" => #Mode - | _ => #Mean - } - Helpers.toFloatFn(fn, dist, ~env) - } - | ("integralSum", [IEvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env) - | ("toString", [IEvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env) - | ("sparkline", [IEvDistribution(dist)]) => - Helpers.toStringFn(ToSparkline(MagicNumbers.Environment.sparklineLength), dist, ~env) - | ("sparkline", [IEvDistribution(dist), IEvNumber(n)]) => - Helpers.toStringFn(ToSparkline(Belt.Float.toInt(n)), dist, ~env) - | ("exp", [IEvDistribution(a)]) => - // https://mathjs.org/docs/reference/functions/exp.html - Helpers.twoDiststoDistFn( - Algebraic(AsDefault), - "pow", - GenericDist.fromFloat(MagicNumbers.Math.e), - a, - ~env, - )->Some - | ("normalize", [IEvDistribution(dist)]) => Helpers.toDistFn(Normalize, dist, ~env) - | ("isNormalized", [IEvDistribution(dist)]) => Helpers.toBoolFn(IsNormalized, dist, ~env) - | ("toPointSet", [IEvDistribution(dist)]) => Helpers.toDistFn(ToPointSet, dist, ~env) - | ("scaleLog", [IEvDistribution(dist)]) => - Helpers.toDistFn(Scale(#Logarithm, MagicNumbers.Math.e), dist, ~env) - | ("scaleLog10", [IEvDistribution(dist)]) => Helpers.toDistFn(Scale(#Logarithm, 10.0), dist, ~env) - | ("scaleLog", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toDistFn(Scale(#Logarithm, float), dist, ~env) - | ("scaleLogWithThreshold", [IEvDistribution(dist), IEvNumber(base), IEvNumber(eps)]) => - Helpers.toDistFn(Scale(#LogarithmWithThreshold(eps), base), dist, ~env) - | ("scaleMultiply", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toDistFn(Scale(#Multiply, float), dist, ~env) - | ("scalePow", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toDistFn(Scale(#Power, float), dist, ~env) - | ("scaleExp", [IEvDistribution(dist)]) => - Helpers.toDistFn(Scale(#Power, MagicNumbers.Math.e), dist, ~env) - | ("cdf", [IEvDistribution(dist), IEvNumber(float)]) => Helpers.toFloatFn(#Cdf(float), dist, ~env) - | ("pdf", [IEvDistribution(dist), IEvNumber(float)]) => Helpers.toFloatFn(#Pdf(float), dist, ~env) - | ("inv", [IEvDistribution(dist), IEvNumber(float)]) => Helpers.toFloatFn(#Inv(float), dist, ~env) - | ("quantile", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toFloatFn(#Inv(float), dist, ~env) - | ("inspect", [IEvDistribution(dist)]) => Helpers.toDistFn(Inspect, dist, ~env) - | ("truncateLeft", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toDistFn(Truncate(Some(float), None), dist, ~env) - | ("truncateRight", [IEvDistribution(dist), IEvNumber(float)]) => - Helpers.toDistFn(Truncate(None, Some(float)), dist, ~env) - | ("truncate", [IEvDistribution(dist), IEvNumber(float1), IEvNumber(float2)]) => - Helpers.toDistFn(Truncate(Some(float1), Some(float2)), dist, ~env) - | ("mx" | "mixture", args) => Helpers.mixture(args, ~env)->Some - | ("log", [IEvDistribution(a)]) => - Helpers.twoDiststoDistFn( - Algebraic(AsDefault), - "log", - a, - GenericDist.fromFloat(MagicNumbers.Math.e), - ~env, - )->Some - | ("log10", [IEvDistribution(a)]) => - Helpers.twoDiststoDistFn( - Algebraic(AsDefault), - "log", - a, - GenericDist.fromFloat(10.0), - ~env, - )->Some - | ("unaryMinus", [IEvDistribution(a)]) => - Helpers.twoDiststoDistFn( - Algebraic(AsDefault), - "multiply", - a, - GenericDist.fromFloat(-1.0), - ~env, - )->Some - | (("add" | "multiply" | "subtract" | "divide" | "pow" | "log") as arithmetic, [_, _] as args) => - Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - Helpers.twoDiststoDistFn(Algebraic(AsDefault), arithmetic, fst, snd, ~env) - ) - | ( - ("dotAdd" - | "dotMultiply" - | "dotSubtract" - | "dotDivide" - | "dotPow") as arithmetic, - [_, _] as args, - ) => - Helpers.catchAndConvertTwoArgsToDists(args)->E.O2.fmap(((fst, snd)) => - Helpers.twoDiststoDistFn(Pointwise, arithmetic, fst, snd, ~env) - ) - | ("dotExp", [IEvDistribution(a)]) => - Helpers.twoDiststoDistFn( - Pointwise, - "dotPow", - GenericDist.fromFloat(MagicNumbers.Math.e), - a, - ~env, - )->Some - | _ => None - } -} - -let genericOutputToReducerValue = (o: DistributionOperation.outputType): result< - internalExpressionValue, - Reducer_ErrorValue.errorValue, -> => - switch o { - | Dist(d) => Ok(ReducerInterface_InternalExpressionValue.IEvDistribution(d)) - | Float(d) => Ok(IEvNumber(d)) - | String(d) => Ok(IEvString(d)) - | Bool(d) => Ok(IEvBool(d)) - | FloatArray(d) => - Ok(IEvArray(d |> E.A.fmap(r => ReducerInterface_InternalExpressionValue.IEvNumber(r)))) - | GenDistError(err) => Error(REDistributionError(err)) - } - -let dispatch = (call: IEV.functionCall, environment) => - dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi deleted file mode 100644 index 4cf8a17d..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi +++ /dev/null @@ -1,4 +0,0 @@ -let dispatch: ( - ReducerInterface_InternalExpressionValue.functionCall, - ReducerInterface_InternalExpressionValue.environment, -) => option> diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Number.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Number.res deleted file mode 100644 index f22df39b..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Number.res +++ /dev/null @@ -1,45 +0,0 @@ -module IEV = ReducerInterface_InternalExpressionValue -type internalExpressionValue = IEV.t - -module ScientificUnit = { - let nameToMultiplier = str => - switch str { - | "n" => Some(1E-9) - | "m" => Some(1E-3) - | "k" => Some(1E3) - | "M" => Some(1E6) - | "B" => Some(1E9) - | "G" => Some(1E9) - | "T" => Some(1E12) - | "P" => Some(1E15) - | _ => None - } - - let getMultiplier = (r: string) => { - let match = Js.String2.match_(r, %re(`/fromUnit_([_a-zA-Z]*)/`)) - switch match { - | Some([_, unit]) => nameToMultiplier(unit) - | _ => None - } - } -} - -let dispatch = (call: IEV.functionCall, _: GenericDist.env): option< - result, -> => { - switch call { - | ( - ("fromUnit_n" - | "fromUnit_m" - | "fromUnit_k" - | "fromUnit_M" - | "fromUnit_B" - | "fromUnit_G" - | "fromUnit_T" - | "fromUnit_P") as op, - [IEvNumber(f)], - ) => - op->ScientificUnit.getMultiplier->E.O2.fmap(multiplier => IEV.IEvNumber(f *. multiplier)->Ok) - | _ => None - } -} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res deleted file mode 100644 index a868eeb5..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_StdLib.res +++ /dev/null @@ -1,4 +0,0 @@ -module Bindings = Reducer_Bindings - -let internalStdLib: Bindings.t = - Bindings.emptyBindings->SquiggleLibrary_Math.makeBindings->SquiggleLibrary_Versions.makeBindings diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res deleted file mode 100644 index ac58532c..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_Value/ReducerInterface_Value_Continuation.res +++ /dev/null @@ -1,27 +0,0 @@ -module InternalExpressionValue = ReducerInterface_InternalExpressionValue - -type t = InternalExpressionValue.nameSpace - -let toValue = nameSpace => InternalExpressionValue.IEvBindings(nameSpace) -let toString = nameSpace => InternalExpressionValue.toString(toValue(nameSpace)) -let toStringResult = rNameSpace => - Belt.Result.map(rNameSpace, toValue(_))->InternalExpressionValue.toStringResult -let toStringOptionResult = orNameSpace => - Belt.Option.map( - orNameSpace, - Belt.Result.map(_, toValue(_)), - )->InternalExpressionValue.toStringOptionResult - -let inspect = (nameSpace, label: string) => Js.log(`${label}: ${toString(nameSpace)}`) - -let inspectOption = (oNameSpace, label: string) => - switch oNameSpace { - | Some(nameSpace) => inspect(nameSpace, label) - | None => Js.log(`${label}: None`) - } - -let minus = (NameSpace(thisContainer): t, NameSpace(thatContainer): t) => { - InternalExpressionValue.NameSpace( - Belt.Map.String.removeMany(thisContainer, Belt.Map.String.keysToArray(thatContainer)), - ) -} diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res index 948c14c3..1942a136 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res @@ -1,266 +1,249 @@ // TODO: Auto clean project based on topology module Bindings = Reducer_Bindings -module Continuation = ReducerInterface_Value_Continuation module ErrorValue = Reducer_ErrorValue -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T module ProjectItem = ReducerProject_ProjectItem module T = ReducerProject_T module Topology = ReducerProject_Topology type t = T.t -module Private = { - type internalProject = T.Private.t - type t = T.Private.t +let getItem = T.getItem +let getSourceIds = T.getSourceIds +let getDependents = Topology.getDependents +let getDependencies = Topology.getDependencies +let getRunOrder = Topology.getRunOrder +let getRunOrderFor = Topology.getRunOrderFor - let getSourceIds = T.Private.getSourceIds - let getItem = T.Private.getItem - let getDependents = Topology.getDependents - let getDependencies = Topology.getDependencies - let getRunOrder = Topology.getRunOrder - let getRunOrderFor = Topology.getRunOrderFor +let createProject = () => { + let project: t = { + items: Belt.MutableMap.String.make(), + stdLib: SquiggleLibrary_StdLib.stdLib, + environment: Reducer_Context.defaultEnvironment, + previousRunOrder: [], + } + project +} - let createProject = () => { - let project: t = { - "iAmProject": true, - "items": Belt.Map.String.empty, - "stdLib": ReducerInterface_StdLib.internalStdLib, - "environment": InternalExpressionValue.defaultEnvironment, - "previousRunOrder": [], - } - project +// will not be necessary when ProjectItem becomes mutable +let setItem = (project: t, sourceId: string, item: ProjectItem.t): unit => { + let _ = Belt.MutableMap.String.set(project.items, sourceId, item) +} + +let rec touchSource_ = (project: t, sourceId: string): unit => { + let item = project->getItem(sourceId) + let newItem = ProjectItem.touchSource(item) + project->setItem(sourceId, newItem) +} +and touchDependents = (project: t, sourceId: string): unit => { + let _ = getDependents(project, sourceId)->Belt.Array.forEach(_, touchSource_(project, _)) +} + +let touchSource = (project: t, sourceId: string): unit => { + touchSource_(project, sourceId) + touchDependents(project, sourceId) +} + +let handleNewTopology = (project: t): unit => { + let previousRunOrder = project.previousRunOrder + let currentRunOrder = Topology.getRunOrder(project) + let diff = Topology.runOrderDiff(currentRunOrder, previousRunOrder) + Belt.Array.forEach(diff, touchSource(project, _)) + project.previousRunOrder = currentRunOrder +} + +let getSource = (project: t, sourceId: string): option => + Belt.MutableMap.String.get(project.items, sourceId)->Belt.Option.map(ProjectItem.getSource) + +let setSource = (project: t, sourceId: string, value: string): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.setSource(value) + project->setItem(sourceId, newItem) + touchDependents(project, sourceId) +} + +let clean = (project: t, sourceId: string): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.clean + project->setItem(sourceId, newItem) +} + +let cleanAll = (project: t): unit => + project->getSourceIds->Belt.Array.forEach(sourceId => clean(project, sourceId)) + +let cleanResults = (project: t, sourceId: string): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.cleanResults + project->setItem(sourceId, newItem) +} + +let cleanAllResults = (project: t): unit => + project->getSourceIds->Belt.Array.forEach(sourceId => project->cleanResults(sourceId)) + +let getIncludes = (project: t, sourceId: string): ProjectItem.T.includesType => + project->getItem(sourceId)->ProjectItem.getIncludes + +let getPastChain = (project: t, sourceId: string): array => + project->getItem(sourceId)->ProjectItem.getPastChain + +let getIncludesAsVariables = (project: t, sourceId: string): ProjectItem.T.importAsVariablesType => + project->getItem(sourceId)->ProjectItem.getIncludesAsVariables + +let getDirectIncludes = (project: t, sourceId: string): array => + project->getItem(sourceId)->ProjectItem.getDirectIncludes + +let setContinues = (project: t, sourceId: string, continues: array): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.setContinues(continues) + project->setItem(sourceId, newItem) + handleNewTopology(project) +} +let getContinues = (project: t, sourceId: string): array => + project->getItem(sourceId)->ProjectItem.getContinues + +let removeContinues = (project: t, sourceId: string): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.removeContinues + project->setItem(sourceId, newItem) + handleNewTopology(project) +} + +let setContinuation = ( + project: t, + sourceId: string, + continuation: ProjectItem.T.continuationArgumentType, +): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.setContinuation(continuation) + project->setItem(sourceId, newItem) +} + +let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType => + project->getItem(sourceId)->ProjectItem.getResult + +let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType => + switch getResultOption(project, sourceId) { + | None => RENeedToRun->Error + | Some(result) => result } - let rec touchSource_ = (project: t, sourceId: string): unit => { - let item = project->getItem(sourceId) - let newItem = ProjectItem.touchSource(item) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } - and touchDependents = (project: t, sourceId: string): unit => { - let _ = getDependents(project, sourceId)->Belt.Array.forEach(_, touchSource_(project, _)) - } +let setResult = (project: t, sourceId: string, value: ProjectItem.T.resultArgumentType): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.setResult(value) + project->setItem(sourceId, newItem) +} - let touchSource = (project: t, sourceId: string): unit => { - touchSource_(project, sourceId) - touchDependents(project, sourceId) - } +let parseIncludes = (project: t, sourceId: string): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.parseIncludes + project->setItem(sourceId, newItem) + handleNewTopology(project) +} - let handleNewTopology = (project: t): unit => { - let previousRunOrder = project["previousRunOrder"] - let currentRunOrder = Topology.getRunOrder(project) - let diff = Topology.runOrderDiff(currentRunOrder, previousRunOrder) - Belt.Array.forEach(diff, touchSource(project, _)) - T.Private.setFieldPreviousRunOrder(project, currentRunOrder) - } +let rawParse = (project: t, sourceId): unit => { + let newItem = project->getItem(sourceId)->ProjectItem.rawParse + project->setItem(sourceId, newItem) +} - let getSource = (project: t, sourceId: string): option => - Belt.Map.String.get(project["items"], sourceId)->Belt.Option.map(ProjectItem.getSource) +let getStdLib = (project: t): Reducer_T.namespace => project.stdLib +let setStdLib = (project: t, value: Reducer_T.namespace): unit => { + project.stdLib = value +} - let setSource = (project: t, sourceId: string, value: string): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.setSource(value) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - touchDependents(project, sourceId) - } +let getEnvironment = (project: t): Reducer_T.environment => project.environment +let setEnvironment = (project: t, value: Reducer_T.environment): unit => { + project.environment = value +} - let clean = (project: t, sourceId: string): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.clean - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } +let getBindings = (project: t, sourceId: string): Reducer_T.namespace => { + project->getItem(sourceId)->ProjectItem.getContinuation +} - let cleanAll = (project: t): unit => - getSourceIds(project)->Belt.Array.forEach(sourceId => clean(project, sourceId)) +let getBindingsAsRecord = (project: t, sourceId: string): Reducer_T.value => { + project->getBindings(sourceId)->Reducer_Namespace.toRecord +} - let cleanResults = (project: t, sourceId: string): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.cleanResults - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } +let getContinuationsBefore = (project: t, sourceId: string): array => { + project->getPastChain(sourceId)->Belt.Array.map(project->getBindings) +} - let cleanAllResults = (project: t): unit => - getSourceIds(project)->Belt.Array.forEach(sourceId => cleanResults(project, sourceId)) +let linkDependencies = (project: t, sourceId: string): Reducer_T.namespace => { + let pastChain = project->getPastChain(sourceId) + let namespace = Reducer_Namespace.mergeMany( + Belt.Array.concatMany([ + [project->getStdLib], + pastChain->Belt.Array.map(project->getBindings), + pastChain->Belt.Array.map(id => + Reducer_Namespace.fromArray([ + ( + "__result__", + switch project->getResult(id) { + | Ok(result) => result + | Error(error) => error->Reducer_ErrorValue.ErrorException->raise + }, + ), + ]) + ), + ]), + ) - let getIncludes = (project: t, sourceId: string): ProjectItem.T.includesType => - project->getItem(sourceId)->ProjectItem.getIncludes - - // let getDirectIncludes = (project: t, sourceId: string): array => - // project->getItem(sourceId)->ProjectItem.getDirectIncludes - - let getPastChain = (project: t, sourceId: string): array => - project->getItem(sourceId)->ProjectItem.getPastChain - - let getIncludesAsVariables = ( - project: t, - sourceId: string, - ): ProjectItem.T.importAsVariablesType => - project->getItem(sourceId)->ProjectItem.getIncludesAsVariables - - let getDirectIncludes = (project: t, sourceId: string): array => - project->getItem(sourceId)->ProjectItem.getDirectIncludes - - let setContinues = (project: t, sourceId: string, continues: array): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.setContinues(continues) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - handleNewTopology(project) - } - let getContinues = (project: t, sourceId: string): array => - ProjectItem.getContinues(project->getItem(sourceId)) - - let removeContinues = (project: t, sourceId: string): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.removeContinues - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - handleNewTopology(project) - } - - let getContinuation = (project: t, sourceId: string): ProjectItem.T.continuationArgumentType => - project->getItem(sourceId)->ProjectItem.getContinuation - - let setContinuation = ( - project: t, - sourceId: string, - continuation: ProjectItem.T.continuationArgumentType, - ): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.setContinuation(continuation) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } - - let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType => - project->getItem(sourceId)->ProjectItem.getResult - - let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType => - switch getResultOption(project, sourceId) { - | None => RENeedToRun->Error - | Some(result) => result - } - - let setResult = (project: t, sourceId: string, value: ProjectItem.T.resultArgumentType): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.setResult(value) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } - - let parseIncludes = (project: t, sourceId: string): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.parseIncludes - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - handleNewTopology(project) - } - - let rawParse = (project: t, sourceId): unit => { - let newItem = project->getItem(sourceId)->ProjectItem.rawParse - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - } - - let getStdLib = (project: t): Reducer_Bindings.t => project["stdLib"] - let setStdLib = (project: t, value: Reducer_Bindings.t): unit => - T.Private.setFieldStdLib(project, value) - - let getEnvironment = (project: t): InternalExpressionValue.environment => project["environment"] - let setEnvironment = (project: t, value: InternalExpressionValue.environment): unit => - T.Private.setFieldEnvironment(project, value) - - let getBindings = (project: t, sourceId: string): ProjectItem.T.bindingsArgumentType => { - let those = project->getContinuation(sourceId) - let these = project->getStdLib - let ofUser = Continuation.minus(those, these) - ofUser - } - - let buildProjectAccessors = (project: t): ProjectAccessorsT.t => { - states: {continuation: Bindings.emptyBindings}, - stdLib: getStdLib(project), - environment: getEnvironment(project), - } - - let getContinuationsBefore = (project: t, sourceId: string): array< - ProjectItem.T.continuation, - > => { - let pastNameSpaces = project->getPastChain(sourceId)->Belt.Array.map(getBindings(project, _)) - let theLength = Belt.Array.length(pastNameSpaces) - if theLength == 0 { - // `getContinuationBefore ${sourceId}: stdLib`->Js.log - [project->getStdLib] - } else { - // `getContinuationBefore ${sourceId}: ${lastOne} = ${InternalExpressionValue.toStringBindings( - // project->getBindings(lastOne), - // )}`->Js.log - pastNameSpaces - } - } - - let linkDependencies = (project: t, sourceId: string): ProjectItem.T.continuation => { - module NameSpace = Reducer_Bindings - let continuationsBefore = project->getContinuationsBefore(sourceId) - let nameSpace = NameSpace.emptyNameSpace->NameSpace.chainTo(continuationsBefore) - let includesAsVariables = project->getIncludesAsVariables(sourceId) - Belt.Array.reduce(includesAsVariables, nameSpace, (currentNameSpace, (variable, includeFile)) => - Bindings.set( - currentNameSpace, - variable, - getBindings(project, includeFile)->InternalExpressionValue.IEvBindings, - ) + let includesAsVariables = project->getIncludesAsVariables(sourceId) + Belt.Array.reduce(includesAsVariables, namespace, (acc, (variable, includeFile)) => + acc->Reducer_Namespace.set( + variable, + project->getBindings(includeFile)->Reducer_Namespace.toRecord, ) - } + ) +} - let doLinkAndRun = (project: t, sourceId: string): unit => { - let accessors = buildProjectAccessors(project) - let states = accessors.states - let continuation = linkDependencies(project, sourceId) - let newItem = project->getItem(sourceId)->ProjectItem.run(continuation, accessors) - Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _) - setContinuation(project, sourceId, states.continuation) - } +let doLinkAndRun = (project: t, sourceId: string): unit => { + let context = Reducer_Context.createContext( + project->linkDependencies(sourceId), + project->getEnvironment, + ) + let newItem = project->getItem(sourceId)->ProjectItem.run(context) + // Js.log("after run " ++ newItem.continuation->Reducer_Bindings.toString) + project->setItem(sourceId, newItem) +} - type runState = ProjectItem.T.resultArgumentType +type runState = ProjectItem.T.resultArgumentType - let tryRunWithResult = ( - project: t, - sourceId: string, - rPrevResult: ProjectItem.T.resultArgumentType, - ): ProjectItem.T.resultArgumentType => { - switch getResultOption(project, sourceId) { - | Some(result) => result // already ran - | None => - switch rPrevResult { - | Error(error) => { - setResult(project, sourceId, Error(error)) - Error(error) - } - | Ok(_prevResult) => { - doLinkAndRun(project, sourceId) - getResultOption(project, sourceId)->Belt.Option.getWithDefault(rPrevResult) - } +let tryRunWithResult = ( + project: t, + sourceId: string, + rPrevResult: ProjectItem.T.resultArgumentType, +): ProjectItem.T.resultArgumentType => { + switch project->getResultOption(sourceId) { + | Some(result) => result // already ran + | None => + switch rPrevResult { + | Error(error) => { + project->setResult(sourceId, Error(error)) + Error(error) + } + | Ok(_prevResult) => { + project->doLinkAndRun(sourceId) + project->getResultOption(sourceId)->Belt.Option.getWithDefault(rPrevResult) } } } - - let runAll = (project: t): unit => { - let runOrder = Topology.getRunOrder(project) - let initialState = Ok(InternalExpressionValue.IEvVoid) - let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => - tryRunWithResult(project, currId, currState) - ) - } - - let run = (project: t, sourceId: string): unit => { - let runOrder = Topology.getRunOrderFor(project, sourceId) - let initialState = Ok(InternalExpressionValue.IEvVoid) - let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => - tryRunWithResult(project, currId, currState) - ) - } - - let evaluate = (sourceCode: string) => { - let project = createProject() - setSource(project, "main", sourceCode) - runAll(project) - let those = project->getContinuation("main") - let these = project->getStdLib - let ofUser = Continuation.minus(those, these) - - ( - getResultOption(project, "main")->Belt.Option.getWithDefault( - InternalExpressionValue.IEvVoid->Ok, - ), - ofUser, - ) - } +} + +let runAll = (project: t): unit => { + let runOrder = Topology.getRunOrder(project) + let initialState = Ok(Reducer_T.IEvVoid) + let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => + project->tryRunWithResult(currId, currState) + ) +} + +let run = (project: t, sourceId: string): unit => { + let runOrder = Topology.getRunOrderFor(project, sourceId) + let initialState = Ok(Reducer_T.IEvVoid) + let _finalState = Belt.Array.reduce(runOrder, initialState, (currState, currId) => + project->tryRunWithResult(currId, currState) + ) +} + +let evaluate = (sourceCode: string) => { + let project = createProject() + project->setSource("main", sourceCode) + project->runAll + + ( + project->getResultOption("main")->Belt.Option.getWithDefault(Reducer_T.IEvVoid->Ok), + project->getBindings("main")->Reducer_Namespace.toMap, + ) } diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res deleted file mode 100644 index 9cc06e8f..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectAccessors_T.res +++ /dev/null @@ -1,40 +0,0 @@ -module ProjectItemT = ReducerProject_ProjectItem_T -module Bindings = Reducer_Bindings -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue - -type states = {mutable continuation: ProjectItemT.continuationArgumentType} - -type projectAccessors = { - stdLib: Reducer_Bindings.t, - environment: ExpressionT.environment, - states: states, -} - -type t = projectAccessors - -let identityAccessors: t = { - // We need the states at the end of the runtime. - // Accessors can be modified but states will stay as the same pointer - states: { - continuation: Bindings.emptyBindings, - }, - stdLib: ReducerInterface_StdLib.internalStdLib, - environment: InternalExpressionValue.defaultEnvironment, -} - -let identityAccessorsWithEnvironment = (environment): t => { - states: { - continuation: Bindings.emptyBindings, - }, - stdLib: ReducerInterface_StdLib.internalStdLib, - environment: environment, -} - -// to support change of environment in runtime -let setEnvironment = (this: t, environment: ExpressionT.environment): t => { - { - ...this, - environment: environment, - } -} diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res index 7676ddef..93d434a3 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res @@ -1,69 +1,60 @@ -// TODO: Use actual types instead of aliases in public functions // TODO: Use topological sorting to prevent unnecessary runs -module Bindings = Reducer_Bindings -module Continuation = ReducerInterface_Value_Continuation -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T -module ReducerFnT = ReducerProject_ReducerFn_T module T = ReducerProject_ProjectItem_T type projectItem = T.projectItem type t = T.t -let emptyItem = T.ProjectItem({ +let emptyItem: projectItem = { source: "", rawParse: None, expression: None, - continuation: Bindings.emptyBindings, + continuation: Reducer_Namespace.make(), result: None, continues: [], includes: []->Ok, directIncludes: [], includeAsVariables: [], -}) +} // source -> rawParse -> includes -> expression -> continuation -> result -let getSource = (T.ProjectItem(r)): T.sourceType => r.source -let getRawParse = (T.ProjectItem(r)): T.rawParseType => r.rawParse -let getExpression = (T.ProjectItem(r)): T.expressionType => r.expression -let getContinuation = (T.ProjectItem(r)): T.continuationArgumentType => r.continuation -let getResult = (T.ProjectItem(r)): T.resultType => r.result +let getSource = (r: t): T.sourceType => r.source +let getRawParse = (r: t): T.rawParseType => r.rawParse +let getExpression = (r: t): T.expressionType => r.expression +let getContinuation = (r: t): T.continuationArgumentType => r.continuation +let getResult = (r: t): T.resultType => r.result -let getContinues = (T.ProjectItem(r)): T.continuesType => r.continues -let getIncludes = (T.ProjectItem(r)): T.includesType => r.includes -let getDirectIncludes = (T.ProjectItem(r)): array => r.directIncludes -let getIncludesAsVariables = (T.ProjectItem(r)): T.importAsVariablesType => r.includeAsVariables +let getContinues = (r: t): T.continuesType => r.continues +let getIncludes = (r: t): T.includesType => r.includes +let getDirectIncludes = (r: t): array => r.directIncludes +let getIncludesAsVariables = (r: t): T.importAsVariablesType => r.includeAsVariables let touchSource = (this: t): t => { - let T.ProjectItem(r) = emptyItem - T.ProjectItem({ + let r = emptyItem + { ...r, source: getSource(this), continues: getContinues(this), includes: getIncludes(this), includeAsVariables: getIncludesAsVariables(this), directIncludes: getDirectIncludes(this), - }) + } } let touchRawParse = (this: t): t => { - let T.ProjectItem(r) = emptyItem - T.ProjectItem({ - ...r, + { + ...emptyItem, source: getSource(this), continues: getContinues(this), includes: getIncludes(this), includeAsVariables: getIncludesAsVariables(this), directIncludes: getDirectIncludes(this), rawParse: getRawParse(this), - }) + } } let touchExpression = (this: t): t => { - let T.ProjectItem(r) = emptyItem - T.ProjectItem({ - ...r, + { + ...this, source: getSource(this), continues: getContinues(this), includes: getIncludes(this), @@ -71,46 +62,42 @@ let touchExpression = (this: t): t => { directIncludes: getDirectIncludes(this), rawParse: getRawParse(this), expression: getExpression(this), - }) + } } -let resetIncludes = (T.ProjectItem(r): t): t => { - T.ProjectItem({ - ...r, - includes: []->Ok, - includeAsVariables: [], - directIncludes: [], - }) +let resetIncludes = (r: t): t => { + ...r, + includes: []->Ok, + includeAsVariables: [], + directIncludes: [], } -let setSource = (T.ProjectItem(r): t, source: T.sourceArgumentType): t => - T.ProjectItem({...r, source: source})->resetIncludes->touchSource +let setSource = (r: t, source: T.sourceArgumentType): t => + {...r, source: source}->resetIncludes->touchSource -let setRawParse = (T.ProjectItem(r): t, rawParse: T.rawParseArgumentType): t => - T.ProjectItem({...r, rawParse: Some(rawParse)})->touchRawParse +let setRawParse = (r: t, rawParse: T.rawParseArgumentType): t => + {...r, rawParse: Some(rawParse)}->touchRawParse -let setExpression = (T.ProjectItem(r): t, expression: T.expressionArgumentType): t => - T.ProjectItem({...r, expression: Some(expression)})->touchExpression +let setExpression = (r: t, expression: T.expressionArgumentType): t => + {...r, expression: Some(expression)}->touchExpression -let setContinuation = (T.ProjectItem(r): t, continuation: T.continuationArgumentType): t => { - T.ProjectItem({...r, continuation: continuation}) +let setContinuation = (r: t, continuation: T.continuationArgumentType): t => { + ...r, + continuation: continuation, } -let setResult = (T.ProjectItem(r): t, result: T.resultArgumentType): t => T.ProjectItem({ +let setResult = (r: t, result: T.resultArgumentType): t => { ...r, result: Some(result), -}) +} let cleanResults = touchExpression let clean = (this: t): t => { - let T.ProjectItem(r) = emptyItem - T.ProjectItem({ - ...r, - source: getSource(this), - continuation: getContinuation(this), - result: getResult(this), - }) + ...this, + source: getSource(this), + continuation: getContinuation(this), + result: getResult(this), } let getImmediateDependencies = (this: t): T.includesType => @@ -120,27 +107,27 @@ let getPastChain = (this: t): array => { Js.Array2.concat(getDirectIncludes(this), getContinues(this)) } -let setContinues = (T.ProjectItem(r): t, continues: array): t => - T.ProjectItem({...r, continues: continues})->touchSource -let removeContinues = (T.ProjectItem(r): t): t => T.ProjectItem({...r, continues: []})->touchSource +let setContinues = (this: t, continues: array): t => + {...this, continues: continues}->touchSource -let setIncludes = (T.ProjectItem(r): t, includes: T.includesType): t => T.ProjectItem({ - ...r, +let removeContinues = (this: t): t => {...this, continues: []}->touchSource + +let setIncludes = (this: t, includes: T.includesType): t => { + ...this, includes: includes, -}) +} -let setImportAsVariables = ( - T.ProjectItem(r): t, - includeAsVariables: T.importAsVariablesType, -): t => T.ProjectItem({...r, includeAsVariables: includeAsVariables}) +let setImportAsVariables = (this: t, includeAsVariables: T.importAsVariablesType): t => { + ...this, + includeAsVariables: includeAsVariables, +} -let setDirectImports = (T.ProjectItem(r): t, directIncludes: array): t => T.ProjectItem({ - ...r, +let setDirectImports = (this: t, directIncludes: array): t => { + ...this, directIncludes: directIncludes, -}) +} let parseIncludes = (this: t): t => { - let T.ProjectItem(r) = this let rRawImportAsVariables = getSource(this)->ReducerProject_ParseIncludes.parseIncludes switch rRawImportAsVariables { | Error(e) => resetIncludes(this)->setIncludes(Error(e)) @@ -152,12 +139,12 @@ let parseIncludes = (this: t): t => { rawImportAsVariables ->Belt.Array.keep(((variable, _file)) => variable == "") ->Belt.Array.map(((_variable, file)) => file) - T.ProjectItem({ - ...r, + { + ...this, includes: includes, includeAsVariables: includeAsVariables, directIncludes: directIncludes, - }) + } } } } @@ -172,44 +159,40 @@ let doBuildExpression = (this: t): T.expressionType => ->Belt.Option.map(o => o->Belt.Result.map(r => r->Reducer_Peggy_ToExpression.fromNode)) let buildExpression = (this: t): t => { - let withRawParse: t = this->rawParse - switch withRawParse->getExpression { - | Some(_) => withRawParse + let this = this->rawParse + switch this->getExpression { + | Some(_) => this // cached | None => - withRawParse - ->doBuildExpression - ->Belt.Option.map(setExpression(withRawParse, _)) - ->E.O2.defaultFn(() => withRawParse) + this->doBuildExpression->Belt.Option.map(setExpression(this, _))->E.O2.defaultFn(() => this) } } -let doBuildResult = ( - this: t, - aContinuation: T.continuation, - accessors: ProjectAccessorsT.t, -): T.resultType => - this - ->getExpression - ->Belt.Option.map( - Belt.Result.flatMap(_, expression => +let failRun = (this: t, e: Reducer_ErrorValue.errorValue): t => + this->setResult(e->Error)->setContinuation(Reducer_Namespace.make()) + +let doRun = (this: t, context: Reducer_T.context): t => + switch this->getExpression { + | Some(expressionResult) => + switch expressionResult { + | Ok(expression) => try { - Reducer_Expression.reduceExpressionInProject(expression, aContinuation, accessors)->Ok + let (result, contextAfterEvaluation) = Reducer_Expression.evaluate(expression, context) + this + ->setResult(result->Ok) + ->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals) } catch { - | exn => Reducer_ErrorValue.fromException(exn)->Error + | Reducer_ErrorValue.ErrorException(e) => this->failRun(e) + | _ => this->failRun(RETodo("unhandled rescript exception")) } - ), - ) + | Error(e) => this->failRun(e) + } + | None => this->failRun(RETodo("attempt to run without expression")) + } -let buildResult = (this: t, aContinuation: T.continuation, accessors: ProjectAccessorsT.t): t => { - let withExpression: t = this->buildExpression - switch withExpression->getResult { - | Some(_) => withExpression - | None => - withExpression - ->doBuildResult(aContinuation, accessors) - ->Belt.Option.map(setResult(withExpression, _)) - ->E.O2.defaultFn(() => withExpression) +let run = (this: t, context: Reducer_T.context): t => { + let this = this->buildExpression + switch this->getResult { + | Some(_) => this + | None => this->doRun(context) } } - -let run = buildResult diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res index caa80da4..bf1cbfcb 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res @@ -1,6 +1,5 @@ module Parse = Reducer_Peggy_Parse module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue open Reducer_ErrorValue type sourceArgumentType = string @@ -9,13 +8,10 @@ type rawParseArgumentType = result type rawParseType = option type expressionArgumentType = result type expressionType = option -type continuation = InternalExpressionValue.nameSpace -type continuationArgumentType = InternalExpressionValue.nameSpace +type continuationArgumentType = Reducer_T.namespace type continuationType = option type continuationResultType = option> -type bindingsArgumentType = InternalExpressionValue.nameSpace -type bindingsType = option -type resultArgumentType = result +type resultArgumentType = result type resultType = option type continuesArgumentType = array type continuesType = array @@ -23,17 +19,16 @@ type includesArgumentType = string type includesType = result, errorValue> type importAsVariablesType = array<(string, string)> -type projectItem = - | ProjectItem({ - source: sourceType, - rawParse: rawParseType, - expression: expressionType, - continuation: continuationArgumentType, - result: resultType, - continues: continuesType, - includes: includesType, //For loader - includeAsVariables: importAsVariablesType, //For linker - directIncludes: array, - }) //For linker +type projectItem = { + source: sourceType, + rawParse: rawParseType, + expression: expressionType, + continuation: continuationArgumentType, + result: resultType, + continues: continuesType, + includes: includesType, //For loader + includeAsVariables: importAsVariablesType, //For linker + directIncludes: array, +} type t = projectItem diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res deleted file mode 100644 index 2dc926be..00000000 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ReducerFn_T.res +++ /dev/null @@ -1,5 +0,0 @@ -module ExpressionT = Reducer_Expression_T -module InternalExpressionValue = ReducerInterface_InternalExpressionValue -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T - -type t = (ExpressionT.t, ExpressionT.bindings, ProjectAccessorsT.t) => InternalExpressionValue.t diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res index e1ec91f4..0eac623a 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_T.res @@ -1,37 +1,16 @@ module ProjectItem = ReducerProject_ProjectItem -module ExpressionT = Reducer_Expression_T -module ProjectAccessorsT = ReducerProject_ProjectAccessors_T @genType.opaque -type project = {"iAmProject": bool} -//re-export -@genType +type project = { + items: Belt.MutableMap.String.t, + mutable stdLib: Reducer_Namespace.t, + mutable environment: Reducer_T.environment, + mutable previousRunOrder: array, +} type t = project -module Private = { - type internalProject = { - "iAmProject": bool, - "items": Belt.Map.String.t, - "stdLib": Reducer_Bindings.t, - "environment": ExpressionT.environment, - "previousRunOrder": array, - } - type t = internalProject +// these functions are used in ReducerProject_Topology, so they are defined here to avoid circular dependencies +let getSourceIds = (project: t): array => Belt.MutableMap.String.keysToArray(project.items) - @set - external setFieldItems: (t, Belt.Map.String.t) => unit = "items" - @set - external setFieldStdLib: (t, Reducer_Bindings.t) => unit = "stdLib" - @set - external setFieldEnvironment: (t, ExpressionT.environment) => unit = "environment" - @set - external setFieldPreviousRunOrder: (t, array) => unit = "previousRunOrder" - - external castFromInternalProject: t => project = "%identity" - external castToInternalProject: project => t = "%identity" - - let getSourceIds = (this: t): array => Belt.Map.String.keysToArray(this["items"]) - - let getItem = (this: t, sourceId: string) => - Belt.Map.String.getWithDefault(this["items"], sourceId, ProjectItem.emptyItem) -} +let getItem = (project: t, sourceId: string) => + Belt.MutableMap.String.getWithDefault(project.items, sourceId, ProjectItem.emptyItem) diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_Topology.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_Topology.res index 20491678..682dd0d6 100644 --- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_Topology.res +++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_Topology.res @@ -1,12 +1,12 @@ module ProjectItem = ReducerProject_ProjectItem module T = ReducerProject_T -type t = T.Private.t +type t = T.t -let getSourceIds = T.Private.getSourceIds -let getItem = T.Private.getItem +let getSourceIds = T.getSourceIds +let getItem = T.getItem let getImmediateDependencies = (this: t, sourceId: string): ProjectItem.T.includesType => - getItem(this, sourceId)->ProjectItem.getImmediateDependencies + this->getItem(sourceId)->ProjectItem.getImmediateDependencies type topologicalSortState = (Belt.Map.String.t, list) let rec topologicalSortUtil = ( @@ -31,16 +31,16 @@ let rec topologicalSortUtil = ( } let getTopologicalSort = (this: t): array => { - let (_visited, stack) = getSourceIds(this)->Belt.Array.reduce((Belt.Map.String.empty, list{}), ( - (currVisited, currStack), - currId, - ) => - if !Belt.Map.String.getWithDefault(currVisited, currId, false) { - topologicalSortUtil(this, currId, (currVisited, currStack)) - } else { - (currVisited, currStack) - } - ) + let (_visited, stack) = + this + ->getSourceIds + ->Belt.Array.reduce((Belt.Map.String.empty, list{}), ((currVisited, currStack), currId) => + if !Belt.Map.String.getWithDefault(currVisited, currId, false) { + topologicalSortUtil(this, currId, (currVisited, currStack)) + } else { + (currVisited, currStack) + } + ) Belt.List.reverse(stack)->Belt.List.toArray } diff --git a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res index 14668a52..631b05fb 100644 --- a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res +++ b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Math.res @@ -1,5 +1,3 @@ -module Bindings = Reducer_Bindings - let availableNumbers: array<(string, float)> = [ ("Math.pi", Js.Math._PI), ("Math.e", Js.Math._E), @@ -13,10 +11,7 @@ let availableNumbers: array<(string, float)> = [ ("Math.tau", 6.283185307179586), ] -let mathBindings: Bindings.t = +let make = (): Reducer_Namespace.t => availableNumbers - ->E.A2.fmap(((name, v)) => (name, ReducerInterface_InternalExpressionValue.IEvNumber(v))) - ->Bindings.fromArray - -let makeBindings = (previousBindings: Bindings.t): Bindings.t => - previousBindings->Bindings.mergeFrom(mathBindings) + ->E.A2.fmap(((name, v)) => (name, Reducer_T.IEvNumber(v))) + ->Reducer_Namespace.fromArray diff --git a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_StdLib.res b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_StdLib.res new file mode 100644 index 00000000..3be93851 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_StdLib.res @@ -0,0 +1,59 @@ +exception ErrorException = Reducer_ErrorValue.ErrorException + +let stdLib: Reducer_T.namespace = { + // constants + let res = + Reducer_Namespace.make() + ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Math.make()) + ->Reducer_Namespace.mergeFrom(SquiggleLibrary_Versions.make()) + + // array and record lookups + let res = res->Reducer_Namespace.set( + "$_atIndex_$", + Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => { + switch inputs { + | [IEvArray(aValueArray), IEvNumber(fIndex)] => { + let index = Belt.Int.fromFloat(fIndex) // TODO - fail on non-integer indices? + + switch Belt.Array.get(aValueArray, index) { + | Some(value) => value + | None => REArrayIndexNotFound("Array index not found", index)->ErrorException->raise + } + } + | [IEvRecord(dict), IEvString(sIndex)] => + switch Belt.Map.String.get(dict, sIndex) { + | Some(value) => value + | None => + RERecordPropertyNotFound("Record property not found", sIndex)->ErrorException->raise + } + | _ => REOther("Trying to access key on wrong value")->ErrorException->raise + } + })->Reducer_T.IEvLambda, + ) + + // some lambdas can't be expressed in function registry (e.g. `mx` with its variadic number of parameters) + let res = FunctionRegistry_Library.nonRegistryLambdas->Belt.Array.reduce(res, ( + cur, + (name, lambda), + ) => { + cur->Reducer_Namespace.set(name, lambda->Reducer_T.IEvLambda) + }) + + // bind the entire FunctionRegistry + let res = + FunctionRegistry_Library.registry + ->FunctionRegistry_Core.Registry.allNames + ->Belt.Array.reduce(res, (cur, name) => { + cur->Reducer_Namespace.set( + name, + Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => { + switch FunctionRegistry_Library.call(name, arguments, environment, reducer) { + | Ok(value) => value + | Error(error) => error->Reducer_ErrorValue.ErrorException->raise + } + })->Reducer_T.IEvLambda, + ) + }) + + res +} diff --git a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Versions.res b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Versions.res index a54eab14..70cf7b1e 100644 --- a/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Versions.res +++ b/packages/squiggle-lang/src/rescript/SquiggleLibrary/SquiggleLibrary_Versions.res @@ -1,9 +1,2 @@ -module Bindings = Reducer_Bindings - -let bindings: Bindings.t = - [ - ("System.version", ReducerInterface_InternalExpressionValue.IEvString("0.4.0-dev")), - ]->Bindings.fromArray - -let makeBindings = (previousBindings: Bindings.t): Bindings.t => - previousBindings->Bindings.mergeFrom(bindings) +let make = (): Reducer_Namespace.t => + [("System.version", Reducer_T.IEvString("0.4.0-dev"))]->Reducer_Namespace.fromArray diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res deleted file mode 100644 index fa537347..00000000 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ /dev/null @@ -1,58 +0,0 @@ -open ForTS__Types -/* -This is meant as a file to contain @genType declarations as needed for Typescript. -I would ultimately want to have all @genType declarations here, vs. other files, but -@genType doesn't play as nicely with renaming Modules and functions as -would be preferable. - -The below few seem to work fine. In the future there's definitely more work to do here. -*/ - -// For backwards compatibility: -//Alternatives if one wants to keep the old habits -@genType type samplingParams = environment -@genType type squiggleValue_Dist = squiggleValue_Distribution //alternative -@genType type genericDist = squiggleValue_Distribution //alternative -@genType type sampleSetDist = sampleSetDistribution //alternative -@genType type symbolicDist = symbolicDistribution //alternative -@genType type resultDist = result //alternative -@genType type resultFloat = result //alternative -@genType type resultString = result //alternative - -@genType -let makeSampleSetDist: array => result< - sampleSetDist, - SampleSetDist.sampleSetError, -> = SampleSetDist.make - -//TODO: ForTS Interface module candid -@genType -let toPointSet: ( - squiggleValue_Distribution, - ~xyPointLength: int, - ~sampleCount: int, - ~xSelection: DistributionTypes.DistributionOperation.pointsetXSelection=?, - unit, -) => result = GenericDist.toPointSet - -@genType -type mixedShape = PointSetTypes.mixedShape - -@genType -type discreteShape = PointSetTypes.discreteShape - -@genType -type continuousShape = PointSetTypes.continuousShape - -@genType -let distributionErrorToString = ForTS_Distribution_Error.toString - -@genType -let defaultSamplingEnv = ForTS_Distribution.defaultEnvironment - -// Umur: opaque types -// @genType -// type declarationArg = Declaration.arg - -// @genType -// type declaration<'a> = Declaration.declaration<'a> diff --git a/packages/squiggle-lang/src/rescript/Utility/E/E_A.res b/packages/squiggle-lang/src/rescript/Utility/E/E_A.res index 5bef1d32..6ea01f45 100644 --- a/packages/squiggle-lang/src/rescript/Utility/E/E_A.res +++ b/packages/squiggle-lang/src/rescript/Utility/E/E_A.res @@ -229,9 +229,11 @@ module Floats = { let floatCompare: (float, float) => int = compare let sort = t => { - let r = t - r |> Array.fast_sort(floatCompare) - r + let typedArray = t->Js.TypedArray2.Float64Array.make + typedArray->Js.TypedArray2.Float64Array.sortInPlace->ignore + // why is there no standard function in Resctipt for this? + let typedArrayToArray: Js.TypedArray2.Float64Array.t => t = %raw(`a => Array.from(a)`) + typedArrayToArray(typedArray) } let getNonFinite = (t: t) => Belt.Array.getBy(t, r => !Js.Float.isFinite(r)) diff --git a/packages/squiggle-lang/src/rescript/Utility/Lodash.res b/packages/squiggle-lang/src/rescript/Utility/Lodash.res deleted file mode 100644 index d4359408..00000000 --- a/packages/squiggle-lang/src/rescript/Utility/Lodash.res +++ /dev/null @@ -1,5 +0,0 @@ -@module("lodash") external min: array<'a> => 'a = "min" -@module("lodash") external max: array<'a> => 'a = "max" -@module("lodash") external uniq: array<'a> => array<'a> = "uniq" -@module("lodash") -external countBy: (array<'a>, 'a => 'b) => Js.Dict.t = "countBy" diff --git a/packages/squiggle-lang/src/rescript/Utility/Mathjs.res b/packages/squiggle-lang/src/rescript/Utility/Mathjs.res deleted file mode 100644 index 5e1be176..00000000 --- a/packages/squiggle-lang/src/rescript/Utility/Mathjs.res +++ /dev/null @@ -1,9 +0,0 @@ -@module("./MathjsWrapper.js") -external parseMathExt: string => Js.Json.t = "parseMath" - -let parseMath = (str: string): result => - switch parseMathExt(str) { - | exception Js.Exn.Error(err) => Error(Js.Exn.message(err) |> E.O.default("MathJS Parse Error")) - | exception _ => Error("MathJS Parse Error") - | j => Ok(j) - } diff --git a/packages/squiggle-lang/src/rescript/Utility/MathjsWrapper.js b/packages/squiggle-lang/src/rescript/Utility/MathjsWrapper.js deleted file mode 100644 index 99bb3776..00000000 --- a/packages/squiggle-lang/src/rescript/Utility/MathjsWrapper.js +++ /dev/null @@ -1,9 +0,0 @@ -const math = require("mathjs"); - -function parseMath(f) { - return JSON.parse(JSON.stringify(math.parse(f))); -} - -module.exports = { - parseMath, -}; diff --git a/packages/squiggle-lang/src/rescript/shims/Js.shim.ts b/packages/squiggle-lang/src/rescript/shims/Js.shim.ts deleted file mode 100644 index 76f971a3..00000000 --- a/packages/squiggle-lang/src/rescript/shims/Js.shim.ts +++ /dev/null @@ -1 +0,0 @@ -export type Dict_t = { [key: string]: T }; diff --git a/packages/squiggle-lang/tsconfig.json b/packages/squiggle-lang/tsconfig.json index b9433729..92350eb1 100644 --- a/packages/squiggle-lang/tsconfig.json +++ b/packages/squiggle-lang/tsconfig.json @@ -12,7 +12,8 @@ "declarationDir": "./dist", "declaration": true, "composite": true, - "target": "ES6" + "target": "ES6", + "esModuleInterop": true }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.spec.ts", "webpack.config.js"] diff --git a/packages/vscode-ext/package.json b/packages/vscode-ext/package.json index 210b4a23..6c5a5a23 100644 --- a/packages/vscode-ext/package.json +++ b/packages/vscode-ext/package.json @@ -3,7 +3,7 @@ "displayName": "Squiggle", "description": "Squiggle language support", "license": "MIT", - "version": "0.4.2", + "version": "0.5.0-alpha.2", "publisher": "QURI", "repository": { "type": "git", diff --git a/packages/website/package.json b/packages/website/package.json index 2cfbb0c8..185d6204 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -15,7 +15,7 @@ "@docusaurus/core": "2.1.0", "@docusaurus/preset-classic": "2.1.0", "@heroicons/react": "^1.0.6", - "@quri/squiggle-components": "^0.4.1", + "@quri/squiggle-components": "^0.5.0-alpha.2", "base64-js": "^1.5.1", "clsx": "^1.2.1", "hast-util-is-element": "2.1.2", diff --git a/yarn.lock b/yarn.lock index 27829c68..d64e4032 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15557,6 +15557,18 @@ rescript-fast-check@^1.1.1: dependencies: fast-check "^2.17.0" +rescript-js-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/rescript-js-iterator/-/rescript-js-iterator-1.1.0.tgz#7fc1e6097cec5f3b847189e112deed0bca331f4d" + integrity sha512-9VSGfAUXdmOl0OoersJZknpGjCi4gsIuejcHQwIYKwoPPlfswFvrKjwOLTy4SoqM2037RxiwxsMPRlWgfDY22A== + +rescript-js-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/rescript-js-map/-/rescript-js-map-1.1.0.tgz#5e4e57d9147733bd2d14307d65f0f8febb5850fc" + integrity sha512-kJB43pIRv/Cd9OY17Fpof+xq6WTCqagdW+zrmBxFIj8SpK48rG4nF8jXSsYK3ksdwIJwh7J3sx3pwA27YkeM+A== + dependencies: + rescript-js-iterator "^1.1.0" + rescript@^9.1.4: version "9.1.4" resolved "https://registry.yarnpkg.com/rescript/-/rescript-9.1.4.tgz#1eb126f98d6c16942c0bf0df67c050198e580515"