Refactor parsing to lib files

This commit is contained in:
Sam Nolan 2022-07-13 15:54:45 +10:00
parent a5a131daf1
commit 9cc000070b
3 changed files with 125 additions and 119 deletions

View File

@ -17,6 +17,8 @@ import {
DistributionChartSpecOptions,
} from "../lib/distributionSpecBuilder";
import { NumberShower } from "./NumberShower";
import { Plot, parsePlot } from "../lib/plotting";
import { flattenResult, all } from "../lib/utility";
export type DistributionPlottingSettings = {
/** Whether to show a summary of means, stdev, percentiles etc */
@ -25,12 +27,6 @@ export type DistributionPlottingSettings = {
showControls: boolean;
} & DistributionChartSpecOptions;
export type LabeledDistribution = { name: string; distribution: Distribution };
export type Plot = {
distributions: LabeledDistribution[];
};
export type DistributionChartProps = {
plot: Plot;
width?: number;
@ -42,99 +38,6 @@ export function defaultPlot(distribution: Distribution): Plot {
return { distributions: [{ name: "default", distribution }] };
}
function error<a, b>(err: b): result<a, b> {
return { tag: "Error", value: err };
}
function ok<a, b>(x: a): result<a, b> {
return { tag: "Ok", value: x };
}
function parseString(expr: squiggleExpression): result<string, string> {
if (expr.tag === "string") {
return ok(expr.value);
} else {
return error("Expression was not string");
}
}
function parseRecord(
expr: squiggleExpression
): result<{ [key: string]: squiggleExpression }, string> {
if (expr.tag === "record") {
return ok(expr.value);
} else {
return error("Expression was not a record");
}
}
function parseDistribution(
expr: squiggleExpression
): result<Distribution, string> {
if (expr.tag === "distribution") {
return ok(expr.value);
} else {
return error("Expression was not a distribution");
}
}
function parseArray(
expr: squiggleExpression
): result<squiggleExpression[], string> {
if (expr.tag === "array") {
return ok(expr.value);
} else {
return error("Expression was not a distribution");
}
}
function parseField<a>(
record: { [key: string]: squiggleExpression },
field: string,
parser: (expr: squiggleExpression) => result<a, string>
): result<a, string> {
if (record[field]) {
return parser(record[field]);
} else {
return error("record does not have field " + field);
}
}
function resultBind<a, b, c>(
x: result<a, b>,
fn: (y: a) => result<c, b>
): result<c, b> {
if (x.tag === "Ok") {
return fn(x.value);
} else {
return x;
}
}
function parseLabeledDistribution(
x: squiggleExpression
): result<LabeledDistribution, string> {
return resultBind(parseRecord(x), (record) =>
resultBind(parseField(record, "name", parseString), (name) =>
resultBind(
parseField(record, "distribution", parseDistribution),
(distribution) => ok({ name, distribution })
)
)
);
}
function parsePlot(record: {
[key: string]: squiggleExpression;
}): result<Plot, string> {
return resultBind(parseField(record, "distributions", parseArray), (array) =>
resultBind(
flattenResult(array.map(parseLabeledDistribution)),
(distributions) => ok({ distributions })
)
);
}
export function makePlot(record: {
[key: string]: squiggleExpression;
}): Plot | void {
@ -143,26 +46,6 @@ export function makePlot(record: {
return plotResult.value;
}
}
function all(arr: boolean[]): boolean {
return arr.reduce((x, y) => x && y, true);
}
function flattenResult<a, b>(x: result<a, b>[]): result<a[], b> {
if (x.length === 0) {
return { tag: "Ok", value: [] };
} else {
if (x[0].tag === "Error") {
return x[0];
} else {
let rest = flattenResult(x.splice(1));
if (rest.tag === "Error") {
return rest;
} else {
return { tag: "Ok", value: [x[0].value].concat(rest.value) };
}
}
}
}
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
const {

View File

@ -0,0 +1,90 @@
import { Distribution, result, squiggleExpression } from "@quri/squiggle-lang";
import { flattenResult, resultBind } from "./utility";
export type LabeledDistribution = { name: string; distribution: Distribution };
export type Plot = {
distributions: LabeledDistribution[];
};
function error<a, b>(err: b): result<a, b> {
return { tag: "Error", value: err };
}
function ok<a, b>(x: a): result<a, b> {
return { tag: "Ok", value: x };
}
function parseString(expr: squiggleExpression): result<string, string> {
if (expr.tag === "string") {
return ok(expr.value);
} else {
return error("Expression was not string");
}
}
function parseRecord(
expr: squiggleExpression
): result<{ [key: string]: squiggleExpression }, string> {
if (expr.tag === "record") {
return ok(expr.value);
} else {
return error("Expression was not a record");
}
}
function parseDistribution(
expr: squiggleExpression
): result<Distribution, string> {
if (expr.tag === "distribution") {
return ok(expr.value);
} else {
return error("Expression was not a distribution");
}
}
function parseArray(
expr: squiggleExpression
): result<squiggleExpression[], string> {
if (expr.tag === "array") {
return ok(expr.value);
} else {
return error("Expression was not a distribution");
}
}
function parseField<a>(
record: { [key: string]: squiggleExpression },
field: string,
parser: (expr: squiggleExpression) => result<a, string>
): result<a, string> {
if (record[field]) {
return parser(record[field]);
} else {
return error("record does not have field " + field);
}
}
function parseLabeledDistribution(
x: squiggleExpression
): result<LabeledDistribution, string> {
return resultBind(parseRecord(x), (record) =>
resultBind(parseField(record, "name", parseString), (name) =>
resultBind(
parseField(record, "distribution", parseDistribution),
(distribution) => ok({ name, distribution })
)
)
);
}
export function parsePlot(record: {
[key: string]: squiggleExpression;
}): result<Plot, string> {
return resultBind(parseField(record, "distributions", parseArray), (array) =>
resultBind(
flattenResult(array.map(parseLabeledDistribution)),
(distributions) => ok({ distributions })
)
);
}

View File

@ -0,0 +1,33 @@
import { result } from "@quri/squiggle-lang";
export function flattenResult<a, b>(x: result<a, b>[]): result<a[], b> {
if (x.length === 0) {
return { tag: "Ok", value: [] };
} else {
if (x[0].tag === "Error") {
return x[0];
} else {
let rest = flattenResult(x.splice(1));
if (rest.tag === "Error") {
return rest;
} else {
return { tag: "Ok", value: [x[0].value].concat(rest.value) };
}
}
}
}
export function resultBind<a, b, c>(
x: result<a, b>,
fn: (y: a) => result<c, b>
): result<c, b> {
if (x.tag === "Ok") {
return fn(x.value);
} else {
return x;
}
}
export function all(arr: boolean[]): boolean {
return arr.reduce((x, y) => x && y, true);
}