store location in values; render both result and bindings

This commit is contained in:
Vyacheslav Matyukhin 2022-08-30 01:51:44 +04:00
parent ddfd4e0024
commit a7bbfad94b
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
22 changed files with 238 additions and 243 deletions

View File

@ -71,7 +71,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
distributionChartActions,
enableLocalSettings = false,
}) => {
const result = useSquiggle({
const { result, bindings } = useSquiggle({
code,
environment,
// jsImports,
@ -98,15 +98,27 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
};
return (
<SquiggleViewer
result={result}
width={width}
height={height}
distributionPlotSettings={distributionPlotSettings}
chartSettings={chartSettings}
environment={environment ?? defaultEnvironment}
enableLocalSettings={enableLocalSettings}
/>
<div>
<SquiggleViewer
result={result}
width={width}
height={height}
distributionPlotSettings={distributionPlotSettings}
chartSettings={chartSettings}
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>
);
}
);

View File

@ -37,13 +37,18 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
*/
const VariableList: React.FC<{
path: string[];
value: SqValue;
heading: string;
children: (settings: MergedItemSettings) => React.ReactNode;
}> = ({ path, heading, children }) => (
<VariableBox path={path} heading={heading}>
}> = ({ value, heading, children }) => (
<VariableBox value={value} heading={heading}>
{(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)}
</div>
)}
@ -52,54 +57,41 @@ const VariableList: React.FC<{
export interface Props {
/** The output of squiggle's run */
expression: 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[];
value: SqValue;
width?: number;
}
export const ExpressionViewer: React.FC<Props> = ({
path,
expression,
width,
}) => {
export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
const { getMergedSettings } = useContext(ViewerContext);
if (typeof expression !== "object") {
return (
<VariableList path={path} heading="Error">
{() => `Unknown expression: ${expression}`}
</VariableList>
);
}
switch (expression.tag) {
switch (value.tag) {
case SqValueTag.Number:
return (
<VariableBox path={path} heading="Number">
<VariableBox value={value} heading="Number">
{() => (
<div className="font-semibold text-slate-600">
<NumberShower precision={3} number={expression.value} />
<NumberShower precision={3} number={value.value} />
</div>
)}
</VariableBox>
);
case SqValueTag.Distribution: {
const distType = expression.value.tag;
const distType = value.value.tag;
return (
<VariableBox
path={path}
value={value}
heading={`Distribution (${distType})\n${
distType === SqDistributionTag.Symbolic
? expression.value.toString()
? value.value.toString()
: ""
}`}
renderSettingsMenu={({ onChange }) => {
const shape = expression.value.pointSet(
getMergedSettings(path).environment
const shape = value.value.pointSet(
getMergedSettings(value.location).environment
);
return (
<ItemSettingsMenu
path={path}
value={value}
onChange={onChange}
disableLogX={
shape.tag === "Ok" && hasMassBelowZero(shape.value.asShape())
@ -112,7 +104,7 @@ export const ExpressionViewer: React.FC<Props> = ({
{(settings) => {
return (
<DistributionChart
plot={defaultPlot(expression.value)}
plot={defaultPlot(value.value)}
environment={settings.environment}
{...settings.distributionPlotSettings}
height={settings.height}
@ -125,12 +117,12 @@ export const ExpressionViewer: React.FC<Props> = ({
}
case SqValueTag.String:
return (
<VariableBox path={path} heading="String">
<VariableBox value={value} heading="String">
{() => (
<>
<span className="text-slate-400">"</span>
<span className="text-slate-600 font-semibold font-mono">
{expression.value}
{value.value}
</span>
<span className="text-slate-400">"</span>
</>
@ -139,61 +131,61 @@ export const ExpressionViewer: React.FC<Props> = ({
);
case SqValueTag.Bool:
return (
<VariableBox path={path} heading="Boolean">
{() => expression.value.toString()}
<VariableBox value={value} heading="Boolean">
{() => value.value.toString()}
</VariableBox>
);
case SqValueTag.Symbol:
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-600">{expression.value}</span>
<span className="text-slate-600">{value.value}</span>
</>
)}
</VariableBox>
);
case SqValueTag.Call:
return (
<VariableBox path={path} heading="Call">
{() => expression.value}
<VariableBox value={value} heading="Call">
{() => value.value}
</VariableBox>
);
case SqValueTag.ArrayString:
return (
<VariableBox path={path} heading="Array String">
{() => expression.value.map((r) => `"${r}"`).join(", ")}
<VariableBox value={value} heading="Array String">
{() => value.value.map((r) => `"${r}"`).join(", ")}
</VariableBox>
);
case SqValueTag.Date:
return (
<VariableBox path={path} heading="Date">
{() => expression.value.toDateString()}
<VariableBox value={value} heading="Date">
{() => value.value.toDateString()}
</VariableBox>
);
case SqValueTag.Void:
return (
<VariableBox path={path} heading="Void">
<VariableBox value={value} heading="Void">
{() => "Void"}
</VariableBox>
);
case SqValueTag.TimeDuration: {
return (
<VariableBox path={path} heading="Time Duration">
{() => <NumberShower precision={3} number={expression.value} />}
<VariableBox value={value} heading="Time Duration">
{() => <NumberShower precision={3} number={value.value} />}
</VariableBox>
);
}
case SqValueTag.Lambda:
return (
<VariableBox
path={path}
value={value}
heading="Function"
renderSettingsMenu={({ onChange }) => {
return (
<ItemSettingsMenu
path={path}
value={value}
onChange={onChange}
withFunctionSettings={true}
/>
@ -202,11 +194,11 @@ export const ExpressionViewer: React.FC<Props> = ({
>
{(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()
.join(",")})`}</div>
<FunctionChart
fn={expression.value}
fn={value.value}
chartSettings={settings.chartSettings}
distributionPlotSettings={settings.distributionPlotSettings}
height={settings.height}
@ -222,13 +214,13 @@ export const ExpressionViewer: React.FC<Props> = ({
case SqValueTag.Declaration: {
return (
<VariableBox
path={path}
value={value}
heading="Function Declaration"
renderSettingsMenu={({ onChange }) => {
return (
<ItemSettingsMenu
onChange={onChange}
path={path}
value={value}
withFunctionSettings={true}
/>
);
@ -252,16 +244,15 @@ export const ExpressionViewer: React.FC<Props> = ({
}
case SqValueTag.Module: {
return (
<VariableList path={path} heading="Module">
<VariableList value={value} heading="Module">
{(_) =>
expression.value
value.value
.entries()
.filter(([key, _]) => !key.match(/^(Math|System)\./))
.map(([key, r]) => (
<ExpressionViewer
key={key}
path={[...path, key]}
expression={r}
value={r}
width={width !== undefined ? width - 20 : width}
/>
))
@ -270,16 +261,16 @@ export const ExpressionViewer: React.FC<Props> = ({
);
}
case SqValueTag.Record:
const plot = makePlot(expression.value);
const plot = makePlot(value.value);
if (plot) {
return (
<VariableBox
path={path}
value={value}
heading="Plot"
renderSettingsMenu={({ onChange }) => {
let disableLogX = plot.distributions.some((x) => {
let pointSet = x.distribution.pointSet(
getMergedSettings(path).environment
getMergedSettings(value.location).environment
);
return (
pointSet.tag === "Ok" &&
@ -288,7 +279,7 @@ export const ExpressionViewer: React.FC<Props> = ({
});
return (
<ItemSettingsMenu
path={path}
value={value}
onChange={onChange}
disableLogX={disableLogX}
withFunctionSettings={false}
@ -311,15 +302,14 @@ export const ExpressionViewer: React.FC<Props> = ({
);
} else {
return (
<VariableList path={path} heading="Record">
<VariableList value={value} heading="Record">
{(_) =>
expression.value
value.value
.entries()
.map(([key, r]) => (
<ExpressionViewer
key={key}
path={[...path, key]}
expression={r}
value={r}
width={width !== undefined ? width - 20 : width}
/>
))
@ -329,15 +319,14 @@ export const ExpressionViewer: React.FC<Props> = ({
}
case SqValueTag.Array:
return (
<VariableList path={path} heading="Array">
<VariableList value={value} heading="Array">
{(_) =>
expression.value
value.value
.getValues()
.map((r, i) => (
<ExpressionViewer
key={i}
path={[...path, String(i)]}
expression={r}
value={r}
width={width !== undefined ? width - 20 : width}
/>
))
@ -346,13 +335,11 @@ export const ExpressionViewer: React.FC<Props> = ({
);
default: {
return (
<VariableList path={path} heading="Error">
<VariableList value={value} heading="Error">
{() => (
<div>
<span>No display for type: </span>{" "}
<span className="font-semibold text-slate-600">
{expression.tag}
</span>
<span className="font-semibold text-slate-600">{value.tag}</span>
</div>
)}
</VariableList>

View File

@ -4,13 +4,14 @@ import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { Modal } from "../ui/Modal";
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
import { Path, pathAsString } from "./utils";
import { ViewerContext } from "./ViewerContext";
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
import { PlaygroundContext } from "../SquigglePlayground";
import { SqValue } from "@quri/squiggle-lang";
import { locationAsString } from "./utils";
type Props = {
path: Path;
value: SqValue;
onChange: () => void;
disableLogX?: boolean;
withFunctionSettings: boolean;
@ -19,7 +20,7 @@ type Props = {
const ItemSettingsModal: React.FC<
Props & { close: () => void; resetScroll: () => void }
> = ({
path,
value,
onChange,
disableLogX,
withFunctionSettings,
@ -29,7 +30,7 @@ const ItemSettingsModal: React.FC<
const { setSettings, getSettings, getMergedSettings } =
useContext(ViewerContext);
const mergedSettings = getMergedSettings(path);
const mergedSettings = getMergedSettings(value.location);
const { register, watch } = useForm({
resolver: yupResolver(viewSettingsSchema),
@ -53,8 +54,8 @@ const ItemSettingsModal: React.FC<
});
useEffect(() => {
const subscription = watch((vars) => {
const settings = getSettings(path); // get the latest version
setSettings(path, {
const settings = getSettings(value.location); // get the latest version
setSettings(value.location, {
...settings,
distributionPlotSettings: {
showSummary: vars.showSummary,
@ -75,7 +76,7 @@ const ItemSettingsModal: React.FC<
onChange();
});
return () => subscription.unsubscribe();
}, [getSettings, setSettings, onChange, path, watch]);
}, [getSettings, setSettings, onChange, value.location, watch]);
const { getLeftPanelElement } = useContext(PlaygroundContext);
@ -83,7 +84,7 @@ const ItemSettingsModal: React.FC<
<Modal container={getLeftPanelElement()} close={close}>
<Modal.Header>
Chart settings
{path.length ? (
{value.location.path.items.length ? (
<>
{" for "}
<span
@ -91,7 +92,7 @@ const ItemSettingsModal: React.FC<
className="cursor-pointer"
onClick={resetScroll}
>
{pathAsString(path)}
{locationAsString(value.location)}
</span>{" "}
</>
) : (
@ -120,7 +121,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
if (!enableLocalSettings) {
return null;
}
const settings = getSettings(props.path);
const settings = getSettings(props.value.location);
const resetScroll = () => {
if (!ref.current) return;
@ -139,7 +140,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
{settings.distributionPlotSettings || settings.chartSettings ? (
<button
onClick={() => {
setSettings(props.path, {
setSettings(props.value.location, {
...settings,
distributionPlotSettings: undefined,
chartSettings: undefined,

View File

@ -1,3 +1,4 @@
import { SqValue, SqValueLocation } from "@quri/squiggle-lang";
import React, { useContext, useReducer } from "react";
import { Tooltip } from "../ui/Tooltip";
import { LocalItemSettings, MergedItemSettings } from "./utils";
@ -8,14 +9,14 @@ type SettingsMenuParams = {
};
type VariableBoxProps = {
path: string[];
value: SqValue;
heading: string;
renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode;
children: (settings: MergedItemSettings) => React.ReactNode;
};
export const VariableBox: React.FC<VariableBoxProps> = ({
path,
value: { location },
heading = "Error",
renderSettingsMenu,
children,
@ -27,10 +28,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
// So we use `forceUpdate` to force rerendering.
const [_, forceUpdate] = useReducer((x) => x + 1, 0);
const settings = getSettings(path);
const settings = getSettings(location);
const setSettingsAndUpdate = (newSettings: LocalItemSettings) => {
setSettings(path, newSettings);
setSettings(location, newSettings);
forceUpdate();
};
@ -38,8 +39,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed });
};
const isTopLevel = path.length === 0;
const name = isTopLevel ? "Result" : path[path.length - 1];
const isTopLevel = location.path.items.length === 0;
const name = isTopLevel
? { result: "Result", bindings: "Bindings" }[location.path.root]
: location.path.items[location.path.items.length - 1];
return (
<div>
@ -65,13 +68,13 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
</header>
{settings.collapsed ? null : (
<div className="flex w-full">
{path.length ? (
{location.path.items.length ? (
<div
className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
onClick={toggleCollapsed}
></div>
) : null}
<div className="grow">{children(getMergedSettings(path))}</div>
<div className="grow">{children(getMergedSettings(location))}</div>
</div>
)}
</div>

View File

@ -1,14 +1,14 @@
import { defaultEnvironment } from "@quri/squiggle-lang";
import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
import React from "react";
import { LocalItemSettings, MergedItemSettings, Path } from "./utils";
import { LocalItemSettings, MergedItemSettings } from "./utils";
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).
// 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.
getSettings(path: Path): LocalItemSettings;
getMergedSettings(path: Path): MergedItemSettings;
setSettings(path: Path, value: LocalItemSettings): void;
getSettings(location: SqValueLocation): LocalItemSettings;
getMergedSettings(location: SqValueLocation): MergedItemSettings;
setSettings(location: SqValueLocation, value: LocalItemSettings): void;
enableLocalSettings: boolean; // show local settings icon in the UI
};

View File

@ -1,21 +1,20 @@
import React, { useCallback, useRef } from "react";
import { environment } from "@quri/squiggle-lang";
import { environment, SqValueLocation } from "@quri/squiggle-lang";
import { DistributionPlottingSettings } from "../DistributionChart";
import { FunctionChartSettings } from "../FunctionChart";
import { ExpressionViewer } from "./ExpressionViewer";
import { ViewerContext } from "./ViewerContext";
import {
LocalItemSettings,
locationAsString,
MergedItemSettings,
Path,
pathAsString,
} from "./utils";
import { useSquiggle } from "../../lib/hooks";
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
type Props = {
/** The output of squiggle's run */
result: ReturnType<typeof useSquiggle>;
result: ReturnType<typeof useSquiggle>["result"];
width?: number;
height: number;
distributionPlotSettings: DistributionPlottingSettings;
@ -45,22 +44,22 @@ export const SquiggleViewer: React.FC<Props> = ({
const settingsRef = useRef<Settings>({});
const getSettings = useCallback(
(path: Path) => {
return settingsRef.current[pathAsString(path)] || defaultSettings;
(location: SqValueLocation) => {
return settingsRef.current[locationAsString(location)] || defaultSettings;
},
[settingsRef]
);
const setSettings = useCallback(
(path: Path, value: LocalItemSettings) => {
settingsRef.current[pathAsString(path)] = value;
(location: SqValueLocation, value: LocalItemSettings) => {
settingsRef.current[locationAsString(location)] = value;
},
[settingsRef]
);
const getMergedSettings = useCallback(
(path: Path) => {
const localSettings = getSettings(path);
(location: SqValueLocation) => {
const localSettings = getSettings(location);
const result: MergedItemSettings = {
distributionPlotSettings: {
...distributionPlotSettings,
@ -91,7 +90,7 @@ export const SquiggleViewer: React.FC<Props> = ({
}}
>
{result.tag === "Ok" ? (
<ExpressionViewer path={[]} expression={result.value} width={width} />
<ExpressionViewer value={result.value} width={width} />
) : (
<SquiggleErrorAlert error={result.value} />
)}

View File

@ -1,6 +1,6 @@
import { DistributionPlottingSettings } from "../DistributionChart";
import { FunctionChartSettings } from "../FunctionChart";
import { environment } from "@quri/squiggle-lang";
import { environment, SqValueLocation } from "@quri/squiggle-lang";
export type LocalItemSettings = {
collapsed: boolean;
@ -17,6 +17,5 @@ export type MergedItemSettings = {
environment: environment;
};
export type Path = string[];
export const pathAsString = (path: Path) => path.join(".");
export const locationAsString = (location: SqValueLocation) =>
location.path.root + "/" + location.path.items.join(".");

View File

@ -18,12 +18,10 @@ type SquiggleArgs = {
export const useSquiggle = (args: SquiggleArgs) => {
const result = useMemo(
() => {
const { result, bindings } = run(args.code, {
const result = run(args.code, {
environment: args.environment,
});
return resultMap(result, (v) =>
v.tag === SqValueTag.Void ? bindings.asValue() : v
);
return result;
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
@ -37,10 +35,8 @@ export const useSquiggle = (args: SquiggleArgs) => {
const { onChange } = args;
useEffect(() => {
onChange?.(result.tag === "Ok" ? result.value : undefined);
onChange?.(result.result.tag === "Ok" ? result.result.value : undefined);
}, [result, onChange]);
console.log(result);
return result;
};

View File

@ -1,16 +1,15 @@
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
import { wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
type T = RSArray.squiggleValue_Array;
export class SqArray {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T, public location: SqValueLocation) {}
getValues() {
return RSArray.getValues(this._value).map(wrapValue);
return RSArray.getValues(this._value).map((v, i) =>
wrapValue(v, this.location.extend(i))
);
}
}

View File

@ -16,11 +16,14 @@ export const wrapDistribution = (value: T): SqDistribution => {
abstract class SqAbstractDistribution {
abstract tag: Tag;
_value: T;
constructor(value: T) {
this._value = value;
}
constructor(private _value: T) {}
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) {
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 {
tag = Tag.PointSet;
value() {
return valueMethod(this, RSDistribution.getPointSet);
return this.valueMethod(RSDistribution.getPointSet);
}
}
@ -97,7 +91,7 @@ export class SqSampleSetDistribution extends SqAbstractDistribution {
tag = Tag.SampleSet;
value() {
return valueMethod(this, RSDistribution.getSampleSet);
return this.valueMethod(RSDistribution.getSampleSet);
}
}
@ -105,7 +99,7 @@ export class SqSymbolicDistribution extends SqAbstractDistribution {
tag = Tag.Symbolic;
value() {
return valueMethod(this, RSDistribution.getSymbolic);
return this.valueMethod(RSDistribution.getSymbolic);
}
}

View File

@ -3,11 +3,7 @@ import * as RSDistributionError from "../rescript/ForTS/ForTS_Distribution/ForTS
type T = RSDistributionError.distributionError;
export class SqDistributionError {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T) {}
toString() {
return RSDistributionError.toString(this._value);

View File

@ -1,11 +1,7 @@
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
export class SqError {
_value: RSErrorValue.reducerErrorValue;
constructor(_value: RSErrorValue.reducerErrorValue) {
this._value = _value;
}
constructor(private _value: RSErrorValue.reducerErrorValue) {}
toString() {
return RSErrorValue.toString(this._value);

View File

@ -1,13 +1,10 @@
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
import { SqValueLocation } from "./SqValueLocation";
type T = RSLambda.squiggleValue_Lambda;
export class SqLambda {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T, public location: SqValueLocation) {}
parameters() {
return RSLambda.parameters(this._value);

View File

@ -3,9 +3,5 @@ import * as RSDeclaration from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_Squi
type T = RSDeclaration.squiggleValue_Declaration;
export class SqLambdaDeclaration {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T) {}
}

View File

@ -1,20 +1,23 @@
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
import { SqModuleValue, wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
export class SqModule {
_value: RSModuleValue.squiggleValue_Module;
constructor(_value: RSModuleValue.squiggleValue_Module) {
this._value = _value;
}
constructor(
private _value: RSModuleValue.squiggleValue_Module,
public location: SqValueLocation
) {}
entries() {
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() {
return new SqModuleValue(RSModuleValue.toSquiggleValue(this._value));
return new SqModuleValue(
RSModuleValue.toSquiggleValue(this._value),
this.location
);
}
}

View File

@ -25,29 +25,22 @@ export const wrapPointSetDist = (value: T) => {
};
abstract class SqAbstractPointSetDist {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T) {}
abstract asShape(): SqShape;
}
const valueMethod = <IR>(
_this: SqAbstractPointSetDist,
rsMethod: (v: T) => IR | null | undefined
) => {
const value = rsMethod(_this._value);
if (!value) throw new Error("Internal casting error");
return 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;
};
}
export class SqMixedPointSetDist extends SqAbstractPointSetDist {
tag = Tag.Mixed as const;
get value(): RSPointSetDist.mixedShape {
return valueMethod(this, RSPointSetDist.getMixed);
return this.valueMethod(RSPointSetDist.getMixed);
}
asShape() {
@ -63,7 +56,7 @@ export class SqDiscretePointSetDist extends SqAbstractPointSetDist {
tag = Tag.Discrete as const;
get value(): RSPointSetDist.discreteShape {
return valueMethod(this, RSPointSetDist.getDiscrete);
return this.valueMethod(RSPointSetDist.getDiscrete);
}
asShape() {
@ -79,7 +72,7 @@ export class SqContinuousPointSetDist extends SqAbstractPointSetDist {
tag = Tag.Continuous as const;
get value(): RSPointSetDist.continuousShape {
return valueMethod(this, RSPointSetDist.getContinues);
return this.valueMethod(RSPointSetDist.getContinues);
}
asShape() {

View File

@ -5,13 +5,10 @@ import { SqError } from "./SqError";
import { SqModule } from "./SqModule";
import { wrapValue } from "./SqValue";
import { resultMap2 } from "./types";
import { SqValueLocation } from "./SqValueLocation";
export class SqProject {
_value: RSProject.reducerProject;
constructor(_value: RSProject.reducerProject) {
this._value = _value;
}
constructor(private _value: RSProject.reducerProject) {}
static create() {
return new SqProject(RSProject.createProject());
@ -94,14 +91,27 @@ export class SqProject {
}
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) {
const innerResult = RSProject.getResult(this._value, sourceId);
return resultMap2(
innerResult,
wrapValue,
(v) =>
wrapValue(
v,
new SqValueLocation(this, sourceId, {
root: "result",
items: [],
})
),
(v: reducerErrorValue) => new SqError(v)
);
}

View File

@ -1,18 +1,15 @@
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
import { wrapValue } from "./SqValue";
import { SqValueLocation } from "./SqValueLocation";
type T = RSRecord.squiggleValue_Record;
export class SqRecord {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T, public location: SqValueLocation) {}
entries() {
return RSRecord.getKeyValuePairs(this._value).map(
([k, v]) => [k, wrapValue(v)] as const
([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
);
}
}

View File

@ -3,9 +3,5 @@ import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleVal
type T = RSType.squiggleValue_Type;
export class SqType {
_value: T;
constructor(_value: T) {
this._value = _value;
}
constructor(private _value: T) {}
}

View File

@ -7,42 +7,38 @@ import { SqModule } from "./SqModule";
import { SqRecord } from "./SqRecord";
import { SqArray } from "./SqArray";
import { SqType } from "./SqType";
import { SqProject } from "./SqProject";
import { SqValueLocation } from "./SqValueLocation";
export { Tag as SqValueTag };
type T = RSValue.squiggleValue;
export const wrapValue = (value: T): SqValue => {
export const wrapValue = (value: T, location: SqValueLocation): SqValue => {
const tag = RSValue.getTag(value);
return new tagToClass[tag](value);
return new tagToClass[tag](value, location);
};
export abstract class SqAbstractValue {
abstract tag: Tag;
_value: T;
constructor(value: T) {
this._value = value;
}
constructor(private _value: T, public location: SqValueLocation) {}
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 {
tag = Tag.Array as const;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
get value() {
return valueMethod(this, RSValue.getTypeIdentifier);
return this.valueMethod(RSValue.getTypeIdentifier);
}
}

View 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],
});
}
}

View File

@ -1,6 +1,7 @@
import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
import { SqProject } from "./SqProject";
import { SqValue, SqValueTag } from "./SqValue";
export { SqValueLocation } from "./SqValueLocation";
export { result } from "../rescript/ForTS/ForTS_Result_tag";
export { SqDistribution, SqDistributionTag } from "./SqDistribution";
export { SqDistributionError } from "./SqDistributionError";