wrote interpreter (independent of type inferencer)
This commit is contained in:
parent
2279c54229
commit
32bdc23ea7
6 changed files with 104 additions and 602 deletions
652
src/eval/eval.ts
652
src/eval/eval.ts
|
|
@ -1,588 +1,78 @@
|
|||
// import { Double, eqType, fnType, getHumanReadableName, getSymbol, Int, isTypeVar, occurring, prettyT, recomputeTypeVarsWithInverse, substitute, symbolFunction, transitivelyGrow, trie, TYPE_VARS, UNBOUND_SYMBOLS } from "dope2";
|
||||
import { trie } from "dope2";
|
||||
import type { CallBlockState } from "../component/expr/CallBlock";
|
||||
import type { ExprBlockState } from "../component/expr/ExprBlock";
|
||||
import type { InputBlockState } from "../component/expr/InputBlock";
|
||||
import type { LambdaBlockState } from "../component/expr/LambdaBlock";
|
||||
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
||||
|
||||
// import type { ExprBlockState } from "./ExprBlock";
|
||||
// import type { InputValueType } from "./InputBlock";
|
||||
export interface DynamicEnvironment {
|
||||
names: any;
|
||||
}
|
||||
|
||||
// const IS_DEV = (import.meta.env.MODE === "development");
|
||||
// const VERBOSE = true && IS_DEV;
|
||||
export type EvalResult = any;
|
||||
|
||||
// export interface Environment {
|
||||
// names: any;
|
||||
// nextFreeTypeVar: number;
|
||||
// typeVars: Set<string>;
|
||||
// }
|
||||
export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult {
|
||||
if (s.kind === "input") {
|
||||
return evalInput(s, env);
|
||||
}
|
||||
else if (s.kind === "call") {
|
||||
return evalCall(s, env);
|
||||
}
|
||||
else if (s.kind === "let") {
|
||||
return evalLet(s, env);
|
||||
}
|
||||
else { // (s.kind === "lambda")
|
||||
return evalLambda(s, env);
|
||||
}
|
||||
}
|
||||
|
||||
// interface Type {
|
||||
// symbol: string;
|
||||
// params: any[];
|
||||
// };
|
||||
export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult {
|
||||
if (s.value.kind === "literal") {
|
||||
const ctor = {
|
||||
Int: BigInt,
|
||||
Double: Number,
|
||||
}[s.value.type] as (s: string) => any;
|
||||
return ctor(s.text);
|
||||
}
|
||||
else if (s.value.kind === "name") {
|
||||
const found = trie.get(env.names)(s.text);
|
||||
if (found) {
|
||||
if (found.recursive) {
|
||||
return found.i();
|
||||
}
|
||||
return found.i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// type Unification = Map<string, any>;
|
||||
export function evalCall(s: CallBlockState, env: DynamicEnvironment): EvalResult {
|
||||
const fn = evalExpr(s.fn, env);
|
||||
const input = evalExpr(s.input, env);
|
||||
if (fn !== undefined && input !== undefined) {
|
||||
return fn(input);
|
||||
}
|
||||
}
|
||||
|
||||
// export interface DeepError {
|
||||
// kind: "error";
|
||||
// e: Error;
|
||||
// depth: number;
|
||||
// t: Type;
|
||||
// unification: Unification;
|
||||
// }
|
||||
export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult {
|
||||
const valueEnv = {
|
||||
names: trie.insert(env.names)(s.name)({
|
||||
recursive: true,
|
||||
i: () => value,
|
||||
}),
|
||||
};
|
||||
const value = evalExpr(s.value, valueEnv);
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.name)({i: value}),
|
||||
}
|
||||
return evalExpr(s.inner, innerEnv);
|
||||
}
|
||||
|
||||
// // a dynamically typed value = tuple (instance, type)
|
||||
// export interface Dynamic {
|
||||
// kind: "value",
|
||||
// i: any;
|
||||
// t: Type;
|
||||
// unification: Unification;
|
||||
// };
|
||||
|
||||
// export interface Unknown {
|
||||
// kind: "unknown";
|
||||
// t: Type;
|
||||
// unification: Unification;
|
||||
// }
|
||||
|
||||
// // the value of every block is either known (Dynamic), an error, or unknown
|
||||
// export type ResolvedType = Dynamic | DeepError | Unknown;
|
||||
|
||||
// class NotFoundError extends Error {}
|
||||
|
||||
// export const evalExprBlock = (s: ExprBlockState, env: Environment): [ResolvedType,Environment] => {
|
||||
// if (s.kind === "input") {
|
||||
// return evalInputBlock(s.text, s.value, env);
|
||||
// }
|
||||
// else if (s.kind === "call") {
|
||||
// return evalCallBlock(s.fn, s.input, env);
|
||||
// }
|
||||
// else if (s.kind === "let") {
|
||||
// return evalLetInBlock(s.value, s.name, s.inner, env);
|
||||
// }
|
||||
// else { // (s.kind === "lambda")
|
||||
// const [resolved, env2] = evalLambdaBlock(s.paramName, s.expr, env);
|
||||
// return [resolved, env2];
|
||||
// }
|
||||
// };
|
||||
|
||||
// export function evalInputBlock(text: string, value: InputValueType, env: Environment): [ResolvedType,Environment] {
|
||||
// if (value.kind === "literal") {
|
||||
// return parseLiteral(text, value.type, env);
|
||||
// }
|
||||
// else if (value.kind === "name") {
|
||||
// const found = trie.get(env.names)(text);
|
||||
// if (found) {
|
||||
// return [found, env]; // don't rewrite lambda parameters
|
||||
// }
|
||||
// }
|
||||
// const [t, env2] = makeTypeVar(env, 'err')
|
||||
// return [{
|
||||
// kind: "error",
|
||||
// t,
|
||||
// e: new NotFoundError(`'${text}' not found`),
|
||||
// depth: 0,
|
||||
// unification: new Map(),
|
||||
// }, env2];
|
||||
// }
|
||||
|
||||
// export function evalCallBlock(fn: ExprBlockState, input: ExprBlockState, env: Environment): [ResolvedType,Environment] {
|
||||
// let __debug = `
|
||||
// ========= evalCallBlock =========
|
||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
||||
// env.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
||||
// fn.kind : ${fn.kind}
|
||||
// input.kind : ${input.kind}`;
|
||||
// let [fnResolved, env2] = evalExprBlock(fn, env);
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// ==== evalCallBlock (fn) ====
|
||||
// fn.t : ${prettyT(fnResolved.t)}
|
||||
// env2.typeVars : ${[...env2.typeVars].map(getHumanReadableName)}
|
||||
// env2.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env2.nextFreeTypeVar])}`;
|
||||
// }
|
||||
// if (fnResolved.kind !== "unknown") {
|
||||
// let fnInverse;
|
||||
// [fnResolved, env2, fnInverse] = recomputeTypeVarsForEnv("<name unused>", fnResolved, env2);
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// --- recomp ---
|
||||
// fn.t : ${prettyT(fnResolved.t)}
|
||||
// fnInverse : ${[...fnInverse].map(([symbol,type])=>`${getHumanReadableName(symbol)} => ${getHumanReadableName(type)})`).join(',')}
|
||||
// env2.typeVars : ${[...env2.typeVars].map(getHumanReadableName)}
|
||||
// env2.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env2.nextFreeTypeVar])}`;
|
||||
// }
|
||||
// }
|
||||
// let [inputResolved, env3] = evalExprBlock(input, env2);
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// ==== evalCallBlock (input) ====
|
||||
// input.t : ${prettyT(inputResolved.t)}
|
||||
// env3.typeVars : ${[...env3.typeVars].map(getHumanReadableName)}
|
||||
// env3.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env3.nextFreeTypeVar])}`;
|
||||
// }
|
||||
// if (inputResolved.kind !== "unknown") {
|
||||
// let inputInverse;
|
||||
// [inputResolved, env3, inputInverse] = recomputeTypeVarsForEnv("<name unused>", inputResolved, env3);
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// --- recomp ---
|
||||
// input.t : ${prettyT(inputResolved.t)}
|
||||
// inputInverse : ${[...inputInverse].map(([symbol,type])=>`${getHumanReadableName(symbol)} => ${getHumanReadableName(type)})`).join(',')}
|
||||
// env3.typeVars : ${[...env3.typeVars].map(getHumanReadableName)}
|
||||
// env3.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env3.nextFreeTypeVar])}`;
|
||||
// }
|
||||
// }
|
||||
// const result = evalCallBlock2(fnResolved, inputResolved, env3);
|
||||
// if (VERBOSE) {
|
||||
// // @ts-ignore
|
||||
// result[0].__debug = __debug + (result[0].__debug || '');
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// export function evalCallBlock2(fnResolved: ResolvedType, inputResolved: ResolvedType, env: Environment): [ResolvedType,Environment] {
|
||||
// if (getSymbol(fnResolved.t) !== symbolFunction) {
|
||||
// // not a function...
|
||||
// if (isTypeVar(fnResolved.t)) {
|
||||
// // ... but typeVars are OK (good guys!)
|
||||
// }
|
||||
// else {
|
||||
// // worst outcome
|
||||
// return makeError(env,
|
||||
// new NotAFunctionError(`${prettyT(fnResolved.t)} is not a function type!`),
|
||||
// mergeUnifications(fnResolved.unification, inputResolved.unification),
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// // it's is a function, continue...
|
||||
// return evalCallBlock3(fnResolved, inputResolved, env);
|
||||
// };
|
||||
|
||||
// const highestTypeVar = type => {
|
||||
// let highest = -1;
|
||||
// for (const typeVar of occurring(type)) {
|
||||
// highest = Math.max(highest, UNBOUND_SYMBOLS.indexOf(typeVar));
|
||||
// }
|
||||
// return highest;
|
||||
// }
|
||||
// const highestTypeVar2 = typeVars => {
|
||||
// let highest = -1;
|
||||
// for (const typeVar of typeVars) {
|
||||
// highest = Math.max(highest, UNBOUND_SYMBOLS.indexOf(typeVar));
|
||||
// }
|
||||
// return highest;
|
||||
// }
|
||||
|
||||
|
||||
// const inverseUnification = (uni, inverse) => {
|
||||
// return new Map([...uni]
|
||||
// .filter(([symbol]) => !inverse.has(inverse.get(symbol)))
|
||||
// .map(([symbol, types]) => [inverse.get(symbol) || symbol, types])
|
||||
// );
|
||||
// }
|
||||
|
||||
// export function recomputeTypeVarsForEnv(name: string, resolved: ResolvedType, env: Environment): [ResolvedType,Environment,any] {
|
||||
// const [[newType], inverse] = recomputeTypeVarsWithInverse([resolved.t], findTempIdx(env));
|
||||
// const newResolved: ResolvedType = {
|
||||
// ...resolved,
|
||||
// t: newType,
|
||||
// unification: inverseUnification(resolved.unification, inverse),
|
||||
// };
|
||||
|
||||
// // hacky
|
||||
// const typeVars = env.typeVars.union(occurring(newType)) as Set<string>;
|
||||
// const newEnv: Environment = {
|
||||
// // names: trie.insert(env.names)(name)(newResolved),
|
||||
// names: env.names,
|
||||
// typeVars,
|
||||
// nextFreeTypeVar: highestTypeVar2(typeVars) + 1,
|
||||
// };
|
||||
|
||||
// return [newResolved, newEnv, inverse];
|
||||
// }
|
||||
|
||||
// function removeTypeVarsFromEnv(toRemove, env: Environment): Environment {
|
||||
// const newTypeVars = env.typeVars.difference(toRemove);
|
||||
// return {
|
||||
// names: env.names,
|
||||
// typeVars: newTypeVars,
|
||||
// nextFreeTypeVar: highestTypeVar2(newTypeVars) + 1,
|
||||
// }
|
||||
// }
|
||||
|
||||
// function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, env: Environment): [ResolvedType,Environment] {
|
||||
// let __debug = '';
|
||||
// try {
|
||||
// if (occurring(fnResolved.t).intersection(occurring(inputResolved.t)).size > 0) {
|
||||
// throw new Error(`Precondition failed: function (${prettyT(fnResolved.t)}) and its input (${prettyT(inputResolved.t)}) have overlapping typevars!`);
|
||||
// }
|
||||
|
||||
// const typeVarsOfFnAndInput = occurring(fnResolved.t).union(occurring(inputResolved.t));
|
||||
|
||||
// // turn input in to a function
|
||||
// const [abstractOutputType, env2] = makeTypeVar(env, "<out>");
|
||||
// const matchFnType = fnType(_ => inputResolved.t)(_ => abstractOutputType);
|
||||
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// ========= evalCallBlock3 =========
|
||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
||||
// nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
||||
// fnKind : ${fnResolved.kind}
|
||||
// inputKind : ${inputResolved.kind}
|
||||
// fnType : ${prettyT(fnResolved.t)}
|
||||
// inputType : ${prettyT(inputResolved.t)}
|
||||
// matchFnType : ${prettyT(matchFnType)}`;
|
||||
// }
|
||||
|
||||
// // unify both functions
|
||||
// const unification = /*transitivelyGrow*/(unifyLL(fnResolved.t, matchFnType));
|
||||
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// unification : ${prettyU(unification)}`;
|
||||
// }
|
||||
|
||||
// const unificationR = reduceUnification(unification);
|
||||
// const unifiedFnType = substitute(
|
||||
// // matchFnType,
|
||||
// fnResolved.t,
|
||||
// unificationR, []);
|
||||
|
||||
// const outType = unifiedFnType.params[1](unifiedFnType);
|
||||
|
||||
// const typeVarsOfOutType = occurring(outType);
|
||||
|
||||
// const removedTypeVars = typeVarsOfFnAndInput.difference(typeVarsOfOutType);
|
||||
|
||||
// // const newEnv = (outType === abstractOutputType) ? env2 : env;
|
||||
// const newEnv = removeTypeVarsFromEnv(removedTypeVars, env);
|
||||
|
||||
// // we don't want to 'bubble up' our outType substitution, because it's just a temporary variable
|
||||
// const unificationWithoutOutType = new Map([...unification].filter(([symbol]) => symbol !== abstractOutputType.symbol));
|
||||
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// unificationR : ${prettyRU(unificationR)}
|
||||
// unifiedFnType : ${prettyT(unifiedFnType)}
|
||||
// outType : ${prettyT(outType)}
|
||||
// removedTypeVars : ${[...removedTypeVars].map(getHumanReadableName)}
|
||||
// fn.unification : ${prettyU(fnResolved.unification)}
|
||||
// input.unification : ${prettyU(inputResolved.unification)}
|
||||
// unificationWithoutOutType: ${prettyU(unificationWithoutOutType)}`;
|
||||
// }
|
||||
|
||||
// const grandUnification = transitivelyGrow([fnResolved.unification, inputResolved.unification]
|
||||
// .reduce(mergeUnifications, unificationWithoutOutType));
|
||||
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// grandUnification : ${prettyU(grandUnification)}
|
||||
// newEnv.typeVars : ${[...newEnv.typeVars].map(getHumanReadableName)}
|
||||
// newEnv.newFreeTypeVar : ${getHumanReadableName(UNBOUND_SYMBOLS[newEnv.nextFreeTypeVar])}`;
|
||||
// }
|
||||
|
||||
|
||||
// if (inputResolved.kind === "error") {
|
||||
// // throw inputResolved.e;
|
||||
// return [{
|
||||
// kind: "error",
|
||||
// e: inputResolved.e, // bubble up the error
|
||||
// depth: 0,
|
||||
// t: outType,
|
||||
// unification: grandUnification,
|
||||
// // @ts-ignore
|
||||
// __debug,
|
||||
// }, newEnv];
|
||||
// }
|
||||
// if (fnResolved.kind === "error") {
|
||||
// // throw fnResolved.e;
|
||||
// // also bubble up
|
||||
// return [{
|
||||
// kind: "error",
|
||||
// e: fnResolved.e,
|
||||
// depth: fnResolved.depth+1,
|
||||
// t: outType,
|
||||
// unification: grandUnification,
|
||||
// // @ts-ignore
|
||||
// __debug,
|
||||
// }, newEnv];
|
||||
// }
|
||||
// // if the above statement did not throw => types are compatible...
|
||||
// if (inputResolved.kind === "value" && fnResolved.kind === "value") {
|
||||
// const outValue = fnResolved.i(inputResolved.i);
|
||||
// // console.log('outValue:', outValue);
|
||||
// return [{
|
||||
// kind: "value",
|
||||
// i: outValue,
|
||||
// t: outType,
|
||||
// unification: grandUnification,
|
||||
// // @ts-ignore
|
||||
// __debug,
|
||||
// }, newEnv];
|
||||
// }
|
||||
// else {
|
||||
// // we don't know the value, but we do know the type:
|
||||
// return [{
|
||||
// kind: "unknown",
|
||||
// t: outType,
|
||||
// unification: grandUnification,
|
||||
// // @ts-ignore
|
||||
// __debug,
|
||||
// }, newEnv];
|
||||
// }
|
||||
// }
|
||||
// catch (e) {
|
||||
// // if ((e instanceof UnifyError)) {
|
||||
// if (VERBOSE) {
|
||||
// __debug += '\n\nUnifyError! ' + (e as Error).message;
|
||||
// }
|
||||
// const err = makeError(env, e as Error);
|
||||
// // @ts-ignore
|
||||
// err[0].__debug = __debug;
|
||||
// return err;
|
||||
// // }
|
||||
// throw e;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export function evalLetInBlock(value: ExprBlockState, name: string, inner: ExprBlockState, env: Environment): [ResolvedType,Environment] {
|
||||
// const [valueResolved] = evalExprBlock(value, env);
|
||||
// const innerEnv = makeInnerEnv(env, name, valueResolved);
|
||||
// return evalExprBlock(inner, innerEnv);
|
||||
// }
|
||||
|
||||
// const prettyRU = (rUni: Map<string, Type>) => {
|
||||
// return '{'+[...rUni].map(([symbol,type]) => `${getHumanReadableName(symbol)} => ${prettyT(type)}`).join(', ')+'}';
|
||||
// }
|
||||
|
||||
// function fixPoint(varName: string, expr: ExprBlockState, env: Environment) {
|
||||
// let [varType, innerEnv] = makeTypeVar(env, varName);
|
||||
// let round = 0;
|
||||
// let __debug = '';
|
||||
// while (true) {
|
||||
// const [innerResolved] = evalExprBlock(expr, innerEnv);
|
||||
// const newInnerT = substitute(innerResolved.t, reduceUnification(innerResolved.unification), []);
|
||||
|
||||
// round++;
|
||||
// if (round === 10) {
|
||||
// throw new Error("too many rounds!");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// export function evalLambdaBlock(paramName: string, expr: ExprBlockState, env: Environment): [ResolvedType,Environment,Environment] {
|
||||
// let [paramType, innerEnv] = makeTypeVar(env, paramName);
|
||||
// let round = 0;
|
||||
// let __debug = '';
|
||||
// while (true) {
|
||||
// // fixpoint computation...
|
||||
// const [exprResolved] = evalExprBlock(expr, innerEnv);
|
||||
// const lambdaT = fnType(_ => paramType)(_ => exprResolved.t);
|
||||
// // This is the only place in the code where we actually do something with the 'substitutions'. We compute the type of our lambda function:
|
||||
// const reduced = reduceUnification(exprResolved.unification);
|
||||
// const lambdaTSubstituted = substitute(
|
||||
// lambdaT,
|
||||
// reduced,
|
||||
// []); // <- not important
|
||||
|
||||
// let lambdaResolved: ResolvedType;
|
||||
// if (exprResolved.kind === "error") {
|
||||
// lambdaResolved = {
|
||||
// kind: "error",
|
||||
// t: lambdaTSubstituted,
|
||||
// depth: 0,
|
||||
// e: exprResolved.e,
|
||||
// unification: exprResolved.unification,
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// lambdaResolved = {
|
||||
// kind: "value",
|
||||
// t: lambdaTSubstituted,
|
||||
// i: (x: any) => {
|
||||
// const innerEnv = makeInnerEnv(env, paramName, {
|
||||
// kind: "value",
|
||||
// i: x,
|
||||
// t: lambdaTSubstituted,
|
||||
// unification: new Map(),
|
||||
// });
|
||||
// const [result] = evalExprBlock(expr, innerEnv);
|
||||
// if (result.kind === "value") {
|
||||
// return result.i;
|
||||
// }
|
||||
// },
|
||||
// unification: exprResolved.unification,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // const [betterResolved, newInnerEnv] = recomputeTypeVarsForEnv("<name unused>", lambdaResolved, env);
|
||||
// const [betterResolved, newInnerEnv] = [lambdaResolved, env];
|
||||
|
||||
// const inferredParamType = betterResolved.t.params[0](betterResolved.t);
|
||||
|
||||
// if (VERBOSE) {
|
||||
// __debug += `
|
||||
// ====== evalLambdaBlock (round ${round}) ======
|
||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
||||
// nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
||||
// paramName : ${paramName}
|
||||
// paramType : ${prettyT(paramType)}
|
||||
// innerEnv.typevars : ${[...innerEnv.typeVars].map(getHumanReadableName)}
|
||||
// paramType : ${prettyT(paramType)}
|
||||
// exprType : ${prettyT(exprResolved.t)}
|
||||
// exprUnification : ${prettyU(exprResolved.unification)}
|
||||
// exprUnificationR : ${prettyRU(reduced)}
|
||||
// lambdaType : ${prettyT(lambdaT)}
|
||||
// lambdaTypeSubsituted: ${prettyT(lambdaTSubstituted)}
|
||||
// betterResolvedT : ${prettyT(betterResolved.t)}
|
||||
// inferredParamType : ${prettyT(inferredParamType)}`;
|
||||
// }
|
||||
|
||||
// if (eqType(paramType)(inferredParamType)) {
|
||||
// // reached fixpoint
|
||||
// // @ts-ignore
|
||||
// lambdaResolved.__debug = __debug;
|
||||
// return [lambdaResolved, env, innerEnv];
|
||||
// }
|
||||
// else {
|
||||
// paramType = inferredParamType;
|
||||
// innerEnv = {
|
||||
// ...newInnerEnv,
|
||||
// names: trie.insert(newInnerEnv.names)(paramName)({
|
||||
// kind: "unknown",
|
||||
// t: inferredParamType,
|
||||
// unification: new Map(), // <-- ??
|
||||
// }),
|
||||
// };
|
||||
// round++;
|
||||
// if (round === 10) {
|
||||
// throw new Error("too many rounds!");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// export function scoreResolved(resolved: ResolvedType, outPriority: (s:ResolvedType) => number) {
|
||||
// const bias = outPriority(resolved);
|
||||
|
||||
// if (resolved.kind === "value") {
|
||||
// return bias;
|
||||
// }
|
||||
// else if (resolved.kind === "unknown") {
|
||||
// return bias;
|
||||
// }
|
||||
// if (resolved.e instanceof UnifyError) {
|
||||
// return -1 + bias; // parameter doesn't match
|
||||
// }
|
||||
// else {
|
||||
// return -2 + bias; // even worse: fn is not a function!
|
||||
// }
|
||||
// }
|
||||
|
||||
// export function makeInnerEnv(env: Environment, name: string, value: ResolvedType): Environment {
|
||||
// if (name !== "") {
|
||||
// return {
|
||||
// names: trie.insert(env.names)(name)(value),
|
||||
// nextFreeTypeVar: env.nextFreeTypeVar,
|
||||
// typeVars: env.typeVars,
|
||||
// }
|
||||
// }
|
||||
// return env;
|
||||
// }
|
||||
|
||||
// function findTempIdx(env: Environment) {
|
||||
// let idx = 16;
|
||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
||||
// idx++;
|
||||
// }
|
||||
// return idx;
|
||||
// }
|
||||
|
||||
// export function makeTypeVar(env: Environment, name: string): [Type, Environment] {
|
||||
// let idx = 0;
|
||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
||||
// idx++;
|
||||
// }
|
||||
// const typeVar = TYPE_VARS[idx];
|
||||
// const unknown: Unknown = {
|
||||
// kind: "unknown",
|
||||
// t: typeVar,
|
||||
// unification: new Map(),
|
||||
// };
|
||||
// return [ typeVar, {
|
||||
// names: trie.insert(env.names)(name)(unknown),
|
||||
// nextFreeTypeVar: idx + 1,
|
||||
// typeVars: new Set([...env.typeVars, UNBOUND_SYMBOLS[idx]]),
|
||||
// }];
|
||||
// }
|
||||
|
||||
// function makeError(env: Environment, e: Error, unification: Unification=new Map()): [DeepError, Environment] {
|
||||
// let idx = 0;
|
||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
||||
// idx++;
|
||||
// }
|
||||
// const typeVar = TYPE_VARS[idx];
|
||||
// const deepError: DeepError = {
|
||||
// kind: "error",
|
||||
// t: typeVar,
|
||||
// unification: new Map(),
|
||||
// e,
|
||||
// depth: 0,
|
||||
// };
|
||||
// return [deepError, {
|
||||
// names: env.names,
|
||||
// nextFreeTypeVar: idx + 1,
|
||||
// typeVars: new Set([...env.typeVars, UNBOUND_SYMBOLS[idx]]),
|
||||
// }];
|
||||
// }
|
||||
|
||||
// export function removeFocus(state: ExprBlockState): ExprBlockState {
|
||||
// if (state.kind === "input") {
|
||||
// return { ...state, focus: false };
|
||||
// }
|
||||
// else if (state.kind === "call") {
|
||||
// return {
|
||||
// ...state,
|
||||
// fn: removeFocus(state.fn),
|
||||
// input: removeFocus(state.input),
|
||||
// };
|
||||
// }
|
||||
// else if (state.kind === "lambda") {
|
||||
// return {
|
||||
// ...state,
|
||||
// focus: false,
|
||||
// expr: removeFocus(state.expr),
|
||||
// };
|
||||
// }
|
||||
// else { // state.kind === "let"
|
||||
// return {
|
||||
// ...state,
|
||||
// focus: false,
|
||||
// value: removeFocus(state.value),
|
||||
// inner: removeFocus(state.inner),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// export function addFocusRightMost(state: ExprBlockState) : ExprBlockState {
|
||||
// if (state.kind === "input") {
|
||||
// return { ...state, focus: true };
|
||||
// }
|
||||
// else if (state.kind === "call") {
|
||||
// return {
|
||||
// ... state,
|
||||
// input: addFocusRightMost(state.input),
|
||||
// };
|
||||
// }
|
||||
// else if (state.kind === "lambda") {
|
||||
// return {
|
||||
// ...state,
|
||||
// expr: addFocusRightMost(state.expr),
|
||||
// };
|
||||
// }
|
||||
// else { // state.kind === "let"
|
||||
// return {
|
||||
// ...state,
|
||||
// inner: addFocusRightMost(state.inner),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
export function evalLambda(s: LambdaBlockState, env: DynamicEnvironment): EvalResult {
|
||||
const fn = x => {
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.paramName)({i: x})
|
||||
};
|
||||
return evalExpr(s.expr, innerEnv);
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { LambdaBlockState } from "../component/expr/LambdaBlock";
|
|||
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
||||
import { memoize } from "../util/memoize";
|
||||
|
||||
export interface Environment {
|
||||
export interface StaticEnvironment {
|
||||
names: any;
|
||||
typevars: Set<string>;
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ export type Substitutions = Map<string, Type>;
|
|||
interface TypeInfoCommon {
|
||||
type: Type;
|
||||
subs: Substitutions;
|
||||
newEnv: Environment;
|
||||
newEnv: StaticEnvironment;
|
||||
err?: IncompatibleTypesError;
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ export interface TypeInfoCall extends TypeInfoCommon {
|
|||
}
|
||||
export interface TypeInfoLet extends TypeInfoCommon {
|
||||
kind: "let";
|
||||
innerEnv: Environment;
|
||||
innerEnv: StaticEnvironment;
|
||||
value: TypeInfo;
|
||||
inner: TypeInfo;
|
||||
}
|
||||
|
|
@ -44,12 +44,12 @@ export interface TypeInfoLambda extends TypeInfoCommon {
|
|||
kind: "lambda";
|
||||
paramType: Type;
|
||||
inner: TypeInfo;
|
||||
innerEnv: Environment;
|
||||
innerEnv: StaticEnvironment;
|
||||
}
|
||||
|
||||
export type TypeInfo = TypeInfoInput | TypeInfoCall | TypeInfoLet | TypeInfoLambda;
|
||||
|
||||
export const inferType = memoize(function inferType(s: ExprBlockState, env: Environment): TypeInfo {
|
||||
export const inferType = memoize(function inferType(s: ExprBlockState, env: StaticEnvironment): TypeInfo {
|
||||
if (s.kind === "input") {
|
||||
return inferTypeInput(s, env);
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ export const inferType = memoize(function inferType(s: ExprBlockState, env: Envi
|
|||
}
|
||||
});
|
||||
|
||||
export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState, env: Environment): TypeInfoInput {
|
||||
export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState, env: StaticEnvironment): TypeInfoInput {
|
||||
if (s.value.kind === "literal") {
|
||||
const type = {
|
||||
Int: Int,
|
||||
|
|
@ -102,7 +102,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
|||
}
|
||||
});
|
||||
|
||||
export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, env: Environment): TypeInfoCall {
|
||||
export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, env: StaticEnvironment): TypeInfoCall {
|
||||
const fnTypeInfo = inferType(s.fn, env);
|
||||
const inputEnv = fnTypeInfo.newEnv;
|
||||
const inputTypeInfo = inferType(s.input, inputEnv);
|
||||
|
|
@ -162,7 +162,7 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e
|
|||
}
|
||||
});
|
||||
|
||||
export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLet {
|
||||
export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: StaticEnvironment): TypeInfoLet {
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env);
|
||||
// to eval the 'inner' expr, we only need to add our parameter to the environment:
|
||||
const innerEnv = {
|
||||
|
|
@ -181,7 +181,7 @@ export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, en
|
|||
};
|
||||
});
|
||||
|
||||
export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: Environment): TypeInfoLambda {
|
||||
export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: StaticEnvironment): TypeInfoLambda {
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env);
|
||||
return {
|
||||
kind: "lambda",
|
||||
|
|
@ -192,7 +192,7 @@ export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockSt
|
|||
|
||||
// Given a named value whose type we know nothing about, and an expression that computes the value (which may recursively contain the value), compute the type of the value.
|
||||
// Why? Both lambda functions and let-expressions can refer to themselves recursively. To infer their type, we need to recompute the type and feed it back to itself until some fixed point is reached.
|
||||
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: Environment) {
|
||||
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment) {
|
||||
let [paramType] = typeUnknown(env);
|
||||
const paramTypeVar = paramType.symbol;
|
||||
|
||||
|
|
@ -238,7 +238,7 @@ const highestTypeVar2 = (typevars: Iterable<string>) => {
|
|||
}
|
||||
return highest;
|
||||
}
|
||||
function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
||||
function rewriteType(type: Type, env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||
const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1);
|
||||
const newTypeVars = occurring(recomputed);
|
||||
return [recomputed, {
|
||||
|
|
@ -246,7 +246,7 @@ function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
|||
typevars: env.typevars.union(newTypeVars),
|
||||
}];
|
||||
}
|
||||
function typeUnknown(env: Environment): [Type, Environment] {
|
||||
function typeUnknown(env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||
const type = TYPE_VARS[highestTypeVar2(env.typevars)+1];
|
||||
const newEnv = {
|
||||
names: env.names,
|
||||
|
|
@ -254,7 +254,7 @@ function typeUnknown(env: Environment): [Type, Environment] {
|
|||
};
|
||||
return [type, newEnv];
|
||||
}
|
||||
function rewriteInferredType(type: Type, env: Environment): [Type, Environment] {
|
||||
function rewriteInferredType(type: Type, env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||
const substitutions = new Map();
|
||||
const newTypeVars = new Set(env.typevars);
|
||||
let i = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue