suggestions work again, improve error reporting
This commit is contained in:
parent
9050581a10
commit
69175c8cb1
12 changed files with 259 additions and 282 deletions
|
|
@ -460,53 +460,6 @@
|
|||
// }
|
||||
// }
|
||||
|
||||
// function parseLiteral(text: string, type: string, env: Environment): [ResolvedType,Environment] {
|
||||
// // dirty
|
||||
// if (type === "Int") {
|
||||
// return parseAsInt(text, env);
|
||||
// }
|
||||
// if (type === "Double") {
|
||||
// return parseAsDouble(text, env);
|
||||
// }
|
||||
// return makeError(env, new Error("Failed to parse"));
|
||||
// }
|
||||
|
||||
// function parseAsDouble(text: string, env: Environment): [ResolvedType,Environment] {
|
||||
// if (text !== '') {
|
||||
// const num = Number(text);
|
||||
// if (!Number.isNaN(num)) {
|
||||
// return [{
|
||||
// kind: "value",
|
||||
// i: num,
|
||||
// t: Double,
|
||||
// unification: new Map(),
|
||||
// }, env];
|
||||
// }
|
||||
// }
|
||||
// return makeError(env, new Error("Failed to parse as Double"));
|
||||
// }
|
||||
// function parseAsInt(text: string, env: Environment): [ResolvedType,Environment] {
|
||||
// if (text !== '') {
|
||||
// try {
|
||||
// return [{
|
||||
// kind: "value",
|
||||
// i: BigInt(text),
|
||||
// t: Int,
|
||||
// unification: new Map(),
|
||||
// }, env]; // may throw
|
||||
// }
|
||||
// catch {}
|
||||
// }
|
||||
// return makeError(env, new Error("Failed to parse as Int"));
|
||||
// }
|
||||
|
||||
// const literalParsers = [parseAsDouble, parseAsInt];
|
||||
|
||||
// export function attemptParseLiteral(text: string, env: Environment): Dynamic[] {
|
||||
// return literalParsers.map(parseFn => parseFn(text, env))
|
||||
// .map(([resolved]) => resolved)
|
||||
// .filter((resolved) => (resolved.kind !== "unknown" && resolved.kind !== "error")) as unknown as Dynamic[];
|
||||
// }
|
||||
|
||||
// export function scoreResolved(resolved: ResolvedType, outPriority: (s:ResolvedType) => number) {
|
||||
// const bias = outPriority(resolved);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettySS, recomputeTypeVars, substitute, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2";
|
||||
import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettyS, prettySS, prettyT, recomputeTypeVars, substitute, SubstitutionCycle, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2";
|
||||
|
||||
import type { CallBlockState } from "../component/expr/CallBlock";
|
||||
import type { ExprBlockState } from "../component/expr/ExprBlock";
|
||||
|
|
@ -18,20 +18,35 @@ export interface Type {
|
|||
|
||||
export type Substitutions = Map<string, Type>;
|
||||
|
||||
export interface TypeInfo {
|
||||
interface TypeInfoCommon {
|
||||
type: Type;
|
||||
subs: Substitutions;
|
||||
newEnv: Environment;
|
||||
err?: IncompatibleTypesError;
|
||||
}
|
||||
|
||||
export interface TypeInfoLambda extends TypeInfo {
|
||||
export interface TypeInfoInput extends TypeInfoCommon {
|
||||
kind: "input";
|
||||
}
|
||||
export interface TypeInfoCall extends TypeInfoCommon {
|
||||
kind: "call";
|
||||
fn: TypeInfo;
|
||||
input: TypeInfo;
|
||||
}
|
||||
export interface TypeInfoLet extends TypeInfoCommon {
|
||||
kind: "let";
|
||||
innerEnv: Environment;
|
||||
value: TypeInfo;
|
||||
inner: TypeInfo;
|
||||
}
|
||||
export interface TypeInfoLambda extends TypeInfoCommon {
|
||||
kind: "lambda";
|
||||
paramType: Type;
|
||||
inner: TypeInfo;
|
||||
innerEnv: Environment;
|
||||
}
|
||||
export interface TypeInfoCall extends TypeInfo {
|
||||
inputEnv: Environment;
|
||||
}
|
||||
|
||||
export type TypeInfo = TypeInfoInput | TypeInfoCall | TypeInfoLet | TypeInfoLambda;
|
||||
|
||||
export function inferType(s: ExprBlockState, env: Environment): TypeInfo {
|
||||
if (s.kind === "input") {
|
||||
|
|
@ -48,13 +63,14 @@ export function inferType(s: ExprBlockState, env: Environment): TypeInfo {
|
|||
}
|
||||
}
|
||||
|
||||
export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfo {
|
||||
export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfoInput {
|
||||
if (s.value.kind === "literal") {
|
||||
const type = {
|
||||
Int: Int,
|
||||
Double: Double,
|
||||
}[s.value.type] as Type;
|
||||
return {
|
||||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv: env,
|
||||
|
|
@ -67,18 +83,21 @@ export function inferTypeInput(s: InputBlockState, env: Environment): TypeInfo {
|
|||
? [found.t, env]
|
||||
: rewriteType(found.t, env);
|
||||
return {
|
||||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
};
|
||||
}
|
||||
}
|
||||
// kind === "text", or name not found
|
||||
// kind === "gibberish", or name not found
|
||||
const [type, newEnv] = typeUnknown(env);
|
||||
return {
|
||||
kind: "input",
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
err: new Error("Gibberish"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,41 +123,59 @@ export function inferTypeCall(s: CallBlockState, env: Environment): TypeInfoCall
|
|||
type = returnType;
|
||||
newEnv = envWithReturn;
|
||||
}
|
||||
const mergedSubs = mergeSubstitutionsN([
|
||||
fnTypeInfo.subs,
|
||||
inputTypeInfo.subs,
|
||||
subs,
|
||||
]);
|
||||
let mergedSubs;
|
||||
try {
|
||||
mergedSubs = mergeSubstitutionsN([
|
||||
fnTypeInfo.subs,
|
||||
inputTypeInfo.subs,
|
||||
subs,
|
||||
]);
|
||||
} catch (e) {
|
||||
if (e instanceof SubstitutionCycle) {
|
||||
// wrap error
|
||||
throw new IncompatibleTypesError(fnTypeInfo.type, fakeFnType, e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return {
|
||||
kind: "call",
|
||||
type,
|
||||
subs: mergedSubs,
|
||||
newEnv,
|
||||
inputEnv,
|
||||
fn: fnTypeInfo,
|
||||
input: inputTypeInfo,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof IncompatibleTypesError) {
|
||||
const [type, newEnv] = typeUnknown(env);
|
||||
return {
|
||||
kind: "call",
|
||||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
err: e,
|
||||
inputEnv,
|
||||
fn: fnTypeInfo,
|
||||
input: inputTypeInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLambda {
|
||||
export function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLet {
|
||||
const valTypeInfo = inferType(s.value, env);
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.name)({kind: "value", t: valTypeInfo.type}),
|
||||
typevars: env.typevars,
|
||||
};
|
||||
const innerTypeInfo = inferType(s.inner, innerEnv);
|
||||
return {
|
||||
...inferType(s.inner, innerEnv),
|
||||
paramType: valTypeInfo.type,
|
||||
kind: "let",
|
||||
type: innerTypeInfo.type,
|
||||
subs: innerTypeInfo.subs,
|
||||
newEnv: env,
|
||||
value: valTypeInfo,
|
||||
inner: innerTypeInfo,
|
||||
innerEnv,
|
||||
};
|
||||
}
|
||||
|
|
@ -154,21 +191,23 @@ export function inferTypeLambda(s: LambdaBlockState, env: Environment): TypeInfo
|
|||
names: trie.insert(env.names)(s.paramName)({kind: "unknown", t: paramType}),
|
||||
typevars: env.typevars.union(occurring(paramType) as Set<string>),
|
||||
};
|
||||
const typeInfo = inferType(s.expr, innerEnv);
|
||||
const subsWithoutPType = new Map(typeInfo.subs);
|
||||
const innerTypeInfo = inferType(s.expr, innerEnv);
|
||||
const subsWithoutPType = new Map(innerTypeInfo.subs);
|
||||
subsWithoutPType.delete(paramTypeVar);
|
||||
const inferredPType = substitute(paramType, typeInfo.subs, []);
|
||||
const inferredPType = substitute(paramType, innerTypeInfo.subs, []);
|
||||
const inferredPType2 = rewriteInferredType(inferredPType, env);
|
||||
if (eqType(inferredPType2)(paramType)) {
|
||||
return {
|
||||
type: fnType(_ => paramType)(_ => typeInfo.type),
|
||||
kind: "lambda",
|
||||
type: fnType(_ => paramType)(_ => innerTypeInfo.type),
|
||||
subs: subsWithoutPType,
|
||||
newEnv: env, // no change
|
||||
paramType,
|
||||
inner: innerTypeInfo,
|
||||
innerEnv,
|
||||
};
|
||||
}
|
||||
if ((iterations++) == 4) {
|
||||
if ((iterations++) == 10) {
|
||||
throw new Error("too many iterations!");
|
||||
}
|
||||
// console.log("-----------------", iterations);
|
||||
|
|
@ -192,9 +231,10 @@ const highestTypeVar2 = (typevars: Iterable<string>) => {
|
|||
}
|
||||
function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
||||
const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1);
|
||||
return [type, {
|
||||
const newTypeVars = occurring(recomputed);
|
||||
return [recomputed, {
|
||||
names: env.names,
|
||||
typevars: env.typevars.union(occurring(recomputed)),
|
||||
typevars: env.typevars.union(newTypeVars),
|
||||
}];
|
||||
}
|
||||
function typeUnknown(env: Environment): [Type, Environment] {
|
||||
|
|
@ -218,3 +258,19 @@ function rewriteInferredType(type: Type, env: Environment) {
|
|||
}
|
||||
return substitute(type, substitutions, []);
|
||||
}
|
||||
export function scoreTypeInfo(typeInfo: TypeInfo): number {
|
||||
const bias = typeInfo.err ? -1 : 0;
|
||||
if (typeInfo.kind === "input") {
|
||||
return bias;
|
||||
}
|
||||
else if (typeInfo.kind === "call") {
|
||||
return bias + scoreTypeInfo(typeInfo.fn) + scoreTypeInfo(typeInfo.input);
|
||||
}
|
||||
else if (typeInfo.kind === "let") {
|
||||
return bias + scoreTypeInfo(typeInfo.value) + scoreTypeInfo(typeInfo.inner);
|
||||
}
|
||||
else if (typeInfo.kind === "lambda") {
|
||||
return bias + scoreTypeInfo(typeInfo.inner);
|
||||
}
|
||||
return 0; // shut up typescript
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue