Merge branch 'develop' into Documentation-auto-generation

* develop: (22 commits)
  components-lint respects components prettierignore
  format main.css and prettierignore base.css
  scoped tailwind preflight, top-level .squiggle wrapper
  grammar: identifiers with modules
  Refactored reducerInterface files
  Added simple scientific units
  Added stdev to table of stats, when needed
  Added stdev and variance to Distribution Operation Constructors
  Quick addition of stdev-variance-min-max-mode
  ⬆️ Bump @types/node from 17.0.38 to 17.0.40
  ⬆️ Bump webpack from 5.72.1 to 5.73.0
  ⬆️ Bump @storybook/react from 6.5.6 to 6.5.7
  ⬆️ Bump ts-node from 10.8.0 to 10.8.1
  ⬆️ Bump @storybook/builder-webpack5 from 6.5.6 to 6.5.7
  ⬆️ Bump peggy from 2.0.0 to 2.0.1
  ⬆️ Bump @storybook/addon-essentials from 6.5.6 to 6.5.7
  ⬆️ Bump @storybook/preset-create-react-app from 4.1.1 to 4.1.2
  ⬆️ Bump @hookform/resolvers from 2.8.10 to 2.9.0
  ⬆️ Bump typescript from 4.7.2 to 4.7.3
  ⬆️ Bump @storybook/manager-webpack5 from 6.5.6 to 6.5.7
  ...
This commit is contained in:
Ozzie Gooen 2022-06-09 16:50:46 -07:00
commit 8fd5c6a1be
34 changed files with 1352 additions and 796 deletions

View File

@ -100,7 +100,7 @@ jobs:
uses: creyD/prettier_action@v4.2 uses: creyD/prettier_action@v4.2
with: with:
dry: true dry: true
prettier_options: --check packages/components prettier_options: --check packages/components --ignore-path packages/components/.prettierignore
components-bundle-build: components-bundle-build:
name: Components bundle and build name: Components bundle and build

View File

@ -1,2 +1,3 @@
dist/ dist/
storybook-static storybook-static
src/styles/base.css

View File

@ -1,4 +1,15 @@
import "../src/tailwind.css"; import "../src/styles/main.css";
import "!style-loader!css-loader!postcss-loader!../src/styles/main.css";
import { SquiggleContainer } from "../src/components/SquiggleContainer";
export const decorators = [
(Story) => (
<SquiggleContainer>
<Story />
</SquiggleContainer>
),
];
export const parameters = { export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" }, actions: { argTypesRegex: "^on[A-Z].*" },
controls: { controls: {

View File

@ -5,7 +5,7 @@
"dependencies": { "dependencies": {
"@headlessui/react": "^1.6.4", "@headlessui/react": "^1.6.4",
"@heroicons/react": "^1.0.6", "@heroicons/react": "^1.0.6",
"@hookform/resolvers": "^2.8.10", "@hookform/resolvers": "^2.9.0",
"@quri/squiggle-lang": "^0.2.8", "@quri/squiggle-lang": "^0.2.8",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
@ -22,42 +22,44 @@
}, },
"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.6", "@storybook/addon-actions": "^6.5.7",
"@storybook/addon-essentials": "^6.5.6", "@storybook/addon-essentials": "^6.5.7",
"@storybook/addon-links": "^6.5.6", "@storybook/addon-links": "^6.5.7",
"@storybook/builder-webpack5": "^6.5.6", "@storybook/builder-webpack5": "^6.5.7",
"@storybook/manager-webpack5": "^6.5.6", "@storybook/manager-webpack5": "^6.5.7",
"@storybook/node-logger": "^6.5.6", "@storybook/node-logger": "^6.5.6",
"@storybook/preset-create-react-app": "^4.1.1", "@storybook/preset-create-react-app": "^4.1.2",
"@storybook/react": "^6.5.6", "@storybook/react": "^6.5.7",
"@tailwindcss/forms": "^0.5.2", "@tailwindcss/forms": "^0.5.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.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.36", "@types/node": "^17.0.40",
"@types/react": "^18.0.9", "@types/react": "^18.0.9",
"@types/react-dom": "^18.0.5", "@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",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
"postcss-cli": "^9.1.0",
"postcss-import": "^14.1.0",
"postcss-loader": "^7.0.0", "postcss-loader": "^7.0.0",
"react-scripts": "^5.0.1", "react-scripts": "^5.0.1",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"tailwindcss": "^3.0.24", "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.7.2", "typescript": "^4.7.3",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
"webpack": "^5.72.1", "webpack": "^5.73.0",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.0" "webpack-dev-server": "^4.9.0"
}, },
"scripts": { "scripts": {
"start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public", "start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build": "tsc -b && tailwindcss -i ./src/tailwind.css -o ./dist/main.css && build-storybook -s public", "build": "tsc -b && postcss ./src/styles/main.css -o ./dist/main.css && build-storybook -s public",
"bundle": "webpack", "bundle": "webpack",
"all": "yarn bundle && yarn build", "all": "yarn bundle && yarn build",
"lint": "prettier --check .", "lint": "prettier --check .",

View File

@ -1,5 +1,7 @@
module.exports = { module.exports = {
plugins: { plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
cssnano: {}, cssnano: {},

View File

@ -4,6 +4,7 @@ import {
InformationCircleIcon, InformationCircleIcon,
CheckCircleIcon, CheckCircleIcon,
} from "@heroicons/react/solid"; } from "@heroicons/react/solid";
import clsx from "clsx";
export const Alert: React.FC<{ export const Alert: React.FC<{
heading: string; heading: string;
@ -23,18 +24,18 @@ export const Alert: React.FC<{
children, children,
}) => { }) => {
return ( return (
<div className={`rounded-md p-4 ${backgroundColor}`}> <div className={clsx("rounded-md p-4", backgroundColor)}>
<div className="flex"> <div className="flex">
<Icon <Icon
className={`h-5 w-5 flex-shrink-0 ${iconColor}`} className={clsx("h-5 w-5 flex-shrink-0", iconColor)}
aria-hidden="true" aria-hidden="true"
/> />
<div className="ml-3"> <div className="ml-3">
<header className={`text-sm font-medium ${headingColor}`}> <header className={clsx("text-sm font-medium", headingColor)}>
{heading} {heading}
</header> </header>
{children && React.Children.count(children) ? ( {children && React.Children.count(children) ? (
<div className={`mt-2 text-sm ${bodyColor}`}>{children}</div> <div className={clsx("mt-2 text-sm", bodyColor)}>{children}</div>
) : null} ) : null}
</div> </div>
</div> </div>

View File

@ -9,6 +9,8 @@ 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 { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
import { useSize } from "react-use"; import { useSize } from "react-use";
import clsx from "clsx";
import { import {
linearXScale, linearXScale,
logXScale, logXScale,
@ -128,7 +130,7 @@ export const CheckBox: React.FC<CheckBoxProps> = ({
onChange={() => onChange(!value)} onChange={() => onChange(!value)}
disabled={disabled} disabled={disabled}
/> />
<label className={disabled ? "text-slate-400" : ""}> {label}</label> <label className={clsx(disabled && "text-slate-400")}> {label}</label>
</span> </span>
); );
}; };
@ -153,6 +155,7 @@ type SummaryTableProps = {
const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => { const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
const mean = distribution.mean(); const mean = distribution.mean();
const stdev = distribution.stdev();
const p5 = distribution.inv(0.05); const p5 = distribution.inv(0.05);
const p10 = distribution.inv(0.1); const p10 = distribution.inv(0.1);
const p25 = distribution.inv(0.25); const p25 = distribution.inv(0.25);
@ -161,6 +164,9 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
const p90 = distribution.inv(0.9); const p90 = distribution.inv(0.9);
const p95 = distribution.inv(0.95); const p95 = distribution.inv(0.95);
const hasResult = (x: result<number, distributionError>): boolean =>
x.tag === "Ok";
const unwrapResult = ( const unwrapResult = (
x: result<number, distributionError> x: result<number, distributionError>
): React.ReactNode => { ): React.ReactNode => {
@ -180,6 +186,7 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
<thead className="bg-slate-50"> <thead className="bg-slate-50">
<tr> <tr>
<TableHeadCell>{"Mean"}</TableHeadCell> <TableHeadCell>{"Mean"}</TableHeadCell>
{hasResult(stdev) && <TableHeadCell>{"Stdev"}</TableHeadCell>}
<TableHeadCell>{"5%"}</TableHeadCell> <TableHeadCell>{"5%"}</TableHeadCell>
<TableHeadCell>{"10%"}</TableHeadCell> <TableHeadCell>{"10%"}</TableHeadCell>
<TableHeadCell>{"25%"}</TableHeadCell> <TableHeadCell>{"25%"}</TableHeadCell>
@ -192,6 +199,7 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
<tbody> <tbody>
<tr> <tr>
<Cell>{unwrapResult(mean)}</Cell> <Cell>{unwrapResult(mean)}</Cell>
{hasResult(stdev) && <Cell>{unwrapResult(stdev)}</Cell>}
<Cell>{unwrapResult(p5)}</Cell> <Cell>{unwrapResult(p5)}</Cell>
<Cell>{unwrapResult(p10)}</Cell> <Cell>{unwrapResult(p10)}</Cell>
<Cell>{unwrapResult(p25)}</Cell> <Cell>{unwrapResult(p25)}</Cell>

View File

@ -0,0 +1,25 @@
import React, { useContext } from "react";
type Props = {
children: React.ReactNode;
};
type SquiggleContextShape = {
containerized: boolean;
};
const SquiggleContext = React.createContext<SquiggleContextShape>({
containerized: false,
});
export const SquiggleContainer: React.FC<Props> = ({ children }) => {
const context = useContext(SquiggleContext);
if (context.containerized) {
return <>{children}</>;
} else {
return (
<SquiggleContext.Provider value={{ containerized: true }}>
<div className="squiggle">{children}</div>
</SquiggleContext.Provider>
);
}
};

View File

@ -15,6 +15,7 @@ import {
defaultBindings, defaultBindings,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
import { SquiggleContainer } from "./SquiggleContainer";
export interface SquiggleEditorProps { export interface SquiggleEditorProps {
/** The input string for squiggle */ /** The input string for squiggle */
@ -64,29 +65,31 @@ export let SquiggleEditor: React.FC<SquiggleEditorProps> = ({
count: diagramCount, count: diagramCount,
}; };
return ( return (
<div> <SquiggleContainer>
<div className="border border-grey-200 p-2 m-4"> <div>
<CodeEditor <div className="border border-grey-200 p-2 m-4">
value={expression} <CodeEditor
onChange={setExpression} value={expression}
oneLine={true} onChange={setExpression}
showGutter={false} oneLine={true}
height={20} showGutter={false}
height={20}
/>
</div>
<SquiggleChart
width={width}
environment={environment}
squiggleString={expression}
chartSettings={chartSettings}
onChange={onChange}
bindings={bindings}
jsImports={jsImports}
showTypes={showTypes}
showControls={showControls}
showSummary={showSummary}
/> />
</div> </div>
<SquiggleChart </SquiggleContainer>
width={width}
environment={environment}
squiggleString={expression}
chartSettings={chartSettings}
onChange={onChange}
bindings={bindings}
jsImports={jsImports}
showTypes={showTypes}
showControls={showControls}
showSummary={showSummary}
/>
</div>
); );
}; };
@ -171,18 +174,22 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
React.useEffect(runSquiggleAndUpdateBindings, [expression]); React.useEffect(runSquiggleAndUpdateBindings, [expression]);
return ( return (
<div> <SquiggleContainer>
<div className="border border-grey-200 p-2 m-4"> <div>
<CodeEditor <div className="border border-grey-200 p-2 m-4">
value={expression} <CodeEditor
onChange={setExpression} value={expression}
oneLine={true} onChange={setExpression}
showGutter={false} oneLine={true}
height={20} showGutter={false}
/> height={20}
/>
</div>
{error !== null ? (
<ErrorAlert heading="Error">{error}</ErrorAlert>
) : null}
</div> </div>
{error !== null ? <ErrorAlert heading="Error">{error}</ErrorAlert> : null} </SquiggleContainer>
</div>
); );
}; };

View File

@ -10,6 +10,7 @@ import {
CogIcon, CogIcon,
CurrencyDollarIcon, CurrencyDollarIcon,
} from "@heroicons/react/solid"; } from "@heroicons/react/solid";
import clsx from "clsx";
import { defaultBindings, environment } from "@quri/squiggle-lang"; import { defaultBindings, environment } from "@quri/squiggle-lang";
@ -17,6 +18,7 @@ import { SquiggleChart } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
import { JsonEditor } from "./JsonEditor"; import { JsonEditor } from "./JsonEditor";
import { ErrorAlert, SuccessAlert } from "./Alert"; import { ErrorAlert, SuccessAlert } from "./Alert";
import { SquiggleContainer } from "./SquiggleContainer";
interface PlaygroundProps { interface PlaygroundProps {
/** The initial squiggle string to put in the playground */ /** The initial squiggle string to put in the playground */
@ -87,10 +89,6 @@ const schema = yup
}) })
.required(); .required();
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ");
}
type StyledTabProps = { type StyledTabProps = {
name: string; name: string;
icon: (props: React.ComponentProps<"svg">) => JSX.Element; icon: (props: React.ComponentProps<"svg">) => JSX.Element;
@ -102,15 +100,13 @@ const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
{({ selected }) => ( {({ selected }) => (
<button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100"> <button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
<span <span
className={classNames( className={clsx(
"p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium", "p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
selected selected && "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
? "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
: ""
)} )}
> >
<Icon <Icon
className={classNames( className={clsx(
"-ml-0.5 mr-2 h-4 w-4", "-ml-0.5 mr-2 h-4 w-4",
selected selected
? "text-slate-500" ? "text-slate-500"
@ -118,11 +114,11 @@ const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
)} )}
/> />
<span <span
className={ className={clsx(
selected selected
? "text-gray-900" ? "text-gray-900"
: "text-gray-600 group-hover:text-gray-900" : "text-gray-600 group-hover:text-gray-900"
} )}
> >
{name} {name}
</span> </span>
@ -160,13 +156,14 @@ function InputItem<T>({
type: "number"; type: "number";
register: UseFormRegister<T>; register: UseFormRegister<T>;
}) { }) {
const 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";
return ( return (
<label className="block"> <label className="block">
<div className="text-sm font-medium text-gray-600 mb-1">{label}</div> <div className="text-sm font-medium text-gray-600 mb-1">{label}</div>
<input type={type} {...register(name)} className={numberStyle} /> <input
type={type}
{...register(name)}
className="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"
/>
</label> </label>
); );
} }
@ -378,54 +375,57 @@ const SquigglePlayground: FC<PlaygroundProps> = ({
); );
return ( return (
<Tab.Group> <SquiggleContainer>
<div className="pb-4"> <Tab.Group>
<Tab.List className="flex w-fit p-0.5 rounded-md bg-slate-100 hover:bg-slate-200"> <div className="pb-4">
<StyledTab name="Code" icon={CodeIcon} /> <Tab.List className="flex w-fit p-0.5 rounded-md bg-slate-100 hover:bg-slate-200">
<StyledTab name="Sampling Settings" icon={CogIcon} /> <StyledTab name="Code" icon={CodeIcon} />
<StyledTab name="View Settings" icon={ChartSquareBarIcon} /> <StyledTab name="Sampling Settings" icon={CogIcon} />
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} /> <StyledTab name="View Settings" icon={ChartSquareBarIcon} />
</Tab.List> <StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
</div> </Tab.List>
<div className="flex" style={{ height }}>
<div className="w-1/2">
<Tab.Panels>
<Tab.Panel>
<div className="border border-slate-200">
<CodeEditor
value={squiggleString}
onChange={setSquiggleString}
oneLine={false}
showGutter={true}
height={height - 1}
/>
</div>
</Tab.Panel>
<Tab.Panel>{samplingSettings}</Tab.Panel>
<Tab.Panel>{viewSettings}</Tab.Panel>
<Tab.Panel>{inputVariableSettings}</Tab.Panel>
</Tab.Panels>
</div> </div>
<div className="flex" style={{ height }}>
<div className="w-1/2">
<Tab.Panels>
<Tab.Panel>
<div className="border border-slate-200">
<CodeEditor
value={squiggleString}
onChange={setSquiggleString}
oneLine={false}
showGutter={true}
height={height - 1}
/>
</div>
</Tab.Panel>
<Tab.Panel>{samplingSettings}</Tab.Panel>
<Tab.Panel>{viewSettings}</Tab.Panel>
<Tab.Panel>{inputVariableSettings}</Tab.Panel>
</Tab.Panels>
</div>
<div className="w-1/2 p-2 pl-4"> <div className="w-1/2 p-2 pl-4">
<div style={{ maxHeight: height }}> <div style={{ maxHeight: height }}>
<SquiggleChart <SquiggleChart
squiggleString={squiggleString} squiggleString={squiggleString}
environment={env} environment={env}
chartSettings={chartSettings} chartSettings={chartSettings}
height={vars.chartHeight} height={vars.chartHeight}
showTypes={vars.showTypes} showTypes={vars.showTypes}
showControls={vars.showControls} showControls={vars.showControls}
bindings={defaultBindings} bindings={defaultBindings}
jsImports={imports} jsImports={imports}
showSummary={vars.showSummary} showSummary={vars.showSummary}
/> />
</div>
</div> </div>
</div> </div>
</div> </Tab.Group>
</Tab.Group> </SquiggleContainer>
); );
}; };
export default SquigglePlayground; export default SquigglePlayground;
export function renderSquigglePlaygroundToDom(props: PlaygroundProps) { export function renderSquigglePlaygroundToDom(props: PlaygroundProps) {
const parent = document.createElement("div"); const parent = document.createElement("div");

View File

@ -9,5 +9,6 @@ export {
default as SquigglePlayground, default as SquigglePlayground,
renderSquigglePlaygroundToDom, renderSquigglePlaygroundToDom,
} from "./components/SquigglePlayground"; } from "./components/SquigglePlayground";
export { SquiggleContainer } from "./components/SquiggleContainer";
export { mergeBindings } from "@quri/squiggle-lang"; export { mergeBindings } from "@quri/squiggle-lang";

View File

@ -0,0 +1,510 @@
.squiggle {
/*
This file contains:
1) Base Tailwind preflight styles
2) Base https://github.com/tailwindlabs/tailwindcss-forms styles
(Both are wrapped in .squiggle)
*/
/*
1. Use a consistent sensible line-height in all browsers.
2. Prevent adjustments of font size after orientation changes in iOS.
3. Use a more readable tab size.
4. Use the user's configured `sans` font-family by default.
*/
/* html { */
line-height: 1.5; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-moz-tab-size: 4; /* 3 */
tab-size: 4; /* 3 */
font-family: theme('fontFamily.sans', ui-sans-serif, system-ui, -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"); /* 4 */
/* } */
/*
1. Remove the margin in all browsers.
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
*/
/* body { */
margin: 0; /* 1 */
line-height: inherit; /* 2 */
/* } */
/*
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: theme('borderColor.DEFAULT', currentColor); /* 2 */
}
::before,
::after {
--tw-content: '';
}
/*
1. Add the correct height in Firefox.
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
3. Ensure horizontal rules are visible by default.
*/
hr {
height: 0; /* 1 */
color: inherit; /* 2 */
border-top-width: 1px; /* 3 */
}
/*
Add the correct text decoration in Chrome, Edge, and Safari.
*/
abbr:where([title]) {
text-decoration: underline dotted;
}
/*
Remove the default font size and weight for headings.
*/
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/*
Reset links to optimize for opt-in styling instead of opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/*
Add the correct font weight in Edge and Safari.
*/
b,
strong {
font-weight: bolder;
}
/*
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp,
pre {
font-family: theme('fontFamily.mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); /* 1 */
font-size: 1em; /* 2 */
}
/*
Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/*
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/*
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
}
/*
1. Change the font styles in all browsers.
2. Remove the margin in Firefox and Safari.
3. Remove default padding in all browsers.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
font-weight: inherit; /* 1 */
line-height: inherit; /* 1 */
color: inherit; /* 1 */
margin: 0; /* 2 */
padding: 0; /* 3 */
}
/*
Remove the inheritance of text transform in Edge and Firefox.
*/
button,
select {
text-transform: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Remove default button styles.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
-webkit-appearance: button; /* 1 */
background-color: transparent; /* 2 */
background-image: none; /* 2 */
}
/*
Use the modern Firefox focus style for all focusable elements.
*/
:-moz-focusring {
outline: auto;
}
/*
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
*/
:-moz-ui-invalid {
box-shadow: none;
}
/*
Add the correct vertical alignment in Chrome and Firefox.
*/
progress {
vertical-align: baseline;
}
/*
Correct the cursor style of increment and decrement buttons in Safari.
*/
::-webkit-inner-spin-button,
::-webkit-outer-spin-button {
height: auto;
}
/*
1. Correct the odd appearance in Chrome and Safari.
2. Correct the outline style in Safari.
*/
[type='search'] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/*
Remove the inner padding in Chrome and Safari on macOS.
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
1. Correct the inability to style clickable types in iOS and Safari.
2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/*
Add the correct display in Chrome and Safari.
*/
summary {
display: list-item;
}
/*
Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
fieldset {
margin: 0;
padding: 0;
}
legend {
padding: 0;
}
ol,
ul,
menu {
list-style: none;
margin: 0;
padding: 0;
}
/*
Prevent resizing textareas horizontally by default.
*/
textarea {
resize: vertical;
}
/*
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
2. Set the default placeholder color to the user's configured gray 400 color.
*/
input::placeholder,
textarea::placeholder {
opacity: 1; /* 1 */
color: theme('colors.gray.400', #9ca3af); /* 2 */
}
/*
Set the default cursor for buttons.
*/
button,
[role="button"] {
cursor: pointer;
}
/*
Make sure disabled buttons don't get the pointer cursor.
*/
:disabled {
cursor: default;
}
/*
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
This can trigger a poorly considered lint error in some tools but is included by design.
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block; /* 1 */
vertical-align: middle; /* 2 */
}
/*
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
*/
img,
video {
max-width: 100%;
height: auto;
}
/* these styles were generated by tailwindcss-forms */
[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select {
appearance: none;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
border-radius: 0px;
padding-top: 0.5rem;
padding-right: 0.75rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
font-size: 1rem;
line-height: 1.5rem;
--tw-shadow: 0 0 #0000;
}
[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
border-color: #2563eb;
}
input::placeholder,textarea::placeholder {
color: #6b7280;
opacity: 1;
}
::-webkit-datetime-edit-fields-wrapper {
padding: 0;
}
::-webkit-date-and-time-value {
min-height: 1.5em;
}
::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field {
padding-top: 0;
padding-bottom: 0;
}
select {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
background-position: right 0.5rem center;
background-repeat: no-repeat;
background-size: 1.5em 1.5em;
padding-right: 2.5rem;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
[multiple] {
background-image: initial;
background-position: initial;
background-repeat: unset;
background-size: initial;
padding-right: 0.75rem;
-webkit-print-color-adjust: unset;
print-color-adjust: unset;
}
[type='checkbox'],[type='radio'] {
appearance: none;
padding: 0;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
display: inline-block;
vertical-align: middle;
background-origin: border-box;
-webkit-user-select: none;
user-select: none;
flex-shrink: 0;
height: 1rem;
width: 1rem;
color: #2563eb;
background-color: #fff;
border-color: #6b7280;
border-width: 1px;
--tw-shadow: 0 0 #0000;
}
[type='checkbox'] {
border-radius: 0px;
}
[type='radio'] {
border-radius: 100%;
}
[type='checkbox']:focus,[type='radio']:focus {
outline: 2px solid transparent;
outline-offset: 2px;
--tw-ring-inset: var(--tw-empty,/*!*/ /*!*/);
--tw-ring-offset-width: 2px;
--tw-ring-offset-color: #fff;
--tw-ring-color: #2563eb;
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
[type='checkbox']:checked,[type='radio']:checked {
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
}
[type='radio']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
}
[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus {
border-color: transparent;
background-color: currentColor;
}
[type='checkbox']:indeterminate {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e");
border-color: transparent;
background-color: currentColor;
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
}
[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus {
border-color: transparent;
background-color: currentColor;
}
[type='file'] {
background: unset;
border-color: inherit;
border-width: 0;
border-radius: 0;
padding: 0;
font-size: unset;
line-height: inherit;
}
[type='file']:focus {
outline: 1px solid ButtonText;
outline: 1px auto -webkit-focus-ring-color;
}
}

View File

@ -0,0 +1,8 @@
@import "./base.css";
@tailwind components;
@tailwind utilities;
/* necessary hack because scoped preflight in ./base.css has higher specificity */
.ace_cursor {
border-left: 2px solid !important;
}

View File

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

View File

@ -3,5 +3,6 @@ module.exports = {
theme: { theme: {
extend: {}, extend: {},
}, },
important: ".squiggle",
plugins: [require("@tailwindcss/forms")], plugins: [require("@tailwindcss/forms")],
}; };

View File

@ -5,7 +5,7 @@ module.exports = {
mode: "production", mode: "production",
devtool: "source-map", devtool: "source-map",
profile: true, profile: true,
entry: ["./src/index.ts", "./src/tailwind.css"], entry: ["./src/index.ts", "./src/styles/main.css"],
module: { module: {
rules: [ rules: [
{ {

View File

@ -232,6 +232,7 @@ describe("Peggy parse", () => {
}) })
describe("unit", () => { describe("unit", () => {
testParse("1m", "{(::fromUnit_m 1)}") testParse("1m", "{(::fromUnit_m 1)}")
testParse("1M", "{(::fromUnit_M 1)}")
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}") testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
}) })
}) })

View File

@ -56,14 +56,14 @@
"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": "^2.0.0", "peggy": "^2.0.1",
"reanalyze": "^2.22.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.1",
"typescript": "^4.7.2", "typescript": "^4.7.3",
"webpack": "^5.72.1", "webpack": "^5.73.0",
"webpack-cli": "^4.9.2" "webpack-cli": "^4.9.2"
}, },
"source": "./src/js/index.ts", "source": "./src/js/index.ts",

View File

@ -11,6 +11,7 @@ import {
import { result, resultMap, Ok } from "./types"; import { result, resultMap, Ok } from "./types";
import { import {
Constructors_mean, Constructors_mean,
Constructors_stdev,
Constructors_sample, Constructors_sample,
Constructors_pdf, Constructors_pdf,
Constructors_cdf, Constructors_cdf,
@ -69,6 +70,10 @@ export class Distribution {
return Constructors_mean({ env: this.env }, this.t); return Constructors_mean({ env: this.env }, this.t);
} }
stdev(): result<number, distributionError> {
return Constructors_stdev({ env: this.env }, this.t);
}
sample(): result<number, distributionError> { sample(): result<number, distributionError> {
return Constructors_sample({ env: this.env }, this.t); return Constructors_sample({ env: this.env }, this.t);
} }

View File

@ -265,6 +265,8 @@ module Constructors = {
module C = DistributionTypes.Constructors.UsingDists module C = DistributionTypes.Constructors.UsingDists
open OutputLocal open OutputLocal
let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR let mean = (~env, dist) => C.mean(dist)->run(~env)->toFloatR
let stdev = (~env, dist) => C.stdev(dist)->run(~env)->toFloatR
let variance = (~env, dist) => C.variance(dist)->run(~env)->toFloatR
let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR let sample = (~env, dist) => C.sample(dist)->run(~env)->toFloatR
let cdf = (~env, dist, f) => C.cdf(dist, f)->run(~env)->toFloatR let cdf = (~env, dist, f) => C.cdf(dist, f)->run(~env)->toFloatR
let inv = (~env, dist, f) => C.inv(dist, f)->run(~env)->toFloatR let inv = (~env, dist, f) => C.inv(dist, f)->run(~env)->toFloatR

View File

@ -49,6 +49,10 @@ module Constructors: {
@genType @genType
let mean: (~env: env, genericDist) => result<float, error> let mean: (~env: env, genericDist) => result<float, error>
@genType @genType
let stdev: (~env: env, genericDist) => result<float, error>
@genType
let variance: (~env: env, genericDist) => result<float, error>
@genType
let sample: (~env: env, genericDist) => result<float, error> let sample: (~env: env, genericDist) => result<float, error>
@genType @genType
let cdf: (~env: env, genericDist, float) => result<float, error> let cdf: (~env: env, genericDist, float) => result<float, error>

View File

@ -30,9 +30,9 @@ module Error = {
@genType @genType
let toString = (err: error): string => let toString = (err: error): string =>
switch err { switch err {
| NotYetImplemented => "Function Not Yet Implemented" | NotYetImplemented => "Function not yet implemented"
| Unreachable => "Unreachable" | Unreachable => "Unreachable"
| DistributionVerticalShiftIsInvalid => "Distribution Vertical Shift is Invalid" | DistributionVerticalShiftIsInvalid => "Distribution vertical shift is invalid"
| ArgumentError(s) => `Argument Error ${s}` | ArgumentError(s) => `Argument Error ${s}`
| LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}` | LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}`
| SampleSetError(TooFewSamples) => "Too Few Samples" | SampleSetError(TooFewSamples) => "Too Few Samples"
@ -68,6 +68,11 @@ module DistributionOperation = {
| #Mean | #Mean
| #Sample | #Sample
| #IntegralSum | #IntegralSum
| #Mode
| #Stdev
| #Min
| #Max
| #Variance
] ]
type toScaleFn = [ type toScaleFn = [
@ -117,6 +122,11 @@ module DistributionOperation = {
| ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})` | ToFloat(#Cdf(r)) => `cdf(${E.Float.toFixed(r)})`
| ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})` | ToFloat(#Inv(r)) => `inv(${E.Float.toFixed(r)})`
| ToFloat(#Mean) => `mean` | ToFloat(#Mean) => `mean`
| ToFloat(#Min) => `min`
| ToFloat(#Max) => `max`
| ToFloat(#Stdev) => `stdev`
| ToFloat(#Variance) => `variance`
| ToFloat(#Mode) => `mode`
| ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})` | ToFloat(#Pdf(r)) => `pdf(${E.Float.toFixed(r)})`
| ToFloat(#Sample) => `sample` | ToFloat(#Sample) => `sample`
| ToFloat(#IntegralSum) => `integralSum` | ToFloat(#IntegralSum) => `integralSum`
@ -151,6 +161,8 @@ module Constructors = {
module UsingDists = { module UsingDists = {
@genType @genType
let mean = (dist): t => FromDist(ToFloat(#Mean), dist) let mean = (dist): t => FromDist(ToFloat(#Mean), dist)
let stdev = (dist): t => FromDist(ToFloat(#Stdev), dist)
let variance = (dist): t => FromDist(ToFloat(#Variance), dist)
let sample = (dist): t => FromDist(ToFloat(#Sample), dist) let sample = (dist): t => FromDist(ToFloat(#Sample), dist)
let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist) let cdf = (dist, x): t => FromDist(ToFloat(#Cdf(x)), dist)
let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist) let inv = (dist, x): t => FromDist(ToFloat(#Inv(x)), dist)

View File

@ -108,7 +108,7 @@ let toFloatOperation = (
) => { ) => {
switch distToFloatOperation { switch distToFloatOperation {
| #IntegralSum => Ok(integralEndY(t)) | #IntegralSum => Ok(integralEndY(t))
| (#Pdf(_) | #Cdf(_) | #Inv(_) | #Mean | #Sample) as op => { | (#Pdf(_) | #Cdf(_) | #Inv(_) | #Mean | #Sample | #Min | #Max) as op => {
let trySymbolicSolution = switch (t: t) { let trySymbolicSolution = switch (t: t) {
| Symbolic(r) => SymbolicDist.T.operate(op, r)->E.R.toOption | Symbolic(r) => SymbolicDist.T.operate(op, r)->E.R.toOption
| _ => None | _ => None
@ -118,6 +118,8 @@ let toFloatOperation = (
| (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some | (SampleSet(sampleSet), #Mean) => SampleSetDist.mean(sampleSet)->Some
| (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some | (SampleSet(sampleSet), #Sample) => SampleSetDist.sample(sampleSet)->Some
| (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some | (SampleSet(sampleSet), #Inv(r)) => SampleSetDist.percentile(sampleSet, r)->Some
| (SampleSet(sampleSet), #Min) => SampleSetDist.min(sampleSet)->Some
| (SampleSet(sampleSet), #Max) => SampleSetDist.max(sampleSet)->Some
| _ => None | _ => None
} }
@ -130,6 +132,16 @@ let toFloatOperation = (
} }
} }
} }
| (#Stdev | #Variance | #Mode) as op =>
switch t {
| SampleSet(s) =>
switch op {
| #Stdev => SampleSetDist.stdev(s)->Ok
| #Variance => SampleSetDist.variance(s)->Ok
| #Mode => SampleSetDist.mode(s)->Ok
}
| _ => Error(DistributionTypes.NotYetImplemented)
}
} }
} }

View File

@ -254,6 +254,8 @@ let operate = (distToFloatOp: Operation.distToFloatOperation, s): float =>
| #Inv(f) => inv(f, s) | #Inv(f) => inv(f, s)
| #Sample => sample(s) | #Sample => sample(s)
| #Mean => T.mean(s) | #Mean => T.mean(s)
| #Min => T.minX(s)
| #Max => T.maxX(s)
} }
let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineError> => let toSparkline = (t: t, bucketCount): result<string, PointSetTypes.sparklineError> =>

View File

@ -449,6 +449,8 @@ module T = {
| #Cdf(f) => Ok(cdf(f, s)) | #Cdf(f) => Ok(cdf(f, s))
| #Pdf(f) => Ok(pdf(f, s)) | #Pdf(f) => Ok(pdf(f, s))
| #Inv(f) => Ok(inv(f, s)) | #Inv(f) => Ok(inv(f, s))
| #Min => Ok(min(s))
| #Max => Ok(max(s))
| #Sample => Ok(sample(s)) | #Sample => Ok(sample(s))
| #Mean => mean(s) | #Mean => mean(s)
} }

View File

@ -224,31 +224,23 @@ postOperator = indexedValue
indexedValue indexedValue
= collectionElement = collectionElement
/ recordElement
/ atom / atom
collectionElement collectionElement
= head:atom &('['/'('/'.') = head:atom &('['/'('/'.')
tail:( tail:(
_ '[' _nl arg:expression _nl ']' {return {fn: postOperatorToFunction['[]'], args: [arg]}} _ '[' _nl arg:expression _nl ']' {return {fn: postOperatorToFunction['[]'], args: [arg]}}
/ _ '(' _nl args:array_functionArguments _nl ')' {return {fn: postOperatorToFunction['()'], args: args}} / _ '(' _nl args:array_functionArguments _nl ')' {return {fn: postOperatorToFunction['()'], args: args}}
/ '.' arg:$dollarIdentifier {return {fn: postOperatorToFunction['[]'], args: [nodeString(arg)]}} / '.' arg:$dollarIdentifier {return {fn: postOperatorToFunction['[]'], args: [nodeString(arg)]}}
)* )*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return makeFunctionCall(element.fn, [result, ...element.args]) return makeFunctionCall(element.fn, [result, ...element.args])
}, head)} }, head)}
array_functionArguments array_functionArguments
= head:expression tail:(_ ',' _nl @expression)* = head:expression tail:(_ ',' _nl @expression)*
{ return [head, ...tail]; } { return [head, ...tail]; }
recordElement
= head:dollarIdentifier &'.'
tail:(_ '.' _nl arg:$dollarIdentifier {return {fn: postOperatorToFunction['.'], args: [nodeString(arg)]}})*
{ return tail.reduce(function(result, element) {
return makeFunctionCall(element.fn, [result, ...element.args])
}, head)}
atom atom
= '(' _nl expression:expression _nl ')' {return expression} = '(' _nl expression:expression _nl ')' {return expression}
/ basicValue / basicValue
@ -259,19 +251,36 @@ basicLiteral
= string = string
/ number / number
/ boolean / boolean
/ dollarIdentifierWithModule
/ dollarIdentifier / dollarIdentifier
dollarIdentifierWithModule 'identifier'
= head:moduleIdentifier
tail:('.' _nl @moduleIdentifier)* '.' _nl
final:dollarIdentifier
{ tail.push(final);
return tail.reduce(function(result, element) {
return makeFunctionCall(postOperatorToFunction['[]'], [result, element])
}, head)}
identifier 'identifier' identifier 'identifier'
= ([_a-z]+[_a-z0-9]i*) {return nodeIdentifier(text())} = ([_a-z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
unitIdentifier 'identifier'
= ([_a-zA-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
dollarIdentifier '$identifier' dollarIdentifier '$identifier'
= ([\$_a-z]+[\$_a-z0-9]i*) {return nodeIdentifier(text())} = ([\$_a-z]+[\$_a-z0-9]i*) {return nodeIdentifier(text())}
moduleIdentifier 'identifier'
= ([A-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
string 'string' string 'string'
= characters:("'" @([^'])* "'") {return nodeString(characters.join(''))} = characters:("'" @([^'])* "'") {return nodeString(characters.join(''))}
/ characters:('"' @([^"])* '"') {return nodeString(characters.join(''))} / characters:('"' @([^"])* '"') {return nodeString(characters.join(''))}
number = number:(float / integer) unit:identifier? number = number:(float / integer) unit:unitIdentifier?
{ {
if (unit === null) if (unit === null)
{ return number } { return number }
@ -335,7 +344,7 @@ recordConstructor 'record'
_ 'whitespace' _ 'whitespace'
= whiteSpaceCharactersOrComment* = whiteSpaceCharactersOrComment*
_nl 'optional whitespace or newline' _nl 'whitespace or newline'
= (whiteSpaceCharactersOrComment / commentOrNewLine)* = (whiteSpaceCharactersOrComment / commentOrNewLine)*
__ 'whitespace' __ 'whitespace'

View File

@ -0,0 +1,27 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}

View File

@ -1,32 +1,7 @@
module EV = ReducerInterface_ExpressionValue module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue type expressionValue = EV.expressionValue
let dateDispatch = (call: EV.functionCall, _: DistributionOperation.env): option< let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}
let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>, result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => { > => {
switch call { switch call {
@ -55,16 +30,3 @@ let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): op
| _ => None | _ => None
} }
} }
let dispatch = (call: EV.functionCall, env: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch dateDispatch(call, env) {
| Some(r) => Some(r)
| None =>
switch durationDispatch(call, env) {
| Some(r) => Some(r)
| None => None
}
}
}

View File

@ -14,15 +14,27 @@ type expressionValue = ExpressionValue.expressionValue
Map external calls of Reducer Map external calls of Reducer
*/ */
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
let registry = FunctionRegistry_Library.registry
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment, chain): result< let dispatch = (call: ExpressionValue.functionCall, environment, chain): result<
expressionValue, expressionValue,
'e, 'e,
> => > => {
switch ReducerInterface_GenericDistribution.dispatch(call, environment) { E.A.O.firstSomeFn([
| Some(r) => r () => ReducerInterface_GenericDistribution.dispatch(call, environment),
| None => () => ReducerInterface_Date.dispatch(call, environment),
ReducerInterface_DateTime.dispatch(call, environment) |> E.O.default(chain(call, environment)) () => ReducerInterface_Duration.dispatch(call, environment),
} () => ReducerInterface_Number.dispatch(call, environment),
() => tryRegistry(call, environment),
])->E.O2.default(chain(call, environment))
}
/* /*
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally. If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.

View File

@ -207,7 +207,18 @@ let dispatchToGenericOutput = (
| ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env) | ("sample", [EvDistribution(dist)]) => Helpers.toFloatFn(#Sample, dist, ~env)
| ("sampleN", [EvDistribution(dist), EvNumber(n)]) => | ("sampleN", [EvDistribution(dist), EvNumber(n)]) =>
Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n)))) Some(FloatArray(GenericDist.sampleN(dist, Belt.Int.fromFloat(n))))
| ("mean", [EvDistribution(dist)]) => Helpers.toFloatFn(#Mean, dist, ~env) | (("mean" | "stdev" | "variance" | "min" | "max" | "mode") as op, [EvDistribution(dist)]) => {
let fn = switch op {
| "mean" => #Mean
| "stdev" => #Stdev
| "variance" => #Variance
| "min" => #Min
| "max" => #Max
| "mode" => #Mode
| _ => #Mean
}
Helpers.toFloatFn(fn, dist, ~env)
}
| ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env) | ("integralSum", [EvDistribution(dist)]) => Helpers.toFloatFn(#IntegralSum, dist, ~env)
| ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env) | ("toString", [EvDistribution(dist)]) => Helpers.toStringFn(ToString, dist, ~env)
| ("toSparkline", [EvDistribution(dist)]) => | ("toSparkline", [EvDistribution(dist)]) =>
@ -348,20 +359,5 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
| GenDistError(err) => Error(REDistributionError(err)) | GenDistError(err) => Error(REDistributionError(err))
} }
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call. let dispatch = (call: ExpressionValue.functionCall, environment) =>
let registry = FunctionRegistry_Library.registry dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment) => {
let regularDispatch =
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)
switch regularDispatch {
| Some(x) => Some(x)
| None => tryRegistry(call, environment)
}
}

View File

@ -0,0 +1,45 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
module ScientificUnit = {
let nameToMultiplier = str =>
switch str {
| "n" => Some(1E-9)
| "m" => Some(1E-3)
| "k" => Some(1E3)
| "M" => Some(1E6)
| "B" => Some(1E9)
| "G" => Some(1E9)
| "T" => Some(1E12)
| "P" => Some(1E15)
| _ => None
}
let getMultiplier = (r: string) => {
let match = Js.String2.match_(r, %re(`/fromUnit_([_a-zA-Z]*)/`))
switch match {
| Some([_, unit]) => nameToMultiplier(unit)
| _ => None
}
}
}
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| (
("fromUnit_n"
| "fromUnit_m"
| "fromUnit_k"
| "fromUnit_M"
| "fromUnit_B"
| "fromUnit_G"
| "fromUnit_T"
| "fromUnit_P") as op,
[EvNumber(f)],
) =>
op->ScientificUnit.getMultiplier->E.O2.fmap(multiplier => EV.EvNumber(f *. multiplier)->Ok)
| _ => None
}
}

View File

@ -26,6 +26,8 @@ type distToFloatOperation = [
| #Inv(float) | #Inv(float)
| #Mean | #Mean
| #Sample | #Sample
| #Min
| #Max
] ]
module Convolution = { module Convolution = {

View File

@ -16,7 +16,6 @@
"@docusaurus/preset-classic": "2.0.0-beta.21", "@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", "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",

1073
yarn.lock

File diff suppressed because it is too large Load Diff