This commit is contained in:
dcodeIO 2017-10-07 14:29:43 +02:00
parent f55fc70220
commit 50116acede
12 changed files with 762 additions and 179 deletions

View File

@ -94,11 +94,16 @@ export enum NodeKind {
BINARY,
CALL,
ELEMENTACCESS,
FALSE,
LITERAL,
NEW,
NULL,
PARENTHESIZED,
PROPERTYACCESS,
SELECT,
SUPER,
THIS,
TRUE,
UNARYPOSTFIX,
UNARYPREFIX,
@ -163,6 +168,7 @@ export function nodeKindToString(kind: NodeKind): string {
case NodeKind.EXPORTIMPORT: return "EXPORTIMPORT";
case NodeKind.EXPRESSION: return "EXPRESSION";
case NodeKind.INTERFACE: return "INTERFACE";
case NodeKind.FALSE: return "FALSE";
case NodeKind.FOR: return "FOR";
case NodeKind.FUNCTION: return "FUNCTION";
case NodeKind.IF: return "IF";
@ -170,10 +176,14 @@ export function nodeKindToString(kind: NodeKind): string {
case NodeKind.IMPORTDECLARATION: return "IMPORTDECLARATION";
case NodeKind.METHOD: return "METHOD";
case NodeKind.NAMESPACE: return "NAMESPACE";
case NodeKind.NULL: return "NULL";
case NodeKind.FIELD: return "PROPERTY";
case NodeKind.RETURN: return "RETURN";
case NodeKind.SUPER: return "SUPER";
case NodeKind.SWITCH: return "SWITCH";
case NodeKind.THIS: return "THIS";
case NodeKind.THROW: return "THROW";
case NodeKind.TRUE: return "TRUE";
case NodeKind.TRY: return "TRY";
case NodeKind.VARIABLE: return "VARIABLE";
case NodeKind.VARIABLEDECLARATION: return "VARIABLEDECLARATION";
@ -286,6 +296,12 @@ export abstract class Expression extends Node {
return expr;
}
static createFalse(range: Range): FalseExpression {
const expr: FalseExpression = new FalseExpression();
expr.range = range;
return expr;
}
static createFloatLiteral(value: f64, range: Range): FloatLiteralExpression {
const expr: FloatLiteralExpression = new FloatLiteralExpression();
expr.range = range;
@ -310,6 +326,12 @@ export abstract class Expression extends Node {
return expr;
}
static createNull(range: Range): NullExpression {
const expr: NullExpression = new NullExpression();
expr.range = range;
return expr;
}
static createParenthesized(expression: Expression, range: Range): ParenthesizedExpression {
const expr: ParenthesizedExpression = new ParenthesizedExpression();
expr.range = range;
@ -348,6 +370,24 @@ export abstract class Expression extends Node {
return expr;
}
static createSuper(range: Range): SuperExpression {
const expr: SuperExpression = new SuperExpression();
expr.range = range;
return expr;
}
static createThis(range: Range): ThisExpression {
const expr: ThisExpression = new ThisExpression();
expr.range = range;
return expr;
}
static createTrue(range: Range): TrueExpression {
const expr: TrueExpression = new TrueExpression();
expr.range = range;
return expr;
}
static createUnaryPostfix(operator: Token, expression: Expression, range: Range): UnaryPostfixExpression {
const expr: UnaryPostfixExpression = new UnaryPostfixExpression();
expr.range = range;
@ -614,6 +654,11 @@ export class NewExpression extends CallExpression {
}
}
export class NullExpression extends IdentifierExpression {
kind = NodeKind.NULL;
name = "null";
}
export class ParenthesizedExpression extends Expression {
kind = NodeKind.PARENTHESIZED;
@ -711,6 +756,26 @@ export class StringLiteralExpression extends LiteralExpression {
}
}
export class SuperExpression extends IdentifierExpression {
kind = NodeKind.SUPER;
name = "super";
}
export class ThisExpression extends IdentifierExpression {
kind = NodeKind.THIS;
name = "this";
}
export class TrueExpression extends IdentifierExpression {
kind = NodeKind.TRUE;
name = "true";
}
export class FalseExpression extends IdentifierExpression {
kind = NodeKind.FALSE;
name = "false";
}
export abstract class UnaryExpression extends Expression {
operator: Token;
expression: Expression;
@ -798,7 +863,7 @@ export abstract class Statement extends Node {
static createBlock(statements: Statement[], range: Range): BlockStatement {
const stmt: BlockStatement = new BlockStatement();
stmt.range = range;
for (let i: i32 = 0, k = (stmt.statements = statements).length; i < k; ++i) statements[i].parent = stmt;
for (let i: i32 = 0, k: i32 = (stmt.statements = statements).length; i < k; ++i) statements[i].parent = stmt;
return stmt;
}
@ -1055,13 +1120,14 @@ export abstract class Statement extends Node {
return stmt;
}
static createTry(statements: Statement[], catchVariable: VariableDeclaration, catchStatements: Statement[], range: Range): TryStatement {
static createTry(statements: Statement[], catchVariable: IdentifierExpression | null, catchStatements: Statement[] | null, finallyStatements: Statement[] | null, range: Range): TryStatement {
const stmt: TryStatement = new TryStatement();
stmt.range = range;
let i: i32, k: i32;
for (i = 0, k = (stmt.statements = statements).length; i < k; ++i) statements[i].parent = stmt;
(stmt.catchVariable = catchVariable).parent = stmt;
for (i = 0, k = (stmt.catchStatements = catchStatements).length; i < k; ++i) catchStatements[i].parent = stmt;
if (stmt.catchVariable = catchVariable) (<IdentifierExpression>catchVariable).parent = stmt;
if (stmt.catchStatements = catchStatements) for (i = 0, k = (<Statement[]>catchStatements).length; i < k; ++i) (<Statement[]>catchStatements)[i].parent = stmt;
if (stmt.finallyStatements = finallyStatements) for (i = 0, k = (<Statement[]>finallyStatements).length; i < k; ++i) (<Statement[]>finallyStatements)[i].parent = stmt;
return stmt;
}
@ -1117,7 +1183,7 @@ export class Source extends Node {
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
serialize(sb: string[]): void {
for (let i: i32 = 0, k = this.statements.length; i < k; ++i) {
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
const statement: Statement = this.statements[i];
statement.serialize(sb);
const last: string = sb[sb.length - 1];
@ -1142,7 +1208,7 @@ export class BlockStatement extends Statement {
sb.push("{\n");
for (let i: i32 = 0, k: i32 = this.statements.length; i < k; ++i) {
this.statements[i].serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1211,7 +1277,7 @@ export class ClassDeclaration extends DeclarationStatement {
sb.push(" {\n");
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1493,7 +1559,7 @@ export class FunctionDeclaration extends DeclarationStatement {
for (i = 0, k = (<Statement[]>this.statements).length; i < k; ++i) {
const statement: Statement = (<Statement[]>this.statements)[i];
statement.serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1592,7 +1658,7 @@ export class InterfaceDeclaration extends DeclarationStatement {
sb.push(" {\n");
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1623,7 +1689,7 @@ export class NamespaceDeclaration extends DeclarationStatement {
kind = NodeKind.NAMESPACE;
modifiers: Modifier[];
members: DeclarationStatement[];
members: Statement[];
serialize(sb: string[]): void {
let i: i32, k: i32;
@ -1636,7 +1702,7 @@ export class NamespaceDeclaration extends DeclarationStatement {
sb.push(" {\n");
for (i = 0, k = this.members.length; i < k; ++i) {
this.members[i].serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1720,7 +1786,7 @@ export class SwitchCase extends Node {
if (i > 0)
sb.push("\n");
this.statements[i].serialize(sb);
if (builderEndsWith(CharCode.CLOSEBRACE, sb))
if (builderEndsWith(sb, CharCode.CLOSEBRACE))
sb.push("\n");
else
sb.push(";\n");
@ -1761,8 +1827,9 @@ export class TryStatement extends Statement {
kind = NodeKind.TRY;
statements: Statement[];
catchVariable: VariableDeclaration;
catchStatements: Statement[];
catchVariable: IdentifierExpression | null;
catchStatements: Statement[] | null;
finallyStatements: Statement[] | null;
serialize(sb: string[]): void {
sb.push("try {\n");
@ -1771,12 +1838,22 @@ export class TryStatement extends Statement {
this.statements[i].serialize(sb);
sb.push(";\n");
}
sb.push("} catch (");
this.catchVariable.serialize(sb);
sb.push(") {\n");
for (i = 0, k = this.catchStatements.length; i < k; ++i) {
this.catchStatements[i].serialize(sb);
sb.push(";\n");
if (this.catchVariable) {
sb.push("} catch (");
(<IdentifierExpression>this.catchVariable).serialize(sb);
sb.push(") {\n");
if (this.catchStatements)
for (i = 0, k = (<Statement[]>this.catchStatements).length; i < k; ++i) {
(<Statement[]>this.catchStatements)[i].serialize(sb);
sb.push(";\n");
}
}
if (this.finallyStatements) {
sb.push("} finally {\n");
for (i = 0, k = (<Statement[]>this.finallyStatements).length; i < k; ++i) {
(<Statement[]>this.finallyStatements)[i].serialize(sb);
sb.push(";\n");
}
}
sb.push("}");
}
@ -1857,7 +1934,7 @@ export function serialize(node: Node, indent: i32 = 0): string {
return sb.join("");
}
function builderEndsWith(code: CharCode, sb: string[]): bool {
function builderEndsWith(sb: string[], code: CharCode): bool {
if (sb.length) {
const last: string = sb[sb.length - 1];
return last.length ? last.charCodeAt(last.length - 1) == code : false;

View File

@ -172,11 +172,15 @@ export class Module {
ref: BinaryenModuleRef;
lit: BinaryenLiteral;
noEmit: bool;
static MAX_MEMORY_WASM32: BinaryenIndex = 0xffff;
static create(): Module {
const module: Module = new Module();
module.ref = _BinaryenModuleCreate();
module.lit = _malloc(16);
module.noEmit = false;
return module;
}
@ -186,17 +190,27 @@ export class Module {
const module: Module = new Module();
module.ref = _BinaryenModuleRead(cArr, buffer.length);
module.lit = _malloc(16);
module.noEmit = false;
return module;
} finally {
_free(cArr);
}
}
static MAX_MEMORY_WASM32: BinaryenIndex = 0xffff;
static createStub(): Module {
const module: Module = new Module();
module.ref = 0;
module.lit = 0;
module.noEmit = true;
return module;
}
private constructor() { }
// types
addFunctionType(name: string, result: Type, paramTypes: Type[]): BinaryenFunctionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(paramTypes);
try {
@ -208,6 +222,7 @@ export class Module {
}
getFunctionTypeBySignature(result: Type, paramTypes: Type[]): BinaryenFunctionTypeRef {
if (this.noEmit) return 0;
const cArr: CArray<i32> = allocI32Array(paramTypes);
try {
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes.length);
@ -219,34 +234,41 @@ export class Module {
// expressions
createI32(value: i32): BinaryenExpressionRef {
if (this.noEmit) return 0;
_BinaryenLiteralInt32(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createI64(lo: i32, hi: i32): BinaryenExpressionRef {
if (this.noEmit) return 0;
_BinaryenLiteralInt64(this.lit, lo, hi);
return _BinaryenConst(this.ref, this.lit);
}
createF32(value: f32): BinaryenExpressionRef {
if (this.noEmit) return 0;
_BinaryenLiteralFloat32(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createF64(value: f64): BinaryenExpressionRef {
if (this.noEmit) return 0;
_BinaryenLiteralFloat64(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createUnary(op: UnaryOp, expr: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenUnary(this.ref, op, expr);
}
createBinary(op: BinaryOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenBinary(this.ref, op, left, right);
}
createHost(op: HostOp, name: string | null = null, operands: BinaryenExpressionRef[] | null = null): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(operands);
try {
@ -258,14 +280,17 @@ export class Module {
}
createGetLocal(index: i32, type: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenGetLocal(this.ref, index, type);
}
createTeeLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenTeeLocal(this.ref, index, value);
}
createGetGlobal(name: string, type: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
try {
return _BinaryenGetGlobal(this.ref, cStr, type);
@ -274,21 +299,15 @@ export class Module {
}
}
createTeeGlobal(name: string, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef {
// emulated, lives here for simplicity reasons
return this.createBlock(null, [
this.createSetGlobal(name, value),
this.createGetGlobal(name, type)
], type);
}
// statements
createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenSetLocal(this.ref, index, value);
}
createSetGlobal(name: string, value: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
try {
return _BinaryenSetGlobal(this.ref, cStr, value);
@ -298,6 +317,7 @@ export class Module {
}
createBlock(label: string | null, children: BinaryenExpressionRef[], type: Type = Type.Undefined): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(label);
const cArr: CArray<i32> = allocI32Array(children);
try {
@ -309,6 +329,7 @@ export class Module {
}
createBreak(label: string | null, condition: BinaryenExpressionRef = 0, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(label);
try {
return _BinaryenBreak(this.ref, cStr, condition, value);
@ -318,10 +339,12 @@ export class Module {
}
createDrop(expression: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenDrop(this.ref, expression);
}
createLoop(label: string | null, body: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(label);
try {
return _BinaryenLoop(this.ref, cStr, body);
@ -331,22 +354,27 @@ export class Module {
}
createIf(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef = 0): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenIf(this.ref, condition, ifTrue, ifFalse);
}
createNop(): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenNop(this.ref);
}
createReturn(expression: BinaryenExpressionRef = 0): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenReturn(this.ref, expression);
}
createSelect(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
}
createSwitch(names: string[], defaultName: string | null, condition: BinaryenExpressionRef, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
if (this.noEmit) return 0;
const strs: CString[] = new Array(names.length);
let i: i32, k: i32 = names.length;
for (i = 0; i < k; ++i) strs[i] = allocString(names[i]);
@ -362,6 +390,7 @@ export class Module {
}
createCall(target: BinaryenFunctionRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cArr: CArray<i32> = allocI32Array(operands);
try {
return _BinaryenCall(this.ref, target, cArr, operands.length, returnType);
@ -371,6 +400,7 @@ export class Module {
}
createCallImport(target: BinaryenImportRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
const cArr: CArray<i32> = allocI32Array(operands);
try {
return _BinaryenCallImport(this.ref, target, cArr, operands.length, returnType);
@ -380,12 +410,14 @@ export class Module {
}
createUnreachable(): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenUnreachable(this.ref);
}
// meta
addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenImportRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
try {
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
@ -395,6 +427,7 @@ export class Module {
}
addFunction(name: string, type: BinaryenFunctionTypeRef, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(varTypes);
try {
@ -406,6 +439,7 @@ export class Module {
}
addExport(internalName: string, externalName: string): BinaryenExportRef {
if (this.noEmit) return 0;
const cStr1: CString = allocString(internalName);
const cStr2: CString = allocString(externalName);
try {
@ -417,6 +451,7 @@ export class Module {
}
removeExport(externalName: string): void {
if (this.noEmit) return;
const cStr = allocString(externalName);
try {
_BinaryenRemoveExport(this.ref, cStr);
@ -426,6 +461,7 @@ export class Module {
}
addImport(internalName: string, externalModuleName: string, externalBaseName: string, type: BinaryenFunctionTypeRef): BinaryenImportRef {
if (this.noEmit) return 0;
const cStr1: CString = allocString(internalName);
const cStr2: CString = allocString(externalModuleName);
const cStr3: CString = allocString(externalBaseName);
@ -439,6 +475,7 @@ export class Module {
}
removeImport(internalName: string): void {
if (this.noEmit) return;
const cStr: CString = allocString(internalName);
try {
_BinaryenRemoveImport(this.ref, cStr);
@ -448,6 +485,7 @@ export class Module {
}
setMemory(initial: BinaryenIndex, maximum: BinaryenIndex, segments: MemorySegment[], target: Target, exportName: string | null = null): void {
if (this.noEmit) return;
const cStr: CString = allocString(exportName);
let i: i32, k: i32 = segments.length;
const segs: CArray<u8>[] = new Array(k);
@ -477,18 +515,22 @@ export class Module {
}
setStart(func: BinaryenFunctionRef): void {
if (this.noEmit) return;
_BinaryenSetStart(this.ref, func);
}
optimize(): void {
if (this.noEmit) return;
_BinaryenModuleOptimize(this.ref);
}
validate(): bool {
if (this.noEmit) return false;
return _BinaryenModuleValidate(this.ref) == 1;
}
dispose(): void {
if (!this.ref) return; // sic
_BinaryenModuleDispose(this.ref);
_free(this.lit);
}
@ -547,7 +589,7 @@ function allocString(str: string | null): CString {
if (!str) return 0;
const ptr: usize = _malloc(stringLengthUTF8((<string>str)) + 1);
let idx: usize = ptr;
for (let i: i32 = 0, k = (<string>str).length; i < k; ++i) {
for (let i: i32 = 0, k: i32 = (<string>str).length; i < k; ++i) {
let u: i32 = (<string>str).charCodeAt(i);
if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k)
u = 0x10000 + ((u & 0x3FF) << 10) | ((<string>str).charCodeAt(++i) & 0x3FF);

View File

@ -6,8 +6,10 @@ import { CharCode, I64, U64, normalizePath, sb } from "./util";
import { Token } from "./tokenizer";
import {
Node,
NodeKind,
TypeNode,
TypeParameter,
Source,
// statements
@ -65,8 +67,10 @@ import {
ClassType,
FunctionType,
LocalType,
Type,
TypeKind
TypeKind,
typeArgumentsToString
} from "./types";
@ -80,13 +84,15 @@ export class Options {
noEmit: bool = false;
}
const VOID: BinaryenExpressionRef = 0;
export class Compiler extends DiagnosticEmitter {
program: Program;
options: Options;
module: Module;
startFunction: FunctionType = new FunctionType(Type.void, new Array());
startFunction: FunctionType = new FunctionType([], [], Type.void);
startFunctionBody: BinaryenExpressionRef[] = new Array();
currentType: Type = Type.void;
@ -99,7 +105,7 @@ export class Compiler extends DiagnosticEmitter {
classes: Map<string,ClassType> = new Map();
enums: Set<string> = new Set();
functions: Map<string,FunctionType> = new Map();
globals: Set<string> = new Set();
globals: Map<string,Type> = new Map();
static compile(program: Program, options: Options | null = null): Module {
const compiler: Compiler = new Compiler(program, options);
@ -110,7 +116,8 @@ export class Compiler extends DiagnosticEmitter {
super(program.diagnostics);
this.program = program;
this.options = options ? options : new Options();
this.module = 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 {
@ -121,7 +128,7 @@ export class Compiler extends DiagnosticEmitter {
// start by compiling entry file exports
const entrySource: Source = program.sources[0];
for (let i: i32 = 0, k = entrySource.statements.length; i < k; ++i) {
for (let i: i32 = 0, k: i32 = entrySource.statements.length; i < k; ++i) {
const statement: Statement = entrySource.statements[i];
switch (statement.kind) {
@ -177,7 +184,7 @@ export class Compiler extends DiagnosticEmitter {
if (!typeRef)
typeRef = this.module.addFunctionType("v", BinaryenType.None, []);
this.module.setStart(
this.module.addFunction(".start", typeRef, [], this.module.createBlock("", this.startFunctionBody))
this.module.addFunction("", typeRef, typesToBinaryenTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody))
);
}
@ -207,43 +214,146 @@ export class Compiler extends DiagnosticEmitter {
}
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);
previousValueName = this.compileEnumValue(valueDeclarations[i], previousValueName, isExport);
this.enums.add(name);
}
compileEnumValue(declaration: EnumValueDeclaration, previousName: string | null): string {
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;
if (!this.options.noEmit) {
if (!initializer) initializer = previousName == null
? this.module.createI32(0)
: this.module.createBinary(BinaryOp.AddI32,
this.module.createGetGlobal(previousName, BinaryenType.I32),
this.module.createI32(1)
);
this.module.addGlobal(name, BinaryenType.I32, false, initializer);
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)
);
}
}
if (initializeInStart) {
this.module.addGlobal(name, BinaryenType.I32, true, this.module.createI32(-1));
this.startFunctionBody.push(this.module.createSetGlobal(name, 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;
}
compileFunction(declaration: FunctionDeclaration, typeArguments: Type[]): void {
/* const binaryenResultType: BinaryenType = declaration.returnType ? this.resolveType(<TypeNode>declaration.returnType, true) : BinaryenType.None;
const binaryenParamTypes: BinaryenType[] = new Array();
checkTypeArguments(typeParameters: TypeParameter[], typeArguments: Type[], reportNode: Node | null = null): bool {
if (typeParameters.length != typeArguments.length) {
if (reportNode)
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 TypeParameter#extendsType
return true;
}
compileFunction(declaration: FunctionDeclaration, typeArguments: Type[], inheritedTypeArgumentsMap: Map<string,Type> | null = null, reportNode: Node | null = null): void {
if (!declaration.statements) {
if (reportNode)
this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, (<Node>reportNode).range);
return;
}
if (!this.checkTypeArguments(declaration.typeParameters, typeArguments, reportNode)) // reports if requested
return;
let globalName: string = this.program.mangleInternalName(declaration);
// inherit type arguments, i.e. from class
const typeArgumentsMap: Map<string,Type> = new Map();
if (inheritedTypeArgumentsMap)
for (let [key, val] of (<Map<string,Type>>inheritedTypeArgumentsMap))
typeArgumentsMap.set(key, val);
// set (possibly override) type arguments for this specific call
let i: i32, k: i32 = typeArguments.length;
if (k) {
for (i = 0; i < k; ++i)
typeArgumentsMap.set(declaration.typeParameters[i].identifier.name, typeArguments[i]);
globalName += typeArgumentsToString(typeArguments);
}
if (this.functions.has(globalName)) {
if (reportNode)
this.error(DiagnosticCode.Duplicate_function_implementation, (<Node>reportNode).range);
return;
}
// resolve parameters
k = declaration.parameters.length;
const parameterNames: string[] = new Array(k);
const parameterTypes: Type[] = new Array(k);
for (i = 0; i < k; ++i) {
parameterNames[i] = declaration.parameters[i].identifier.name;
const typeNode: TypeNode | null = declaration.parameters[i].type;
if (typeNode) {
const type: Type | null = this.resolveType(<TypeNode>typeNode, typeArgumentsMap, true); // reports
if (type)
parameterTypes[i] = <Type>type;
else
return;
} else
return; // TODO: infer type? (currently reported by parser)
}
// resolve return type
const typeNode: TypeNode | null = declaration.returnType;
let returnType: Type;
if (typeNode) {
const type: Type | null = this.resolveType(<TypeNode>typeNode, typeArgumentsMap, true); // reports
if (type)
returnType = <Type>type;
else
return;
} else
return; // TODO: infer type? (currently reported by parser)
// compile statements
const functionType: FunctionType = new FunctionType(typeArguments, parameterTypes, returnType, parameterNames);
this.functions.set(globalName, 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;
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("", binaryenResultType, binaryenParamTypes); */
throw new Error("not implemented");
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);
}
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);
this.compileGlobal(declarations[i], isConst, isExport);
}
compileGlobal(declaration: VariableDeclaration, isConst: bool): void {
compileGlobal(declaration: VariableDeclaration, isConst: bool, isExport: bool): void {
const type: Type | null = declaration.type ? this.resolveType(<TypeNode>declaration.type) : null; // reports
if (!type)
return;
@ -252,15 +362,55 @@ export class Compiler extends DiagnosticEmitter {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
return;
}
if (!this.options.noEmit) {
const binaryenType: BinaryenType = typeToBinaryenType(<Type>type);
const initializer: BinaryenExpressionRef = declaration.initializer ? this.compileExpression(<Expression>declaration.initializer, <Type>type) : typeToBinaryenZero(this.module, <Type>type);
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
if (initializeInStart) {
this.module.addGlobal(name, binaryenType, false, typeToBinaryenZero(this.module, <Type>type));
this.startFunctionBody.push(initializer);
} else
this.module.addGlobal(name, binaryenType, !isConst, initializer);
}
this.globals.add(name);
// 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);
}
compileNamespace(declaration: NamespaceDeclaration): void {
const members: Statement[] = declaration.members;
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)
this.compileClass(<ClassDeclaration>member, []);
break;
case NodeKind.ENUM:
this.compileEnum(<EnumDeclaration>member);
break;
case NodeKind.FUNCTION:
if (!(<FunctionDeclaration>member).typeParameters.length)
this.compileFunction(<FunctionDeclaration>member, []);
break;
case NodeKind.NAMESPACE:
this.compileNamespace(<NamespaceDeclaration>member);
break;
case NodeKind.VARIABLE:
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("not implemented");
}
@ -296,11 +446,11 @@ export class Compiler extends DiagnosticEmitter {
break;
case NodeKind.VARIABLEDECLARATION:
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, (<VariableStatement>declaration.parent).modifiers));
this.compileGlobal(<VariableDeclaration>declaration, hasModifier(ModifierKind.CONST, (<VariableStatement>declaration.parent).modifiers), member.range.source.isEntry);
break;
default:
throw new Error("unexpected declaration kind");
throw new Error("unexpected export declaration kind");
}
} else
throw new Error("unexpected missing declaration");
@ -322,20 +472,96 @@ export class Compiler extends DiagnosticEmitter {
// types
resolveType(node: TypeNode, typeArgumentsMap: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null {
// TODO: resolve node and its arguments using typeArgumentsMap
// TODO: make types for classes in program.ts
// resolve parameters
const k: i32 = node.parameters.length;
const paramTypes: Type[] = new Array(k);
for (let i: i32 = 0; i < k; ++i) {
const paramType: Type | null = this.resolveType(node.parameters[i], typeArgumentsMap, reportNotFound);
if (!paramType)
return null;
paramTypes[i] = <Type>paramType;
}
let globalName: string = node.identifier.name;
if (k) // not a placeholder
globalName += typeArgumentsToString(paramTypes);
else if (typeArgumentsMap && (<Map<string,Type>>typeArgumentsMap).has(globalName)) // possibly a placeholder
return <Type>(<Map<string,Type>>typeArgumentsMap).get(globalName);
const types: Map<string,Type> = this.program.types;
const globalName: string = node.identifier.name;
// resolve local type
const localName: string = node.range.source.normalizedPath + "/" + globalName;
if (types.has(localName))
return <Type>types.get(localName);
// resolve global type
if (types.has(globalName))
return <Type>types.get(globalName);
if (reportNotFound)
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, globalName);
return null;
}
resolveExpressionType(expression: Expression, contextualType: Type): Type {
const previousType: Type = this.currentType;
const previousNoEmit: bool = this.module.noEmit;
this.module.noEmit = true;
this.compileExpression(expression, contextualType, false); // now performs a dry run
const type: Type = this.currentType;
this.currentType = previousType;
this.module.noEmit = previousNoEmit;
return type;
}
resolveClassType(expression: Expression, typeArguments: TypeNode[], typeArgumentsMap: Map<string,Type> | null = null, create: bool = false): ClassType | null {
if (expression.kind == NodeKind.THIS) {
if (this.currentClass)
return <ClassType>this.currentClass;
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
return null;
}
if (expression.kind == NodeKind.IDENTIFIER) {
throw new Error("not implemented");
}
if (expression.kind == NodeKind.ELEMENTACCESS) {
throw new Error("not implemented");
}
if (expression.kind == NodeKind.PROPERTYACCESS) {
throw new Error("not implemented");
}
return null;
}
resolveFunctionType(expression: Expression, typeArguments: TypeNode[], typeArgumentsMap: Map<string,Type> | null = null, create: bool = false): FunctionType | null {
const k: i32 = typeArguments.length;
const resolvedTypeArguments: Type[] = new Array(k);
for (let i: i32 = 0; i < k; ++i) {
const type: Type | null = this.resolveType(typeArguments[i], typeArgumentsMap, true); // reports
if (!type)
return null;
resolvedTypeArguments[i] = <Type>type;
}
if (expression.kind == NodeKind.IDENTIFIER) {
let globalName: string = (<IdentifierExpression>expression).name;
if (k)
globalName += typeArgumentsToString(resolvedTypeArguments);
const localName = expression.range.source.normalizedPath + "/" + globalName;
if (this.functions.has(localName))
return <FunctionType>this.functions.get(localName);
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) {
throw new Error("not implemented");
}
return null;
}
// statements
compileStatement(statement: Statement): BinaryenExpressionRef {
@ -395,15 +621,35 @@ export class Compiler extends DiagnosticEmitter {
}
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
throw new Error("not implemented");
const context: string | null = this.currentFunction.breakContext;
if (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);
return this.module.createUnreachable();
}
compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef {
throw new Error("not implemented");
const context: string | null = this.currentFunction.breakContext;
if (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);
return this.module.createUnreachable();
}
compileDoStatement(statement: DoStatement): BinaryenExpressionRef {
throw new Error("not implemented");
const label: string = this.currentFunction.enterBreakContext();
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
this.currentFunction.leaveBreakContext();
const breakLabel: string = "break$" + label;
const continueLabel: string = "continue$" + label;
return this.module.createBlock(breakLabel, [
this.module.createLoop(continueLabel,
this.module.createBlock(null, [
body,
this.module.createBreak(continueLabel, condition)
]))
]);
}
compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef {
@ -415,10 +661,24 @@ export class Compiler extends DiagnosticEmitter {
}
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : 0;
const condition: BinaryenExpressionRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : 0;
const incrementor: BinaryenExpressionRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 0;
throw new Error("not implemented");
const context: string = this.currentFunction.enterBreakContext();
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : this.module.createNop();
const condition: BinaryenExpressionRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : this.module.createI32(1);
const incrementor: BinaryenExpressionRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : this.module.createNop();
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
this.currentFunction.leaveBreakContext();
const continueLabel: string = "continue$" + context;
const breakLabel: string = "break$" + context;
return this.module.createBlock(breakLabel, [
initializer,
this.module.createLoop(continueLabel, this.module.createBlock(null, [
this.module.createIf(condition, this.module.createBlock(null, [
body,
incrementor,
this.module.createBreak(continueLabel)
]))
]))
]);
}
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
@ -446,10 +706,35 @@ export class Compiler extends DiagnosticEmitter {
compileTryStatement(statement: TryStatement): BinaryenExpressionRef {
throw new Error("not implemented");
// can't yet support something like: try { return ... } finally { ... }
// worthwhile to investigate lowering returns to block results (here)?
}
compileVariableStatement(statement: VariableStatement): BinaryenExpressionRef {
throw new Error("not implemented");
// top-level variables become globals
if (this.currentFunction == this.startFunction) {
this.compileGlobals(statement);
return this.module.createNop();
}
// other variables become locals
const declarations: VariableDeclaration[] = statement.declarations;
const initializers: BinaryenExpressionRef[] = new Array();
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
const declaration: VariableDeclaration = declarations[i];
if (declaration.type) {
const name: string = declaration.identifier.name;
const type: Type | null = this.resolveType(<TypeNode>declaration.type, this.currentFunction.typeArgumentsMap, true); // reports
if (type) {
if (this.currentFunction.locals.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); // recoverable
else
this.currentFunction.addLocal(<Type>type, name);
if (declaration.initializer)
initializers.push(this.compileAssignment(declaration.identifier, <Expression>declaration.initializer, Type.void));
}
}
}
return initializers.length ? this.module.createBlock(null, initializers) : this.module.createNop();
}
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
@ -541,7 +826,7 @@ export class Compiler extends DiagnosticEmitter {
// void to any
if (fromType.kind == TypeKind.VOID)
throw new Error("illegal conversion");
throw new Error("unexpected conversion from void");
// any to void
if (toType.kind == TypeKind.VOID)
@ -773,6 +1058,9 @@ export class Compiler extends DiagnosticEmitter {
this.currentType = Type.bool;
break;
case Token.EQUALS:
return this.compileAssignment(expression.left, expression.right, contextualType);
case Token.PLUS_EQUALS:
compound = Token.EQUALS;
case Token.PLUS:
@ -835,7 +1123,7 @@ export class Compiler extends DiagnosticEmitter {
left = this.compileExpression(expression.left, contextualType, false);
right = this.compileExpression(expression.right, this.currentType);
if (this.currentType.isAnyFloat)
throw new Error("not implemented"); // TODO: fmod
throw new Error("not implemented"); // TODO: internal fmod
op = this.currentType.isLongInteger
? BinaryOp.RemI64
: BinaryOp.RemI32;
@ -908,17 +1196,56 @@ export class Compiler extends DiagnosticEmitter {
default:
throw new Error("not implemented");
}
return compound
? this.compileAssignment(expression.left, this.module.createBinary(op, left, right), this.currentType != Type.void)
: this.module.createBinary(op, left, right);
if (compound) {
right = this.module.createBinary(op, left, right);
return this.compileAssignmentWithValue(expression.left, right, contextualType != Type.void);
}
return this.module.createBinary(op, left, right);
}
compileAssignment(expression: Expression, value: BinaryenExpressionRef, tee: bool = false): BinaryenExpressionRef {
compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): BinaryenExpressionRef {
this.currentType = this.resolveExpressionType(expression, contextualType);
return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, this.currentType), contextualType != Type.void);
}
compileAssignmentWithValue(expression: Expression, valueWithCorrectType: BinaryenExpressionRef, tee: bool = false): BinaryenExpressionRef {
if (expression.kind == NodeKind.IDENTIFIER) {
const name: string = (<IdentifierExpression>expression).name;
const locals: Map<string,LocalType> = this.currentFunction.locals;
if (locals.has(name)) {
const local: LocalType = <LocalType>locals.get(name);
if (tee) {
this.currentType = local.type;
return this.module.createTeeLocal(local.index, valueWithCorrectType);
}
this.currentType = Type.void;
return this.module.createSetLocal(local.index, valueWithCorrectType);
}
const globals: Map<string,Type> = this.globals;
if (globals.has(name)) {
const globalType: Type = <Type>globals.get(name);
if (tee) {
const globalBinaryenType: BinaryenType = typeToBinaryenType(globalType);
this.currentType = globalType;
return this.module.createBlock(null, [ // teeGlobal
this.module.createSetGlobal(name, valueWithCorrectType),
this.module.createGetGlobal(name, globalBinaryenType)
], globalBinaryenType);
}
this.currentType = Type.void;
return this.module.createSetGlobal(name, valueWithCorrectType);
}
}
throw new Error("not implemented");
}
compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef {
const functionType: FunctionType | null = this.resolveFunctionType(expression.expression, expression.typeArguments, this.currentFunction.typeArgumentsMap, true);
if (!functionType)
return this.module.createUnreachable();
// TODO: compile if not yet compiled
// TODO: validate parameters and call
// this.module.createCall(functionType.ref, operands, typeToBinaryenType(functionType.returnType));
throw new Error("not implemented");
}
@ -927,7 +1254,20 @@ export class Compiler extends DiagnosticEmitter {
}
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
const name: string = expression.name;
const locals: Map<string,LocalType> = this.currentFunction.locals;
if (locals.has(name)) {
const local: LocalType = <LocalType>locals.get(name);
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));
}
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name);
return this.module.createUnreachable();
}
compileLiteralExpression(expression: LiteralExpression, contextualType: Type): BinaryenExpressionRef {
@ -1006,9 +1346,8 @@ export class Compiler extends DiagnosticEmitter {
binaryenType = BinaryenType.I32;
binaryenOne = this.module.createI32(1);
}
const getValue: BinaryenExpressionRef = this.compileExpression(expression.expression, contextualType);
const setValue: BinaryenExpressionRef = this.compileAssignment(expression.expression, this.module.createBinary(op, getValue, binaryenOne), false); // reports
const setValue: BinaryenExpressionRef = this.compileAssignmentWithValue(expression.expression, this.module.createBinary(op, getValue, binaryenOne), false); // reports
return this.module.createBlock(null, [
getValue,
@ -1017,16 +1356,18 @@ export class Compiler extends DiagnosticEmitter {
}
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
const operandExpression: Expression = expression.expression;
let operand: BinaryenExpressionRef;
let op: UnaryOp;
switch (expression.operator) {
case Token.PLUS:
return this.compileExpression(expression.expression, contextualType); // noop
return this.compileExpression(operandExpression, contextualType);
case Token.MINUS:
operand = this.compileExpression(expression.expression, contextualType);
operand = this.compileExpression(operandExpression, contextualType);
if (this.currentType == Type.f32)
op = UnaryOp.NegF32;
else if (this.currentType == Type.f64)
@ -1038,27 +1379,27 @@ export class Compiler extends DiagnosticEmitter {
break;
case Token.PLUS_PLUS:
operand = this.compileExpression(expression.expression, contextualType);
operand = this.compileExpression(operandExpression, contextualType);
return this.currentType == Type.f32
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)), contextualType != Type.void)
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddF32, operand, this.module.createF32(1)), contextualType != Type.void)
: this.currentType == Type.f64
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)), contextualType != Type.void)
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddF64, operand, this.module.createF64(1)), contextualType != Type.void)
: this.currentType.isLongInteger
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)), contextualType != Type.void);
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.AddI32, operand, this.module.createI32(1)), contextualType != Type.void);
case Token.MINUS_MINUS:
operand = this.compileExpression(expression.expression, contextualType);
operand = this.compileExpression(operandExpression, contextualType);
return this.currentType == Type.f32
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)), contextualType != Type.void)
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubF32, operand, this.module.createF32(1)), contextualType != Type.void)
: this.currentType == Type.f64
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)), contextualType != Type.void)
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubF64, operand, this.module.createF64(1)), contextualType != Type.void)
: this.currentType.isLongInteger
? this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignment(expression.expression, this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)), contextualType != Type.void);
? this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubI64, operand, this.module.createI64(1, 0)), contextualType != Type.void)
: this.compileAssignmentWithValue(operandExpression, this.module.createBinary(BinaryOp.SubI32, operand, this.module.createI32(1)), contextualType != Type.void);
case Token.EXCLAMATION:
operand = this.compileExpression(expression.expression, Type.bool, false);
operand = this.compileExpression(operandExpression, Type.bool, false);
if (this.currentType == Type.f32) {
this.currentType = Type.bool;
return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0));
@ -1074,7 +1415,7 @@ export class Compiler extends DiagnosticEmitter {
break;
case Token.TILDE:
operand = this.compileExpression(expression.expression, contextualType.isAnyFloat ? Type.i64 : contextualType);
operand = this.compileExpression(operandExpression, contextualType.isAnyFloat ? Type.i64 : contextualType);
return this.currentType.isLongInteger
? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1))
: this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1));
@ -1082,7 +1423,6 @@ export class Compiler extends DiagnosticEmitter {
default:
throw new Error("not implemented");
}
return this.module.createUnary(op, operand);
}
}
@ -1101,6 +1441,14 @@ function typeToBinaryenType(type: Type): BinaryenType {
: BinaryenType.None;
}
function typesToBinaryenTypes(types: Type[]): BinaryenType[] {
const k: i32 = types.length;
const ret: BinaryenType[] = new Array(k);
for (let i: i32 = 0; i < k; ++i)
ret[i] = typeToBinaryenType(types[i]);
return ret;
}
function typeToBinaryenZero(module: Module, type: Type): BinaryenExpressionRef {
return type.kind == TypeKind.F32
? module.createF32(0)
@ -1135,7 +1483,7 @@ function typeToSignatureNamePart(type: Type): string {
function typesToSignatureName(paramTypes: Type[], returnType: Type): string {
sb.length = 0;
for (let i: i32 = 0, k = paramTypes.length; i < k; ++i)
for (let i: i32 = 0, k: i32 = paramTypes.length; i < k; ++i)
sb.push(typeToSignatureNamePart(paramTypes[i]));
sb.push(typeToSignatureNamePart(returnType));
return sb.join("");

View File

@ -19,6 +19,8 @@ export enum DiagnosticCode {
An_accessor_cannot_have_type_parameters = 1094,
A_set_accessor_cannot_have_a_return_type_annotation = 1095,
Type_parameter_list_cannot_be_empty = 1098,
A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement = 1104,
A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement = 1105,
A_return_statement_can_only_be_used_within_a_function_body = 1108,
Expression_expected = 1109,
Type_expected = 1110,
@ -46,8 +48,11 @@ export enum DiagnosticCode {
Generic_type_0_requires_1_type_argument_s = 2314,
Type_0_is_not_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322,
_this_cannot_be_referenced_in_current_location = 2332,
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,
Expected_0_type_arguments_but_got_1 = 2558
}
export function diagnosticCodeToString(code: DiagnosticCode): string {
@ -70,6 +75,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1094: return "An accessor cannot have type parameters.";
case 1095: return "A 'set' accessor cannot have a return type annotation.";
case 1098: return "Type parameter list cannot be empty.";
case 1104: return "A 'continue' statement can only be used within an enclosing iteration statement.";
case 1105: return "A 'break' statement can only be used within an enclosing iteration or switch statement.";
case 1108: return "A 'return' statement can only be used within a function body.";
case 1109: return "Expression expected.";
case 1110: return "Type expected.";
@ -97,8 +104,11 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
case 2332: return "'this' cannot be referenced in current location.";
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
case 2391: return "Function implementation is missing or not immediately following the declaration.";
case 2393: return "Duplicate function implementation.";
case 2558: return "Expected {0} type arguments, but got {1}.";
default: return "";
}
}

View File

@ -18,6 +18,8 @@
"An accessor cannot have type parameters.": 1094,
"A 'set' accessor cannot have a return type annotation.": 1095,
"Type parameter list cannot be empty.": 1098,
"A 'continue' statement can only be used within an enclosing iteration statement.": 1104,
"A 'break' statement can only be used within an enclosing iteration or switch statement.": 1105,
"A 'return' statement can only be used within a function body.": 1108,
"Expression expected.": 1109,
"Type expected.": 1110,
@ -46,6 +48,9 @@
"Generic type '{0}' requires {1} type argument(s).": 2314,
"Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322,
"'this' cannot be referenced in current location.": 2332,
"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,
"Expected {0} type arguments, but got {1}.": 2558
}

View File

@ -10,7 +10,7 @@
import { Program } from "./program";
import { Tokenizer, Token, Range } from "./tokenizer";
import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics";
import { normalizePath } from "./util";
import { normalizePath, I64 } from "./util";
import {
NodeKind,
@ -233,9 +233,9 @@ export class Parser extends DiagnosticEmitter {
// this
} else if (token == Token.THIS) {
type = TypeNode.create(Expression.createIdentifier("this", tn.range()), [], false, tn.range(startPos, tn.pos));
type = TypeNode.create(Expression.createThis(tn.range()), [], false, tn.range(startPos, tn.pos));
// true, false
// true
} else if (token == Token.TRUE || token == Token.FALSE) {
type = TypeNode.create(Expression.createIdentifier("bool", tn.range()), [], false, tn.range(startPos, tn.pos));
@ -951,6 +951,7 @@ export class Parser extends DiagnosticEmitter {
}
parseExpressionStatement(tn: Tokenizer): ExpressionStatement | null {
// at previous token
const expr: Expression | null = this.parseExpression(tn);
if (!expr)
return null;
@ -961,7 +962,7 @@ export class Parser extends DiagnosticEmitter {
parseForStatement(tn: Tokenizer): ForStatement | null {
// at 'for': '(' Statement? Expression? ';' Expression? ')' Statement
const startRange: Range = tn.range();
const startPos: i32 = tn.tokenPos;
if (tn.skip(Token.OPENPAREN)) {
const initializer: Statement | null = this.parseStatement(tn); // skips the semicolon (actually an expression)
if (!initializer)
@ -976,10 +977,13 @@ export class Parser extends DiagnosticEmitter {
const incrementor: Expression | null = this.parseExpression(tn);
if (!incrementor)
return null;
const statement: Statement | null = this.parseStatement(tn);
if (!statement)
return null;
return Statement.createFor(initializer, condition, incrementor, statement, Range.join(startRange, tn.range()));
if (tn.skip(Token.CLOSEPAREN)) {
const statement: Statement | null = this.parseStatement(tn);
if (!statement)
return null;
return Statement.createFor(initializer, condition, incrementor, statement, tn.range(startPos, tn.pos));
} else
this.error(DiagnosticCode._0_expected, tn.range(), ")");
} else
this.error(DiagnosticCode._0_expected, tn.range(), ";");
} else
@ -1095,8 +1099,7 @@ export class Parser extends DiagnosticEmitter {
}
parseTryStatement(tn: Tokenizer): TryStatement | null {
// at 'try': '{' Statement* '}' 'catch' '(' VariableMember ')' '{' Statement* '}' ';'?
// TODO: 'finally' '{' Statement* '}'
// at 'try': '{' Statement* '}' ('catch' '(' VariableMember ')' '{' Statement* '}')? ('finally' '{' Statement* '}'? ';'?
const startRange: Range = tn.range();
if (tn.skip(Token.OPENBRACE)) {
const statements: Statement[] = new Array();
@ -1106,31 +1109,55 @@ export class Parser extends DiagnosticEmitter {
return null;
statements.push(<Statement>stmt);
}
let catchVariable: IdentifierExpression | null = null;
let catchStatements: Statement[] | null = null;
let finallyStatements: Statement[] | null = null;
if (tn.skip(Token.CATCH)) {
if (tn.skip(Token.OPENPAREN)) {
const catchVariable: VariableDeclaration | null = this.parseVariableDeclaration(tn);
if (!catchVariable)
return null;
if (tn.skip(Token.CLOSEPAREN)) {
if (tn.skip(Token.OPENBRACE)) {
const catchStatements: Statement[] = new Array();
while (!tn.skip(Token.CLOSEBRACE)) {
const stmt: Statement | null = this.parseStatement(tn);
if (!stmt)
return null;
catchStatements.push(<Statement>stmt);
}
const ret: TryStatement = Statement.createTry(statements, catchVariable, catchStatements, Range.join(startRange, tn.range()));
tn.skip(Token.SEMICOLON);
return ret;
} else
this.error(DiagnosticCode._0_expected, tn.range(), "{");
} else
this.error(DiagnosticCode._0_expected, tn.range(), ")");
} else
if (!tn.skip(Token.OPENPAREN)) {
this.error(DiagnosticCode._0_expected, tn.range(), "(");
} else
return null;
}
if (!tn.skip(Token.IDENTIFIER)) {
this.error(DiagnosticCode.Identifier_expected, tn.range());
return null;
}
catchVariable = Expression.createIdentifier(tn.readIdentifier(), tn.range());
if (!tn.skip(Token.CLOSEPAREN)) {
this.error(DiagnosticCode._0_expected, tn.range(), ")");
return null;
}
if (!tn.skip(Token.OPENBRACE)) {
this.error(DiagnosticCode._0_expected, tn.range(), "{");
return null;
}
catchStatements = new Array();
while (!tn.skip(Token.CLOSEBRACE)) {
const stmt: Statement | null = this.parseStatement(tn);
if (!stmt)
return null;
catchStatements.push(<Statement>stmt);
}
}
if (tn.skip(Token.FINALLY)) {
if (!tn.skip(Token.OPENBRACE)) {
this.error(DiagnosticCode._0_expected, tn.range(), "{");
return null;
}
finallyStatements = new Array();
while (!tn.skip(Token.CLOSEBRACE)) {
const stmt: Statement | null = this.parseStatement(tn);
if (!stmt)
return null;
finallyStatements.push(<Statement>stmt);
}
}
if (!(catchStatements || finallyStatements)) {
this.error(DiagnosticCode._0_expected, tn.range(), "catch");
return null;
}
const ret: TryStatement = Statement.createTry(statements, catchVariable, catchStatements, finallyStatements, Range.join(startRange, tn.range()));
tn.skip(Token.SEMICOLON);
return ret;
} else
this.error(DiagnosticCode._0_expected, tn.range(), "{");
return null;
@ -1164,12 +1191,12 @@ export class Parser extends DiagnosticEmitter {
const token: Token = tn.next();
const startPos: i32 = tn.tokenPos;
if (token == Token.FALSE)
return Expression.createIdentifier("false", tn.range());
if (token == Token.NULL)
return Expression.createIdentifier("null", tn.range());
return Expression.createNull(tn.range());
if (token == Token.TRUE)
return Expression.createIdentifier("true", tn.range());
return Expression.createTrue(tn.range());
if (token == Token.FALSE)
return Expression.createFalse(tn.range());
let p: Precedence = determinePrecedencePrefix(token);
if (p != Precedence.INVALID) {
@ -1262,6 +1289,10 @@ export class Parser extends DiagnosticEmitter {
return Expression.createAssertion(AssertionKind.PREFIX, <Expression>expr, <TypeNode>toType, tn.range(startPos, tn.pos));
}
// IdentifierExpression
case Token.IDENTIFIER:
return Expression.createIdentifier(tn.readIdentifier(), tn.range(startPos, tn.pos));
// StringLiteralExpression
case Token.STRINGLITERAL:
return Expression.createStringLiteral(tn.readString(), tn.range(startPos, tn.pos));
@ -1278,10 +1309,6 @@ export class Parser extends DiagnosticEmitter {
case Token.REGEXPLITERAL:
return Expression.createRegexpLiteral(tn.readRegexp(), tn.range(startPos, tn.pos));
// IdentifierExpression
case Token.IDENTIFIER:
return Expression.createIdentifier(tn.readIdentifier(), tn.range(startPos, tn.pos));
default:
this.error(DiagnosticCode.Expression_expected, tn.range());
return null;

View File

@ -39,6 +39,8 @@ class QueuedImport {
declaration: ImportDeclaration;
}
const typesStub: Map<string,Type> = new Map();
export class Program extends DiagnosticEmitter {
sources: Source[];
@ -48,7 +50,7 @@ export class Program extends DiagnosticEmitter {
/** Internal map of names to declarations. */
names: Map<string,DeclarationStatement> = new Map();
/** Separate map of internal type names to declarations. */
types: Map<string,Type> = new Map();
types: Map<string,Type> = typesStub;
/** Separate map of internal export names to declarations. */
exports: Map<string,DeclarationStatement> = new Map();
@ -59,8 +61,22 @@ export class Program extends DiagnosticEmitter {
initialize(target: Target): void {
this.target = target;
initializeBasicTypes(this.types, target);
this.types = new Map([
["i8", Type.i8],
["i16", Type.i16],
["i32", Type.i32],
["i64", Type.i64],
["isize", target == Target.WASM64 ? Type.isize64 : Type.isize32],
["u8", Type.u8],
["u16", Type.u16],
["u32", Type.u32],
["u64", Type.u64],
["usize", target == Target.WASM64 ? Type.usize64 : Type.usize32],
["bool", Type.bool],
["f32", Type.f32],
["f64", Type.f64],
["void", Type.void]
]);
const queuedExports: Map<string,QueuedExport> = new Map();
const queuedImports: QueuedImport[] = new Array();
@ -127,8 +143,8 @@ export class Program extends DiagnosticEmitter {
for (let i: i32 = 0, k: i32 = queuedImports.length; i < k; ++i) {
const queuedImport: QueuedImport = queuedImports[i];
const internalName: string = queuedImport.internalName;
let importName: string = queuedImport.importName;
const seen: Set<QueuedExport> = new Set();
let importName: string = queuedImport.importName;
while (queuedExports.has(importName)) {
const queuedExport: QueuedExport = <QueuedExport>queuedExports.get(importName);
importName = queuedExport.importName;
@ -327,7 +343,7 @@ export class Program extends DiagnosticEmitter {
private initializeVariables(statement: VariableStatement, isNamespaceMember: bool = false): void {
const declarations: VariableDeclaration[] = statement.declarations;
const isExport: bool = !isNamespaceMember && hasModifier(ModifierKind.EXPORT, statement.modifiers);
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
const declaration: VariableDeclaration = declarations[i];
const internalName: string = this.mangleInternalName(declaration);
this.addName(internalName, declaration);
@ -394,20 +410,3 @@ export class Program extends DiagnosticEmitter {
throw new Error("unexpected parent");
}
}
function initializeBasicTypes(types: Map<string,Type>, target: Target): void {
types.set("i8", Type.i8);
types.set("i16", Type.i16);
types.set("i32", Type.i32);
types.set("i64", Type.i64);
types.set("isize", target == Target.WASM64 ? Type.isize64 : Type.isize32);
types.set("u8", Type.u8);
types.set("u16", Type.u16);
types.set("u32", Type.u32);
types.set("u64", Type.u64);
types.set("usize", target == Target.WASM64 ? Type.usize64 : Type.usize32);
types.set("bool", Type.bool);
types.set("f32", Type.f32);
types.set("f64", Type.f64);
types.set("void", Type.void);
}

View File

@ -475,6 +475,9 @@ export class Tokenizer extends DiagnosticEmitter {
case CharCode.SLASH:
if (++this.pos < this.end) {
if (text.charCodeAt(this.pos) == CharCode.SLASH) { // single-line comment
if (this.pos + 1 < this.end && text.charCodeAt(this.pos + 1) == CharCode.SLASH) {
// TODO: triple-slash directives, i.e. '/// <reference path="some.d.ts" />'
}
while (++this.pos < this.end) {
if (isLineBreak(text.charCodeAt(this.pos)))
break;
@ -690,7 +693,7 @@ export class Tokenizer extends DiagnosticEmitter {
const posBefore: i32 = this.pos;
const tokenBefore: Token = this.token;
const tokenPosBefore: i32 = this.tokenPos;
if ((this.nextToken = this.unsafeNext(token == Token.IDENTIFIER)) == token) {
if ((this.token = this.unsafeNext(token == Token.IDENTIFIER)) == token) {
this.nextToken = -1;
return true;
} else {

View File

@ -27,7 +27,6 @@
"index.ts",
"parser.ts",
"program.ts",
"reflection.ts",
"tokenizer.ts",
"types.ts",
"util.ts",

View File

@ -1,3 +1,5 @@
import { sb } from "./util";
export const enum TypeKind {
// signed integers
@ -54,12 +56,14 @@ export class Type {
}
asNullable(): Type {
if (this.kind != TypeKind.USIZE)
throw new Error("unexpected non-usize nullable");
if (!this.nullableType)
(this.nullableType = new Type(this.kind, this.size)).nullable = true;
return <Type>this.nullableType;
}
toString(): string {
toString(kindOnly: bool = false): string {
switch (this.kind) {
case TypeKind.I8: return "i8";
case TypeKind.I16: return "i16";
@ -69,7 +73,7 @@ export class Type {
case TypeKind.U16: return "u16";
case TypeKind.U32: return "u32";
case TypeKind.U64: return "u64";
case TypeKind.USIZE: return "usize";
case TypeKind.USIZE: return this.classType && !kindOnly ? this.classType.toString() : "usize";
case TypeKind.BOOL: return "bool";
case TypeKind.F32: return "f32";
case TypeKind.F64: return "f64";
@ -99,40 +103,99 @@ export class Type {
export class FunctionType {
returnType: Type;
typeArguments: Type[];
parameterTypes: Type[];
returnType: Type;
additionalLocals: Type[];
typeArgumentsMap: Map<string, Type> | null = null;
locals: Map<string, LocalType> = new Map();
breakContext: string | null = null;
private breakMajor: i32 = 0;
private breakMinor: i32 = 0;
constructor(returnType: Type, parameterTypes: Type[]) {
this.returnType = returnType;
constructor(typeArguments: Type[], parameterTypes: Type[], returnType: Type, parameterNames: string[] | null = null) {
this.typeArguments = typeArguments;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
this.additionalLocals = new Array();
if (parameterNames) {
if (parameterTypes.length != (<string[]>parameterNames).length)
throw new Error("unexpected parameter count mismatch");;
for (let i: i32 = 0, k: i32 = parameterTypes.length; i < k; ++i) {
const name: string = (<string[]>parameterNames)[i];
if (this.locals.has(name))
throw new Error("duplicate parameter name");
this.locals.set(name, new LocalType(i, parameterTypes[i]));
}
}
}
enterBreakContext(): string {
if (!this.breakMinor)
this.breakMajor++;
return this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
return this.breakContext = this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
}
leaveBreakContext(): void {
if (--this.breakMinor < 0)
throw new Error("unexpected unbalanced break context");
if (this.breakMinor == 0 && !--this.breakMajor)
this.breakContext = null;
}
addLocal(type: Type, name: string | null = null): i32 {
// internal locals don't necessarily need names if referenced by index only
if (name && this.locals.has(<string>name))
throw new Error("duplicate local name");
const index: i32 = this.parameterTypes.length + this.additionalLocals.length;
this.additionalLocals.push(type);
if (name)
this.locals.set(<string>name, new LocalType(index, type));
return index;
}
}
export class LocalType {
index: i32;
type: Type;
constructor(index: i32, type: Type) {
this.index = index;
this.type = type;
}
}
export class ClassType {
type: Type;
typeArgumentsMap: Map<string, Type> | null = null;
name: string;
typeArguments: Type[];
base: ClassType | null;
constructor(usizeType: Type, base: ClassType | null = null) {
this.type = usizeType.asClass(this);
type: Type;
typeArgumentsMap: Map<string, Type> | null = null;
constructor(name: string, usizeType: Type, typeArguments: Type[], base: ClassType | null = null) {
this.name = name;
this.typeArguments = typeArguments;
this.base = base;
this.type = usizeType.asClass(this);
}
toString(): string {
let str: string = this.typeArguments.length ? this.name + typeArgumentsToString(this.typeArguments) : this.name;
return this.type.nullable ? str + " | null" : str;
}
}
export function typeArgumentsToString(typeArguments: Type[]): string {
const k: i32 = typeArguments.length;
if (!k)
return "";
sb.length = 0;
for (let i: i32 = 0; i < k; ++i)
sb[i] = typeArguments[i].toString();
return "<" + sb.join(",") + ">";
}

View File

@ -1,4 +1,7 @@
export { I64, U64 } from "./util/i64";
export { CharCode, isLineBreak} from "./util/charcode";
export { I64, U64 } from "./util/i64";
export { normalize as normalizePath, resolve as resolvePath, dirname } from "./util/path";
export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb

View File

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