Merge pull request #1172 from quantified-uncertainty/error-locations
Error locations and stacktraces
This commit is contained in:
commit
2bb9622edd
|
@ -30,7 +30,7 @@ export const Alert: React.FC<{
|
|||
className={clsx("h-5 w-5 flex-shrink-0", iconColor)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<div className="ml-3">
|
||||
<div className="ml-3 grow">
|
||||
<header className={clsx("text-sm font-medium", headingColor)}>
|
||||
{heading}
|
||||
</header>
|
||||
|
|
|
@ -5,6 +5,8 @@ import AceEditor from "react-ace";
|
|||
import "ace-builds/src-noconflict/mode-golang";
|
||||
import "ace-builds/src-noconflict/theme-github";
|
||||
|
||||
import { SqLocation } from "@quri/squiggle-lang";
|
||||
|
||||
interface CodeEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
|
@ -13,15 +15,17 @@ interface CodeEditorProps {
|
|||
width?: number;
|
||||
height: number;
|
||||
showGutter?: boolean;
|
||||
errorLocations?: SqLocation[];
|
||||
}
|
||||
|
||||
export const CodeEditor: FC<CodeEditorProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
onSubmit,
|
||||
height,
|
||||
oneLine = false,
|
||||
showGutter = false,
|
||||
height,
|
||||
errorLocations = [],
|
||||
}) => {
|
||||
const lineCount = value.split("\n").length;
|
||||
const id = useMemo(() => _.uniqueId(), []);
|
||||
|
@ -30,8 +34,11 @@ export const CodeEditor: FC<CodeEditorProps> = ({
|
|||
const onSubmitRef = useRef<typeof onSubmit | null>(null);
|
||||
onSubmitRef.current = onSubmit;
|
||||
|
||||
const editorEl = useRef<AceEditor | null>(null);
|
||||
|
||||
return (
|
||||
<AceEditor
|
||||
ref={editorEl}
|
||||
value={value}
|
||||
mode="golang"
|
||||
theme="github"
|
||||
|
@ -59,6 +66,14 @@ export const CodeEditor: FC<CodeEditorProps> = ({
|
|||
exec: () => onSubmitRef.current?.(),
|
||||
},
|
||||
]}
|
||||
markers={errorLocations?.map((location) => ({
|
||||
startRow: location.start.line - 1,
|
||||
startCol: location.start.column - 1,
|
||||
endRow: location.end.line - 1,
|
||||
endCol: location.end.column - 1,
|
||||
className: "ace-error-marker",
|
||||
type: "text",
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import * as React from "react";
|
||||
import { SqLambda, environment, SqValueTag } from "@quri/squiggle-lang";
|
||||
import {
|
||||
SqLambda,
|
||||
environment,
|
||||
SqValueTag,
|
||||
SqError,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
||||
import { FunctionChart1Number } from "./FunctionChart1Number";
|
||||
import { DistributionPlottingSettings } from "./DistributionChart";
|
||||
import { ErrorAlert, MessageAlert } from "./Alert";
|
||||
import { MessageAlert } from "./Alert";
|
||||
import { SquiggleErrorAlert } from "./SquiggleErrorAlert";
|
||||
|
||||
export type FunctionChartSettings = {
|
||||
start: number;
|
||||
|
@ -19,6 +25,25 @@ interface FunctionChartProps {
|
|||
height: number;
|
||||
}
|
||||
|
||||
const FunctionCallErrorAlert = ({ error }: { error: SqError }) => {
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
if (expanded) {
|
||||
}
|
||||
return (
|
||||
<MessageAlert heading="Function Display Failed">
|
||||
<div className="space-y-2">
|
||||
<span
|
||||
className="underline decoration-dashed cursor-pointer"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
{expanded ? "Hide" : "Show"} error details
|
||||
</span>
|
||||
{expanded ? <SquiggleErrorAlert error={error} /> : null}
|
||||
</div>
|
||||
</MessageAlert>
|
||||
);
|
||||
};
|
||||
|
||||
export const FunctionChart: React.FC<FunctionChartProps> = ({
|
||||
fn,
|
||||
chartSettings,
|
||||
|
@ -26,7 +51,8 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
|||
distributionPlotSettings,
|
||||
height,
|
||||
}) => {
|
||||
if (fn.parameters.length > 1) {
|
||||
console.log(fn.parameters().length);
|
||||
if (fn.parameters().length !== 1) {
|
||||
return (
|
||||
<MessageAlert heading="Function Display Not Supported">
|
||||
Only functions with one parameter are displayed.
|
||||
|
@ -47,9 +73,7 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
|||
const validResult = getValidResult();
|
||||
|
||||
if (validResult.tag === "Error") {
|
||||
return (
|
||||
<ErrorAlert heading="Error">{validResult.value.toString()}</ErrorAlert>
|
||||
);
|
||||
return <FunctionCallErrorAlert error={validResult.value} />;
|
||||
}
|
||||
|
||||
switch (validResult.value.tag) {
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import * as React from "react";
|
||||
import {
|
||||
SqValue,
|
||||
environment,
|
||||
resultMap,
|
||||
SqValueTag,
|
||||
SqProject,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { SqValue, environment, SqProject } from "@quri/squiggle-lang";
|
||||
import { useSquiggle } from "../lib/hooks";
|
||||
import { SquiggleViewer } from "./SquiggleViewer";
|
||||
import { JsImports } from "../lib/jsImports";
|
||||
import { getValueToRender } from "../lib/utility";
|
||||
|
||||
export type SquiggleChartProps = {
|
||||
/** The input string for squiggle */
|
||||
|
@ -71,16 +66,11 @@ type ProjectExecutionProps = {
|
|||
};
|
||||
const defaultOnChange = () => {};
|
||||
const defaultImports: JsImports = {};
|
||||
const defaultContinues: string[] = [];
|
||||
|
||||
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||
(props: SquiggleChartProps) => {
|
||||
export const splitSquiggleChartSettings = (props: SquiggleChartProps) => {
|
||||
const {
|
||||
executionId = 0,
|
||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
||||
height = 200,
|
||||
jsImports = defaultImports,
|
||||
showSummary = false,
|
||||
width,
|
||||
logX = false,
|
||||
expY = false,
|
||||
diagramStart = 0,
|
||||
|
@ -93,32 +83,8 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
|||
title,
|
||||
xAxisType = "number",
|
||||
distributionChartActions,
|
||||
enableLocalSettings = false,
|
||||
code,
|
||||
continues = [],
|
||||
} = props;
|
||||
|
||||
const p = React.useMemo(() => {
|
||||
if (props.project) {
|
||||
return props.project;
|
||||
} else {
|
||||
const p = SqProject.create();
|
||||
if (props.environment) {
|
||||
p.setEnvironment(props.environment);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}, [props.project, props.environment]);
|
||||
|
||||
const { result, bindings } = useSquiggle({
|
||||
continues,
|
||||
project: p,
|
||||
code,
|
||||
jsImports,
|
||||
onChange,
|
||||
executionId,
|
||||
});
|
||||
|
||||
const distributionPlotSettings = {
|
||||
showSummary,
|
||||
logX,
|
||||
|
@ -138,13 +104,51 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
|||
count: diagramCount,
|
||||
};
|
||||
|
||||
const resultToRender = resultMap(result, (value) =>
|
||||
value.tag === SqValueTag.Void ? bindings.asValue() : value
|
||||
);
|
||||
return { distributionPlotSettings, chartSettings };
|
||||
};
|
||||
|
||||
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||
(props) => {
|
||||
const { distributionPlotSettings, chartSettings } =
|
||||
splitSquiggleChartSettings(props);
|
||||
|
||||
const {
|
||||
code,
|
||||
jsImports = defaultImports,
|
||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
||||
executionId = 0,
|
||||
width,
|
||||
height = 200,
|
||||
enableLocalSettings = false,
|
||||
continues = defaultContinues,
|
||||
} = props;
|
||||
|
||||
const p = React.useMemo(() => {
|
||||
if (props.project) {
|
||||
return props.project;
|
||||
} else {
|
||||
const p = SqProject.create();
|
||||
if (props.environment) {
|
||||
p.setEnvironment(props.environment);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}, [props.project, props.environment]);
|
||||
|
||||
const resultAndBindings = useSquiggle({
|
||||
continues,
|
||||
project: p,
|
||||
code,
|
||||
jsImports,
|
||||
onChange,
|
||||
executionId,
|
||||
});
|
||||
|
||||
const valueToRender = getValueToRender(resultAndBindings);
|
||||
|
||||
return (
|
||||
<SquiggleViewer
|
||||
result={resultToRender}
|
||||
result={valueToRender}
|
||||
width={width}
|
||||
height={height}
|
||||
distributionPlotSettings={distributionPlotSettings}
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import React from "react";
|
||||
import { CodeEditor } from "./CodeEditor";
|
||||
import { SquiggleContainer } from "./SquiggleContainer";
|
||||
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
|
||||
import { useMaybeControlledValue } from "../lib/hooks";
|
||||
import {
|
||||
splitSquiggleChartSettings,
|
||||
SquiggleChartProps,
|
||||
} from "./SquiggleChart";
|
||||
import { useMaybeControlledValue, useSquiggle } from "../lib/hooks";
|
||||
import { JsImports } from "../lib/jsImports";
|
||||
import { defaultEnvironment, SqLocation, SqProject } from "@quri/squiggle-lang";
|
||||
import { SquiggleViewer } from "./SquiggleViewer";
|
||||
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
||||
|
||||
const WrappedCodeEditor: React.FC<{
|
||||
code: string;
|
||||
setCode: (code: string) => void;
|
||||
}> = ({ code, setCode }) => (
|
||||
errorLocations?: SqLocation[];
|
||||
}> = ({ code, setCode, errorLocations }) => (
|
||||
<div className="border border-grey-200 p-2 m-4">
|
||||
<CodeEditor
|
||||
value={code}
|
||||
|
@ -15,6 +23,7 @@ const WrappedCodeEditor: React.FC<{
|
|||
oneLine={true}
|
||||
showGutter={false}
|
||||
height={20}
|
||||
errorLocations={errorLocations}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -24,6 +33,9 @@ export type SquiggleEditorProps = SquiggleChartProps & {
|
|||
onCodeChange?: (code: string) => void;
|
||||
};
|
||||
|
||||
const defaultOnChange = () => {};
|
||||
const defaultImports: JsImports = {};
|
||||
|
||||
export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
||||
const [code, setCode] = useMaybeControlledValue({
|
||||
value: props.code,
|
||||
|
@ -31,11 +43,54 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
|||
onChange: props.onCodeChange,
|
||||
});
|
||||
|
||||
let chartProps = { ...props, code };
|
||||
const { distributionPlotSettings, chartSettings } =
|
||||
splitSquiggleChartSettings(props);
|
||||
|
||||
const {
|
||||
environment,
|
||||
jsImports = defaultImports,
|
||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
||||
executionId = 0,
|
||||
width,
|
||||
height = 200,
|
||||
enableLocalSettings = false,
|
||||
} = props;
|
||||
|
||||
const project = React.useMemo(() => {
|
||||
const p = SqProject.create();
|
||||
if (environment) {
|
||||
p.setEnvironment(environment);
|
||||
}
|
||||
return p;
|
||||
}, [environment]);
|
||||
|
||||
const resultAndBindings = useSquiggle({
|
||||
code,
|
||||
project,
|
||||
jsImports,
|
||||
onChange,
|
||||
executionId,
|
||||
});
|
||||
|
||||
const valueToRender = getValueToRender(resultAndBindings);
|
||||
const errorLocations = getErrorLocations(resultAndBindings.result);
|
||||
|
||||
return (
|
||||
<SquiggleContainer>
|
||||
<WrappedCodeEditor code={code} setCode={setCode} />
|
||||
<SquiggleChart {...chartProps} />
|
||||
<WrappedCodeEditor
|
||||
code={code}
|
||||
setCode={setCode}
|
||||
errorLocations={errorLocations}
|
||||
/>
|
||||
<SquiggleViewer
|
||||
result={valueToRender}
|
||||
width={width}
|
||||
height={height}
|
||||
distributionPlotSettings={distributionPlotSettings}
|
||||
chartSettings={chartSettings}
|
||||
environment={environment ?? defaultEnvironment}
|
||||
enableLocalSettings={enableLocalSettings}
|
||||
/>
|
||||
</SquiggleContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SqError } from "@quri/squiggle-lang";
|
||||
import { SqError, SqFrame } from "@quri/squiggle-lang";
|
||||
import React from "react";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
||||
|
@ -6,6 +6,39 @@ type Props = {
|
|||
error: SqError;
|
||||
};
|
||||
|
||||
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
|
||||
return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>;
|
||||
const StackTraceFrame: React.FC<{ frame: SqFrame }> = ({ frame }) => {
|
||||
const location = frame.location();
|
||||
return (
|
||||
<div>
|
||||
{frame.name()}
|
||||
{location
|
||||
? ` at line ${location.start.line}, column ${location.start.column}`
|
||||
: ""}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const StackTrace: React.FC<Props> = ({ error }) => {
|
||||
const frames = error.getFrameArray();
|
||||
return frames.length ? (
|
||||
<div>
|
||||
<div className="font-medium">Stack trace:</div>
|
||||
<div className="ml-4">
|
||||
{frames.map((frame, i) => (
|
||||
<StackTraceFrame frame={frame} key={i} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
|
||||
return (
|
||||
<ErrorAlert heading="Error">
|
||||
<div className="space-y-4">
|
||||
<div>{error.toString()}</div>
|
||||
<StackTrace error={error} />
|
||||
</div>
|
||||
</ErrorAlert>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,11 @@ import React, {
|
|||
} from "react";
|
||||
import { useForm, UseFormRegister, useWatch } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { useMaybeControlledValue, useRunnerState } from "../lib/hooks";
|
||||
import {
|
||||
useMaybeControlledValue,
|
||||
useRunnerState,
|
||||
useSquiggle,
|
||||
} from "../lib/hooks";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import {
|
||||
ChartSquareBarIcon,
|
||||
|
@ -24,9 +28,9 @@ import {
|
|||
} from "@heroicons/react/solid";
|
||||
import clsx from "clsx";
|
||||
|
||||
import { environment } from "@quri/squiggle-lang";
|
||||
import { environment, SqProject } from "@quri/squiggle-lang";
|
||||
|
||||
import { SquiggleChart, SquiggleChartProps } from "./SquiggleChart";
|
||||
import { SquiggleChartProps } from "./SquiggleChart";
|
||||
import { CodeEditor } from "./CodeEditor";
|
||||
import { JsonEditor } from "./JsonEditor";
|
||||
import { ErrorAlert, SuccessAlert } from "./Alert";
|
||||
|
@ -40,6 +44,8 @@ import { HeadedSection } from "./ui/HeadedSection";
|
|||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
||||
import { Button } from "./ui/Button";
|
||||
import { JsImports } from "../lib/jsImports";
|
||||
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
||||
import { SquiggleViewer } from "./SquiggleViewer";
|
||||
|
||||
type PlaygroundProps = SquiggleChartProps & {
|
||||
/** The initial squiggle string to put in the playground */
|
||||
|
@ -282,7 +288,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
onSettingsChange?.(vars);
|
||||
}, [vars, onSettingsChange]);
|
||||
|
||||
const env: environment = useMemo(
|
||||
const environment: environment = useMemo(
|
||||
() => ({
|
||||
sampleCount: Number(vars.sampleCount),
|
||||
xyPointLength: Number(vars.xyPointLength),
|
||||
|
@ -299,26 +305,59 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
executionId,
|
||||
} = useRunnerState(code);
|
||||
|
||||
const project = React.useMemo(() => {
|
||||
const p = SqProject.create();
|
||||
if (environment) {
|
||||
p.setEnvironment(environment);
|
||||
}
|
||||
return p;
|
||||
}, [environment]);
|
||||
|
||||
const resultAndBindings = useSquiggle({
|
||||
code,
|
||||
project,
|
||||
jsImports: imports,
|
||||
executionId,
|
||||
});
|
||||
|
||||
const valueToRender = getValueToRender(resultAndBindings);
|
||||
|
||||
const squiggleChart =
|
||||
renderedCode === "" ? null : (
|
||||
<div className="relative">
|
||||
{isRunning ? (
|
||||
<div className="absolute inset-0 bg-white opacity-0 animate-semi-appear" />
|
||||
) : null}
|
||||
<SquiggleChart
|
||||
code={renderedCode}
|
||||
executionId={executionId}
|
||||
environment={env}
|
||||
{...vars}
|
||||
jsImports={imports}
|
||||
<SquiggleViewer
|
||||
result={valueToRender}
|
||||
environment={environment}
|
||||
height={vars.chartHeight || 150}
|
||||
distributionPlotSettings={{
|
||||
showSummary: vars.showSummary ?? false,
|
||||
logX: vars.logX ?? false,
|
||||
expY: vars.expY ?? false,
|
||||
format: vars.tickFormat,
|
||||
minX: vars.minX,
|
||||
maxX: vars.maxX,
|
||||
title: vars.title,
|
||||
actions: vars.distributionChartActions,
|
||||
}}
|
||||
chartSettings={{
|
||||
start: vars.diagramStart ?? 0,
|
||||
stop: vars.diagramStop ?? 10,
|
||||
count: vars.diagramCount ?? 20,
|
||||
}}
|
||||
enableLocalSettings={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const errorLocations = getErrorLocations(resultAndBindings.result);
|
||||
|
||||
const firstTab = vars.showEditor ? (
|
||||
<div className="border border-slate-200">
|
||||
<CodeEditor
|
||||
errorLocations={errorLocations}
|
||||
value={code}
|
||||
onChange={setCode}
|
||||
onSubmit={run}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SqValue, SqValueLocation } from "@quri/squiggle-lang";
|
||||
import { SqValue } from "@quri/squiggle-lang";
|
||||
import React, { useContext, useReducer } from "react";
|
||||
import { Tooltip } from "../ui/Tooltip";
|
||||
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
||||
|
@ -70,7 +70,7 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
|
|||
<div className="flex w-full">
|
||||
{location.path.items.length ? (
|
||||
<div
|
||||
className="border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
|
||||
className="shrink-0 border-l-2 border-slate-200 hover:border-indigo-600 w-4 cursor-pointer"
|
||||
onClick={toggleCollapsed}
|
||||
></div>
|
||||
) : null}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { SqProject, SqValue } from "@quri/squiggle-lang";
|
||||
import {
|
||||
result,
|
||||
SqError,
|
||||
SqProject,
|
||||
SqRecord,
|
||||
SqValue,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { JsImports, jsImportsToSquiggleCode } from "../jsImports";
|
||||
import * as uuid from "uuid";
|
||||
|
@ -8,29 +14,36 @@ type SquiggleArgs = {
|
|||
executionId?: number;
|
||||
jsImports?: JsImports;
|
||||
project: SqProject;
|
||||
continues: string[];
|
||||
continues?: string[];
|
||||
onChange?: (expr: SqValue | undefined, sourceName: string) => void;
|
||||
};
|
||||
|
||||
const importSourceName = (sourceName: string) => "imports-" + sourceName;
|
||||
export type ResultAndBindings = {
|
||||
result: result<SqValue, SqError>;
|
||||
bindings: SqRecord;
|
||||
};
|
||||
|
||||
export const useSquiggle = (args: SquiggleArgs) => {
|
||||
const importSourceName = (sourceName: string) => "imports-" + sourceName;
|
||||
const defaultContinues = [];
|
||||
|
||||
export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
|
||||
const sourceName = useMemo(() => uuid.v4(), []);
|
||||
|
||||
const env = args.project.getEnvironment();
|
||||
const continues = args.continues || defaultContinues;
|
||||
|
||||
const result = useMemo(
|
||||
() => {
|
||||
const project = args.project;
|
||||
|
||||
project.setSource(sourceName, args.code);
|
||||
let continues = args.continues;
|
||||
let fullContinues = continues;
|
||||
if (args.jsImports && Object.keys(args.jsImports).length) {
|
||||
const importsSource = jsImportsToSquiggleCode(args.jsImports);
|
||||
project.setSource(importSourceName(sourceName), importsSource);
|
||||
continues = args.continues.concat(importSourceName(sourceName));
|
||||
fullContinues = continues.concat(importSourceName(sourceName));
|
||||
}
|
||||
project.setContinues(sourceName, continues);
|
||||
project.setContinues(sourceName, fullContinues);
|
||||
project.run(sourceName);
|
||||
const result = project.getResult(sourceName);
|
||||
const bindings = project.getBindings(sourceName);
|
||||
|
@ -45,7 +58,7 @@ export const useSquiggle = (args: SquiggleArgs) => {
|
|||
args.jsImports,
|
||||
args.executionId,
|
||||
sourceName,
|
||||
args.continues,
|
||||
continues,
|
||||
args.project,
|
||||
env,
|
||||
]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { result } from "@quri/squiggle-lang";
|
||||
import { result, resultMap, SqValueTag } from "@quri/squiggle-lang";
|
||||
import { ResultAndBindings } from "./hooks/useSquiggle";
|
||||
|
||||
export function flattenResult<a, b>(x: result<a, b>[]): result<a[], b> {
|
||||
if (x.length === 0) {
|
||||
|
@ -35,3 +36,18 @@ export function all(arr: boolean[]): boolean {
|
|||
export function some(arr: boolean[]): boolean {
|
||||
return arr.reduce((x, y) => x || y, false);
|
||||
}
|
||||
|
||||
export function getValueToRender({ result, bindings }: ResultAndBindings) {
|
||||
return resultMap(result, (value) =>
|
||||
value.tag === SqValueTag.Void ? bindings.asValue() : value
|
||||
);
|
||||
}
|
||||
|
||||
export function getErrorLocations(result: ResultAndBindings["result"]) {
|
||||
if (result.tag === "Error") {
|
||||
const location = result.value.location();
|
||||
return location ? [location] : [];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,3 +22,8 @@ but this line is still necessary for proper initialization of `--tw-*` variables
|
|||
.ace_cursor {
|
||||
border-left: 2px solid !important;
|
||||
}
|
||||
|
||||
.ace-error-marker {
|
||||
position: absolute;
|
||||
border-bottom: 1px solid red;
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@ describe("Peggy parse", () => {
|
|||
|
||||
describe("lambda", () => {
|
||||
testParse("{|x| x}", "{{|:x| :x}}")
|
||||
testParse("f={|x| x}", "{:f = {{|: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
|
||||
})
|
||||
|
|
|
@ -9,12 +9,12 @@ open Jest
|
|||
open Expect
|
||||
|
||||
let expectParseToBe = (expr, answer) =>
|
||||
Parse.parse(expr)->Parse.toStringResult->expect->toBe(answer)
|
||||
Parse.parse(expr, "test")->Parse.toStringResult->expect->toBe(answer)
|
||||
|
||||
let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||
|
||||
let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
||||
let rExpr = Parse.parse(expr)->Result.map(ToExpression.fromNode)
|
||||
let expectExpressionToBe = (expr, answer, ~v="_", ()) => {
|
||||
let rExpr = Parse.parse(expr, "test")->Result.map(ToExpression.fromNode)
|
||||
let a1 = rExpr->ExpressionT.toStringResultOkless
|
||||
|
||||
if v == "_" {
|
||||
|
@ -22,6 +22,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
|||
} else {
|
||||
let a2 =
|
||||
rExpr
|
||||
->E.R2.errMap(e => e->SqError.fromParseError)
|
||||
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
|
||||
->Reducer_Value.toStringResultOkless
|
||||
(a1, a2)->expect->toEqual((answer, v))
|
||||
|
@ -29,16 +30,16 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
|||
}
|
||||
|
||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||
test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||
test(expr, () => expectExpressionToBe(expr, answer, ~v, ()))
|
||||
|
||||
module MyOnly = {
|
||||
let testParse = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||
Only.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||
Only.test(expr, () => expectExpressionToBe(expr, answer, ~v, ()))
|
||||
}
|
||||
|
||||
module MySkip = {
|
||||
let testParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||
Skip.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||
Skip.test(expr, () => expectExpressionToBe(expr, answer, ~v, ()))
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ describe("Peggy to Expression", () => {
|
|||
|
||||
describe("lambda", () => {
|
||||
testToExpression("{|x| x}", "{|x| x}", ~v="lambda(x=>internal code)", ())
|
||||
testToExpression("f={|x| x}", "f = {{|x| x}}", ())
|
||||
testToExpression("f={|x| x}", "f = {|x| x}", ())
|
||||
testToExpression("f(x)=x", "f = {|x| {x}}", ()) // Function definitions are lambda assignments
|
||||
testToExpression("f(x)=x ? 1 : 0", "f = {|x| {x ? (1) : (0)}}", ())
|
||||
})
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
module ErrorValue = Reducer_ErrorValue
|
||||
module Expression = Reducer_Expression
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
|
||||
|
@ -9,7 +8,7 @@ let unwrapRecord = rValue =>
|
|||
rValue->Belt.Result.flatMap(value =>
|
||||
switch value {
|
||||
| Reducer_T.IEvRecord(aRecord) => Ok(aRecord)
|
||||
| _ => ErrorValue.RETodo("TODO: Internal bindings must be returned")->Error
|
||||
| _ => SqError.Message.RETodo("TODO: Internal bindings must be returned")->Error
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ describe("test exceptions", () => {
|
|||
testDescriptionEvalToBe(
|
||||
"javascript exception",
|
||||
"javascriptraise('div by 0')",
|
||||
"Error(Error: 'div by 0')",
|
||||
"Error(JS Exception: Error: 'div by 0')",
|
||||
)
|
||||
// testDescriptionEvalToBe(
|
||||
// "rescript exception",
|
||||
|
@ -78,3 +78,33 @@ describe("test exceptions", () => {
|
|||
// "Error(TODO: unhandled rescript exception)",
|
||||
// )
|
||||
})
|
||||
|
||||
describe("stacktraces", () => {
|
||||
test("nested calls", () => {
|
||||
open Expect
|
||||
|
||||
let error =
|
||||
Expression.BackCompatible.evaluateString(`
|
||||
f(x) = {
|
||||
y = "a"
|
||||
x + y
|
||||
}
|
||||
g = {|x| f(x)}
|
||||
h(x) = g(x)
|
||||
h(5)
|
||||
`)
|
||||
->E.R.getError
|
||||
->E.O2.toExn("oops")
|
||||
->SqError.toStringWithStackTrace
|
||||
|
||||
expect(
|
||||
error,
|
||||
)->toBe(`Error: There are function matches for add(), but with different arguments: [add(number, number)]; [add(distribution, number)]; [add(number, distribution)]; [add(distribution, distribution)]; [add(date, duration)]; [add(duration, duration)]
|
||||
Stack trace:
|
||||
f at line 4, column 5
|
||||
g at line 6, column 12
|
||||
h at line 7, column 10
|
||||
<top> at line 8, column 3
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -25,7 +25,7 @@ x=1`,
|
|||
let mainIncludes = Project.getIncludes(project, "main")
|
||||
switch mainIncludes {
|
||||
| Ok(includes) => expect(includes) == ["common"]
|
||||
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
|
||||
| Error(error) => fail(error->SqError.toString)
|
||||
}
|
||||
})
|
||||
test("past chain", () => {
|
||||
|
@ -60,7 +60,7 @@ x=1`,
|
|||
let mainIncludes = Project.getIncludes(project, "main")
|
||||
switch mainIncludes {
|
||||
| Ok(includes) => expect(includes) == ["common", "myModule"]
|
||||
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
|
||||
| Error(error) => fail(error->SqError.toString)
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -99,7 +99,7 @@ x=1`,
|
|||
let mainIncludes = Project.getIncludes(project, "main")
|
||||
switch mainIncludes {
|
||||
| Ok(includes) => expect(includes) == ["common", "common2", "myModule"]
|
||||
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
|
||||
| Error(error) => fail(error->SqError.toString)
|
||||
}
|
||||
})
|
||||
test("direct past chain", () => {
|
||||
|
|
|
@ -36,7 +36,7 @@ Here we will finally proceed to a real life scenario. */
|
|||
/* Parse includes has set the includes */
|
||||
switch project->Project.getIncludes("main") {
|
||||
| Ok(includes) => includes->expect == ["common"]
|
||||
| Error(err) => err->Reducer_ErrorValue.errorToString->fail
|
||||
| Error(err) => err->SqError.toString->fail
|
||||
}
|
||||
/* If the includes cannot be parsed then you get a syntax error.
|
||||
Otherwise you get the includes.
|
||||
|
@ -85,7 +85,7 @@ Here we will finally proceed to a real life scenario. */
|
|||
let rIncludes = project->Project.getIncludes(sourceName)
|
||||
switch rIncludes {
|
||||
/* Maybe there is an include syntax error */
|
||||
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
|
||||
| Error(err) => err->SqError.toString->Js.Exn.raiseError
|
||||
|
||||
| Ok(includes) =>
|
||||
includes->Belt.Array.forEach(newIncludeName => {
|
||||
|
@ -169,7 +169,7 @@ Here we will finally proceed to a real life scenario. */
|
|||
test("getIncludes", () => {
|
||||
switch Project.getIncludes(project, "main") {
|
||||
| Ok(includes) => includes->expect == ["common"]
|
||||
| Error(err) => err->Reducer_ErrorValue.errorToString->fail
|
||||
| Error(err) => err->SqError.toString->fail
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
41
packages/squiggle-lang/__tests__/SqError_test.res
Normal file
41
packages/squiggle-lang/__tests__/SqError_test.res
Normal file
|
@ -0,0 +1,41 @@
|
|||
open Jest
|
||||
open Expect
|
||||
|
||||
describe("SqError.Message", () => {
|
||||
test("toString", () =>
|
||||
expect(SqError.Message.REOther("test error")->SqError.Message.toString)->toBe(
|
||||
"Error: test error",
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
describe("SqError", () => {
|
||||
test("fromMessage", () =>
|
||||
expect(SqError.Message.REOther("test error")->SqError.fromMessage->SqError.toString)->toBe(
|
||||
"Error: test error",
|
||||
)
|
||||
)
|
||||
|
||||
test("toStringWithStackTrace with empty stacktrace", () =>
|
||||
expect(
|
||||
SqError.Message.REOther("test error")->SqError.fromMessage->SqError.toStringWithStackTrace,
|
||||
)->toBe("Error: test error")
|
||||
)
|
||||
|
||||
test("toStringWithStackTrace", () => {
|
||||
let frameStack =
|
||||
Reducer_FrameStack.make()
|
||||
->Reducer_FrameStack.extend("frame1", None)
|
||||
->Reducer_FrameStack.extend("frame2", None)
|
||||
|
||||
expect(
|
||||
SqError.Message.REOther("test error")
|
||||
->SqError.fromMessageWithFrameStack(frameStack)
|
||||
->SqError.toStringWithStackTrace,
|
||||
)->toBe(`Error: test error
|
||||
Stack trace:
|
||||
frame2
|
||||
frame1
|
||||
`)
|
||||
})
|
||||
})
|
|
@ -36,6 +36,6 @@ export const run = (src, { output, sampleCount } = {}) => {
|
|||
"Time:",
|
||||
String(time),
|
||||
result.tag === "Error" ? red(result.tag) : green(result.tag),
|
||||
result.tag === "Error" ? result.value.toString() : ""
|
||||
result.tag === "Error" ? result.value.toStringWithFrameStack() : ""
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
#!/usr/bin/env node
|
||||
import { run } from "./lib.mjs";
|
||||
|
||||
const src = process.argv[2];
|
||||
import { Command } from "commander";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program.arguments("<string>");
|
||||
|
||||
const options = program.parse(process.argv);
|
||||
|
||||
const src = program.args[0];
|
||||
if (!src) {
|
||||
throw new Error("Expected src");
|
||||
}
|
||||
console.log(`Running ${src}`);
|
||||
|
||||
const sampleCount = process.env.SAMPLE_COUNT;
|
||||
|
||||
|
|
|
@ -1,17 +1,48 @@
|
|||
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
|
||||
import * as RSError from "../rescript/SqError.gen";
|
||||
import * as RSReducerT from "../rescript/Reducer/Reducer_T.gen";
|
||||
|
||||
import * as RSFrameStack from "../rescript/Reducer/Reducer_FrameStack.gen";
|
||||
|
||||
export { location as SqLocation } from "../rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.gen";
|
||||
|
||||
export class SqError {
|
||||
constructor(private _value: RSErrorValue.reducerErrorValue) {}
|
||||
constructor(private _value: RSError.t) {}
|
||||
|
||||
toString() {
|
||||
return RSErrorValue.toString(this._value);
|
||||
return RSError.toString(this._value);
|
||||
}
|
||||
|
||||
static createTodoError(v: string) {
|
||||
return new SqError(RSErrorValue.createTodoError(v));
|
||||
toStringWithStackTrace() {
|
||||
return RSError.toStringWithStackTrace(this._value);
|
||||
}
|
||||
|
||||
static createOtherError(v: string) {
|
||||
return new SqError(RSErrorValue.createOtherError(v));
|
||||
return new SqError(RSError.createOtherError(v));
|
||||
}
|
||||
|
||||
getTopFrame(): SqFrame | undefined {
|
||||
const frame = RSFrameStack.getTopFrame(RSError.getFrameStack(this._value));
|
||||
return frame ? new SqFrame(frame) : undefined;
|
||||
}
|
||||
|
||||
getFrameArray(): SqFrame[] {
|
||||
const frames = RSError.getFrameArray(this._value);
|
||||
return frames.map((frame) => new SqFrame(frame));
|
||||
}
|
||||
|
||||
location() {
|
||||
return this.getTopFrame()?.location();
|
||||
}
|
||||
}
|
||||
|
||||
export class SqFrame {
|
||||
constructor(private _value: RSReducerT.frame) {}
|
||||
|
||||
name(): string {
|
||||
return RSFrameStack.Frame.getName(this._value);
|
||||
}
|
||||
|
||||
location() {
|
||||
return RSFrameStack.Frame.getLocation(this._value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
|
||||
import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
|
||||
import * as RSError from "../rescript/SqError.gen";
|
||||
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
|
||||
import { SqError } from "./SqError";
|
||||
import { SqRecord } from "./SqRecord";
|
||||
|
@ -54,7 +54,7 @@ export class SqProject {
|
|||
return resultMap2(
|
||||
RSProject.getIncludes(this._value, sourceId),
|
||||
(a) => a,
|
||||
(v: reducerErrorValue) => new SqError(v)
|
||||
(v: RSError.t) => new SqError(v)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ export class SqProject {
|
|||
items: [],
|
||||
})
|
||||
),
|
||||
(v: reducerErrorValue) => new SqError(v)
|
||||
(v: RSError.t) => new SqError(v)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { isParenthesisNode } from "mathjs";
|
||||
import { SqProject } from "./SqProject";
|
||||
|
||||
type PathItem = string | number;
|
||||
|
|
|
@ -13,7 +13,7 @@ export {
|
|||
environment,
|
||||
defaultEnvironment,
|
||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
||||
export { SqError } from "./SqError";
|
||||
export { SqError, SqFrame, SqLocation } from "./SqError";
|
||||
export { SqShape } from "./SqPointSetDist";
|
||||
|
||||
export { resultMap } from "./types";
|
||||
|
|
|
@ -5,7 +5,7 @@ let nameSpace = "" // no namespaced versions
|
|||
|
||||
type simpleDefinition = {
|
||||
inputs: array<frType>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorMessage>,
|
||||
}
|
||||
|
||||
let makeFnMany = (name: string, definitions: array<simpleDefinition>) =>
|
||||
|
@ -22,7 +22,7 @@ let makeFnMany = (name: string, definitions: array<simpleDefinition>) =>
|
|||
let makeFn = (
|
||||
name: string,
|
||||
inputs: array<frType>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorMessage>,
|
||||
) => makeFnMany(name, [{inputs: inputs, fn: fn}])
|
||||
|
||||
let library = [
|
||||
|
|
|
@ -67,7 +67,7 @@ module Integration = {
|
|||
let applyFunctionAtFloatToFloatOption = (point: float) => {
|
||||
// Defined here so that it has access to environment, reducer
|
||||
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
|
||||
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
|
||||
let resultAsInternalExpression = Reducer_Lambda.doLambdaCall(
|
||||
aLambda,
|
||||
[pointAsInternalExpression],
|
||||
environment,
|
||||
|
@ -77,7 +77,7 @@ module Integration = {
|
|||
| Reducer_T.IEvNumber(x) => Ok(x)
|
||||
| _ =>
|
||||
Error(
|
||||
"Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther,
|
||||
"Error 1 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->SqError.Message.REOther,
|
||||
)
|
||||
}
|
||||
result
|
||||
|
@ -143,8 +143,8 @@ module Integration = {
|
|||
| Error(b) =>
|
||||
("Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++
|
||||
"Original error: " ++
|
||||
b->Reducer_ErrorValue.errorToString)
|
||||
->Reducer_ErrorValue.REOther
|
||||
b->SqError.Message.toString)
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
}
|
||||
result
|
||||
|
@ -169,7 +169,7 @@ module Integration = {
|
|||
let result = switch inputs {
|
||||
| [_, _, _, IEvNumber(0.0)] =>
|
||||
"Integration error 4 in Danger.integrate: Increment can't be 0."
|
||||
->Reducer_ErrorValue.REOther
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
| [
|
||||
IEvLambda(aLambda),
|
||||
|
@ -187,7 +187,7 @@ module Integration = {
|
|||
)
|
||||
| _ =>
|
||||
Error(
|
||||
Reducer_ErrorValue.REOther(
|
||||
SqError.Message.REOther(
|
||||
"Integration error 5 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))",
|
||||
),
|
||||
)
|
||||
|
@ -213,7 +213,7 @@ module Integration = {
|
|||
let result = switch inputs {
|
||||
| [_, _, _, IEvNumber(0.0)] =>
|
||||
"Integration error in Danger.integrate: Increment can't be 0."
|
||||
->Reducer_ErrorValue.REOther
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
| [IEvLambda(aLambda), IEvNumber(min), IEvNumber(max), IEvNumber(epsilon)] =>
|
||||
Helpers.integrateFunctionBetweenWithNumIntegrationPoints(
|
||||
|
@ -225,11 +225,11 @@ module Integration = {
|
|||
reducer,
|
||||
)->E.R2.errMap(b =>
|
||||
("Integration error 7 in Danger.integrate. Something went wrong along the way: " ++
|
||||
b->Reducer_ErrorValue.errorToString)->Reducer_ErrorValue.REOther
|
||||
b->SqError.Message.toString)->SqError.Message.REOther
|
||||
)
|
||||
| _ =>
|
||||
"Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))"
|
||||
->Reducer_ErrorValue.REOther
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
}
|
||||
result
|
||||
|
@ -246,7 +246,7 @@ module DiminishingReturns = {
|
|||
module Helpers = {
|
||||
type diminishingReturnsAccumulatorInner = {
|
||||
optimalAllocations: array<float>,
|
||||
currentMarginalReturns: result<array<float>, errorValue>,
|
||||
currentMarginalReturns: result<array<float>, errorMessage>,
|
||||
}
|
||||
let findBiggestElementIndex = (xs: array<float>) =>
|
||||
E.A.reducei(xs, 0, (acc, newElement, index) => {
|
||||
|
@ -255,7 +255,7 @@ module DiminishingReturns = {
|
|||
| false => acc
|
||||
}
|
||||
})
|
||||
type diminishingReturnsAccumulator = result<diminishingReturnsAccumulatorInner, errorValue>
|
||||
type diminishingReturnsAccumulator = result<diminishingReturnsAccumulatorInner, errorMessage>
|
||||
// TODO: This is so complicated, it probably should be its own file. It might also make sense to have it work in Rescript directly, taking in a function rather than a reducer; then something else can wrap that function in the reducer/lambdas/environment.
|
||||
/*
|
||||
The key idea for this function is that
|
||||
|
@ -290,25 +290,25 @@ module DiminishingReturns = {
|
|||
) {
|
||||
| (false, _, _, _) =>
|
||||
Error(
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."->Reducer_ErrorValue.REOther,
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, number of functions should be greater than 1."->SqError.Message.REOther,
|
||||
)
|
||||
| (_, false, _, _) =>
|
||||
Error(
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."->Reducer_ErrorValue.REOther,
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, funds should be greater than 0."->SqError.Message.REOther,
|
||||
)
|
||||
| (_, _, false, _) =>
|
||||
Error(
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."->Reducer_ErrorValue.REOther,
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be greater than 0."->SqError.Message.REOther,
|
||||
)
|
||||
| (_, _, _, false) =>
|
||||
Error(
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."->Reducer_ErrorValue.REOther,
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions, approximateIncrement should be smaller than funds amount."->SqError.Message.REOther,
|
||||
)
|
||||
| (true, true, true, true) => {
|
||||
let applyFunctionAtPoint = (lambda, point: float) => {
|
||||
// Defined here so that it has access to environment, reducer
|
||||
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
|
||||
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
|
||||
let resultAsInternalExpression = Reducer_Lambda.doLambdaCall(
|
||||
lambda,
|
||||
[pointAsInternalExpression],
|
||||
environment,
|
||||
|
@ -318,7 +318,7 @@ module DiminishingReturns = {
|
|||
| Reducer_T.IEvNumber(x) => Ok(x)
|
||||
| _ =>
|
||||
Error(
|
||||
"Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->Reducer_ErrorValue.REOther,
|
||||
"Error 1 in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead"->SqError.Message.REOther,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ module DiminishingReturns = {
|
|||
| Reducer_T.IEvLambda(lambda) => Ok(lambda)
|
||||
| _ =>
|
||||
"Error in Danger.optimalAllocationGivenDiminishingMarginalReturnsForManyFunctions. A member of the array wasn't a function"
|
||||
->Reducer_ErrorValue.REOther
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
}
|
||||
}, innerlambdas)
|
||||
|
@ -433,7 +433,7 @@ module DiminishingReturns = {
|
|||
}
|
||||
| _ =>
|
||||
"Error in Danger.diminishingMarginalReturnsForTwoFunctions"
|
||||
->Reducer_ErrorValue.REOther
|
||||
->SqError.Message.REOther
|
||||
->Error
|
||||
},
|
||||
(),
|
||||
|
|
|
@ -4,7 +4,7 @@ open FunctionRegistry_Helpers
|
|||
let makeFn = (
|
||||
name: string,
|
||||
inputs: array<frType>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorValue>,
|
||||
fn: array<Reducer_T.value> => result<Reducer_T.value, errorMessage>,
|
||||
) =>
|
||||
Function.make(
|
||||
~name,
|
||||
|
@ -66,7 +66,7 @@ let library = [
|
|||
| [IEvNumber(year)] =>
|
||||
switch DateTime.Date.makeFromYear(year) {
|
||||
| Ok(t) => IEvDate(t)->Ok
|
||||
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error
|
||||
| Error(e) => SqError.Message.RETodo(e)->Error
|
||||
}
|
||||
| _ => Error(impossibleError)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ module Internals = {
|
|||
->E.A2.fmap(((key, value)) => Wrappers.evArray([IEvString(key), value]))
|
||||
->Wrappers.evArray
|
||||
|
||||
let fromList = (items: array<Reducer_T.value>): result<Reducer_T.value, errorValue> =>
|
||||
let fromList = (items: array<Reducer_T.value>): result<Reducer_T.value, errorMessage> =>
|
||||
items
|
||||
->E.A2.fmap(item => {
|
||||
switch (item: Reducer_T.value) {
|
||||
|
@ -76,7 +76,7 @@ let library = [
|
|||
->Belt.Array.map(dictValue =>
|
||||
switch dictValue {
|
||||
| IEvRecord(dict) => dict
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
)
|
||||
->Internals.mergeMany
|
||||
|
|
|
@ -16,13 +16,14 @@ module DistributionCreation = {
|
|||
r
|
||||
->E.R.bind(Process.DistOrNumberToDist.twoValuesUsingSymbolicDist(~fn, ~values=_, ~env))
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(e => Reducer_ErrorValue.REOther(e))
|
||||
->E.R2.errMap(e => SqError.Message.REOther(e))
|
||||
|
||||
let make = (name, fn) => {
|
||||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -31,8 +32,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))
|
||||
->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -41,10 +44,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, env, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev"))
|
||||
->process(~fn, ~env),
|
||||
->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -55,13 +58,14 @@ module DistributionCreation = {
|
|||
r
|
||||
->E.R.bind(Process.DistOrNumberToDist.oneValueUsingSymbolicDist(~fn, ~value=_, ~env))
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(e => Reducer_ErrorValue.REOther(e))
|
||||
->E.R2.errMap(e => SqError.Message.REOther(e))
|
||||
|
||||
let make = (name, fn) =>
|
||||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -296,7 +296,7 @@ module Old = {
|
|||
|
||||
let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
|
||||
Reducer_T.value,
|
||||
Reducer_ErrorValue.errorValue,
|
||||
SqError.Message.t,
|
||||
> =>
|
||||
switch o {
|
||||
| Dist(d) => Ok(Reducer_T.IEvDistribution(d))
|
||||
|
@ -311,9 +311,9 @@ module Old = {
|
|||
switch dispatchToGenericOutput(call, environment) {
|
||||
| Some(o) => genericOutputToReducerValue(o)
|
||||
| None =>
|
||||
Reducer_ErrorValue.REOther("Internal error in FR_GenericDist implementation")
|
||||
->Reducer_ErrorValue.ErrorException
|
||||
->raise
|
||||
SqError.Message.REOther(
|
||||
"Internal error in FR_GenericDist implementation",
|
||||
)->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +326,7 @@ let makeProxyFn = (name: string, inputs: array<frType>) => {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs,
|
||||
~run=(inputs, env, _) => Old.dispatch((name, inputs), env),
|
||||
~run=(inputs, context, _) => Old.dispatch((name, inputs), context.environment),
|
||||
(),
|
||||
),
|
||||
],
|
||||
|
@ -402,9 +402,9 @@ let library = E.A.concatMany([
|
|||
])
|
||||
|
||||
// FIXME - impossible to implement with FR due to arbitrary parameters length;
|
||||
let mxLambda = Reducer_Expression_Lambda.makeFFILambda((inputs, env, _) => {
|
||||
switch Old.dispatch(("mx", inputs), env) {
|
||||
let mxLambda = Reducer_Lambda.makeFFILambda("mx", (inputs, context, _) => {
|
||||
switch Old.dispatch(("mx", inputs), context.environment) {
|
||||
| Ok(value) => value
|
||||
| Error(e) => e->Reducer_ErrorValue.ErrorException->raise
|
||||
| Error(e) => e->SqError.Message.throw
|
||||
}
|
||||
})
|
||||
|
|
|
@ -30,11 +30,11 @@ module Internals = {
|
|||
let map = (
|
||||
array: array<Reducer_T.value>,
|
||||
eLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): Reducer_T.value => {
|
||||
Belt.Array.map(array, elem =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(eLambdaValue, [elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(eLambdaValue, [elem], context, reducer)
|
||||
)->Wrappers.evArray
|
||||
}
|
||||
|
||||
|
@ -42,11 +42,11 @@ module Internals = {
|
|||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
aValueArray->E.A.reduce(initialValue, (acc, elem) =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(aLambdaValue, [acc, elem], context, reducer)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -54,22 +54,22 @@ module Internals = {
|
|||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
aValueArray->Belt.Array.reduceReverse(initialValue, (acc, elem) =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(aLambdaValue, [acc, elem], context, reducer)
|
||||
)
|
||||
}
|
||||
|
||||
let filter = (
|
||||
aValueArray,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
Js.Array2.filter(aValueArray, elem => {
|
||||
let result = Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [elem], env, reducer)
|
||||
let result = Reducer_Lambda.doLambdaCall(aLambdaValue, [elem], context, reducer)
|
||||
switch result {
|
||||
| IEvBool(true) => true
|
||||
| _ => false
|
||||
|
|
|
@ -16,30 +16,30 @@ let inputsToDist = (inputs: array<Reducer_T.value>, xyShapeToPointSetDist) => {
|
|||
let yValue = map->Belt.Map.String.get("y")
|
||||
switch (xValue, yValue) {
|
||||
| (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y)
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
)
|
||||
->Ok
|
||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||
->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist)))
|
||||
| _ => impossibleError->Reducer_ErrorValue.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
|
||||
module Internal = {
|
||||
type t = PointSetDist.t
|
||||
|
||||
let toType = (r): result<Reducer_T.value, Reducer_ErrorValue.errorValue> =>
|
||||
let toType = (r): result<Reducer_T.value, SqError.Message.t> =>
|
||||
switch r {
|
||||
| Ok(r) => Ok(Wrappers.evDistribution(PointSet(r)))
|
||||
| Error(err) => Error(REOperationError(err))
|
||||
}
|
||||
|
||||
let doLambdaCall = (aLambdaValue, list, env, reducer) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
switch Reducer_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
| Reducer_T.IEvNumber(f) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
@ -61,18 +61,18 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(inputs, env, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
switch inputs {
|
||||
| [IEvDistribution(dist)] =>
|
||||
GenericDist.toPointSet(
|
||||
dist,
|
||||
~xyPointLength=env.xyPointLength,
|
||||
~sampleCount=env.sampleCount,
|
||||
~xyPointLength=context.environment.xyPointLength,
|
||||
~sampleCount=context.environment.sampleCount,
|
||||
(),
|
||||
)
|
||||
->E.R2.fmap(Wrappers.pointSet)
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
|
||||
->E.R2.errMap(e => SqError.Message.REDistributionError(e))
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
|
|
@ -10,41 +10,40 @@ module Internal = {
|
|||
let doLambdaCall = (
|
||||
aLambdaValue,
|
||||
list,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
switch Reducer_Lambda.doLambdaCall(aLambdaValue, list, context, reducer) {
|
||||
| IEvNumber(f) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
||||
let toType = (r): result<Reducer_T.value, Reducer_ErrorValue.errorValue> =>
|
||||
let toType = (r): result<Reducer_T.value, SqError.Message.t> =>
|
||||
switch r {
|
||||
| Ok(r) => Ok(Wrappers.evDistribution(SampleSet(r)))
|
||||
| Error(r) => Error(REDistributionError(SampleSetError(r)))
|
||||
}
|
||||
|
||||
//TODO: I don't know why this seems to need at least one input
|
||||
let fromFn = (aLambdaValue, environment: Reducer_T.environment, reducer: Reducer_T.reducerFn) => {
|
||||
let sampleCount = environment.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
|
||||
let fromFn = (aLambdaValue, context: Reducer_T.context, reducer: Reducer_T.reducerFn) => {
|
||||
let sampleCount = context.environment.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], context, reducer)
|
||||
Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen
|
||||
}
|
||||
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], context, reducer)
|
||||
SampleSetDist.samplesMap(~fn, sampleSetDist)->toType
|
||||
}
|
||||
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let fn = (a, b) =>
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], environment, reducer)
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = (a, b) => doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], context, reducer)
|
||||
SampleSetDist.map2(~fn, ~t1, ~t2)->toType
|
||||
}
|
||||
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = (a, b, c) =>
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], environment, reducer)
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], context, reducer)
|
||||
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,7 @@ module Internal = {
|
|||
let mapN = (
|
||||
aValueArray: array<Reducer_T.value>,
|
||||
aLambdaValue,
|
||||
environment: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer,
|
||||
) => {
|
||||
switch parseSampleSetArray(aValueArray) {
|
||||
|
@ -69,7 +68,7 @@ module Internal = {
|
|||
doLambdaCall(
|
||||
aLambdaValue,
|
||||
[IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))],
|
||||
environment,
|
||||
context,
|
||||
reducer,
|
||||
)
|
||||
SampleSetDist.mapN(~fn, ~t1)->toType
|
||||
|
@ -89,13 +88,13 @@ let libaryBase = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(inputs, environment, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
switch inputs {
|
||||
| [IEvDistribution(dist)] =>
|
||||
GenericDist.toSampleSetDist(dist, environment.sampleCount)
|
||||
GenericDist.toSampleSetDist(dist, context.environment.sampleCount)
|
||||
->E.R2.fmap(Wrappers.sampleSet)
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
|
||||
->E.R2.errMap(e => SqError.Message.REDistributionError(e))
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
(),
|
||||
|
@ -163,7 +162,7 @@ let libaryBase = [
|
|||
| [IEvLambda(lambda)] =>
|
||||
switch Internal.fromFn(lambda, environment, reducer) {
|
||||
| Ok(r) => Ok(r->Wrappers.sampleSet->Wrappers.evDistribution)
|
||||
| Error(e) => e->Reducer_ErrorValue.REOperationError->Error
|
||||
| Error(e) => e->SqError.Message.REOperationError->Error
|
||||
}
|
||||
| _ => Error(impossibleError)
|
||||
},
|
||||
|
@ -290,7 +289,7 @@ module Comparison = {
|
|||
r
|
||||
->E.R2.fmap(r => r->Wrappers.sampleSet->Wrappers.evDistribution)
|
||||
->E.R2.errMap(e =>
|
||||
e->DistributionTypes.Error.sampleErrorToDistErr->Reducer_ErrorValue.REDistributionError
|
||||
e->DistributionTypes.Error.sampleErrorToDistErr->SqError.Message.REDistributionError
|
||||
)
|
||||
|
||||
let mkBig = (name, withDist, withFloat) =>
|
||||
|
|
|
@ -6,7 +6,7 @@ let requiresNamespace = true
|
|||
let runScoring = (estimate, answer, prior, env) => {
|
||||
GenericDist.Score.logScore(~estimate, ~answer, ~prior, ~env)
|
||||
->E.R2.fmap(FunctionRegistry_Helpers.Wrappers.evNumber)
|
||||
->E.R2.errMap(e => Reducer_ErrorValue.REDistributionError(e))
|
||||
->E.R2.errMap(e => SqError.Message.REDistributionError(e))
|
||||
}
|
||||
|
||||
let library = [
|
||||
|
@ -30,15 +30,15 @@ let library = [
|
|||
("prior", FRTypeDist),
|
||||
]),
|
||||
],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(
|
||||
inputs,
|
||||
("estimate", "answer", "prior"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), environment)
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), context.environment)
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), environment)
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), context.environment)
|
||||
| Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -48,15 +48,15 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="logScore",
|
||||
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(
|
||||
inputs,
|
||||
("estimate", "answer"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d)]) =>
|
||||
runScoring(estimate, Score_Dist(d), None, environment)
|
||||
runScoring(estimate, Score_Dist(d), None, context.environment)
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d)]) =>
|
||||
runScoring(estimate, Score_Scalar(d), None, environment)
|
||||
runScoring(estimate, Score_Scalar(d), None, context.environment)
|
||||
| Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -76,10 +76,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="klDivergence",
|
||||
~inputs=[FRTypeDist, FRTypeDist],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch inputs {
|
||||
| [IEvDistribution(estimate), IEvDistribution(d)] =>
|
||||
runScoring(estimate, Score_Dist(d), None, environment)
|
||||
runScoring(estimate, Score_Dist(d), None, context.environment)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@genType type reducerProject = ReducerProject_T.project //re-export
|
||||
|
||||
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
|
||||
type error = SqError.t //use
|
||||
type errorMessage = SqError.Message.t //use
|
||||
|
||||
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
|
||||
type squiggleValue_Record = ForTS_SquiggleValue.squiggleValue_Record //use
|
||||
|
@ -103,10 +104,8 @@ let cleanAllResults = (project: reducerProject): unit => project->Private.cleanA
|
|||
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->Private.getIncludes(sourceId)
|
||||
let getIncludes = (project: reducerProject, sourceId: string): result<array<string>, error> =>
|
||||
project->Private.getIncludes(sourceId)
|
||||
|
||||
/* Other sources contributing to the global namespace of this source. */
|
||||
@genType
|
||||
|
@ -198,10 +197,8 @@ let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Rec
|
|||
Get the result after running this source file or the project
|
||||
*/
|
||||
@genType
|
||||
let getResult = (project: reducerProject, sourceId: string): result<
|
||||
squiggleValue,
|
||||
reducerErrorValue,
|
||||
> => project->Private.getResult(sourceId)
|
||||
let getResult = (project: reducerProject, sourceId: string): result<squiggleValue, error> =>
|
||||
project->Private.getResult(sourceId)
|
||||
|
||||
/*
|
||||
This is a convenience function to get the result of a single source without creating a project.
|
||||
|
@ -209,10 +206,8 @@ 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_Record,
|
||||
) => Private.evaluate(sourceCode)
|
||||
let evaluate = (sourceCode: string): (result<squiggleValue, error>, squiggleValue_Record) =>
|
||||
Private.evaluate(sourceCode)
|
||||
|
||||
@genType
|
||||
let setEnvironment = (project: reducerProject, environment: environment): unit =>
|
||||
|
@ -220,24 +215,3 @@ let setEnvironment = (project: reducerProject, environment: environment): unit =
|
|||
|
||||
@genType
|
||||
let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment
|
||||
|
||||
/*
|
||||
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,
|
||||
// )
|
||||
// }
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
@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)
|
|
@ -1,5 +1,5 @@
|
|||
@genType type squiggleValue = Reducer_T.value //re-export
|
||||
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
|
||||
type error = SqError.t //use
|
||||
|
||||
@genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type
|
||||
@genType type squiggleValue_Record = Reducer_T.map //re-export recursive type
|
||||
|
@ -69,7 +69,7 @@ let toString = (variant: squiggleValue) => Reducer_Value.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>) =>
|
||||
let toStringResult = (variantResult: result<squiggleValue, error>) =>
|
||||
Reducer_Value.toStringResult(variantResult)
|
||||
|
||||
@genType
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
@genType type squiggleValue_Lambda = Reducer_T.lambdaValue //re-export
|
||||
|
||||
@genType
|
||||
let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringFunction(v)
|
||||
let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringLambda(v)
|
||||
|
||||
@genType
|
||||
let parameters = (v: squiggleValue_Lambda): array<string> => {
|
||||
v.parameters
|
||||
}
|
||||
let parameters = (v: squiggleValue_Lambda): array<string> => Reducer_Lambda.parameters(v)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
@genType type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //re-export
|
||||
@genType type syntaxErrorLocation = ForTS_Reducer_ErrorValue.syntaxErrorLocation //re-export
|
||||
|
||||
@genType type reducerProject = ForTS_ReducerProject.reducerProject //re-export
|
||||
@genType type squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export
|
||||
@genType type squiggleValue_Array = ForTS_SquiggleValue_Array.squiggleValue_Array //re-export
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
type internalExpressionValueType = Reducer_Value.internalExpressionValueType
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
type errorMessage = SqError.Message.t
|
||||
|
||||
/*
|
||||
Function Registry "Type". A type, without any other information.
|
||||
|
@ -30,9 +30,9 @@ type fnDefinition = {
|
|||
inputs: array<frType>,
|
||||
run: (
|
||||
array<Reducer_T.value>,
|
||||
Reducer_T.environment,
|
||||
Reducer_T.context,
|
||||
Reducer_T.reducerFn,
|
||||
) => result<Reducer_T.value, errorValue>,
|
||||
) => result<Reducer_T.value, errorMessage>,
|
||||
}
|
||||
|
||||
type function = {
|
||||
|
@ -122,11 +122,11 @@ module FnDefinition = {
|
|||
let run = (
|
||||
t: t,
|
||||
args: array<Reducer_T.value>,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
switch t->isMatch(args) {
|
||||
| true => t.run(args, env, reducer)
|
||||
| true => t.run(args, context, reducer)
|
||||
| false => REOther("Incorrect Types")->Error
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ module Function = {
|
|||
nameSpace: nameSpace,
|
||||
definitions: definitions,
|
||||
output: output,
|
||||
examples: examples |> E.O.default([]),
|
||||
examples: examples->E.O2.default([]),
|
||||
isExperimental: isExperimental,
|
||||
requiresNamespace: requiresNamespace,
|
||||
description: description,
|
||||
|
@ -225,9 +225,9 @@ module Registry = {
|
|||
registry,
|
||||
fnName: string,
|
||||
args: array<Reducer_T.value>,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): result<Reducer_T.value, errorValue> => {
|
||||
): result<Reducer_T.value, errorMessage> => {
|
||||
switch Belt.Map.String.get(registry.fnNameDict, fnName) {
|
||||
| Some(definitions) => {
|
||||
let showNameMatchDefinitions = () => {
|
||||
|
@ -241,7 +241,7 @@ module Registry = {
|
|||
|
||||
let match = definitions->Js.Array2.find(def => def->FnDefinition.isMatch(args))
|
||||
switch match {
|
||||
| Some(def) => def->FnDefinition.run(args, env, reducer)
|
||||
| Some(def) => def->FnDefinition.run(args, context, reducer)
|
||||
| None => REOther(showNameMatchDefinitions())->Error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ open FunctionRegistry_Core
|
|||
open Reducer_T
|
||||
|
||||
let impossibleErrorString = "Wrong inputs / Logically impossible"
|
||||
let impossibleError: errorValue = impossibleErrorString->Reducer_ErrorValue.REOther
|
||||
let wrapError = e => Reducer_ErrorValue.REOther(e)
|
||||
let impossibleError: errorMessage = impossibleErrorString->SqError.Message.REOther
|
||||
let wrapError = e => SqError.Message.REOther(e)
|
||||
|
||||
module Wrappers = {
|
||||
let symbolic = r => DistributionTypes.Symbolic(r)
|
||||
|
|
|
@ -4,9 +4,13 @@ let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv
|
|||
|
||||
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
|
||||
{
|
||||
frameStack: list{},
|
||||
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
|
||||
environment: environment,
|
||||
inFunction: None,
|
||||
}
|
||||
}
|
||||
|
||||
let createDefaultContext = (): t => createContext(SquiggleLibrary_StdLib.stdLib, defaultEnvironment)
|
||||
let currentFunctionName = (t: t): string => {
|
||||
t.inFunction->E.O2.fmap(Reducer_Lambda_T.name)->E.O2.default(Reducer_T.topFrameName)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// types are disabled until review and rewrite for 0.5 interpreter compatibility
|
||||
/*
|
||||
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
module T = Reducer_Dispatch_T
|
||||
module TypeChecker = Reducer_Type_TypeChecker
|
||||
open Reducer_Value
|
||||
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
|
||||
let makeFromTypes = jumpTable => {
|
||||
let dispatchChainPiece: T.dispatchChainPiece = (
|
||||
(fnName, fnArgs): functionCall,
|
||||
accessors: ProjectAccessorsT.t,
|
||||
) => {
|
||||
let jumpTableEntry = jumpTable->Js.Array2.find(elem => {
|
||||
let (candidName, candidType, _) = elem
|
||||
candidName == fnName && TypeChecker.checkITypeArgumentsBool(candidType, fnArgs)
|
||||
})
|
||||
switch jumpTableEntry {
|
||||
| Some((_, _, bridgeFn)) => bridgeFn(fnArgs, accessors)->Some
|
||||
| _ => None
|
||||
}
|
||||
}
|
||||
dispatchChainPiece
|
||||
}
|
||||
|
||||
*/
|
|
@ -1,21 +0,0 @@
|
|||
// module ExpressionT = Reducer_Expression_T
|
||||
// module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
|
||||
|
||||
// // Each piece of the dispatch chain computes the result or returns None so that the chain can continue
|
||||
// type dispatchChainPiece = (
|
||||
// Reducer_Value.functionCall,
|
||||
// ProjectAccessorsT.t,
|
||||
// ) => option<result<Reducer_T.value, Reducer_ErrorValue.errorValue>>
|
||||
|
||||
// type dispatchChainPieceWithReducer = (
|
||||
// Reducer_Value.functionCall,
|
||||
// ProjectAccessorsT.t,
|
||||
// Reducer_T.reducerFn,
|
||||
// ) => option<result<Reducer_T.value, Reducer_ErrorValue.errorValue>>
|
||||
|
||||
// // This is a switch statement case implementation: get the arguments and compute the result
|
||||
// type genericIEvFunction = (
|
||||
// array<Reducer_T.value>,
|
||||
// ProjectAccessorsT.t,
|
||||
// ) => result<Reducer_T.value, Reducer_ErrorValue.errorValue>
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
//TODO: Do not export here but in ForTS__Types
|
||||
@gentype.import("peggy") @genType.as("LocationRange")
|
||||
type syntaxErrorLocation
|
||||
|
||||
@genType.opaque
|
||||
type errorValue =
|
||||
| REArityError(option<string>, int, int)
|
||||
| REArrayIndexNotFound(string, int)
|
||||
| REAssignmentExpected
|
||||
| REDistributionError(DistributionTypes.error)
|
||||
| REExpectedType(string, string)
|
||||
| REExpressionExpected
|
||||
| REFunctionExpected(string)
|
||||
| REFunctionNotFound(string)
|
||||
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception
|
||||
| REMacroNotFound(string)
|
||||
| RENotAFunction(string)
|
||||
| REOperationError(Operation.operationError)
|
||||
| RERecordPropertyNotFound(string, string)
|
||||
| RESymbolNotFound(string)
|
||||
| RESyntaxError(string, option<syntaxErrorLocation>)
|
||||
| RETodo(string) // To do
|
||||
| REUnitNotFound(string)
|
||||
| RENeedToRun
|
||||
| REOther(string)
|
||||
|
||||
type t = errorValue
|
||||
|
||||
exception ErrorException(errorValue)
|
||||
|
||||
let errorToString = err =>
|
||||
switch err {
|
||||
| REArityError(_oFnName, arity, usedArity) =>
|
||||
`${Js.String.make(arity)} arguments expected. Instead ${Js.String.make(
|
||||
usedArity,
|
||||
)} argument(s) were passed.`
|
||||
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
||||
| REAssignmentExpected => "Assignment expected"
|
||||
| REExpressionExpected => "Expression expected"
|
||||
| REFunctionExpected(msg) => `Function expected: ${msg}`
|
||||
| REFunctionNotFound(msg) => `Function not found: ${msg}`
|
||||
| REDistributionError(err) => `Distribution Math Error: ${DistributionTypes.Error.toString(err)}`
|
||||
| REOperationError(err) => `Math Error: ${Operation.Error.toString(err)}`
|
||||
| REJavaScriptExn(omsg, oname) => {
|
||||
let answer = "JS Exception:"
|
||||
let answer = switch oname {
|
||||
| Some(name) => `${answer} ${name}`
|
||||
| _ => answer
|
||||
}
|
||||
let answer = switch omsg {
|
||||
| Some(msg) => `${answer}: ${msg}`
|
||||
| _ => answer
|
||||
}
|
||||
answer
|
||||
}
|
||||
| REMacroNotFound(macro) => `Macro not found: ${macro}`
|
||||
| RENotAFunction(valueString) => `${valueString} is not a function`
|
||||
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
||||
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
||||
| RESyntaxError(desc, _) => `Syntax Error: ${desc}`
|
||||
| RETodo(msg) => `TODO: ${msg}`
|
||||
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
|
||||
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
||||
| RENeedToRun => "Need to run"
|
||||
| REOther(msg) => `Error: ${msg}`
|
||||
}
|
||||
|
||||
let fromException = exn =>
|
||||
switch exn {
|
||||
| ErrorException(e) => e
|
||||
| Js.Exn.Error(e) =>
|
||||
switch Js.Exn.message(e) {
|
||||
| Some(message) => REOther(message)
|
||||
| None =>
|
||||
switch Js.Exn.name(e) {
|
||||
| Some(name) => REOther(name)
|
||||
| None => REOther("Unknown error")
|
||||
}
|
||||
}
|
||||
| _e => REOther("Unknown error")
|
||||
}
|
||||
|
||||
let toException = (errorValue: t) => raise(ErrorException(errorValue))
|
|
@ -1,3 +0,0 @@
|
|||
// There are switch statement cases in the code which are impossible to reach by design.
|
||||
// ImpossibleException is a sign of programming error.
|
||||
exception ImpossibleException(string)
|
|
@ -1,16 +1,25 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module Lambda = Reducer_Expression_Lambda
|
||||
module Result = Belt.Result
|
||||
module T = Reducer_T
|
||||
|
||||
type errorValue = Reducer_ErrorValue.errorValue
|
||||
let toLocation = (expression: T.expression): Reducer_Peggy_Parse.location => {
|
||||
expression.ast.location
|
||||
}
|
||||
|
||||
let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) =>
|
||||
error->SqError.throwMessageWithFrameStack(
|
||||
context.frameStack->Reducer_FrameStack.extend(
|
||||
context->Reducer_Context.currentFunctionName,
|
||||
Some(expression->toLocation),
|
||||
),
|
||||
)
|
||||
|
||||
/*
|
||||
Recursively evaluate the expression
|
||||
*/
|
||||
let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||
// Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`)
|
||||
switch expression {
|
||||
switch expression.content {
|
||||
| T.EBlock(statements) => {
|
||||
let innerContext = {...context, bindings: context.bindings->Bindings.extend}
|
||||
let (value, _) =
|
||||
|
@ -49,7 +58,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
let (key, _) = eKey->evaluate(context)
|
||||
let keyString = switch key {
|
||||
| IEvString(s) => s
|
||||
| _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise
|
||||
| _ => REOther("Record keys must be strings")->throwFrom(expression, context)
|
||||
}
|
||||
let (value, _) = eValue->evaluate(context)
|
||||
(keyString, value)
|
||||
|
@ -73,7 +82,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
| T.ESymbol(name) =>
|
||||
switch context.bindings->Bindings.get(name) {
|
||||
| Some(v) => (v, context)
|
||||
| None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise
|
||||
| None => RESymbolNotFound(name)->throwFrom(expression, context)
|
||||
}
|
||||
|
||||
| T.EValue(value) => (value, context)
|
||||
|
@ -82,28 +91,39 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
let (predicateResult, _) = predicate->evaluate(context)
|
||||
switch predicateResult {
|
||||
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
|
||||
| _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise
|
||||
| _ => REExpectedType("Boolean", "")->throwFrom(expression, context)
|
||||
}
|
||||
}
|
||||
|
||||
| T.ELambda(parameters, body) => (
|
||||
Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda,
|
||||
| T.ELambda(parameters, body, name) => (
|
||||
Reducer_Lambda.makeLambda(
|
||||
name,
|
||||
parameters,
|
||||
context.bindings,
|
||||
body,
|
||||
expression->toLocation,
|
||||
)->T.IEvLambda,
|
||||
context,
|
||||
)
|
||||
|
||||
| T.ECall(fn, args) => {
|
||||
let (lambda, _) = fn->evaluate(context)
|
||||
let argValues = Js.Array2.map(args, arg => {
|
||||
let argValues = Belt.Array.map(args, arg => {
|
||||
let (argValue, _) = arg->evaluate(context)
|
||||
argValue
|
||||
})
|
||||
switch lambda {
|
||||
| T.IEvLambda(lambda) => (
|
||||
Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate),
|
||||
| T.IEvLambda(lambda) => {
|
||||
let result = Reducer_Lambda.doLambdaCallFrom(
|
||||
lambda,
|
||||
argValues,
|
||||
context,
|
||||
evaluate,
|
||||
Some(expression->toLocation), // we have to pass the location of a current expression here, to put it on frameStack
|
||||
)
|
||||
| _ =>
|
||||
RENotAFunction(lambda->Reducer_Value.toString)->Reducer_ErrorValue.ErrorException->raise
|
||||
(result, context)
|
||||
}
|
||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,19 +132,22 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
module BackCompatible = {
|
||||
// Those methods are used to support the existing tests
|
||||
// If they are used outside limited testing context, error location reporting will fail
|
||||
let parse = (peggyCode: string): result<T.expression, errorValue> =>
|
||||
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode)
|
||||
let parse = (peggyCode: string): result<T.expression, Reducer_Peggy_Parse.parseError> =>
|
||||
peggyCode->Reducer_Peggy_Parse.parse("main")->Result.map(Reducer_Peggy_ToExpression.fromNode)
|
||||
|
||||
let evaluate = (expression: T.expression): result<T.value, errorValue> => {
|
||||
let context = Reducer_Context.createDefaultContext()
|
||||
let createDefaultContext = () =>
|
||||
Reducer_Context.createContext(SquiggleLibrary_StdLib.stdLib, Reducer_Context.defaultEnvironment)
|
||||
|
||||
let evaluate = (expression: T.expression): result<T.value, SqError.t> => {
|
||||
let context = createDefaultContext()
|
||||
try {
|
||||
let (value, _) = expression->evaluate(context)
|
||||
value->Ok
|
||||
} catch {
|
||||
| exn => Reducer_ErrorValue.fromException(exn)->Error
|
||||
| exn => exn->SqError.fromException->Error
|
||||
}
|
||||
}
|
||||
|
||||
let evaluateString = (peggyCode: string): result<T.value, errorValue> =>
|
||||
parse(peggyCode)->Result.flatMap(evaluate)
|
||||
let evaluateString = (peggyCode: string): result<T.value, SqError.t> =>
|
||||
parse(peggyCode)->E.R2.errMap(e => e->SqError.fromParseError)->Result.flatMap(evaluate)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
module BErrorValue = Reducer_ErrorValue
|
||||
module T = Reducer_T
|
||||
|
||||
type errorValue = BErrorValue.errorValue
|
||||
type expression = Reducer_T.expression
|
||||
type expressionContent = Reducer_T.expressionContent
|
||||
|
||||
let eArray = (anArray: array<T.expression>) => anArray->T.EArray
|
||||
let eArray = (anArray: array<T.expression>): expressionContent => anArray->T.EArray
|
||||
|
||||
let eBool = aBool => aBool->T.IEvBool->T.EValue
|
||||
|
||||
let eCall = (fn: expression, args: array<expression>): expression => T.ECall(fn, args)
|
||||
let eCall = (fn: expression, args: array<expression>): expressionContent => T.ECall(fn, args)
|
||||
|
||||
let eLambda = (parameters: array<string>, expr: expression) => T.ELambda(parameters, expr)
|
||||
let eLambda = (
|
||||
parameters: array<string>,
|
||||
expr: expression,
|
||||
name: option<string>,
|
||||
): expressionContent => T.ELambda(parameters, expr, name)
|
||||
|
||||
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
|
||||
|
||||
|
@ -18,13 +21,13 @@ let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord
|
|||
|
||||
let eString = aString => aString->T.IEvString->T.EValue
|
||||
|
||||
let eSymbol = (name: string): expression => T.ESymbol(name)
|
||||
let eSymbol = (name: string): expressionContent => T.ESymbol(name)
|
||||
|
||||
let eBlock = (exprs: array<expression>): expression => T.EBlock(exprs)
|
||||
let eBlock = (exprs: array<expression>): expressionContent => T.EBlock(exprs)
|
||||
|
||||
let eProgram = (exprs: array<expression>): expression => T.EProgram(exprs)
|
||||
let eProgram = (exprs: array<expression>): expressionContent => T.EProgram(exprs)
|
||||
|
||||
let eLetStatement = (symbol: string, valueExpression: expression): expression => T.EAssign(
|
||||
let eLetStatement = (symbol: string, valueExpression: expression): expressionContent => T.EAssign(
|
||||
symbol,
|
||||
valueExpression,
|
||||
)
|
||||
|
@ -33,11 +36,8 @@ let eTernary = (
|
|||
predicate: expression,
|
||||
trueCase: expression,
|
||||
falseCase: expression,
|
||||
): expression => T.ETernary(predicate, trueCase, falseCase)
|
||||
): expressionContent => T.ETernary(predicate, trueCase, falseCase)
|
||||
|
||||
let eIdentifier = (name: string): expression => name->T.ESymbol
|
||||
let eIdentifier = (name: string): expressionContent => name->T.ESymbol
|
||||
|
||||
// let eTypeIdentifier = (name: string): expression =>
|
||||
// name->T.IEvTypeIdentifier->T.EValue
|
||||
|
||||
let eVoid: expression = T.IEvVoid->T.EValue
|
||||
let eVoid: expressionContent = T.IEvVoid->T.EValue
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
module ErrorValue = Reducer_ErrorValue
|
||||
|
||||
let doLambdaCall = (
|
||||
lambdaValue: Reducer_T.lambdaValue,
|
||||
args,
|
||||
environment: Reducer_T.environment,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): Reducer_T.value => {
|
||||
lambdaValue.body(args, environment, reducer)
|
||||
}
|
||||
|
||||
let makeLambda = (
|
||||
parameters: array<string>,
|
||||
bindings: Reducer_T.bindings,
|
||||
body: Reducer_T.expression,
|
||||
): Reducer_T.lambdaValue => {
|
||||
// TODO - clone bindings to avoid later redefinitions affecting lambdas?
|
||||
|
||||
// Note: with this implementation, FFI lambdas (created by other methods than calling `makeLambda`) are allowed to violate the rules, pollute the bindings, etc.
|
||||
// Not sure yet if that's a bug or a feature.
|
||||
// FunctionRegistry functions are unaffected by this, their API is too limited.
|
||||
|
||||
let lambda = (
|
||||
arguments: array<Reducer_T.value>,
|
||||
environment: Reducer_T.environment,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
let argsLength = arguments->Js.Array2.length
|
||||
let parametersLength = parameters->Js.Array2.length
|
||||
if argsLength !== parametersLength {
|
||||
ErrorValue.REArityError(None, parametersLength, argsLength)->ErrorValue.ErrorException->raise
|
||||
}
|
||||
|
||||
let localBindings = bindings->Reducer_Bindings.extend
|
||||
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
|
||||
currentBindings,
|
||||
parameter,
|
||||
index,
|
||||
) => {
|
||||
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
|
||||
})
|
||||
|
||||
let (value, _) = reducer(
|
||||
body,
|
||||
{bindings: localBindingsWithParameters, environment: environment},
|
||||
)
|
||||
value
|
||||
}
|
||||
|
||||
{
|
||||
// context: bindings,
|
||||
body: lambda,
|
||||
parameters: parameters,
|
||||
}
|
||||
}
|
||||
|
||||
let makeFFILambda = (body: Reducer_T.lambdaBody): Reducer_T.lambdaValue => {
|
||||
body: body,
|
||||
parameters: ["..."],
|
||||
}
|
|
@ -12,7 +12,7 @@ let semicolonJoin = values =>
|
|||
Converts the expression to String
|
||||
*/
|
||||
let rec toString = (expression: t) =>
|
||||
switch expression {
|
||||
switch expression.content {
|
||||
| EBlock(statements) =>
|
||||
`{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}`
|
||||
| EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin
|
||||
|
@ -24,37 +24,23 @@ let rec toString = (expression: t) =>
|
|||
`${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
|
||||
| EAssign(name, value) => `${name} = ${value->toString}`
|
||||
| ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})`
|
||||
| ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}`
|
||||
| ELambda(parameters, body, _) => `{|${parameters->commaJoin}| ${body->toString}}`
|
||||
| EValue(aValue) => Reducer_Value.toString(aValue)
|
||||
}
|
||||
|
||||
let toStringResult = codeResult =>
|
||||
switch codeResult {
|
||||
| Ok(a) => `Ok(${toString(a)})`
|
||||
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
|
||||
| Error(m) => `Error(${Reducer_Peggy_Parse.toStringError(m)})`
|
||||
}
|
||||
|
||||
let toStringResultOkless = codeResult =>
|
||||
switch codeResult {
|
||||
| Ok(a) => toString(a)
|
||||
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})`
|
||||
| Error(m) => `Error(${Reducer_Peggy_Parse.toStringError(m)})`
|
||||
}
|
||||
|
||||
let inspect = (expr: t): t => {
|
||||
Js.log(toString(expr))
|
||||
expr
|
||||
}
|
||||
|
||||
let inspectResult = (r: result<t, Reducer_ErrorValue.errorValue>): result<
|
||||
t,
|
||||
Reducer_ErrorValue.errorValue,
|
||||
> => {
|
||||
Js.log(toStringResult(r))
|
||||
r
|
||||
}
|
||||
|
||||
let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t =>
|
||||
switch rExpression {
|
||||
| Ok(expression) => expression
|
||||
| Error(errorValue) => Reducer_ErrorValue.toException(errorValue)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// This is called "frameStack" and not "callStack", because the last frame in errors is often not a function call.
|
||||
// A "frame" is a pair of a scope (function or top-level scope, currently stored as a string) and a location inside it.
|
||||
// See this comment to deconfuse about what a frame is: https://github.com/quantified-uncertainty/squiggle/pull/1172#issuecomment-1264115038
|
||||
type t = Reducer_T.frameStack
|
||||
|
||||
module Frame = {
|
||||
let toString = ({name, location}: Reducer_T.frame) =>
|
||||
name ++
|
||||
switch location {
|
||||
| Some(location) =>
|
||||
` at line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}` // TODO - source id?
|
||||
| None => ""
|
||||
}
|
||||
|
||||
@genType
|
||||
let getLocation = (t: Reducer_T.frame): option<Reducer_Peggy_Parse.location> => t.location
|
||||
|
||||
@genType
|
||||
let getName = (t: Reducer_T.frame): string => t.name
|
||||
}
|
||||
|
||||
let make = (): t => list{}
|
||||
|
||||
let extend = (t: t, name: string, location: option<Reducer_Peggy_Parse.location>) =>
|
||||
t->Belt.List.add({
|
||||
name: name,
|
||||
location: location,
|
||||
})
|
||||
|
||||
// this is useful for SyntaxErrors
|
||||
let makeSingleFrameStack = (location: Reducer_Peggy_Parse.location): t =>
|
||||
make()->extend(Reducer_T.topFrameName, Some(location))
|
||||
|
||||
// this includes the left offset because it's mostly used in SqError.toStringWithStackTrace
|
||||
let toString = (t: t) =>
|
||||
t
|
||||
->Belt.List.map(s => " " ++ s->Frame.toString ++ "\n")
|
||||
->Belt.List.toArray
|
||||
->Js.Array2.joinWith("")
|
||||
|
||||
@genType
|
||||
let toFrameArray = (t: t): array<Reducer_T.frame> => t->Belt.List.toArray
|
||||
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<Reducer_T.frame> => t->Belt.List.head
|
||||
|
||||
let isEmpty = (t: t): bool =>
|
||||
switch t->Belt.List.head {
|
||||
| Some(_) => true
|
||||
| None => false
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
type t = Reducer_T.lambdaValue
|
||||
|
||||
// user-defined functions, i.e. `add2 = {|x, y| x + y}`, are built by this method
|
||||
let makeLambda = (
|
||||
name: option<string>,
|
||||
parameters: array<string>,
|
||||
bindings: Reducer_T.bindings,
|
||||
body: Reducer_T.expression,
|
||||
location: Reducer_Peggy_Parse.location,
|
||||
): t => {
|
||||
let lambda = (
|
||||
arguments: array<Reducer_T.value>,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
let argsLength = arguments->E.A.length
|
||||
let parametersLength = parameters->E.A.length
|
||||
if argsLength !== parametersLength {
|
||||
SqError.Message.REArityError(None, parametersLength, argsLength)->SqError.Message.throw
|
||||
}
|
||||
|
||||
// create new bindings scope - technically not necessary, since bindings are immutable, but might help with debugging/new features in the future
|
||||
let localBindings = bindings->Reducer_Bindings.extend
|
||||
|
||||
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
|
||||
currentBindings,
|
||||
parameter,
|
||||
index,
|
||||
) => {
|
||||
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
|
||||
})
|
||||
|
||||
let lambdaContext: Reducer_T.context = {
|
||||
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
|
||||
environment: context.environment, // environment at the moment when lambda is called
|
||||
frameStack: context.frameStack, // already extended in `doLambdaCall`
|
||||
inFunction: context.inFunction, // already updated in `doLambdaCall`
|
||||
}
|
||||
|
||||
let (value, _) = reducer(body, lambdaContext)
|
||||
value
|
||||
}
|
||||
|
||||
FnLambda({
|
||||
// context: bindings,
|
||||
name: name,
|
||||
body: lambda,
|
||||
parameters: parameters,
|
||||
location: location,
|
||||
})
|
||||
}
|
||||
|
||||
// stdlib functions (everything in FunctionRegistry) are built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
||||
let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
|
||||
// Note: current bindings could be accidentally exposed here through context (compare with native lambda implementation above, where we override them with local bindings).
|
||||
// But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident.
|
||||
body: body,
|
||||
name: name,
|
||||
})
|
||||
|
||||
// this function doesn't scale to FunctionRegistry's polymorphic functions
|
||||
let parameters = (t: t): array<string> => {
|
||||
switch t {
|
||||
| FnLambda({parameters}) => parameters
|
||||
| FnBuiltin(_) => ["..."]
|
||||
}
|
||||
}
|
||||
|
||||
let doLambdaCallFrom = (
|
||||
t: t,
|
||||
args: array<Reducer_T.value>,
|
||||
context: Reducer_T.context,
|
||||
reducer,
|
||||
location: option<Reducer_Peggy_Parse.location>,
|
||||
) => {
|
||||
let newContext = {
|
||||
...context,
|
||||
frameStack: context.frameStack->Reducer_FrameStack.extend(
|
||||
context->Reducer_Context.currentFunctionName,
|
||||
location,
|
||||
),
|
||||
inFunction: Some(t),
|
||||
}
|
||||
|
||||
SqError.rethrowWithFrameStack(() => {
|
||||
switch t {
|
||||
| FnLambda({body}) => body(args, newContext, reducer)
|
||||
| FnBuiltin({body}) => body(args, newContext, reducer)
|
||||
}
|
||||
}, newContext.frameStack)
|
||||
}
|
||||
|
||||
let doLambdaCall = (t: t, args, context, reducer) => {
|
||||
doLambdaCallFrom(t, args, context, reducer, None)
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
type t = Reducer_T.lambdaValue
|
||||
|
||||
let name = (t: t): string => {
|
||||
switch t {
|
||||
| FnLambda({name}) => name->E.O2.default("<anonymous>")
|
||||
| FnBuiltin({name}) => name
|
||||
}
|
||||
}
|
|
@ -7,26 +7,26 @@
|
|||
start
|
||||
= _nl start:outerBlock _nl finalComment? {return start}
|
||||
|
||||
zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
|
||||
zeroOMoreArgumentsBlockOrExpression = lambda / innerBlockOrExpression
|
||||
|
||||
outerBlock
|
||||
= statements:array_statements finalExpression: (statementSeparator @expression)?
|
||||
{ if (finalExpression) statements.push(finalExpression)
|
||||
return h.nodeProgram(statements) }
|
||||
return h.nodeProgram(statements, location()) }
|
||||
/ finalExpression: expression
|
||||
{ return h.nodeProgram([finalExpression]) }
|
||||
{ return h.nodeProgram([finalExpression], location()) }
|
||||
|
||||
innerBlockOrExpression
|
||||
= quotedInnerBlock
|
||||
/ finalExpression: expression
|
||||
{ return h.nodeBlock([finalExpression])}
|
||||
{ return h.nodeBlock([finalExpression], location())}
|
||||
|
||||
quotedInnerBlock
|
||||
= '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||
{ if (finalExpression) statements.push(finalExpression)
|
||||
return h.nodeBlock(statements) }
|
||||
return h.nodeBlock(statements, location()) }
|
||||
/ '{' _nl finalExpression: expression _nl '}'
|
||||
{ return h.nodeBlock([finalExpression]) }
|
||||
{ return h.nodeBlock([finalExpression], location()) }
|
||||
|
||||
array_statements
|
||||
= head:statement tail:(statementSeparator @array_statements )
|
||||
|
@ -42,16 +42,16 @@ statement
|
|||
voidStatement
|
||||
= "call" _nl value:zeroOMoreArgumentsBlockOrExpression
|
||||
{ var variable = h.nodeIdentifier("_", location());
|
||||
return h.nodeLetStatement(variable, value); }
|
||||
return h.nodeLetStatement(variable, value, location()); }
|
||||
|
||||
letStatement
|
||||
= variable:variable _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression
|
||||
{ return h.nodeLetStatement(variable, value) }
|
||||
{ return h.nodeLetStatement(variable, value, location()) }
|
||||
|
||||
defunStatement
|
||||
= variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
|
||||
{ var value = h.nodeLambda(args, body)
|
||||
return h.nodeLetStatement(variable, value) }
|
||||
{ var value = h.nodeLambda(args, body, location(), variable)
|
||||
return h.nodeLetStatement(variable, value, location()) }
|
||||
|
||||
assignmentOp "assignment" = '='
|
||||
|
||||
|
@ -67,16 +67,16 @@ ifthenelse
|
|||
= 'if' __nl condition:logicalAdditive
|
||||
__nl 'then' __nl trueExpression:innerBlockOrExpression
|
||||
__nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression)
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression) }
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression, location()) }
|
||||
|
||||
ternary
|
||||
= condition:logicalAdditive _ '?' _nl trueExpression:logicalAdditive _ ':' _nl falseExpression:(ternary/logicalAdditive)
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression) }
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression, location()) }
|
||||
|
||||
logicalAdditive
|
||||
= head:logicalMultiplicative tail:(_ operator:logicalAdditiveOp _nl arg:logicalMultiplicative {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
logicalAdditiveOp "operator" = '||'
|
||||
|
@ -85,21 +85,21 @@ logicalAdditive
|
|||
logicalMultiplicative
|
||||
= head:equality tail:(_ operator:logicalMultiplicativeOp _nl arg:equality {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
logicalMultiplicativeOp "operator" = '&&'
|
||||
|
||||
equality
|
||||
= left:relational _ operator:equalityOp _nl right:relational
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])}
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right], location())}
|
||||
/ relational
|
||||
|
||||
equalityOp "operator" = '=='/'!='
|
||||
|
||||
relational
|
||||
= left:credibleInterval _ operator:relationalOp _nl right:credibleInterval
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])}
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right], location())}
|
||||
/ credibleInterval
|
||||
|
||||
relationalOp "operator" = '<='/'<'/'>='/'>'
|
||||
|
@ -107,7 +107,7 @@ relational
|
|||
credibleInterval
|
||||
= head:additive tail:(__ operator:credibleIntervalOp __nl arg:additive {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
credibleIntervalOp "operator" = 'to'
|
||||
|
@ -115,7 +115,7 @@ credibleInterval
|
|||
additive
|
||||
= head:multiplicative tail:(_ operator:additiveOp _nl arg:multiplicative {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
additiveOp "operator" = '+' / '-' / '.+' / '.-'
|
||||
|
@ -123,7 +123,7 @@ additive
|
|||
multiplicative
|
||||
= head:power tail:(_ operator:multiplicativeOp _nl arg:power {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
multiplicativeOp "operator" = '*' / '/' / '.*' / './'
|
||||
|
@ -131,7 +131,7 @@ multiplicative
|
|||
power
|
||||
= head:chainFunctionCall tail:(_ operator:powerOp _nl arg:chainFunctionCall {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
|
||||
}, head)}
|
||||
|
||||
powerOp "operator" = '^' / '.^'
|
||||
|
@ -139,7 +139,7 @@ power
|
|||
chainFunctionCall
|
||||
= head:unary tail:(_ ('->'/'|>') _nl chained:chainedFunction {return chained})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(element.fnName, [result, ...element.args])
|
||||
return h.makeFunctionCall(element.fnName, [result, ...element.args], location())
|
||||
}, head)}
|
||||
|
||||
chainedFunction
|
||||
|
@ -154,7 +154,7 @@ chainFunctionCall
|
|||
|
||||
unary
|
||||
= unaryOperator:unaryOperator _nl right:(unary/postOperator)
|
||||
{ return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right])}
|
||||
{ return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right], location())}
|
||||
/ postOperator
|
||||
|
||||
unaryOperator "unary operator"
|
||||
|
@ -169,17 +169,17 @@ collectionElement
|
|||
tail:(
|
||||
_ '[' _nl arg:expression _nl ']' {return {fn: h.postOperatorToFunction['[]'], args: [arg]}}
|
||||
/ _ '(' _nl args:array_functionArguments _nl ')' {return {fn: h.postOperatorToFunction['()'], args: args}}
|
||||
/ '.' arg:$dollarIdentifier {return {fn: h.postOperatorToFunction['[]'], args: [h.nodeString(arg)]}}
|
||||
/ '.' arg:$dollarIdentifier {return {fn: h.postOperatorToFunction['[]'], args: [h.nodeString(arg, location())]}}
|
||||
)*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return h.makeFunctionCall(element.fn, [result, ...element.args])
|
||||
return h.makeFunctionCall(element.fn, [result, ...element.args], location())
|
||||
}, head)}
|
||||
|
||||
array_functionArguments
|
||||
= head:expression tail:(_ ',' _nl @expression)*
|
||||
{ return [head, ...tail]; }
|
||||
/ ""
|
||||
{return [h.nodeVoid()];}
|
||||
{return [h.nodeVoid(location())];}
|
||||
|
||||
atom
|
||||
= '(' _nl expression:expression _nl ')' {return expression}
|
||||
|
@ -195,7 +195,7 @@ basicLiteral
|
|||
/ voidLiteral
|
||||
|
||||
voidLiteral 'void'
|
||||
= "()" {return h.nodeVoid();}
|
||||
= "()" {return h.nodeVoid(location());}
|
||||
|
||||
variable = dollarIdentifierWithModule / dollarIdentifier
|
||||
|
||||
|
@ -221,36 +221,36 @@ dollarIdentifier '$identifier'
|
|||
= ([\$_a-z]+[\$_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
||||
|
||||
moduleIdentifier 'identifier'
|
||||
= ([A-Z]+[_a-z0-9]i*) {return h.nodeModuleIdentifier(text())}
|
||||
= ([A-Z]+[_a-z0-9]i*) {return h.nodeModuleIdentifier(text(), location())}
|
||||
|
||||
|
||||
string 'string'
|
||||
= characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''))}
|
||||
/ characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''))}
|
||||
= characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''), location())}
|
||||
/ characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''), location())}
|
||||
|
||||
number = number:(float / integer) unit:unitIdentifier?
|
||||
{
|
||||
if (unit === null)
|
||||
{ return number }
|
||||
else
|
||||
{ return h.makeFunctionCall('fromUnit_'+unit.value, [number])
|
||||
{ return h.makeFunctionCall('fromUnit_'+unit.value, [number], location())
|
||||
}
|
||||
}
|
||||
|
||||
integer 'integer'
|
||||
= d+ !"\." ![e]i
|
||||
{ return h.nodeInteger(parseInt(text()))}
|
||||
{ return h.nodeInteger(parseInt(text()), location())}
|
||||
|
||||
float 'float'
|
||||
= $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent)
|
||||
{ return h.nodeFloat(parseFloat(text()))}
|
||||
{ return h.nodeFloat(parseFloat(text()), location())}
|
||||
|
||||
floatExponent = [e]i '-'? d+
|
||||
d = [0-9]
|
||||
|
||||
boolean 'boolean'
|
||||
= ('true'/'false') ! [a-z]i ! [_$]
|
||||
{ return h.nodeBoolean(text() === 'true')}
|
||||
{ return h.nodeBoolean(text() === 'true', location())}
|
||||
|
||||
valueConstructor
|
||||
= recordConstructor
|
||||
|
@ -261,15 +261,15 @@ valueConstructor
|
|||
lambda
|
||||
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||
{ statements.push(finalExpression)
|
||||
return h.nodeLambda(args, h.nodeBlock(statements)) }
|
||||
return h.nodeLambda(args, h.nodeBlock(statements, location()), location(), undefined) }
|
||||
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
|
||||
{ return h.nodeLambda(args, finalExpression) }
|
||||
{ return h.nodeLambda(args, finalExpression, location(), undefined) }
|
||||
|
||||
arrayConstructor 'array'
|
||||
= '[' _nl ']'
|
||||
{ return h.constructArray([]); }
|
||||
{ return h.constructArray([], location()); }
|
||||
/ '[' _nl args:array_elements _nl ']'
|
||||
{ return h.constructArray(args); }
|
||||
{ return h.constructArray(args, location()); }
|
||||
|
||||
array_elements
|
||||
= head:expression tail:(_ ',' _nl @expression)*
|
||||
|
@ -277,7 +277,7 @@ arrayConstructor 'array'
|
|||
|
||||
recordConstructor 'record'
|
||||
= '{' _nl args:array_recordArguments _nl end_of_record
|
||||
{ return h.constructRecord(args); }
|
||||
{ return h.constructRecord(args, location()); }
|
||||
|
||||
end_of_record
|
||||
= '}'
|
||||
|
@ -289,7 +289,7 @@ recordConstructor 'record'
|
|||
|
||||
keyValuePair
|
||||
= key:expression _ ':' _nl value:expression
|
||||
{ return h.nodeKeyValue(key, value)}
|
||||
{ return h.nodeKeyValue(key, value, location())}
|
||||
|
||||
// Separators
|
||||
|
||||
|
|
|
@ -1,22 +1,37 @@
|
|||
module Extra = Reducer_Extra
|
||||
open Reducer_ErrorValue
|
||||
|
||||
type node = {"type": string}
|
||||
@genType
|
||||
type locationPoint = {
|
||||
line: int,
|
||||
column: int,
|
||||
}
|
||||
@genType
|
||||
type location = {
|
||||
source: string,
|
||||
start: locationPoint,
|
||||
end: locationPoint,
|
||||
}
|
||||
|
||||
@module("./Reducer_Peggy_GeneratedParser.js") external parse__: string => node = "parse"
|
||||
type node = {"type": string, "location": location}
|
||||
|
||||
type withLocation = {"location": Reducer_ErrorValue.syntaxErrorLocation}
|
||||
type parseError = SyntaxError(string, location)
|
||||
|
||||
type parseResult = result<node, parseError>
|
||||
|
||||
@module("./Reducer_Peggy_GeneratedParser.js")
|
||||
external parse__: (string, {"grammarSource": string}) => node = "parse"
|
||||
|
||||
type withLocation = {"location": location}
|
||||
external castWithLocation: Js.Exn.t => withLocation = "%identity"
|
||||
|
||||
let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.syntaxErrorLocation =>
|
||||
castWithLocation(error)["location"]
|
||||
let syntaxErrorToLocation = (error: Js.Exn.t): location => castWithLocation(error)["location"]
|
||||
|
||||
let parse = (expr: string): result<node, errorValue> =>
|
||||
let parse = (expr: string, source: string): parseResult =>
|
||||
try {
|
||||
Ok(parse__(expr))
|
||||
Ok(parse__(expr, {"grammarSource": source}))
|
||||
} catch {
|
||||
| Js.Exn.Error(obj) =>
|
||||
RESyntaxError(Belt.Option.getExn(Js.Exn.message(obj)), syntaxErrorToLocation(obj)->Some)->Error
|
||||
SyntaxError(Belt.Option.getExn(Js.Exn.message(obj)), syntaxErrorToLocation(obj))->Error
|
||||
}
|
||||
|
||||
type nodeBlock = {...node, "statements": array<node>}
|
||||
|
@ -29,32 +44,35 @@ type nodeIdentifier = {...node, "value": string}
|
|||
type nodeInteger = {...node, "value": int}
|
||||
type nodeKeyValue = {...node, "key": node, "value": node}
|
||||
type nodeRecord = {...node, "elements": array<nodeKeyValue>}
|
||||
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node}
|
||||
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node, "name": option<string>}
|
||||
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
||||
type nodeModuleIdentifier = {...node, "value": string}
|
||||
type nodeString = {...node, "value": string}
|
||||
type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node}
|
||||
// type nodeTypeIdentifier = {...node, "value": string}
|
||||
type nodeVoid = node
|
||||
|
||||
type peggyNode =
|
||||
| PgNodeBlock(nodeBlock)
|
||||
| PgNodeProgram(nodeProgram)
|
||||
| PgNodeArray(nodeArray)
|
||||
| PgNodeRecord(nodeRecord)
|
||||
| PgNodeBoolean(nodeBoolean)
|
||||
| PgNodeFloat(nodeFloat)
|
||||
| PgNodeCall(nodeCall)
|
||||
| PgNodeIdentifier(nodeIdentifier)
|
||||
| PgNodeInteger(nodeInteger)
|
||||
| PgNodeKeyValue(nodeKeyValue)
|
||||
| PgNodeLambda(nodeLambda)
|
||||
| PgNodeLetStatement(nodeLetStatement)
|
||||
| PgNodeModuleIdentifier(nodeModuleIdentifier)
|
||||
| PgNodeString(nodeString)
|
||||
| PgNodeTernary(nodeTernary)
|
||||
// | PgNodeTypeIdentifier(nodeTypeIdentifier)
|
||||
| PgNodeVoid(nodeVoid)
|
||||
type astContent =
|
||||
| ASTBlock(nodeBlock)
|
||||
| ASTProgram(nodeProgram)
|
||||
| ASTArray(nodeArray)
|
||||
| ASTRecord(nodeRecord)
|
||||
| ASTBoolean(nodeBoolean)
|
||||
| ASTFloat(nodeFloat)
|
||||
| ASTCall(nodeCall)
|
||||
| ASTIdentifier(nodeIdentifier)
|
||||
| ASTInteger(nodeInteger)
|
||||
| ASTKeyValue(nodeKeyValue)
|
||||
| ASTLambda(nodeLambda)
|
||||
| ASTLetStatement(nodeLetStatement)
|
||||
| ASTModuleIdentifier(nodeModuleIdentifier)
|
||||
| ASTString(nodeString)
|
||||
| ASTTernary(nodeTernary)
|
||||
| ASTVoid(nodeVoid)
|
||||
|
||||
type ast = {
|
||||
location: location,
|
||||
content: astContent,
|
||||
}
|
||||
|
||||
external castNodeBlock: node => nodeBlock = "%identity"
|
||||
external castNodeProgram: node => nodeProgram = "%identity"
|
||||
|
@ -71,80 +89,92 @@ external castNodeLetStatement: node => nodeLetStatement = "%identity"
|
|||
external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity"
|
||||
external castNodeString: node => nodeString = "%identity"
|
||||
external castNodeTernary: node => nodeTernary = "%identity"
|
||||
// external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity"
|
||||
external castNodeVoid: node => nodeVoid = "%identity"
|
||||
|
||||
exception UnsupportedPeggyNodeType(string) // This should never happen; programming error
|
||||
let castNodeType = (node: node) =>
|
||||
switch node["type"] {
|
||||
| "Block" => node->castNodeBlock->PgNodeBlock
|
||||
| "Program" => node->castNodeBlock->PgNodeProgram
|
||||
| "Array" => node->castNodeArray->PgNodeArray
|
||||
| "Record" => node->castNodeRecord->PgNodeRecord
|
||||
| "Boolean" => node->castNodeBoolean->PgNodeBoolean
|
||||
| "Call" => node->castNodeCall->PgNodeCall
|
||||
| "Float" => node->castNodeFloat->PgNodeFloat
|
||||
| "Identifier" => node->castNodeIdentifier->PgNodeIdentifier
|
||||
| "Integer" => node->castNodeInteger->PgNodeInteger
|
||||
| "KeyValue" => node->castNodeKeyValue->PgNodeKeyValue
|
||||
| "Lambda" => node->castNodeLambda->PgNodeLambda
|
||||
| "LetStatement" => node->castNodeLetStatement->PgNodeLetStatement
|
||||
| "ModuleIdentifier" => node->castNodeModuleIdentifier->PgNodeModuleIdentifier
|
||||
| "String" => node->castNodeString->PgNodeString
|
||||
| "Ternary" => node->castNodeTernary->PgNodeTernary
|
||||
// | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier
|
||||
| "Void" => node->castNodeVoid->PgNodeVoid
|
||||
let nodeToAST = (node: node) => {
|
||||
let content = switch node["type"] {
|
||||
| "Block" => node->castNodeBlock->ASTBlock
|
||||
| "Program" => node->castNodeBlock->ASTProgram
|
||||
| "Array" => node->castNodeArray->ASTArray
|
||||
| "Record" => node->castNodeRecord->ASTRecord
|
||||
| "Boolean" => node->castNodeBoolean->ASTBoolean
|
||||
| "Call" => node->castNodeCall->ASTCall
|
||||
| "Float" => node->castNodeFloat->ASTFloat
|
||||
| "Identifier" => node->castNodeIdentifier->ASTIdentifier
|
||||
| "Integer" => node->castNodeInteger->ASTInteger
|
||||
| "KeyValue" => node->castNodeKeyValue->ASTKeyValue
|
||||
| "Lambda" => node->castNodeLambda->ASTLambda
|
||||
| "LetStatement" => node->castNodeLetStatement->ASTLetStatement
|
||||
| "ModuleIdentifier" => node->castNodeModuleIdentifier->ASTModuleIdentifier
|
||||
| "String" => node->castNodeString->ASTString
|
||||
| "Ternary" => node->castNodeTernary->ASTTernary
|
||||
| "Void" => node->castNodeVoid->ASTVoid
|
||||
| _ => raise(UnsupportedPeggyNodeType(node["type"]))
|
||||
}
|
||||
|
||||
let rec pgToString = (peggyNode: peggyNode): string => {
|
||||
{location: node["location"], content: content}
|
||||
}
|
||||
|
||||
let nodeIdentifierToAST = (node: nodeIdentifier) => {
|
||||
{location: node["location"], content: node->ASTIdentifier}
|
||||
}
|
||||
|
||||
let nodeKeyValueToAST = (node: nodeKeyValue) => {
|
||||
{location: node["location"], content: node->ASTKeyValue}
|
||||
}
|
||||
|
||||
let rec pgToString = (ast: ast): string => {
|
||||
let argsToString = (args: array<nodeIdentifier>): string =>
|
||||
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString
|
||||
args->Belt.Array.map(arg => arg->nodeIdentifierToAST->pgToString)->Js.Array2.toString
|
||||
|
||||
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
|
||||
nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||
nodes->Belt.Array.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||
|
||||
let pgNodesToStringUsingSeparator = (nodes: array<peggyNode>, separator: string): string =>
|
||||
nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||
let pgNodesToStringUsingSeparator = (nodes: array<ast>, separator: string): string =>
|
||||
nodes->Belt.Array.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
|
||||
|
||||
switch peggyNode {
|
||||
| PgNodeBlock(node)
|
||||
| PgNodeProgram(node) =>
|
||||
switch ast.content {
|
||||
| ASTBlock(node)
|
||||
| ASTProgram(node) =>
|
||||
"{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
|
||||
| PgNodeArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]"
|
||||
| PgNodeRecord(node) =>
|
||||
| ASTArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]"
|
||||
| ASTRecord(node) =>
|
||||
"{" ++
|
||||
node["elements"]
|
||||
->Js.Array2.map(element => PgNodeKeyValue(element))
|
||||
->Belt.Array.map(element => element->nodeKeyValueToAST)
|
||||
->pgNodesToStringUsingSeparator(", ") ++ "}"
|
||||
| PgNodeBoolean(node) => node["value"]->Js.String.make
|
||||
| PgNodeCall(node) =>
|
||||
| ASTBoolean(node) => node["value"]->Js.String.make
|
||||
| ASTCall(node) =>
|
||||
"(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")"
|
||||
| PgNodeFloat(node) => node["value"]->Js.String.make
|
||||
| PgNodeIdentifier(node) => `:${node["value"]}`
|
||||
| PgNodeInteger(node) => node["value"]->Js.String.make
|
||||
| PgNodeKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"])
|
||||
| PgNodeLambda(node) =>
|
||||
"{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}"
|
||||
| PgNodeLetStatement(node) =>
|
||||
pgToString(PgNodeIdentifier(node["variable"])) ++ " = " ++ toString(node["value"])
|
||||
| PgNodeModuleIdentifier(node) => `@${node["value"]}`
|
||||
| PgNodeString(node) => `'${node["value"]->Js.String.make}'`
|
||||
| PgNodeTernary(node) =>
|
||||
| ASTFloat(node) => node["value"]->Js.String.make
|
||||
| ASTIdentifier(node) => `:${node["value"]}`
|
||||
| ASTInteger(node) => node["value"]->Js.String.make
|
||||
| ASTKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"])
|
||||
| ASTLambda(node) => "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}"
|
||||
| ASTLetStatement(node) =>
|
||||
pgToString(node["variable"]->nodeIdentifierToAST) ++ " = " ++ toString(node["value"])
|
||||
| ASTModuleIdentifier(node) => `@${node["value"]}`
|
||||
| ASTString(node) => `'${node["value"]->Js.String.make}'`
|
||||
| ASTTernary(node) =>
|
||||
"(::$$_ternary_$$ " ++
|
||||
toString(node["condition"]) ++
|
||||
" " ++
|
||||
toString(node["trueExpression"]) ++
|
||||
" " ++
|
||||
toString(node["falseExpression"]) ++ ")"
|
||||
// | PgNodeTypeIdentifier(node) => `#${node["value"]}`
|
||||
| PgNodeVoid(_node) => "()"
|
||||
| ASTVoid(_node) => "()"
|
||||
}
|
||||
}
|
||||
and toString = (node: node): string => node->castNodeType->pgToString
|
||||
and toString = (node: node): string => node->nodeToAST->pgToString
|
||||
|
||||
let toStringResult = (rNode: result<node, errorValue>): string =>
|
||||
switch rNode {
|
||||
| Ok(node) => toString(node)
|
||||
| Error(error) => `Error(${errorToString(error)})`
|
||||
let toStringError = (error: parseError): string => {
|
||||
let SyntaxError(message, _) = error
|
||||
`Syntax Error: ${message}}`
|
||||
}
|
||||
|
||||
let toStringResult = (rNode: parseResult): string =>
|
||||
switch rNode {
|
||||
| Ok(node) => node->toString
|
||||
| Error(error) => `Error(${error->toStringError})`
|
||||
}
|
||||
|
|
|
@ -3,23 +3,27 @@ module ExpressionT = Reducer_Expression_T
|
|||
module Parse = Reducer_Peggy_Parse
|
||||
|
||||
type expression = Reducer_T.expression
|
||||
type expressionContent = Reducer_T.expressionContent
|
||||
|
||||
let rec fromNode = (node: Parse.node): expression => {
|
||||
let ast = Parse.nodeToAST(node)
|
||||
|
||||
let content: expressionContent = {
|
||||
let caseBlock = nodeBlock =>
|
||||
ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode))
|
||||
|
||||
let caseProgram = nodeProgram =>
|
||||
ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(fromNode))
|
||||
|
||||
let caseLambda = (nodeLambda: Parse.nodeLambda): expression => {
|
||||
let caseLambda = (nodeLambda: Parse.nodeLambda): expressionContent => {
|
||||
let args =
|
||||
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
|
||||
let body = nodeLambda["body"]->fromNode
|
||||
|
||||
ExpressionBuilder.eLambda(args, body)
|
||||
ExpressionBuilder.eLambda(args, body, nodeLambda["name"])
|
||||
}
|
||||
|
||||
let caseRecord = (nodeRecord): expression => {
|
||||
let caseRecord = (nodeRecord): expressionContent => {
|
||||
nodeRecord["elements"]
|
||||
->Js.Array2.map(keyValueNode => (
|
||||
keyValueNode["key"]->fromNode,
|
||||
|
@ -28,30 +32,30 @@ let rec fromNode = (node: Parse.node): expression => {
|
|||
->ExpressionBuilder.eRecord
|
||||
}
|
||||
|
||||
switch Parse.castNodeType(node) {
|
||||
| PgNodeBlock(nodeBlock) => caseBlock(nodeBlock)
|
||||
| PgNodeProgram(nodeProgram) => caseProgram(nodeProgram)
|
||||
| PgNodeArray(nodeArray) =>
|
||||
switch ast.content {
|
||||
| ASTBlock(nodeBlock) => caseBlock(nodeBlock)
|
||||
| ASTProgram(nodeProgram) => caseProgram(nodeProgram)
|
||||
| ASTArray(nodeArray) =>
|
||||
ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode))
|
||||
| PgNodeRecord(nodeRecord) => caseRecord(nodeRecord)
|
||||
| PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"])
|
||||
| PgNodeCall(nodeCall) =>
|
||||
| ASTRecord(nodeRecord) => caseRecord(nodeRecord)
|
||||
| ASTBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"])
|
||||
| ASTCall(nodeCall) =>
|
||||
ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode))
|
||||
| PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"])
|
||||
| PgNodeIdentifier(nodeIdentifier) => ExpressionBuilder.eSymbol(nodeIdentifier["value"])
|
||||
| PgNodeInteger(nodeInteger) => ExpressionBuilder.eNumber(Belt.Int.toFloat(nodeInteger["value"]))
|
||||
| PgNodeKeyValue(nodeKeyValue) =>
|
||||
| ASTFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"])
|
||||
| ASTIdentifier(nodeIdentifier) => ExpressionBuilder.eSymbol(nodeIdentifier["value"])
|
||||
| ASTInteger(nodeInteger) => ExpressionBuilder.eNumber(Belt.Int.toFloat(nodeInteger["value"]))
|
||||
| ASTKeyValue(nodeKeyValue) =>
|
||||
ExpressionBuilder.eArray([fromNode(nodeKeyValue["key"]), fromNode(nodeKeyValue["value"])])
|
||||
| PgNodeLambda(nodeLambda) => caseLambda(nodeLambda)
|
||||
| PgNodeLetStatement(nodeLetStatement) =>
|
||||
| ASTLambda(nodeLambda) => caseLambda(nodeLambda)
|
||||
| ASTLetStatement(nodeLetStatement) =>
|
||||
ExpressionBuilder.eLetStatement(
|
||||
nodeLetStatement["variable"]["value"],
|
||||
fromNode(nodeLetStatement["value"]),
|
||||
)
|
||||
| PgNodeModuleIdentifier(nodeModuleIdentifier) =>
|
||||
| ASTModuleIdentifier(nodeModuleIdentifier) =>
|
||||
ExpressionBuilder.eIdentifier(nodeModuleIdentifier["value"])
|
||||
| PgNodeString(nodeString) => ExpressionBuilder.eString(nodeString["value"])
|
||||
| PgNodeTernary(nodeTernary) =>
|
||||
| ASTString(nodeString) => ExpressionBuilder.eString(nodeString["value"])
|
||||
| ASTTernary(nodeTernary) =>
|
||||
ExpressionBuilder.eTernary(
|
||||
fromNode(nodeTernary["condition"]),
|
||||
fromNode(nodeTernary["trueExpression"]),
|
||||
|
@ -59,6 +63,12 @@ let rec fromNode = (node: Parse.node): expression => {
|
|||
)
|
||||
// | PgNodeTypeIdentifier(nodeTypeIdentifier) =>
|
||||
// ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"])
|
||||
| PgNodeVoid(_) => ExpressionBuilder.eVoid
|
||||
| ASTVoid(_) => ExpressionBuilder.eVoid
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ast: ast,
|
||||
content: content,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,83 +34,92 @@ export const postOperatorToFunction = {
|
|||
"[]": "$_atIndex_$",
|
||||
};
|
||||
|
||||
type NodeBlock = {
|
||||
type Node = {
|
||||
location: LocationRange;
|
||||
};
|
||||
|
||||
type NodeBlock = Node & {
|
||||
type: "Block";
|
||||
statements: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeProgram = {
|
||||
type NodeProgram = Node & {
|
||||
type: "Program";
|
||||
statements: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeArray = {
|
||||
type NodeArray = Node & {
|
||||
type: "Array";
|
||||
elements: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeRecord = {
|
||||
type NodeRecord = Node & {
|
||||
type: "Record";
|
||||
elements: NodeKeyValue[];
|
||||
};
|
||||
|
||||
type NodeCall = {
|
||||
type NodeCall = Node & {
|
||||
type: "Call";
|
||||
fn: AnyPeggyNode;
|
||||
args: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeFloat = {
|
||||
type NodeFloat = Node & {
|
||||
type: "Float";
|
||||
value: number;
|
||||
};
|
||||
|
||||
type NodeInteger = {
|
||||
type NodeInteger = Node & {
|
||||
type: "Integer";
|
||||
value: number;
|
||||
};
|
||||
|
||||
type NodeIdentifier = {
|
||||
type NodeIdentifier = Node & {
|
||||
type: "Identifier";
|
||||
value: string;
|
||||
};
|
||||
|
||||
type NodeLetStatement = {
|
||||
type NodeLetStatement = Node & {
|
||||
type: "LetStatement";
|
||||
variable: NodeIdentifier;
|
||||
value: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeLambda = {
|
||||
type NodeLambda = Node & {
|
||||
type: "Lambda";
|
||||
args: AnyPeggyNode[];
|
||||
body: AnyPeggyNode;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
type NodeTernary = {
|
||||
type NodeTernary = Node & {
|
||||
type: "Ternary";
|
||||
condition: AnyPeggyNode;
|
||||
trueExpression: AnyPeggyNode;
|
||||
falseExpression: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeKeyValue = {
|
||||
type NodeKeyValue = Node & {
|
||||
type: "KeyValue";
|
||||
key: AnyPeggyNode;
|
||||
value: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeString = {
|
||||
type NodeString = Node & {
|
||||
type: "String";
|
||||
value: string;
|
||||
location?: LocationRange;
|
||||
};
|
||||
|
||||
type NodeBoolean = {
|
||||
type NodeBoolean = Node & {
|
||||
type: "Boolean";
|
||||
value: boolean;
|
||||
};
|
||||
|
||||
type NodeVoid = Node & {
|
||||
type: "Void";
|
||||
};
|
||||
|
||||
export type AnyPeggyNode =
|
||||
| NodeArray
|
||||
| NodeRecord
|
||||
|
@ -125,47 +134,78 @@ export type AnyPeggyNode =
|
|||
| NodeTernary
|
||||
| NodeKeyValue
|
||||
| NodeString
|
||||
| NodeBoolean;
|
||||
| NodeBoolean
|
||||
| NodeVoid;
|
||||
|
||||
export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) {
|
||||
export function makeFunctionCall(
|
||||
fn: string,
|
||||
args: AnyPeggyNode[],
|
||||
location: LocationRange
|
||||
) {
|
||||
if (fn === "$$_applyAll_$$") {
|
||||
return nodeCall(args[0], args.splice(1));
|
||||
return nodeCall(args[0], args.splice(1), location);
|
||||
} else {
|
||||
return nodeCall(nodeIdentifier(fn), args);
|
||||
return nodeCall(nodeIdentifier(fn, location), args, location);
|
||||
}
|
||||
}
|
||||
|
||||
export function constructArray(elements: AnyPeggyNode[]) {
|
||||
return { type: "Array", elements };
|
||||
export function constructArray(
|
||||
elements: AnyPeggyNode[],
|
||||
location: LocationRange
|
||||
): NodeArray {
|
||||
return { type: "Array", elements, location };
|
||||
}
|
||||
export function constructRecord(elements: AnyPeggyNode[]) {
|
||||
return { type: "Record", elements };
|
||||
export function constructRecord(
|
||||
elements: NodeKeyValue[],
|
||||
location: LocationRange
|
||||
): NodeRecord {
|
||||
return { type: "Record", elements, location };
|
||||
}
|
||||
|
||||
export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock {
|
||||
return { type: "Block", statements };
|
||||
export function nodeBlock(
|
||||
statements: AnyPeggyNode[],
|
||||
location: LocationRange
|
||||
): NodeBlock {
|
||||
return { type: "Block", statements, location };
|
||||
}
|
||||
export function nodeProgram(statements: AnyPeggyNode[]): NodeProgram {
|
||||
return { type: "Program", statements };
|
||||
export function nodeProgram(
|
||||
statements: AnyPeggyNode[],
|
||||
location: LocationRange
|
||||
): NodeProgram {
|
||||
return { type: "Program", statements, location };
|
||||
}
|
||||
export function nodeBoolean(value: boolean): NodeBoolean {
|
||||
return { type: "Boolean", value };
|
||||
export function nodeBoolean(
|
||||
value: boolean,
|
||||
location: LocationRange
|
||||
): NodeBoolean {
|
||||
return { type: "Boolean", value, location };
|
||||
}
|
||||
export function nodeCall(fn: AnyPeggyNode, args: AnyPeggyNode[]): NodeCall {
|
||||
return { type: "Call", fn, args };
|
||||
export function nodeCall(
|
||||
fn: AnyPeggyNode,
|
||||
args: AnyPeggyNode[],
|
||||
location: LocationRange
|
||||
): NodeCall {
|
||||
return { type: "Call", fn, args, location };
|
||||
}
|
||||
export function nodeFloat(value: number): NodeFloat {
|
||||
return { type: "Float", value };
|
||||
export function nodeFloat(value: number, location: LocationRange): NodeFloat {
|
||||
return { type: "Float", value, location };
|
||||
}
|
||||
export function nodeIdentifier(value: string): NodeIdentifier {
|
||||
return { type: "Identifier", value };
|
||||
export function nodeIdentifier(
|
||||
value: string,
|
||||
location: LocationRange
|
||||
): NodeIdentifier {
|
||||
return { type: "Identifier", value, location };
|
||||
}
|
||||
export function nodeInteger(value: number): NodeInteger {
|
||||
return { type: "Integer", value };
|
||||
export function nodeInteger(
|
||||
value: number,
|
||||
location: LocationRange
|
||||
): NodeInteger {
|
||||
return { type: "Integer", value, location };
|
||||
}
|
||||
export function nodeKeyValue(
|
||||
key: AnyPeggyNode,
|
||||
value: AnyPeggyNode
|
||||
value: AnyPeggyNode,
|
||||
location: LocationRange
|
||||
): NodeKeyValue {
|
||||
if (key.type === "Identifier") {
|
||||
key = {
|
||||
|
@ -173,43 +213,46 @@ export function nodeKeyValue(
|
|||
type: "String",
|
||||
};
|
||||
}
|
||||
return { type: "KeyValue", key, value };
|
||||
return { type: "KeyValue", key, value, location };
|
||||
}
|
||||
export function nodeLambda(
|
||||
args: AnyPeggyNode[],
|
||||
body: AnyPeggyNode
|
||||
body: AnyPeggyNode,
|
||||
location: LocationRange,
|
||||
name?: NodeIdentifier
|
||||
): NodeLambda {
|
||||
return { type: "Lambda", args, body };
|
||||
return { type: "Lambda", args, body, location, name: name?.value };
|
||||
}
|
||||
export function nodeLetStatement(
|
||||
variable: NodeIdentifier,
|
||||
value: AnyPeggyNode
|
||||
value: AnyPeggyNode,
|
||||
location: LocationRange
|
||||
): NodeLetStatement {
|
||||
return { type: "LetStatement", variable, value };
|
||||
const patchedValue =
|
||||
value.type === "Lambda" ? { ...value, name: variable.value } : value;
|
||||
return { type: "LetStatement", variable, value: patchedValue, location };
|
||||
}
|
||||
export function nodeModuleIdentifier(value: string) {
|
||||
return { type: "ModuleIdentifier", value };
|
||||
export function nodeModuleIdentifier(value: string, location: LocationRange) {
|
||||
return { type: "ModuleIdentifier", value, location };
|
||||
}
|
||||
export function nodeString(value: string): NodeString {
|
||||
return { type: "String", value };
|
||||
export function nodeString(value: string, location: LocationRange): NodeString {
|
||||
return { type: "String", value, location };
|
||||
}
|
||||
export function nodeTernary(
|
||||
condition: AnyPeggyNode,
|
||||
trueExpression: AnyPeggyNode,
|
||||
falseExpression: AnyPeggyNode
|
||||
falseExpression: AnyPeggyNode,
|
||||
location: LocationRange
|
||||
): NodeTernary {
|
||||
return {
|
||||
type: "Ternary",
|
||||
condition,
|
||||
trueExpression,
|
||||
falseExpression,
|
||||
location,
|
||||
};
|
||||
}
|
||||
|
||||
export function nodeTypeIdentifier(typeValue: string) {
|
||||
return { type: "TypeIdentifier", value: typeValue };
|
||||
}
|
||||
|
||||
export function nodeVoid() {
|
||||
return { type: "Void" };
|
||||
export function nodeVoid(location: LocationRange): NodeVoid {
|
||||
return { type: "Void", location };
|
||||
}
|
||||
|
|
|
@ -15,14 +15,18 @@ type rec value =
|
|||
| IEvVoid
|
||||
@genType.opaque and arrayValue = array<value>
|
||||
@genType.opaque and map = Belt.Map.String.t<value>
|
||||
and lambdaBody = (array<value>, environment, reducerFn) => value
|
||||
and lambdaBody = (array<value>, context, reducerFn) => value
|
||||
@genType.opaque
|
||||
and lambdaValue = {
|
||||
and lambdaValue =
|
||||
| FnLambda({
|
||||
parameters: array<string>,
|
||||
body: lambdaBody,
|
||||
}
|
||||
location: Reducer_Peggy_Parse.location,
|
||||
name: option<string>,
|
||||
})
|
||||
| FnBuiltin({body: lambdaBody, name: string})
|
||||
@genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue>
|
||||
and expression =
|
||||
and expressionContent =
|
||||
| EBlock(array<expression>)
|
||||
// programs are similar to blocks, but don't create an inner scope. there can be only one program at the top level of the expression.
|
||||
| EProgram(array<expression>)
|
||||
|
@ -32,18 +36,34 @@ and expression =
|
|||
| ETernary(expression, expression, expression)
|
||||
| EAssign(string, expression)
|
||||
| ECall(expression, array<expression>)
|
||||
| ELambda(array<string>, expression)
|
||||
| ELambda(array<string>, expression, option<string>)
|
||||
| EValue(value)
|
||||
|
||||
and expression = {
|
||||
ast: Reducer_Peggy_Parse.ast,
|
||||
content: expressionContent,
|
||||
}
|
||||
|
||||
and namespace = Belt.Map.String.t<value>
|
||||
and bindings = {
|
||||
namespace: namespace,
|
||||
parent: option<bindings>,
|
||||
}
|
||||
|
||||
@genType.opaque
|
||||
and frame = {
|
||||
name: string,
|
||||
location: option<Reducer_Peggy_Parse.location>, // can be empty for calls from builtin functions
|
||||
}
|
||||
@genType.opaque and frameStack = list<frame>
|
||||
|
||||
and context = {
|
||||
bindings: bindings,
|
||||
environment: environment,
|
||||
frameStack: frameStack,
|
||||
inFunction: option<lambdaValue>,
|
||||
}
|
||||
|
||||
and reducerFn = (expression, context) => (value, context)
|
||||
|
||||
let topFrameName = "<top>"
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
// deprecated, use Reducer_T instead
|
||||
// (value methods should be moved to Reducer_Value.res)
|
||||
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
type environment = GenericDist.env
|
||||
module T = Reducer_T
|
||||
|
||||
|
@ -32,10 +28,12 @@ and toStringCall = fName => `:${fName}`
|
|||
and toStringDate = date => DateTime.Date.toString(date)
|
||||
and toStringDeclaration = d => Declaration.toString(d, r => toString(IEvLambda(r)))
|
||||
and toStringDistribution = dist => GenericDist.toString(dist)
|
||||
and toStringLambda = (lambdaValue: T.lambdaValue) =>
|
||||
`lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
|
||||
and toStringFunction = (lambdaValue: T.lambdaValue) =>
|
||||
`function(${Js.Array2.toString(lambdaValue.parameters)})`
|
||||
and toStringLambda = (lambdaValue: T.lambdaValue) => {
|
||||
switch lambdaValue {
|
||||
| FnLambda({parameters}) => `lambda(${Js.Array2.toString(parameters)}=>internal code)`
|
||||
| FnBuiltin(_) => "Builtin function"
|
||||
}
|
||||
}
|
||||
and toStringNumber = aNumber => Js.String.make(aNumber)
|
||||
and toStringRecord = aMap => aMap->toStringMap
|
||||
and toStringString = aString => `'${aString}'`
|
||||
|
@ -76,25 +74,13 @@ let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})
|
|||
let toStringResult = x =>
|
||||
switch x {
|
||||
| Ok(a) => `Ok(${toString(a)})`
|
||||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||
| Error(m) => `Error(${SqError.toString(m)})`
|
||||
}
|
||||
|
||||
let toStringOptionResult = x =>
|
||||
switch x {
|
||||
| Some(a) => toStringResult(a)
|
||||
| None => "None"
|
||||
}
|
||||
|
||||
let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string =>
|
||||
let toStringResultOkless = (codeResult: result<t, SqError.t>): string =>
|
||||
switch codeResult {
|
||||
| Ok(a) => toString(a)
|
||||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||
}
|
||||
|
||||
let toStringResultRecord = x =>
|
||||
switch x {
|
||||
| Ok(a) => `Ok(${toStringMap(a)})`
|
||||
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
|
||||
| Error(m) => `Error(${SqError.toString(m)})`
|
||||
}
|
||||
|
||||
type internalExpressionValueType =
|
||||
|
@ -156,10 +142,10 @@ let functionCallSignatureToString = (functionCallSignature: functionCallSignatur
|
|||
|
||||
let arrayToValueArray = (arr: array<t>): array<t> => arr
|
||||
|
||||
let resultToValue = (rExpression: result<t, Reducer_ErrorValue.t>): t =>
|
||||
let resultToValue = (rExpression: result<t, SqError.Message.t>): t =>
|
||||
switch rExpression {
|
||||
| Ok(expression) => expression
|
||||
| Error(errorValue) => Reducer_ErrorValue.toException(errorValue)
|
||||
| Error(errorValue) => SqError.Message.throw(errorValue)
|
||||
}
|
||||
|
||||
let recordToKeyValuePairs = (record: T.map): array<(string, t)> => record->Belt.Map.String.toArray
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// TODO: Auto clean project based on topology
|
||||
|
||||
module Bindings = Reducer_Bindings
|
||||
module ErrorValue = Reducer_ErrorValue
|
||||
module ProjectItem = ReducerProject_ProjectItem
|
||||
module T = ReducerProject_T
|
||||
module Topology = ReducerProject_Topology
|
||||
|
@ -121,7 +120,7 @@ let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType =
|
|||
|
||||
let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType =>
|
||||
switch getResultOption(project, sourceId) {
|
||||
| None => RENeedToRun->Error
|
||||
| None => RENeedToRun->SqError.fromMessage->Error
|
||||
| Some(result) => result
|
||||
}
|
||||
|
||||
|
@ -175,7 +174,7 @@ let linkDependencies = (project: t, sourceId: string): Reducer_T.namespace => {
|
|||
"__result__",
|
||||
switch project->getResult(id) {
|
||||
| Ok(result) => result
|
||||
| Error(error) => error->Reducer_ErrorValue.ErrorException->raise
|
||||
| Error(error) => error->SqError.throw
|
||||
},
|
||||
),
|
||||
])
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
@module("./ReducerProject_IncludeParser.js")
|
||||
external parse__: string => array<array<string>> = "parse"
|
||||
|
||||
let parseIncludes = (expr: string): result<
|
||||
array<(string, string)>,
|
||||
Reducer_ErrorValue.errorValue,
|
||||
> =>
|
||||
let parseIncludes = (expr: string): result<array<(string, string)>, SqError.t> =>
|
||||
try {
|
||||
let answer = parse__(expr)
|
||||
// let logEntry = answer->Js.Array2.joinWith(",")
|
||||
|
@ -12,8 +9,9 @@ let parseIncludes = (expr: string): result<
|
|||
Belt.Array.map(answer, item => (item[0], item[1]))->Ok
|
||||
} catch {
|
||||
| Js.Exn.Error(obj) =>
|
||||
RESyntaxError(
|
||||
Belt.Option.getExn(Js.Exn.message(obj)),
|
||||
Reducer_Peggy_Parse.syntaxErrorToLocation(obj)->Some,
|
||||
)->Error
|
||||
RESyntaxError(Belt.Option.getExn(Js.Exn.message(obj)))
|
||||
->SqError.fromMessageWithFrameStack(
|
||||
Reducer_FrameStack.makeSingleFrameStack(Reducer_Peggy_Parse.syntaxErrorToLocation(obj)),
|
||||
)
|
||||
->Error
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ module T = ReducerProject_ProjectItem_T
|
|||
type projectItem = T.projectItem
|
||||
type t = T.t
|
||||
|
||||
let emptyItem: projectItem = {
|
||||
let emptyItem = (sourceId: string): projectItem => {
|
||||
source: "",
|
||||
sourceId: sourceId,
|
||||
rawParse: None,
|
||||
expression: None,
|
||||
continuation: Reducer_Namespace.make(),
|
||||
|
@ -18,6 +19,7 @@ let emptyItem: projectItem = {
|
|||
// source -> rawParse -> includes -> expression -> continuation -> result
|
||||
|
||||
let getSource = (r: t): T.sourceType => r.source
|
||||
let getSourceId = (r: t): T.sourceType => r.sourceId
|
||||
let getRawParse = (r: t): T.rawParseType => r.rawParse
|
||||
let getExpression = (r: t): T.expressionType => r.expression
|
||||
let getContinuation = (r: t): T.continuationArgumentType => r.continuation
|
||||
|
@ -29,7 +31,7 @@ let getDirectIncludes = (r: t): array<string> => r.directIncludes
|
|||
let getIncludesAsVariables = (r: t): T.importAsVariablesType => r.includeAsVariables
|
||||
|
||||
let touchSource = (this: t): t => {
|
||||
let r = emptyItem
|
||||
let r = emptyItem(this->getSourceId)
|
||||
{
|
||||
...r,
|
||||
source: getSource(this),
|
||||
|
@ -41,8 +43,9 @@ let touchSource = (this: t): t => {
|
|||
}
|
||||
|
||||
let touchRawParse = (this: t): t => {
|
||||
let r = emptyItem(this->getSourceId)
|
||||
{
|
||||
...emptyItem,
|
||||
...r,
|
||||
source: getSource(this),
|
||||
continues: getContinues(this),
|
||||
includes: getIncludes(this),
|
||||
|
@ -148,7 +151,8 @@ let parseIncludes = (this: t): t => {
|
|||
}
|
||||
}
|
||||
}
|
||||
let doRawParse = (this: t): T.rawParseArgumentType => this->getSource->Reducer_Peggy_Parse.parse
|
||||
let doRawParse = (this: t): T.rawParseArgumentType =>
|
||||
this->getSource->Reducer_Peggy_Parse.parse(this.sourceId)->E.R2.errMap(SqError.fromParseError)
|
||||
|
||||
let rawParse = (this: t): t =>
|
||||
this->getRawParse->E.O2.defaultFn(() => doRawParse(this))->setRawParse(this, _)
|
||||
|
@ -167,7 +171,7 @@ let buildExpression = (this: t): t => {
|
|||
}
|
||||
}
|
||||
|
||||
let failRun = (this: t, e: Reducer_ErrorValue.errorValue): t =>
|
||||
let failRun = (this: t, e: SqError.t): t =>
|
||||
this->setResult(e->Error)->setContinuation(Reducer_Namespace.make())
|
||||
|
||||
let doRun = (this: t, context: Reducer_T.context): t =>
|
||||
|
@ -181,12 +185,11 @@ let doRun = (this: t, context: Reducer_T.context): t =>
|
|||
->setResult(result->Ok)
|
||||
->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals)
|
||||
} catch {
|
||||
| Reducer_ErrorValue.ErrorException(e) => this->failRun(e)
|
||||
| _ => this->failRun(RETodo("unhandled rescript exception"))
|
||||
| e => this->failRun(e->SqError.fromException)
|
||||
}
|
||||
| Error(e) => this->failRun(e)
|
||||
}
|
||||
| None => this->failRun(RETodo("attempt to run without expression"))
|
||||
| None => this->failRun(RETodo("attempt to run without expression")->SqError.fromMessage)
|
||||
}
|
||||
|
||||
let run = (this: t, context: Reducer_T.context): t => {
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
module Parse = Reducer_Peggy_Parse
|
||||
module ExpressionT = Reducer_Expression_T
|
||||
open Reducer_ErrorValue
|
||||
|
||||
type sourceArgumentType = string
|
||||
type sourceType = string
|
||||
type rawParseArgumentType = result<Parse.node, errorValue>
|
||||
type rawParseArgumentType = result<Parse.node, SqError.t>
|
||||
type rawParseType = option<rawParseArgumentType>
|
||||
type expressionArgumentType = result<ExpressionT.t, errorValue>
|
||||
type expressionArgumentType = result<ExpressionT.t, SqError.t>
|
||||
type expressionType = option<expressionArgumentType>
|
||||
type continuationArgumentType = Reducer_T.namespace
|
||||
type continuationType = option<continuationArgumentType>
|
||||
type continuationResultType = option<result<continuationArgumentType, errorValue>>
|
||||
type resultArgumentType = result<Reducer_T.value, errorValue>
|
||||
type resultArgumentType = result<Reducer_T.value, SqError.t>
|
||||
type resultType = option<resultArgumentType>
|
||||
type continuesArgumentType = array<string>
|
||||
type continuesType = array<string>
|
||||
type includesArgumentType = string
|
||||
type includesType = result<array<string>, errorValue>
|
||||
type includesType = result<array<string>, SqError.t>
|
||||
type importAsVariablesType = array<(string, string)>
|
||||
|
||||
type projectItem = {
|
||||
source: sourceType,
|
||||
sourceId: string,
|
||||
rawParse: rawParseType,
|
||||
expression: expressionType,
|
||||
continuation: continuationArgumentType,
|
||||
|
|
|
@ -13,4 +13,4 @@ type t = project
|
|||
let getSourceIds = (project: t): array<string> => Belt.MutableMap.String.keysToArray(project.items)
|
||||
|
||||
let getItem = (project: t, sourceId: string) =>
|
||||
Belt.MutableMap.String.getWithDefault(project.items, sourceId, ProjectItem.emptyItem)
|
||||
Belt.MutableMap.String.getWithDefault(project.items, sourceId, ProjectItem.emptyItem(sourceId))
|
||||
|
|
159
packages/squiggle-lang/src/rescript/SqError.res
Normal file
159
packages/squiggle-lang/src/rescript/SqError.res
Normal file
|
@ -0,0 +1,159 @@
|
|||
type location = Reducer_Peggy_Parse.location
|
||||
|
||||
// Messages don't contain any stack trace information.
|
||||
// FunctionRegistry functions are allowed to throw MessageExceptions, though,
|
||||
// because they will be caught and rewrapped by Reducer_Lambda code.
|
||||
module Message = {
|
||||
@genType.opaque
|
||||
type t =
|
||||
| REArityError(option<string>, int, int)
|
||||
| REArrayIndexNotFound(string, int)
|
||||
| REAssignmentExpected
|
||||
| REDistributionError(DistributionTypes.error)
|
||||
| REExpectedType(string, string)
|
||||
| REExpressionExpected
|
||||
| REFunctionExpected(string)
|
||||
| REFunctionNotFound(string)
|
||||
| REJavaScriptExn(option<string>, option<string>) // Javascript Exception
|
||||
| REMacroNotFound(string)
|
||||
| RENotAFunction(string)
|
||||
| REOperationError(Operation.operationError)
|
||||
| RERecordPropertyNotFound(string, string)
|
||||
| RESymbolNotFound(string)
|
||||
| RESyntaxError(string)
|
||||
| RETodo(string) // To do
|
||||
| REUnitNotFound(string)
|
||||
| RENeedToRun
|
||||
| REOther(string)
|
||||
|
||||
exception MessageException(t)
|
||||
|
||||
let toString = (err: t) =>
|
||||
switch err {
|
||||
| REArityError(_oFnName, arity, usedArity) =>
|
||||
`${Js.String.make(arity)} arguments expected. Instead ${Js.String.make(
|
||||
usedArity,
|
||||
)} argument(s) were passed.`
|
||||
| REArrayIndexNotFound(msg, index) => `${msg}: ${Js.String.make(index)}`
|
||||
| REAssignmentExpected => "Assignment expected"
|
||||
| REExpressionExpected => "Expression expected"
|
||||
| REFunctionExpected(msg) => `Function expected: ${msg}`
|
||||
| REFunctionNotFound(msg) => `Function not found: ${msg}`
|
||||
| REDistributionError(err) =>
|
||||
`Distribution Math Error: ${DistributionTypes.Error.toString(err)}`
|
||||
| REOperationError(err) => `Math Error: ${Operation.Error.toString(err)}`
|
||||
| REJavaScriptExn(omsg, oname) => {
|
||||
let answer = "JS Exception:"
|
||||
let answer = switch oname {
|
||||
| Some(name) => `${answer} ${name}`
|
||||
| _ => answer
|
||||
}
|
||||
let answer = switch omsg {
|
||||
| Some(msg) => `${answer}: ${msg}`
|
||||
| _ => answer
|
||||
}
|
||||
answer
|
||||
}
|
||||
| REMacroNotFound(macro) => `Macro not found: ${macro}`
|
||||
| RENotAFunction(valueString) => `${valueString} is not a function`
|
||||
| RERecordPropertyNotFound(msg, index) => `${msg}: ${index}`
|
||||
| RESymbolNotFound(symbolName) => `${symbolName} is not defined`
|
||||
| RESyntaxError(desc) => `Syntax Error: ${desc}`
|
||||
| RETodo(msg) => `TODO: ${msg}`
|
||||
| REExpectedType(typeName, valueString) => `Expected type: ${typeName} but got: ${valueString}`
|
||||
| REUnitNotFound(unitName) => `Unit not found: ${unitName}`
|
||||
| RENeedToRun => "Need to run"
|
||||
| REOther(msg) => `Error: ${msg}`
|
||||
}
|
||||
|
||||
let fromException = exn =>
|
||||
switch exn {
|
||||
| MessageException(e) => e
|
||||
| Js.Exn.Error(e) =>
|
||||
switch Js.Exn.message(e) {
|
||||
| Some(message) => REOther(message)
|
||||
| None =>
|
||||
switch Js.Exn.name(e) {
|
||||
| Some(name) => REOther(name)
|
||||
| None => REOther("Unknown error")
|
||||
}
|
||||
}
|
||||
| _e => REOther("Unknown error")
|
||||
}
|
||||
|
||||
let throw = (errorValue: t) => errorValue->MessageException->raise
|
||||
}
|
||||
|
||||
@genType.opaque
|
||||
type t = {
|
||||
message: Message.t,
|
||||
frameStack: Reducer_FrameStack.t,
|
||||
}
|
||||
|
||||
exception SqException(t)
|
||||
|
||||
let fromMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
||||
message: message,
|
||||
frameStack: frameStack,
|
||||
}
|
||||
|
||||
// this shouldn't be used much, since frame stack will be empty
|
||||
// but it's useful for global errors, e.g. in ReducerProject or somethere in the frontend
|
||||
@genType
|
||||
let fromMessage = (message: Message.t) =>
|
||||
fromMessageWithFrameStack(message, Reducer_FrameStack.make())
|
||||
|
||||
let fromParseError = (SyntaxError(message, location): Reducer_Peggy_Parse.parseError) =>
|
||||
RESyntaxError(message)->fromMessageWithFrameStack(
|
||||
Reducer_FrameStack.makeSingleFrameStack(location),
|
||||
)
|
||||
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.getTopFrame
|
||||
|
||||
@genType
|
||||
let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
||||
|
||||
@genType
|
||||
let toString = (t: t): string => t.message->Message.toString
|
||||
|
||||
@genType
|
||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessage
|
||||
|
||||
@genType
|
||||
let getFrameArray = (t: t): array<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.toFrameArray
|
||||
|
||||
@genType
|
||||
let toStringWithStackTrace = (t: t) =>
|
||||
t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty {
|
||||
"\nStack trace:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
||||
} else {
|
||||
""
|
||||
}
|
||||
let throw = (t: t) => t->SqException->raise
|
||||
|
||||
let throwMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
||||
message->fromMessageWithFrameStack(frameStack)->throw
|
||||
|
||||
// this shouldn't be used for most runtime errors - the resulting error would have an empty framestack
|
||||
let fromException = exn =>
|
||||
switch exn {
|
||||
| SqException(e) => e
|
||||
| Message.MessageException(e) => e->fromMessage
|
||||
| Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
|
||||
| _ => REOther("Unknown exception")->fromMessage
|
||||
}
|
||||
|
||||
// converts raw exceptions into exceptions with framestack attached
|
||||
// already converted exceptions won't be affected
|
||||
let rethrowWithFrameStack = (fn: unit => 'a, frameStack: Reducer_FrameStack.t) => {
|
||||
try {
|
||||
fn()
|
||||
} catch {
|
||||
| SqException(e) => e->throw // exception already has a framestack
|
||||
| Message.MessageException(e) => e->throwMessageWithFrameStack(frameStack) // probably comes from FunctionRegistry, adding framestack
|
||||
| Js.Exn.Error(obj) =>
|
||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessageWithFrameStack(frameStack)
|
||||
| _ => REOther("Unknown exception")->throwMessageWithFrameStack(frameStack)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
exception ErrorException = Reducer_ErrorValue.ErrorException
|
||||
|
||||
let stdLib: Reducer_T.namespace = {
|
||||
// constants
|
||||
let res =
|
||||
|
@ -10,23 +8,23 @@ let stdLib: Reducer_T.namespace = {
|
|||
// array and record lookups
|
||||
let res = res->Reducer_Namespace.set(
|
||||
"$_atIndex_$",
|
||||
Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => {
|
||||
Reducer_Lambda.makeFFILambda("$_atIndex_$", (inputs, _, _) => {
|
||||
switch inputs {
|
||||
| [IEvArray(aValueArray), IEvNumber(fIndex)] => {
|
||||
let index = Belt.Int.fromFloat(fIndex) // TODO - fail on non-integer indices?
|
||||
|
||||
switch Belt.Array.get(aValueArray, index) {
|
||||
| Some(value) => value
|
||||
| None => REArrayIndexNotFound("Array index not found", index)->ErrorException->raise
|
||||
| None => REArrayIndexNotFound("Array index not found", index)->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
| [IEvRecord(dict), IEvString(sIndex)] =>
|
||||
switch Belt.Map.String.get(dict, sIndex) {
|
||||
| Some(value) => value
|
||||
| None =>
|
||||
RERecordPropertyNotFound("Record property not found", sIndex)->ErrorException->raise
|
||||
RERecordPropertyNotFound("Record property not found", sIndex)->SqError.Message.throw
|
||||
}
|
||||
| _ => REOther("Trying to access key on wrong value")->ErrorException->raise
|
||||
| _ => REOther("Trying to access key on wrong value")->SqError.Message.throw
|
||||
}
|
||||
})->Reducer_T.IEvLambda,
|
||||
)
|
||||
|
@ -46,10 +44,10 @@ let stdLib: Reducer_T.namespace = {
|
|||
->Belt.Array.reduce(res, (cur, name) => {
|
||||
cur->Reducer_Namespace.set(
|
||||
name,
|
||||
Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => {
|
||||
switch FunctionRegistry_Library.call(name, arguments, environment, reducer) {
|
||||
Reducer_Lambda.makeFFILambda(name, (arguments, context, reducer) => {
|
||||
switch FunctionRegistry_Library.call(name, arguments, context, reducer) {
|
||||
| Ok(value) => value
|
||||
| Error(error) => error->Reducer_ErrorValue.ErrorException->raise
|
||||
| Error(error) => error->SqError.Message.throw
|
||||
}
|
||||
})->Reducer_T.IEvLambda,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user