Progress; Restructuring

This commit is contained in:
dcodeIO 2017-10-02 12:52:15 +02:00
parent e14d02e040
commit f55fc70220
20 changed files with 1008 additions and 887 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
node_modules/
npm-debug.*
out/
raw/

View File

@ -53,6 +53,7 @@
VariableStatement <> VariableDeclaration
WhileStatement
Parameter
Source
SwitchCase
TypeNode
TypeParameter
@ -65,8 +66,8 @@
*/
import { Token, operatorTokenToString, Range } from "./tokenizer";
import { CharCode, I64, sb } from "./util";
import { Token, Tokenizer, operatorTokenToString, Range } from "./tokenizer";
import { CharCode, I64, normalizePath, resolvePath } from "./util";
export { Range } from "./tokenizer";
@ -106,6 +107,7 @@ export enum NodeKind {
BREAK,
CLASS, // is also declaration
CONTINUE,
DECORATOR,
DO,
EMPTY,
ENUM, // is also declaration
@ -202,7 +204,7 @@ export class TypeNode extends Node {
this.identifier.serialize(sb);
if (this.parameters.length) {
sb.push("<");
for (let i: i32 = 0; i < this.parameters.length; ++i) {
for (let i: i32 = 0, k: i32 = this.parameters.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.parameters[i].serialize(sb);
@ -398,7 +400,7 @@ export class ArrayLiteralExpression extends LiteralExpression {
serialize(sb: string[]): void {
sb.push("[");
for (let i: i32 = 0; i < this.elementExpressions.length; ++i) {
for (let i: i32 = 0, k: i32 = this.elementExpressions.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
if (this.elementExpressions[i])
@ -410,7 +412,7 @@ export class ArrayLiteralExpression extends LiteralExpression {
serializeAsTree(sb: string[], indent: i32 = 0): void {
pushIndent(sb, indent++);
sb.push("[\n");
for (let i: i32 = 0; i < this.elementExpressions.length; ++i) {
for (let i: i32 = 0, k: i32 = this.elementExpressions.length; i < k; ++i) {
pushIndent(sb, indent);
if (i > 0)
sb.push(",\n");
@ -499,9 +501,10 @@ export class CallExpression extends Expression {
serialize(sb: string[]): void {
this.expression.serialize(sb);
let i: i32, k: i32;
if (this.typeArguments.length) {
sb.push("<");
for (let i: i32 = 0; i < this.typeArguments.length; ++i) {
for (i = 0, k = this.typeArguments.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.typeArguments[i].serialize(sb);
@ -509,7 +512,7 @@ export class CallExpression extends Expression {
sb.push(">(");
} else
sb.push("(");
for (let i: i32 = 0; i < this.arguments.length; ++i) {
for (i = 0, k = this.arguments.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.arguments[i].serialize(sb);
@ -806,7 +809,7 @@ export abstract class Statement extends Node {
return stmt;
}
static createClass(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], extendsType: TypeNode | null, implementsTypes: TypeNode[], members: DeclarationStatement[], range: Range): ClassDeclaration {
static createClass(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], extendsType: TypeNode | null, implementsTypes: TypeNode[], members: DeclarationStatement[], decorators: DecoratorStatement[], range: Range): ClassDeclaration {
const stmt: ClassDeclaration = new ClassDeclaration();
stmt.range = range;
let i: i32, k: i32;
@ -816,6 +819,7 @@ export abstract class Statement extends Node {
if (stmt.extendsType = extendsType) (<TypeNode>extendsType).parent = stmt;
for (i = 0, k = (stmt.implementsTypes = implementsTypes).length; i < k; ++i) implementsTypes[i].parent = stmt;
for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt;
for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt;
return stmt;
}
@ -826,6 +830,14 @@ export abstract class Statement extends Node {
return stmt;
}
static createDecorator(expression: Expression, args: Expression[], range: Range): DecoratorStatement {
const stmt: DecoratorStatement = new DecoratorStatement();
stmt.range = range;
(stmt.expression = expression).parent = stmt;
for (let i: i32 = 0, k: i32 = (stmt.arguments = args).length; i < k; ++i) args[i].parent = stmt;
return stmt;
}
static createDo(statement: Statement, condition: Expression, range: Range): DoStatement {
const stmt: DoStatement = new DoStatement();
stmt.range = range;
@ -865,6 +877,7 @@ export abstract class Statement extends Node {
for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt;
for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt;
stmt.path = path;
stmt.normalizedPath = path == null ? null : resolvePath(normalizePath(<string>path), range.source.normalizedPath);
return stmt;
}
@ -905,6 +918,7 @@ export abstract class Statement extends Node {
stmt.range = range;
for (let i: i32 = 0, k: i32 = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt;
stmt.path = path;
stmt.normalizedPath = resolvePath(normalizePath(path), range.source.normalizedPath);
return stmt;
}
@ -926,7 +940,7 @@ export abstract class Statement extends Node {
return stmt;
}
static createField(modifiers: Modifier[], identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, range: Range): FieldDeclaration {
static createField(modifiers: Modifier[], identifier: IdentifierExpression, type: TypeNode | null, initializer: Expression | null, decorators: DecoratorStatement[], range: Range): FieldDeclaration {
const stmt: FieldDeclaration = new FieldDeclaration();
stmt.range = range;
let i: i32, k: i32;
@ -934,6 +948,7 @@ export abstract class Statement extends Node {
(stmt.identifier = identifier).parent = stmt;
if (stmt.type = type) (<TypeNode>type).parent = stmt;
if (stmt.initializer = initializer) (<Expression>initializer).parent = stmt;
for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt;
return stmt;
}
@ -965,7 +980,7 @@ export abstract class Statement extends Node {
return elem;
}
static createFunction(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, range: Range): FunctionDeclaration {
static createFunction(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, decorators: DecoratorStatement[], range: Range): FunctionDeclaration {
const stmt: FunctionDeclaration = new FunctionDeclaration();
stmt.range = range;
let i: i32, k: i32;
@ -975,10 +990,11 @@ export abstract class Statement extends Node {
for (i = 0, k = (stmt.parameters = parameters).length; i < k; ++i) parameters[i].parent = stmt;
if (stmt.returnType = returnType) (<TypeNode>returnType).parent = stmt;
if (stmt.statements = statements) for (i = 0, k = (<Statement[]>statements).length; i < k; ++i) (<Statement[]>statements)[i].parent = stmt;
for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt;
return stmt;
}
static createMethod(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, range: Range): MethodDeclaration {
static createMethod(modifiers: Modifier[], identifier: IdentifierExpression, typeParameters: TypeParameter[], parameters: Parameter[], returnType: TypeNode | null, statements: Statement[] | null, decorators: DecoratorStatement[], range: Range): MethodDeclaration {
const stmt: MethodDeclaration = new MethodDeclaration();
stmt.range = range;
let i: i32, k: i32;
@ -988,6 +1004,7 @@ export abstract class Statement extends Node {
for (i = 0, k = (stmt.parameters = parameters).length; i < k; ++i) parameters[i].parent = stmt;
if (stmt.returnType = returnType) (<TypeNode>returnType).parent = stmt;;
if (stmt.statements = statements) for (i = 0, k = (<Statement[]>statements).length; i < k; ++i) (<Statement[]>statements)[i].parent = stmt;
for (i = 0, k = (stmt.decorators = decorators).length; i < k; ++i) decorators[i].parent = stmt;
return stmt;
}
@ -1053,7 +1070,7 @@ export abstract class Statement extends Node {
stmt.range = range;
let i: i32, k: i32;
for (i = 0, k = (stmt.modifiers = modifiers).length; i < k; ++i) modifiers[i].parent = stmt;
for (i = 0, k = (stmt.members = declarations).length; i < k; ++i) declarations[i].parent = stmt;
for (i = 0, k = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt;
return stmt;
}
@ -1075,13 +1092,30 @@ export abstract class Statement extends Node {
}
}
export abstract class SourceNode extends Node { // extended by Source
export class Source extends Node {
kind = NodeKind.SOURCE;
parent = null;
path: string;
normalizedPath: string;
statements: Statement[];
text: string;
tokenizer: Tokenizer | null = null;
isEntry: bool;
constructor(path: string, text: string, isEntry: bool) {
super();
this.path = path;
this.normalizedPath = normalizePath(path, true);
this.statements = new Array();
this.range = new Range(this, 0, text.length);
this.text = text;
this.isEntry = isEntry;
}
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
serialize(sb: string[]): void {
for (let i: i32 = 0, k = this.statements.length; i < k; ++i) {
const statement: Statement = this.statements[i];
@ -1097,7 +1131,6 @@ export abstract class SourceNode extends Node { // extended by Source
export abstract class DeclarationStatement extends Statement {
identifier: IdentifierExpression;
reflectionIndex: i32 = -1;
}
export class BlockStatement extends Statement {
@ -1107,9 +1140,9 @@ export class BlockStatement extends Statement {
serialize(sb: string[]): void {
sb.push("{\n");
for (let i: i32 = 0; i < this.statements.length; ++i) {
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
this.statements[i].serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1140,9 +1173,15 @@ export class ClassDeclaration extends DeclarationStatement {
extendsType: TypeNode | null;
implementsTypes: TypeNode[];
members: DeclarationStatement[];
decorators: DecoratorStatement[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.decorators.length; i < k; ++i) {
this.decorators[i].serialize(sb);
sb.push("\n");
}
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
@ -1150,7 +1189,7 @@ export class ClassDeclaration extends DeclarationStatement {
sb.push(this.identifier.name);
if (this.typeParameters.length) {
sb.push("<");
for (let i: i32 = 0; i < this.typeParameters.length; ++i) {
for (i = 0, k = this.typeParameters.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.typeParameters[i].serialize(sb);
@ -1163,16 +1202,16 @@ export class ClassDeclaration extends DeclarationStatement {
}
if (this.implementsTypes.length) {
sb.push(" implements ");
for (let i: i32 = 0; i < this.implementsTypes.length; ++i) {
for (i = 0, k = this.implementsTypes.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.implementsTypes[i].serialize(sb);
}
}
sb.push(" {\n");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1195,6 +1234,25 @@ export class ContinueStatement extends Statement {
}
}
export class DecoratorStatement extends Statement {
kind = NodeKind.DECORATOR;
expression: Expression;
arguments: Expression[];
serialize(sb: string[]): void {
sb.push("@");
this.expression.serialize(sb);
sb.push("(");
for (let i: i32 = 0, k: i32 = this.arguments.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.arguments[i].serialize(sb);
}
sb.push(")");
}
}
export class DoStatement extends Statement {
kind = NodeKind.DO;
@ -1227,14 +1285,15 @@ export class EnumDeclaration extends DeclarationStatement {
members: EnumValueDeclaration[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
sb.push("enum ");
this.identifier.serialize(sb);
sb.push(" {\n");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.members.length; i < k; ++i) {
if (i > 0)
sb.push(",\n");
this.members[i].serialize(sb);
@ -1292,14 +1351,16 @@ export class ExportStatement extends Statement {
modifiers: Modifier[];
members: ExportMember[];
path: string | null;
normalizedPath: string | null;
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
sb.push("export {\n");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.members.length; i < k; ++i) {
if (i > 0)
sb.push(",\n");
this.members[i].serialize(sb);
@ -1329,9 +1390,15 @@ export class FieldDeclaration extends DeclarationStatement {
modifiers: Modifier[];
type: TypeNode | null;
initializer: Expression | null;
decorators: DecoratorStatement[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.decorators.length; i < k; ++i) {
this.decorators[i].serialize(sb);
sb.push("\n");
}
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
@ -1382,9 +1449,15 @@ export class FunctionDeclaration extends DeclarationStatement {
parameters: Parameter[];
returnType: TypeNode | null;
statements: Statement[] | null;
decorators: DecoratorStatement[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.decorators.length; i < k; ++i) {
this.decorators[i].serialize(sb);
sb.push("\n");
}
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
@ -1394,9 +1467,10 @@ export class FunctionDeclaration extends DeclarationStatement {
protected serializeCommon(sb: string[]): void {
this.identifier.serialize(sb);
let i: i32, k: i32;
if (this.typeParameters.length) {
sb.push("<");
for (let i: i32 = 0; i < this.typeParameters.length; ++i) {
for (i = 0, k = this.typeParameters.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.typeParameters[i].serialize(sb);
@ -1404,7 +1478,7 @@ export class FunctionDeclaration extends DeclarationStatement {
sb.push(">");
}
sb.push("(");
for (let i: i32 = 0; i < this.parameters.length; ++i) {
for (i = 0, k = this.parameters.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.parameters[i].serialize(sb);
@ -1416,10 +1490,10 @@ export class FunctionDeclaration extends DeclarationStatement {
sb.push(")");
if (this.statements) {
sb.push(" {\n");
for (let i: i32 = 0; i < (<Statement[]>this.statements).length; ++i) {
for (i = 0, k = (<Statement[]>this.statements).length; i < k; ++i) {
const statement: Statement = (<Statement[]>this.statements)[i];
statement.serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1472,10 +1546,11 @@ export class ImportStatement extends Statement {
kind = NodeKind.IMPORT;
declarations: ImportDeclaration[];
path: string;
normalizedPath: string;
serialize(sb: string[]): void {
sb.push("import {\n");
for (let i: i32 = 0; i < this.declarations.length; ++i) {
for (let i: i32 = 0, k: i32 = this.declarations.length; i < k; ++i) {
if (i > 0)
sb.push(",\n");
this.declarations[i].serialize(sb);
@ -1494,7 +1569,8 @@ export class InterfaceDeclaration extends DeclarationStatement {
members: Statement[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
@ -1502,7 +1578,7 @@ export class InterfaceDeclaration extends DeclarationStatement {
this.identifier.serialize(sb);
if (this.typeParameters.length) {
sb.push("<");
for (let i: i32 = 0; i < this.typeParameters.length; ++i) {
for (i = 0, k = this.typeParameters.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.typeParameters[i].serialize(sb);
@ -1514,9 +1590,9 @@ export class InterfaceDeclaration extends DeclarationStatement {
(<TypeNode>this.extendsType).serialize(sb);
}
sb.push(" {\n");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1530,7 +1606,12 @@ export class MethodDeclaration extends FunctionDeclaration {
kind = NodeKind.METHOD;
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.decorators.length; i < k; ++i) {
this.decorators[i].serialize(sb);
sb.push("\n");
}
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
@ -1545,16 +1626,17 @@ export class NamespaceDeclaration extends DeclarationStatement {
members: DeclarationStatement[];
serialize(sb: string[]): void {
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
}
sb.push("namespace ");
this.identifier.serialize(sb);
sb.push(" {\n");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1634,11 +1716,11 @@ export class SwitchCase extends Node {
sb.push(":\n");
} else
sb.push("default:\n");
for (let i: i32 = 0; i < this.statements.length; ++i) {
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
if (i > 0)
sb.push("\n");
this.statements[i].serialize(sb);
if (endsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
sb.push("\n");
else
sb.push(";\n");
@ -1656,7 +1738,7 @@ export class SwitchStatement extends Statement {
sb.push("switch (");
this.expression.serialize(sb);
sb.push(") {\n");
for (let i: i32 = 0; i < this.cases.length; ++i) {
for (let i: i32 = 0, k: i32 = this.cases.length; i < k; ++i) {
this.cases[i].serialize(sb);
sb.push("\n");
}
@ -1684,14 +1766,15 @@ export class TryStatement extends Statement {
serialize(sb: string[]): void {
sb.push("try {\n");
for (let i: i32 = 0; i < this.statements.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.statements.length; i < k; ++i) {
this.statements[i].serialize(sb);
sb.push(";\n");
}
sb.push("} catch (");
this.catchVariable.serialize(sb);
sb.push(") {\n");
for (let i: i32 = 0; i < this.catchStatements.length; ++i) {
for (i = 0, k = this.catchStatements.length; i < k; ++i) {
this.catchStatements[i].serialize(sb);
sb.push(";\n");
}
@ -1722,11 +1805,12 @@ export class VariableStatement extends Statement {
kind = NodeKind.VARIABLE;
modifiers: Modifier[];
members: VariableDeclaration[];
declarations: VariableDeclaration[];
serialize(sb: string[]): void {
let isConst: bool = false;
for (let i: i32 = 0; i < this.modifiers.length; ++i) {
let i: i32, k: i32;
for (i = 0, k = this.modifiers.length; i < k; ++i) {
this.modifiers[i].serialize(sb);
sb.push(" ");
if (this.modifiers[i].modifierKind == ModifierKind.CONST)
@ -1734,10 +1818,10 @@ export class VariableStatement extends Statement {
}
if (!isConst)
sb.push("let ");
for (let i: i32 = 0; i < this.members.length; ++i) {
for (i = 0, k = this.declarations.length; i < k; ++i) {
if (i > 0)
sb.push(", ");
this.members[i].serialize(sb);
this.declarations[i].serialize(sb);
}
}
}
@ -1768,12 +1852,12 @@ export function isDeclarationStatement(kind: NodeKind): bool {
}
export function serialize(node: Node, indent: i32 = 0): string {
sb.length = 0;
const sb: string[] = new Array(); // shared builder could grow too much
node.serialize(sb);
return sb.join("");
}
function endsWith(code: CharCode, sb: string[]): bool {
function builderEndsWith(code: CharCode, sb: string[]): bool {
if (sb.length) {
const last: string = sb[sb.length - 1];
return last.length ? last.charCodeAt(last.length - 1) == code : false;

2
src/binaryen.d.ts vendored
View File

@ -219,7 +219,7 @@ declare type BinaryenExportRef = usize;
declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef;
declare function _BinaryenRemoveExport(module: BinaryenModuleRef, externalName: CString): void;
declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenImportRef;
declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenImportRef; // sic
declare function _BinaryenSetFunctionTable(module: BinaryenModuleRef, funcs: CArray<BinaryenFunctionRef>, numFuncs: BinaryenIndex): void;

View File

@ -394,7 +394,7 @@ export class Module {
}
}
addFunction(name: string, type: Type, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef {
addFunction(name: string, type: BinaryenFunctionTypeRef, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef {
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(varTypes);
try {

View File

@ -2,23 +2,28 @@ import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
import { hasModifier } from "./parser";
import { Program } from "./program";
import { CharCode, I64, U64 } from "./util";
import { CharCode, I64, U64, normalizePath, sb } from "./util";
import { Token } from "./tokenizer";
import {
NodeKind,
TypeNode,
Source,
// statements
BlockStatement,
BreakStatement,
ClassDeclaration,
ContinueStatement,
DeclarationStatement,
DoStatement,
EmptyStatement,
EnumDeclaration,
EnumValueDeclaration,
ExportMember,
ExportStatement,
ExpressionStatement,
FieldDeclaration,
FunctionDeclaration,
ForStatement,
IfStatement,
@ -58,19 +63,12 @@ import {
} from "./ast";
import {
Enum,
Class,
Field,
Function,
GlobalVariable,
LocalVariable,
Namespace,
Method,
Source,
ClassType,
FunctionType,
Type,
TypeKind
} from "./reflection";
} from "./types";
export enum Target {
WASM32,
@ -79,6 +77,7 @@ export enum Target {
export class Options {
target: Target = Target.WASM32;
noEmit: bool = false;
}
export class Compiler extends DiagnosticEmitter {
@ -87,16 +86,20 @@ export class Compiler extends DiagnosticEmitter {
options: Options;
module: Module;
startFunction: FunctionType = new FunctionType(Type.void, new Array());
startFunctionBody: BinaryenExpressionRef[] = new Array();
currentType: Type = Type.void;
currentClass: Class | null = null;
currentFunction: Function | null = null;
breakMajor: i32 = 0;
breakMinor: i32 = 0;
currentClass: ClassType | null = null;
currentFunction: FunctionType = this.startFunction;
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
memorySegments: MemorySegment[] = new Array();
statements: BinaryenExpressionRef[] = new Array(); // TODO: make start function
classes: Map<string,ClassType> = new Map();
enums: Set<string> = new Set();
functions: Map<string,FunctionType> = new Map();
globals: Set<string> = new Set();
static compile(program: Program, options: Options | null = null): Module {
const compiler: Compiler = new Compiler(program, options);
@ -123,60 +126,59 @@ export class Compiler extends DiagnosticEmitter {
switch (statement.kind) {
case NodeKind.CLASS:
if (hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>statement).modifiers) && !(<ClassDeclaration>statement).typeParameters.length) {
const cl: Class = Class.create(<ClassDeclaration>statement, []).exportAs((<ClassDeclaration>statement).identifier.name);
this.program.addClass(cl);
this.compileClass(cl);
}
if (hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>statement).modifiers) && !(<ClassDeclaration>statement).typeParameters.length)
this.compileClass(<ClassDeclaration>statement, []);
break;
case NodeKind.ENUM:
if (hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>statement).modifiers)) {
const en: Enum = Enum.create(<EnumDeclaration>statement).exportAs((<EnumDeclaration>statement).identifier.name);
this.program.addEnum(en);
this.compileEnum(en);
}
if (hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>statement).modifiers))
this.compileEnum(<EnumDeclaration>statement);
break;
case NodeKind.FUNCTION:
if (hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers) && !(<FunctionDeclaration>statement).typeParameters.length) {
const fn: Function = Function.create(<FunctionDeclaration>statement, []).exportAs((<FunctionDeclaration>statement).identifier.name);
this.program.addFunction(fn);
this.compileFunction(fn);
}
if (hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers) && !(<FunctionDeclaration>statement).typeParameters.length)
this.compileFunction(<FunctionDeclaration>statement, []);
break;
case NodeKind.IMPORT:
break;
case NodeKind.NAMESPACE:
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
this.program.addNamespace(ns);
this.compileNamespace(ns);
}
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers))
this.compileNamespace(<NamespaceDeclaration>statement);
break;
case NodeKind.VARIABLE:
if (hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers)) {
const gls: GlobalVariable[] = GlobalVariable.create(<VariableStatement>statement);
for (let j: i32 = 0, l: i32 = gls.length; j < l; ++j) {
const gl: GlobalVariable = gls[j];
gl.exportName = (<VariableDeclaration>gl.declaration).identifier.name; // WASM can't do this right now
this.program.addGlobal(gl);
this.compileGlobal(gl);
}
}
if (hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers))
this.compileGlobals(<VariableStatement>statement);
break;
case NodeKind.EXPORT:
// TODO: obtain referenced declaration and export that
break;
default:
this.statements.push(this.compileStatement(statement));
case NodeKind.EXPORT: {
this.compileExports(<ExportStatement>statement);
break;
}
default: {
const previousClass: ClassType | null = this.currentClass;
const previousFunction: FunctionType = this.currentFunction;
this.currentClass = null;
this.currentFunction = this.startFunction;
this.startFunctionBody.push(this.compileStatement(statement));
this.currentClass = previousClass;
this.currentFunction = previousFunction;
break;
}
}
}
// make start function if not empty
if (this.startFunctionBody.length) {
let typeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(BinaryenType.None, []);
if (!typeRef)
typeRef = this.module.addFunctionType("v", BinaryenType.None, []);
this.module.setStart(
this.module.addFunction(".start", typeRef, [], this.module.createBlock("", this.startFunctionBody))
);
}
// set up memory size and static segments
@ -193,34 +195,115 @@ export class Compiler extends DiagnosticEmitter {
return this.module;
}
compileClass(cl: Class): void {
compileClass(declaration: ClassDeclaration, typeArguments: Type[]): void {
throw new Error("not implemented");
}
compileEnum(en: Enum): void {
for (let i: i32 = 0, k = en.declaration.members.length; i < k; ++i) {
const declaration: EnumValueDeclaration = en.declaration.members[i];
const name: string = this.program.mangleName(declaration);
const value: BinaryenExpressionRef = declaration.value
? this.compileExpression(<Expression>declaration.value, Type.i32)
: this.module.createI32(i); // TODO
this.module.addGlobal(name, BinaryenType.I32, false, value);
if (en.exportName != null) {
// TODO: WASM does not yet support non-function exports
}
compileEnum(declaration: EnumDeclaration): void {
const name: string = this.program.mangleInternalName(declaration);
if (this.enums.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
return;
}
const valueDeclarations: EnumValueDeclaration[] = declaration.members;
let previousValueName: string | null = null;
for (let i: i32 = 0, k: i32 = valueDeclarations.length; i < k; ++i)
previousValueName = this.compileEnumValue(valueDeclarations[i], previousValueName);
this.enums.add(name);
}
compileFunction(fn: Function): void {
compileEnumValue(declaration: EnumValueDeclaration, previousName: string | null): string {
const name: string = this.program.mangleInternalName(declaration);
let initializer: BinaryenExpressionRef = declaration.value ? this.compileExpression(<Expression>declaration.value, Type.i32) : 0;
if (!this.options.noEmit) {
if (!initializer) initializer = previousName == null
? this.module.createI32(0)
: this.module.createBinary(BinaryOp.AddI32,
this.module.createGetGlobal(previousName, BinaryenType.I32),
this.module.createI32(1)
);
this.module.addGlobal(name, BinaryenType.I32, false, initializer);
}
return name;
}
compileFunction(declaration: FunctionDeclaration, typeArguments: Type[]): void {
/* const binaryenResultType: BinaryenType = declaration.returnType ? this.resolveType(<TypeNode>declaration.returnType, true) : BinaryenType.None;
const binaryenParamTypes: BinaryenType[] = new Array();
let binaryenTypeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes);
if (!binaryenTypeRef)
binaryenTypeRef = this.module.addFunctionType("", binaryenResultType, binaryenParamTypes); */
throw new Error("not implemented");
}
compileGlobal(gl: GlobalVariable): void {
compileGlobals(statement: VariableStatement): void {
const declarations: VariableDeclaration[] = statement.declarations;
const isConst: bool = hasModifier(ModifierKind.CONST, statement.modifiers);
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i)
this.compileGlobal(declarations[i], isConst);
}
compileGlobal(declaration: VariableDeclaration, isConst: bool): void {
const type: Type | null = declaration.type ? this.resolveType(<TypeNode>declaration.type) : null; // reports
if (!type)
return;
const name: string = this.program.mangleInternalName(declaration);
if (this.globals.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
return;
}
if (!this.options.noEmit) {
const binaryenType: BinaryenType = typeToBinaryenType(<Type>type);
const initializer: BinaryenExpressionRef = declaration.initializer ? this.compileExpression(<Expression>declaration.initializer, <Type>type) : typeToBinaryenZero(this.module, <Type>type);
this.module.addGlobal(name, binaryenType, !isConst, initializer);
}
this.globals.add(name);
}
compileNamespace(declaration: NamespaceDeclaration): void {
throw new Error("not implemented");
}
compileNamespace(ns: Namespace): void {
throw new Error("not implemented");
compileExports(statement: ExportStatement): void {
const members: ExportMember[] = statement.members;
const normalizedPath: string | null = statement.path == null ? null : normalizePath(<string>statement.path);
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.compileExport(members[i], normalizedPath);
}
compileExport(member: ExportMember, normalizedPath: string | null): void {
const exportName: string = normalizedPath + "/" + member.externalIdentifier.name;
const declaration: DeclarationStatement | null = <DeclarationStatement | null>this.program.exports.get(exportName);
if (declaration) {
switch ((<DeclarationStatement>declaration).kind) {
case NodeKind.CLASS:
if (!(<ClassDeclaration>declaration).typeParameters.length)
this.compileClass(<ClassDeclaration>declaration, []);
break;
case NodeKind.ENUM:
this.compileEnum(<EnumDeclaration>declaration);
break;
case NodeKind.FUNCTION:
if (!(<FunctionDeclaration>declaration).typeParameters.length)
this.compileFunction(<FunctionDeclaration>declaration, []);
break;
case NodeKind.NAMESPACE:
this.compileNamespace(<NamespaceDeclaration>declaration);
break;
case NodeKind.VARIABLEDECLARATION:
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, (<VariableStatement>declaration.parent).modifiers));
break;
default:
throw new Error("unexpected declaration kind");
}
} else
throw new Error("unexpected missing declaration");
}
// memory
@ -238,31 +321,23 @@ export class Compiler extends DiagnosticEmitter {
// types
resolveType(node: TypeNode, reportNotFound: bool = true): Type | null {
resolveType(node: TypeNode, typeArgumentsMap: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null {
// TODO: resolve node and its arguments using typeArgumentsMap
// TODO: make types for classes in program.ts
const types: Map<string,Type> = this.program.types;
const name: string = node.identifier.name;
if (types.has(name)) {
const type: Type = <Type>types.get(name);
return type;
}
const globalName: string = node.identifier.name;
const localName: string = node.range.source.normalizedPath + "/" + globalName;
if (types.has(localName))
return <Type>types.get(localName);
if (types.has(globalName))
return <Type>types.get(globalName);
if (reportNotFound)
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, name);
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, globalName);
return null;
}
// statements
enterBreakContext(): string {
if (this.breakMinor == 0)
++this.breakMajor;
++this.breakMinor;
return this.breakMajor.toString() + "." + this.breakMinor.toString();
}
leaveBreakContext(): void {
--this.breakMinor;
}
compileStatement(statement: Statement): BinaryenExpressionRef {
switch (statement.kind) {
@ -355,7 +430,7 @@ export class Compiler extends DiagnosticEmitter {
compileReturnStatement(statement: ReturnStatement): BinaryenExpressionRef {
if (this.currentFunction) {
const expression: BinaryenExpressionRef = statement.expression ? this.compileExpression(<Expression>statement.expression, (<Function>this.currentFunction).returnType) : 0;
const expression: BinaryenExpressionRef = statement.expression ? this.compileExpression(<Expression>statement.expression, this.currentFunction.returnType) : 0;
return this.module.createReturn(expression);
}
return this.module.createUnreachable();
@ -379,11 +454,11 @@ export class Compiler extends DiagnosticEmitter {
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
const label: string = this.enterBreakContext();
const label: string = this.currentFunction.enterBreakContext();
const breakLabel: string = "break$" + label;
const continueLabel: string = "continue$" + label;
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
this.leaveBreakContext();
this.currentFunction.leaveBreakContext();
return this.module.createBlock(breakLabel, [
this.module.createLoop(continueLabel,
this.module.createIf(condition, this.module.createBlock("", [
@ -616,7 +691,7 @@ export class Compiler extends DiagnosticEmitter {
}
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef {
const toType: Type | null = this.resolveType(expression.toType); // reports
const toType: Type | null = this.resolveType(expression.toType, this.currentFunction.typeArgumentsMap); // reports
if (toType && toType != contextualType) {
const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, <Type>toType);
return this.convertExpression(expr, this.currentType, <Type>toType);
@ -835,11 +910,11 @@ export class Compiler extends DiagnosticEmitter {
}
return compound
? this.compileAssignment(expression.left, this.module.createBinary(op, left, right))
? this.compileAssignment(expression.left, this.module.createBinary(op, left, right), this.currentType != Type.void)
: this.module.createBinary(op, left, right);
}
compileAssignment(expression: Expression, value: BinaryenExpressionRef): BinaryenExpressionRef {
compileAssignment(expression: Expression, value: BinaryenExpressionRef, tee: bool = false): BinaryenExpressionRef {
throw new Error("not implemented");
}
@ -908,18 +983,37 @@ export class Compiler extends DiagnosticEmitter {
}
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef {
let operand: BinaryenExpressionRef;
let op: UnaryOp;
const operator: Token = expression.operator;
switch (expression.operator) {
let op: BinaryOp;
let binaryenType: BinaryenType;
let binaryenOne: BinaryenExpressionRef;
case Token.PLUS_PLUS:
case Token.MINUS_MINUS:
default:
throw new Error("not implemented");
if (contextualType == Type.f32) {
op = operator == Token.PLUS_PLUS ? BinaryOp.AddF32 : BinaryOp.SubF32;
binaryenType = BinaryenType.F32;
binaryenOne = this.module.createF32(1);
} else if (contextualType == Type.f64) {
op = operator == Token.PLUS_PLUS ? BinaryOp.AddF64 : BinaryOp.SubF64;
binaryenType = BinaryenType.F64;
binaryenOne = this.module.createF64(1);
} else if (contextualType.isLongInteger) {
op = operator == Token.PLUS_PLUS ? BinaryOp.AddI64 : BinaryOp.SubI64;
binaryenType = BinaryenType.I64;
binaryenOne = this.module.createI64(1, 0);
} else {
op = operator == Token.PLUS_PLUS ? BinaryOp.AddI32 : BinaryOp.SubI32;
binaryenType = BinaryenType.I32;
binaryenOne = this.module.createI32(1);
}
// return this.module.createBinary(op, operand);
const getValue: BinaryenExpressionRef = this.compileExpression(expression.expression, contextualType);
const setValue: BinaryenExpressionRef = this.compileAssignment(expression.expression, this.module.createBinary(op, getValue, binaryenOne), false); // reports
return this.module.createBlock(null, [
getValue,
setValue
], binaryenType);
}
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
@ -929,7 +1023,7 @@ export class Compiler extends DiagnosticEmitter {
switch (expression.operator) {
case Token.PLUS:
return this.compileExpression(expression.expression, contextualType);
return this.compileExpression(expression.expression, contextualType); // noop
case Token.MINUS:
operand = this.compileExpression(expression.expression, contextualType);
@ -943,8 +1037,25 @@ export class Compiler extends DiagnosticEmitter {
: this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand);
break;
// case Token.PLUS_PLUS:
// case Token.MINUS_MINUS:
case Token.PLUS_PLUS:
operand = this.compileExpression(expression.expression, contextualType);
return this.currentType == Type.f32
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)), contextualType != Type.void)
: this.currentType == Type.f64
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)), contextualType != Type.void)
: this.currentType.isLongInteger
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)), contextualType != Type.void);
case Token.MINUS_MINUS:
operand = this.compileExpression(expression.expression, contextualType);
return this.currentType == Type.f32
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)), contextualType != Type.void)
: this.currentType == Type.f64
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)), contextualType != Type.void)
: this.currentType.isLongInteger
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)), contextualType != Type.void);
case Token.EXCLAMATION:
operand = this.compileExpression(expression.expression, Type.bool, false);
@ -975,3 +1086,57 @@ export class Compiler extends DiagnosticEmitter {
return this.module.createUnary(op, operand);
}
}
// helpers
function typeToBinaryenType(type: Type): BinaryenType {
return type.kind == TypeKind.F32
? BinaryenType.F32
: type.kind == TypeKind.F64
? BinaryenType.F64
: type.isLongInteger
? BinaryenType.I64
: type.isAnyInteger || type.kind == TypeKind.BOOL
? BinaryenType.I32
: BinaryenType.None;
}
function typeToBinaryenZero(module: Module, type: Type): BinaryenExpressionRef {
return type.kind == TypeKind.F32
? module.createF32(0)
: type.kind == TypeKind.F64
? module.createF64(0)
: type.isLongInteger
? module.createI64(0, 0)
: module.createI32(0);
}
function typeToBinaryenOne(module: Module, type: Type): BinaryenExpressionRef {
return type.kind == TypeKind.F32
? module.createF32(1)
: type.kind == TypeKind.F64
? module.createF64(1)
: type.isLongInteger
? module.createI64(1, 0)
: module.createI32(1);
}
function typeToSignatureNamePart(type: Type): string {
return type.kind == TypeKind.VOID
? "v"
: type.kind == TypeKind.F32
? "f"
: type.kind == TypeKind.F64
? "F"
: type.isLongInteger
? "I"
: "i";
}
function typesToSignatureName(paramTypes: Type[], returnType: Type): string {
sb.length = 0;
for (let i: i32 = 0, k = paramTypes.length; i < k; ++i)
sb.push(typeToSignatureNamePart(paramTypes[i]));
sb.push(typeToSignatureNamePart(returnType));
return sb.join("");
}

View File

@ -39,12 +39,14 @@ export enum DiagnosticCode {
An_implementation_cannot_be_declared_in_ambient_contexts = 1183,
An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive = 1198,
Unterminated_Unicode_escape_sequence = 1199,
Decorators_are_not_valid_here = 1206,
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
Duplicate_identifier_0 = 2300,
Cannot_find_name_0 = 2304,
Generic_type_0_requires_1_type_argument_s = 2314,
Type_0_is_not_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322,
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391
}
@ -88,12 +90,14 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1183: return "An implementation cannot be declared in ambient contexts.";
case 1198: return "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.";
case 1199: return "Unterminated Unicode escape sequence.";
case 1206: return "Decorators are not valid here.";
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
case 2300: return "Duplicate identifier '{0}'.";
case 2304: return "Cannot find name '{0}'.";
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
case 2391: return "Function implementation is missing or not immediately following the declaration.";
default: return "";
}

View File

@ -38,6 +38,7 @@
"An implementation cannot be declared in ambient contexts.": 1183,
"An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.": 1198,
"Unterminated Unicode escape sequence.": 1199,
"Decorators are not valid here.": 1206,
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
"Duplicate identifier '{0}'.": 2300,
@ -45,5 +46,6 @@
"Generic type '{0}' requires {1} type argument(s).": 2314,
"Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322,
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
"Function implementation is missing or not immediately following the declaration.": 2391
}

View File

@ -1,5 +1,5 @@
import { Range } from "./ast";
import { CharCode, isLineBreak, sb } from "./util";
import { isLineBreak, sb } from "./util";
import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";

View File

@ -21,7 +21,7 @@
import { Module } from "./binaryen";
import { Compiler } from "./compiler";
import { DiagnosticMessage, DiagnosticCategory, DiagnosticCode } from "./diagnostics";
import { DiagnosticMessage, DiagnosticCategory } from "./diagnostics";
import { Parser } from "./parser";
import { Program } from "./program";

View File

@ -8,30 +8,26 @@
*/
import { Program } from "./program";
import { Source } from "./reflection";
import { Tokenizer, Token, Range } from "./tokenizer";
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
import { I64, normalizePath, resolvePath } from "./util";
import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics";
import { normalizePath } from "./util";
import {
NodeKind,
Source,
// types
TypeNode,
// expressions
AssertionExpression,
AssertionKind,
Expression,
FloatLiteralExpression,
IdentifierExpression,
IntegerLiteralExpression,
LiteralExpression,
LiteralKind,
// statements
BlockStatement,
ClassDeclaration,
DecoratorStatement,
DoStatement,
EnumDeclaration,
EnumValueDeclaration,
@ -79,8 +75,6 @@ export class Parser extends DiagnosticEmitter {
for (let i: i32 = 0, k: i32 = this.program.sources.length; i < k; ++i)
if (this.program.sources[i].normalizedPath == normalizedPath)
throw Error("duplicate source");
if (isEntry)
this.seenlog.add(normalizedPath);
const source: Source = new Source(path, text, isEntry);
@ -91,6 +85,17 @@ export class Parser extends DiagnosticEmitter {
while (!tn.skip(Token.ENDOFFILE)) {
let decorators: DecoratorStatement[] | null = null;
while (tn.skip(Token.AT)) {
const decorator: DecoratorStatement | null = this.parseDecorator(tn);
if (!decorator)
break;
if (!decorators)
decorators = new Array();
(<DecoratorStatement[]>decorators).push(<DecoratorStatement>decorator);
}
let modifiers: Modifier[] | null = null;
if (tn.skip(Token.EXPORT))
@ -127,7 +132,8 @@ export class Parser extends DiagnosticEmitter {
break;
case Token.FUNCTION:
statement = this.parseFunction(tn, modifiers ? <Modifier[]>modifiers : createModifiers());
statement = this.parseFunction(tn, modifiers ? <Modifier[]>modifiers : createModifiers(), decorators);
decorators = null;
break;
case Token.ABSTRACT:
@ -139,7 +145,8 @@ export class Parser extends DiagnosticEmitter {
// fall through
case Token.CLASS:
statement = this.parseClass(tn, modifiers ? <Modifier[]>modifiers : createModifiers());
statement = this.parseClass(tn, modifiers ? <Modifier[]>modifiers : createModifiers(), decorators);
decorators = null;
break;
case Token.IMPORT:
@ -170,6 +177,9 @@ export class Parser extends DiagnosticEmitter {
break;
}
if (decorators)
for (let i: i32 = 0, k: i32 = (<DecoratorStatement[]>decorators).length; i < k; ++i)
this.error(DiagnosticCode.Decorators_are_not_valid_here, (<DecoratorStatement[]>decorators)[i].range);
if (!statement)
return;
statement.parent = source;
@ -270,12 +280,12 @@ export class Parser extends DiagnosticEmitter {
}
// ... [][]
while (tn.skip(Token.OPENBRACKET)) {
let bracketRange: Range = tn.range();
let bracketStart: i32 = tn.tokenPos;
if (!tn.skip(Token.CLOSEBRACKET)) {
this.error(DiagnosticCode._0_expected, tn.range(), "]");
return null;
}
bracketRange = Range.join(bracketRange, tn.range());
const bracketRange = tn.range(bracketStart, tn.pos);
// ...[] | null
let nullable: bool = false;
@ -297,6 +307,32 @@ export class Parser extends DiagnosticEmitter {
// statements
parseDecorator(tn: Tokenizer): DecoratorStatement | null {
// at '@': Identifier ('.' Identifier)* '(' Arguments
const startPos: i32 = tn.tokenPos;
if (tn.skip(Token.IDENTIFIER)) {
let name: string = tn.readIdentifier();
let expression: Expression = Expression.createIdentifier(name, tn.range(startPos, tn.pos));
while (tn.skip(Token.DOT)) {
if (tn.skip(Token.IDENTIFIER)) {
name = tn.readIdentifier();
expression = Expression.createPropertyAccess(expression, Expression.createIdentifier(name, tn.range()), tn.range(startPos, tn.pos));
} else {
this.error(DiagnosticCode.Identifier_expected, tn.range());
return null;
}
}
if (tn.skip(Token.OPENPAREN)) {
const args: Expression[] | null = this.parseArguments(tn);
if (args)
return Statement.createDecorator(expression, <Expression[]>args, tn.range(startPos, tn.pos));
} else
this.error(DiagnosticCode._0_expected, tn.range(), "(");
} else
this.error(DiagnosticCode.Identifier_expected, tn.range());
return null;
}
parseVariable(tn: Tokenizer, modifiers: Modifier[]): VariableStatement | null {
// at ('const' | 'let' | 'var'): VariableDeclaration (',' VariableDeclaration)* ';'?
const startPos: i32 = modifiers.length ? modifiers[0].range.start : tn.tokenPos;
@ -481,9 +517,14 @@ export class Parser extends DiagnosticEmitter {
return null;
}
parseFunction(tn: Tokenizer, modifiers: Modifier[]): FunctionDeclaration | null {
parseFunction(tn: Tokenizer, modifiers: Modifier[], decorators: DecoratorStatement[] | null): FunctionDeclaration | null {
// at 'function': Identifier ('<' TypeParameters)? '(' Parameters (':' Type)? '{' Statement* '}' ';'?
const startPos: i32 = modifiers.length ? modifiers[0].range.start : tn.tokenPos;
const startPos: i32 = decorators && (<DecoratorStatement[]>decorators).length
? (<DecoratorStatement[]>decorators)[0].range.start
: modifiers.length
? modifiers[0].range.start
: tn.tokenPos;
if (!tn.skip(Token.IDENTIFIER)) {
this.error(DiagnosticCode.Identifier_expected, tn.range(tn.pos));
return null;
@ -524,14 +565,18 @@ export class Parser extends DiagnosticEmitter {
}
} else if (!isDeclare)
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range(tn.pos));
const ret: FunctionDeclaration = Statement.createFunction(modifiers, identifier, typeParameters, <Parameter[]>parameters, returnType, statements, tn.range(startPos, tn.pos));
const ret: FunctionDeclaration = Statement.createFunction(modifiers, identifier, typeParameters, <Parameter[]>parameters, returnType, statements, decorators ? <DecoratorStatement[]>decorators : new Array(0), tn.range(startPos, tn.pos));
tn.skip(Token.SEMICOLON);
return ret;
}
parseClass(tn: Tokenizer, modifiers: Modifier[]): ClassDeclaration | null {
parseClass(tn: Tokenizer, modifiers: Modifier[], decorators: DecoratorStatement[] | null): ClassDeclaration | null {
// at 'class': Identifier ('<' TypeParameters)? ('extends' Type)? ('implements' Type (',' Type)*)? '{' ClassMember* '}'
const startPos: i32 = modifiers.length ? modifiers[0].range.start : tn.tokenPos;
const startPos: i32 = decorators && (<DecoratorStatement[]>decorators).length
? (<DecoratorStatement[]>decorators)[0].range.start
: modifiers.length
? modifiers[0].range.start
: tn.tokenPos;
if (tn.skip(Token.IDENTIFIER)) {
const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range());
@ -572,7 +617,7 @@ export class Parser extends DiagnosticEmitter {
members.push(<DeclarationStatement>member);
} while (!tn.skip(Token.CLOSEBRACE));
}
return Statement.createClass(modifiers, identifier, <TypeParameter[]>typeParameters, extendsType, implementsTypes, members, tn.range(startPos, tn.pos));
return Statement.createClass(modifiers, identifier, <TypeParameter[]>typeParameters, extendsType, implementsTypes, members, decorators ? <DecoratorStatement[]>decorators : new Array(0), tn.range(startPos, tn.pos));
} else
this.error(DiagnosticCode._0_expected, tn.range(), "{");
} else
@ -584,6 +629,15 @@ export class Parser extends DiagnosticEmitter {
// ('public' | 'private' | 'protected')? ('static' | 'abstract')? ('get' | 'set')? Identifier ...
const startRange: Range = tn.range();
let decorators: DecoratorStatement[] = new Array();
while (tn.skip(Token.AT)) {
const decorator: DecoratorStatement | null = this.parseDecorator(tn);
if (!decorator)
break;
decorators.push(<DecoratorStatement>decorator);
}
let modifiers: Modifier[] | null = null;
if (tn.skip(Token.PUBLIC))
@ -641,7 +695,7 @@ export class Parser extends DiagnosticEmitter {
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range()); // recoverable
}
const ret: MethodDeclaration = Statement.createMethod(modifiers ? modifiers : createModifiers(), identifier, <TypeParameter[]>typeParameters, <Parameter[]>parameters, returnType, statements, Range.join(startRange, tn.range()));
const ret: MethodDeclaration = Statement.createMethod(modifiers ? modifiers : createModifiers(), identifier, <TypeParameter[]>typeParameters, <Parameter[]>parameters, returnType, statements, decorators, Range.join(startRange, tn.range()));
tn.skip(Token.SEMICOLON);
return ret;
@ -666,7 +720,7 @@ export class Parser extends DiagnosticEmitter {
if (!initializer)
return null;
}
const ret: FieldDeclaration = Statement.createField(modifiers ? modifiers : createModifiers(), identifier, type, initializer, Range.join(startRange, tn.range()));
const ret: FieldDeclaration = Statement.createField(modifiers ? modifiers : createModifiers(), identifier, type, initializer, decorators, Range.join(startRange, tn.range()));
tn.skip(Token.SEMICOLON);
return ret;
}
@ -694,19 +748,18 @@ export class Parser extends DiagnosticEmitter {
}
let path: string | null = null;
if (tn.skip(Token.FROM)) {
if (tn.skip(Token.STRINGLITERAL)) {
if (tn.skip(Token.STRINGLITERAL))
path = tn.readString();
const resolvedPath: string = resolvePath(normalizePath(path), tn.source.normalizedPath);
if (!this.seenlog.has(resolvedPath)) {
this.backlog.push(resolvedPath);
this.seenlog.add(resolvedPath);
}
} else {
else {
this.error(DiagnosticCode.String_literal_expected, tn.range());
return null;
}
}
const ret: ExportStatement = Statement.createExport(modifiers, members, path, Range.join(startRange, tn.range()));
if (ret.normalizedPath && !this.seenlog.has(<string>ret.normalizedPath)) {
this.backlog.push(<string>ret.normalizedPath);
this.seenlog.add(<string>ret.normalizedPath);
}
tn.skip(Token.SEMICOLON);
return ret;
} else
@ -753,12 +806,11 @@ export class Parser extends DiagnosticEmitter {
if (tn.skip(Token.FROM)) {
if (tn.skip(Token.STRINGLITERAL)) {
const path: string = tn.readString();
const resolvedPath: string = resolvePath(normalizePath(path), tn.source.normalizedPath);
if (!this.seenlog.has(resolvedPath)) {
this.backlog.push(resolvedPath);
this.seenlog.add(resolvedPath);
}
const ret: ImportStatement = Statement.createImport(members, path, Range.join(startRange, tn.range()));
if (!this.seenlog.has(ret.normalizedPath)) {
this.backlog.push(ret.normalizedPath);
this.seenlog.add(ret.normalizedPath);
}
tn.skip(Token.SEMICOLON);
return ret;
} else
@ -1153,6 +1205,8 @@ export class Parser extends DiagnosticEmitter {
}
// UnaryPrefixExpression
if ((<Expression>operand).kind != NodeKind.IDENTIFIER && (<Expression>operand).kind != NodeKind.ELEMENTACCESS && (<Expression>operand).kind != NodeKind.PROPERTYACCESS)
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, (<Expression>operand).range);
return Expression.createUnaryPrefix(token, <Expression>operand, tn.range(startPos, tn.pos));
}
@ -1320,6 +1374,8 @@ export class Parser extends DiagnosticEmitter {
// UnaryPostfixExpression
} else if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
if (expr.kind != NodeKind.IDENTIFIER && expr.kind != NodeKind.ELEMENTACCESS && expr.kind != NodeKind.PROPERTYACCESS)
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, expr.range);
expr = Expression.createUnaryPostfix(token, expr, tn.range(startPos, tn.pos));
// SelectExpression

View File

@ -1,15 +1,13 @@
import { Target } from "./compiler";
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
import { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
import { hasModifier } from "./parser";
import { normalizePath, resolvePath, trimExtension } from "./util";
import { Type } from "./types";
import {
ModifierKind,
Node,
NodeKind,
SourceNode,
ModifierKind,
IdentifierExpression,
Source,
ClassDeclaration,
DeclarationStatement,
@ -36,7 +34,7 @@ class QueuedExport {
}
class QueuedImport {
globalName: string;
internalName: string;
importName: string;
declaration: ImportDeclaration;
}
@ -47,20 +45,13 @@ export class Program extends DiagnosticEmitter {
diagnosticsOffset: i32 = 0;
target: Target = Target.WASM32;
// internal names to declarations
/** Internal map of names to declarations. */
names: Map<string,DeclarationStatement> = new Map();
// type names to types
/** Separate map of internal type names to declarations. */
types: Map<string,Type> = new Map();
// export names to declarations (separate from internal names)
/** Separate map of internal export names to declarations. */
exports: Map<string,DeclarationStatement> = new Map();
// reflection instances
classes: Class[] = new Array();
enums: Enum[] = new Array();
functions: Function[] = new Array();
globals: GlobalVariable[] = new Array();
namespaces: Namespace[] = new Array();
constructor(diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics);
this.sources = new Array();
@ -68,12 +59,13 @@ export class Program extends DiagnosticEmitter {
initialize(target: Target): void {
this.target = target;
initializeBasicTypes(this.types, target);
const exportsMap: Map<string,QueuedExport> = new Map();
const importsQueue: QueuedImport[] = new Array();
const queuedExports: Map<string,QueuedExport> = new Map();
const queuedImports: QueuedImport[] = new Array();
// build initial lookup maps of global and export names to declarations
// build initial lookup maps of internal and export names to declarations
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
const source: Source = this.sources[i];
const statements: Statement[] = source.statements;
@ -90,7 +82,7 @@ export class Program extends DiagnosticEmitter {
break;
case NodeKind.EXPORT:
this.initializeExports(<ExportStatement>statement, exportsMap);
this.initializeExports(<ExportStatement>statement, queuedExports);
break;
case NodeKind.FUNCTION:
@ -98,7 +90,7 @@ export class Program extends DiagnosticEmitter {
break;
case NodeKind.IMPORT:
this.initializeImports(<ImportStatement>statement, exportsMap, importsQueue);
this.initializeImports(<ImportStatement>statement, queuedExports, queuedImports);
break;
case NodeKind.INTERFACE:
@ -116,52 +108,53 @@ export class Program extends DiagnosticEmitter {
}
}
// at this point exports map should be resolvable
for (let [key, val] of exportsMap.entries()) {
// at this point queued exports should be resolvable
for (let [exportName, queuedExport] of queuedExports.entries()) {
const seen: Set<QueuedExport> = new Set();
while (exportsMap.has(val.importName)) {
val = <QueuedExport>exportsMap.get(val.importName);
if (seen.has(val))
while (queuedExports.has(queuedExport.importName)) {
queuedExport = <QueuedExport>queuedExports.get(queuedExport.importName);
if (seen.has(queuedExport))
break;
seen.add(val);
seen.add(queuedExport);
}
if (this.exports.has(val.importName))
this.addExport(key, <DeclarationStatement>this.exports.get(val.importName));
if (this.exports.has(queuedExport.importName))
this.addExport(exportName, <DeclarationStatement>this.exports.get(queuedExport.importName));
else
this.error(DiagnosticCode.Cannot_find_name_0, val.member.externalIdentifier.range, val.importName);
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.importName);
}
// at this point imports queue should be resolvable
for (let i: i32 = 0, k: i32 = importsQueue.length; i < k; ++i) {
const queued: QueuedImport = importsQueue[i];
const globalName: string = queued.globalName;
let importName: string = queued.importName;
// at this point also queued imports should be resolvable
for (let i: i32 = 0, k: i32 = queuedImports.length; i < k; ++i) {
const queuedImport: QueuedImport = queuedImports[i];
const internalName: string = queuedImport.internalName;
let importName: string = queuedImport.importName;
const seen: Set<QueuedExport> = new Set();
while (exportsMap.has(importName)) {
const val: QueuedExport = <QueuedExport>exportsMap.get(importName);
importName = val.importName;
if (seen.has(val))
while (queuedExports.has(importName)) {
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(importName);
importName = queuedExport.importName;
if (seen.has(queuedExport))
break;
seen.add(val);
seen.add(queuedExport);
}
if (this.exports.has(importName)) {
if (this.names.has(globalName))
this.error(DiagnosticCode.Duplicate_identifier_0, queued.declaration.identifier.range, globalName);
if (this.names.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, queuedImport.declaration.identifier.range, internalName);
else {
const declaration: DeclarationStatement = <DeclarationStatement>this.exports.get(importName);
this.names.set(globalName, declaration);
this.names.set(internalName, declaration);
// TODO: also mirror (non-private) member names?
// wouldn't it be better to look up members based on their parent?
}
} else
this.error(DiagnosticCode.Cannot_find_name_0, queued.declaration.externalIdentifier.range, importName);
this.error(DiagnosticCode.Cannot_find_name_0, queuedImport.declaration.externalIdentifier.range, importName);
}
}
private initializeClass(declaration: ClassDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
const members: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
@ -182,87 +175,91 @@ export class Program extends DiagnosticEmitter {
}
private initializeField(declaration: FieldDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
}
private initializeEnum(declaration: EnumDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
const members: EnumValueDeclaration[] = declaration.members;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.initializeEnumValue(members[i]);
}
private initializeEnumValue(declaration: EnumValueDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
}
private initializeExports(statement: ExportStatement, exportsMap: Map<string,QueuedExport>): void {
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
const members: ExportMember[] = statement.members;
const normalizedPath: string | null = statement.path == null ? null : normalizePath(<string>statement.path);
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.initializeExport(members[i], normalizedPath, exportsMap);
this.initializeExport(members[i], statement.normalizedPath, queuedExports);
}
private initializeExport(member: ExportMember, normalizedPath: string | null, exportsMap: Map<string,QueuedExport>): void {
private initializeExport(member: ExportMember, normalizedPath: string | null, queuedExports: Map<string,QueuedExport>): void {
const exportName: string = member.range.source.normalizedPath + "/" + member.externalIdentifier.name;
if (exportsMap.has(exportName))
if (queuedExports.has(exportName))
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
else {
const queued: QueuedExport = new QueuedExport();
queued.importName = normalizedPath == null
const queuedExport: QueuedExport = new QueuedExport();
queuedExport.importName = normalizedPath == null
? member.range.source.normalizedPath + "/" + member.identifier.name // local
: (<string>normalizedPath) + "/" + member.identifier.name; // re-export
queued.member = member;
exportsMap.set(exportName, queued);
queuedExport.member = member;
queuedExports.set(exportName, queuedExport);
}
}
private initializeFunction(declaration: FunctionDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
}
private initializeImports(statement: ImportStatement, exportsMap: Map<string,QueuedExport>, importsQueue: QueuedImport[]): void {
private initializeImports(statement: ImportStatement, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
const members: ImportDeclaration[] = statement.declarations;
const normalizedPath: string = normalizePath(statement.path);
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
const declaration: ImportDeclaration = members[i];
this.initializeImport(declaration, normalizedPath, exportsMap, importsQueue);
this.initializeImport(declaration, statement.normalizedPath, queuedExports, queuedImports);
}
}
private initializeImport(declaration: ImportDeclaration, normalizedPath: string, exportsMap: Map<string,QueuedExport>, importsQueue: QueuedImport[]): void {
private initializeImport(declaration: ImportDeclaration, normalizedPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
const importName: string = normalizedPath + "/" + declaration.externalIdentifier.name;
let resolvedImportName: string = importName;
while (exportsMap.has(resolvedImportName))
resolvedImportName = (<QueuedExport>exportsMap.get(resolvedImportName)).importName;
const globalName: string = this.mangleName(declaration);
const seen: Set<QueuedExport> = new Set();
while (queuedExports.has(resolvedImportName)) {
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(resolvedImportName);
resolvedImportName = queuedExport.importName;
if (seen.has(queuedExport))
break;
seen.add(queuedExport);
}
const internalName: string = this.mangleInternalName(declaration);
if (this.exports.has(resolvedImportName)) { // resolvable right away
if (this.names.has(globalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName);
if (this.names.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.names.set(globalName, <DeclarationStatement>this.exports.get(resolvedImportName));
this.names.set(internalName, <DeclarationStatement>this.exports.get(resolvedImportName));
} else { // points to yet unresolved export
const queued: QueuedImport = new QueuedImport();
queued.globalName = globalName;
queued.importName = importName;
queued.declaration = declaration;
importsQueue.push(queued);
const queuedImport: QueuedImport = new QueuedImport();
queuedImport.internalName = internalName;
queuedImport.importName = importName;
queuedImport.declaration = declaration;
queuedImports.push(queuedImport);
}
}
private initializeInterface(declaration: InterfaceDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
const members: Statement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
@ -283,15 +280,15 @@ export class Program extends DiagnosticEmitter {
}
private initializeMethod(declaration: MethodDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
}
private initializeNamespace(declaration: NamespaceDeclaration): void {
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
const members: Statement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
@ -327,33 +324,33 @@ export class Program extends DiagnosticEmitter {
}
}
private initializeVariables(statement: VariableStatement, insideNamespace: bool = false): void {
const declarations: VariableDeclaration[] = statement.members;
const isExport: bool = !insideNamespace && hasModifier(ModifierKind.EXPORT, statement.modifiers);
private initializeVariables(statement: VariableStatement, isNamespaceMember: bool = false): void {
const declarations: VariableDeclaration[] = statement.declarations;
const isExport: bool = !isNamespaceMember && hasModifier(ModifierKind.EXPORT, statement.modifiers);
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
const declaration: VariableDeclaration = declarations[i];
const globalName: string = this.mangleName(declaration);
this.addName(globalName, declaration);
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
if (isExport)
this.addExport(globalName, declaration);
this.addExport(/* same as */internalName, declaration);
}
}
private addName(globalName: string, declaration: DeclarationStatement): void {
if (this.names.has(globalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName); // recoverable
private addName(internalName: string, declaration: DeclarationStatement): void {
if (this.names.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); // recoverable
else
this.names.set(globalName, declaration);
this.names.set(internalName, declaration);
}
private addExport(globalName: string, declaration: DeclarationStatement): void {
if (this.exports.has(globalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName); // recoverable
private addExport(exportName: string, declaration: DeclarationStatement): void {
if (this.exports.has(exportName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, exportName); // recoverable
else
this.exports.set(globalName, declaration);
this.exports.set(exportName, declaration);
}
mangleName(declaration: DeclarationStatement): string {
mangleInternalName(declaration: DeclarationStatement): string {
let name: string = declaration.identifier.name;
let parent: Node | null = declaration.parent;
if (parent) {
@ -367,28 +364,28 @@ export class Program extends DiagnosticEmitter {
(declaration.kind == NodeKind.FIELD && !hasModifier(ModifierKind.STATIC, (<FieldDeclaration>declaration).modifiers)) ||
(declaration.kind == NodeKind.METHOD && !hasModifier(ModifierKind.STATIC, (<MethodDeclaration>declaration).modifiers))
)
return this.mangleName(<DeclarationStatement>parent) + "#" + name;
return this.mangleInternalName(<DeclarationStatement>parent) + "#" + name;
// otherwise fall through
}
case NodeKind.ENUM:
case NodeKind.ENUMVALUE:
case NodeKind.NAMESPACE:
return this.mangleName(<DeclarationStatement>parent) + "." + name;
return this.mangleInternalName(<DeclarationStatement>parent) + "." + name;
case NodeKind.IMPORT: {
const impParent: Node | null = (<ImportStatement>parent).parent;
if (impParent && impParent.kind == NodeKind.SOURCE)
return (<SourceNode>impParent).path + "/" + name;
const importParent: Node | null = (<ImportStatement>parent).parent;
if (importParent && importParent.kind == NodeKind.SOURCE)
return (<Source>importParent).path + "/" + name;
break;
}
case NodeKind.VARIABLE: {
const varParent: Node | null = (<VariableStatement>parent).parent;
if (varParent) {
if (varParent.kind == NodeKind.SOURCE)
return <SourceNode>varParent == this.sources[0] ? name : (<SourceNode>varParent).path + "/" + name;
if (varParent.kind == NodeKind.NAMESPACE)
return this.mangleName(<DeclarationStatement>varParent) + "." + name;
const variableParent: Node | null = (<VariableStatement>parent).parent;
if (variableParent) {
if (variableParent.kind == NodeKind.SOURCE)
return <Source>variableParent == this.sources[0] ? name : (<Source>variableParent).path + "/" + name;
if (variableParent.kind == NodeKind.NAMESPACE)
return this.mangleInternalName(<DeclarationStatement>variableParent) + "." + name;
}
break;
}
@ -396,34 +393,9 @@ export class Program extends DiagnosticEmitter {
}
throw new Error("unexpected parent");
}
addClass(cl: Class): void {
cl.declaration.reflectionIndex = this.classes.length;
this.classes.push(cl);
}
addEnum(en: Enum): void {
en.declaration.reflectionIndex = this.enums.length;
this.enums.push(en);
}
addFunction(fn: Function): void {
fn.declaration.reflectionIndex = this.functions.length;
this.functions.push(fn);
}
addGlobal(gl: GlobalVariable): void {
gl.declaration.reflectionIndex = this.globals.length;
this.globals.push(gl);
}
addNamespace(ns: Namespace): void {
ns.declaration.reflectionIndex = this.namespaces.length;
this.namespaces.push(ns);
}
}
function initializeBasicTypes(types: Map<string,Type>, target: Target) {
function initializeBasicTypes(types: Map<string,Type>, target: Target): void {
types.set("i8", Type.i8);
types.set("i16", Type.i16);
types.set("i32", Type.i32);
@ -435,5 +407,7 @@ function initializeBasicTypes(types: Map<string,Type>, target: Target) {
types.set("u64", Type.u64);
types.set("usize", target == Target.WASM64 ? Type.usize64 : Type.usize32);
types.set("bool", Type.bool);
types.set("f32", Type.f32);
types.set("f64", Type.f64);
types.set("void", Type.void);
}

View File

@ -1,310 +0,0 @@
/*
Reflection objects are largely independent of their respective declarations in
order to make it easier to introduce internal objects.
Base
Class
Enum
Field
Function
Method
Import
Namespace
Source
Type ~ TypeKind
VariableBase
GlobalVariable
LocalVariable
*/
import {
ClassDeclaration,
DeclarationStatement,
EnumDeclaration,
Expression,
FunctionDeclaration,
ImportDeclaration,
ImportStatement,
MethodDeclaration,
ModifierKind,
NamespaceDeclaration,
Node,
FieldDeclaration,
SourceNode,
Statement,
NodeKind,
TypeParameter,
VariableDeclaration,
VariableStatement
} from "./ast";
import { DiagnosticMessage } from "./diagnostics";
import { Token, Tokenizer, Range } from "./tokenizer";
import { hasModifier } from "./parser";
import { normalizePath, trimExtension } from "./util";
export abstract class Base {
name: string;
exportName: string | null = null;
constructor(name: string) {
this.name = name;
}
get isExport(): bool { return this.exportName != null; }
exportAs(exportName: string): this {
this.exportName = exportName;
return this;
}
}
export const enum TypeKind {
I8,
I16,
I32,
I64,
ISIZE,
U8,
U16,
U32,
U64,
USIZE,
F32,
F64,
BOOL,
VOID
}
export function typeKindToString(kind: TypeKind): string {
switch (kind) {
case TypeKind.I8: return "i8";
case TypeKind.I16: return "i16";
case TypeKind.I32: return "i32";
case TypeKind.I64: return "i64";
case TypeKind.ISIZE: return "isize";
case TypeKind.U8: return "u8";
case TypeKind.U16: return "u16";
case TypeKind.U32: return "u32";
case TypeKind.U64: return "u64";
case TypeKind.USIZE: return "usize";
case TypeKind.F32: return "f32";
case TypeKind.F64: return "f64";
case TypeKind.BOOL: return "bool";
case TypeKind.VOID: return "void";
}
return "invalid";
}
export class Type extends Base {
kind: TypeKind;
size: i32;
constructor(kind: TypeKind, size: i32) {
super(typeKindToString(kind));
this.kind = kind;
this.size = size;
}
get bitSize(): i32 { return this.size << 3; }
get smallIntegerShift(): i32 { return 32 - (this.size << 3); }
get smallIntegerMask(): i32 { return -1 >>> 32 - (this.size << 3); }
get isAnyInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.USIZE; }
get isSmallInteger(): bool { return this.size == 1 || this.size == 2; }
get isLongInteger(): bool { return this.size == 8 && this.kind != TypeKind.F64; }
get isUnsignedInteger(): bool { return this.kind >= TypeKind.U8 && this.kind <= TypeKind.USIZE; }
get isSignedInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.ISIZE; }
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
toString(): string {
return typeKindToString(this.kind);
}
static readonly i8: Type = new Type(TypeKind.I8, 1);
static readonly i16: Type = new Type(TypeKind.I16, 2);
static readonly i32: Type = new Type(TypeKind.I32, 4);
static readonly i64: Type = new Type(TypeKind.I64, 8);
static readonly isize32: Type = new Type(TypeKind.I32, 4);
static readonly isize64: Type = new Type(TypeKind.I64, 8);
static readonly u8: Type = new Type(TypeKind.U8, 1);
static readonly u16: Type = new Type(TypeKind.U16, 2);
static readonly u32: Type = new Type(TypeKind.U32, 4);
static readonly u64: Type = new Type(TypeKind.U64, 8);
static readonly usize32: Type = new Type(TypeKind.U32, 4);
static readonly usize64: Type = new Type(TypeKind.U64, 8);
static readonly f32: Type = new Type(TypeKind.F32, 4);
static readonly f64: Type = new Type(TypeKind.F64, 8);
static readonly bool: Type = new Type(TypeKind.BOOL, 1);
static readonly void: Type = new Type(TypeKind.VOID, 0);
}
export class Source extends SourceNode {
text: string;
tokenizer: Tokenizer | null = null;
statements: Statement[];
isEntry: bool;
normalizedPath: string;
constructor(path: string, text: string, isEntry: bool = false) {
super();
this.range = new Range(this, 0, text.length);
this.path = path;
this.text = text;
this.statements = new Array();
this.isEntry = isEntry;
this.normalizedPath = normalizePath(trimExtension(path));
}
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
}
export class Import extends Base {
declaration: ImportDeclaration | null;
externalName: string;
static create(declaration: ImportStatement): Import[] {
const count: i32 = declaration.declarations.length;
const imports: Import[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: ImportDeclaration = declaration.declarations[i];
const imprt: Import = new Import(decl.identifier.name);
imprt.declaration = decl;
imprt.externalName = decl.externalIdentifier.name;
imports[i] = imprt;
}
return imports;
}
}
export abstract class Variable extends Base {
type: Type;
}
export class GlobalVariable extends Variable {
declaration: VariableDeclaration;
mutable: bool;
static create(declaration: VariableStatement): GlobalVariable[] {
const mutable: bool = hasModifier(ModifierKind.CONST, declaration.modifiers);
const count: i32 = declaration.members.length;
const variables: GlobalVariable[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: VariableDeclaration = declaration.members[i];
const variable: GlobalVariable = new GlobalVariable(decl.identifier.name);
variable.declaration = decl;
variable.mutable = mutable;
variables[i] = variable;
}
return variables;
}
}
export class LocalVariable extends Variable {
declaration: VariableDeclaration;
index: i32;
static create(declaration: VariableStatement, index: i32): LocalVariable[] {
const count: i32 = declaration.members.length;
const variables: LocalVariable[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: VariableDeclaration = declaration.members[i];
const variable: LocalVariable = new LocalVariable(decl.identifier.name);
variable.declaration = decl;
variable.index = index;
variables[i] = variable;
}
return variables;
}
}
export class Namespace extends Base {
declaration: NamespaceDeclaration;
members: Base[];
static create(declaration: NamespaceDeclaration): Namespace {
const ns: Namespace = new Namespace(declaration.identifier.name);
ns.declaration = declaration;
const members: Base[] = ns.members = new Array();
// TODO: insert members
return ns;
}
}
export class Enum extends Base {
declaration: EnumDeclaration;
isConst: bool;
values: Map<string,Expression | null> = new Map();
static create(declaration: EnumDeclaration): Enum {
const enm: Enum = new Enum(declaration.identifier.name);
enm.declaration = declaration;
enm.isConst = hasModifier(ModifierKind.CONST, declaration.modifiers);
// TODO: insert values
return enm;
}
}
export class Function extends Base {
declaration: FunctionDeclaration;
typeArguments: Type[];
returnType: Type;
statements: Statement[];
static create(declaration: FunctionDeclaration, typeArguments: Type[]): Function {
throw new Error("not implemented");
}
}
export class Class extends Base {
declaration: ClassDeclaration;
typeArguments: Type[];
baseClass: Class | null;
memberNames: Set<string>;
methods: Map<string,Method>;
fields: Map<string,Field>;
static create(declaration: ClassDeclaration, typeArguments: Type[]): Class {
const clazz: Class = new Class(declaration.identifier.name);
clazz.typeArguments = typeArguments;
return clazz;
}
}
export class Method extends Function {
declaration: MethodDeclaration; // more specific
isInstance: bool;
static create(declaration: MethodDeclaration, typeArguments: Type[]): Method {
throw new Error("not implemented");
}
}
export class Field extends Base {
declaration: FieldDeclaration | null;
type: Type;
offset: i32;
initializer: Expression | null;
static create(declaration: FieldDeclaration, offset: i32): Field {
const field: Field = new Field(declaration.identifier.name);
field.declaration = declaration;
field.offset = offset;
field.initializer = declaration.initializer;
return field;
}
}

View File

@ -20,7 +20,7 @@
*/
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
import { Source } from "./reflection";
import { Source } from "./ast";
import { I64, CharCode, isLineBreak } from "./util";
export enum Token {

View File

@ -10,6 +10,7 @@
"node"
],
"downlevelIteration": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"alwaysStrict": true,
"outDir": "../out"
@ -28,7 +29,10 @@
"program.ts",
"reflection.ts",
"tokenizer.ts",
"types.ts",
"util.ts",
"util/i64.ts"
"util/charcode.ts",
"util/i64.ts",
"util/path.ts"
]
}

138
src/types.ts Normal file
View File

@ -0,0 +1,138 @@
export const enum TypeKind {
// signed integers
I8,
I16,
I32,
I64,
ISIZE,
// unsigned integers
U8,
U16,
U32,
U64,
USIZE,
BOOL, // sic
// floats
F32,
F64,
VOID
}
export class Type {
kind: TypeKind;
size: i32;
classType: ClassType | null;
nullable: bool = false;
nullableType: Type | null = null; // cached, of this type
constructor(kind: TypeKind, size: i32) {
this.kind = kind;
this.size = size;
this.classType = null;
}
get smallIntegerShift(): i32 { return 32 - this.size; }
get smallIntegerMask(): i32 { return -1 >>> (32 - this.size); }
get isAnyInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.BOOL; }
get isSmallInteger(): bool { return this.size != 0 && this.size < 32; }
get isLongInteger(): bool { return this.size == 64 && this.kind != TypeKind.F64; }
get isUnsignedInteger(): bool { return this.kind >= TypeKind.U8 && this.kind <= TypeKind.BOOL; }
get isSignedInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.ISIZE; }
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
asClass(classType: ClassType): Type {
const ret: Type = new Type(this.kind, this.size);
ret.classType = classType;
return ret;
}
asNullable(): Type {
if (!this.nullableType)
(this.nullableType = new Type(this.kind, this.size)).nullable = true;
return <Type>this.nullableType;
}
toString(): string {
switch (this.kind) {
case TypeKind.I8: return "i8";
case TypeKind.I16: return "i16";
case TypeKind.I32: return "i32";
case TypeKind.ISIZE: return "isize";
case TypeKind.U8: return "u8";
case TypeKind.U16: return "u16";
case TypeKind.U32: return "u32";
case TypeKind.U64: return "u64";
case TypeKind.USIZE: return "usize";
case TypeKind.BOOL: return "bool";
case TypeKind.F32: return "f32";
case TypeKind.F64: return "f64";
case TypeKind.VOID: return "void";
default:
throw new Error("unexpected type kind");
}
}
static readonly i8: Type = new Type(TypeKind.I8, 8);
static readonly i16: Type = new Type(TypeKind.I16, 16);
static readonly i32: Type = new Type(TypeKind.I32, 32);
static readonly i64: Type = new Type(TypeKind.I64, 64);
static readonly isize32: Type = new Type(TypeKind.I32, 32);
static readonly isize64: Type = new Type(TypeKind.I64, 64);
static readonly u8: Type = new Type(TypeKind.U8, 8);
static readonly u16: Type = new Type(TypeKind.U16, 16);
static readonly u32: Type = new Type(TypeKind.U32, 32);
static readonly u64: Type = new Type(TypeKind.U64, 64);
static readonly usize32: Type = new Type(TypeKind.U32, 32);
static readonly usize64: Type = new Type(TypeKind.U64, 64);
static readonly bool: Type = new Type(TypeKind.BOOL, 1);
static readonly f32: Type = new Type(TypeKind.F32, 32);
static readonly f64: Type = new Type(TypeKind.F64, 64);
static readonly void: Type = new Type(TypeKind.VOID, 0);
}
export class FunctionType {
returnType: Type;
parameterTypes: Type[];
additionalLocals: Type[];
typeArgumentsMap: Map<string, Type> | null = null;
private breakMajor: i32 = 0;
private breakMinor: i32 = 0;
constructor(returnType: Type, parameterTypes: Type[]) {
this.returnType = returnType;
this.parameterTypes = parameterTypes;
this.additionalLocals = new Array();
}
enterBreakContext(): string {
if (!this.breakMinor)
this.breakMajor++;
return this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
}
leaveBreakContext(): void {
if (--this.breakMinor < 0)
throw new Error("unexpected unbalanced break context");
}
}
export class ClassType {
type: Type;
typeArgumentsMap: Map<string, Type> | null = null;
base: ClassType | null;
constructor(usizeType: Type, base: ClassType | null = null) {
this.type = usizeType.asClass(this);
this.base = base;
}
}

View File

@ -1,235 +1,4 @@
export { I64, U64 } from "./util/i64";
export const enum CharCode {
NULL = 0,
LINEFEED = 0x0A,
CARRIAGERETURN = 0x0D,
LINESEPARATOR = 0x2028,
PARAGRAPHSEPARATOR = 0x2029,
NEXTLINE = 0x0085,
SPACE = 0x20,
NONBREAKINGSPACE = 0xA0,
ENQUAD = 0x2000,
EMQUAD = 0x2001,
ENSPACE = 0x2002,
EMSPACE = 0x2003,
THREEPEREMSPACE = 0x2004,
FOURPEREMSPACE = 0x2005,
SIXPEREMSPACE = 0x2006,
FIGURESPACE = 0x2007,
PUNCTUATIONSPACE = 0x2008,
THINSPACE = 0x2009,
HAIRSPACE = 0x200A,
ZEROWIDTHSPACE = 0x200B,
NARRINOBREAKSPACE = 0x202F,
IDEOGRAPHICSPACE = 0x3000,
MATHEMATICALSPACE = 0x205F,
OGHAM = 0x1680,
_ = 0x5F,
$ = 0x24,
_0 = 0x30,
_1 = 0x31,
_2 = 0x32,
_3 = 0x33,
_4 = 0x34,
_5 = 0x35,
_6 = 0x36,
_7 = 0x37,
_8 = 0x38,
_9 = 0x39,
a = 0x61,
b = 0x62,
c = 0x63,
d = 0x64,
e = 0x65,
f = 0x66,
g = 0x67,
h = 0x68,
i = 0x69,
j = 0x6A,
k = 0x6B,
l = 0x6C,
m = 0x6D,
n = 0x6E,
o = 0x6F,
p = 0x70,
q = 0x71,
r = 0x72,
s = 0x73,
t = 0x74,
u = 0x75,
v = 0x76,
w = 0x77,
x = 0x78,
y = 0x79,
z = 0x7A,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4A,
K = 0x4B,
L = 0x4C,
M = 0x4D,
N = 0x4E,
O = 0x4F,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5a,
AMPERSAND = 0x26,
ASTERISK = 0x2A,
AT = 0x40,
BACKSLASH = 0x5C,
BACKTICK = 0x60,
BAR = 0x7C,
CARET = 0x5E,
CLOSEBRACE = 0x7D,
CLOSEBRACKET = 0x5D,
CLOSEPAREN = 0x29,
COLON = 0x3A,
COMMA = 0x2C,
DOT = 0x2E,
DOUBLEQUOTE = 0x22,
EQUALS = 0x3D,
EXCLAMATION = 0x21,
GREATERTHAN = 0x3E,
HASH = 0x23,
LESSTHAN = 0x3C,
MINUS = 0x2D,
OPENBRACE = 0x7B,
OPENBRACKET = 0x5B,
OPENPAREN = 0x28,
PERCENT = 0x25,
PLUS = 0x2B,
QUESTION = 0x3F,
SEMICOLON = 0x3B,
SINGLEQUOTE = 0x27,
SLASH = 0x2F,
TILDE = 0x7E,
BACKSPACE = 0x08,
FORMFEED = 0x0C,
BYTEORDERMARK = 0xFEFF,
TAB = 0x09,
VERTICALTAB = 0x0B
}
export function isLineBreak(c: i32): bool {
return c == CharCode.LINEFEED
|| c == CharCode.CARRIAGERETURN
|| c == CharCode.LINESEPARATOR
|| c == CharCode.PARAGRAPHSEPARATOR;
}
export { CharCode, isLineBreak} from "./util/charcode";
export { normalize as normalizePath, resolve as resolvePath, dirname } from "./util/path";
export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb
export function normalizePath(path: string, separator: CharCode = CharCode.SLASH): string {
// expects a relative path
let pos: i32 = 0;
let len: i32 = path.length;
// trim leading './'
while (pos + 1 < len && path.charCodeAt(pos) == CharCode.DOT && path.charCodeAt(pos + 1) == separator)
pos += 2;
if (pos > 0) {
path = path.substring(pos);
len -= pos;
pos = 0;
}
let atEnd: bool;
while (pos + 1 < len) {
atEnd = false;
// we are only interested in '/.' sequences ...
if (path.charCodeAt(pos) == separator && path.charCodeAt(pos + 1) == CharCode.DOT) {
// '/.' ( '/' | $ )
if (
(atEnd = pos + 2 == len)
||
pos + 2 < len && path.charCodeAt(pos + 2) == separator
) {
path = atEnd
? path.substring(0, pos)
: path.substring(0, pos) + path.substring(pos + 2);
len -= 2;
continue;
}
// '/.' ( './' | '.' $ )
if (
(atEnd = pos + 3 == len) && path.charCodeAt(pos + 2) == CharCode.DOT
||
pos + 3 < len && path.charCodeAt(pos + 2) == CharCode.DOT && path.charCodeAt(pos + 3) == separator
) {
// find preceeding '/'
let ipos: i32 = pos;
while (--ipos >= 0) {
if (path.charCodeAt(ipos) == separator) {
if (pos - ipos != 3 || path.charCodeAt(ipos + 1) != CharCode.DOT || path.charCodeAt(ipos + 2) != CharCode.DOT) { // exclude '..' itself
path = atEnd
? path.substring(0, ipos)
: path.substring(0, ipos) + path.substring(pos + 3);
len -= pos + 3 - ipos;
pos = ipos - 1; // incremented again at end of loop
}
break;
}
}
// if there's no preceeding '/', trim start if non-empty
if (ipos < 0 && pos > 0) {
if (pos != 2 || path.charCodeAt(0) != CharCode.DOT || path.charCodeAt(1) != CharCode.DOT) { // exclude '..' itself
path = path.substring(pos + 4);
len = path.length;
continue;
}
}
}
}
pos++;
}
return len > 0 ? path : ".";
}
export function dirname(normalizedPath: string, separator: CharCode = CharCode.SLASH): string {
let pos: i32 = normalizedPath.length;
while (--pos > 0)
if (normalizedPath.charCodeAt(pos) == separator)
return normalizedPath.substring(0, pos);
return ".";
}
export function resolvePath(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
return normalizePath(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
}
export function trimExtension(path: string): string {
const len: i32 = path.length;
if (len > 3 && path.charCodeAt(len - 3) == CharCode.DOT && (path.charCodeAt(len - 2) == CharCode.t || path.charCodeAt(len - 2) == CharCode.a) && path.charCodeAt(len - 1) == CharCode.s)
return path.substring(0, len - 3);
return path;
}

140
src/util/charcode.ts Normal file
View File

@ -0,0 +1,140 @@
export const enum CharCode {
NULL = 0,
LINEFEED = 0x0A,
CARRIAGERETURN = 0x0D,
LINESEPARATOR = 0x2028,
PARAGRAPHSEPARATOR = 0x2029,
NEXTLINE = 0x0085,
SPACE = 0x20,
NONBREAKINGSPACE = 0xA0,
ENQUAD = 0x2000,
EMQUAD = 0x2001,
ENSPACE = 0x2002,
EMSPACE = 0x2003,
THREEPEREMSPACE = 0x2004,
FOURPEREMSPACE = 0x2005,
SIXPEREMSPACE = 0x2006,
FIGURESPACE = 0x2007,
PUNCTUATIONSPACE = 0x2008,
THINSPACE = 0x2009,
HAIRSPACE = 0x200A,
ZEROWIDTHSPACE = 0x200B,
NARRINOBREAKSPACE = 0x202F,
IDEOGRAPHICSPACE = 0x3000,
MATHEMATICALSPACE = 0x205F,
OGHAM = 0x1680,
_ = 0x5F,
$ = 0x24,
_0 = 0x30,
_1 = 0x31,
_2 = 0x32,
_3 = 0x33,
_4 = 0x34,
_5 = 0x35,
_6 = 0x36,
_7 = 0x37,
_8 = 0x38,
_9 = 0x39,
a = 0x61,
b = 0x62,
c = 0x63,
d = 0x64,
e = 0x65,
f = 0x66,
g = 0x67,
h = 0x68,
i = 0x69,
j = 0x6A,
k = 0x6B,
l = 0x6C,
m = 0x6D,
n = 0x6E,
o = 0x6F,
p = 0x70,
q = 0x71,
r = 0x72,
s = 0x73,
t = 0x74,
u = 0x75,
v = 0x76,
w = 0x77,
x = 0x78,
y = 0x79,
z = 0x7A,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4A,
K = 0x4B,
L = 0x4C,
M = 0x4D,
N = 0x4E,
O = 0x4F,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5a,
AMPERSAND = 0x26,
ASTERISK = 0x2A,
AT = 0x40,
BACKSLASH = 0x5C,
BACKTICK = 0x60,
BAR = 0x7C,
CARET = 0x5E,
CLOSEBRACE = 0x7D,
CLOSEBRACKET = 0x5D,
CLOSEPAREN = 0x29,
COLON = 0x3A,
COMMA = 0x2C,
DOT = 0x2E,
DOUBLEQUOTE = 0x22,
EQUALS = 0x3D,
EXCLAMATION = 0x21,
GREATERTHAN = 0x3E,
HASH = 0x23,
LESSTHAN = 0x3C,
MINUS = 0x2D,
OPENBRACE = 0x7B,
OPENBRACKET = 0x5B,
OPENPAREN = 0x28,
PERCENT = 0x25,
PLUS = 0x2B,
QUESTION = 0x3F,
SEMICOLON = 0x3B,
SINGLEQUOTE = 0x27,
SLASH = 0x2F,
TILDE = 0x7E,
BACKSPACE = 0x08,
FORMFEED = 0x0C,
BYTEORDERMARK = 0xFEFF,
TAB = 0x09,
VERTICALTAB = 0x0B
}
export function isLineBreak(c: CharCode): bool {
return c == CharCode.LINEFEED
|| c == CharCode.CARRIAGERETURN
|| c == CharCode.LINESEPARATOR
|| c == CharCode.PARAGRAPHSEPARATOR;
}

90
src/util/path.ts Normal file
View File

@ -0,0 +1,90 @@
import { CharCode } from "./charcode";
export function normalize(path: string, trimExtension: bool = false, separator: CharCode = CharCode.SLASH): string {
// expects a relative path
let pos: i32 = 0;
let len: i32 = path.length;
// trim leading './'
while (pos + 1 < len && path.charCodeAt(pos) == CharCode.DOT && path.charCodeAt(pos + 1) == separator)
pos += 2;
// trim extension if requested
if (trimExtension && len > pos + 3 && path.charCodeAt(len - 3) == CharCode.DOT && (path.charCodeAt(len - 2) == CharCode.t || path.charCodeAt(len - 2) == CharCode.a) && path.charCodeAt(len - 1) == CharCode.s)
len = len - 3;
if (pos > 0 || len < path.length) {
path = path.substring(pos, len);
len -= pos;
pos = 0;
}
let atEnd: bool;
while (pos + 1 < len) {
atEnd = false;
// we are only interested in '/.' sequences ...
if (path.charCodeAt(pos) == separator && path.charCodeAt(pos + 1) == CharCode.DOT) {
// '/.' ( '/' | $ )
if (
(atEnd = pos + 2 == len)
||
pos + 2 < len && path.charCodeAt(pos + 2) == separator
) {
path = atEnd
? path.substring(0, pos)
: path.substring(0, pos) + path.substring(pos + 2);
len -= 2;
continue;
}
// '/.' ( './' | '.' $ )
if (
(atEnd = pos + 3 == len) && path.charCodeAt(pos + 2) == CharCode.DOT
||
pos + 3 < len && path.charCodeAt(pos + 2) == CharCode.DOT && path.charCodeAt(pos + 3) == separator
) {
// find preceeding '/'
let ipos: i32 = pos;
while (--ipos >= 0) {
if (path.charCodeAt(ipos) == separator) {
if (pos - ipos != 3 || path.charCodeAt(ipos + 1) != CharCode.DOT || path.charCodeAt(ipos + 2) != CharCode.DOT) { // exclude '..' itself
path = atEnd
? path.substring(0, ipos)
: path.substring(0, ipos) + path.substring(pos + 3);
len -= pos + 3 - ipos;
pos = ipos - 1; // incremented again at end of loop
}
break;
}
}
// if there's no preceeding '/', trim start if non-empty
if (ipos < 0 && pos > 0) {
if (pos != 2 || path.charCodeAt(0) != CharCode.DOT || path.charCodeAt(1) != CharCode.DOT) { // exclude '..' itself
path = path.substring(pos + 4);
len = path.length;
continue;
}
}
}
}
pos++;
}
return len > 0 ? path : ".";
}
export function resolve(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
return normalize(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
}
export function dirname(normalizedPath: string, separator: CharCode = CharCode.SLASH): string {
let pos: i32 = normalizedPath.length;
while (--pos > 0)
if (normalizedPath.charCodeAt(pos) == separator)
return normalizedPath.substring(0, pos);
return ".";
}

View File

@ -3,10 +3,10 @@ import { Compiler } from "../src/compiler";
import { Parser } from "../src/parser";
const files: Map<string,string> = new Map([
["main", `import { Test } from "./a"; export { TestAlias } from "./d";`],
["main", `import { Test as TestAlias } from "./a"; export { TestAlias } from "./d"; if (1) {} export const a: i32 = 123;`],
["a", `export { Test } from "./b";`],
["b", `export { Test } from "./c";`],
["c", `export enum Test { ONE = 1 }`],
["c", `export enum Test { ONE = 1, TWO = 2 }`],
["d", `export { Test as TestAlias } from "./b";`]
]);
@ -24,14 +24,14 @@ const program = parser.finish();
const compiler = new Compiler(program);
const module = compiler.compile();
console.log("names", program.names);
console.log("exports", program.exports);
console.log("names", program.names.keys());
console.log("exports", program.exports.keys());
// module.optimize();
// module.validate(); // global initializers can't use i32.add etc. yet
/* _BinaryenModulePrint(module.ref);
_BinaryenModulePrint(module.ref);
console.log("--- statements ---");
/* console.log("--- statements ---");
compiler.statements.forEach(stmt => {
_BinaryenExpressionPrint(stmt);
}); */

View File

@ -2,3 +2,6 @@ function simple(): void {
}
function typeparams<T, V extends T>(a: V | null = null): void {
}
@decorator()
function withdecorator(): void {
}