squiggle/packages/squiggle-lang/src/js/index.ts

210 lines
5.6 KiB
TypeScript
Raw Normal View History

import * as _ from "lodash";
import {
samplingParams,
environment,
defaultEnvironment,
2022-04-29 13:50:57 +00:00
evaluatePartialUsingExternalBindings,
2022-04-29 19:02:24 +00:00
evaluateUsingOptions,
2022-04-29 13:50:57 +00:00
externalBindings,
expressionValue,
errorValue,
distributionError,
toPointSet,
continuousShape,
discreteShape,
distributionErrorToString,
internalCode,
mixedShape,
sampleSetDist,
symbolicDist,
} from "../rescript/TypescriptInterface.gen";
export {
makeSampleSetDist,
errorValueToString,
distributionErrorToString,
} from "../rescript/TypescriptInterface.gen";
2022-04-29 19:02:43 +00:00
export type {
samplingParams,
errorValue,
externalBindings as bindings,
2022-04-29 20:29:42 +00:00
jsImports,
2022-04-29 19:02:43 +00:00
};
2022-04-08 19:55:04 +00:00
import {
jsValueToBinding,
jsValue,
rescriptExport,
squiggleExpression,
convertRawToTypescript,
} from "./rescript_interop";
import { result, resultMap, tag, tagged } from "./types";
import { Distribution } from "./distribution";
2022-04-11 03:16:31 +00:00
2022-04-29 20:29:42 +00:00
export { Distribution, squiggleExpression, result, resultMap };
2022-04-11 03:16:31 +00:00
export let defaultSamplingInputs: samplingParams = {
sampleCount: 10000,
xyPointLength: 10000,
};
2022-04-11 06:16:29 +00:00
export type result<a, b> =
| {
tag: "Ok";
2022-04-11 00:48:45 +00:00
value: a;
}
| {
tag: "Error";
2022-04-11 00:48:45 +00:00
value: b;
2022-04-09 02:55:06 +00:00
};
2022-04-11 00:51:43 +00:00
export function resultMap<a, b, c>(
r: result<a, c>,
mapFn: (x: a) => b
): result<b, c> {
if (r.tag === "Ok") {
return { tag: "Ok", value: mapFn(r.value) };
} else {
return r;
}
}
function Ok<a, b>(x: a): result<a, b> {
return { tag: "Ok", value: x };
2022-04-11 06:16:29 +00:00
}
type tagged<a, b> = { tag: a; value: b };
2022-04-11 03:16:31 +00:00
function tag<a, b>(x: a, y: b): tagged<a, b> {
return { tag: x, value: y };
2022-04-11 03:16:31 +00:00
}
export type squiggleExpression =
| tagged<"symbol", string>
| tagged<"string", string>
2022-04-12 23:34:06 +00:00
| tagged<"call", string>
2022-04-25 07:20:27 +00:00
| tagged<"lambda", [string[], internalCode]>
| tagged<"array", squiggleExpression[]>
| tagged<"boolean", boolean>
| tagged<"distribution", Distribution>
| tagged<"number", number>
| tagged<"record", { [key: string]: squiggleExpression }>;
2022-04-11 03:16:31 +00:00
export function run(
squiggleString: string,
2022-04-29 13:50:57 +00:00
bindings?: externalBindings,
environment?: environment,
2022-04-29 20:29:42 +00:00
imports?: jsImports
2022-04-11 03:16:31 +00:00
): result<squiggleExpression, errorValue> {
2022-04-29 19:02:43 +00:00
let b = bindings ? bindings : defaultBindings;
2022-04-29 20:29:42 +00:00
let i = imports ? imports : defaultImports;
let e = environment ? environment : defaultEnvironment;
2022-04-29 19:02:24 +00:00
let res: result<expressionValue, errorValue> = evaluateUsingOptions(
{ externalBindings: mergeImports(b, i), environment: e },
2022-04-29 19:02:24 +00:00
squiggleString
);
return resultMap(res, (x) => createTsExport(x, e));
2022-04-11 03:16:31 +00:00
}
2022-04-29 13:50:57 +00:00
// Run Partial. A partial is a block of code that doesn't return a value
export function runPartial(
squiggleString: string,
2022-04-29 18:46:44 +00:00
bindings?: externalBindings,
environment?: environment,
2022-04-29 20:29:42 +00:00
imports?: jsImports
2022-04-29 13:50:57 +00:00
): result<externalBindings, errorValue> {
2022-04-29 19:02:43 +00:00
let b = bindings ? bindings : defaultBindings;
2022-04-29 20:29:42 +00:00
let i = imports ? imports : defaultImports;
let e = environment ? environment : defaultEnvironment;
2022-04-29 18:46:44 +00:00
return evaluatePartialUsingExternalBindings(
squiggleString,
mergeImports(b, i),
e
2022-04-29 18:46:44 +00:00
);
}
2022-04-29 20:29:42 +00:00
function mergeImports(
2022-04-29 18:46:44 +00:00
bindings: externalBindings,
2022-04-29 20:29:42 +00:00
imports: jsImports
2022-04-29 18:46:44 +00:00
): externalBindings {
2022-04-29 20:29:42 +00:00
let transformedImports = Object.fromEntries(
Object.entries(imports).map(([key, value]) => [
2022-04-29 18:46:44 +00:00
"$" + key,
jsValueToBinding(value),
])
);
2022-04-29 20:29:42 +00:00
return _.merge(bindings, transformedImports);
2022-04-29 13:50:57 +00:00
}
2022-04-29 20:29:42 +00:00
type jsImports = { [key: string]: jsValue };
2022-04-29 18:46:44 +00:00
2022-04-29 20:29:42 +00:00
export let defaultImports: jsImports = {};
2022-04-29 19:02:43 +00:00
export let defaultBindings: externalBindings = {};
function createTsExport(
x: expressionValue,
environment: environment
): squiggleExpression {
2022-04-11 03:16:31 +00:00
switch (x.tag) {
case "EvArray":
// genType doesn't convert anything more than 2 layers down into {tag: x, value: x}
// format, leaving it as the raw values. This converts the raw values
// directly into typescript values.
//
// The casting here is because genType is about the types of the returned
// values, claiming they are fully recursive when that's not actually the
// case
return tag(
"array",
x.value.map((arrayItem): squiggleExpression => {
switch (arrayItem.tag) {
case "EvRecord":
return tag(
"record",
_.mapValues(arrayItem.value, (recordValue: unknown) =>
convertRawToTypescript(
recordValue as rescriptExport,
environment
)
)
);
case "EvArray":
let y = arrayItem.value as unknown as rescriptExport[];
return tag(
"array",
y.map((childArrayItem) =>
convertRawToTypescript(childArrayItem, environment)
)
);
default:
return createTsExport(arrayItem, environment);
}
})
);
case "EvArrayString":
return tag("arraystring", x.value);
2022-04-11 03:16:31 +00:00
case "EvBool":
return tag("boolean", x.value);
2022-04-12 15:12:19 +00:00
case "EvCall":
return tag("call", x.value);
2022-04-25 07:20:27 +00:00
case "EvLambda":
return tag("lambda", x.value);
2022-04-11 03:16:31 +00:00
case "EvDistribution":
return tag("distribution", new Distribution(x.value, environment));
2022-04-11 03:16:31 +00:00
case "EvNumber":
return tag("number", x.value);
case "EvRecord":
// genType doesn't support records, so we have to do the raw conversion ourself
let result: tagged<"record", { [key: string]: squiggleExpression }> = tag(
"record",
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, environment)
)
);
return result;
case "EvString":
return tag("string", x.value);
case "EvSymbol":
return tag("symbol", x.value);
2022-04-11 03:16:31 +00:00
}
}