mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Progress; Restructuring
This commit is contained in:
parent
e14d02e040
commit
f55fc70220
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
node_modules/
|
||||
npm-debug.*
|
||||
out/
|
||||
raw/
|
||||
|
||||
|
186
src/ast.ts
186
src/ast.ts
@ -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
2
src/binaryen.d.ts
vendored
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
375
src/compiler.ts
375
src/compiler.ts
@ -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,62 +126,61 @@ 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
|
||||
case NodeKind.EXPORT: {
|
||||
this.compileExports(<ExportStatement>statement);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
this.statements.push(this.compileStatement(statement));
|
||||
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
|
||||
const initial: U64 = this.memoryOffset.clone();
|
||||
const initialOverlaps: U64 = initial.clone();
|
||||
@ -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);
|
||||
@ -939,12 +1033,29 @@ export class Compiler extends DiagnosticEmitter {
|
||||
op = UnaryOp.NegF64;
|
||||
else
|
||||
return this.currentType.isLongInteger
|
||||
? this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand)
|
||||
: this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand);
|
||||
? this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand)
|
||||
: 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);
|
||||
@ -965,8 +1076,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.TILDE:
|
||||
operand = this.compileExpression(expression.expression, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||
return this.currentType.isLongInteger
|
||||
? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1))
|
||||
: this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1));
|
||||
? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1))
|
||||
: this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1));
|
||||
|
||||
default:
|
||||
throw new Error("not implemented");
|
||||
@ -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("");
|
||||
}
|
||||
|
@ -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 "";
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
126
src/parser.ts
126
src/parser.ts
@ -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,9 +75,7 @@ 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);
|
||||
this.seenlog.add(normalizedPath);
|
||||
|
||||
const source: Source = new Source(path, text, isEntry);
|
||||
this.program.sources.push(source);
|
||||
@ -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
|
||||
|
256
src/program.ts
256
src/program.ts
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
138
src/types.ts
Normal 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;
|
||||
}
|
||||
}
|
235
src/util.ts
235
src/util.ts
@ -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
140
src/util/charcode.ts
Normal 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
90
src/util/path.ts
Normal 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 ".";
|
||||
}
|
@ -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);
|
||||
}); */
|
@ -2,3 +2,6 @@ function simple(): void {
|
||||
}
|
||||
function typeparams<T, V extends T>(a: V | null = null): void {
|
||||
}
|
||||
@decorator()
|
||||
function withdecorator(): void {
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user