diff --git a/packages/components/package.json b/packages/components/package.json index de8efb96..3b50afbf 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,19 +1,19 @@ { "name": "@quri/squiggle-components", - "version": "0.2.14", + "version": "0.2.15", "license": "MIT", "dependencies": { - "react-ace": "10.1.0", "@quri/squiggle-lang": "^0.2.7", - "react-dom": "^18.1.0", - "vega": "^5.22.1", - "vega-embed": "^6.20.6", - "vega-lite": "^5.2.0", - "react-vega": "^7.5.0", - "react": "^18.1.0", "@react-hook/size": "^2.1.2", "lodash": "^4.17.21", - "styled-components": "^5.3.5" + "react": "^18.1.0", + "react-ace": "10.1.0", + "react-dom": "^18.1.0", + "react-vega": "^7.5.0", + "styled-components": "^5.3.5", + "vega": "^5.22.1", + "vega-embed": "^6.20.6", + "vega-lite": "^5.2.0" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.16.7", @@ -25,27 +25,26 @@ "@storybook/node-logger": "^6.4.22", "@storybook/preset-create-react-app": "^4.1.0", "@storybook/react": "^6.4.22", - "@types/styled-components": "^5.1.24", - "@types/webpack": "^5.28.0", - "style-loader": "^3.3.1", - "ts-loader": "^9.2.9", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.8.1", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/user-event": "^14.1.1", "@types/jest": "^27.4.0", - "web-vitals": "^2.1.4", "@types/lodash": "^4.14.182", "@types/node": "^17.0.29", "@types/react": "^18.0.3", "@types/react-dom": "^18.0.2", + "@types/styled-components": "^5.1.24", + "@types/webpack": "^5.28.0", "cross-env": "^7.0.3", "react-scripts": "5.0.1", + "style-loader": "^3.3.1", + "ts-loader": "^9.2.9", "tsconfig-paths-webpack-plugin": "^3.5.2", "typescript": "^4.6.3", - "webpack-cli": "^4.9.2" + "web-vitals": "^2.1.4", + "webpack": "^5.72.0", + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.8.1" }, "scripts": { "start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public", diff --git a/packages/squiggle-lang/__tests__/TS/JS_test.ts b/packages/squiggle-lang/__tests__/TS/JS_test.ts index e522eb95..62051261 100644 --- a/packages/squiggle-lang/__tests__/TS/JS_test.ts +++ b/packages/squiggle-lang/__tests__/TS/JS_test.ts @@ -68,6 +68,29 @@ describe("Partials", () => { }); }); +describe("Parameters", () => { + test("Can pass parameters into partials and cells", () => { + let bindings = testRunPartial(`y = $x + 2`, {}, { x: 1 }); // y = 3 + let bindings2 = testRunPartial(`z = $x + y * $a`, bindings, { a: 3 }); // z = 1 + 3 * 3 = 10 + expect(testRun(`z + $x + $a + y`, bindings2)).toEqual({ + tag: "number", + value: 17, + }); + }); + test("Complicated deep parameters", () => { + expect( + testRun( + `$x.y[0][0].w + $x.z + $u.v`, + {}, + { x: { y: [[{ w: 1 }]], z: 2 }, u: { v: 3 } } + ) + ).toEqual({ + tag: "number", + value: 6, + }); + }); +}); + describe("Distribution", () => { //It's important that sampleCount is less than 9. If it's more, than that will create randomness //Also, note, the value should be created using makeSampleSetDist() later on. diff --git a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts index 7d51c98e..3bbf42d8 100644 --- a/packages/squiggle-lang/__tests__/TS/TestHelpers.ts +++ b/packages/squiggle-lang/__tests__/TS/TestHelpers.ts @@ -6,11 +6,20 @@ import { errorValueToString, } from "../../src/js/index"; -export function testRun(x: string, bindings = {}): squiggleExpression { - let squiggleResult = run(x, bindings, { - sampleCount: 1000, - xyPointLength: 100, - }); +export function testRun( + x: string, + bindings = {}, + parameters = {} +): squiggleExpression { + let squiggleResult = run( + x, + bindings, + { + sampleCount: 1000, + xyPointLength: 100, + }, + parameters + ); // return squiggleResult.value if (squiggleResult.tag === "Ok") { return squiggleResult.value; @@ -23,11 +32,20 @@ export function testRun(x: string, bindings = {}): squiggleExpression { } } -export function testRunPartial(x: string, bindings: bindings = {}): bindings { - let squiggleResult = runPartial(x, bindings, { - sampleCount: 1000, - xyPointLength: 100, - }); +export function testRunPartial( + x: string, + bindings: bindings = {}, + parameters = {} +): bindings { + let squiggleResult = runPartial( + x, + bindings, + { + sampleCount: 1000, + xyPointLength: 100, + }, + parameters + ); if (squiggleResult.tag === "Ok") { return squiggleResult.value; } else { diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index fb67e47a..cd63c922 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -99,25 +99,71 @@ export type squiggleExpression = export function run( squiggleString: string, bindings?: externalBindings, - samplingInputs?: samplingParams + samplingInputs?: samplingParams, + parameters?: parameters ): result { let b = bindings ? bindings : {}; + let p = parameters ? parameters : {}; let si: samplingParams = samplingInputs ? samplingInputs : defaultSamplingInputs; let result: result = - evaluateUsingExternalBindings(squiggleString, b); + evaluateUsingExternalBindings(squiggleString, mergeParameters(b, p)); return resultMap(result, (x) => createTsExport(x, si)); } // Run Partial. A partial is a block of code that doesn't return a value export function runPartial( squiggleString: string, - bindings: externalBindings, - _samplingInputs?: samplingParams + bindings?: externalBindings, + _samplingInputs?: samplingParams, + parameters?: parameters ): result { - return evaluatePartialUsingExternalBindings(squiggleString, bindings); + let b = bindings ? bindings : {}; + let p = parameters ? parameters : {}; + + return evaluatePartialUsingExternalBindings( + squiggleString, + mergeParameters(b, p) + ); +} + +function mergeParameters( + bindings: externalBindings, + parameters: parameters +): externalBindings { + let transformedParemeters = Object.fromEntries( + Object.entries(parameters).map(([key, value]) => [ + "$" + key, + jsValueToBinding(value), + ]) + ); + return _.merge(bindings, transformedParemeters); +} + +type parameters = { [key: string]: jsValue }; + +type jsValue = + | string + | number + | jsValue[] + | { [key: string]: jsValue } + | boolean; + +function jsValueToBinding(value: jsValue): rescriptExport { + if (typeof value === "boolean") { + return { TAG: 1, _0: value as boolean }; + } else if (typeof value === "string") { + return { TAG: 6, _0: value as string }; + } else if (typeof value === "number") { + return { TAG: 4, _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) }; + } } function createTsExport( diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res index ab9fb711..86a7a593 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Eval.res @@ -19,6 +19,7 @@ let eval__: string => 'a = %raw(`function (expr) { return {value: Mathjs.evaluat */ let eval = (expr: string): result => { try { + Js.log(expr) let answer = eval__(expr) answer["value"]->JavaScript.Gate.jsToEv } catch {