TS & components (WIP)

This commit is contained in:
Vyacheslav Matyukhin 2022-08-27 21:46:43 +04:00
parent 68ddf35387
commit 32cefb40da
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
33 changed files with 1348 additions and 678 deletions

View File

@ -2,10 +2,10 @@ import * as React from "react";
import { import {
Distribution, Distribution,
result, result,
distributionError, DistributionError,
distributionErrorToString, SquiggleValue,
squiggleExpression,
resultMap, resultMap,
SquiggleRecord,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { Vega } from "react-vega"; import { Vega } from "react-vega";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
@ -36,9 +36,7 @@ export function defaultPlot(distribution: Distribution): Plot {
return { distributions: [{ name: "default", distribution }] }; return { distributions: [{ name: "default", distribution }] };
} }
export function makePlot(record: { export function makePlot(record: SquiggleRecord): Plot | void {
[key: string]: squiggleExpression;
}): Plot | void {
const plotResult = parsePlot(record); const plotResult = parsePlot(record);
if (plotResult.tag === "Ok") { if (plotResult.tag === "Ok") {
return plotResult.value; return plotResult.value;
@ -50,18 +48,17 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
const [sized] = useSize((size) => { const [sized] = useSize((size) => {
let shapes = flattenResult( let shapes = flattenResult(
plot.distributions.map((x) => plot.distributions.map((x) =>
resultMap(x.distribution.pointSet(), (shape) => ({ resultMap(x.distribution.pointSet(), (pointSet) => ({
...pointSet.asShape(),
name: x.name, name: x.name,
// color: x.color, // not supported yet // color: x.color, // not supported yet
continuous: shape.continuous,
discrete: shape.discrete,
})) }))
) )
); );
if (shapes.tag === "Error") { if (shapes.tag === "Error") {
return ( return (
<ErrorAlert heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(shapes.value)} {shapes.value.toString()}
</ErrorAlert> </ErrorAlert>
); );
} }
@ -134,18 +131,18 @@ const SummaryTable: React.FC<SummaryTableProps> = ({ distribution }) => {
const p90 = distribution.inv(0.9); const p90 = distribution.inv(0.9);
const p95 = distribution.inv(0.95); const p95 = distribution.inv(0.95);
const hasResult = (x: result<number, distributionError>): boolean => const hasResult = (x: result<number, DistributionError>): boolean =>
x.tag === "Ok"; x.tag === "Ok";
const unwrapResult = ( const unwrapResult = (
x: result<number, distributionError> x: result<number, DistributionError>
): React.ReactNode => { ): React.ReactNode => {
if (x.tag === "Ok") { if (x.tag === "Ok") {
return <NumberShower number={x.value} />; return <NumberShower number={x.value} />;
} else { } else {
return ( return (
<ErrorAlert heading="Distribution Error"> <ErrorAlert heading="Distribution Error">
{distributionErrorToString(x.value)} {x.value.toString()}
</ErrorAlert> </ErrorAlert>
); );
} }

View File

@ -1,10 +1,5 @@
import * as React from "react"; import * as React from "react";
import { import { LambdaValue, environment, runForeign } from "@quri/squiggle-lang";
lambdaValue,
environment,
runForeign,
errorValueToString,
} from "@quri/squiggle-lang";
import { FunctionChart1Dist } from "./FunctionChart1Dist"; import { FunctionChart1Dist } from "./FunctionChart1Dist";
import { FunctionChart1Number } from "./FunctionChart1Number"; import { FunctionChart1Number } from "./FunctionChart1Number";
import { DistributionPlottingSettings } from "./DistributionChart"; import { DistributionPlottingSettings } from "./DistributionChart";
@ -17,7 +12,7 @@ export type FunctionChartSettings = {
}; };
interface FunctionChartProps { interface FunctionChartProps {
fn: lambdaValue; fn: LambdaValue;
chartSettings: FunctionChartSettings; chartSettings: FunctionChartSettings;
distributionPlotSettings: DistributionPlottingSettings; distributionPlotSettings: DistributionPlottingSettings;
environment: environment; environment: environment;

View File

@ -1,11 +1,7 @@
import * as React from "react"; import * as React from "react";
import { import {
squiggleExpression, SquiggleValue,
bindings,
environment, environment,
jsImports,
defaultImports,
defaultBindings,
defaultEnvironment, defaultEnvironment,
} from "@quri/squiggle-lang"; } from "@quri/squiggle-lang";
import { useSquiggle } from "../lib/hooks"; import { useSquiggle } from "../lib/hooks";
@ -27,14 +23,12 @@ export interface SquiggleChartProps {
/** If the result is a function, the amount of stops sampled */ /** If the result is a function, the amount of stops sampled */
diagramCount?: number; diagramCount?: number;
/** When the squiggle code gets reevaluated */ /** When the squiggle code gets reevaluated */
onChange?(expr: squiggleExpression | undefined): void; onChange?(expr: SquiggleValue | undefined): void;
/** CSS width of the element */ /** CSS width of the element */
width?: number; width?: number;
height?: number; height?: number;
/** Bindings of previous variables declared */
bindings?: bindings;
/** JS imported parameters */ /** JS imported parameters */
jsImports?: jsImports; // jsImports?: jsImports;
/** Whether to show a summary of the distribution */ /** Whether to show a summary of the distribution */
showSummary?: boolean; showSummary?: boolean;
/** Set the x scale to be logarithmic by deault */ /** Set the x scale to be logarithmic by deault */
@ -65,8 +59,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
environment, environment,
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
height = 200, height = 200,
bindings = defaultBindings, // jsImports = defaultImports,
jsImports = defaultImports,
showSummary = false, showSummary = false,
width, width,
logX = false, logX = false,
@ -84,9 +77,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
}) => { }) => {
const result = useSquiggle({ const result = useSquiggle({
code, code,
bindings,
environment, environment,
jsImports, // jsImports,
onChange, onChange,
executionId, executionId,
}); });

View File

@ -1,11 +1,8 @@
import React from "react"; import React from "react";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
import { environment, bindings, jsImports } from "@quri/squiggle-lang";
import { defaultImports, defaultBindings } from "@quri/squiggle-lang";
import { SquiggleContainer } from "./SquiggleContainer"; import { SquiggleContainer } from "./SquiggleContainer";
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
import { useSquigglePartial, useMaybeControlledValue } from "../lib/hooks"; import { useMaybeControlledValue } from "../lib/hooks";
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
const WrappedCodeEditor: React.FC<{ const WrappedCodeEditor: React.FC<{
code: string; code: string;
@ -42,51 +39,3 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
</SquiggleContainer> </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>
);
};

View File

@ -1,11 +1,11 @@
import { errorValue, errorValueToString } from "@quri/squiggle-lang"; import { ErrorValue } from "@quri/squiggle-lang";
import React from "react"; import React from "react";
import { ErrorAlert } from "./Alert"; import { ErrorAlert } from "./Alert";
type Props = { type Props = {
error: errorValue; error: ErrorValue;
}; };
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => { export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
return <ErrorAlert heading="Error">{errorValueToString(error)}</ErrorAlert>; return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>;
}; };

View File

@ -24,7 +24,7 @@ import {
} from "@heroicons/react/solid"; } from "@heroicons/react/solid";
import clsx from "clsx"; import clsx from "clsx";
import { defaultBindings, environment } from "@quri/squiggle-lang"; import { environment } from "@quri/squiggle-lang";
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart"; import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
import { CodeEditor } from "./CodeEditor"; import { CodeEditor } from "./CodeEditor";
@ -309,8 +309,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
executionId={executionId} executionId={executionId}
environment={env} environment={env}
{...vars} {...vars}
bindings={defaultBindings} // jsImports={imports}
jsImports={imports}
enableLocalSettings={true} enableLocalSettings={true}
/> />
</div> </div>

View File

@ -1,5 +1,9 @@
import React from "react"; import React from "react";
import { squiggleExpression, declaration } from "@quri/squiggle-lang"; import {
DistributionTag,
SquiggleValue,
SquiggleValueTag,
} from "@quri/squiggle-lang";
import { NumberShower } from "../NumberShower"; import { NumberShower } from "../NumberShower";
import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart"; import { DistributionChart, defaultPlot, makePlot } from "../DistributionChart";
import { FunctionChart, FunctionChartSettings } from "../FunctionChart"; import { FunctionChart, FunctionChartSettings } from "../FunctionChart";
@ -9,6 +13,8 @@ import { ItemSettingsMenu } from "./ItemSettingsMenu";
import { hasMassBelowZero } from "../../lib/distributionUtils"; import { hasMassBelowZero } from "../../lib/distributionUtils";
import { MergedItemSettings } from "./utils"; import { MergedItemSettings } from "./utils";
/*
// DISABLED FOR 0.4 branch, for now
function getRange<a>(x: declaration<a>) { function getRange<a>(x: declaration<a>) {
const first = x.args[0]; const first = x.args[0];
switch (first.tag) { switch (first.tag) {
@ -31,6 +37,7 @@ function getChartSettings<a>(x: declaration<a>): FunctionChartSettings {
count: 20, count: 20,
}; };
} }
*/
const VariableList: React.FC<{ const VariableList: React.FC<{
path: string[]; path: string[];
@ -48,7 +55,7 @@ const VariableList: React.FC<{
export interface Props { export interface Props {
/** The output of squiggle's run */ /** The output of squiggle's run */
expression: squiggleExpression; expression: SquiggleValue;
/** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */ /** Path to the current item, e.g. `['foo', 'bar', '3']` for `foo.bar[3]`; can be empty on the top-level item. */
path: string[]; path: string[];
width?: number; width?: number;
@ -67,7 +74,7 @@ export const ExpressionViewer: React.FC<Props> = ({
); );
} }
switch (expression.tag) { switch (expression.tag) {
case "number": case SquiggleValueTag.SvtNumber:
return ( return (
<VariableBox path={path} heading="Number"> <VariableBox path={path} heading="Number">
{() => ( {() => (
@ -77,13 +84,15 @@ export const ExpressionViewer: React.FC<Props> = ({
)} )}
</VariableBox> </VariableBox>
); );
case "distribution": { case SquiggleValueTag.SvtDistribution: {
const distType = expression.value.type(); const distType = expression.value.tag;
return ( return (
<VariableBox <VariableBox
path={path} path={path}
heading={`Distribution (${distType})\n${ heading={`Distribution (${distType})\n${
distType === "Symbolic" ? expression.value.toString() : "" distType === DistributionTag.DtSymbolic
? expression.value.toString()
: ""
}`} }`}
renderSettingsMenu={({ onChange }) => { renderSettingsMenu={({ onChange }) => {
const shape = expression.value.pointSet(); const shape = expression.value.pointSet();
@ -112,7 +121,7 @@ export const ExpressionViewer: React.FC<Props> = ({
</VariableBox> </VariableBox>
); );
} }
case "string": case SquiggleValueTag.SvtString:
return ( return (
<VariableBox path={path} heading="String"> <VariableBox path={path} heading="String">
{() => ( {() => (
@ -126,13 +135,13 @@ export const ExpressionViewer: React.FC<Props> = ({
)} )}
</VariableBox> </VariableBox>
); );
case "boolean": case SquiggleValueTag.SvtBool:
return ( return (
<VariableBox path={path} heading="Boolean"> <VariableBox path={path} heading="Boolean">
{() => expression.value.toString()} {() => expression.value.toString()}
</VariableBox> </VariableBox>
); );
case "symbol": case SquiggleValueTag.SvtSymbol:
return ( return (
<VariableBox path={path} heading="Symbol"> <VariableBox path={path} heading="Symbol">
{() => ( {() => (
@ -143,38 +152,38 @@ export const ExpressionViewer: React.FC<Props> = ({
)} )}
</VariableBox> </VariableBox>
); );
case "call": case SquiggleValueTag.SvtCall:
return ( return (
<VariableBox path={path} heading="Call"> <VariableBox path={path} heading="Call">
{() => expression.value} {() => expression.value}
</VariableBox> </VariableBox>
); );
case "arraystring": case SquiggleValueTag.SvtArrayString:
return ( return (
<VariableBox path={path} heading="Array String"> <VariableBox path={path} heading="Array String">
{() => expression.value.map((r) => `"${r}"`).join(", ")} {() => expression.value.map((r) => `"${r}"`).join(", ")}
</VariableBox> </VariableBox>
); );
case "date": case SquiggleValueTag.SvtDate:
return ( return (
<VariableBox path={path} heading="Date"> <VariableBox path={path} heading="Date">
{() => expression.value.toDateString()} {() => expression.value.toDateString()}
</VariableBox> </VariableBox>
); );
case "void": case SquiggleValueTag.SvtVoid:
return ( return (
<VariableBox path={path} heading="Void"> <VariableBox path={path} heading="Void">
{() => "Void"} {() => "Void"}
</VariableBox> </VariableBox>
); );
case "timeDuration": { case SquiggleValueTag.SvtTimeDuration: {
return ( return (
<VariableBox path={path} heading="Time Duration"> <VariableBox path={path} heading="Time Duration">
{() => <NumberShower precision={3} number={expression.value} />} {() => <NumberShower precision={3} number={expression.value} />}
</VariableBox> </VariableBox>
); );
} }
case "lambda": case SquiggleValueTag.SvtLambda:
return ( return (
<VariableBox <VariableBox
path={path} path={path}
@ -208,7 +217,7 @@ export const ExpressionViewer: React.FC<Props> = ({
)} )}
</VariableBox> </VariableBox>
); );
case "lambdaDeclaration": { case SquiggleValueTag.SvtDeclaration: {
return ( return (
<VariableBox <VariableBox
path={path} path={path}
@ -224,21 +233,22 @@ export const ExpressionViewer: React.FC<Props> = ({
}} }}
> >
{(settings) => ( {(settings) => (
<FunctionChart <div>NOT IMPLEMENTED IN 0.4 YET</div>
fn={expression.value.fn} // <FunctionChart
chartSettings={getChartSettings(expression.value)} // fn={expression.value.fn}
distributionPlotSettings={settings.distributionPlotSettings} // chartSettings={getChartSettings(expression.value)}
height={settings.height} // distributionPlotSettings={settings.distributionPlotSettings}
environment={{ // height={settings.height}
sampleCount: settings.environment.sampleCount / 10, // environment={{
xyPointLength: settings.environment.xyPointLength / 10, // sampleCount: settings.environment.sampleCount / 10,
}} // xyPointLength: settings.environment.xyPointLength / 10,
/> // }}
// />
)} )}
</VariableBox> </VariableBox>
); );
} }
case "module": { case SquiggleValueTag.SvtModule: {
return ( return (
<VariableList path={path} heading="Module"> <VariableList path={path} heading="Module">
{(_) => {(_) =>
@ -256,7 +266,7 @@ export const ExpressionViewer: React.FC<Props> = ({
</VariableList> </VariableList>
); );
} }
case "record": case SquiggleValueTag.SvtRecord:
const plot = makePlot(expression.value); const plot = makePlot(expression.value);
if (plot) { if (plot) {
return ( return (
@ -308,11 +318,13 @@ export const ExpressionViewer: React.FC<Props> = ({
</VariableList> </VariableList>
); );
} }
case "array": case SquiggleValueTag.SvtArray:
return ( return (
<VariableList path={path} heading="Array"> <VariableList path={path} heading="Array">
{(_) => {(_) =>
expression.value.map((r, i) => ( expression.value
.getValues()
.map((r, i) => (
<ExpressionViewer <ExpressionViewer
key={i} key={i}
path={[...path, String(i)]} path={[...path, String(i)]}

View File

@ -1,7 +1,5 @@
export { SquiggleChart } from "./components/SquiggleChart"; export { SquiggleChart } from "./components/SquiggleChart";
export { SquiggleEditor, SquigglePartial } from "./components/SquiggleEditor"; export { SquiggleEditor } from "./components/SquiggleEditor";
export { SquigglePlayground } from "./components/SquigglePlayground"; export { SquigglePlayground } from "./components/SquigglePlayground";
export { SquiggleContainer } from "./components/SquiggleContainer"; export { SquiggleContainer } from "./components/SquiggleContainer";
export { SquiggleEditorWithImportedBindings } from "./components/SquiggleEditorWithImportedBindings"; export { SquiggleEditorWithImportedBindings } from "./components/SquiggleEditorWithImportedBindings";
export { mergeBindings } from "@quri/squiggle-lang";

View File

@ -1,3 +1,3 @@
export { useMaybeControlledValue } from "./useMaybeControlledValue"; export { useMaybeControlledValue } from "./useMaybeControlledValue";
export { useSquiggle, useSquigglePartial } from "./useSquiggle"; export { useSquiggle } from "./useSquiggle";
export { useRunnerState } from "./useRunnerState"; export { useRunnerState } from "./useRunnerState";

View File

@ -1,34 +1,25 @@
import { import { environment, run, SquiggleValue } from "@quri/squiggle-lang";
bindings,
environment,
jsImports,
run,
runPartial,
} from "@quri/squiggle-lang";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
type SquiggleArgs<T extends ReturnType<typeof run | typeof runPartial>> = { type SquiggleArgs = {
code: string; code: string;
executionId?: number; executionId?: number;
bindings?: bindings; // jsImports?: jsImports;
jsImports?: jsImports;
environment?: environment; environment?: environment;
onChange?: (expr: Extract<T, { tag: "Ok" }>["value"] | undefined) => void; onChange?: (expr: SquiggleValue | undefined) => void;
}; };
const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>( export const useSquiggle = (args: SquiggleArgs) => {
args: SquiggleArgs<T>, const result = useMemo(
f: (...args: Parameters<typeof run>) => T () => {
) => { const { result } = run(args.code, { environment: args.environment });
const result: T = useMemo<T>( return result;
() => f(args.code, args.bindings, args.environment, args.jsImports), },
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[ [
f,
args.code, args.code,
args.bindings, // args.environment,
args.environment, // args.jsImports,
args.jsImports,
args.executionId, args.executionId,
] ]
); );
@ -41,13 +32,3 @@ const useSquiggleAny = <T extends ReturnType<typeof run | typeof runPartial>>(
return result; 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);
};

View File

@ -1,5 +1,5 @@
import * as yup from "yup"; import * as yup from "yup";
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang"; import { Distribution, result, SquiggleRecord } from "@quri/squiggle-lang";
export type LabeledDistribution = { export type LabeledDistribution = {
name: string; name: string;
@ -53,9 +53,7 @@ const schema = yup
}), }),
}); });
export function parsePlot(record: { export function parsePlot(record: SquiggleRecord): result<Plot, string> {
[key: string]: squiggleExpression;
}): result<Plot, string> {
try { try {
const plotRecord = schema.validateSync(record); const plotRecord = schema.validateSync(record);
return ok({ return ok({

View File

@ -1,10 +1,6 @@
import { import { Project, SquiggleValue } from "../../src/js";
Distribution, import { NumberValue } from "../../src/js/SquiggleValue";
resultMap, import { failDefault, testRun } from "./TestHelpers";
defaultBindings,
mergeBindings,
} from "../../src/js/index";
import { testRun, testRunPartial } from "./TestHelpers";
function Ok<b>(x: b) { function Ok<b>(x: b) {
return { tag: "Ok", value: x }; return { tag: "Ok", value: x };
@ -12,150 +8,175 @@ function Ok<b>(x: b) {
describe("Simple calculations and results", () => { describe("Simple calculations and results", () => {
test("mean(normal(5,2))", () => { test("mean(normal(5,2))", () => {
expect(testRun("mean(normal(5,2))")).toEqual({ const result = testRun("mean(normal(5,2))"); // FIXME
tag: "number", expect(result.tag).toEqual("Number");
value: 5, switch (result.tag) {
}); case "Number":
expect(result.value()).toEqual(5);
break;
default:
fail();
}
// tag: "number",
// value: 5,
// });
}); });
test("10+10", () => { test("10+10", () => {
let foo = testRun("10 + 10"); let result = testRun("10 + 10") as NumberValue;
expect(foo).toEqual({ tag: "number", value: 20 }); expect(result.tag).toEqual("Number");
}); switch (result.tag) {
}); case "Number":
describe("Log function", () => { expect(result.value()).toEqual(20);
test("log(1) = 0", () => { break;
let foo = testRun("log(1)"); default:
expect(foo).toEqual({ tag: "number", value: 0 }); fail();
}
}); });
}); });
// describe("Log function", () => {
// test("log(1) = 0", () => {
// let foo = testRun("log(1)");
// expect(foo).toEqual({ tag: "number", value: 0 });
// });
// });
describe("Array", () => { // describe("Array", () => {
test("nested Array", () => { // test("nested Array", () => {
expect(testRun("[[1]]")).toEqual({ // expect(testRun("[[1]]")).toEqual({
tag: "array", // tag: "array",
value: [ // value: [
{ // {
tag: "array", // tag: "array",
value: [ // value: [
{ // {
tag: "number", // tag: "number",
value: 1, // value: 1,
}, // },
], // ],
}, // },
], // ],
}); // });
}); // });
}); // });
describe("Record", () => { // describe("Record", () => {
test("Return record", () => { // test("Return record", () => {
expect(testRun("{a: 1}")).toEqual({ // expect(testRun("{a: 1}")).toEqual({
tag: "record", // tag: "record",
value: { // value: {
a: { // a: {
tag: "number", // tag: "number",
value: 1, // value: 1,
}, // },
}, // },
}); // });
}); // });
}); // });
describe("Partials", () => { // describe("Partials", () => {
test("Can pass variables between partials and cells", () => { // test("Can pass variables between partials and cells", () => {
let bindings = testRunPartial(`x = 5`); // const project = Project.create();
let bindings2 = testRunPartial(`y = x + 2`, bindings); // project.setSource("p1", "x = 5");
expect(testRun(`y + 3`, bindings2)).toEqual({ // project.setSource("p2", "y = x + 2");
tag: "number", // project.setSource("main", "y + 3");
value: 10, // project.run("main");
}); // const result = project.getResult("main");
}); // expect(result.tag).toEqual("Ok");
test("Can merge bindings from three partials", () => { // expect(result.value).toBeInstanceOf(SquiggleValue);
let bindings1 = testRunPartial(`x = 1`); // expect(result.value).toHaveProperty("tag", "number");
let bindings2 = testRunPartial(`y = 2`); // failDefault(); // FIXME
let bindings3 = testRunPartial(`z = 3`); // // let bindings = testRunPartial(`x = 5`);
expect( // // let bindings2 = testRunPartial(`y = x + 2`, bindings);
testRun(`x + y + z`, mergeBindings([bindings1, bindings2, bindings3])) // // expect(testRun(`y + 3`, bindings2)).toEqual({
).toEqual({ // // tag: "number",
tag: "number", // // value: 10,
value: 6, // // });
}); // });
}); // 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", () => { // describe("JS Imports", () => {
test("Can pass parameters into partials and cells", () => { // test("Can pass parameters into partials and cells", () => {
let bindings = testRunPartial(`y = $x + 2`, defaultBindings, { x: 1 }); // let bindings = testRunPartial(`y = $x + 2`, defaultBindings, { x: 1 });
let bindings2 = testRunPartial(`z = y + $a`, bindings, { a: 3 }); // let bindings2 = testRunPartial(`z = y + $a`, bindings, { a: 3 });
expect(testRun(`z`, bindings2)).toEqual({ // expect(testRun(`z`, bindings2)).toEqual({
tag: "number", // tag: "number",
value: 6, // value: 6,
}); // });
}); // });
test("Complicated deep parameters", () => { // test("Complicated deep parameters", () => {
expect( // expect(
testRun(`$x.y[0][0].w + $x.z + $u.v`, defaultBindings, { // testRun(`$x.y[0][0].w + $x.z + $u.v`, defaultBindings, {
x: { y: [[{ w: 1 }]], z: 2 }, // x: { y: [[{ w: 1 }]], z: 2 },
u: { v: 3 }, // u: { v: 3 },
}) // })
).toEqual({ // ).toEqual({
tag: "number", // tag: "number",
value: 6, // value: 6,
}); // });
}); // });
}); // });
describe("Distribution", () => { // describe("Distribution", () => {
//It's important that sampleCount is less than 9. If it's more, than that will create randomness // //It's important that sampleCount is less than 9. If it's more, than that will create randomness
//Also, note, the value should be created using makeSampleSetDist() later on. // //Also, note, the value should be created using makeSampleSetDist() later on.
let env = { sampleCount: 8, xyPointLength: 100 }; // let env = { sampleCount: 8, xyPointLength: 100 };
let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30]; // let dist1Samples = [3, 4, 5, 6, 6, 7, 10, 15, 30];
let dist1SampleCount = dist1Samples.length; // let dist1SampleCount = dist1Samples.length;
let dist = new Distribution( // let dist = new Distribution(
{ tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] }, // { tag: "SampleSet", value: [3, 4, 5, 6, 6, 7, 10, 15, 30] },
env // env
); // );
let dist2 = new Distribution( // let dist2 = new Distribution(
{ tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] }, // { tag: "SampleSet", value: [20, 22, 24, 29, 30, 35, 38, 44, 52] },
env // env
); // );
test("mean", () => { // test("mean", () => {
expect(dist.mean().value).toBeCloseTo(9.5555555); // expect(dist.mean().value).toBeCloseTo(9.5555555);
}); // });
test("pdf", () => { // test("pdf", () => {
expect(dist.pdf(5.0).value).toBeCloseTo(0.10499097598222966, 1); // expect(dist.pdf(5.0).value).toBeCloseTo(0.10499097598222966, 1);
}); // });
test("cdf", () => { // test("cdf", () => {
expect(dist.cdf(5.0).value).toBeCloseTo( // expect(dist.cdf(5.0).value).toBeCloseTo(
dist1Samples.filter((x) => x <= 5).length / dist1SampleCount, // dist1Samples.filter((x) => x <= 5).length / dist1SampleCount,
1 // 1
); // );
}); // });
test("inv", () => { // test("inv", () => {
expect(dist.inv(0.5).value).toBeCloseTo(6); // expect(dist.inv(0.5).value).toBeCloseTo(6);
}); // });
test("toPointSet", () => { // test("toPointSet", () => {
expect( // expect(
resultMap(dist.toPointSet(), (r: Distribution) => r.toString()) // resultMap(dist.toPointSet(), (r: Distribution) => r.toString())
).toEqual(Ok("Point Set Distribution")); // ).toEqual(Ok("Point Set Distribution"));
}); // });
test("toSparkline", () => { // test("toSparkline", () => {
expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁"); // expect(dist.toSparkline(20).value).toEqual("▁▁▃▇█▇▄▂▂▂▁▁▁▁▁▂▂▁▁▁");
}); // });
test("algebraicAdd", () => { // test("algebraicAdd", () => {
expect( // expect(
resultMap(dist.algebraicAdd(dist2), (r: Distribution) => // resultMap(dist.algebraicAdd(dist2), (r: Distribution) =>
r.toSparkline(20) // r.toSparkline(20)
).value // ).value
).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁")); // ).toEqual(Ok("▁▁▂▄▆████▇▆▄▄▃▃▃▂▁▁▁"));
}); // });
test("pointwiseAdd", () => { // test("pointwiseAdd", () => {
expect( // expect(
resultMap(dist.pointwiseAdd(dist2), (r: Distribution) => // resultMap(dist.pointwiseAdd(dist2), (r: Distribution) =>
r.toSparkline(20) // r.toSparkline(20)
).value // ).value
).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁")); // ).toEqual(Ok("▁▂██▃▃▃▃▄▅▄▃▃▂▂▂▁▁▁▁"));
}); // });
}); // });

View File

@ -1,60 +1,21 @@
import { import { run, SquiggleValue } from "../../src/js/index";
run,
runPartial,
bindings,
squiggleExpression,
errorValueToString,
defaultImports,
defaultBindings,
jsImports,
} from "../../src/js/index";
export function testRun( export function testRun(x: string) {
x: string, const { result, bindings } = run(x); // FIXME - set environment
bindings: bindings = defaultBindings, // x,
imports: jsImports = defaultImports // bindings,
): squiggleExpression { // {
let squiggleResult = run( // sampleCount: 1000,
x, // xyPointLength: 100,
bindings, // },
{ // imports
sampleCount: 1000, // );
xyPointLength: 100,
}, if (result.tag === "Ok") {
imports return result.value;
);
if (squiggleResult.tag === "Ok") {
return squiggleResult.value;
} else { } else {
throw new Error( throw new Error(
`Expected squiggle expression to evaluate but got error: ${errorValueToString( `Expected squiggle expression to evaluate but got error: ${result.value}`
squiggleResult.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
)}`
); );
} }
} }

View File

@ -0,0 +1,93 @@
import * as RSDistribution from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
import { distributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_tag";
import { DistributionError } from "./DistributionError";
import { wrapPointSetDist } from "./PointSetDist";
import { resultMap2 } from "./types";
type T = RSDistribution.distribution;
export { Tag };
export const wrapDistribution = (value: T): Distribution => {
const tag = RSDistribution.getTag(value);
return new tagToClass[tag](value);
};
abstract class AbstractDistribution {
abstract tag: Tag;
_value: T;
constructor(value: T) {
this._value = value;
}
pointSet() {
const env: any = "TODO";
const innerResult = RSDistribution.toPointSet(this._value, env);
return resultMap2(
innerResult,
wrapPointSetDist,
(v: RSDistribution.distributionError) => new DistributionError(v)
);
}
toString() {
RSDistribution.toString(this._value);
}
mean() {
return RSDistribution.mean(this._value);
}
inv(n: number) {
return RSDistribution.inv(this._value, n);
}
stdev() {
return RSDistribution.stdev(this._value);
}
}
const valueMethod = <IR>(
_this: AbstractDistribution,
rsMethod: (v: T) => IR | null | undefined
) => {
const value = rsMethod(_this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
export class PointSetDistribution extends AbstractDistribution {
tag = Tag.DtPointSet;
value() {
return valueMethod(this, RSDistribution.getPointSet);
}
}
export class SampleSetDistribution extends AbstractDistribution {
tag = Tag.DtSampleSet;
value() {
return valueMethod(this, RSDistribution.getSampleSet);
}
}
export class SymbolicDistribution extends AbstractDistribution {
tag = Tag.DtSymbolic;
value() {
return valueMethod(this, RSDistribution.getSymbolic);
}
}
const tagToClass = {
[Tag.DtPointSet]: PointSetDistribution,
[Tag.DtSampleSet]: SampleSetDistribution,
[Tag.DtSymbolic]: SymbolicDistribution,
} as const;
export type Distribution =
| PointSetDistribution
| SampleSetDistribution
| SymbolicDistribution;

View File

@ -0,0 +1,15 @@
import * as RSDistributionError from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Error.gen";
type T = RSDistributionError.distributionError;
export class DistributionError {
_value: T;
constructor(_value: T) {
this._value = _value;
}
toString() {
return RSDistributionError.toString(this._value);
}
}

View File

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

View File

@ -0,0 +1,11 @@
import * as RSLambda from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Lambda.gen";
type T = RSLambda.squiggleValue_Lambda;
export class Lambda {
_value: T;
constructor(_value: T) {
this._value = _value;
}
}

View File

@ -0,0 +1,11 @@
import * as RSDeclaration from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Declaration.gen";
type T = RSDeclaration.squiggleValue_Declaration;
export class LambdaDeclaration {
_value: T;
constructor(_value: T) {
this._value = _value;
}
}

View File

@ -0,0 +1,9 @@
import * as RSModuleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Module.gen";
export class NameSpace {
_value: RSModuleValue.squiggleValue_Module;
constructor(_value: RSModuleValue.squiggleValue_Module) {
this._value = _value;
}
}

View File

@ -0,0 +1,103 @@
import * as _ from "lodash";
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 point = { x: number; y: number };
type shape = {
continuous: point[];
discrete: point[];
};
const shapePoints = (
x: RSPointSetDist.continuousShape | RSPointSetDist.discreteShape
): point[] => {
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 AbstractPointSetDist {
_value: T;
constructor(_value: T) {
this._value = _value;
}
abstract asShape(): shape;
}
const valueMethod = <IR>(
_this: AbstractPointSetDist,
rsMethod: (v: T) => IR | null | undefined
) => {
const value = rsMethod(_this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
export class MixedPointSetDist extends AbstractPointSetDist {
tag = Tag.PstMixed as const;
get value(): RSPointSetDist.mixedShape {
return valueMethod(this, RSPointSetDist.getMixed);
}
asShape() {
const v = this.value;
return {
discrete: shapePoints(v.discrete),
continuous: shapePoints(v.continuous),
};
}
}
export class DiscretePointSetDist extends AbstractPointSetDist {
tag = Tag.PstDiscrete as const;
get value(): RSPointSetDist.discreteShape {
return valueMethod(this, RSPointSetDist.getDiscrete);
}
asShape() {
const v = this.value;
return {
discrete: shapePoints(v),
continuous: [],
};
}
}
export class ContinuousPointSetDist extends AbstractPointSetDist {
tag = Tag.PstContinuous as const;
get value(): RSPointSetDist.continuousShape {
return valueMethod(this, RSPointSetDist.getContinues);
}
asShape() {
const v = this.value;
return {
discrete: [],
continuous: shapePoints(v),
};
}
}
const tagToClass = {
[Tag.PstMixed]: MixedPointSetDist,
[Tag.PstDiscrete]: DiscretePointSetDist,
[Tag.PstContinuous]: ContinuousPointSetDist,
} as const;
export type PointSetDist =
| MixedPointSetDist
| DiscretePointSetDist
| ContinuousPointSetDist;

View File

@ -0,0 +1,112 @@
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 { ErrorValue } from "./ErrorValue";
import { NameSpace as NameSpace } from "./NameSpace";
import { wrapSquiggleValue } from "./SquiggleValue";
import { resultMap2 } from "./types";
export class Project {
_value: RSProject.reducerProject;
constructor(_value: RSProject.reducerProject) {
this._value = _value;
}
static create() {
return new Project(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 ErrorValue(v)
);
}
getContinues(sourceId: string) {
return RSProject.getContinues(this._value, sourceId);
}
setContinues(sourceId: string, continues: string[]) {
return RSProject.setContinues(this._value, sourceId, continues);
}
getDependencies(sourceId: string) {
return RSProject.getDependencies(this._value, sourceId);
}
getDependents(sourceId: string) {
return RSProject.getDependents(this._value, sourceId);
}
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 NameSpace(RSProject.getBindings(this._value, sourceId));
}
getResult(sourceId: string) {
const innerResult = RSProject.getResult(this._value, sourceId);
return resultMap2(
innerResult,
wrapSquiggleValue,
(v: reducerErrorValue) => new ErrorValue(v)
);
}
setEnvironment(environment: environment) {
RSProject.setEnvironment(this._value, environment);
}
}

View File

@ -0,0 +1,18 @@
import * as RSRecord from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Record.gen";
import { AbstractSquiggleValue, wrapSquiggleValue } from "./SquiggleValue";
type T = RSRecord.squiggleValue_Record;
export class Record {
_value: T;
constructor(_value: T) {
this._value = _value;
}
entries() {
return RSRecord.getKeyValuePairs(this._value).map(
([k, v]) => [k, wrapSquiggleValue(v)] as const
);
}
}

View File

@ -0,0 +1,16 @@
import * as RSArray from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Array.gen";
import { AbstractSquiggleValue, wrapSquiggleValue } from "./SquiggleValue";
type T = RSArray.squiggleValue_Array;
export class SquiggleArray {
_value: T;
constructor(_value: T) {
this._value = _value;
}
getValues() {
return RSArray.getValues(this._value).map(wrapSquiggleValue);
}
}

View File

@ -0,0 +1,212 @@
import * as RSSquiggleValue from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.gen";
import { squiggleValueTag as Tag } from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_tag";
import { Distribution, wrapDistribution } from "./Distribution";
import { Lambda } from "./Lambda";
import { LambdaDeclaration } from "./LambdaDeclaration";
import { NameSpace } from "./NameSpace";
import { Record } from "./Record";
import { SquiggleArray } from "./SquiggleArray";
import { Type } from "./Type";
export { Tag };
type T = RSSquiggleValue.squiggleValue;
export const wrapSquiggleValue = (value: T): SquiggleValue => {
const tag = RSSquiggleValue.getTag(value);
return new tagToClass[tag](value);
};
export abstract class AbstractSquiggleValue {
abstract tag: Tag;
_value: T;
constructor(value: T) {
this._value = value;
}
}
const valueMethod = <IR>(
_this: AbstractSquiggleValue,
rsMethod: (v: T) => IR | null | undefined
) => {
const value = rsMethod(_this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
export class ArrayValue extends AbstractSquiggleValue {
tag = Tag.SvtArray as const;
get value() {
return new SquiggleArray(valueMethod(this, RSSquiggleValue.getArray));
}
}
export class ArrayStringValue extends AbstractSquiggleValue {
tag = Tag.SvtArrayString as const;
get value() {
return valueMethod(this, RSSquiggleValue.getArrayString);
}
}
export class BoolValue extends AbstractSquiggleValue {
tag = Tag.SvtBool as const;
get value() {
return valueMethod(this, RSSquiggleValue.getBool);
}
}
export class CallValue extends AbstractSquiggleValue {
tag = Tag.SvtCall as const;
get value() {
return valueMethod(this, RSSquiggleValue.getCall);
}
}
export class DateValue extends AbstractSquiggleValue {
tag = Tag.SvtDate as const;
get value() {
return valueMethod(this, RSSquiggleValue.getDate);
}
}
export class DeclarationValue extends AbstractSquiggleValue {
tag = Tag.SvtDeclaration as const;
get value() {
return new LambdaDeclaration(
valueMethod(this, RSSquiggleValue.getDeclaration)
);
}
}
export class DistributionValue extends AbstractSquiggleValue {
tag = Tag.SvtDistribution as const;
get value() {
return wrapDistribution(valueMethod(this, RSSquiggleValue.getDistribution));
}
}
export class LambdaValue extends AbstractSquiggleValue {
tag = Tag.SvtLambda as const;
get value() {
return new Lambda(valueMethod(this, RSSquiggleValue.getLambda));
}
}
export class ModuleValue extends AbstractSquiggleValue {
tag = Tag.SvtModule as const;
get value() {
return new NameSpace(valueMethod(this, RSSquiggleValue.getModule));
}
}
export class NumberValue extends AbstractSquiggleValue {
tag = Tag.SvtNumber as const;
get value() {
return valueMethod(this, RSSquiggleValue.getNumber);
}
}
export class RecordValue extends AbstractSquiggleValue {
tag = Tag.SvtRecord as const;
get value() {
return new Record(valueMethod(this, RSSquiggleValue.getRecord));
}
}
export class StringValue extends AbstractSquiggleValue {
tag = Tag.SvtString as const;
get value(): string {
return valueMethod(this, RSSquiggleValue.getString);
}
}
export class SymbolValue extends AbstractSquiggleValue {
tag = Tag.SvtSymbol as const;
get value(): string {
return valueMethod(this, RSSquiggleValue.getSymbol);
}
}
export class TimeDurationValue extends AbstractSquiggleValue {
tag = Tag.SvtTimeDuration as const;
get value() {
return valueMethod(this, RSSquiggleValue.getTimeDuration);
}
}
export class TypeValue extends AbstractSquiggleValue {
tag = Tag.SvtType as const;
get value() {
return new Type(valueMethod(this, RSSquiggleValue.getType));
}
}
export class TypeIdentifierValue extends AbstractSquiggleValue {
tag = Tag.SvtTypeIdentifier as const;
get value() {
return valueMethod(this, RSSquiggleValue.getTypeIdentifier);
}
}
export class VoidValue extends AbstractSquiggleValue {
tag = Tag.SvtVoid as const;
}
const tagToClass = {
[Tag.SvtArray]: ArrayValue,
[Tag.SvtArrayString]: ArrayStringValue,
[Tag.SvtBool]: BoolValue,
[Tag.SvtCall]: CallValue,
[Tag.SvtDate]: DateValue,
[Tag.SvtDeclaration]: DeclarationValue,
[Tag.SvtDistribution]: DistributionValue,
[Tag.SvtLambda]: LambdaValue,
[Tag.SvtModule]: ModuleValue,
[Tag.SvtNumber]: NumberValue,
[Tag.SvtRecord]: RecordValue,
[Tag.SvtString]: StringValue,
[Tag.SvtSymbol]: SymbolValue,
[Tag.SvtTimeDuration]: TimeDurationValue,
[Tag.SvtType]: TypeValue,
[Tag.SvtTypeIdentifier]: TypeIdentifierValue,
[Tag.SvtVoid]: VoidValue,
} as const;
// FIXME
// type AnySquiggleValue = typeof tagToClass[keyof typeof tagToClass];
export type SquiggleValue =
| ArrayValue
| ArrayStringValue
| BoolValue
| CallValue
| DateValue
| DeclarationValue
| DistributionValue
| LambdaValue
| ModuleValue
| NumberValue
| RecordValue
| StringValue
| SymbolValue
| TimeDurationValue
| TypeValue
| TypeIdentifierValue
| VoidValue;

View File

@ -0,0 +1,11 @@
import * as RSType from "../rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue_Type.gen";
type T = RSType.squiggleValue_Type;
export class Type {
_value: T;
constructor(_value: T) {
this._value = _value;
}
}

View File

@ -1,252 +1,93 @@
import * as _ from "lodash"; import * as RSDistribution from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
import { import { distributionTag as Tag } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_tag";
genericDist, import { DistributionError } from "./DistributionError";
continuousShape, import { wrapPointSetDist } from "./PointSetDist";
discreteShape, import { resultMap2 } from "./types";
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 }; type T = RSDistribution.distribution;
export { Tag };
function shapePoints(x: continuousShape | discreteShape): point[] { export const wrapDistribution = (value: T): Distribution => {
let xs = x.xyShape.xs; const tag = RSDistribution.getTag(value);
let ys = x.xyShape.ys;
return _.zipWith(xs, ys, (x, y) => ({ x, y })); return new tagToClass[tag](value);
}
export type shape = {
continuous: point[];
discrete: point[];
}; };
export class Distribution { abstract class AbstractDistribution {
t: genericDist; abstract tag: Tag;
env: environment; _value: T;
constructor(t: genericDist, env: environment) { constructor(value: T) {
this.t = t; this._value = value;
this.env = env;
return this;
} }
mapResultDist( pointSet() {
r: result<genericDist, distributionError> const env: any = "TODO";
): result<Distribution, distributionError> { const innerResult = RSDistribution.toPointSet(this._value, env);
return resultMap(r, (v: genericDist) => new Distribution(v, this.env)); return resultMap2(
} innerResult,
wrapPointSetDist,
mean(): result<number, distributionError> { (v: RSDistribution.distributionError) => new DistributionError(v)
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() { toString() {
return this.t.tag; RSDistribution.toString(this._value);
} }
pointSet(): result<shape, distributionError> { mean() {
let pointSet = toPointSet( return RSDistribution.mean(this._value);
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> { inv(n: number) {
return this.mapResultDist( return RSDistribution.inv(this._value, n);
Constructors_toPointSet({ env: this.env }, this.t)
);
} }
toSampleSet(n: number): result<Distribution, distributionError> { stdev() {
return this.mapResultDist( return RSDistribution.stdev(this._value);
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)
);
} }
} }
const valueMethod = <IR>(
_this: AbstractDistribution,
rsMethod: (v: T) => IR | null | undefined
) => {
const value = rsMethod(_this._value);
if (!value) throw new Error("Internal casting error");
return value;
};
export class PointSetDistribution extends AbstractDistribution {
tag = Tag.DtPointSet;
value() {
return valueMethod(this, RSDistribution.getPointSet);
}
}
export class SampleSetDistribution extends AbstractDistribution {
tag = Tag.DtSampleSet;
value() {
return valueMethod(this, RSDistribution.getSampleSet);
}
}
export class SymbolicDistribution extends AbstractDistribution {
tag = Tag.DtSymbolic;
value() {
return valueMethod(this, RSDistribution.getSymbolic);
}
}
const tagToClass = {
[Tag.DtPointSet]: PointSetDistribution,
[Tag.DtSampleSet]: SampleSetDistribution,
[Tag.DtSymbolic]: SymbolicDistribution,
} as const;
export type Distribution =
| PointSetDistribution
| SampleSetDistribution
| SymbolicDistribution;

View File

@ -10,54 +10,87 @@ Instead of a global function namespace we should use modules under ForTS directl
*/ */
import * as _ from "lodash"; import { environment } from "../rescript/ForTS/ForTS_ReducerProject.gen";
import type { import { Project } from "./Project";
environment, import { SquiggleValue, Tag as SquiggleValueTag } from "./SquiggleValue";
expressionValue, export { result } from "../rescript/ForTS/ForTS_Result_tag";
externalBindings, export { Project, SquiggleValue };
errorValue, export { Distribution, Tag as DistributionTag } from "./Distribution";
} from "../rescript/TypescriptInterface.gen"; export { DistributionError } from "./DistributionError";
import { export { Record as SquiggleRecord } from "./Record";
defaultEnvironment, export { SquiggleValueTag };
evaluatePartialUsingExternalBindings,
evaluateUsingOptions,
foreignFunctionInterface,
} from "../rescript/TypescriptInterface.gen";
export { export {
makeSampleSetDist, environment,
errorValueToString, defaultEnvironment,
distributionErrorToString, } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
} from "../rescript/TypescriptInterface.gen"; export { ErrorValue } from "./ErrorValue";
export type { export { resultMap } from "./types";
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";
export { Distribution, resultMap, defaultEnvironment }; // import * as _ from "lodash";
export type { result, shape, environment, lambdaValue, squiggleExpression }; // import type {
// environment,
// expressionValue,
// externalBindings,
// errorValue,
// } from "../rescript/TypescriptInterface.gen";
// import {
// defaultEnvironment,
// evaluatePartialUsingExternalBindings,
// evaluateUsingOptions,
// foreignFunctionInterface,
// } from "../rescript/TypescriptInterface.gen";
// 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";
export { parse } from "./parse"; // export { Distribution, resultMap, defaultEnvironment };
// export type { result, shape, environment, lambdaValue, squiggleExpression };
export let defaultSamplingInputs: environment = { // export { parse } from "./parse";
sampleCount: 10000,
xyPointLength: 10000, // export let defaultSamplingInputs: environment = {
}; // sampleCount: 10000,
// xyPointLength: 10000,
// };
/* Umur: All the functions below are invalid. ForTS_Reducer project is the new way to do this. */ /* Umur: All the functions below are invalid. ForTS_Reducer project is the new way to do this. */
export const run = (
code: string,
options?: {
environment?: environment;
}
) => {
const project = Project.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 };
};
// export function run( // export function run(
// squiggleString: string, // squiggleString: string,
// bindings?: externalBindings, // bindings?: externalBindings,

View File

@ -0,0 +1,252 @@
// 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)
// );
// }
// }

View File

@ -1,23 +1,23 @@
import { // import {
errorValue, // errorValue,
parse as parseRescript, // parse as parseRescript,
} from "../rescript/TypescriptInterface.gen"; // } from "../rescript/TypescriptInterface.gen";
import { result } from "./types"; // import { result } from "./types";
import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers"; // import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers";
export function parse( // export function parse(
squiggleString: string // squiggleString: string
): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> { // ): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> {
const maybeExpression = parseRescript(squiggleString); // const maybeExpression = parseRescript(squiggleString);
if (maybeExpression.tag === "Ok") { // if (maybeExpression.tag === "Ok") {
return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode }; // return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode };
} else { // } else {
if ( // if (
typeof maybeExpression.value !== "object" || // typeof maybeExpression.value !== "object" ||
maybeExpression.value.tag !== "RESyntaxError" // maybeExpression.value.tag !== "RESyntaxError"
) { // ) {
throw new Error("Expected syntax error"); // throw new Error("Expected syntax error");
} // }
return { tag: "Error", value: maybeExpression.value }; // return { tag: "Error", value: maybeExpression.value };
} // }
} // }

View File

@ -1,21 +1,26 @@
export type result<a, b> = import { result } from "../rescript/ForTS/ForTS_Result_tag";
| { export { result };
tag: "Ok";
value: a;
}
| {
tag: "Error";
value: b;
};
export function resultMap<a, b, c>( export function resultMap<a, b, c>(
r: result<a, c>, r: result<a, b>,
mapFn: (x: a) => b mapValue: (x: a) => c
): result<b, c> { ): result<c, b> {
if (r.tag === "Ok") { if (r.tag === "Ok") {
return { tag: "Ok", value: mapFn(r.value) }; return { tag: "Ok", value: mapValue(r.value) };
} else { } 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) };
} }
} }

View File

@ -1,5 +1,6 @@
// Genetic Distribution happens to be abstract distribution // Genetic Distribution happens to be abstract distribution
@genType type distribution = DistributionTypes.genericDist @genType type distribution = DistributionTypes.genericDist
@genType type distributionError = DistributionTypes.error
@genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution @genType type pointSetDistribution = ForTS_Distribution_PointSetDistribution.pointSetDistribution
@genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution @genType type sampleSetDistribution = ForTS_Distribution_SampleSetDistribution.sampleSetDistribution
@genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution @genType type symbolicDistribution = ForTS_Distribution_SymbolicDistribution.symbolicDistribution
@ -73,3 +74,11 @@ let inv = DistributionOperation.Constructors.inv
let pdf = DistributionOperation.Constructors.pdf let pdf = DistributionOperation.Constructors.pdf
@genType @genType
let normalize = DistributionOperation.Constructors.normalize 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)

View File

@ -1,4 +1,7 @@
@genType type pointSetDistribution = PointSetTypes.pointSetDist @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") @module("ForTS_Distribution_PointSetDistribution_tag") @scope("pointSetDistributionTag")
external pstMixed_: int = "PstMixed" external pstMixed_: int = "PstMixed"

View File

@ -11,9 +11,9 @@
"outDir": "./dist", "outDir": "./dist",
"declarationDir": "./dist", "declarationDir": "./dist",
"declaration": true, "declaration": true,
"composite": true "composite": true,
},
"target": "ES6", "target": "ES6",
},
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts", "webpack.config.js"] "exclude": ["node_modules", "**/*.spec.ts", "webpack.config.js"]
} }