mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Progress
This commit is contained in:
parent
f55fc70220
commit
50116acede
119
src/ast.ts
119
src/ast.ts
@ -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;
|
||||
|
@ -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);
|
||||
|
482
src/compiler.ts
482
src/compiler.ts
@ -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("");
|
||||
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
107
src/parser.ts
107
src/parser.ts
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -27,7 +27,6 @@
|
||||
"index.ts",
|
||||
"parser.ts",
|
||||
"program.ts",
|
||||
"reflection.ts",
|
||||
"tokenizer.ts",
|
||||
"types.ts",
|
||||
"util.ts",
|
||||
|
83
src/types.ts
83
src/types.ts
@ -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(",") + ">";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user