Merge branch 'develop' into reducer-type-grammar

This commit is contained in:
Umur Ozkul 2022-06-02 14:29:33 +02:00
commit 9e4a70c516
28 changed files with 2769 additions and 2239 deletions

View File

@ -1,3 +1,4 @@
import "../src/tailwind.css";
export const parameters = { export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" }, actions: { argTypesRegex: "^on[A-Z].*" },
controls: { controls: {

View File

@ -3,6 +3,8 @@
"version": "0.2.20", "version": "0.2.20",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@headlessui/react": "^1.6.4",
"@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.8.10", "@hookform/resolvers": "^2.8.10",
"@quri/squiggle-lang": "^0.2.8", "@quri/squiggle-lang": "^0.2.8",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",
@ -10,10 +12,9 @@
"react": "^18.1.0", "react": "^18.1.0",
"react-ace": "^10.1.0", "react-ace": "^10.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"react-hook-form": "^7.31.2", "react-hook-form": "^7.31.3",
"react-use": "^17.4.0", "react-use": "^17.4.0",
"react-vega": "^7.5.1", "react-vega": "^7.5.1",
"styled-components": "^5.3.5",
"vega": "^5.22.1", "vega": "^5.22.1",
"vega-embed": "^6.20.6", "vega-embed": "^6.20.6",
"vega-lite": "^5.2.0", "vega-lite": "^5.2.0",
@ -21,30 +22,32 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.17.12", "@babel/plugin-proposal-private-property-in-object": "^7.17.12",
"@storybook/addon-actions": "^6.5.3", "@storybook/addon-actions": "^6.5.6",
"@storybook/addon-essentials": "^6.5.4", "@storybook/addon-essentials": "^6.5.6",
"@storybook/addon-links": "^6.5.4", "@storybook/addon-links": "^6.5.6",
"@storybook/builder-webpack5": "^6.5.4", "@storybook/builder-webpack5": "^6.5.6",
"@storybook/manager-webpack5": "^6.5.4", "@storybook/manager-webpack5": "^6.5.6",
"@storybook/node-logger": "^6.5.4", "@storybook/node-logger": "^6.5.6",
"@storybook/preset-create-react-app": "^4.1.1", "@storybook/preset-create-react-app": "^4.1.1",
"@storybook/react": "^6.5.4", "@storybook/react": "^6.5.6",
"@tailwindcss/forms": "^0.5.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.2.0", "@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.2.0", "@testing-library/user-event": "^14.2.0",
"@types/jest": "^27.5.0", "@types/jest": "^27.5.0",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.14.182",
"@types/node": "^17.0.35", "@types/node": "^17.0.36",
"@types/react": "^18.0.9", "@types/react": "^18.0.9",
"@types/react-dom": "^18.0.4", "@types/react-dom": "^18.0.5",
"@types/styled-components": "^5.1.24", "@types/styled-components": "^5.1.24",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"tailwindcss": "^3.0.24",
"ts-loader": "^9.3.0", "ts-loader": "^9.3.0",
"tsconfig-paths-webpack-plugin": "^3.5.2", "tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.6.3", "typescript": "^4.7.2",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
"webpack": "^5.72.1", "webpack": "^5.72.1",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",

View File

@ -0,0 +1,83 @@
import * as React from "react";
import {
XCircleIcon,
InformationCircleIcon,
CheckCircleIcon,
} from "@heroicons/react/solid";
export const Alert: React.FC<{
heading: string;
backgroundColor: string;
headingColor: string;
bodyColor: string;
icon: React.ReactNode;
children: React.ReactNode;
}> = ({
heading = "Error",
backgroundColor,
headingColor,
bodyColor,
icon,
children,
}) => {
return (
<div className={`rounded-md p-4 ${backgroundColor}`}>
<div className="flex">
<div className="flex-shrink-0">{icon}</div>
<div className="ml-3">
<h3 className={`text-sm font-medium ${headingColor}`}>{heading}</h3>
<div className={`mt-2 text-sm ${bodyColor}`}>{children}</div>
</div>
</div>
</div>
);
};
export const ErrorAlert: React.FC<{
heading: string;
children: React.ReactNode;
}> = ({ heading = "Error", children }) => (
<Alert
heading={heading}
children={children}
backgroundColor="bg-red-100"
headingColor="text-red-800"
bodyColor="text-red-700"
icon={<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />}
/>
);
export const MessageAlert: React.FC<{
heading: string;
children: React.ReactNode;
}> = ({ heading = "Error", children }) => (
<Alert
heading={heading}
children={children}
backgroundColor="bg-slate-100"
headingColor="text-slate-700"
bodyColor="text-slate-700"
icon={
<InformationCircleIcon
className="h-5 w-5 text-slate-400"
aria-hidden="true"
/>
}
/>
);
export const SuccessAlert: React.FC<{
heading: string;
children: React.ReactNode;
}> = ({ heading = "Error", children }) => (
<Alert
heading={heading}
children={children}
backgroundColor="bg-green-50"
headingColor="text-green-800"
bodyColor="text-green-700"
icon={
<CheckCircleIcon className="h-5 w-5 text-green-400" aria-hidden="true" />
}
/>
);

View File

@ -29,6 +29,7 @@ export let CodeEditor: FC<CodeEditorProps> = ({
mode="golang" mode="golang"
theme="github" theme="github"
width={"100%"} width={"100%"}
fontSize={14}
height={String(height) + "px"} height={String(height) + "px"}
minLines={oneLine ? lineCount : undefined} minLines={oneLine ? lineCount : undefined}
maxLines={oneLine ? lineCount : undefined} maxLines={oneLine ? lineCount : undefined}

View File

@ -8,7 +8,7 @@ import {
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { Vega, VisualizationSpec } from "react-vega"; import { Vega, VisualizationSpec } from "react-vega";
import * as chartSpecification from "../vega-specs/spec-distributions.json"; import * as chartSpecification from "../vega-specs/spec-distributions.json";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert } from "./Alert";
import { useSize } from "react-use"; import { useSize } from "react-use";
import { import {
linearXScale, linearXScale,
@ -16,7 +16,6 @@ import {
linearYScale, linearYScale,
expYScale, expYScale,
} from "./DistributionVegaScales"; } from "./DistributionVegaScales";
import styled from "styled-components";
import { NumberShower } from "./NumberShower"; import { NumberShower } from "./NumberShower";
type DistributionChartProps = { type DistributionChartProps = {
@ -46,6 +45,12 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
shape.value.discrete.some((x) => x.x <= 0); shape.value.discrete.some((x) => x.x <= 0);
let spec = buildVegaSpec(isLogX, isExpY); let spec = buildVegaSpec(isLogX, isExpY);
let widthProp = width ? width : size.width; let widthProp = width ? width : size.width;
if (widthProp < 20) {
console.warn(
`Width of Distribution is set to ${widthProp}, which is too small`
);
widthProp = 20;
}
// Check whether we should disable the checkbox // Check whether we should disable the checkbox
var logCheckbox = ( var logCheckbox = (
@ -66,7 +71,7 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
} }
var result = ( var result = (
<ChartContainer width={widthProp + "px"}> <div style={{ width: widthProp + "px" }}>
<Vega <Vega
spec={spec} spec={spec}
data={{ con: shape.value.continuous, dis: shape.value.discrete }} data={{ con: shape.value.continuous, dis: shape.value.discrete }}
@ -74,20 +79,22 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
height={height} height={height}
actions={false} actions={false}
/> />
{showSummary && <SummaryTable distribution={distribution} />} <div className="flex justify-center">
{showSummary && <SummaryTable distribution={distribution} />}
</div>
{showControls && ( {showControls && (
<div> <div>
{logCheckbox} {logCheckbox}
<CheckBox label="Exp Y scale" value={isExpY} onChange={setExpY} /> <CheckBox label="Exp Y scale" value={isExpY} onChange={setExpY} />
</div> </div>
)} )}
</ChartContainer> </div>
); );
} else { } else {
var result = ( var result = (
<ErrorBox heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(shape.value)} {distributionErrorToString(shape.value)}
</ErrorBox> </ErrorAlert>
); );
} }
@ -96,12 +103,6 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
return sized; return sized;
}; };
type ChartContainerProps = { width: string };
let ChartContainer = styled.div<ChartContainerProps>`
width: ${(props) => props.width};
`;
function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec { function buildVegaSpec(isLogX: boolean, isExpY: boolean): VisualizationSpec {
return { return {
...chartSpecification, ...chartSpecification,
@ -120,10 +121,6 @@ interface CheckBoxProps {
tooltip?: string; tooltip?: string;
} }
const Label = styled.label<{ disabled: boolean }>`
${(props) => props.disabled && "color: #999;"}
`;
export const CheckBox = ({ export const CheckBox = ({
label, label,
onChange, onChange,
@ -139,7 +136,7 @@ export const CheckBox = ({
onChange={() => onChange(!value)} onChange={() => onChange(!value)}
disabled={disabled} disabled={disabled}
/> />
<Label disabled={disabled}>{label}</Label> <label className={disabled ? "text-slate-400" : ""}> {label}</label>
</span> </span>
); );
}; };
@ -148,34 +145,6 @@ type SummaryTableProps = {
distribution: Distribution; distribution: Distribution;
}; };
const Table = styled.table`
margin-left: auto;
margin-right: auto;
border-collapse: collapse;
text-align: center;
border-style: hidden;
`;
const TableHead = styled.thead`
border-bottom: 1px solid rgb(141 149 167);
`;
const TableHeadCell = styled.th`
border-right: 1px solid rgb(141 149 167);
border-left: 1px solid rgb(141 149 167);
padding: 0.3em;
`;
const TableBody = styled.tbody``;
const Row = styled.tr``;
const Cell = styled.td`
padding: 0.3em;
border-right: 1px solid rgb(141 149 167);
border-left: 1px solid rgb(141 149 167);
`;
const SummaryTable: React.FC<SummaryTableProps> = ({ const SummaryTable: React.FC<SummaryTableProps> = ({
distribution, distribution,
}: SummaryTableProps) => { }: SummaryTableProps) => {
@ -194,17 +163,30 @@ const SummaryTable: React.FC<SummaryTableProps> = ({
return <NumberShower number={x.value} />; return <NumberShower number={x.value} />;
} else { } else {
return ( return (
<ErrorBox heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(x.value)} {distributionErrorToString(x.value)}
</ErrorBox> </ErrorAlert>
); );
} }
}; };
let TableHeadCell: React.FC<{ children: React.ReactNode }> = ({
children,
}) => (
<th className="border border-slate-200 bg-slate-50 py-1 px-2 text-slate-500 font-semibold">
{children}
</th>
);
let Cell: React.FC<{ children: React.ReactNode }> = ({ children }) => (
<td className="border border-slate-200 py-1 px-2 text-slate-900 ">
{children}
</td>
);
return ( return (
<Table> <table className="border border-collapse border-slate-400">
<TableHead> <thead className="bg-slate-50">
<Row> <tr>
<TableHeadCell>{"Mean"}</TableHeadCell> <TableHeadCell>{"Mean"}</TableHeadCell>
<TableHeadCell>{"5%"}</TableHeadCell> <TableHeadCell>{"5%"}</TableHeadCell>
<TableHeadCell>{"10%"}</TableHeadCell> <TableHeadCell>{"10%"}</TableHeadCell>
@ -213,10 +195,10 @@ const SummaryTable: React.FC<SummaryTableProps> = ({
<TableHeadCell>{"75%"}</TableHeadCell> <TableHeadCell>{"75%"}</TableHeadCell>
<TableHeadCell>{"90%"}</TableHeadCell> <TableHeadCell>{"90%"}</TableHeadCell>
<TableHeadCell>{"95%"}</TableHeadCell> <TableHeadCell>{"95%"}</TableHeadCell>
</Row> </tr>
</TableHead> </thead>
<TableBody> <tbody>
<Row> <tr>
<Cell>{unwrapResult(mean)}</Cell> <Cell>{unwrapResult(mean)}</Cell>
<Cell>{unwrapResult(p5)}</Cell> <Cell>{unwrapResult(p5)}</Cell>
<Cell>{unwrapResult(p10)}</Cell> <Cell>{unwrapResult(p10)}</Cell>
@ -225,8 +207,8 @@ const SummaryTable: React.FC<SummaryTableProps> = ({
<Cell>{unwrapResult(p75)}</Cell> <Cell>{unwrapResult(p75)}</Cell>
<Cell>{unwrapResult(p90)}</Cell> <Cell>{unwrapResult(p90)}</Cell>
<Cell>{unwrapResult(p95)}</Cell> <Cell>{unwrapResult(p95)}</Cell>
</Row> </tr>
</TableBody> </tbody>
</Table> </table>
); );
}; };

View File

@ -1,20 +0,0 @@
import * as React from "react";
import styled from "styled-components";
const ShowError = styled.div`
border: 1px solid #792e2e;
background: #eee2e2;
padding: 0.4em 0.8em;
`;
export const ErrorBox: React.FC<{
heading: string;
children: React.ReactNode;
}> = ({ heading = "Error", children }) => {
return (
<ShowError>
<h3>{heading}</h3>
{children}
</ShowError>
);
};

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang"; import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang";
import { FunctionChart1Dist } from "./FunctionChart1Dist"; import { FunctionChart1Dist } from "./FunctionChart1Dist";
import { FunctionChart1Number } from "./FunctionChart1Number"; import { FunctionChart1Number } from "./FunctionChart1Number";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert, MessageAlert } from "./Alert";
export type FunctionChartSettings = { export type FunctionChartSettings = {
start: number; start: number;
@ -23,9 +23,16 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
environment, environment,
height, height,
}: FunctionChartProps) => { }: FunctionChartProps) => {
let result1 = runForeign(fn, [chartSettings.start], environment); if (fn.parameters.length > 1) {
let result2 = runForeign(fn, [chartSettings.stop], environment); return (
let getValidResult = () => { <MessageAlert heading="Function Display Not Supported">
Only functions with one parameter are displayed.
</MessageAlert>
);
}
const result1 = runForeign(fn, [chartSettings.start], environment);
const result2 = runForeign(fn, [chartSettings.stop], environment);
const getValidResult = () => {
if (result1.tag === "Ok") { if (result1.tag === "Ok") {
return result1; return result1;
} else if (result2.tag === "Ok") { } else if (result2.tag === "Ok") {
@ -34,41 +41,39 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
return result1; return result1;
} }
}; };
let validResult = getValidResult(); const validResult = getValidResult();
let resultType = validResult.tag === "Ok" ? validResult.value.tag : "Error"; const resultType =
validResult.tag === "Ok" ? validResult.value.tag : ("Error" as const);
let component = () => { switch (resultType) {
switch (resultType) { case "distribution":
case "distribution": return (
return ( <FunctionChart1Dist
<FunctionChart1Dist fn={fn}
fn={fn} chartSettings={chartSettings}
chartSettings={chartSettings} environment={environment}
environment={environment} height={height}
height={height} />
/> );
); case "number":
case "number": return (
return ( <FunctionChart1Number
<FunctionChart1Number fn={fn}
fn={fn} chartSettings={chartSettings}
chartSettings={chartSettings} environment={environment}
environment={environment} height={height}
height={height} />
/> );
); case "Error":
case "Error": return (
return ( <ErrorAlert heading="Error">The function failed to be run</ErrorAlert>
<ErrorBox heading="Error">The function failed to be run</ErrorBox> );
); default:
default: return (
return ( <MessageAlert heading="Function Display Not Supported">
<ErrorBox heading="No Viewer"> There is no function visualization for this type of output:{" "}
There is no function visualization for this type of function <span className="font-bold">{resultType}</span>
</ErrorBox> </MessageAlert>
); );
} }
};
return component();
}; };

View File

@ -15,7 +15,7 @@ import { createClassFromSpec } from "react-vega";
import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
import { DistributionChart } from "./DistributionChart"; import { DistributionChart } from "./DistributionChart";
import { NumberShower } from "./NumberShower"; import { NumberShower } from "./NumberShower";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert } from "./Alert";
let SquigglePercentilesChart = createClassFromSpec({ let SquigglePercentilesChart = createClassFromSpec({
spec: percentilesSpec as Spec, spec: percentilesSpec as Spec,
@ -197,7 +197,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
{showChart} {showChart}
{_.entries(getPercentilesMemoized.errors).map( {_.entries(getPercentilesMemoized.errors).map(
([errorName, errorPoints]) => ( ([errorName, errorPoints]) => (
<ErrorBox key={errorName} heading={errorName}> <ErrorAlert key={errorName} heading={errorName}>
Values:{" "} Values:{" "}
{errorPoints {errorPoints
.map((r, i) => <NumberShower key={i} number={r.x} />) .map((r, i) => <NumberShower key={i} number={r.x} />)
@ -206,7 +206,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
{a}, {b} {a}, {b}
</> </>
))} ))}
</ErrorBox> </ErrorAlert>
) )
)} )}
</> </>

View File

@ -10,7 +10,7 @@ import {
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega"; import { createClassFromSpec } from "react-vega";
import * as lineChartSpec from "../vega-specs/spec-line-chart.json"; import * as lineChartSpec from "../vega-specs/spec-line-chart.json";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert } from "./Alert";
let SquiggleLineChart = createClassFromSpec({ let SquiggleLineChart = createClassFromSpec({
spec: lineChartSpec as Spec, spec: lineChartSpec as Spec,
@ -110,9 +110,9 @@ export const FunctionChart1Number: React.FC<FunctionChart1NumberProps> = ({
actions={false} actions={false}
/> />
{getFunctionImageMemoized.errors.map(({ x, value }) => ( {getFunctionImageMemoized.errors.map(({ x, value }) => (
<ErrorBox key={x} heading={value}> <ErrorAlert key={x} heading={value}>
Error at point ${x} Error at point ${x}
</ErrorBox> </ErrorAlert>
))} ))}
</> </>
); );

View File

@ -1,6 +1,5 @@
import * as React from "react"; import * as React from "react";
import _ from "lodash"; import _ from "lodash";
import styled from "styled-components";
import { import {
run, run,
errorValueToString, errorValueToString,
@ -11,12 +10,11 @@ import {
defaultImports, defaultImports,
defaultBindings, defaultBindings,
defaultEnvironment, defaultEnvironment,
declarationArg,
declaration, declaration,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { NumberShower } from "./NumberShower"; import { NumberShower } from "./NumberShower";
import { DistributionChart } from "./DistributionChart"; import { DistributionChart } from "./DistributionChart";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert } from "./Alert";
import { FunctionChart, FunctionChartSettings } from "./FunctionChart"; import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
function getRange<a>(x: declaration<a>) { function getRange<a>(x: declaration<a>) {
@ -41,24 +39,6 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
}; };
} }
const variableBox = {
Component: styled.div`
background: white;
border: 1px solid #eee;
border-radius: 2px;
margin-bottom: 0.4em;
`,
Heading: styled.div`
border-bottom: 1px solid #eee;
padding-left: 0.8em;
padding-right: 0.8em;
padding-top: 0.1em;
`,
Body: styled.div`
padding: 0.4em 0.8em;
`,
};
interface VariableBoxProps { interface VariableBoxProps {
heading: string; heading: string;
children: React.ReactNode; children: React.ReactNode;
@ -72,20 +52,18 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
}: VariableBoxProps) => { }: VariableBoxProps) => {
if (showTypes) { if (showTypes) {
return ( return (
<variableBox.Component> <div className="bg-white border border-grey-200 m-2">
<variableBox.Heading> <div className="border-b border-grey-200 p-3">
<h3>{heading}</h3> <h3 className="font-mono">{heading}</h3>
</variableBox.Heading> </div>
<variableBox.Body>{children}</variableBox.Body> <div className="p-3">{children}</div>
</variableBox.Component> </div>
); );
} else { } else {
return <div>{children}</div>; return <div>{children}</div>;
} }
}; };
let RecordKeyHeader = styled.h3``;
export interface SquiggleItemProps { export interface SquiggleItemProps {
/** The input string for squiggle */ /** The input string for squiggle */
expression: squiggleExpression; expression: squiggleExpression;
@ -117,7 +95,9 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
case "number": case "number":
return ( return (
<VariableBox heading="Number" showTypes={showTypes}> <VariableBox heading="Number" showTypes={showTypes}>
<NumberShower precision={3} number={expression.value} /> <div className="font-semibold text-slate-600">
<NumberShower precision={3} number={expression.value} />
</div>
</VariableBox> </VariableBox>
); );
case "distribution": { case "distribution": {
@ -146,10 +126,13 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
} }
case "string": case "string":
return ( return (
<VariableBox <VariableBox heading="String" showTypes={showTypes}>
heading="String" <span className="text-slate-400">"</span>
showTypes={showTypes} <span className="text-slate-600 font-semibold">
>{`"${expression.value}"`}</VariableBox> {expression.value}
</span>
<span className="text-slate-400">"</span>
</VariableBox>
); );
case "boolean": case "boolean":
return ( return (
@ -160,7 +143,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
case "symbol": case "symbol":
return ( return (
<VariableBox heading="Symbol" showTypes={showTypes}> <VariableBox heading="Symbol" showTypes={showTypes}>
{expression.value} <span className="text-slate-500 mr-2">Undefined Symbol:</span>
<span className="text-slate-600">{expression.value}</span>
</VariableBox> </VariableBox>
); );
case "call": case "call":
@ -173,17 +157,24 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
return ( return (
<VariableBox heading="Array" showTypes={showTypes}> <VariableBox heading="Array" showTypes={showTypes}>
{expression.value.map((r, i) => ( {expression.value.map((r, i) => (
<SquiggleItem <div key={i} className="flex flex-row pt-1">
key={i} <div className="flex-none bg-slate-100 rounded-sm px-1">
expression={r} <h3 className="text-slate-400 font-mono">{i}</h3>
width={width !== undefined ? width - 20 : width} </div>
height={50} <div className="px-2 mb-2 grow ">
showTypes={showTypes} <SquiggleItem
showControls={showControls} key={i}
chartSettings={chartSettings} expression={r}
environment={environment} width={width !== undefined ? width - 20 : width}
showSummary={showSummary} height={50}
/> showTypes={showTypes}
showControls={showControls}
chartSettings={chartSettings}
environment={environment}
showSummary={showSummary}
/>
</div>
</div>
))} ))}
</VariableBox> </VariableBox>
); );
@ -191,18 +182,22 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
return ( return (
<VariableBox heading="Record" showTypes={showTypes}> <VariableBox heading="Record" showTypes={showTypes}>
{Object.entries(expression.value).map(([key, r]) => ( {Object.entries(expression.value).map(([key, r]) => (
<div key={key}> <div key={key} className="flex flex-row pt-1">
<RecordKeyHeader>{key}</RecordKeyHeader> <div className="flex-none pr-2">
<SquiggleItem <h3 className="text-slate-500 font-mono">{key}:</h3>
expression={r} </div>
width={width !== undefined ? width - 20 : width} <div className="pl-2 pr-2 mb-2 grow bg-gray-50 border border-gray-100 rounded-sm">
height={height / 3} <SquiggleItem
showTypes={showTypes} expression={r}
showSummary={showSummary} width={width !== undefined ? width - 20 : width}
showControls={showControls} height={height / 3}
chartSettings={chartSettings} showTypes={showTypes}
environment={environment} showSummary={showSummary}
/> showControls={showControls}
chartSettings={chartSettings}
environment={environment}
/>
</div>
</div> </div>
))} ))}
</VariableBox> </VariableBox>
@ -229,6 +224,9 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
case "lambda": case "lambda":
return ( return (
<VariableBox heading="Function" showTypes={showTypes}> <VariableBox heading="Function" showTypes={showTypes}>
<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.parameters.join(
","
)})`}</div>
<FunctionChart <FunctionChart
fn={expression.value} fn={expression.value}
chartSettings={chartSettings} chartSettings={chartSettings}
@ -287,12 +285,6 @@ export interface SquiggleChartProps {
showControls?: boolean; showControls?: boolean;
} }
const ChartWrapper = styled.div`
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
`;
let defaultChartSettings = { start: 0, stop: 10, count: 20 }; let defaultChartSettings = { start: 0, stop: 10, count: 20 };
export const SquiggleChart: React.FC<SquiggleChartProps> = ({ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
@ -328,10 +320,10 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
); );
} else { } else {
internal = ( internal = (
<ErrorBox heading={"Parse Error"}> <ErrorAlert heading={"Parse Error"}>
{errorValueToString(expressionResult.value)} {errorValueToString(expressionResult.value)}
</ErrorBox> </ErrorAlert>
); );
} }
return <ChartWrapper>{internal}</ChartWrapper>; return internal;
}; };

View File

@ -2,7 +2,6 @@ import * as React from "react";
import * as ReactDOM from "react-dom"; import * as ReactDOM from "react-dom";
import { SquiggleChart } from "./SquiggleChart"; import { SquiggleChart } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
import styled from "styled-components";
import type { import type {
squiggleExpression, squiggleExpression,
environment, environment,
@ -15,7 +14,7 @@ import {
defaultImports, defaultImports,
defaultBindings, defaultBindings,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { ErrorBox } from "./ErrorBox"; import { ErrorAlert } from "./Alert";
export interface SquiggleEditorProps { export interface SquiggleEditorProps {
/** The input string for squiggle */ /** The input string for squiggle */
@ -44,12 +43,6 @@ export interface SquiggleEditorProps {
showSummary?: boolean; showSummary?: boolean;
} }
const Input = styled.div`
border: 1px solid #ddd;
padding: 0.3em 0.3em;
margin-bottom: 1em;
`;
export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
initialSquiggleString = "", initialSquiggleString = "",
width, width,
@ -72,7 +65,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
}; };
return ( return (
<div> <div>
<Input> <div className="border border-grey-200 p-2 m-4">
<CodeEditor <CodeEditor
value={expression} value={expression}
onChange={setExpression} onChange={setExpression}
@ -80,7 +73,7 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
showGutter={false} showGutter={false}
height={20} height={20}
/> />
</Input> </div>
<SquiggleChart <SquiggleChart
width={width} width={width}
environment={environment} environment={environment}
@ -179,7 +172,7 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
return ( return (
<div> <div>
<Input> <div className="border border-grey-200 p-2 m-4">
<CodeEditor <CodeEditor
value={expression} value={expression}
onChange={setExpression} onChange={setExpression}
@ -187,8 +180,12 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
showGutter={false} showGutter={false}
height={20} height={20}
/> />
</Input> </div>
{error !== null ? <ErrorBox heading="Error">{error}</ErrorBox> : <></>} {error !== null ? (
<ErrorAlert heading="Error">{error}</ErrorAlert>
) : (
<></>
)}
</div> </div>
); );
}; };

View File

@ -4,80 +4,17 @@ import ReactDOM from "react-dom";
import { SquiggleChart } from "./SquiggleChart"; import { SquiggleChart } from "./SquiggleChart";
import CodeEditor from "./CodeEditor"; import CodeEditor from "./CodeEditor";
import JsonEditor from "./JsonEditor"; import JsonEditor from "./JsonEditor";
import styled from "styled-components";
import { useForm, useWatch } from "react-hook-form"; import { useForm, useWatch } from "react-hook-form";
import * as yup from "yup"; import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { defaultBindings, environment } from "@quri/squiggle-lang"; import { defaultBindings, environment } from "@quri/squiggle-lang";
import { Tab } from "@headlessui/react";
interface FieldFloatProps { import { CodeIcon } from "@heroicons/react/solid";
label: string; import { CogIcon } from "@heroicons/react/solid";
className?: string; import { ChartSquareBarIcon } from "@heroicons/react/solid";
value: number; import { CurrencyDollarIcon } from "@heroicons/react/solid";
onChange: (value: number) => void; import { Fragment } from "react";
} import { ErrorAlert, SuccessAlert } from "./Alert";
const Input = styled.input``;
const FormItem = (props: { label: string; children: ReactElement }) => (
<div>
<label>{props.label}</label>
{props.children}
</div>
);
function FieldFloat(Props: FieldFloatProps) {
let [contents, setContents] = useState(Props.value + "");
return (
<FormItem label={Props.label}>
<Input
value={contents}
className={Props.className ? Props.className : ""}
onChange={(e) => {
setContents(e.target.value);
let result = parseFloat(contents);
if (_.isFinite(result)) {
Props.onChange(result);
}
}}
/>
</FormItem>
);
}
interface ShowBoxProps {
height: number;
}
const ShowBox = styled.div<ShowBoxProps>`
border: 1px solid #eee;
border-radius: 2px;
height: ${(props) => props.height};
`;
interface TitleProps {
readonly maxHeight: number;
}
const Display = styled.div<TitleProps>`
background: #f6f6f6;
border-left: 1px solid #eee;
height: 100vh;
padding: 3px;
overflow-y: auto;
max-height: ${(props) => props.maxHeight}px;
`;
interface RowProps {
readonly leftPercentage: number;
}
const Row = styled.div<RowProps>`
display: grid;
grid-template-columns: ${(p) => p.leftPercentage}% ${(p) =>
100 - p.leftPercentage}%;
`;
const Col = styled.div``;
interface PlaygroundProps { interface PlaygroundProps {
/** The initial squiggle string to put in the playground */ /** The initial squiggle string to put in the playground */
@ -124,6 +61,27 @@ const schema = yup
showControls: yup.boolean(), showControls: yup.boolean(),
showSummary: yup.boolean(), showSummary: yup.boolean(),
showSettingsPage: yup.boolean().default(false), showSettingsPage: yup.boolean().default(false),
diagramStart: yup
.number()
.required()
.positive()
.integer()
.default(0)
.min(0),
diagramStop: yup
.number()
.required()
.positive()
.integer()
.default(10)
.min(0),
diagramCount: yup
.number()
.required()
.positive()
.integer()
.default(20)
.min(2),
}) })
.required(); .required();
@ -133,12 +91,68 @@ type InputProps = {
}; };
const InputItem: React.FC<InputProps> = ({ label, children }) => ( const InputItem: React.FC<InputProps> = ({ label, children }) => (
<div> <div className="col-span-4">
<label>{label}</label> <label className="block text-sm font-medium text-gray-600">{label}</label>
{children} <div className="mt-1">{children}</div>
</div> </div>
); );
let numberStyle =
"max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md";
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
}
type StyledTabProps = {
name: string;
iconName: string;
};
const StyledTab: React.FC<StyledTabProps> = ({ name, iconName }) => {
let iconStyle = (isSelected: boolean) =>
classNames(
"-ml-0.5 mr-2 h-4 w-4 ",
isSelected ? "text-slate-500" : "text-gray-400 group-hover:text-gray-900"
);
let icon = (selected: boolean) =>
({
code: <CodeIcon className={iconStyle(selected)} />,
cog: <CogIcon className={iconStyle(selected)} />,
squareBar: <ChartSquareBarIcon className={iconStyle(selected)} />,
dollar: <CurrencyDollarIcon className={iconStyle(selected)} />,
}[iconName]);
return (
<Tab key={name} as={Fragment}>
{({ selected }) => (
<button className="flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100 ">
<span
className={classNames(
"p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
selected
? "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
: ""
)}
>
{icon(selected)}
<span
className={
selected
? "text-gray-900"
: "text-gray-600 group-hover:text-gray-900"
}
>
{name}
</span>
</span>
</button>
)}
</Tab>
);
};
let SquigglePlayground: FC<PlaygroundProps> = ({ let SquigglePlayground: FC<PlaygroundProps> = ({
initialSquiggleString = "", initialSquiggleString = "",
height = 500, height = 500,
@ -150,19 +164,7 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
let [importString, setImportString] = useState("{}"); let [importString, setImportString] = useState("{}");
let [imports, setImports] = useState({}); let [imports, setImports] = useState({});
let [importsAreValid, setImportsAreValid] = useState(true); let [importsAreValid, setImportsAreValid] = useState(true);
let [diagramStart, setDiagramStart] = useState(0); const { register, control } = useForm({
let [diagramStop, setDiagramStop] = useState(10);
let [diagramCount, setDiagramCount] = useState(20);
let chartSettings = {
start: diagramStart,
stop: diagramStop,
count: diagramCount,
};
const {
register,
formState: { errors },
control,
} = useForm({
resolver: yupResolver(schema), resolver: yupResolver(schema),
defaultValues: { defaultValues: {
sampleCount: 1000, sampleCount: 1000,
@ -173,11 +175,19 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
showSummary: showSummary, showSummary: showSummary,
leftSizePercent: 50, leftSizePercent: 50,
showSettingsPage: false, showSettingsPage: false,
diagramStart: 0,
diagramStop: 10,
diagramCount: 20,
}, },
}); });
const vars = useWatch({ const vars = useWatch({
control, control,
}); });
let chartSettings = {
start: Number(vars.diagramStart),
stop: Number(vars.diagramStop),
count: Number(vars.diagramCount),
};
let env: environment = { let env: environment = {
sampleCount: Number(vars.sampleCount), sampleCount: Number(vars.sampleCount),
xyPointLength: Number(vars.xyPointLength), xyPointLength: Number(vars.xyPointLength),
@ -191,80 +201,222 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
setImportsAreValid(false); setImportsAreValid(false);
} }
}; };
let samplingSettings = (
<div className="space-y-6 p-3 max-w-xl">
<InputItem label="Sample Count">
<>
<input
type="number"
{...register("sampleCount")}
className={numberStyle}
/>
<p className="mt-2 text-sm text-gray-500">
How many samples to use for Monte Carlo simulations. This can
occasionally be overridden by specific Squiggle programs.
</p>
</>
</InputItem>
<InputItem label="Coordinate Count (For PointSet Shapes)">
<>
<input
type="number"
{...register("xyPointLength")}
className={numberStyle}
/>
<p className="mt-2 text-sm text-gray-500">
When distributions are converted into PointSet shapes, we need to
know how many coordinates to use.
</p>
</>
</InputItem>
</div>
);
let viewSettings = (
<div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
<div className="space-y-2">
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
General Display Settings
</h3>
<InputItem label="Chart Height (in pixels)">
<input
type="number"
{...register("chartHeight")}
className={numberStyle}
/>
</InputItem>
<div className="relative flex items-start pt-3">
<div className="flex items-center h-5">
<input
type="checkbox"
{...register("showTypes")}
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<label className="font-medium text-gray-700">
Show information about displayed types.
</label>
</div>
</div>
</div>
<div className="space-y-2 pt-8">
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
Distribution Display Settings
</h3>
<div className="relative flex items-start">
<div className="flex items-center h-5">
<input
type="checkbox"
{...register("showControls")}
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<label className="font-medium text-gray-700">
Show toggles to adjust scale of x and y axes
</label>
</div>
</div>
<div className="relative flex items-start">
<div className="flex items-center h-5">
<input
type="checkbox"
{...register("showSummary")}
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<label className="font-medium text-gray-700">
Show summary statistics
</label>
</div>
</div>
</div>
<div className="space-y-2 pt-8">
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
Function Display Settings
</h3>
<p className="mt-2 text-sm text-gray-500">
When displaying functions of single variables that return numbers or
distributions, we need to use defaults for the x-axis. We need to
select a minimum and maximum value of x to sample, and a number n of
the number of points to sample.
</p>
<div className="pt-4 grid grid-cols-1 gap-y-4 gap-x-4">
<InputItem label="Min X Value">
<input
type="number"
{...register("diagramStart")}
className={numberStyle}
/>
</InputItem>
<InputItem label="Max X Value">
<input
type="number"
{...register("diagramStop")}
className={numberStyle}
/>
</InputItem>
<InputItem label="Points between X min and X max to sample">
<input
type="number"
{...register("diagramCount")}
className={numberStyle}
/>
</InputItem>
</div>
</div>
</div>
);
let inputVariableSettings = (
<div className="space-y-6 p-3 max-w-3xl">
<h3 className="text-lg leading-6 font-medium text-gray-900">
Import Variables from JSON
</h3>
<p className="mt-2 text-sm text-gray-500">
You can import variables from JSON into your Squiggle code. Variables
are accessed with dollar signs. For example, "timeNow" would be accessed
as "$timeNow".
</p>
<div className="border border-slate-200 mt-6 mb-2">
<JsonEditor
value={importString}
onChange={getChangeJson}
oneLine={false}
showGutter={true}
height={150}
/>
</div>
<div className="p-1 pt-2">
{importsAreValid ? (
<SuccessAlert heading="Valid Json">
<></>
</SuccessAlert>
) : (
<ErrorAlert heading="Invalid JSON">
You must use valid json in this editor.
</ErrorAlert>
)}
</div>
</div>
);
return ( return (
<ShowBox height={height}> <Tab.Group>
<input type="checkbox" {...register("showSettingsPage")} /> <div className=" flex-col flex">
<Row leftPercentage={vars.leftSizePercent || 50}> <div className="pb-4">
<Col> <Tab.List className="p-0.5 rounded-md bg-slate-100 hover:bg-slate-200 inline-flex">
{vars.showSettingsPage ? ( <StyledTab name="Code" iconName="code" />
<> <StyledTab name="Sampling Settings" iconName="cog" />
<InputItem label="Sample Count"> <StyledTab name="View Settings" iconName="squareBar" />
<input type="number" {...register("sampleCount")} /> <StyledTab name="Input Variables" iconName="dollar" />
</InputItem> </Tab.List>
<InputItem label="XYPointLength Count"> </div>
<input type="number" {...register("xyPointLength")} /> <div className="flex" style={{ height: height + "px" }}>
</InputItem> <div className="w-1/2">
<InputItem label="Chart Height (in Pixels)"> <Tab.Panels>
<input type="number" {...register("chartHeight")} /> <Tab.Panel>
</InputItem> <div className="border border-slate-200">
<InputItem label="Show Types"> <CodeEditor
<input type="checkbox" {...register("showTypes")} /> value={squiggleString}
</InputItem> onChange={setSquiggleString}
<InputItem label="Show Controls">
<input type="checkbox" {...register("showControls")} />
</InputItem>
<InputItem label="Show Summary Statistics">
<input type="checkbox" {...register("showSummary")} />
</InputItem>
<InputItem label="Editor Width">
<input
type="range"
min="1"
max="100"
className="slider"
{...register("leftSizePercent")}
/>
</InputItem>
<InputItem label="Json Editor for imports">
<>
<JsonEditor
value={importString}
onChange={getChangeJson}
oneLine={false} oneLine={false}
showGutter={true} showGutter={true}
height={100} height={height - 1}
/> />
{importsAreValid ? "Valid" : "Invalid"} </div>
</> </Tab.Panel>
</InputItem> <Tab.Panel>{samplingSettings}</Tab.Panel>
</> <Tab.Panel>{viewSettings}</Tab.Panel>
) : ( <Tab.Panel>{inputVariableSettings}</Tab.Panel>
<CodeEditor </Tab.Panels>
value={squiggleString} </div>
onChange={setSquiggleString}
oneLine={false} <div className="w-1/2 p-2 pl-4">
showGutter={true} <div style={{ maxHeight: height + "px" }}>
height={height - 3} <SquiggleChart
/> squiggleString={squiggleString}
)} environment={env}
</Col> chartSettings={chartSettings}
<Col> height={vars.chartHeight}
<Display maxHeight={height - 3}> showTypes={vars.showTypes}
<SquiggleChart showControls={vars.showControls}
squiggleString={squiggleString} bindings={defaultBindings}
environment={env} jsImports={imports}
chartSettings={chartSettings} showSummary={vars.showSummary}
height={vars.chartHeight} />
showTypes={vars.showTypes} </div>
showControls={vars.showControls} </div>
bindings={defaultBindings} </div>
jsImports={imports} </div>
showSummary={vars.showSummary} </Tab.Group>
/>
</Display>
</Col>
</Row>
</ShowBox>
); );
}; };
export default SquigglePlayground; export default SquigglePlayground;

View File

@ -1,6 +1,5 @@
import SquigglePlayground from "../components/SquigglePlayground"; import SquigglePlayground from "../components/SquigglePlayground";
import { Canvas, Meta, Story, Props } from "@storybook/addon-docs"; import { Canvas, Meta, Story, Props } from "@storybook/addon-docs";
import styled from "styled-components";
<Meta title="Squiggle/SquigglePlayground" component={SquigglePlayground} /> <Meta title="Squiggle/SquigglePlayground" component={SquigglePlayground} />

View File

@ -0,0 +1,4 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind forms;

View File

@ -33,6 +33,7 @@
"from": { "from": {
"data": "con" "data": "con"
}, },
"interpolate": "linear",
"encode": { "encode": {
"update": { "update": {
"x": { "x": {
@ -48,10 +49,10 @@
"value": 0 "value": 0
}, },
"fill": { "fill": {
"value": "#4C78A8" "value": "#739ECC"
}, },
"interpolate": { "interpolate": {
"value": "monotone" "value": "linear"
}, },
"fillOpacity": { "fillOpacity": {
"value": 1 "value": 1

View File

@ -0,0 +1,7 @@
module.exports = {
content: ["./src/**/*.{html,tsx,ts,js,jsx}"],
theme: {
extend: {},
},
plugins: [require("@tailwindcss/forms")],
};

View File

@ -38,7 +38,7 @@
"dependencies": { "dependencies": {
"@stdlib/stats": "^0.0.13", "@stdlib/stats": "^0.0.13",
"jstat": "^1.9.5", "jstat": "^1.9.5",
"mathjs": "^10.5.2", "mathjs": "^10.6.0",
"pdfast": "^0.2.0", "pdfast": "^0.2.0",
"rescript": "^9.1.4" "rescript": "^9.1.4"
}, },
@ -51,18 +51,18 @@
"chalk": "^5.0.1", "chalk": "^5.0.1",
"codecov": "^3.8.3", "codecov": "^3.8.3",
"fast-check": "^2.25.0", "fast-check": "^2.25.0",
"gentype": "^4.3.0", "gentype": "^4.4.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moduleserve": "^0.9.1", "moduleserve": "^0.9.1",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"peggy": "^1.2.0", "peggy": "^2.0.0",
"reanalyze": "^2.19.0", "reanalyze": "^2.22.0",
"rescript-fast-check": "^1.1.1", "rescript-fast-check": "^1.1.1",
"ts-jest": "^27.1.4", "ts-jest": "^27.1.4",
"ts-loader": "^9.3.0", "ts-loader": "^9.3.0",
"ts-node": "^10.8.0", "ts-node": "^10.8.0",
"typescript": "^4.6.3", "typescript": "^4.7.2",
"webpack": "^5.72.1", "webpack": "^5.72.1",
"webpack-cli": "^4.9.2" "webpack-cli": "^4.9.2"
}, },

View File

@ -1,18 +1,22 @@
import * as _ from "lodash"; import * as _ from "lodash";
import { import type {
environment, environment,
expressionValue,
externalBindings,
errorValue,
} from "../rescript/TypescriptInterface.gen";
import {
defaultEnvironment, defaultEnvironment,
evaluatePartialUsingExternalBindings, evaluatePartialUsingExternalBindings,
evaluateUsingOptions, evaluateUsingOptions,
externalBindings,
expressionValue,
errorValue,
foreignFunctionInterface, foreignFunctionInterface,
} from "../rescript/TypescriptInterface.gen"; } from "../rescript/TypescriptInterface.gen";
export { export {
makeSampleSetDist, makeSampleSetDist,
errorValueToString, errorValueToString,
distributionErrorToString, distributionErrorToString,
} from "../rescript/TypescriptInterface.gen";
export type {
distributionError, distributionError,
declarationArg, declarationArg,
declaration, declaration,
@ -30,16 +34,8 @@ import {
import { result, resultMap, tag, tagged } from "./types"; import { result, resultMap, tag, tagged } from "./types";
import { Distribution, shape } from "./distribution"; import { Distribution, shape } from "./distribution";
export { export { Distribution, resultMap, defaultEnvironment };
Distribution, export type { result, shape, environment, lambdaValue, squiggleExpression };
squiggleExpression,
result,
resultMap,
shape,
lambdaValue,
environment,
defaultEnvironment,
};
export let defaultSamplingInputs: environment = { export let defaultSamplingInputs: environment = {
sampleCount: 10000, sampleCount: 10000,

View File

@ -1,5 +1,5 @@
import * as _ from "lodash"; import * as _ from "lodash";
import { import type {
expressionValue, expressionValue,
mixedShape, mixedShape,
sampleSetDist, sampleSetDist,

View File

@ -231,9 +231,8 @@ let doN = (n, fn) => {
} }
let sample = (t: t): float => { let sample = (t: t): float => {
let randomItem = Random.float(1.) let randomItem = Random.float(1.0)
let bar = t |> T.Integral.yToX(randomItem) t |> T.Integral.yToX(randomItem)
bar
} }
let isFloat = (t: t) => let isFloat = (t: t) =>

View File

@ -9,6 +9,7 @@ module Wrappers = {
} }
module Prepare = { module Prepare = {
type t = frValue
type ts = array<frValue> type ts = array<frValue>
type err = string type err = string
@ -26,6 +27,14 @@ module Prepare = {
| _ => Error(impossibleError) | _ => Error(impossibleError)
} }
} }
module Array = {
let openA = (inputs: t): result<ts, err> =>
switch inputs {
| FRValueArray(n) => Ok(n)
| _ => Error(impossibleError)
}
}
} }
module ToValueTuple = { module ToValueTuple = {
@ -55,6 +64,19 @@ module Prepare = {
values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber) values->ToValueArray.Record.twoArgs->E.R.bind(twoDistOrNumber)
} }
} }
module ToArrayRecordPairs = {
let twoArgs = (input: t): result<array<ts>, err> => {
let array = input->ToValueArray.Array.openA
let pairs =
array->E.R.bind(pairs =>
pairs
->E.A2.fmap(xyCoord => [xyCoord]->ToValueArray.Record.twoArgs)
->E.A.R.firstErrorOrOpen
)
pairs
}
}
} }
module Process = { module Process = {

View File

@ -30,9 +30,46 @@ module Declaration = {
} }
} }
let inputsTodist = (inputs: array<FunctionRegistry_Core.frValue>, makeDist) => {
let array = inputs->E.A.unsafe_get(0)->Prepare.ToValueArray.Array.openA
let xyCoords =
array->E.R.bind(xyCoords =>
xyCoords
->E.A2.fmap(xyCoord =>
[xyCoord]->Prepare.ToValueArray.Record.twoArgs->E.R.bind(Prepare.ToValueTuple.twoNumbers)
)
->E.A.R.firstErrorOrOpen
)
let expressionValue =
xyCoords
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
->E.R2.fmap(r => ReducerInterface_ExpressionValue.EvDistribution(PointSet(makeDist(r))))
expressionValue
}
let registry = [ let registry = [
Function.make( Function.make(
~name="FnMake", ~name="toContinuousPointSet",
~definitions=[
FnDefinition.make(
~name="toContinuousPointSet",
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
~run=(inputs, _) => inputsTodist(inputs, r => Continuous(Continuous.make(r))),
),
],
),
Function.make(
~name="toDiscretePointSet",
~definitions=[
FnDefinition.make(
~name="toDiscretePointSet",
~inputs=[FRTypeArray(FRTypeRecord([("x", FRTypeNumeric), ("y", FRTypeNumeric)]))],
~run=(inputs, _) => inputsTodist(inputs, r => Discrete(Discrete.make(r))),
),
],
),
Function.make(
~name="Declaration",
~definitions=[ ~definitions=[
FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => { FnDefinition.make(~name="declareFn", ~inputs=[Declaration.frType], ~run=(inputs, _) => {
inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue inputs->E.A.unsafe_get(0)->Declaration.fromExpressionValue

View File

@ -571,6 +571,7 @@ module A = {
let tail = Belt.Array.sliceToEnd(_, 1) let tail = Belt.Array.sliceToEnd(_, 1)
let zip = Belt.Array.zip let zip = Belt.Array.zip
let unzip = Belt.Array.unzip
let zip3 = (a, b, c) => let zip3 = (a, b, c) =>
Belt.Array.zip(a, b)->Belt.Array.zip(c)->Belt.Array.map((((v1, v2), v3)) => (v1, v2, v3)) Belt.Array.zip(a, b)->Belt.Array.zip(c)->Belt.Array.map((((v1, v2), v3)) => (v1, v2, v3))
// This zips while taking the longest elements of each array. // This zips while taking the longest elements of each array.

View File

@ -148,6 +148,11 @@ module T = {
| None => Ok(attempt) | None => Ok(attempt)
} }
} }
let makeFromZipped = (values: array<(float, float)>) => {
let (xs, ys) = E.A.unzip(values)
make(~xs, ~ys)
}
} }
module Ts = { module Ts = {

View File

@ -20,6 +20,7 @@ const config = {
projectName: "squiggle", // Usually your repo name. projectName: "squiggle", // Usually your repo name.
plugins: [ plugins: [
"docusaurus-tailwindcss",
() => ({ () => ({
configureWebpack(config, isServer, utils, content) { configureWebpack(config, isServer, utils, content) {
return { return {

View File

@ -12,16 +12,17 @@
"format": "prettier --write ." "format": "prettier --write ."
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "2.0.0-beta.20", "@docusaurus/core": "2.0.0-beta.21",
"@docusaurus/preset-classic": "2.0.0-beta.20", "@docusaurus/preset-classic": "2.0.0-beta.21",
"@quri/squiggle-components": "^0.2.20", "@quri/squiggle-components": "^0.2.20",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"docusaurus-tailwindcss": "^0.1.0",
"hast-util-is-element": "2.1.2",
"prism-react-renderer": "^1.3.3", "prism-react-renderer": "^1.3.3",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"remark-math": "^3",
"rehype-katex": "^5", "rehype-katex": "^5",
"hast-util-is-element": "2.1.2" "remark-math": "^3"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [

View File

@ -4,6 +4,10 @@
* work well for content-centric websites. * work well for content-centric websites.
*/ */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can override the default Infima variables here. */ /* You can override the default Infima variables here. */
:root { :root {
--ifm-color-primary: #2488df; --ifm-color-primary: #2488df;

3927
yarn.lock

File diff suppressed because it is too large Load Diff