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

204 lines
5.9 KiB
TypeScript
Raw Normal View History

import * as _ from "lodash";
import type {
environment,
expressionValue,
externalBindings,
errorValue,
} from "../rescript/TypescriptInterface.gen";
import {
defaultEnvironment,
2022-04-29 13:50:57 +00:00
evaluatePartialUsingExternalBindings,
2022-04-29 19:02:24 +00:00
evaluateUsingOptions,
2022-05-10 15:52:13 +00:00
foreignFunctionInterface,
parse as parseRescript,
} from "../rescript/TypescriptInterface.gen";
export {
makeSampleSetDist,
errorValueToString,
distributionErrorToString,
} from "../rescript/TypescriptInterface.gen";
export type {
2022-05-02 19:04:16 +00:00
distributionError,
declarationArg,
declaration,
} from "../rescript/TypescriptInterface.gen";
2022-05-10 15:52:13 +00:00
export type { errorValue, externalBindings as bindings, jsImports };
2022-04-08 19:55:04 +00:00
import {
jsValueToBinding,
2022-05-10 15:52:13 +00:00
jsValueToExpressionValue,
jsValue,
rescriptExport,
squiggleExpression,
convertRawToTypescript,
2022-05-10 15:52:13 +00:00
lambdaValue,
} from "./rescript_interop";
import { Ok, result, resultMap, tag, tagged } from "./types";
2022-05-02 18:29:59 +00:00
import { Distribution, shape } from "./distribution";
2022-04-11 03:16:31 +00:00
export { Distribution, resultMap, defaultEnvironment };
export type { result, shape, environment, lambdaValue, squiggleExpression };
2022-05-10 15:52:13 +00:00
export let defaultSamplingInputs: environment = {
sampleCount: 10000,
xyPointLength: 10000,
};
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: mergeImportsWithBindings(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
}
export function parse(
squiggleString: string
): result<null, Extract<errorValue, { tag: "RESyntaxError" }>> {
const maybeExpression = parseRescript(squiggleString);
if (maybeExpression.tag === "Ok") {
return Ok(null); // TODO - return AST
} else {
if (
typeof maybeExpression.value !== "object" ||
maybeExpression.value.tag !== "RESyntaxError"
) {
throw new Error("Expected syntax error");
}
return { tag: "Error", value: maybeExpression.value };
}
}
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,
mergeImportsWithBindings(b, i),
e
2022-04-29 18:46:44 +00:00
);
}
2022-05-10 15:52:13 +00:00
export function runForeign(
fn: lambdaValue,
args: jsValue[],
environment?: environment
): result<squiggleExpression, errorValue> {
let e = environment ? environment : defaultEnvironment;
let res: result<expressionValue, errorValue> = foreignFunctionInterface(
fn,
args.map(jsValueToExpressionValue),
e
);
return resultMap(res, (x) => createTsExport(x, e));
}
function mergeImportsWithBindings(
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 = {};
export function mergeBindings(
allBindings: externalBindings[]
): externalBindings {
return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
}
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 =>
convertRawToTypescript(
arrayItem as unknown as rescriptExport,
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-05-22 22:37:07 +00:00
case "EvDate":
return tag("date", x.value);
case "EvTimeDuration":
return tag("timeDuration", x.value);
case "EvDeclaration":
return tag("lambdaDeclaration", x.value);
case "EvTypeIdentifier":
return tag("typeIdentifier", x.value);
2022-06-09 13:27:51 +00:00
case "EvModule":
2022-06-09 13:53:07 +00:00
let moduleResult: tagged<
"module",
{ [key: string]: squiggleExpression }
> = tag(
2022-06-09 13:44:03 +00:00
"module",
_.mapValues(x.value, (x: unknown) =>
convertRawToTypescript(x as rescriptExport, environment)
)
);
return moduleResult;
2022-04-11 03:16:31 +00:00
}
}