store location in values; render both result and bindings
This commit is contained in:
parent
ddfd4e0024
commit
a7bbfad94b
|
@ -71,7 +71,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
distributionChartActions,
|
distributionChartActions,
|
||||||
enableLocalSettings = false,
|
enableLocalSettings = false,
|
||||||
}) => {
|
}) => {
|
||||||
const result = useSquiggle({
|
const { result, bindings } = useSquiggle({
|
||||||
code,
|
code,
|
||||||
environment,
|
environment,
|
||||||
// jsImports,
|
// jsImports,
|
||||||
|
@ -98,15 +98,27 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquiggleViewer
|
<div>
|
||||||
result={result}
|
<SquiggleViewer
|
||||||
width={width}
|
result={result}
|
||||||
height={height}
|
width={width}
|
||||||
distributionPlotSettings={distributionPlotSettings}
|
height={height}
|
||||||
chartSettings={chartSettings}
|
distributionPlotSettings={distributionPlotSettings}
|
||||||
environment={environment ?? defaultEnvironment}
|
chartSettings={chartSettings}
|
||||||
enableLocalSettings={enableLocalSettings}
|
environment={environment ?? defaultEnvironment}
|
||||||
/>
|
enableLocalSettings={enableLocalSettings}
|
||||||
|
/>
|
||||||
|
<hr className="my-4" />
|
||||||
|
<SquiggleViewer
|
||||||
|
result={{ tag: "Ok", value: bindings.asValue() }}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
distributionPlotSettings={distributionPlotSettings}
|
||||||
|
chartSettings={chartSettings}
|
||||||
|
environment={environment ?? defaultEnvironment}
|
||||||
|
enableLocalSettings={enableLocalSettings}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,13 +37,18 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const VariableList: React.FC<{
|
const VariableList: React.FC<{
|
||||||
path: string[];
|
value: SqValue;
|
||||||
heading: string;
|
heading: string;
|
||||||
children: (settings: MergedItemSettings) => React.ReactNode;
|
children: (settings: MergedItemSettings) => React.ReactNode;
|
||||||
}> = ({ path, heading, children }) => (
|
}> = ({ value, heading, children }) => (
|
||||||
<VariableBox path={path} heading={heading}>
|
<VariableBox value={value} heading={heading}>
|
||||||
{(settings) => (
|
{(settings) => (
|
||||||
<div className={clsx("space-y-3", path.length ? "pt-1 mt-1" : null)}>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"space-y-3",
|
||||||
|
value.location.path.items.length ? "pt-1 mt-1" : null
|
||||||
|
)}
|
||||||
|
>
|
||||||
{children(settings)}
|
{children(settings)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -52,54 +57,41 @@ const VariableList: React.FC<{
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
/** The output of squiggle's run */
|
/** The output of squiggle's run */
|
||||||
expression: SqValue;
|
value: SqValue;
|
||||||
/** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */
|
|
||||||
path: string[];
|
|
||||||
width?: number;
|
width?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExpressionViewer: React.FC<Props> = ({
|
export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
||||||
path,
|
|
||||||
expression,
|
|
||||||
width,
|
|
||||||
}) => {
|
|
||||||
const { getMergedSettings } = useContext(ViewerContext);
|
const { getMergedSettings } = useContext(ViewerContext);
|
||||||
|
|
||||||
if (typeof expression !== "object") {
|
switch (value.tag) {
|
||||||
return (
|
|
||||||
<VariableList path={path} heading="Error">
|
|
||||||
{() => `Unknown expression: ${expression}`}
|
|
||||||
</VariableList>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
switch (expression.tag) {
|
|
||||||
case SqValueTag.Number:
|
case SqValueTag.Number:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Number">
|
<VariableBox value={value} heading="Number">
|
||||||
{() => (
|
{() => (
|
||||||
<div className="font-semibold text-slate-600">
|
<div className="font-semibold text-slate-600">
|
||||||
<NumberShower precision={3} number={expression.value} />
|
<NumberShower precision={3} number={value.value} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.Distribution: {
|
case SqValueTag.Distribution: {
|
||||||
const distType = expression.value.tag;
|
const distType = value.value.tag;
|
||||||
return (
|
return (
|
||||||
<VariableBox
|
<VariableBox
|
||||||
path={path}
|
value={value}
|
||||||
heading={`Distribution (${distType})\n${
|
heading={`Distribution (${distType})\n${
|
||||||
distType === SqDistributionTag.Symbolic
|
distType === SqDistributionTag.Symbolic
|
||||||
? expression.value.toString()
|
? value.value.toString()
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
renderSettingsMenu={({ onChange }) => {
|
renderSettingsMenu={({ onChange }) => {
|
||||||
const shape = expression.value.pointSet(
|
const shape = value.value.pointSet(
|
||||||
getMergedSettings(path).environment
|
getMergedSettings(value.location).environment
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ItemSettingsMenu
|
<ItemSettingsMenu
|
||||||
path={path}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disableLogX={
|
disableLogX={
|
||||||
shape.tag === "Ok" && hasMassBelowZero(shape.value.asShape())
|
shape.tag === "Ok" && hasMassBelowZero(shape.value.asShape())
|
||||||
|
@ -112,7 +104,7 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
{(settings) => {
|
{(settings) => {
|
||||||
return (
|
return (
|
||||||
<DistributionChart
|
<DistributionChart
|
||||||
plot={defaultPlot(expression.value)}
|
plot={defaultPlot(value.value)}
|
||||||
environment={settings.environment}
|
environment={settings.environment}
|
||||||
{...settings.distributionPlotSettings}
|
{...settings.distributionPlotSettings}
|
||||||
height={settings.height}
|
height={settings.height}
|
||||||
|
@ -125,12 +117,12 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
case SqValueTag.String:
|
case SqValueTag.String:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="String">
|
<VariableBox value={value} heading="String">
|
||||||
{() => (
|
{() => (
|
||||||
<>
|
<>
|
||||||
<span className="text-slate-400">"</span>
|
<span className="text-slate-400">"</span>
|
||||||
<span className="text-slate-600 font-semibold font-mono">
|
<span className="text-slate-600 font-semibold font-mono">
|
||||||
{expression.value}
|
{value.value}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-slate-400">"</span>
|
<span className="text-slate-400">"</span>
|
||||||
</>
|
</>
|
||||||
|
@ -139,61 +131,61 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
);
|
);
|
||||||
case SqValueTag.Bool:
|
case SqValueTag.Bool:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Boolean">
|
<VariableBox value={value} heading="Boolean">
|
||||||
{() => expression.value.toString()}
|
{() => value.value.toString()}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.Symbol:
|
case SqValueTag.Symbol:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Symbol">
|
<VariableBox value={value} heading="Symbol">
|
||||||
{() => (
|
{() => (
|
||||||
<>
|
<>
|
||||||
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
|
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
|
||||||
<span className="text-slate-600">{expression.value}</span>
|
<span className="text-slate-600">{value.value}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.Call:
|
case SqValueTag.Call:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Call">
|
<VariableBox value={value} heading="Call">
|
||||||
{() => expression.value}
|
{() => value.value}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.ArrayString:
|
case SqValueTag.ArrayString:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Array String">
|
<VariableBox value={value} heading="Array String">
|
||||||
{() => expression.value.map((r) => `"${r}"`).join(", ")}
|
{() => value.value.map((r) => `"${r}"`).join(", ")}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.Date:
|
case SqValueTag.Date:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Date">
|
<VariableBox value={value} heading="Date">
|
||||||
{() => expression.value.toDateString()}
|
{() => value.value.toDateString()}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.Void:
|
case SqValueTag.Void:
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Void">
|
<VariableBox value={value} heading="Void">
|
||||||
{() => "Void"}
|
{() => "Void"}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
case SqValueTag.TimeDuration: {
|
case SqValueTag.TimeDuration: {
|
||||||
return (
|
return (
|
||||||
<VariableBox path={path} heading="Time Duration">
|
<VariableBox value={value} heading="Time Duration">
|
||||||
{() => <NumberShower precision={3} number={expression.value} />}
|
{() => <NumberShower precision={3} number={value.value} />}
|
||||||
</VariableBox>
|
</VariableBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case SqValueTag.Lambda:
|
case SqValueTag.Lambda:
|
||||||
return (
|
return (
|
||||||
<VariableBox
|
<VariableBox
|
||||||
path={path}
|
value={value}
|
||||||
heading="Function"
|
heading="Function"
|
||||||
renderSettingsMenu={({ onChange }) => {
|
renderSettingsMenu={({ onChange }) => {
|
||||||
return (
|
return (
|
||||||
<ItemSettingsMenu
|
<ItemSettingsMenu
|
||||||
path={path}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
withFunctionSettings={true}
|
withFunctionSettings={true}
|
||||||
/>
|
/>
|
||||||
|
@ -202,11 +194,11 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
>
|
>
|
||||||
{(settings) => (
|
{(settings) => (
|
||||||
<>
|
<>
|
||||||
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value
|
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${value.value
|
||||||
.parameters()
|
.parameters()
|
||||||
.join(",")})`}</div>
|
.join(",")})`}</div>
|
||||||
<FunctionChart
|
<FunctionChart
|
||||||
fn={expression.value}
|
fn={value.value}
|
||||||
chartSettings={settings.chartSettings}
|
chartSettings={settings.chartSettings}
|
||||||
distributionPlotSettings={settings.distributionPlotSettings}
|
distributionPlotSettings={settings.distributionPlotSettings}
|
||||||
height={settings.height}
|
height={settings.height}
|
||||||
|
@ -222,13 +214,13 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
case SqValueTag.Declaration: {
|
case SqValueTag.Declaration: {
|
||||||
return (
|
return (
|
||||||
<VariableBox
|
<VariableBox
|
||||||
path={path}
|
value={value}
|
||||||
heading="Function Declaration"
|
heading="Function Declaration"
|
||||||
renderSettingsMenu={({ onChange }) => {
|
renderSettingsMenu={({ onChange }) => {
|
||||||
return (
|
return (
|
||||||
<ItemSettingsMenu
|
<ItemSettingsMenu
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
path={path}
|
value={value}
|
||||||
withFunctionSettings={true}
|
withFunctionSettings={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -252,16 +244,15 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
case SqValueTag.Module: {
|
case SqValueTag.Module: {
|
||||||
return (
|
return (
|
||||||
<VariableList path={path} heading="Module">
|
<VariableList value={value} heading="Module">
|
||||||
{(_) =>
|
{(_) =>
|
||||||
expression.value
|
value.value
|
||||||
.entries()
|
.entries()
|
||||||
.filter(([key, _]) => !key.match(/^(Math|System)\./))
|
.filter(([key, _]) => !key.match(/^(Math|System)\./))
|
||||||
.map(([key, r]) => (
|
.map(([key, r]) => (
|
||||||
<ExpressionViewer
|
<ExpressionViewer
|
||||||
key={key}
|
key={key}
|
||||||
path={[...path, key]}
|
value={r}
|
||||||
expression={r}
|
|
||||||
width={width !== undefined ? width - 20 : width}
|
width={width !== undefined ? width - 20 : width}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
@ -270,16 +261,16 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case SqValueTag.Record:
|
case SqValueTag.Record:
|
||||||
const plot = makePlot(expression.value);
|
const plot = makePlot(value.value);
|
||||||
if (plot) {
|
if (plot) {
|
||||||
return (
|
return (
|
||||||
<VariableBox
|
<VariableBox
|
||||||
path={path}
|
value={value}
|
||||||
heading="Plot"
|
heading="Plot"
|
||||||
renderSettingsMenu={({ onChange }) => {
|
renderSettingsMenu={({ onChange }) => {
|
||||||
let disableLogX = plot.distributions.some((x) => {
|
let disableLogX = plot.distributions.some((x) => {
|
||||||
let pointSet = x.distribution.pointSet(
|
let pointSet = x.distribution.pointSet(
|
||||||
getMergedSettings(path).environment
|
getMergedSettings(value.location).environment
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
pointSet.tag === "Ok" &&
|
pointSet.tag === "Ok" &&
|
||||||
|
@ -288,7 +279,7 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<ItemSettingsMenu
|
<ItemSettingsMenu
|
||||||
path={path}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disableLogX={disableLogX}
|
disableLogX={disableLogX}
|
||||||
withFunctionSettings={false}
|
withFunctionSettings={false}
|
||||||
|
@ -311,15 +302,14 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<VariableList path={path} heading="Record">
|
<VariableList value={value} heading="Record">
|
||||||
{(_) =>
|
{(_) =>
|
||||||
expression.value
|
value.value
|
||||||
.entries()
|
.entries()
|
||||||
.map(([key, r]) => (
|
.map(([key, r]) => (
|
||||||
<ExpressionViewer
|
<ExpressionViewer
|
||||||
key={key}
|
key={key}
|
||||||
path={[...path, key]}
|
value={r}
|
||||||
expression={r}
|
|
||||||
width={width !== undefined ? width - 20 : width}
|
width={width !== undefined ? width - 20 : width}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
@ -329,15 +319,14 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
case SqValueTag.Array:
|
case SqValueTag.Array:
|
||||||
return (
|
return (
|
||||||
<VariableList path={path} heading="Array">
|
<VariableList value={value} heading="Array">
|
||||||
{(_) =>
|
{(_) =>
|
||||||
expression.value
|
value.value
|
||||||
.getValues()
|
.getValues()
|
||||||
.map((r, i) => (
|
.map((r, i) => (
|
||||||
<ExpressionViewer
|
<ExpressionViewer
|
||||||
key={i}
|
key={i}
|
||||||
path={[...path, String(i)]}
|
value={r}
|
||||||
expression={r}
|
|
||||||
width={width !== undefined ? width - 20 : width}
|
width={width !== undefined ? width - 20 : width}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
@ -346,13 +335,11 @@ export const ExpressionViewer: React.FC<Props> = ({
|
||||||
);
|
);
|
||||||
default: {
|
default: {
|
||||||
return (
|
return (
|
||||||
<VariableList path={path} heading="Error">
|
<VariableList value={value} heading="Error">
|
||||||
{() => (
|
{() => (
|
||||||
<div>
|
<div>
|
||||||
<span>No display for type: </span>{" "}
|
<span>No display for type: </span>{" "}
|
||||||
<span className="font-semibold text-slate-600">
|
<span className="font-semibold text-slate-600">{value.tag}</span>
|
||||||
{expression.tag}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</VariableList>
|
</VariableList>
|
||||||
|
|
|
@ -4,13 +4,14 @@ import { useForm } from "react-hook-form";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { Modal } from "../ui/Modal";
|
import { Modal } from "../ui/Modal";
|
||||||
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
|
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
|
||||||
import { Path, pathAsString } from "./utils";
|
|
||||||
import { ViewerContext } from "./ViewerContext";
|
import { ViewerContext } from "./ViewerContext";
|
||||||
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
|
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
|
||||||
import { PlaygroundContext } from "../SquigglePlayground";
|
import { PlaygroundContext } from "../SquigglePlayground";
|
||||||
|
import { SqValue } from "@quri/squiggle-lang";
|
||||||
|
import { locationAsString } from "./utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
path: Path;
|
value: SqValue;
|
||||||
onChange: () => void;
|
onChange: () => void;
|
||||||
disableLogX?: boolean;
|
disableLogX?: boolean;
|
||||||
withFunctionSettings: boolean;
|
withFunctionSettings: boolean;
|
||||||
|
@ -19,7 +20,7 @@ type Props = {
|
||||||
const ItemSettingsModal: React.FC<
|
const ItemSettingsModal: React.FC<
|
||||||
Props & { close: () => void; resetScroll: () => void }
|
Props & { close: () => void; resetScroll: () => void }
|
||||||
> = ({
|
> = ({
|
||||||
path,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
disableLogX,
|
disableLogX,
|
||||||
withFunctionSettings,
|
withFunctionSettings,
|
||||||
|
@ -29,7 +30,7 @@ const ItemSettingsModal: React.FC<
|
||||||
const { setSettings, getSettings, getMergedSettings } =
|
const { setSettings, getSettings, getMergedSettings } =
|
||||||
useContext(ViewerContext);
|
useContext(ViewerContext);
|
||||||
|
|
||||||
const mergedSettings = getMergedSettings(path);
|
const mergedSettings = getMergedSettings(value.location);
|
||||||
|
|
||||||
const { register, watch } = useForm({
|
const { register, watch } = useForm({
|
||||||
resolver: yupResolver(viewSettingsSchema),
|
resolver: yupResolver(viewSettingsSchema),
|
||||||
|
@ -53,8 +54,8 @@ const ItemSettingsModal: React.FC<
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = watch((vars) => {
|
const subscription = watch((vars) => {
|
||||||
const settings = getSettings(path); // get the latest version
|
const settings = getSettings(value.location); // get the latest version
|
||||||
setSettings(path, {
|
setSettings(value.location, {
|
||||||
...settings,
|
...settings,
|
||||||
distributionPlotSettings: {
|
distributionPlotSettings: {
|
||||||
showSummary: vars.showSummary,
|
showSummary: vars.showSummary,
|
||||||
|
@ -75,7 +76,7 @@ const ItemSettingsModal: React.FC<
|
||||||
onChange();
|
onChange();
|
||||||
});
|
});
|
||||||
return () => subscription.unsubscribe();
|
return () => subscription.unsubscribe();
|
||||||
}, [getSettings, setSettings, onChange, path, watch]);
|
}, [getSettings, setSettings, onChange, value.location, watch]);
|
||||||
|
|
||||||
const { getLeftPanelElement } = useContext(PlaygroundContext);
|
const { getLeftPanelElement } = useContext(PlaygroundContext);
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ const ItemSettingsModal: React.FC<
|
||||||
<Modal container={getLeftPanelElement()} close={close}>
|
<Modal container={getLeftPanelElement()} close={close}>
|
||||||
<Modal.Header>
|
<Modal.Header>
|
||||||
Chart settings
|
Chart settings
|
||||||
{path.length ? (
|
{value.location.path.items.length ? (
|
||||||
<>
|
<>
|
||||||
{" for "}
|
{" for "}
|
||||||
<span
|
<span
|
||||||
|
@ -91,7 +92,7 @@ const ItemSettingsModal: React.FC<
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={resetScroll}
|
onClick={resetScroll}
|
||||||
>
|
>
|
||||||
{pathAsString(path)}
|
{locationAsString(value.location)}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -120,7 +121,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
|
||||||
if (!enableLocalSettings) {
|
if (!enableLocalSettings) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const settings = getSettings(props.path);
|
const settings = getSettings(props.value.location);
|
||||||
|
|
||||||
const resetScroll = () => {
|
const resetScroll = () => {
|
||||||
if (!ref.current) return;
|
if (!ref.current) return;
|
||||||
|
@ -139,7 +140,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
|
||||||
{settings.distributionPlotSettings || settings.chartSettings ? (
|
{settings.distributionPlotSettings || settings.chartSettings ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSettings(props.path, {
|
setSettings(props.value.location, {
|
||||||
...settings,
|
...settings,
|
||||||
distributionPlotSettings: undefined,
|
distributionPlotSettings: undefined,
|
||||||
chartSettings: undefined,
|
chartSettings: undefined,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SqValue, SqValueLocation } from "@quri/squiggle-lang";
|
||||||
import React, { useContext, useReducer } from "react";
|
import React, { useContext, useReducer } from "react";
|
||||||
import { Tooltip } from "../ui/Tooltip";
|
import { Tooltip } from "../ui/Tooltip";
|
||||||
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
||||||
|
@ -8,14 +9,14 @@ type SettingsMenuParams = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type VariableBoxProps = {
|
type VariableBoxProps = {
|
||||||
path: string[];
|
value: SqValue;
|
||||||
heading: string;
|
heading: string;
|
||||||
renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode;
|
renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode;
|
||||||
children: (settings: MergedItemSettings) => React.ReactNode;
|
children: (settings: MergedItemSettings) => React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const VariableBox: React.FC<VariableBoxProps> = ({
|
export const VariableBox: React.FC<VariableBoxProps> = ({
|
||||||
path,
|
value: { location },
|
||||||
heading = "Error",
|
heading = "Error",
|
||||||
renderSettingsMenu,
|
renderSettingsMenu,
|
||||||
children,
|
children,
|
||||||
|
@ -27,10 +28,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
|
||||||
// So we use `forceUpdate` to force rerendering.
|
// So we use `forceUpdate` to force rerendering.
|
||||||
const [_, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [_, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
const settings = getSettings(path);
|
const settings = getSettings(location);
|
||||||
|
|
||||||
const setSettingsAndUpdate = (newSettings: LocalItemSettings) => {
|
const setSettingsAndUpdate = (newSettings: LocalItemSettings) => {
|
||||||
setSettings(path, newSettings);
|
setSettings(location, newSettings);
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,8 +39,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
|
||||||
setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed });
|
setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed });
|
||||||
};
|
};
|
||||||
|
|
||||||
const isTopLevel = path.length === 0;
|
const isTopLevel = location.path.items.length === 0;
|
||||||
const name = isTopLevel ? "Result" : path[path.length - 1];
|
const name = isTopLevel
|
||||||
|
? { result: "Result", bindings: "Bindings" }[location.path.root]
|
||||||
|
: location.path.items[location.path.items.length - 1];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -65,13 +68,13 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
|
||||||
</header>
|
</header>
|
||||||
{settings.collapsed ? null : (
|
{settings.collapsed ? null : (
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
{path.length ? (
|
{location.path.items.length ? (
|
||||||
<div
|
<div
|
||||||
className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
|
className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
|
||||||
onClick={toggleCollapsed}
|
onClick={toggleCollapsed}
|
||||||
></div>
|
></div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="grow">{children(getMergedSettings(path))}</div>
|
<div className="grow">{children(getMergedSettings(location))}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { defaultEnvironment } from "@quri/squiggle-lang";
|
import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { LocalItemSettings, MergedItemSettings, Path } from "./utils";
|
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
||||||
|
|
||||||
type ViewerContextShape = {
|
type ViewerContextShape = {
|
||||||
// Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
|
// Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
|
||||||
// Instead, we keep settings in local state and notify the global context via setSettings to pass them down the component tree again if it got rebuilt from scratch.
|
// Instead, we keep settings in local state and notify the global context via setSettings to pass them down the component tree again if it got rebuilt from scratch.
|
||||||
// See ./SquiggleViewer.tsx and ./VariableBox.tsx for other implementation details on this.
|
// See ./SquiggleViewer.tsx and ./VariableBox.tsx for other implementation details on this.
|
||||||
getSettings(path: Path): LocalItemSettings;
|
getSettings(location: SqValueLocation): LocalItemSettings;
|
||||||
getMergedSettings(path: Path): MergedItemSettings;
|
getMergedSettings(location: SqValueLocation): MergedItemSettings;
|
||||||
setSettings(path: Path, value: LocalItemSettings): void;
|
setSettings(location: SqValueLocation, value: LocalItemSettings): void;
|
||||||
enableLocalSettings: boolean; // show local settings icon in the UI
|
enableLocalSettings: boolean; // show local settings icon in the UI
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
import React, { useCallback, useRef } from "react";
|
import React, { useCallback, useRef } from "react";
|
||||||
import { environment } from "@quri/squiggle-lang";
|
import { environment, SqValueLocation } from "@quri/squiggle-lang";
|
||||||
import { DistributionPlottingSettings } from "../DistributionChart";
|
import { DistributionPlottingSettings } from "../DistributionChart";
|
||||||
import { FunctionChartSettings } from "../FunctionChart";
|
import { FunctionChartSettings } from "../FunctionChart";
|
||||||
import { ExpressionViewer } from "./ExpressionViewer";
|
import { ExpressionViewer } from "./ExpressionViewer";
|
||||||
import { ViewerContext } from "./ViewerContext";
|
import { ViewerContext } from "./ViewerContext";
|
||||||
import {
|
import {
|
||||||
LocalItemSettings,
|
LocalItemSettings,
|
||||||
|
locationAsString,
|
||||||
MergedItemSettings,
|
MergedItemSettings,
|
||||||
Path,
|
|
||||||
pathAsString,
|
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { useSquiggle } from "../../lib/hooks";
|
import { useSquiggle } from "../../lib/hooks";
|
||||||
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
|
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** The output of squiggle's run */
|
/** The output of squiggle's run */
|
||||||
result: ReturnType<typeof useSquiggle>;
|
result: ReturnType<typeof useSquiggle>["result"];
|
||||||
width?: number;
|
width?: number;
|
||||||
height: number;
|
height: number;
|
||||||
distributionPlotSettings: DistributionPlottingSettings;
|
distributionPlotSettings: DistributionPlottingSettings;
|
||||||
|
@ -45,22 +44,22 @@ export const SquiggleViewer: React.FC<Props> = ({
|
||||||
const settingsRef = useRef<Settings>({});
|
const settingsRef = useRef<Settings>({});
|
||||||
|
|
||||||
const getSettings = useCallback(
|
const getSettings = useCallback(
|
||||||
(path: Path) => {
|
(location: SqValueLocation) => {
|
||||||
return settingsRef.current[pathAsString(path)] || defaultSettings;
|
return settingsRef.current[locationAsString(location)] || defaultSettings;
|
||||||
},
|
},
|
||||||
[settingsRef]
|
[settingsRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
const setSettings = useCallback(
|
const setSettings = useCallback(
|
||||||
(path: Path, value: LocalItemSettings) => {
|
(location: SqValueLocation, value: LocalItemSettings) => {
|
||||||
settingsRef.current[pathAsString(path)] = value;
|
settingsRef.current[locationAsString(location)] = value;
|
||||||
},
|
},
|
||||||
[settingsRef]
|
[settingsRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
const getMergedSettings = useCallback(
|
const getMergedSettings = useCallback(
|
||||||
(path: Path) => {
|
(location: SqValueLocation) => {
|
||||||
const localSettings = getSettings(path);
|
const localSettings = getSettings(location);
|
||||||
const result: MergedItemSettings = {
|
const result: MergedItemSettings = {
|
||||||
distributionPlotSettings: {
|
distributionPlotSettings: {
|
||||||
...distributionPlotSettings,
|
...distributionPlotSettings,
|
||||||
|
@ -91,7 +90,7 @@ export const SquiggleViewer: React.FC<Props> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{result.tag === "Ok" ? (
|
{result.tag === "Ok" ? (
|
||||||
<ExpressionViewer path={[]} expression={result.value} width={width} />
|
<ExpressionViewer value={result.value} width={width} />
|
||||||
) : (
|
) : (
|
||||||
<SquiggleErrorAlert error={result.value} />
|
<SquiggleErrorAlert error={result.value} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { DistributionPlottingSettings } from "../DistributionChart";
|
import { DistributionPlottingSettings } from "../DistributionChart";
|
||||||
import { FunctionChartSettings } from "../FunctionChart";
|
import { FunctionChartSettings } from "../FunctionChart";
|
||||||
import { environment } from "@quri/squiggle-lang";
|
import { environment, SqValueLocation } from "@quri/squiggle-lang";
|
||||||
|
|
||||||
export type LocalItemSettings = {
|
export type LocalItemSettings = {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
|
@ -17,6 +17,5 @@ export type MergedItemSettings = {
|
||||||
environment: environment;
|
environment: environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Path = string[];
|
export const locationAsString = (location: SqValueLocation) =>
|
||||||
|
location.path.root + "/" + location.path.items.join(".");
|
||||||
export const pathAsString = (path: Path) => path.join(".");
|
|
||||||
|
|
|
@ -18,12 +18,10 @@ type SquiggleArgs = {
|
||||||
export const useSquiggle = (args: SquiggleArgs) => {
|
export const useSquiggle = (args: SquiggleArgs) => {
|
||||||
const result = useMemo(
|
const result = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const { result, bindings } = run(args.code, {
|
const result = run(args.code, {
|
||||||
environment: args.environment,
|
environment: args.environment,
|
||||||
});
|
});
|
||||||
return resultMap(result, (v) =>
|
return result;
|
||||||
v.tag === SqValueTag.Void ? bindings.asValue() : v
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[
|
[
|
||||||
|
@ -37,10 +35,8 @@ export const useSquiggle = (args: SquiggleArgs) => {
|
||||||
const { onChange } = args;
|
const { onChange } = args;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange?.(result.tag === "Ok" ? result.value : undefined);
|
onChange?.(result.result.tag === "Ok" ? result.result.value : undefined);
|
||||||
}, [result, onChange]);
|
}, [result, onChange]);
|
||||||
|
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
|
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
|
||||||
import { wrapValue } from "./SqValue";
|
import { wrapValue } from "./SqValue";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
type T = RSArray.squiggleValue_Array;
|
type T = RSArray.squiggleValue_Array;
|
||||||
|
|
||||||
export class SqArray {
|
export class SqArray {
|
||||||
_value: T;
|
constructor(private _value: T, public location: SqValueLocation) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getValues() {
|
getValues() {
|
||||||
return RSArray.getValues(this._value).map(wrapValue);
|
return RSArray.getValues(this._value).map((v, i) =>
|
||||||
|
wrapValue(v, this.location.extend(i))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,14 @@ export const wrapDistribution = (value: T): SqDistribution => {
|
||||||
|
|
||||||
abstract class SqAbstractDistribution {
|
abstract class SqAbstractDistribution {
|
||||||
abstract tag: Tag;
|
abstract tag: Tag;
|
||||||
_value: T;
|
|
||||||
|
|
||||||
constructor(value: T) {
|
constructor(private _value: T) {}
|
||||||
this._value = value;
|
|
||||||
}
|
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
|
||||||
|
const value = rsMethod(this._value);
|
||||||
|
if (!value) throw new Error("Internal casting error");
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
pointSet(env: environment) {
|
pointSet(env: environment) {
|
||||||
const innerResult = RSDistribution.toPointSet(this._value, env);
|
const innerResult = RSDistribution.toPointSet(this._value, env);
|
||||||
|
@ -76,20 +79,11 @@ abstract class SqAbstractDistribution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueMethod = <IR>(
|
|
||||||
_this: SqAbstractDistribution,
|
|
||||||
rsMethod: (v: T) => IR | null | undefined
|
|
||||||
) => {
|
|
||||||
const value = rsMethod(_this._value);
|
|
||||||
if (!value) throw new Error("Internal casting error");
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SqPointSetDistribution extends SqAbstractDistribution {
|
export class SqPointSetDistribution extends SqAbstractDistribution {
|
||||||
tag = Tag.PointSet;
|
tag = Tag.PointSet;
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return valueMethod(this, RSDistribution.getPointSet);
|
return this.valueMethod(RSDistribution.getPointSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +91,7 @@ export class SqSampleSetDistribution extends SqAbstractDistribution {
|
||||||
tag = Tag.SampleSet;
|
tag = Tag.SampleSet;
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return valueMethod(this, RSDistribution.getSampleSet);
|
return this.valueMethod(RSDistribution.getSampleSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +99,7 @@ export class SqSymbolicDistribution extends SqAbstractDistribution {
|
||||||
tag = Tag.Symbolic;
|
tag = Tag.Symbolic;
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return valueMethod(this, RSDistribution.getSymbolic);
|
return this.valueMethod(RSDistribution.getSymbolic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,7 @@ import * as RSDistributionError from "../rescript/ForTS/ForTS_Distribution/ForTS
|
||||||
type T = RSDistributionError.distributionError;
|
type T = RSDistributionError.distributionError;
|
||||||
|
|
||||||
export class SqDistributionError {
|
export class SqDistributionError {
|
||||||
_value: T;
|
constructor(private _value: T) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return RSDistributionError.toString(this._value);
|
return RSDistributionError.toString(this._value);
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
|
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
|
||||||
|
|
||||||
export class SqError {
|
export class SqError {
|
||||||
_value: RSErrorValue.reducerErrorValue;
|
constructor(private _value: RSErrorValue.reducerErrorValue) {}
|
||||||
|
|
||||||
constructor(_value: RSErrorValue.reducerErrorValue) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return RSErrorValue.toString(this._value);
|
return RSErrorValue.toString(this._value);
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
|
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
type T = RSLambda.squiggleValue_Lambda;
|
type T = RSLambda.squiggleValue_Lambda;
|
||||||
|
|
||||||
export class SqLambda {
|
export class SqLambda {
|
||||||
_value: T;
|
constructor(private _value: T, public location: SqValueLocation) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters() {
|
parameters() {
|
||||||
return RSLambda.parameters(this._value);
|
return RSLambda.parameters(this._value);
|
||||||
|
|
|
@ -3,9 +3,5 @@ import * as RSDeclaration from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_Squi
|
||||||
type T = RSDeclaration.squiggleValue_Declaration;
|
type T = RSDeclaration.squiggleValue_Declaration;
|
||||||
|
|
||||||
export class SqLambdaDeclaration {
|
export class SqLambdaDeclaration {
|
||||||
_value: T;
|
constructor(private _value: T) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
|
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
|
||||||
import { SqModuleValue, wrapValue } from "./SqValue";
|
import { SqModuleValue, wrapValue } from "./SqValue";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
export class SqModule {
|
export class SqModule {
|
||||||
_value: RSModuleValue.squiggleValue_Module;
|
constructor(
|
||||||
|
private _value: RSModuleValue.squiggleValue_Module,
|
||||||
constructor(_value: RSModuleValue.squiggleValue_Module) {
|
public location: SqValueLocation
|
||||||
this._value = _value;
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
entries() {
|
entries() {
|
||||||
return RSModuleValue.getKeyValuePairs(this._value).map(
|
return RSModuleValue.getKeyValuePairs(this._value).map(
|
||||||
([k, v]) => [k, wrapValue(v)] as const
|
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
asValue() {
|
asValue() {
|
||||||
return new SqModuleValue(RSModuleValue.toSquiggleValue(this._value));
|
return new SqModuleValue(
|
||||||
|
RSModuleValue.toSquiggleValue(this._value),
|
||||||
|
this.location
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,29 +25,22 @@ export const wrapPointSetDist = (value: T) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract class SqAbstractPointSetDist {
|
abstract class SqAbstractPointSetDist {
|
||||||
_value: T;
|
constructor(private _value: T) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract asShape(): SqShape;
|
abstract asShape(): SqShape;
|
||||||
}
|
|
||||||
|
|
||||||
const valueMethod = <IR>(
|
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
|
||||||
_this: SqAbstractPointSetDist,
|
const value = rsMethod(this._value);
|
||||||
rsMethod: (v: T) => IR | null | undefined
|
if (!value) throw new Error("Internal casting error");
|
||||||
) => {
|
return value;
|
||||||
const value = rsMethod(_this._value);
|
};
|
||||||
if (!value) throw new Error("Internal casting error");
|
}
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SqMixedPointSetDist extends SqAbstractPointSetDist {
|
export class SqMixedPointSetDist extends SqAbstractPointSetDist {
|
||||||
tag = Tag.Mixed as const;
|
tag = Tag.Mixed as const;
|
||||||
|
|
||||||
get value(): RSPointSetDist.mixedShape {
|
get value(): RSPointSetDist.mixedShape {
|
||||||
return valueMethod(this, RSPointSetDist.getMixed);
|
return this.valueMethod(RSPointSetDist.getMixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
asShape() {
|
asShape() {
|
||||||
|
@ -63,7 +56,7 @@ export class SqDiscretePointSetDist extends SqAbstractPointSetDist {
|
||||||
tag = Tag.Discrete as const;
|
tag = Tag.Discrete as const;
|
||||||
|
|
||||||
get value(): RSPointSetDist.discreteShape {
|
get value(): RSPointSetDist.discreteShape {
|
||||||
return valueMethod(this, RSPointSetDist.getDiscrete);
|
return this.valueMethod(RSPointSetDist.getDiscrete);
|
||||||
}
|
}
|
||||||
|
|
||||||
asShape() {
|
asShape() {
|
||||||
|
@ -79,7 +72,7 @@ export class SqContinuousPointSetDist extends SqAbstractPointSetDist {
|
||||||
tag = Tag.Continuous as const;
|
tag = Tag.Continuous as const;
|
||||||
|
|
||||||
get value(): RSPointSetDist.continuousShape {
|
get value(): RSPointSetDist.continuousShape {
|
||||||
return valueMethod(this, RSPointSetDist.getContinues);
|
return this.valueMethod(RSPointSetDist.getContinues);
|
||||||
}
|
}
|
||||||
|
|
||||||
asShape() {
|
asShape() {
|
||||||
|
|
|
@ -5,13 +5,10 @@ import { SqError } from "./SqError";
|
||||||
import { SqModule } from "./SqModule";
|
import { SqModule } from "./SqModule";
|
||||||
import { wrapValue } from "./SqValue";
|
import { wrapValue } from "./SqValue";
|
||||||
import { resultMap2 } from "./types";
|
import { resultMap2 } from "./types";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
export class SqProject {
|
export class SqProject {
|
||||||
_value: RSProject.reducerProject;
|
constructor(private _value: RSProject.reducerProject) {}
|
||||||
|
|
||||||
constructor(_value: RSProject.reducerProject) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static create() {
|
static create() {
|
||||||
return new SqProject(RSProject.createProject());
|
return new SqProject(RSProject.createProject());
|
||||||
|
@ -94,14 +91,27 @@ export class SqProject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getBindings(sourceId: string) {
|
getBindings(sourceId: string) {
|
||||||
return new SqModule(RSProject.getBindings(this._value, sourceId));
|
return new SqModule(
|
||||||
|
RSProject.getBindings(this._value, sourceId),
|
||||||
|
new SqValueLocation(this, sourceId, {
|
||||||
|
root: "bindings",
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getResult(sourceId: string) {
|
getResult(sourceId: string) {
|
||||||
const innerResult = RSProject.getResult(this._value, sourceId);
|
const innerResult = RSProject.getResult(this._value, sourceId);
|
||||||
return resultMap2(
|
return resultMap2(
|
||||||
innerResult,
|
innerResult,
|
||||||
wrapValue,
|
(v) =>
|
||||||
|
wrapValue(
|
||||||
|
v,
|
||||||
|
new SqValueLocation(this, sourceId, {
|
||||||
|
root: "result",
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
),
|
||||||
(v: reducerErrorValue) => new SqError(v)
|
(v: reducerErrorValue) => new SqError(v)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
|
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
|
||||||
import { wrapValue } from "./SqValue";
|
import { wrapValue } from "./SqValue";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
type T = RSRecord.squiggleValue_Record;
|
type T = RSRecord.squiggleValue_Record;
|
||||||
|
|
||||||
export class SqRecord {
|
export class SqRecord {
|
||||||
_value: T;
|
constructor(private _value: T, public location: SqValueLocation) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
entries() {
|
entries() {
|
||||||
return RSRecord.getKeyValuePairs(this._value).map(
|
return RSRecord.getKeyValuePairs(this._value).map(
|
||||||
([k, v]) => [k, wrapValue(v)] as const
|
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,5 @@ import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleVal
|
||||||
type T = RSType.squiggleValue_Type;
|
type T = RSType.squiggleValue_Type;
|
||||||
|
|
||||||
export class SqType {
|
export class SqType {
|
||||||
_value: T;
|
constructor(private _value: T) {}
|
||||||
|
|
||||||
constructor(_value: T) {
|
|
||||||
this._value = _value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,42 +7,38 @@ import { SqModule } from "./SqModule";
|
||||||
import { SqRecord } from "./SqRecord";
|
import { SqRecord } from "./SqRecord";
|
||||||
import { SqArray } from "./SqArray";
|
import { SqArray } from "./SqArray";
|
||||||
import { SqType } from "./SqType";
|
import { SqType } from "./SqType";
|
||||||
|
import { SqProject } from "./SqProject";
|
||||||
|
import { SqValueLocation } from "./SqValueLocation";
|
||||||
|
|
||||||
export { Tag as SqValueTag };
|
export { Tag as SqValueTag };
|
||||||
|
|
||||||
type T = RSValue.squiggleValue;
|
type T = RSValue.squiggleValue;
|
||||||
|
|
||||||
export const wrapValue = (value: T): SqValue => {
|
export const wrapValue = (value: T, location: SqValueLocation): SqValue => {
|
||||||
const tag = RSValue.getTag(value);
|
const tag = RSValue.getTag(value);
|
||||||
|
|
||||||
return new tagToClass[tag](value);
|
return new tagToClass[tag](value, location);
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class SqAbstractValue {
|
export abstract class SqAbstractValue {
|
||||||
abstract tag: Tag;
|
abstract tag: Tag;
|
||||||
_value: T;
|
|
||||||
|
|
||||||
constructor(value: T) {
|
constructor(private _value: T, public location: SqValueLocation) {}
|
||||||
this._value = value;
|
|
||||||
}
|
protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
|
||||||
|
const value = rsMethod(this._value);
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
throw new Error("Internal casting error");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueMethod = <IR>(
|
|
||||||
_this: SqAbstractValue,
|
|
||||||
rsMethod: (v: T) => IR | null | undefined
|
|
||||||
) => {
|
|
||||||
const value = rsMethod(_this._value);
|
|
||||||
if (value === undefined || value === null) {
|
|
||||||
throw new Error("Internal casting error");
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SqArrayValue extends SqAbstractValue {
|
export class SqArrayValue extends SqAbstractValue {
|
||||||
tag = Tag.Array as const;
|
tag = Tag.Array as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqArray(valueMethod(this, RSValue.getArray));
|
return new SqArray(this.valueMethod(RSValue.getArray), this.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +46,7 @@ export class SqArrayStringValue extends SqAbstractValue {
|
||||||
tag = Tag.ArrayString as const;
|
tag = Tag.ArrayString as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getArrayString);
|
return this.valueMethod(RSValue.getArrayString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +54,7 @@ export class SqBoolValue extends SqAbstractValue {
|
||||||
tag = Tag.Bool as const;
|
tag = Tag.Bool as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getBool);
|
return this.valueMethod(RSValue.getBool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +62,7 @@ export class SqCallValue extends SqAbstractValue {
|
||||||
tag = Tag.Call as const;
|
tag = Tag.Call as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getCall);
|
return this.valueMethod(RSValue.getCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +70,7 @@ export class SqDateValue extends SqAbstractValue {
|
||||||
tag = Tag.Date as const;
|
tag = Tag.Date as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getDate);
|
return this.valueMethod(RSValue.getDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +78,7 @@ export class SqDeclarationValue extends SqAbstractValue {
|
||||||
tag = Tag.Declaration as const;
|
tag = Tag.Declaration as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqLambdaDeclaration(valueMethod(this, RSValue.getDeclaration));
|
return new SqLambdaDeclaration(this.valueMethod(RSValue.getDeclaration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +86,7 @@ export class SqDistributionValue extends SqAbstractValue {
|
||||||
tag = Tag.Distribution as const;
|
tag = Tag.Distribution as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return wrapDistribution(valueMethod(this, RSValue.getDistribution));
|
return wrapDistribution(this.valueMethod(RSValue.getDistribution));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +94,7 @@ export class SqLambdaValue extends SqAbstractValue {
|
||||||
tag = Tag.Lambda as const;
|
tag = Tag.Lambda as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqLambda(valueMethod(this, RSValue.getLambda));
|
return new SqLambda(this.valueMethod(RSValue.getLambda), this.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +102,7 @@ export class SqModuleValue extends SqAbstractValue {
|
||||||
tag = Tag.Module as const;
|
tag = Tag.Module as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqModule(valueMethod(this, RSValue.getModule));
|
return new SqModule(this.valueMethod(RSValue.getModule), this.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +110,7 @@ export class SqNumberValue extends SqAbstractValue {
|
||||||
tag = Tag.Number as const;
|
tag = Tag.Number as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getNumber);
|
return this.valueMethod(RSValue.getNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +118,7 @@ export class SqRecordValue extends SqAbstractValue {
|
||||||
tag = Tag.Record as const;
|
tag = Tag.Record as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqRecord(valueMethod(this, RSValue.getRecord));
|
return new SqRecord(this.valueMethod(RSValue.getRecord), this.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +126,7 @@ export class SqStringValue extends SqAbstractValue {
|
||||||
tag = Tag.String as const;
|
tag = Tag.String as const;
|
||||||
|
|
||||||
get value(): string {
|
get value(): string {
|
||||||
return valueMethod(this, RSValue.getString);
|
return this.valueMethod(RSValue.getString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +134,7 @@ export class SqSymbolValue extends SqAbstractValue {
|
||||||
tag = Tag.Symbol as const;
|
tag = Tag.Symbol as const;
|
||||||
|
|
||||||
get value(): string {
|
get value(): string {
|
||||||
return valueMethod(this, RSValue.getSymbol);
|
return this.valueMethod(RSValue.getSymbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +142,7 @@ export class SqTimeDurationValue extends SqAbstractValue {
|
||||||
tag = Tag.TimeDuration as const;
|
tag = Tag.TimeDuration as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getTimeDuration);
|
return this.valueMethod(RSValue.getTimeDuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +150,7 @@ export class SqTypeValue extends SqAbstractValue {
|
||||||
tag = Tag.Type as const;
|
tag = Tag.Type as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return new SqType(valueMethod(this, RSValue.getType));
|
return new SqType(this.valueMethod(RSValue.getType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +158,7 @@ export class SqTypeIdentifierValue extends SqAbstractValue {
|
||||||
tag = Tag.TypeIdentifier as const;
|
tag = Tag.TypeIdentifier as const;
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
return valueMethod(this, RSValue.getTypeIdentifier);
|
return this.valueMethod(RSValue.getTypeIdentifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
packages/squiggle-lang/src/js/SqValueLocation.ts
Normal file
24
packages/squiggle-lang/src/js/SqValueLocation.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { isParenthesisNode } from "mathjs";
|
||||||
|
import { SqProject } from "./SqProject";
|
||||||
|
|
||||||
|
type PathItem = string | number;
|
||||||
|
|
||||||
|
type SqValuePath = {
|
||||||
|
root: "result" | "bindings";
|
||||||
|
items: PathItem[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SqValueLocation {
|
||||||
|
constructor(
|
||||||
|
public project: SqProject,
|
||||||
|
public sourceId: string,
|
||||||
|
public path: SqValuePath
|
||||||
|
) {}
|
||||||
|
|
||||||
|
extend(item: PathItem) {
|
||||||
|
return new SqValueLocation(this.project, this.sourceId, {
|
||||||
|
root: this.path.root,
|
||||||
|
items: [...this.path.items, item],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
|
import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
|
||||||
import { SqProject } from "./SqProject";
|
import { SqProject } from "./SqProject";
|
||||||
import { SqValue, SqValueTag } from "./SqValue";
|
import { SqValue, SqValueTag } from "./SqValue";
|
||||||
|
export { SqValueLocation } from "./SqValueLocation";
|
||||||
export { result } from "../rescript/ForTS/ForTS_Result_tag";
|
export { result } from "../rescript/ForTS/ForTS_Result_tag";
|
||||||
export { SqDistribution, SqDistributionTag } from "./SqDistribution";
|
export { SqDistribution, SqDistributionTag } from "./SqDistribution";
|
||||||
export { SqDistributionError } from "./SqDistributionError";
|
export { SqDistributionError } from "./SqDistributionError";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user