[WIP] Further attempt to get typeclasses working
This commit is contained in:
parent
719dbfcad4
commit
e01a970377
11 changed files with 424 additions and 102 deletions
|
@ -13,7 +13,7 @@ export class Analyser {
|
|||
|
||||
const addReference = (scope: Scope, name: string) => {
|
||||
const target = scope.lookup(name);
|
||||
if (source === null || target === null || target.kind === SyntaxKind.Param) {
|
||||
if (source === null || target === null || isParam(target.kind)) {
|
||||
return;
|
||||
}
|
||||
assert(source.kind === SyntaxKind.LetDeclaration);
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
ExprOperator,
|
||||
Identifier,
|
||||
IdentifierAlt,
|
||||
InstanceDeclaration,
|
||||
LetDeclaration,
|
||||
Pattern,
|
||||
ReferenceExpression,
|
||||
|
@ -23,58 +24,31 @@ import {
|
|||
KindMismatchDiagnostic,
|
||||
ModuleNotFoundDiagnostic,
|
||||
TypeclassNotFoundDiagnostic,
|
||||
TypeclassDeclaredTwiceDiagnostic,
|
||||
FieldNotFoundDiagnostic,
|
||||
TypeMismatchDiagnostic,
|
||||
} 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 { 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";
|
||||
|
||||
// export class Qual {
|
||||
// class IsIn {
|
||||
|
||||
// public constructor(
|
||||
// public preds: Pred[],
|
||||
// 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 className: string,
|
||||
// public type: Type,
|
||||
// ) {
|
||||
|
||||
// }
|
||||
|
||||
// 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 {
|
||||
Type,
|
||||
|
@ -371,6 +345,11 @@ function hasTypeVar(typeVars: TVSet, type: Type): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
interface ClassMeta {
|
||||
decl: ClassDeclaration | null;
|
||||
instances: Set<InstanceDeclaration>;
|
||||
}
|
||||
|
||||
export class Checker {
|
||||
|
||||
private nextTypeVarId = 0;
|
||||
|
@ -382,7 +361,7 @@ export class Checker {
|
|||
private boolType = this.createTCon('Bool');
|
||||
private unitType = buildTupleType([]);
|
||||
|
||||
private classDecls = new Map<string, ClassDeclaration>();
|
||||
private classDecls = new Map<string, ClassMeta>();
|
||||
private globalKindEnv = new KindEnv();
|
||||
private globalTypeEnv = new TypeEnv();
|
||||
|
||||
|
@ -779,8 +758,8 @@ export class Checker {
|
|||
case ConstraintKind.Empty:
|
||||
return constraint;
|
||||
case ConstraintKind.Equal:
|
||||
constraint.left = this.simplifyType(constraint.left)
|
||||
constraint.right = this.simplifyType(constraint.right)
|
||||
// constraint.left = this.simplifyType(constraint.left)
|
||||
// constraint.right = this.simplifyType(constraint.right)
|
||||
const newConstraint = constraint.substitute(sub);
|
||||
newConstraint.node = node;
|
||||
newConstraint.prevInstantiation = constraint;
|
||||
|
@ -877,13 +856,15 @@ export class Checker {
|
|||
}
|
||||
|
||||
case SyntaxKind.NestedTypeExpression:
|
||||
case SyntaxKind.InstanceTypeExpression:
|
||||
{
|
||||
kind = this.inferKindFromTypeExpression(node.typeExpr, env);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected ${node}`);
|
||||
assertNever(node);
|
||||
|
||||
}
|
||||
|
||||
// We store the kind on the node so there is a one-to-one correspondence
|
||||
|
@ -1580,6 +1561,7 @@ export class Checker {
|
|||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.InstanceTypeExpression:
|
||||
case SyntaxKind.NestedTypeExpression:
|
||||
type = this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
||||
break;
|
||||
|
@ -1613,11 +1595,16 @@ export class Checker {
|
|||
|
||||
case SyntaxKind.TypeExpressionWithConstraints:
|
||||
{
|
||||
// TODO
|
||||
// for (const constraint of node.constraints) {
|
||||
// implementationLimitation(constraint.types.length === 1);
|
||||
// this.addConstraint(new CClass(constraint.name.text, this.inferTypeExpression(constraint.types[0]), constraint.name));
|
||||
// }
|
||||
for (const constraint of node.constraints) {
|
||||
implementationLimitation(constraint.types.length === 1);
|
||||
this.addConstraint(
|
||||
new CClass(
|
||||
constraint.name.text,
|
||||
this.inferTypeExpression(constraint.types[0]),
|
||||
constraint.name
|
||||
)
|
||||
);
|
||||
}
|
||||
return this.inferTypeExpression(node.typeExpr, introduceTypeVars);
|
||||
}
|
||||
|
||||
|
@ -1646,7 +1633,7 @@ export class Checker {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
||||
switch (node.kind) {
|
||||
|
@ -1816,6 +1815,11 @@ export class Checker {
|
|||
|
||||
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 env = info.typeEnv = new TypeEnv();
|
||||
for (const tv of node.types) {
|
||||
|
@ -1830,14 +1834,19 @@ export class Checker {
|
|||
|
||||
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));
|
||||
}
|
||||
|
||||
const info = this.getInfo(node);
|
||||
info.typeEnv = new TypeEnv();
|
||||
for (const element of node.elements) {
|
||||
this.initialize(element);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1974,23 +1983,30 @@ export class Checker {
|
|||
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 restType = new TNil(node);
|
||||
|
||||
fields.set(labelTag, tagType);
|
||||
|
||||
if (node.fields !== null) {
|
||||
for (const field of node.fields) {
|
||||
fields.set(field.name.text, this.inferTypeExpression(field.typeExpr));
|
||||
}
|
||||
}
|
||||
|
||||
const type = this.createTCon(node.name.text, node.name);
|
||||
const recordType = TField.build(fields, restType);
|
||||
|
||||
this.polyContextStack.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), new TArrow(recordType, type)), Symkind.Var);
|
||||
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);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -2041,7 +2057,16 @@ export class Checker {
|
|||
|
||||
const paramTypes = node.params.map(param => {
|
||||
const paramType = this.createTRegularVar();
|
||||
switch (param.kind) {
|
||||
case SyntaxKind.PlainParam:
|
||||
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;
|
||||
});
|
||||
|
||||
|
@ -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 constraint: Constraint | null = null;
|
||||
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 {
|
||||
|
||||
const swap = () => { [right, left] = [left, right]; }
|
||||
|
@ -2132,7 +2175,6 @@ export class Checker {
|
|||
return this.unify(left.type, right.type, enableDiagnostics);
|
||||
}
|
||||
|
||||
|
||||
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}`);
|
||||
|
@ -2167,30 +2209,33 @@ export class Checker {
|
|||
// propagating the type classes that 'left' requires to 'right'.
|
||||
// If 'right' is another type variable, we're lucky. We just copy
|
||||
// the missing type classes from 'left' to 'right'. Otherwise,
|
||||
const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
|
||||
if (isTVar(type)) {
|
||||
for (const constraint of classes) {
|
||||
type.context.add(constraint);
|
||||
}
|
||||
} else if (type.kind === TypeKind.Con) {
|
||||
for (const constraint of classes) {
|
||||
propagateClassTCon(constraint, type);
|
||||
}
|
||||
} else {
|
||||
//const propagateClasses = (classes: Iterable<ClassDeclaration>, type: Type) => {
|
||||
// if (isTVar(type)) {
|
||||
// for (const constraint of classes) {
|
||||
// type.context.add(constraint);
|
||||
// }
|
||||
// } else if (isSignature(type)) {
|
||||
// const sig = getSignature(type);
|
||||
// for (const constraint of classes) {
|
||||
// propagateClassTCon(constraint, sig);
|
||||
// }
|
||||
// } else {
|
||||
// assert(false);
|
||||
//this.diagnostics.add(new );
|
||||
}
|
||||
}
|
||||
// //this.diagnostics.add(new );
|
||||
// }
|
||||
//}
|
||||
|
||||
const propagateClassTCon = (clazz: ClassDeclaration, type: TCon) => {
|
||||
const s = this.findInstanceContext(type, clazz);
|
||||
let i = 0;
|
||||
for (const classes of s) {
|
||||
propagateClasses(classes, type.types[i++]);
|
||||
}
|
||||
}
|
||||
//const propagateClassTCon = (clazz: ClassDeclaration, sig: Type[]) => {
|
||||
// const s = this.findInstanceContext(sig, clazz);
|
||||
// let i = 1;
|
||||
// for (const classes of s) {
|
||||
// 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.
|
||||
left.set(right);
|
||||
|
@ -2305,6 +2350,35 @@ export class Checker {
|
|||
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 {
|
||||
|
||||
let queue = [ constraint ];
|
||||
|
@ -2317,6 +2391,9 @@ export class Checker {
|
|||
|
||||
switch (constraint.kind) {
|
||||
|
||||
case ConstraintKind.Empty:
|
||||
break;
|
||||
|
||||
case ConstraintKind.Many:
|
||||
{
|
||||
for (const element of constraint.elements) {
|
||||
|
@ -2337,13 +2414,20 @@ export class Checker {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { first, InspectFn, last, toStringTag } from "./util";
|
|||
|
||||
export const enum ConstraintKind {
|
||||
Equal,
|
||||
// Class,
|
||||
Many,
|
||||
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) {
|
||||
return '[]';
|
||||
}
|
||||
let out = '[\n';
|
||||
const newOptions = { ...options, depth: depth === null ? null : depth - 1 };
|
||||
out += this.elements.map(constraint => ' ' + inspect(constraint, newOptions)).join('\n');
|
||||
out += this.elements.map(constraint => ' ' + inspect(constraint, opts)).join('\n');
|
||||
out += '\n]';
|
||||
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 {
|
||||
|
||||
public readonly kind = ConstraintKind.Empty;
|
||||
|
@ -132,6 +162,7 @@ export class CEmpty extends ConstraintBase {
|
|||
|
||||
export type Constraint
|
||||
= CEqual
|
||||
// | CClass
|
||||
| CMany
|
||||
| CEmpty
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { isNodeWithScope, Scope } from "./scope"
|
|||
import type { Kind, Scheme } from "./checker"
|
||||
import type { Type } from "./types";
|
||||
import { Emitter } from "./emitter";
|
||||
import { warn } from "console";
|
||||
|
||||
export type TextSpan = [number, number];
|
||||
|
||||
|
@ -81,7 +82,7 @@ export class TextFile {
|
|||
|
||||
}
|
||||
|
||||
export const enum SyntaxKind {
|
||||
export enum SyntaxKind {
|
||||
|
||||
// Tokens
|
||||
Identifier,
|
||||
|
@ -140,6 +141,7 @@ export const enum SyntaxKind {
|
|||
NestedTypeExpression,
|
||||
TupleTypeExpression,
|
||||
ForallTypeExpression,
|
||||
InstanceTypeExpression,
|
||||
TypeExpressionWithConstraints,
|
||||
|
||||
// Patterns
|
||||
|
@ -204,12 +206,15 @@ export const enum SyntaxKind {
|
|||
EnumDeclarationStructElement,
|
||||
EnumDeclarationTupleElement,
|
||||
|
||||
// Parameters
|
||||
PlainParam,
|
||||
InstanceParam,
|
||||
|
||||
// Other nodes
|
||||
WrappedOperator,
|
||||
MatchArm,
|
||||
Initializer,
|
||||
TypeAssert,
|
||||
Param,
|
||||
SourceFile,
|
||||
ClassConstraint,
|
||||
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
|
||||
= ReferenceTypeExpression
|
||||
| ArrowTypeExpression
|
||||
|
@ -1495,6 +1538,7 @@ export type TypeExpression
|
|||
| TupleTypeExpression
|
||||
| ForallTypeExpression
|
||||
| TypeExpressionWithConstraints
|
||||
| InstanceTypeExpression
|
||||
|
||||
export class NamedPattern extends SyntaxBase {
|
||||
|
||||
|
@ -2494,9 +2538,43 @@ export type Statement
|
|||
| IfStatement
|
||||
| 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 pattern: Pattern,
|
||||
|
@ -2504,8 +2582,8 @@ export class Param extends SyntaxBase {
|
|||
super();
|
||||
}
|
||||
|
||||
public clone(): Param {
|
||||
return new Param(
|
||||
public clone(): PlainParam {
|
||||
return new PlainParam(
|
||||
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 {
|
||||
|
||||
public readonly kind = SyntaxKind.EnumDeclarationStructElement;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
|
||||
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 { 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_BOLD = "\u001b[1m"
|
||||
|
@ -537,6 +539,13 @@ export function describeType(type: Type): string {
|
|||
}
|
||||
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);
|
||||
type = type.restType;
|
||||
while (type.kind === TypeKind.Field) {
|
||||
|
|
|
@ -91,7 +91,7 @@ export class Emitter {
|
|||
this.writer.write(node.name.text);
|
||||
break;
|
||||
|
||||
case SyntaxKind.Param:
|
||||
case SyntaxKind.PlainParam:
|
||||
this.emit(node.pattern);
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
import { warn } from "console";
|
||||
import {
|
||||
ReferenceTypeExpression,
|
||||
SourceFile,
|
||||
|
@ -73,8 +74,12 @@ import {
|
|||
Annotations,
|
||||
ExprOperator,
|
||||
Integer,
|
||||
InstanceParam,
|
||||
PlainParam,
|
||||
InstanceTypeExpression,
|
||||
} from "./cst"
|
||||
import { Stream } from "./util";
|
||||
import { wrap } from "module";
|
||||
|
||||
export class ParseError extends Error {
|
||||
|
||||
|
@ -196,7 +201,24 @@ export class Parser {
|
|||
}
|
||||
|
||||
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) {
|
||||
case SyntaxKind.Identifier:
|
||||
return this.parseVarTypeExpression();
|
||||
|
@ -893,8 +915,18 @@ export class Parser {
|
|||
}
|
||||
|
||||
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();
|
||||
return new Param(pattern);
|
||||
return new PlainParam(pattern);
|
||||
}
|
||||
|
||||
private lookaheadIsAssignment(): boolean {
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
FunctionExpression,
|
||||
Backslash,
|
||||
canHaveInstanceDeclaration,
|
||||
vistEachChild
|
||||
visitEachChild
|
||||
} from "../cst";
|
||||
import { Pass } from "../program";
|
||||
import { assert } from "../util";
|
||||
|
@ -50,7 +50,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
|||
|
||||
private visit(node: Syntax): Syntax {
|
||||
if (canHaveInstanceDeclaration(node)) {
|
||||
return vistEachChild(node, this.visit.bind(this));
|
||||
return visitEachChild(node, this.visit.bind(this));
|
||||
}
|
||||
if (node.kind === SyntaxKind.InstanceDeclaration) {
|
||||
const decl = new LetDeclaration(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
|
||||
import { warn } from "console";
|
||||
import {
|
||||
SyntaxKind,
|
||||
Token,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { warn } from "console";
|
||||
import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst";
|
||||
import { MultiMap } from "./util";
|
||||
import { MultiMap, assertNever } from "./util";
|
||||
|
||||
export type NodeWithScope
|
||||
= SourceFile
|
||||
|
@ -96,13 +97,23 @@ export class Scope {
|
|||
case SyntaxKind.StructDeclaration:
|
||||
{
|
||||
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;
|
||||
}
|
||||
case SyntaxKind.LetDeclaration:
|
||||
{
|
||||
for (const param of node.params) {
|
||||
switch (param.kind) {
|
||||
case SyntaxKind.PlainParam:
|
||||
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.body !== null && node.body.kind === SyntaxKind.BlockBody) {
|
||||
|
@ -116,7 +127,7 @@ export class Scope {
|
|||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unexpected ${node.constructor.name}`);
|
||||
assertNever(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export enum TypeKind {
|
|||
Nil,
|
||||
Absent,
|
||||
Present,
|
||||
Tag,
|
||||
}
|
||||
|
||||
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
|
||||
= TCon
|
||||
| TArrow
|
||||
|
@ -467,11 +502,43 @@ export type Type
|
|||
| TNil
|
||||
| TPresent
|
||||
| TAbsent
|
||||
// | TTag
|
||||
|
||||
export type TVar
|
||||
= TRegularVar
|
||||
| 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 {
|
||||
if (a.kind !== b.kind) {
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue