mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +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": {
|
||||
"@types/chalk": "^0.4.31",
|
||||
"@types/diff": "^3.2.2",
|
||||
|
12
src/ast.ts
12
src/ast.ts
@ -112,6 +112,7 @@ export enum NodeKind {
|
||||
ENUMVALUE, // is also declaration
|
||||
EXPORT,
|
||||
EXPORTIMPORT,
|
||||
EXPORTMEMBER,
|
||||
EXPRESSION,
|
||||
INTERFACE,
|
||||
FOR,
|
||||
@ -875,11 +876,11 @@ export abstract class Statement extends Node {
|
||||
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();
|
||||
elem.range = range;
|
||||
(elem.identifier = identifier).parent = elem;
|
||||
if (elem.asIdentifier = asIdentifier) (<IdentifierExpression>asIdentifier).parent = elem;
|
||||
(elem.externalIdentifier = externalIdentifier ? <IdentifierExpression>externalIdentifier : identifier).parent = elem;
|
||||
return elem;
|
||||
}
|
||||
|
||||
@ -1272,14 +1273,15 @@ export class ExportImportStatement extends Node {
|
||||
|
||||
export class ExportMember extends Node {
|
||||
|
||||
kind = NodeKind.EXPORTMEMBER;
|
||||
identifier: IdentifierExpression;
|
||||
asIdentifier: IdentifierExpression | null;
|
||||
externalIdentifier: IdentifierExpression;
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
this.identifier.serialize(sb);
|
||||
if (this.asIdentifier) {
|
||||
if (this.externalIdentifier.name != this.identifier.name) {
|
||||
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 { hasModifier } from "./parser";
|
||||
import { Program } from "./program";
|
||||
import { CharCode, U64 } from "./util";
|
||||
import { CharCode, I64, U64 } from "./util";
|
||||
import { Token } from "./tokenizer";
|
||||
import {
|
||||
|
||||
NodeKind,
|
||||
@ -16,10 +17,12 @@ import {
|
||||
DoStatement,
|
||||
EmptyStatement,
|
||||
EnumDeclaration,
|
||||
EnumValueDeclaration,
|
||||
ExpressionStatement,
|
||||
FunctionDeclaration,
|
||||
ForStatement,
|
||||
IfStatement,
|
||||
ImportStatement,
|
||||
MethodDeclaration,
|
||||
ModifierKind,
|
||||
NamespaceDeclaration,
|
||||
@ -93,6 +96,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
|
||||
memorySegments: MemorySegment[] = new Array();
|
||||
|
||||
statements: BinaryenExpressionRef[] = new Array(); // TODO: make start function
|
||||
|
||||
static compile(program: Program, options: Options | null = null): Module {
|
||||
const compiler: Compiler = new Compiler(program, options);
|
||||
return compiler.compile();
|
||||
@ -141,6 +146,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
break;
|
||||
|
||||
case NodeKind.IMPORT:
|
||||
break;
|
||||
|
||||
case NodeKind.NAMESPACE:
|
||||
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
|
||||
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
|
||||
@ -162,7 +170,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -186,7 +198,17 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -230,6 +252,17 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// 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 {
|
||||
switch (statement.kind) {
|
||||
|
||||
@ -283,7 +316,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
const children: BinaryenExpressionRef[] = new Array(substatements.length);
|
||||
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
|
||||
children[i] = this.compileStatement(substatements[i]);
|
||||
return this.module.createBlock(null, children);
|
||||
return this.module.createBlock(null, children, BinaryenType.None);
|
||||
}
|
||||
|
||||
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
|
||||
@ -303,21 +336,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef {
|
||||
const expression: BinaryenExpressionRef = this.compileExpression(statement.expression, Type.void);
|
||||
return this.currentType == Type.void ? expression : this.module.createDrop(expression);
|
||||
return this.compileExpression(statement.expression, Type.void);
|
||||
}
|
||||
|
||||
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
|
||||
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 incrementor: BinaryenExportRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 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");
|
||||
}
|
||||
|
||||
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
|
||||
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -346,72 +378,85 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
compileExpression(expression: Expression, resultType: Type): BinaryenExpressionRef {
|
||||
this.currentType = resultType;
|
||||
compileExpression(expression: Expression, contextualType: Type, convert: bool = true): BinaryenExpressionRef {
|
||||
this.currentType = contextualType;
|
||||
|
||||
let expr: BinaryenExpressionRef;
|
||||
switch (expression.kind) {
|
||||
|
||||
case NodeKind.ASSERTION:
|
||||
expr = this.compileAssertionExpression(<AssertionExpression>expression, resultType);
|
||||
expr = this.compileAssertionExpression(<AssertionExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.BINARY:
|
||||
expr = this.compileBinaryExpression(<BinaryExpression>expression, resultType);
|
||||
expr = this.compileBinaryExpression(<BinaryExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.CALL:
|
||||
expr = this.compileCallExpression(<CallExpression>expression, resultType);
|
||||
expr = this.compileCallExpression(<CallExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.ELEMENTACCESS:
|
||||
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, resultType);
|
||||
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.IDENTIFIER:
|
||||
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, resultType);
|
||||
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.LITERAL:
|
||||
expr = this.compileLiteralExpression(<LiteralExpression>expression, resultType);
|
||||
expr = this.compileLiteralExpression(<LiteralExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.NEW:
|
||||
expr = this.compileNewExpression(<NewExpression>expression, resultType);
|
||||
expr = this.compileNewExpression(<NewExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.PARENTHESIZED:
|
||||
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, resultType);
|
||||
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.PROPERTYACCESS:
|
||||
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, resultType);
|
||||
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.SELECT:
|
||||
expr = this.compileSelectExpression(<SelectExpression>expression, resultType);
|
||||
expr = this.compileSelectExpression(<SelectExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.UNARYPOSTFIX:
|
||||
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, resultType);
|
||||
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.UNARYPREFIX:
|
||||
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, resultType);
|
||||
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("unexpected expression kind");
|
||||
}
|
||||
|
||||
if (this.currentType != resultType) {
|
||||
expr = this.convertExpression(expr, this.currentType, resultType);
|
||||
this.currentType = resultType;
|
||||
if (convert && this.currentType != contextualType) {
|
||||
expr = this.convertExpression(expr, this.currentType, contextualType);
|
||||
this.currentType = contextualType;
|
||||
}
|
||||
|
||||
return expr;
|
||||
@ -580,6 +625,221 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@ -599,23 +859,27 @@ export class Compiler extends DiagnosticEmitter {
|
||||
switch (expression.literalKind) {
|
||||
// case LiteralKind.ARRAY:
|
||||
|
||||
case LiteralKind.FLOAT:
|
||||
if (contextualType == Type.f32)
|
||||
return this.module.createF32((<FloatLiteralExpression>expression).value);
|
||||
case LiteralKind.FLOAT: {
|
||||
const floatValue: f64 = (<FloatLiteralExpression>expression).value;
|
||||
if (contextualType == Type.f32 && (Math.fround(floatValue) as f64) == floatValue)
|
||||
return this.module.createF32(floatValue);
|
||||
this.currentType = Type.f64;
|
||||
return this.module.createF64((<FloatLiteralExpression>expression).value);
|
||||
return this.module.createF64(floatValue);
|
||||
}
|
||||
|
||||
case LiteralKind.INTEGER:
|
||||
if (contextualType == Type.bool)
|
||||
return this.module.createI32((<IntegerLiteralExpression>expression).value.isOdd ? 1 : 0)
|
||||
case LiteralKind.INTEGER: {
|
||||
const intValue: I64 = (<IntegerLiteralExpression>expression).value;
|
||||
if (contextualType == Type.bool && (intValue.isZero || intValue.isOne))
|
||||
return this.module.createI32(intValue.isZero ? 0 : 1);
|
||||
if (contextualType.isLongInteger)
|
||||
return this.module.createI64((<IntegerLiteralExpression>expression).value.lo, (<IntegerLiteralExpression>expression).value.hi);
|
||||
const value: i32 = (<IntegerLiteralExpression>expression).value.toI32();
|
||||
if (contextualType.isSmallInteger)
|
||||
return contextualType.isSignedInteger
|
||||
? this.module.createI32(value << contextualType.smallIntegerShift >> contextualType.smallIntegerShift)
|
||||
: this.module.createI32(value & contextualType.smallIntegerMask);
|
||||
return this.module.createI32(value);
|
||||
return this.module.createI64(intValue.lo, intValue.hi);
|
||||
if (!intValue.fitsInI32) {
|
||||
this.currentType = Type.i64;
|
||||
return this.module.createI64(intValue.lo, intValue.hi);
|
||||
}
|
||||
this.currentType = Type.i32;
|
||||
return this.module.createI32(intValue.toI32());
|
||||
}
|
||||
|
||||
// case LiteralKind.OBJECT:
|
||||
// case LiteralKind.REGEXP:
|
||||
@ -644,10 +908,70 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
this.diagnostics.push(message);
|
||||
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 {
|
||||
|
@ -11,7 +11,7 @@ import { Program } from "./program";
|
||||
import { Source } from "./reflection";
|
||||
import { Tokenizer, Token, Range } from "./tokenizer";
|
||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
|
||||
import { I64 } from "./util";
|
||||
import { I64, normalizePath, resolvePath } from "./util";
|
||||
import {
|
||||
|
||||
NodeKind,
|
||||
@ -67,6 +67,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
|
||||
program: Program;
|
||||
backlog: string[] = new Array();
|
||||
seenlog: Set<string> = new Set();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -74,12 +75,14 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
parseFile(text: string, path: string, isEntry: bool): void {
|
||||
if (path.startsWith("./"))
|
||||
path = path.substring(2);
|
||||
const normalizedPath: string = normalizePath(path);
|
||||
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");
|
||||
|
||||
if (isEntry)
|
||||
this.seenlog.add(normalizedPath);
|
||||
|
||||
const source: Source = new Source(path, text, isEntry);
|
||||
this.program.sources.push(source);
|
||||
|
||||
@ -189,6 +192,8 @@ export class Parser extends DiagnosticEmitter {
|
||||
finish(): Program {
|
||||
if (this.backlog.length)
|
||||
throw new Error("backlog is not empty");
|
||||
this.backlog = new Array(0);
|
||||
this.seenlog.clear();
|
||||
return this.program;
|
||||
}
|
||||
|
||||
@ -689,9 +694,14 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
let path: string | null = null;
|
||||
if (tn.skip(Token.FROM)) {
|
||||
if (tn.skip(Token.STRINGLITERAL))
|
||||
if (tn.skip(Token.STRINGLITERAL)) {
|
||||
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());
|
||||
return null;
|
||||
}
|
||||
@ -743,6 +753,11 @@ export class Parser extends DiagnosticEmitter {
|
||||
if (tn.skip(Token.FROM)) {
|
||||
if (tn.skip(Token.STRINGLITERAL)) {
|
||||
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()));
|
||||
tn.skip(Token.SEMICOLON);
|
||||
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 { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
|
||||
import { hasModifier } from "./parser";
|
||||
import { normalizePath, resolvePath } from "./util";
|
||||
import { normalizePath, resolvePath, trimExtension } from "./util";
|
||||
import {
|
||||
|
||||
Node,
|
||||
NodeKind,
|
||||
SourceNode,
|
||||
ModifierKind,
|
||||
IdentifierExpression,
|
||||
|
||||
ClassDeclaration,
|
||||
DeclarationStatement,
|
||||
EnumDeclaration,
|
||||
EnumValueDeclaration,
|
||||
ExportMember,
|
||||
ExportStatement,
|
||||
FieldDeclaration,
|
||||
FunctionDeclaration,
|
||||
ImportDeclaration,
|
||||
@ -27,15 +30,31 @@ import {
|
||||
|
||||
} from "./ast";
|
||||
|
||||
class QueuedExport {
|
||||
importName: string;
|
||||
member: ExportMember;
|
||||
}
|
||||
|
||||
class QueuedImport {
|
||||
globalName: string;
|
||||
importName: string;
|
||||
declaration: ImportDeclaration;
|
||||
}
|
||||
|
||||
export class Program extends DiagnosticEmitter {
|
||||
|
||||
sources: Source[];
|
||||
diagnosticsOffset: i32 = 0;
|
||||
target: Target = Target.WASM32;
|
||||
|
||||
// internal names to declarations
|
||||
names: Map<string,DeclarationStatement> = new Map();
|
||||
// type names to types
|
||||
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();
|
||||
enums: Enum[] = new Array();
|
||||
functions: Function[] = new Array();
|
||||
@ -51,9 +70,10 @@ export class Program extends DiagnosticEmitter {
|
||||
this.target = 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) {
|
||||
const source: Source = this.sources[i];
|
||||
const statements: Statement[] = source.statements;
|
||||
@ -69,13 +89,16 @@ export class Program extends DiagnosticEmitter {
|
||||
this.initializeEnum(<EnumDeclaration>statement);
|
||||
break;
|
||||
|
||||
case NodeKind.EXPORT:
|
||||
this.initializeExports(<ExportStatement>statement, exportsMap);
|
||||
break;
|
||||
|
||||
case NodeKind.FUNCTION:
|
||||
this.initializeFunction(<FunctionDeclaration>statement);
|
||||
break;
|
||||
|
||||
case NodeKind.IMPORT:
|
||||
this.initializeImports(<ImportStatement>statement);
|
||||
importStatements.push(<ImportStatement>statement);
|
||||
this.initializeImports(<ImportStatement>statement, exportsMap, importsQueue);
|
||||
break;
|
||||
|
||||
case NodeKind.INTERFACE:
|
||||
@ -93,26 +116,52 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// resolve imports to their respective declarations
|
||||
for (let i: i32 = 0, k: i32 = importStatements.length; i < k; ++i) {
|
||||
const statement: ImportStatement = importStatements[i];
|
||||
const importPath: string = resolvePath(normalizePath(statement.path), statement.range.source.normalizedPath);
|
||||
const members: ImportDeclaration[] = statement.declarations;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j){
|
||||
const declaration: ImportDeclaration = members[j];
|
||||
const importedName: string = declaration.externalIdentifier.name;
|
||||
for (let m: i32 = 0, n: i32 = this.sources.length; m < n; ++n) {
|
||||
const source: Source = this.sources[m];
|
||||
if (source.path == importPath) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
// at this point exports map should be resolvable
|
||||
for (let [key, val] of exportsMap.entries()) {
|
||||
const seen: Set<QueuedExport> = new Set();
|
||||
while (exportsMap.has(val.importName)) {
|
||||
val = <QueuedExport>exportsMap.get(val.importName);
|
||||
if (seen.has(val))
|
||||
break;
|
||||
seen.add(val);
|
||||
}
|
||||
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 {
|
||||
this.addName(declaration);
|
||||
private initializeClass(declaration: ClassDeclaration): void {
|
||||
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;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
@ -132,37 +181,88 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
initializeField(declaration: FieldDeclaration): void {
|
||||
this.addName(declaration);
|
||||
private initializeField(declaration: FieldDeclaration): void {
|
||||
const globalName: string = this.mangleName(declaration);
|
||||
this.addName(globalName, declaration);
|
||||
}
|
||||
|
||||
initializeEnum(declaration: EnumDeclaration): void {
|
||||
this.addName(declaration);
|
||||
private initializeEnum(declaration: EnumDeclaration): void {
|
||||
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;
|
||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||
this.initializeEnumValue(members[i]);
|
||||
}
|
||||
|
||||
initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||
this.addName(declaration);
|
||||
private initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||
const globalName: string = this.mangleName(declaration);
|
||||
this.addName(globalName, declaration);
|
||||
}
|
||||
|
||||
initializeFunction(declaration: FunctionDeclaration): void {
|
||||
this.addName(declaration);
|
||||
}
|
||||
|
||||
initializeImports(statement: ImportStatement): void {
|
||||
const members: ImportDeclaration[] = statement.declarations;
|
||||
private initializeExports(statement: ExportStatement, exportsMap: Map<string,QueuedExport>): void {
|
||||
const members: ExportMember[] = statement.members;
|
||||
const normalizedPath: string | null = statement.path == null ? null : normalizePath(<string>statement.path);
|
||||
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 {
|
||||
this.addName(declaration);
|
||||
private initializeExport(member: ExportMember, normalizedPath: string | null, exportsMap: Map<string,QueuedExport>): void {
|
||||
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 {
|
||||
this.addName(declaration);
|
||||
private initializeFunction(declaration: FunctionDeclaration): void {
|
||||
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;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
@ -182,12 +282,16 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
initializeMethod(declaration: MethodDeclaration): void {
|
||||
this.addName(declaration);
|
||||
private initializeMethod(declaration: MethodDeclaration): void {
|
||||
const globalName: string = this.mangleName(declaration);
|
||||
this.addName(globalName, declaration);
|
||||
}
|
||||
|
||||
initializeNamespace(declaration: NamespaceDeclaration): void {
|
||||
this.addName(declaration);
|
||||
private initializeNamespace(declaration: NamespaceDeclaration): 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;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
@ -214,7 +318,7 @@ export class Program extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case NodeKind.VARIABLE:
|
||||
this.initializeVariables(<VariableStatement>statement);
|
||||
this.initializeVariables(<VariableStatement>statement, true);
|
||||
break;
|
||||
|
||||
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 isExport: bool = !insideNamespace && hasModifier(ModifierKind.EXPORT, statement.modifiers);
|
||||
for (let i: i32 = 0, k = declarations.length; i < k; ++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 {
|
||||
const name: string = this.mangleName(declaration);
|
||||
if (this.names.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); // recoverable
|
||||
private addName(globalName: string, declaration: DeclarationStatement): void {
|
||||
if (this.names.has(globalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, globalName); // recoverable
|
||||
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 {
|
||||
@ -246,7 +360,7 @@ export class Program extends DiagnosticEmitter {
|
||||
switch (parent.kind) {
|
||||
|
||||
case NodeKind.SOURCE:
|
||||
return (<SourceNode>parent).path + "/" + name;
|
||||
return (<Source>parent).normalizedPath + "/" + name;
|
||||
|
||||
case NodeKind.CLASS: {
|
||||
if (
|
||||
@ -314,12 +428,12 @@ function initializeBasicTypes(types: Map<string,Type>, target: Target) {
|
||||
types.set("i16", Type.i16);
|
||||
types.set("i32", Type.i32);
|
||||
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("u16", Type.u16);
|
||||
types.set("u32", Type.u32);
|
||||
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("void", Type.void);
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import {
|
||||
import { DiagnosticMessage } from "./diagnostics";
|
||||
import { Token, Tokenizer, Range } from "./tokenizer";
|
||||
import { hasModifier } from "./parser";
|
||||
import { normalizePath } from "./util";
|
||||
import { normalizePath, trimExtension } from "./util";
|
||||
|
||||
export abstract class Base {
|
||||
|
||||
@ -146,7 +146,7 @@ export class Type extends Base {
|
||||
export class Source extends SourceNode {
|
||||
|
||||
text: string;
|
||||
tokenizer: Tokenizer | null;
|
||||
tokenizer: Tokenizer | null = null;
|
||||
statements: Statement[];
|
||||
isEntry: bool;
|
||||
normalizedPath: string;
|
||||
@ -158,7 +158,7 @@ export class Source extends SourceNode {
|
||||
this.text = text;
|
||||
this.statements = new Array();
|
||||
this.isEntry = isEntry;
|
||||
this.normalizedPath = normalizePath(path);
|
||||
this.normalizedPath = normalizePath(trimExtension(path));
|
||||
}
|
||||
|
||||
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
|
||||
|
@ -9,6 +9,7 @@
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"downlevelIteration": true,
|
||||
"strictNullChecks": true,
|
||||
"alwaysStrict": true,
|
||||
"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 {
|
||||
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;
|
||||
}
|
||||
|
||||
get isOne(): bool {
|
||||
return this.lo == 1 && this.hi == 0;
|
||||
}
|
||||
|
||||
get isPositive(): bool {
|
||||
return this.hi >= 0;
|
||||
}
|
||||
@ -47,6 +51,10 @@ export class I64 {
|
||||
return (this.lo & 1) == 0;
|
||||
}
|
||||
|
||||
get fitsInI32(): bool {
|
||||
return this.hi == 0 || (this.hi == -1 && this.lo < 0);
|
||||
}
|
||||
|
||||
toI32(): i32 {
|
||||
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