Refactor by splitting into multiple files
This commit is contained in:
parent
767572b197
commit
f58011e50e
17 changed files with 1369 additions and 1281 deletions
|
@ -1,18 +1,14 @@
|
||||||
{
|
{
|
||||||
"name": "@samvv/bolt",
|
"name": "@boltlang/bolt",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "A new programming language for the web",
|
"description": "A new programming language for the web",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"bolt": "lib/bin/bolt.js",
|
"bolt": "lib/bin/bolt.js"
|
||||||
"bolt-self": "lib/bin/bolt-self.js"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "node ./lib/bin/bolt-self.js check"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/BoltJS"
|
"url": "https://github.com/samvv/BoltJS"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"programming-language",
|
"programming-language",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import "source-map-support/register"
|
import "source-map-support/register"
|
||||||
|
import "reflect-metadata"
|
||||||
|
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import util from "util"
|
import util from "util"
|
||||||
|
|
File diff suppressed because it is too large
Load diff
168
compiler/src/constraints.ts
Normal file
168
compiler/src/constraints.ts
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
|
||||||
|
import { InspectOptions } from "util";
|
||||||
|
import { Syntax } from "./cst"
|
||||||
|
import { TVar, TVSub, Type } from "./types";
|
||||||
|
import { first, InspectFn, last, toStringTag } from "./util";
|
||||||
|
|
||||||
|
export const enum ConstraintKind {
|
||||||
|
Equal,
|
||||||
|
Many,
|
||||||
|
Empty,
|
||||||
|
Class,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ConstraintBase {
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public node: Syntax | null = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public prevInstantiation: Constraint | null = null;
|
||||||
|
|
||||||
|
public *getNodes(): Iterable<Syntax> {
|
||||||
|
let curr: Constraint | null = this as any;
|
||||||
|
while (curr !== null) {
|
||||||
|
if (curr.node !== null) {
|
||||||
|
yield curr.node;
|
||||||
|
}
|
||||||
|
curr = curr.prevInstantiation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get lastNode(): Syntax | null {
|
||||||
|
return last(this.getNodes()[Symbol.iterator]()) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get firstNode(): Syntax | null {
|
||||||
|
return first(this.getNodes()[Symbol.iterator]()) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract freeTypeVars(): Iterable<TVar>;
|
||||||
|
|
||||||
|
public abstract substitute(sub: TVSub): Constraint;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CEqual extends ConstraintBase {
|
||||||
|
|
||||||
|
public readonly kind = ConstraintKind.Equal;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public left: Type,
|
||||||
|
public right: Type,
|
||||||
|
public node: Syntax | null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): CEqual {
|
||||||
|
return new CEqual(
|
||||||
|
this.left.substitute(sub),
|
||||||
|
this.right.substitute(sub),
|
||||||
|
this.node,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public *freeTypeVars(): Iterable<TVar> {
|
||||||
|
yield* this.left.getTypeVars();
|
||||||
|
yield* this.right.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_currentDepth: number, options: InspectOptions, inspect: InspectFn): string {
|
||||||
|
return inspect(this.left, options) + ' ~ ' + inspect(this.right, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CMany extends ConstraintBase {
|
||||||
|
|
||||||
|
public readonly kind = ConstraintKind.Many;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public elements: Constraint[]
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): CMany {
|
||||||
|
const newElements = [];
|
||||||
|
for (const element of this.elements) {
|
||||||
|
newElements.push(element.substitute(sub));
|
||||||
|
}
|
||||||
|
return new CMany(newElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public *freeTypeVars(): Iterable<TVar> {
|
||||||
|
for (const element of this.elements) {
|
||||||
|
yield* element.freeTypeVars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](currentDepth: number, { depth = 2, ...options }: 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 += '\n]';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CClass extends ConstraintBase {
|
||||||
|
|
||||||
|
public readonly kind = ConstraintKind.Class;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public className: string,
|
||||||
|
public type: Type,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): CClass {
|
||||||
|
return new CClass(this.className, this.type.substitute(sub));
|
||||||
|
}
|
||||||
|
|
||||||
|
public freeTypeVars(): Iterable<TVar> {
|
||||||
|
return this.type.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return this.className + ' ' + inspect(this.type, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CEmpty extends ConstraintBase {
|
||||||
|
|
||||||
|
public readonly kind = ConstraintKind.Empty;
|
||||||
|
|
||||||
|
public substitute(_sub: TVSub): CEmpty {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public *freeTypeVars(): Iterable<TVar> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag]() {
|
||||||
|
return 'ε';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Constraint
|
||||||
|
= CEqual
|
||||||
|
| CMany
|
||||||
|
| CEmpty
|
||||||
|
| CClass
|
||||||
|
|
||||||
|
export class ConstraintSet extends Array<Constraint> {
|
||||||
|
|
||||||
|
}
|
|
@ -4,7 +4,8 @@ import path from "path"
|
||||||
|
|
||||||
import { assert, deserializable, implementationLimitation, IndentWriter, JSONObject, JSONValue, nonenumerable, unreachable } from "./util";
|
import { assert, deserializable, implementationLimitation, IndentWriter, JSONObject, JSONValue, nonenumerable, unreachable } from "./util";
|
||||||
import { isNodeWithScope, Scope } from "./scope"
|
import { isNodeWithScope, Scope } from "./scope"
|
||||||
import { InferContext, Kind, KindEnv, Scheme, Type, TypeEnv } from "./checker"
|
import type { InferContext, Kind, KindEnv, Scheme, TypeEnv } from "./checker"
|
||||||
|
import type { Type } from "./types";
|
||||||
import { Emitter } from "./emitter";
|
import { Emitter } from "./emitter";
|
||||||
|
|
||||||
export type TextSpan = [number, number];
|
export type TextSpan = [number, number];
|
||||||
|
@ -121,6 +122,7 @@ export const enum SyntaxKind {
|
||||||
IfKeyword,
|
IfKeyword,
|
||||||
ElifKeyword,
|
ElifKeyword,
|
||||||
ElseKeyword,
|
ElseKeyword,
|
||||||
|
ForallKeyword,
|
||||||
LineFoldEnd,
|
LineFoldEnd,
|
||||||
BlockEnd,
|
BlockEnd,
|
||||||
BlockStart,
|
BlockStart,
|
||||||
|
@ -133,6 +135,8 @@ export const enum SyntaxKind {
|
||||||
AppTypeExpression,
|
AppTypeExpression,
|
||||||
NestedTypeExpression,
|
NestedTypeExpression,
|
||||||
TupleTypeExpression,
|
TupleTypeExpression,
|
||||||
|
ForallTypeExpression,
|
||||||
|
TypeExpressionWithConstraints,
|
||||||
|
|
||||||
// Patterns
|
// Patterns
|
||||||
NamedPattern,
|
NamedPattern,
|
||||||
|
@ -1201,6 +1205,21 @@ export class VBar extends TokenBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class ForallKeyword extends TokenBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.ForallKeyword;
|
||||||
|
|
||||||
|
public get text(): string {
|
||||||
|
return 'forall';
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): ForallKeyword {
|
||||||
|
return new ForallKeyword(this.startPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export type Token
|
export type Token
|
||||||
= RArrow
|
= RArrow
|
||||||
| RArrowAlt
|
| RArrowAlt
|
||||||
|
@ -1243,10 +1262,78 @@ export type Token
|
||||||
| ElifKeyword
|
| ElifKeyword
|
||||||
| EnumKeyword
|
| EnumKeyword
|
||||||
| ForeignKeyword
|
| ForeignKeyword
|
||||||
|
| ForallKeyword
|
||||||
|
|
||||||
export type TokenKind
|
export type TokenKind
|
||||||
= Token['kind']
|
= Token['kind']
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class ForallTypeExpression extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.ForallTypeExpression;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public forallKeyword: ForallKeyword,
|
||||||
|
public varTypeExps: VarTypeExpression[],
|
||||||
|
public dot: Dot,
|
||||||
|
public typeExpr: TypeExpression,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): ForallTypeExpression {
|
||||||
|
return new ForallTypeExpression(
|
||||||
|
this.forallKeyword.clone(),
|
||||||
|
this.varTypeExps.map(e => e.clone()),
|
||||||
|
this.dot.clone(),
|
||||||
|
this.typeExpr.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.forallKeyword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.typeExpr.getLastToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TypeExpressionWithConstraints extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.TypeExpressionWithConstraints;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public constraints: ClassConstraint[],
|
||||||
|
public rarrowAlt: RArrowAlt,
|
||||||
|
public typeExpr: TypeExpression,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): TypeExpressionWithConstraints {
|
||||||
|
return new TypeExpressionWithConstraints(
|
||||||
|
this.constraints.map(c => c.clone()),
|
||||||
|
this.rarrowAlt.clone(),
|
||||||
|
this.typeExpr.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
if (this.constraints.length > 0) {
|
||||||
|
return this.constraints[0].getFirstToken();
|
||||||
|
}
|
||||||
|
return this.rarrowAlt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.typeExpr.getLastToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@deserializable()
|
@deserializable()
|
||||||
export class ArrowTypeExpression extends SyntaxBase {
|
export class ArrowTypeExpression extends SyntaxBase {
|
||||||
|
|
||||||
|
@ -1437,6 +1524,8 @@ export type TypeExpression
|
||||||
| AppTypeExpression
|
| AppTypeExpression
|
||||||
| NestedTypeExpression
|
| NestedTypeExpression
|
||||||
| TupleTypeExpression
|
| TupleTypeExpression
|
||||||
|
| ForallTypeExpression
|
||||||
|
| TypeExpressionWithConstraints
|
||||||
|
|
||||||
@deserializable()
|
@deserializable()
|
||||||
export class NamedPattern extends SyntaxBase {
|
export class NamedPattern extends SyntaxBase {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
import { TypeKind, type Type, Kind, KindType } from "./checker";
|
import { Kind, KindType } from "./checker";
|
||||||
|
import { type Type, TypeKind } 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, deserializable, IndentWriter } from "./util";
|
import { assertNever, countDigits, deserializable, IndentWriter } from "./util";
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ const enum DiagnosticKind {
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
TypeclassNotFound,
|
TypeclassNotFound,
|
||||||
TypeclassDecaredTwice,
|
TypeclassDecaredTwice,
|
||||||
|
TypeclassNotImplemented,
|
||||||
BindingNotFound,
|
BindingNotFound,
|
||||||
ModuleNotFound,
|
ModuleNotFound,
|
||||||
FieldNotFound,
|
FieldNotFound,
|
||||||
|
@ -114,7 +116,8 @@ export class TypeclassNotFoundDiagnostic extends DiagnosticBase {
|
||||||
public level = Level.Error;
|
public level = Level.Error;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public name: IdentifierAlt,
|
public name: string,
|
||||||
|
public node: Syntax | null = null,
|
||||||
public origin: InstanceDeclaration | ClassConstraint | null = null,
|
public origin: InstanceDeclaration | ClassConstraint | null = null,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -122,6 +125,23 @@ export class TypeclassNotFoundDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TypeclassNotImplementedDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
|
public readonly kind = DiagnosticKind.TypeclassNotImplemented;
|
||||||
|
|
||||||
|
public level = Level.Error;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public name: string,
|
||||||
|
public type: Type,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@deserializable()
|
@deserializable()
|
||||||
export class BindingNotFoundDiagnostic extends DiagnosticBase {
|
export class BindingNotFoundDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
|
@ -212,6 +232,7 @@ export type Diagnostic
|
||||||
= UnexpectedCharDiagnostic
|
= UnexpectedCharDiagnostic
|
||||||
| TypeclassNotFoundDiagnostic
|
| TypeclassNotFoundDiagnostic
|
||||||
| TypeclassDeclaredTwiceDiagnostic
|
| TypeclassDeclaredTwiceDiagnostic
|
||||||
|
| TypeclassNotImplementedDiagnostic
|
||||||
| BindingNotFoundDiagnostic
|
| BindingNotFoundDiagnostic
|
||||||
| TypeMismatchDiagnostic
|
| TypeMismatchDiagnostic
|
||||||
| UnexpectedTokenDiagnostic
|
| UnexpectedTokenDiagnostic
|
||||||
|
@ -304,15 +325,17 @@ export class ConsoleDiagnostics implements Diagnostics {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DiagnosticKind.TypeclassNotFound:
|
case DiagnosticKind.TypeclassNotFound:
|
||||||
this.writer.write(`the type class ${ANSI_FG_MAGENTA + diagnostic.name.text + ANSI_RESET} was not found.\n\n`);
|
this.writer.write(`the type class ${ANSI_FG_MAGENTA + diagnostic.name + ANSI_RESET} was not found.\n\n`);
|
||||||
this.writer.write(printNode(diagnostic.name) + '\n');
|
if (diagnostic.node !== null) {
|
||||||
if (diagnostic.origin !== null) {
|
this.writer.write(printNode(diagnostic.node) + '\n');
|
||||||
this.writer.indent();
|
|
||||||
this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
|
|
||||||
this.writer.write(`${ANSI_FG_MAGENTA + diagnostic.name.text + ANSI_RESET} is required by ${ANSI_FG_MAGENTA + diagnostic.origin.name.text + ANSI_RESET}\n\n`);
|
|
||||||
this.writer.write(printNode(diagnostic.origin.name) + '\n');
|
|
||||||
this.writer.dedent();
|
|
||||||
}
|
}
|
||||||
|
// if (diagnostic.origin !== null) {
|
||||||
|
// this.writer.indent();
|
||||||
|
// this.writer.write(ANSI_FG_YELLOW + ANSI_BOLD + 'info: ' + ANSI_RESET);
|
||||||
|
// this.writer.write(`${ANSI_FG_MAGENTA + diagnostic.name + ANSI_RESET} is required by ${ANSI_FG_MAGENTA + diagnostic.origin.name.text + ANSI_RESET}\n\n`);
|
||||||
|
// this.writer.write(printNode(diagnostic.origin.name) + '\n');
|
||||||
|
// this.writer.dedent();
|
||||||
|
// }
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DiagnosticKind.BindingNotFound:
|
case DiagnosticKind.BindingNotFound:
|
||||||
|
|
|
@ -44,7 +44,6 @@ import {
|
||||||
IfStatement,
|
IfStatement,
|
||||||
MemberExpression,
|
MemberExpression,
|
||||||
IdentifierAlt,
|
IdentifierAlt,
|
||||||
WrappedOperator,
|
|
||||||
ArrowTypeExpression,
|
ArrowTypeExpression,
|
||||||
EnumDeclarationStructElement,
|
EnumDeclarationStructElement,
|
||||||
EnumDeclaration,
|
EnumDeclaration,
|
||||||
|
@ -67,6 +66,8 @@ import {
|
||||||
InstanceDeclaration,
|
InstanceDeclaration,
|
||||||
ClassConstraintClause,
|
ClassConstraintClause,
|
||||||
AssignStatement,
|
AssignStatement,
|
||||||
|
ForallTypeExpression,
|
||||||
|
TypeExpressionWithConstraints,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Stream } from "./util";
|
import { Stream } from "./util";
|
||||||
|
|
||||||
|
@ -253,7 +254,57 @@ export class Parser {
|
||||||
return new AppTypeExpression(operator, args);
|
return new AppTypeExpression(operator, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public parseTypeExpressionWithConstraints(): TypeExpression {
|
||||||
|
if (!this.lookaheadHasClassConstraints()) {
|
||||||
|
return this.parseArrowTypeExpression();
|
||||||
|
}
|
||||||
|
const constraints = [];
|
||||||
|
let rarrowAlt;
|
||||||
|
for (;;) {
|
||||||
|
const constraint = this.parseClassConstraint();
|
||||||
|
constraints.push(constraint);
|
||||||
|
const t1 = this.getToken();
|
||||||
|
if (t1.kind === SyntaxKind.RArrowAlt) {
|
||||||
|
rarrowAlt = t1;
|
||||||
|
break;
|
||||||
|
} else if (t1.kind !== SyntaxKind.Comma) {
|
||||||
|
this.raiseParseError(t1, [ SyntaxKind.RArrowAlt, SyntaxKind.Comma ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const type = this.parseArrowTypeExpression();
|
||||||
|
return new TypeExpressionWithConstraints(constraints, rarrowAlt, type);
|
||||||
|
}
|
||||||
|
|
||||||
public parseTypeExpression(): TypeExpression {
|
public parseTypeExpression(): TypeExpression {
|
||||||
|
const t0 = this.peekToken();
|
||||||
|
switch (t0.kind) {
|
||||||
|
case SyntaxKind.ForallKeyword:
|
||||||
|
{
|
||||||
|
this.getToken();
|
||||||
|
let dot;
|
||||||
|
const typeVarExps = [];
|
||||||
|
for (;;) {
|
||||||
|
const t1 = this.peekToken();
|
||||||
|
if (t1.kind === SyntaxKind.Dot) {
|
||||||
|
dot = t1;
|
||||||
|
this.getToken();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
typeVarExps.push(this.parseVarTypeExpression());
|
||||||
|
}
|
||||||
|
return new ForallTypeExpression(
|
||||||
|
t0,
|
||||||
|
typeVarExps,
|
||||||
|
dot,
|
||||||
|
this.parseTypeExpression(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return this.parseTypeExpressionWithConstraints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseArrowTypeExpression(): TypeExpression {
|
||||||
let returnType = this.parseAppTypeExpressionOrBelow();
|
let returnType = this.parseAppTypeExpressionOrBelow();
|
||||||
const paramTypes = [];
|
const paramTypes = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -822,6 +873,20 @@ export class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lookaheadHasClassConstraints(): boolean {
|
||||||
|
for (let i = 1;; i++) {
|
||||||
|
const token = this.peekToken(i);
|
||||||
|
switch (token.kind) {
|
||||||
|
case SyntaxKind.RArrowAlt:
|
||||||
|
return true;
|
||||||
|
case SyntaxKind.BlockStart:
|
||||||
|
case SyntaxKind.LineFoldEnd:
|
||||||
|
case SyntaxKind.Equals:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public parseLetDeclaration(): LetDeclaration {
|
public parseLetDeclaration(): LetDeclaration {
|
||||||
let t0 = this.getToken();
|
let t0 = this.getToken();
|
||||||
let pubKeyword = null;
|
let pubKeyword = null;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import { CBuiltinType, CBuiltinTypeKind, CCallExpr, CConstExpr, CDecl, CDir, CExpr, CExprStmt, CFuncDecl, CIncDir, CNode, CProgram, CRefExpr, CStmt } from "../c";
|
import { CBuiltinType, CBuiltinTypeKind, CCallExpr, CConstExpr, CDecl, CDir, CExpr, CExprStmt, CFuncDecl, CIncDir, CNode, CProgram, CRefExpr, CStmt } from "../c";
|
||||||
import { Expression, Syntax, SyntaxKind } from "../cst";
|
import { Expression, Syntax, SyntaxKind } from "../cst";
|
||||||
import { Pass } from "../types";
|
import type { Pass } from "../program";
|
||||||
import { assert } from "../util";
|
import { assert } from "../util";
|
||||||
|
|
||||||
interface Context {
|
interface Context {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Syntax } from "../cst";
|
import { Syntax } from "../cst";
|
||||||
import { JSNode, JSProgram } from "../js";
|
import { JSNode, JSProgram } from "../js";
|
||||||
import { Pass } from "../types";
|
import type { Pass } from "../program";
|
||||||
|
|
||||||
export class BoltToJS implements Pass<Syntax, JSNode> {
|
export class BoltToJS implements Pass<Syntax, JSNode> {
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
canHaveInstanceDeclaration,
|
canHaveInstanceDeclaration,
|
||||||
vistEachChild
|
vistEachChild
|
||||||
} from "../cst";
|
} from "../cst";
|
||||||
import { Pass } from "../types";
|
import { Pass } from "../program";
|
||||||
import { assert } from "../util";
|
import { assert } from "../util";
|
||||||
|
|
||||||
function encode(typeExpr: TypeExpression): string {
|
function encode(typeExpr: TypeExpression): string {
|
||||||
|
@ -58,7 +58,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
||||||
new LetKeyword(),
|
new LetKeyword(),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
new NamedPattern(new Identifier(this.mangleInstance(node))),
|
new NamedPattern(new Identifier(null, this.mangleInstance(node))),
|
||||||
[],
|
[],
|
||||||
null, // TODO
|
null, // TODO
|
||||||
new ExprBody(
|
new ExprBody(
|
||||||
|
@ -69,7 +69,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
||||||
assert(element.kind === SyntaxKind.LetDeclaration);
|
assert(element.kind === SyntaxKind.LetDeclaration);
|
||||||
assert(element.pattern.kind === SyntaxKind.NamedPattern);
|
assert(element.pattern.kind === SyntaxKind.NamedPattern);
|
||||||
return new StructExpressionField(
|
return new StructExpressionField(
|
||||||
new Identifier(element.pattern.name.text),
|
new Identifier(null, element.pattern.name.text),
|
||||||
new Equals(),
|
new Equals(),
|
||||||
new FunctionExpression(new Backslash(), element.params, element.body!)
|
new FunctionExpression(new Backslash(), element.params, element.body!)
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,14 @@ import { SourceFile, TextFile } from "./cst";
|
||||||
import { ConsoleDiagnostics, Diagnostics } from "./diagnostics";
|
import { ConsoleDiagnostics, Diagnostics } from "./diagnostics";
|
||||||
import { Checker } from "./checker";
|
import { Checker } from "./checker";
|
||||||
import { Analyser } from "./analysis";
|
import { Analyser } from "./analysis";
|
||||||
import { Newable, Pass } from "./types";
|
|
||||||
|
export interface Pass<In, Out> {
|
||||||
|
apply(input: In): Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Newable<T> {
|
||||||
|
new (...args: any[]): T;
|
||||||
|
}
|
||||||
|
|
||||||
type AnyPass = Pass<any, any>;
|
type AnyPass = Pass<any, any>;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import {
|
||||||
ClassKeyword,
|
ClassKeyword,
|
||||||
InstanceKeyword,
|
InstanceKeyword,
|
||||||
Backslash,
|
Backslash,
|
||||||
|
ForallKeyword,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Diagnostics } from "./diagnostics"
|
import { Diagnostics } from "./diagnostics"
|
||||||
import { Stream, BufferedStream, assert } from "./util";
|
import { Stream, BufferedStream, assert } from "./util";
|
||||||
|
@ -383,6 +384,7 @@ export class Scanner extends BufferedStream<Token> {
|
||||||
case 'match': return new MatchKeyword(startPos);
|
case 'match': return new MatchKeyword(startPos);
|
||||||
case 'foreign': return new ForeignKeyword(startPos);
|
case 'foreign': return new ForeignKeyword(startPos);
|
||||||
case 'mod': return new ModKeyword(startPos);
|
case 'mod': return new ModKeyword(startPos);
|
||||||
|
case 'forall': return new ForallKeyword(startPos);
|
||||||
default:
|
default:
|
||||||
if (isUpper(text[0])) {
|
if (isUpper(text[0])) {
|
||||||
return new IdentifierAlt(startPos, text);
|
return new IdentifierAlt(startPos, text);
|
||||||
|
|
312
compiler/src/solver.ts
Normal file
312
compiler/src/solver.ts
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
import { Constraint, ConstraintKind } from "./constraints";
|
||||||
|
import { Diagnostics, FieldNotFoundDiagnostic, TypeclassNotFoundDiagnostic, TypeclassNotImplementedDiagnostic, TypeMismatchDiagnostic } from "./diagnostics";
|
||||||
|
import { TAbsent, TField, TVar, TVSub, Type, TypeBase, TypeKind } from "./types";
|
||||||
|
import { assert } from "./util";
|
||||||
|
|
||||||
|
export class ConstraintSolver {
|
||||||
|
|
||||||
|
private path: string[] = [];
|
||||||
|
private constraint: Constraint | null = null;
|
||||||
|
private maxTypeErrorCount = 5;
|
||||||
|
|
||||||
|
public solution = new TVSub;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public diagnostics: Diagnostics,
|
||||||
|
private nextTypeVarId: number,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private find(type: Type): Type {
|
||||||
|
while (type.kind === TypeKind.Var && this.solution.has(type)) {
|
||||||
|
type = this.solution.get(type)!;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unifyField(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
||||||
|
|
||||||
|
const swap = () => { [right, left] = [left, right]; }
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Absent && right.kind === TypeKind.Absent) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right.kind === TypeKind.Absent) {
|
||||||
|
swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Absent) {
|
||||||
|
assert(right.kind === TypeKind.Present);
|
||||||
|
const fieldName = this.path[this.path.length-1];
|
||||||
|
if (enableDiagnostics) {
|
||||||
|
this.diagnostics.add(
|
||||||
|
new FieldNotFoundDiagnostic(fieldName, left.node, right.type.node, this.constraint!.firstNode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(left.kind === TypeKind.Present && right.kind === TypeKind.Present);
|
||||||
|
return this.unify(left.type, right.type, enableDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private unify(left: Type, right: Type, enableDiagnostics: boolean): boolean {
|
||||||
|
|
||||||
|
left = this.find(left);
|
||||||
|
right = this.find(right);
|
||||||
|
|
||||||
|
// 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}`);
|
||||||
|
|
||||||
|
const swap = () => { [right, left] = [left, right]; }
|
||||||
|
|
||||||
|
if (left.kind !== TypeKind.Var && right.kind === TypeKind.Var) {
|
||||||
|
swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Var) {
|
||||||
|
|
||||||
|
// Perform an occurs check, verifying whether left occurs
|
||||||
|
// somewhere inside the structure of right. If so, unification
|
||||||
|
// makes no sense.
|
||||||
|
if (right.hasTypeVar(left)) {
|
||||||
|
// TODO print a diagnostic
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are ready to join the types, so the first thing we do is
|
||||||
|
// 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 (type.kind === TypeKind.Var) {
|
||||||
|
// for (const constraint of classes) {
|
||||||
|
// type.context.add(constraint);
|
||||||
|
// }
|
||||||
|
// } else if (type.kind === TypeKind.Con) {
|
||||||
|
// for (const constraint of classes) {
|
||||||
|
// propagateClassTCon(constraint, type);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// //assert(false);
|
||||||
|
// //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.argTypes[i++]);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//propagateClasses(left.context, right);
|
||||||
|
|
||||||
|
// We are all clear; set the actual type of left to right.
|
||||||
|
this.solution.set(left, right);
|
||||||
|
|
||||||
|
// These types will be join, and we'd like to track that
|
||||||
|
// into a special chain.
|
||||||
|
TypeBase.join(left, right);
|
||||||
|
|
||||||
|
// if (left.node !== null) {
|
||||||
|
// right.node = left.node;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Arrow && right.kind === TypeKind.Arrow) {
|
||||||
|
let success = true;
|
||||||
|
if (!this.unify(left.paramType, right.paramType, enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (!this.unify(left.returnType, right.returnType, enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
TypeBase.join(left, right);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Tuple && right.kind === TypeKind.Tuple) {
|
||||||
|
if (left.elementTypes.length === right.elementTypes.length) {
|
||||||
|
let success = false;
|
||||||
|
const count = left.elementTypes.length;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
if (!this.unify(left.elementTypes[i], right.elementTypes[i], enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
TypeBase.join(left, right);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Con && right.kind === TypeKind.Con) {
|
||||||
|
if (left.id === right.id) {
|
||||||
|
assert(left.argTypes.length === right.argTypes.length);
|
||||||
|
const count = left.argTypes.length;
|
||||||
|
let success = true;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
if (!this.unify(left.argTypes[i], right.argTypes[i], enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
TypeBase.join(left, right);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Nil && right.kind === TypeKind.Nil) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Field && right.kind === TypeKind.Field) {
|
||||||
|
if (left.name === right.name) {
|
||||||
|
let success = true;
|
||||||
|
this.path.push(left.name);
|
||||||
|
if (!this.unifyField(left.type, right.type, enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
this.path.pop();
|
||||||
|
if (!this.unify(left.restType, right.restType, enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
let success = true;
|
||||||
|
const newRestType = new TVar(this.nextTypeVarId++);
|
||||||
|
if (!this.unify(left.restType, new TField(right.name, right.type, newRestType), enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (!this.unify(right.restType, new TField(left.name, left.type, newRestType), enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Nil && right.kind === TypeKind.Field) {
|
||||||
|
swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Field && right.kind === TypeKind.Nil) {
|
||||||
|
let success = true;
|
||||||
|
this.path.push(left.name);
|
||||||
|
if (!this.unifyField(left.type, new TAbsent(right.node), enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
this.path.pop();
|
||||||
|
if (!this.unify(left.restType, right, enableDiagnostics)) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.Nominal && right.kind === TypeKind.Nominal) {
|
||||||
|
if (left.decl === right.decl) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// fall through to error reporting
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.kind === TypeKind.App && right.kind === TypeKind.App) {
|
||||||
|
return this.unify(left.left, right.left, enableDiagnostics)
|
||||||
|
&& this.unify(left.right, right.right, enableDiagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableDiagnostics) {
|
||||||
|
this.diagnostics.add(
|
||||||
|
new TypeMismatchDiagnostic(
|
||||||
|
left.substitute(this.solution),
|
||||||
|
right.substitute(this.solution),
|
||||||
|
[...this.constraint!.getNodes()],
|
||||||
|
this.path,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public solve(constraint: Constraint): void {
|
||||||
|
|
||||||
|
let queue = [ constraint ];
|
||||||
|
let next = [];
|
||||||
|
let isNext = false;
|
||||||
|
|
||||||
|
let errorCount = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
if (queue.length === 0) {
|
||||||
|
if (next.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
isNext = true;
|
||||||
|
queue = next;
|
||||||
|
next = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const constraint = queue.shift()!;
|
||||||
|
|
||||||
|
sw: switch (constraint.kind) {
|
||||||
|
|
||||||
|
case ConstraintKind.Many:
|
||||||
|
{
|
||||||
|
for (const element of constraint.elements) {
|
||||||
|
queue.push(element);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// case ConstraintKind.Class:
|
||||||
|
// {
|
||||||
|
// if (constraint.type.kind === TypeKind.Var) {
|
||||||
|
// if (isNext) {
|
||||||
|
// // TODO
|
||||||
|
// } else {
|
||||||
|
// next.push(constraint);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// const classDecl = this.lookupClass(constraint.className);
|
||||||
|
// if (classDecl === null) {
|
||||||
|
// this.diagnostics.add(new TypeclassNotFoundDiagnostic(constraint.className, constraint.node));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// for (const instance of classDecl.getInstances()) {
|
||||||
|
// if (this.unify(instance.inferredType, constraint.type, false)) {
|
||||||
|
// break sw;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// this.diagnostics.add(new TypeclassNotImplementedDiagnostic(constraint.className, constraint.type, constraint.node));
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
|
||||||
|
case ConstraintKind.Equal:
|
||||||
|
{
|
||||||
|
this.constraint = constraint;
|
||||||
|
if (!this.unify(constraint.left, constraint.right, true)) {
|
||||||
|
errorCount++;
|
||||||
|
if (errorCount === this.maxTypeErrorCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,226 +0,0 @@
|
||||||
|
|
||||||
## Record types can be unified without causing an error
|
|
||||||
|
|
||||||
```
|
|
||||||
struct Person.
|
|
||||||
email: String
|
|
||||||
age: Int
|
|
||||||
|
|
||||||
let bert
|
|
||||||
= Person {
|
|
||||||
email = "bar@boo.com",
|
|
||||||
age = 32
|
|
||||||
}
|
|
||||||
let bob
|
|
||||||
= Person {
|
|
||||||
email = "boo",
|
|
||||||
age = 43
|
|
||||||
}
|
|
||||||
|
|
||||||
bert == bob
|
|
||||||
```
|
|
||||||
|
|
||||||
## Return types are polymorphic
|
|
||||||
|
|
||||||
```
|
|
||||||
let id x = x
|
|
||||||
|
|
||||||
id 1
|
|
||||||
id "foo"
|
|
||||||
id True
|
|
||||||
```
|
|
||||||
|
|
||||||
## Nested definitions work
|
|
||||||
|
|
||||||
```
|
|
||||||
let foo x.
|
|
||||||
let bar y z = y + z - x
|
|
||||||
bar
|
|
||||||
|
|
||||||
foo True
|
|
||||||
```
|
|
||||||
|
|
||||||
## Everything that can be type-checked will be type-checked
|
|
||||||
|
|
||||||
```
|
|
||||||
let foo n.
|
|
||||||
let f : String = 1
|
|
||||||
return n
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recursive definitions do not cause infinite loops in the type-checker
|
|
||||||
|
|
||||||
```
|
|
||||||
let fac n = fac_2 n
|
|
||||||
|
|
||||||
let fac_2 n = fac_3 n + fac n
|
|
||||||
|
|
||||||
let fac_3 n = fac_2 (n-1)
|
|
||||||
|
|
||||||
not (fac 1)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example with mutual recursion works
|
|
||||||
|
|
||||||
```
|
|
||||||
let is_even x.
|
|
||||||
if x == 0.
|
|
||||||
return True
|
|
||||||
else.
|
|
||||||
return is_odd (x-1)
|
|
||||||
|
|
||||||
let is_odd x.
|
|
||||||
if x == 1.
|
|
||||||
return False
|
|
||||||
else.
|
|
||||||
return is_even (x-1)
|
|
||||||
|
|
||||||
not (is_even True)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Polymorphic records can be partially typed
|
|
||||||
|
|
||||||
```
|
|
||||||
struct Timestamped a b.
|
|
||||||
first: a
|
|
||||||
second: b
|
|
||||||
timestamp: Int
|
|
||||||
|
|
||||||
type Foo = Timestamped Int
|
|
||||||
|
|
||||||
type Bar = Foo Int
|
|
||||||
|
|
||||||
let t : Bar = Timestamped { first = "bar", second = 1, timestamp = 12345 }
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extensible records work
|
|
||||||
|
|
||||||
```
|
|
||||||
struct Timestamped a.
|
|
||||||
data: a
|
|
||||||
timestamp: Int
|
|
||||||
|
|
||||||
let t = Timestamped { data = "foo", timestamp = 12345 }
|
|
||||||
|
|
||||||
t.data == 1
|
|
||||||
t.data == "foo"
|
|
||||||
|
|
||||||
let u = Timestamped { data = True, timestamp = 12345 }
|
|
||||||
|
|
||||||
u.data == "foo"
|
|
||||||
u.data == False
|
|
||||||
```
|
|
||||||
|
|
||||||
## A recursive function is automatically instantiated
|
|
||||||
|
|
||||||
```
|
|
||||||
let fac n.
|
|
||||||
if n == 0.
|
|
||||||
return 1
|
|
||||||
else.
|
|
||||||
return n * fac (n-"foo")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Enum-declarations are correctly typed
|
|
||||||
|
|
||||||
```
|
|
||||||
enum Maybe a.
|
|
||||||
Just a
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
let right_1 : Maybe Int = Just 1
|
|
||||||
let right_2 : Maybe String = Just "foo"
|
|
||||||
let wrong : Maybe Int = Just "foo"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Kind inference works
|
|
||||||
|
|
||||||
```
|
|
||||||
enum Maybe a.
|
|
||||||
Just a
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
let foo_1 : Maybe
|
|
||||||
let foo_2 : Maybe Int
|
|
||||||
let foo_3 : Maybe Int Int
|
|
||||||
let foo_4 : Maybe Int Int Int
|
|
||||||
```
|
|
||||||
|
|
||||||
## Can indirectly apply a polymorphic datatype to some type
|
|
||||||
|
|
||||||
```
|
|
||||||
enum Maybe a.
|
|
||||||
Just a
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
enum App a b.
|
|
||||||
MkApp (a b)
|
|
||||||
|
|
||||||
enum Foo.
|
|
||||||
MkFoo (App Maybe Int)
|
|
||||||
|
|
||||||
let f : Foo = MkFoo (MkApp (Just 1))
|
|
||||||
```
|
|
||||||
|
|
||||||
## Record-declarations inside enum-declarations work
|
|
||||||
|
|
||||||
```
|
|
||||||
enum Shape.
|
|
||||||
Circle.
|
|
||||||
radius: Int
|
|
||||||
Rect.
|
|
||||||
width: Int
|
|
||||||
height: Int
|
|
||||||
|
|
||||||
let z = Circle { radius = 12 }
|
|
||||||
let a = Rect { width = 12, height = 12 }
|
|
||||||
|
|
||||||
a == z
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tuple types are correctly inferred and unified
|
|
||||||
|
|
||||||
```
|
|
||||||
let foo_1 : (Int, Int, Int) = (1, 2, 3)
|
|
||||||
let foo_2 : (Int, Int, Int) = (1, 2, "foo")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Module references work
|
|
||||||
|
|
||||||
```
|
|
||||||
mod CD.
|
|
||||||
mod A.
|
|
||||||
struct Foo
|
|
||||||
mod B.
|
|
||||||
let alpha: A.Foo
|
|
||||||
```
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
## Rest-expressions on extensible records work
|
|
||||||
|
|
||||||
```
|
|
||||||
struct Point.
|
|
||||||
x: Int
|
|
||||||
y: Int
|
|
||||||
z: Int
|
|
||||||
|
|
||||||
let foo { x, y, .. } : Point -> Int = x + y
|
|
||||||
|
|
||||||
foo { x = 1, y = 2 }
|
|
||||||
```
|
|
||||||
=======
|
|
||||||
|
|
||||||
## A polymorphic function is properly generalized when assigned to a new variable
|
|
||||||
|
|
||||||
```
|
|
||||||
let id x = x
|
|
||||||
let id2 = id
|
|
||||||
let id3 = id
|
|
||||||
|
|
||||||
id3 1
|
|
||||||
id3 "bla"
|
|
||||||
|
|
||||||
id2 1
|
|
||||||
id2 "bla"
|
|
||||||
````
|
|
||||||
>>>>>>> 11bae0fed0d58eafebd863b4e3bf117176176cb1
|
|
|
@ -1,8 +1,566 @@
|
||||||
|
import { InspectOptions } from "util";
|
||||||
|
import { ClassDeclaration, EnumDeclaration, StructDeclaration, Syntax } from "./cst";
|
||||||
|
import { deserializable, ignore, InspectFn, toStringTag } from "./util";
|
||||||
|
|
||||||
export interface Pass<In, Out> {
|
export enum TypeKind {
|
||||||
apply(input: In): Out;
|
Arrow,
|
||||||
|
Var,
|
||||||
|
Con,
|
||||||
|
Tuple,
|
||||||
|
App,
|
||||||
|
Nominal,
|
||||||
|
Field,
|
||||||
|
Nil,
|
||||||
|
Absent,
|
||||||
|
Present,
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class TypeBase {
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
public abstract readonly kind: TypeKind;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
public next: Type = this as any;
|
||||||
|
|
||||||
|
public abstract node: Syntax | null;
|
||||||
|
|
||||||
|
public static join(a: Type, b: Type): void {
|
||||||
|
const keep = a.next;
|
||||||
|
a.next = b;
|
||||||
|
b.next = keep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract getTypeVars(): Iterable<TVar>;
|
||||||
|
|
||||||
|
public abstract shallowClone(): Type;
|
||||||
|
|
||||||
|
public abstract substitute(sub: TVSub): Type;
|
||||||
|
|
||||||
|
public hasTypeVar(tv: TVar): boolean {
|
||||||
|
for (const other of this.getTypeVars()) {
|
||||||
|
if (tv.id === other.id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract [toStringTag](depth: number, options: InspectOptions, inspect: InspectFn): string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isType(value: any): value is Type {
|
||||||
|
return value !== undefined
|
||||||
|
&& value !== null
|
||||||
|
&& value instanceof TypeBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TVar extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Var;
|
||||||
|
|
||||||
|
@ignore
|
||||||
|
public context = new Set<ClassDeclaration>();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public id: number,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
yield this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): TVar {
|
||||||
|
return new TVar(this.id, this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
const other = sub.get(this);
|
||||||
|
return other === undefined
|
||||||
|
? this : other.substitute(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag]() {
|
||||||
|
return 'a' + this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TNil extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Nil;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public node: Syntax | null = null
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(_sub: TVSub): Type {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): Type {
|
||||||
|
return new TNil(this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag]() {
|
||||||
|
return '∂Abs';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TAbsent extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Absent;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(_sub: TVSub): Type {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): Type {
|
||||||
|
return new TAbsent(this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag]() {
|
||||||
|
return 'Abs';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TPresent extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Present;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public type: Type,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
return new TPresent(this.type.substitute(sub), this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTypeVars(): Iterable<TVar> {
|
||||||
|
return this.type.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): Type {
|
||||||
|
return new TPresent(this.type, this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return 'Pre ' + inspect(this.type, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TArrow extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Arrow;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public paramType: Type,
|
||||||
|
public returnType: Type,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static build(paramTypes: Type[], returnType: Type, node: Syntax | null = null): Type {
|
||||||
|
let result = returnType;
|
||||||
|
for (let i = paramTypes.length-1; i >= 0; i--) {
|
||||||
|
result = new TArrow(paramTypes[i], result, node);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
yield* this.paramType.getTypeVars();
|
||||||
|
yield* this.returnType.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): TArrow {
|
||||||
|
return new TArrow(
|
||||||
|
this.paramType,
|
||||||
|
this.returnType,
|
||||||
|
this.node,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
let changed = false;
|
||||||
|
const newParamType = this.paramType.substitute(sub);
|
||||||
|
if (newParamType !== this.paramType) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
const newReturnType = this.returnType.substitute(sub);
|
||||||
|
if (newReturnType !== this.returnType) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return changed ? new TArrow(newParamType, newReturnType, this.node) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return inspect(this.paramType, options) + ' -> ' + inspect(this.returnType, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TCon extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Con;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public id: number,
|
||||||
|
public argTypes: Type[],
|
||||||
|
public displayName: string,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
for (const argType of this.argTypes) {
|
||||||
|
yield* argType.getTypeVars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): TCon {
|
||||||
|
return new TCon(
|
||||||
|
this.id,
|
||||||
|
this.argTypes,
|
||||||
|
this.displayName,
|
||||||
|
this.node,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
let changed = false;
|
||||||
|
const newArgTypes = [];
|
||||||
|
for (const argType of this.argTypes) {
|
||||||
|
const newArgType = argType.substitute(sub);
|
||||||
|
if (newArgType !== argType) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
newArgTypes.push(newArgType);
|
||||||
|
}
|
||||||
|
return changed ? new TCon(this.id, newArgTypes, this.displayName, this.node) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return this.displayName + ' ' + this.argTypes.map(t => inspect(t, options)).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TTuple extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Tuple;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public elementTypes: Type[],
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
for (const elementType of this.elementTypes) {
|
||||||
|
yield* elementType.getTypeVars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): TTuple {
|
||||||
|
return new TTuple(
|
||||||
|
this.elementTypes,
|
||||||
|
this.node,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
let changed = false;
|
||||||
|
const newElementTypes = [];
|
||||||
|
for (const elementType of this.elementTypes) {
|
||||||
|
const newElementType = elementType.substitute(sub);
|
||||||
|
if (newElementType !== elementType) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
newElementTypes.push(newElementType);
|
||||||
|
}
|
||||||
|
return changed ? new TTuple(newElementTypes, this.node) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return this.elementTypes.map(t => inspect(t, options)).join(' × ');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TField extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Field;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public name: string,
|
||||||
|
public type: Type,
|
||||||
|
public restType: Type,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTypeVars(): Iterable<TVar> {
|
||||||
|
return this.type.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): TField {
|
||||||
|
return new TField(
|
||||||
|
this.name,
|
||||||
|
this.type,
|
||||||
|
this.restType,
|
||||||
|
this.node,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static sort(type: Type): Type {
|
||||||
|
const fields = new Map<string, TField>();
|
||||||
|
while (type.kind === TypeKind.Field) {
|
||||||
|
fields.set(type.name, type);
|
||||||
|
type = type.restType;
|
||||||
|
}
|
||||||
|
const keys = [...fields.keys()].sort().reverse();
|
||||||
|
let out: Type = type;
|
||||||
|
for (const key of keys) {
|
||||||
|
const field = fields.get(key)!;
|
||||||
|
out = new TField(key, field.type, out, field.node);
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
const newType = this.type.substitute(sub);
|
||||||
|
const newRestType = this.restType.substitute(sub);
|
||||||
|
return newType !== this.type || newRestType !== this.restType
|
||||||
|
? new TField(this.name, newType, newRestType, this.node) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
let out = '{ ' + this.name + ': ' + inspect(this.type, options);
|
||||||
|
let type = this.restType;
|
||||||
|
while (type.kind === TypeKind.Field) {
|
||||||
|
out += '; ' + type.name + ': ' + inspect(type.type, options);
|
||||||
|
type = type.restType;
|
||||||
|
}
|
||||||
|
if (type.kind !== TypeKind.Nil) {
|
||||||
|
out += '; ' + inspect(type, options);
|
||||||
|
}
|
||||||
|
return out + ' }'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TApp extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.App;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public left: Type,
|
||||||
|
public right: Type,
|
||||||
|
public node: Syntax | null = null
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static build(resultType: Type, types: Type[], node: Syntax | null = null): Type {
|
||||||
|
for (let i = 0; i < types.length; i++) {
|
||||||
|
resultType = new TApp(types[i], resultType, node);
|
||||||
|
}
|
||||||
|
return resultType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
yield* this.left.getTypeVars();
|
||||||
|
yield* this.right.getTypeVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone() {
|
||||||
|
return new TApp(
|
||||||
|
this.left,
|
||||||
|
this.right,
|
||||||
|
this.node
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(sub: TVSub): Type {
|
||||||
|
let changed = false;
|
||||||
|
const newOperatorType = this.left.substitute(sub);
|
||||||
|
if (newOperatorType !== this.left) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
const newArgType = this.right.substitute(sub);
|
||||||
|
if (newArgType !== this.right) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return changed ? new TApp(newOperatorType, newArgType, this.node) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
return inspect(this.left, options) + ' ' + inspect(this.right, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@deserializable()
|
||||||
|
export class TNominal extends TypeBase {
|
||||||
|
|
||||||
|
public readonly kind = TypeKind.Nominal;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public decl: StructDeclaration | EnumDeclaration,
|
||||||
|
public node: Syntax | null = null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public *getTypeVars(): Iterable<TVar> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public shallowClone(): Type {
|
||||||
|
return new TNominal(
|
||||||
|
this.decl,
|
||||||
|
this.node,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public substitute(_sub: TVSub): Type {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag]() {
|
||||||
|
return this.decl.name.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Type
|
||||||
|
= TCon
|
||||||
|
| TArrow
|
||||||
|
| TVar
|
||||||
|
| TTuple
|
||||||
|
| TApp
|
||||||
|
| TNominal
|
||||||
|
| TField
|
||||||
|
| TNil
|
||||||
|
| TPresent
|
||||||
|
| TAbsent
|
||||||
|
|
||||||
|
|
||||||
|
export class TVSet {
|
||||||
|
|
||||||
|
private mapping = new Map<number, TVar>();
|
||||||
|
|
||||||
|
public constructor(iterable?: Iterable<TVar>) {
|
||||||
|
if (iterable !== undefined) {
|
||||||
|
for (const tv of iterable) {
|
||||||
|
this.add(tv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(tv: TVar): void {
|
||||||
|
this.mapping.set(tv.id, tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(tv: TVar): boolean {
|
||||||
|
return this.mapping.has(tv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public intersectsType(type: Type): boolean {
|
||||||
|
for (const tv of type.getTypeVars()) {
|
||||||
|
if (this.has(tv)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(tv: TVar): void {
|
||||||
|
this.mapping.delete(tv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get size(): number {
|
||||||
|
return this.mapping.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [Symbol.iterator](): Iterator<TVar> {
|
||||||
|
return this.mapping.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public [toStringTag](_depth: number, options: InspectOptions, inspect: InspectFn) {
|
||||||
|
let out = '{ ';
|
||||||
|
let first = true;
|
||||||
|
for (const tv of this) {
|
||||||
|
if (first) first = false;
|
||||||
|
else out += ', ';
|
||||||
|
out += inspect(tv, options);
|
||||||
|
}
|
||||||
|
return out + ' }';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TVSub {
|
||||||
|
|
||||||
|
private mapping = new Map<number, Type>();
|
||||||
|
|
||||||
|
public set(tv: TVar, type: Type): void {
|
||||||
|
this.mapping.set(tv.id, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(tv: TVar): Type | undefined {
|
||||||
|
return this.mapping.get(tv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(tv: TVar): boolean {
|
||||||
|
return this.mapping.has(tv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(tv: TVar): void {
|
||||||
|
this.mapping.delete(tv.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public values(): Iterable<Type> {
|
||||||
|
return this.mapping.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Newable<T> {
|
|
||||||
new (...args: any[]): T;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
import "reflect-metadata"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import stream from "stream"
|
import stream from "stream"
|
||||||
import { InspectOptions } from "util";
|
import { InspectOptions } from "util";
|
||||||
|
|
10
package.json
Normal file
10
package.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "bolt-workspace",
|
||||||
|
"private": true,
|
||||||
|
"workspaces": [
|
||||||
|
"compiler"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"typescript": "^5.0.4"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue