Re-write the type-checker to make it more extensible
This commit is contained in:
parent
742dd6ba5d
commit
32a9d288de
14 changed files with 758 additions and 429 deletions
168
src/ast.d.ts
vendored
168
src/ast.d.ts
vendored
|
@ -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;
|
||||||
|
|
538
src/checker.ts
538
src/checker.ts
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createType(node: Syntax): Type {
|
public registerSourceFile(node: BoltSourceFile): void {
|
||||||
|
this.addAllSymbolsInNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
console.error(`creating type for ${kindToString(node.kind)}`);
|
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);
|
||||||
if (node.body !== null) {
|
break;
|
||||||
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;
|
}
|
||||||
|
|
||||||
|
private getScopeSurroundingNode(node: Syntax): Scope {
|
||||||
|
if (node.kind === SyntaxKind.BoltSourceFile) {
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
if (a instanceof FunctionType && b instanceof FunctionType) {
|
return this.getScopeForNode(node.parentNode);
|
||||||
if (a.paramTypes.length !== b.paramTypes.length) {
|
}
|
||||||
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 returnType = this.intersectTypes(a.returnType, b.returnType);
|
const parentScope = this.getParentScope(scope);
|
||||||
const paramTypes = a.paramTypes.map((_, i) => this.intersectTypes(a.paramTypes[i], b.paramTypes[i]));
|
if (parentScope === null) {
|
||||||
return new FunctionType(paramTypes, returnType)
|
break;
|
||||||
|
}
|
||||||
|
scope = parentScope;
|
||||||
}
|
}
|
||||||
return noneType;
|
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
53
src/common.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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
37
src/diagnostics.ts
Normal 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
while (true) {
|
if (tokens.peek(2).kind === SyntaxKind.BoltDot) {
|
||||||
const t0 = tokens.peek(2);
|
modulePath = [];
|
||||||
if (t0.kind !== SyntaxKind.BoltDot) {
|
while (true) {
|
||||||
break;
|
modulePath.push(tokens.get() as BoltIdentifier)
|
||||||
|
tokens.get();
|
||||||
|
const t0 = tokens.peek(2);
|
||||||
|
if (t0.kind !== SyntaxKind.BoltDot) {
|
||||||
|
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,19 +448,14 @@ 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 {
|
throw new ParseError(t0, KIND_STATEMENT_T0);
|
||||||
return this.parseExpressionStatement(tokens);
|
|
||||||
} catch (e) {
|
|
||||||
if (!(e instanceof ParseError)) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
throw new ParseError(t0, KIND_STATEMENT_T0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
189
src/types.ts
Normal 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.`);
|
||||||
|
}
|
||||||
|
|
47
src/util.ts
47
src/util.ts
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue