Merge pull request #1094 from quantified-uncertainty/danger-adjustments
Danger adjustments
This commit is contained in:
		
						commit
						6c3f719e74
					
				
							
								
								
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -12,9 +12,13 @@ updates:
 | 
			
		|||
    commit-message:
 | 
			
		||||
      prefix: "⬆️"
 | 
			
		||||
    open-pull-requests-limit: 100
 | 
			
		||||
    labels:
 | 
			
		||||
      - "dependencies"
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
    directory: "/"
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: "weekly"
 | 
			
		||||
    commit-message:
 | 
			
		||||
      prefix: "⬆️"
 | 
			
		||||
    labels:
 | 
			
		||||
      - "dependencies"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@quri/squiggle-components",
 | 
			
		||||
  "version": "0.3.2",
 | 
			
		||||
  "version": "0.4.0-alpha.1",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@floating-ui/react-dom": "^1.0.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
    "@headlessui/react": "^1.6.6",
 | 
			
		||||
    "@heroicons/react": "^1.0.6",
 | 
			
		||||
    "@hookform/resolvers": "^2.9.7",
 | 
			
		||||
    "@quri/squiggle-lang": "^0.3.0",
 | 
			
		||||
    "@quri/squiggle-lang": "^0.4.0-alpha.0",
 | 
			
		||||
    "@react-hook/size": "^2.1.2",
 | 
			
		||||
    "clsx": "^1.2.1",
 | 
			
		||||
    "framer-motion": "^7.2.1",
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@
 | 
			
		|||
    "@types/jest": "^27.5.0",
 | 
			
		||||
    "@types/lodash": "^4.14.184",
 | 
			
		||||
    "@types/node": "^18.7.13",
 | 
			
		||||
    "@types/react": "^18.0.9",
 | 
			
		||||
    "@types/react": "^18.0.18",
 | 
			
		||||
    "@types/styled-components": "^5.1.26",
 | 
			
		||||
    "@types/webpack": "^5.28.0",
 | 
			
		||||
    "cross-env": "^7.0.3",
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +66,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "start": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
 | 
			
		||||
    "build:cjs": "tsc -b",
 | 
			
		||||
    "build:cjs": "rm -rf dist/src && tsc -b",
 | 
			
		||||
    "build:css": "postcss ./src/styles/main.css -o ./dist/main.css",
 | 
			
		||||
    "build:storybook": "build-storybook -s public",
 | 
			
		||||
    "build": "yarn run build:cjs && yarn run build:css && yarn run build:storybook",
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +74,7 @@
 | 
			
		|||
    "all": "yarn bundle && yarn build",
 | 
			
		||||
    "lint": "prettier --check .",
 | 
			
		||||
    "format": "prettier --write .",
 | 
			
		||||
    "prepack": "yarn bundle && tsc -b"
 | 
			
		||||
    "prepack": "yarn run build:cjs && yarn run bundle"
 | 
			
		||||
  },
 | 
			
		||||
  "eslintConfig": {
 | 
			
		||||
    "extends": [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
import * as React from "react";
 | 
			
		||||
import {
 | 
			
		||||
  Distribution,
 | 
			
		||||
  SqDistribution,
 | 
			
		||||
  result,
 | 
			
		||||
  distributionError,
 | 
			
		||||
  distributionErrorToString,
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  SqDistributionError,
 | 
			
		||||
  resultMap,
 | 
			
		||||
  SqRecord,
 | 
			
		||||
  environment,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { Vega } from "react-vega";
 | 
			
		||||
import { ErrorAlert } from "./Alert";
 | 
			
		||||
| 
						 | 
				
			
			@ -28,17 +28,16 @@ export type DistributionPlottingSettings = {
 | 
			
		|||
 | 
			
		||||
export type DistributionChartProps = {
 | 
			
		||||
  plot: Plot;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height: number;
 | 
			
		||||
} & DistributionPlottingSettings;
 | 
			
		||||
 | 
			
		||||
export function defaultPlot(distribution: Distribution): Plot {
 | 
			
		||||
export function defaultPlot(distribution: SqDistribution): Plot {
 | 
			
		||||
  return { distributions: [{ name: "default", distribution }] };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function makePlot(record: {
 | 
			
		||||
  [key: string]: squiggleExpression;
 | 
			
		||||
}): Plot | void {
 | 
			
		||||
export function makePlot(record: SqRecord): Plot | void {
 | 
			
		||||
  const plotResult = parsePlot(record);
 | 
			
		||||
  if (plotResult.tag === "Ok") {
 | 
			
		||||
    return plotResult.value;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,22 +45,29 @@ export function makePlot(record: {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
 | 
			
		||||
  const { plot, height, showSummary, width, logX, actions = false } = props;
 | 
			
		||||
  const {
 | 
			
		||||
    plot,
 | 
			
		||||
    environment,
 | 
			
		||||
    height,
 | 
			
		||||
    showSummary,
 | 
			
		||||
    width,
 | 
			
		||||
    logX,
 | 
			
		||||
    actions = false,
 | 
			
		||||
  } = props;
 | 
			
		||||
  const [sized] = useSize((size) => {
 | 
			
		||||
    let shapes = flattenResult(
 | 
			
		||||
      plot.distributions.map((x) =>
 | 
			
		||||
        resultMap(x.distribution.pointSet(), (shape) => ({
 | 
			
		||||
    const shapes = flattenResult(
 | 
			
		||||
      plot.distributions.map((x) => {
 | 
			
		||||
        return resultMap(x.distribution.pointSet(environment), (pointSet) => ({
 | 
			
		||||
          ...pointSet.asShape(),
 | 
			
		||||
          name: x.name,
 | 
			
		||||
          // color: x.color, // not supported yet
 | 
			
		||||
          continuous: shape.continuous,
 | 
			
		||||
          discrete: shape.discrete,
 | 
			
		||||
        }))
 | 
			
		||||
      )
 | 
			
		||||
        }));
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
    if (shapes.tag === "Error") {
 | 
			
		||||
      return (
 | 
			
		||||
        <ErrorAlert heading="Distribution Error">
 | 
			
		||||
          {distributionErrorToString(shapes.value)}
 | 
			
		||||
          {shapes.value.toString()}
 | 
			
		||||
        </ErrorAlert>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +102,10 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
 | 
			
		|||
        )}
 | 
			
		||||
        <div className="flex justify-center">
 | 
			
		||||
          {showSummary && plot.distributions.length === 1 && (
 | 
			
		||||
            <SummaryTable distribution={plot.distributions[0].distribution} />
 | 
			
		||||
            <SummaryTable
 | 
			
		||||
              distribution={plot.distributions[0].distribution}
 | 
			
		||||
              environment={environment}
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -120,32 +129,36 @@ const Cell: React.FC<{ children: React.ReactNode }> = ({ children }) => (
 | 
			
		|||
);
 | 
			
		||||
 | 
			
		||||
type SummaryTableProps = {
 | 
			
		||||
  distribution: Distribution;
 | 
			
		||||
  distribution: SqDistribution;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
 | 
			
		||||
  const mean = distribution.mean();
 | 
			
		||||
  const stdev = distribution.stdev();
 | 
			
		||||
  const p5 = distribution.inv(0.05);
 | 
			
		||||
  const p10 = distribution.inv(0.1);
 | 
			
		||||
  const p25 = distribution.inv(0.25);
 | 
			
		||||
  const p50 = distribution.inv(0.5);
 | 
			
		||||
  const p75 = distribution.inv(0.75);
 | 
			
		||||
  const p90 = distribution.inv(0.9);
 | 
			
		||||
  const p95 = distribution.inv(0.95);
 | 
			
		||||
const SummaryTable: React.FC<SummaryTableProps> = ({
 | 
			
		||||
  distribution,
 | 
			
		||||
  environment,
 | 
			
		||||
}) => {
 | 
			
		||||
  const mean = distribution.mean(environment);
 | 
			
		||||
  const stdev = distribution.stdev(environment);
 | 
			
		||||
  const p5 = distribution.inv(environment, 0.05);
 | 
			
		||||
  const p10 = distribution.inv(environment, 0.1);
 | 
			
		||||
  const p25 = distribution.inv(environment, 0.25);
 | 
			
		||||
  const p50 = distribution.inv(environment, 0.5);
 | 
			
		||||
  const p75 = distribution.inv(environment, 0.75);
 | 
			
		||||
  const p90 = distribution.inv(environment, 0.9);
 | 
			
		||||
  const p95 = distribution.inv(environment, 0.95);
 | 
			
		||||
 | 
			
		||||
  const hasResult = (x: result<number, distributionError>): boolean =>
 | 
			
		||||
  const hasResult = (x: result<number, SqDistributionError>): boolean =>
 | 
			
		||||
    x.tag === "Ok";
 | 
			
		||||
 | 
			
		||||
  const unwrapResult = (
 | 
			
		||||
    x: result<number, distributionError>
 | 
			
		||||
    x: result<number, SqDistributionError>
 | 
			
		||||
  ): React.ReactNode => {
 | 
			
		||||
    if (x.tag === "Ok") {
 | 
			
		||||
      return <NumberShower number={x.value} />;
 | 
			
		||||
    } else {
 | 
			
		||||
      return (
 | 
			
		||||
        <ErrorAlert heading="Distribution Error">
 | 
			
		||||
          {distributionErrorToString(x.value)}
 | 
			
		||||
          {x.value.toString()}
 | 
			
		||||
        </ErrorAlert>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,5 @@
 | 
			
		|||
import * as React from "react";
 | 
			
		||||
import {
 | 
			
		||||
  lambdaValue,
 | 
			
		||||
  environment,
 | 
			
		||||
  runForeign,
 | 
			
		||||
  errorValueToString,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { SqLambda, environment, SqValueTag } from "@quri/squiggle-lang";
 | 
			
		||||
import { FunctionChart1Dist } from "./FunctionChart1Dist";
 | 
			
		||||
import { FunctionChart1Number } from "./FunctionChart1Number";
 | 
			
		||||
import { DistributionPlottingSettings } from "./DistributionChart";
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +12,7 @@ export type FunctionChartSettings = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
interface FunctionChartProps {
 | 
			
		||||
  fn: lambdaValue;
 | 
			
		||||
  fn: SqLambda;
 | 
			
		||||
  chartSettings: FunctionChartSettings;
 | 
			
		||||
  distributionPlotSettings: DistributionPlottingSettings;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,8 +33,8 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
 | 
			
		|||
      </MessageAlert>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  const result1 = runForeign(fn, [chartSettings.start], environment);
 | 
			
		||||
  const result2 = runForeign(fn, [chartSettings.stop], environment);
 | 
			
		||||
  const result1 = fn.call([chartSettings.start]);
 | 
			
		||||
  const result2 = fn.call([chartSettings.stop]);
 | 
			
		||||
  const getValidResult = () => {
 | 
			
		||||
    if (result1.tag === "Ok") {
 | 
			
		||||
      return result1;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,14 +48,12 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
 | 
			
		|||
 | 
			
		||||
  if (validResult.tag === "Error") {
 | 
			
		||||
    return (
 | 
			
		||||
      <ErrorAlert heading="Error">
 | 
			
		||||
        {errorValueToString(validResult.value)}
 | 
			
		||||
      </ErrorAlert>
 | 
			
		||||
      <ErrorAlert heading="Error">{validResult.value.toString()}</ErrorAlert>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (validResult.value.tag) {
 | 
			
		||||
    case "distribution":
 | 
			
		||||
    case SqValueTag.Distribution:
 | 
			
		||||
      return (
 | 
			
		||||
        <FunctionChart1Dist
 | 
			
		||||
          fn={fn}
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +63,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
 | 
			
		|||
          distributionPlotSettings={distributionPlotSettings}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    case "number":
 | 
			
		||||
    case SqValueTag.Number:
 | 
			
		||||
      return (
 | 
			
		||||
        <FunctionChart1Number
 | 
			
		||||
          fn={fn}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,14 +2,13 @@ import * as React from "react";
 | 
			
		|||
import _ from "lodash";
 | 
			
		||||
import type { Spec } from "vega";
 | 
			
		||||
import {
 | 
			
		||||
  Distribution,
 | 
			
		||||
  SqDistribution,
 | 
			
		||||
  result,
 | 
			
		||||
  lambdaValue,
 | 
			
		||||
  SqLambda,
 | 
			
		||||
  environment,
 | 
			
		||||
  runForeign,
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  errorValue,
 | 
			
		||||
  errorValueToString,
 | 
			
		||||
  SqError,
 | 
			
		||||
  SqValue,
 | 
			
		||||
  SqValueTag,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { createClassFromSpec } from "react-vega";
 | 
			
		||||
import * as percentilesSpec from "../vega-specs/spec-percentiles.json";
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +45,7 @@ export type FunctionChartSettings = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
interface FunctionChart1DistProps {
 | 
			
		||||
  fn: lambdaValue;
 | 
			
		||||
  fn: SqLambda;
 | 
			
		||||
  chartSettings: FunctionChartSettings;
 | 
			
		||||
  distributionPlotSettings: DistributionPlottingSettings;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
| 
						 | 
				
			
			@ -77,9 +76,17 @@ type errors = _.Dictionary<
 | 
			
		|||
  }[]
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
type point = { x: number; value: result<Distribution, string> };
 | 
			
		||||
type point = { x: number; value: result<SqDistribution, string> };
 | 
			
		||||
 | 
			
		||||
let getPercentiles = ({ chartSettings, fn, environment }) => {
 | 
			
		||||
let getPercentiles = ({
 | 
			
		||||
  chartSettings,
 | 
			
		||||
  fn,
 | 
			
		||||
  environment,
 | 
			
		||||
}: {
 | 
			
		||||
  chartSettings: FunctionChartSettings;
 | 
			
		||||
  fn: SqLambda;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
}) => {
 | 
			
		||||
  let chartPointsToRender = _rangeByCount(
 | 
			
		||||
    chartSettings.start,
 | 
			
		||||
    chartSettings.stop,
 | 
			
		||||
| 
						 | 
				
			
			@ -87,9 +94,9 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
 | 
			
		|||
  );
 | 
			
		||||
 | 
			
		||||
  let chartPointsData: point[] = chartPointsToRender.map((x) => {
 | 
			
		||||
    let result = runForeign(fn, [x], environment);
 | 
			
		||||
    let result = fn.call([x]);
 | 
			
		||||
    if (result.tag === "Ok") {
 | 
			
		||||
      if (result.value.tag === "distribution") {
 | 
			
		||||
      if (result.value.tag === SqValueTag.Distribution) {
 | 
			
		||||
        return { x, value: { tag: "Ok", value: result.value.value } };
 | 
			
		||||
      } else {
 | 
			
		||||
        return {
 | 
			
		||||
| 
						 | 
				
			
			@ -104,13 +111,13 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
 | 
			
		|||
    } else {
 | 
			
		||||
      return {
 | 
			
		||||
        x,
 | 
			
		||||
        value: { tag: "Error", value: errorValueToString(result.value) },
 | 
			
		||||
        value: { tag: "Error", value: result.value.toString() },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  let initialPartition: [
 | 
			
		||||
    { x: number; value: Distribution }[],
 | 
			
		||||
    { x: number; value: SqDistribution }[],
 | 
			
		||||
    { x: number; value: string }[]
 | 
			
		||||
  ] = [[], []];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,26 +133,23 @@ let getPercentiles = ({ chartSettings, fn, environment }) => {
 | 
			
		|||
  let groupedErrors: errors = _.groupBy(errors, (x) => x.value);
 | 
			
		||||
 | 
			
		||||
  let percentiles: percentiles = functionImage.map(({ x, value }) => {
 | 
			
		||||
    // We convert it to to a pointSet distribution first, so that in case its a sample set
 | 
			
		||||
    // distribution, it doesn't internally convert it to a pointSet distribution for every
 | 
			
		||||
    // single inv() call.
 | 
			
		||||
    let toPointSet: Distribution = unwrap(value.toPointSet());
 | 
			
		||||
    return {
 | 
			
		||||
    const res = {
 | 
			
		||||
      x: x,
 | 
			
		||||
      p1: unwrap(toPointSet.inv(0.01)),
 | 
			
		||||
      p5: unwrap(toPointSet.inv(0.05)),
 | 
			
		||||
      p10: unwrap(toPointSet.inv(0.1)),
 | 
			
		||||
      p20: unwrap(toPointSet.inv(0.2)),
 | 
			
		||||
      p30: unwrap(toPointSet.inv(0.3)),
 | 
			
		||||
      p40: unwrap(toPointSet.inv(0.4)),
 | 
			
		||||
      p50: unwrap(toPointSet.inv(0.5)),
 | 
			
		||||
      p60: unwrap(toPointSet.inv(0.6)),
 | 
			
		||||
      p70: unwrap(toPointSet.inv(0.7)),
 | 
			
		||||
      p80: unwrap(toPointSet.inv(0.8)),
 | 
			
		||||
      p90: unwrap(toPointSet.inv(0.9)),
 | 
			
		||||
      p95: unwrap(toPointSet.inv(0.95)),
 | 
			
		||||
      p99: unwrap(toPointSet.inv(0.99)),
 | 
			
		||||
      p1: unwrap(value.inv(environment, 0.01)),
 | 
			
		||||
      p5: unwrap(value.inv(environment, 0.05)),
 | 
			
		||||
      p10: unwrap(value.inv(environment, 0.1)),
 | 
			
		||||
      p20: unwrap(value.inv(environment, 0.2)),
 | 
			
		||||
      p30: unwrap(value.inv(environment, 0.3)),
 | 
			
		||||
      p40: unwrap(value.inv(environment, 0.4)),
 | 
			
		||||
      p50: unwrap(value.inv(environment, 0.5)),
 | 
			
		||||
      p60: unwrap(value.inv(environment, 0.6)),
 | 
			
		||||
      p70: unwrap(value.inv(environment, 0.7)),
 | 
			
		||||
      p80: unwrap(value.inv(environment, 0.8)),
 | 
			
		||||
      p90: unwrap(value.inv(environment, 0.9)),
 | 
			
		||||
      p95: unwrap(value.inv(environment, 0.95)),
 | 
			
		||||
      p99: unwrap(value.inv(environment, 0.99)),
 | 
			
		||||
    };
 | 
			
		||||
    return res;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return { percentiles, errors: groupedErrors };
 | 
			
		||||
| 
						 | 
				
			
			@ -168,19 +172,20 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
 | 
			
		|||
  const signalListeners = { mousemove: handleHover, mouseout: handleOut };
 | 
			
		||||
 | 
			
		||||
  //TODO: This custom error handling is a bit hacky and should be improved.
 | 
			
		||||
  let mouseItem: result<squiggleExpression, errorValue> = !!mouseOverlay
 | 
			
		||||
    ? runForeign(fn, [mouseOverlay], environment)
 | 
			
		||||
  let mouseItem: result<SqValue, SqError> = !!mouseOverlay
 | 
			
		||||
    ? fn.call([mouseOverlay])
 | 
			
		||||
    : {
 | 
			
		||||
        tag: "Error",
 | 
			
		||||
        value: {
 | 
			
		||||
          tag: "RETodo",
 | 
			
		||||
          value: "Hover x-coordinate returned NaN. Expected a number.",
 | 
			
		||||
        },
 | 
			
		||||
        value: SqError.createOtherError(
 | 
			
		||||
          "Hover x-coordinate returned NaN. Expected a number."
 | 
			
		||||
        ),
 | 
			
		||||
      };
 | 
			
		||||
  let showChart =
 | 
			
		||||
    mouseItem.tag === "Ok" && mouseItem.value.tag === "distribution" ? (
 | 
			
		||||
    mouseItem.tag === "Ok" &&
 | 
			
		||||
    mouseItem.value.tag === SqValueTag.Distribution ? (
 | 
			
		||||
      <DistributionChart
 | 
			
		||||
        plot={defaultPlot(mouseItem.value.value)}
 | 
			
		||||
        environment={environment}
 | 
			
		||||
        width={400}
 | 
			
		||||
        height={50}
 | 
			
		||||
        {...distributionPlotSettings}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,11 @@
 | 
			
		|||
import * as React from "react";
 | 
			
		||||
import _ from "lodash";
 | 
			
		||||
import type { Spec } from "vega";
 | 
			
		||||
import {
 | 
			
		||||
  result,
 | 
			
		||||
  lambdaValue,
 | 
			
		||||
  environment,
 | 
			
		||||
  runForeign,
 | 
			
		||||
  errorValueToString,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { result, SqLambda, environment, SqValueTag } from "@quri/squiggle-lang";
 | 
			
		||||
import { createClassFromSpec } from "react-vega";
 | 
			
		||||
import * as lineChartSpec from "../vega-specs/spec-line-chart.json";
 | 
			
		||||
import { ErrorAlert } from "./Alert";
 | 
			
		||||
import { squiggleValueTag } from "@quri/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag";
 | 
			
		||||
 | 
			
		||||
let SquiggleLineChart = createClassFromSpec({
 | 
			
		||||
  spec: lineChartSpec as Spec,
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +25,7 @@ export type FunctionChartSettings = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
interface FunctionChart1NumberProps {
 | 
			
		||||
  fn: lambdaValue;
 | 
			
		||||
  fn: SqLambda;
 | 
			
		||||
  chartSettings: FunctionChartSettings;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
  height: number;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +33,15 @@ interface FunctionChart1NumberProps {
 | 
			
		|||
 | 
			
		||||
type point = { x: number; value: result<number, string> };
 | 
			
		||||
 | 
			
		||||
let getFunctionImage = ({ chartSettings, fn, environment }) => {
 | 
			
		||||
let getFunctionImage = ({
 | 
			
		||||
  chartSettings,
 | 
			
		||||
  fn,
 | 
			
		||||
  environment,
 | 
			
		||||
}: {
 | 
			
		||||
  chartSettings: FunctionChartSettings;
 | 
			
		||||
  fn: SqLambda;
 | 
			
		||||
  environment: environment;
 | 
			
		||||
}) => {
 | 
			
		||||
  let chartPointsToRender = _rangeByCount(
 | 
			
		||||
    chartSettings.start,
 | 
			
		||||
    chartSettings.stop,
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +49,9 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => {
 | 
			
		|||
  );
 | 
			
		||||
 | 
			
		||||
  let chartPointsData: point[] = chartPointsToRender.map((x) => {
 | 
			
		||||
    let result = runForeign(fn, [x], environment);
 | 
			
		||||
    let result = fn.call([x]);
 | 
			
		||||
    if (result.tag === "Ok") {
 | 
			
		||||
      if (result.value.tag == "number") {
 | 
			
		||||
      if (result.value.tag === SqValueTag.Number) {
 | 
			
		||||
        return { x, value: { tag: "Ok", value: result.value.value } };
 | 
			
		||||
      } else {
 | 
			
		||||
        return {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +65,7 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => {
 | 
			
		|||
    } else {
 | 
			
		||||
      return {
 | 
			
		||||
        x,
 | 
			
		||||
        value: { tag: "Error", value: errorValueToString(result.value) },
 | 
			
		||||
        value: { tag: "Error", value: result.value.toString() },
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,14 @@
 | 
			
		|||
import * as React from "react";
 | 
			
		||||
import {
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  bindings,
 | 
			
		||||
  SqValue,
 | 
			
		||||
  environment,
 | 
			
		||||
  jsImports,
 | 
			
		||||
  defaultImports,
 | 
			
		||||
  defaultBindings,
 | 
			
		||||
  defaultEnvironment,
 | 
			
		||||
  resultMap,
 | 
			
		||||
  SqValueTag,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { useSquiggle } from "../lib/hooks";
 | 
			
		||||
import { SquiggleViewer } from "./SquiggleViewer";
 | 
			
		||||
import { JsImports } from "../lib/jsImports";
 | 
			
		||||
 | 
			
		||||
export interface SquiggleChartProps {
 | 
			
		||||
  /** The input string for squiggle */
 | 
			
		||||
| 
						 | 
				
			
			@ -27,14 +26,12 @@ export interface SquiggleChartProps {
 | 
			
		|||
  /** If the result is a function, the amount of stops sampled */
 | 
			
		||||
  diagramCount?: number;
 | 
			
		||||
  /** When the squiggle code gets reevaluated */
 | 
			
		||||
  onChange?(expr: squiggleExpression | undefined): void;
 | 
			
		||||
  onChange?(expr: SqValue | undefined): void;
 | 
			
		||||
  /** CSS width of the element */
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height?: number;
 | 
			
		||||
  /** Bindings of previous variables declared */
 | 
			
		||||
  bindings?: bindings;
 | 
			
		||||
  /** JS imported parameters */
 | 
			
		||||
  jsImports?: jsImports;
 | 
			
		||||
  jsImports?: JsImports;
 | 
			
		||||
  /** Whether to show a summary of the distribution */
 | 
			
		||||
  showSummary?: boolean;
 | 
			
		||||
  /** Set the x scale to be logarithmic by deault */
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +54,7 @@ export interface SquiggleChartProps {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
const defaultOnChange = () => {};
 | 
			
		||||
const defaultImports: JsImports = {};
 | 
			
		||||
 | 
			
		||||
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
			
		||||
  ({
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +63,6 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
			
		|||
    environment,
 | 
			
		||||
    onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
 | 
			
		||||
    height = 200,
 | 
			
		||||
    bindings = defaultBindings,
 | 
			
		||||
    jsImports = defaultImports,
 | 
			
		||||
    showSummary = false,
 | 
			
		||||
    width,
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +79,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
			
		|||
    distributionChartActions,
 | 
			
		||||
    enableLocalSettings = false,
 | 
			
		||||
  }) => {
 | 
			
		||||
    const result = useSquiggle({
 | 
			
		||||
    const { result, bindings } = useSquiggle({
 | 
			
		||||
      code,
 | 
			
		||||
      bindings,
 | 
			
		||||
      environment,
 | 
			
		||||
      jsImports,
 | 
			
		||||
      onChange,
 | 
			
		||||
| 
						 | 
				
			
			@ -109,9 +105,13 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
 | 
			
		|||
      count: diagramCount,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const resultToRender = resultMap(result, (value) =>
 | 
			
		||||
      value.tag === SqValueTag.Void ? bindings.asValue() : value
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <SquiggleViewer
 | 
			
		||||
        result={result}
 | 
			
		||||
        result={resultToRender}
 | 
			
		||||
        width={width}
 | 
			
		||||
        height={height}
 | 
			
		||||
        distributionPlotSettings={distributionPlotSettings}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,8 @@
 | 
			
		|||
import React from "react";
 | 
			
		||||
import { CodeEditor } from "./CodeEditor";
 | 
			
		||||
import { environment, bindings, jsImports } from "@quri/squiggle-lang";
 | 
			
		||||
import { defaultImports, defaultBindings } from "@quri/squiggle-lang";
 | 
			
		||||
import { SquiggleContainer } from "./SquiggleContainer";
 | 
			
		||||
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
 | 
			
		||||
import { useSquigglePartial, useMaybeControlledValue } from "../lib/hooks";
 | 
			
		||||
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
 | 
			
		||||
import { useMaybeControlledValue } from "../lib/hooks";
 | 
			
		||||
 | 
			
		||||
const WrappedCodeEditor: React.FC<{
 | 
			
		||||
  code: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,51 +39,3 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
 | 
			
		|||
    </SquiggleContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface SquigglePartialProps {
 | 
			
		||||
  /** The text inside the input (controlled) */
 | 
			
		||||
  code?: string;
 | 
			
		||||
  /** The default text inside the input (unControlled) */
 | 
			
		||||
  defaultCode?: string;
 | 
			
		||||
  /** when the environment changes. Used again for notebook magic*/
 | 
			
		||||
  onChange?(expr: bindings | undefined): void;
 | 
			
		||||
  /** When the code changes */
 | 
			
		||||
  onCodeChange?(code: string): void;
 | 
			
		||||
  /** Previously declared variables */
 | 
			
		||||
  bindings?: bindings;
 | 
			
		||||
  /** If the output requires monte carlo sampling, the amount of samples */
 | 
			
		||||
  environment?: environment;
 | 
			
		||||
  /** Variables imported from js */
 | 
			
		||||
  jsImports?: jsImports;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const SquigglePartial: React.FC<SquigglePartialProps> = ({
 | 
			
		||||
  code: controlledCode,
 | 
			
		||||
  defaultCode = "",
 | 
			
		||||
  onChange,
 | 
			
		||||
  onCodeChange,
 | 
			
		||||
  bindings = defaultBindings,
 | 
			
		||||
  environment,
 | 
			
		||||
  jsImports = defaultImports,
 | 
			
		||||
}: SquigglePartialProps) => {
 | 
			
		||||
  const [code, setCode] = useMaybeControlledValue<string>({
 | 
			
		||||
    value: controlledCode,
 | 
			
		||||
    defaultValue: defaultCode,
 | 
			
		||||
    onChange: onCodeChange,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const result = useSquigglePartial({
 | 
			
		||||
    code,
 | 
			
		||||
    bindings,
 | 
			
		||||
    environment,
 | 
			
		||||
    jsImports,
 | 
			
		||||
    onChange,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SquiggleContainer>
 | 
			
		||||
      <WrappedCodeEditor code={code} setCode={setCode} />
 | 
			
		||||
      {result.tag !== "Ok" ? <SquiggleErrorAlert error={result.value} /> : null}
 | 
			
		||||
    </SquiggleContainer>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
import React from "react";
 | 
			
		||||
import { SquiggleEditor } from "./SquiggleEditor";
 | 
			
		||||
import type { SquiggleEditorProps } from "./SquiggleEditor";
 | 
			
		||||
import { runPartial, defaultBindings } from "@quri/squiggle-lang";
 | 
			
		||||
import type {
 | 
			
		||||
  result,
 | 
			
		||||
  errorValue,
 | 
			
		||||
  bindings as bindingsType,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
function resultDefault(x: result<bindingsType, errorValue>): bindingsType {
 | 
			
		||||
  switch (x.tag) {
 | 
			
		||||
    case "Ok":
 | 
			
		||||
      return x.value;
 | 
			
		||||
    case "Error":
 | 
			
		||||
      return defaultBindings;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SquiggleEditorWithImportedBindingsProps = SquiggleEditorProps & {
 | 
			
		||||
  bindingsImportUrl: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const SquiggleEditorWithImportedBindings: React.FC<
 | 
			
		||||
  SquiggleEditorWithImportedBindingsProps
 | 
			
		||||
> = (props) => {
 | 
			
		||||
  const { bindingsImportUrl, ...editorProps } = props;
 | 
			
		||||
  const [bindingsResult, setBindingsResult] = React.useState({
 | 
			
		||||
    tag: "Ok",
 | 
			
		||||
    value: defaultBindings,
 | 
			
		||||
  } as result<bindingsType, errorValue>);
 | 
			
		||||
  React.useEffect(() => {
 | 
			
		||||
    async function retrieveBindings(fileName: string) {
 | 
			
		||||
      let contents = await fetch(fileName).then((response) => {
 | 
			
		||||
        return response.text();
 | 
			
		||||
      });
 | 
			
		||||
      setBindingsResult(
 | 
			
		||||
        runPartial(
 | 
			
		||||
          contents,
 | 
			
		||||
          editorProps.bindings,
 | 
			
		||||
          editorProps.environment,
 | 
			
		||||
          editorProps.jsImports
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    retrieveBindings(bindingsImportUrl);
 | 
			
		||||
  }, [bindingsImportUrl]);
 | 
			
		||||
  const deliveredBindings = resultDefault(bindingsResult);
 | 
			
		||||
  return (
 | 
			
		||||
    <SquiggleEditor {...{ ...editorProps, bindings: deliveredBindings }} />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
import { errorValue, errorValueToString } from "@quri/squiggle-lang";
 | 
			
		||||
import { SqError } from "@quri/squiggle-lang";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { ErrorAlert } from "./Alert";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  error: errorValue;
 | 
			
		||||
  error: SqError;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
 | 
			
		||||
  return <ErrorAlert heading="Error">{errorValueToString(error)}</ErrorAlert>;
 | 
			
		||||
  return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ import {
 | 
			
		|||
} from "@heroicons/react/solid";
 | 
			
		||||
import clsx from "clsx";
 | 
			
		||||
 | 
			
		||||
import { defaultBindings, environment } from "@quri/squiggle-lang";
 | 
			
		||||
import { environment } from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
 | 
			
		||||
import { CodeEditor } from "./CodeEditor";
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +39,7 @@ import { ViewSettings, viewSettingsSchema } from "./ViewSettings";
 | 
			
		|||
import { HeadedSection } from "./ui/HeadedSection";
 | 
			
		||||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
 | 
			
		||||
import { Button } from "./ui/Button";
 | 
			
		||||
import { JsImports } from "../lib/jsImports";
 | 
			
		||||
 | 
			
		||||
type PlaygroundProps = SquiggleChartProps & {
 | 
			
		||||
  /** The initial squiggle string to put in the playground */
 | 
			
		||||
| 
						 | 
				
			
			@ -112,8 +113,8 @@ const SamplingSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
 | 
			
		|||
);
 | 
			
		||||
 | 
			
		||||
const InputVariablesSettings: React.FC<{
 | 
			
		||||
  initialImports: any; // TODO - any json type
 | 
			
		||||
  setImports: (imports: any) => void;
 | 
			
		||||
  initialImports: JsImports;
 | 
			
		||||
  setImports: (imports: JsImports) => void;
 | 
			
		||||
}> = ({ initialImports, setImports }) => {
 | 
			
		||||
  const [importString, setImportString] = useState(() =>
 | 
			
		||||
    JSON.stringify(initialImports)
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +123,7 @@ const InputVariablesSettings: React.FC<{
 | 
			
		|||
 | 
			
		||||
  const onChange = (value: string) => {
 | 
			
		||||
    setImportString(value);
 | 
			
		||||
    let imports = {} as any;
 | 
			
		||||
    let imports = {};
 | 
			
		||||
    try {
 | 
			
		||||
      imports = JSON.parse(value);
 | 
			
		||||
      setImportsAreValid(true);
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +252,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
    onChange: onCodeChange,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const [imports, setImports] = useState({});
 | 
			
		||||
  const [imports, setImports] = useState<JsImports>({});
 | 
			
		||||
 | 
			
		||||
  const { register, control } = useForm({
 | 
			
		||||
    resolver: yupResolver(schema),
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +310,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
 | 
			
		|||
          executionId={executionId}
 | 
			
		||||
          environment={env}
 | 
			
		||||
          {...vars}
 | 
			
		||||
          bindings={defaultBindings}
 | 
			
		||||
          jsImports={imports}
 | 
			
		||||
          enableLocalSettings={true}
 | 
			
		||||
        />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import React from "react";
 | 
			
		||||
import { squiggleExpression, declaration } from "@quri/squiggle-lang";
 | 
			
		||||
import React, { useContext } from "react";
 | 
			
		||||
import { SqDistributionTag, SqValue, SqValueTag } from "@quri/squiggle-lang";
 | 
			
		||||
import { NumberShower } from "../NumberShower";
 | 
			
		||||
import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
 | 
			
		||||
import { FunctionChart, FunctionChartSettings } from "../FunctionChart";
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +8,10 @@ import { VariableBox } from "./VariableBox";
 | 
			
		|||
import { ItemSettingsMenu } from "./ItemSettingsMenu";
 | 
			
		||||
import { hasMassBelowZero } from "../../lib/distributionUtils";
 | 
			
		||||
import { MergedItemSettings } from "./utils";
 | 
			
		||||
import { ViewerContext } from "./ViewerContext";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
// DISABLED FOR 0.4 branch, for now
 | 
			
		||||
function getRange<a>(x: declaration<a>) {
 | 
			
		||||
  const first = x.args[0];
 | 
			
		||||
  switch (first.tag) {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,15 +34,21 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
 | 
			
		|||
    count: 20,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
const VariableList: React.FC<{
 | 
			
		||||
  path: string[];
 | 
			
		||||
  value: SqValue;
 | 
			
		||||
  heading: string;
 | 
			
		||||
  children: (settings: MergedItemSettings) => React.ReactNode;
 | 
			
		||||
}> = ({ path, heading, children }) => (
 | 
			
		||||
  <VariableBox path={path} heading={heading}>
 | 
			
		||||
}> = ({ value, heading, children }) => (
 | 
			
		||||
  <VariableBox value={value} heading={heading}>
 | 
			
		||||
    {(settings) => (
 | 
			
		||||
      <div className={clsx("space-y-3", path.length ? "pt-1 mt-1" : null)}>
 | 
			
		||||
      <div
 | 
			
		||||
        className={clsx(
 | 
			
		||||
          "space-y-3",
 | 
			
		||||
          value.location.path.items.length ? "pt-1 mt-1" : null
 | 
			
		||||
        )}
 | 
			
		||||
      >
 | 
			
		||||
        {children(settings)}
 | 
			
		||||
      </div>
 | 
			
		||||
    )}
 | 
			
		||||
| 
						 | 
				
			
			@ -48,51 +57,44 @@ const VariableList: React.FC<{
 | 
			
		|||
 | 
			
		||||
export interface Props {
 | 
			
		||||
  /** The output of squiggle's run */
 | 
			
		||||
  expression: squiggleExpression;
 | 
			
		||||
  /** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */
 | 
			
		||||
  path: string[];
 | 
			
		||||
  value: SqValue;
 | 
			
		||||
  width?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		||||
  path,
 | 
			
		||||
  expression,
 | 
			
		||||
  width,
 | 
			
		||||
}) => {
 | 
			
		||||
  if (typeof expression !== "object") {
 | 
			
		||||
    return (
 | 
			
		||||
      <VariableList path={path} heading="Error">
 | 
			
		||||
        {() => `Unknown expression: ${expression}`}
 | 
			
		||||
      </VariableList>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  switch (expression.tag) {
 | 
			
		||||
    case "number":
 | 
			
		||||
export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
 | 
			
		||||
  const { getMergedSettings } = useContext(ViewerContext);
 | 
			
		||||
 | 
			
		||||
  switch (value.tag) {
 | 
			
		||||
    case SqValueTag.Number:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Number">
 | 
			
		||||
        <VariableBox value={value} heading="Number">
 | 
			
		||||
          {() => (
 | 
			
		||||
            <div className="font-semibold text-slate-600">
 | 
			
		||||
              <NumberShower precision={3} number={expression.value} />
 | 
			
		||||
              <NumberShower precision={3} number={value.value} />
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "distribution": {
 | 
			
		||||
      const distType = expression.value.type();
 | 
			
		||||
    case SqValueTag.Distribution: {
 | 
			
		||||
      const distType = value.value.tag;
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox
 | 
			
		||||
          path={path}
 | 
			
		||||
          value={value}
 | 
			
		||||
          heading={`Distribution (${distType})\n${
 | 
			
		||||
            distType === "Symbolic" ? expression.value.toString() : ""
 | 
			
		||||
            distType === SqDistributionTag.Symbolic
 | 
			
		||||
              ? value.value.toString()
 | 
			
		||||
              : ""
 | 
			
		||||
          }`}
 | 
			
		||||
          renderSettingsMenu={({ onChange }) => {
 | 
			
		||||
            const shape = expression.value.pointSet();
 | 
			
		||||
            const shape = value.value.pointSet(
 | 
			
		||||
              getMergedSettings(value.location).environment
 | 
			
		||||
            );
 | 
			
		||||
            return (
 | 
			
		||||
              <ItemSettingsMenu
 | 
			
		||||
                path={path}
 | 
			
		||||
                value={value}
 | 
			
		||||
                onChange={onChange}
 | 
			
		||||
                disableLogX={
 | 
			
		||||
                  shape.tag === "Ok" && hasMassBelowZero(shape.value)
 | 
			
		||||
                  shape.tag === "Ok" && hasMassBelowZero(shape.value.asShape())
 | 
			
		||||
                }
 | 
			
		||||
                withFunctionSettings={false}
 | 
			
		||||
              />
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +104,8 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
          {(settings) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <DistributionChart
 | 
			
		||||
                plot={defaultPlot(expression.value)}
 | 
			
		||||
                plot={defaultPlot(value.value)}
 | 
			
		||||
                environment={settings.environment}
 | 
			
		||||
                {...settings.distributionPlotSettings}
 | 
			
		||||
                height={settings.height}
 | 
			
		||||
                width={width}
 | 
			
		||||
| 
						 | 
				
			
			@ -112,77 +115,77 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    case "string":
 | 
			
		||||
    case SqValueTag.String:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="String">
 | 
			
		||||
        <VariableBox value={value} heading="String">
 | 
			
		||||
          {() => (
 | 
			
		||||
            <>
 | 
			
		||||
              <span className="text-slate-400">"</span>
 | 
			
		||||
              <span className="text-slate-600 font-semibold font-mono">
 | 
			
		||||
                {expression.value}
 | 
			
		||||
                {value.value}
 | 
			
		||||
              </span>
 | 
			
		||||
              <span className="text-slate-400">"</span>
 | 
			
		||||
            </>
 | 
			
		||||
          )}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "boolean":
 | 
			
		||||
    case SqValueTag.Bool:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Boolean">
 | 
			
		||||
          {() => expression.value.toString()}
 | 
			
		||||
        <VariableBox value={value} heading="Boolean">
 | 
			
		||||
          {() => value.value.toString()}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "symbol":
 | 
			
		||||
    case SqValueTag.Symbol:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Symbol">
 | 
			
		||||
        <VariableBox value={value} heading="Symbol">
 | 
			
		||||
          {() => (
 | 
			
		||||
            <>
 | 
			
		||||
              <span className="text-slate-500 mr-2">Undefined Symbol:</span>
 | 
			
		||||
              <span className="text-slate-600">{expression.value}</span>
 | 
			
		||||
              <span className="text-slate-600">{value.value}</span>
 | 
			
		||||
            </>
 | 
			
		||||
          )}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "call":
 | 
			
		||||
    case SqValueTag.Call:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Call">
 | 
			
		||||
          {() => expression.value}
 | 
			
		||||
        <VariableBox value={value} heading="Call">
 | 
			
		||||
          {() => value.value}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "arraystring":
 | 
			
		||||
    case SqValueTag.ArrayString:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Array String">
 | 
			
		||||
          {() => expression.value.map((r) => `"${r}"`).join(", ")}
 | 
			
		||||
        <VariableBox value={value} heading="Array String">
 | 
			
		||||
          {() => value.value.map((r) => `"${r}"`).join(", ")}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "date":
 | 
			
		||||
    case SqValueTag.Date:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Date">
 | 
			
		||||
          {() => expression.value.toDateString()}
 | 
			
		||||
        <VariableBox value={value} heading="Date">
 | 
			
		||||
          {() => value.value.toDateString()}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "void":
 | 
			
		||||
    case SqValueTag.Void:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Void">
 | 
			
		||||
        <VariableBox value={value} heading="Void">
 | 
			
		||||
          {() => "Void"}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "timeDuration": {
 | 
			
		||||
    case SqValueTag.TimeDuration: {
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox path={path} heading="Time Duration">
 | 
			
		||||
          {() => <NumberShower precision={3} number={expression.value} />}
 | 
			
		||||
        <VariableBox value={value} heading="Time Duration">
 | 
			
		||||
          {() => <NumberShower precision={3} number={value.value} />}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    case "lambda":
 | 
			
		||||
    case SqValueTag.Lambda:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox
 | 
			
		||||
          path={path}
 | 
			
		||||
          value={value}
 | 
			
		||||
          heading="Function"
 | 
			
		||||
          renderSettingsMenu={({ onChange }) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <ItemSettingsMenu
 | 
			
		||||
                path={path}
 | 
			
		||||
                value={value}
 | 
			
		||||
                onChange={onChange}
 | 
			
		||||
                withFunctionSettings={true}
 | 
			
		||||
              />
 | 
			
		||||
| 
						 | 
				
			
			@ -191,11 +194,11 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
        >
 | 
			
		||||
          {(settings) => (
 | 
			
		||||
            <>
 | 
			
		||||
              <div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value.parameters.join(
 | 
			
		||||
                ","
 | 
			
		||||
              )})`}</div>
 | 
			
		||||
              <div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${value.value
 | 
			
		||||
                .parameters()
 | 
			
		||||
                .join(",")})`}</div>
 | 
			
		||||
              <FunctionChart
 | 
			
		||||
                fn={expression.value}
 | 
			
		||||
                fn={value.value}
 | 
			
		||||
                chartSettings={settings.chartSettings}
 | 
			
		||||
                distributionPlotSettings={settings.distributionPlotSettings}
 | 
			
		||||
                height={settings.height}
 | 
			
		||||
| 
						 | 
				
			
			@ -208,47 +211,48 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
          )}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    case "lambdaDeclaration": {
 | 
			
		||||
    case SqValueTag.Declaration: {
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableBox
 | 
			
		||||
          path={path}
 | 
			
		||||
          value={value}
 | 
			
		||||
          heading="Function Declaration"
 | 
			
		||||
          renderSettingsMenu={({ onChange }) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <ItemSettingsMenu
 | 
			
		||||
                onChange={onChange}
 | 
			
		||||
                path={path}
 | 
			
		||||
                value={value}
 | 
			
		||||
                withFunctionSettings={true}
 | 
			
		||||
              />
 | 
			
		||||
            );
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {(settings) => (
 | 
			
		||||
            <FunctionChart
 | 
			
		||||
              fn={expression.value.fn}
 | 
			
		||||
              chartSettings={getChartSettings(expression.value)}
 | 
			
		||||
              distributionPlotSettings={settings.distributionPlotSettings}
 | 
			
		||||
              height={settings.height}
 | 
			
		||||
              environment={{
 | 
			
		||||
                sampleCount: settings.environment.sampleCount / 10,
 | 
			
		||||
                xyPointLength: settings.environment.xyPointLength / 10,
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
            <div>NOT IMPLEMENTED IN 0.4 YET</div>
 | 
			
		||||
            // <FunctionChart
 | 
			
		||||
            //   fn={expression.value.fn}
 | 
			
		||||
            //   chartSettings={getChartSettings(expression.value)}
 | 
			
		||||
            //   distributionPlotSettings={settings.distributionPlotSettings}
 | 
			
		||||
            //   height={settings.height}
 | 
			
		||||
            //   environment={{
 | 
			
		||||
            //     sampleCount: settings.environment.sampleCount / 10,
 | 
			
		||||
            //     xyPointLength: settings.environment.xyPointLength / 10,
 | 
			
		||||
            //   }}
 | 
			
		||||
            // />
 | 
			
		||||
          )}
 | 
			
		||||
        </VariableBox>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    case "module": {
 | 
			
		||||
    case SqValueTag.Module: {
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableList path={path} heading="Module">
 | 
			
		||||
        <VariableList value={value} heading="Module">
 | 
			
		||||
          {(_) =>
 | 
			
		||||
            Object.entries(expression.value)
 | 
			
		||||
              .filter(([key, _]) => !key.match(/^(Math|System)\./))
 | 
			
		||||
            value.value
 | 
			
		||||
              .entries()
 | 
			
		||||
              .filter(([key, _]) => !key.match(/^(__result__)$/))
 | 
			
		||||
              .map(([key, r]) => (
 | 
			
		||||
                <ExpressionViewer
 | 
			
		||||
                  key={key}
 | 
			
		||||
                  path={[...path, key]}
 | 
			
		||||
                  expression={r}
 | 
			
		||||
                  value={r}
 | 
			
		||||
                  width={width !== undefined ? width - 20 : width}
 | 
			
		||||
                />
 | 
			
		||||
              ))
 | 
			
		||||
| 
						 | 
				
			
			@ -256,23 +260,26 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
        </VariableList>
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    case "record":
 | 
			
		||||
      const plot = makePlot(expression.value);
 | 
			
		||||
    case SqValueTag.Record:
 | 
			
		||||
      const plot = makePlot(value.value);
 | 
			
		||||
      if (plot) {
 | 
			
		||||
        return (
 | 
			
		||||
          <VariableBox
 | 
			
		||||
            path={path}
 | 
			
		||||
            heading={"Plot"}
 | 
			
		||||
            value={value}
 | 
			
		||||
            heading="Plot"
 | 
			
		||||
            renderSettingsMenu={({ onChange }) => {
 | 
			
		||||
              let disableLogX = plot.distributions.some((x) => {
 | 
			
		||||
                let pointSet = x.distribution.pointSet();
 | 
			
		||||
                let pointSet = x.distribution.pointSet(
 | 
			
		||||
                  getMergedSettings(value.location).environment
 | 
			
		||||
                );
 | 
			
		||||
                return (
 | 
			
		||||
                  pointSet.tag === "Ok" && hasMassBelowZero(pointSet.value)
 | 
			
		||||
                  pointSet.tag === "Ok" &&
 | 
			
		||||
                  hasMassBelowZero(pointSet.value.asShape())
 | 
			
		||||
                );
 | 
			
		||||
              });
 | 
			
		||||
              return (
 | 
			
		||||
                <ItemSettingsMenu
 | 
			
		||||
                  path={path}
 | 
			
		||||
                  value={value}
 | 
			
		||||
                  onChange={onChange}
 | 
			
		||||
                  disableLogX={disableLogX}
 | 
			
		||||
                  withFunctionSettings={false}
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +291,7 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
              return (
 | 
			
		||||
                <DistributionChart
 | 
			
		||||
                  plot={plot}
 | 
			
		||||
                  environment={settings.environment}
 | 
			
		||||
                  {...settings.distributionPlotSettings}
 | 
			
		||||
                  height={settings.height}
 | 
			
		||||
                  width={width}
 | 
			
		||||
| 
						 | 
				
			
			@ -294,44 +302,44 @@ export const ExpressionViewer: React.FC<Props> = ({
 | 
			
		|||
        );
 | 
			
		||||
      } else {
 | 
			
		||||
        return (
 | 
			
		||||
          <VariableList path={path} heading="Record">
 | 
			
		||||
          <VariableList value={value} heading="Record">
 | 
			
		||||
            {(_) =>
 | 
			
		||||
              Object.entries(expression.value).map(([key, r]) => (
 | 
			
		||||
                <ExpressionViewer
 | 
			
		||||
                  key={key}
 | 
			
		||||
                  path={[...path, key]}
 | 
			
		||||
                  expression={r}
 | 
			
		||||
                  width={width !== undefined ? width - 20 : width}
 | 
			
		||||
                />
 | 
			
		||||
              ))
 | 
			
		||||
              value.value
 | 
			
		||||
                .entries()
 | 
			
		||||
                .map(([key, r]) => (
 | 
			
		||||
                  <ExpressionViewer
 | 
			
		||||
                    key={key}
 | 
			
		||||
                    value={r}
 | 
			
		||||
                    width={width !== undefined ? width - 20 : width}
 | 
			
		||||
                  />
 | 
			
		||||
                ))
 | 
			
		||||
            }
 | 
			
		||||
          </VariableList>
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    case "array":
 | 
			
		||||
    case SqValueTag.Array:
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableList path={path} heading="Array">
 | 
			
		||||
        <VariableList value={value} heading="Array">
 | 
			
		||||
          {(_) =>
 | 
			
		||||
            expression.value.map((r, i) => (
 | 
			
		||||
              <ExpressionViewer
 | 
			
		||||
                key={i}
 | 
			
		||||
                path={[...path, String(i)]}
 | 
			
		||||
                expression={r}
 | 
			
		||||
                width={width !== undefined ? width - 20 : width}
 | 
			
		||||
              />
 | 
			
		||||
            ))
 | 
			
		||||
            value.value
 | 
			
		||||
              .getValues()
 | 
			
		||||
              .map((r, i) => (
 | 
			
		||||
                <ExpressionViewer
 | 
			
		||||
                  key={i}
 | 
			
		||||
                  value={r}
 | 
			
		||||
                  width={width !== undefined ? width - 20 : width}
 | 
			
		||||
                />
 | 
			
		||||
              ))
 | 
			
		||||
          }
 | 
			
		||||
        </VariableList>
 | 
			
		||||
      );
 | 
			
		||||
    default: {
 | 
			
		||||
      return (
 | 
			
		||||
        <VariableList path={path} heading="Error">
 | 
			
		||||
        <VariableList value={value} heading="Error">
 | 
			
		||||
          {() => (
 | 
			
		||||
            <div>
 | 
			
		||||
              <span>No display for type: </span>{" "}
 | 
			
		||||
              <span className="font-semibold text-slate-600">
 | 
			
		||||
                {expression.tag}
 | 
			
		||||
              </span>
 | 
			
		||||
              <span className="font-semibold text-slate-600">{value.tag}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </VariableList>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,13 +4,14 @@ import { useForm } from "react-hook-form";
 | 
			
		|||
import { yupResolver } from "@hookform/resolvers/yup";
 | 
			
		||||
import { Modal } from "../ui/Modal";
 | 
			
		||||
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
 | 
			
		||||
import { Path, pathAsString } from "./utils";
 | 
			
		||||
import { ViewerContext } from "./ViewerContext";
 | 
			
		||||
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
 | 
			
		||||
import { PlaygroundContext } from "../SquigglePlayground";
 | 
			
		||||
import { SqValue } from "@quri/squiggle-lang";
 | 
			
		||||
import { locationAsString } from "./utils";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  path: Path;
 | 
			
		||||
  value: SqValue;
 | 
			
		||||
  onChange: () => void;
 | 
			
		||||
  disableLogX?: boolean;
 | 
			
		||||
  withFunctionSettings: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +20,7 @@ type Props = {
 | 
			
		|||
const ItemSettingsModal: React.FC<
 | 
			
		||||
  Props & { close: () => void; resetScroll: () => void }
 | 
			
		||||
> = ({
 | 
			
		||||
  path,
 | 
			
		||||
  value,
 | 
			
		||||
  onChange,
 | 
			
		||||
  disableLogX,
 | 
			
		||||
  withFunctionSettings,
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +30,7 @@ const ItemSettingsModal: React.FC<
 | 
			
		|||
  const { setSettings, getSettings, getMergedSettings } =
 | 
			
		||||
    useContext(ViewerContext);
 | 
			
		||||
 | 
			
		||||
  const mergedSettings = getMergedSettings(path);
 | 
			
		||||
  const mergedSettings = getMergedSettings(value.location);
 | 
			
		||||
 | 
			
		||||
  const { register, watch } = useForm({
 | 
			
		||||
    resolver: yupResolver(viewSettingsSchema),
 | 
			
		||||
| 
						 | 
				
			
			@ -53,8 +54,8 @@ const ItemSettingsModal: React.FC<
 | 
			
		|||
  });
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const subscription = watch((vars) => {
 | 
			
		||||
      const settings = getSettings(path); // get the latest version
 | 
			
		||||
      setSettings(path, {
 | 
			
		||||
      const settings = getSettings(value.location); // get the latest version
 | 
			
		||||
      setSettings(value.location, {
 | 
			
		||||
        ...settings,
 | 
			
		||||
        distributionPlotSettings: {
 | 
			
		||||
          showSummary: vars.showSummary,
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +76,7 @@ const ItemSettingsModal: React.FC<
 | 
			
		|||
      onChange();
 | 
			
		||||
    });
 | 
			
		||||
    return () => subscription.unsubscribe();
 | 
			
		||||
  }, [getSettings, setSettings, onChange, path, watch]);
 | 
			
		||||
  }, [getSettings, setSettings, onChange, value.location, watch]);
 | 
			
		||||
 | 
			
		||||
  const { getLeftPanelElement } = useContext(PlaygroundContext);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +84,7 @@ const ItemSettingsModal: React.FC<
 | 
			
		|||
    <Modal container={getLeftPanelElement()} close={close}>
 | 
			
		||||
      <Modal.Header>
 | 
			
		||||
        Chart settings
 | 
			
		||||
        {path.length ? (
 | 
			
		||||
        {value.location.path.items.length ? (
 | 
			
		||||
          <>
 | 
			
		||||
            {" for "}
 | 
			
		||||
            <span
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +92,7 @@ const ItemSettingsModal: React.FC<
 | 
			
		|||
              className="cursor-pointer"
 | 
			
		||||
              onClick={resetScroll}
 | 
			
		||||
            >
 | 
			
		||||
              {pathAsString(path)}
 | 
			
		||||
              {locationAsString(value.location)}
 | 
			
		||||
            </span>{" "}
 | 
			
		||||
          </>
 | 
			
		||||
        ) : (
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +121,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
 | 
			
		|||
  if (!enableLocalSettings) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
  const settings = getSettings(props.path);
 | 
			
		||||
  const settings = getSettings(props.value.location);
 | 
			
		||||
 | 
			
		||||
  const resetScroll = () => {
 | 
			
		||||
    if (!ref.current) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +140,7 @@ export const ItemSettingsMenu: React.FC<Props> = (props) => {
 | 
			
		|||
      {settings.distributionPlotSettings || settings.chartSettings ? (
 | 
			
		||||
        <button
 | 
			
		||||
          onClick={() => {
 | 
			
		||||
            setSettings(props.path, {
 | 
			
		||||
            setSettings(props.value.location, {
 | 
			
		||||
              ...settings,
 | 
			
		||||
              distributionPlotSettings: undefined,
 | 
			
		||||
              chartSettings: undefined,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
import { SqValue, SqValueLocation } from "@quri/squiggle-lang";
 | 
			
		||||
import React, { useContext, useReducer } from "react";
 | 
			
		||||
import { Tooltip } from "../ui/Tooltip";
 | 
			
		||||
import { LocalItemSettings, MergedItemSettings } from "./utils";
 | 
			
		||||
| 
						 | 
				
			
			@ -8,14 +9,14 @@ type SettingsMenuParams = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
type VariableBoxProps = {
 | 
			
		||||
  path: string[];
 | 
			
		||||
  value: SqValue;
 | 
			
		||||
  heading: string;
 | 
			
		||||
  renderSettingsMenu?: (params: SettingsMenuParams) => React.ReactNode;
 | 
			
		||||
  children: (settings: MergedItemSettings) => React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const VariableBox: React.FC<VariableBoxProps> = ({
 | 
			
		||||
  path,
 | 
			
		||||
  value: { location },
 | 
			
		||||
  heading = "Error",
 | 
			
		||||
  renderSettingsMenu,
 | 
			
		||||
  children,
 | 
			
		||||
| 
						 | 
				
			
			@ -27,10 +28,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
 | 
			
		|||
  // So we use `forceUpdate` to force rerendering.
 | 
			
		||||
  const [_, forceUpdate] = useReducer((x) => x + 1, 0);
 | 
			
		||||
 | 
			
		||||
  const settings = getSettings(path);
 | 
			
		||||
  const settings = getSettings(location);
 | 
			
		||||
 | 
			
		||||
  const setSettingsAndUpdate = (newSettings: LocalItemSettings) => {
 | 
			
		||||
    setSettings(path, newSettings);
 | 
			
		||||
    setSettings(location, newSettings);
 | 
			
		||||
    forceUpdate();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,8 +39,10 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
 | 
			
		|||
    setSettingsAndUpdate({ ...settings, collapsed: !settings.collapsed });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const isTopLevel = path.length === 0;
 | 
			
		||||
  const name = isTopLevel ? "Result" : path[path.length - 1];
 | 
			
		||||
  const isTopLevel = location.path.items.length === 0;
 | 
			
		||||
  const name = isTopLevel
 | 
			
		||||
    ? { result: "Result", bindings: "Bindings" }[location.path.root]
 | 
			
		||||
    : location.path.items[location.path.items.length - 1];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
| 
						 | 
				
			
			@ -65,13 +68,13 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
 | 
			
		|||
      </header>
 | 
			
		||||
      {settings.collapsed ? null : (
 | 
			
		||||
        <div className="flex w-full">
 | 
			
		||||
          {path.length ? (
 | 
			
		||||
          {location.path.items.length ? (
 | 
			
		||||
            <div
 | 
			
		||||
              className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
 | 
			
		||||
              onClick={toggleCollapsed}
 | 
			
		||||
            ></div>
 | 
			
		||||
          ) : null}
 | 
			
		||||
          <div className="grow">{children(getMergedSettings(path))}</div>
 | 
			
		||||
          <div className="grow">{children(getMergedSettings(location))}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,14 @@
 | 
			
		|||
import { defaultEnvironment } from "@quri/squiggle-lang";
 | 
			
		||||
import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { LocalItemSettings, MergedItemSettings, Path } from "./utils";
 | 
			
		||||
import { LocalItemSettings, MergedItemSettings } from "./utils";
 | 
			
		||||
 | 
			
		||||
type ViewerContextShape = {
 | 
			
		||||
  // Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
 | 
			
		||||
  // Instead, we keep settings in local state and notify the global context via setSettings to pass them down the component tree again if it got rebuilt from scratch.
 | 
			
		||||
  // See ./SquiggleViewer.tsx and ./VariableBox.tsx for other implementation details on this.
 | 
			
		||||
  getSettings(path: Path): LocalItemSettings;
 | 
			
		||||
  getMergedSettings(path: Path): MergedItemSettings;
 | 
			
		||||
  setSettings(path: Path, value: LocalItemSettings): void;
 | 
			
		||||
  getSettings(location: SqValueLocation): LocalItemSettings;
 | 
			
		||||
  getMergedSettings(location: SqValueLocation): MergedItemSettings;
 | 
			
		||||
  setSettings(location: SqValueLocation, value: LocalItemSettings): void;
 | 
			
		||||
  enableLocalSettings: boolean; // show local settings icon in the UI
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,21 +1,20 @@
 | 
			
		|||
import React, { useCallback, useRef } from "react";
 | 
			
		||||
import { environment } from "@quri/squiggle-lang";
 | 
			
		||||
import { environment, SqValueLocation } from "@quri/squiggle-lang";
 | 
			
		||||
import { DistributionPlottingSettings } from "../DistributionChart";
 | 
			
		||||
import { FunctionChartSettings } from "../FunctionChart";
 | 
			
		||||
import { ExpressionViewer } from "./ExpressionViewer";
 | 
			
		||||
import { ViewerContext } from "./ViewerContext";
 | 
			
		||||
import {
 | 
			
		||||
  LocalItemSettings,
 | 
			
		||||
  locationAsString,
 | 
			
		||||
  MergedItemSettings,
 | 
			
		||||
  Path,
 | 
			
		||||
  pathAsString,
 | 
			
		||||
} from "./utils";
 | 
			
		||||
import { useSquiggle } from "../../lib/hooks";
 | 
			
		||||
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  /** The output of squiggle's run */
 | 
			
		||||
  result: ReturnType<typeof useSquiggle>;
 | 
			
		||||
  result: ReturnType<typeof useSquiggle>["result"];
 | 
			
		||||
  width?: number;
 | 
			
		||||
  height: number;
 | 
			
		||||
  distributionPlotSettings: DistributionPlottingSettings;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,22 +44,22 @@ export const SquiggleViewer: React.FC<Props> = ({
 | 
			
		|||
  const settingsRef = useRef<Settings>({});
 | 
			
		||||
 | 
			
		||||
  const getSettings = useCallback(
 | 
			
		||||
    (path: Path) => {
 | 
			
		||||
      return settingsRef.current[pathAsString(path)] || defaultSettings;
 | 
			
		||||
    (location: SqValueLocation) => {
 | 
			
		||||
      return settingsRef.current[locationAsString(location)] || defaultSettings;
 | 
			
		||||
    },
 | 
			
		||||
    [settingsRef]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const setSettings = useCallback(
 | 
			
		||||
    (path: Path, value: LocalItemSettings) => {
 | 
			
		||||
      settingsRef.current[pathAsString(path)] = value;
 | 
			
		||||
    (location: SqValueLocation, value: LocalItemSettings) => {
 | 
			
		||||
      settingsRef.current[locationAsString(location)] = value;
 | 
			
		||||
    },
 | 
			
		||||
    [settingsRef]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const getMergedSettings = useCallback(
 | 
			
		||||
    (path: Path) => {
 | 
			
		||||
      const localSettings = getSettings(path);
 | 
			
		||||
    (location: SqValueLocation) => {
 | 
			
		||||
      const localSettings = getSettings(location);
 | 
			
		||||
      const result: MergedItemSettings = {
 | 
			
		||||
        distributionPlotSettings: {
 | 
			
		||||
          ...distributionPlotSettings,
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +90,7 @@ export const SquiggleViewer: React.FC<Props> = ({
 | 
			
		|||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {result.tag === "Ok" ? (
 | 
			
		||||
        <ExpressionViewer path={[]} expression={result.value} width={width} />
 | 
			
		||||
        <ExpressionViewer value={result.value} width={width} />
 | 
			
		||||
      ) : (
 | 
			
		||||
        <SquiggleErrorAlert error={result.value} />
 | 
			
		||||
      )}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import { DistributionPlottingSettings } from "../DistributionChart";
 | 
			
		||||
import { FunctionChartSettings } from "../FunctionChart";
 | 
			
		||||
import { environment } from "@quri/squiggle-lang";
 | 
			
		||||
import { environment, SqValueLocation } from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
export type LocalItemSettings = {
 | 
			
		||||
  collapsed: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,5 @@ export type MergedItemSettings = {
 | 
			
		|||
  environment: environment;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Path = string[];
 | 
			
		||||
 | 
			
		||||
export const pathAsString = (path: Path) => path.join(".");
 | 
			
		||||
export const locationAsString = (location: SqValueLocation) =>
 | 
			
		||||
  location.path.items.join(".");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,4 @@
 | 
			
		|||
export { SquiggleChart } from "./components/SquiggleChart";
 | 
			
		||||
export { SquiggleEditor, SquigglePartial } from "./components/SquiggleEditor";
 | 
			
		||||
export { SquiggleEditor } from "./components/SquiggleEditor";
 | 
			
		||||
export { SquigglePlayground } from "./components/SquigglePlayground";
 | 
			
		||||
export { SquiggleContainer } from "./components/SquiggleContainer";
 | 
			
		||||
export { SquiggleEditorWithImportedBindings } from "./components/SquiggleEditorWithImportedBindings";
 | 
			
		||||
 | 
			
		||||
export { mergeBindings } from "@quri/squiggle-lang";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import { shape } from "@quri/squiggle-lang";
 | 
			
		||||
import { SqShape } from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
export const hasMassBelowZero = (shape: shape) =>
 | 
			
		||||
export const hasMassBelowZero = (shape: SqShape) =>
 | 
			
		||||
  shape.continuous.some((x) => x.x <= 0) ||
 | 
			
		||||
  shape.discrete.some((x) => x.x <= 0);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,3 @@
 | 
			
		|||
export { useMaybeControlledValue } from "./useMaybeControlledValue";
 | 
			
		||||
export { useSquiggle, useSquigglePartial } from "./useSquiggle";
 | 
			
		||||
export { useSquiggle } from "./useSquiggle";
 | 
			
		||||
export { useRunnerState } from "./useRunnerState";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,53 +1,42 @@
 | 
			
		|||
import {
 | 
			
		||||
  bindings,
 | 
			
		||||
  environment,
 | 
			
		||||
  jsImports,
 | 
			
		||||
  run,
 | 
			
		||||
  runPartial,
 | 
			
		||||
} from "@quri/squiggle-lang";
 | 
			
		||||
import { environment, SqProject, SqValue } from "@quri/squiggle-lang";
 | 
			
		||||
import { useEffect, useMemo } from "react";
 | 
			
		||||
import { JsImports, jsImportsToSquiggleCode } from "../jsImports";
 | 
			
		||||
 | 
			
		||||
type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = {
 | 
			
		||||
type SquiggleArgs = {
 | 
			
		||||
  code: string;
 | 
			
		||||
  executionId?: number;
 | 
			
		||||
  bindings?: bindings;
 | 
			
		||||
  jsImports?: jsImports;
 | 
			
		||||
  jsImports?: JsImports;
 | 
			
		||||
  environment?: environment;
 | 
			
		||||
  onChange?: (expr: Extract<T, { tag: "Ok" }>["value"] | undefined) => void;
 | 
			
		||||
  onChange?: (expr: SqValue | undefined) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>(
 | 
			
		||||
  args: SquiggleArgs<T>,
 | 
			
		||||
  f: (...args: Parameters<typeof run>) => T
 | 
			
		||||
) => {
 | 
			
		||||
  const result: T = useMemo<T>(
 | 
			
		||||
    () => f(args.code, args.bindings, args.environment, args.jsImports),
 | 
			
		||||
export const useSquiggle = (args: SquiggleArgs) => {
 | 
			
		||||
  const result = useMemo(
 | 
			
		||||
    () => {
 | 
			
		||||
      const project = SqProject.create();
 | 
			
		||||
      project.setSource("main", args.code);
 | 
			
		||||
      if (args.environment) {
 | 
			
		||||
        project.setEnvironment(args.environment);
 | 
			
		||||
      }
 | 
			
		||||
      if (args.jsImports && Object.keys(args.jsImports).length) {
 | 
			
		||||
        const importsSource = jsImportsToSquiggleCode(args.jsImports);
 | 
			
		||||
        project.setSource("imports", importsSource);
 | 
			
		||||
        project.setContinues("main", ["imports"]);
 | 
			
		||||
      }
 | 
			
		||||
      project.run("main");
 | 
			
		||||
      const result = project.getResult("main");
 | 
			
		||||
      const bindings = project.getBindings("main");
 | 
			
		||||
      return { result, bindings };
 | 
			
		||||
    },
 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
			
		||||
    [
 | 
			
		||||
      f,
 | 
			
		||||
      args.code,
 | 
			
		||||
      args.bindings,
 | 
			
		||||
      args.environment,
 | 
			
		||||
      args.jsImports,
 | 
			
		||||
      args.executionId,
 | 
			
		||||
    ]
 | 
			
		||||
    [args.code, args.environment, args.jsImports, args.executionId]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const { onChange } = args;
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    onChange?.(result.tag === "Ok" ? result.value : undefined);
 | 
			
		||||
    onChange?.(result.result.tag === "Ok" ? result.result.value : undefined);
 | 
			
		||||
  }, [result, onChange]);
 | 
			
		||||
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useSquigglePartial = (
 | 
			
		||||
  args: SquiggleArgs<ReturnType<typeof runPartial>>
 | 
			
		||||
) => {
 | 
			
		||||
  return useSquiggleAny(args, runPartial);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useSquiggle = (args: SquiggleArgs<ReturnType<typeof run>>) => {
 | 
			
		||||
  return useSquiggleAny(args, run);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										51
									
								
								packages/components/src/lib/jsImports.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								packages/components/src/lib/jsImports.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
type JsImportsValue =
 | 
			
		||||
  | number
 | 
			
		||||
  | string
 | 
			
		||||
  | JsImportsValue[]
 | 
			
		||||
  | {
 | 
			
		||||
      [k: string]: JsImportsValue;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export type JsImports = {
 | 
			
		||||
  [k: string]: JsImportsValue;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const quote = (arg: string) => `"${arg.replace(new RegExp('"', "g"), '\\"')}"`;
 | 
			
		||||
 | 
			
		||||
const jsImportsValueToSquiggleCode = (v: JsImportsValue): string => {
 | 
			
		||||
  if (typeof v === "number") {
 | 
			
		||||
    return String(v);
 | 
			
		||||
  } else if (typeof v === "string") {
 | 
			
		||||
    return quote(v);
 | 
			
		||||
  } else if (v instanceof Array) {
 | 
			
		||||
    return "[" + v.map((x) => jsImportsValueToSquiggleCode(x)) + "]";
 | 
			
		||||
  } else {
 | 
			
		||||
    if (Object.keys(v).length) {
 | 
			
		||||
      return (
 | 
			
		||||
        "{" +
 | 
			
		||||
        Object.entries(v)
 | 
			
		||||
          .map(([k, v]) => `${quote(k)}:${jsImportsValueToSquiggleCode(v)},`)
 | 
			
		||||
          .join("") +
 | 
			
		||||
        "}"
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return "0"; // squiggle doesn't support empty `{}`
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const jsImportsToSquiggleCode = (v: JsImports) => {
 | 
			
		||||
  const validId = new RegExp("[a-zA-Z][[a-zA-Z0-9]*");
 | 
			
		||||
  let result = Object.entries(v)
 | 
			
		||||
    .map(([k, v]) => {
 | 
			
		||||
      if (!k.match(validId)) {
 | 
			
		||||
        return ""; // skipping without warnings; can be improved
 | 
			
		||||
      }
 | 
			
		||||
      return `$${k} = ${jsImportsValueToSquiggleCode(v)}\n`;
 | 
			
		||||
    })
 | 
			
		||||
    .join("");
 | 
			
		||||
  if (!result) {
 | 
			
		||||
    result = "$__no_valid_imports__ = 1"; // without this generated squiggle code can be invalid
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
import * as yup from "yup";
 | 
			
		||||
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang";
 | 
			
		||||
import { SqDistribution, result, SqRecord } from "@quri/squiggle-lang";
 | 
			
		||||
 | 
			
		||||
export type LabeledDistribution = {
 | 
			
		||||
  name: string;
 | 
			
		||||
  distribution: Distribution;
 | 
			
		||||
  distribution: SqDistribution;
 | 
			
		||||
  color?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -53,9 +53,7 @@ const schema = yup
 | 
			
		|||
    }),
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
export function parsePlot(record: {
 | 
			
		||||
  [key: string]: squiggleExpression;
 | 
			
		||||
}): result<Plot, string> {
 | 
			
		||||
export function parsePlot(record: SqRecord): result<Plot, string> {
 | 
			
		||||
  try {
 | 
			
		||||
    const plotRecord = schema.validateSync(record);
 | 
			
		||||
    return ok({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("Name Space", () => {
 | 
			
		||||
  let value = InternalExpressionValue.IEvNumber(1967.0)
 | 
			
		||||
  let nameSpace = Bindings.emptyNameSpace->Bindings.set("value", value)
 | 
			
		||||
  test("get", () => {
 | 
			
		||||
    expect(Bindings.get(nameSpace, "value")) == Some(value)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("chain and get", () => {
 | 
			
		||||
    let mainNameSpace = Bindings.emptyNameSpace->Bindings.chainTo([nameSpace])
 | 
			
		||||
    expect(Bindings.get(mainNameSpace, "value")) == Some(value)
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("chain and set", () => {
 | 
			
		||||
    let mainNameSpace0 = Bindings.emptyNameSpace->Bindings.chainTo([nameSpace])
 | 
			
		||||
    let mainNameSpace =
 | 
			
		||||
      mainNameSpace0->Bindings.set("value", InternalExpressionValue.IEvNumber(1968.0))
 | 
			
		||||
    expect(Bindings.get(mainNameSpace, "value")) == Some(InternalExpressionValue.IEvNumber(1968.0))
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +1,14 @@
 | 
			
		|||
module ExpressionValue = ReducerInterface.ExternalExpressionValue
 | 
			
		||||
module ExpressionValue = ReducerInterface.InternalExpressionValue
 | 
			
		||||
module Expression = Reducer_Expression
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
 | 
			
		||||
let expectEvalToBe = (expr: string, answer: string) =>
 | 
			
		||||
  Reducer.evaluate(expr)->ExpressionValue.toStringResult->expect->toBe(answer)
 | 
			
		||||
let expectEvalToBe = (sourceCode: string, answer: string) =>
 | 
			
		||||
  Expression.BackCompatible.evaluateString(sourceCode)
 | 
			
		||||
  ->ExpressionValue.toStringResult
 | 
			
		||||
  ->expect
 | 
			
		||||
  ->toBe(answer)
 | 
			
		||||
 | 
			
		||||
let testEval = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
// Reducer_Helpers
 | 
			
		||||
module ErrorValue = Reducer_ErrorValue
 | 
			
		||||
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
 | 
			
		||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +14,4 @@ let removeDefaultsInternal = (iev: InternalExpressionValue.t) => {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let removeDefaultsExternal = (ev: ExternalExpressionValue.t): ExternalExpressionValue.t =>
 | 
			
		||||
  ev->InternalExpressionValue.toInternal->removeDefaultsInternal->InternalExpressionValue.toExternal
 | 
			
		||||
 | 
			
		||||
let rRemoveDefaultsInternal = r => Belt.Result.map(r, removeDefaultsInternal)
 | 
			
		||||
let rRemoveDefaultsExternal = r => Belt.Result.map(r, removeDefaultsExternal)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
module MathJs = Reducer_MathJs
 | 
			
		||||
module ErrorValue = Reducer.ErrorValue
 | 
			
		||||
module ErrorValue = Reducer_ErrorValue
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open ExpectJs
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,359 +3,464 @@ open Reducer_Peggy_TestHelpers
 | 
			
		|||
 | 
			
		||||
describe("Peggy parse", () => {
 | 
			
		||||
  describe("float", () => {
 | 
			
		||||
    testParse("1.", "{1}")
 | 
			
		||||
    testParse("1.1", "{1.1}")
 | 
			
		||||
    testParse(".1", "{0.1}")
 | 
			
		||||
    testParse("0.1", "{0.1}")
 | 
			
		||||
    testParse("1e1", "{10}")
 | 
			
		||||
    testParse("1e-1", "{0.1}")
 | 
			
		||||
    testParse(".1e1", "{1}")
 | 
			
		||||
    testParse("0.1e1", "{1}")
 | 
			
		||||
    testParse("1.", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("1.1", "{(::$_endOfOuterBlock_$ () 1.1)}")
 | 
			
		||||
    testParse(".1", "{(::$_endOfOuterBlock_$ () 0.1)}")
 | 
			
		||||
    testParse("0.1", "{(::$_endOfOuterBlock_$ () 0.1)}")
 | 
			
		||||
    testParse("1e1", "{(::$_endOfOuterBlock_$ () 10)}")
 | 
			
		||||
    testParse("1e-1", "{(::$_endOfOuterBlock_$ () 0.1)}")
 | 
			
		||||
    testParse(".1e1", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("0.1e1", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("literals operators parenthesis", () => {
 | 
			
		||||
    // Note that there is always an outer block. Otherwise, external bindings are ignrored at the first statement
 | 
			
		||||
    testParse("1", "{1}")
 | 
			
		||||
    testParse("'hello'", "{'hello'}")
 | 
			
		||||
    testParse("true", "{true}")
 | 
			
		||||
    testParse("1+2", "{(::add 1 2)}")
 | 
			
		||||
    testParse("add(1,2)", "{(::add 1 2)}")
 | 
			
		||||
    testParse("(1)", "{1}")
 | 
			
		||||
    testParse("(1+2)", "{(::add 1 2)}")
 | 
			
		||||
    testParse("1", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("'hello'", "{(::$_endOfOuterBlock_$ () 'hello')}")
 | 
			
		||||
    testParse("true", "{(::$_endOfOuterBlock_$ () true)}")
 | 
			
		||||
    testParse("1+2", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
 | 
			
		||||
    testParse("add(1,2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
 | 
			
		||||
    testParse("(1)", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("(1+2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("unary", () => {
 | 
			
		||||
    testParse("-1", "{(::unaryMinus 1)}")
 | 
			
		||||
    testParse("!true", "{(::not true)}")
 | 
			
		||||
    testParse("1 + -1", "{(::add 1 (::unaryMinus 1))}")
 | 
			
		||||
    testParse("-a[0]", "{(::unaryMinus (::$_atIndex_$ :a 0))}")
 | 
			
		||||
    testParse("!a[0]", "{(::not (::$_atIndex_$ :a 0))}")
 | 
			
		||||
    testParse("-1", "{(::$_endOfOuterBlock_$ () (::unaryMinus 1))}")
 | 
			
		||||
    testParse("!true", "{(::$_endOfOuterBlock_$ () (::not true))}")
 | 
			
		||||
    testParse("1 + -1", "{(::$_endOfOuterBlock_$ () (::add 1 (::unaryMinus 1)))}")
 | 
			
		||||
    testParse("-a[0]", "{(::$_endOfOuterBlock_$ () (::unaryMinus (::$_atIndex_$ :a 0)))}")
 | 
			
		||||
    testParse("!a[0]", "{(::$_endOfOuterBlock_$ () (::not (::$_atIndex_$ :a 0)))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("multiplicative", () => {
 | 
			
		||||
    testParse("1 * 2", "{(::multiply 1 2)}")
 | 
			
		||||
    testParse("1 / 2", "{(::divide 1 2)}")
 | 
			
		||||
    testParse("1 * 2 * 3", "{(::multiply (::multiply 1 2) 3)}")
 | 
			
		||||
    testParse("1 * 2 / 3", "{(::divide (::multiply 1 2) 3)}")
 | 
			
		||||
    testParse("1 / 2 * 3", "{(::multiply (::divide 1 2) 3)}")
 | 
			
		||||
    testParse("1 / 2 / 3", "{(::divide (::divide 1 2) 3)}")
 | 
			
		||||
    testParse("1 * 2 + 3 * 4", "{(::add (::multiply 1 2) (::multiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 - 3 * 4", "{(::subtract (::multiply 1 2) (::multiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 .+ 3 * 4", "{(::dotAdd (::multiply 1 2) (::multiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 .- 3 * 4", "{(::dotSubtract (::multiply 1 2) (::multiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 + 3 .* 4", "{(::add (::multiply 1 2) (::dotMultiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 + 3 / 4", "{(::add (::multiply 1 2) (::divide 3 4))}")
 | 
			
		||||
    testParse("1 * 2 + 3 ./ 4", "{(::add (::multiply 1 2) (::dotDivide 3 4))}")
 | 
			
		||||
    testParse("1 * 2 - 3 .* 4", "{(::subtract (::multiply 1 2) (::dotMultiply 3 4))}")
 | 
			
		||||
    testParse("1 * 2 - 3 / 4", "{(::subtract (::multiply 1 2) (::divide 3 4))}")
 | 
			
		||||
    testParse("1 * 2 - 3 ./ 4", "{(::subtract (::multiply 1 2) (::dotDivide 3 4))}")
 | 
			
		||||
    testParse("1 * 2 - 3 * 4^5", "{(::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5)))}")
 | 
			
		||||
    testParse("1 * 2", "{(::$_endOfOuterBlock_$ () (::multiply 1 2))}")
 | 
			
		||||
    testParse("1 / 2", "{(::$_endOfOuterBlock_$ () (::divide 1 2))}")
 | 
			
		||||
    testParse("1 * 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::multiply 1 2) 3))}")
 | 
			
		||||
    testParse("1 * 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::multiply 1 2) 3))}")
 | 
			
		||||
    testParse("1 / 2 * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::divide 1 2) 3))}")
 | 
			
		||||
    testParse("1 / 2 / 3", "{(::$_endOfOuterBlock_$ () (::divide (::divide 1 2) 3))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 + 3 * 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::multiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 * 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 .+ 3 * 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::dotAdd (::multiply 1 2) (::multiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 .- 3 * 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::dotSubtract (::multiply 1 2) (::multiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 + 3 .* 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotMultiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 + 3 / 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::divide 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 + 3 ./ 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::multiply 1 2) (::dotDivide 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 .* 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotMultiply 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 / 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::divide 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 ./ 4",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::dotDivide 3 4)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 * 4^5",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow 4 5))))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * 2 - 3 * 4^5^6",
 | 
			
		||||
      "{(::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6)))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::subtract (::multiply 1 2) (::multiply 3 (::pow (::pow 4 5) 6))))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 * -a[-2]",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2)))))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("1 * -a[-2]", "{(::multiply 1 (::unaryMinus (::$_atIndex_$ :a (::unaryMinus 2))))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("multi-line", () => {
 | 
			
		||||
    testParse("x=1; 2", "{:x = {1}; 2}")
 | 
			
		||||
    testParse("x=1; y=2", "{:x = {1}; :y = {2}}")
 | 
			
		||||
    testParse("x=1; 2", "{:x = {1}; (::$_endOfOuterBlock_$ () 2)}")
 | 
			
		||||
    testParse("x=1; y=2", "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("variables", () => {
 | 
			
		||||
    testParse("x = 1", "{:x = {1}}")
 | 
			
		||||
    testParse("x", "{:x}")
 | 
			
		||||
    testParse("x = 1; x", "{:x = {1}; :x}")
 | 
			
		||||
    testParse("x = 1", "{:x = {1}; (::$_endOfOuterBlock_$ () ())}")
 | 
			
		||||
    testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
 | 
			
		||||
    testParse("x = 1; x", "{:x = {1}; (::$_endOfOuterBlock_$ () :x)}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("functions", () => {
 | 
			
		||||
    testParse("identity(x) = x", "{:identity = {|:x| {:x}}}") // Function definitions become lambda assignments
 | 
			
		||||
    testParse("identity(x)", "{(::identity :x)}")
 | 
			
		||||
    testParse("identity(x) = x", "{:identity = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions become lambda assignments
 | 
			
		||||
    testParse("identity(x)", "{(::$_endOfOuterBlock_$ () (::identity :x))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("arrays", () => {
 | 
			
		||||
    testParse("[]", "{(::$_constructArray_$ ())}")
 | 
			
		||||
    testParse("[0, 1, 2]", "{(::$_constructArray_$ (0 1 2))}")
 | 
			
		||||
    testParse("['hello', 'world']", "{(::$_constructArray_$ ('hello' 'world'))}")
 | 
			
		||||
    testParse("([0,1,2])[1]", "{(::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1)}")
 | 
			
		||||
    testParse("[]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ()))}")
 | 
			
		||||
    testParse("[0, 1, 2]", "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ (0 1 2)))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "['hello', 'world']",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$_constructArray_$ ('hello' 'world')))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "([0,1,2])[1]",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ (::$_constructArray_$ (0 1 2)) 1))}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("records", () => {
 | 
			
		||||
    testParse("{a: 1, b: 2}", "{(::$_constructRecord_$ ('a': 1 'b': 2))}")
 | 
			
		||||
    testParse("{1+0: 1, 2+0: 2}", "{(::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2))}") // key can be any expression
 | 
			
		||||
    testParse("record.property", "{(::$_atIndex_$ :record 'property')}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "{a: 1, b: 2}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ('a': 1 'b': 2)))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "{1+0: 1, 2+0: 2}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$_constructRecord_$ ((::add 1 0): 1 (::add 2 0): 2)))}",
 | 
			
		||||
    ) // key can be any expression
 | 
			
		||||
    testParse("record.property", "{(::$_endOfOuterBlock_$ () (::$_atIndex_$ :record 'property'))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("post operators", () => {
 | 
			
		||||
    //function call, array and record access are post operators with higher priority than unary operators
 | 
			
		||||
    testParse("a==!b(1)", "{(::equal :a (::not (::b 1)))}")
 | 
			
		||||
    testParse("a==!b[1]", "{(::equal :a (::not (::$_atIndex_$ :b 1)))}")
 | 
			
		||||
    testParse("a==!b.one", "{(::equal :a (::not (::$_atIndex_$ :b 'one')))}")
 | 
			
		||||
    testParse("a==!b(1)", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::b 1))))}")
 | 
			
		||||
    testParse("a==!b[1]", "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 1))))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a==!b.one",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::equal :a (::not (::$_atIndex_$ :b 'one'))))}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("comments", () => {
 | 
			
		||||
    testParse("1 # This is a line comment", "{1}")
 | 
			
		||||
    testParse("1 // This is a line comment", "{1}")
 | 
			
		||||
    testParse("1 /* This is a multi line comment */", "{1}")
 | 
			
		||||
    testParse("/* This is a multi line comment */ 1", "{1}")
 | 
			
		||||
    testParse("1 # This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("1 // This is a line comment", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("1 /* This is a multi line comment */", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse("/* This is a multi line comment */ 1", "{(::$_endOfOuterBlock_$ () 1)}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      `
 | 
			
		||||
    /* This is 
 | 
			
		||||
    a multi line 
 | 
			
		||||
    comment */
 | 
			
		||||
    1`,
 | 
			
		||||
      "{1}",
 | 
			
		||||
  /* This is 
 | 
			
		||||
  a multi line 
 | 
			
		||||
  comment */
 | 
			
		||||
  1`,
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () 1)}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("ternary operator", () => {
 | 
			
		||||
    testParse("true ? 2 : 3", "{(::$$_ternary_$$ true 2 3)}")
 | 
			
		||||
    testParse("true ? 2 : 3", "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true 2 3))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "false ? 2 : false ? 4 : 5",
 | 
			
		||||
      "{(::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false 2 (::$$_ternary_$$ false 4 5)))}",
 | 
			
		||||
    ) // nested ternary
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("if then else", () => {
 | 
			
		||||
    testParse("if true then 2 else 3", "{(::$$_ternary_$$ true {2} {3})}")
 | 
			
		||||
    testParse("if false then {2} else {3}", "{(::$$_ternary_$$ false {2} {3})}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "if true then 2 else 3",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ true {2} {3}))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "if false then {2} else {3}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} {3}))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "if false then {2} else if false then {4} else {5}",
 | 
			
		||||
      "{(::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5}))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ false {2} (::$$_ternary_$$ false {4} {5})))}",
 | 
			
		||||
    ) //nested if
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("logical", () => {
 | 
			
		||||
    testParse("true || false", "{(::or true false)}")
 | 
			
		||||
    testParse("true && false", "{(::and true false)}")
 | 
			
		||||
    testParse("a * b + c", "{(::add (::multiply :a :b) :c)}") // for comparison
 | 
			
		||||
    testParse("a && b || c", "{(::or (::and :a :b) :c)}")
 | 
			
		||||
    testParse("a && b || c && d", "{(::or (::and :a :b) (::and :c :d))}")
 | 
			
		||||
    testParse("a && !b || c", "{(::or (::and :a (::not :b)) :c)}")
 | 
			
		||||
    testParse("a && b==c || d", "{(::or (::and :a (::equal :b :c)) :d)}")
 | 
			
		||||
    testParse("a && b!=c || d", "{(::or (::and :a (::unequal :b :c)) :d)}")
 | 
			
		||||
    testParse("a && !(b==c) || d", "{(::or (::and :a (::not (::equal :b :c))) :d)}")
 | 
			
		||||
    testParse("a && b>=c || d", "{(::or (::and :a (::largerEq :b :c)) :d)}")
 | 
			
		||||
    testParse("a && !(b>=c) || d", "{(::or (::and :a (::not (::largerEq :b :c))) :d)}")
 | 
			
		||||
    testParse("a && b<=c || d", "{(::or (::and :a (::smallerEq :b :c)) :d)}")
 | 
			
		||||
    testParse("a && b>c || d", "{(::or (::and :a (::larger :b :c)) :d)}")
 | 
			
		||||
    testParse("a && b<c || d", "{(::or (::and :a (::smaller :b :c)) :d)}")
 | 
			
		||||
    testParse("a && b<c[i] || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d)}")
 | 
			
		||||
    testParse("a && b<c.i || d", "{(::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d)}")
 | 
			
		||||
    testParse("a && b<c(i) || d", "{(::or (::and :a (::smaller :b (::c :i))) :d)}")
 | 
			
		||||
    testParse("a && b<1+2 || d", "{(::or (::and :a (::smaller :b (::add 1 2))) :d)}")
 | 
			
		||||
    testParse("true || false", "{(::$_endOfOuterBlock_$ () (::or true false))}")
 | 
			
		||||
    testParse("true && false", "{(::$_endOfOuterBlock_$ () (::and true false))}")
 | 
			
		||||
    testParse("a * b + c", "{(::$_endOfOuterBlock_$ () (::add (::multiply :a :b) :c))}") // for comparison
 | 
			
		||||
    testParse("a && b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) :c))}")
 | 
			
		||||
    testParse("a && b || c && d", "{(::$_endOfOuterBlock_$ () (::or (::and :a :b) (::and :c :d)))}")
 | 
			
		||||
    testParse("a && !b || c", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not :b)) :c))}")
 | 
			
		||||
    testParse("a && b==c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::equal :b :c)) :d))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b!=c || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::unequal :b :c)) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && !(b==c) || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::equal :b :c))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b>=c || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::largerEq :b :c)) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && !(b>=c) || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::not (::largerEq :b :c))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<=c || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smallerEq :b :c)) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("a && b>c || d", "{(::$_endOfOuterBlock_$ () (::or (::and :a (::larger :b :c)) :d))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<c || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b :c)) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<c[i] || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c :i))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<c.i || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::$_atIndex_$ :c 'i'))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<c(i) || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::c :i))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<1+2 || d",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 2))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<1+2*3 || d",
 | 
			
		||||
      "{(::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d)}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<1+2*-3+4 || d",
 | 
			
		||||
      "{(::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d)}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::or (::and :a (::smaller :b (::add (::add 1 (::multiply 2 (::unaryMinus 3))) 4))) :d))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a && b<1+2*3 || d ? true : false",
 | 
			
		||||
      "{(::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false)}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::$$_ternary_$$ (::or (::and :a (::smaller :b (::add 1 (::multiply 2 3)))) :d) true false))}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("pipe", () => {
 | 
			
		||||
    testParse("1 -> add(2)", "{(::add 1 2)}")
 | 
			
		||||
    testParse("-1 -> add(2)", "{(::add (::unaryMinus 1) 2)}")
 | 
			
		||||
    testParse("-a[1] -> add(2)", "{(::add (::unaryMinus (::$_atIndex_$ :a 1)) 2)}")
 | 
			
		||||
    testParse("-f(1) -> add(2)", "{(::add (::unaryMinus (::f 1)) 2)}")
 | 
			
		||||
    testParse("1 + 2 -> add(3)", "{(::add 1 (::add 2 3))}")
 | 
			
		||||
    testParse("1 -> add(2) * 3", "{(::multiply (::add 1 2) 3)}")
 | 
			
		||||
    testParse("1 -> subtract(2)", "{(::subtract 1 2)}")
 | 
			
		||||
    testParse("-1 -> subtract(2)", "{(::subtract (::unaryMinus 1) 2)}")
 | 
			
		||||
    testParse("1 -> subtract(2) * 3", "{(::multiply (::subtract 1 2) 3)}")
 | 
			
		||||
    testParse("1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
 | 
			
		||||
    testParse("-1 -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus 1) 2))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "-a[1] -> add(2)",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::$_atIndex_$ :a 1)) 2))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("-f(1) -> add(2)", "{(::$_endOfOuterBlock_$ () (::add (::unaryMinus (::f 1)) 2))}")
 | 
			
		||||
    testParse("1 + 2 -> add(3)", "{(::$_endOfOuterBlock_$ () (::add 1 (::add 2 3)))}")
 | 
			
		||||
    testParse("1 -> add(2) * 3", "{(::$_endOfOuterBlock_$ () (::multiply (::add 1 2) 3))}")
 | 
			
		||||
    testParse("1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract 1 2))}")
 | 
			
		||||
    testParse("-1 -> subtract(2)", "{(::$_endOfOuterBlock_$ () (::subtract (::unaryMinus 1) 2))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 -> subtract(2) * 3",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::multiply (::subtract 1 2) 3))}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("elixir pipe", () => {
 | 
			
		||||
    //handled together with -> so there is no need for seperate tests
 | 
			
		||||
    testParse("1 |> add(2)", "{(::add 1 2)}")
 | 
			
		||||
    testParse("1 |> add(2)", "{(::$_endOfOuterBlock_$ () (::add 1 2))}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("to", () => {
 | 
			
		||||
    testParse("1 to 2", "{(::credibleIntervalToDistribution 1 2)}")
 | 
			
		||||
    testParse("-1 to -2", "{(::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2))}") // lower than unary
 | 
			
		||||
    testParse("1 to 2", "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution 1 2))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "-1 to -2",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::unaryMinus 1) (::unaryMinus 2)))}",
 | 
			
		||||
    ) // lower than unary
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a[1] to a[2]",
 | 
			
		||||
      "{(::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 1) (::$_atIndex_$ :a 2)))}",
 | 
			
		||||
    ) // lower than post
 | 
			
		||||
    testParse(
 | 
			
		||||
      "a.p1 to a.p2",
 | 
			
		||||
      "{(::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2'))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::$_atIndex_$ :a 'p1') (::$_atIndex_$ :a 'p2')))}",
 | 
			
		||||
    ) // lower than post
 | 
			
		||||
    testParse("1 to 2 + 3", "{(::add (::credibleIntervalToDistribution 1 2) 3)}") // higher than binary operators
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1 to 2 + 3",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::add (::credibleIntervalToDistribution 1 2) 3))}",
 | 
			
		||||
    ) // higher than binary operators
 | 
			
		||||
    testParse(
 | 
			
		||||
      "1->add(2) to 3->add(4) -> add(4)",
 | 
			
		||||
      "{(::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4))}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::credibleIntervalToDistribution (::add 1 2) (::add (::add 3 4) 4)))}",
 | 
			
		||||
    ) // lower than chain
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("inner block", () => {
 | 
			
		||||
    // inner blocks are 0 argument lambdas. They can be used whenever a value is required.
 | 
			
		||||
    // Like lambdas they have a local scope.
 | 
			
		||||
    testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; :x}")
 | 
			
		||||
    testParse("x={y=1; y}; x", "{:x = {:y = {1}; :y}; (::$_endOfOuterBlock_$ () :x)}")
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("lambda", () => {
 | 
			
		||||
    testParse("{|x| x}", "{{|:x| {:x}}}")
 | 
			
		||||
    testParse("f={|x| x}", "{:f = {{|:x| {:x}}}}")
 | 
			
		||||
    testParse("f(x)=x", "{:f = {|:x| {:x}}}") // Function definitions are lambda assignments
 | 
			
		||||
    testParse("f(x)=x ? 1 : 0", "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}}") // Function definitions are lambda assignments
 | 
			
		||||
    testParse("{|x| x}", "{(::$_endOfOuterBlock_$ () {|:x| {:x}})}")
 | 
			
		||||
    testParse("f={|x| x}", "{:f = {{|:x| {:x}}}; (::$_endOfOuterBlock_$ () ())}")
 | 
			
		||||
    testParse("f(x)=x", "{:f = {|:x| {:x}}; (::$_endOfOuterBlock_$ () ())}") // Function definitions are lambda assignments
 | 
			
		||||
    testParse(
 | 
			
		||||
      "f(x)=x ? 1 : 0",
 | 
			
		||||
      "{:f = {|:x| {(::$$_ternary_$$ :x 1 0)}}; (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    ) // Function definitions are lambda assignments
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("Using lambda as value", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "myadd(x,y)=x+y; z=myadd; z",
 | 
			
		||||
      "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; :z}",
 | 
			
		||||
      "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {:myadd}; (::$_endOfOuterBlock_$ () :z)}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "myadd(x,y)=x+y; z=[myadd]; z",
 | 
			
		||||
      "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ (:myadd))}; :z}",
 | 
			
		||||
      "{:myadd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructArray_$ (:myadd))}; (::$_endOfOuterBlock_$ () :z)}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "myaddd(x,y)=x+y; z={x: myaddd}; z",
 | 
			
		||||
      "{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; :z}",
 | 
			
		||||
      "{:myaddd = {|:x,:y| {(::add :x :y)}}; :z = {(::$_constructRecord_$ ('x': :myaddd))}; (::$_endOfOuterBlock_$ () :z)}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("f({|x| x+1})", "{(::$_endOfOuterBlock_$ () (::f {|:x| {(::add :x 1)}}))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "map(arr, {|x| x+1})",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::map :arr {|:x| {(::add :x 1)}}))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("f({|x| x+1})", "{(::f {|:x| {(::add :x 1)}})}")
 | 
			
		||||
    testParse("map(arr, {|x| x+1})", "{(::map :arr {|:x| {(::add :x 1)}})}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "map([1,2,3], {|x| x+1})",
 | 
			
		||||
      "{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "[1,2,3]->map({|x| x+1})",
 | 
			
		||||
      "{(::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}})}",
 | 
			
		||||
      "{(::$_endOfOuterBlock_$ () (::map (::$_constructArray_$ (1 2 3)) {|:x| {(::add :x 1)}}))}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("unit", () => {
 | 
			
		||||
    testParse("1m", "{(::fromUnit_m 1)}")
 | 
			
		||||
    testParse("1M", "{(::fromUnit_M 1)}")
 | 
			
		||||
    testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
 | 
			
		||||
    testParse("1m", "{(::$_endOfOuterBlock_$ () (::fromUnit_m 1))}")
 | 
			
		||||
    testParse("1M", "{(::$_endOfOuterBlock_$ () (::fromUnit_M 1))}")
 | 
			
		||||
    testParse("1m+2cm", "{(::$_endOfOuterBlock_$ () (::add (::fromUnit_m 1) (::fromUnit_cm 2)))}")
 | 
			
		||||
  })
 | 
			
		||||
  describe("Module", () => {
 | 
			
		||||
    testParse("x", "{:x}")
 | 
			
		||||
    testParse("Math.pi", "{:Math.pi}")
 | 
			
		||||
    testParse("x", "{(::$_endOfOuterBlock_$ () :x)}")
 | 
			
		||||
    testParse("Math.pi", "{(::$_endOfOuterBlock_$ () :Math.pi)}")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("parsing new line", () => {
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  a + 
 | 
			
		||||
  b`,
 | 
			
		||||
    "{(::add :a :b)}",
 | 
			
		||||
 a + 
 | 
			
		||||
 b`,
 | 
			
		||||
    "{(::$_endOfOuterBlock_$ () (::add :a :b))}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  x=
 | 
			
		||||
  1`,
 | 
			
		||||
    "{:x = {1}}",
 | 
			
		||||
 x=
 | 
			
		||||
 1`,
 | 
			
		||||
    "{:x = {1}; (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  x=1
 | 
			
		||||
  y=2`,
 | 
			
		||||
    "{:x = {1}; :y = {2}}",
 | 
			
		||||
 x=1
 | 
			
		||||
 y=2`,
 | 
			
		||||
    "{:x = {1}; :y = {2}; (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  x={
 | 
			
		||||
   y=2;
 | 
			
		||||
   y }
 | 
			
		||||
  x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; :x}",
 | 
			
		||||
 x={
 | 
			
		||||
  y=2;
 | 
			
		||||
  y }
 | 
			
		||||
 x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  x={
 | 
			
		||||
   y=2
 | 
			
		||||
   y }
 | 
			
		||||
  x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; :x}",
 | 
			
		||||
 x={
 | 
			
		||||
  y=2
 | 
			
		||||
  y }
 | 
			
		||||
 x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  x={
 | 
			
		||||
   y=2
 | 
			
		||||
   y 
 | 
			
		||||
   }
 | 
			
		||||
  x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; :x}",
 | 
			
		||||
 x={
 | 
			
		||||
  y=2
 | 
			
		||||
  y 
 | 
			
		||||
  }
 | 
			
		||||
 x`,
 | 
			
		||||
    "{:x = {:y = {2}; :y}; (::$_endOfOuterBlock_$ () :x)}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
 x=1
 | 
			
		||||
 y=2
 | 
			
		||||
 z=3
 | 
			
		||||
 `,
 | 
			
		||||
    "{:x = {1}; :y = {2}; :z = {3}; (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
 f={
 | 
			
		||||
  x=1
 | 
			
		||||
  y=2
 | 
			
		||||
  z=3
 | 
			
		||||
  `,
 | 
			
		||||
    "{:x = {1}; :y = {2}; :z = {3}}",
 | 
			
		||||
  x+y+z
 | 
			
		||||
 }
 | 
			
		||||
 `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  f={
 | 
			
		||||
    x=1
 | 
			
		||||
    y=2
 | 
			
		||||
    z=3
 | 
			
		||||
    x+y+z
 | 
			
		||||
 f={
 | 
			
		||||
  x=1
 | 
			
		||||
  y=2
 | 
			
		||||
  z=3
 | 
			
		||||
  x+y+z
 | 
			
		||||
 }
 | 
			
		||||
 g=f+4
 | 
			
		||||
 g
 | 
			
		||||
 `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () :g)}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
 f =
 | 
			
		||||
  {
 | 
			
		||||
   x=1; //x
 | 
			
		||||
   y=2 //y
 | 
			
		||||
   z=
 | 
			
		||||
    3
 | 
			
		||||
   x+
 | 
			
		||||
    y+
 | 
			
		||||
    z
 | 
			
		||||
  }
 | 
			
		||||
  `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}}",
 | 
			
		||||
 g =
 | 
			
		||||
  f +
 | 
			
		||||
   4
 | 
			
		||||
 g ->
 | 
			
		||||
  h ->
 | 
			
		||||
  p ->
 | 
			
		||||
  q 
 | 
			
		||||
 `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::$_endOfOuterBlock_$ () (::q (::p (::h :g))))}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  f={
 | 
			
		||||
    x=1
 | 
			
		||||
    y=2
 | 
			
		||||
    z=3
 | 
			
		||||
    x+y+z
 | 
			
		||||
  }
 | 
			
		||||
  g=f+4
 | 
			
		||||
  g
 | 
			
		||||
  `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; :g}",
 | 
			
		||||
  a |>
 | 
			
		||||
  b |>
 | 
			
		||||
  c |>
 | 
			
		||||
  d 
 | 
			
		||||
 `,
 | 
			
		||||
    "{(::$_endOfOuterBlock_$ () (::d (::c (::b :a))))}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
  f =
 | 
			
		||||
    {
 | 
			
		||||
      x=1; //x
 | 
			
		||||
      y=2  //y
 | 
			
		||||
      z=
 | 
			
		||||
        3
 | 
			
		||||
      x+
 | 
			
		||||
        y+
 | 
			
		||||
        z
 | 
			
		||||
    }
 | 
			
		||||
  g =
 | 
			
		||||
    f +
 | 
			
		||||
      4
 | 
			
		||||
  g ->
 | 
			
		||||
    h ->
 | 
			
		||||
    p ->
 | 
			
		||||
    q 
 | 
			
		||||
  `,
 | 
			
		||||
    "{:f = {:x = {1}; :y = {2}; :z = {3}; (::add (::add :x :y) :z)}; :g = {(::add :f 4)}; (::q (::p (::h :g)))}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
    a |>
 | 
			
		||||
    b |>
 | 
			
		||||
    c |>
 | 
			
		||||
    d 
 | 
			
		||||
  `,
 | 
			
		||||
    "{(::d (::c (::b :a)))}",
 | 
			
		||||
  )
 | 
			
		||||
  testParse(
 | 
			
		||||
    `
 | 
			
		||||
    a |>
 | 
			
		||||
    b |>
 | 
			
		||||
    c |>
 | 
			
		||||
    d +
 | 
			
		||||
    e
 | 
			
		||||
  `,
 | 
			
		||||
    "{(::add (::d (::c (::b :a))) :e)}",
 | 
			
		||||
  a |>
 | 
			
		||||
  b |>
 | 
			
		||||
  c |>
 | 
			
		||||
  d +
 | 
			
		||||
  e
 | 
			
		||||
 `,
 | 
			
		||||
    "{(::$_endOfOuterBlock_$ () (::add (::d (::c (::b :a))) :e))}",
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,77 +3,83 @@ open Reducer_Peggy_TestHelpers
 | 
			
		|||
 | 
			
		||||
describe("Peggy parse type", () => {
 | 
			
		||||
  describe("type of", () => {
 | 
			
		||||
    testParse("p: number", "{(::$_typeOf_$ :p #number)}")
 | 
			
		||||
    testParse("p: number", "{(::$_typeOf_$ :p #number); (::$_endOfOuterBlock_$ () ())}")
 | 
			
		||||
  })
 | 
			
		||||
  describe("type alias", () => {
 | 
			
		||||
    testParse("type index=number", "{(::$_typeAlias_$ #index #number)}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "type index=number",
 | 
			
		||||
      "{(::$_typeAlias_$ #index #number); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type or", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: number|string",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type function", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "f: number=>number=>number",
 | 
			
		||||
      "{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("high priority contract", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: number<-min<-max(100)|string",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: number<-memberOf([1,3,5])",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("low priority contract", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: number | string $ opaque",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type array", () => {
 | 
			
		||||
    testParse("answer: [number]", "{(::$_typeOf_$ :answer (::$_typeArray_$ #number))}")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: [number]",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeArray_$ #number)); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type record", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: {a: number, b: string}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type constructor", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: Age(number)",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: Complex(number, number)",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number)))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: Person({age: number, name: string})",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string)))))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string))))))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "weekend: Saturday | Sunday",
 | 
			
		||||
      "{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ())))))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type parenthesis", () => {
 | 
			
		||||
    //$ is introduced to avoid parenthesis
 | 
			
		||||
    testParse(
 | 
			
		||||
      "answer: (number|string)<-opaque",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
 | 
			
		||||
      "{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string))))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("squiggle expressions in type contracts", () => {
 | 
			
		||||
    testParse(
 | 
			
		||||
      "odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
 | 
			
		||||
      "{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
 | 
			
		||||
      "{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2))); (::$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,13 +23,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
 | 
			
		|||
  } else {
 | 
			
		||||
    let a2 =
 | 
			
		||||
      rExpr
 | 
			
		||||
      ->Result.flatMap(expr =>
 | 
			
		||||
        Expression.reduceExpression(
 | 
			
		||||
          expr,
 | 
			
		||||
          ReducerInterface_StdLib.internalStdLib,
 | 
			
		||||
          ExpressionValue.defaultEnvironment,
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
      ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
 | 
			
		||||
      ->Reducer_Helpers.rRemoveDefaultsInternal
 | 
			
		||||
      ->ExpressionValue.toStringResultOkless
 | 
			
		||||
    (a1, a2)->expect->toEqual((answer, v))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
module Bindings = Reducer_Bindings
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Reducer_Peggy_TestHelpers
 | 
			
		||||
 | 
			
		||||
describe("Peggy Outer Block", () => {
 | 
			
		||||
  testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
 | 
			
		||||
  testToExpression("x=1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ~v="()", ())
 | 
			
		||||
  testToExpression(
 | 
			
		||||
    "x=1; y=2",
 | 
			
		||||
    "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    ~v="()",
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
  testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
 | 
			
		||||
  testToExpression(
 | 
			
		||||
    "x={a=1; a}; x",
 | 
			
		||||
    "{(:$_let_$ :x {(:$_let_$ :a {1}); :a}); (:$_endOfOuterBlock_$ () :x)}",
 | 
			
		||||
    ~v="1",
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -7,101 +7,138 @@ open Reducer_Peggy_TestHelpers
 | 
			
		|||
describe("Peggy to Expression", () => {
 | 
			
		||||
  describe("literals operators parenthesis", () => {
 | 
			
		||||
    // Note that there is always an outer block. Otherwise, external bindings are ignored at the first statement
 | 
			
		||||
    testToExpression("1", "{1}", ~v="1", ())
 | 
			
		||||
    testToExpression("'hello'", "{'hello'}", ~v="'hello'", ())
 | 
			
		||||
    testToExpression("true", "{true}", ~v="true", ())
 | 
			
		||||
    testToExpression("1+2", "{(:add 1 2)}", ~v="3", ())
 | 
			
		||||
    testToExpression("add(1,2)", "{(:add 1 2)}", ~v="3", ())
 | 
			
		||||
    testToExpression("(1)", "{1}", ())
 | 
			
		||||
    testToExpression("(1+2)", "{(:add 1 2)}", ())
 | 
			
		||||
    testToExpression("1", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
 | 
			
		||||
    testToExpression("'hello'", "{(:$_endOfOuterBlock_$ () 'hello')}", ~v="'hello'", ())
 | 
			
		||||
    testToExpression("true", "{(:$_endOfOuterBlock_$ () true)}", ~v="true", ())
 | 
			
		||||
    testToExpression("1+2", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
 | 
			
		||||
    testToExpression("add(1,2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
 | 
			
		||||
    testToExpression("(1)", "{(:$_endOfOuterBlock_$ () 1)}", ())
 | 
			
		||||
    testToExpression("(1+2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ())
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("unary", () => {
 | 
			
		||||
    testToExpression("-1", "{(:unaryMinus 1)}", ~v="-1", ())
 | 
			
		||||
    testToExpression("!true", "{(:not true)}", ~v="false", ())
 | 
			
		||||
    testToExpression("1 + -1", "{(:add 1 (:unaryMinus 1))}", ~v="0", ())
 | 
			
		||||
    testToExpression("-a[0]", "{(:unaryMinus (:$_atIndex_$ :a 0))}", ())
 | 
			
		||||
    testToExpression("-1", "{(:$_endOfOuterBlock_$ () (:unaryMinus 1))}", ~v="-1", ())
 | 
			
		||||
    testToExpression("!true", "{(:$_endOfOuterBlock_$ () (:not true))}", ~v="false", ())
 | 
			
		||||
    testToExpression("1 + -1", "{(:$_endOfOuterBlock_$ () (:add 1 (:unaryMinus 1)))}", ~v="0", ())
 | 
			
		||||
    testToExpression("-a[0]", "{(:$_endOfOuterBlock_$ () (:unaryMinus (:$_atIndex_$ :a 0)))}", ())
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("multi-line", () => {
 | 
			
		||||
    testToExpression("x=1; 2", "{(:$_let_$ :x {1}); 2}", ~v="2", ())
 | 
			
		||||
    testToExpression("x=1; y=2", "{(:$_let_$ :x {1}); (:$_let_$ :y {2})}", ~v="@{x: 1,y: 2}", ())
 | 
			
		||||
    testToExpression("x=1; 2", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () 2)}", ~v="2", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "x=1; y=2",
 | 
			
		||||
      "{(:$_let_$ :x {1}); (:$_let_$ :y {2}); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("variables", () => {
 | 
			
		||||
    testToExpression("x = 1", "{(:$_let_$ :x {1})}", ~v="@{x: 1}", ())
 | 
			
		||||
    testToExpression("x", "{:x}", ~v=":x", ()) //TODO: value should return error
 | 
			
		||||
    testToExpression("x = 1; x", "{(:$_let_$ :x {1}); :x}", ~v="1", ())
 | 
			
		||||
    testToExpression("x = 1", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () ())}", ())
 | 
			
		||||
    testToExpression("x", "{(:$_endOfOuterBlock_$ () :x)}", ~v="Error(x is not defined)", ()) //TODO: value should return error
 | 
			
		||||
    testToExpression("x = 1; x", "{(:$_let_$ :x {1}); (:$_endOfOuterBlock_$ () :x)}", ~v="1", ())
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("functions", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "identity(x) = x",
 | 
			
		||||
      "{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x}))}",
 | 
			
		||||
      ~v="@{identity: lambda(x=>internal code)}",
 | 
			
		||||
      "{(:$_let_$ :identity (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // Function definitions become lambda assignments
 | 
			
		||||
    testToExpression("identity(x)", "{(:identity :x)}", ()) // Note value returns error properly
 | 
			
		||||
    testToExpression("identity(x)", "{(:$_endOfOuterBlock_$ () (:identity :x))}", ()) // Note value returns error properly
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f(x) = x> 2 ? 0 : 1; f(3)",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:f 3)}",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ (:larger :x 2) 0 1)})); (:$_endOfOuterBlock_$ () (:f 3))}",
 | 
			
		||||
      ~v="0",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("arrays", () => {
 | 
			
		||||
    testToExpression("[]", "{(:$_constructArray_$ ())}", ~v="[]", ())
 | 
			
		||||
    testToExpression("[0, 1, 2]", "{(:$_constructArray_$ (0 1 2))}", ~v="[0,1,2]", ())
 | 
			
		||||
    testToExpression("[]", "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ()))}", ~v="[]", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "[0, 1, 2]",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ (0 1 2)))}",
 | 
			
		||||
      ~v="[0,1,2]",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "['hello', 'world']",
 | 
			
		||||
      "{(:$_constructArray_$ ('hello' 'world'))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_constructArray_$ ('hello' 'world')))}",
 | 
			
		||||
      ~v="['hello','world']",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression("([0,1,2])[1]", "{(:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1)}", ~v="1", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "([0,1,2])[1]",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ (:$_constructArray_$ (0 1 2)) 1))}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("records", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "{a: 1, b: 2}",
 | 
			
		||||
      "{(:$_constructRecord_$ (('a' 1) ('b' 2)))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (('a' 1) ('b' 2))))}",
 | 
			
		||||
      ~v="{a: 1,b: 2}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "{1+0: 1, 2+0: 2}",
 | 
			
		||||
      "{(:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2)))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_constructRecord_$ (((:add 1 0) 1) ((:add 2 0) 2))))}",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // key can be any expression
 | 
			
		||||
    testToExpression("record.property", "{(:$_atIndex_$ :record 'property')}", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "record.property",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "record={property: 1}; record.property",
 | 
			
		||||
      "{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_atIndex_$ :record 'property')}",
 | 
			
		||||
      "{(:$_let_$ :record {(:$_constructRecord_$ (('property' 1)))}); (:$_endOfOuterBlock_$ () (:$_atIndex_$ :record 'property'))}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("comments", () => {
 | 
			
		||||
    testToExpression("1 # This is a line comment", "{1}", ~v="1", ())
 | 
			
		||||
    testToExpression("1 // This is a line comment", "{1}", ~v="1", ())
 | 
			
		||||
    testToExpression("1 /* This is a multi line comment */", "{1}", ~v="1", ())
 | 
			
		||||
    testToExpression("/* This is a multi line comment */ 1", "{1}", ~v="1", ())
 | 
			
		||||
    testToExpression("1 # This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
 | 
			
		||||
    testToExpression("1 // This is a line comment", "{(:$_endOfOuterBlock_$ () 1)}", ~v="1", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "1 /* This is a multi line comment */",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () 1)}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "/* This is a multi line comment */ 1",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () 1)}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("ternary operator", () => {
 | 
			
		||||
    testToExpression("true ? 1 : 0", "{(:$$_ternary_$$ true 1 0)}", ~v="1", ())
 | 
			
		||||
    testToExpression("false ? 1 : 0", "{(:$$_ternary_$$ false 1 0)}", ~v="0", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "true ? 1 : 0",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 0))}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "false ? 1 : 0",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 0))}",
 | 
			
		||||
      ~v="0",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "true ? 1 : false ? 2 : 0",
 | 
			
		||||
      "{(:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 1 (:$$_ternary_$$ false 2 0)))}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // nested ternary
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "false ? 1 : false ? 2 : 0",
 | 
			
		||||
      "{(:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false 1 (:$$_ternary_$$ false 2 0)))}",
 | 
			
		||||
      ~v="0",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // nested ternary
 | 
			
		||||
| 
						 | 
				
			
			@ -109,21 +146,21 @@ describe("Peggy to Expression", () => {
 | 
			
		|||
      testToExpression(
 | 
			
		||||
        // expression binding
 | 
			
		||||
        "f(a) = a > 5 ? 1 : 0; f(6)",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:f 6)}",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) 1 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
 | 
			
		||||
        ~v="1",
 | 
			
		||||
        (),
 | 
			
		||||
      )
 | 
			
		||||
      testToExpression(
 | 
			
		||||
        // when true binding
 | 
			
		||||
        "f(a) = a > 5 ? a : 0; f(6)",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:f 6)}",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:larger :a 5) :a 0)})); (:$_endOfOuterBlock_$ () (:f 6))}",
 | 
			
		||||
        ~v="6",
 | 
			
		||||
        (),
 | 
			
		||||
      )
 | 
			
		||||
      testToExpression(
 | 
			
		||||
        // when false binding
 | 
			
		||||
        "f(a) = a < 5 ? 1 : a; f(6)",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:f 6)}",
 | 
			
		||||
        "{(:$_let_$ :f (:$$_lambda_$$ [a] {(:$$_ternary_$$ (:smaller :a 5) 1 :a)})); (:$_endOfOuterBlock_$ () (:f 6))}",
 | 
			
		||||
        ~v="6",
 | 
			
		||||
        (),
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			@ -131,23 +168,41 @@ describe("Peggy to Expression", () => {
 | 
			
		|||
  })
 | 
			
		||||
 | 
			
		||||
  describe("if then else", () => {
 | 
			
		||||
    testToExpression("if true then 2 else 3", "{(:$$_ternary_$$ true {2} {3})}", ())
 | 
			
		||||
    testToExpression("if true then {2} else {3}", "{(:$$_ternary_$$ true {2} {3})}", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "if true then 2 else 3",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "if true then {2} else {3}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true {2} {3}))}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "if false then {2} else if false then {4} else {5}",
 | 
			
		||||
      "{(:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5}))}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_ternary_$$ false {2} (:$$_ternary_$$ false {4} {5})))}",
 | 
			
		||||
      (),
 | 
			
		||||
    ) //nested if
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("pipe", () => {
 | 
			
		||||
    testToExpression("1 -> add(2)", "{(:add 1 2)}", ~v="3", ())
 | 
			
		||||
    testToExpression("-1 -> add(2)", "{(:add (:unaryMinus 1) 2)}", ~v="1", ()) // note that unary has higher priority naturally
 | 
			
		||||
    testToExpression("1 -> add(2) * 3", "{(:multiply (:add 1 2) 3)}", ~v="9", ())
 | 
			
		||||
    testToExpression("1 -> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "-1 -> add(2)",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:add (:unaryMinus 1) 2))}",
 | 
			
		||||
      ~v="1",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // note that unary has higher priority naturally
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "1 -> add(2) * 3",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:multiply (:add 1 2) 3))}",
 | 
			
		||||
      ~v="9",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("elixir pipe", () => {
 | 
			
		||||
    testToExpression("1 |> add(2)", "{(:add 1 2)}", ~v="3", ())
 | 
			
		||||
    testToExpression("1 |> add(2)", "{(:$_endOfOuterBlock_$ () (:add 1 2))}", ~v="3", ())
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // see testParse for priorities of to and credibleIntervalToDistribution
 | 
			
		||||
| 
						 | 
				
			
			@ -157,30 +212,31 @@ describe("Peggy to Expression", () => {
 | 
			
		|||
    // Like lambdas they have a local scope.
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "y=99; x={y=1; y}",
 | 
			
		||||
      "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y})}",
 | 
			
		||||
      ~v="@{x: 1,y: 99}",
 | 
			
		||||
      "{(:$_let_$ :y {99}); (:$_let_$ :x {(:$_let_$ :y {1}); :y}); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("lambda", () => {
 | 
			
		||||
    testToExpression("{|x| x}", "{(:$$_lambda_$$ [x] {:x})}", ~v="lambda(x=>internal code)", ())
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "{|x| x}",
 | 
			
		||||
      "{(:$_endOfOuterBlock_$ () (:$$_lambda_$$ [x] {:x}))}",
 | 
			
		||||
      ~v="lambda(x=>internal code)",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f={|x| x}",
 | 
			
		||||
      "{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})})}",
 | 
			
		||||
      ~v="@{f: lambda(x=>internal code)}",
 | 
			
		||||
      "{(:$_let_$ :f {(:$$_lambda_$$ [x] {:x})}); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f(x)=x",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))}",
 | 
			
		||||
      ~v="@{f: lambda(x=>internal code)}",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    ) // Function definitions are lambda assignments
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f(x)=x ? 1 : 0",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)}))}",
 | 
			
		||||
      ~v="@{f: lambda(x=>internal code)}",
 | 
			
		||||
      "{(:$_let_$ :f (:$$_lambda_$$ [x] {(:$$_ternary_$$ :x 1 0)})); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			@ -188,12 +244,12 @@ describe("Peggy to Expression", () => {
 | 
			
		|||
  describe("module", () => {
 | 
			
		||||
    // testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
 | 
			
		||||
    // Only.test("stdlibrary", () => {
 | 
			
		||||
    //   ReducerInterface_StdLib.internalStdLib
 | 
			
		||||
    //   ->IEvBindings
 | 
			
		||||
    //   ->InternalExpressionValue.toString
 | 
			
		||||
    //   ->expect
 | 
			
		||||
    //   ->toBe("")
 | 
			
		||||
    //  ReducerInterface_StdLib.internalStdLib
 | 
			
		||||
    //  ->IEvBindings
 | 
			
		||||
    //  ->InternalExpressionValue.toString
 | 
			
		||||
    //  ->expect
 | 
			
		||||
    //  ->toBe("")
 | 
			
		||||
    // })
 | 
			
		||||
    testToExpression("Math.pi", "{:Math.pi}", ~v="3.141592653589793", ())
 | 
			
		||||
    testToExpression("Math.pi", "{(:$_endOfOuterBlock_$ () :Math.pi)}", ~v="3.141592653589793", ())
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,92 +5,92 @@ describe("Peggy Types to Expression", () => {
 | 
			
		|||
  describe("type of", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "p: number",
 | 
			
		||||
      "{(:$_typeOf_$ :p #number)}",
 | 
			
		||||
      ~v="@{_typeReferences_: {p: #number}}",
 | 
			
		||||
      "{(:$_typeOf_$ :p #number); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {p: #number}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type alias", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "type index=number",
 | 
			
		||||
      "{(:$_typeAlias_$ #index #number)}",
 | 
			
		||||
      ~v="@{_typeAliases_: {index: #number}}",
 | 
			
		||||
      "{(:$_typeAlias_$ #index #number); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeAliases_: {index: #number}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type or", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number|string|distribution",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution)))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {typeOr: [#number,#string,#distribution],typeTag: 'typeOr'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("type function", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f: number=>number=>number",
 | 
			
		||||
      "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number)))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {f: {inputs: [#number,#number],output: #number,typeTag: 'typeFunction'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "f: number=>number",
 | 
			
		||||
      "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number)))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {f: {inputs: [#number],output: #number,typeTag: 'typeFunction'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("high priority contract", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-min(1)<-max(100)|string",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string)))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {typeOr: [{max: 100,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'},#string],typeTag: 'typeOr'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-memberOf([1,3,5])",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5)))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {memberOf: [1,3,5],typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-min(1)",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1)); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-max(10)",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10)); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {max: 10,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-min(1)<-max(10)",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10)); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number<-max(10)<-min(1)",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1)); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {max: 10,min: 1,typeIdentifier: #number,typeTag: 'typeIdentifier'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("low priority contract", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "answer: number | string $ opaque",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
 | 
			
		||||
      ~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
 | 
			
		||||
      "{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string))))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeReferences_: {answer: {opaque: true,typeOr: [#number,#string],typeTag: 'typeOr'}}}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("squiggle expressions in type contracts", () => {
 | 
			
		||||
    testToExpression(
 | 
			
		||||
      "odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
 | 
			
		||||
      "{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2)))}",
 | 
			
		||||
      ~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
 | 
			
		||||
      "{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:concat :odds1 :odds2))); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
      // ~v="@{_typeAliases_: {odds: {memberOf: [1,3,5,7,9],typeIdentifier: #number,typeTag: 'typeIdentifier'}},odds1: [1,3,5],odds2: [7,9]}",
 | 
			
		||||
      (),
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,18 +3,23 @@ open Reducer_Peggy_TestHelpers
 | 
			
		|||
 | 
			
		||||
describe("Peggy void", () => {
 | 
			
		||||
  //literal
 | 
			
		||||
  testToExpression("()", "{()}", ~v="()", ())
 | 
			
		||||
  testToExpression("()", "{(:$_endOfOuterBlock_$ () ())}", ~v="()", ())
 | 
			
		||||
  testToExpression(
 | 
			
		||||
    "fn()=1",
 | 
			
		||||
    "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1}))}",
 | 
			
		||||
    ~v="@{fn: lambda(_=>internal code)}",
 | 
			
		||||
    "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    // ~v="@{fn: lambda(_=>internal code)}",
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
  testToExpression(
 | 
			
		||||
    "fn()=1; fn()",
 | 
			
		||||
    "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:$_endOfOuterBlock_$ () (:fn ()))}",
 | 
			
		||||
    ~v="1",
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
  testToExpression("fn()=1; fn()", "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:fn ())}", ~v="1", ())
 | 
			
		||||
  testToExpression(
 | 
			
		||||
    "fn(a)=(); call fn(1)",
 | 
			
		||||
    "{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)})}",
 | 
			
		||||
    ~v="@{_: (),fn: lambda(a=>internal code)}",
 | 
			
		||||
    "{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)}); (:$_endOfOuterBlock_$ () ())}",
 | 
			
		||||
    // ~v="@{_: (),fn: lambda(a=>internal code)}",
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
module ExpressionT = Reducer_Expression_T
 | 
			
		||||
module ExternalExpressionValue = ReducerInterface.ExternalExpressionValue
 | 
			
		||||
module ErrorValue = Reducer_ErrorValue
 | 
			
		||||
module Expression = Reducer_Expression
 | 
			
		||||
module ExpressionT = Reducer_Expression_T
 | 
			
		||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
| 
						 | 
				
			
			@ -8,30 +9,26 @@ open Expect
 | 
			
		|||
let unwrapRecord = rValue =>
 | 
			
		||||
  rValue->Belt.Result.flatMap(value =>
 | 
			
		||||
    switch value {
 | 
			
		||||
    | ExternalExpressionValue.EvRecord(aRecord) => Ok(aRecord)
 | 
			
		||||
    | _ => ErrorValue.RETodo("TODO: External bindings must be returned")->Error
 | 
			
		||||
    | InternalExpressionValue.IEvRecord(aRecord) => Ok(aRecord)
 | 
			
		||||
    | _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error
 | 
			
		||||
    }
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
let expectParseToBe = (expr: string, answer: string) =>
 | 
			
		||||
  Reducer.parse(expr)->ExpressionT.toStringResult->expect->toBe(answer)
 | 
			
		||||
let expectParseToBe = (code: string, answer: string) =>
 | 
			
		||||
  Expression.BackCompatible.parse(code)->ExpressionT.toStringResult->expect->toBe(answer)
 | 
			
		||||
 | 
			
		||||
let expectEvalToBe = (expr: string, answer: string) =>
 | 
			
		||||
  Reducer.evaluate(expr)
 | 
			
		||||
  ->Reducer_Helpers.rRemoveDefaultsExternal
 | 
			
		||||
  ->ExternalExpressionValue.toStringResult
 | 
			
		||||
let expectEvalToBe = (code: string, answer: string) =>
 | 
			
		||||
  Expression.BackCompatible.evaluateString(code)
 | 
			
		||||
  ->Reducer_Helpers.rRemoveDefaultsInternal
 | 
			
		||||
  ->InternalExpressionValue.toStringResult
 | 
			
		||||
  ->expect
 | 
			
		||||
  ->toBe(answer)
 | 
			
		||||
 | 
			
		||||
let expectEvalError = (expr: string) =>
 | 
			
		||||
  Reducer.evaluate(expr)->ExternalExpressionValue.toStringResult->expect->toMatch("Error\(")
 | 
			
		||||
 | 
			
		||||
let expectEvalBindingsToBe = (expr: string, bindings: Reducer.externalBindings, answer: string) =>
 | 
			
		||||
  Reducer.evaluateUsingOptions(expr, ~externalBindings=Some(bindings), ~environment=None)
 | 
			
		||||
  ->Reducer_Helpers.rRemoveDefaultsExternal
 | 
			
		||||
  ->ExternalExpressionValue.toStringResult
 | 
			
		||||
let expectEvalError = (code: string) =>
 | 
			
		||||
  Expression.BackCompatible.evaluateString(code)
 | 
			
		||||
  ->InternalExpressionValue.toStringResult
 | 
			
		||||
  ->expect
 | 
			
		||||
  ->toBe(answer)
 | 
			
		||||
  ->toMatch("Error\(")
 | 
			
		||||
 | 
			
		||||
let testParseToBe = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
 | 
			
		||||
let testDescriptionParseToBe = (desc, expr, answer) =>
 | 
			
		||||
| 
						 | 
				
			
			@ -40,18 +37,12 @@ let testDescriptionParseToBe = (desc, expr, answer) =>
 | 
			
		|||
let testEvalError = expr => test(expr, () => expectEvalError(expr))
 | 
			
		||||
let testEvalToBe = (expr, answer) => test(expr, () => expectEvalToBe(expr, answer))
 | 
			
		||||
let testDescriptionEvalToBe = (desc, expr, answer) => test(desc, () => expectEvalToBe(expr, answer))
 | 
			
		||||
let testEvalBindingsToBe = (expr, bindingsList, answer) =>
 | 
			
		||||
  test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
 | 
			
		||||
 | 
			
		||||
module MySkip = {
 | 
			
		||||
  let testParseToBe = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
 | 
			
		||||
  let testEvalToBe = (expr, answer) => Skip.test(expr, () => expectEvalToBe(expr, answer))
 | 
			
		||||
  let testEvalBindingsToBe = (expr, bindingsList, answer) =>
 | 
			
		||||
    Skip.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
 | 
			
		||||
}
 | 
			
		||||
module MyOnly = {
 | 
			
		||||
  let testParseToBe = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
 | 
			
		||||
  let testEvalToBe = (expr, answer) => Only.test(expr, () => expectEvalToBe(expr, answer))
 | 
			
		||||
  let testEvalBindingsToBe = (expr, bindingsList, answer) =>
 | 
			
		||||
    Only.test(expr, () => expectEvalBindingsToBe(expr, bindingsList->Js.Dict.fromList, answer))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,14 @@
 | 
			
		|||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
module BindingsReplacer = Reducer_Expression_BindingsReplacer
 | 
			
		||||
module Expression = Reducer_Expression
 | 
			
		||||
// module ExpressionValue = ReducerInterface.ExpressionValue
 | 
			
		||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
 | 
			
		||||
module ExpressionWithContext = Reducer_ExpressionWithContext
 | 
			
		||||
module InternalExpressionValue = ReducerInterface.InternalExpressionValue
 | 
			
		||||
module Macro = Reducer_Expression_Macro
 | 
			
		||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
 | 
			
		||||
module T = Reducer_Expression_T
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
let testMacro_ = (
 | 
			
		||||
  tester,
 | 
			
		||||
| 
						 | 
				
			
			@ -21,8 +21,8 @@ let testMacro_ = (
 | 
			
		|||
    expr
 | 
			
		||||
    ->Macro.expandMacroCall(
 | 
			
		||||
      bindings,
 | 
			
		||||
      InternalExpressionValue.defaultEnvironment,
 | 
			
		||||
      Expression.reduceExpression,
 | 
			
		||||
      ProjectAccessorsT.identityAccessors,
 | 
			
		||||
      Expression.reduceExpressionInProject,
 | 
			
		||||
    )
 | 
			
		||||
    ->ExpressionWithContext.toStringResult
 | 
			
		||||
    ->expect
 | 
			
		||||
| 
						 | 
				
			
			@ -41,8 +41,8 @@ let testMacroEval_ = (
 | 
			
		|||
    expr
 | 
			
		||||
    ->Macro.doMacroCall(
 | 
			
		||||
      bindings,
 | 
			
		||||
      InternalExpressionValue.defaultEnvironment,
 | 
			
		||||
      Expression.reduceExpression,
 | 
			
		||||
      ProjectAccessorsT.identityAccessors,
 | 
			
		||||
      Expression.reduceExpressionInProject,
 | 
			
		||||
    )
 | 
			
		||||
    ->InternalExpressionValue.toStringResult
 | 
			
		||||
    ->expect
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ open Jest
 | 
			
		|||
open Expect
 | 
			
		||||
 | 
			
		||||
let myIevEval = (aTypeSourceCode: string) =>
 | 
			
		||||
  TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
 | 
			
		||||
  TypeCompile.ievFromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
 | 
			
		||||
let myIevEvalToString = (aTypeSourceCode: string) =>
 | 
			
		||||
  myIevEval(aTypeSourceCode)->InternalExpressionValue.toStringResult
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ let myIevTest = (test, aTypeSourceCode, answer) =>
 | 
			
		|||
  test(aTypeSourceCode, () => myIevExpectEqual(aTypeSourceCode, answer))
 | 
			
		||||
 | 
			
		||||
let myTypeEval = (aTypeSourceCode: string) =>
 | 
			
		||||
  TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpression)
 | 
			
		||||
  TypeCompile.fromTypeExpression(aTypeSourceCode, Expression.reduceExpressionInProject)
 | 
			
		||||
let myTypeEvalToString = (aTypeSourceCode: string) => myTypeEval(aTypeSourceCode)->T.toStringResult
 | 
			
		||||
 | 
			
		||||
let myTypeExpectEqual = (aTypeSourceCode, answer) =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
module Bindings = Reducer_Bindings
 | 
			
		||||
module ErrorValue = Reducer_ErrorValue
 | 
			
		||||
module Expression = Reducer_Expression
 | 
			
		||||
module ExpressionT = Reducer_Expression_T
 | 
			
		||||
module ErrorValue = Reducer_ErrorValue
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
 | 
			
		||||
module T = Reducer_Type_T
 | 
			
		||||
module TypeChecker = Reducer_Type_TypeChecker
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,10 +14,10 @@ let checkArgumentsSourceCode = (aTypeSourceCode: string, sourceCode: string): re
 | 
			
		|||
  'v,
 | 
			
		||||
  ErrorValue.t,
 | 
			
		||||
> => {
 | 
			
		||||
  let reducerFn = Expression.reduceExpression
 | 
			
		||||
  let reducerFn = Expression.reduceExpressionInProject
 | 
			
		||||
  let rResult =
 | 
			
		||||
    Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
 | 
			
		||||
      reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
 | 
			
		||||
    Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
 | 
			
		||||
      reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
 | 
			
		||||
    )
 | 
			
		||||
  rResult->Belt.Result.flatMap(result =>
 | 
			
		||||
    switch result {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		|||
module Bindings = Reducer_Bindings
 | 
			
		||||
module T = Reducer_Type_T
 | 
			
		||||
module TypeChecker = Reducer_Type_TypeChecker
 | 
			
		||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,10 @@ let isTypeOfSourceCode = (aTypeSourceCode: string, sourceCode: string): result<
 | 
			
		|||
  'v,
 | 
			
		||||
  ErrorValue.t,
 | 
			
		||||
> => {
 | 
			
		||||
  let reducerFn = Expression.reduceExpression
 | 
			
		||||
  let reducerFn = Expression.reduceExpressionInProject
 | 
			
		||||
  let rResult =
 | 
			
		||||
    Reducer.parse(sourceCode)->Belt.Result.flatMap(expr =>
 | 
			
		||||
      reducerFn(expr, Bindings.emptyBindings, InternalExpressionValue.defaultEnvironment)
 | 
			
		||||
    Expression.BackCompatible.parse(sourceCode)->Belt.Result.flatMap(expr =>
 | 
			
		||||
      reducerFn(expr, Bindings.emptyBindings, ProjectAccessorsT.identityAccessors)
 | 
			
		||||
    )
 | 
			
		||||
  rResult->Belt.Result.flatMap(result => TypeChecker.isTypeOf(aTypeSourceCode, result, reducerFn))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,11 @@ open Expect
 | 
			
		|||
module DispatchT = Reducer_Dispatch_T
 | 
			
		||||
module Expression = Reducer_Expression
 | 
			
		||||
module ExpressionT = Reducer_Expression_T
 | 
			
		||||
module TypeCompile = Reducer_Type_Compile
 | 
			
		||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
 | 
			
		||||
module ProjectReducerFnT = ReducerProject_ReducerFn_T
 | 
			
		||||
module TypeChecker = Reducer_Type_TypeChecker
 | 
			
		||||
module TypeCompile = Reducer_Type_Compile
 | 
			
		||||
 | 
			
		||||
open ReducerInterface_InternalExpressionValue
 | 
			
		||||
 | 
			
		||||
type errorValue = Reducer_ErrorValue.errorValue
 | 
			
		||||
| 
						 | 
				
			
			@ -14,13 +17,14 @@ type errorValue = Reducer_ErrorValue.errorValue
 | 
			
		|||
// In dispatchChainPiece, we execute an return the result of execution if there is a type match.
 | 
			
		||||
// Otherwise we return None so that the call chain can continue.
 | 
			
		||||
// So we want to build a function like
 | 
			
		||||
// dispatchChainPiece = (call: functionCall, environment): option<result<internalExpressionValue, errorValue>>
 | 
			
		||||
// dispatchChainPiece = (call: functionCall, accessors): option<result<internalExpressionValue, errorValue>>
 | 
			
		||||
// Use accessors.environment to get the environment finally.
 | 
			
		||||
 | 
			
		||||
// Now lets make the dispatchChainPiece itself.
 | 
			
		||||
// Note that I am not passing the reducer to the dispatchChainPiece as an argument because it is in the context anyway.
 | 
			
		||||
// Keep in mind that reducerFn is necessary for map/reduce so dispatchChainPiece should have a reducerFn in context.
 | 
			
		||||
 | 
			
		||||
let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispatchChainPiece => {
 | 
			
		||||
let makeMyDispatchChainPiece = (reducer: ProjectReducerFnT.t): DispatchT.dispatchChainPiece => {
 | 
			
		||||
  // Let's have a pure implementations
 | 
			
		||||
  module Implementation = {
 | 
			
		||||
    let stringConcat = (a: string, b: string): string => Js.String2.concat(a, b)
 | 
			
		||||
| 
						 | 
				
			
			@ -45,15 +49,15 @@ let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispa
 | 
			
		|||
 | 
			
		||||
  // Let's bridge the pure implementation to expression values
 | 
			
		||||
  module Bridge = {
 | 
			
		||||
    let stringConcat: DispatchT.genericIEvFunction = (args, _environment) => {
 | 
			
		||||
    let stringConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
 | 
			
		||||
      let (a, b) = extractStringString(args)
 | 
			
		||||
      Implementation.stringConcat(a, b)->IEvString->Ok
 | 
			
		||||
    }
 | 
			
		||||
    let arrayConcat: DispatchT.genericIEvFunction = (args, _environment) => {
 | 
			
		||||
    let arrayConcat: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
 | 
			
		||||
      let (a, b) = extractArrayArray(args)
 | 
			
		||||
      Implementation.arrayConcat(a, b)->IEvArray->Ok
 | 
			
		||||
    }
 | 
			
		||||
    let plot: DispatchT.genericIEvFunction = (args, _environment) => {
 | 
			
		||||
    let plot: DispatchT.genericIEvFunction = (args, _accessors: ProjectAccessorsT.t) => {
 | 
			
		||||
      switch args {
 | 
			
		||||
      // Just assume that we are doing the business of extracting and converting the deep record
 | 
			
		||||
      | [IEvRecord(_)] => Implementation.plot({"title": "This is a plot"})->IEvString->Ok
 | 
			
		||||
| 
						 | 
				
			
			@ -98,12 +102,12 @@ let makeMyDispatchChainPiece = (reducer: ExpressionT.reducerFn): DispatchT.dispa
 | 
			
		|||
// Exactly the same as the one used in real life
 | 
			
		||||
let _dispatch = (
 | 
			
		||||
  call: functionCall,
 | 
			
		||||
  environment,
 | 
			
		||||
  reducer: Reducer_Expression_T.reducerFn,
 | 
			
		||||
  accessors: ProjectAccessorsT.t,
 | 
			
		||||
  reducer: ProjectReducerFnT.t,
 | 
			
		||||
  chain,
 | 
			
		||||
): result<internalExpressionValue, 'e> => {
 | 
			
		||||
  let dispatchChainPiece = makeMyDispatchChainPiece(reducer)
 | 
			
		||||
  dispatchChainPiece(call, environment)->E.O2.defaultFn(() => chain(call, environment, reducer))
 | 
			
		||||
  dispatchChainPiece(call, accessors)->E.O2.defaultFn(() => chain(call, accessors, reducer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// What is important about this implementation?
 | 
			
		||||
| 
						 | 
				
			
			@ -112,12 +116,12 @@ let _dispatch = (
 | 
			
		|||
// B) Complicated recursive record types are not a problem.
 | 
			
		||||
 | 
			
		||||
describe("Type Dispatch", () => {
 | 
			
		||||
  let reducerFn = Expression.reduceExpression
 | 
			
		||||
  let reducerFn = Expression.reduceExpressionInProject
 | 
			
		||||
  let dispatchChainPiece = makeMyDispatchChainPiece(reducerFn)
 | 
			
		||||
  test("stringConcat", () => {
 | 
			
		||||
    let call: functionCall = ("concat", [IEvString("hello"), IEvString("world")])
 | 
			
		||||
 | 
			
		||||
    let result = dispatchChainPiece(call, defaultEnvironment)
 | 
			
		||||
    let result = dispatchChainPiece(call, ProjectAccessorsT.identityAccessors)
 | 
			
		||||
    expect(result)->toEqual(Some(Ok(IEvString("helloworld"))))
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +0,0 @@
 | 
			
		|||
open Jest
 | 
			
		||||
open Reducer_TestHelpers
 | 
			
		||||
 | 
			
		||||
describe("Eval with Bindings", () => {
 | 
			
		||||
  testEvalBindingsToBe("x", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(1)")
 | 
			
		||||
  testEvalBindingsToBe("x+1", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
 | 
			
		||||
  testParseToBe("y = x+1; y", "Ok({(:$_let_$ :y {(:add :x 1)}); :y})")
 | 
			
		||||
  testEvalBindingsToBe("y = x+1; y", list{("x", ExternalExpressionValue.EvNumber(1.))}, "Ok(2)")
 | 
			
		||||
  testEvalBindingsToBe(
 | 
			
		||||
    "y = x+1",
 | 
			
		||||
    list{("x", ExternalExpressionValue.EvNumber(1.))},
 | 
			
		||||
    "Ok(@{x: 1,y: 2})",
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -2,8 +2,14 @@ open Jest
 | 
			
		|||
open Reducer_TestHelpers
 | 
			
		||||
 | 
			
		||||
describe("Parse function assignment", () => {
 | 
			
		||||
  testParseToBe("f(x)=x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x}))})")
 | 
			
		||||
  testParseToBe("f(x)=2*x", "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)}))})")
 | 
			
		||||
  testParseToBe(
 | 
			
		||||
    "f(x)=x",
 | 
			
		||||
    "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {:x})); (:$_endOfOuterBlock_$ () ())})",
 | 
			
		||||
  )
 | 
			
		||||
  testParseToBe(
 | 
			
		||||
    "f(x)=2*x",
 | 
			
		||||
    "Ok({(:$_let_$ :f (:$$_lambda_$$ [x] {(:multiply 2 :x)})); (:$_endOfOuterBlock_$ () ())})",
 | 
			
		||||
  )
 | 
			
		||||
  //MathJs does not allow blocks in function definitions
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,33 +39,27 @@ describe("symbol not defined", () => {
 | 
			
		|||
})
 | 
			
		||||
 | 
			
		||||
describe("call and bindings", () => {
 | 
			
		||||
  testEvalToBe("f(x)=x+1", "Ok(@{f: lambda(x=>internal code)})")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; f(0)", "Ok(1)")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; f(1)", "Ok(2)")
 | 
			
		||||
  testEvalToBe("f=1;y=2", "Ok(@{f: 1,y: 2})")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; y=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2})")
 | 
			
		||||
  testEvalToBe("f=1;y=2", "Ok(())")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; y=f(1); y", "Ok(2)")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; y=f(1); f(1)", "Ok(2)")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; y=f(1); z=f(1)", "Ok(@{f: lambda(x=>internal code),y: 2,z: 2})")
 | 
			
		||||
  testEvalToBe(
 | 
			
		||||
    "f(x)=x+1; g(x)=f(x)+1",
 | 
			
		||||
    "Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code)})",
 | 
			
		||||
  )
 | 
			
		||||
  testEvalToBe("f(x)=x+1; y=f(1); z=f(1); z", "Ok(2)")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(0)", "Ok(2)")
 | 
			
		||||
  testParseToBe(
 | 
			
		||||
    "f=99; g(x)=f; g(2)",
 | 
			
		||||
    "Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:g 2)})",
 | 
			
		||||
    "Ok({(:$_let_$ :f {99}); (:$_let_$ :g (:$$_lambda_$$ [x] {:f})); (:$_endOfOuterBlock_$ () (:g 2))})",
 | 
			
		||||
  )
 | 
			
		||||
  testEvalToBe("f=99; g(x)=f; g(2)", "Ok(99)")
 | 
			
		||||
  testEvalToBe("f(x)=x; g(x)=f(x); g(2)", "Ok(2)")
 | 
			
		||||
  testEvalToBe(
 | 
			
		||||
    "f(x)=x+1; g(x)=f(x)+1; y=g(2)",
 | 
			
		||||
    "Ok(@{f: lambda(x=>internal code),g: lambda(x=>internal code),y: 4})",
 | 
			
		||||
  )
 | 
			
		||||
  testEvalToBe("f(x)=x+1; g(x)=f(x)+1; y=g(2); y", "Ok(4)")
 | 
			
		||||
  testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("function tricks", () => {
 | 
			
		||||
  testEvalError("f(x)=f(y)=2; f(2)") //Error because chain assignment is not allowed
 | 
			
		||||
  testEvalToBe("y=2;g(x)=y+1;g(2)", "Ok(3)")
 | 
			
		||||
  testEvalToBe("y=2;g(x)=inspect(y)+1", "Ok(@{g: lambda(x=>internal code),y: 2})")
 | 
			
		||||
  testEvalToBe("y=2;g(x)=inspect(y)+1;y", "Ok(2)")
 | 
			
		||||
  MySkip.testEvalToBe("f(x) = x(x); f(f)", "????") // TODO: Infinite loop. Any solution? Catching proper exception or timeout?
 | 
			
		||||
  MySkip.testEvalToBe("f(x, x)=x+x; f(1,2)", "????") // TODO: Duplicate parameters
 | 
			
		||||
  testEvalToBe("myadd(x,y)=x+y; z=myadd; z", "Ok(lambda(x,y=>internal code))")
 | 
			
		||||
| 
						 | 
				
			
			@ -73,10 +67,7 @@ describe("function tricks", () => {
 | 
			
		|||
})
 | 
			
		||||
 | 
			
		||||
describe("lambda in structures", () => {
 | 
			
		||||
  testEvalToBe(
 | 
			
		||||
    "myadd(x,y)=x+y; z=[myadd]",
 | 
			
		||||
    "Ok(@{myadd: lambda(x,y=>internal code),z: [lambda(x,y=>internal code)]})",
 | 
			
		||||
  )
 | 
			
		||||
  testEvalToBe("myadd(x,y)=x+y; z=[myadd]", "Ok(())")
 | 
			
		||||
  testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0]", "Ok(lambda(x,y=>internal code))")
 | 
			
		||||
  testEvalToBe("myadd(x,y)=x+y; z=[myadd]; z[0](3,2)", "Ok(5)")
 | 
			
		||||
  testEvalToBe("myaddd(x,y)=x+y; z={x: myaddd}; z", "Ok({x: lambda(x,y=>internal code)})")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,10 @@ open Jest
 | 
			
		|||
open Reducer_TestHelpers
 | 
			
		||||
 | 
			
		||||
describe("Parse ternary operator", () => {
 | 
			
		||||
  testParseToBe("true ? 'YES' : 'NO'", "Ok({(:$$_ternary_$$ true 'YES' 'NO')})")
 | 
			
		||||
  testParseToBe(
 | 
			
		||||
    "true ? 'YES' : 'NO'",
 | 
			
		||||
    "Ok({(:$_endOfOuterBlock_$ () (:$$_ternary_$$ true 'YES' 'NO'))})",
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("Evaluate ternary operator", () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ describe("eval", () => {
 | 
			
		|||
    testEvalToBe("x=1; y=x+1; y+1", "Ok(3)")
 | 
			
		||||
    testEvalError("1; x=1")
 | 
			
		||||
    testEvalError("1; 1")
 | 
			
		||||
    testEvalToBe("x=1; x=1", "Ok(@{x: 1})")
 | 
			
		||||
    testEvalToBe("x=1; x=1; x", "Ok(1)")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,28 +119,40 @@ describe("eval on distribution functions", () => {
 | 
			
		|||
 | 
			
		||||
describe("parse on distribution functions", () => {
 | 
			
		||||
  describe("power", () => {
 | 
			
		||||
    testParse("normal(5,2) ^ normal(5,1)", "Ok({(:pow (:normal 5 2) (:normal 5 1))})")
 | 
			
		||||
    testParse("3 ^ normal(5,1)", "Ok({(:pow 3 (:normal 5 1))})")
 | 
			
		||||
    testParse("normal(5,2) ^ 3", "Ok({(:pow (:normal 5 2) 3)})")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "normal(5,2) ^ normal(5,1)",
 | 
			
		||||
      "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) (:normal 5 1)))})",
 | 
			
		||||
    )
 | 
			
		||||
    testParse("3 ^ normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:pow 3 (:normal 5 1)))})")
 | 
			
		||||
    testParse("normal(5,2) ^ 3", "Ok({(:$_endOfOuterBlock_$ () (:pow (:normal 5 2) 3))})")
 | 
			
		||||
  })
 | 
			
		||||
  describe("subtraction", () => {
 | 
			
		||||
    testParse("10 - normal(5,1)", "Ok({(:subtract 10 (:normal 5 1))})")
 | 
			
		||||
    testParse("normal(5,1) - 10", "Ok({(:subtract (:normal 5 1) 10)})")
 | 
			
		||||
    testParse("10 - normal(5,1)", "Ok({(:$_endOfOuterBlock_$ () (:subtract 10 (:normal 5 1)))})")
 | 
			
		||||
    testParse("normal(5,1) - 10", "Ok({(:$_endOfOuterBlock_$ () (:subtract (:normal 5 1) 10))})")
 | 
			
		||||
  })
 | 
			
		||||
  describe("pointwise arithmetic expressions", () => {
 | 
			
		||||
    testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
 | 
			
		||||
    testParse(
 | 
			
		||||
      ~skip=true,
 | 
			
		||||
      "normal(5,2) .- normal(5,1)",
 | 
			
		||||
      "Ok((:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1))))",
 | 
			
		||||
      "Ok((:$_endOfOuterBlock_$ () (:$$_block_$$ (:dotSubtract (:normal 5 2) (:normal 5 1)))))",
 | 
			
		||||
      // TODO: !!! returns "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})"
 | 
			
		||||
    )
 | 
			
		||||
    testParse("normal(5,2) .* normal(5,1)", "Ok({(:dotMultiply (:normal 5 2) (:normal 5 1))})")
 | 
			
		||||
    testParse("normal(5,2) ./ normal(5,1)", "Ok({(:dotDivide (:normal 5 2) (:normal 5 1))})")
 | 
			
		||||
    testParse("normal(5,2) .^ normal(5,1)", "Ok({(:dotPow (:normal 5 2) (:normal 5 1))})")
 | 
			
		||||
    testParse(
 | 
			
		||||
      "normal(5,2) .* normal(5,1)",
 | 
			
		||||
      "Ok({(:$_endOfOuterBlock_$ () (:dotMultiply (:normal 5 2) (:normal 5 1)))})",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "normal(5,2) ./ normal(5,1)",
 | 
			
		||||
      "Ok({(:$_endOfOuterBlock_$ () (:dotDivide (:normal 5 2) (:normal 5 1)))})",
 | 
			
		||||
    )
 | 
			
		||||
    testParse(
 | 
			
		||||
      "normal(5,2) .^ normal(5,1)",
 | 
			
		||||
      "Ok({(:$_endOfOuterBlock_$ () (:dotPow (:normal 5 2) (:normal 5 1)))})",
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
  describe("equality", () => {
 | 
			
		||||
    testParse("5 == normal(5,2)", "Ok({(:equal 5 (:normal 5 2))})")
 | 
			
		||||
    testParse("5 == normal(5,2)", "Ok({(:$_endOfOuterBlock_$ () (:equal 5 (:normal 5 2)))})")
 | 
			
		||||
  })
 | 
			
		||||
  describe("pointwise adding two normals", () => {
 | 
			
		||||
    testParse(~skip=true, "normal(5,2) .+ normal(5,1)", "Ok((:dotAdd (:normal 5 2) (:normal 5 1)))")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
open ReducerInterface.ExternalExpressionValue
 | 
			
		||||
open ReducerInterface.InternalExpressionValue
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
 | 
			
		||||
describe("ExpressionValue", () => {
 | 
			
		||||
  test("argsToString", () => expect([EvNumber(1.), EvString("a")]->argsToString)->toBe("1,'a'"))
 | 
			
		||||
  test("argsToString", () => expect([IEvNumber(1.), IEvString("a")]->argsToString)->toBe("1,'a'"))
 | 
			
		||||
 | 
			
		||||
  test("toStringFunctionCall", () =>
 | 
			
		||||
    expect(("fn", [EvNumber(1.), EvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
 | 
			
		||||
    expect(("fn", [IEvNumber(1.), IEvString("a")])->toStringFunctionCall)->toBe("fn(1,'a')")
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module Topology = ReducerProject_Topology
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("Topology Diff", () => {
 | 
			
		||||
  test("when equal 1x", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a"], ["a"])->expect == []
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("when equal 3x", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a", "b", "c"], ["a", "b", "c"])->expect == []
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("less dependents", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a", "b"], ["a", "b", "c", "d"])->expect == []
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("more dependents", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a", "b", "c", "d"], ["a", "b"])->expect == ["c", "d"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("change midway", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a", "b", "bb", "c", "d"], ["a", "b", "c", "d"])->expect == [
 | 
			
		||||
        "bb",
 | 
			
		||||
        "c",
 | 
			
		||||
        "d",
 | 
			
		||||
      ]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("swap", () => {
 | 
			
		||||
    Topology.runOrderDiff(["a", "b", "c", "d"], ["a", "c", "b", "d"])->expect == ["b", "c", "d"]
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("Parse includes", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(
 | 
			
		||||
    project,
 | 
			
		||||
    "main",
 | 
			
		||||
    `
 | 
			
		||||
#include 'common'
 | 
			
		||||
x=1`,
 | 
			
		||||
  )
 | 
			
		||||
  Project.parseIncludes(project, "main")
 | 
			
		||||
  test("dependencies", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["common"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("getIncludes", () => {
 | 
			
		||||
    let mainIncludes = Project.getIncludes(project, "main")
 | 
			
		||||
    switch mainIncludes {
 | 
			
		||||
    | Ok(includes) => expect(includes) == ["common"]
 | 
			
		||||
    | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  let internalProject = project->Project.T.Private.castToInternalProject
 | 
			
		||||
  test("past chain", () => {
 | 
			
		||||
    expect(Project.Private.getPastChain(internalProject, "main")) == ["common"]
 | 
			
		||||
  })
 | 
			
		||||
  test("import as variables", () => {
 | 
			
		||||
    expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("Parse includes", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(
 | 
			
		||||
    project,
 | 
			
		||||
    "main",
 | 
			
		||||
    `
 | 
			
		||||
#include 'common'
 | 
			
		||||
#include 'myModule' as myVariable
 | 
			
		||||
x=1`,
 | 
			
		||||
  )
 | 
			
		||||
  Project.parseIncludes(project, "main")
 | 
			
		||||
 | 
			
		||||
  test("dependencies", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["common", "myModule"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("dependents", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("getIncludes", () => {
 | 
			
		||||
    let mainIncludes = Project.getIncludes(project, "main")
 | 
			
		||||
    switch mainIncludes {
 | 
			
		||||
    | Ok(includes) => expect(includes) == ["common", "myModule"]
 | 
			
		||||
    | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  let internalProject = project->Project.T.Private.castToInternalProject
 | 
			
		||||
 | 
			
		||||
  test("direct past chain", () => {
 | 
			
		||||
    expect(Project.Private.getPastChain(internalProject, "main")) == ["common"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("direct includes", () => {
 | 
			
		||||
    expect(Project.Private.getDirectIncludes(internalProject, "main")) == ["common"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("include as variables", () => {
 | 
			
		||||
    expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [
 | 
			
		||||
        ("myVariable", "myModule"),
 | 
			
		||||
      ]
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("Parse multiple direct includes", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(
 | 
			
		||||
    project,
 | 
			
		||||
    "main",
 | 
			
		||||
    `
 | 
			
		||||
#include 'common' 
 | 
			
		||||
#include 'common2'
 | 
			
		||||
#include 'myModule' as myVariable
 | 
			
		||||
x=1`,
 | 
			
		||||
  )
 | 
			
		||||
  Project.parseIncludes(project, "main")
 | 
			
		||||
  test("dependencies", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["common", "common2", "myModule"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("getIncludes", () => {
 | 
			
		||||
    let mainIncludes = Project.getIncludes(project, "main")
 | 
			
		||||
    switch mainIncludes {
 | 
			
		||||
    | Ok(includes) => expect(includes) == ["common", "common2", "myModule"]
 | 
			
		||||
    | Error(error) => fail(error->Reducer_ErrorValue.errorToString)
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  let internalProject = project->Project.T.Private.castToInternalProject
 | 
			
		||||
  test("direct past chain", () => {
 | 
			
		||||
    expect(Project.getPastChain(project, "main")) == ["common", "common2"]
 | 
			
		||||
  })
 | 
			
		||||
  test("include as variables", () => {
 | 
			
		||||
    expect(Project.Private.getIncludesAsVariables(internalProject, "main")) == [
 | 
			
		||||
        ("myVariable", "myModule"),
 | 
			
		||||
      ]
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,195 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
// test("", () => expect(1)->toBe(1))
 | 
			
		||||
 | 
			
		||||
let runFetchResult = (project, sourceId) => {
 | 
			
		||||
  Project.run(project, sourceId)
 | 
			
		||||
  Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let runFetchFlatBindings = (project, sourceId) => {
 | 
			
		||||
  Project.run(project, sourceId)
 | 
			
		||||
  Project.getBindings(project, sourceId)
 | 
			
		||||
  ->Bindings.removeResult
 | 
			
		||||
  ->InternalExpressionValue.toStringBindings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
test("setting continuation", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  let privateProject = project->Project.T.Private.castToInternalProject
 | 
			
		||||
  let sampleBindings = Bindings.emptyBindings->Bindings.set("test", IEvVoid)
 | 
			
		||||
  Project.Private.setContinuation(privateProject, "main", sampleBindings)
 | 
			
		||||
  let answer = Project.Private.getContinuation(privateProject, "main")
 | 
			
		||||
  expect(answer)->toBe(sampleBindings)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("test result true", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "main", "true")
 | 
			
		||||
  runFetchResult(project, "main")->expect->toBe("Ok(true)")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("test result false", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "main", "false")
 | 
			
		||||
  runFetchResult(project, "main")->expect->toBe("Ok(false)")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("test library", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "main", "x=Math.pi; x")
 | 
			
		||||
  runFetchResult(project, "main")->expect->toBe("Ok(3.141592653589793)")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test("test bindings", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "variables", "myVariable=666")
 | 
			
		||||
  runFetchFlatBindings(project, "variables")->expect->toBe("@{myVariable: 666}")
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("project1", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "first", "x=1")
 | 
			
		||||
  Project.setSource(project, "main", "x")
 | 
			
		||||
  Project.setContinues(project, "main", ["first"])
 | 
			
		||||
  let internalProject = project->Project.T.Private.castToInternalProject
 | 
			
		||||
 | 
			
		||||
  test("runOrder", () => {
 | 
			
		||||
    expect(Project.getRunOrder(project)) == ["first", "main"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents first", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "first")) == ["main"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependencies first", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "first")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents main", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("dependencies main", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["first"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("past chain first", () => {
 | 
			
		||||
    expect(Project.Private.getPastChain(internalProject, "first")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("past chain main", () => {
 | 
			
		||||
    expect(Project.Private.getPastChain(internalProject, "main")) == ["first"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("test result", () => {
 | 
			
		||||
    runFetchResult(project, "main")->expect->toBe("Ok(1)")
 | 
			
		||||
  })
 | 
			
		||||
  test("test bindings", () => {
 | 
			
		||||
    runFetchFlatBindings(project, "first")->expect->toBe("@{x: 1}")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("project2", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setContinues(project, "main", ["second"])
 | 
			
		||||
  Project.setContinues(project, "second", ["first"])
 | 
			
		||||
  Project.setSource(project, "first", "x=1")
 | 
			
		||||
  Project.setSource(project, "second", "y=2")
 | 
			
		||||
  Project.setSource(project, "main", "y")
 | 
			
		||||
 | 
			
		||||
  test("runOrder", () => {
 | 
			
		||||
    expect(Project.getRunOrder(project)) == ["first", "second", "main"]
 | 
			
		||||
  })
 | 
			
		||||
  test("runOrderFor", () => {
 | 
			
		||||
    expect(Project.getRunOrderFor(project, "first")) == ["first"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependencies first", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "first")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents first", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "first")) == ["second", "main"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents main", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("dependencies main", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["first", "second"]
 | 
			
		||||
  })
 | 
			
		||||
  test("test result", () => {
 | 
			
		||||
    runFetchResult(project, "main")->expect->toBe("Ok(2)")
 | 
			
		||||
  })
 | 
			
		||||
  test("test bindings", () => {
 | 
			
		||||
    runFetchFlatBindings(project, "main")->expect->toBe("@{x: 1,y: 2}")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("project with include", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setContinues(project, "main", ["second"])
 | 
			
		||||
  Project.setContinues(project, "second", ["first"])
 | 
			
		||||
 | 
			
		||||
  Project.setSource(
 | 
			
		||||
    project,
 | 
			
		||||
    "first",
 | 
			
		||||
    `
 | 
			
		||||
  #include 'common'
 | 
			
		||||
  x=1`,
 | 
			
		||||
  )
 | 
			
		||||
  Project.parseIncludes(project, "first")
 | 
			
		||||
  Project.parseIncludes(project, "first") //The only way of setting includes
 | 
			
		||||
  //Don't forget to parse includes after changing the source
 | 
			
		||||
 | 
			
		||||
  Project.setSource(project, "common", "common=0")
 | 
			
		||||
  Project.setSource(
 | 
			
		||||
    project,
 | 
			
		||||
    "second",
 | 
			
		||||
    `
 | 
			
		||||
  #include 'common'
 | 
			
		||||
  y=2`,
 | 
			
		||||
  )
 | 
			
		||||
  Project.parseIncludes(project, "second") //The only way of setting includes
 | 
			
		||||
 | 
			
		||||
  Project.setSource(project, "main", "y")
 | 
			
		||||
 | 
			
		||||
  test("runOrder", () => {
 | 
			
		||||
    expect(Project.getRunOrder(project)) == ["common", "first", "second", "main"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("runOrderFor", () => {
 | 
			
		||||
    expect(Project.getRunOrderFor(project, "first")) == ["common", "first"]
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("dependencies first", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "first")) == ["common"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents first", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "first")) == ["second", "main"]
 | 
			
		||||
  })
 | 
			
		||||
  test("dependents main", () => {
 | 
			
		||||
    expect(Project.getDependents(project, "main")) == []
 | 
			
		||||
  })
 | 
			
		||||
  test("dependencies main", () => {
 | 
			
		||||
    expect(Project.getDependencies(project, "main")) == ["common", "first", "second"]
 | 
			
		||||
  })
 | 
			
		||||
  test("test result", () => {
 | 
			
		||||
    runFetchResult(project, "main")->expect->toBe("Ok(2)")
 | 
			
		||||
  })
 | 
			
		||||
  test("test bindings", () => {
 | 
			
		||||
    runFetchFlatBindings(project, "main")->expect->toBe("@{common: 0,x: 1,y: 2}")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("project with independent sources", () => {
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "first", "1")
 | 
			
		||||
  Project.setSource(project, "second", "2")
 | 
			
		||||
  test("run order of first", () => {
 | 
			
		||||
    expect(Project.getRunOrderFor(project, "first")) == ["first"]
 | 
			
		||||
  })
 | 
			
		||||
  test("run order of second", () => {
 | 
			
		||||
    expect(Project.getRunOrderFor(project, "second")) == ["second"]
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("ReducerProject Tutorial", () => {
 | 
			
		||||
  describe("Single source", () => {
 | 
			
		||||
    /*
 | 
			
		||||
Case "Running a single source". 
 | 
			
		||||
*/
 | 
			
		||||
    test("run", () => {
 | 
			
		||||
      /* Let's start with running a single source and getting Result as well as the Bindings 
 | 
			
		||||
       First you need to create a project. A project is a collection of sources. 
 | 
			
		||||
       Project takes care of the dependencies between the sources, correct compilation and run order. 
 | 
			
		||||
       You can run any source in the project. It will be compiled and run if it is not already done else already existing results will be presented.
 | 
			
		||||
       The dependencies will be automatically compiled and run. So you don't need to worry about that in a multi source project.
 | 
			
		||||
       In summary you issue a run command on the whole project or on a specific source to ensure that there is a result for that source.
 | 
			
		||||
 */
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
      /* Every source has a name. This is used for debugging, dependencies and error messages. */
 | 
			
		||||
      Project.setSource(project, "main", "1 + 2")
 | 
			
		||||
      /* Let's run "main" source. */
 | 
			
		||||
      project->Project.run("main")
 | 
			
		||||
      /* Now you have a result for "main" source. 
 | 
			
		||||
       Running one by one is necessary for UI to navigate among the sources and to see the results by source. 
 | 
			
		||||
       And you're free to run any source you want. 
 | 
			
		||||
       You will look at the results of this source and you don't want to run the others if not required.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
      /* However, you could also run the whole project.
 | 
			
		||||
       If you have all the sources, you can always run the whole project. 
 | 
			
		||||
       Dependencies and recompiling on demand will be taken care of by the project. 
 | 
			
		||||
 */
 | 
			
		||||
      project->Project.runAll
 | 
			
		||||
 | 
			
		||||
      /* Either with run or runAll you executed the project. 
 | 
			
		||||
       You can get the result of a specific source by calling getResult for that source. 
 | 
			
		||||
       You can get the bindings of a specific source by calling getBindings for that source. 
 | 
			
		||||
       If there is any runtime error, getResult will return the error.
 | 
			
		||||
 | 
			
		||||
       Note that getResult returns None if the source has not been run.
 | 
			
		||||
       Getting None means you have forgotten to run the source.
 | 
			
		||||
 */
 | 
			
		||||
      let result = project->Project.getResult("main")
 | 
			
		||||
      let bindings = project->Project.getBindings("main")->Bindings.removeResult
 | 
			
		||||
 | 
			
		||||
      /* Let's display the result and bindings */
 | 
			
		||||
      (
 | 
			
		||||
        result->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings->InternalExpressionValue.toStringBindings,
 | 
			
		||||
      )->expect == ("Ok(3)", "@{}")
 | 
			
		||||
      /* You've got 3 with empty bindings. */
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    test("run summary", () => {
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
      Project.setSource(project, "main", "1 + 2")
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
      let result = Project.getResult(project, "main")
 | 
			
		||||
      let bindings = Project.getBindings(project, "main")->Bindings.removeResult
 | 
			
		||||
      /* Now you have external bindings and external result. */
 | 
			
		||||
      (
 | 
			
		||||
        result->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
 | 
			
		||||
      )->expect == ("Ok(3)", "@{}")
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    test("run with an environment", () => {
 | 
			
		||||
      /* Running the source code like above allows you to set a custom environment */
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
 | 
			
		||||
      /* Optional. Set your custom environment anytime before running */
 | 
			
		||||
      Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment)
 | 
			
		||||
 | 
			
		||||
      Project.setSource(project, "main", "1 + 2")
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
      let result = Project.getResult(project, "main")
 | 
			
		||||
      let _bindings = Project.getBindings(project, "main")
 | 
			
		||||
      result->InternalExpressionValue.toStringResult->expect == "Ok(3)"
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    test("shortcut", () => {
 | 
			
		||||
      /* If you are running single source without includes and you don't need a custom environment, you can use the shortcut. */
 | 
			
		||||
      /* Examples above was to prepare you for the multi source tutorial. */
 | 
			
		||||
      let (result, bindings) = Project.evaluate("1+2")
 | 
			
		||||
      (
 | 
			
		||||
        result->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
 | 
			
		||||
      )->expect == ("Ok(3)", "@{}")
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
//TODO multiple sources
 | 
			
		||||
//TODO multiple sources with includes. Introduction to includes
 | 
			
		||||
//TODO multiple sources with multi level includes. Cycle detection
 | 
			
		||||
//TODO
 | 
			
		||||
//TODO: Implement a runOrder consideration - clean results based on run order.
 | 
			
		||||
//TODO: runOrder vs setSource/touchSource
 | 
			
		||||
//TODO: Advanced details: (below)
 | 
			
		||||
//TODO  runOrder. includes vs continues. Run order based reexecution
 | 
			
		||||
//TODO: dependents and reexecution
 | 
			
		||||
//TODO: dependencies and reexecution
 | 
			
		||||
//TODO: cleanAllResults clean
 | 
			
		||||
//TODO: cleanAll clean 
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("ReducerProject Tutorial", () => {
 | 
			
		||||
  describe("Multi source", () => {
 | 
			
		||||
    /*
 | 
			
		||||
     Case "Running multiple sources" */
 | 
			
		||||
    test("Chaining", () => {
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
      /* This time let's add 3 sources and chain them together */
 | 
			
		||||
      Project.setSource(project, "source1", "x=1")
 | 
			
		||||
 | 
			
		||||
      Project.setSource(project, "source2", "y=2")
 | 
			
		||||
      /* To run, source2 depends on source1 */
 | 
			
		||||
      Project.setContinues(project, "source2", ["source1"])
 | 
			
		||||
 | 
			
		||||
      Project.setSource(project, "source3", "z=3")
 | 
			
		||||
      /* To run, source3 depends on source2 */
 | 
			
		||||
      Project.setContinues(project, "source3", ["source2"])
 | 
			
		||||
 | 
			
		||||
      /* Now we can run the project */
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
 | 
			
		||||
      /* And let's check the result and bindings of source3 */
 | 
			
		||||
      let result3 = Project.getResult(project, "source3")
 | 
			
		||||
      let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
 | 
			
		||||
 | 
			
		||||
      (
 | 
			
		||||
        result3->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings3->InternalExpressionValue.toStringBindings,
 | 
			
		||||
      )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    test("Depending", () => {
 | 
			
		||||
      /* Instead of chaining the sources, we could have a dependency tree */
 | 
			
		||||
      /* The point here is that any source can depend on multiple sources */
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
 | 
			
		||||
      /* This time source1 and source2 are not depending on anything */
 | 
			
		||||
      Project.setSource(project, "source1", "x=1")
 | 
			
		||||
      Project.setSource(project, "source2", "y=2")
 | 
			
		||||
 | 
			
		||||
      Project.setSource(project, "source3", "z=3")
 | 
			
		||||
      /* To run, source3 depends on source1 and source3 together */
 | 
			
		||||
      Project.setContinues(project, "source3", ["source1", "source2"])
 | 
			
		||||
 | 
			
		||||
      /* Now we can run the project */
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
 | 
			
		||||
      /* And let's check the result and bindings of source3 */
 | 
			
		||||
      let result3 = Project.getResult(project, "source3")
 | 
			
		||||
      let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
 | 
			
		||||
 | 
			
		||||
      (
 | 
			
		||||
        result3->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings3->InternalExpressionValue.toStringBindings,
 | 
			
		||||
      )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    test("Intro to including", () => {
 | 
			
		||||
      /* Though it would not be practical for a storybook, 
 | 
			
		||||
        let's write the same project above with includes.
 | 
			
		||||
        You will see that parsing includes is setting the dependencies the same way as before. */
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
 | 
			
		||||
      /* This time source1 and source2 are not depending on anything */
 | 
			
		||||
      Project.setSource(project, "source1", "x=1")
 | 
			
		||||
      Project.setSource(project, "source2", "y=2")
 | 
			
		||||
 | 
			
		||||
      Project.setSource(
 | 
			
		||||
        project,
 | 
			
		||||
        "source3",
 | 
			
		||||
        `
 | 
			
		||||
      #include "source1"
 | 
			
		||||
      #include "source2"
 | 
			
		||||
      z=3`,
 | 
			
		||||
      )
 | 
			
		||||
      /* We need to parse the includes to set the dependencies */
 | 
			
		||||
      Project.parseIncludes(project, "source3")
 | 
			
		||||
 | 
			
		||||
      /* Now we can run the project */
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
 | 
			
		||||
      /* And let's check the result and bindings of source3 
 | 
			
		||||
      This time you are getting all the variables because we are including the other sources 
 | 
			
		||||
      Behind the scenes parseIncludes is setting the dependencies */
 | 
			
		||||
      let result3 = Project.getResult(project, "source3")
 | 
			
		||||
      let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
 | 
			
		||||
 | 
			
		||||
      (
 | 
			
		||||
        result3->InternalExpressionValue.toStringResult,
 | 
			
		||||
        bindings3->InternalExpressionValue.toStringBindings,
 | 
			
		||||
      )->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
 | 
			
		||||
      /*
 | 
			
		||||
      Doing it like this is too verbose for a storybook 
 | 
			
		||||
      But I hope you have seen the relation of setContinues and parseIncludes */
 | 
			
		||||
      /*
 | 
			
		||||
         Dealing with includes needs more. 
 | 
			
		||||
         - There are parse errors
 | 
			
		||||
         - There are cyclic includes
 | 
			
		||||
         - And the depended source1 and source2 is not already there in the project
 | 
			
		||||
         - If you knew the includes before hand there would not be point of the include directive.
 | 
			
		||||
         More on those on the next section. */
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,179 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("ReducerProject Tutorial", () => {
 | 
			
		||||
  /* Case: Includes
 | 
			
		||||
In the previous tutorial we have set the similarity between setContinues and parseIncludes.
 | 
			
		||||
Here we will finally proceed to a real life scenario. */
 | 
			
		||||
 | 
			
		||||
  describe("parseIncludes", () => {
 | 
			
		||||
    /* Here we investigate the details about parseIncludes, before setting up a real life scenario in the next section. */
 | 
			
		||||
    /* Everything happens inside a project, so let's have a project */
 | 
			
		||||
    let project = Project.createProject()
 | 
			
		||||
    Project.setSource(
 | 
			
		||||
      project,
 | 
			
		||||
      "main",
 | 
			
		||||
      `
 | 
			
		||||
    #include "common"
 | 
			
		||||
    x=1
 | 
			
		||||
    `,
 | 
			
		||||
    )
 | 
			
		||||
    /* We need to parse includes after changing the source */
 | 
			
		||||
    Project.parseIncludes(project, "main")
 | 
			
		||||
    test("getDependencies", () => {
 | 
			
		||||
      /* Parse includes has set the dependencies */
 | 
			
		||||
      Project.getDependencies(project, "main")->expect == ["common"]
 | 
			
		||||
      /* If there were no includes than there would be no dependencies */
 | 
			
		||||
      /* However if there was a syntax error at includes then would be no dependencies also */
 | 
			
		||||
      /* Therefore looking at dependencies is not the right way to load includes */
 | 
			
		||||
      /* getDependencies does not distinguish between setContinues or parseIncludes */
 | 
			
		||||
    })
 | 
			
		||||
    test("getIncludes", () => {
 | 
			
		||||
      /* Parse includes has set the includes */
 | 
			
		||||
      switch Project.getIncludes(project, "main") {
 | 
			
		||||
      | Ok(includes) => includes->expect == ["common"]
 | 
			
		||||
      | Error(err) => err->Reducer_ErrorValue.errorToString->fail
 | 
			
		||||
      }
 | 
			
		||||
      /* If the includes cannot be parsed then you get a syntax error.
 | 
			
		||||
      Otherwise you get the includes.
 | 
			
		||||
      If there is no syntax error then you can load that file and use setSource to add it to the project.
 | 
			
		||||
      And so on recursively... */
 | 
			
		||||
    })
 | 
			
		||||
    test("getDependents", () => {
 | 
			
		||||
      /* For any reason, you are able to query what other sources
 | 
			
		||||
        include or depend on the current source.
 | 
			
		||||
        But you don't need to use this to execute the projects.
 | 
			
		||||
        It is provided for completeness of information. */
 | 
			
		||||
      Project.getDependents(project, "main")->expect == []
 | 
			
		||||
      /* Nothing is depending on or including main */
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    describe("Real Like", () => {
 | 
			
		||||
      /* Now let's look at recursive and possibly cyclic includes */
 | 
			
		||||
      /* There is no function provided to load the include files.
 | 
			
		||||
    Because we have no idea if will it be an ordinary function or will it use promises.
 | 
			
		||||
    Therefore one has to write a function to load sources recursively and and setSources
 | 
			
		||||
    while checking for dependencies */
 | 
			
		||||
 | 
			
		||||
      /* Let's make a dummy loader */
 | 
			
		||||
      let loadSource = (sourceName: string) =>
 | 
			
		||||
        switch sourceName {
 | 
			
		||||
        | "source1" => "x=1"
 | 
			
		||||
        | "source2" => `
 | 
			
		||||
            #include "source1"
 | 
			
		||||
            y=2`
 | 
			
		||||
        | "source3" => `
 | 
			
		||||
            #include "source2"
 | 
			
		||||
            z=3`
 | 
			
		||||
        | _ => `source ${sourceName} not found`->Js.Exn.raiseError
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      /* let's recursively load the sources */
 | 
			
		||||
      let rec loadIncludesRecursively = (project, sourceName, visited) => {
 | 
			
		||||
        if Js.Array2.includes(visited, sourceName) {
 | 
			
		||||
          /* Oh we have already visited this source. There is an include cycle */
 | 
			
		||||
          "Cyclic include ${sourceName}"->Js.Exn.raiseError
 | 
			
		||||
        } else {
 | 
			
		||||
          let newVisited = Js.Array2.copy(visited)
 | 
			
		||||
          let _ = Js.Array2.push(newVisited, sourceName)
 | 
			
		||||
          /* Let's parse the includes and dive into them */
 | 
			
		||||
          Project.parseIncludes(project, sourceName)
 | 
			
		||||
          let rIncludes = Project.getIncludes(project, sourceName)
 | 
			
		||||
          switch rIncludes {
 | 
			
		||||
          /* Maybe there is an include syntax error */
 | 
			
		||||
          | Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
 | 
			
		||||
 | 
			
		||||
          | Ok(includes) =>
 | 
			
		||||
            Belt.Array.forEach(includes, newIncludeName => {
 | 
			
		||||
              /* We have got one of the new includes.
 | 
			
		||||
               Let's load it and add it to the project */
 | 
			
		||||
              let newSource = loadSource(newIncludeName)
 | 
			
		||||
              Project.setSource(project, newIncludeName, newSource)
 | 
			
		||||
              /* The new source is loaded and added to the project. */
 | 
			
		||||
              /* Of course the new source might have includes too. */
 | 
			
		||||
              /* Let's recursively load them */
 | 
			
		||||
              loadIncludesRecursively(project, newIncludeName, newVisited)
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      /* As we have a fake source loader and a recursive include handler,
 | 
			
		||||
       We can not set up a real project */
 | 
			
		||||
 | 
			
		||||
      /* * Here starts our real life project! * */
 | 
			
		||||
 | 
			
		||||
      let project = Project.createProject()
 | 
			
		||||
 | 
			
		||||
      /* main includes source3 which includes source2 which includes source1 */
 | 
			
		||||
      Project.setSource(
 | 
			
		||||
        project,
 | 
			
		||||
        "main",
 | 
			
		||||
        `
 | 
			
		||||
        #include "source3"
 | 
			
		||||
        x+y+z
 | 
			
		||||
        `,
 | 
			
		||||
      )
 | 
			
		||||
      /* Setting source requires parsing and loading the includes recursively */
 | 
			
		||||
      loadIncludesRecursively(project, "main", []) //No visited yet
 | 
			
		||||
 | 
			
		||||
      /* Let's salt it more. Let's have another source in the project which also has includes */
 | 
			
		||||
      /* doubleX includes source1 which is eventually included by main as well */
 | 
			
		||||
      Project.setSource(
 | 
			
		||||
        project,
 | 
			
		||||
        "doubleX",
 | 
			
		||||
        `
 | 
			
		||||
        #include "source1"
 | 
			
		||||
        doubleX = x * 2
 | 
			
		||||
        `,
 | 
			
		||||
      )
 | 
			
		||||
      loadIncludesRecursively(project, "doubleX", [])
 | 
			
		||||
      /* Remember, any time you set a source, you need to load includes recursively */
 | 
			
		||||
 | 
			
		||||
      /* As doubleX is not included by main, it is not loaded recursively.
 | 
			
		||||
       So we link it to the project as a dependency */
 | 
			
		||||
      Project.setContinues(project, "main", ["doubleX"])
 | 
			
		||||
 | 
			
		||||
      /* Let's run the project */
 | 
			
		||||
      Project.runAll(project)
 | 
			
		||||
      let result = Project.getResult(project, "main")
 | 
			
		||||
      let bindings = Project.getBindings(project, "main")
 | 
			
		||||
      /* And see the result and bindings.. */
 | 
			
		||||
      test("recursive includes", () => {
 | 
			
		||||
        (
 | 
			
		||||
          result->InternalExpressionValue.toStringResult,
 | 
			
		||||
          bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
 | 
			
		||||
        )->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}")
 | 
			
		||||
        /* Everything as expected */
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe("Includes myFile as myVariable", () => {
 | 
			
		||||
    /* Instead of including into global space you can also put a module into a record variable */
 | 
			
		||||
    let project = Project.createProject()
 | 
			
		||||
    Project.setSource(
 | 
			
		||||
      project,
 | 
			
		||||
      "main",
 | 
			
		||||
      `
 | 
			
		||||
    #include "common" as common
 | 
			
		||||
    x=1
 | 
			
		||||
    `,
 | 
			
		||||
    )
 | 
			
		||||
    Project.parseIncludes(project, "main")
 | 
			
		||||
    test("getDependencies", () => {
 | 
			
		||||
      Project.getDependencies(project, "main")->expect == ["common"]
 | 
			
		||||
    })
 | 
			
		||||
    test("getIncludes", () => {
 | 
			
		||||
      switch Project.getIncludes(project, "main") {
 | 
			
		||||
      | Ok(includes) => includes->expect == ["common"]
 | 
			
		||||
      | Error(err) => err->Reducer_ErrorValue.errorToString->fail
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("ReducerProject Tutorial", () => {
 | 
			
		||||
  /* Let's build a project that depends on values from the UI */
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "main", "x+y+z")
 | 
			
		||||
  /* x, y and z is not defined in the project but they has to come from the user */
 | 
			
		||||
  test("Injecting user values", () => {
 | 
			
		||||
    /* User has input the values */
 | 
			
		||||
    let x = 1
 | 
			
		||||
    let y = 2
 | 
			
		||||
    let z = 3
 | 
			
		||||
    /* Then we construct a source code to define those values */
 | 
			
		||||
    let userCode = `
 | 
			
		||||
      x = ${x->Js.Int.toString}
 | 
			
		||||
      y = ${y->Js.Int.toString}
 | 
			
		||||
      z = ${z->Js.Int.toString}
 | 
			
		||||
    `
 | 
			
		||||
    /* We inject the user code into the project */
 | 
			
		||||
    Project.setSource(project, "userCode", userCode)
 | 
			
		||||
    /* "main" is depending on the user code */
 | 
			
		||||
    Project.setContinues(project, "main", ["userCode"])
 | 
			
		||||
    /* We can now run the project */
 | 
			
		||||
    Project.runAll(project)
 | 
			
		||||
    let result = Project.getResult(project, "main")
 | 
			
		||||
    result->InternalExpressionValue.toStringResult->expect == "Ok(6)"
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/* Note that this is not final version of the project */
 | 
			
		||||
/* In the future, for safety, we will provide a way to inject values instead of a source code */
 | 
			
		||||
/* But time is limited for now... */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
@@warning("-44")
 | 
			
		||||
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
 | 
			
		||||
module Project = ForTS_ReducerProject
 | 
			
		||||
module Bindings = Reducer_Bindings
 | 
			
		||||
 | 
			
		||||
open Jest
 | 
			
		||||
open Expect
 | 
			
		||||
open Expect.Operators
 | 
			
		||||
 | 
			
		||||
describe("ReducerProject Tutorial", () => {
 | 
			
		||||
  /* Let's build a project to provide a function. */
 | 
			
		||||
  /* But we will call that function on an array of user input. */
 | 
			
		||||
  let project = Project.createProject()
 | 
			
		||||
  Project.setSource(project, "library", "double(x) = x * 2")
 | 
			
		||||
  /* userCode is not here yet but its dependency is fixed. So we can set it once and for all */
 | 
			
		||||
  Project.setContinues(project, "userCode", ["library"])
 | 
			
		||||
 | 
			
		||||
  let userValues = [1, 2, 3, 4, 5]
 | 
			
		||||
 | 
			
		||||
  let userResults = Belt.Array.map(userValues, aUserValue => {
 | 
			
		||||
    let userCode = `double(${aUserValue->Js.Int.toString})`
 | 
			
		||||
    /* Put the constructed source in the project */
 | 
			
		||||
    /* We have already set that it depends on "library" */
 | 
			
		||||
    Project.setSource(project, "userCode", userCode)
 | 
			
		||||
    /* Run the project */
 | 
			
		||||
    Project.runAll(project)
 | 
			
		||||
    /* Get the result */
 | 
			
		||||
    Project.getResult(project, "userCode")
 | 
			
		||||
    /* I have to remind you that the "library" is run only once and for all.
 | 
			
		||||
     The library is not run for each user value. */
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test("userResults", () => {
 | 
			
		||||
    let userResultsAsString = Belt.Array.map(userResults, aResult =>
 | 
			
		||||
      aResult->InternalExpressionValue.toStringResult
 | 
			
		||||
    )
 | 
			
		||||
    userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"]
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -2,8 +2,12 @@ open Jest
 | 
			
		|||
open Expect
 | 
			
		||||
open Reducer_TestHelpers
 | 
			
		||||
 | 
			
		||||
let expectEvalToBeOk = (expr: string) =>
 | 
			
		||||
  Reducer.evaluate(expr)->Reducer_Helpers.rRemoveDefaultsExternal->E.R.isOk->expect->toBe(true)
 | 
			
		||||
let expectEvalToBeOk = (code: string) =>
 | 
			
		||||
  Reducer_Expression.BackCompatible.evaluateString(code)
 | 
			
		||||
  ->Reducer_Helpers.rRemoveDefaultsInternal
 | 
			
		||||
  ->E.R.isOk
 | 
			
		||||
  ->expect
 | 
			
		||||
  ->toBe(true)
 | 
			
		||||
 | 
			
		||||
let registry = FunctionRegistry_Library.registry
 | 
			
		||||
let examples = E.A.to_list(FunctionRegistry_Core.Registry.allExamples(registry))
 | 
			
		||||
| 
						 | 
				
			
			@ -97,8 +101,8 @@ describe("FunctionRegistry Library", () => {
 | 
			
		|||
      ((fn, example)) => {
 | 
			
		||||
        let responseType =
 | 
			
		||||
          example
 | 
			
		||||
          ->Reducer.evaluate
 | 
			
		||||
          ->E.R2.fmap(ReducerInterface_InternalExpressionValue.externalValueToValueType)
 | 
			
		||||
          ->Reducer_Expression.BackCompatible.evaluateString
 | 
			
		||||
          ->E.R2.fmap(ReducerInterface_InternalExpressionValue.valueToValueType)
 | 
			
		||||
        let expectedOutputType = fn.output |> E.O.toExn("")
 | 
			
		||||
        expect(responseType)->toEqual(Ok(expectedOutputType))
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,5 @@
 | 
			
		|||
import {
 | 
			
		||||
  Distribution,
 | 
			
		||||
  resultMap,
 | 
			
		||||
  defaultBindings,
 | 
			
		||||
  mergeBindings,
 | 
			
		||||
} from "../../src/js/index";
 | 
			
		||||
import { testRun, testRunPartial } from "./TestHelpers";
 | 
			
		||||
import { run, SqProject, SqValue, SqValueTag } from "../../src/js";
 | 
			
		||||
import { testRun } from "./TestHelpers";
 | 
			
		||||
 | 
			
		||||
function Ok<b>(x: b) {
 | 
			
		||||
  return { tag: "Ok", value: x };
 | 
			
		||||
| 
						 | 
				
			
			@ -12,97 +7,57 @@ function Ok<b>(x: b) {
 | 
			
		|||
 | 
			
		||||
describe("Simple calculations and results", () => {
 | 
			
		||||
  test("mean(normal(5,2))", () => {
 | 
			
		||||
    expect(testRun("mean(normal(5,2))")).toEqual({
 | 
			
		||||
      tag: "number",
 | 
			
		||||
      value: 5,
 | 
			
		||||
    });
 | 
			
		||||
    const result = testRun("mean(normal(5,2))"); // FIXME
 | 
			
		||||
    expect(result.toString()).toEqual("5");
 | 
			
		||||
  });
 | 
			
		||||
  test("10+10", () => {
 | 
			
		||||
    let foo = testRun("10 + 10");
 | 
			
		||||
    expect(foo).toEqual({ tag: "number", value: 20 });
 | 
			
		||||
    let result = testRun("10 + 10");
 | 
			
		||||
    expect(result.toString()).toEqual("20");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
describe("Log function", () => {
 | 
			
		||||
  test("log(1) = 0", () => {
 | 
			
		||||
    let foo = testRun("log(1)");
 | 
			
		||||
    expect(foo).toEqual({ tag: "number", value: 0 });
 | 
			
		||||
    expect(foo.toString()).toEqual("0");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("Array", () => {
 | 
			
		||||
  test("nested Array", () => {
 | 
			
		||||
    expect(testRun("[[1]]")).toEqual({
 | 
			
		||||
      tag: "array",
 | 
			
		||||
      value: [
 | 
			
		||||
        {
 | 
			
		||||
          tag: "array",
 | 
			
		||||
          value: [
 | 
			
		||||
            {
 | 
			
		||||
              tag: "number",
 | 
			
		||||
              value: 1,
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
    expect(testRun("[[ 1 ]]").toString()).toEqual("[[1]]");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("Record", () => {
 | 
			
		||||
  test("Return record", () => {
 | 
			
		||||
    expect(testRun("{a: 1}")).toEqual({
 | 
			
		||||
      tag: "record",
 | 
			
		||||
      value: {
 | 
			
		||||
        a: {
 | 
			
		||||
          tag: "number",
 | 
			
		||||
          value: 1,
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
    expect(testRun("{a:1}").toString()).toEqual("{a: 1}");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("Partials", () => {
 | 
			
		||||
  test("Can pass variables between partials and cells", () => {
 | 
			
		||||
    let bindings = testRunPartial(`x = 5`);
 | 
			
		||||
    let bindings2 = testRunPartial(`y = x + 2`, bindings);
 | 
			
		||||
    expect(testRun(`y + 3`, bindings2)).toEqual({
 | 
			
		||||
      tag: "number",
 | 
			
		||||
      value: 10,
 | 
			
		||||
    });
 | 
			
		||||
describe("Continues", () => {
 | 
			
		||||
  test("Bindings from continues are accessible", () => {
 | 
			
		||||
    const project = SqProject.create();
 | 
			
		||||
    project.setSource("p1", "x = 5");
 | 
			
		||||
    project.setSource("p2", "y = x + 2");
 | 
			
		||||
    project.setSource("main", "y + 3");
 | 
			
		||||
    project.setContinues("main", ["p2"]);
 | 
			
		||||
    project.setContinues("p2", ["p1"]);
 | 
			
		||||
    project.run("main");
 | 
			
		||||
    const result = project.getResult("main");
 | 
			
		||||
    expect(result.tag).toEqual("Ok");
 | 
			
		||||
    expect(result.value.toString()).toEqual("10");
 | 
			
		||||
  });
 | 
			
		||||
  test("Can merge bindings from three partials", () => {
 | 
			
		||||
    let bindings1 = testRunPartial(`x = 1`);
 | 
			
		||||
    let bindings2 = testRunPartial(`y = 2`);
 | 
			
		||||
    let bindings3 = testRunPartial(`z = 3`);
 | 
			
		||||
    expect(
 | 
			
		||||
      testRun(`x + y + z`, mergeBindings([bindings1, bindings2, bindings3]))
 | 
			
		||||
    ).toEqual({
 | 
			
		||||
      tag: "number",
 | 
			
		||||
      value: 6,
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
describe("JS Imports", () => {
 | 
			
		||||
  test("Can pass parameters into partials and cells", () => {
 | 
			
		||||
    let bindings = testRunPartial(`y = $x + 2`, defaultBindings, { x: 1 });
 | 
			
		||||
    let bindings2 = testRunPartial(`z = y + $a`, bindings, { a: 3 });
 | 
			
		||||
    expect(testRun(`z`, bindings2)).toEqual({
 | 
			
		||||
      tag: "number",
 | 
			
		||||
      value: 6,
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  test("Complicated deep parameters", () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      testRun(`$x.y[0][0].w + $x.z + $u.v`, defaultBindings, {
 | 
			
		||||
        x: { y: [[{ w: 1 }]], z: 2 },
 | 
			
		||||
        u: { v: 3 },
 | 
			
		||||
      })
 | 
			
		||||
    ).toEqual({
 | 
			
		||||
      tag: "number",
 | 
			
		||||
      value: 6,
 | 
			
		||||
    });
 | 
			
		||||
    const project = SqProject.create();
 | 
			
		||||
    project.setSource("p1", "x = 1");
 | 
			
		||||
    project.setSource("p2", "y = 2");
 | 
			
		||||
    project.setSource("p3", "z = 3");
 | 
			
		||||
    project.setSource("main", "x + y + z");
 | 
			
		||||
    project.setContinues("main", ["p1", "p2", "p3"]);
 | 
			
		||||
    project.run("main");
 | 
			
		||||
    const result = project.getResult("main");
 | 
			
		||||
    expect(result.tag).toEqual("Ok");
 | 
			
		||||
    expect(result.value.toString()).toEqual("6");
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -112,50 +67,62 @@ describe("Distribution", () => {
 | 
			
		|||
  let env = { sampleCount: 8, xyPointLength: 100 };
 | 
			
		||||
  let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30];
 | 
			
		||||
  let dist1SampleCount = dist1Samples.length;
 | 
			
		||||
  let dist = new Distribution(
 | 
			
		||||
    { tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] },
 | 
			
		||||
    env
 | 
			
		||||
  );
 | 
			
		||||
  let dist2 = new Distribution(
 | 
			
		||||
    { tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] },
 | 
			
		||||
    env
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const buildDist = (samples: number[]) => {
 | 
			
		||||
    const src = `SampleSet.fromList([${samples.join(",")}])`;
 | 
			
		||||
    const { result } = run(src, {
 | 
			
		||||
      environment: env,
 | 
			
		||||
    });
 | 
			
		||||
    if (result.tag !== "Ok") {
 | 
			
		||||
      throw new Error(
 | 
			
		||||
        `Failed to build SampleSet: from ${src}: ${result.value}`
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    const dist = result.value;
 | 
			
		||||
    if (dist.tag !== SqValueTag.Distribution) {
 | 
			
		||||
      throw new Error("Expected Distribution");
 | 
			
		||||
    }
 | 
			
		||||
    return dist.value;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const dist = buildDist(dist1Samples);
 | 
			
		||||
  const dist2 = buildDist([20, 22, 24, 29, 30, 35, 38, 44, 52]);
 | 
			
		||||
 | 
			
		||||
  test("mean", () => {
 | 
			
		||||
    expect(dist.mean().value).toBeCloseTo(9.5555555);
 | 
			
		||||
    expect(dist.mean(env).value).toBeCloseTo(9.5555555);
 | 
			
		||||
  });
 | 
			
		||||
  test("pdf", () => {
 | 
			
		||||
    expect(dist.pdf(5.0).value).toBeCloseTo(0.10499097598222966, 1);
 | 
			
		||||
    expect(dist.pdf(env, 5.0).value).toBeCloseTo(0.10499097598222966, 1);
 | 
			
		||||
  });
 | 
			
		||||
  test("cdf", () => {
 | 
			
		||||
    expect(dist.cdf(5.0).value).toBeCloseTo(
 | 
			
		||||
    expect(dist.cdf(env, 5.0).value).toBeCloseTo(
 | 
			
		||||
      dist1Samples.filter((x) => x <= 5).length / dist1SampleCount,
 | 
			
		||||
      1
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
  test("inv", () => {
 | 
			
		||||
    expect(dist.inv(0.5).value).toBeCloseTo(6);
 | 
			
		||||
  });
 | 
			
		||||
  test("toPointSet", () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      resultMap(dist.toPointSet(), (r: Distribution) => r.toString())
 | 
			
		||||
    ).toEqual(Ok("Point Set Distribution"));
 | 
			
		||||
  });
 | 
			
		||||
  test("toSparkline", () => {
 | 
			
		||||
    expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁");
 | 
			
		||||
  });
 | 
			
		||||
  test("algebraicAdd", () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
 | 
			
		||||
        r.toSparkline(20)
 | 
			
		||||
      ).value
 | 
			
		||||
    ).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
 | 
			
		||||
  });
 | 
			
		||||
  test("pointwiseAdd", () => {
 | 
			
		||||
    expect(
 | 
			
		||||
      resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
 | 
			
		||||
        r.toSparkline(20)
 | 
			
		||||
      ).value
 | 
			
		||||
    ).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁"));
 | 
			
		||||
    expect(dist.inv(env, 0.5).value).toBeCloseTo(6);
 | 
			
		||||
  });
 | 
			
		||||
  // test("toPointSet", () => {
 | 
			
		||||
  //   expect(
 | 
			
		||||
  //     resultMap(dist.toPointSet(), (r: Distribution) => r.toString())
 | 
			
		||||
  //   ).toEqual(Ok("Point Set Distribution"));
 | 
			
		||||
  // });
 | 
			
		||||
  // test("toSparkline", () => {
 | 
			
		||||
  //   expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁");
 | 
			
		||||
  // });
 | 
			
		||||
  // test("algebraicAdd", () => {
 | 
			
		||||
  //   expect(
 | 
			
		||||
  //     resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
 | 
			
		||||
  //       r.toSparkline(20)
 | 
			
		||||
  //     ).value
 | 
			
		||||
  //   ).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
 | 
			
		||||
  // });
 | 
			
		||||
  // test("pointwiseAdd", () => {
 | 
			
		||||
  //   expect(
 | 
			
		||||
  //     resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
 | 
			
		||||
  //       r.toSparkline(20)
 | 
			
		||||
  //     ).value
 | 
			
		||||
  //   ).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁"));
 | 
			
		||||
  // });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
// import { errorValueToString } from "../../src/js/index";
 | 
			
		||||
import * as fc from "fast-check";
 | 
			
		||||
import { testRun } from "./TestHelpers";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,3 @@
 | 
			
		|||
import {
 | 
			
		||||
  run,
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  errorValue,
 | 
			
		||||
  result,
 | 
			
		||||
} from "../../src/js/index";
 | 
			
		||||
import { testRun } from "./TestHelpers";
 | 
			
		||||
import * as fc from "fast-check";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,9 +36,9 @@ describe("Squiggle's parser is whitespace insensitive", () => {
 | 
			
		|||
        whitespaceGen(),
 | 
			
		||||
        whitespaceGen(),
 | 
			
		||||
        (a, b, c, d, e, f, g, h) => {
 | 
			
		||||
          expect(testRun(squiggleString(a, b, c, d, e, f, g, h))).toEqual(
 | 
			
		||||
            squiggleOutput
 | 
			
		||||
          );
 | 
			
		||||
          expect(
 | 
			
		||||
            testRun(squiggleString(a, b, c, d, e, f, g, h))
 | 
			
		||||
          ).toEqualSqValue(squiggleOutput);
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
// import { errorValueToString } from "../../src/js/index";
 | 
			
		||||
import { testRun, expectErrorToBeBounded } from "./TestHelpers";
 | 
			
		||||
import { testRun, expectErrorToBeBounded, SqValueTag } from "./TestHelpers";
 | 
			
		||||
import * as fc from "fast-check";
 | 
			
		||||
 | 
			
		||||
describe("Mean of mixture is weighted average of means", () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +19,7 @@ describe("Mean of mixture is weighted average of means", () => {
 | 
			
		|||
          let lognormalWeight = y / weightDenom;
 | 
			
		||||
          let betaMean = 1 / (1 + b / a);
 | 
			
		||||
          let lognormalMean = m + s ** 2 / 2;
 | 
			
		||||
          if (res.tag == "number") {
 | 
			
		||||
          if (res.tag === SqValueTag.Number) {
 | 
			
		||||
            expectErrorToBeBounded(
 | 
			
		||||
              res.value,
 | 
			
		||||
              betaWeight * betaMean + lognormalWeight * lognormalMean,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,13 @@
 | 
			
		|||
import { Distribution } from "../../src/js/index";
 | 
			
		||||
import { expectErrorToBeBounded, failDefault, testRun } from "./TestHelpers";
 | 
			
		||||
import { expectErrorToBeBounded, testRun, SqValueTag } from "./TestHelpers";
 | 
			
		||||
import * as fc from "fast-check";
 | 
			
		||||
 | 
			
		||||
// Beware: float64Array makes it appear in an infinite loop.
 | 
			
		||||
let arrayGen = () =>
 | 
			
		||||
  fc
 | 
			
		||||
    .float32Array({
 | 
			
		||||
    .float64Array({
 | 
			
		||||
      minLength: 10,
 | 
			
		||||
      max: 999999999999999,
 | 
			
		||||
      min: -999999999999999,
 | 
			
		||||
      maxLength: 10000,
 | 
			
		||||
      noDefaultInfinity: true,
 | 
			
		||||
      noNaN: true,
 | 
			
		||||
| 
						 | 
				
			
			@ -14,36 +15,41 @@ let arrayGen = () =>
 | 
			
		|||
    .filter(
 | 
			
		||||
      (xs_) => Math.min(...Array.from(xs_)) != Math.max(...Array.from(xs_))
 | 
			
		||||
    );
 | 
			
		||||
describe("cumulative density function", () => {
 | 
			
		||||
  let n = 10000;
 | 
			
		||||
 | 
			
		||||
let makeSampleSet = (samples: number[]) => {
 | 
			
		||||
  let sampleList = samples.map((x) => x.toFixed(20)).join(",");
 | 
			
		||||
  let result = testRun(`SampleSet.fromList([${sampleList}])`);
 | 
			
		||||
  if (result.tag === SqValueTag.Distribution) {
 | 
			
		||||
    return result.value;
 | 
			
		||||
  } else {
 | 
			
		||||
    fail("Expected to be distribution");
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const env = { sampleCount: 10000, xyPointLength: 100 };
 | 
			
		||||
 | 
			
		||||
describe("cumulative density function", () => {
 | 
			
		||||
  // We should fix this.
 | 
			
		||||
  test.skip("'s codomain is bounded above", () => {
 | 
			
		||||
    fc.assert(
 | 
			
		||||
      fc.property(arrayGen(), fc.float(), (xs_, x) => {
 | 
			
		||||
        let xs = Array.from(xs_);
 | 
			
		||||
        // Should compute with squiggle strings once interpreter has `sample`
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(x).value;
 | 
			
		||||
        let result = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = result.cdf(env, x).value;
 | 
			
		||||
        let epsilon = 5e-7;
 | 
			
		||||
        expect(cdfValue).toBeLessThanOrEqual(1 + epsilon);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test("'s codomain is bounded below", () => {
 | 
			
		||||
  test.skip("'s codomain is bounded below", () => {
 | 
			
		||||
    fc.assert(
 | 
			
		||||
      fc.property(arrayGen(), fc.float(), (xs_, x) => {
 | 
			
		||||
        let xs = Array.from(xs_);
 | 
			
		||||
        // Should compute with squiggle strings once interpreter has `sample`
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(x).value;
 | 
			
		||||
        let result = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = result.cdf(env, x).value;
 | 
			
		||||
        expect(cdfValue).toBeGreaterThanOrEqual(0);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +63,8 @@ describe("cumulative density function", () => {
 | 
			
		|||
        let xs = Array.from(xs_);
 | 
			
		||||
        let max = Math.max(...xs);
 | 
			
		||||
        // Should compute with squiggle strings once interpreter has `sample`
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(max).value;
 | 
			
		||||
        let result = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = result.cdf(env, max).value;
 | 
			
		||||
        expect(cdfValue).toBeCloseTo(1.0, 2);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -74,11 +77,8 @@ describe("cumulative density function", () => {
 | 
			
		|||
        let xs = Array.from(xs_);
 | 
			
		||||
        let min = Math.min(...xs);
 | 
			
		||||
        // Should compute with squiggle strings once interpreter has `sample`
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(min).value;
 | 
			
		||||
        let result = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = result.cdf(env, min).value;
 | 
			
		||||
        let max = Math.max(...xs);
 | 
			
		||||
        let epsilon = 5e-3;
 | 
			
		||||
        if (max - min < epsilon) {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,11 +95,8 @@ describe("cumulative density function", () => {
 | 
			
		|||
    fc.assert(
 | 
			
		||||
      fc.property(arrayGen(), fc.float(), (xs_, x) => {
 | 
			
		||||
        let xs = Array.from(xs_);
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(x).value;
 | 
			
		||||
        let dist = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = dist.cdf(env, x).value;
 | 
			
		||||
        let max = Math.max(...xs);
 | 
			
		||||
        if (x > max) {
 | 
			
		||||
          let epsilon = (x - max) / x;
 | 
			
		||||
| 
						 | 
				
			
			@ -107,21 +104,18 @@ describe("cumulative density function", () => {
 | 
			
		|||
        } else if (typeof cdfValue == "number") {
 | 
			
		||||
          expect(Math.round(1e5 * cdfValue) / 1e5).toBeLessThanOrEqual(1);
 | 
			
		||||
        } else {
 | 
			
		||||
          failDefault();
 | 
			
		||||
          fail();
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test("is non-negative everywhere with zero when x is lower than the min", () => {
 | 
			
		||||
  test.skip("is non-negative everywhere with zero when x is lower than the min", () => {
 | 
			
		||||
    fc.assert(
 | 
			
		||||
      fc.property(arrayGen(), fc.float(), (xs_, x) => {
 | 
			
		||||
        let xs = Array.from(xs_);
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let cdfValue = dist.cdf(x).value;
 | 
			
		||||
        let dist = makeSampleSet(xs);
 | 
			
		||||
        let cdfValue = dist.cdf(env, x).value;
 | 
			
		||||
        expect(cdfValue).toBeGreaterThanOrEqual(0);
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +124,7 @@ describe("cumulative density function", () => {
 | 
			
		|||
 | 
			
		||||
// I no longer believe this is true.
 | 
			
		||||
describe("probability density function", () => {
 | 
			
		||||
  let n = 1000;
 | 
			
		||||
  const env = { sampleCount: 1000, xyPointLength: 100 };
 | 
			
		||||
 | 
			
		||||
  test.skip("assigns to the max at most the weight of the mean", () => {
 | 
			
		||||
    fc.assert(
 | 
			
		||||
| 
						 | 
				
			
			@ -139,12 +133,9 @@ describe("probability density function", () => {
 | 
			
		|||
        let max = Math.max(...xs);
 | 
			
		||||
        let mean = xs.reduce((a, b) => a + b, 0.0) / xs.length;
 | 
			
		||||
        // Should be from squiggleString once interpreter exposes sampleset
 | 
			
		||||
        let dist = new Distribution(
 | 
			
		||||
          { tag: "SampleSet", value: xs },
 | 
			
		||||
          { sampleCount: n, xyPointLength: 100 }
 | 
			
		||||
        );
 | 
			
		||||
        let pdfValueMean = dist.pdf(mean).value;
 | 
			
		||||
        let pdfValueMax = dist.pdf(max).value;
 | 
			
		||||
        let dist = makeSampleSet(xs);
 | 
			
		||||
        let pdfValueMean = dist.pdf(env, mean).value;
 | 
			
		||||
        let pdfValueMax = dist.pdf(env, max).value;
 | 
			
		||||
        if (typeof pdfValueMean == "number" && typeof pdfValueMax == "number") {
 | 
			
		||||
          expect(pdfValueMax).toBeLessThanOrEqual(pdfValueMean);
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -164,11 +155,9 @@ describe("mean is mean", () => {
 | 
			
		|||
        (xs_) => {
 | 
			
		||||
          let xs = Array.from(xs_);
 | 
			
		||||
          let n = xs.length;
 | 
			
		||||
          let dist = new Distribution(
 | 
			
		||||
            { tag: "SampleSet", value: xs },
 | 
			
		||||
            { sampleCount: 2 * n, xyPointLength: 4 * n }
 | 
			
		||||
          );
 | 
			
		||||
          let mean = dist.mean();
 | 
			
		||||
          let dist = makeSampleSet(xs);
 | 
			
		||||
          let myEnv = { sampleCount: 2 * n, xyPointLength: 4 * n };
 | 
			
		||||
          let mean = dist.mean(myEnv);
 | 
			
		||||
          if (typeof mean.value == "number") {
 | 
			
		||||
            expectErrorToBeBounded(
 | 
			
		||||
              mean.value,
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +166,7 @@ describe("mean is mean", () => {
 | 
			
		|||
              1
 | 
			
		||||
            );
 | 
			
		||||
          } else {
 | 
			
		||||
            failDefault();
 | 
			
		||||
            fail();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			@ -191,11 +180,9 @@ describe("mean is mean", () => {
 | 
			
		|||
        (xs_) => {
 | 
			
		||||
          let xs = Array.from(xs_);
 | 
			
		||||
          let n = xs.length;
 | 
			
		||||
          let dist = new Distribution(
 | 
			
		||||
            { tag: "SampleSet", value: xs },
 | 
			
		||||
            { sampleCount: Math.floor(n / 2), xyPointLength: 4 * n }
 | 
			
		||||
          );
 | 
			
		||||
          let mean = dist.mean();
 | 
			
		||||
          let dist = makeSampleSet(xs);
 | 
			
		||||
          let myEnv = { sampleCount: Math.floor(n / 2), xyPointLength: 4 * n };
 | 
			
		||||
          let mean = dist.mean(myEnv);
 | 
			
		||||
          if (typeof mean.value == "number") {
 | 
			
		||||
            expectErrorToBeBounded(
 | 
			
		||||
              mean.value,
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +191,7 @@ describe("mean is mean", () => {
 | 
			
		|||
              1
 | 
			
		||||
            );
 | 
			
		||||
          } else {
 | 
			
		||||
            failDefault();
 | 
			
		||||
            fail();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import * as fc from "fast-check";
 | 
			
		|||
describe("Scalar manipulation is well-modeled by javascript math", () => {
 | 
			
		||||
  test("in the case of natural logarithms", () => {
 | 
			
		||||
    fc.assert(
 | 
			
		||||
      fc.property(fc.integer(), (x) => {
 | 
			
		||||
      fc.property(fc.nat(), (x) => {
 | 
			
		||||
        let squiggleString = `log(${x})`;
 | 
			
		||||
        let squiggleResult = testRun(squiggleString);
 | 
			
		||||
        if (x == 0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,68 +1,31 @@
 | 
			
		|||
import {
 | 
			
		||||
  run,
 | 
			
		||||
  runPartial,
 | 
			
		||||
  bindings,
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  errorValueToString,
 | 
			
		||||
  defaultImports,
 | 
			
		||||
  defaultBindings,
 | 
			
		||||
  jsImports,
 | 
			
		||||
} from "../../src/js/index";
 | 
			
		||||
import { run, SqValueTag } from "../../src/js";
 | 
			
		||||
export { SqValueTag };
 | 
			
		||||
 | 
			
		||||
export function testRun(
 | 
			
		||||
  x: string,
 | 
			
		||||
  bindings: bindings = defaultBindings,
 | 
			
		||||
  imports: jsImports = defaultImports
 | 
			
		||||
): squiggleExpression {
 | 
			
		||||
  let squiggleResult = run(
 | 
			
		||||
    x,
 | 
			
		||||
    bindings,
 | 
			
		||||
    {
 | 
			
		||||
expect.extend({
 | 
			
		||||
  toEqualSqValue(x, y) {
 | 
			
		||||
    // hack via https://github.com/facebook/jest/issues/10329#issuecomment-820656061
 | 
			
		||||
    const { getMatchers } = require("expect/build/jestMatchersObject");
 | 
			
		||||
    return getMatchers().toEqual.call(this, x.toString(), y.toString());
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export function testRun(x: string) {
 | 
			
		||||
  const { result, bindings } = run(x, {
 | 
			
		||||
    environment: {
 | 
			
		||||
      sampleCount: 1000,
 | 
			
		||||
      xyPointLength: 100,
 | 
			
		||||
    },
 | 
			
		||||
    imports
 | 
			
		||||
  );
 | 
			
		||||
  if (squiggleResult.tag === "Ok") {
 | 
			
		||||
    return squiggleResult.value;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (result.tag === "Ok") {
 | 
			
		||||
    return result.value;
 | 
			
		||||
  } else {
 | 
			
		||||
    throw new Error(
 | 
			
		||||
      `Expected squiggle expression to evaluate but got error: ${errorValueToString(
 | 
			
		||||
        squiggleResult.value
 | 
			
		||||
      )}`
 | 
			
		||||
      `Expected squiggle expression to evaluate but got error: ${result.value}`
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function testRunPartial(
 | 
			
		||||
  x: string,
 | 
			
		||||
  bindings: bindings = defaultBindings,
 | 
			
		||||
  imports: jsImports = defaultImports
 | 
			
		||||
): bindings {
 | 
			
		||||
  let squiggleResult = runPartial(
 | 
			
		||||
    x,
 | 
			
		||||
    bindings,
 | 
			
		||||
    {
 | 
			
		||||
      sampleCount: 1000,
 | 
			
		||||
      xyPointLength: 100,
 | 
			
		||||
    },
 | 
			
		||||
    imports
 | 
			
		||||
  );
 | 
			
		||||
  if (squiggleResult.tag === "Ok") {
 | 
			
		||||
    return squiggleResult.value;
 | 
			
		||||
  } else {
 | 
			
		||||
    throw new Error(
 | 
			
		||||
      `Expected squiggle expression to evaluate but got error: ${errorValueToString(
 | 
			
		||||
        squiggleResult.value
 | 
			
		||||
      )}`
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function failDefault() {
 | 
			
		||||
  expect("be reached").toBe("codepath should never");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This appears also in `TestHelpers.res`. According to https://www.math.net/percent-error, it computes
 | 
			
		||||
 * absolute error when numerical stability concerns make me not want to compute relative error.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@quri/squiggle-lang",
 | 
			
		||||
  "version": "0.3.1",
 | 
			
		||||
  "version": "0.4.0-alpha.0",
 | 
			
		||||
  "homepage": "https://squiggle-language.com",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								packages/squiggle-lang/src/js/SqArray.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/squiggle-lang/src/js/SqArray.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
 | 
			
		||||
import { wrapValue } from "./SqValue";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
 | 
			
		||||
type T = RSArray.squiggleValue_Array;
 | 
			
		||||
 | 
			
		||||
export class SqArray {
 | 
			
		||||
  constructor(private _value: T, public location: SqValueLocation) {}
 | 
			
		||||
 | 
			
		||||
  getValues() {
 | 
			
		||||
    return RSArray.getValues(this._value).map((v, i) =>
 | 
			
		||||
      wrapValue(v, this.location.extend(i))
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								packages/squiggle-lang/src/js/SqDistribution.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								packages/squiggle-lang/src/js/SqDistribution.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
import * as RSDistribution from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
 | 
			
		||||
import { distributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_tag";
 | 
			
		||||
import { environment } from "../rescript/ForTS/ForTS__Types.gen";
 | 
			
		||||
import { SqDistributionError } from "./SqDistributionError";
 | 
			
		||||
import { wrapPointSetDist } from "./SqPointSetDist";
 | 
			
		||||
import { resultMap2 } from "./types";
 | 
			
		||||
 | 
			
		||||
type T = RSDistribution.distribution;
 | 
			
		||||
export { Tag as SqDistributionTag };
 | 
			
		||||
 | 
			
		||||
export const wrapDistribution = (value: T): SqDistribution => {
 | 
			
		||||
  const tag = RSDistribution.getTag(value);
 | 
			
		||||
 | 
			
		||||
  return new tagToClass[tag](value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
abstract class SqAbstractDistribution {
 | 
			
		||||
  abstract tag: Tag;
 | 
			
		||||
 | 
			
		||||
  constructor(private _value: T) {}
 | 
			
		||||
 | 
			
		||||
  protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
 | 
			
		||||
    const value = rsMethod(this._value);
 | 
			
		||||
    if (!value) throw new Error("Internal casting error");
 | 
			
		||||
    return value;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  pointSet(env: environment) {
 | 
			
		||||
    const innerResult = RSDistribution.toPointSet(this._value, env);
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      innerResult,
 | 
			
		||||
      wrapPointSetDist,
 | 
			
		||||
      (v: RSDistribution.distributionError) => new SqDistributionError(v)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toString() {
 | 
			
		||||
    RSDistribution.toString(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mean(env: environment) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSDistribution.mean({ env }, this._value),
 | 
			
		||||
      (v: number) => v,
 | 
			
		||||
      (e: RSDistribution.distributionError) => new SqDistributionError(e)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pdf(env: environment, n: number) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSDistribution.pdf({ env }, this._value, n),
 | 
			
		||||
      (v: number) => v,
 | 
			
		||||
      (e: RSDistribution.distributionError) => new SqDistributionError(e)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cdf(env: environment, n: number) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSDistribution.cdf({ env }, this._value, n),
 | 
			
		||||
      (v: number) => v,
 | 
			
		||||
      (e: RSDistribution.distributionError) => new SqDistributionError(e)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inv(env: environment, n: number) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSDistribution.inv({ env }, this._value, n),
 | 
			
		||||
      (v: number) => v,
 | 
			
		||||
      (e: RSDistribution.distributionError) => new SqDistributionError(e)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stdev(env: environment) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSDistribution.stdev({ env }, this._value),
 | 
			
		||||
      (v: number) => v,
 | 
			
		||||
      (e: RSDistribution.distributionError) => new SqDistributionError(e)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqPointSetDistribution extends SqAbstractDistribution {
 | 
			
		||||
  tag = Tag.PointSet;
 | 
			
		||||
 | 
			
		||||
  value() {
 | 
			
		||||
    return this.valueMethod(RSDistribution.getPointSet);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqSampleSetDistribution extends SqAbstractDistribution {
 | 
			
		||||
  tag = Tag.SampleSet;
 | 
			
		||||
 | 
			
		||||
  value() {
 | 
			
		||||
    return this.valueMethod(RSDistribution.getSampleSet);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqSymbolicDistribution extends SqAbstractDistribution {
 | 
			
		||||
  tag = Tag.Symbolic;
 | 
			
		||||
 | 
			
		||||
  value() {
 | 
			
		||||
    return this.valueMethod(RSDistribution.getSymbolic);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const tagToClass = {
 | 
			
		||||
  [Tag.PointSet]: SqPointSetDistribution,
 | 
			
		||||
  [Tag.SampleSet]: SqSampleSetDistribution,
 | 
			
		||||
  [Tag.Symbolic]: SqSymbolicDistribution,
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export type SqDistribution =
 | 
			
		||||
  | SqPointSetDistribution
 | 
			
		||||
  | SqSampleSetDistribution
 | 
			
		||||
  | SqSymbolicDistribution;
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/squiggle-lang/src/js/SqDistributionError.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/squiggle-lang/src/js/SqDistributionError.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
import * as RSDistributionError from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Error.gen";
 | 
			
		||||
 | 
			
		||||
type T = RSDistributionError.distributionError;
 | 
			
		||||
 | 
			
		||||
export class SqDistributionError {
 | 
			
		||||
  constructor(private _value: T) {}
 | 
			
		||||
 | 
			
		||||
  toString() {
 | 
			
		||||
    return RSDistributionError.toString(this._value);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/squiggle-lang/src/js/SqError.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/squiggle-lang/src/js/SqError.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
 | 
			
		||||
 | 
			
		||||
export class SqError {
 | 
			
		||||
  constructor(private _value: RSErrorValue.reducerErrorValue) {}
 | 
			
		||||
 | 
			
		||||
  toString() {
 | 
			
		||||
    return RSErrorValue.toString(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static createTodoError(v: string) {
 | 
			
		||||
    return new SqError(RSErrorValue.createTodoError(v));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static createOtherError(v: string) {
 | 
			
		||||
    return new SqError(RSErrorValue.createOtherError(v));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								packages/squiggle-lang/src/js/SqLambda.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								packages/squiggle-lang/src/js/SqLambda.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
 | 
			
		||||
import { SqError } from "./SqError";
 | 
			
		||||
import { SqValue } from "./SqValue";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
import { result } from "./types";
 | 
			
		||||
 | 
			
		||||
type T = RSLambda.squiggleValue_Lambda;
 | 
			
		||||
 | 
			
		||||
export class SqLambda {
 | 
			
		||||
  constructor(private _value: T, public location: SqValueLocation) {}
 | 
			
		||||
 | 
			
		||||
  parameters() {
 | 
			
		||||
    return RSLambda.parameters(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  call(args: (number | string)[]): result<SqValue, SqError> {
 | 
			
		||||
    const { project, sourceId } = this.location;
 | 
			
		||||
    // Might be good to use uuid instead, but there's no way to remove sources from projects.
 | 
			
		||||
    // So this is not thread-safe.
 | 
			
		||||
    const callId = "__lambda__";
 | 
			
		||||
    const quote = (arg: string) =>
 | 
			
		||||
      `"${arg.replace(new RegExp('"', "g"), '\\"')}"`;
 | 
			
		||||
    const argsSource = args
 | 
			
		||||
      .map((arg) => (typeof arg === "number" ? arg : quote(arg)))
 | 
			
		||||
      .join(",");
 | 
			
		||||
 | 
			
		||||
    // end expression values are exposed in bindings via secret `__result__` variable and we can access them through it
 | 
			
		||||
    const pathItems = [
 | 
			
		||||
      ...(this.location.path.root === "result" ? ["__result__"] : []),
 | 
			
		||||
      ...this.location.path.items,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    // full function name, e.g. foo.bar[3].baz
 | 
			
		||||
    const functionNameSource = pathItems
 | 
			
		||||
      .map((item, i) =>
 | 
			
		||||
        typeof item === "string" ? (i ? "." + item : item) : `[${item}]`
 | 
			
		||||
      )
 | 
			
		||||
      .join("");
 | 
			
		||||
 | 
			
		||||
    // something like: foo.bar[3].baz(1,2,3)
 | 
			
		||||
    const source = `${functionNameSource}(${argsSource})`;
 | 
			
		||||
 | 
			
		||||
    project.setSource(callId, source);
 | 
			
		||||
    project.setContinues(callId, [sourceId]);
 | 
			
		||||
    project.run(callId);
 | 
			
		||||
    return project.getResult(callId);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/squiggle-lang/src/js/SqLambdaDeclaration.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/squiggle-lang/src/js/SqLambdaDeclaration.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
import * as RSDeclaration from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.gen";
 | 
			
		||||
 | 
			
		||||
type T = RSDeclaration.squiggleValue_Declaration;
 | 
			
		||||
 | 
			
		||||
export class SqLambdaDeclaration {
 | 
			
		||||
  constructor(private _value: T) {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								packages/squiggle-lang/src/js/SqModule.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								packages/squiggle-lang/src/js/SqModule.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
 | 
			
		||||
import { SqModuleValue, wrapValue } from "./SqValue";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
 | 
			
		||||
export class SqModule {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private _value: RSModuleValue.squiggleValue_Module,
 | 
			
		||||
    public location: SqValueLocation
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  entries() {
 | 
			
		||||
    return RSModuleValue.getKeyValuePairs(this._value).map(
 | 
			
		||||
      ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  asValue() {
 | 
			
		||||
    return new SqModuleValue(
 | 
			
		||||
      RSModuleValue.toSquiggleValue(this._value),
 | 
			
		||||
      this.location
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get(k: string) {
 | 
			
		||||
    const v = RSModuleValue.get(this._value, k);
 | 
			
		||||
    return v === undefined || v === null
 | 
			
		||||
      ? undefined
 | 
			
		||||
      : wrapValue(v, this.location.extend(k));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								packages/squiggle-lang/src/js/SqPointSetDist.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								packages/squiggle-lang/src/js/SqPointSetDist.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
import * as _ from "lodash";
 | 
			
		||||
import { wrapDistribution } from "./SqDistribution";
 | 
			
		||||
import * as RSPointSetDist from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution.gen";
 | 
			
		||||
import { pointSetDistributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_PointSetDistribution_tag";
 | 
			
		||||
 | 
			
		||||
type T = RSPointSetDist.pointSetDistribution;
 | 
			
		||||
 | 
			
		||||
export type SqPoint = { x: number; y: number };
 | 
			
		||||
export type SqShape = {
 | 
			
		||||
  continuous: SqPoint[];
 | 
			
		||||
  discrete: SqPoint[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const shapePoints = (
 | 
			
		||||
  x: RSPointSetDist.continuousShape | RSPointSetDist.discreteShape
 | 
			
		||||
): SqPoint[] => {
 | 
			
		||||
  let xs = x.xyShape.xs;
 | 
			
		||||
  let ys = x.xyShape.ys;
 | 
			
		||||
  return _.zipWith(xs, ys, (x, y) => ({ x, y }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const wrapPointSetDist = (value: T) => {
 | 
			
		||||
  const tag = RSPointSetDist.getTag(value);
 | 
			
		||||
 | 
			
		||||
  return new tagToClass[tag](value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
abstract class SqAbstractPointSetDist {
 | 
			
		||||
  constructor(private _value: T) {}
 | 
			
		||||
 | 
			
		||||
  abstract asShape(): SqShape;
 | 
			
		||||
 | 
			
		||||
  protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
 | 
			
		||||
    const value = rsMethod(this._value);
 | 
			
		||||
    if (!value) throw new Error("Internal casting error");
 | 
			
		||||
    return value;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  asDistribution() {
 | 
			
		||||
    return wrapDistribution(RSPointSetDist.toDistribution(this._value));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqMixedPointSetDist extends SqAbstractPointSetDist {
 | 
			
		||||
  tag = Tag.Mixed as const;
 | 
			
		||||
 | 
			
		||||
  get value(): RSPointSetDist.mixedShape {
 | 
			
		||||
    return this.valueMethod(RSPointSetDist.getMixed);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  asShape() {
 | 
			
		||||
    const v = this.value;
 | 
			
		||||
    return {
 | 
			
		||||
      discrete: shapePoints(v.discrete),
 | 
			
		||||
      continuous: shapePoints(v.continuous),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqDiscretePointSetDist extends SqAbstractPointSetDist {
 | 
			
		||||
  tag = Tag.Discrete as const;
 | 
			
		||||
 | 
			
		||||
  get value(): RSPointSetDist.discreteShape {
 | 
			
		||||
    return this.valueMethod(RSPointSetDist.getDiscrete);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  asShape() {
 | 
			
		||||
    const v = this.value;
 | 
			
		||||
    return {
 | 
			
		||||
      discrete: shapePoints(v),
 | 
			
		||||
      continuous: [],
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqContinuousPointSetDist extends SqAbstractPointSetDist {
 | 
			
		||||
  tag = Tag.Continuous as const;
 | 
			
		||||
 | 
			
		||||
  get value(): RSPointSetDist.continuousShape {
 | 
			
		||||
    return this.valueMethod(RSPointSetDist.getContinues);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  asShape() {
 | 
			
		||||
    const v = this.value;
 | 
			
		||||
    return {
 | 
			
		||||
      discrete: [],
 | 
			
		||||
      continuous: shapePoints(v),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const tagToClass = {
 | 
			
		||||
  [Tag.Mixed]: SqMixedPointSetDist,
 | 
			
		||||
  [Tag.Discrete]: SqDiscretePointSetDist,
 | 
			
		||||
  [Tag.Continuous]: SqContinuousPointSetDist,
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
export type SqPointSetDist =
 | 
			
		||||
  | SqMixedPointSetDist
 | 
			
		||||
  | SqDiscretePointSetDist
 | 
			
		||||
  | SqContinuousPointSetDist;
 | 
			
		||||
							
								
								
									
										114
									
								
								packages/squiggle-lang/src/js/SqProject.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								packages/squiggle-lang/src/js/SqProject.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
 | 
			
		||||
import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
 | 
			
		||||
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
 | 
			
		||||
import { SqError } from "./SqError";
 | 
			
		||||
import { SqModule } from "./SqModule";
 | 
			
		||||
import { wrapValue } from "./SqValue";
 | 
			
		||||
import { resultMap2 } from "./types";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
 | 
			
		||||
export class SqProject {
 | 
			
		||||
  constructor(private _value: RSProject.reducerProject) {}
 | 
			
		||||
 | 
			
		||||
  static create() {
 | 
			
		||||
    return new SqProject(RSProject.createProject());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSourceIds() {
 | 
			
		||||
    return RSProject.getSourceIds(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setSource(sourceId: string, value: string) {
 | 
			
		||||
    return RSProject.setSource(this._value, sourceId, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSource(sourceId: string) {
 | 
			
		||||
    return RSProject.getSource(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  touchSource(sourceId: string) {
 | 
			
		||||
    return RSProject.touchSource(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clean(sourceId: string) {
 | 
			
		||||
    return RSProject.clean(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cleanAll() {
 | 
			
		||||
    return RSProject.cleanAll(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cleanResults(sourceId: string) {
 | 
			
		||||
    return RSProject.cleanResults(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cleanAllResults() {
 | 
			
		||||
    return RSProject.cleanAllResults(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getIncludes(sourceId: string) {
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      RSProject.getIncludes(this._value, sourceId),
 | 
			
		||||
      (a) => a,
 | 
			
		||||
      (v: reducerErrorValue) => new SqError(v)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getContinues(sourceId: string) {
 | 
			
		||||
    return RSProject.getContinues(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setContinues(sourceId: string, continues: string[]) {
 | 
			
		||||
    return RSProject.setContinues(this._value, sourceId, continues);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getRunOrder() {
 | 
			
		||||
    return RSProject.getRunOrder(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getRunOrderFor(sourceId: string) {
 | 
			
		||||
    return RSProject.getRunOrderFor(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  parseIncludes(sourceId: string) {
 | 
			
		||||
    return RSProject.parseIncludes(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  run(sourceId: string) {
 | 
			
		||||
    return RSProject.run(this._value, sourceId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  runAll() {
 | 
			
		||||
    return RSProject.runAll(this._value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getBindings(sourceId: string) {
 | 
			
		||||
    return new SqModule(
 | 
			
		||||
      RSProject.getBindings(this._value, sourceId),
 | 
			
		||||
      new SqValueLocation(this, sourceId, {
 | 
			
		||||
        root: "bindings",
 | 
			
		||||
        items: [],
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getResult(sourceId: string) {
 | 
			
		||||
    const innerResult = RSProject.getResult(this._value, sourceId);
 | 
			
		||||
    return resultMap2(
 | 
			
		||||
      innerResult,
 | 
			
		||||
      (v) =>
 | 
			
		||||
        wrapValue(
 | 
			
		||||
          v,
 | 
			
		||||
          new SqValueLocation(this, sourceId, {
 | 
			
		||||
            root: "result",
 | 
			
		||||
            items: [],
 | 
			
		||||
          })
 | 
			
		||||
        ),
 | 
			
		||||
      (v: reducerErrorValue) => new SqError(v)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setEnvironment(environment: environment) {
 | 
			
		||||
    RSProject.setEnvironment(this._value, environment);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								packages/squiggle-lang/src/js/SqRecord.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/squiggle-lang/src/js/SqRecord.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
 | 
			
		||||
import { wrapValue } from "./SqValue";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
 | 
			
		||||
type T = RSRecord.squiggleValue_Record;
 | 
			
		||||
 | 
			
		||||
export class SqRecord {
 | 
			
		||||
  constructor(private _value: T, public location: SqValueLocation) {}
 | 
			
		||||
 | 
			
		||||
  entries() {
 | 
			
		||||
    return RSRecord.getKeyValuePairs(this._value).map(
 | 
			
		||||
      ([k, v]) => [k, wrapValue(v, this.location.extend(k))] as const
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/squiggle-lang/src/js/SqType.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/squiggle-lang/src/js/SqType.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.gen";
 | 
			
		||||
 | 
			
		||||
type T = RSType.squiggleValue_Type;
 | 
			
		||||
 | 
			
		||||
export class SqType {
 | 
			
		||||
  constructor(private _value: T) {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										216
									
								
								packages/squiggle-lang/src/js/SqValue.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								packages/squiggle-lang/src/js/SqValue.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,216 @@
 | 
			
		|||
import * as RSValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.gen";
 | 
			
		||||
import { squiggleValueTag as Tag } from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag";
 | 
			
		||||
import { wrapDistribution } from "./SqDistribution";
 | 
			
		||||
import { SqLambda } from "./SqLambda";
 | 
			
		||||
import { SqLambdaDeclaration } from "./SqLambdaDeclaration";
 | 
			
		||||
import { SqModule } from "./SqModule";
 | 
			
		||||
import { SqRecord } from "./SqRecord";
 | 
			
		||||
import { SqArray } from "./SqArray";
 | 
			
		||||
import { SqType } from "./SqType";
 | 
			
		||||
import { SqProject } from "./SqProject";
 | 
			
		||||
import { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
 | 
			
		||||
export { Tag as SqValueTag };
 | 
			
		||||
 | 
			
		||||
type T = RSValue.squiggleValue;
 | 
			
		||||
 | 
			
		||||
export const wrapValue = (value: T, location: SqValueLocation): SqValue => {
 | 
			
		||||
  const tag = RSValue.getTag(value);
 | 
			
		||||
 | 
			
		||||
  return new tagToClass[tag](value, location);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export abstract class SqAbstractValue {
 | 
			
		||||
  abstract tag: Tag;
 | 
			
		||||
 | 
			
		||||
  constructor(private _value: T, public location: SqValueLocation) {}
 | 
			
		||||
 | 
			
		||||
  protected valueMethod = <IR>(rsMethod: (v: T) => IR | null | undefined) => {
 | 
			
		||||
    const value = rsMethod(this._value);
 | 
			
		||||
    if (value === undefined || value === null) {
 | 
			
		||||
      throw new Error("Internal casting error");
 | 
			
		||||
    }
 | 
			
		||||
    return value;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  toString() {
 | 
			
		||||
    return RSValue.toString(this._value);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqArrayValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Array as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqArray(this.valueMethod(RSValue.getArray), this.location);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqArrayStringValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.ArrayString as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getArrayString);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqBoolValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Bool as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getBool);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqCallValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Call as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getCall);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqDateValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Date as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getDate);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqDeclarationValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Declaration as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqLambdaDeclaration(this.valueMethod(RSValue.getDeclaration));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqDistributionValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Distribution as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return wrapDistribution(this.valueMethod(RSValue.getDistribution));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqLambdaValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Lambda as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqLambda(this.valueMethod(RSValue.getLambda), this.location);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqModuleValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Module as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqModule(this.valueMethod(RSValue.getModule), this.location);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqNumberValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Number as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getNumber);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqRecordValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Record as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqRecord(this.valueMethod(RSValue.getRecord), this.location);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqStringValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.String as const;
 | 
			
		||||
 | 
			
		||||
  get value(): string {
 | 
			
		||||
    return this.valueMethod(RSValue.getString);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqSymbolValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Symbol as const;
 | 
			
		||||
 | 
			
		||||
  get value(): string {
 | 
			
		||||
    return this.valueMethod(RSValue.getSymbol);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqTimeDurationValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.TimeDuration as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getTimeDuration);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqTypeValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Type as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return new SqType(this.valueMethod(RSValue.getType));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqTypeIdentifierValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.TypeIdentifier as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return this.valueMethod(RSValue.getTypeIdentifier);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class SqVoidValue extends SqAbstractValue {
 | 
			
		||||
  tag = Tag.Void as const;
 | 
			
		||||
 | 
			
		||||
  get value() {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const tagToClass = {
 | 
			
		||||
  [Tag.Array]: SqArrayValue,
 | 
			
		||||
  [Tag.ArrayString]: SqArrayStringValue,
 | 
			
		||||
  [Tag.Bool]: SqBoolValue,
 | 
			
		||||
  [Tag.Call]: SqCallValue,
 | 
			
		||||
  [Tag.Date]: SqDateValue,
 | 
			
		||||
  [Tag.Declaration]: SqDeclarationValue,
 | 
			
		||||
  [Tag.Distribution]: SqDistributionValue,
 | 
			
		||||
  [Tag.Lambda]: SqLambdaValue,
 | 
			
		||||
  [Tag.Module]: SqModuleValue,
 | 
			
		||||
  [Tag.Number]: SqNumberValue,
 | 
			
		||||
  [Tag.Record]: SqRecordValue,
 | 
			
		||||
  [Tag.String]: SqStringValue,
 | 
			
		||||
  [Tag.Symbol]: SqSymbolValue,
 | 
			
		||||
  [Tag.TimeDuration]: SqTimeDurationValue,
 | 
			
		||||
  [Tag.Type]: SqTypeValue,
 | 
			
		||||
  [Tag.TypeIdentifier]: SqTypeIdentifierValue,
 | 
			
		||||
  [Tag.Void]: SqVoidValue,
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
// FIXME
 | 
			
		||||
// type SqValue = typeof tagToClass[keyof typeof tagToClass];
 | 
			
		||||
export type SqValue =
 | 
			
		||||
  | SqArrayValue
 | 
			
		||||
  | SqArrayStringValue
 | 
			
		||||
  | SqBoolValue
 | 
			
		||||
  | SqCallValue
 | 
			
		||||
  | SqDateValue
 | 
			
		||||
  | SqDeclarationValue
 | 
			
		||||
  | SqDistributionValue
 | 
			
		||||
  | SqLambdaValue
 | 
			
		||||
  | SqModuleValue
 | 
			
		||||
  | SqNumberValue
 | 
			
		||||
  | SqRecordValue
 | 
			
		||||
  | SqStringValue
 | 
			
		||||
  | SqSymbolValue
 | 
			
		||||
  | SqTimeDurationValue
 | 
			
		||||
  | SqTypeValue
 | 
			
		||||
  | SqTypeIdentifierValue
 | 
			
		||||
  | SqVoidValue;
 | 
			
		||||
							
								
								
									
										24
									
								
								packages/squiggle-lang/src/js/SqValueLocation.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								packages/squiggle-lang/src/js/SqValueLocation.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
import { isParenthesisNode } from "mathjs";
 | 
			
		||||
import { SqProject } from "./SqProject";
 | 
			
		||||
 | 
			
		||||
type PathItem = string | number;
 | 
			
		||||
 | 
			
		||||
type SqValuePath = {
 | 
			
		||||
  root: "result" | "bindings";
 | 
			
		||||
  items: PathItem[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class SqValueLocation {
 | 
			
		||||
  constructor(
 | 
			
		||||
    public project: SqProject,
 | 
			
		||||
    public sourceId: string,
 | 
			
		||||
    public path: SqValuePath
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  extend(item: PathItem) {
 | 
			
		||||
    return new SqValueLocation(this.project, this.sourceId, {
 | 
			
		||||
      root: this.path.root,
 | 
			
		||||
      items: [...this.path.items, item],
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,252 +0,0 @@
 | 
			
		|||
import * as _ from "lodash";
 | 
			
		||||
import {
 | 
			
		||||
  genericDist,
 | 
			
		||||
  continuousShape,
 | 
			
		||||
  discreteShape,
 | 
			
		||||
  environment,
 | 
			
		||||
  distributionError,
 | 
			
		||||
  toPointSet,
 | 
			
		||||
  distributionErrorToString,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
import { result, resultMap, Ok } from "./types";
 | 
			
		||||
import {
 | 
			
		||||
  Constructors_mean,
 | 
			
		||||
  Constructors_stdev,
 | 
			
		||||
  Constructors_sample,
 | 
			
		||||
  Constructors_pdf,
 | 
			
		||||
  Constructors_cdf,
 | 
			
		||||
  Constructors_inv,
 | 
			
		||||
  Constructors_normalize,
 | 
			
		||||
  Constructors_isNormalized,
 | 
			
		||||
  Constructors_toPointSet,
 | 
			
		||||
  Constructors_toSampleSet,
 | 
			
		||||
  Constructors_truncate,
 | 
			
		||||
  Constructors_inspect,
 | 
			
		||||
  Constructors_toString,
 | 
			
		||||
  Constructors_toSparkline,
 | 
			
		||||
  Constructors_algebraicAdd,
 | 
			
		||||
  Constructors_algebraicMultiply,
 | 
			
		||||
  Constructors_algebraicDivide,
 | 
			
		||||
  Constructors_algebraicSubtract,
 | 
			
		||||
  Constructors_algebraicLogarithm,
 | 
			
		||||
  Constructors_algebraicPower,
 | 
			
		||||
  Constructors_pointwiseAdd,
 | 
			
		||||
  Constructors_pointwiseMultiply,
 | 
			
		||||
  Constructors_pointwiseDivide,
 | 
			
		||||
  Constructors_pointwiseSubtract,
 | 
			
		||||
  Constructors_pointwiseLogarithm,
 | 
			
		||||
  Constructors_pointwisePower,
 | 
			
		||||
} from "../rescript/Distributions/DistributionOperation.gen";
 | 
			
		||||
 | 
			
		||||
export type point = { x: number; y: number };
 | 
			
		||||
 | 
			
		||||
function shapePoints(x: continuousShape | discreteShape): point[] {
 | 
			
		||||
  let xs = x.xyShape.xs;
 | 
			
		||||
  let ys = x.xyShape.ys;
 | 
			
		||||
  return _.zipWith(xs, ys, (x, y) => ({ x, y }));
 | 
			
		||||
}
 | 
			
		||||
export type shape = {
 | 
			
		||||
  continuous: point[];
 | 
			
		||||
  discrete: point[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class Distribution {
 | 
			
		||||
  t: genericDist;
 | 
			
		||||
  env: environment;
 | 
			
		||||
 | 
			
		||||
  constructor(t: genericDist, env: environment) {
 | 
			
		||||
    this.t = t;
 | 
			
		||||
    this.env = env;
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mapResultDist(
 | 
			
		||||
    r: result<genericDist, distributionError>
 | 
			
		||||
  ): result<Distribution, distributionError> {
 | 
			
		||||
    return resultMap(r, (v: genericDist) => new Distribution(v, this.env));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mean(): result<number, distributionError> {
 | 
			
		||||
    return Constructors_mean({ env: this.env }, this.t);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  stdev(): result<number, distributionError> {
 | 
			
		||||
    return Constructors_stdev({ env: this.env }, this.t);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sample(): result<number, distributionError> {
 | 
			
		||||
    return Constructors_sample({ env: this.env }, this.t);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pdf(n: number): result<number, distributionError> {
 | 
			
		||||
    return Constructors_pdf({ env: this.env }, this.t, n);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cdf(n: number): result<number, distributionError> {
 | 
			
		||||
    return Constructors_cdf({ env: this.env }, this.t, n);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inv(n: number): result<number, distributionError> {
 | 
			
		||||
    return Constructors_inv({ env: this.env }, this.t, n);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isNormalized(): result<boolean, distributionError> {
 | 
			
		||||
    return Constructors_isNormalized({ env: this.env }, this.t);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  normalize(): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_normalize({ env: this.env }, this.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type() {
 | 
			
		||||
    return this.t.tag;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointSet(): result<shape, distributionError> {
 | 
			
		||||
    let pointSet = toPointSet(
 | 
			
		||||
      this.t,
 | 
			
		||||
      {
 | 
			
		||||
        xyPointLength: this.env.xyPointLength,
 | 
			
		||||
        sampleCount: this.env.sampleCount,
 | 
			
		||||
      },
 | 
			
		||||
      undefined
 | 
			
		||||
    );
 | 
			
		||||
    if (pointSet.tag === "Ok") {
 | 
			
		||||
      let distribution = pointSet.value;
 | 
			
		||||
      if (distribution.tag === "Continuous") {
 | 
			
		||||
        return Ok({
 | 
			
		||||
          continuous: shapePoints(distribution.value),
 | 
			
		||||
          discrete: [],
 | 
			
		||||
        });
 | 
			
		||||
      } else if (distribution.tag === "Discrete") {
 | 
			
		||||
        return Ok({
 | 
			
		||||
          discrete: shapePoints(distribution.value),
 | 
			
		||||
          continuous: [],
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        return Ok({
 | 
			
		||||
          discrete: shapePoints(distribution.value.discrete),
 | 
			
		||||
          continuous: shapePoints(distribution.value.continuous),
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return pointSet;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toPointSet(): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_toPointSet({ env: this.env }, this.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toSampleSet(n: number): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_toSampleSet({ env: this.env }, this.t, n)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  truncate(
 | 
			
		||||
    left: number,
 | 
			
		||||
    right: number
 | 
			
		||||
  ): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_truncate({ env: this.env }, this.t, left, right)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  inspect(): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(Constructors_inspect({ env: this.env }, this.t));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toString(): string {
 | 
			
		||||
    let result = Constructors_toString({ env: this.env }, this.t);
 | 
			
		||||
    if (result.tag === "Ok") {
 | 
			
		||||
      return result.value;
 | 
			
		||||
    } else {
 | 
			
		||||
      return distributionErrorToString(result.value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toSparkline(n: number): result<string, distributionError> {
 | 
			
		||||
    return Constructors_toSparkline({ env: this.env }, this.t, n);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicAdd(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicAdd({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicMultiply(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicMultiply({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicDivide(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicDivide({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicSubtract(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicSubtract({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicLogarithm(
 | 
			
		||||
    d2: Distribution
 | 
			
		||||
  ): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicLogarithm({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  algebraicPower(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_algebraicPower({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwiseAdd(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwiseAdd({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwiseMultiply(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwiseMultiply({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwiseDivide(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwiseDivide({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwiseSubtract(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwiseSubtract({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwiseLogarithm(
 | 
			
		||||
    d2: Distribution
 | 
			
		||||
  ): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwiseLogarithm({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  pointwisePower(d2: Distribution): result<Distribution, distributionError> {
 | 
			
		||||
    return this.mapResultDist(
 | 
			
		||||
      Constructors_pointwisePower({ env: this.env }, this.t, d2.t)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,205 +1,36 @@
 | 
			
		|||
import * as _ from "lodash";
 | 
			
		||||
import type {
 | 
			
		||||
  environment,
 | 
			
		||||
  expressionValue,
 | 
			
		||||
  externalBindings,
 | 
			
		||||
  errorValue,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
import {
 | 
			
		||||
  defaultEnvironment,
 | 
			
		||||
  evaluatePartialUsingExternalBindings,
 | 
			
		||||
  evaluateUsingOptions,
 | 
			
		||||
  foreignFunctionInterface,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
 | 
			
		||||
import { SqProject } from "./SqProject";
 | 
			
		||||
import { SqValue, SqValueTag } from "./SqValue";
 | 
			
		||||
export { SqValueLocation } from "./SqValueLocation";
 | 
			
		||||
export { result } from "../rescript/ForTS/ForTS_Result_tag";
 | 
			
		||||
export { SqDistribution, SqDistributionTag } from "./SqDistribution";
 | 
			
		||||
export { SqDistributionError } from "./SqDistributionError";
 | 
			
		||||
export { SqRecord } from "./SqRecord";
 | 
			
		||||
export { SqLambda } from "./SqLambda";
 | 
			
		||||
export { SqProject };
 | 
			
		||||
export { SqValue, SqValueTag };
 | 
			
		||||
export {
 | 
			
		||||
  makeSampleSetDist,
 | 
			
		||||
  errorValueToString,
 | 
			
		||||
  distributionErrorToString,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
export type {
 | 
			
		||||
  distributionError,
 | 
			
		||||
  declarationArg,
 | 
			
		||||
  declaration,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
export type { errorValue, externalBindings as bindings, jsImports };
 | 
			
		||||
import {
 | 
			
		||||
  jsValueToBinding,
 | 
			
		||||
  jsValueToExpressionValue,
 | 
			
		||||
  jsValue,
 | 
			
		||||
  rescriptExport,
 | 
			
		||||
  squiggleExpression,
 | 
			
		||||
  convertRawToTypescript,
 | 
			
		||||
  lambdaValue,
 | 
			
		||||
} from "./rescript_interop";
 | 
			
		||||
import { result, resultMap, tag, tagged } from "./types";
 | 
			
		||||
import { Distribution, shape } from "./distribution";
 | 
			
		||||
  environment,
 | 
			
		||||
  defaultEnvironment,
 | 
			
		||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
 | 
			
		||||
export { SqError } from "./SqError";
 | 
			
		||||
export { SqShape } from "./SqPointSetDist";
 | 
			
		||||
 | 
			
		||||
export { Distribution, resultMap, defaultEnvironment };
 | 
			
		||||
export type { result, shape, environment, lambdaValue, squiggleExpression };
 | 
			
		||||
export { resultMap } from "./types";
 | 
			
		||||
 | 
			
		||||
export { parse } from "./parse";
 | 
			
		||||
 | 
			
		||||
export let defaultSamplingInputs: environment = {
 | 
			
		||||
  sampleCount: 1000,
 | 
			
		||||
  xyPointLength: 1000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function run(
 | 
			
		||||
  squiggleString: string,
 | 
			
		||||
  bindings?: externalBindings,
 | 
			
		||||
  environment?: environment,
 | 
			
		||||
  imports?: jsImports
 | 
			
		||||
): result<squiggleExpression, errorValue> {
 | 
			
		||||
  let b = bindings ? bindings : defaultBindings;
 | 
			
		||||
  let i = imports ? imports : defaultImports;
 | 
			
		||||
  let e = environment ? environment : defaultEnvironment;
 | 
			
		||||
  let res: result<expressionValue, errorValue> = evaluateUsingOptions(
 | 
			
		||||
    { externalBindings: mergeImportsWithBindings(b, i), environment: e },
 | 
			
		||||
    squiggleString
 | 
			
		||||
  );
 | 
			
		||||
  return resultMap(res, (x) => createTsExport(x, e));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Run Partial. A partial is a block of code that doesn't return a value
 | 
			
		||||
export function runPartial(
 | 
			
		||||
  squiggleString: string,
 | 
			
		||||
  bindings?: externalBindings,
 | 
			
		||||
  environment?: environment,
 | 
			
		||||
  imports?: jsImports
 | 
			
		||||
): result<externalBindings, errorValue> {
 | 
			
		||||
  let b = bindings ? bindings : defaultBindings;
 | 
			
		||||
  let i = imports ? imports : defaultImports;
 | 
			
		||||
  let e = environment ? environment : defaultEnvironment;
 | 
			
		||||
 | 
			
		||||
  return evaluatePartialUsingExternalBindings(
 | 
			
		||||
    squiggleString,
 | 
			
		||||
    mergeImportsWithBindings(b, i),
 | 
			
		||||
    e
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function runForeign(
 | 
			
		||||
  fn: lambdaValue,
 | 
			
		||||
  args: jsValue[],
 | 
			
		||||
  environment?: environment
 | 
			
		||||
): result<squiggleExpression, errorValue> {
 | 
			
		||||
  let e = environment ? environment : defaultEnvironment;
 | 
			
		||||
  let res: result<expressionValue, errorValue> = foreignFunctionInterface(
 | 
			
		||||
    fn,
 | 
			
		||||
    args.map(jsValueToExpressionValue),
 | 
			
		||||
    e
 | 
			
		||||
  );
 | 
			
		||||
  return resultMap(res, (x) => createTsExport(x, e));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mergeImportsWithBindings(
 | 
			
		||||
  bindings: externalBindings,
 | 
			
		||||
  imports: jsImports
 | 
			
		||||
): externalBindings {
 | 
			
		||||
  let transformedImports = Object.fromEntries(
 | 
			
		||||
    Object.entries(imports).map(([key, value]) => [
 | 
			
		||||
      "$" + key,
 | 
			
		||||
      jsValueToBinding(value),
 | 
			
		||||
    ])
 | 
			
		||||
  );
 | 
			
		||||
  return _.merge(bindings, transformedImports);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type jsImports = { [key: string]: jsValue };
 | 
			
		||||
 | 
			
		||||
export let defaultImports: jsImports = {};
 | 
			
		||||
export let defaultBindings: externalBindings = {};
 | 
			
		||||
 | 
			
		||||
export function mergeBindings(
 | 
			
		||||
  allBindings: externalBindings[]
 | 
			
		||||
): externalBindings {
 | 
			
		||||
  return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createTsExport(
 | 
			
		||||
  x: expressionValue,
 | 
			
		||||
  environment: environment
 | 
			
		||||
): squiggleExpression {
 | 
			
		||||
  switch (x) {
 | 
			
		||||
    case "EvVoid":
 | 
			
		||||
      return tag("void", "");
 | 
			
		||||
    default: {
 | 
			
		||||
      switch (x.tag) {
 | 
			
		||||
        case "EvArray":
 | 
			
		||||
          // genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
 | 
			
		||||
          // format, leaving it as the raw values. This converts the raw values
 | 
			
		||||
          // directly into typescript values.
 | 
			
		||||
          //
 | 
			
		||||
          // The casting here is because genType is about the types of the returned
 | 
			
		||||
          // values, claiming they are fully recursive when that's not actually the
 | 
			
		||||
          // case
 | 
			
		||||
          return tag(
 | 
			
		||||
            "array",
 | 
			
		||||
            x.value.map(
 | 
			
		||||
              (arrayItem): squiggleExpression =>
 | 
			
		||||
                convertRawToTypescript(
 | 
			
		||||
                  arrayItem as unknown as rescriptExport,
 | 
			
		||||
                  environment
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
        case "EvArrayString":
 | 
			
		||||
          return tag("arraystring", x.value);
 | 
			
		||||
        case "EvBool":
 | 
			
		||||
          return tag("boolean", x.value);
 | 
			
		||||
        case "EvCall":
 | 
			
		||||
          return tag("call", x.value);
 | 
			
		||||
        case "EvLambda":
 | 
			
		||||
          return tag("lambda", x.value);
 | 
			
		||||
        case "EvDistribution":
 | 
			
		||||
          return tag("distribution", new Distribution(x.value, environment));
 | 
			
		||||
        case "EvNumber":
 | 
			
		||||
          return tag("number", x.value);
 | 
			
		||||
        case "EvRecord":
 | 
			
		||||
          // genType doesn't support records, so we have to do the raw conversion ourself
 | 
			
		||||
          let result: tagged<"record", { [key: string]: squiggleExpression }> =
 | 
			
		||||
            tag(
 | 
			
		||||
              "record",
 | 
			
		||||
              _.mapValues(x.value, (x: unknown) =>
 | 
			
		||||
                convertRawToTypescript(x as rescriptExport, environment)
 | 
			
		||||
              )
 | 
			
		||||
            );
 | 
			
		||||
          return result;
 | 
			
		||||
        case "EvString":
 | 
			
		||||
          return tag("string", x.value);
 | 
			
		||||
        case "EvSymbol":
 | 
			
		||||
          return tag("symbol", x.value);
 | 
			
		||||
        case "EvDate":
 | 
			
		||||
          return tag("date", x.value);
 | 
			
		||||
        case "EvTimeDuration":
 | 
			
		||||
          return tag("timeDuration", x.value);
 | 
			
		||||
        case "EvDeclaration":
 | 
			
		||||
          return tag("lambdaDeclaration", x.value);
 | 
			
		||||
        case "EvTypeIdentifier":
 | 
			
		||||
          return tag("typeIdentifier", x.value);
 | 
			
		||||
        case "EvType":
 | 
			
		||||
          let typeResult: tagged<
 | 
			
		||||
            "type",
 | 
			
		||||
            { [key: string]: squiggleExpression }
 | 
			
		||||
          > = tag(
 | 
			
		||||
            "type",
 | 
			
		||||
            _.mapValues(x.value, (x: unknown) =>
 | 
			
		||||
              convertRawToTypescript(x as rescriptExport, environment)
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
          return typeResult;
 | 
			
		||||
        case "EvModule":
 | 
			
		||||
          let moduleResult: tagged<
 | 
			
		||||
            "module",
 | 
			
		||||
            { [key: string]: squiggleExpression }
 | 
			
		||||
          > = tag(
 | 
			
		||||
            "module",
 | 
			
		||||
            _.mapValues(x.value, (x: unknown) =>
 | 
			
		||||
              convertRawToTypescript(x as rescriptExport, environment)
 | 
			
		||||
            )
 | 
			
		||||
          );
 | 
			
		||||
          return moduleResult;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
export const run = (
 | 
			
		||||
  code: string,
 | 
			
		||||
  options?: {
 | 
			
		||||
    environment?: environment;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
) => {
 | 
			
		||||
  const project = SqProject.create();
 | 
			
		||||
  project.setSource("main", code);
 | 
			
		||||
  if (options?.environment) {
 | 
			
		||||
    project.setEnvironment(options.environment);
 | 
			
		||||
  }
 | 
			
		||||
  project.run("main");
 | 
			
		||||
  const result = project.getResult("main");
 | 
			
		||||
  const bindings = project.getBindings("main");
 | 
			
		||||
  return { result, bindings };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,23 +1,23 @@
 | 
			
		|||
import {
 | 
			
		||||
  errorValue,
 | 
			
		||||
  parse as parseRescript,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
import { result } from "./types";
 | 
			
		||||
import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers";
 | 
			
		||||
// import {
 | 
			
		||||
//   errorValue,
 | 
			
		||||
//   parse as parseRescript,
 | 
			
		||||
// } from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
// import { result } from "./types";
 | 
			
		||||
// import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers";
 | 
			
		||||
 | 
			
		||||
export function parse(
 | 
			
		||||
  squiggleString: string
 | 
			
		||||
): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> {
 | 
			
		||||
  const maybeExpression = parseRescript(squiggleString);
 | 
			
		||||
  if (maybeExpression.tag === "Ok") {
 | 
			
		||||
    return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode };
 | 
			
		||||
  } else {
 | 
			
		||||
    if (
 | 
			
		||||
      typeof maybeExpression.value !== "object" ||
 | 
			
		||||
      maybeExpression.value.tag !== "RESyntaxError"
 | 
			
		||||
    ) {
 | 
			
		||||
      throw new Error("Expected syntax error");
 | 
			
		||||
    }
 | 
			
		||||
    return { tag: "Error", value: maybeExpression.value };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// export function parse(
 | 
			
		||||
//   squiggleString: string
 | 
			
		||||
// ): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> {
 | 
			
		||||
//   const maybeExpression = parseRescript(squiggleString);
 | 
			
		||||
//   if (maybeExpression.tag === "Ok") {
 | 
			
		||||
//     return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode };
 | 
			
		||||
//   } else {
 | 
			
		||||
//     if (
 | 
			
		||||
//       typeof maybeExpression.value !== "object" ||
 | 
			
		||||
//       maybeExpression.value.tag !== "RESyntaxError"
 | 
			
		||||
//     ) {
 | 
			
		||||
//       throw new Error("Expected syntax error");
 | 
			
		||||
//     }
 | 
			
		||||
//     return { tag: "Error", value: maybeExpression.value };
 | 
			
		||||
//   }
 | 
			
		||||
// }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,269 +0,0 @@
 | 
			
		|||
import * as _ from "lodash";
 | 
			
		||||
import type {
 | 
			
		||||
  expressionValue,
 | 
			
		||||
  mixedShape,
 | 
			
		||||
  sampleSetDist,
 | 
			
		||||
  genericDist,
 | 
			
		||||
  environment,
 | 
			
		||||
  symbolicDist,
 | 
			
		||||
  discreteShape,
 | 
			
		||||
  continuousShape,
 | 
			
		||||
  lambdaValue,
 | 
			
		||||
  lambdaDeclaration,
 | 
			
		||||
  declarationArg,
 | 
			
		||||
} from "../rescript/TypescriptInterface.gen";
 | 
			
		||||
import { Distribution } from "./distribution";
 | 
			
		||||
import { tagged, tag } from "./types";
 | 
			
		||||
// This file is here to compensate for genType not fully recursively converting types
 | 
			
		||||
 | 
			
		||||
// Raw rescript types.
 | 
			
		||||
export type rescriptExport =
 | 
			
		||||
  | 0 // EvVoid
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 0; // EvArray
 | 
			
		||||
      _0: rescriptExport[];
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 1; // EvString
 | 
			
		||||
      _0: string[];
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 2; // EvBool
 | 
			
		||||
      _0: boolean;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 3; // EvCall
 | 
			
		||||
      _0: string;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 4; // EvDistribution
 | 
			
		||||
      _0: rescriptDist;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 5; // EvLambda
 | 
			
		||||
      _0: lambdaValue;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 6; // EvNumber
 | 
			
		||||
      _0: number;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 7; // EvRecord
 | 
			
		||||
      _0: { [key: string]: rescriptExport };
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 8; // EvString
 | 
			
		||||
      _0: string;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 9; // EvSymbol
 | 
			
		||||
      _0: string;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 10; // EvDate
 | 
			
		||||
      _0: Date;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 11; // EvTimeDuration
 | 
			
		||||
      _0: number;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 12; // EvDeclaration
 | 
			
		||||
      _0: rescriptLambdaDeclaration;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 13; // EvTypeIdentifier
 | 
			
		||||
      _0: string;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 14; // EvModule
 | 
			
		||||
      _0: { [key: string]: rescriptExport };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
type rescriptDist =
 | 
			
		||||
  | { TAG: 0; _0: rescriptPointSetDist }
 | 
			
		||||
  | { TAG: 1; _0: sampleSetDist }
 | 
			
		||||
  | { TAG: 2; _0: symbolicDist };
 | 
			
		||||
 | 
			
		||||
type rescriptPointSetDist =
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 0; // Mixed
 | 
			
		||||
      _0: mixedShape;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 1; // Discrete
 | 
			
		||||
      _0: discreteShape;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 2; // ContinuousShape
 | 
			
		||||
      _0: continuousShape;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
type rescriptLambdaDeclaration = {
 | 
			
		||||
  readonly fn: lambdaValue;
 | 
			
		||||
  readonly args: rescriptDeclarationArg[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type rescriptDeclarationArg =
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 0; // Float
 | 
			
		||||
      min: number;
 | 
			
		||||
      max: number;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      TAG: 1; // Date
 | 
			
		||||
      min: Date;
 | 
			
		||||
      max: Date;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export type squiggleExpression =
 | 
			
		||||
  | tagged<"symbol", string>
 | 
			
		||||
  | tagged<"string", string>
 | 
			
		||||
  | tagged<"call", string>
 | 
			
		||||
  | tagged<"lambda", lambdaValue>
 | 
			
		||||
  | tagged<"array", squiggleExpression[]>
 | 
			
		||||
  | tagged<"arraystring", string[]>
 | 
			
		||||
  | tagged<"boolean", boolean>
 | 
			
		||||
  | tagged<"distribution", Distribution>
 | 
			
		||||
  | tagged<"number", number>
 | 
			
		||||
  | tagged<"date", Date>
 | 
			
		||||
  | tagged<"timeDuration", number>
 | 
			
		||||
  | tagged<"lambdaDeclaration", lambdaDeclaration>
 | 
			
		||||
  | tagged<"record", { [key: string]: squiggleExpression }>
 | 
			
		||||
  | tagged<"type", { [key: string]: squiggleExpression }>
 | 
			
		||||
  | tagged<"typeIdentifier", string>
 | 
			
		||||
  | tagged<"module", { [key: string]: squiggleExpression }>
 | 
			
		||||
  | tagged<"void", string>;
 | 
			
		||||
 | 
			
		||||
export { lambdaValue };
 | 
			
		||||
 | 
			
		||||
export function convertRawToTypescript(
 | 
			
		||||
  result: rescriptExport,
 | 
			
		||||
  environment: environment
 | 
			
		||||
): squiggleExpression {
 | 
			
		||||
  if (typeof result === "number") {
 | 
			
		||||
    // EvVoid
 | 
			
		||||
    return tag("void", "");
 | 
			
		||||
  }
 | 
			
		||||
  switch (result.TAG) {
 | 
			
		||||
    case 0: // EvArray
 | 
			
		||||
      return tag(
 | 
			
		||||
        "array",
 | 
			
		||||
        result._0.map((x) => convertRawToTypescript(x, environment))
 | 
			
		||||
      );
 | 
			
		||||
    case 1: // EvArrayString
 | 
			
		||||
      return tag("arraystring", result._0);
 | 
			
		||||
    case 2: // EvBool
 | 
			
		||||
      return tag("boolean", result._0);
 | 
			
		||||
    case 3: // EvCall
 | 
			
		||||
      return tag("call", result._0);
 | 
			
		||||
    case 4: // EvDistribution
 | 
			
		||||
      return tag(
 | 
			
		||||
        "distribution",
 | 
			
		||||
        new Distribution(
 | 
			
		||||
          convertRawDistributionToGenericDist(result._0),
 | 
			
		||||
          environment
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
    case 5: // EvDistribution
 | 
			
		||||
      return tag("lambda", result._0);
 | 
			
		||||
    case 6: // EvNumber
 | 
			
		||||
      return tag("number", result._0);
 | 
			
		||||
    case 7: // EvRecord
 | 
			
		||||
      return tag(
 | 
			
		||||
        "record",
 | 
			
		||||
        _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
 | 
			
		||||
      );
 | 
			
		||||
    case 8: // EvString
 | 
			
		||||
      return tag("string", result._0);
 | 
			
		||||
    case 9: // EvSymbol
 | 
			
		||||
      return tag("symbol", result._0);
 | 
			
		||||
    case 10: // EvDate
 | 
			
		||||
      return tag("date", result._0);
 | 
			
		||||
    case 11: // EvTimeDuration
 | 
			
		||||
      return tag("number", result._0);
 | 
			
		||||
    case 12: // EvDeclaration
 | 
			
		||||
      return tag("lambdaDeclaration", {
 | 
			
		||||
        fn: result._0.fn,
 | 
			
		||||
        args: result._0.args.map(convertDeclaration),
 | 
			
		||||
      });
 | 
			
		||||
    case 13: // EvSymbol
 | 
			
		||||
      return tag("typeIdentifier", result._0);
 | 
			
		||||
    case 14: // EvModule
 | 
			
		||||
      return tag(
 | 
			
		||||
        "module",
 | 
			
		||||
        _.mapValues(result._0, (x) => convertRawToTypescript(x, environment))
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertDeclaration(
 | 
			
		||||
  declarationArg: rescriptDeclarationArg
 | 
			
		||||
): declarationArg {
 | 
			
		||||
  switch (declarationArg.TAG) {
 | 
			
		||||
    case 0: // Float
 | 
			
		||||
      return tag("Float", { min: declarationArg.min, max: declarationArg.max });
 | 
			
		||||
    case 1: // Date
 | 
			
		||||
      return tag("Date", { min: declarationArg.min, max: declarationArg.max });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertRawDistributionToGenericDist(
 | 
			
		||||
  result: rescriptDist
 | 
			
		||||
): genericDist {
 | 
			
		||||
  switch (result.TAG) {
 | 
			
		||||
    case 0: // Point Set Dist
 | 
			
		||||
      switch (result._0.TAG) {
 | 
			
		||||
        case 0: // Mixed
 | 
			
		||||
          return tag("PointSet", tag("Mixed", result._0._0));
 | 
			
		||||
        case 1: // Discrete
 | 
			
		||||
          return tag("PointSet", tag("Discrete", result._0._0));
 | 
			
		||||
        case 2: // Continuous
 | 
			
		||||
          return tag("PointSet", tag("Continuous", result._0._0));
 | 
			
		||||
      }
 | 
			
		||||
    case 1: // Sample Set Dist
 | 
			
		||||
      return tag("SampleSet", result._0);
 | 
			
		||||
    case 2: // Symbolic Dist
 | 
			
		||||
      return tag("Symbolic", result._0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type jsValue =
 | 
			
		||||
  | string
 | 
			
		||||
  | number
 | 
			
		||||
  | jsValue[]
 | 
			
		||||
  | { [key: string]: jsValue }
 | 
			
		||||
  | boolean;
 | 
			
		||||
 | 
			
		||||
export function jsValueToBinding(value: jsValue): rescriptExport {
 | 
			
		||||
  if (typeof value === "boolean") {
 | 
			
		||||
    return { TAG: 2, _0: value as boolean };
 | 
			
		||||
  } else if (typeof value === "string") {
 | 
			
		||||
    return { TAG: 8, _0: value as string };
 | 
			
		||||
  } else if (typeof value === "number") {
 | 
			
		||||
    return { TAG: 6, _0: value as number };
 | 
			
		||||
  } else if (Array.isArray(value)) {
 | 
			
		||||
    return { TAG: 0, _0: value.map(jsValueToBinding) };
 | 
			
		||||
  } else {
 | 
			
		||||
    // Record
 | 
			
		||||
    return { TAG: 7, _0: _.mapValues(value, jsValueToBinding) };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function jsValueToExpressionValue(value: jsValue): expressionValue {
 | 
			
		||||
  if (typeof value === "boolean") {
 | 
			
		||||
    return { tag: "EvBool", value: value as boolean };
 | 
			
		||||
  } else if (typeof value === "string") {
 | 
			
		||||
    return { tag: "EvString", value: value as string };
 | 
			
		||||
  } else if (typeof value === "number") {
 | 
			
		||||
    return { tag: "EvNumber", value: value as number };
 | 
			
		||||
  } else if (Array.isArray(value)) {
 | 
			
		||||
    return { tag: "EvArray", value: value.map(jsValueToExpressionValue) };
 | 
			
		||||
  } else {
 | 
			
		||||
    // Record
 | 
			
		||||
    return {
 | 
			
		||||
      tag: "EvRecord",
 | 
			
		||||
      value: _.mapValues(value, jsValueToExpressionValue),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,30 +1,29 @@
 | 
			
		|||
export type result<a, b> =
 | 
			
		||||
  | {
 | 
			
		||||
      tag: "Ok";
 | 
			
		||||
      value: a;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      tag: "Error";
 | 
			
		||||
      value: b;
 | 
			
		||||
    };
 | 
			
		||||
import { result } from "../rescript/ForTS/ForTS_Result_tag";
 | 
			
		||||
export { result };
 | 
			
		||||
 | 
			
		||||
export function resultMap<a, b, c>(
 | 
			
		||||
  r: result<a, c>,
 | 
			
		||||
  mapFn: (x: a) => b
 | 
			
		||||
): result<b, c> {
 | 
			
		||||
  r: result<a, b>,
 | 
			
		||||
  mapValue: (x: a) => c
 | 
			
		||||
): result<c, b> {
 | 
			
		||||
  if (r.tag === "Ok") {
 | 
			
		||||
    return { tag: "Ok", value: mapFn(r.value) };
 | 
			
		||||
    return { tag: "Ok", value: mapValue(r.value) };
 | 
			
		||||
  } else {
 | 
			
		||||
    return r;
 | 
			
		||||
    return { tag: "Error", value: r.value };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function resultMap2<a, b, c, d>(
 | 
			
		||||
  r: result<a, b>,
 | 
			
		||||
  mapValue: (x: a) => c,
 | 
			
		||||
  mapError: (y: b) => d
 | 
			
		||||
): result<c, d> {
 | 
			
		||||
  if (r.tag === "Ok") {
 | 
			
		||||
    return { tag: "Ok", value: mapValue(r.value) };
 | 
			
		||||
  } else {
 | 
			
		||||
    return { tag: "Error", value: mapError(r.value) };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function Ok<a, b>(x: a): result<a, b> {
 | 
			
		||||
  return { tag: "Ok", value: x };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type tagged<a, b> = { tag: a; value: b };
 | 
			
		||||
 | 
			
		||||
export function tag<a, b>(x: a, y: b): tagged<a, b> {
 | 
			
		||||
  return { tag: x, value: y };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
@genType
 | 
			
		||||
//FIXME accessor methods or not opaque?
 | 
			
		||||
@genType.opaque
 | 
			
		||||
type genericDist =
 | 
			
		||||
  | PointSet(PointSetTypes.pointSetDist)
 | 
			
		||||
  | SampleSet(SampleSetDist.t)
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +7,7 @@ type genericDist =
 | 
			
		|||
 | 
			
		||||
type asAlgebraicCombinationStrategy = AsDefault | AsSymbolic | AsMonteCarlo | AsConvolution
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
@genType.opaque
 | 
			
		||||
type error =
 | 
			
		||||
  | NotYetImplemented
 | 
			
		||||
  | Unreachable
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +28,6 @@ module Error = {
 | 
			
		|||
 | 
			
		||||
  let fromString = (s: string): t => OtherError(s)
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
  let toString = (err: error): string =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | NotYetImplemented => "Function not yet implemented"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
//TODO: multimodal, add interface, test somehow, track performance, refactor sampleSet, refactor ASTEvaluator.res.
 | 
			
		||||
 | 
			
		||||
type t = DistributionTypes.genericDist
 | 
			
		||||
type error = DistributionTypes.error
 | 
			
		||||
type toPointSetFn = t => result<PointSetTypes.pointSetDist, error>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ type pointSetDistMonad<'a, 'b, 'c> =
 | 
			
		|||
  | Discrete('b)
 | 
			
		||||
  | Continuous('c)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
@genType.opaque
 | 
			
		||||
type pointSetDist = pointSetDistMonad<mixedShape, discreteShape, continuousShape>
 | 
			
		||||
 | 
			
		||||
module ShapeMonad = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
// Genetic Distribution happens to be abstract distribution
 | 
			
		||||
@genType type distribution = DistributionTypes.genericDist
 | 
			
		||||
@genType type distributionError = DistributionTypes.error
 | 
			
		||||
@genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution
 | 
			
		||||
@genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution
 | 
			
		||||
@genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution
 | 
			
		||||
 | 
			
		||||
type environment = ForTS_Distribution_Environment.environment //use
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let defaultEnvironment: environment = DistributionOperation.defaultEnv
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_tag") @scope("distributionTag")
 | 
			
		||||
external dtPointSet_: string = "PointSet"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_tag") @scope("distributionTag")
 | 
			
		||||
external dtSampleSet_: string = "SampleSet"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_tag") @scope("distributionTag")
 | 
			
		||||
external dtSymbolic_: string = "Symbolic"
 | 
			
		||||
 | 
			
		||||
@genType.import("./ForTS_Distribution_tag")
 | 
			
		||||
type distributionTag
 | 
			
		||||
 | 
			
		||||
external castEnum: string => distributionTag = "%identity"
 | 
			
		||||
 | 
			
		||||
// type genericDist =
 | 
			
		||||
//   | PointSet(PointSetTypes.pointSetDist)
 | 
			
		||||
//   | SampleSet(SampleSetDist.t)
 | 
			
		||||
//   | Symbolic(SymbolicDistTypes.symbolicDist)
 | 
			
		||||
@genType
 | 
			
		||||
let getTag = (variant: distribution): distributionTag =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | PointSet(_) => dtPointSet_->castEnum
 | 
			
		||||
  | SampleSet(_) => dtSampleSet_->castEnum
 | 
			
		||||
  | Symbolic(_) => dtSymbolic_->castEnum
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getPointSet = (variant: distribution): option<pointSetDistribution> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | PointSet(dist) => dist->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getSampleSet = (variant: distribution): option<sampleSetDistribution> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | SampleSet(dist) => dist->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getSymbolic = (variant: distribution): option<symbolicDistribution> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | Symbolic(dist) => dist->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let mean = DistributionOperation.Constructors.mean
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let stdev = DistributionOperation.Constructors.stdev
 | 
			
		||||
@genType
 | 
			
		||||
let variance = DistributionOperation.Constructors.variance
 | 
			
		||||
@genType
 | 
			
		||||
let sample = DistributionOperation.Constructors.sample
 | 
			
		||||
@genType
 | 
			
		||||
let cdf = DistributionOperation.Constructors.cdf
 | 
			
		||||
@genType
 | 
			
		||||
let inv = DistributionOperation.Constructors.inv
 | 
			
		||||
@genType
 | 
			
		||||
let pdf = DistributionOperation.Constructors.pdf
 | 
			
		||||
@genType
 | 
			
		||||
let normalize = DistributionOperation.Constructors.normalize
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toPointSet = (variant: distribution, env: environment) =>
 | 
			
		||||
  GenericDist.toPointSet(
 | 
			
		||||
    variant,
 | 
			
		||||
    ~sampleCount=env.sampleCount,
 | 
			
		||||
    ~xyPointLength=env.xyPointLength,
 | 
			
		||||
    (),
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (variant: distribution) => GenericDist.toString(variant)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
@genType type environment = GenericDist.env //re-export
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
@genType type distributionError = DistributionTypes.error
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (e: distributionError) => DistributionTypes.Error.toString(e)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
@genType type pointSetDistribution = PointSetTypes.pointSetDist
 | 
			
		||||
@genType type continuousShape = PointSetTypes.continuousShape
 | 
			
		||||
@genType type discreteShape = PointSetTypes.discreteShape
 | 
			
		||||
@genType type mixedShape = PointSetTypes.mixedShape
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
 | 
			
		||||
external pstMixed_: string = "Mixed"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
 | 
			
		||||
external pstDiscrete_: string = "Discrete"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
 | 
			
		||||
external pstContinuous_: string = "Continuous"
 | 
			
		||||
 | 
			
		||||
@genType.import("./ForTS_Distribution_PointSetDistribution_tag")
 | 
			
		||||
type pointSetDistributionTag
 | 
			
		||||
 | 
			
		||||
external castEnum: string => pointSetDistributionTag = "%identity"
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getTag = (variant: pointSetDistribution): pointSetDistributionTag =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | Mixed(_) => pstMixed_->castEnum
 | 
			
		||||
  | Discrete(_) => pstDiscrete_->castEnum
 | 
			
		||||
  | Continuous(_) => pstContinuous_->castEnum
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getMixed = (variant: pointSetDistribution): 'd =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | Mixed(mixed) => mixed->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getDiscrete = (variant: pointSetDistribution): 'd =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | Discrete(discrete) => discrete->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getContinues = (variant: pointSetDistribution): 'd =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | Continuous(continuous) => continuous->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toDistribution = (v: pointSetDistribution): DistributionTypes.genericDist => PointSet(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
export enum pointSetDistributionTag {
 | 
			
		||||
  Mixed = "Mixed",
 | 
			
		||||
  Discrete = "Discrete",
 | 
			
		||||
  Continuous = "Continuous",
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
@genType type sampleSetDistribution = SampleSetDist.t
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
@genType type symbolicDistribution = SymbolicDistTypes.symbolicDist
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
export enum distributionTag {
 | 
			
		||||
  PointSet = "PointSet",
 | 
			
		||||
  SampleSet = "SampleSet",
 | 
			
		||||
  Symbolic = "Symbolic",
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,240 @@
 | 
			
		|||
@genType type reducerProject = ReducerProject_T.t //re-export
 | 
			
		||||
 | 
			
		||||
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
 | 
			
		||||
 | 
			
		||||
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
 | 
			
		||||
type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //use
 | 
			
		||||
 | 
			
		||||
type environment = ForTS_Distribution_Environment.environment //use
 | 
			
		||||
 | 
			
		||||
module T = ReducerProject_T
 | 
			
		||||
module Private = ReducerProject.Private
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  PUBLIC FUNCTIONS
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
A project links and runs sources that continue or include each other.
 | 
			
		||||
 | 
			
		||||
Creates a new project to hold the sources, executables, bindings, and other data. 
 | 
			
		||||
The new project runs the sources according to their topological sorting because of the includes and continues.
 | 
			
		||||
 | 
			
		||||
Any source can include or continue other sources. "Therefore, the project is a graph data structure."
 | 
			
		||||
The difference between including and continuing is that includes are stated inside the source code while continues are stated in the project.
 | 
			
		||||
 | 
			
		||||
To run a group of source codes and get results/bindings, the necessary methods are
 | 
			
		||||
- setSource
 | 
			
		||||
- setContinues
 | 
			
		||||
- parseIncludes
 | 
			
		||||
- run or runAll
 | 
			
		||||
- getExternalBindings
 | 
			
		||||
- getExternalResult
 | 
			
		||||
 | 
			
		||||
A project has a public field tag with a constant value "reducerProject"
 | 
			
		||||
project = {tag: "reducerProject"}
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Answer all the source ids of all the sources in the project.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getSourceIds = (project: reducerProject): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getSourceIds
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Sets the source for a given source Id.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let setSource = (project: reducerProject, sourceId: string, value: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.setSource(sourceId, value)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Gets the source for a given source id.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getSource = (project: reducerProject, sourceId: string): option<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getSource(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Touches the source for a given source id. This and dependent, sources are set to be re-evaluated.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let touchSource = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.touchSource(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again.
 | 
			
		||||
 | 
			
		||||
Normally, you would never need the compilation artifacts again as the results with the same sources would never change. However, they are needed in case of any debugging reruns
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let clean = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.clean(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Cleans all the compilation artifacts in all of the project
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let cleanAll = (project: reducerProject): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.cleanAll
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Cleans results. Compilation stays untouched to be able to re-run the source.
 | 
			
		||||
You would not do this if you were not trying to debug the source code.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let cleanResults = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.cleanResults(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Cleans all results. Compilations remains untouched to rerun the source.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let cleanAllResults = (project: reducerProject): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.cleanAllResults
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getIncludes = (project: reducerProject, sourceId: string): result<
 | 
			
		||||
  array<string>,
 | 
			
		||||
  reducerErrorValue,
 | 
			
		||||
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId)
 | 
			
		||||
 | 
			
		||||
/* Other sources contributing to the global namespace of this source. */
 | 
			
		||||
@genType
 | 
			
		||||
let getPastChain = (project: reducerProject, sourceId: string): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getPastChain(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Answers the source codes after which this source code is continuing
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getContinues = (project: reducerProject, sourceId: string): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getContinues(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 "continues" acts like hidden includes in the source. 
 | 
			
		||||
 It is used to define a continuation that is not visible in the source code. 
 | 
			
		||||
 You can chain source codes on the web interface for example
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
This source depends on the array of sources returned.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getDependencies = (project: reducerProject, sourceId: string): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getDependencies(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
The sources returned are dependent on this
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getDependents = (project: reducerProject, sourceId: string): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getDependents(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Get the run order for the sources in the project.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getRunOrder = (project: reducerProject): array<string> =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getRunOrder
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Get the run order to get the results of this specific source
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getRunOrderFor = (project: reducerProject, sourceId: string) =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Parse includes so that you can load them before running. 
 | 
			
		||||
Load includes by calling getIncludes which returns the includes that have been parsed. 
 | 
			
		||||
It is your responsibility to load the includes before running.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let parseIncludes = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.parseIncludes(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Parse the source code if it is not done already. 
 | 
			
		||||
Use getRawParse to get the parse tree. 
 | 
			
		||||
You would need this function if you want to see the parse tree without running the source code.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let rawParse = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.rawParse(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Runs a specific source code if it is not done already. The code is parsed if it is not already done. It runs the dependencies if it is not already done.
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let run = (project: reducerProject, sourceId: string): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.run(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Runs all of the sources in a project. Their results and bindings will be available
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let runAll = (project: reducerProject): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.runAll
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Get the bindings after running this source fil. The bindings are local to the source
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Module =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.getBindings(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Get the result after running this source file or the project
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let getResult = (project: reducerProject, sourceId: string): result<
 | 
			
		||||
  squiggleValue,
 | 
			
		||||
  reducerErrorValue,
 | 
			
		||||
> => project->T.Private.castToInternalProject->Private.getResult(sourceId)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
This is a convenience function to get the result of a single source without creating a project. 
 | 
			
		||||
However, without a project, you cannot handle include directives.
 | 
			
		||||
The source has to be include free
 | 
			
		||||
*/
 | 
			
		||||
@genType
 | 
			
		||||
let evaluate = (sourceCode: string): (
 | 
			
		||||
  result<squiggleValue, reducerErrorValue>,
 | 
			
		||||
  squiggleValue_Module,
 | 
			
		||||
) => Private.evaluate(sourceCode)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let setEnvironment = (project: reducerProject, environment: environment): unit =>
 | 
			
		||||
  project->T.Private.castToInternalProject->Private.setEnvironment(environment)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Foreign function interface is intentionally demolished.
 | 
			
		||||
There is another way to do that: Umur.
 | 
			
		||||
Also there is no more conversion from javascript to squiggle values currently.
 | 
			
		||||
If the conversion to the new project is too difficult, I can add it later.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// let foreignFunctionInterface = (
 | 
			
		||||
//   lambdaValue: squiggleValue_Lambda,
 | 
			
		||||
//   argArray: array<squiggleValue>,
 | 
			
		||||
//   environment: environment,
 | 
			
		||||
// ): result<squiggleValue, reducerErrorValue> => {
 | 
			
		||||
//   let accessors = ReducerProject_ProjectAccessors_T.identityAccessorsWithEnvironment(environment)
 | 
			
		||||
//   Reducer_Expression_Lambda.foreignFunctionInterface(
 | 
			
		||||
//     lambdaValue,
 | 
			
		||||
//     argArray,
 | 
			
		||||
//     accessors,
 | 
			
		||||
//     Reducer_Expression.reduceExpressionInProject,
 | 
			
		||||
//   )
 | 
			
		||||
// }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
@genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias
 | 
			
		||||
@genType type syntaxErrorLocation = Reducer_ErrorValue.syntaxErrorLocation //alias
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (e: reducerErrorValue): string => Reducer_ErrorValue.errorToString(e)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> =>
 | 
			
		||||
  switch e {
 | 
			
		||||
  | RESyntaxError(_, optionalLocation) => optionalLocation
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let createTodoError = (v: string) => Reducer_ErrorValue.RETodo(v)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let createOtherError = (v: string) => Reducer_ErrorValue.REOther(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
export type result<a, b> =
 | 
			
		||||
  | {
 | 
			
		||||
      tag: "Ok";
 | 
			
		||||
      value: a;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      tag: "Error";
 | 
			
		||||
      value: b;
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,213 @@
 | 
			
		|||
@genType type squiggleValue = ReducerInterface_InternalExpressionValue.t //re-export
 | 
			
		||||
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
 | 
			
		||||
 | 
			
		||||
@genType type squiggleValue_Array = ReducerInterface_InternalExpressionValue.squiggleArray //re-export recursive type
 | 
			
		||||
@genType type squiggleValue_Module = ReducerInterface_InternalExpressionValue.nameSpace //re-export recursive type
 | 
			
		||||
@genType type squiggleValue_Record = ReducerInterface_InternalExpressionValue.map //re-export recursive type
 | 
			
		||||
@genType type squiggleValue_Type = ReducerInterface_InternalExpressionValue.map //re-export recursive type
 | 
			
		||||
type squiggleValue_Declaration = ForTS_SquiggleValue_Declaration.squiggleValue_Declaration //use
 | 
			
		||||
type squiggleValue_Distribution = ForTS_SquiggleValue_Distribution.squiggleValue_Distribution //use
 | 
			
		||||
type squiggleValue_Lambda = ForTS_SquiggleValue_Lambda.squiggleValue_Lambda //use
 | 
			
		||||
 | 
			
		||||
// Return values are kept as they are if they are JavaScript types.
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtArray_: string = "Array"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtArrayString_: string = "ArrayString"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtBool_: string = "Bool"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtCall_: string = "Call"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtDate_: string = "Date"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtDeclaration_: string = "Declaration"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtDistribution_: string = "Distribution"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtLambda_: string = "Lambda"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtModule_: string = "Module"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtNumber_: string = "Number"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtRecord_: string = "Record"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtString_: string = "String"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtSymbol_: string = "Symbol"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtTimeDuration_: string = "TimeDuration"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtType_: string = "Type"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtTypeIdentifier_: string = "TypeIdentifier"
 | 
			
		||||
 | 
			
		||||
@module("./ForTS_SquiggleValue_tag") @scope("squiggleValueTag")
 | 
			
		||||
external svtVoid_: string = "Void"
 | 
			
		||||
 | 
			
		||||
@genType.import("./ForTS_SquiggleValue_tag")
 | 
			
		||||
type squiggleValueTag
 | 
			
		||||
 | 
			
		||||
external castEnum: string => squiggleValueTag = "%identity"
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getTag = (variant: squiggleValue): squiggleValueTag =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvArray(_) => svtArray_->castEnum
 | 
			
		||||
  | IEvArrayString(_) => svtArrayString_->castEnum
 | 
			
		||||
  | IEvBool(_) => svtBool_->castEnum
 | 
			
		||||
  | IEvCall(_) => svtCall_->castEnum //Impossible
 | 
			
		||||
  | IEvDate(_) => svtDate_->castEnum
 | 
			
		||||
  | IEvDeclaration(_) => svtDeclaration_->castEnum
 | 
			
		||||
  | IEvDistribution(_) => svtDistribution_->castEnum
 | 
			
		||||
  | IEvLambda(_) => svtLambda_->castEnum
 | 
			
		||||
  | IEvBindings(_) => svtModule_->castEnum //Impossible
 | 
			
		||||
  | IEvNumber(_) => svtNumber_->castEnum
 | 
			
		||||
  | IEvRecord(_) => svtRecord_->castEnum
 | 
			
		||||
  | IEvString(_) => svtString_->castEnum
 | 
			
		||||
  | IEvSymbol(_) => svtSymbol_->castEnum
 | 
			
		||||
  | IEvTimeDuration(_) => svtTimeDuration_->castEnum
 | 
			
		||||
  | IEvType(_) => svtType_->castEnum
 | 
			
		||||
  | IEvTypeIdentifier(_) => svtTypeIdentifier_->castEnum
 | 
			
		||||
  | IEvVoid => svtVoid_->castEnum
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (variant: squiggleValue) =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.toString(variant)
 | 
			
		||||
 | 
			
		||||
// This is a useful method for unit tests.
 | 
			
		||||
// Convert the result along with the error message to a string.
 | 
			
		||||
@genType
 | 
			
		||||
let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.toStringResult(variantResult)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getArray = (variant: squiggleValue): option<squiggleValue_Array> =>
 | 
			
		||||
  //FIXME: Convert
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvArray(arrayLike) => arrayLike->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getArrayString = (variant: squiggleValue): option<array<string>> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvArrayString(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getBool = (variant: squiggleValue): option<bool> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvBool(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getCall = (variant: squiggleValue): option<string> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvCall(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getDate = (variant: squiggleValue): option<Js.Date.t> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvDate(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getDeclaration = (variant: squiggleValue): option<squiggleValue_Declaration> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvDeclaration(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getDistribution = (variant: squiggleValue): option<squiggleValue_Distribution> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvDistribution(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getLambda = (variant: squiggleValue): option<squiggleValue_Lambda> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvLambda(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getModule = (variant: squiggleValue): option<squiggleValue_Module> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvBindings(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getNumber = (variant: squiggleValue): option<float> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvNumber(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getRecord = (variant: squiggleValue): option<squiggleValue_Record> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvRecord(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getString = (variant: squiggleValue): option<string> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvString(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getSymbol = (variant: squiggleValue): option<string> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvSymbol(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getTimeDuration = (variant: squiggleValue): option<float> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvTimeDuration(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getType = (variant: squiggleValue): option<squiggleValue_Type> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvType(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getTypeIdentifier = (variant: squiggleValue): option<string> =>
 | 
			
		||||
  switch variant {
 | 
			
		||||
  | IEvTypeIdentifier(value) => value->Some
 | 
			
		||||
  | _ => None
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
type squiggleValue = ForTS_SquiggleValue.squiggleValue
 | 
			
		||||
@genType type squiggleValue_Array = ForTS_SquiggleValue.squiggleValue_Array //re-export recursive type
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let getValues = (v: squiggleValue_Array): array<squiggleValue> =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.arrayToValueArray(v)
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (v: squiggleValue_Array): string =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.toStringArray(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
@genType type squiggleValue_Declaration = ReducerInterface_InternalExpressionValue.lambdaDeclaration //re-export
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (v: squiggleValue_Declaration): string =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.toStringDeclaration(v)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
@genType type squiggleValue_Distribution = ForTS_Distribution.distribution
 | 
			
		||||
 | 
			
		||||
@genType
 | 
			
		||||
let toString = (v: squiggleValue_Distribution): string =>
 | 
			
		||||
  ReducerInterface_InternalExpressionValue.toStringDistribution(v)
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user