Type Aliases!
This commit is contained in:
parent
32815301fa
commit
25c3ac5c0d
3 changed files with 47 additions and 2 deletions
|
|
@ -173,6 +173,7 @@ export type TypeDefinition = {
|
|||
name: string
|
||||
params: string[]
|
||||
constructors: TypeConstructor[]
|
||||
alias?: TypeAST
|
||||
line?: number
|
||||
column?: number
|
||||
start?: number
|
||||
|
|
|
|||
|
|
@ -715,6 +715,13 @@ export class Parser {
|
|||
|
||||
this.expect('equals');
|
||||
|
||||
// If next token isn't a type-ident, it's a type alias
|
||||
if (this.current().kind !== 'type-ident') {
|
||||
const alias = this.parseType();
|
||||
if (this.current().kind === 'semicolon') this.advance();
|
||||
return { kind: 'type-definition', name, params, constructors: [], alias, ...this.getPos(nameToken) };
|
||||
}
|
||||
|
||||
// Parse constructors separated by |
|
||||
const constructors: TypeConstructor[] = [];
|
||||
constructors.push(this.parseTypeConstructor());
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ function applySubst(type: TypeAST, subst: Subst): TypeAST {
|
|||
}
|
||||
|
||||
function unify(t1: TypeAST, t2: TypeAST, subst: Subst): string | null {
|
||||
const a = applySubst(t1, subst);
|
||||
const b = applySubst(t2, subst);
|
||||
const a = resolveAlias(applySubst(t1, subst));
|
||||
const b = resolveAlias(applySubst(t2, subst));
|
||||
|
||||
// Same type name
|
||||
if (a.kind === 'type-name' && b.kind === 'type-name' && a.name === b.name) return null;
|
||||
|
|
@ -376,6 +376,7 @@ let typeConstructors = new Map<string, string[]>();
|
|||
|
||||
export function typecheck(defs: Definition[], typeDefs: TypeDefinition[] = [], classDefs: ClassDefinition[] = [], instances: InstanceDeclaration[] = []) {
|
||||
const env: TypeEnv = new Map();
|
||||
const typeAliases = new Map<string, { params: string[], type: TypeAST }>();
|
||||
|
||||
// Register instances as a lookup: className -> Set of type names
|
||||
const instanceMap = new Map<string, Set<string>>();
|
||||
|
|
@ -395,6 +396,11 @@ export function typecheck(defs: Definition[], typeDefs: TypeDefinition[] = [], c
|
|||
|
||||
// Register constructors
|
||||
for (const td of typeDefs) {
|
||||
if (td.alias) {
|
||||
typeAliases.set(td.name, { params: td.params, type: td.alias });
|
||||
continue; // don't register constructors for aliases
|
||||
}
|
||||
|
||||
typeConstructors.set(td.name, td.constructors.map(c => c.name));
|
||||
|
||||
const resultType: TypeAST = td.params.length === 0
|
||||
|
|
@ -414,6 +420,8 @@ export function typecheck(defs: Definition[], typeDefs: TypeDefinition[] = [], c
|
|||
}
|
||||
}
|
||||
|
||||
moduleTypeAliases = typeAliases;
|
||||
|
||||
// Register all annotated defs in env first so they can ref eachother
|
||||
for (const def of defs) {
|
||||
if (def.annotation) {
|
||||
|
|
@ -543,3 +551,32 @@ function checkExhaustiveness(scrutType: TypeAST, cases: { pattern: Pattern }[],
|
|||
warn(`Non-exhaustive match, missing: ${missing.join(', ')}`, expr);
|
||||
}
|
||||
}
|
||||
|
||||
let moduleTypeAliases = new Map<string, { params: string[], type: TypeAST }>();
|
||||
|
||||
function resolveAlias(t: TypeAST): TypeAST {
|
||||
if (t.kind === 'type-name' && moduleTypeAliases.has(t.name)) {
|
||||
return moduleTypeAliases.get(t.name)!.type;
|
||||
}
|
||||
|
||||
if (t.kind === 'type-apply' && t.constructor.kind === 'type-name' && moduleTypeAliases.has(t.constructor.name)) {
|
||||
const alias = moduleTypeAliases.get(t.constructor.name);
|
||||
// Substitute params: alias.params[i] -> t.args[i]
|
||||
let result = alias.type;
|
||||
for (let i = 0; i < alias.params.length && i < t.args.length; i++) {
|
||||
result = substituteTypeVar(alias.params[i], t.args[i], result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
function substituteTypeVar(name: string, replacement: TypeAST, type: TypeAST): TypeAST {
|
||||
switch (type.kind) {
|
||||
case 'type-var': return type.name === name ? replacement : type;
|
||||
case 'type-name': return type;
|
||||
case 'type-function': return { kind: 'type-function', param: substituteTypeVar(name, replacement, type.param), result: substituteTypeVar(name, replacement, type.result) };
|
||||
case 'type-apply': return { kind: 'type-apply', constructor: substituteTypeVar(name, replacement, type.constructor), args: type.args.map(a => substituteTypeVar(name, replacement, a)) };
|
||||
case 'type-record': return { kind: 'type-record', fields: type.fields.map(f => ({ name: f.name, type: substituteTypeVar(name, replacement, f.type) })) };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue