Fixing some unchecked errors
This commit is contained in:
parent
f272ffaca2
commit
b97eb52c21
2 changed files with 50 additions and 21 deletions
|
|
@ -17,6 +17,7 @@ Maybe a = None | Some a;
|
||||||
# | [n, [x, ...xs]] \ nth (n - 1) xs;
|
# | [n, [x, ...xs]] \ nth (n - 1) xs;
|
||||||
|
|
||||||
map : (a \ b) \ List a \ List b = f list \ list
|
map : (a \ b) \ List a \ List b = f list \ list
|
||||||
|
# map : (a \ b) \ Int \ List b = f list \ list
|
||||||
| [] \ []
|
| [] \ []
|
||||||
| [x, ...xs] \ [f x, ...map f xs];
|
| [x, ...xs] \ [f x, ...map f xs];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,15 @@ function infer(expr: AST, env: TypeEnv, subst: Subst): TypeAST | null {
|
||||||
return t ? applySubst(t, subst) : null;
|
return t ? applySubst(t, subst) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'list': {
|
||||||
|
if (expr.elements.length === 0) {
|
||||||
|
return { kind: 'type-apply', constructor: { kind: 'type-name', name: 'List' }, args: [{ kind: 'type-var', name: '_empty' }] };
|
||||||
|
}
|
||||||
|
const first = infer(expr.elements[0], env, subst);
|
||||||
|
if (!first) return null;
|
||||||
|
return { kind: 'type-apply', constructor: { kind: 'type-name', name: 'List' }, args: [first] };
|
||||||
|
}
|
||||||
|
|
||||||
case 'record': {
|
case 'record': {
|
||||||
const fields: { name: string, type: TypeAST }[] = [];
|
const fields: { name: string, type: TypeAST }[] = [];
|
||||||
for (const entry of expr.entries) {
|
for (const entry of expr.entries) {
|
||||||
|
|
@ -173,7 +182,10 @@ function check(expr: AST, expected: TypeAST, env: TypeEnv, subst: Subst): string
|
||||||
const scrutType = infer(expr.expr, env, subst);
|
const scrutType = infer(expr.expr, env, subst);
|
||||||
for (const c of expr.cases) {
|
for (const c of expr.cases) {
|
||||||
const caseEnv = new Map(env);
|
const caseEnv = new Map(env);
|
||||||
if (scrutType) bindPattern(c.pattern, scrutType, caseEnv, subst);
|
if (scrutType) {
|
||||||
|
const patErr = bindPattern(c.pattern, scrutType, caseEnv, subst);
|
||||||
|
if (patErr) warn(patErr, c.result);
|
||||||
|
}
|
||||||
const err = check(c.result, expected, caseEnv, subst);
|
const err = check(c.result, expected, caseEnv, subst);
|
||||||
if (err) warn(err, c.result);
|
if (err) warn(err, c.result);
|
||||||
}
|
}
|
||||||
|
|
@ -188,6 +200,17 @@ function check(expr: AST, expected: TypeAST, env: TypeEnv, subst: Subst): string
|
||||||
return check(expr.body, expected, newEnv, subst);
|
return check(expr.body, expected, newEnv, subst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// List literal against List type
|
||||||
|
if (expr.kind === 'list' && exp.kind === 'type-apply' && exp.constructor.kind === 'type-name' && exp.constructor.name === 'List') {
|
||||||
|
const elemType = exp.args[0];
|
||||||
|
for (const elem of expr.elements) {
|
||||||
|
if (elem.kind === 'list-spread') continue;
|
||||||
|
const err = check(elem, elemType, env, subst);
|
||||||
|
if (err) return err;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback: infer and unify
|
// Fallback: infer and unify
|
||||||
const inferred = infer(expr, env, subst);
|
const inferred = infer(expr, env, subst);
|
||||||
if (!inferred) return null; // Can't infer, skip silently
|
if (!inferred) return null; // Can't infer, skip silently
|
||||||
|
|
@ -199,19 +222,37 @@ function warn(msg: string, expr: AST) {
|
||||||
console.warn(`TypeError${loc}: ${msg}`);
|
console.warn(`TypeError${loc}: ${msg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindPattern(pattern: Pattern, type: TypeAST, env: TypeEnv, subst: Subst): void {
|
function bindPattern(pattern: Pattern, type: TypeAST, env: TypeEnv, subst: Subst): string | null {
|
||||||
const t = applySubst(type, subst);
|
const t = applySubst(type, subst);
|
||||||
|
|
||||||
switch (pattern.kind) {
|
switch (pattern.kind) {
|
||||||
case 'var':
|
case 'var':
|
||||||
env.set(pattern.name, t);
|
env.set(pattern.name, t);
|
||||||
break;
|
return null;
|
||||||
|
case 'wildcard':
|
||||||
|
case 'literal':
|
||||||
|
return null;
|
||||||
case 'constructor':
|
case 'constructor':
|
||||||
// TODO: look up ctor arg types
|
// TODO: look up ctor arg types
|
||||||
break;
|
break;
|
||||||
case 'list':
|
case 'list':
|
||||||
case 'list-spread':
|
case 'list-spread':
|
||||||
// TODO: bind element types
|
if (t.kind === 'type-apply' && t.constructor.kind === 'type-name' && t.constructor.name === 'List' && t.args.length === 1) {
|
||||||
break;
|
const elemType = t.args[0];
|
||||||
|
if (pattern.kind === 'list') {
|
||||||
|
for (const elem of pattern.elements) {
|
||||||
|
bindPattern(elem, elemType, env, subst);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const elem of pattern.head) {
|
||||||
|
bindPattern(elem, elemType, env, subst);
|
||||||
|
}
|
||||||
|
env.set(pattern.spread, t);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (t.kind === 'type-var') return null;
|
||||||
|
return `Connet match ${prettyPrintType(t)} against list pattern`;
|
||||||
case 'record':
|
case 'record':
|
||||||
if (t.kind === 'type-record') {
|
if (t.kind === 'type-record') {
|
||||||
for (const [key, pat] of Object.entries(pattern.fields)) {
|
for (const [key, pat] of Object.entries(pattern.fields)) {
|
||||||
|
|
@ -219,28 +260,15 @@ function bindPattern(pattern: Pattern, type: TypeAST, env: TypeEnv, subst: Subst
|
||||||
if (field) bindPattern(pat, field.type, env, subst);
|
if (field) bindPattern(pat, field.type, env, subst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
return null;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const int: TypeAST = { kind: 'type-name', name: 'Int' };
|
|
||||||
// const float: TypeAST = { kind: 'type-name', name: 'Float' };
|
|
||||||
// const str: TypeAST = { kind: 'type-name', name: 'String' };
|
|
||||||
// const bool: TypeAST = { kind: 'type-name', name: 'Bool' };
|
|
||||||
// const tvar = (name: string): TypeAST => ({ kind: 'type-var', name });
|
|
||||||
// const tfn = (param: TypeAST, result: TypeAST): TypeAST => ({ kind: 'type-function', param, result });
|
|
||||||
|
|
||||||
export function typecheck(defs: Definition[]) {
|
export function typecheck(defs: Definition[]) {
|
||||||
const env: TypeEnv = new Map();
|
const env: TypeEnv = new Map();
|
||||||
|
|
||||||
// seed env with builtin types
|
|
||||||
// env.set('cat', tfn(str, tfn(str, str)));
|
|
||||||
// env.set('add', tfn(int, tfn(int, int)));
|
|
||||||
// env.set('sub', tfn(int, tfn(int, int)));
|
|
||||||
// env.set('mul', tfn(int, tfn(int, int)));
|
|
||||||
// env.set('div', tfn(int, tfn(int, int)));
|
|
||||||
// env.set('eq', tfn(tvar('a'), tfn(tvar('a'), bool)));
|
|
||||||
|
|
||||||
// Register all annotated defs in env first so they can ref eachother
|
// Register all annotated defs in env first so they can ref eachother
|
||||||
for (const def of defs) {
|
for (const def of defs) {
|
||||||
if (def.annotation) {
|
if (def.annotation) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue