mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-13 15:01:28 +00:00
Cleanup; Initial switch support; Relooper interface fwiw
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,3 @@ node_modules/
|
|||||||
npm-debug.*
|
npm-debug.*
|
||||||
out/
|
out/
|
||||||
raw/
|
raw/
|
||||||
|
|
||||||
|
169
src/ast.ts
169
src/ast.ts
@ -404,7 +404,7 @@ export abstract class Expression extends Node {
|
|||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract serializeAsTree(sb: string[], indent: i32);
|
abstract serializeAsTree(sb: string[], indent: i32): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum LiteralKind {
|
export const enum LiteralKind {
|
||||||
@ -541,10 +541,10 @@ export class CallExpression extends Expression {
|
|||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
this.expression.serialize(sb);
|
this.expression.serialize(sb);
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32 = this.typeArguments.length;
|
||||||
if (this.typeArguments.length) {
|
if (k) {
|
||||||
sb.push("<");
|
sb.push("<");
|
||||||
for (i = 0, k = this.typeArguments.length; i < k; ++i) {
|
for (i = 0; i < k; ++i) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
sb.push(", ");
|
sb.push(", ");
|
||||||
this.typeArguments[i].serialize(sb);
|
this.typeArguments[i].serialize(sb);
|
||||||
@ -935,14 +935,14 @@ export abstract class Statement extends Node {
|
|||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createExport(modifiers: Modifier[], members: ExportMember[], path: string | null, range: Range): ExportStatement {
|
static createExport(modifiers: Modifier[], members: ExportMember[], path: StringLiteralExpression | null, range: Range): ExportStatement {
|
||||||
const stmt: ExportStatement = new ExportStatement();
|
const stmt: ExportStatement = new ExportStatement();
|
||||||
stmt.range = range;
|
stmt.range = range;
|
||||||
let i: i32, k: i32;
|
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.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;
|
for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt;
|
||||||
stmt.path = path;
|
stmt.path = path;
|
||||||
stmt.normalizedPath = path == null ? null : resolvePath(normalizePath(<string>path), range.source.normalizedPath);
|
stmt.normalizedPath = path ? resolvePath(normalizePath(path.value), range.source.normalizedPath) : null;
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -978,12 +978,12 @@ export abstract class Statement extends Node {
|
|||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createImport(declarations: ImportDeclaration[], path: string, range: Range): ImportStatement {
|
static createImport(declarations: ImportDeclaration[], path: StringLiteralExpression, range: Range): ImportStatement {
|
||||||
const stmt: ImportStatement = new ImportStatement();
|
const stmt: ImportStatement = new ImportStatement();
|
||||||
stmt.range = range;
|
stmt.range = range;
|
||||||
for (let i: i32 = 0, k: i32 = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt;
|
for (let i: i32 = 0, k: i32 = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt;
|
||||||
stmt.path = path;
|
stmt.path = path;
|
||||||
stmt.normalizedPath = resolvePath(normalizePath(path), range.source.normalizedPath);
|
stmt.normalizedPath = resolvePath(normalizePath(path.value), range.source.normalizedPath);
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1196,7 +1196,17 @@ export class Source extends Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class DeclarationStatement extends Statement {
|
export abstract class DeclarationStatement extends Statement {
|
||||||
|
|
||||||
identifier: IdentifierExpression;
|
identifier: IdentifierExpression;
|
||||||
|
modifiers: Modifier[] | null;
|
||||||
|
private _cachedInternalName: string | null = null;
|
||||||
|
globalExportName: string | null = null;
|
||||||
|
|
||||||
|
get internalName(): string {
|
||||||
|
if (this._cachedInternalName == null)
|
||||||
|
this._cachedInternalName = mangleInternalName(this);
|
||||||
|
return this._cachedInternalName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BlockStatement extends Statement {
|
export class BlockStatement extends Statement {
|
||||||
@ -1225,7 +1235,7 @@ export class BreakStatement extends Statement {
|
|||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
if (this.label) {
|
if (this.label) {
|
||||||
sb.push("break ");
|
sb.push("break ");
|
||||||
sb.push((<IdentifierExpression>this.label).name);
|
(<IdentifierExpression>this.label).serialize(name);
|
||||||
} else
|
} else
|
||||||
sb.push("break");
|
sb.push("break");
|
||||||
}
|
}
|
||||||
@ -1234,7 +1244,6 @@ export class BreakStatement extends Statement {
|
|||||||
export class ClassDeclaration extends DeclarationStatement {
|
export class ClassDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.CLASS;
|
kind = NodeKind.CLASS;
|
||||||
modifiers: Modifier[];
|
|
||||||
typeParameters: TypeParameter[];
|
typeParameters: TypeParameter[];
|
||||||
extendsType: TypeNode | null;
|
extendsType: TypeNode | null;
|
||||||
implementsTypes: TypeNode[];
|
implementsTypes: TypeNode[];
|
||||||
@ -1247,10 +1256,11 @@ export class ClassDeclaration extends DeclarationStatement {
|
|||||||
this.decorators[i].serialize(sb);
|
this.decorators[i].serialize(sb);
|
||||||
sb.push("\n");
|
sb.push("\n");
|
||||||
}
|
}
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("class ");
|
sb.push("class ");
|
||||||
sb.push(this.identifier.name);
|
sb.push(this.identifier.name);
|
||||||
if (this.typeParameters.length) {
|
if (this.typeParameters.length) {
|
||||||
@ -1294,7 +1304,7 @@ export class ContinueStatement extends Statement {
|
|||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
if (this.label) {
|
if (this.label) {
|
||||||
sb.push("continue ");
|
sb.push("continue ");
|
||||||
sb.push(this.label.name);
|
(<IdentifierExpression>this.label).serialize(sb);
|
||||||
} else
|
} else
|
||||||
sb.push("continue");
|
sb.push("continue");
|
||||||
}
|
}
|
||||||
@ -1347,15 +1357,15 @@ export class EmptyStatement extends Statement {
|
|||||||
export class EnumDeclaration extends DeclarationStatement {
|
export class EnumDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.ENUM;
|
kind = NodeKind.ENUM;
|
||||||
modifiers: Modifier[];
|
|
||||||
members: EnumValueDeclaration[];
|
members: EnumValueDeclaration[];
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32;
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("enum ");
|
sb.push("enum ");
|
||||||
this.identifier.serialize(sb);
|
this.identifier.serialize(sb);
|
||||||
sb.push(" {\n");
|
sb.push(" {\n");
|
||||||
@ -1371,6 +1381,7 @@ export class EnumDeclaration extends DeclarationStatement {
|
|||||||
export class EnumValueDeclaration extends DeclarationStatement {
|
export class EnumValueDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.ENUMVALUE;
|
kind = NodeKind.ENUMVALUE;
|
||||||
|
modifiers = null;
|
||||||
value: Expression | null;
|
value: Expression | null;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
@ -1414,29 +1425,29 @@ export class ExportMember extends Node {
|
|||||||
export class ExportStatement extends Statement {
|
export class ExportStatement extends Statement {
|
||||||
|
|
||||||
kind = NodeKind.EXPORT;
|
kind = NodeKind.EXPORT;
|
||||||
modifiers: Modifier[];
|
modifiers: Modifier[] | null;
|
||||||
members: ExportMember[];
|
members: ExportMember[];
|
||||||
path: string | null;
|
path: StringLiteralExpression | null;
|
||||||
normalizedPath: string | null;
|
normalizedPath: string | null;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32;
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("export {\n");
|
sb.push("export {\n");
|
||||||
for (i = 0, k = this.members.length; i < k; ++i) {
|
for (i = 0, k = this.members.length; i < k; ++i) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
sb.push(",\n");
|
sb.push(",\n");
|
||||||
this.members[i].serialize(sb);
|
this.members[i].serialize(sb);
|
||||||
}
|
}
|
||||||
if (this.path == null)
|
if (this.path) {
|
||||||
sb.push("\n}");
|
|
||||||
else {
|
|
||||||
sb.push("\n} from ");
|
sb.push("\n} from ");
|
||||||
sb.push(JSON.stringify(this.path));
|
this.path.serialize(sb);
|
||||||
}
|
} else
|
||||||
|
sb.push("\n}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1453,7 +1464,6 @@ export class ExpressionStatement extends Statement {
|
|||||||
export class FieldDeclaration extends DeclarationStatement {
|
export class FieldDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.FIELD;
|
kind = NodeKind.FIELD;
|
||||||
modifiers: Modifier[];
|
|
||||||
type: TypeNode | null;
|
type: TypeNode | null;
|
||||||
initializer: Expression | null;
|
initializer: Expression | null;
|
||||||
decorators: DecoratorStatement[];
|
decorators: DecoratorStatement[];
|
||||||
@ -1464,10 +1474,11 @@ export class FieldDeclaration extends DeclarationStatement {
|
|||||||
this.decorators[i].serialize(sb);
|
this.decorators[i].serialize(sb);
|
||||||
sb.push("\n");
|
sb.push("\n");
|
||||||
}
|
}
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
this.identifier.serialize(sb);
|
this.identifier.serialize(sb);
|
||||||
if (this.type) {
|
if (this.type) {
|
||||||
sb.push(": ");
|
sb.push(": ");
|
||||||
@ -1510,7 +1521,6 @@ export class ForStatement extends Statement {
|
|||||||
export class FunctionDeclaration extends DeclarationStatement {
|
export class FunctionDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.FUNCTION;
|
kind = NodeKind.FUNCTION;
|
||||||
modifiers: Modifier[];
|
|
||||||
typeParameters: TypeParameter[];
|
typeParameters: TypeParameter[];
|
||||||
parameters: Parameter[];
|
parameters: Parameter[];
|
||||||
returnType: TypeNode | null;
|
returnType: TypeNode | null;
|
||||||
@ -1523,10 +1533,11 @@ export class FunctionDeclaration extends DeclarationStatement {
|
|||||||
this.decorators[i].serialize(sb);
|
this.decorators[i].serialize(sb);
|
||||||
sb.push("\n");
|
sb.push("\n");
|
||||||
}
|
}
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("function ");
|
sb.push("function ");
|
||||||
this.serializeCommon(sb);
|
this.serializeCommon(sb);
|
||||||
}
|
}
|
||||||
@ -1596,6 +1607,7 @@ export class IfStatement extends Statement {
|
|||||||
export class ImportDeclaration extends DeclarationStatement {
|
export class ImportDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.IMPORTDECLARATION;
|
kind = NodeKind.IMPORTDECLARATION;
|
||||||
|
modifiers = null;
|
||||||
externalIdentifier: IdentifierExpression;
|
externalIdentifier: IdentifierExpression;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
@ -1611,7 +1623,7 @@ export class ImportStatement extends Statement {
|
|||||||
|
|
||||||
kind = NodeKind.IMPORT;
|
kind = NodeKind.IMPORT;
|
||||||
declarations: ImportDeclaration[];
|
declarations: ImportDeclaration[];
|
||||||
path: string;
|
path: StringLiteralExpression;
|
||||||
normalizedPath: string;
|
normalizedPath: string;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
@ -1622,24 +1634,24 @@ export class ImportStatement extends Statement {
|
|||||||
this.declarations[i].serialize(sb);
|
this.declarations[i].serialize(sb);
|
||||||
}
|
}
|
||||||
sb.push("\n} from ");
|
sb.push("\n} from ");
|
||||||
sb.push(JSON.stringify(this.path));
|
this.path.serialize(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InterfaceDeclaration extends DeclarationStatement {
|
export class InterfaceDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.INTERFACE;
|
kind = NodeKind.INTERFACE;
|
||||||
modifiers: Modifier[];
|
|
||||||
typeParameters: TypeParameter[];
|
typeParameters: TypeParameter[];
|
||||||
extendsType: TypeNode | null;
|
extendsType: TypeNode | null;
|
||||||
members: Statement[];
|
members: Statement[];
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32;
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("interface ");
|
sb.push("interface ");
|
||||||
this.identifier.serialize(sb);
|
this.identifier.serialize(sb);
|
||||||
if (this.typeParameters.length) {
|
if (this.typeParameters.length) {
|
||||||
@ -1677,10 +1689,11 @@ export class MethodDeclaration extends FunctionDeclaration {
|
|||||||
this.decorators[i].serialize(sb);
|
this.decorators[i].serialize(sb);
|
||||||
sb.push("\n");
|
sb.push("\n");
|
||||||
}
|
}
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
super.serializeCommon(sb);
|
super.serializeCommon(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1688,15 +1701,15 @@ export class MethodDeclaration extends FunctionDeclaration {
|
|||||||
export class NamespaceDeclaration extends DeclarationStatement {
|
export class NamespaceDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.NAMESPACE;
|
kind = NodeKind.NAMESPACE;
|
||||||
modifiers: Modifier[];
|
|
||||||
members: Statement[];
|
members: Statement[];
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32;
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
}
|
sb.push(" ");
|
||||||
|
}
|
||||||
sb.push("namespace ");
|
sb.push("namespace ");
|
||||||
this.identifier.serialize(sb);
|
this.identifier.serialize(sb);
|
||||||
sb.push(" {\n");
|
sb.push(" {\n");
|
||||||
@ -1862,6 +1875,7 @@ export class TryStatement extends Statement {
|
|||||||
export class VariableDeclaration extends DeclarationStatement {
|
export class VariableDeclaration extends DeclarationStatement {
|
||||||
|
|
||||||
kind = NodeKind.VARIABLEDECLARATION;
|
kind = NodeKind.VARIABLEDECLARATION;
|
||||||
|
modifiers = null;
|
||||||
type: TypeNode | null;
|
type: TypeNode | null;
|
||||||
initializer: Expression | null;
|
initializer: Expression | null;
|
||||||
|
|
||||||
@ -1881,18 +1895,19 @@ export class VariableDeclaration extends DeclarationStatement {
|
|||||||
export class VariableStatement extends Statement {
|
export class VariableStatement extends Statement {
|
||||||
|
|
||||||
kind = NodeKind.VARIABLE;
|
kind = NodeKind.VARIABLE;
|
||||||
modifiers: Modifier[];
|
modifiers: Modifier[] | null;
|
||||||
declarations: VariableDeclaration[];
|
declarations: VariableDeclaration[];
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
let isConst: bool = false;
|
let isConst: bool = false;
|
||||||
let i: i32, k: i32;
|
let i: i32, k: i32;
|
||||||
for (i = 0, k = this.modifiers.length; i < k; ++i) {
|
if (this.modifiers)
|
||||||
this.modifiers[i].serialize(sb);
|
for (i = 0, k = (<Modifier[]>this.modifiers).length; i < k; ++i) {
|
||||||
sb.push(" ");
|
(<Modifier[]>this.modifiers)[i].serialize(sb);
|
||||||
if (this.modifiers[i].modifierKind == ModifierKind.CONST)
|
sb.push(" ");
|
||||||
isConst = true;
|
if ((<Modifier[]>this.modifiers)[i].modifierKind == ModifierKind.CONST)
|
||||||
}
|
isConst = true;
|
||||||
|
}
|
||||||
if (!isConst)
|
if (!isConst)
|
||||||
sb.push("let ");
|
sb.push("let ");
|
||||||
for (i = 0, k = this.declarations.length; i < k; ++i) {
|
for (i = 0, k = this.declarations.length; i < k; ++i) {
|
||||||
@ -1917,15 +1932,12 @@ export class WhileStatement extends Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDeclarationStatement(kind: NodeKind): bool {
|
export function hasModifier(kind: ModifierKind, modifiers: Modifier[] | null): bool {
|
||||||
return kind == NodeKind.CLASS
|
if (modifiers)
|
||||||
|| kind == NodeKind.ENUM
|
for (let i: i32 = 0, k: i32 = (<Modifier[]>modifiers).length; i < k; ++i)
|
||||||
|| kind == NodeKind.ENUMVALUE
|
if ((<Modifier[]>modifiers)[i].modifierKind == kind)
|
||||||
|| kind == NodeKind.FIELD
|
return true;
|
||||||
|| kind == NodeKind.FUNCTION
|
return false;
|
||||||
|| kind == NodeKind.METHOD
|
|
||||||
|| kind == NodeKind.NAMESPACE
|
|
||||||
|| kind == NodeKind.VARIABLEDECLARATION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serialize(node: Node, indent: i32 = 0): string {
|
export function serialize(node: Node, indent: i32 = 0): string {
|
||||||
@ -1934,6 +1946,17 @@ export function serialize(node: Node, indent: i32 = 0): string {
|
|||||||
return sb.join("");
|
return sb.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mangleInternalName(declaration: DeclarationStatement): string {
|
||||||
|
let name: string = declaration.identifier.name;
|
||||||
|
if (!declaration.parent)
|
||||||
|
return name;
|
||||||
|
if (declaration.parent.kind == NodeKind.CLASS)
|
||||||
|
return (<ClassDeclaration>declaration.parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? "." : "#") + name;
|
||||||
|
if (declaration.parent.kind == NodeKind.NAMESPACE || declaration.parent.kind == NodeKind.ENUM)
|
||||||
|
return (<DeclarationStatement>declaration.parent).internalName + "." + name;
|
||||||
|
return declaration.range.source.normalizedPath + "/" + name;
|
||||||
|
}
|
||||||
|
|
||||||
function builderEndsWith(sb: string[], code: CharCode): bool {
|
function builderEndsWith(sb: string[], code: CharCode): bool {
|
||||||
if (sb.length) {
|
if (sb.length) {
|
||||||
const last: string = sb[sb.length - 1];
|
const last: string = sb[sb.length - 1];
|
||||||
|
@ -534,6 +534,65 @@ export class Module {
|
|||||||
_BinaryenModuleDispose(this.ref);
|
_BinaryenModuleDispose(this.ref);
|
||||||
_free(this.lit);
|
_free(this.lit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createRelooper(): Relooper {
|
||||||
|
return this.noEmit ? Relooper.createStub(this) : Relooper.create(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Relooper {
|
||||||
|
|
||||||
|
module: Module;
|
||||||
|
ref: RelooperRef;
|
||||||
|
noEmit: bool;
|
||||||
|
|
||||||
|
static create(module: Module): Relooper {
|
||||||
|
const relooper: Relooper = new Relooper();
|
||||||
|
relooper.module = module;
|
||||||
|
relooper.ref = _RelooperCreate();
|
||||||
|
relooper.noEmit = false;
|
||||||
|
return relooper;
|
||||||
|
}
|
||||||
|
|
||||||
|
static createStub(module: Module): Relooper {
|
||||||
|
const relooper: Relooper = new Relooper();
|
||||||
|
relooper.module = module;
|
||||||
|
relooper.ref = 0;
|
||||||
|
relooper.noEmit = true;
|
||||||
|
return relooper;
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
addBlock(code: BinaryenExpressionRef): RelooperBlockRef {
|
||||||
|
if (this.noEmit) return 0;
|
||||||
|
return _RelooperAddBlock(this.ref, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
addBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: BinaryenExpressionRef = 0, code: BinaryenExpressionRef = 0): void {
|
||||||
|
if (this.noEmit) return;
|
||||||
|
_RelooperAddBranch(from, to, condition, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
addBlockWithSwitch(code: BinaryenExpressionRef, condition: BinaryenExpressionRef): RelooperBlockRef {
|
||||||
|
if (this.noEmit) return 0;
|
||||||
|
return _RelooperAddBlockWithSwitch(this.ref, code, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
addBranchForSwitch(from: RelooperBlockRef, to: RelooperBlockRef, indexes: i32[], code: BinaryenExpressionRef = 0): void {
|
||||||
|
if (this.noEmit) return;
|
||||||
|
const cArr: CArray<i32> = allocI32Array(indexes);
|
||||||
|
try {
|
||||||
|
_RelooperAddBranchForSwitch(from, to, cArr, indexes.length, code);
|
||||||
|
} finally {
|
||||||
|
_free(cArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAndDispose(entry: RelooperBlockRef, labelHelper: BinaryenIndex): BinaryenExpressionRef {
|
||||||
|
if (this.noEmit) return 0;
|
||||||
|
return _RelooperRenderAndDispose(this.ref, entry, labelHelper, this.module.ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
482
src/compiler.ts
482
src/compiler.ts
@ -1,6 +1,5 @@
|
|||||||
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType } from "./binaryen";
|
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType, Relooper } from "./binaryen";
|
||||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
import { hasModifier } from "./parser";
|
|
||||||
import { Program } from "./program";
|
import { Program } from "./program";
|
||||||
import { CharCode, I64, U64, normalizePath, sb } from "./util";
|
import { CharCode, I64, U64, normalizePath, sb } from "./util";
|
||||||
import { Token } from "./tokenizer";
|
import { Token } from "./tokenizer";
|
||||||
@ -35,6 +34,7 @@ import {
|
|||||||
NamespaceDeclaration,
|
NamespaceDeclaration,
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
Statement,
|
Statement,
|
||||||
|
SwitchCase,
|
||||||
SwitchStatement,
|
SwitchStatement,
|
||||||
ThrowStatement,
|
ThrowStatement,
|
||||||
TryStatement,
|
TryStatement,
|
||||||
@ -60,7 +60,9 @@ import {
|
|||||||
SelectExpression,
|
SelectExpression,
|
||||||
StringLiteralExpression,
|
StringLiteralExpression,
|
||||||
UnaryPostfixExpression,
|
UnaryPostfixExpression,
|
||||||
UnaryPrefixExpression
|
UnaryPrefixExpression,
|
||||||
|
|
||||||
|
hasModifier
|
||||||
|
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import {
|
import {
|
||||||
@ -75,17 +77,21 @@ import {
|
|||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export enum Target {
|
export enum Target {
|
||||||
|
/** WebAssembly with 32-bit pointers. */
|
||||||
WASM32,
|
WASM32,
|
||||||
|
/** WebAssembly with 64-bit pointers. Experimental / not supported by any runtime yet. */
|
||||||
WASM64
|
WASM64
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Options {
|
export class Options {
|
||||||
|
/** WebAssembly target. Defaults to {@link Target.WASM32}. */
|
||||||
target: Target = Target.WASM32;
|
target: Target = Target.WASM32;
|
||||||
|
/** If true, performs compilation as usual but doesn't produce any output (all calls to Binaryen are nops). */
|
||||||
noEmit: bool = false;
|
noEmit: bool = false;
|
||||||
|
/** If true, compiles everything instead of just reachable code. */
|
||||||
|
noTreeShaking: bool = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VOID: BinaryenExpressionRef = 0;
|
|
||||||
|
|
||||||
export class Compiler extends DiagnosticEmitter {
|
export class Compiler extends DiagnosticEmitter {
|
||||||
|
|
||||||
program: Program;
|
program: Program;
|
||||||
@ -98,10 +104,12 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
currentType: Type = Type.void;
|
currentType: Type = Type.void;
|
||||||
currentClass: ClassType | null = null;
|
currentClass: ClassType | null = null;
|
||||||
currentFunction: FunctionType = this.startFunction;
|
currentFunction: FunctionType = this.startFunction;
|
||||||
|
disallowContinue: bool = true;
|
||||||
|
|
||||||
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
||||||
memorySegments: MemorySegment[] = new Array();
|
memorySegments: MemorySegment[] = new Array();
|
||||||
|
|
||||||
|
files: Set<string> = new Set();
|
||||||
classes: Map<string,ClassType> = new Map();
|
classes: Map<string,ClassType> = new Map();
|
||||||
enums: Set<string> = new Set();
|
enums: Set<string> = new Set();
|
||||||
functions: Map<string,FunctionType> = new Map();
|
functions: Map<string,FunctionType> = new Map();
|
||||||
@ -117,7 +125,6 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
this.program = program;
|
this.program = program;
|
||||||
this.options = options ? options : new Options();
|
this.options = options ? options : new Options();
|
||||||
this.module = this.options.noEmit ? Module.createStub() : Module.create();
|
this.module = this.options.noEmit ? Module.createStub() : Module.create();
|
||||||
// noEmit performs compilation as usual but all binaryen methods are nops instead
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compile(): Module {
|
compile(): Module {
|
||||||
@ -126,56 +133,11 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
// initialize lookup maps
|
// initialize lookup maps
|
||||||
program.initialize(this.options.target);
|
program.initialize(this.options.target);
|
||||||
|
|
||||||
// start by compiling entry file exports
|
// compile entry file (exactly one, usually)
|
||||||
const entrySource: Source = program.sources[0];
|
for (let i: i32 = 0, k = program.sources.length; i < k; ++i) {
|
||||||
for (let i: i32 = 0, k: i32 = entrySource.statements.length; i < k; ++i) {
|
const source: Source = program.sources[i];
|
||||||
const statement: Statement = entrySource.statements[i];
|
if (source.isEntry)
|
||||||
switch (statement.kind) {
|
this.compileFile(source);
|
||||||
|
|
||||||
case NodeKind.CLASS:
|
|
||||||
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))
|
|
||||||
this.compileEnum(<EnumDeclaration>statement);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind.FUNCTION:
|
|
||||||
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))
|
|
||||||
this.compileNamespace(<NamespaceDeclaration>statement);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind.VARIABLE:
|
|
||||||
if (hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers))
|
|
||||||
this.compileGlobals(<VariableStatement>statement);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeKind.EXPORT: {
|
|
||||||
this.compileExports(<ExportStatement>statement);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
const previousClass: ClassType | null = this.currentClass;
|
|
||||||
const previousFunction: FunctionType = this.currentFunction;
|
|
||||||
this.currentClass = null;
|
|
||||||
this.currentFunction = this.startFunction;
|
|
||||||
this.startFunctionBody.push(this.compileStatement(statement));
|
|
||||||
this.currentClass = previousClass;
|
|
||||||
this.currentFunction = previousFunction;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make start function if not empty
|
// make start function if not empty
|
||||||
@ -184,7 +146,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (!typeRef)
|
if (!typeRef)
|
||||||
typeRef = this.module.addFunctionType("v", BinaryenType.None, []);
|
typeRef = this.module.addFunctionType("v", BinaryenType.None, []);
|
||||||
this.module.setStart(
|
this.module.setStart(
|
||||||
this.module.addFunction("", typeRef, typesToBinaryenTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody))
|
this.module.addFunction("start", typeRef, typesToBinaryenTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,48 +164,126 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
return this.module;
|
return this.module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileFile(source: Source): void {
|
||||||
|
if (this.files.has(source.normalizedPath))
|
||||||
|
return;
|
||||||
|
this.files.add(source.normalizedPath);
|
||||||
|
|
||||||
|
const isEntry: bool = source.isEntry;
|
||||||
|
const noTreeShaking: bool = this.options.noTreeShaking;
|
||||||
|
for (let i: i32 = 0, k: i32 = source.statements.length; i < k; ++i) {
|
||||||
|
const statement: Statement = source.statements[i];
|
||||||
|
switch (statement.kind) {
|
||||||
|
|
||||||
|
case NodeKind.CLASS:
|
||||||
|
if ((noTreeShaking || isEntry && hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>statement).modifiers)) && !(<ClassDeclaration>statement).typeParameters.length)
|
||||||
|
this.compileClass(<ClassDeclaration>statement, []);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.ENUM:
|
||||||
|
if (noTreeShaking || isEntry && hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>statement).modifiers))
|
||||||
|
this.compileEnum(<EnumDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.FUNCTION:
|
||||||
|
if ((noTreeShaking || isEntry && hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers)) && !(<FunctionDeclaration>statement).typeParameters.length)
|
||||||
|
this.compileFunction(<FunctionDeclaration>statement, []);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.IMPORT:
|
||||||
|
this.compileFileByPath((<ImportStatement>statement).normalizedPath, (<ImportStatement>statement).path);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.NAMESPACE:
|
||||||
|
if (noTreeShaking || isEntry && hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers))
|
||||||
|
this.compileNamespace(<NamespaceDeclaration>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.VARIABLE:
|
||||||
|
if (noTreeShaking || isEntry && hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers))
|
||||||
|
this.compileGlobals(<VariableStatement>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NodeKind.EXPORT:
|
||||||
|
if ((<ExportStatement>statement).path)
|
||||||
|
this.compileFileByPath((<StringLiteralExpression>(<ExportStatement>statement).path).value, <StringLiteralExpression>(<ExportStatement>statement).path);
|
||||||
|
if (noTreeShaking || isEntry)
|
||||||
|
this.compileExports(<ExportStatement>statement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// otherwise a top-level statement that is part of the start function's body
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileFileByPath(normalizedPath: string, reportNode: Node): void {
|
||||||
|
for (let j: i32 = 0, l: i32 = this.program.sources.length; j < l; ++j) {
|
||||||
|
const importedSource: Source = this.program.sources[j];
|
||||||
|
if (importedSource.normalizedPath == normalizedPath) {
|
||||||
|
this.compileFile(importedSource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.error(DiagnosticCode.File_0_not_found, reportNode.range, normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
compileClass(declaration: ClassDeclaration, typeArguments: Type[]): void {
|
compileClass(declaration: ClassDeclaration, typeArguments: Type[]): void {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
compileEnum(declaration: EnumDeclaration): void {
|
compileEnum(declaration: EnumDeclaration): void {
|
||||||
const name: string = this.program.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
if (this.enums.has(name)) {
|
if (this.enums.has(internalName)) {
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.enums.add(internalName);
|
||||||
const valueDeclarations: EnumValueDeclaration[] = declaration.members;
|
const valueDeclarations: EnumValueDeclaration[] = declaration.members;
|
||||||
let previousValueName: string | null = null;
|
let previousValueName: string | null = null;
|
||||||
const isExport: bool = declaration.range.source.isEntry && hasModifier(ModifierKind.EXPORT, declaration.modifiers);
|
|
||||||
for (let i: i32 = 0, k: i32 = valueDeclarations.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = valueDeclarations.length; i < k; ++i)
|
||||||
previousValueName = this.compileEnumValue(valueDeclarations[i], previousValueName, isExport);
|
previousValueName = this.compileEnumValue(valueDeclarations[i], previousValueName);
|
||||||
this.enums.add(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileEnumValue(declaration: EnumValueDeclaration, previousName: string | null, isExport: bool): string {
|
compileEnumValue(declaration: EnumValueDeclaration, previousName: string | null): string {
|
||||||
const name: string = this.program.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
let initializer: BinaryenExpressionRef = declaration.value ? this.compileExpression(<Expression>declaration.value, Type.i32) : 0;
|
// TODO: WASM does not support complex initializers for globals yet, hence we make such globals mutable and initialize in start
|
||||||
let initializeInStart: bool = declaration.value ? (<Expression>declaration.value).kind != NodeKind.LITERAL : true;
|
let initializer: BinaryenExpressionRef;
|
||||||
// TODO: WASM does not support binary initializers for globals yet, hence me make them mutable and initialize in start
|
let initializeInStart: bool;
|
||||||
if (!initializer) {
|
if (declaration.value) {
|
||||||
if (previousName == null) {
|
initializer = this.compileExpression(<Expression>declaration.value, Type.i32);
|
||||||
initializer = this.module.createI32(0);
|
initializeInStart = declaration.value.kind != NodeKind.LITERAL;
|
||||||
initializeInStart = false;
|
} else if (previousName == null) {
|
||||||
} else {
|
initializer = this.module.createI32(0);
|
||||||
initializer = this.module.createBinary(BinaryOp.AddI32,
|
initializeInStart = false;
|
||||||
this.module.createGetGlobal(previousName, BinaryenType.I32),
|
} else {
|
||||||
this.module.createI32(1)
|
initializer = this.module.createBinary(BinaryOp.AddI32,
|
||||||
);
|
this.module.createGetGlobal(previousName, BinaryenType.I32),
|
||||||
}
|
this.module.createI32(1)
|
||||||
|
);
|
||||||
|
initializeInStart = true;
|
||||||
}
|
}
|
||||||
if (initializeInStart) {
|
if (initializeInStart) {
|
||||||
this.module.addGlobal(name, BinaryenType.I32, true, this.module.createI32(-1));
|
this.module.addGlobal(internalName, BinaryenType.I32, true, this.module.createI32(-1));
|
||||||
this.startFunctionBody.push(this.module.createSetGlobal(name, initializer));
|
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
|
||||||
} else
|
} else
|
||||||
this.module.addGlobal(name, BinaryenType.I32, false, initializer);
|
this.module.addGlobal(internalName, BinaryenType.I32, false, initializer);
|
||||||
// TODO: WASM does not support exporting globals yet (the following produces invalid code referencing a non-existent function)
|
|
||||||
// this.module.addExport(name, (<EnumDeclaration>declaration.parent).identifier.name + "." + declaration.identifier.name);
|
// export if applicable
|
||||||
return name;
|
if (declaration.parent && (<Node>declaration.parent).kind == NodeKind.ENUM && (<EnumDeclaration>declaration.parent).globalExportName != null) {
|
||||||
|
// TODO: WASM does not support exporting globals yet
|
||||||
|
// this.module.addExport(internalName, <string>(<EnumDeclaration>declaration.parent).exportName);
|
||||||
|
}
|
||||||
|
return internalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTypeArguments(typeParameters: TypeParameter[], typeArguments: Type[], reportNode: Node | null = null): bool {
|
checkTypeArguments(typeParameters: TypeParameter[], typeArguments: Type[], reportNode: Node | null = null): bool {
|
||||||
@ -252,6 +292,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, (<Node>reportNode).range, typeParameters.length.toString(10), typeArguments.length.toString(10));
|
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, (<Node>reportNode).range, typeParameters.length.toString(10), typeArguments.length.toString(10));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// TODO: check class types, arrays
|
||||||
// TODO: check TypeParameter#extendsType
|
// TODO: check TypeParameter#extendsType
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -266,7 +307,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (!this.checkTypeArguments(declaration.typeParameters, typeArguments, reportNode)) // reports if requested
|
if (!this.checkTypeArguments(declaration.typeParameters, typeArguments, reportNode)) // reports if requested
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let globalName: string = this.program.mangleInternalName(declaration);
|
let internalName: string = declaration.internalName;
|
||||||
|
|
||||||
// inherit type arguments, i.e. from class
|
// inherit type arguments, i.e. from class
|
||||||
const typeArgumentsMap: Map<string,Type> = new Map();
|
const typeArgumentsMap: Map<string,Type> = new Map();
|
||||||
@ -279,10 +320,10 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (k) {
|
if (k) {
|
||||||
for (i = 0; i < k; ++i)
|
for (i = 0; i < k; ++i)
|
||||||
typeArgumentsMap.set(declaration.typeParameters[i].identifier.name, typeArguments[i]);
|
typeArgumentsMap.set(declaration.typeParameters[i].identifier.name, typeArguments[i]);
|
||||||
globalName += typeArgumentsToString(typeArguments);
|
internalName += typeArgumentsToString(typeArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.functions.has(globalName)) {
|
if (this.functions.has(internalName)) {
|
||||||
if (reportNode)
|
if (reportNode)
|
||||||
this.error(DiagnosticCode.Duplicate_function_implementation, (<Node>reportNode).range);
|
this.error(DiagnosticCode.Duplicate_function_implementation, (<Node>reportNode).range);
|
||||||
return;
|
return;
|
||||||
@ -319,96 +360,100 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
// compile statements
|
// compile statements
|
||||||
const functionType: FunctionType = new FunctionType(typeArguments, parameterTypes, returnType, parameterNames);
|
const functionType: FunctionType = new FunctionType(typeArguments, parameterTypes, returnType, parameterNames);
|
||||||
this.functions.set(globalName, functionType);
|
this.functions.set(internalName, functionType);
|
||||||
const previousFunction: FunctionType = this.currentFunction;
|
const previousFunction: FunctionType = this.currentFunction;
|
||||||
this.currentFunction = functionType;
|
this.currentFunction = functionType;
|
||||||
const statements: Statement[] = <Statement[]>declaration.statements;
|
const stmts: BinaryenExpressionRef[] = this.compileStatements(<Statement[]>declaration.statements);
|
||||||
k = statements.length;
|
|
||||||
const body: BinaryenExpressionRef[] = new Array(k);
|
|
||||||
let hasErrors: bool = false;
|
|
||||||
for (i = 0; i < k; ++i)
|
|
||||||
if (!(body[i] = this.compileStatement(statements[i])))
|
|
||||||
hasErrors = true;
|
|
||||||
this.currentFunction = previousFunction;
|
this.currentFunction = previousFunction;
|
||||||
|
|
||||||
if (hasErrors)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// create function
|
// create function
|
||||||
const binaryenResultType: BinaryenType = typeToBinaryenType(returnType);
|
const binaryenResultType: BinaryenType = typeToBinaryenType(returnType);
|
||||||
const binaryenParamTypes: BinaryenType[] = typesToBinaryenTypes(parameterTypes);
|
const binaryenParamTypes: BinaryenType[] = typesToBinaryenTypes(parameterTypes);
|
||||||
let binaryenTypeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes);
|
let binaryenTypeRef: BinaryenFunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes);
|
||||||
if (!binaryenTypeRef)
|
if (!binaryenTypeRef)
|
||||||
binaryenTypeRef = this.module.addFunctionType(typesToSignatureName(parameterTypes, returnType), binaryenResultType, binaryenParamTypes);
|
binaryenTypeRef = this.module.addFunctionType(typesToSignatureName(parameterTypes, returnType), binaryenResultType, binaryenParamTypes);
|
||||||
this.module.addFunction(globalName, binaryenTypeRef, typesToBinaryenTypes(functionType.additionalLocals), this.module.createBlock(null, body));
|
this.module.addFunction(internalName, binaryenTypeRef, typesToBinaryenTypes(functionType.additionalLocals), this.module.createBlock(null, stmts, BinaryenType.None));
|
||||||
if (declaration.range.source.isEntry && hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
|
||||||
this.module.addExport(globalName, declaration.identifier.name);
|
// export if applicable
|
||||||
|
if (declaration.globalExportName != null)
|
||||||
|
this.module.addExport(internalName, <string>declaration.globalExportName);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileGlobals(statement: VariableStatement): void {
|
compileGlobals(statement: VariableStatement): void {
|
||||||
const declarations: VariableDeclaration[] = statement.declarations;
|
const declarations: VariableDeclaration[] = statement.declarations;
|
||||||
const isConst: bool = hasModifier(ModifierKind.CONST, statement.modifiers);
|
const isConst: bool = hasModifier(ModifierKind.CONST, statement.modifiers);
|
||||||
const isExport: bool = statement.range.source.isEntry && hasModifier(ModifierKind.EXPORT, statement.modifiers);
|
|
||||||
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i)
|
||||||
this.compileGlobal(declarations[i], isConst, isExport);
|
this.compileGlobal(declarations[i], isConst);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileGlobal(declaration: VariableDeclaration, isConst: bool, isExport: bool): void {
|
compileGlobal(declaration: VariableDeclaration, isConst: bool): void {
|
||||||
const type: Type | null = declaration.type ? this.resolveType(<TypeNode>declaration.type) : null; // reports
|
const type: Type | null = declaration.type ? this.resolveType(<TypeNode>declaration.type) : null; // reports
|
||||||
if (!type)
|
if (!type)
|
||||||
return;
|
return;
|
||||||
const name: string = this.program.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
if (this.globals.has(name)) {
|
if (this.globals.has(internalName)) {
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const binaryenType: BinaryenType = typeToBinaryenType(<Type>type);
|
const binaryenType: BinaryenType = typeToBinaryenType(<Type>type);
|
||||||
const initializer: BinaryenExpressionRef = declaration.initializer ? this.compileExpression(<Expression>declaration.initializer, <Type>type) : typeToBinaryenZero(this.module, <Type>type);
|
// TODO: WASM does not support complex initializers for globals yet, hence me such globals mutable and initialize in start
|
||||||
let initializeInStart: bool = declaration.initializer ? (<Expression>declaration.initializer).kind != NodeKind.LITERAL : false;
|
let initializer: BinaryenExportRef;
|
||||||
// TODO: WASM does not support binary initializers for globals yet, hence me make them mutable and initialize in start
|
let initializeInStart: bool;
|
||||||
|
if (declaration.initializer) {
|
||||||
|
initializer = this.compileExpression(<Expression>declaration.initializer, <Type>type);
|
||||||
|
initializeInStart = (<Expression>declaration.initializer).kind != NodeKind.LITERAL;
|
||||||
|
} else {
|
||||||
|
initializer = typeToBinaryenZero(this.module, <Type>type);
|
||||||
|
initializeInStart = false;
|
||||||
|
}
|
||||||
if (initializeInStart) {
|
if (initializeInStart) {
|
||||||
this.module.addGlobal(name, binaryenType, false, typeToBinaryenZero(this.module, <Type>type));
|
this.module.addGlobal(internalName, binaryenType, true, typeToBinaryenZero(this.module, <Type>type));
|
||||||
this.startFunctionBody.push(initializer);
|
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
|
||||||
} else
|
} else
|
||||||
this.module.addGlobal(name, binaryenType, !isConst, initializer);
|
this.module.addGlobal(internalName, binaryenType, !isConst, initializer);
|
||||||
// TODO: WASM does not support exporting globals yet (the following produces invalid code referencing a non-existent function)
|
this.globals.set(internalName, <Type>type);
|
||||||
// this.module.addExport(name, declaration.identifier.name);
|
|
||||||
this.globals.set(name, <Type>type);
|
// export if applicable
|
||||||
|
if (declaration.globalExportName != null) {
|
||||||
|
// TODO: WASM does not support exporting globals yet
|
||||||
|
// this.module.addExport(internalName, <string>declaration.exportName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileNamespace(declaration: NamespaceDeclaration): void {
|
compileNamespace(declaration: NamespaceDeclaration): void {
|
||||||
const members: Statement[] = declaration.members;
|
const members: Statement[] = declaration.members;
|
||||||
|
const noTreeShaking: bool = this.options.noTreeShaking;
|
||||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
|
||||||
const member: Statement = members[i];
|
const member: Statement = members[i];
|
||||||
switch (member.kind) {
|
switch (member.kind) {
|
||||||
|
|
||||||
case NodeKind.CLASS:
|
case NodeKind.CLASS:
|
||||||
if (!(<ClassDeclaration>member).typeParameters.length)
|
if ((noTreeShaking || hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>member).modifiers)) && !(<ClassDeclaration>member).typeParameters.length)
|
||||||
this.compileClass(<ClassDeclaration>member, []);
|
this.compileClass(<ClassDeclaration>member, []);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.ENUM:
|
case NodeKind.ENUM:
|
||||||
this.compileEnum(<EnumDeclaration>member);
|
if (noTreeShaking || hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>member).modifiers))
|
||||||
|
this.compileEnum(<EnumDeclaration>member);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.FUNCTION:
|
case NodeKind.FUNCTION:
|
||||||
if (!(<FunctionDeclaration>member).typeParameters.length)
|
if ((noTreeShaking || hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>member).modifiers)) && !(<FunctionDeclaration>member).typeParameters.length)
|
||||||
this.compileFunction(<FunctionDeclaration>member, []);
|
this.compileFunction(<FunctionDeclaration>member, []);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.NAMESPACE:
|
case NodeKind.NAMESPACE:
|
||||||
this.compileNamespace(<NamespaceDeclaration>member);
|
if (noTreeShaking || hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>member).modifiers))
|
||||||
|
this.compileNamespace(<NamespaceDeclaration>member);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.VARIABLE:
|
case NodeKind.VARIABLE:
|
||||||
this.compileGlobals(<VariableStatement>member);
|
if (noTreeShaking || hasModifier(ModifierKind.EXPORT, (<VariableStatement>member).modifiers))
|
||||||
|
this.compileGlobals(<VariableStatement>member);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO: some form of internal visibility?
|
|
||||||
// case NodeKind.EXPORT:
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error("unexpected namespace member kind");
|
throw new Error("unexpected namespace member");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
@ -416,14 +461,14 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
compileExports(statement: ExportStatement): void {
|
compileExports(statement: ExportStatement): void {
|
||||||
const members: ExportMember[] = statement.members;
|
const members: ExportMember[] = statement.members;
|
||||||
const normalizedPath: string | null = statement.path == null ? null : normalizePath(<string>statement.path);
|
const normalizedPath: string | null = statement.path ? normalizePath(<string>(<StringLiteralExpression>statement.path).value) : statement.range.source.normalizedPath;
|
||||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||||
this.compileExport(members[i], normalizedPath);
|
this.compileExport(members[i], normalizedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileExport(member: ExportMember, normalizedPath: string | null): void {
|
compileExport(member: ExportMember, normalizedPath: string): void {
|
||||||
const exportName: string = normalizedPath + "/" + member.externalIdentifier.name;
|
const internalExportName: string = normalizedPath + "/" + member.identifier.name;
|
||||||
const declaration: DeclarationStatement | null = <DeclarationStatement | null>this.program.exports.get(exportName);
|
const declaration: DeclarationStatement | null = <DeclarationStatement | null>this.program.exports.get(internalExportName);
|
||||||
if (declaration) {
|
if (declaration) {
|
||||||
switch ((<DeclarationStatement>declaration).kind) {
|
switch ((<DeclarationStatement>declaration).kind) {
|
||||||
|
|
||||||
@ -446,7 +491,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.VARIABLEDECLARATION:
|
case NodeKind.VARIABLEDECLARATION:
|
||||||
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, (<VariableStatement>declaration.parent).modifiers), member.range.source.isEntry);
|
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, (<VariableStatement>declaration.parent).modifiers));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -554,9 +599,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (this.functions.has(globalName))
|
if (this.functions.has(globalName))
|
||||||
return <FunctionType>this.functions.get(globalName);
|
return <FunctionType>this.functions.get(globalName);
|
||||||
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, globalName);
|
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, globalName);
|
||||||
return null;
|
} else {
|
||||||
}
|
|
||||||
if (expression.kind == NodeKind.PROPERTYACCESS) {
|
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -609,28 +652,36 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
case NodeKind.WHILE:
|
case NodeKind.WHILE:
|
||||||
return this.compileWhileStatement(<WhileStatement>statement);
|
return this.compileWhileStatement(<WhileStatement>statement);
|
||||||
}
|
}
|
||||||
throw new Error("not implemented");
|
throw new Error("unexpected statement kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
compileStatements(statements: Statement[]): BinaryenExpressionRef[] {
|
||||||
|
const k: i32 = statements.length;
|
||||||
|
const stmts: BinaryenExpressionRef[] = new Array(k);
|
||||||
|
for (let i: i32 = 0; i < k; ++i)
|
||||||
|
stmts[i] = this.compileStatement(statements[i]);
|
||||||
|
return stmts;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileBlockStatement(statement: BlockStatement): BinaryenExpressionRef {
|
compileBlockStatement(statement: BlockStatement): BinaryenExpressionRef {
|
||||||
const substatements: Statement[] = statement.statements;
|
return this.module.createBlock(null, this.compileStatements(statement.statements), BinaryenType.None);
|
||||||
const children: BinaryenExpressionRef[] = new Array(substatements.length);
|
|
||||||
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
|
|
||||||
children[i] = this.compileStatement(substatements[i]);
|
|
||||||
return this.module.createBlock(null, children, BinaryenType.None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
||||||
|
if (statement.label)
|
||||||
|
throw new Error("not implemented");
|
||||||
const context: string | null = this.currentFunction.breakContext;
|
const context: string | null = this.currentFunction.breakContext;
|
||||||
if (context)
|
if (context != null)
|
||||||
return this.module.createBreak("break$" + (<string>context));
|
return this.module.createBreak("break$" + (<string>context));
|
||||||
this.error(DiagnosticCode.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement, statement.range);
|
this.error(DiagnosticCode.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement, statement.range);
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef {
|
compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef {
|
||||||
|
if (statement.label)
|
||||||
|
throw new Error("not implemented");
|
||||||
const context: string | null = this.currentFunction.breakContext;
|
const context: string | null = this.currentFunction.breakContext;
|
||||||
if (context)
|
if (context != null && !this.disallowContinue)
|
||||||
return this.module.createBreak("continue$" + (<string>context));
|
return this.module.createBreak("continue$" + (<string>context));
|
||||||
this.error(DiagnosticCode.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement, statement.range);
|
this.error(DiagnosticCode.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement, statement.range);
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
@ -648,8 +699,8 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
this.module.createBlock(null, [
|
this.module.createBlock(null, [
|
||||||
body,
|
body,
|
||||||
this.module.createBreak(continueLabel, condition)
|
this.module.createBreak(continueLabel, condition)
|
||||||
]))
|
], BinaryenType.None))
|
||||||
]);
|
], BinaryenType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef {
|
compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef {
|
||||||
@ -676,9 +727,9 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
body,
|
body,
|
||||||
incrementor,
|
incrementor,
|
||||||
this.module.createBreak(continueLabel)
|
this.module.createBreak(continueLabel)
|
||||||
]))
|
], BinaryenType.None))
|
||||||
]))
|
], BinaryenType.None))
|
||||||
]);
|
], BinaryenType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
||||||
@ -697,7 +748,57 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileSwitchStatement(statement: SwitchStatement): BinaryenExpressionRef {
|
compileSwitchStatement(statement: SwitchStatement): BinaryenExpressionRef {
|
||||||
throw new Error("not implemented");
|
const context: string = this.currentFunction.enterBreakContext();
|
||||||
|
const previousDisallowContinue: bool = this.disallowContinue;
|
||||||
|
this.disallowContinue = true;
|
||||||
|
|
||||||
|
// introduce a local for evaluating the condition (exactly once)
|
||||||
|
const localIndex: i32 = this.currentFunction.addLocal(Type.i32);
|
||||||
|
let i: i32, k: i32 = statement.cases.length;
|
||||||
|
|
||||||
|
// prepend initializer to inner block
|
||||||
|
const breaks: BinaryenExpressionRef[] = new Array(1 + k);
|
||||||
|
breaks[0] = this.module.createSetLocal(localIndex, this.compileExpression(statement.expression, Type.i32)); // initializer
|
||||||
|
|
||||||
|
// make one br_if per (possibly dynamic) labeled case
|
||||||
|
// TODO: take advantage of br_table where labels are known to be (sequential) constant (ideally Binaryen's optimizer would)
|
||||||
|
let breakIndex: i32 = 1;
|
||||||
|
let defaultIndex: i32 = -1;
|
||||||
|
for (i = 0; i < k; ++i) {
|
||||||
|
const case_: SwitchCase = statement.cases[i];
|
||||||
|
if (case_.label) {
|
||||||
|
breaks[breakIndex++] = this.module.createBreak("case" + i.toString(10) + "$" + context, this.module.createBinary(BinaryOp.EqI32,
|
||||||
|
this.module.createGetLocal(localIndex, BinaryenType.I32),
|
||||||
|
this.compileExpression(case_.label, Type.i32)
|
||||||
|
));
|
||||||
|
} else
|
||||||
|
defaultIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise br to default respectively out of the switch if there is no default case
|
||||||
|
breaks[breakIndex] = this.module.createBreak((defaultIndex >= 0
|
||||||
|
? "case" + defaultIndex.toString(10)
|
||||||
|
: "break"
|
||||||
|
) + "$" + context);
|
||||||
|
|
||||||
|
// nest blocks in order
|
||||||
|
let currentBlock: BinaryenExpressionRef = this.module.createBlock("case0$" + context, breaks, BinaryenType.None);
|
||||||
|
for (i = 0; i < k; ++i) {
|
||||||
|
const case_: SwitchCase = statement.cases[i];
|
||||||
|
const nextLabel: string = i == k - 1
|
||||||
|
? "break$" + context
|
||||||
|
: "case" + (i + 1).toString(10) + "$" + context;
|
||||||
|
const l: i32 = case_.statements.length;
|
||||||
|
const body: BinaryenExpressionRef[] = new Array(1 + l);
|
||||||
|
body[0] = currentBlock;
|
||||||
|
for (let j: i32 = 0; j < l; ++j)
|
||||||
|
body[j + 1] = this.compileStatement(case_.statements[j]);
|
||||||
|
currentBlock = this.module.createBlock(nextLabel, body, BinaryenType.None);
|
||||||
|
}
|
||||||
|
this.currentFunction.leaveBreakContext();
|
||||||
|
this.disallowContinue = previousDisallowContinue;
|
||||||
|
|
||||||
|
return currentBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileThrowStatement(statement: ThrowStatement): BinaryenExpressionRef {
|
compileThrowStatement(statement: ThrowStatement): BinaryenExpressionRef {
|
||||||
@ -734,24 +835,24 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return initializers.length ? this.module.createBlock(null, initializers) : this.module.createNop();
|
return initializers.length ? this.module.createBlock(null, initializers, BinaryenType.None) : this.module.createNop();
|
||||||
}
|
}
|
||||||
|
|
||||||
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
|
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
|
||||||
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
|
||||||
const label: string = this.currentFunction.enterBreakContext();
|
const label: string = this.currentFunction.enterBreakContext();
|
||||||
|
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
||||||
const breakLabel: string = "break$" + label;
|
const breakLabel: string = "break$" + label;
|
||||||
const continueLabel: string = "continue$" + label;
|
const continueLabel: string = "continue$" + label;
|
||||||
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
||||||
this.currentFunction.leaveBreakContext();
|
this.currentFunction.leaveBreakContext();
|
||||||
return this.module.createBlock(breakLabel, [
|
return this.module.createBlock(breakLabel, [
|
||||||
this.module.createLoop(continueLabel,
|
this.module.createLoop(continueLabel,
|
||||||
this.module.createIf(condition, this.module.createBlock("", [
|
this.module.createIf(condition, this.module.createBlock(null, [
|
||||||
body,
|
body,
|
||||||
this.module.createBreak(continueLabel)
|
this.module.createBreak(continueLabel)
|
||||||
]))
|
], BinaryenType.None))
|
||||||
)
|
)
|
||||||
]);
|
], BinaryenType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// expressions
|
// expressions
|
||||||
@ -779,6 +880,10 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.IDENTIFIER:
|
case NodeKind.IDENTIFIER:
|
||||||
|
case NodeKind.FALSE:
|
||||||
|
case NodeKind.NULL:
|
||||||
|
case NodeKind.THIS:
|
||||||
|
case NodeKind.TRUE:
|
||||||
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, contextualType);
|
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1254,19 +1359,76 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef {
|
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
const name: string = expression.name;
|
|
||||||
|
// null
|
||||||
|
if (expression.kind == NodeKind.NULL) {
|
||||||
|
if (contextualType.classType) // keep contextualType
|
||||||
|
return this.options.target == Target.WASM64 ? this.module.createI64(0, 0) : this.module.createI32(0);
|
||||||
|
if (this.options.target == Target.WASM64) {
|
||||||
|
this.currentType = Type.u64;
|
||||||
|
return this.module.createI64(0, 0);
|
||||||
|
} else {
|
||||||
|
this.currentType = Type.u32;
|
||||||
|
return this.module.createI32(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// true
|
||||||
|
} else if (expression.kind == NodeKind.TRUE) {
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
return this.module.createI32(1);
|
||||||
|
|
||||||
|
// false
|
||||||
|
} else if (expression.kind == NodeKind.FALSE) {
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
return this.module.createI32(0);
|
||||||
|
|
||||||
|
// this
|
||||||
|
} else if (expression.kind == NodeKind.THIS) {
|
||||||
|
if (/* TODO: this.currentFunction.isInstance &&*/ this.currentClass) {
|
||||||
|
this.currentType = this.currentClass.type;
|
||||||
|
return this.module.createGetLocal(0, typeToBinaryenType(this.currentType));
|
||||||
|
}
|
||||||
|
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
|
||||||
|
this.currentType = this.options.target == Target.WASM64 ? Type.u64 : Type.u32;
|
||||||
|
return this.module.createUnreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalName: string = expression.name; // same as local variable name
|
||||||
|
|
||||||
|
// local variable
|
||||||
const locals: Map<string,LocalType> = this.currentFunction.locals;
|
const locals: Map<string,LocalType> = this.currentFunction.locals;
|
||||||
if (locals.has(name)) {
|
if (locals.has(globalName)) {
|
||||||
const local: LocalType = <LocalType>locals.get(name);
|
const local: LocalType = <LocalType>locals.get(globalName);
|
||||||
this.currentType = local.type;
|
this.currentType = local.type;
|
||||||
return this.module.createGetLocal(local.index, typeToBinaryenType(this.currentType));
|
return this.module.createGetLocal(local.index, typeToBinaryenType(this.currentType));
|
||||||
}
|
}
|
||||||
const globals: Map<string,Type> = this.globals;
|
|
||||||
if (globals.has(name)) {
|
// global in local file
|
||||||
this.currentType = <Type>globals.get(name);
|
const localName: string = expression.range.source.normalizedPath + "/" + globalName;
|
||||||
return this.module.createGetGlobal(name, typeToBinaryenType(this.currentType));
|
let determinedName: string = localName;
|
||||||
|
if (this.program.names.has(localName)) {
|
||||||
|
const declaration: DeclarationStatement = <DeclarationStatement>this.program.names.get(localName);
|
||||||
|
if (declaration.kind == NodeKind.VARIABLEDECLARATION) {
|
||||||
|
if (!this.globals.has(declaration.internalName))
|
||||||
|
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, declaration.modifiers));
|
||||||
|
}
|
||||||
|
|
||||||
|
// global across files
|
||||||
|
} else if (this.program.names.has(globalName)) {
|
||||||
|
const declaration: DeclarationStatement = <DeclarationStatement>this.program.names.get(globalName);
|
||||||
|
if (declaration.kind == NodeKind.VARIABLEDECLARATION) {
|
||||||
|
if (!this.globals.has(declaration.internalName))
|
||||||
|
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, declaration.modifiers));
|
||||||
|
determinedName = globalName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name);
|
|
||||||
|
const globals: Map<string,Type> = this.globals;
|
||||||
|
if (globals.has(determinedName)) {
|
||||||
|
this.currentType = <Type>globals.get(determinedName);
|
||||||
|
return this.module.createGetGlobal(determinedName, typeToBinaryenType(this.currentType));
|
||||||
|
}
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, determinedName);
|
||||||
return this.module.createUnreachable();
|
return this.module.createUnreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ export enum DiagnosticCode {
|
|||||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
|
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,
|
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
|
||||||
Duplicate_function_implementation = 2393,
|
Duplicate_function_implementation = 2393,
|
||||||
Expected_0_type_arguments_but_got_1 = 2558
|
Expected_0_type_arguments_but_got_1 = 2558,
|
||||||
|
File_0_not_found = 6054
|
||||||
}
|
}
|
||||||
|
|
||||||
export function diagnosticCodeToString(code: DiagnosticCode): string {
|
export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||||
@ -109,6 +110,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
|||||||
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
||||||
case 2393: return "Duplicate function implementation.";
|
case 2393: return "Duplicate function implementation.";
|
||||||
case 2558: return "Expected {0} type arguments, but got {1}.";
|
case 2558: return "Expected {0} type arguments, but got {1}.";
|
||||||
|
case 6054: return "File '{0}' not found.";
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,5 +52,7 @@
|
|||||||
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
|
"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,
|
"Function implementation is missing or not immediately following the declaration.": 2391,
|
||||||
"Duplicate function implementation.": 2393,
|
"Duplicate function implementation.": 2393,
|
||||||
"Expected {0} type arguments, but got {1}.": 2558
|
"Expected {0} type arguments, but got {1}.": 2558,
|
||||||
|
|
||||||
|
"File '{0}' not found.": 6054
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
const globalScope = typeof window !== "undefined" && window
|
const globalScope: any = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
|
||||||
|| typeof global !== "undefined" && global
|
|
||||||
|| self;
|
|
||||||
|
|
||||||
globalScope["store"] = function store_u8(ptr, val) {
|
globalScope["store"] = function store_u8(ptr: number, val: number) {
|
||||||
binaryen.HEAPU8[ptr] = val;
|
binaryen.HEAPU8[ptr] = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
globalScope["load"] = function load_u8(ptr) {
|
globalScope["load"] = function load_u8(ptr: number) {
|
||||||
return binaryen.HEAPU8[ptr];
|
return binaryen.HEAPU8[ptr];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,10 +23,13 @@ import {
|
|||||||
AssertionKind,
|
AssertionKind,
|
||||||
Expression,
|
Expression,
|
||||||
IdentifierExpression,
|
IdentifierExpression,
|
||||||
|
StringLiteralExpression,
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
BlockStatement,
|
BlockStatement,
|
||||||
|
BreakStatement,
|
||||||
ClassDeclaration,
|
ClassDeclaration,
|
||||||
|
ContinueStatement,
|
||||||
DecoratorStatement,
|
DecoratorStatement,
|
||||||
DoStatement,
|
DoStatement,
|
||||||
EnumDeclaration,
|
EnumDeclaration,
|
||||||
@ -55,7 +58,9 @@ import {
|
|||||||
TypeParameter,
|
TypeParameter,
|
||||||
VariableStatement,
|
VariableStatement,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
WhileStatement
|
WhileStatement,
|
||||||
|
|
||||||
|
hasModifier
|
||||||
|
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
|
|
||||||
@ -202,7 +207,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
finish(): Program {
|
finish(): Program {
|
||||||
if (this.backlog.length)
|
if (this.backlog.length)
|
||||||
throw new Error("backlog is not empty");
|
throw new Error("backlog is not empty");
|
||||||
this.backlog = new Array(0);
|
this.backlog = [];
|
||||||
this.seenlog.clear();
|
this.seenlog.clear();
|
||||||
return this.program;
|
return this.program;
|
||||||
}
|
}
|
||||||
@ -536,7 +541,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
if (!typeParameters)
|
if (!typeParameters)
|
||||||
return null;
|
return null;
|
||||||
} else
|
} else
|
||||||
typeParameters = new Array(0);
|
typeParameters = [];
|
||||||
if (!tn.skip(Token.OPENPAREN)) {
|
if (!tn.skip(Token.OPENPAREN)) {
|
||||||
this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "(");
|
this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "(");
|
||||||
return null;
|
return null;
|
||||||
@ -565,7 +570,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
} else if (!isDeclare)
|
} else if (!isDeclare)
|
||||||
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range(tn.pos));
|
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, decorators ? <DecoratorStatement[]>decorators : new Array(0), tn.range(startPos, tn.pos));
|
const ret: FunctionDeclaration = Statement.createFunction(modifiers, identifier, typeParameters, <Parameter[]>parameters, returnType, statements, decorators ? <DecoratorStatement[]>decorators : [], tn.range(startPos, tn.pos));
|
||||||
tn.skip(Token.SEMICOLON);
|
tn.skip(Token.SEMICOLON);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -587,7 +592,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
if (!typeParameters)
|
if (!typeParameters)
|
||||||
return null;
|
return null;
|
||||||
} else
|
} else
|
||||||
typeParameters = new Array(0);
|
typeParameters = [];
|
||||||
|
|
||||||
let extendsType: TypeNode | null = null;
|
let extendsType: TypeNode | null = null;
|
||||||
if (tn.skip(Token.EXTENDS)) {
|
if (tn.skip(Token.EXTENDS)) {
|
||||||
@ -617,7 +622,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
members.push(<DeclarationStatement>member);
|
members.push(<DeclarationStatement>member);
|
||||||
} while (!tn.skip(Token.CLOSEBRACE));
|
} while (!tn.skip(Token.CLOSEBRACE));
|
||||||
}
|
}
|
||||||
return Statement.createClass(modifiers, identifier, <TypeParameter[]>typeParameters, extendsType, implementsTypes, members, decorators ? <DecoratorStatement[]>decorators : new Array(0), tn.range(startPos, tn.pos));
|
return Statement.createClass(modifiers, identifier, <TypeParameter[]>typeParameters, extendsType, implementsTypes, members, decorators ? <DecoratorStatement[]>decorators : [], tn.range(startPos, tn.pos));
|
||||||
} else
|
} else
|
||||||
this.error(DiagnosticCode._0_expected, tn.range(), "{");
|
this.error(DiagnosticCode._0_expected, tn.range(), "{");
|
||||||
} else
|
} else
|
||||||
@ -665,7 +670,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
if (!typeParameters)
|
if (!typeParameters)
|
||||||
return null;
|
return null;
|
||||||
} else
|
} else
|
||||||
typeParameters = new Array(0);
|
typeParameters = [];
|
||||||
|
|
||||||
// method: '(' Parameters (':' Type)? '{' Statement* '}' ';'?
|
// method: '(' Parameters (':' Type)? '{' Statement* '}' ';'?
|
||||||
if (tn.skip(Token.OPENPAREN)) {
|
if (tn.skip(Token.OPENPAREN)) {
|
||||||
@ -746,10 +751,10 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let path: string | null = null;
|
let path: StringLiteralExpression | null = null;
|
||||||
if (tn.skip(Token.FROM)) {
|
if (tn.skip(Token.FROM)) {
|
||||||
if (tn.skip(Token.STRINGLITERAL))
|
if (tn.skip(Token.STRINGLITERAL))
|
||||||
path = tn.readString();
|
path = Expression.createStringLiteral(tn.readString(), tn.range());
|
||||||
else {
|
else {
|
||||||
this.error(DiagnosticCode.String_literal_expected, tn.range());
|
this.error(DiagnosticCode.String_literal_expected, tn.range());
|
||||||
return null;
|
return null;
|
||||||
@ -805,7 +810,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
if (tn.skip(Token.FROM)) {
|
if (tn.skip(Token.FROM)) {
|
||||||
if (tn.skip(Token.STRINGLITERAL)) {
|
if (tn.skip(Token.STRINGLITERAL)) {
|
||||||
const path: string = tn.readString();
|
const path: StringLiteralExpression = Expression.createStringLiteral(tn.readString(), tn.range());
|
||||||
const ret: ImportStatement = Statement.createImport(members, path, Range.join(startRange, tn.range()));
|
const ret: ImportStatement = Statement.createImport(members, path, Range.join(startRange, tn.range()));
|
||||||
if (!this.seenlog.has(ret.normalizedPath)) {
|
if (!this.seenlog.has(ret.normalizedPath)) {
|
||||||
this.backlog.push(ret.normalizedPath);
|
this.backlog.push(ret.normalizedPath);
|
||||||
@ -866,9 +871,15 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
const token: Token = tn.next();
|
const token: Token = tn.next();
|
||||||
switch (token) {
|
switch (token) {
|
||||||
|
|
||||||
|
case Token.BREAK:
|
||||||
|
return this.parseBreak(tn);
|
||||||
|
|
||||||
case Token.CONST:
|
case Token.CONST:
|
||||||
return this.parseVariable(tn, [ Statement.createModifier(ModifierKind.CONST, tn.range()) ]);
|
return this.parseVariable(tn, [ Statement.createModifier(ModifierKind.CONST, tn.range()) ]);
|
||||||
|
|
||||||
|
case Token.CONTINUE:
|
||||||
|
return this.parseContinue(tn);
|
||||||
|
|
||||||
case Token.DO:
|
case Token.DO:
|
||||||
return this.parseDoStatement(tn);
|
return this.parseDoStatement(tn);
|
||||||
|
|
||||||
@ -926,6 +937,30 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseBreak(tn: Tokenizer): BreakStatement | null {
|
||||||
|
// at 'break': Identifier? ';'?
|
||||||
|
let identifier: IdentifierExpression | null = null;
|
||||||
|
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
|
||||||
|
tn.next(true);
|
||||||
|
identifier = Expression.createIdentifier(tn.readIdentifier(), tn.range());
|
||||||
|
}
|
||||||
|
const ret: ContinueStatement = Statement.createBreak(identifier, tn.range());
|
||||||
|
tn.skip(Token.SEMICOLON);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseContinue(tn: Tokenizer): ContinueStatement | null {
|
||||||
|
// at 'continue': Identifier? ';'?
|
||||||
|
let identifier: IdentifierExpression | null = null;
|
||||||
|
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
|
||||||
|
tn.next(true);
|
||||||
|
identifier = Expression.createIdentifier(tn.readIdentifier(), tn.range());
|
||||||
|
}
|
||||||
|
const ret: ContinueStatement = Statement.createContinue(identifier, tn.range());
|
||||||
|
tn.skip(Token.SEMICOLON);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
parseDoStatement(tn: Tokenizer): DoStatement | null {
|
parseDoStatement(tn: Tokenizer): DoStatement | null {
|
||||||
// at 'do': Statement 'while' '(' Expression ')' ';'?
|
// at 'do': Statement 'while' '(' Expression ')' ';'?
|
||||||
const startPos: i32 = tn.tokenPos;
|
const startPos: i32 = tn.tokenPos;
|
||||||
@ -1027,7 +1062,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
return null;
|
return null;
|
||||||
if (tn.skip(Token.CLOSEPAREN)) {
|
if (tn.skip(Token.CLOSEPAREN)) {
|
||||||
if (tn.skip(Token.OPENBRACE)) {
|
if (tn.skip(Token.OPENBRACE)) {
|
||||||
const cases: SwitchCase[] = new Array(0);
|
const cases: SwitchCase[] = [];
|
||||||
while (!tn.skip(Token.CLOSEBRACE)) {
|
while (!tn.skip(Token.CLOSEBRACE)) {
|
||||||
const case_: SwitchCase | null = this.parseSwitchCase(tn);
|
const case_: SwitchCase | null = this.parseSwitchCase(tn);
|
||||||
if (!case_)
|
if (!case_)
|
||||||
@ -1232,8 +1267,9 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnaryPrefixExpression
|
// UnaryPrefixExpression
|
||||||
if ((<Expression>operand).kind != NodeKind.IDENTIFIER && (<Expression>operand).kind != NodeKind.ELEMENTACCESS && (<Expression>operand).kind != NodeKind.PROPERTYACCESS)
|
if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS)
|
||||||
this.error(DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, (<Expression>operand).range);
|
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));
|
return Expression.createUnaryPrefix(token, <Expression>operand, tn.range(startPos, tn.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,20 +1351,20 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tryParseTypeArgumentsBeforeArguments(tn: Tokenizer): IdentifierExpression[] | null {
|
tryParseTypeArgumentsBeforeArguments(tn: Tokenizer): TypeNode[] | null {
|
||||||
// at '<': Identifier (',' Identifier)* '>' '('
|
// at '<': Identifier (',' Identifier)* '>' '('
|
||||||
tn.mark();
|
tn.mark();
|
||||||
if (!tn.skip(Token.LESSTHAN))
|
if (!tn.skip(Token.LESSTHAN))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const typeArguments: IdentifierExpression[] = new Array();
|
const typeArguments: TypeNode[] = [];
|
||||||
do {
|
do {
|
||||||
const token: Token = tn.next();
|
const type: TypeNode | null = this.parseType(tn);
|
||||||
if (token != Token.IDENTIFIER) {
|
if (!type) {
|
||||||
tn.reset();
|
tn.reset();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
typeArguments.push(Expression.createIdentifier(tn.readIdentifier(), tn.range()));
|
typeArguments.push(type);
|
||||||
} while (tn.skip(Token.COMMA));
|
} while (tn.skip(Token.COMMA));
|
||||||
if (!(tn.skip(Token.GREATERTHAN) && tn.skip(Token.OPENPAREN))) {
|
if (!(tn.skip(Token.GREATERTHAN) && tn.skip(Token.OPENPAREN))) {
|
||||||
tn.reset();
|
tn.reset();
|
||||||
@ -1360,16 +1396,16 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
if (!expr)
|
if (!expr)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const startPos: i32 = expr.range.start;
|
const startPos: i32 = expr.range.start;
|
||||||
|
|
||||||
// CallExpression
|
// CallExpression
|
||||||
const typeArguments: IdentifierExpression[] | null = this.tryParseTypeArgumentsBeforeArguments(tn);
|
const typeArguments: TypeNode[] | null = this.tryParseTypeArgumentsBeforeArguments(tn);
|
||||||
// there might be better ways to distinguish a LESSTHAN from a CALL
|
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
|
||||||
if (typeArguments || tn.skip(Token.OPENPAREN)) {
|
if (typeArguments || tn.skip(Token.OPENPAREN)) {
|
||||||
const args: Expression[] | null = this.parseArguments(tn);
|
const args: Expression[] | null = this.parseArguments(tn);
|
||||||
if (!args)
|
if (!args)
|
||||||
return null;
|
return null;
|
||||||
expr = Expression.createCall(expr, typeArguments ? <IdentifierExpression[]>typeArguments : new Array(0), args, tn.range(startPos, tn.pos));
|
expr = Expression.createCall(expr, <TypeNode[]>(typeArguments ? typeArguments : []), args, tn.range(startPos, tn.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let token: Token;
|
let token: Token;
|
||||||
@ -1625,14 +1661,6 @@ function addModifier(modifier: Modifier, modifiers: Modifier[] | null): Modifier
|
|||||||
return modifiers;
|
return modifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasModifier(kind: ModifierKind, modifiers: Modifier[] | null): bool {
|
|
||||||
if (modifiers != null)
|
|
||||||
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i)
|
|
||||||
if (modifiers[i].modifierKind == kind)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModifier(kind: ModifierKind, modifiers: Modifier[]): Modifier {
|
function getModifier(kind: ModifierKind, modifiers: Modifier[]): Modifier {
|
||||||
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i)
|
||||||
if (modifiers[i].modifierKind == kind)
|
if (modifiers[i].modifierKind == kind)
|
||||||
|
137
src/program.ts
137
src/program.ts
@ -1,6 +1,5 @@
|
|||||||
import { Target } from "./compiler";
|
import { Target } from "./compiler";
|
||||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
import { hasModifier } from "./parser";
|
|
||||||
import { Type } from "./types";
|
import { Type } from "./types";
|
||||||
import {
|
import {
|
||||||
|
|
||||||
@ -24,12 +23,15 @@ import {
|
|||||||
NamespaceDeclaration,
|
NamespaceDeclaration,
|
||||||
Statement,
|
Statement,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableStatement
|
VariableStatement,
|
||||||
|
|
||||||
|
hasModifier
|
||||||
|
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
|
|
||||||
class QueuedExport {
|
class QueuedExport {
|
||||||
importName: string;
|
isForeign: bool;
|
||||||
|
referencedName: string;
|
||||||
member: ExportMember;
|
member: ExportMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +49,12 @@ export class Program extends DiagnosticEmitter {
|
|||||||
diagnosticsOffset: i32 = 0;
|
diagnosticsOffset: i32 = 0;
|
||||||
target: Target = Target.WASM32;
|
target: Target = Target.WASM32;
|
||||||
|
|
||||||
/** Internal map of names to declarations. */
|
/** Map of internal names to declarations. */
|
||||||
names: Map<string,DeclarationStatement> = new Map();
|
names: Map<string,DeclarationStatement> = new Map();
|
||||||
/** Separate map of internal type names to declarations. */
|
/** Separate map of internal type names to declarations. */
|
||||||
types: Map<string,Type> = typesStub;
|
types: Map<string,Type> = typesStub;
|
||||||
/** Separate map of internal export names to declarations. */
|
/** Separate map of internal export names to declarations. */
|
||||||
exports: Map<string,DeclarationStatement> = new Map();
|
exports: Map<string,DeclarationStatement> = new Map(); // not global exports
|
||||||
|
|
||||||
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||||
super(diagnostics);
|
super(diagnostics);
|
||||||
@ -81,7 +83,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
const queuedExports: Map<string,QueuedExport> = new Map();
|
const queuedExports: Map<string,QueuedExport> = new Map();
|
||||||
const queuedImports: QueuedImport[] = new Array();
|
const queuedImports: QueuedImport[] = new Array();
|
||||||
|
|
||||||
// build initial lookup maps of internal and export names to declarations
|
// build initial lookup maps of internal names to declarations
|
||||||
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
|
||||||
const source: Source = this.sources[i];
|
const source: Source = this.sources[i];
|
||||||
const statements: Statement[] = source.statements;
|
const statements: Statement[] = source.statements;
|
||||||
@ -125,21 +127,36 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// at this point queued exports should be resolvable
|
// at this point queued exports should be resolvable
|
||||||
for (let [exportName, queuedExport] of queuedExports.entries()) {
|
// export { add }
|
||||||
const seen: Set<QueuedExport> = new Set();
|
// export { sub } from "./other"
|
||||||
while (queuedExports.has(queuedExport.importName)) {
|
for (let [exportName, queuedExport] of queuedExports) {
|
||||||
queuedExport = <QueuedExport>queuedExports.get(queuedExport.importName);
|
if (queuedExport.isForeign) {
|
||||||
if (seen.has(queuedExport))
|
const seen: Set<QueuedExport> = new Set();
|
||||||
break;
|
while (queuedExports.has(queuedExport.referencedName)) {
|
||||||
seen.add(queuedExport);
|
queuedExport = <QueuedExport>queuedExports.get(queuedExport.referencedName);
|
||||||
|
if (seen.has(queuedExport))
|
||||||
|
break;
|
||||||
|
seen.add(queuedExport);
|
||||||
|
}
|
||||||
|
if (this.exports.has(queuedExport.referencedName)) {
|
||||||
|
const declaration: DeclarationStatement = <DeclarationStatement>this.exports.get(queuedExport.referencedName);
|
||||||
|
this.addExport(exportName, declaration);
|
||||||
|
if (queuedExport.member.range.source.isEntry)
|
||||||
|
declaration.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||||
|
} else
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
|
||||||
|
} else /* local */ {
|
||||||
|
if (this.names.has(queuedExport.referencedName)) {
|
||||||
|
const declaration: DeclarationStatement = <DeclarationStatement>this.names.get(queuedExport.referencedName);
|
||||||
|
this.addExport(exportName, declaration);
|
||||||
|
if (queuedExport.member.range.source.isEntry)
|
||||||
|
declaration.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||||
|
} else
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
|
||||||
}
|
}
|
||||||
if (this.exports.has(queuedExport.importName))
|
|
||||||
this.addExport(exportName, <DeclarationStatement>this.exports.get(queuedExport.importName));
|
|
||||||
else
|
|
||||||
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.importName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point also queued imports should be resolvable
|
// at this point queued imports should be resolvable as well
|
||||||
for (let i: i32 = 0, k: i32 = queuedImports.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = queuedImports.length; i < k; ++i) {
|
||||||
const queuedImport: QueuedImport = queuedImports[i];
|
const queuedImport: QueuedImport = queuedImports[i];
|
||||||
const internalName: string = queuedImport.internalName;
|
const internalName: string = queuedImport.internalName;
|
||||||
@ -147,7 +164,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
let importName: string = queuedImport.importName;
|
let importName: string = queuedImport.importName;
|
||||||
while (queuedExports.has(importName)) {
|
while (queuedExports.has(importName)) {
|
||||||
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(importName);
|
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(importName);
|
||||||
importName = queuedExport.importName;
|
importName = queuedExport.referencedName;
|
||||||
if (seen.has(queuedExport))
|
if (seen.has(queuedExport))
|
||||||
break;
|
break;
|
||||||
seen.add(queuedExport);
|
seen.add(queuedExport);
|
||||||
@ -167,7 +184,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeClass(declaration: ClassDeclaration): void {
|
private initializeClass(declaration: ClassDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -191,12 +208,11 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeField(declaration: FieldDeclaration): void {
|
private initializeField(declaration: FieldDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
this.addName(declaration.internalName, declaration);
|
||||||
this.addName(internalName, declaration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeEnum(declaration: EnumDeclaration): void {
|
private initializeEnum(declaration: EnumDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -206,8 +222,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeEnumValue(declaration: EnumValueDeclaration): void {
|
private initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
this.addName(declaration.internalName, declaration);
|
||||||
this.addName(internalName, declaration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
|
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
|
||||||
@ -222,16 +237,20 @@ export class Program extends DiagnosticEmitter {
|
|||||||
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
|
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
|
||||||
else {
|
else {
|
||||||
const queuedExport: QueuedExport = new QueuedExport();
|
const queuedExport: QueuedExport = new QueuedExport();
|
||||||
queuedExport.importName = normalizedPath == null
|
if (normalizedPath == null) {
|
||||||
? member.range.source.normalizedPath + "/" + member.identifier.name // local
|
queuedExport.isForeign = false;
|
||||||
: (<string>normalizedPath) + "/" + member.identifier.name; // re-export
|
queuedExport.referencedName = member.range.source.normalizedPath + "/" + member.identifier.name;
|
||||||
|
} else {
|
||||||
|
queuedExport.isForeign = true;
|
||||||
|
queuedExport.referencedName = (<string>normalizedPath) + "/" + member.identifier.name;
|
||||||
|
}
|
||||||
queuedExport.member = member;
|
queuedExport.member = member;
|
||||||
queuedExports.set(exportName, queuedExport);
|
queuedExports.set(exportName, queuedExport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeFunction(declaration: FunctionDeclaration): void {
|
private initializeFunction(declaration: FunctionDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -251,12 +270,12 @@ export class Program extends DiagnosticEmitter {
|
|||||||
const seen: Set<QueuedExport> = new Set();
|
const seen: Set<QueuedExport> = new Set();
|
||||||
while (queuedExports.has(resolvedImportName)) {
|
while (queuedExports.has(resolvedImportName)) {
|
||||||
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(resolvedImportName);
|
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(resolvedImportName);
|
||||||
resolvedImportName = queuedExport.importName;
|
resolvedImportName = queuedExport.referencedName;
|
||||||
if (seen.has(queuedExport))
|
if (seen.has(queuedExport))
|
||||||
break;
|
break;
|
||||||
seen.add(queuedExport);
|
seen.add(queuedExport);
|
||||||
}
|
}
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
if (this.exports.has(resolvedImportName)) { // resolvable right away
|
if (this.exports.has(resolvedImportName)) { // resolvable right away
|
||||||
if (this.names.has(internalName))
|
if (this.names.has(internalName))
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||||
@ -272,7 +291,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeInterface(declaration: InterfaceDeclaration): void {
|
private initializeInterface(declaration: InterfaceDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -296,12 +315,11 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeMethod(declaration: MethodDeclaration): void {
|
private initializeMethod(declaration: MethodDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
this.addName(declaration.internalName, declaration);
|
||||||
this.addName(internalName, declaration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeNamespace(declaration: NamespaceDeclaration): void {
|
private initializeNamespace(declaration: NamespaceDeclaration): void {
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -345,7 +363,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
const isExport: bool = !isNamespaceMember && hasModifier(ModifierKind.EXPORT, statement.modifiers);
|
const isExport: bool = !isNamespaceMember && hasModifier(ModifierKind.EXPORT, statement.modifiers);
|
||||||
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
|
||||||
const declaration: VariableDeclaration = declarations[i];
|
const declaration: VariableDeclaration = declarations[i];
|
||||||
const internalName: string = this.mangleInternalName(declaration);
|
const internalName: string = declaration.internalName;
|
||||||
this.addName(internalName, declaration);
|
this.addName(internalName, declaration);
|
||||||
if (isExport)
|
if (isExport)
|
||||||
this.addExport(/* same as */internalName, declaration);
|
this.addExport(/* same as */internalName, declaration);
|
||||||
@ -362,51 +380,10 @@ export class Program extends DiagnosticEmitter {
|
|||||||
private addExport(exportName: string, declaration: DeclarationStatement): void {
|
private addExport(exportName: string, declaration: DeclarationStatement): void {
|
||||||
if (this.exports.has(exportName))
|
if (this.exports.has(exportName))
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, exportName); // recoverable
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, exportName); // recoverable
|
||||||
else
|
else {
|
||||||
this.exports.set(exportName, declaration);
|
this.exports.set(exportName, declaration);
|
||||||
}
|
if (declaration.range.source.isEntry)
|
||||||
|
declaration.globalExportName = declaration.identifier.name;
|
||||||
mangleInternalName(declaration: DeclarationStatement): string {
|
|
||||||
let name: string = declaration.identifier.name;
|
|
||||||
let parent: Node | null = declaration.parent;
|
|
||||||
if (parent) {
|
|
||||||
switch (parent.kind) {
|
|
||||||
|
|
||||||
case NodeKind.SOURCE:
|
|
||||||
return (<Source>parent).normalizedPath + "/" + name;
|
|
||||||
|
|
||||||
case NodeKind.CLASS: {
|
|
||||||
if (
|
|
||||||
(declaration.kind == NodeKind.FIELD && !hasModifier(ModifierKind.STATIC, (<FieldDeclaration>declaration).modifiers)) ||
|
|
||||||
(declaration.kind == NodeKind.METHOD && !hasModifier(ModifierKind.STATIC, (<MethodDeclaration>declaration).modifiers))
|
|
||||||
)
|
|
||||||
return this.mangleInternalName(<DeclarationStatement>parent) + "#" + name;
|
|
||||||
// otherwise fall through
|
|
||||||
}
|
|
||||||
case NodeKind.ENUM:
|
|
||||||
case NodeKind.ENUMVALUE:
|
|
||||||
case NodeKind.NAMESPACE:
|
|
||||||
return this.mangleInternalName(<DeclarationStatement>parent) + "." + name;
|
|
||||||
|
|
||||||
case NodeKind.IMPORT: {
|
|
||||||
const importParent: Node | null = (<ImportStatement>parent).parent;
|
|
||||||
if (importParent && importParent.kind == NodeKind.SOURCE)
|
|
||||||
return (<Source>importParent).path + "/" + name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NodeKind.VARIABLE: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
throw new Error("unexpected parent");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
"outDir": "../out"
|
"outDir": "../out"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -28,25 +28,18 @@ mod.addExport("lit", "lit");
|
|||||||
|
|
||||||
mod.addGlobal("42", Type.I32, false, mod.createI32(42));
|
mod.addGlobal("42", Type.I32, false, mod.createI32(42));
|
||||||
|
|
||||||
const aSwitch = mod.addFunctionType("i", Type.I32, []);
|
const aSwitch = mod.addFunctionType("ii", Type.I32, [ Type.I32 ]);
|
||||||
mod.addFunction("aSwitch", aSwitch, [], mod.createBlock("", [
|
const rl = mod.createRelooper();
|
||||||
mod.createBlock("a", [
|
const b0 = rl.addBlockWithSwitch(mod.createNop(), mod.createGetLocal(0, Type.I32));
|
||||||
mod.createBlock("b", [
|
let b1, b2, b3;
|
||||||
mod.createBlock("c", [
|
rl.addBranchForSwitch(b0, b2 = rl.addBlock(mod.createReturn(mod.createI32(1))), [1]); // indexed branch
|
||||||
mod.createBlock("d", [
|
rl.addBranchForSwitch(b0, b3 = rl.addBlock(mod.createReturn(mod.createI32(2))), [2]); // indexed branch
|
||||||
mod.createSwitch(
|
rl.addBranch(b0, b1 = rl.addBlock(mod.createDrop(mod.createI32(0)))); // default branch
|
||||||
["a", "b", "c"],
|
rl.addBranch(b1, b2);
|
||||||
"d",
|
|
||||||
mod.createI32(4)
|
mod.addFunction("aSwitch", aSwitch, [ Type.I32 ], mod.createBlock(null, [
|
||||||
)
|
rl.renderAndDispose(b0, 1),
|
||||||
]),
|
mod.createUnreachable()
|
||||||
mod.createReturn(mod.createI32(3))
|
|
||||||
]),
|
|
||||||
mod.createReturn(mod.createI32(2))
|
|
||||||
]),
|
|
||||||
mod.createReturn(mod.createI32(1))
|
|
||||||
]),
|
|
||||||
mod.createReturn(mod.createI32(0))
|
|
||||||
]));
|
]));
|
||||||
mod.addExport("aSwitch", "aSwitch");
|
mod.addExport("aSwitch", "aSwitch");
|
||||||
|
|
||||||
|
@ -11,12 +11,37 @@ import { Parser } from "../src/parser";
|
|||||||
]); */
|
]); */
|
||||||
|
|
||||||
const files: Map<string,string> = new Map([
|
const files: Map<string,string> = new Map([
|
||||||
["main", `
|
["main",
|
||||||
export function add(a: i32, b: i32): i32 { let c: i32 = a + b; return c; }
|
`
|
||||||
`]
|
function add(a: i32, b: i32): i32 { return a + b; };
|
||||||
|
export { add };
|
||||||
|
export { sub as notadd } from "../other";
|
||||||
|
2+3;
|
||||||
|
export function switchMe(n: i32): i32 {
|
||||||
|
switch (n) {
|
||||||
|
case 0:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 2;
|
||||||
|
case 1:
|
||||||
|
return 1;
|
||||||
|
case -1:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
`],
|
||||||
|
|
||||||
|
["../other",
|
||||||
|
`
|
||||||
|
export function sub(a: i32, b: i32): i32 { return a - b + c; };
|
||||||
|
let c: i32 = 42 >> 31;
|
||||||
|
1+2;
|
||||||
|
`]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
|
||||||
parser.parseFile(<string>files.get("main"), "main", true);
|
parser.parseFile(<string>files.get("main"), "main", true);
|
||||||
do {
|
do {
|
||||||
let nextFile = parser.nextFile();
|
let nextFile = parser.nextFile();
|
||||||
@ -30,15 +55,7 @@ const program = parser.finish();
|
|||||||
const compiler = new Compiler(program);
|
const compiler = new Compiler(program);
|
||||||
const module = compiler.compile();
|
const module = compiler.compile();
|
||||||
|
|
||||||
console.log("names", program.names.keys());
|
// module.optimize();
|
||||||
console.log("exports", program.exports.keys());
|
module.validate();
|
||||||
|
|
||||||
module.optimize();
|
|
||||||
// module.validate(); // global initializers can't use i32.add etc. yet
|
|
||||||
if (!module.noEmit)
|
if (!module.noEmit)
|
||||||
_BinaryenModulePrint(module.ref);
|
_BinaryenModulePrint(module.ref);
|
||||||
|
|
||||||
/* console.log("--- statements ---");
|
|
||||||
compiler.statements.forEach(stmt => {
|
|
||||||
_BinaryenExpressionPrint(stmt);
|
|
||||||
}); */
|
|
Reference in New Issue
Block a user