From d76c2f8ac7385f55a79aba7f9a283a96c873fa94 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 1 Sep 2022 17:26:11 +0400 Subject: [PATCH 1/4] new jsImports (WIP) --- .../src/components/SquiggleChart.tsx | 16 +++++++--- .../components/src/lib/hooks/useSquiggle.ts | 32 +++++++++++++++---- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index b935a932..c796b931 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -4,12 +4,19 @@ import { environment, defaultEnvironment, resultMap, - SqValueLocation, SqValueTag, } from "@quri/squiggle-lang"; import { useSquiggle } from "../lib/hooks"; import { SquiggleViewer } from "./SquiggleViewer"; +type jsImports = + | number + | string + | jsImports[] + | { + [k: string]: jsImports; + }; + export interface SquiggleChartProps { /** The input string for squiggle */ code?: string; @@ -31,7 +38,7 @@ export interface SquiggleChartProps { width?: number; height?: number; /** JS imported parameters */ - // jsImports?: jsImports; + jsImports?: jsImports; /** Whether to show a summary of the distribution */ showSummary?: boolean; /** Set the x scale to be logarithmic by deault */ @@ -54,6 +61,7 @@ export interface SquiggleChartProps { } const defaultOnChange = () => {}; +const defaultImports = {}; export const SquiggleChart: React.FC = React.memo( ({ @@ -62,7 +70,7 @@ export const SquiggleChart: React.FC = React.memo( environment, onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here height = 200, - // jsImports = defaultImports, + jsImports = defaultImports, showSummary = false, width, logX = false, @@ -81,7 +89,7 @@ export const SquiggleChart: React.FC = React.memo( const { result, bindings } = useSquiggle({ code, environment, - // jsImports, + jsImports, onChange, executionId, }); diff --git a/packages/components/src/lib/hooks/useSquiggle.ts b/packages/components/src/lib/hooks/useSquiggle.ts index 1086a684..1a6b3c18 100644 --- a/packages/components/src/lib/hooks/useSquiggle.ts +++ b/packages/components/src/lib/hooks/useSquiggle.ts @@ -1,10 +1,18 @@ -import { environment, run, SqValue } from "@quri/squiggle-lang"; +import { environment, SqProject, SqValue } from "@quri/squiggle-lang"; import { useEffect, useMemo } from "react"; +export type jsImports = + | number + | string + | jsImports[] + | { + [k: string]: jsImports; + }; + type SquiggleArgs = { code: string; executionId?: number; - // jsImports?: jsImports; + jsImports?: jsImports; environment?: environment; onChange?: (expr: SqValue | undefined) => void; }; @@ -12,10 +20,22 @@ type SquiggleArgs = { export const useSquiggle = (args: SquiggleArgs) => { const result = useMemo( () => { - const result = run(args.code, { - environment: args.environment, - }); - return result; + const project = SqProject.create(); + project.setSource("main", args.code); + if (args.environment) { + project.setEnvironment(args.environment); + } + if (args.jsImports) { + console.log(JSON.stringify(args.jsImports)); + project.setSource( + "zzz", // due to bug in topology implementation, can be renamed later + "imports = " + JSON.stringify(args.jsImports) + ); + } + project.run("main"); + const result = project.getResult("main"); + const bindings = project.getBindings("main"); + return { result, bindings }; }, // eslint-disable-next-line react-hooks/exhaustive-deps [ From 8842f7b25e9ccd8b64a84ee9d046a481b191b5e9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 1 Sep 2022 18:15:36 +0400 Subject: [PATCH 2/4] delete getDependents and getDependencies methods from TS API --- packages/squiggle-lang/src/js/SqProject.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/squiggle-lang/src/js/SqProject.ts b/packages/squiggle-lang/src/js/SqProject.ts index 7e607b9d..4c2dea7c 100644 --- a/packages/squiggle-lang/src/js/SqProject.ts +++ b/packages/squiggle-lang/src/js/SqProject.ts @@ -62,14 +62,6 @@ export class SqProject { return RSProject.setContinues(this._value, sourceId, continues); } - getDependencies(sourceId: string) { - return RSProject.getDependencies(this._value, sourceId); - } - - getDependents(sourceId: string) { - return RSProject.getDependents(this._value, sourceId); - } - getRunOrder() { return RSProject.getRunOrder(this._value); } From 64ed32a17c20702ea1eed7f5e5a9fa9aaa01dfff Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 1 Sep 2022 18:44:13 +0400 Subject: [PATCH 3/4] support jsImports --- .../src/components/SquiggleChart.tsx | 13 ++---- .../src/components/SquigglePlayground.tsx | 11 ++--- .../components/src/lib/hooks/useSquiggle.ts | 28 ++++--------- packages/components/src/lib/jsImports.ts | 41 +++++++++++++++++++ 4 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 packages/components/src/lib/jsImports.ts diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index c796b931..dfc909f9 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -8,14 +8,7 @@ import { } from "@quri/squiggle-lang"; import { useSquiggle } from "../lib/hooks"; import { SquiggleViewer } from "./SquiggleViewer"; - -type jsImports = - | number - | string - | jsImports[] - | { - [k: string]: jsImports; - }; +import { JsImports } from "../lib/jsImports"; export interface SquiggleChartProps { /** The input string for squiggle */ @@ -38,7 +31,7 @@ export interface SquiggleChartProps { width?: number; height?: number; /** JS imported parameters */ - jsImports?: jsImports; + jsImports?: JsImports; /** Whether to show a summary of the distribution */ showSummary?: boolean; /** Set the x scale to be logarithmic by deault */ @@ -61,7 +54,7 @@ export interface SquiggleChartProps { } const defaultOnChange = () => {}; -const defaultImports = {}; +const defaultImports: JsImports = {}; export const SquiggleChart: React.FC = React.memo( ({ diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index bf64642b..50eeb924 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -39,6 +39,7 @@ import { ViewSettings, viewSettingsSchema } from "./ViewSettings"; import { HeadedSection } from "./ui/HeadedSection"; import { defaultTickFormat } from "../lib/distributionSpecBuilder"; import { Button } from "./ui/Button"; +import { JsImports } from "../lib/jsImports"; type PlaygroundProps = SquiggleChartProps & { /** The initial squiggle string to put in the playground */ @@ -112,8 +113,8 @@ const SamplingSettings: React.FC<{ register: UseFormRegister }> = ({ ); const InputVariablesSettings: React.FC<{ - initialImports: any; // TODO - any json type - setImports: (imports: any) => void; + initialImports: JsImports; + setImports: (imports: JsImports) => void; }> = ({ initialImports, setImports }) => { const [importString, setImportString] = useState(() => JSON.stringify(initialImports) @@ -122,7 +123,7 @@ const InputVariablesSettings: React.FC<{ const onChange = (value: string) => { setImportString(value); - let imports = {} as any; + let imports = {}; try { imports = JSON.parse(value); setImportsAreValid(true); @@ -251,7 +252,7 @@ export const SquigglePlayground: FC = ({ onChange: onCodeChange, }); - const [imports, setImports] = useState({}); + const [imports, setImports] = useState({}); const { register, control } = useForm({ resolver: yupResolver(schema), @@ -309,7 +310,7 @@ export const SquigglePlayground: FC = ({ executionId={executionId} environment={env} {...vars} - // jsImports={imports} + jsImports={imports} enableLocalSettings={true} /> diff --git a/packages/components/src/lib/hooks/useSquiggle.ts b/packages/components/src/lib/hooks/useSquiggle.ts index 1a6b3c18..5916ac54 100644 --- a/packages/components/src/lib/hooks/useSquiggle.ts +++ b/packages/components/src/lib/hooks/useSquiggle.ts @@ -1,18 +1,11 @@ import { environment, SqProject, SqValue } from "@quri/squiggle-lang"; import { useEffect, useMemo } from "react"; - -export type jsImports = - | number - | string - | jsImports[] - | { - [k: string]: jsImports; - }; +import { JsImports, jsImportsToSquiggleCode } from "../jsImports"; type SquiggleArgs = { code: string; executionId?: number; - jsImports?: jsImports; + jsImports?: JsImports; environment?: environment; onChange?: (expr: SqValue | undefined) => void; }; @@ -25,12 +18,10 @@ export const useSquiggle = (args: SquiggleArgs) => { if (args.environment) { project.setEnvironment(args.environment); } - if (args.jsImports) { - console.log(JSON.stringify(args.jsImports)); - project.setSource( - "zzz", // due to bug in topology implementation, can be renamed later - "imports = " + JSON.stringify(args.jsImports) - ); + if (args.jsImports && Object.keys(args.jsImports).length) { + const importsSource = jsImportsToSquiggleCode(args.jsImports); + project.setSource("imports", importsSource); + project.setContinues("main", ["imports"]); } project.run("main"); const result = project.getResult("main"); @@ -38,12 +29,7 @@ export const useSquiggle = (args: SquiggleArgs) => { return { result, bindings }; }, // eslint-disable-next-line react-hooks/exhaustive-deps - [ - args.code, - args.environment, - // args.jsImports, - args.executionId, - ] + [args.code, args.environment, args.jsImports, args.executionId] ); const { onChange } = args; diff --git a/packages/components/src/lib/jsImports.ts b/packages/components/src/lib/jsImports.ts new file mode 100644 index 00000000..9a0820bb --- /dev/null +++ b/packages/components/src/lib/jsImports.ts @@ -0,0 +1,41 @@ +type JsImportsValue = + | number + | string + | JsImportsValue[] + | { + [k: string]: JsImportsValue; + }; + +export type JsImports = { + [k: string]: JsImportsValue; +}; + +const quote = (arg: string) => `"${arg.replace(new RegExp('"', "g"), '\\"')}"`; + +const jsImportsValueToSquiggleCode = (v: JsImportsValue): string => { + if (typeof v === "number") { + return String(v); + } else if (typeof v === "string") { + return quote(v); + } else if (v instanceof Array) { + return "[" + v.map((x) => jsImportsValueToSquiggleCode(x)) + "]"; + } else { + if (Object.keys(v).length) { + return ( + "{" + + Object.entries(v) + .map(([k, v]) => `${k}:${jsImportsValueToSquiggleCode(v)},`) + .join("") + + "}" + ); + } else { + return "0"; // squiggle doesn't support empty `{}` + } + } +}; + +export const jsImportsToSquiggleCode = (v: JsImports) => { + return Object.entries(v) + .map(([k, v]) => `$${k} = ${jsImportsValueToSquiggleCode(v)}\n`) + .join(""); +}; From e2abc532000961a0954efe3e39c8eff59094ee0c Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 1 Sep 2022 18:55:15 +0400 Subject: [PATCH 4/4] improve code generation for jsImports --- packages/components/src/lib/jsImports.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/components/src/lib/jsImports.ts b/packages/components/src/lib/jsImports.ts index 9a0820bb..dc11eb50 100644 --- a/packages/components/src/lib/jsImports.ts +++ b/packages/components/src/lib/jsImports.ts @@ -24,7 +24,7 @@ const jsImportsValueToSquiggleCode = (v: JsImportsValue): string => { return ( "{" + Object.entries(v) - .map(([k, v]) => `${k}:${jsImportsValueToSquiggleCode(v)},`) + .map(([k, v]) => `${quote(k)}:${jsImportsValueToSquiggleCode(v)},`) .join("") + "}" ); @@ -35,7 +35,17 @@ const jsImportsValueToSquiggleCode = (v: JsImportsValue): string => { }; export const jsImportsToSquiggleCode = (v: JsImports) => { - return Object.entries(v) - .map(([k, v]) => `$${k} = ${jsImportsValueToSquiggleCode(v)}\n`) + const validId = new RegExp("[a-zA-Z][[a-zA-Z0-9]*"); + let result = Object.entries(v) + .map(([k, v]) => { + if (!k.match(validId)) { + return ""; // skipping without warnings; can be improved + } + return `$${k} = ${jsImportsValueToSquiggleCode(v)}\n`; + }) .join(""); + if (!result) { + result = "$__no_valid_imports__ = 1"; // without this generated squiggle code can be invalid + } + return result; };