mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Resolve exports and imports; Initial work on binary and unary ops
This commit is contained in:
parent
2e611610f2
commit
e14d02e040
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chalk": "^0.4.31",
|
"@types/chalk": "^0.4.31",
|
||||||
"@types/diff": "^3.2.2",
|
"@types/diff": "^3.2.2",
|
||||||
|
12
src/ast.ts
12
src/ast.ts
@ -112,6 +112,7 @@ export enum NodeKind {
|
|||||||
ENUMVALUE, // is also declaration
|
ENUMVALUE, // is also declaration
|
||||||
EXPORT,
|
EXPORT,
|
||||||
EXPORTIMPORT,
|
EXPORTIMPORT,
|
||||||
|
EXPORTMEMBER,
|
||||||
EXPRESSION,
|
EXPRESSION,
|
||||||
INTERFACE,
|
INTERFACE,
|
||||||
FOR,
|
FOR,
|
||||||
@ -875,11 +876,11 @@ export abstract class Statement extends Node {
|
|||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static createExportMember(identifier: IdentifierExpression, asIdentifier: IdentifierExpression | null, range: Range): ExportMember {
|
static createExportMember(identifier: IdentifierExpression, externalIdentifier: IdentifierExpression | null, range: Range): ExportMember {
|
||||||
const elem: ExportMember = new ExportMember();
|
const elem: ExportMember = new ExportMember();
|
||||||
elem.range = range;
|
elem.range = range;
|
||||||
(elem.identifier = identifier).parent = elem;
|
(elem.identifier = identifier).parent = elem;
|
||||||
if (elem.asIdentifier = asIdentifier) (<IdentifierExpression>asIdentifier).parent = elem;
|
(elem.externalIdentifier = externalIdentifier ? <IdentifierExpression>externalIdentifier : identifier).parent = elem;
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1272,14 +1273,15 @@ export class ExportImportStatement extends Node {
|
|||||||
|
|
||||||
export class ExportMember extends Node {
|
export class ExportMember extends Node {
|
||||||
|
|
||||||
|
kind = NodeKind.EXPORTMEMBER;
|
||||||
identifier: IdentifierExpression;
|
identifier: IdentifierExpression;
|
||||||
asIdentifier: IdentifierExpression | null;
|
externalIdentifier: IdentifierExpression;
|
||||||
|
|
||||||
serialize(sb: string[]): void {
|
serialize(sb: string[]): void {
|
||||||
this.identifier.serialize(sb);
|
this.identifier.serialize(sb);
|
||||||
if (this.asIdentifier) {
|
if (this.externalIdentifier.name != this.identifier.name) {
|
||||||
sb.push(" as ");
|
sb.push(" as ");
|
||||||
(<IdentifierExpression>this.asIdentifier).serialize(sb);
|
(<IdentifierExpression>this.externalIdentifier).serialize(sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
412
src/compiler.ts
412
src/compiler.ts
@ -1,8 +1,9 @@
|
|||||||
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp } from "./binaryen";
|
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType } from "./binaryen";
|
||||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
import { hasModifier } from "./parser";
|
import { hasModifier } from "./parser";
|
||||||
import { Program } from "./program";
|
import { Program } from "./program";
|
||||||
import { CharCode, U64 } from "./util";
|
import { CharCode, I64, U64 } from "./util";
|
||||||
|
import { Token } from "./tokenizer";
|
||||||
import {
|
import {
|
||||||
|
|
||||||
NodeKind,
|
NodeKind,
|
||||||
@ -16,10 +17,12 @@ import {
|
|||||||
DoStatement,
|
DoStatement,
|
||||||
EmptyStatement,
|
EmptyStatement,
|
||||||
EnumDeclaration,
|
EnumDeclaration,
|
||||||
|
EnumValueDeclaration,
|
||||||
ExpressionStatement,
|
ExpressionStatement,
|
||||||
FunctionDeclaration,
|
FunctionDeclaration,
|
||||||
ForStatement,
|
ForStatement,
|
||||||
IfStatement,
|
IfStatement,
|
||||||
|
ImportStatement,
|
||||||
MethodDeclaration,
|
MethodDeclaration,
|
||||||
ModifierKind,
|
ModifierKind,
|
||||||
NamespaceDeclaration,
|
NamespaceDeclaration,
|
||||||
@ -93,6 +96,8 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
||||||
memorySegments: MemorySegment[] = new Array();
|
memorySegments: MemorySegment[] = new Array();
|
||||||
|
|
||||||
|
statements: BinaryenExpressionRef[] = new Array(); // TODO: make start function
|
||||||
|
|
||||||
static compile(program: Program, options: Options | null = null): Module {
|
static compile(program: Program, options: Options | null = null): Module {
|
||||||
const compiler: Compiler = new Compiler(program, options);
|
const compiler: Compiler = new Compiler(program, options);
|
||||||
return compiler.compile();
|
return compiler.compile();
|
||||||
@ -141,6 +146,9 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NodeKind.IMPORT:
|
||||||
|
break;
|
||||||
|
|
||||||
case NodeKind.NAMESPACE:
|
case NodeKind.NAMESPACE:
|
||||||
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
|
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
|
||||||
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
|
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
|
||||||
@ -162,7 +170,11 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.EXPORT:
|
case NodeKind.EXPORT:
|
||||||
// obtain referenced declaration and export that
|
// TODO: obtain referenced declaration and export that
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.statements.push(this.compileStatement(statement));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +198,17 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileEnum(en: Enum): void {
|
compileEnum(en: Enum): void {
|
||||||
throw new Error("not implemented");
|
for (let i: i32 = 0, k = en.declaration.members.length; i < k; ++i) {
|
||||||
|
const declaration: EnumValueDeclaration = en.declaration.members[i];
|
||||||
|
const name: string = this.program.mangleName(declaration);
|
||||||
|
const value: BinaryenExpressionRef = declaration.value
|
||||||
|
? this.compileExpression(<Expression>declaration.value, Type.i32)
|
||||||
|
: this.module.createI32(i); // TODO
|
||||||
|
this.module.addGlobal(name, BinaryenType.I32, false, value);
|
||||||
|
if (en.exportName != null) {
|
||||||
|
// TODO: WASM does not yet support non-function exports
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileFunction(fn: Function): void {
|
compileFunction(fn: Function): void {
|
||||||
@ -230,6 +252,17 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
// statements
|
// statements
|
||||||
|
|
||||||
|
enterBreakContext(): string {
|
||||||
|
if (this.breakMinor == 0)
|
||||||
|
++this.breakMajor;
|
||||||
|
++this.breakMinor;
|
||||||
|
return this.breakMajor.toString() + "." + this.breakMinor.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveBreakContext(): void {
|
||||||
|
--this.breakMinor;
|
||||||
|
}
|
||||||
|
|
||||||
compileStatement(statement: Statement): BinaryenExpressionRef {
|
compileStatement(statement: Statement): BinaryenExpressionRef {
|
||||||
switch (statement.kind) {
|
switch (statement.kind) {
|
||||||
|
|
||||||
@ -283,7 +316,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
const children: BinaryenExpressionRef[] = new Array(substatements.length);
|
const children: BinaryenExpressionRef[] = new Array(substatements.length);
|
||||||
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
|
||||||
children[i] = this.compileStatement(substatements[i]);
|
children[i] = this.compileStatement(substatements[i]);
|
||||||
return this.module.createBlock(null, children);
|
return this.module.createBlock(null, children, BinaryenType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
||||||
@ -303,21 +336,20 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef {
|
compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef {
|
||||||
const expression: BinaryenExpressionRef = this.compileExpression(statement.expression, Type.void);
|
return this.compileExpression(statement.expression, Type.void);
|
||||||
return this.currentType == Type.void ? expression : this.module.createDrop(expression);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
|
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
|
||||||
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : 0;
|
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : 0;
|
||||||
const condition: BinaryenExportRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : 0;
|
const condition: BinaryenExpressionRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : 0;
|
||||||
const incrementor: BinaryenExportRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 0;
|
const incrementor: BinaryenExpressionRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 0;
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
||||||
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
||||||
const ifTrue: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
const ifTrue: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
||||||
const ifFalse: BinaryenExportRef = statement.elseStatement ? this.compileStatement(<Statement>statement.elseStatement) : 0;
|
const ifFalse: BinaryenExpressionRef = statement.elseStatement ? this.compileStatement(<Statement>statement.elseStatement) : 0;
|
||||||
return this.module.createIf(condition, ifTrue, ifFalse);
|
return this.module.createIf(condition, ifTrue, ifFalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,72 +378,85 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
|
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
|
||||||
throw new Error("not implemented");
|
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
||||||
|
const label: string = this.enterBreakContext();
|
||||||
|
const breakLabel: string = "break$" + label;
|
||||||
|
const continueLabel: string = "continue$" + label;
|
||||||
|
const body: BinaryenExpressionRef = this.compileStatement(statement.statement);
|
||||||
|
this.leaveBreakContext();
|
||||||
|
return this.module.createBlock(breakLabel, [
|
||||||
|
this.module.createLoop(continueLabel,
|
||||||
|
this.module.createIf(condition, this.module.createBlock("", [
|
||||||
|
body,
|
||||||
|
this.module.createBreak(continueLabel)
|
||||||
|
]))
|
||||||
|
)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// expressions
|
// expressions
|
||||||
|
|
||||||
compileExpression(expression: Expression, resultType: Type): BinaryenExpressionRef {
|
compileExpression(expression: Expression, contextualType: Type, convert: bool = true): BinaryenExpressionRef {
|
||||||
this.currentType = resultType;
|
this.currentType = contextualType;
|
||||||
|
|
||||||
let expr: BinaryenExpressionRef;
|
let expr: BinaryenExpressionRef;
|
||||||
switch (expression.kind) {
|
switch (expression.kind) {
|
||||||
|
|
||||||
case NodeKind.ASSERTION:
|
case NodeKind.ASSERTION:
|
||||||
expr = this.compileAssertionExpression(<AssertionExpression>expression, resultType);
|
expr = this.compileAssertionExpression(<AssertionExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.BINARY:
|
case NodeKind.BINARY:
|
||||||
expr = this.compileBinaryExpression(<BinaryExpression>expression, resultType);
|
expr = this.compileBinaryExpression(<BinaryExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.CALL:
|
case NodeKind.CALL:
|
||||||
expr = this.compileCallExpression(<CallExpression>expression, resultType);
|
expr = this.compileCallExpression(<CallExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.ELEMENTACCESS:
|
case NodeKind.ELEMENTACCESS:
|
||||||
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, resultType);
|
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.IDENTIFIER:
|
case NodeKind.IDENTIFIER:
|
||||||
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, resultType);
|
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.LITERAL:
|
case NodeKind.LITERAL:
|
||||||
expr = this.compileLiteralExpression(<LiteralExpression>expression, resultType);
|
expr = this.compileLiteralExpression(<LiteralExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.NEW:
|
case NodeKind.NEW:
|
||||||
expr = this.compileNewExpression(<NewExpression>expression, resultType);
|
expr = this.compileNewExpression(<NewExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.PARENTHESIZED:
|
case NodeKind.PARENTHESIZED:
|
||||||
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, resultType);
|
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.PROPERTYACCESS:
|
case NodeKind.PROPERTYACCESS:
|
||||||
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, resultType);
|
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.SELECT:
|
case NodeKind.SELECT:
|
||||||
expr = this.compileSelectExpression(<SelectExpression>expression, resultType);
|
expr = this.compileSelectExpression(<SelectExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.UNARYPOSTFIX:
|
case NodeKind.UNARYPOSTFIX:
|
||||||
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, resultType);
|
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.UNARYPREFIX:
|
case NodeKind.UNARYPREFIX:
|
||||||
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, resultType);
|
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, contextualType);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error("unexpected expression kind");
|
throw new Error("unexpected expression kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.currentType != resultType) {
|
if (convert && this.currentType != contextualType) {
|
||||||
expr = this.convertExpression(expr, this.currentType, resultType);
|
expr = this.convertExpression(expr, this.currentType, contextualType);
|
||||||
this.currentType = resultType;
|
this.currentType = contextualType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
@ -580,6 +625,221 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileBinaryExpression(expression: BinaryExpression, contextualType: Type): BinaryenExpressionRef {
|
compileBinaryExpression(expression: BinaryExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
|
let op: BinaryOp;
|
||||||
|
let left: BinaryenExpressionRef;
|
||||||
|
let right: BinaryenExpressionRef;
|
||||||
|
let compound: Token = 0;
|
||||||
|
|
||||||
|
switch (expression.operator) {
|
||||||
|
|
||||||
|
case Token.LESSTHAN:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.LtF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.LtF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.LtI64
|
||||||
|
: BinaryOp.LtI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.GREATERTHAN:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.GtF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.GtF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.GtI64
|
||||||
|
: BinaryOp.GtI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.LESSTHAN_EQUALS:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.LeF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.LeF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.LeI64
|
||||||
|
: BinaryOp.LeI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.GREATERTHAN_EQUALS:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.GeF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.GeF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.GeI64
|
||||||
|
: BinaryOp.GeI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.EQUALS_EQUALS:
|
||||||
|
case Token.EQUALS_EQUALS_EQUALS:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.EqF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.EqF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.EqI64
|
||||||
|
: BinaryOp.EqI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.PLUS_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.PLUS:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.AddF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.AddF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.AddI64
|
||||||
|
: BinaryOp.AddI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.MINUS_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.MINUS:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.SubF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.SubF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.SubI64
|
||||||
|
: BinaryOp.SubI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.ASTERISK_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.ASTERISK:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.MulF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.MulF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.MulI64
|
||||||
|
: BinaryOp.MulI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.SLASH_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.SLASH:
|
||||||
|
left = this.compileExpression(expression.left, contextualType, false);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType == Type.f32
|
||||||
|
? BinaryOp.DivF32
|
||||||
|
: this.currentType == Type.f64
|
||||||
|
? BinaryOp.DivF64
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.DivI64
|
||||||
|
: BinaryOp.DivI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.PERCENT_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.PERCENT:
|
||||||
|
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
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.RemI64
|
||||||
|
: BinaryOp.RemI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.LESSTHAN_LESSTHAN_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.LESSTHAN_LESSTHAN:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.ShlI64
|
||||||
|
: BinaryOp.ShlI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.GREATERTHAN_GREATERTHAN_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.GREATERTHAN_GREATERTHAN:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isSignedInteger
|
||||||
|
? this.currentType.isLongInteger
|
||||||
|
? BinaryOp.ShrI64
|
||||||
|
: BinaryOp.ShrI32
|
||||||
|
: this.currentType.isLongInteger
|
||||||
|
? BinaryOp.ShrU64
|
||||||
|
: BinaryOp.ShrU32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.ShrU64
|
||||||
|
: BinaryOp.ShrU32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.AMPERSAND_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.AMPERSAND:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.AndI64
|
||||||
|
: BinaryOp.AndI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.BAR_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.BAR:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.OrI64
|
||||||
|
: BinaryOp.OrI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.CARET_EQUALS:
|
||||||
|
compound = Token.EQUALS;
|
||||||
|
case Token.CARET:
|
||||||
|
left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
right = this.compileExpression(expression.right, this.currentType);
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? BinaryOp.XorI64
|
||||||
|
: BinaryOp.XorI32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
return compound
|
||||||
|
? this.compileAssignment(expression.left, this.module.createBinary(op, left, right))
|
||||||
|
: this.module.createBinary(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAssignment(expression: Expression, value: BinaryenExpressionRef): BinaryenExpressionRef {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,23 +859,27 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
switch (expression.literalKind) {
|
switch (expression.literalKind) {
|
||||||
// case LiteralKind.ARRAY:
|
// case LiteralKind.ARRAY:
|
||||||
|
|
||||||
case LiteralKind.FLOAT:
|
case LiteralKind.FLOAT: {
|
||||||
if (contextualType == Type.f32)
|
const floatValue: f64 = (<FloatLiteralExpression>expression).value;
|
||||||
return this.module.createF32((<FloatLiteralExpression>expression).value);
|
if (contextualType == Type.f32 && (Math.fround(floatValue) as f64) == floatValue)
|
||||||
|
return this.module.createF32(floatValue);
|
||||||
this.currentType = Type.f64;
|
this.currentType = Type.f64;
|
||||||
return this.module.createF64((<FloatLiteralExpression>expression).value);
|
return this.module.createF64(floatValue);
|
||||||
|
}
|
||||||
|
|
||||||
case LiteralKind.INTEGER:
|
case LiteralKind.INTEGER: {
|
||||||
if (contextualType == Type.bool)
|
const intValue: I64 = (<IntegerLiteralExpression>expression).value;
|
||||||
return this.module.createI32((<IntegerLiteralExpression>expression).value.isOdd ? 1 : 0)
|
if (contextualType == Type.bool && (intValue.isZero || intValue.isOne))
|
||||||
|
return this.module.createI32(intValue.isZero ? 0 : 1);
|
||||||
if (contextualType.isLongInteger)
|
if (contextualType.isLongInteger)
|
||||||
return this.module.createI64((<IntegerLiteralExpression>expression).value.lo, (<IntegerLiteralExpression>expression).value.hi);
|
return this.module.createI64(intValue.lo, intValue.hi);
|
||||||
const value: i32 = (<IntegerLiteralExpression>expression).value.toI32();
|
if (!intValue.fitsInI32) {
|
||||||
if (contextualType.isSmallInteger)
|
this.currentType = Type.i64;
|
||||||
return contextualType.isSignedInteger
|
return this.module.createI64(intValue.lo, intValue.hi);
|
||||||
? this.module.createI32(value << contextualType.smallIntegerShift >> contextualType.smallIntegerShift)
|
}
|
||||||
: this.module.createI32(value & contextualType.smallIntegerMask);
|
this.currentType = Type.i32;
|
||||||
return this.module.createI32(value);
|
return this.module.createI32(intValue.toI32());
|
||||||
|
}
|
||||||
|
|
||||||
// case LiteralKind.OBJECT:
|
// case LiteralKind.OBJECT:
|
||||||
// case LiteralKind.REGEXP:
|
// case LiteralKind.REGEXP:
|
||||||
@ -644,10 +908,70 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef {
|
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
throw new Error("not implemented");
|
let operand: BinaryenExpressionRef;
|
||||||
|
let op: UnaryOp;
|
||||||
|
|
||||||
|
switch (expression.operator) {
|
||||||
|
|
||||||
|
case Token.PLUS_PLUS:
|
||||||
|
case Token.MINUS_MINUS:
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
// return this.module.createBinary(op, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
|
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
|
||||||
throw new Error("not implemented");
|
let operand: BinaryenExpressionRef;
|
||||||
|
let op: UnaryOp;
|
||||||
|
|
||||||
|
switch (expression.operator) {
|
||||||
|
|
||||||
|
case Token.PLUS:
|
||||||
|
return this.compileExpression(expression.expression, contextualType);
|
||||||
|
|
||||||
|
case Token.MINUS:
|
||||||
|
operand = this.compileExpression(expression.expression, contextualType);
|
||||||
|
if (this.currentType == Type.f32)
|
||||||
|
op = UnaryOp.NegF32;
|
||||||
|
else if (this.currentType == Type.f64)
|
||||||
|
op = UnaryOp.NegF64;
|
||||||
|
else
|
||||||
|
return this.currentType.isLongInteger
|
||||||
|
? this.module.createBinary(BinaryOp.SubI64, this.module.createI64(0, 0), operand)
|
||||||
|
: this.module.createBinary(BinaryOp.SubI32, this.module.createI32(0), operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case Token.PLUS_PLUS:
|
||||||
|
// case Token.MINUS_MINUS:
|
||||||
|
|
||||||
|
case Token.EXCLAMATION:
|
||||||
|
operand = this.compileExpression(expression.expression, Type.bool, false);
|
||||||
|
if (this.currentType == Type.f32) {
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
return this.module.createBinary(BinaryOp.EqF32, operand, this.module.createF32(0));
|
||||||
|
}
|
||||||
|
if (this.currentType == Type.f64) {
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
return this.module.createBinary(BinaryOp.EqF64, operand, this.module.createF64(0));
|
||||||
|
}
|
||||||
|
op = this.currentType.isLongInteger
|
||||||
|
? UnaryOp.EqzI64 // TODO: does this yield i64 0/1?
|
||||||
|
: UnaryOp.EqzI32;
|
||||||
|
this.currentType = Type.bool;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Token.TILDE:
|
||||||
|
operand = this.compileExpression(expression.expression, contextualType.isAnyFloat ? Type.i64 : contextualType);
|
||||||
|
return this.currentType.isLongInteger
|
||||||
|
? this.module.createBinary(BinaryOp.XorI64, operand, this.module.createI64(-1, -1))
|
||||||
|
: this.module.createBinary(BinaryOp.XorI32, operand, this.module.createI32(-1));
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.module.createUnary(op, operand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,7 @@ export abstract class DiagnosticEmitter {
|
|||||||
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
|
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
|
||||||
this.diagnostics.push(message);
|
this.diagnostics.push(message);
|
||||||
console.log(formatDiagnosticMessage(message, true, true)); // temporary
|
console.log(formatDiagnosticMessage(message, true, true)); // temporary
|
||||||
|
console.log(new Error().stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
|
||||||
|
@ -11,7 +11,7 @@ import { Program } from "./program";
|
|||||||
import { Source } from "./reflection";
|
import { Source } from "./reflection";
|
||||||
import { Tokenizer, Token, Range } from "./tokenizer";
|
import { Tokenizer, Token, Range } from "./tokenizer";
|
||||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
|
||||||
import { I64 } from "./util";
|
import { I64, normalizePath, resolvePath } from "./util";
|
||||||
import {
|
import {
|
||||||
|
|
||||||
NodeKind,
|
NodeKind,
|
||||||
@ -67,6 +67,7 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
|
|
||||||
program: Program;
|
program: Program;
|
||||||
backlog: string[] = new Array();
|
backlog: string[] = new Array();
|
||||||
|
seenlog: Set<string> = new Set();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -74,12 +75,14 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseFile(text: string, path: string, isEntry: bool): void {
|
parseFile(text: string, path: string, isEntry: bool): void {
|
||||||
if (path.startsWith("./"))
|
const normalizedPath: string = normalizePath(path);
|
||||||
path = path.substring(2);
|
|
||||||
for (let i: i32 = 0, k: i32 = this.program.sources.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = this.program.sources.length; i < k; ++i)
|
||||||
if (this.program.sources[i].path == path)
|
if (this.program.sources[i].normalizedPath == normalizedPath)
|
||||||
throw Error("duplicate source");
|
throw Error("duplicate source");
|
||||||
|
|
||||||
|
if (isEntry)
|
||||||
|
this.seenlog.add(normalizedPath);
|
||||||
|
|
||||||
const source: Source = new Source(path, text, isEntry);
|
const source: Source = new Source(path, text, isEntry);
|
||||||
this.program.sources.push(source);
|
this.program.sources.push(source);
|
||||||
|
|
||||||
@ -189,6 +192,8 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
finish(): Program {
|
finish(): Program {
|
||||||
if (this.backlog.length)
|
if (this.backlog.length)
|
||||||
throw new Error("backlog is not empty");
|
throw new Error("backlog is not empty");
|
||||||
|
this.backlog = new Array(0);
|
||||||
|
this.seenlog.clear();
|
||||||
return this.program;
|
return this.program;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,9 +694,14 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
let path: string | null = null;
|
let path: string | null = null;
|
||||||
if (tn.skip(Token.FROM)) {
|
if (tn.skip(Token.FROM)) {
|
||||||
if (tn.skip(Token.STRINGLITERAL))
|
if (tn.skip(Token.STRINGLITERAL)) {
|
||||||
path = tn.readString();
|
path = tn.readString();
|
||||||
else {
|
const resolvedPath: string = resolvePath(normalizePath(path), tn.source.normalizedPath);
|
||||||
|
if (!this.seenlog.has(resolvedPath)) {
|
||||||
|
this.backlog.push(resolvedPath);
|
||||||
|
this.seenlog.add(resolvedPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
this.error(DiagnosticCode.String_literal_expected, tn.range());
|
this.error(DiagnosticCode.String_literal_expected, tn.range());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -743,6 +753,11 @@ export class Parser extends DiagnosticEmitter {
|
|||||||
if (tn.skip(Token.FROM)) {
|
if (tn.skip(Token.FROM)) {
|
||||||
if (tn.skip(Token.STRINGLITERAL)) {
|
if (tn.skip(Token.STRINGLITERAL)) {
|
||||||
const path: string = tn.readString();
|
const path: string = tn.readString();
|
||||||
|
const resolvedPath: string = resolvePath(normalizePath(path), tn.source.normalizedPath);
|
||||||
|
if (!this.seenlog.has(resolvedPath)) {
|
||||||
|
this.backlog.push(resolvedPath);
|
||||||
|
this.seenlog.add(resolvedPath);
|
||||||
|
}
|
||||||
const ret: ImportStatement = Statement.createImport(members, path, Range.join(startRange, tn.range()));
|
const ret: ImportStatement = Statement.createImport(members, path, Range.join(startRange, tn.range()));
|
||||||
tn.skip(Token.SEMICOLON);
|
tn.skip(Token.SEMICOLON);
|
||||||
return ret;
|
return ret;
|
||||||
|
220
src/program.ts
220
src/program.ts
@ -2,18 +2,21 @@ import { Target } from "./compiler";
|
|||||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||||
import { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
|
import { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
|
||||||
import { hasModifier } from "./parser";
|
import { hasModifier } from "./parser";
|
||||||
import { normalizePath, resolvePath } from "./util";
|
import { normalizePath, resolvePath, trimExtension } from "./util";
|
||||||
import {
|
import {
|
||||||
|
|
||||||
Node,
|
Node,
|
||||||
NodeKind,
|
NodeKind,
|
||||||
SourceNode,
|
SourceNode,
|
||||||
ModifierKind,
|
ModifierKind,
|
||||||
|
IdentifierExpression,
|
||||||
|
|
||||||
ClassDeclaration,
|
ClassDeclaration,
|
||||||
DeclarationStatement,
|
DeclarationStatement,
|
||||||
EnumDeclaration,
|
EnumDeclaration,
|
||||||
EnumValueDeclaration,
|
EnumValueDeclaration,
|
||||||
|
ExportMember,
|
||||||
|
ExportStatement,
|
||||||
FieldDeclaration,
|
FieldDeclaration,
|
||||||
FunctionDeclaration,
|
FunctionDeclaration,
|
||||||
ImportDeclaration,
|
ImportDeclaration,
|
||||||
@ -27,15 +30,31 @@ import {
|
|||||||
|
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
|
|
||||||
|
class QueuedExport {
|
||||||
|
importName: string;
|
||||||
|
member: ExportMember;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueuedImport {
|
||||||
|
globalName: string;
|
||||||
|
importName: string;
|
||||||
|
declaration: ImportDeclaration;
|
||||||
|
}
|
||||||
|
|
||||||
export class Program extends DiagnosticEmitter {
|
export class Program extends DiagnosticEmitter {
|
||||||
|
|
||||||
sources: Source[];
|
sources: Source[];
|
||||||
diagnosticsOffset: i32 = 0;
|
diagnosticsOffset: i32 = 0;
|
||||||
target: Target = Target.WASM32;
|
target: Target = Target.WASM32;
|
||||||
|
|
||||||
|
// internal names to declarations
|
||||||
names: Map<string,DeclarationStatement> = new Map();
|
names: Map<string,DeclarationStatement> = new Map();
|
||||||
|
// type names to types
|
||||||
types: Map<string,Type> = new Map();
|
types: Map<string,Type> = new Map();
|
||||||
|
// export names to declarations (separate from internal names)
|
||||||
|
exports: Map<string,DeclarationStatement> = new Map();
|
||||||
|
|
||||||
|
// reflection instances
|
||||||
classes: Class[] = new Array();
|
classes: Class[] = new Array();
|
||||||
enums: Enum[] = new Array();
|
enums: Enum[] = new Array();
|
||||||
functions: Function[] = new Array();
|
functions: Function[] = new Array();
|
||||||
@ -51,9 +70,10 @@ export class Program extends DiagnosticEmitter {
|
|||||||
this.target = target;
|
this.target = target;
|
||||||
initializeBasicTypes(this.types, target);
|
initializeBasicTypes(this.types, target);
|
||||||
|
|
||||||
const importStatements: ImportStatement[] = new Array();
|
const exportsMap: Map<string,QueuedExport> = new Map();
|
||||||
|
const importsQueue: QueuedImport[] = new Array();
|
||||||
|
|
||||||
// build a lookup map of global names to declarations
|
// build initial lookup maps of global and export names to declarations
|
||||||
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
|
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
|
||||||
const source: Source = this.sources[i];
|
const source: Source = this.sources[i];
|
||||||
const statements: Statement[] = source.statements;
|
const statements: Statement[] = source.statements;
|
||||||
@ -69,13 +89,16 @@ export class Program extends DiagnosticEmitter {
|
|||||||
this.initializeEnum(<EnumDeclaration>statement);
|
this.initializeEnum(<EnumDeclaration>statement);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NodeKind.EXPORT:
|
||||||
|
this.initializeExports(<ExportStatement>statement, exportsMap);
|
||||||
|
break;
|
||||||
|
|
||||||
case NodeKind.FUNCTION:
|
case NodeKind.FUNCTION:
|
||||||
this.initializeFunction(<FunctionDeclaration>statement);
|
this.initializeFunction(<FunctionDeclaration>statement);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.IMPORT:
|
case NodeKind.IMPORT:
|
||||||
this.initializeImports(<ImportStatement>statement);
|
this.initializeImports(<ImportStatement>statement, exportsMap, importsQueue);
|
||||||
importStatements.push(<ImportStatement>statement);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.INTERFACE:
|
case NodeKind.INTERFACE:
|
||||||
@ -93,26 +116,52 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve imports to their respective declarations
|
// at this point exports map should be resolvable
|
||||||
for (let i: i32 = 0, k: i32 = importStatements.length; i < k; ++i) {
|
for (let [key, val] of exportsMap.entries()) {
|
||||||
const statement: ImportStatement = importStatements[i];
|
const seen: Set<QueuedExport> = new Set();
|
||||||
const importPath: string = resolvePath(normalizePath(statement.path), statement.range.source.normalizedPath);
|
while (exportsMap.has(val.importName)) {
|
||||||
const members: ImportDeclaration[] = statement.declarations;
|
val = <QueuedExport>exportsMap.get(val.importName);
|
||||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j){
|
if (seen.has(val))
|
||||||
const declaration: ImportDeclaration = members[j];
|
break;
|
||||||
const importedName: string = declaration.externalIdentifier.name;
|
seen.add(val);
|
||||||
for (let m: i32 = 0, n: i32 = this.sources.length; m < n; ++n) {
|
|
||||||
const source: Source = this.sources[m];
|
|
||||||
if (source.path == importPath) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (this.exports.has(val.importName))
|
||||||
|
this.addExport(key, <DeclarationStatement>this.exports.get(val.importName));
|
||||||
|
else
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, val.member.externalIdentifier.range, val.importName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point imports queue should be resolvable
|
||||||
|
for (let i: i32 = 0, k: i32 = importsQueue.length; i < k; ++i) {
|
||||||
|
const queued: QueuedImport = importsQueue[i];
|
||||||
|
const globalName: string = queued.globalName;
|
||||||
|
let importName: string = queued.importName;
|
||||||
|
const seen: Set<QueuedExport> = new Set();
|
||||||
|
while (exportsMap.has(importName)) {
|
||||||
|
const val: QueuedExport = <QueuedExport>exportsMap.get(importName);
|
||||||
|
importName = val.importName;
|
||||||
|
if (seen.has(val))
|
||||||
|
break;
|
||||||
|
seen.add(val);
|
||||||
|
}
|
||||||
|
if (this.exports.has(importName)) {
|
||||||
|
if (this.names.has(globalName))
|
||||||
|
this.error(DiagnosticCode.Duplicate_identifier_0, queued.declaration.identifier.range, globalName);
|
||||||
|
else {
|
||||||
|
const declaration: DeclarationStatement = <DeclarationStatement>this.exports.get(importName);
|
||||||
|
this.names.set(globalName, declaration);
|
||||||
|
// TODO: also mirror (non-private) member names?
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
this.error(DiagnosticCode.Cannot_find_name_0, queued.declaration.externalIdentifier.range, importName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeClass(declaration: ClassDeclaration): void {
|
private initializeClass(declaration: ClassDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
const members: DeclarationStatement[] = declaration.members;
|
const members: DeclarationStatement[] = declaration.members;
|
||||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
const statement: Statement = members[j];
|
const statement: Statement = members[j];
|
||||||
@ -132,37 +181,88 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeField(declaration: FieldDeclaration): void {
|
private initializeField(declaration: FieldDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeEnum(declaration: EnumDeclaration): void {
|
private initializeEnum(declaration: EnumDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
const members: EnumValueDeclaration[] = declaration.members;
|
const members: EnumValueDeclaration[] = declaration.members;
|
||||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||||
this.initializeEnumValue(members[i]);
|
this.initializeEnumValue(members[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeEnumValue(declaration: EnumValueDeclaration): void {
|
private initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeFunction(declaration: FunctionDeclaration): void {
|
private initializeExports(statement: ExportStatement, exportsMap: Map<string,QueuedExport>): void {
|
||||||
this.addName(declaration);
|
const members: ExportMember[] = statement.members;
|
||||||
}
|
const normalizedPath: string | null = statement.path == null ? null : normalizePath(<string>statement.path);
|
||||||
|
|
||||||
initializeImports(statement: ImportStatement): void {
|
|
||||||
const members: ImportDeclaration[] = statement.declarations;
|
|
||||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||||
this.initializeImport(members[i]);
|
this.initializeExport(members[i], normalizedPath, exportsMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeImport(declaration: ImportDeclaration): void {
|
private initializeExport(member: ExportMember, normalizedPath: string | null, exportsMap: Map<string,QueuedExport>): void {
|
||||||
this.addName(declaration);
|
const exportName: string = member.range.source.normalizedPath + "/" + member.externalIdentifier.name;
|
||||||
|
if (exportsMap.has(exportName))
|
||||||
|
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
|
||||||
|
else {
|
||||||
|
const queued: QueuedExport = new QueuedExport();
|
||||||
|
queued.importName = normalizedPath == null
|
||||||
|
? member.range.source.normalizedPath + "/" + member.identifier.name // local
|
||||||
|
: (<string>normalizedPath) + "/" + member.identifier.name; // re-export
|
||||||
|
queued.member = member;
|
||||||
|
exportsMap.set(exportName, queued);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeInterface(declaration: InterfaceDeclaration): void {
|
private initializeFunction(declaration: FunctionDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeImports(statement: ImportStatement, exportsMap: Map<string,QueuedExport>, importsQueue: QueuedImport[]): void {
|
||||||
|
const members: ImportDeclaration[] = statement.declarations;
|
||||||
|
const normalizedPath: string = normalizePath(statement.path);
|
||||||
|
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
|
||||||
|
const declaration: ImportDeclaration = members[i];
|
||||||
|
this.initializeImport(declaration, normalizedPath, exportsMap, importsQueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeImport(declaration: ImportDeclaration, normalizedPath: string, exportsMap: Map<string,QueuedExport>, importsQueue: QueuedImport[]): void {
|
||||||
|
const importName: string = normalizedPath + "/" + declaration.externalIdentifier.name;
|
||||||
|
let resolvedImportName: string = importName;
|
||||||
|
while (exportsMap.has(resolvedImportName))
|
||||||
|
resolvedImportName = (<QueuedExport>exportsMap.get(resolvedImportName)).importName;
|
||||||
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
if (this.exports.has(resolvedImportName)) { // resolvable right away
|
||||||
|
if (this.names.has(globalName))
|
||||||
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName);
|
||||||
|
else
|
||||||
|
this.names.set(globalName, <DeclarationStatement>this.exports.get(resolvedImportName));
|
||||||
|
} else { // points to yet unresolved export
|
||||||
|
const queued: QueuedImport = new QueuedImport();
|
||||||
|
queued.globalName = globalName;
|
||||||
|
queued.importName = importName;
|
||||||
|
queued.declaration = declaration;
|
||||||
|
importsQueue.push(queued);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeInterface(declaration: InterfaceDeclaration): void {
|
||||||
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
const members: Statement[] = declaration.members;
|
const members: Statement[] = declaration.members;
|
||||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
const statement: Statement = members[j];
|
const statement: Statement = members[j];
|
||||||
@ -182,12 +282,16 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeMethod(declaration: MethodDeclaration): void {
|
private initializeMethod(declaration: MethodDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeNamespace(declaration: NamespaceDeclaration): void {
|
private initializeNamespace(declaration: NamespaceDeclaration): void {
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
const members: Statement[] = declaration.members;
|
const members: Statement[] = declaration.members;
|
||||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||||
const statement: Statement = members[j];
|
const statement: Statement = members[j];
|
||||||
@ -214,7 +318,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NodeKind.VARIABLE:
|
case NodeKind.VARIABLE:
|
||||||
this.initializeVariables(<VariableStatement>statement);
|
this.initializeVariables(<VariableStatement>statement, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -223,20 +327,30 @@ export class Program extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeVariables(statement: VariableStatement): void {
|
private initializeVariables(statement: VariableStatement, insideNamespace: bool = false): void {
|
||||||
const declarations: VariableDeclaration[] = statement.members;
|
const declarations: VariableDeclaration[] = statement.members;
|
||||||
|
const isExport: bool = !insideNamespace && hasModifier(ModifierKind.EXPORT, statement.modifiers);
|
||||||
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
|
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
|
||||||
const declaration: VariableDeclaration = declarations[i];
|
const declaration: VariableDeclaration = declarations[i];
|
||||||
this.addName(declaration);
|
const globalName: string = this.mangleName(declaration);
|
||||||
|
this.addName(globalName, declaration);
|
||||||
|
if (isExport)
|
||||||
|
this.addExport(globalName, declaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addName(declaration: DeclarationStatement): void {
|
private addName(globalName: string, declaration: DeclarationStatement): void {
|
||||||
const name: string = this.mangleName(declaration);
|
if (this.names.has(globalName))
|
||||||
if (this.names.has(name))
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName); // recoverable
|
||||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); // recoverable
|
|
||||||
else
|
else
|
||||||
this.names.set(name, declaration);
|
this.names.set(globalName, declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private addExport(globalName: string, declaration: DeclarationStatement): void {
|
||||||
|
if (this.exports.has(globalName))
|
||||||
|
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName); // recoverable
|
||||||
|
else
|
||||||
|
this.exports.set(globalName, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
mangleName(declaration: DeclarationStatement): string {
|
mangleName(declaration: DeclarationStatement): string {
|
||||||
@ -246,7 +360,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
switch (parent.kind) {
|
switch (parent.kind) {
|
||||||
|
|
||||||
case NodeKind.SOURCE:
|
case NodeKind.SOURCE:
|
||||||
return (<SourceNode>parent).path + "/" + name;
|
return (<Source>parent).normalizedPath + "/" + name;
|
||||||
|
|
||||||
case NodeKind.CLASS: {
|
case NodeKind.CLASS: {
|
||||||
if (
|
if (
|
||||||
@ -314,12 +428,12 @@ function initializeBasicTypes(types: Map<string,Type>, target: Target) {
|
|||||||
types.set("i16", Type.i16);
|
types.set("i16", Type.i16);
|
||||||
types.set("i32", Type.i32);
|
types.set("i32", Type.i32);
|
||||||
types.set("i64", Type.i64);
|
types.set("i64", Type.i64);
|
||||||
types.set("isize", target == Target.WASM32 ? Type.isize32 : Type.isize64);
|
types.set("isize", target == Target.WASM64 ? Type.isize64 : Type.isize32);
|
||||||
types.set("u8", Type.u8);
|
types.set("u8", Type.u8);
|
||||||
types.set("u16", Type.u16);
|
types.set("u16", Type.u16);
|
||||||
types.set("u32", Type.u32);
|
types.set("u32", Type.u32);
|
||||||
types.set("u64", Type.u64);
|
types.set("u64", Type.u64);
|
||||||
types.set("usize", target == Target.WASM32 ? Type.usize32 : Type.usize64);
|
types.set("usize", target == Target.WASM64 ? Type.usize64 : Type.usize32);
|
||||||
types.set("bool", Type.bool);
|
types.set("bool", Type.bool);
|
||||||
types.set("void", Type.void);
|
types.set("void", Type.void);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ import {
|
|||||||
import { DiagnosticMessage } from "./diagnostics";
|
import { DiagnosticMessage } from "./diagnostics";
|
||||||
import { Token, Tokenizer, Range } from "./tokenizer";
|
import { Token, Tokenizer, Range } from "./tokenizer";
|
||||||
import { hasModifier } from "./parser";
|
import { hasModifier } from "./parser";
|
||||||
import { normalizePath } from "./util";
|
import { normalizePath, trimExtension } from "./util";
|
||||||
|
|
||||||
export abstract class Base {
|
export abstract class Base {
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ export class Type extends Base {
|
|||||||
export class Source extends SourceNode {
|
export class Source extends SourceNode {
|
||||||
|
|
||||||
text: string;
|
text: string;
|
||||||
tokenizer: Tokenizer | null;
|
tokenizer: Tokenizer | null = null;
|
||||||
statements: Statement[];
|
statements: Statement[];
|
||||||
isEntry: bool;
|
isEntry: bool;
|
||||||
normalizedPath: string;
|
normalizedPath: string;
|
||||||
@ -158,7 +158,7 @@ export class Source extends SourceNode {
|
|||||||
this.text = text;
|
this.text = text;
|
||||||
this.statements = new Array();
|
this.statements = new Array();
|
||||||
this.isEntry = isEntry;
|
this.isEntry = isEntry;
|
||||||
this.normalizedPath = normalizePath(path);
|
this.normalizedPath = normalizePath(trimExtension(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
|
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"types": [
|
"types": [
|
||||||
"node"
|
"node"
|
||||||
],
|
],
|
||||||
|
"downlevelIteration": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"outDir": "../out"
|
"outDir": "../out"
|
||||||
|
@ -226,3 +226,10 @@ export function dirname(normalizedPath: string, separator: CharCode = CharCode.S
|
|||||||
export function resolvePath(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
|
export function resolvePath(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
|
||||||
return normalizePath(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
|
return normalizePath(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function trimExtension(path: string): string {
|
||||||
|
const len: i32 = path.length;
|
||||||
|
if (len > 3 && path.charCodeAt(len - 3) == CharCode.DOT && (path.charCodeAt(len - 2) == CharCode.t || path.charCodeAt(len - 2) == CharCode.a) && path.charCodeAt(len - 1) == CharCode.s)
|
||||||
|
return path.substring(0, len - 3);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
@ -31,6 +31,10 @@ export class I64 {
|
|||||||
return this.lo == 0 && this.hi == 0;
|
return this.lo == 0 && this.hi == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isOne(): bool {
|
||||||
|
return this.lo == 1 && this.hi == 0;
|
||||||
|
}
|
||||||
|
|
||||||
get isPositive(): bool {
|
get isPositive(): bool {
|
||||||
return this.hi >= 0;
|
return this.hi >= 0;
|
||||||
}
|
}
|
||||||
@ -47,6 +51,10 @@ export class I64 {
|
|||||||
return (this.lo & 1) == 0;
|
return (this.lo & 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get fitsInI32(): bool {
|
||||||
|
return this.hi == 0 || (this.hi == -1 && this.lo < 0);
|
||||||
|
}
|
||||||
|
|
||||||
toI32(): i32 {
|
toI32(): i32 {
|
||||||
return this.lo;
|
return this.lo;
|
||||||
}
|
}
|
||||||
|
37
tests/compiler.ts
Normal file
37
tests/compiler.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import "../src/glue/js";
|
||||||
|
import { Compiler } from "../src/compiler";
|
||||||
|
import { Parser } from "../src/parser";
|
||||||
|
|
||||||
|
const files: Map<string,string> = new Map([
|
||||||
|
["main", `import { Test } from "./a"; export { TestAlias } from "./d";`],
|
||||||
|
["a", `export { Test } from "./b";`],
|
||||||
|
["b", `export { Test } from "./c";`],
|
||||||
|
["c", `export enum Test { ONE = 1 }`],
|
||||||
|
["d", `export { Test as TestAlias } from "./b";`]
|
||||||
|
]);
|
||||||
|
|
||||||
|
const parser = new Parser();
|
||||||
|
parser.parseFile(<string>files.get("main"), "main", true);
|
||||||
|
do {
|
||||||
|
let nextFile = parser.nextFile();
|
||||||
|
if (!nextFile)
|
||||||
|
break;
|
||||||
|
if (!files.has(nextFile))
|
||||||
|
throw new Error("file not found: " + nextFile);
|
||||||
|
parser.parseFile(<string>files.get(nextFile), nextFile, false);
|
||||||
|
} while(true);
|
||||||
|
const program = parser.finish();
|
||||||
|
const compiler = new Compiler(program);
|
||||||
|
const module = compiler.compile();
|
||||||
|
|
||||||
|
console.log("names", program.names);
|
||||||
|
console.log("exports", program.exports);
|
||||||
|
|
||||||
|
// module.optimize();
|
||||||
|
// module.validate(); // global initializers can't use i32.add etc. yet
|
||||||
|
/* _BinaryenModulePrint(module.ref);
|
||||||
|
|
||||||
|
console.log("--- statements ---");
|
||||||
|
compiler.statements.forEach(stmt => {
|
||||||
|
_BinaryenExpressionPrint(stmt);
|
||||||
|
}); */
|
Loading…
x
Reference in New Issue
Block a user