JS parameters in squiggle lang

This commit is contained in:
Sam Nolan 2022-04-29 18:46:44 +00:00
parent bb8ed5ce4f
commit d4f929367d
5 changed files with 120 additions and 33 deletions

View File

@ -1,19 +1,19 @@
{ {
"name": "@quri/squiggle-components", "name": "@quri/squiggle-components",
"version": "0.2.14", "version": "0.2.15",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"react-ace": "10.1.0",
"@quri/squiggle-lang": "^0.2.7", "@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", "@react-hook/size": "^2.1.2",
"lodash": "^4.17.21", "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": { "devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.16.7", "@babel/plugin-proposal-private-property-in-object": "^7.16.7",
@ -25,27 +25,26 @@
"@storybook/node-logger": "^6.4.22", "@storybook/node-logger": "^6.4.22",
"@storybook/preset-create-react-app": "^4.1.0", "@storybook/preset-create-react-app": "^4.1.0",
"@storybook/react": "^6.4.22", "@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/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1.1", "@testing-library/react": "^13.1.1",
"@testing-library/user-event": "^14.1.1", "@testing-library/user-event": "^14.1.1",
"@types/jest": "^27.4.0", "@types/jest": "^27.4.0",
"web-vitals": "^2.1.4",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.182",
"@types/node": "^17.0.29", "@types/node": "^17.0.29",
"@types/react": "^18.0.3", "@types/react": "^18.0.3",
"@types/react-dom": "^18.0.2", "@types/react-dom": "^18.0.2",
"@types/styled-components": "^5.1.24",
"@types/webpack": "^5.28.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"style-loader": "^3.3.1",
"ts-loader": "^9.2.9",
"tsconfig-paths-webpack-plugin": "^3.5.2", "tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.3", "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": { "scripts": {
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public", "start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",

View File

@ -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", () => { describe("Distribution", () => {
//It's important that sampleCount is less than 9. If it's more, than that will create randomness //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. //Also, note, the value should be created using makeSampleSetDist() later on.

View File

@ -6,11 +6,20 @@ import {
errorValueToString, errorValueToString,
} from "../../src/js/index"; } from "../../src/js/index";
export function testRun(x: string, bindings = {}): squiggleExpression { export function testRun(
let squiggleResult = run(x, bindings, { x: string,
sampleCount: 1000, bindings = {},
xyPointLength: 100, parameters = {}
}); ): squiggleExpression {
let squiggleResult = run(
x,
bindings,
{
sampleCount: 1000,
xyPointLength: 100,
},
parameters
);
// return squiggleResult.value // return squiggleResult.value
if (squiggleResult.tag === "Ok") { if (squiggleResult.tag === "Ok") {
return squiggleResult.value; return squiggleResult.value;
@ -23,11 +32,20 @@ export function testRun(x: string, bindings = {}): squiggleExpression {
} }
} }
export function testRunPartial(x: string, bindings: bindings = {}): bindings { export function testRunPartial(
let squiggleResult = runPartial(x, bindings, { x: string,
sampleCount: 1000, bindings: bindings = {},
xyPointLength: 100, parameters = {}
}); ): bindings {
let squiggleResult = runPartial(
x,
bindings,
{
sampleCount: 1000,
xyPointLength: 100,
},
parameters
);
if (squiggleResult.tag === "Ok") { if (squiggleResult.tag === "Ok") {
return squiggleResult.value; return squiggleResult.value;
} else { } else {

View File

@ -99,25 +99,71 @@ export type squiggleExpression =
export function run( export function run(
squiggleString: string, squiggleString: string,
bindings?: externalBindings, bindings?: externalBindings,
samplingInputs?: samplingParams samplingInputs?: samplingParams,
parameters?: parameters
): result<squiggleExpression, errorValue> { ): result<squiggleExpression, errorValue> {
let b = bindings ? bindings : {}; let b = bindings ? bindings : {};
let p = parameters ? parameters : {};
let si: samplingParams = samplingInputs let si: samplingParams = samplingInputs
? samplingInputs ? samplingInputs
: defaultSamplingInputs; : defaultSamplingInputs;
let result: result<expressionValue, errorValue> = let result: result<expressionValue, errorValue> =
evaluateUsingExternalBindings(squiggleString, b); evaluateUsingExternalBindings(squiggleString, mergeParameters(b, p));
return resultMap(result, (x) => createTsExport(x, si)); return resultMap(result, (x) => createTsExport(x, si));
} }
// Run Partial. A partial is a block of code that doesn't return a value // Run Partial. A partial is a block of code that doesn't return a value
export function runPartial( export function runPartial(
squiggleString: string, squiggleString: string,
bindings: externalBindings, bindings?: externalBindings,
_samplingInputs?: samplingParams _samplingInputs?: samplingParams,
parameters?: parameters
): result<externalBindings, errorValue> { ): result<externalBindings, errorValue> {
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( function createTsExport(

View File

@ -19,6 +19,7 @@ let eval__: string => 'a = %raw(`function (expr) { return {value: Mathjs.evaluat
*/ */
let eval = (expr: string): result<expressionValue, errorValue> => { let eval = (expr: string): result<expressionValue, errorValue> => {
try { try {
Js.log(expr)
let answer = eval__(expr) let answer = eval__(expr)
answer["value"]->JavaScript.Gate.jsToEv answer["value"]->JavaScript.Gate.jsToEv
} catch { } catch {