diff --git a/packages/components/package.json b/packages/components/package.json index 4888a0a5..3ffc28af 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -9,6 +9,7 @@ "react": "^18.1.0", "react-ace": "^10.1.0", "react-dom": "^18.1.0", + "react-use": "^17.3.2", "react-vega": "^7.5.0", "styled-components": "^5.3.5", "vega": "^5.22.1", @@ -30,7 +31,7 @@ "@testing-library/user-event": "^14.1.1", "@types/jest": "^27.4.0", "@types/lodash": "^4.14.182", - "@types/node": "^17.0.29", + "@types/node": "^17.0.31", "@types/react": "^18.0.3", "@types/react-dom": "^18.0.2", "@types/styled-components": "^5.1.24", @@ -38,7 +39,7 @@ "cross-env": "^7.0.3", "react-scripts": "^5.0.1", "style-loader": "^3.3.1", - "ts-loader": "^9.2.9", + "ts-loader": "^9.3.0", "tsconfig-paths-webpack-plugin": "^3.5.2", "typescript": "^4.6.3", "web-vitals": "^2.1.4", diff --git a/packages/components/src/SquiggleChart.js.map b/packages/components/src/SquiggleChart.js.map deleted file mode 100644 index b344b505..00000000 --- a/packages/components/src/SquiggleChart.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SquiggleChart.js","sourceRoot":"","sources":["SquiggleChart.tsx"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/components/src/components/DistributionChart.tsx b/packages/components/src/components/DistributionChart.tsx index 6bef36c7..5eafecf9 100644 --- a/packages/components/src/components/DistributionChart.tsx +++ b/packages/components/src/components/DistributionChart.tsx @@ -1,45 +1,130 @@ import * as React from "react"; import _ from "lodash"; -import type { Spec } from "vega"; import type { Distribution } from "@quri/squiggle-lang"; import { distributionErrorToString } from "@quri/squiggle-lang"; -import { createClassFromSpec } from "react-vega"; +import { Vega, VisualizationSpec } from "react-vega"; import * as chartSpecification from "../vega-specs/spec-distributions.json"; import { ErrorBox } from "./ErrorBox"; +import { useSize } from "react-use"; +import { + linearXScale, + logXScale, + linearYScale, + expYScale, +} from "./DistributionVegaScales"; import styled from "styled-components"; -let SquiggleVegaChart = createClassFromSpec({ - spec: chartSpecification as Spec, -}); - type DistributionChartProps = { distribution: Distribution; - width: number; + width?: number; height: number; + /** Whether to show the user graph controls (scale etc) */ + showControls?: boolean; }; export const DistributionChart: React.FC = ({ distribution, - width, height, + width, + showControls = false, }: DistributionChartProps) => { + let [isLogX, setLogX] = React.useState(false); + let [isExpY, setExpY] = React.useState(false); let shape = distribution.pointSet(); - if (shape.tag === "Ok") { - let widthProp = width ? width - 20 : undefined; - var result = ( - - ); - } else { - var result = ( - - {distributionErrorToString(shape.value)} - - ); - } - return result; + const [sized, _] = useSize((size) => { + if (shape.tag === "Ok") { + let massBelow0 = + shape.value.continuous.some((x) => x.x <= 0) || + shape.value.discrete.some((x) => x.x <= 0); + let spec = buildVegaSpec(isLogX, isExpY); + let widthProp = width ? width - 20 : size.width - 10; + + // Check whether we should disable the checkbox + var logCheckbox = ( + + ); + if (massBelow0) { + logCheckbox = ( + + ); + } + + var result = ( +
+ + {showControls && ( +
+ {logCheckbox} + +
+ )} +
+ ); + } else { + var result = ( + + {distributionErrorToString(shape.value)} + + ); + } + + return result; + }); + return sized; +}; + +function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec { + return { + ...chartSpecification, + scales: [ + isLogX ? logXScale : linearXScale, + isExpY ? expYScale : linearYScale, + ], + } as VisualizationSpec; +} + +interface CheckBoxProps { + label: string; + onChange: (x: boolean) => void; + value: boolean; + disabled?: boolean; + tooltip?: string; +} + +const Label = styled.label<{ disabled: boolean }>` + ${(props) => props.disabled && "color: #999;"} +`; + +export const CheckBox = ({ + label, + onChange, + value, + disabled = false, + tooltip, +}: CheckBoxProps) => { + return ( + + onChange(!value)} + disabled={disabled} + /> + + + ); }; diff --git a/packages/components/src/components/DistributionVegaScales.ts b/packages/components/src/components/DistributionVegaScales.ts new file mode 100644 index 00000000..f0c5c8f6 --- /dev/null +++ b/packages/components/src/components/DistributionVegaScales.ts @@ -0,0 +1,80 @@ +import type { LogScale, LinearScale, PowScale } from "vega"; +export let linearXScale: LinearScale = { + name: "xscale", + type: "linear", + range: "width", + zero: false, + nice: false, + domain: { + fields: [ + { + data: "con", + field: "x", + }, + { + data: "dis", + field: "x", + }, + ], + }, +}; +export let linearYScale: LinearScale = { + name: "yscale", + type: "linear", + range: "height", + zero: true, + domain: { + fields: [ + { + data: "con", + field: "y", + }, + { + data: "dis", + field: "y", + }, + ], + }, +}; + +export let logXScale: LogScale = { + name: "xscale", + type: "log", + range: "width", + zero: false, + base: 10, + nice: false, + domain: { + fields: [ + { + data: "con", + field: "x", + }, + { + data: "dis", + field: "x", + }, + ], + }, +}; + +export let expYScale: PowScale = { + name: "yscale", + type: "pow", + exponent: 0.1, + range: "height", + zero: true, + nice: false, + domain: { + fields: [ + { + data: "con", + field: "y", + }, + { + data: "dis", + field: "y", + }, + ], + }, +}; diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 699a7e28..9e65c130 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -33,18 +33,29 @@ const variableBox = { `, }; -export const VariableBox: React.FC<{ +interface VariableBoxProps { heading: string; children: React.ReactNode; -}> = ({ heading = "Error", children }) => { - return ( - - -

{heading}

-
- {children} -
- ); + showTypes?: boolean; +} + +export const VariableBox: React.FC = ({ + heading = "Error", + children, + showTypes = false, +}: VariableBoxProps) => { + if (showTypes) { + return ( + + +

{heading}

+
+ {children} +
+ ); + } else { + return <>{children}; + } }; let RecordKeyHeader = styled.h3``; @@ -52,27 +63,36 @@ let RecordKeyHeader = styled.h3``; export interface SquiggleItemProps { /** The input string for squiggle */ expression: squiggleExpression; - width: number; + width?: number; height: number; + /** Whether to show type information */ + showTypes?: boolean; + /** Whether to show users graph controls (scale etc) */ + showControls?: boolean; } const SquiggleItem: React.FC = ({ expression, width, height, + showTypes = false, + showControls = false, }: SquiggleItemProps) => { switch (expression.tag) { case "number": return ( - + ); case "distribution": { let distType = expression.value.type(); return ( - - {distType === "Symbolic" ? ( + + {distType === "Symbolic" && showTypes ? ( <>
{expression.value.toString()}
@@ -83,47 +103,77 @@ const SquiggleItem: React.FC = ({ distribution={expression.value} height={height} width={width} + showControls={showControls} />
); } case "string": return ( - {`"${expression.value}"`} + {`"${expression.value}"`} ); case "boolean": return ( - + {expression.value.toString()} ); case "symbol": - return {expression.value}; + return ( + + {expression.value} + + ); case "call": - return {expression.value}; + return ( + + {expression.value} + + ); case "array": return ( - + {expression.value.map((r) => ( - + ))} ); case "record": return ( - + {Object.entries(expression.value).map(([key, r]) => ( <> {key} - + ))} ); - default: + case "arraystring": + return ( + + {expression.value.map((r) => `"${r}"`)} + + ); + case "lambda": return ( - {"We don't currently have a working viewer for record types."} + There is no viewer currently available for function types. ); } @@ -153,6 +203,10 @@ export interface SquiggleChartProps { bindings?: bindings; /** JS imported parameters */ jsImports?: jsImports; + /** Whether to show type information about returns, default false */ + showTypes?: boolean; + /** Whether to show graph controls (scale etc)*/ + showControls?: boolean; } const ChartWrapper = styled.div` @@ -169,7 +223,9 @@ export const SquiggleChart: React.FC = ({ height = 60, bindings = defaultBindings, jsImports = defaultImports, - width = NaN, + width, + showTypes = false, + showControls = false, }: SquiggleChartProps) => { let samplingInputs: samplingParams = { sampleCount: sampleCount, @@ -186,7 +242,13 @@ export const SquiggleChart: React.FC = ({ let expression = expressionResult.value; onChange(expression); internal = ( - + ); } else { internal = ( diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 23686a4f..572dbd76 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -40,6 +40,10 @@ export interface SquiggleEditorProps { bindings?: bindings; /** JS Imports */ jsImports?: jsImports; + /** Whether to show detail about types of the returns, default false */ + showTypes?: boolean; + /** Whether to give users access to graph controls */ + showControls: boolean; } const Input = styled.div` @@ -50,7 +54,7 @@ const Input = styled.div` export let SquiggleEditor: React.FC = ({ initialSquiggleString = "", - width = 500, + width, sampleCount, outputXYPoints, kernelWidth, @@ -61,6 +65,8 @@ export let SquiggleEditor: React.FC = ({ onChange, bindings = defaultBindings, jsImports = defaultImports, + showTypes = false, + showControls = false, }: SquiggleEditorProps) => { let [expression, setExpression] = React.useState(initialSquiggleString); return ( @@ -87,6 +93,8 @@ export let SquiggleEditor: React.FC = ({ onChange={onChange} bindings={bindings} jsImports={jsImports} + showTypes={showTypes} + showControls={showControls} /> ); @@ -145,6 +153,8 @@ export interface SquigglePartialProps { bindings?: bindings; /** Variables imported from js */ jsImports?: jsImports; + /** Whether to give users access to graph controls */ + showControls?: boolean; } export let SquigglePartial: React.FC = ({ @@ -160,15 +170,25 @@ export let SquigglePartial: React.FC = ({ xyPointLength: outputXYPoints, }; let [expression, setExpression] = React.useState(initialSquiggleString); - let squiggleResult = runPartial( - expression, - bindings, - samplingInputs, - jsImports - ); - if (squiggleResult.tag == "Ok") { - if (onChange) onChange(squiggleResult.value); - } + let [error, setError] = React.useState(null); + + let runSquiggleAndUpdateBindings = () => { + let squiggleResult = runPartial( + expression, + bindings, + samplingInputs, + jsImports + ); + if (squiggleResult.tag == "Ok") { + if (onChange) onChange(squiggleResult.value); + setError(null); + } else { + setError(errorValueToString(squiggleResult.value)); + } + }; + + React.useEffect(runSquiggleAndUpdateBindings, [expression]); + return (
@@ -180,13 +200,7 @@ export let SquigglePartial: React.FC = ({ height={20} /> - {squiggleResult.tag == "Error" ? ( - - {errorValueToString(squiggleResult.value)} - - ) : ( - <> - )} + {error !== null ? {error} : <>}
); }; diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index a8ad84d5..424cff8d 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -43,6 +43,8 @@ function FieldFloat(Props: FieldFloatProps) { interface Props { initialSquiggleString?: string; height?: number; + showTypes?: boolean; + showControls?: boolean; } interface Props2 { @@ -55,10 +57,6 @@ const ShowBox = styled.div` height: ${(props) => props.height}; `; -const MyComponent = styled.div` - color: ${(props) => props.theme.colors.main}; -`; - interface TitleProps { readonly maxHeight: number; } @@ -74,13 +72,15 @@ const Display = styled.div` const Row = styled.div` display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 50% 50%; `; const Col = styled.div``; let SquigglePlayground: FC = ({ initialSquiggleString = "", height = 300, + showTypes = false, + showControls = false, }: Props) => { let [squiggleString, setSquiggleString] = useState(initialSquiggleString); let [sampleCount, setSampleCount] = useState(1000); @@ -112,6 +112,8 @@ let SquigglePlayground: FC = ({ diagramCount={diagramCount} pointDistLength={pointDistLength} height={150} + showTypes={showTypes} + showControls={showControls} /> diff --git a/packages/components/src/stories/SquigglePartial.stories.mdx b/packages/components/src/stories/SquigglePartial.stories.mdx new file mode 100644 index 00000000..b4446402 --- /dev/null +++ b/packages/components/src/stories/SquigglePartial.stories.mdx @@ -0,0 +1,51 @@ +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/components/src/vega-specs/spec-distributions.json b/packages/components/src/vega-specs/spec-distributions.json index 5b6ed261..5ca9576f 100644 --- a/packages/components/src/vega-specs/spec-distributions.json +++ b/packages/components/src/vega-specs/spec-distributions.json @@ -3,7 +3,6 @@ "description": "A basic area chart example", "width": 500, "height": 100, - "autosize": "fit", "padding": 5, "data": [ { @@ -13,72 +12,8 @@ "name": "dis" } ], - "signals": [ - { - "name": "xscale", - "description": "The transform of the x scale", - "value": false, - "bind": { - "input": "checkbox", - "name": "log x scale" - } - }, - { - "name": "yscale", - "description": "The transform of the y scale", - "value": false, - "bind": { - "input": "checkbox", - "name": "log y scale" - } - } - ], - "scales": [ - { - "name": "xscale", - "type": "pow", - "exponent": { - "signal": "xscale ? 0.1 : 1" - }, - "range": "width", - "zero": false, - "nice": false, - "domain": { - "fields": [ - { - "data": "con", - "field": "x" - }, - { - "data": "dis", - "field": "x" - } - ] - } - }, - { - "name": "yscale", - "type": "pow", - "exponent": { - "signal": "yscale ? 0.1 : 1" - }, - "range": "height", - "nice": true, - "zero": true, - "domain": { - "fields": [ - { - "data": "con", - "field": "y" - }, - { - "data": "dis", - "field": "y" - } - ] - } - } - ], + "signals": [], + "scales": [], "axes": [ { "orient": "bottom", 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 new file mode 100644 index 00000000..7bbc43dd --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros_test.res @@ -0,0 +1,142 @@ +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))") + // 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", EvNumber(2.))]), exampleStatementX), + "Ok((:$setBindings {x: 2} :y 2))", + ) + // An expression does not return a binding, thus error + testMacro([], eBindStatement(eBindings([]), exampleExpression), "Error(Assignment expected)") + // When bindings from previous statement are missing the context is injected. This must be the first statement of a block + testMacro( + [("z", EvNumber(99.))], + eBindStatementDefault(exampleStatementY), + "Ok((:$setBindings {z: 99} :y 1))", + ) +}) + +describe("bindExpression", () => { + // x is simply bound in the expression + testMacro([], eBindExpression(eBindings([("x", EvNumber(2.))]), eSymbol("x")), "Ok(2)") + // When an let statement is the end expression then bindings are returned + testMacro( + [], + eBindExpression(eBindings([("x", EvNumber(2.))]), exampleStatementY), + "Ok((:$exportBindings (:$setBindings {x: 2} :y 1)))", + ) + // Now let's reduce that expression + testMacroEval( + [], + eBindExpression(eBindings([("x", EvNumber(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", EvNumber(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 (:$$block 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 (:$$block (:$$block :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 (:$$block (:$let :y (:add :x 1)) :y)))", + ) + MyOnly.testMacroEval( + [("x", EvNumber(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))") + // 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", EvNumber(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", EvNumber(666.))], callLambdaExpression, "Ok(667)") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Expression/Reducer_Expression_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Expression/Reducer_Expression_test.res new file mode 100644 index 00000000..f20d95f7 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Expression/Reducer_Expression_test.res @@ -0,0 +1,6 @@ +open Jest +open Expect + +test("dummy", () => { + expect(true)->toBe(true) +}) 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 index 355c69ea..6f232d0e 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsEval_test.res @@ -1,5 +1,5 @@ open ReducerInterface.ExpressionValue -module MathJs = Reducer.MathJs +module MathJs = Reducer_MathJs module ErrorValue = Reducer.ErrorValue open Jest diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res index 6282c14d..b085eeb3 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res @@ -1,4 +1,4 @@ -module Parse = Reducer.MathJs.Parse +module Parse = Reducer_MathJs.Parse module Result = Belt.Result open Jest @@ -18,8 +18,14 @@ module MySkip = { Skip.test(desc, () => expectParseToBe(expr, answer)) } +module MyOnly = { + let testParse = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer)) + let testDescriptionParse = (desc, expr, answer) => + Only.test(desc, () => expectParseToBe(expr, answer)) +} + describe("MathJs parse", () => { - describe("literals operators paranthesis", () => { + describe("literals operators parenthesis", () => { testParse("1", "1") testParse("'hello'", "'hello'") testParse("true", "true") @@ -40,15 +46,15 @@ describe("MathJs parse", () => { }) describe("functions", () => { - MySkip.testParse("identity(x) = x", "???") - MySkip.testParse("identity(x)", "???") + testParse("identity(x) = x", "identity = (x) => x") + testParse("identity(x)", "identity(x)") }) describe("arrays", () => { testDescriptionParse("empty", "[]", "[]") testDescriptionParse("define", "[0, 1, 2]", "[0, 1, 2]") testDescriptionParse("define with strings", "['hello', 'world']", "['hello', 'world']") - MySkip.testParse("range(0, 4)", "range(0, 4)") + testParse("range(0, 4)", "range(0, 4)") testDescriptionParse("index", "([0,1,2])[1]", "([0, 1, 2])[1]") }) @@ -58,11 +64,6 @@ describe("MathJs parse", () => { }) describe("comments", () => { - MySkip.testDescriptionParse("define", "# This is a comment", "???") - }) - - describe("if statement", () => { - // TODO Tertiary operator instead - MySkip.testDescriptionParse("define", "if (true) { 1 } else { 0 }", "???") + testDescriptionParse("define", "1 # This is a comment", "1") }) }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res index 581f6c8a..766acd43 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestHelpers.res @@ -1,40 +1,31 @@ -module Expression = Reducer.Expression +module ExpressionT = Reducer_Expression_T module ExpressionValue = ReducerInterface.ExpressionValue +module ErrorValue = Reducer_ErrorValue open Jest open Expect +let unwrapRecord = rValue => + rValue->Belt.Result.flatMap(value => + switch value { + | ExpressionValue.EvRecord(aRecord) => Ok(aRecord) + | _ => ErrorValue.RETodo("TODO: External bindings must be returned")->Error + } + ) + let expectParseToBe = (expr: string, answer: string) => - Reducer.parse(expr)->Expression.toStringResult->expect->toBe(answer) - -let expectParseOuterToBe = (expr: string, answer: string) => - Reducer.parseOuter(expr)->Expression.toStringResult->expect->toBe(answer) - -let expectParsePartialToBe = (expr: string, answer: string) => - Reducer.parsePartial(expr)->Expression.toStringResult->expect->toBe(answer) + Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer) let expectEvalToBe = (expr: string, answer: string) => Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer) let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) => - Reducer.evaluateUsingExternalBindings(expr, bindings) + Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None) ->ExpressionValue.toStringResult ->expect ->toBe(answer) -let expectEvalPartialBindingsToBe = ( - expr: string, - bindings: Reducer.externalBindings, - answer: string, -) => - Reducer.evaluatePartialUsingExternalBindings(expr, bindings) - ->ExpressionValue.toStringResultRecord - ->expect - ->toBe(answer) - let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)) -let testParseOuterToBe = (expr, answer) => test(expr, () => expectParseOuterToBe(expr, answer)) -let testParsePartialToBe = (expr, answer) => test(expr, () => expectParsePartialToBe(expr, answer)) let testDescriptionParseToBe = (desc, expr, answer) => test(desc, () => expectParseToBe(expr, answer)) @@ -42,34 +33,16 @@ let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answe let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer)) let testEvalBindingsToBe = (expr, bindingsList, answer) => test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) -let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => - test(expr, () => expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) module MySkip = { let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer)) - let testParseOuterToBe = (expr, answer) => - Skip.test(expr, () => expectParseOuterToBe(expr, answer)) - let testParsePartialToBe = (expr, answer) => - Skip.test(expr, () => expectParsePartialToBe(expr, answer)) let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer)) let testEvalBindingsToBe = (expr, bindingsList, answer) => Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) - let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => - Skip.test(expr, () => - expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer) - ) } module MyOnly = { let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer)) - let testParseOuterToBe = (expr, answer) => - Only.test(expr, () => expectParseOuterToBe(expr, answer)) - let testParsePartialToBe = (expr, answer) => - Only.test(expr, () => expectParsePartialToBe(expr, answer)) let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer)) let testEvalBindingsToBe = (expr, bindingsList, answer) => Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer)) - let testEvalPartialBindingsToBe = (expr, bindingsList, answer) => - Only.test(expr, () => - expectEvalPartialBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer) - ) } diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res new file mode 100644 index 00000000..330f7da0 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_TestMacroHelpers.res @@ -0,0 +1,82 @@ +open Jest +open Expect + +module Bindings = Reducer_Expression_Bindings +module Expression = Reducer_Expression +module ExpressionValue = ReducerInterface_ExpressionValue +module ExpressionWithContext = Reducer_ExpressionWithContext +module Macro = Reducer_Expression_Macro +module T = Reducer_Expression_T + +let testMacro_ = ( + tester, + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedCode: string, +) => { + let bindings = Belt.Map.String.fromArray(bindArray) + tester(expr->T.toString, () => + expr + ->Macro.expandMacroCall( + bindings, + ExpressionValue.defaultEnvironment, + Expression.reduceExpression, + ) + ->ExpressionWithContext.toStringResult + ->expect + ->toEqual(expectedCode) + ) +} + +let testMacroEval_ = ( + tester, + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedValue: string, +) => { + let bindings = Belt.Map.String.fromArray(bindArray) + tester(expr->T.toString, () => + expr + ->Macro.doMacroCall(bindings, ExpressionValue.defaultEnvironment, Expression.reduceExpression) + ->ExpressionValue.toStringResult + ->expect + ->toEqual(expectedValue) + ) +} + +let testMacro = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedExpr: string, +) => testMacro_(test, bindArray, expr, expectedExpr) +let testMacroEval = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedValue: string, +) => testMacroEval_(test, bindArray, expr, expectedValue) + +module MySkip = { + let testMacro = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedExpr: string, + ) => testMacro_(Skip.test, bindArray, expr, expectedExpr) + let testMacroEval = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedValue: string, + ) => testMacroEval_(Skip.test, bindArray, expr, expectedValue) +} + +module MyOnly = { + let testMacro = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedExpr: string, + ) => testMacro_(Only.test, bindArray, expr, expectedExpr) + let testMacroEval = ( + bindArray: array<(string, ExpressionValue.expressionValue)>, + expr: T.expression, + expectedValue: string, + ) => testMacroEval_(Only.test, bindArray, expr, expectedValue) +} diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res new file mode 100644 index 00000000..f005c1fc --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_debugging_test.res @@ -0,0 +1,16 @@ +open Jest +open Reducer_TestHelpers + +/* + You can wrap around any expression with inspect(expr) to log the value of that expression. + This is useful for debugging. inspect(expr) returns the value of expr, but also prints it out. + + There is a second version of inspect that takes a label, which will print out the label and the value. + + inspectPerformace(expr, label) will print out the value of expr, the label, and the time it took to evaluate expr. +*/ +describe("Debugging", () => { + testEvalToBe("inspect(1)", "Ok(1)") + testEvalToBe("inspect(1, \"one\")", "Ok(1)") + testEvalToBe("inspectPerformance(1, \"one\")", "Ok(1)") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res index ce834ed1..3a903343 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_externalBindings_test.res @@ -1,60 +1,63 @@ +// TODO: Reimplement with usual parse open Jest open Reducer_TestHelpers -describe("Parse for Bindings", () => { - testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))") - testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))") - testParseOuterToBe( - "y = x+1; y", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))", - ) -}) +// describe("Parse for Bindings", () => { +// testParseOuterToBe("x", "Ok((:$$bindExpression (:$$bindings) :x))") +// testParseOuterToBe("x+1", "Ok((:$$bindExpression (:$$bindings) (:add :x 1)))") +// testParseOuterToBe( +// "y = x+1; y", +// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) :y))", +// ) +// }) -describe("Parse Partial", () => { - testParsePartialToBe( - "x", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))", - ) - testParsePartialToBe( - "y=x", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))", - ) - testParsePartialToBe( - "y=x+1", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))", - ) - testParsePartialToBe( - "y = x+1; z = y", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))", - ) -}) +// describe("Parse Partial", () => { +// testParsePartialToBe( +// "x", +// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) :x) (:$exportVariablesExpression)))", +// ) +// testParsePartialToBe( +// "y=x", +// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y :x)) (:$exportVariablesExpression)))", +// ) +// testParsePartialToBe( +// "y=x+1", +// "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$exportVariablesExpression)))", +// ) +// testParsePartialToBe( +// "y = x+1; z = y", +// "Ok((:$$bindExpression (:$$bindStatement (:$$bindStatement (:$$bindings) (:$let :y (:add :x 1))) (:$let :z :y)) (:$exportVariablesExpression)))", +// ) +// }) describe("Eval with Bindings", () => { testEvalBindingsToBe("x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(1)") testEvalBindingsToBe("x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)") + testParseToBe("y = x+1; y", "Ok((:$$block (:$$block (:$let :y (:add :x 1)) :y)))") testEvalBindingsToBe("y = x+1; y", list{("x", ExpressionValue.EvNumber(1.))}, "Ok(2)") + testEvalBindingsToBe("y = x+1", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 2})") }) /* Partial code is a partial code fragment that is cut out from a larger code. Therefore it does not end with an expression. */ -describe("Eval Partial", () => { - testEvalPartialBindingsToBe( - // A partial cannot end with an expression - "x", - list{("x", ExpressionValue.EvNumber(1.))}, - "Error(Assignment expected)", - ) - testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1, y: 1})") - testEvalPartialBindingsToBe( - "y=x+1", - list{("x", ExpressionValue.EvNumber(1.))}, - "Ok({x: 1, y: 2})", - ) - testEvalPartialBindingsToBe( - "y = x+1; z = y", - list{("x", ExpressionValue.EvNumber(1.))}, - "Ok({x: 1, y: 2, z: 2})", - ) -}) +// describe("Eval Partial", () => { +// testEvalPartialBindingsToBe( +// // A partial cannot end with an expression +// "x", +// list{("x", ExpressionValue.EvNumber(1.))}, +// "Error(Assignment expected)", +// ) +// testEvalPartialBindingsToBe("y=x", list{("x", ExpressionValue.EvNumber(1.))}, "Ok({x: 1,y: 1})") +// testEvalPartialBindingsToBe( +// "y=x+1", +// list{("x", ExpressionValue.EvNumber(1.))}, +// "Ok({x: 1,y: 2})", +// ) +// testEvalPartialBindingsToBe( +// "y = x+1; z = y", +// list{("x", ExpressionValue.EvNumber(1.))}, +// "Ok({x: 1,y: 2,z: 2})", +// ) +// }) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res new file mode 100644 index 00000000..bb3e2220 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionAssignment_test.res @@ -0,0 +1,12 @@ +open Jest +open Reducer_TestHelpers + +describe("Parse function assignment", () => { + testParseToBe("f(x)=x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block :x)))))") + testParseToBe("f(x)=2*x", "Ok((:$$block (:$let :f (:$$lambda [x] (:$$block (:multiply 2 :x))))))") + //MathJs does not allow blocks in function definitions +}) + +describe("Evaluate function assignment", () => { + testEvalToBe("f(x)=x; f(1)", "Ok(1)") +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res new file mode 100644 index 00000000..284364e6 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -0,0 +1,77 @@ +open Jest +open Reducer_TestHelpers + +describe("Arity check", () => { + testEvalToBe("f(x,y) = x + y; f(1,2)", "Ok(3)") + testEvalToBe( + "f(x,y) = x + y; f(1)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y) = x + y; f(1,2,3)", + "Error(2 arguments expected. Instead 3 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x+y; f(1,2,3,4)", + "Error(2 arguments expected. Instead 4 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x+y; f(1)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe( + "f(x,y)=x(y); f(f)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) + testEvalToBe("f(x)=x; f(f)", "Ok(lambda(x=>internal code))") + testEvalToBe( + "f(x,y)=x(y); f(z)", + "Error(2 arguments expected. Instead 1 argument(s) were passed.)", + ) +}) + +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(1); f(2)", "Error(2 is not a function)") +}) + +describe("call and bindings", () => { + testEvalToBe("f(x)=x+1", "Ok({f: lambda(x=>internal code)})") + testEvalToBe("f(x)=x+1; f(1)", "Ok(2)") + testEvalToBe("f=1;y=2", "Ok({f: 1,y: 2})") + testEvalToBe("f(x)=x+1; y=f(1)", "Ok({f: lambda(x=>internal code),y: 2})") + testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)") + testEvalToBe("f(x)=x+1; y=f(1); z=f(1)", "Ok({f: lambda(x=>internal code),y: 2,z: 2})") + testEvalToBe( + "f(x)=x+1; g(x)=f(x)+1", + "Ok({f: lambda(x=>internal code),g: lambda(x=>internal code)})", + ) + testParseToBe( + "f=99; g(x)=f; g(2)", + "Ok((:$$block (:$$block (:$let :f 99) (:$let :g (:$$lambda [x] (:$$block :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)", + "Ok({f: lambda(x=>internal code),g: lambda(x=>internal code),y: 4})", + ) + testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)") +}) + +describe("function trics", () => { + testParseToBe( + "f(x)=f(y)=2; f(2)", + "Ok((:$$block (:$$block (:$let :f (:$$lambda [x] (:$$block (:$let :f (:$$lambda [y] (:$$block 2)))))) (:f 2))))", + ) + testEvalToBe("f(x)=f(y)=2; f(2)", "Ok({f: lambda(y=>internal code),x: 2})") + testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)") + testEvalToBe("y=2;g(x)=inspect(y)+1", "Ok({g: lambda(x=>internal code),y: 2})") + MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout? + MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters + MySkip.testEvalToBe("myadd(x,y)=x+y; z=[add]; z[0](3,2)", "????") //TODO: to fix with new parser + MySkip.testEvalToBe("myaddd(x,y)=x+y; z={x: add}; z.x(3,2)", "????") //TODO: to fix with new parser +}) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res index 5514b67c..80468c0f 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_test.res @@ -10,46 +10,39 @@ describe("reducer using mathjs parse", () => { // Those tests toString that we are converting mathjs parse tree to what we need describe("expressions", () => { - testParseToBe("1", "Ok(1)") - testParseToBe("(1)", "Ok(1)") - testParseToBe("1+2", "Ok((:add 1 2))") - testParseToBe("1+2", "Ok((:add 1 2))") - testParseToBe("1+2", "Ok((:add 1 2))") - testParseToBe("1+2*3", "Ok((:add 1 (:multiply 2 3)))") + testParseToBe("1", "Ok((:$$block 1))") + testParseToBe("(1)", "Ok((:$$block 1))") + testParseToBe("1+2", "Ok((:$$block (:add 1 2)))") + testParseToBe("1+2*3", "Ok((:$$block (:add 1 (:multiply 2 3))))") }) describe("arrays", () => { //Note. () is a empty list in Lisp // The only builtin structure in Lisp is list. There are no arrays // [1,2,3] becomes (1 2 3) - testDescriptionParseToBe("empty", "[]", "Ok(())") - testParseToBe("[1, 2, 3]", "Ok((1 2 3))") - testParseToBe("['hello', 'world']", "Ok(('hello' 'world'))") - testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$atIndex (0 1 2) (1)))") + testDescriptionParseToBe("empty", "[]", "Ok((:$$block ()))") + testParseToBe("[1, 2, 3]", "Ok((:$$block (1 2 3)))") + testParseToBe("['hello', 'world']", "Ok((:$$block ('hello' 'world')))") + testDescriptionParseToBe("index", "([0,1,2])[1]", "Ok((:$$block (:$atIndex (0 1 2) (1))))") }) describe("records", () => { - testDescriptionParseToBe("define", "{a: 1, b: 2}", "Ok((:$constructRecord (('a' 1) ('b' 2))))") + testDescriptionParseToBe( + "define", + "{a: 1, b: 2}", + "Ok((:$$block (:$constructRecord (('a' 1) ('b' 2)))))", + ) testDescriptionParseToBe( "use", "{a: 1, b: 2}.a", - "Ok((:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a')))", + "Ok((:$$block (:$atIndex (:$constructRecord (('a' 1) ('b' 2))) ('a'))))", ) }) describe("multi-line", () => { - testParseToBe("1; 2", "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) 1) 2))") - testParseToBe( - "1+1; 2+1", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:add 1 1)) (:add 2 1)))", - ) + testParseToBe("1; 2", "Ok((:$$block (:$$block 1 2)))") + testParseToBe("1+1; 2+1", "Ok((:$$block (:$$block (:add 1 1) (:add 2 1))))") }) describe("assignment", () => { - testParseToBe( - "x=1; x", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x 1)) :x))", - ) - testParseToBe( - "x=1+1; x+1", - "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x (:add 1 1))) (:add :x 1)))", - ) + testParseToBe("x=1; x", "Ok((:$$block (:$$block (:$let :x 1) :x)))") + testParseToBe("x=1+1; x+1", "Ok((:$$block (:$$block (:$let :x (:add 1 1)) (:add :x 1))))") }) }) @@ -70,13 +63,13 @@ describe("eval", () => { }) describe("arrays", () => { test("empty array", () => expectEvalToBe("[]", "Ok([])")) - testEvalToBe("[1, 2, 3]", "Ok([1, 2, 3])") - testEvalToBe("['hello', 'world']", "Ok(['hello', 'world'])") + testEvalToBe("[1, 2, 3]", "Ok([1,2,3])") + testEvalToBe("['hello', 'world']", "Ok(['hello','world'])") testEvalToBe("([0,1,2])[1]", "Ok(1)") testDescriptionEvalToBe("index not found", "([0,1,2])[10]", "Error(Array index not found: 10)") }) describe("records", () => { - test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1, b: 2})")) + test("define", () => expectEvalToBe("{a: 1, b: 2}", "Ok({a: 1,b: 2})")) test("index", () => expectEvalToBe("{a: 1}.a", "Ok(1)")) test("index not found", () => expectEvalToBe("{a: 1}.b", "Error(Record property not found: b)")) }) @@ -91,7 +84,7 @@ describe("eval", () => { testEvalToBe("x=1; y=x+1; y+1", "Ok(3)") testEvalToBe("1; x=1", "Error(Assignment expected)") testEvalToBe("1; 1", "Error(Assignment expected)") - testEvalToBe("x=1; x=1", "Error(Expression expected)") + testEvalToBe("x=1; x=1", "Ok({x: 1})") }) }) diff --git a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res index 4df843bb..bb330479 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_Distribution_test.res @@ -119,27 +119,34 @@ describe("eval on distribution functions", () => { describe("parse on distribution functions", () => { describe("power", () => { - 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))") + testParse("normal(5,2) ^ normal(5,1)", "Ok((:$$block (:pow (:normal 5 2) (:normal 5 1))))") + testParse("3 ^ normal(5,1)", "Ok((:$$block (:pow 3 (:normal 5 1))))") + testParse("normal(5,2) ^ 3", "Ok((:$$block (:pow (:normal 5 2) 3)))") }) describe("subtraction", () => { - testParse("10 - normal(5,1)", "Ok((:subtract 10 (:normal 5 1)))") - testParse("normal(5,1) - 10", "Ok((:subtract (:normal 5 1) 10))") + testParse("10 - normal(5,1)", "Ok((:$$block (:subtract 10 (:normal 5 1))))") + testParse("normal(5,1) - 10", "Ok((:$$block (: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((:dotSubtract (:normal 5 2) (:normal 5 1)))", + "Ok((:$$block (:dotSubtract (:normal 5 2) (:normal 5 1))))", + // TODO: !!! returns "Ok((:$$block (: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)))") + testParse( + "normal(5,2) .* normal(5,1)", + "Ok((:$$block (:dotMultiply (:normal 5 2) (:normal 5 1))))", + ) + testParse( + "normal(5,2) ./ normal(5,1)", + "Ok((:$$block (:dotDivide (:normal 5 2) (:normal 5 1))))", + ) + testParse("normal(5,2) .^ normal(5,1)", "Ok((:$$block (:dotPow (:normal 5 2) (:normal 5 1))))") }) describe("equality", () => { - testParse("5 == normal(5,2)", "Ok((:equal 5 (:normal 5 2)))") + testParse("5 == normal(5,2)", "Ok((:$$block (: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__/ReducerInterface/ReducerInterface_ExpressionValue_test.res b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res index fc2d555e..888aca7e 100644 --- a/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res +++ b/packages/squiggle-lang/__tests__/ReducerInterface/ReducerInterface_ExpressionValue_test.res @@ -3,9 +3,9 @@ open Jest open Expect describe("ExpressionValue", () => { - test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1, 'a'")) + test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1,'a'")) test("toStringFunctionCall", () => - expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1, 'a')") + expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')") ) }) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 40f7e7c7..98748e63 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -4,7 +4,9 @@ "homepage": "https://squiggle-language.com", "license": "MIT", "scripts": { - "build": "rescript build -with-deps && tsc", + "build": "yarn build:rescript && yarn build:typescript", + "build:rescript": "rescript build -with-deps", + "build:typescript": "tsc", "bundle": "webpack", "start": "rescript build -w -with-deps", "clean": "rescript clean && rm -r dist", @@ -55,7 +57,7 @@ "nyc": "^15.1.0", "reanalyze": "^2.19.0", "ts-jest": "^27.1.4", - "ts-loader": "^9.2.8", + "ts-loader": "^9.3.0", "ts-node": "^10.7.0", "typescript": "^4.6.3", "webpack": "^5.72.0", diff --git a/packages/squiggle-lang/src/js/distribution.ts b/packages/squiggle-lang/src/js/distribution.ts index 603dfaa9..44c15882 100644 --- a/packages/squiggle-lang/src/js/distribution.ts +++ b/packages/squiggle-lang/src/js/distribution.ts @@ -3,7 +3,7 @@ import { genericDist, continuousShape, discreteShape, - samplingParams, + environment, distributionError, toPointSet, distributionErrorToString, @@ -51,9 +51,9 @@ export type shape = { export class Distribution { t: genericDist; - env: samplingParams; + env: environment; - constructor(t: genericDist, env: samplingParams) { + constructor(t: genericDist, env: environment) { this.t = t; this.env = env; return this; diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index ce4d9428..589548e3 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -1,8 +1,10 @@ import * as _ from "lodash"; import { samplingParams, - evaluateUsingExternalBindings, + environment, + defaultEnvironment, evaluatePartialUsingExternalBindings, + evaluateUsingOptions, externalBindings, expressionValue, errorValue, @@ -11,6 +13,7 @@ export { makeSampleSetDist, errorValueToString, distributionErrorToString, + distributionError, } from "../rescript/TypescriptInterface.gen"; export type { samplingParams, @@ -26,9 +29,9 @@ import { convertRawToTypescript, } from "./rescript_interop"; import { result, resultMap, tag, tagged } from "./types"; -import { Distribution } from "./distribution"; +import { Distribution, shape } from "./distribution"; -export { Distribution, squiggleExpression, result, resultMap }; +export { Distribution, squiggleExpression, result, resultMap, shape }; export let defaultSamplingInputs: samplingParams = { sampleCount: 10000, @@ -38,33 +41,34 @@ export let defaultSamplingInputs: samplingParams = { export function run( squiggleString: string, bindings?: externalBindings, - samplingInputs?: samplingParams, + environment?: environment, imports?: jsImports ): result { let b = bindings ? bindings : defaultBindings; let i = imports ? imports : defaultImports; - let si: samplingParams = samplingInputs - ? samplingInputs - : defaultSamplingInputs; - - let result: result = - evaluateUsingExternalBindings(squiggleString, mergeImports(b, i)); - return resultMap(result, (x) => createTsExport(x, si)); + let e = environment ? environment : defaultEnvironment; + let res: result = evaluateUsingOptions( + { externalBindings: mergeImports(b, i), environment: e }, + squiggleString + ); + return resultMap(res, (x) => createTsExport(x, e)); } // Run Partial. A partial is a block of code that doesn't return a value export function runPartial( squiggleString: string, bindings?: externalBindings, - _samplingInputs?: samplingParams, + environment?: environment, imports?: jsImports ): result { let b = bindings ? bindings : defaultBindings; let i = imports ? imports : defaultImports; + let e = environment ? environment : defaultEnvironment; return evaluatePartialUsingExternalBindings( squiggleString, - mergeImports(b, i) + mergeImports(b, i), + e ); } @@ -88,7 +92,7 @@ export let defaultBindings: externalBindings = {}; function createTsExport( x: expressionValue, - sampEnv: samplingParams + environment: environment ): squiggleExpression { switch (x.tag) { case "EvArray": @@ -107,7 +111,10 @@ function createTsExport( return tag( "record", _.mapValues(arrayItem.value, (recordValue: unknown) => - convertRawToTypescript(recordValue as rescriptExport, sampEnv) + convertRawToTypescript( + recordValue as rescriptExport, + environment + ) ) ); case "EvArray": @@ -115,20 +122,24 @@ function createTsExport( return tag( "array", y.map((childArrayItem) => - convertRawToTypescript(childArrayItem, sampEnv) + convertRawToTypescript(childArrayItem, environment) ) ); default: - return createTsExport(arrayItem, sampEnv); + return createTsExport(arrayItem, environment); } }) ); + case "EvArrayString": + return tag("arraystring", x.value); case "EvBool": return tag("boolean", x.value); case "EvCall": return tag("call", x.value); + case "EvLambda": + return tag("lambda", x.value); case "EvDistribution": - return tag("distribution", new Distribution(x.value, sampEnv)); + return tag("distribution", new Distribution(x.value, environment)); case "EvNumber": return tag("number", x.value); case "EvRecord": @@ -136,7 +147,7 @@ function createTsExport( let result: tagged<"record", { [key: string]: squiggleExpression }> = tag( "record", _.mapValues(x.value, (x: unknown) => - convertRawToTypescript(x as rescriptExport, sampEnv) + convertRawToTypescript(x as rescriptExport, environment) ) ); return result; diff --git a/packages/squiggle-lang/src/js/rescript_interop.ts b/packages/squiggle-lang/src/js/rescript_interop.ts index b017699f..45f4124b 100644 --- a/packages/squiggle-lang/src/js/rescript_interop.ts +++ b/packages/squiggle-lang/src/js/rescript_interop.ts @@ -3,10 +3,11 @@ import { mixedShape, sampleSetDist, genericDist, - samplingParams, + environment, symbolicDist, discreteShape, continuousShape, + lambdaValue, } from "../rescript/TypescriptInterface.gen"; import { Distribution } from "./distribution"; import { tagged, tag } from "./types"; @@ -19,31 +20,39 @@ export type rescriptExport = _0: rescriptExport[]; } | { - TAG: 1; // EvBool + TAG: 1; // EvString + _0: string[]; + } + | { + TAG: 2; // EvBool _0: boolean; } | { - TAG: 2; // EvCall + TAG: 3; // EvCall _0: string; } | { - TAG: 3; // EvDistribution + TAG: 4; // EvDistribution _0: rescriptDist; } | { - TAG: 4; // EvNumber + TAG: 5; // EvLambda + _0: lambdaValue; + } + | { + TAG: 6; // EvNumber _0: number; } | { - TAG: 5; // EvRecord + TAG: 7; // EvRecord _0: { [key: string]: rescriptExport }; } | { - TAG: 6; // EvString + TAG: 8; // EvString _0: string; } | { - TAG: 7; // EvSymbol + TAG: 9; // EvSymbol _0: string; }; @@ -70,7 +79,9 @@ export type squiggleExpression = | tagged<"symbol", string> | tagged<"string", string> | tagged<"call", string> + | tagged<"lambda", lambdaValue> | tagged<"array", squiggleExpression[]> + | tagged<"arraystring", string[]> | tagged<"boolean", boolean> | tagged<"distribution", Distribution> | tagged<"number", number> @@ -78,36 +89,40 @@ export type squiggleExpression = export function convertRawToTypescript( result: rescriptExport, - sampEnv: samplingParams + environment: environment ): squiggleExpression { switch (result.TAG) { case 0: // EvArray return tag( "array", - result._0.map((x) => convertRawToTypescript(x, sampEnv)) + result._0.map((x) => convertRawToTypescript(x, environment)) ); - case 1: // EvBool + case 1: // EvArrayString + return tag("arraystring", result._0); + case 2: // EvBool return tag("boolean", result._0); - case 2: // EvCall + case 3: // EvCall return tag("call", result._0); - case 3: // EvDistribution + case 4: // EvDistribution return tag( "distribution", new Distribution( convertRawDistributionToGenericDist(result._0), - sampEnv + environment ) ); - case 4: // EvNumber + case 5: // EvDistribution + return tag("lambda", result._0); + case 6: // EvNumber return tag("number", result._0); - case 5: // EvRecord + case 7: // EvRecord return tag( "record", - _.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv)) + _.mapValues(result._0, (x) => convertRawToTypescript(x, environment)) ); - case 6: // EvString + case 8: // EvString return tag("string", result._0); - case 7: // EvSymbol + case 9: // EvSymbol return tag("symbol", result._0); } } @@ -141,15 +156,15 @@ export type jsValue = export function jsValueToBinding(value: jsValue): rescriptExport { if (typeof value === "boolean") { - return { TAG: 1, _0: value as boolean }; + return { TAG: 2, _0: value as boolean }; } else if (typeof value === "string") { - return { TAG: 6, _0: value as string }; + return { TAG: 8, _0: value as string }; } else if (typeof value === "number") { - return { TAG: 4, _0: value as number }; + return { TAG: 6, _0: value as number }; } else if (Array.isArray(value)) { return { TAG: 0, _0: value.map(jsValueToBinding) }; } else { // Record - return { TAG: 5, _0: _.mapValues(value, jsValueToBinding) }; + return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) }; } } diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res index 5f07c6a8..1af12e1a 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.res @@ -9,6 +9,11 @@ type env = { xyPointLength: int, } +let defaultEnv = { + sampleCount: 10000, + xyPointLength: 10000, +} + type outputType = | Dist(genericDist) | Float(float) diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi index fcaeb5e4..77aa546b 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionOperation/DistributionOperation.resi @@ -4,6 +4,9 @@ type env = { xyPointLength: int, } +@genType +let defaultEnv: env + open DistributionTypes @genType diff --git a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res index 0d413bf4..a9151a8f 100644 --- a/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res +++ b/packages/squiggle-lang/src/rescript/Distributions/DistributionTypes.res @@ -114,6 +114,7 @@ module DistributionOperation = { | ToFloat(#Mean) => `mean` | ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` | ToFloat(#Sample) => `sample` + | ToFloat(#IntegralSum) => `integralSum` | ToDist(Normalize) => `normalize` | ToDist(ToPointSet) => `toPointSet` | ToDist(ToSampleSet(r)) => `toSampleSet(${E.I.toString(r)})` diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res index d2e4858f..1ee57529 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res @@ -1,15 +1,11 @@ -module Dispatch = Reducer_Dispatch module ErrorValue = Reducer_ErrorValue module Expression = Reducer_Expression -module Extra = Reducer_Extra -module Js = Reducer_Js -module MathJs = Reducer_MathJs -type expressionValue = Reducer_Expression.expressionValue -type externalBindings = Expression.externalBindings -let evaluate = Expression.eval -let evaluateUsingExternalBindings = Expression.evalUsingExternalBindings -let evaluatePartialUsingExternalBindings = Expression.evalPartialUsingExternalBindings +type environment = ReducerInterface_ExpressionValue.environment +type errorValue = Reducer_ErrorValue.errorValue +type expressionValue = ReducerInterface_ExpressionValue.expressionValue +type externalBindings = ReducerInterface_ExpressionValue.externalBindings +let evaluate = Expression.evaluate +let evaluateUsingOptions = Expression.evaluateUsingOptions +let evaluatePartialUsingExternalBindings = Expression.evaluatePartialUsingExternalBindings let parse = Expression.parse -let parseOuter = Expression.parseOuter -let parsePartial = Expression.parsePartial diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi index 8bbfc0b5..71b394fb 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi @@ -1,26 +1,28 @@ -module Dispatch = Reducer_Dispatch module ErrorValue = Reducer_ErrorValue module Expression = Reducer_Expression -module Extra = Reducer_Extra -module Js = Reducer_Js -module MathJs = Reducer_MathJs +@genType +type environment = ReducerInterface_ExpressionValue.environment +@genType +type errorValue = Reducer_ErrorValue.errorValue @genType type expressionValue = ReducerInterface_ExpressionValue.expressionValue @genType type externalBindings = ReducerInterface_ExpressionValue.externalBindings + @genType -let evaluate: string => result -@genType -let evaluateUsingExternalBindings: ( +let evaluateUsingOptions: ( + ~environment: option, + ~externalBindings: option, string, - externalBindings, -) => result +) => result @genType let evaluatePartialUsingExternalBindings: ( string, - externalBindings, -) => result -let parse: string => result -let parseOuter: string => result -let parsePartial: string => result + QuriSquiggleLang.ReducerInterface_ExpressionValue.externalBindings, + QuriSquiggleLang.ReducerInterface_ExpressionValue.environment, +) => result +@genType +let evaluate: string => result + +let parse: string => result 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 index 21c91dc2..25fc05a7 100644 --- 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 @@ -1,5 +1,6 @@ module ExternalLibrary = ReducerInterface.ExternalLibrary module MathJs = Reducer_MathJs +module Bindings = Reducer_Expression_Bindings open ReducerInterface.ExpressionValue open Reducer_ErrorValue @@ -11,7 +12,7 @@ open Reducer_ErrorValue exception TestRescriptException -let callInternal = (call: functionCall): result<'b, errorValue> => { +let callInternal = (call: functionCall, _environment): result<'b, errorValue> => { let callMathJs = (call: functionCall): result<'b, errorValue> => switch call { | ("javascriptraise", [msg]) => Js.Exn.raiseError(toString(msg)) // For Tests @@ -20,12 +21,12 @@ let callInternal = (call: functionCall): result<'b, errorValue> => { } let constructRecord = arrayOfPairs => { - Belt.Array.map(arrayOfPairs, pairValue => { + Belt.Array.map(arrayOfPairs, pairValue => switch pairValue { | EvArray([EvString(key), valueValue]) => (key, valueValue) | _ => ("wrong key type", pairValue->toStringWithType->EvString) } - }) + ) ->Js.Dict.fromArray ->EvRecord ->Ok @@ -43,16 +44,58 @@ let callInternal = (call: functionCall): result<'b, errorValue> => { | None => RERecordPropertyNotFound("Record property not found", sIndex)->Error } + let inspect = (value: expressionValue) => { + Js.log(value->toString) + value->Ok + } + + let inspectLabel = (value: expressionValue, label: string) => { + Js.log(`${label}: ${value->toString}`) + value->Ok + } + + /* + NOTE: This function is cancelled. The related issue is + https://github.com/webpack/webpack/issues/13435 + */ + let inspectPerformance = (value: expressionValue, label: string) => { + // let _ = %raw("{performance} = require('perf_hooks')") + // let start = %raw(`performance.now()`) + // let finish = %raw(`performance.now()`) + // let performance = finish - start + // Js.log(`${label}: ${value->toString} performance: ${Js.String.make(performance)}ms`) + // TODO find a way of failing the hook gracefully, also needs a block parameter + Js.log(`${label}: ${value->toString}`) + value->Ok + } + + let doSetBindings = ( + externalBindings: externalBindings, + symbol: string, + value: expressionValue, + ) => { + Bindings.fromExternalBindings(externalBindings) + ->Belt.Map.String.set(symbol, value) + ->Bindings.toExternalBindings + ->EvRecord + ->Ok + } + + let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok + switch call { - // | ("$constructRecord", pairArray) - // | ("$atIndex", [EvArray(anArray), EvNumber(fIndex)]) => arrayAtIndex(anArray, fIndex) - // | ("$atIndex", [EvRecord(aRecord), EvString(sIndex)]) => recordAtIndex(aRecord, sIndex) - | ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) | ("$atIndex", [EvArray(aValueArray), EvArray([EvNumber(fIndex)])]) => arrayAtIndex(aValueArray, fIndex) | ("$atIndex", [EvRecord(dict), EvArray([EvString(sIndex)])]) => recordAtIndex(dict, sIndex) | ("$atIndex", [obj, index]) => (toStringWithType(obj) ++ "??~~~~" ++ toStringWithType(index))->EvString->Ok + | ("$constructRecord", [EvArray(arrayOfPairs)]) => constructRecord(arrayOfPairs) + | ("inspect", [value, EvString(label)]) => inspectLabel(value, label) + | ("inspect", [value]) => inspect(value) + | ("inspectPerformance", [value, EvString(label)]) => inspectPerformance(value, label) + | ("$setBindings", [EvRecord(externalBindings), EvSymbol(symbol), value]) => + doSetBindings(externalBindings, symbol, value) + | ("$exportBindings", [EvRecord(externalBindings)]) => doExportBindings(externalBindings) | call => callMathJs(call) } } @@ -60,12 +103,12 @@ let callInternal = (call: functionCall): result<'b, errorValue> => { /* Reducer uses Result monad while reducing expressions */ -let dispatch = (call: functionCall): result => +let dispatch = (call: functionCall, environment): result => 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), callInternal) + ExternalLibrary.dispatch((Js.String.make(fn), args), environment, callInternal) } catch { | Js.Exn.Error(obj) => REJavaScriptExn(Js.Exn.message(obj), Js.Exn.name(obj))->Error | _ => RETodo("unhandled rescript exception")->Error 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 index b7df0ff3..c106c0ff 100644 --- 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 @@ -3,120 +3,170 @@ 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_Expression_Bindings module ExpressionT = Reducer_Expression_T module ExpressionValue = ReducerInterface.ExpressionValue +module ExpressionWithContext = Reducer_ExpressionWithContext module Result = Belt.Result +open Reducer_Expression_ExpressionBuilder -open Reducer_ErrorValue - +type environment = ExpressionValue.environment +type errorValue = Reducer_ErrorValue.errorValue type expression = ExpressionT.expression - -type reducerFn = ( - expression, - ExpressionT.bindings, -) => result +type expressionValue = ExpressionValue.expressionValue +type expressionWithContext = ExpressionWithContext.expressionWithContext let dispatchMacroCall = ( - list: list, + macroExpression: expression, bindings: ExpressionT.bindings, - reduceExpression: reducerFn, -): result => { - let rec replaceSymbols = (expression: expression, bindings: ExpressionT.bindings): result< - expression, - errorValue, - > => - switch expression { - | ExpressionT.EValue(EvSymbol(aSymbol)) => - switch bindings->Belt.Map.String.get(aSymbol) { - | Some(boundExpression) => boundExpression->Ok - | None => RESymbolNotFound(aSymbol)->Error - } - | ExpressionT.EValue(_) => expression->Ok - | ExpressionT.EBindings(_) => expression->Ok - | ExpressionT.EList(list) => { - let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) => - racc->Result.flatMap(acc => { - each - ->replaceSymbols(bindings) - ->Result.flatMap(newNode => { - acc->Belt.List.add(newNode)->Ok - }) - }) - ) - racc->Result.map(acc => acc->ExpressionT.EList) - } - } - - let doBindStatement = (statement: expression, bindings: ExpressionT.bindings) => { + environment, + reduceExpression: ExpressionT.reducerFn, +): result => { + let doBindStatement = (bindingExpr: expression, statement: expression, environment) => switch statement { - | ExpressionT.EList(list{ - ExpressionT.EValue(EvCall("$let")), - ExpressionT.EValue(EvSymbol(aSymbol)), - expressionToReduce, - }) => { - let rNewExpressionToReduce = replaceSymbols(expressionToReduce, bindings) + | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => { + let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment) - let rNewValue = - rNewExpressionToReduce->Result.flatMap(newExpressionToReduce => - reduceExpression(newExpressionToReduce, bindings) + rExternalBindingsValue->Result.flatMap(externalBindingsValue => { + let newBindings = Bindings.fromValue(externalBindingsValue) + + // Js.log( + // `bindStatement ${Bindings.toString(newBindings)}<==${ExpressionT.toString( + // bindingExpr, + // )} statement: $let ${ExpressionT.toString(symbolExpr)}=${ExpressionT.toString( + // statement, + // )}`, + // ) + + let rNewStatement = Bindings.replaceSymbols(newBindings, statement) + rNewStatement->Result.map(newStatement => + ExpressionWithContext.withContext( + eFunction( + "$setBindings", + list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement}, + ), + newBindings, + ) ) - - let rNewExpression = rNewValue->Result.map(newValue => ExpressionT.EValue(newValue)) - rNewExpression->Result.map(newExpression => - Belt.Map.String.set(bindings, aSymbol, newExpression)->ExpressionT.EBindings - ) + }) } | _ => REAssignmentExpected->Error } - } - let doExportVariableExpression = (bindings: ExpressionT.bindings) => { - let emptyDictionary: Js.Dict.t = Js.Dict.empty() - let reducedBindings = bindings->Belt.Map.String.keep((_key, value) => - switch value { - | ExpressionT.EValue(_) => true - | _ => false - } - ) - let externalBindings = reducedBindings->Belt.Map.String.reduce(emptyDictionary, ( - acc, - key, - expressionValue, - ) => { - let value = switch expressionValue { - | EValue(aValue) => aValue - | _ => EvSymbol("internal") - } - Js.Dict.set(acc, key, value) - acc - }) - externalBindings->ExpressionValue.EvRecord->ExpressionT.EValue->Ok - } + let doBindExpression = (bindingExpr: expression, statement: expression, environment): result< + expressionWithContext, + errorValue, + > => + switch statement { + | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), symbolExpr, statement}) => { + let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment) - let doBindExpression = (expression: expression, bindings: ExpressionT.bindings) => - switch expression { - | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$let")), ..._}) => - REExpressionExpected->Error - | ExpressionT.EList(list{ExpressionT.EValue(EvCall("$exportVariablesExpression"))}) => - doExportVariableExpression(bindings) - | _ => replaceSymbols(expression, bindings) + rExternalBindingsValue->Result.flatMap(externalBindingsValue => { + let newBindings = Bindings.fromValue(externalBindingsValue) + let rNewStatement = Bindings.replaceSymbols(newBindings, statement) + rNewStatement->Result.map(newStatement => + ExpressionWithContext.withContext( + eFunction( + "$exportBindings", + list{ + eFunction( + "$setBindings", + list{ + newBindings->Bindings.toExternalBindings->eRecord, + symbolExpr, + newStatement, + }, + ), + }, + ), + newBindings, + ) + ) + }) + } + | _ => { + let rExternalBindingsValue: result = reduceExpression( + bindingExpr, + bindings, + environment, + ) + + rExternalBindingsValue->Result.flatMap(externalBindingsValue => { + let newBindings = Bindings.fromValue(externalBindingsValue) + let rNewStatement = Bindings.replaceSymbols(newBindings, statement) + rNewStatement->Result.map(newStatement => + ExpressionWithContext.withContext(newStatement, newBindings) + ) + }) + } } - switch list { - | list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok + let doBlock = (exprs: list, _bindings: ExpressionT.bindings, _environment): result< + expressionWithContext, + errorValue, + > => { + 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)->Ok + } - | list{ - ExpressionT.EValue(EvCall("$$bindStatement")), - ExpressionT.EBindings(bindings), - statement, - } => - doBindStatement(statement, bindings) - | list{ - ExpressionT.EValue(EvCall("$$bindExpression")), - ExpressionT.EBindings(bindings), - expression, - } => - doBindExpression(expression, bindings) - | _ => list->ExpressionT.EList->Ok + let doLambdaDefinition = ( + bindings: ExpressionT.bindings, + parameters: array, + lambdaDefinition: ExpressionT.expression, + ) => + ExpressionWithContext.noContext( + eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition), + )->Ok + + let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result< + expressionWithContext, + errorValue, + > => + switch aList { + | list{ + ExpressionT.EValue(EvCall("$$bindStatement")), + bindingExpr: ExpressionT.expression, + statement, + } => + doBindStatement(bindingExpr, statement, environment) + | list{ExpressionT.EValue(EvCall("$$bindStatement")), statement} => + // bindings of the context are used when there is no binding expression + doBindStatement(eRecord(Bindings.toExternalBindings(bindings)), statement, environment) + | list{ + ExpressionT.EValue(EvCall("$$bindExpression")), + bindingExpr: ExpressionT.expression, + expression, + } => + doBindExpression(bindingExpr, expression, environment) + | list{ExpressionT.EValue(EvCall("$$bindExpression")), expression} => + // bindings of the context are used when there is no binding expression + doBindExpression(eRecord(Bindings.toExternalBindings(bindings)), expression, environment) + | list{ExpressionT.EValue(EvCall("$$block")), ...exprs} => doBlock(exprs, bindings, environment) + | list{ + ExpressionT.EValue(EvCall("$$lambda")), + ExpressionT.EValue(EvArrayString(parameters)), + lambdaDefinition, + } => + doLambdaDefinition(bindings, parameters, lambdaDefinition) + | _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok + } + + switch macroExpression { + | EList(aList) => expandExpressionList(aList, bindings, environment) + | _ => ExpressionWithContext.noContext(macroExpression)->Ok } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 96b73fd2..7964c3a4 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -1,15 +1,17 @@ @genType type errorValue = + | REArityError(option, int, int) //TODO: Binding a lambda to a variable should record the variable name in lambda for error reporting | REArrayIndexNotFound(string, int) | REAssignmentExpected + | REDistributionError(DistributionTypes.error) | REExpressionExpected | REFunctionExpected(string) | REJavaScriptExn(option, option) // Javascript Exception | REMacroNotFound(string) + | RENotAFunction(string) | RERecordPropertyNotFound(string, string) | RESymbolNotFound(string) | RESyntaxError(string) - | REDistributionError(DistributionTypes.error) | RETodo(string) // To do type t = errorValue @@ -17,6 +19,10 @@ type t = errorValue @genType let errorToString = err => switch err { + | REArityError(_oFnName, arity, usedArity) => + `${Js.String.make(arity)} arguments expected. Instead ${Js.String.make( + usedArity, + )} argument(s) were passed.` | REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}` | REAssignmentExpected => "Assignment expected" | REExpressionExpected => "Expression expected" @@ -35,6 +41,7 @@ let errorToString = err => answer } | REMacroNotFound(macro) => `Macro not found: ${macro}` + | RENotAFunction(valueString) => `${valueString} is not a function` | RERecordPropertyNotFound(msg, index) => `${msg}: ${index}` | RESymbolNotFound(symbolName) => `${symbolName} is not defined` | RESyntaxError(desc) => `Syntax Error: ${desc}` 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 b45b8e14..e1df1418 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,35 +1,22 @@ +module Bindings = Reducer_Expression_Bindings module BuiltIn = Reducer_Dispatch_BuiltIn +module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionValue = ReducerInterface.ExpressionValue module Extra = Reducer_Extra +module Lambda = Reducer_Expression_Lambda +module Macro = Reducer_Expression_Macro module MathJs = Reducer_MathJs module Result = Belt.Result module T = Reducer_Expression_T -open Reducer_ErrorValue +type environment = ReducerInterface_ExpressionValue.environment +type errorValue = Reducer_ErrorValue.errorValue type expression = T.expression -type expressionValue = ExpressionValue.expressionValue +type expressionValue = ReducerInterface_ExpressionValue.expressionValue +type externalBindings = ReducerInterface_ExpressionValue.externalBindings +type internalCode = ReducerInterface_ExpressionValue.internalCode type t = expression -/* - Shows the expression as text of expression -*/ -let rec toString = expression => - switch expression { - | T.EBindings(_) => "$$bound" - | T.EList(aList) => - `(${Belt.List.map(aList, aValue => toString(aValue)) - ->Extra.List.interperse(" ") - ->Belt.List.toArray - ->Js.String.concatMany("")})` - | EValue(aValue) => ExpressionValue.toString(aValue) - } - -let toStringResult = codeResult => - switch codeResult { - | Ok(a) => `Ok(${toString(a)})` - | Error(m) => `Error(${Js.String.make(m)})` - } - /* Converts a MathJs code to expression */ @@ -39,148 +26,115 @@ let parse_ = (expr: string, parser, converter): result => let parse = (mathJsCode: string): result => mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromNode) -let parsePartial = (mathJsCode: string): result => - mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromPartialNode) - -let parseOuter = (mathJsCode: string): result => - mathJsCode->parse_(MathJs.Parse.parse, MathJs.ToExpression.fromOuterNode) - -let defaultBindings: T.bindings = Belt.Map.String.empty - /* Recursively evaluate/reduce the expression (Lisp AST) */ -let rec reduceExpression = (expression: t, bindings: T.bindings): result => { - /* - Macros are like functions but instead of taking values as parameters, - they take expressions as parameters and return a new expression. - Macros are used to define language building blocks. They are like Lisp macros. - */ - let doMacroCall = (list: list, bindings: T.bindings): result => - Reducer_Dispatch_BuiltInMacros.dispatchMacroCall(list, bindings, reduceExpression) +let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result< + expressionValue, + 'e, +> => { + // Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`) + switch expression { + | T.EValue(value) => value->Ok + | T.EList(list) => + switch list { + | list{EValue(EvCall(fName)), ..._args} => + switch Macro.isMacroName(fName) { + // A macro expands then reduces itself + | true => Macro.doMacroCall(expression, bindings, environment, reduceExpression) + | false => reduceExpressionList(list, bindings, environment) + } + | _ => reduceExpressionList(list, bindings, environment) + } + } +} - /* +and reduceExpressionList = ( + expressions: list, + bindings: T.bindings, + environment: environment, +): result => { + let racc: result, 'e> = expressions->Belt.List.reduceReverse(Ok(list{}), ( + racc, + each: expression, + ) => + racc->Result.flatMap(acc => { + each + ->reduceExpression(bindings, environment) + ->Result.map(newNode => { + acc->Belt.List.add(newNode) + }) + }) + ) + racc->Result.flatMap(acc => acc->reduceValueList(environment)) +} + +/* After reducing each level of expression(Lisp AST), we have a value list to evaluate */ - let reduceValueList = (valueList: list): result => - switch valueList { - | list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch - | _ => valueList->Belt.List.toArray->ExpressionValue.EvArray->Ok - } +and reduceValueList = (valueList: list, environment): result< + expressionValue, + 'e, +> => + switch valueList { + | list{EvCall(fName), ...args} => (fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment) - let rec seekMacros = (expression: t, bindings: T.bindings): result => - switch expression { - | T.EValue(_value) => expression->Ok - | T.EBindings(_value) => expression->Ok - | T.EList(list) => { - let racc: result, 'e> = list->Belt.List.reduceReverse(Ok(list{}), ( - racc, - each: expression, - ) => - racc->Result.flatMap(acc => { - each - ->seekMacros(bindings) - ->Result.flatMap(newNode => { - acc->Belt.List.add(newNode)->Ok - }) - }) - ) - racc->Result.flatMap(acc => acc->doMacroCall(bindings)) - } - } + | list{EvLambda(lamdaCall), ...args} => + Lambda.doLambdaCall(lamdaCall, args, environment, reduceExpression) + | _ => + valueList + ->Lambda.checkIfReduced + ->Result.flatMap(reducedValueList => + reducedValueList->Belt.List.toArray->ExpressionValue.EvArray->Ok + ) + } - let rec reduceExpandedExpression = (expression: t): result => - switch expression { - | T.EValue(value) => value->Ok - | T.EList(list) => { - let racc: result, 'e> = list->Belt.List.reduceReverse(Ok(list{}), ( - racc, - each: expression, - ) => - racc->Result.flatMap(acc => { - each - ->reduceExpandedExpression - ->Result.flatMap(newNode => { - acc->Belt.List.add(newNode)->Ok - }) - }) - ) - racc->Result.flatMap(acc => acc->reduceValueList) - } - | EBindings(_bindings) => RETodo("Error: Bindings cannot be reduced to values")->Error - } +let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result< + expressionValue, + 'e, +> => reduceExpression(aExpression, bindings, environment) - let rExpandedExpression: result = expression->seekMacros(bindings) - rExpandedExpression->Result.flatMap(expandedExpression => - expandedExpression->reduceExpandedExpression - ) -} +let evaluateUsingOptions = ( + ~environment: option, + ~externalBindings: option, + code: string, +): result => { + let anEnvironment = switch environment { + | Some(env) => env + | None => ReducerInterface_ExpressionValue.defaultEnvironment + } -let evalUsingExternalBindingsExpression_ = (aExpression, bindings): result => - reduceExpression(aExpression, bindings) + let anExternalBindings = switch externalBindings { + | Some(bindings) => bindings + | None => ReducerInterface_ExpressionValue.defaultExternalBindings + } -/* - Evaluates MathJs code via Reducer using bindings and answers the result. - When bindings are used, the code is a partial code as if it is cut from a larger code. - Therefore all statements are assignments. -*/ -let evalPartialUsingExternalBindings_ = (codeText: string, bindings: T.bindings) => { - parsePartial(codeText)->Result.flatMap(expression => - expression->evalUsingExternalBindingsExpression_(bindings) - ) -} + let bindings = anExternalBindings->Bindings.fromExternalBindings -/* - Evaluates MathJs code via Reducer using bindings and answers the result. - When bindings are used, the code is a partial code as if it is cut from a larger code. - Therefore all statments are assignments. -*/ -let evalOuterWBindings_ = (codeText: string, bindings: T.bindings) => { - parseOuter(codeText)->Result.flatMap(expression => - expression->evalUsingExternalBindingsExpression_(bindings) - ) + parse(code)->Result.flatMap(expr => evalUsingBindingsExpression_(expr, bindings, anEnvironment)) } /* Evaluates MathJs code and bindings via Reducer and answers the result */ -let eval = (codeText: string) => { - parse(codeText)->Result.flatMap(expression => - expression->evalUsingExternalBindingsExpression_(defaultBindings) - ) -} - -type externalBindings = ReducerInterface.ExpressionValue.externalBindings //Js.Dict.t - -let externalBindingsToBindings = (externalBindings: externalBindings): T.bindings => { - let keys = Js.Dict.keys(externalBindings) - keys->Belt.Array.reduce(defaultBindings, (acc, key) => { - let value = Js.Dict.unsafeGet(externalBindings, key) - acc->Belt.Map.String.set(key, T.EValue(value)) - }) -} -/* - Evaluates code with external bindings. External bindings are a record of expression values. -*/ -let evalUsingExternalBindings = (code: string, externalBindings: externalBindings) => { - let bindings = externalBindings->externalBindingsToBindings - evalOuterWBindings_(code, bindings) -} - -/* - Evaluates code with external bindings. External bindings are a record of expression values. - The code is a partial code as if it is cut from a larger code. Therefore all statments are assignments. -*/ -let evalPartialUsingExternalBindings = (code: string, externalBindings: externalBindings): result< - externalBindings, - 'e, -> => { - let bindings = externalBindings->externalBindingsToBindings - let answer = evalPartialUsingExternalBindings_(code, bindings) - answer->Result.flatMap(answer => - switch answer { - | EvRecord(aRecord) => Ok(aRecord) - | _ => RETodo("TODO: External bindings must be returned")->Error - } +let evaluate = (code: string): result => { + evaluateUsingOptions(~environment=None, ~externalBindings=None, code) +} +let eval = evaluate +let evaluatePartialUsingExternalBindings = ( + code: string, + externalBindings: ReducerInterface_ExpressionValue.externalBindings, + environment: ReducerInterface_ExpressionValue.environment, +): result => { + let rAnswer = evaluateUsingOptions( + ~environment=Some(environment), + ~externalBindings=Some(externalBindings), + code, ) + switch rAnswer { + | Ok(EvRecord(externalBindings)) => Ok(externalBindings) + | Ok(_) => + Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`)) + | Error(err) => err->Error + } } 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 new file mode 100644 index 00000000..dacd2462 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_ExpressionWithContext.res @@ -0,0 +1,45 @@ +module Bindings = Reducer_Expression_Bindings +module ErrorValue = Reducer_ErrorValue +module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue +module Result = Belt.Result + +type bindings = ExpressionT.bindings +type context = bindings +type environment = ExpressionValue.environment +type errorValue = Reducer_ErrorValue.errorValue +type expression = ExpressionT.expression +type expressionValue = ExpressionValue.expressionValue +type externalBindings = ReducerInterface_ExpressionValue.externalBindings +type reducerFn = ExpressionT.reducerFn + +type expressionWithContext = + | ExpressionWithContext(expression, context) + | ExpressionNoContext(expression) + +let callReducer = ( + expressionWithContext: expressionWithContext, + bindings: bindings, + environment: environment, + reducer: reducerFn, +): result => + switch expressionWithContext { + | ExpressionNoContext(expr) => reducer(expr, bindings, environment) + | ExpressionWithContext(expr, context) => reducer(expr, context, environment) + } + +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: ${Bindings.toString(context)}` + } + +let toStringResult = rExpressionWithContext => + switch rExpressionWithContext { + | Ok(expressionWithContext) => `Ok(${toString(expressionWithContext)})` + | Error(errorValue) => ErrorValue.errorToString(errorValue) + } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res new file mode 100644 index 00000000..7c0c048a --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Bindings.res @@ -0,0 +1,85 @@ +module ErrorValue = Reducer_ErrorValue +module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue +module Result = Belt.Result + +type errorValue = Reducer_ErrorValue.errorValue +type expression = ExpressionT.expression +type expressionValue = ExpressionValue.expressionValue +type externalBindings = ReducerInterface_ExpressionValue.externalBindings + +let defaultBindings: ExpressionT.bindings = Belt.Map.String.empty + +let fromExternalBindings = (externalBindings: externalBindings): ExpressionT.bindings => { + let keys = Js.Dict.keys(externalBindings) + keys->Belt.Array.reduce(defaultBindings, (acc, key) => { + let value = Js.Dict.unsafeGet(externalBindings, key) + acc->Belt.Map.String.set(key, value) + }) +} + +let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => { + let keys = Belt.Map.String.keysToArray(bindings) + keys->Belt.Array.reduce(Js.Dict.empty(), (acc, key) => { + let value = bindings->Belt.Map.String.getExn(key) + Js.Dict.set(acc, key, value) + acc + }) +} + +let fromValue = (aValue: expressionValue) => + switch aValue { + | EvRecord(externalBindings) => fromExternalBindings(externalBindings) + | _ => defaultBindings + } + +let externalFromArray = anArray => Js.Dict.fromArray(anArray) + +let isMacroName = (fName: string): bool => fName->Js.String2.startsWith("$$") + +let rec replaceSymbols = (bindings: ExpressionT.bindings, expression: expression): result< + expression, + errorValue, +> => + switch expression { + | ExpressionT.EValue(value) => + replaceSymbolOnValue(bindings, value)->Result.map(evValue => evValue->ExpressionT.EValue) + | ExpressionT.EList(list) => + switch list { + | list{EValue(EvCall(fName)), ..._args} => + switch isMacroName(fName) { + // A macro reduces itself so we dont dive in it + | true => expression->Ok + | false => replaceSymbolsOnExpressionList(bindings, list) + } + | _ => replaceSymbolsOnExpressionList(bindings, list) + } + } + +and replaceSymbolsOnExpressionList = (bindings, list) => { + let racc = list->Belt.List.reduceReverse(Ok(list{}), (racc, each: expression) => + racc->Result.flatMap(acc => { + replaceSymbols(bindings, each)->Result.flatMap(newNode => { + acc->Belt.List.add(newNode)->Ok + }) + }) + ) + racc->Result.map(acc => acc->ExpressionT.EList) +} +and replaceSymbolOnValue = (bindings, evValue: expressionValue) => + switch evValue { + | EvSymbol(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->Ok + | EvCall(symbol) => Belt.Map.String.getWithDefault(bindings, symbol, evValue)->checkIfCallable + | _ => evValue->Ok + } +and checkIfCallable = (evValue: expressionValue) => + switch evValue { + | EvCall(_) | EvLambda(_) => evValue->Ok + | _ => ErrorValue.RENotAFunction(ExpressionValue.toString(evValue))->Error + } + +let toString = (bindings: ExpressionT.bindings) => + bindings->toExternalBindings->ExpressionValue.EvRecord->ExpressionValue.toString + +let externalBindingsToString = (externalBindings: externalBindings) => + externalBindings->ExpressionValue.EvRecord->ExpressionValue.toString 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 new file mode 100644 index 00000000..9c9f922e --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -0,0 +1,66 @@ +module BBindings = Reducer_Expression_Bindings +module BErrorValue = Reducer_ErrorValue +module BExpressionT = Reducer_Expression_T +module BExpressionValue = ReducerInterface.ExpressionValue + +type errorValue = BErrorValue.errorValue +type expression = BExpressionT.expression +type internalCode = ReducerInterface_ExpressionValue.internalCode + +external castExpressionToInternalCode: expression => internalCode = "%identity" + +let eArray = anArray => anArray->BExpressionValue.EvArray->BExpressionT.EValue + +let eArrayString = anArray => anArray->BExpressionValue.EvArrayString->BExpressionT.EValue + +let eBindings = (anArray: array<(string, BExpressionValue.expressionValue)>) => + anArray->Js.Dict.fromArray->EvRecord->BExpressionT.EValue + +let eBool = aBool => aBool->BExpressionValue.EvBool->BExpressionT.EValue + +let eCall = (name: string): expression => name->BExpressionValue.EvCall->BExpressionT.EValue + +let eFunction = (fName: string, lispArgs: list): expression => { + let fn = fName->eCall + list{fn, ...lispArgs}->BExpressionT.EList +} + +let eLambda = ( + parameters: array, + context: BExpressionValue.externalBindings, + expr: expression, +) => { + // Js.log(`eLambda context ${BBindings.externalBindingsToString(context)}`) + BExpressionValue.EvLambda({ + parameters: parameters, + context: context, + body: expr->castExpressionToInternalCode, + })->BExpressionT.EValue +} + +let eNumber = aNumber => aNumber->BExpressionValue.EvNumber->BExpressionT.EValue + +let eRecord = aRecord => aRecord->BExpressionValue.EvRecord->BExpressionT.EValue + +let eString = aString => aString->BExpressionValue.EvString->BExpressionT.EValue + +let eSymbol = (name: string): expression => name->BExpressionValue.EvSymbol->BExpressionT.EValue + +let eList = (list: list): expression => list->BExpressionT.EList + +let eBlock = (exprs: list): expression => eFunction("$$block", exprs) + +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}) 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 new file mode 100644 index 00000000..55931cc7 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Lambda.res @@ -0,0 +1,60 @@ +module Bindings = Reducer_Expression_Bindings +module ErrorValue = Reducer_ErrorValue +module ExpressionBuilder = Reducer_Expression_ExpressionBuilder +module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue +module Result = Belt.Result + +type environment = ReducerInterface_ExpressionValue.environment +type expression = ExpressionT.expression +type expressionValue = ReducerInterface_ExpressionValue.expressionValue +type externalBindings = ReducerInterface_ExpressionValue.externalBindings +type internalCode = ReducerInterface_ExpressionValue.internalCode + +external castInternalCodeToExpression: internalCode => expression = "%identity" + +let checkArity = (lambdaValue: ExpressionValue.lambdaValue, args: list) => { + let argsLength = Belt.List.length(args) + let parametersLength = Js.Array2.length(lambdaValue.parameters) + if argsLength !== parametersLength { + ErrorValue.REArityError(None, parametersLength, argsLength)->Error + } else { + args->Ok + } +} + +let checkIfReduced = (args: list) => + args->Belt.List.reduceReverse(Ok(list{}), (rAcc, arg) => + rAcc->Result.flatMap(acc => + switch arg { + | EvSymbol(symbol) => ErrorValue.RESymbolNotFound(symbol)->Error + | _ => list{arg, ...acc}->Ok + } + ) + ) + +let applyParametersToLambda = ( + lambdaValue: ExpressionValue.lambdaValue, + args, + environment, + reducer: ExpressionT.reducerFn, +): result => { + checkArity(lambdaValue, args)->Result.flatMap(args => + checkIfReduced(args)->Result.flatMap(args => { + let expr = castInternalCodeToExpression(lambdaValue.body) + let parameterList = lambdaValue.parameters->Belt.List.fromArray + let zippedParameterList = parameterList->Belt.List.zip(args) + let bindings = Belt.List.reduce( + zippedParameterList, + lambdaValue.context->Bindings.fromExternalBindings, + (acc, (variable, variableValue)) => acc->Belt.Map.String.set(variable, variableValue), + ) + let newExpression = ExpressionBuilder.eBlock(list{expr}) + reducer(newExpression, bindings, environment) + }) + ) +} + +let doLambdaCall = (lambdaValue: ExpressionValue.lambdaValue, args, environment, reducer) => { + applyParametersToLambda(lambdaValue, args, environment, reducer) +} 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 new file mode 100644 index 00000000..23fb70f8 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Macro.res @@ -0,0 +1,44 @@ +module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue +module ExpressionWithContext = Reducer_ExpressionWithContext +module Result = Belt.Result + +type environment = ExpressionValue.environment +type expression = ExpressionT.expression +type expressionValue = ExpressionValue.expressionValue +type expressionWithContext = ExpressionWithContext.expressionWithContext + +let expandMacroCall = ( + macroExpression: expression, + bindings: ExpressionT.bindings, + environment: environment, + reduceExpression: ExpressionT.reducerFn, +): result => + Reducer_Dispatch_BuiltInMacros.dispatchMacroCall( + macroExpression, + bindings, + environment, + reduceExpression, + ) + +let doMacroCall = ( + macroExpression: expression, + bindings: ExpressionT.bindings, + environment: environment, + reduceExpression: ExpressionT.reducerFn, +): result => + expandMacroCall( + macroExpression, + bindings, + environment, + reduceExpression, + )->Result.flatMap(expressionWithContext => + ExpressionWithContext.callReducer( + expressionWithContext, + bindings, + environment, + 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 fe938316..b21ba8b7 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,5 +1,3 @@ -open ReducerInterface.ExpressionValue - /* 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 @@ -8,8 +6,51 @@ open ReducerInterface.ExpressionValue 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. */ +module Extra = Reducer_Extra +module ExpressionValue = ReducerInterface.ExpressionValue + +type expressionValue = ExpressionValue.expressionValue +type environment = ExpressionValue.environment + type rec expression = | EList(list) // A list to map-reduce | EValue(expressionValue) // Irreducible built-in value. Reducer should not know the internals. External libraries are responsible - | EBindings(bindings) // let/def kind of statements return bindings -and bindings = Belt.Map.String.t +and bindings = Belt.Map.String.t + +type reducerFn = ( + expression, + bindings, + environment, +) => result + +/* + Converts the expression to String +*/ +let rec toString = expression => + switch expression { + | EList(aList) => + `(${Belt.List.map(aList, aValue => toString(aValue)) + ->Extra.List.interperse(" ") + ->Belt.List.toArray + ->Js.String.concatMany("")})` + | EValue(aValue) => ExpressionValue.toString(aValue) + } + +let toStringResult = codeResult => + switch codeResult { + | Ok(a) => `Ok(${toString(a)})` + | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` + } + +let inspect = (expr: expression): expression => { + Js.log(toString(expr)) + expr +} + +let inspectResult = (r: result): result< + expression, + Reducer_ErrorValue.errorValue, +> => { + Js.log(toStringResult(r)) + r +} 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 index ad83edb1..7cd220bc 100644 --- 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 @@ -8,11 +8,10 @@ external castString: unit => string = "%identity" /* As JavaScript returns us any type, we need to type check and cast type propertype before using it */ -let jsToEv = (jsValue): result => { +let jsToEv = (jsValue): result => switch Js.typeof(jsValue) { | "boolean" => jsValue->castBool->EvBool->Ok | "number" => jsValue->castNumber->EvNumber->Ok | "string" => jsValue->castString->EvString->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_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res index e3e2955c..1f5df97d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res @@ -11,11 +11,10 @@ type block = {"node": node} type blockNode = {...node, "blocks": array} //conditionalNode type constantNode = {...node, "value": unit} -//functionAssignmentNode +type functionAssignmentNode = {...node, "name": string, "params": array, "expr": node} type indexNode = {...node, "dimensions": array} type objectNode = {...node, "properties": Js.Dict.t} type accessorNode = {...node, "object": node, "index": indexNode, "name": string} - type parenthesisNode = {...node, "content": node} //rangeNode //relationalNode @@ -33,6 +32,7 @@ external castAssignmentNodeWAccessor: node => assignmentNodeWAccessor = "%identi external castAssignmentNodeWIndex: node => assignmentNodeWIndex = "%identity" external castBlockNode: node => blockNode = "%identity" external castConstantNode: node => constantNode = "%identity" +external castFunctionAssignmentNode: node => functionAssignmentNode = "%identity" external castFunctionNode: node => functionNode = "%identity" external castIndexNode: node => indexNode = "%identity" external castObjectNode: node => objectNode = "%identity" @@ -59,6 +59,7 @@ type mathJsNode = | MjAssignmentNode(assignmentNode) | MjBlockNode(blockNode) | MjConstantNode(constantNode) + | MjFunctionAssignmentNode(functionAssignmentNode) | MjFunctionNode(functionNode) | MjIndexNode(indexNode) | MjObjectNode(objectNode) @@ -82,6 +83,7 @@ let castNodeType = (node: node) => { | "AssignmentNode" => node->decideAssignmentNode | "BlockNode" => node->castBlockNode->MjBlockNode->Ok | "ConstantNode" => node->castConstantNode->MjConstantNode->Ok + | "FunctionAssignmentNode" => node->castFunctionAssignmentNode->MjFunctionAssignmentNode->Ok | "FunctionNode" => node->castFunctionNode->MjFunctionNode->Ok | "IndexNode" => node->castIndexNode->MjIndexNode->Ok | "ObjectNode" => node->castObjectNode->MjObjectNode->Ok @@ -118,6 +120,10 @@ let rec toString = (mathJsNode: mathJsNode): string => { ->Extra.Array.interperse(", ") ->Js.String.concatMany("") + let toStringFunctionAssignmentNode = (faNode: functionAssignmentNode): string => { + let paramNames = Js.Array2.toString(faNode["params"]) + `${faNode["name"]} = (${paramNames}) => ${toStringMathJsNode(faNode["expr"])}` + } let toStringFunctionNode = (fnode: functionNode): string => `${fnode->nameOfFunctionNode}(${fnode["args"]->toStringNodeArray})` @@ -152,6 +158,7 @@ let rec toString = (mathJsNode: mathJsNode): string => { `${aNode["object"]->toStringSymbolNode} = ${aNode["value"]->toStringMathJsNode}` | MjBlockNode(bNode) => `{${bNode["blocks"]->toStringBlocks}}` | MjConstantNode(cNode) => cNode["value"]->toStringValue + | MjFunctionAssignmentNode(faNode) => faNode->toStringFunctionAssignmentNode | MjFunctionNode(fNode) => fNode->toStringFunctionNode | MjIndexNode(iNode) => iNode->toStringIndexNode | MjObjectNode(oNode) => oNode->toStringObjectNode diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res index a8b7b39a..ca040190 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res @@ -1,45 +1,35 @@ +/* * WARNING. DO NOT EDIT, BEAUTIFY, COMMENT ON OR REFACTOR THIS CODE. +We will stop using MathJs parser and +this whole file will go to trash +**/ module ErrorValue = Reducer_ErrorValue -module ExpressionValue = ReducerInterface.ExpressionValue +module ExpressionBuilder = Reducer_Expression_ExpressionBuilder module ExpressionT = Reducer_Expression_T +module ExpressionValue = ReducerInterface.ExpressionValue module JavaScript = Reducer_Js module Parse = Reducer_MathJs_Parse module Result = Belt.Result +type errorValue = ErrorValue.errorValue type expression = ExpressionT.expression type expressionValue = ExpressionValue.expressionValue -type errorValue = ErrorValue.errorValue -let passToFunction = (fName: string, rLispArgs): result => { - let toEvCallValue = (name: string): expression => name->ExpressionValue.EvCall->ExpressionT.EValue +let blockToNode = block => block["node"] - let fn = fName->toEvCallValue - rLispArgs->Result.flatMap(lispArgs => list{fn, ...lispArgs}->ExpressionT.EList->Ok) -} - -type blockTag = - | ImportVariablesStatement - | ExportVariablesExpression -type tagOrNode = - | BlockTag(blockTag) - | BlockNode(Parse.node) - -let toTagOrNode = block => BlockNode(block["node"]) - -let rec fromNode = (mathJsNode: Parse.node): result => +let rec fromInnerNode = (mathJsNode: Parse.node): result => Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { let fromNodeList = (nodeList: list): result, 'e> => Belt.List.reduceReverse(nodeList, Ok(list{}), (racc, currNode) => racc->Result.flatMap(acc => - fromNode(currNode)->Result.map(currCode => list{currCode, ...acc}) + fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc}) ) ) - let toEvSymbolValue = (name: string): expression => - name->ExpressionValue.EvSymbol->ExpressionT.EValue - let caseFunctionNode = fNode => { - let lispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList - passToFunction(fNode->Parse.nameOfFunctionNode, lispArgs) + let rLispArgs = fNode["args"]->Belt.List.fromArray->fromNodeList + rLispArgs->Result.map(lispArgs => + ExpressionBuilder.eFunction(fNode->Parse.nameOfFunctionNode, lispArgs) + ) } let caseObjectNode = oNode => { @@ -49,19 +39,16 @@ let rec fromNode = (mathJsNode: Parse.node): result => (key: string, value: Parse.node), ) => racc->Result.flatMap(acc => - fromNode(value)->Result.map(valueExpression => { + fromInnerNode(value)->Result.map(valueExpression => { let entryCode = - list{ - key->ExpressionValue.EvString->ExpressionT.EValue, - valueExpression, - }->ExpressionT.EList + list{ExpressionBuilder.eString(key), valueExpression}->ExpressionT.EList list{entryCode, ...acc} }) ) ) rargs->Result.flatMap(args => - passToFunction("$constructRecord", list{ExpressionT.EList(args)}->Ok) - ) // $consturctRecord gets a single argument: List of key-value paiers + ExpressionBuilder.eFunction("$constructRecord", list{ExpressionT.EList(args)})->Ok + ) // $constructRecord gets a single argument: List of key-value paiers } oNode["properties"]->Js.Dict.entries->Belt.List.fromArray->fromObjectEntries @@ -73,7 +60,7 @@ let rec fromNode = (mathJsNode: Parse.node): result => Ok(list{}), (racc, currentPropertyMathJsNode) => racc->Result.flatMap(acc => - fromNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{ + fromInnerNode(currentPropertyMathJsNode)->Result.map(propertyCode => list{ propertyCode, ...acc, }) @@ -84,18 +71,41 @@ let rec fromNode = (mathJsNode: Parse.node): result => let caseAccessorNode = (objectNode, indexNode) => { caseIndexNode(indexNode)->Result.flatMap(indexCode => { - fromNode(objectNode)->Result.flatMap(objectCode => - passToFunction("$atIndex", list{objectCode, indexCode}->Ok) + fromInnerNode(objectNode)->Result.flatMap(objectCode => + ExpressionBuilder.eFunction("$atIndex", list{objectCode, indexCode})->Ok ) }) } + let caseBlock = (nodesArray: array): result => { + let rStatements: result, 'a> = + nodesArray + ->Belt.List.fromArray + ->Belt.List.reduceReverse(Ok(list{}), (racc, currNode) => + racc->Result.flatMap(acc => + fromInnerNode(currNode)->Result.map(currCode => list{currCode, ...acc}) + ) + ) + rStatements->Result.map(statements => ExpressionBuilder.eBlock(statements)) + } + let caseAssignmentNode = aNode => { - let symbol = aNode["object"]["name"]->toEvSymbolValue - let rValueExpression = fromNode(aNode["value"]) + let symbolName = aNode["object"]["name"] + let rValueExpression = fromInnerNode(aNode["value"]) + rValueExpression->Result.map(valueExpression => + ExpressionBuilder.eLetStatement(symbolName, valueExpression) + ) + } + + let caseFunctionAssignmentNode = faNode => { + let symbol = faNode["name"]->ExpressionBuilder.eSymbol + let rValueExpression = fromInnerNode(faNode["expr"]) + rValueExpression->Result.flatMap(valueExpression => { - let lispArgs = list{symbol, valueExpression}->Ok - passToFunction("$let", lispArgs) + let lispParams = ExpressionBuilder.eArrayString(faNode["params"]) + let valueBlock = ExpressionBuilder.eBlock(list{valueExpression}) + let lambda = ExpressionBuilder.eFunction("$$lambda", list{lispParams, valueBlock}) + ExpressionBuilder.eFunction("$let", list{symbol, lambda})->Ok }) } @@ -108,88 +118,22 @@ let rec fromNode = (mathJsNode: Parse.node): result => | MjArrayNode(aNode) => caseArrayNode(aNode) | MjAssignmentNode(aNode) => caseAssignmentNode(aNode) | MjSymbolNode(sNode) => { - let expr: expression = toEvSymbolValue(sNode["name"]) + let expr: expression = ExpressionBuilder.eSymbol(sNode["name"]) let rExpr: result = expr->Ok rExpr } - | MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes + | MjBlockNode(bNode) => bNode["blocks"]->Js.Array2.map(blockToNode)->caseBlock | MjConstantNode(cNode) => cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok) + | MjFunctionAssignmentNode(faNode) => caseFunctionAssignmentNode(faNode) | MjFunctionNode(fNode) => fNode->caseFunctionNode | MjIndexNode(iNode) => caseIndexNode(iNode) | MjObjectNode(oNode) => caseObjectNode(oNode) | MjOperatorNode(opNode) => opNode->Parse.castOperatorNodeToFunctionNode->caseFunctionNode - | MjParenthesisNode(pNode) => pNode["content"]->fromNode + | MjParenthesisNode(pNode) => pNode["content"]->fromInnerNode } rFinalExpression }) -and caseTagOrNodes = (tagOrNodes): result => { - let initialBindings = passToFunction("$$bindings", list{}->Ok) - let lastIndex = Belt.Array.length(tagOrNodes) - 1 - tagOrNodes->Belt.Array.reduceWithIndex(initialBindings, (rPreviousBindings, tagOrNode, i) => { - rPreviousBindings->Result.flatMap(previousBindings => { - let rStatement: result = switch tagOrNode { - | BlockNode(node) => fromNode(node) - | BlockTag(tag) => - switch tag { - | ImportVariablesStatement => passToFunction("$importVariablesStatement", list{}->Ok) - | ExportVariablesExpression => passToFunction("$exportVariablesExpression", list{}->Ok) - } - } - let bindName = if i == lastIndex { - "$$bindExpression" - } else { - "$$bindStatement" - } - - rStatement->Result.flatMap((statement: expression) => { - let lispArgs = list{previousBindings, statement}->Ok - passToFunction(bindName, lispArgs) - }) - }) - }) -} - -let fromPartialNode = (mathJsNode: Parse.node): result => { - Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { - let casePartialBlockNode = (bNode: Parse.blockNode) => { - let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode) - let completed = Js.Array2.concat(blocksOrTags, [BlockTag(ExportVariablesExpression)]) - completed->caseTagOrNodes - } - - let casePartialExpression = (node: Parse.node) => { - let completed = [BlockNode(node), BlockTag(ExportVariablesExpression)] - - completed->caseTagOrNodes - } - - let rFinalExpression: result = switch typedMathJsNode { - | MjBlockNode(bNode) => casePartialBlockNode(bNode) - | _ => casePartialExpression(mathJsNode) - } - rFinalExpression - }) -} - -let fromOuterNode = (mathJsNode: Parse.node): result => { - Parse.castNodeType(mathJsNode)->Result.flatMap(typedMathJsNode => { - let casePartialBlockNode = (bNode: Parse.blockNode) => { - let blocksOrTags = bNode["blocks"]->Belt.Array.map(toTagOrNode) - let completed = blocksOrTags - completed->caseTagOrNodes - } - - let casePartialExpression = (node: Parse.node) => { - let completed = [BlockNode(node)] - completed->caseTagOrNodes - } - - let rFinalExpression: result = switch typedMathJsNode { - | MjBlockNode(bNode) => casePartialBlockNode(bNode) - | _ => casePartialExpression(mathJsNode) - } - rFinalExpression - }) -} +let fromNode = (node: Parse.node): result => + fromInnerNode(node)->Result.map(expr => ExpressionBuilder.eBlock(list{expr})) diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res index 381cc654..5c9ee4b7 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExpressionValue.res @@ -5,37 +5,50 @@ module Extra_Array = Reducer_Extra_Array module ErrorValue = Reducer_ErrorValue +@genType.opaque +type internalCode = Object + @genType type rec expressionValue = | EvArray(array) + | EvArrayString(array) | EvBool(bool) | EvCall(string) // External function call | EvDistribution(DistributionTypes.genericDist) + | EvLambda(lambdaValue) | EvNumber(float) - | EvRecord(Js.Dict.t) + | EvRecord(record) | EvString(string) | EvSymbol(string) +and record = Js.Dict.t +and externalBindings = record +and lambdaValue = { + parameters: array, + context: externalBindings, + body: internalCode, +} @genType -type externalBindings = Js.Dict.t +let defaultExternalBindings: externalBindings = Js.Dict.empty() type functionCall = (string, array) let rec toString = aValue => switch aValue { + | EvArray(anArray) => { + let args = anArray->Js.Array2.map(each => toString(each))->Js.Array2.toString + `[${args}]` + } + | EvArrayString(anArray) => { + let args = anArray->Js.Array2.toString + `[${args}]` + } | EvBool(aBool) => Js.String.make(aBool) | EvCall(fName) => `:${fName}` + | EvLambda(lambdaValue) => `lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)` | EvNumber(aNumber) => Js.String.make(aNumber) | EvString(aString) => `'${aString}'` | EvSymbol(aString) => `:${aString}` - | EvArray(anArray) => { - let args = - anArray - ->Belt.Array.map(each => toString(each)) - ->Extra_Array.interperse(", ") - ->Js.String.concatMany("") - `[${args}]` - } | EvRecord(aRecord) => aRecord->toStringRecord | EvDistribution(dist) => GenericDist.toString(dist) } @@ -43,26 +56,27 @@ and toStringRecord = aRecord => { let pairs = aRecord ->Js.Dict.entries - ->Belt.Array.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`) - ->Extra_Array.interperse(", ") - ->Js.String.concatMany("") + ->Js.Array2.map(((eachKey, eachValue)) => `${eachKey}: ${toString(eachValue)}`) + ->Js.Array2.toString `{${pairs}}` } let toStringWithType = aValue => switch aValue { + | EvArray(_) => `Array::${toString(aValue)}` + | EvArrayString(_) => `ArrayString::${toString(aValue)}` | EvBool(_) => `Bool::${toString(aValue)}` | EvCall(_) => `Call::${toString(aValue)}` + | EvDistribution(_) => `Distribution::${toString(aValue)}` + | EvLambda(_) => `Lambda::${toString(aValue)}` | EvNumber(_) => `Number::${toString(aValue)}` + | EvRecord(_) => `Record::${toString(aValue)}` | EvString(_) => `String::${toString(aValue)}` | EvSymbol(_) => `Symbol::${toString(aValue)}` - | EvArray(_) => `Array::${toString(aValue)}` - | EvRecord(_) => `Record::${toString(aValue)}` - | EvDistribution(_) => `Distribution::${toString(aValue)}` } let argsToString = (args: array): string => { - args->Belt.Array.map(arg => arg->toString)->Extra_Array.interperse(", ")->Js.String.concatMany("") + args->Js.Array2.map(arg => arg->toString)->Js.Array2.toString } let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})` @@ -78,3 +92,9 @@ let toStringResultRecord = x => | Ok(a) => `Ok(${toStringRecord(a)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})` } + +@genType +type environment = DistributionOperation.env + +@genType +let defaultEnvironment: environment = DistributionOperation.defaultEnv diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res index 84d37d95..0bdb0748 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalLibrary.res @@ -14,8 +14,13 @@ type expressionValue = ExpressionValue.expressionValue Map external calls of Reducer */ -let dispatch = (call: ExpressionValue.functionCall, chain): result => - ReducerInterface_GenericDistribution.dispatch(call) |> E.O.default(chain(call)) +let dispatch = (call: ExpressionValue.functionCall, environment, chain): result< + expressionValue, + 'e, +> => + ReducerInterface_GenericDistribution.dispatch(call, environment) |> E.O.default( + chain(call, environment), + ) /* If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res index bf0510d6..542a54a2 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.res @@ -1,12 +1,12 @@ module ExpressionValue = ReducerInterface_ExpressionValue type expressionValue = ReducerInterface_ExpressionValue.expressionValue -let runGenericOperation = DistributionOperation.run( - ~env={ - sampleCount: MagicNumbers.Environment.defaultSampleCount, - xyPointLength: MagicNumbers.Environment.defaultXYPointLength, - }, -) +let defaultEnv: DistributionOperation.env = { + sampleCount: MagicNumbers.Environment.defaultSampleCount, + xyPointLength: MagicNumbers.Environment.defaultXYPointLength, +} + +let runGenericOperation = DistributionOperation.run(~env=defaultEnv) module Helpers = { let arithmeticMap = r => @@ -28,14 +28,13 @@ module Helpers = { let catchAndConvertTwoArgsToDists = (args: array): option<( DistributionTypes.genericDist, DistributionTypes.genericDist, - )> => { + )> => switch args { | [EvDistribution(a), EvDistribution(b)] => Some((a, b)) | [EvNumber(a), EvDistribution(b)] => Some((GenericDist.fromFloat(a), b)) | [EvDistribution(a), EvNumber(b)] => Some((a, GenericDist.fromFloat(b))) | _ => None } - } let toFloatFn = ( fnCall: DistributionTypes.DistributionOperation.toFloat, @@ -119,7 +118,7 @@ module Helpers = { mixtureWithGivenWeights(distributions, weights) } - let mixture = (args: array): DistributionOperation.outputType => { + let mixture = (args: array): DistributionOperation.outputType => switch E.A.last(args) { | Some(EvArray(b)) => { let weights = parseNumberArray(b) @@ -139,7 +138,6 @@ module Helpers = { } | _ => GenDistError(ArgumentError("Last argument of mx must be array or distribution")) } - } } module SymbolicConstructors = { @@ -175,7 +173,7 @@ module SymbolicConstructors = { } } -let dispatchToGenericOutput = (call: ExpressionValue.functionCall): option< +let dispatchToGenericOutput = (call: ExpressionValue.functionCall, _environment): option< DistributionOperation.outputType, > => { let (fnName, args) = call @@ -296,6 +294,6 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result< | GenDistError(err) => Error(REDistributionError(err)) } -let dispatch = call => { - dispatchToGenericOutput(call)->E.O2.fmap(genericOutputToReducerValue) +let dispatch = (call, 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 index fc7ebabc..038f4479 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_GenericDistribution.resi @@ -1,3 +1,5 @@ -let dispatch: ReducerInterface_ExpressionValue.functionCall => option< - result, -> +let defaultEnv: DistributionOperation.env +let dispatch: ( + ReducerInterface_ExpressionValue.functionCall, + ReducerInterface_ExpressionValue.environment, +) => option> diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index f2758ba3..6ebb8377 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -38,7 +38,7 @@ let makeSampleSetDist = SampleSetDist.make let evaluate = Reducer.evaluate @genType -let evaluateUsingExternalBindings = Reducer.evaluateUsingExternalBindings +let evaluateUsingOptions = Reducer.evaluateUsingOptions @genType let evaluatePartialUsingExternalBindings = Reducer.evaluatePartialUsingExternalBindings @@ -49,6 +49,9 @@ type externalBindings = Reducer.externalBindings @genType type expressionValue = ReducerInterface_ExpressionValue.expressionValue +@genType +type recordEV = ReducerInterface_ExpressionValue.record + @genType type errorValue = Reducer_ErrorValue.errorValue @@ -69,3 +72,15 @@ let errorValueToString = Reducer_ErrorValue.errorToString @genType let distributionErrorToString = DistributionTypes.Error.toString + +@genType +type lambdaValue = ReducerInterface_ExpressionValue.lambdaValue + +@genType +let defaultSamplingEnv = ReducerInterface_GenericDistribution.defaultEnv + +@genType +type environment = ReducerInterface_ExpressionValue.environment + +@genType +let defaultEnvironment = ReducerInterface_ExpressionValue.defaultEnvironment diff --git a/yarn.lock b/yarn.lock index 89928253..11bb1227 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4025,6 +4025,11 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/js-cookie@^2.2.6": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" + integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== + "@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -4070,10 +4075,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^17.0.29", "@types/node@^17.0.5": - version "17.0.30" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.30.tgz#2c6e8512acac70815e8176aa30c38025067880ef" - integrity sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw== +"@types/node@*", "@types/node@^17.0.31", "@types/node@^17.0.5": + version "17.0.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d" + integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q== "@types/node@^14.0.10": version "14.18.16" @@ -4721,6 +4726,11 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe" integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw== +"@xobotyi/scrollbar-width@^1.9.5": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" + integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -6991,6 +7001,14 @@ css-has-pseudo@^3.0.4: dependencies: postcss-selector-parser "^6.0.9" +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" @@ -7247,7 +7265,7 @@ csstype@^2.5.7: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda" integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA== -csstype@^3.0.2: +csstype@^3.0.2, csstype@^3.0.6: version "3.0.11" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== @@ -8662,6 +8680,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + fast-url-parser@1.1.3, fast-url-parser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" @@ -8674,6 +8697,11 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== +fastest-stable-stringify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" + integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -9881,6 +9909,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +hyphenate-style-name@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -10037,6 +10070,13 @@ inline-style-parser@0.1.1: resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== +inline-style-prefixer@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" + integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ== + dependencies: + css-in-js-utils "^2.0.0" + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -11163,6 +11203,11 @@ joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -12183,6 +12228,20 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== +nano-css@^5.3.1: + version "5.3.4" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.4.tgz#40af6a83a76f84204f346e8ccaa9169cdae9167b" + integrity sha512-wfcviJB6NOxDIDfr7RFn/GlaN7I/Bhe4d39ZRCJ3xvZX60LVe2qZ+rDqM49nm4YT81gAjzS+ZklhKP/Gnfnubg== + dependencies: + css-tree "^1.1.2" + csstype "^3.0.6" + fastest-stable-stringify "^2.0.2" + inline-style-prefixer "^6.0.0" + rtl-css-js "^1.14.0" + sourcemap-codec "^1.4.8" + stacktrace-js "^2.0.2" + stylis "^4.0.6" + nanoid@^3.1.23, nanoid@^3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -14778,6 +14837,31 @@ react-textarea-autosize@^8.3.0, react-textarea-autosize@^8.3.2: use-composed-ref "^1.0.0" use-latest "^1.0.0" +react-universal-interface@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" + integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== + +react-use@^17.3.2: + version "17.3.2" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.3.2.tgz#448abf515f47c41c32455024db28167cb6e53be8" + integrity sha512-bj7OD0/1wL03KyWmzFXAFe425zziuTf7q8olwCYBfOeFHY1qfO1FAMjROQLsLZYwG4Rx63xAfb7XAbBrJsZmEw== + dependencies: + "@types/js-cookie" "^2.2.6" + "@xobotyi/scrollbar-width" "^1.9.5" + copy-to-clipboard "^3.3.1" + fast-deep-equal "^3.1.3" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.3.1" + react-universal-interface "^0.6.2" + resize-observer-polyfill "^1.5.1" + screenfull "^5.1.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^3.0.1" + ts-easing "^0.2.0" + tslib "^2.1.0" + react-vega@^7.5.0: version "7.5.0" resolved "https://registry.yarnpkg.com/react-vega/-/react-vega-7.5.0.tgz#b9726d4fd7f35299d417d340935e093bf4bed558" @@ -15335,6 +15419,13 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +rtl-css-js@^1.14.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.15.0.tgz#680ed816e570a9ebccba9e1cd0f202c6a8bb2dc0" + integrity sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew== + dependencies: + "@babel/runtime" "^7.1.2" + rtl-detect@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.0.4.tgz#40ae0ea7302a150b96bc75af7d749607392ecac6" @@ -15496,6 +15587,11 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +screenfull@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" + integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== + scroll-into-view-if-needed@^2.2.25: version "2.2.29" resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885" @@ -15669,6 +15765,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -15900,6 +16001,11 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -16024,6 +16130,13 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-generator@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" + integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q== + dependencies: + stackframe "^1.1.1" + stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -16036,6 +16149,23 @@ stackframe@^1.1.1: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1" integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg== +stacktrace-gps@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a" + integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg== + dependencies: + source-map "0.5.6" + stackframe "^1.1.1" + +stacktrace-js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + state-toggle@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" @@ -16348,6 +16478,11 @@ stylehacks@^5.1.0: browserslist "^4.16.6" postcss-selector-parser "^6.0.4" +stylis@^4.0.6: + version "4.1.1" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.1.tgz#e46c6a9bbf7c58db1e65bb730be157311ae1fe12" + integrity sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ== + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -16792,6 +16927,11 @@ ts-dedent@^2.0.0: resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + ts-jest@^27.1.4: version "27.1.4" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" @@ -16806,10 +16946,10 @@ ts-jest@^27.1.4: semver "7.x" yargs-parser "20.x" -ts-loader@^9.2.8, ts-loader@^9.2.9: - version "9.2.9" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.9.tgz#0653e07fa1b4f225d0ca57a84fddbfd43d930f9e" - integrity sha512-b0+vUY2/enb0qYtDQuNlDnJ9900NTiPiJcDJ6sY7ax1CCCwXfYIqPOMm/BwW7jsF1km+Oz8W9s31HLuD+FLIMg== +ts-loader@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.0.tgz#980f4dbfb60e517179e15e10ed98e454b132159f" + integrity sha512-2kLLAdAD+FCKijvGKi9sS0OzoqxLCF3CxHpok7rVgCZ5UldRzH0TkbwG9XECKjBzHsAewntC5oDaI/FwKzEUog== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0"