[WIP] Further attempt to get typeclasses working

This commit is contained in:
Sam Vervaeck 2024-04-08 19:57:10 +02:00
parent 719dbfcad4
commit e01a970377
Signed by: samvv
SSH key fingerprint: SHA256:dIg0ywU1OP+ZYifrYxy8c5esO72cIKB+4/9wkZj1VaY
11 changed files with 424 additions and 102 deletions

View file

@ -13,7 +13,7 @@ export class Analyser {
const addReference = (scope: Scope, name: string) => { const addReference = (scope: Scope, name: string) => {
const target = scope.lookup(name); const target = scope.lookup(name);
if (source === null || target === null || target.kind === SyntaxKind.Param) { if (source === null || target === null || isParam(target.kind)) {
return; return;
} }
assert(source.kind === SyntaxKind.LetDeclaration); assert(source.kind === SyntaxKind.LetDeclaration);

View file

@ -5,6 +5,7 @@ import {
ExprOperator, ExprOperator,
Identifier, Identifier,
IdentifierAlt, IdentifierAlt,
InstanceDeclaration,
LetDeclaration, LetDeclaration,
Pattern, Pattern,
ReferenceExpression, ReferenceExpression,
@ -23,58 +24,31 @@ import {
KindMismatchDiagnostic, KindMismatchDiagnostic,
ModuleNotFoundDiagnostic, ModuleNotFoundDiagnostic,
TypeclassNotFoundDiagnostic, TypeclassNotFoundDiagnostic,
TypeclassDeclaredTwiceDiagnostic,
FieldNotFoundDiagnostic, FieldNotFoundDiagnostic,
TypeMismatchDiagnostic, TypeMismatchDiagnostic,
} from "./diagnostics"; } from "./diagnostics";
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn } from "./util"; import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn, implementationLimitation } from "./util";
import { Analyser } from "./analysis"; import { Analyser } from "./analysis";
import { InspectOptions } from "util"; import { InspectOptions } from "util";
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TRegularVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, buildTupleTypeWithLoc, buildTupleType, isTVar } from "./types"; import { TypeKind, TApp, TArrow, TCon, TField, TNil, TPresent, TRegularVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, buildTupleTypeWithLoc, buildTupleType, labelTag } from "./types";
import { CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints"; import { CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
// export class Qual { // class IsIn {
// public constructor( // public constructor(
// public preds: Pred[], // public className: string,
// public type: Type,
// ) {
// }
// public substitute(sub: TVSub): Qual {
// return new Qual(
// this.preds.map(pred => pred.substitute(sub)),
// this.type.substitute(sub),
// );
// }
// public *getTypeVars() {
// for (const pred of this.preds) {
// yield* pred.type.getTypeVars();
// }
// yield* this.type.getTypeVars();
// }
// }
// class IsInPred {
// public constructor(
// public id: string,
// public type: Type, // public type: Type,
// ) { // ) {
// } // }
// public substitute(sub: TVSub): Pred { // public substitute(sub: TVSub): Pred {
// return new IsInPred(this.id, this.type.substitute(sub)); // return new IsIn(this.className, this.type.substitute(sub));
// } // }
// } // }
// type Pred = IsInPred; // type Pred = IsIn;
export const enum KindType { export const enum KindType {
Type, Type,
@ -371,6 +345,11 @@ function hasTypeVar(typeVars: TVSet, type: Type): boolean {
return false; return false;
} }
interface ClassMeta {
decl: ClassDeclaration | null;
instances: Set<InstanceDeclaration>;
}
export class Checker { export class Checker {
private nextTypeVarId = 0; private nextTypeVarId = 0;
@ -382,7 +361,7 @@ export class Checker {
private boolType = this.createTCon('Bool'); private boolType = this.createTCon('Bool');
private unitType = buildTupleType([]); private unitType = buildTupleType([]);
private classDecls = new Map<string, ClassDeclaration>(); private classDecls = new Map<string, ClassMeta>();
private globalKindEnv = new KindEnv(); private globalKindEnv = new KindEnv();
private globalTypeEnv = new TypeEnv(); private globalTypeEnv = new TypeEnv();
@ -779,8 +758,8 @@ export class Checker {
case ConstraintKind.Empty: case ConstraintKind.Empty:
return constraint; return constraint;
case ConstraintKind.Equal: case ConstraintKind.Equal:
constraint.left = this.simplifyType(constraint.left) // constraint.left = this.simplifyType(constraint.left)
constraint.right = this.simplifyType(constraint.right) // constraint.right = this.simplifyType(constraint.right)
const newConstraint = constraint.substitute(sub); const newConstraint = constraint.substitute(sub);
newConstraint.node = node; newConstraint.node = node;
newConstraint.prevInstantiation = constraint; newConstraint.prevInstantiation = constraint;
@ -877,13 +856,15 @@ export class Checker {
} }
case SyntaxKind.NestedTypeExpression: case SyntaxKind.NestedTypeExpression:
case SyntaxKind.InstanceTypeExpression:
{ {
kind = this.inferKindFromTypeExpression(node.typeExpr, env); kind = this.inferKindFromTypeExpression(node.typeExpr, env);
break; break;
} }
default: default:
throw new Error(`Unexpected ${node}`); assertNever(node);
} }
// We store the kind on the node so there is a one-to-one correspondence // We store the kind on the node so there is a one-to-one correspondence
@ -1580,6 +1561,7 @@ export class Checker {
break; break;
} }
case SyntaxKind.InstanceTypeExpression:
case SyntaxKind.NestedTypeExpression: case SyntaxKind.NestedTypeExpression:
type = this.inferTypeExpression(node.typeExpr, introduceTypeVars); type = this.inferTypeExpression(node.typeExpr, introduceTypeVars);
break; break;
@ -1613,11 +1595,16 @@ export class Checker {
case SyntaxKind.TypeExpressionWithConstraints: case SyntaxKind.TypeExpressionWithConstraints:
{ {
// TODO for (const constraint of node.constraints) {
// for (const constraint of node.constraints) { implementationLimitation(constraint.types.length === 1);
// implementationLimitation(constraint.types.length === 1); this.addConstraint(
// this.addConstraint(new CClass(constraint.name.text, this.inferTypeExpression(constraint.types[0]), constraint.name)); new CClass(
// } constraint.name.text,
this.inferTypeExpression(constraint.types[0]),
constraint.name
)
);
}
return this.inferTypeExpression(node.typeExpr, introduceTypeVars); return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
} }
@ -1646,7 +1633,7 @@ export class Checker {
} }
default: default:
throw new Error(`Unrecognised ${node}`); assertNever(node);
} }
@ -1778,6 +1765,18 @@ export class Checker {
} }
private getClassMeta(name: string): ClassMeta {
let meta = this.classDecls.get(name);
if (meta === undefined) {
meta = {
decl: null,
instances: new Set(),
};
this.classDecls.set(name, meta);
}
return meta;
}
private initialize(node: Syntax): void { private initialize(node: Syntax): void {
switch (node.kind) { switch (node.kind) {
@ -1816,6 +1815,11 @@ export class Checker {
case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassDeclaration:
{ {
const meta = this.getClassMeta(node.name.text);
if (meta.decl !== undefined) {
// TODO class declaration already exists diagnostic
}
meta.decl = node;
const info = this.getInfo(node); const info = this.getInfo(node);
const env = info.typeEnv = new TypeEnv(); const env = info.typeEnv = new TypeEnv();
for (const tv of node.types) { for (const tv of node.types) {
@ -1830,14 +1834,19 @@ export class Checker {
case SyntaxKind.InstanceDeclaration: case SyntaxKind.InstanceDeclaration:
{ {
if (!this.classDecls.has(node.name.text)) { const meta = this.getClassMeta(node.name.text);
meta.instances.add(node);
if (meta.decl === null) {
this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name.text, node.name)); this.diagnostics.add(new TypeclassNotFoundDiagnostic(node.name.text, node.name));
} }
const info = this.getInfo(node); const info = this.getInfo(node);
info.typeEnv = new TypeEnv(); info.typeEnv = new TypeEnv();
for (const element of node.elements) { for (const element of node.elements) {
this.initialize(element); this.initialize(element);
} }
break; break;
} }
@ -1974,23 +1983,30 @@ export class Checker {
typeArgs.push(typeArg); typeArgs.push(typeArg);
} }
// const tagType = TApp.build(
// this.createTCon(node.name.text, node.name),
// typeArgs,
// node.name
// );
const tagType = this.createTCon(node.name.text, node.name);
const fields = new Map<string, Type>(); const fields = new Map<string, Type>();
const restType = new TNil(node); const restType = new TNil(node);
fields.set(labelTag, tagType);
if (node.fields !== null) { if (node.fields !== null) {
for (const field of node.fields) { for (const field of node.fields) {
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr)); fields.set(field.name.text, this.inferTypeExpression(field.typeExpr));
} }
} }
const type = this.createTCon(node.name.text, node.name);
const recordType = TField.build(fields, restType); const recordType = TField.build(fields, restType);
this.polyContextStack.pop(); this.polyContextStack.pop();
this.typeEnvStack.pop(); this.typeEnvStack.pop();
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), type), Symkind.Type); parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), recordType), Symkind.Type);
parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), new TArrow(recordType, type)), Symkind.Var); // parentEnv.add(node.name.text, new Forall(poly.typeVars, new CMany(poly.constraints), new TArrow(recordType, type)), Symkind.Var);
break; break;
} }
@ -2041,7 +2057,16 @@ export class Checker {
const paramTypes = node.params.map(param => { const paramTypes = node.params.map(param => {
const paramType = this.createTRegularVar(); const paramType = this.createTRegularVar();
switch (param.kind) {
case SyntaxKind.PlainParam:
this.inferBindings(param.pattern, paramType) this.inferBindings(param.pattern, paramType)
break;
case SyntaxKind.InstanceParam:
this.addBinding(param.name.text, Forall.mono(paramType), Symkind.Var);
break;
default:
assertNever(param);
}
return paramType; return paramType;
}); });
@ -2094,17 +2119,35 @@ export class Checker {
} }
// private findInstanceContext(sig: Type[], clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
// const contexts = [];
// const meta = this.getClassMeta(clazz.name.text);
// // TODO should be a seperate verification pass somewhere
// // if (meta.decl === null) {
// // this.diagnostics.add(new TypeclassNotFoundDiagnostic(clazz.name.text));
// // }
// for (const instance of meta.instances) {
// let i = 0;
// for (const type of instance.types) {
// // TODO might need unification
// const left = sig[i++];
// const right = this.getTypeOfNode(type);
// if (assignableTo(left, right)) {
// contexts.push(context);
// }
// }
// }
// return contexts;
// }
private path: (string | number)[] = []; private path: (string | number)[] = [];
private constraint: Constraint | null = null; private constraint: Constraint | null = null;
private maxTypeErrorCount = 5; private maxTypeErrorCount = 5;
private find(type: Type): Type {
while (type.kind === TypeKind.RegularVar && this.typeSolution.has(type)) {
type = this.typeSolution.get(type)!;
}
return type;
}
private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean { private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean {
const swap = () => { [right, left] = [left, right]; } const swap = () => { [right, left] = [left, right]; }
@ -2132,7 +2175,6 @@ export class Checker {
return this.unify(left.type, right.type, enableDiagnostics); return this.unify(left.type, right.type, enableDiagnostics);
} }
private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean { private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean {
//console.log(`unify ${describeType(left)} @ ${left.node && left.node.constructor && left.node.constructor.name} ~ ${describeType(right)} @ ${right.node && right.node.constructor && right.node.constructor.name}`); //console.log(`unify ${describeType(left)} @ ${left.node && left.node.constructor && left.node.constructor.name} ~ ${describeType(right)} @ ${right.node && right.node.constructor && right.node.constructor.name}`);
@ -2167,30 +2209,33 @@ export class Checker {
// propagating the type classes that 'left' requires to 'right'. // propagating the type classes that 'left' requires to 'right'.
// If 'right' is another type variable, we're lucky. We just copy // If 'right' is another type variable, we're lucky. We just copy
// the missing type classes from 'left' to 'right'. Otherwise, // the missing type classes from 'left' to 'right'. Otherwise,
const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => { //const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
if (isTVar(type)) { // if (isTVar(type)) {
for (const constraint of classes) { // for (const constraint of classes) {
type.context.add(constraint); // type.context.add(constraint);
} // }
} else if (type.kind === TypeKind.Con) { // } else if (isSignature(type)) {
for (const constraint of classes) { // const sig = getSignature(type);
propagateClassTCon(constraint, type); // for (const constraint of classes) {
} // propagateClassTCon(constraint, sig);
} else { // }
// } else {
// assert(false); // assert(false);
//this.diagnostics.add(new ); // //this.diagnostics.add(new );
} // }
} //}
const propagateClassTCon = (clazz: ClassDeclaration, type: TCon) => { //const propagateClassTCon = (clazz: ClassDeclaration, sig: Type[]) => {
const s = this.findInstanceContext(type, clazz); // const s = this.findInstanceContext(sig, clazz);
let i = 0; // let i = 1;
for (const classes of s) { // for (const classes of s) {
propagateClasses(classes, type.types[i++]); // propagateClasses(classes, sig[i++]);
} // }
} //}
propagateClasses(left.context, right); //if (left.context.size > 0) {
// propagateClasses(left.context, right);
//}
// We are all clear; set the actual type of left to right. // We are all clear; set the actual type of left to right.
left.set(right); left.set(right);
@ -2305,6 +2350,35 @@ export class Checker {
return false; return false;
} }
// private inHnf(p: Pred): boolean {
// let curr = p.type;
// for (;;) {
// if (isTVar(curr)) {
// return true;
// }
// if (curr.kind === TypeKind.Con) {
// return false;
// }
// if (curr.kind === TypeKind.App) {
// curr = curr.left;
// continue;
// }
// unreachable();
// }
// }
// private toHnf(p: Pred): Pred[] {
// if (this.inHnf(p)) {
// return [ p ];
// }
// const result = this.byInst(p);
// if (result === undefined) {
// // TODO add diagnostic
// throw new Error(`context reduction`);
// }
// result.map(this.toHnf.bind(this)).flatten();
// }
public solve(constraint: Constraint): void { public solve(constraint: Constraint): void {
let queue = [ constraint ]; let queue = [ constraint ];
@ -2317,6 +2391,9 @@ export class Checker {
switch (constraint.kind) { switch (constraint.kind) {
case ConstraintKind.Empty:
break;
case ConstraintKind.Many: case ConstraintKind.Many:
{ {
for (const element of constraint.elements) { for (const element of constraint.elements) {
@ -2337,13 +2414,20 @@ export class Checker {
break; break;
} }
case ConstraintKind.Class:
// TODO
break;
default:
assertNever(constraint);;
} }
} }
} }
private lookupClass(name: string): ClassDeclaration | null { private lookupClass(name: string): ClassMeta | null {
return this.classDecls.get(name) ?? null; return this.classDecls.get(name) ?? null;
} }

View file

@ -6,6 +6,7 @@ import { first, InspectFn, last, toStringTag } from "./util";
export const enum ConstraintKind { export const enum ConstraintKind {
Equal, Equal,
// Class,
Many, Many,
Empty, Empty,
} }
@ -99,19 +100,48 @@ export class CMany extends ConstraintBase {
} }
} }
public [toStringTag](currentDepth: number, { depth = 2, ...options }: InspectOptions, inspect: InspectFn): string { public [toStringTag](_depth: number, opts: InspectOptions, inspect: InspectFn): string {
if (this.elements.length === 0) { if (this.elements.length === 0) {
return '[]'; return '[]';
} }
let out = '[\n'; let out = '[\n';
const newOptions = { ...options, depth: depth === null ? null : depth - 1 }; out += this.elements.map(constraint => ' ' + inspect(constraint, opts)).join('\n');
out += this.elements.map(constraint => ' ' + inspect(constraint, newOptions)).join('\n');
out += '\n]'; out += '\n]';
return out; return out;
} }
} }
// export class CClass extends ConstraintBase {
// public readonly kind = ConstraintKind.Class;
// public constructor(
// public className: string,
// public type: Type,
// public node: Syntax,
// ) {
// super();
// }
// public substitute(sub: TVSub): Constraint {
// return new CClass(
// this.className,
// this.type.substitute(sub),
// this.node,
// );
// }
// public *freeTypeVars(): Iterable<TVar> {
// yield* this.type.getTypeVars();
// }
// public [toStringTag](_depth: number, opts: InspectOptions, inspect: InspectFn): string {
// return this.className + ' => ' + inspect(this.type, opts);
// }
// }
export class CEmpty extends ConstraintBase { export class CEmpty extends ConstraintBase {
public readonly kind = ConstraintKind.Empty; public readonly kind = ConstraintKind.Empty;
@ -132,6 +162,7 @@ export class CEmpty extends ConstraintBase {
export type Constraint export type Constraint
= CEqual = CEqual
// | CClass
| CMany | CMany
| CEmpty | CEmpty

View file

@ -7,6 +7,7 @@ import { isNodeWithScope, Scope } from "./scope"
import type { Kind, Scheme } from "./checker" import type { Kind, Scheme } from "./checker"
import type { Type } from "./types"; import type { Type } from "./types";
import { Emitter } from "./emitter"; import { Emitter } from "./emitter";
import { warn } from "console";
export type TextSpan = [number, number]; export type TextSpan = [number, number];
@ -81,7 +82,7 @@ export class TextFile {
} }
export const enum SyntaxKind { export enum SyntaxKind {
// Tokens // Tokens
Identifier, Identifier,
@ -140,6 +141,7 @@ export const enum SyntaxKind {
NestedTypeExpression, NestedTypeExpression,
TupleTypeExpression, TupleTypeExpression,
ForallTypeExpression, ForallTypeExpression,
InstanceTypeExpression,
TypeExpressionWithConstraints, TypeExpressionWithConstraints,
// Patterns // Patterns
@ -204,12 +206,15 @@ export const enum SyntaxKind {
EnumDeclarationStructElement, EnumDeclarationStructElement,
EnumDeclarationTupleElement, EnumDeclarationTupleElement,
// Parameters
PlainParam,
InstanceParam,
// Other nodes // Other nodes
WrappedOperator, WrappedOperator,
MatchArm, MatchArm,
Initializer, Initializer,
TypeAssert, TypeAssert,
Param,
SourceFile, SourceFile,
ClassConstraint, ClassConstraint,
ClassConstraintClause, ClassConstraintClause,
@ -1486,6 +1491,44 @@ export class NestedTypeExpression extends SyntaxBase {
} }
export class InstanceTypeExpression extends SyntaxBase {
public readonly kind = SyntaxKind.InstanceTypeExpression;
public constructor(
public lbrace1: LBrace,
public lbrace2: LBrace,
public name: Identifier | null = null,
public colon: Colon | null = null,
public typeExpr: TypeExpression,
public rbrace1: RBrace,
public rbrace2: RBrace,
) {
super();
}
public clone(): InstanceTypeExpression {
return new InstanceTypeExpression(
this.lbrace1,
this.lbrace2,
this.name,
this.colon,
this.typeExpr,
this.rbrace1,
this.rbrace2,
);
}
public getFirstToken(): Token {
return this.lbrace1;
}
public getLastToken(): Token {
return this.rbrace2;
}
}
export type TypeExpression export type TypeExpression
= ReferenceTypeExpression = ReferenceTypeExpression
| ArrowTypeExpression | ArrowTypeExpression
@ -1495,6 +1538,7 @@ export type TypeExpression
| TupleTypeExpression | TupleTypeExpression
| ForallTypeExpression | ForallTypeExpression
| TypeExpressionWithConstraints | TypeExpressionWithConstraints
| InstanceTypeExpression
export class NamedPattern extends SyntaxBase { export class NamedPattern extends SyntaxBase {
@ -2494,9 +2538,43 @@ export type Statement
| IfStatement | IfStatement
| AssignStatement | AssignStatement
export class Param extends SyntaxBase { export class InstanceParam extends SyntaxBase {
public readonly kind = SyntaxKind.Param; public readonly kind = SyntaxKind.InstanceParam;
public constructor(
public lbrace1: LBrace,
public lbrace2: LBrace,
public name: Identifier,
public rbrace1: RBrace,
public rbrace2: RBrace,
) {
super();
}
public clone(): InstanceParam {
return new InstanceParam(
this.lbrace1,
this.lbrace2,
this.name,
this.rbrace1,
this.rbrace2,
);
}
public getFirstToken(): Token {
return this.lbrace1;
}
public getLastToken(): Token {
return this.rbrace2;
}
}
export class PlainParam extends SyntaxBase {
public readonly kind = SyntaxKind.PlainParam;
public constructor( public constructor(
public pattern: Pattern, public pattern: Pattern,
@ -2504,8 +2582,8 @@ export class Param extends SyntaxBase {
super(); super();
} }
public clone(): Param { public clone(): PlainParam {
return new Param( return new PlainParam(
this.pattern.clone(), this.pattern.clone(),
); );
} }
@ -2520,6 +2598,15 @@ export class Param extends SyntaxBase {
} }
export type Param
= InstanceParam
| PlainParam
export function isParam(node: Syntax): node is Param {
return node.kind === SyntaxKind.PlainParam
|| node.kind === SyntaxKind.InstanceParam;
}
export class EnumDeclarationStructElement extends SyntaxBase { export class EnumDeclarationStructElement extends SyntaxBase {
public readonly kind = SyntaxKind.EnumDeclarationStructElement; public readonly kind = SyntaxKind.EnumDeclarationStructElement;

View file

@ -1,8 +1,10 @@
import { Kind, KindType } from "./checker"; import { Kind, KindType } from "./checker";
import { type Type, TypeKind } from "./types" import { type Type, TypeKind, labelTag } from "./types"
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst"; import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
import { assertNever, countDigits, IndentWriter } from "./util"; import { assert, assertNever, countDigits, IndentWriter } from "./util";
import { unwatchFile } from "fs";
import { warn } from "console";
const ANSI_RESET = "\u001b[0m" const ANSI_RESET = "\u001b[0m"
const ANSI_BOLD = "\u001b[1m" const ANSI_BOLD = "\u001b[1m"
@ -537,6 +539,13 @@ export function describeType(type: Type): string {
} }
case TypeKind.Field: case TypeKind.Field:
{ {
// let curr: Type = type;
// while (curr.kind === TypeKind.Field) {
// if (curr.name === labelTag) {
// return describeType(curr.type);
// }
// curr = curr.restType;
// }
let out = '{ ' + type.name + ': ' + describeType(type.type); let out = '{ ' + type.name + ': ' + describeType(type.type);
type = type.restType; type = type.restType;
while (type.kind === TypeKind.Field) { while (type.kind === TypeKind.Field) {

View file

@ -91,7 +91,7 @@ export class Emitter {
this.writer.write(node.name.text); this.writer.write(node.name.text);
break; break;
case SyntaxKind.Param: case SyntaxKind.PlainParam:
this.emit(node.pattern); this.emit(node.pattern);
break; break;

View file

@ -1,4 +1,5 @@
import { warn } from "console";
import { import {
ReferenceTypeExpression, ReferenceTypeExpression,
SourceFile, SourceFile,
@ -73,8 +74,12 @@ import {
Annotations, Annotations,
ExprOperator, ExprOperator,
Integer, Integer,
InstanceParam,
PlainParam,
InstanceTypeExpression,
} from "./cst" } from "./cst"
import { Stream } from "./util"; import { Stream } from "./util";
import { wrap } from "module";
export class ParseError extends Error { export class ParseError extends Error {
@ -196,7 +201,24 @@ export class Parser {
} }
public parsePrimitiveTypeExpression(): TypeExpression { public parsePrimitiveTypeExpression(): TypeExpression {
const t0 = this.peekToken(); const t0 = this.peekToken(1);
const t1 = this.peekToken(2);
if (t0.kind === SyntaxKind.LBrace && t1.kind === SyntaxKind.LBrace) {
this.getToken();
this.getToken();
let name = null;
let colon = null;
const t2 = this.peekToken(2);
if (t2.kind === SyntaxKind.Colon) {
name = this.expectToken(SyntaxKind.Identifier);
this.getToken();
colon = t2;
}
const typeExpr = this.parseTypeExpression();
const t3 = this.expectToken(SyntaxKind.RBrace);
const t4 = this.expectToken(SyntaxKind.RBrace);
return new InstanceTypeExpression(t0, t1, name, colon, typeExpr, t3, t4);
}
switch (t0.kind) { switch (t0.kind) {
case SyntaxKind.Identifier: case SyntaxKind.Identifier:
return this.parseVarTypeExpression(); return this.parseVarTypeExpression();
@ -893,8 +915,18 @@ export class Parser {
} }
public parseParam(): Param { public parseParam(): Param {
const t0 = this.peekToken(1);
const t1 = this.peekToken(2);
if (t0.kind === SyntaxKind.LBrace && t1.kind === SyntaxKind.LBrace) {
this.getToken();
this.getToken();
const name = this.expectToken(SyntaxKind.Identifier);
const t3 = this.expectToken(SyntaxKind.RBrace);
const t4 = this.expectToken(SyntaxKind.RBrace);
return new InstanceParam(t0, t1, name, t3, t4);
}
const pattern = this.parsePattern(); const pattern = this.parsePattern();
return new Param(pattern); return new PlainParam(pattern);
} }
private lookaheadIsAssignment(): boolean { private lookaheadIsAssignment(): boolean {

View file

@ -17,7 +17,7 @@ import {
FunctionExpression, FunctionExpression,
Backslash, Backslash,
canHaveInstanceDeclaration, canHaveInstanceDeclaration,
vistEachChild visitEachChild
} from "../cst"; } from "../cst";
import { Pass } from "../program"; import { Pass } from "../program";
import { assert } from "../util"; import { assert } from "../util";
@ -50,7 +50,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
private visit(node: Syntax): Syntax { private visit(node: Syntax): Syntax {
if (canHaveInstanceDeclaration(node)) { if (canHaveInstanceDeclaration(node)) {
return vistEachChild(node, this.visit.bind(this)); return visitEachChild(node, this.visit.bind(this));
} }
if (node.kind === SyntaxKind.InstanceDeclaration) { if (node.kind === SyntaxKind.InstanceDeclaration) {
const decl = new LetDeclaration( const decl = new LetDeclaration(

View file

@ -1,4 +1,5 @@
import { warn } from "console";
import { import {
SyntaxKind, SyntaxKind,
Token, Token,

View file

@ -1,5 +1,6 @@
import { warn } from "console";
import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst"; import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst";
import { MultiMap } from "./util"; import { MultiMap, assertNever } from "./util";
export type NodeWithScope export type NodeWithScope
= SourceFile = SourceFile
@ -96,13 +97,23 @@ export class Scope {
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
{ {
this.add(node.name.text, node, Symkind.Type); this.add(node.name.text, node, Symkind.Type);
this.add(node.name.text, node, Symkind.Var); // TODO remove this?
// this.add(node.name.text, node, Symkind.Var);
break; break;
} }
case SyntaxKind.LetDeclaration: case SyntaxKind.LetDeclaration:
{ {
for (const param of node.params) { for (const param of node.params) {
switch (param.kind) {
case SyntaxKind.PlainParam:
this.scanPattern(param.pattern, param); this.scanPattern(param.pattern, param);
break;
case SyntaxKind.InstanceParam:
this.add(node.name.text, param, Symkind.Var);
break;
default:
assertNever(param);
}
} }
if (node === this.node) { if (node === this.node) {
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) { if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
@ -116,7 +127,7 @@ export class Scope {
break; break;
} }
default: default:
throw new Error(`Unexpected ${node.constructor.name}`); assertNever(node);
} }
} }

View file

@ -14,6 +14,7 @@ export enum TypeKind {
Nil, Nil,
Absent, Absent,
Present, Present,
Tag,
} }
export abstract class TypeBase { export abstract class TypeBase {
@ -457,6 +458,40 @@ export class TApp extends TypeBase {
} }
export const labelTag = '____tag';
// export class TTag extends TypeBase {
// public readonly kind = TypeKind.Tag;
// public constructor(
// public name: string,
// public node: Syntax | null = null,
// ) {
// super();
// }
// public shallowClone(): Type {
// return new TTag(
// this.name,
// this.node,
// );
// }
// public *getTypeVars(): Iterable<TVar> {
// // noop
// }
// public substitute(sub: TVSub): Type {
// return this;
// }
// public [toStringTag]() {
// return this.name;
// }
// }
export type Type export type Type
= TCon = TCon
| TArrow | TArrow
@ -467,11 +502,43 @@ export type Type
| TNil | TNil
| TPresent | TPresent
| TAbsent | TAbsent
// | TTag
export type TVar export type TVar
= TRegularVar = TRegularVar
| TRigidVar | TRigidVar
export function getSignature(type: Type): Type[] {
const out = [];
let stack = [ type ];
for (;;) {
const child = stack.pop()!;
if (child.kind === TypeKind.App) {
stack.push(child.left);
stack.push(child.right);
} else {
out.push(child);
}
if (stack.length === 0) {
break;
}
}
return out;
}
export function isSignature(type: Type): boolean {
return type.kind === TypeKind.Con
|| type.kind === TypeKind.App;
}
export function assignableTo(left: Type, right: Type): boolean {
if (left.kind === TypeKind.Con && right.kind == TypeKind.Con) {
return left.id === right.id;
}
return false;
}
export function typesEqual(a: Type, b: Type): boolean { export function typesEqual(a: Type, b: Type): boolean {
if (a.kind !== b.kind) { if (a.kind !== b.kind) {
return false; return false;