Resolve exports and imports; Initial work on binary and unary ops

This commit is contained in:
dcodeIO 2017-09-29 17:25:02 +02:00
parent 2e611610f2
commit e14d02e040
11 changed files with 621 additions and 111 deletions

View File

@ -1,4 +1,5 @@
{
"license": "Apache-2.0",
"devDependencies": {
"@types/chalk": "^0.4.31",
"@types/diff": "^3.2.2",

View File

@ -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);
}
}
}

View File

@ -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 {
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 {
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);
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -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"); }

View File

@ -9,6 +9,7 @@
"types": [
"node"
],
"downlevelIteration": true,
"strictNullChecks": true,
"alwaysStrict": true,
"outDir": "../out"

View File

@ -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;
}

View File

@ -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
View 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);
}); */