Re-write the type-checker to make it more extensible

This commit is contained in:
Sam Vervaeck 2020-05-23 21:15:20 +02:00
parent 742dd6ba5d
commit 32a9d288de
14 changed files with 758 additions and 429 deletions

168
src/ast.d.ts vendored
View file

@ -61,79 +61,79 @@ export const enum SyntaxKind {
BoltResumeStatement = 69, BoltResumeStatement = 69,
BoltExpressionStatement = 70, BoltExpressionStatement = 70,
BoltParameter = 71, BoltParameter = 71,
BoltModule = 74, BoltModule = 75,
BoltFunctionDeclaration = 76, BoltFunctionDeclaration = 77,
BoltVariableDeclaration = 77, BoltVariableDeclaration = 78,
BoltPlainImportSymbol = 79, BoltPlainImportSymbol = 80,
BoltImportDeclaration = 80, BoltImportDeclaration = 81,
BoltTraitDeclaration = 81, BoltTraitDeclaration = 82,
BoltImplDeclaration = 82, BoltImplDeclaration = 83,
BoltTypeAliasDeclaration = 83, BoltTypeAliasDeclaration = 84,
BoltRecordField = 85, BoltRecordField = 86,
BoltRecordDeclaration = 86, BoltRecordDeclaration = 87,
BoltMacroCall = 88, BoltMacroCall = 89,
JSOperator = 91, JSOperator = 92,
JSIdentifier = 92, JSIdentifier = 93,
JSString = 93, JSString = 94,
JSInteger = 94, JSInteger = 95,
JSFromKeyword = 95, JSFromKeyword = 96,
JSReturnKeyword = 96, JSReturnKeyword = 97,
JSTryKeyword = 97, JSTryKeyword = 98,
JSFinallyKeyword = 98, JSFinallyKeyword = 99,
JSCatchKeyword = 99, JSCatchKeyword = 100,
JSImportKeyword = 100, JSImportKeyword = 101,
JSAsKeyword = 101, JSAsKeyword = 102,
JSConstKeyword = 102, JSConstKeyword = 103,
JSLetKeyword = 103, JSLetKeyword = 104,
JSExportKeyword = 104, JSExportKeyword = 105,
JSFunctionKeyword = 105, JSFunctionKeyword = 106,
JSWhileKeyword = 106, JSWhileKeyword = 107,
JSForKeyword = 107, JSForKeyword = 108,
JSCloseBrace = 108, JSCloseBrace = 109,
JSCloseBracket = 109, JSCloseBracket = 110,
JSCloseParen = 110, JSCloseParen = 111,
JSOpenBrace = 111, JSOpenBrace = 112,
JSOpenBracket = 112, JSOpenBracket = 113,
JSOpenParen = 113, JSOpenParen = 114,
JSSemi = 114, JSSemi = 115,
JSComma = 115, JSComma = 116,
JSDot = 116, JSDot = 117,
JSDotDotDot = 117, JSDotDotDot = 118,
JSMulOp = 118, JSMulOp = 119,
JSAddOp = 119, JSAddOp = 120,
JSDivOp = 120, JSDivOp = 121,
JSSubOp = 121, JSSubOp = 122,
JSLtOp = 122, JSLtOp = 123,
JSGtOp = 123, JSGtOp = 124,
JSBOrOp = 124, JSBOrOp = 125,
JSBXorOp = 125, JSBXorOp = 126,
JSBAndOp = 126, JSBAndOp = 127,
JSBNotOp = 127, JSBNotOp = 128,
JSNotOp = 128, JSNotOp = 129,
JSBindPattern = 130, JSBindPattern = 131,
JSConstantExpression = 132, JSConstantExpression = 133,
JSMemberExpression = 133, JSMemberExpression = 134,
JSCallExpression = 134, JSCallExpression = 135,
JSBinaryExpression = 135, JSBinaryExpression = 136,
JSUnaryExpression = 136, JSUnaryExpression = 137,
JSNewExpression = 137, JSNewExpression = 138,
JSSequenceExpression = 138, JSSequenceExpression = 139,
JSConditionalExpression = 139, JSConditionalExpression = 140,
JSLiteralExpression = 141, JSLiteralExpression = 142,
JSReferenceExpression = 142, JSReferenceExpression = 143,
JSCatchBlock = 145, JSCatchBlock = 146,
JSTryCatchStatement = 146, JSTryCatchStatement = 147,
JSExpressionStatement = 147, JSExpressionStatement = 148,
JSConditionalStatement = 148, JSConditionalStatement = 149,
JSReturnStatement = 149, JSReturnStatement = 150,
JSParameter = 150, JSParameter = 151,
JSImportStarBinding = 154, JSImportStarBinding = 155,
JSImportAsBinding = 155, JSImportAsBinding = 156,
JSImportDeclaration = 156, JSImportDeclaration = 157,
JSFunctionDeclaration = 157, JSFunctionDeclaration = 158,
JSArrowFunctionDeclaration = 158, JSArrowFunctionDeclaration = 159,
JSLetDeclaration = 159, JSLetDeclaration = 160,
JSSourceFile = 160, JSSourceFile = 161,
} }
@ -387,7 +387,7 @@ export interface BoltSourceFile extends SyntaxBase<SyntaxKind.BoltSourceFile> {
export interface BoltQualName extends SyntaxBase<SyntaxKind.BoltQualName> { export interface BoltQualName extends SyntaxBase<SyntaxKind.BoltQualName> {
kind: SyntaxKind.BoltQualName; kind: SyntaxKind.BoltQualName;
modulePath: BoltIdentifier[]; modulePath: BoltIdentifier[] | null;
name: BoltSymbol; name: BoltSymbol;
} }
@ -507,7 +507,7 @@ export interface BoltCaseExpression extends SyntaxBase<SyntaxKind.BoltCaseExpres
export interface BoltBlockExpression extends SyntaxBase<SyntaxKind.BoltBlockExpression> { export interface BoltBlockExpression extends SyntaxBase<SyntaxKind.BoltBlockExpression> {
kind: SyntaxKind.BoltBlockExpression; kind: SyntaxKind.BoltBlockExpression;
statements: BoltStatement[]; elements: BoltFunctionBodyElement[];
} }
export interface BoltConstantExpression extends SyntaxBase<SyntaxKind.BoltConstantExpression> { export interface BoltConstantExpression extends SyntaxBase<SyntaxKind.BoltConstantExpression> {
@ -557,6 +557,12 @@ export type BoltDeclaration
| BoltMacroCall | BoltMacroCall
export type BoltTypeDeclaration
= BoltTraitDeclaration
| BoltTypeAliasDeclaration
| BoltRecordDeclaration
export const enum BoltDeclarationModifiers { export const enum BoltDeclarationModifiers {
Mutable = 1,Public = 2,IsType = 4,IsForeign = 8,} Mutable = 1,Public = 2,IsType = 4,IsForeign = 8,}
@ -648,7 +654,7 @@ export interface BoltRecordField extends SyntaxBase<SyntaxKind.BoltRecordField>
export interface BoltRecordDeclaration extends SyntaxBase<SyntaxKind.BoltRecordDeclaration> { export interface BoltRecordDeclaration extends SyntaxBase<SyntaxKind.BoltRecordDeclaration> {
kind: SyntaxKind.BoltRecordDeclaration; kind: SyntaxKind.BoltRecordDeclaration;
modifiers: BoltDeclarationModifiers; modifiers: BoltDeclarationModifiers;
name: BoltQualName; name: BoltIdentifier;
typeParms: BoltTypeParameter[] | null; typeParms: BoltTypeParameter[] | null;
members: BoltRecordMember[] | null; members: BoltRecordMember[] | null;
} }
@ -667,6 +673,9 @@ export type BoltSourceElement
| BoltTypeAliasDeclaration | BoltTypeAliasDeclaration
| BoltRecordDeclaration | BoltRecordDeclaration
| BoltMacroCall | BoltMacroCall
| BoltTraitDeclaration
| BoltTypeAliasDeclaration
| BoltRecordDeclaration
export interface BoltMacroCall extends SyntaxBase<SyntaxKind.BoltMacroCall> { export interface BoltMacroCall extends SyntaxBase<SyntaxKind.BoltMacroCall> {
@ -1379,7 +1388,7 @@ export function createBoltParenthesized(text: string, span?: TextSpan | null): B
export function createBoltBraced(text: string, span?: TextSpan | null): BoltBraced; export function createBoltBraced(text: string, span?: TextSpan | null): BoltBraced;
export function createBoltBracketed(text: string, span?: TextSpan | null): BoltBracketed; export function createBoltBracketed(text: string, span?: TextSpan | null): BoltBracketed;
export function createBoltSourceFile(elements: BoltSourceElement[], span?: TextSpan | null): BoltSourceFile; export function createBoltSourceFile(elements: BoltSourceElement[], span?: TextSpan | null): BoltSourceFile;
export function createBoltQualName(modulePath: BoltIdentifier[], name: BoltSymbol, span?: TextSpan | null): BoltQualName; export function createBoltQualName(modulePath: BoltIdentifier[] | null, name: BoltSymbol, span?: TextSpan | null): BoltQualName;
export function createBoltReferenceTypeExpression(name: BoltQualName, arguments: BoltTypeExpression[] | null, span?: TextSpan | null): BoltReferenceTypeExpression; export function createBoltReferenceTypeExpression(name: BoltQualName, arguments: BoltTypeExpression[] | null, span?: TextSpan | null): BoltReferenceTypeExpression;
export function createBoltTypeParameter(index: number, name: BoltIdentifier, defaultType: BoltTypeExpression | null, span?: TextSpan | null): BoltTypeParameter; export function createBoltTypeParameter(index: number, name: BoltIdentifier, defaultType: BoltTypeExpression | null, span?: TextSpan | null): BoltTypeParameter;
export function createBoltBindPattern(name: BoltIdentifier, span?: TextSpan | null): BoltBindPattern; export function createBoltBindPattern(name: BoltIdentifier, span?: TextSpan | null): BoltBindPattern;
@ -1396,7 +1405,7 @@ export function createBoltMatchArm(pattern: BoltPattern, body: BoltExpression, s
export function createBoltMatchExpression(value: BoltExpression, arms: BoltMatchArm[], span?: TextSpan | null): BoltMatchExpression; export function createBoltMatchExpression(value: BoltExpression, arms: BoltMatchArm[], span?: TextSpan | null): BoltMatchExpression;
export function createBoltCase(test: BoltExpression, result: BoltExpression, span?: TextSpan | null): BoltCase; export function createBoltCase(test: BoltExpression, result: BoltExpression, span?: TextSpan | null): BoltCase;
export function createBoltCaseExpression(cases: BoltCase[], span?: TextSpan | null): BoltCaseExpression; export function createBoltCaseExpression(cases: BoltCase[], span?: TextSpan | null): BoltCaseExpression;
export function createBoltBlockExpression(statements: BoltStatement[], span?: TextSpan | null): BoltBlockExpression; export function createBoltBlockExpression(elements: BoltFunctionBodyElement[], span?: TextSpan | null): BoltBlockExpression;
export function createBoltConstantExpression(value: BoltValue, span?: TextSpan | null): BoltConstantExpression; export function createBoltConstantExpression(value: BoltValue, span?: TextSpan | null): BoltConstantExpression;
export function createBoltReturnStatement(value: BoltExpression | null, span?: TextSpan | null): BoltReturnStatement; export function createBoltReturnStatement(value: BoltExpression | null, span?: TextSpan | null): BoltReturnStatement;
export function createBoltResumeStatement(value: BoltExpression, span?: TextSpan | null): BoltResumeStatement; export function createBoltResumeStatement(value: BoltExpression, span?: TextSpan | null): BoltResumeStatement;
@ -1411,7 +1420,7 @@ export function createBoltTraitDeclaration(modifiers: BoltDeclarationModifiers,
export function createBoltImplDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, trait: BoltTypeExpression, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltImplDeclaration; export function createBoltImplDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, trait: BoltTypeExpression, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltImplDeclaration;
export function createBoltTypeAliasDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, typeExpr: BoltTypeExpression, span?: TextSpan | null): BoltTypeAliasDeclaration; export function createBoltTypeAliasDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, typeExpr: BoltTypeExpression, span?: TextSpan | null): BoltTypeAliasDeclaration;
export function createBoltRecordField(name: BoltIdentifier, type: BoltTypeExpression, span?: TextSpan | null): BoltRecordField; export function createBoltRecordField(name: BoltIdentifier, type: BoltTypeExpression, span?: TextSpan | null): BoltRecordField;
export function createBoltRecordDeclaration(modifiers: BoltDeclarationModifiers, name: BoltQualName, typeParms: BoltTypeParameter[] | null, members: BoltRecordMember[] | null, span?: TextSpan | null): BoltRecordDeclaration; export function createBoltRecordDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParms: BoltTypeParameter[] | null, members: BoltRecordMember[] | null, span?: TextSpan | null): BoltRecordDeclaration;
export function createBoltMacroCall(name: BoltIdentifier, text: string, span?: TextSpan | null): BoltMacroCall; export function createBoltMacroCall(name: BoltIdentifier, text: string, span?: TextSpan | null): BoltMacroCall;
export function createJSOperator(text: string, span?: TextSpan | null): JSOperator; export function createJSOperator(text: string, span?: TextSpan | null): JSOperator;
export function createJSIdentifier(text: string, span?: TextSpan | null): JSIdentifier; export function createJSIdentifier(text: string, span?: TextSpan | null): JSIdentifier;
@ -1546,6 +1555,7 @@ export function isBoltResumeStatement(value: any): value is BoltResumeStatement;
export function isBoltExpressionStatement(value: any): value is BoltExpressionStatement; export function isBoltExpressionStatement(value: any): value is BoltExpressionStatement;
export function isBoltParameter(value: any): value is BoltParameter; export function isBoltParameter(value: any): value is BoltParameter;
export function isBoltDeclaration(value: any): value is BoltDeclaration; export function isBoltDeclaration(value: any): value is BoltDeclaration;
export function isBoltTypeDeclaration(value: any): value is BoltTypeDeclaration;
export function isBoltModule(value: any): value is BoltModule; export function isBoltModule(value: any): value is BoltModule;
export function isBoltFunctionBodyElement(value: any): value is BoltFunctionBodyElement; export function isBoltFunctionBodyElement(value: any): value is BoltFunctionBodyElement;
export function isBoltFunctionDeclaration(value: any): value is BoltFunctionDeclaration; export function isBoltFunctionDeclaration(value: any): value is BoltFunctionDeclaration;

View file

@ -1,343 +1,291 @@
/**
*
* ```
* mod foo {
* type MyType1 = i32;
* mod bar {
* pub type MyType2 = MyType1;
* }
* }
* ```
*
* ```
* mod foo {
* let x = 1;
* mod bar {
* fn do_something(y) {
* return x + y;
* }
* }
* }
* ```
*
* Note that the `pub`-keyword is not present on `MyType1`.
*/
import { import {Syntax, SyntaxKind, BoltReferenceExpression, BoltDeclaration, BoltSourceFile, BoltSyntax, BoltReferenceTypeExpression, BoltTypeDeclaration} from "./ast";
Syntax, import {FastStringMap} from "./util";
kindToString, import {DiagnosticPrinter, E_TYPES_NOT_ASSIGNABLE, E_TYPE_DECLARATION_NOT_FOUND, E_DECLARATION_NOT_FOUND} from "./diagnostics";
SyntaxKind,
BoltImportDeclaration,
BoltPattern,
isBoltTypeAliasDeclaration,
BoltFunctionBodyElement,
} from "./ast"
import { FastStringMap, getFullTextOfQualName } from "./util"
export class Type {
}
export class PrimType extends Type {
}
export class FunctionType extends Type {
constructor(
public paramTypes: Type[],
public returnType: Type,
) {
super();
}
}
export class VariantType extends Type {
constructor(public elementTypes: Type[]) {
super();
}
}
export const stringType = new PrimType()
export const intType = new PrimType()
export const boolType = new PrimType()
export const voidType = new PrimType()
export const anyType = new PrimType()
export const noneType = new PrimType();
export class RecordType {
private fieldTypes = new FastStringMap<string, Type>();
constructor(
iterable: IterableIterator<[string, Type]>,
) {
for (const [name, type] of iterable) {
this.fieldTypes.set(name, type);
}
}
hasField(name: string) {
return name in this.fieldTypes;
}
getTypeOfField(name: string) {
return this.fieldTypes.get(name);
}
}
interface SymbolInfo { interface SymbolInfo {
type: Type | null; declarations: BoltDeclaration[];
definitions: Syntax[];
} }
export class Scope { interface TypeSymbolInfo {
declarations: BoltTypeDeclaration[];
private symbolsByLocalName = new FastStringMap<string, SymbolInfo>();
constructor(
public originatingNode: Syntax,
public parentScope?: Scope | null
) {
}
public getSymbolNamed(name: string): SymbolInfo | null {
let currScope: Scope | null = this;
while (true) {
if (currScope.symbolsByLocalName.has(name)) {
return currScope.symbolsByLocalName.get(name);
}
currScope = currScope.parentScope;
if (currScope === null) {
break;
}
}
return null;
}
public getTypeNamed(name: string): Type | null {
const sym = this.getSymbolNamed(name);
if (sym === null || !introducesNewType(sym.definitions[0].kind)) {
return null;
}
return sym.type!;
}
}
function* map<T, R>(iterable: Iterable<T>, proc: (value: T) => R): IterableIterator<R> {
const iterator = iterable[Symbol.iterator]();
while (true) {
let { done, value }= iterator.next();
if (done) {
break
}
yield proc(value)
}
}
function introducesNewType(kind: SyntaxKind): boolean {
return kind === SyntaxKind.BoltRecordDeclaration
|| kind === SyntaxKind.BoltTypeAliasDeclaration;
} }
function introducesNewScope(kind: SyntaxKind): boolean { function introducesNewScope(kind: SyntaxKind): boolean {
return kind === SyntaxKind.BoltFunctionDeclaration return kind === SyntaxKind.BoltSourceFile
|| kind === SyntaxKind.BoltModule
|| kind === SyntaxKind.BoltFunctionDeclaration
|| kind === SyntaxKind.BoltBlockExpression;
}
function introducesNewTypeScope(kind: SyntaxKind): boolean {
return kind === SyntaxKind.BoltModule
|| kind === SyntaxKind.BoltSourceFile; || kind === SyntaxKind.BoltSourceFile;
} }
function getFullName(node: Syntax) { type Scope = unknown;
let out = [] type TypeScope = unknown;
let curr: Syntax | null = node;
while (true) { function createSymbol(node: BoltDeclaration): SymbolInfo {
switch (curr.kind) { return { declarations: [ node ] };
case SyntaxKind.BoltIdentifier: }
out.unshift(curr.text)
break; function createTypeSymbol(node: BoltTypeDeclaration): TypeSymbolInfo {
case SyntaxKind.BoltModule: return { declarations: [ node ] };
out.unshift(getFullTextOfQualName(curr.name));
break;
case SyntaxKind.BoltRecordDeclaration:
out.unshift(getFullTextOfQualName(curr.name))
break;
}
curr = curr.parentNode;
if (curr === null) {
break;
}
}
return out.join('.');
} }
export class TypeChecker { export class TypeChecker {
private symbols = new FastStringMap<string, Type>(); constructor(private diagnostics: DiagnosticPrinter) {
private types = new Map<Syntax, Type>();
private scopes = new Map<Syntax, Scope>();
private inferTypeFromUsage(bindings: BoltPattern, body: BoltFunctionBodyElement[]) {
return anyType;
} }
private getTypeOfBody(body: BoltFunctionBodyElement[]) { private symbols = new FastStringMap<string, SymbolInfo>();
return anyType; private typeSymbols = new FastStringMap<string, TypeSymbolInfo>();
public checkSourceFile(node: BoltSourceFile): void {
const refExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceExpression);
for (const refExp of refExps) {
if (this.resolveReferenceExpression(refExp) === null) {
this.diagnostics.add({
message: E_DECLARATION_NOT_FOUND,
args: { name: refExp.name.name.text },
severity: 'error',
node: refExp,
})
}
} }
private createType(node: Syntax): Type { const typeRefExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceTypeExpression);
for (const typeRefExp of typeRefExps) {
if (this.resolveTypeReferenceExpression(typeRefExp) === null) {
this.diagnostics.add({
message: E_TYPE_DECLARATION_NOT_FOUND,
args: { name: typeRefExp.name.name.text },
severity: 'error',
node: typeRefExp,
})
}
}
console.error(`creating type for ${kindToString(node.kind)}`); }
public registerSourceFile(node: BoltSourceFile): void {
this.addAllSymbolsInNode(node);
}
private addAllSymbolsInNode(node: BoltSyntax): void {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltReferenceExpression: case SyntaxKind.BoltSourceFile:
return anyType; case SyntaxKind.BoltModule:
for (const element of node.elements) {
case SyntaxKind.BoltConstantExpression: this.addAllSymbolsInNode(element);
return node.value.type; }
break;
case SyntaxKind.BoltExpressionStatement:
return voidType;
case SyntaxKind.BoltCallExpression:
// TODO
return anyType;
case SyntaxKind.BoltFunctionDeclaration: case SyntaxKind.BoltFunctionDeclaration:
let returnType = anyType; {
if (node.returnType !== null) { const scope = this.getScopeSurroundingNode(node);
returnType = this.getTypeOfNode(node.returnType) const sym = createSymbol(node);
this.addSymbol(node.name.text, scope, sym);
break;
} }
if (node.body !== null) {
returnType = this.intersectTypes(returnType, this.getTypeOfBody(node.body))
}
let paramTypes = node.params.map(param => {
let paramType = this.getTypeOfNode(param);
if (node.body !== null) {
paramType = this.intersectTypes(
paramType,
this.inferTypeFromUsage(param.bindings, node.body)
)
}
return paramType
})
return new FunctionType(paramTypes, returnType);
case SyntaxKind.BoltReferenceTypeExpression:
const name = getFullTextOfQualName(node.name);
const scope = this.getScope(node);
let reffed = scope.getTypeNamed(name);
if (reffed === null) {
reffed = anyType;
}
return reffed;
case SyntaxKind.BoltRecordDeclaration: case SyntaxKind.BoltRecordDeclaration:
{
const fullName = getFullName(node); const typeScope = this.getTypeScopeSurroundingNode(node);
let type; const typeSym = createTypeSymbol(node);
this.addTypeSymbol(node.name.text, typeScope, typeSym);
if (node.members === null) {
type = new PrimType();
this.symbols.set(fullName, type);
} else {
type = new RecordType(map(node.members, member => ([field.name.text, this.getTypeOfNode(field.type)])));
this.symbols.set(fullName, type);
} }
return type;
case SyntaxKind.BoltParameter:
if (node.type !== null) {
return this.getTypeOfNode(node.type)
}
return anyType;
default:
throw new Error(`Could not derive type of ${kindToString(node.kind)}`)
} }
} }
public getSymbolNamed(name: string) { private addSymbol(name: string, scope: Scope, sym: SymbolInfo): void {
if (!this.symbols.has(name)) { this.symbols.set(`${name}@${(scope as any).id}`, sym);
}
private addTypeSymbol(name: string, scope: TypeScope, sym: TypeSymbolInfo): void {
this.typeSymbols.set(`${name}@${(scope as any).id}`, sym);
}
public getParentScope(scope: Scope): Scope | null {
let node = scope as Syntax;
if (node.kind === SyntaxKind.BoltSourceFile) {
return null; return null;
} }
return this.symbols.get(name); node = node.parentNode!;
}
public getTypeOfNode(node: Syntax): Type {
if (this.types.has(node)) {
return this.types.get(node)!
}
const newType = this.createType(node)
this.types.set(node, newType)
return newType;
}
public check(node: Syntax) {
this.getTypeOfNode(node);
switch (node.kind) {
case SyntaxKind.BoltRecordDeclaration:
case SyntaxKind.BoltConstantExpression:
break;
case SyntaxKind.BoltExpressionStatement:
this.check(node.expression);
break;
case SyntaxKind.BoltCallExpression:
this.check(node.operator);
for (const operand of node.operands) {
this.check(operand);
}
// TODO check whether the overload matches the referenced operator
break;
case SyntaxKind.BoltFunctionDeclaration:
if (node.body !== null) {
if (Array.isArray(node.body)) {
for (const element of node.body) {
this.check(element)
}
}
}
break;
case SyntaxKind.BoltReferenceExpression:
// TODO implement this
break;
case SyntaxKind.BoltModule:
case SyntaxKind.BoltSourceFile:
for (const element of node.elements) {
this.check(element)
}
break;
default:
throw new Error(`Could not type-check node ${kindToString(node.kind)}`)
}
}
public getScope(node: Syntax): Scope {
while (!introducesNewScope(node.kind)) { while (!introducesNewScope(node.kind)) {
node = node.parentNode!; node = node.parentNode!;
} }
if (this.scopes.has(node)) { return node;
return this.scopes.get(node)!
}
const scope = new Scope(node)
this.scopes.set(node, scope)
return scope
} }
private intersectTypes(a: Type, b: Type): Type { public getParentTypeScope(scope: TypeScope): TypeScope | null {
if (a === noneType || b == noneType) { let node = scope as Syntax;
return noneType; if (node.kind === SyntaxKind.BoltSourceFile) {
return null;
} }
if (b === anyType) { node = node.parentNode!;
return a while (!introducesNewTypeScope(node.kind)) {
node = node.parentNode!;
} }
if (a === anyType) { return node;
return b;
} }
if (a instanceof FunctionType && b instanceof FunctionType) {
if (a.paramTypes.length !== b.paramTypes.length) { private getScopeSurroundingNode(node: Syntax): Scope {
return noneType; if (node.kind === SyntaxKind.BoltSourceFile) {
return node;
} }
const returnType = this.intersectTypes(a.returnType, b.returnType); return this.getScopeForNode(node.parentNode);
const paramTypes = a.paramTypes.map((_, i) => this.intersectTypes(a.paramTypes[i], b.paramTypes[i]));
return new FunctionType(paramTypes, returnType)
} }
return noneType;
private getTypeScopeSurroundingNode(node: Syntax): TypeScope {
if (node.kind === SyntaxKind.BoltSourceFile) {
return node;
}
return this.getScopeForNode(node.parentNode);
}
private getScopeForNode(node: Syntax): Scope {
if (node.kind === SyntaxKind.BoltSourceFile) {
return node;
}
let currNode = node;
while (!introducesNewScope(currNode.kind)) {
currNode = currNode.parentNode!;
}
return currNode;
}
private getTypeScopeForNode(node: Syntax): TypeScope {
if (node.kind === SyntaxKind.BoltSourceFile) {
return node;
}
let currNode = node;
while (!introducesNewTypeScope(currNode.kind)) {
currNode = currNode.parentNode!;
}
return currNode;
}
private lookupSymbolInScope(name: string, scope: Scope): SymbolInfo | null {
const key = `${name}@${(scope as any).id}`;
if (!this.symbols.has(key)) {
return null;
}
return this.symbols.get(key);
}
private lookupSymbolInTypeScope(name: string, scope: TypeScope): TypeSymbolInfo | null {
const key = `${name}@${(scope as any).id}`;
if (!this.typeSymbols.has(key)) {
return null;
}
return this.typeSymbols.get(key);
}
public findSymbolInScopeOf(name: string, scope: Scope): SymbolInfo | null {
while (true) {
const sym = this.lookupSymbolInScope(name, scope);
if (sym !== null) {
return sym;
}
const parentScope = this.getParentScope(scope);
if (parentScope === null) {
break;
}
scope = parentScope;
}
return null;
}
public findSymbolInTypeScopeOf(name: string, scope: TypeScope): TypeSymbolInfo | null {
while (true) {
const sym = this.lookupSymbolInTypeScope(name, scope);
if (sym !== null) {
return sym;
}
const parentTypeScope = this.getParentTypeScope(scope);
if (parentTypeScope === null) {
break;
}
scope = parentTypeScope;
}
return null;
}
public resolveReferenceExpression(node: BoltReferenceExpression): BoltDeclaration | null {
let scope = this.getScopeSurroundingNode(node);
if (node.name.modulePath !== null) {
while (true) {
let shouldSearchParentScopes = false;
let currScope = scope;
for (const name of node.name.modulePath) {
const sym = this.lookupSymbolInScope(name.text, currScope);
if (sym === null) {
shouldSearchParentScopes = true;
break;
}
if (sym.declarations[0].kind !== SyntaxKind.BoltModule) {
shouldSearchParentScopes = true;
break;
}
currScope = this.getScopeForNode(sym.declarations[0]);
}
if (!shouldSearchParentScopes) {
scope = currScope;
break;
}
const parentScope = this.getParentScope(scope);
if (parentScope === null) {
return null;
}
scope = parentScope;
}
}
const sym = this.findSymbolInScopeOf(node.name.name.text, scope);
if (sym === null) {
return null;
}
return sym.declarations[0]!;
}
public resolveTypeReferenceExpression(node: BoltReferenceTypeExpression): BoltTypeDeclaration | null {
const typeScope = this.getTypeScopeSurroundingNode(node);
const typeSym = this.findSymbolInTypeScopeOf(node.name.name.text, typeScope);
if (typeSym === null) {
return null;
}
return typeSym.declarations[0]!;
} }
} }

53
src/common.ts Normal file
View file

@ -0,0 +1,53 @@
import {
BoltFunctionBodyElement,
BoltReturnStatement,
SyntaxKind,
BoltExpression
} from "./ast";
export type BoltFunctionBody = BoltFunctionBodyElement[];
export function getAllReturnStatements(body: BoltFunctionBody): BoltReturnStatement[] {
const results: BoltReturnStatement[] = [];
for (const element of body) {
visit(element);
}
return results;
function visit(node: BoltFunctionBodyElement) {
switch (node.kind) {
case SyntaxKind.BoltReturnStatement:
results.push(node);
break;
case SyntaxKind.BoltExpressionStatement:
visitExpression(node.expression);
break;
}
}
function visitExpression(node: BoltExpression) {
switch (node.kind) {
case SyntaxKind.BoltBlockExpression:
for (const element of node.statements) {
visit(element);
}
break;
case SyntaxKind.BoltMatchExpression:
for (const arm of node.arms) {
visitExpression(arm.body);
}
break;
case SyntaxKind.BoltCallExpression:
visitExpression(node.operator);
for (const operand of node.operands) {
visitExpression(operand);
}
break;
}
}
}

View file

@ -38,8 +38,8 @@ export class Container {
} }
} }
private resolve<T>(factory: Factory<T>): T; public resolve<T>(factory: Factory<T>): T;
private resolve(serviceId: ServiceID): any { public resolve(serviceId: ServiceID): any {
return this.singletons.get(serviceId); return this.singletons.get(serviceId);
} }

37
src/diagnostics.ts Normal file
View file

@ -0,0 +1,37 @@
import chalk from "chalk"
import {Syntax} from "./ast";
import {format, MapLike, FormatArg} from "./util";
export const E_TYPE_DECLARATION_NOT_FOUND = "A type declaration named '{name}' was not found."
export const E_DECLARATION_NOT_FOUND = "Reference to an undefined declaration '{name}'.";
export const E_TYPES_NOT_ASSIGNABLE = "Types {left} and {right} are not assignable.";
export interface Diagnostic {
message: string;
severity: string;
args?: MapLike<FormatArg>;
node?: Syntax;
}
export class DiagnosticPrinter {
public hasErrors = false;
public add(diagnostic: Diagnostic): void {
let out = ''
switch (diagnostic.severity) {
case 'error':
this.hasErrors = true;
out += chalk.bold.red('error: ');
}
if (diagnostic.args !== undefined) {
out += format(diagnostic.message, diagnostic.args);
} else {
out += diagnostic.message;
}
process.stderr.write(out + '\n');
}
}

View file

@ -8,7 +8,7 @@ import { Program } from "./program"
import { TypeChecker } from "./checker" import { TypeChecker } from "./checker"
import { Evaluator } from "./evaluator" import { Evaluator } from "./evaluator"
import { emit } from "./emitter" import { emit } from "./emitter"
import { Syntax } from "./ast" import { Syntax, BoltSourceFile } from "./ast"
import { upsearchSync, FastStringMap, getFileStem, getLanguage } from "./util" import { upsearchSync, FastStringMap, getFileStem, getLanguage } from "./util"
import { Package } from "./package" import { Package } from "./package"
import { verbose, memoize } from "./util" import { verbose, memoize } from "./util"
@ -18,6 +18,7 @@ import CompileBoltToJSTransform from "./transforms/boltToJS"
import ConstFoldTransform from "./transforms/constFold" import ConstFoldTransform from "./transforms/constFold"
import EliminateModulesTransform from "./transforms/eliminateModules" import EliminateModulesTransform from "./transforms/eliminateModules"
import { TransformManager } from "./transforms/index" import { TransformManager } from "./transforms/index"
import {DiagnosticPrinter} from "./diagnostics"
const targetExtensions: MapLike<string> = { const targetExtensions: MapLike<string> = {
'JS': '.mjs', 'JS': '.mjs',
@ -62,12 +63,14 @@ export class Frontend {
public evaluator: Evaluator; public evaluator: Evaluator;
public checker: TypeChecker; public checker: TypeChecker;
public diagnostics: DiagnosticPrinter;
public timing: Timing; public timing: Timing;
private container = new Container(); private container = new Container();
constructor() { constructor() {
this.checker = new TypeChecker(); this.diagnostics = new DiagnosticPrinter();
this.checker = new TypeChecker(this.diagnostics);
this.evaluator = new Evaluator(this.checker); this.evaluator = new Evaluator(this.checker);
this.timing = new Timing(); this.timing = new Timing();
this.container.bindSelf(this.evaluator); this.container.bindSelf(this.evaluator);
@ -87,6 +90,17 @@ export class Frontend {
public compile(program: Program, target: string) { public compile(program: Program, target: string) {
for (const sourceFile of program.getAllSourceFiles()) {
this.checker.registerSourceFile(sourceFile as BoltSourceFile);
}
for (const sourceFile of program.getAllSourceFiles()) {
this.checker.checkSourceFile(sourceFile as BoltSourceFile);
}
if (this.diagnostics.hasErrors) {
throw new Error(`Compilation failed because of type-checking errors.`);
}
switch (target) { switch (target) {
case "JS": case "JS":

View file

@ -58,6 +58,7 @@ import {
BoltFunctionBodyElement, BoltFunctionBodyElement,
createBoltSourceFile, createBoltSourceFile,
BoltRecordField, BoltRecordField,
setParents,
} from "./ast" } from "./ast"
import { parseForeignLanguage } from "./foreign" import { parseForeignLanguage } from "./foreign"
@ -168,24 +169,25 @@ export class Parser {
public parseQualName(tokens: BoltTokenStream): BoltQualName { public parseQualName(tokens: BoltTokenStream): BoltQualName {
const path: BoltIdentifier[] = []; let modulePath = null;
if (tokens.peek(2).kind === SyntaxKind.BoltDot) {
modulePath = [];
while (true) { while (true) {
modulePath.push(tokens.get() as BoltIdentifier)
tokens.get();
const t0 = tokens.peek(2); const t0 = tokens.peek(2);
if (t0.kind !== SyntaxKind.BoltDot) { if (t0.kind !== SyntaxKind.BoltDot) {
break; break;
} }
path.push(tokens.get() as BoltIdentifier) }
tokens.get();
} }
const name = tokens.get(); const name = tokens.get();
if (name.kind !== SyntaxKind.BoltIdentifier) { assertToken(name, SyntaxKind.BoltIdentifier);
throw new ParseError(name, [SyntaxKind.BoltIdentifier]); const startNode = modulePath !== null ? modulePath[0] : name;
}
const startNode = path.length > 0 ? path[0] : name;
const endNode = name; const endNode = name;
const node = createBoltQualName(path, name, null); const node = createBoltQualName(modulePath, name as BoltIdentifier, null);
setOrigNodeRange(node, startNode, endNode); setOrigNodeRange(node, startNode, endNode);
return node; return node;
} }
@ -446,21 +448,16 @@ export class Parser {
public parseStatement(tokens: BoltTokenStream): BoltStatement { public parseStatement(tokens: BoltTokenStream): BoltStatement {
const t0 = tokens.peek(); const t0 = tokens.peek();
if (t0.kind === SyntaxKind.BoltReturnKeyword) { if (KIND_EXPRESSION_T0.indexOf(t0.kind) !== -1) {
return this.parseExpressionStatement(tokens);
} else if (t0.kind === SyntaxKind.BoltReturnKeyword) {
return this.parseReturnStatement(tokens); return this.parseReturnStatement(tokens);
} else if (t0.kind === SyntaxKind.BoltLoopKeyword) { } else if (t0.kind === SyntaxKind.BoltLoopKeyword) {
return this.parseLoopStatement(tokens); return this.parseLoopStatement(tokens);
} else { } else {
try {
return this.parseExpressionStatement(tokens);
} catch (e) {
if (!(e instanceof ParseError)) {
throw e;
}
throw new ParseError(t0, KIND_STATEMENT_T0); throw new ParseError(t0, KIND_STATEMENT_T0);
} }
} }
}
public parseGenericTypeParameter(tokens: BoltTokenStream): BoltTypeParameter { public parseGenericTypeParameter(tokens: BoltTokenStream): BoltTypeParameter {
const t0 = tokens.peek(); const t0 = tokens.peek();
@ -519,7 +516,7 @@ export class Parser {
const t1 = tokens.get(); const t1 = tokens.get();
assertToken(t1, SyntaxKind.BoltIdentifier); assertToken(t1, SyntaxKind.BoltIdentifier);
const name = createBoltQualName([], t1 as BoltIdentifier); const name = t1 as BoltIdentifier;
let t2 = tokens.peek(); let t2 = tokens.peek();
@ -582,6 +579,10 @@ export class Parser {
if (t0.kind === SyntaxKind.EndOfFile) { if (t0.kind === SyntaxKind.EndOfFile) {
break; break;
} }
if (t0.kind === SyntaxKind.BoltSemi) {
tokens.get();
continue;
}
const statement = this.parseStatement(tokens); const statement = this.parseStatement(tokens);
statements.push(statement); statements.push(statement);
} }
@ -1192,6 +1193,8 @@ export function parseSourceFile(filepath: string): BoltSourceFile {
const contents = fs.readFileSync(file.origPath, 'utf8'); const contents = fs.readFileSync(file.origPath, 'utf8');
const scanner = new Scanner(file, contents) const scanner = new Scanner(file, contents)
const parser = new Parser(); const parser = new Parser();
return parser.parseSourceFile(scanner); const sourceFile = parser.parseSourceFile(scanner);
setParents(sourceFile);
return sourceFile;
} }

View file

@ -122,30 +122,30 @@ function isSymbol(ch: string) {
export class Scanner { export class Scanner {
protected buffer: string[] = []; private buffer: string[] = [];
protected scanned: BoltToken[] = []; private scanned: BoltToken[] = [];
protected currPos: TextPos; private currPos: TextPos;
protected offset = 0; private offset = 0;
constructor(public file: TextFile, public input: string, startPos = new TextPos(0,1,1)) { constructor(public file: TextFile, public input: string, startPos = new TextPos(0,1,1)) {
this.currPos = startPos; this.currPos = startPos;
} }
protected readChar() { private readChar() {
if (this.offset == this.input.length) { if (this.offset == this.input.length) {
return EOF return EOF
} }
return this.input[this.offset++] return this.input[this.offset++]
} }
protected peekChar(count = 1) { private peekChar(count = 1) {
while (this.buffer.length < count) { while (this.buffer.length < count) {
this.buffer.push(this.readChar()); this.buffer.push(this.readChar());
} }
return this.buffer[count - 1]; return this.buffer[count - 1];
} }
protected getChar() { private getChar() {
const ch = this.buffer.length > 0 const ch = this.buffer.length > 0
? this.buffer.shift()! ? this.buffer.shift()!
@ -166,7 +166,7 @@ export class Scanner {
return ch return ch
} }
protected takeWhile(pred: (ch: string) => boolean) { private takeWhile(pred: (ch: string) => boolean) {
let text = this.getChar(); let text = this.getChar();
while (true) { while (true) {
const c0 = this.peekChar(); const c0 = this.peekChar();
@ -190,6 +190,12 @@ export class Scanner {
continue; continue;
} }
const c1 = this.peekChar(2);
if (c0 === '/' && c1 == '/') {
this.scanLineComment();
continue;
}
const startPos = this.currPos.clone() const startPos = this.currPos.clone()
if (c0 == EOF) { if (c0 == EOF) {
@ -338,6 +344,37 @@ export class Scanner {
} }
private assertChar(ch: string): void {
const c0 = this.peekChar();
if (c0 !== ch) {
throw new ScanError(this.file, this.currPos.clone(), ch);
}
this.getChar();
}
private scanLineComment(): string {
let text = '';
this.assertChar('/');
this.assertChar('/');
while (true) {
const c0 = this.peekChar();
if (c0 === EOF) {
break;
}
if (c0 == '\n') {
this.getChar();
const c1 = this.peekChar();
if (c1 === '\r') {
this.getChar();
}
break;
}
text += c0
this.getChar();
}
return text;
}
public peek(count = 1): BoltToken { public peek(count = 1): BoltToken {
while (this.scanned.length < count) { while (this.scanned.length < count) {
this.scanned.push(this.scanToken()); this.scanned.push(this.scanToken());

View file

@ -41,7 +41,7 @@ import {
BoltSourceElement, BoltSourceElement,
} from "../ast" } from "../ast"
import { getFullTextOfQualName, hasPublicModifier, setOrigNodeRange, FastStringMap } from "../util" import { hasPublicModifier, setOrigNodeRange } from "../util"
import { Program, SourceFile } from "../program" import { Program, SourceFile } from "../program"
import { Transformer, TransformManager } from "./index" import { Transformer, TransformManager } from "./index"
import { assert } from "../util" import { assert } from "../util"
@ -119,8 +119,9 @@ export class BoltToJSTransform implements Transformer {
); );
case SyntaxKind.BoltReferenceExpression: case SyntaxKind.BoltReferenceExpression:
assert(node.name.modulePath === null);
return createJSReferenceExpression( return createJSReferenceExpression(
getFullTextOfQualName(node.name), node.name.name.text,
node.span, node.span,
); );

View file

@ -10,12 +10,9 @@ import {
BoltMacroCall, BoltMacroCall,
} from "../ast" } from "../ast"
import { TextSpan } from "../text"
import { TypeChecker, Scope } from "../checker" import { TypeChecker, Scope } from "../checker"
import { ParseError } from "../util"
import { BoltTokenStream, Parser, isModifierKeyword } from "../parser" import { BoltTokenStream, Parser, isModifierKeyword } from "../parser"
import { Evaluator, TRUE, FALSE } from "../evaluator" import { Evaluator, TRUE, FALSE } from "../evaluator"
import { setOrigNodeRange, createTokenStream } from "../util"
import { Transformer, TransformManager } from "./index" import { Transformer, TransformManager } from "./index"
import { inject } from "../di" import { inject } from "../di"
import {SourceFile} from "../program" import {SourceFile} from "../program"

View file

@ -1,8 +1,8 @@
import { SourceFile, Program } from "./program" import { SourceFile, Program } from "../program"
import { Container } from "./di" import { Container, Newable } from "../di"
import {Evaluator} from "./evaluator"; import {Evaluator} from "../evaluator";
import {TypeChecker} from "./checker"; import {TypeChecker} from "../checker";
export interface Transformer { export interface Transformer {
isApplicable(node: SourceFile): boolean; isApplicable(node: SourceFile): boolean;
@ -13,10 +13,6 @@ export interface RegisterTransformerOptions {
} }
function createInstance<T>(factory: Factory<T>, ...args: any[]): T {
return new factory(...args);
}
export class TransformManager { export class TransformManager {
private transformers: Transformer[] = []; private transformers: Transformer[] = [];
@ -26,7 +22,7 @@ export class TransformManager {
} }
public register(transformerFactory: Newable<Transformer>, options: RegisterTransformerOptions = {}) { public register(transformerFactory: Newable<Transformer>, options: RegisterTransformerOptions = {}) {
const transformer = this.container.createInstance(transformerFactory, this); const transformer = this.container.createInstance(transformerFactory, this) as Transformer;
this.transformers.push(transformer); this.transformers.push(transformer);
} }

View file

@ -58,6 +58,8 @@ function isSyntax(value) {
&& value.__NODE_TYPE !== undefined; && value.__NODE_TYPE !== undefined;
} }
let nextNodeId = 1;
function createNode(nodeType) { function createNode(nodeType) {
const obj = Object.create(nodeProto); const obj = Object.create(nodeProto);
Object.defineProperty(obj, '__NODE_TYPE', { Object.defineProperty(obj, '__NODE_TYPE', {
@ -73,6 +75,11 @@ function createNode(nodeType) {
return this.__NODE_TYPE.index; return this.__NODE_TYPE.index;
} }
}); });
Object.defineProperty(obj, 'id', {
enumerable: true,
configurable: true,
value: nextNodeId++,
})
obj.span = null; obj.span = null;
return obj; return obj;
} }
@ -113,7 +120,7 @@ for (const nodeName of Object.keys(NODE_TYPES)) {
exported.setParents = function setParents(node, parentNode = null) { exported.setParents = function setParents(node, parentNode = null) {
node.parentNode = parentNode; node.parentNode = parentNode;
for (const child of getChildNodes(node)) { for (const child of node.getChildNodes()) {
setParents(child, node) setParents(child, node)
} }
} }

189
src/types.ts Normal file
View file

@ -0,0 +1,189 @@
import { FastStringMap } from "./util";
enum TypeKind {
OpaqueType,
AnyType,
NeverType,
FunctionType,
RecordType,
VariantType,
TupleType,
}
export type Type
= OpaqueType
| AnyType
| NeverType
| FunctionType
| RecordType
| VariantType
| TupleType
abstract class TypeBase {
abstract kind: TypeKind;
}
export class OpaqueType extends TypeBase {
kind: TypeKind.OpaqueType = TypeKind.OpaqueType;
}
export function isOpaqueType(value: any): value is OpaqueType {
return value.kind === TypeKind.OpaqueType;
}
export class AnyType extends TypeBase {
kind: TypeKind.AnyType = TypeKind.AnyType;
}
export function createAnyType(): AnyType {
return new AnyType();
}
export function isAnyType(value: any): value is AnyType {
return value.kind === TypeKind.AnyType;
}
export class NeverType extends TypeBase {
kind: TypeKind.NeverType = TypeKind.NeverType;
}
export function isNeverType(value: any): value is NeverType {
return value instanceof NeverType;
}
export class FunctionType extends TypeBase {
kind: TypeKind.FunctionType = TypeKind.FunctionType;
constructor(
public paramTypes: Type[],
public returnType: Type,
) {
super();
}
public getParameterCount(): number {
return this.paramTypes.length;
}
public getParamTypeAtIndex(index: number) {
if (index < 0 || index >= this.paramTypes.length) {
throw new Error(`Could not get the parameter type at index ${index} because the index was out of bounds.`);
}
return this.paramTypes[index];
}
}
export function isFunctionType(value: any): value is FunctionType {
return value instanceof FunctionType;
}
export class VariantType extends TypeBase {
kind: TypeKind.VariantType = TypeKind.VariantType;
constructor(public elementTypes: Type[]) {
super();
}
public getOwnElementTypes(): IterableIterator<Type> {
return this.elementTypes[Symbol.iterator]();
}
}
export function isVariantType(value: any): value is VariantType {
return value instanceof VariantType;
}
export class RecordType {
kind: TypeKind.RecordType = TypeKind.RecordType;
private fieldTypes = new FastStringMap<string, Type>();
constructor(
iterable: IterableIterator<[string, Type]>,
) {
for (const [name, type] of iterable) {
this.fieldTypes.set(name, type);
}
}
public hasField(name: string) {
return name in this.fieldTypes;
}
public getTypeOfField(name: string) {
return this.fieldTypes.get(name);
}
}
export function isRecordType(value: any): value is RecordType {
return value.kind === TypeKind.RecordType;
}
export class TupleType extends TypeBase {
kind: TypeKind.TupleType = TypeKind.TupleType;
constructor(public elementTypes: Type[]) {
super();
}
}
export function intersectTypes(a: Type, b: Type): Type {
if (isNeverType(a) || isNeverType(b)) {
return new NeverType();
}
if (isAnyType(b)) {
return a
}
if (isAnyType(a)) {
return b;
}
if (isFunctionType(a) && isFunctionType(b)) {
if (a.paramTypes.length !== b.paramTypes.length) {
return new NeverType();
}
const returnType = intersectTypes(a.returnType, b.returnType);
const paramTypes = a.paramTypes
.map((_, i) => intersectTypes(a.paramTypes[i], b.paramTypes[i]));
return new FunctionType(paramTypes, returnType)
}
return new NeverType();
}
export function isTypeAssignable(a: Type, b: Type): boolean {
if (isNeverType(a)) {
return false;
}
if (isAnyType(b)) {
return true;
}
if (isOpaqueType(a) && isOpaqueType(b)) {
return a === b;
}
if (a.kind !== b.kind) {
return false;
}
if (isFunctionType(a) && isFunctionType(b)) {
if (a.paramTypes.length !== b.paramTypes.length) {
return false;
}
const paramCount = a.getParameterCount();
for (let i = 0; i < paramCount; i++) {
if (!isTypeAssignable(a.getParamTypeAtIndex(i), b.getParamTypeAtIndex(i))) {
return false;
}
}
return true;
}
throw new Error(`Should not get here.`);
}

View file

@ -169,12 +169,11 @@ export function hasPublicModifier(node: BoltDeclaration) {
return (node.modifiers & BoltDeclarationModifiers.Public) > 0; return (node.modifiers & BoltDeclarationModifiers.Public) > 0;
} }
export function getFullTextOfQualName(node: BoltQualName) { export function toDeclarationPath(node: BoltQualName): string[] {
let out = '' if (node.modulePath === null) {
for (const element of node.modulePath) { return [ node.name.text ];
out += element.text + '.';
} }
return out + node.name.text; return [...node.modulePath.map(id => id.text), node.name.text];
} }
export interface Stream<T> { export interface Stream<T> {
@ -451,3 +450,41 @@ export class ScanError extends Error {
} }
} }
export interface MapLike<T> {
[key: string]: T;
}
export type FormatArg = string | Date | number
export function format(message: string, data: MapLike<FormatArg>) {
let out = ''
let name = '';
let insideParam = false;
for (const ch of message) {
if (insideParam) {
if (ch === '}') {
out += data[name]!.toString();
reset();
}
name += ch
} else {
if (ch === '{') {
insideParam = true;
} else {
out += ch;
}
}
}
return out;
function reset() {
name = '';
insideParam = false;
}
}