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