From 00f862725cdac0083a6130620ba8605eabf92b8f Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Thu, 28 Apr 2022 18:59:15 +0000 Subject: [PATCH 1/3] Add Dictionary Viewer and Recursive Array viewer --- .../src/components/SquiggleChart.tsx | 13 ++ packages/squiggle-lang/src/js/index.ts | 148 +++++++++++++++++- 2 files changed, 158 insertions(+), 3 deletions(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 534d164d..7dda92e4 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -44,6 +44,8 @@ export const VariableBox: React.FC<{ ); }; +let RecordKeyHeader = styled.h3``; + export interface SquiggleItemProps { /** The input string for squiggle */ expression: squiggleExpression; @@ -104,6 +106,17 @@ const SquiggleItem: React.FC = ({ ))} ); + case "record": + return ( + + {Object.entries(expression.value).map(([key, r]) => ( + <> + {key} + + + ))} + + ); default: return ( diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 6ff022e0..57c09e1a 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -50,6 +50,67 @@ export let defaultSamplingInputs: samplingParams = { xyPointLength: 10000, }; +import type { t as SampleSetDist_t } from "../rescript/Distributions/SampleSetDist/SampleSetDist.gen"; +import type { mixedShape } from "../rescript/Distributions/PointSetDist/PointSetTypes.gen"; + +import type { symbolicDist as SymbolicDistTypes_symbolicDist } from "../rescript/Distributions/SymbolicDist/SymbolicDistTypes.gen"; + +type rescriptPointSetDist = + | { + TAG: 0; // Mixed + _0: mixedShape; + } + | { + TAG: 1; // Discrete + _0: discreteShape; + } + | { + TAG: 2; // ContinuousShape + _0: continuousShape; + }; + +type rescriptDist = + | { TAG: 0; _0: rescriptPointSetDist } + | { TAG: 1; _0: SampleSetDist_t } + | { TAG: 2; _0: SymbolicDistTypes_symbolicDist }; + +// This is a raw rescript export. genType for some reason only converts half +// the data structure into the format it claims it is. This here is so that +// we can be guided in converting the other half +type rescriptExport = + | { + TAG: 0; // EvArray + _0: rescriptExport[]; + } + | { + TAG: 1; // EvBool + _0: boolean; + } + | { + TAG: 2; // EvCall + _0: string; + } + | { + TAG: 3; // EvDistribution + _0: rescriptDist; + } + | { + TAG: 4; // EvNumber + _0: number; + } + | { + TAG: 5; // EvRecord + _0: { [key: string]: rescriptExport }; + } + | { + TAG: 6; // EvString + _0: string; + } + | { + TAG: 7; // EvSymbol + _0: string; + }; + export type result = | { tag: "Ok"; @@ -90,6 +151,7 @@ export type squiggleExpression = | tagged<"distribution", Distribution> | tagged<"number", number> | tagged<"record", { [key: string]: squiggleExpression }>; + export function run( squiggleString: string, samplingInputs?: samplingParams @@ -101,6 +163,63 @@ export function run( return resultMap(result, (x) => createTsExport(x, si)); } +// Recript half converts recursive data structures +function convertRawToTypescript( + result: rescriptExport, + sampEnv: samplingParams +): squiggleExpression { + switch (result.TAG) { + case 0: // EvArray + return tag( + "array", + result._0.map((x) => convertRawToTypescript(x, sampEnv)) + ); + case 1: // EvBool + return tag("boolean", result._0); + case 2: // EvCall + return tag("call", result._0); + case 3: // EvDistribution + return tag( + "distribution", + new Distribution( + convertRawDistributionToGenericDist(result._0), + sampEnv + ) + ); + case 4: // EvNumber + return tag("number", result._0); + case 5: // EvRecord + return tag( + "record", + _.mapValues(result._0, (x) => convertRawToTypescript(x, sampEnv)) + ); + case 6: // EvString + return tag("string", result._0); + case 7: // EvSymbol + return tag("symbol", result._0); + } +} + +function convertRawDistributionToGenericDist( + result: rescriptDist +): genericDist { + switch (result.TAG) { + case 0: // Point Set Dist + switch (result._0.TAG) { + case 0: // Mixed + return tag("PointSet", tag("Mixed", result._0._0)); + case 1: // Discrete + return tag("PointSet", tag("Discrete", result._0._0)); + case 2: // Continuous + return tag("PointSet", tag("Continuous", result._0._0)); + } + case 1: // Sample Set Dist + return tag("SampleSet", result._0); + case 2: // Symbolic Dist + return tag("Symbolic", result._0); + } +} + function createTsExport( x: expressionValue, sampEnv: samplingParams @@ -109,7 +228,27 @@ function createTsExport( case "EvArray": return tag( "array", - x.value.map((x) => createTsExport(x, sampEnv)) + x.value.map((arrayItem): squiggleExpression => { + switch (arrayItem.tag) { + case "EvRecord": + return tag( + "record", + _.mapValues(arrayItem.value, (recordValue: unknown) => + convertRawToTypescript(recordValue as rescriptExport, sampEnv) + ) + ); + case "EvArray": + let y = arrayItem.value as unknown as rescriptExport[]; + return tag( + "array", + y.map((childArrayItem) => + convertRawToTypescript(childArrayItem, sampEnv) + ) + ); + default: + return createTsExport(arrayItem, sampEnv); + } + }) ); case "EvBool": return tag("boolean", x.value); @@ -120,10 +259,13 @@ function createTsExport( case "EvNumber": return tag("number", x.value); case "EvRecord": - return tag( + let result: tagged<"record", { [key: string]: squiggleExpression }> = tag( "record", - _.mapValues(x.value, (x) => createTsExport(x, sampEnv)) + _.mapValues(x.value, (x: unknown) => + convertRawToTypescript(x as rescriptExport, sampEnv) + ) ); + return result; case "EvString": return tag("string", x.value); case "EvSymbol": From e499c1a887299a0e7ba94fefb9af1820642b3cc2 Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Thu, 28 Apr 2022 20:16:31 +0000 Subject: [PATCH 2/3] Add tests for deeply nested and records --- .../squiggle-lang/__tests__/TS/JS_test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/squiggle-lang/__tests__/TS/JS_test.ts b/packages/squiggle-lang/__tests__/TS/JS_test.ts index 1974dee6..43f981eb 100644 --- a/packages/squiggle-lang/__tests__/TS/JS_test.ts +++ b/packages/squiggle-lang/__tests__/TS/JS_test.ts @@ -42,6 +42,39 @@ describe("Log function", () => { }); }); +describe("Array", () => { + test("nested Array", () => { + expect(testRun("[[1]]")).toEqual({ + tag: "array", + value: [ + { + tag: "array", + value: [ + { + tag: "number", + value: 1, + }, + ], + }, + ], + }); + }); +}); + +describe("Record", () => { + test("Return record", () => { + expect(testRun("{a: 1}")).toEqual({ + tag: "record", + value: { + a: { + tag: "number", + value: 1, + }, + }, + }); + }); +}); + 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. From b9c1f8d7272213a9243e39767dfe6c86ed455ded Mon Sep 17 00:00:00 2001 From: Sam Nolan Date: Thu, 28 Apr 2022 20:26:40 +0000 Subject: [PATCH 3/3] Move around declarations to be a bit more readable --- packages/squiggle-lang/src/js/index.ts | 233 +++++++++--------- .../src/rescript/TypescriptInterface.res | 6 + 2 files changed, 125 insertions(+), 114 deletions(-) diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 57c09e1a..bc66ab99 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -10,6 +10,9 @@ import { continuousShape, discreteShape, distributionErrorToString, + mixedShape, + sampleSetDist, + symbolicDist, } from "../rescript/TypescriptInterface.gen"; export { makeSampleSetDist, @@ -50,67 +53,6 @@ export let defaultSamplingInputs: samplingParams = { xyPointLength: 10000, }; -import type { t as SampleSetDist_t } from "../rescript/Distributions/SampleSetDist/SampleSetDist.gen"; -import type { mixedShape } from "../rescript/Distributions/PointSetDist/PointSetTypes.gen"; - -import type { symbolicDist as SymbolicDistTypes_symbolicDist } from "../rescript/Distributions/SymbolicDist/SymbolicDistTypes.gen"; - -type rescriptPointSetDist = - | { - TAG: 0; // Mixed - _0: mixedShape; - } - | { - TAG: 1; // Discrete - _0: discreteShape; - } - | { - TAG: 2; // ContinuousShape - _0: continuousShape; - }; - -type rescriptDist = - | { TAG: 0; _0: rescriptPointSetDist } - | { TAG: 1; _0: SampleSetDist_t } - | { TAG: 2; _0: SymbolicDistTypes_symbolicDist }; - -// This is a raw rescript export. genType for some reason only converts half -// the data structure into the format it claims it is. This here is so that -// we can be guided in converting the other half -type rescriptExport = - | { - TAG: 0; // EvArray - _0: rescriptExport[]; - } - | { - TAG: 1; // EvBool - _0: boolean; - } - | { - TAG: 2; // EvCall - _0: string; - } - | { - TAG: 3; // EvDistribution - _0: rescriptDist; - } - | { - TAG: 4; // EvNumber - _0: number; - } - | { - TAG: 5; // EvRecord - _0: { [key: string]: rescriptExport }; - } - | { - TAG: 6; // EvString - _0: string; - } - | { - TAG: 7; // EvSymbol - _0: string; - }; - export type result = | { tag: "Ok"; @@ -163,7 +105,69 @@ export function run( return resultMap(result, (x) => createTsExport(x, si)); } -// Recript half converts recursive data structures +function createTsExport( + x: expressionValue, + sampEnv: samplingParams +): squiggleExpression { + switch (x.tag) { + case "EvArray": + // genType doesn't convert anything more than 2 layers down into {tag: x, value: x} + // format, leaving it as the raw values. This converts the raw values + // directly into typescript values. + // + // The casting here is because genType is about the types of the returned + // values, claiming they are fully recursive when that's not actually the + // case + return tag( + "array", + x.value.map((arrayItem): squiggleExpression => { + switch (arrayItem.tag) { + case "EvRecord": + return tag( + "record", + _.mapValues(arrayItem.value, (recordValue: unknown) => + convertRawToTypescript(recordValue as rescriptExport, sampEnv) + ) + ); + case "EvArray": + let y = arrayItem.value as unknown as rescriptExport[]; + return tag( + "array", + y.map((childArrayItem) => + convertRawToTypescript(childArrayItem, sampEnv) + ) + ); + default: + return createTsExport(arrayItem, sampEnv); + } + }) + ); + case "EvBool": + return tag("boolean", x.value); + case "EvCall": + return tag("call", x.value); + case "EvDistribution": + return tag("distribution", new Distribution(x.value, sampEnv)); + case "EvNumber": + return tag("number", x.value); + case "EvRecord": + // genType doesn't support records, so we have to do the raw conversion ourself + let result: tagged<"record", { [key: string]: squiggleExpression }> = tag( + "record", + _.mapValues(x.value, (x: unknown) => + convertRawToTypescript(x as rescriptExport, sampEnv) + ) + ); + return result; + case "EvString": + return tag("string", x.value); + case "EvSymbol": + return tag("symbol", x.value); + } +} + +// Helper functions to convert the recsript representations that genType doesn't +// cover function convertRawToTypescript( result: rescriptExport, sampEnv: samplingParams @@ -220,58 +224,59 @@ function convertRawDistributionToGenericDist( } } -function createTsExport( - x: expressionValue, - sampEnv: samplingParams -): squiggleExpression { - switch (x.tag) { - case "EvArray": - return tag( - "array", - x.value.map((arrayItem): squiggleExpression => { - switch (arrayItem.tag) { - case "EvRecord": - return tag( - "record", - _.mapValues(arrayItem.value, (recordValue: unknown) => - convertRawToTypescript(recordValue as rescriptExport, sampEnv) - ) - ); - case "EvArray": - let y = arrayItem.value as unknown as rescriptExport[]; - return tag( - "array", - y.map((childArrayItem) => - convertRawToTypescript(childArrayItem, sampEnv) - ) - ); - default: - return createTsExport(arrayItem, sampEnv); - } - }) - ); - case "EvBool": - return tag("boolean", x.value); - case "EvCall": - return tag("call", x.value); - case "EvDistribution": - return tag("distribution", new Distribution(x.value, sampEnv)); - case "EvNumber": - return tag("number", x.value); - case "EvRecord": - let result: tagged<"record", { [key: string]: squiggleExpression }> = tag( - "record", - _.mapValues(x.value, (x: unknown) => - convertRawToTypescript(x as rescriptExport, sampEnv) - ) - ); - return result; - case "EvString": - return tag("string", x.value); - case "EvSymbol": - return tag("symbol", x.value); - } -} +// Raw rescript types. +type rescriptExport = + | { + TAG: 0; // EvArray + _0: rescriptExport[]; + } + | { + TAG: 1; // EvBool + _0: boolean; + } + | { + TAG: 2; // EvCall + _0: string; + } + | { + TAG: 3; // EvDistribution + _0: rescriptDist; + } + | { + TAG: 4; // EvNumber + _0: number; + } + | { + TAG: 5; // EvRecord + _0: { [key: string]: rescriptExport }; + } + | { + TAG: 6; // EvString + _0: string; + } + | { + TAG: 7; // EvSymbol + _0: string; + }; + +type rescriptDist = + | { TAG: 0; _0: rescriptPointSetDist } + | { TAG: 1; _0: sampleSetDist } + | { TAG: 2; _0: symbolicDist }; + +type rescriptPointSetDist = + | { + TAG: 0; // Mixed + _0: mixedShape; + } + | { + TAG: 1; // Discrete + _0: discreteShape; + } + | { + TAG: 2; // ContinuousShape + _0: continuousShape; + }; export function resultExn(r: result): a | c { return r.value; diff --git a/packages/squiggle-lang/src/rescript/TypescriptInterface.res b/packages/squiggle-lang/src/rescript/TypescriptInterface.res index fc381729..c4265178 100644 --- a/packages/squiggle-lang/src/rescript/TypescriptInterface.res +++ b/packages/squiggle-lang/src/rescript/TypescriptInterface.res @@ -13,6 +13,12 @@ type samplingParams = DistributionOperation.env @genType type genericDist = DistributionTypes.genericDist +@genType +type sampleSetDist = SampleSetDist.t + +@genType +type symbolicDist = SymbolicDistTypes.symbolicDist + @genType type distributionError = DistributionTypes.error