From d3d4938b684d07593a7e1cbb80b58ce136196ecb Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Fri, 17 Nov 2017 14:33:51 +0100 Subject: [PATCH] Progress --- package-lock.json | 6 +- package.json | 6 +- src/ast.ts | 9 +- src/binaryen.d.ts | 74 ++++- src/binaryen.ts | 136 ++++++-- src/compiler.ts | 74 ++--- src/constants.ts | 4 +- src/diagnosticMessages.generated.ts | 4 + src/diagnosticMessages.json | 2 + src/diagnostics.ts | 20 +- src/evaluator.ts | 68 ++++ src/program.ts | 463 +++++++++++++++------------- src/tsconfig.json | 1 + src/types.ts | 6 +- src/util.ts | 3 - tests/compiler.ts | 5 + tests/tokenizer.ts | 2 +- 17 files changed, 580 insertions(+), 303 deletions(-) create mode 100644 src/evaluator.ts diff --git a/package-lock.json b/package-lock.json index b4be965b..fb6a629d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,9 +41,9 @@ "dev": true }, "binaryen": { - "version": "37.0.0-nightly.20170912", - "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-37.0.0-nightly.20170912.tgz", - "integrity": "sha512-yXLgHkUvTMqEV1vkixAaldnS4w6WOrqY+30Cx9k+JjBzmA6wJTQr0F32xFg/4MPr4NRZOdP/AnI8ais4nhfgIw==" + "version": "39.0.0-nightly.20171116", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-39.0.0-nightly.20171116.tgz", + "integrity": "sha512-ljl/qPne0+8hYtNWITRSAtxNM1EG5NnvTg+HRmSUdNAK2j9wcyAAg5uVj+TgipEqY82kmHt5C9+TSQNEwaxgrw==" }, "chalk": { "version": "2.1.0", diff --git a/package.json b/package.json index 2017873e..377dca49 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,10 @@ }, "dependencies": { "@types/node": "^8.0.28", - "binaryen": "37.0.0-nightly.20170912" + "binaryen": "39.0.0-nightly.20171116" + }, + "scripts": { + "build": "tsc -P src", + "test:compiler": "ts-node -P src tests/compiler" } } diff --git a/src/ast.ts b/src/ast.ts index 5555cee9..f3cec461 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1091,7 +1091,7 @@ export class Source extends Node { tokenizer: Tokenizer | null = null; isEntry: bool; - constructor(path: string, text: string, isEntry: bool) { + constructor(path: string, text: string, isEntry: bool = false) { super(); this.path = path; this.normalizedPath = normalizePath(path, true); @@ -1392,6 +1392,7 @@ export class ExpressionStatement extends Statement { serialize(sb: string[]): void { this.expression.serialize(sb); + sb.push(";"); } } @@ -1773,6 +1774,7 @@ export class ThrowStatement extends Statement { serialize(sb: string[]): void { sb.push("throw "); this.expression.serialize(sb); + sb.push(";"); } } @@ -1895,10 +1897,11 @@ export function serialize(node: Node, indent: i32 = 0): string { } export function mangleInternalPath(path: string): string { - if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH) + // TODO: not necessary with current config + /* if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH) path = path.replace("/", PATH_DELIMITER); if (PARENT_SUBST != "..") - path = path.replace("..", PARENT_SUBST); + path = path.replace("..", PARENT_SUBST); */ return path; } diff --git a/src/binaryen.d.ts b/src/binaryen.d.ts index 23866c55..9e07d3f7 100644 --- a/src/binaryen.d.ts +++ b/src/binaryen.d.ts @@ -15,7 +15,7 @@ declare function _free(ptr: usize): void; declare type BinaryenIndex = u32; -declare type BinaryenType = u32; +declare type BinaryenType = i32; declare function _BinaryenNone(): BinaryenType; declare function _BinaryenInt32(): BinaryenType; @@ -24,6 +24,37 @@ declare function _BinaryenFloat32(): BinaryenType; declare function _BinaryenFloat64(): BinaryenType; declare function _BinaryenUndefined(): BinaryenType; +declare type BinaryenExpressionId = i32; + +declare function _BinaryenInvalidId(): BinaryenExpressionId; +declare function _BinaryenBlockId(): BinaryenExpressionId; +declare function _BinaryenIfId(): BinaryenExpressionId; +declare function _BinaryenLoopId(): BinaryenExpressionId; +declare function _BinaryenBreakId(): BinaryenExpressionId; +declare function _BinaryenSwitchId(): BinaryenExpressionId; +declare function _BinaryenCallId(): BinaryenExpressionId; +declare function _BinaryenCallImportId(): BinaryenExpressionId; +declare function _BinaryenCallIndirectId(): BinaryenExpressionId; +declare function _BinaryenGetLocalId(): BinaryenExpressionId; +declare function _BinaryenSetLocalId(): BinaryenExpressionId; +declare function _BinaryenGetGlobalId(): BinaryenExpressionId; +declare function _BinaryenSetGlobalId(): BinaryenExpressionId; +declare function _BinaryenLoadId(): BinaryenExpressionId; +declare function _BinaryenStoreId(): BinaryenExpressionId; +declare function _BinaryenConstId(): BinaryenExpressionId; +declare function _BinaryenUnaryId(): BinaryenExpressionId; +declare function _BinaryenBinaryId(): BinaryenExpressionId; +declare function _BinaryenSelectId(): BinaryenExpressionId; +declare function _BinaryenDropId(): BinaryenExpressionId; +declare function _BinaryenReturnId(): BinaryenExpressionId; +declare function _BinaryenHostId(): BinaryenExpressionId; +declare function _BinaryenNopId(): BinaryenExpressionId; +declare function _BinaryenUnreachableId(): BinaryenExpressionId; +declare function _BinaryenAtomicCmpxchgId(): BinaryenExpressionId; +declare function _BinaryenAtomicRMWId(): BinaryenExpressionId; +declare function _BinaryenAtomicWaitId(): BinaryenExpressionId; +declare function _BinaryenAtomicWakeId(): BinaryenExpressionId; + declare type BinaryenModuleRef = usize; declare function _BinaryenModuleCreate(): BinaryenModuleRef; @@ -34,10 +65,11 @@ declare type CString = usize; declare type CArray = usize; declare function _BinaryenAddFunctionType(module: BinaryenModuleRef, name: CString, result: BinaryenType, paramTypes: CArray, numParams: BinaryenIndex): BinaryenFunctionTypeRef; +declare function _BinaryenGetFunctionTypeBySignature(module: BinaryenModuleRef, result: BinaryenType, paramTypes: CArray, numParams: BinaryenIndex): BinaryenFunctionTypeRef; declare type BinaryenLiteral = CArray; -// LLVM C ABI with `out` being a buffer of 16 bytes receiving the BinaryenLiteral struct +// LLVM C ABI with `out` being a buffer of 16 bytes receiving the BinaryenLiteral struct. // union value starts at offset 8 due to alignment (?) declare function _BinaryenLiteralInt32(out: BinaryenLiteral, x: i32): void; declare function _BinaryenLiteralInt64(out: BinaryenLiteral, x: i32, y: i32): void; @@ -171,10 +203,22 @@ declare function _BinaryenLtFloat64(): BinaryenOp; declare function _BinaryenLeFloat64(): BinaryenOp; declare function _BinaryenGtFloat64(): BinaryenOp; declare function _BinaryenGeFloat64(): BinaryenOp; -declare function _BinaryenPageSize(): BinaryenOp; -declare function _BinaryenCurrentMemory(): BinaryenOp; -declare function _BinaryenGrowMemory(): BinaryenOp; -declare function _BinaryenHasFeature(): BinaryenOp; + +declare type BinaryenHostOp = BinaryenOp; + +declare function _BinaryenPageSize(): BinaryenHostOp; +declare function _BinaryenCurrentMemory(): BinaryenHostOp; +declare function _BinaryenGrowMemory(): BinaryenHostOp; +declare function _BinaryenHasFeature(): BinaryenHostOp; + +declare type BinaryenAtomicRMWOp = BinaryenOp; + +declare function _BinaryenAtomicRMWAdd(): BinaryenAtomicRMWOp; +declare function _BinaryenAtomicRMWSub(): BinaryenAtomicRMWOp; +declare function _BinaryenAtomicRMWAnd(): BinaryenAtomicRMWOp; +declare function _BinaryenAtomicRMWOr(): BinaryenAtomicRMWOp; +declare function _BinaryenAtomicRMWXor(): BinaryenAtomicRMWOp; +declare function _BinaryenAtomicRMWXchg(): BinaryenAtomicRMWOp; declare type BinaryenExpressionRef = usize; @@ -202,8 +246,19 @@ declare function _BinaryenReturn(module: BinaryenModuleRef, value: BinaryenExpre declare function _BinaryenHost(module: BinaryenModuleRef, op: BinaryenOp, name: CString | 0, operands: CArray, numOperands: BinaryenIndex): BinaryenExpressionRef; declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef; declare function _BinaryenUnreachable(module: BinaryenModuleRef): BinaryenExpressionRef; +declare function _BinaryenAtomicRMW(module: BinaryenModuleRef, op: BinaryenAtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef; +declare function _BinaryenAtomicCmpxchg(module: BinaryenModuleRef, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef; +declare function _BinaryenAtomicWait(module: BinaryenModuleRef, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef; +declare function _BinaryenAtomicWake(module: BinaryenModuleRef, ptr: BinaryenExpressionRef, wakeCount: BinaryenExpressionRef): BinaryenExpressionRef; +declare function _BinaryenExpressionGetId(expr: BinaryenExpressionRef): BinaryenExpressionId; +declare function _BinaryenExpressionGetType(expr: BinaryenExpressionRef): BinaryenType; declare function _BinaryenExpressionPrint(expr: BinaryenExpressionRef): void; +declare function _BinaryenConstGetValueI32(expr: BinaryenExpressionRef): i32; +declare function _BinaryenConstGetValueI64Low(expr: BinaryenExpressionRef): i32; +declare function _BinaryenConstGetValueI64High(expr: BinaryenExpressionRef): i32; +declare function _BinaryenConstGetValueF32(expr: BinaryenExpressionRef): f32; +declare function _BinaryenConstGetValueF64(expr: BinaryenExpressionRef): f64; declare type BinaryenFunctionRef = usize; @@ -219,7 +274,9 @@ declare type BinaryenExportRef = usize; declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef; declare function _BinaryenRemoveExport(module: BinaryenModuleRef, externalName: CString): void; -declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenImportRef; // sic +declare type BinaryenGlobalRef = usize; + +declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenGlobalRef; declare function _BinaryenSetFunctionTable(module: BinaryenModuleRef, funcs: CArray, numFuncs: BinaryenIndex): void; @@ -232,6 +289,7 @@ declare function _BinaryenModulePrint(module: BinaryenModuleRef): void; declare function _BinaryenModulePrintAsmjs(module: BinaryenModuleRef): void; declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32; declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void; +declare function _BinaryenModuleRunPasses(module: BinaryenModuleRef, passes: string[]): void; declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void; declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize; declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef; @@ -248,5 +306,3 @@ declare function _RelooperAddBranchForSwitch(from: RelooperBlockRef, to: Reloope declare function _RelooperRenderAndDispose(relooper: RelooperRef, entry: RelooperBlockRef, labelHelper: BinaryenIndex, module: BinaryenModuleRef): BinaryenExpressionRef; declare function _BinaryenSetAPITracing(on: i32): void; - -declare function _BinaryenGetFunctionTypeBySignature(module: BinaryenModuleRef, result: BinaryenType, paramTypes: CArray, numParams: BinaryenIndex): BinaryenFunctionTypeRef; diff --git a/src/binaryen.ts b/src/binaryen.ts index 726edb15..ecaf5ac4 100644 --- a/src/binaryen.ts +++ b/src/binaryen.ts @@ -1,4 +1,4 @@ -import { U64 } from "./util"; +import { I64, U64 } from "./util"; import { Target } from "./compiler"; export enum Type { @@ -10,6 +10,37 @@ export enum Type { Undefined = _BinaryenUndefined() } +export enum ExpressionId { + Invalid = _BinaryenInvalidId(), + Block = _BinaryenBlockId(), + If = _BinaryenIfId(), + Loop = _BinaryenLoopId(), + Break = _BinaryenBreakId(), + Switch = _BinaryenSwitchId(), + Call = _BinaryenCallId(), + CallImport = _BinaryenCallImportId(), + CallIndirect = _BinaryenCallIndirectId(), + GetLocal = _BinaryenGetLocalId(), + SetLocal = _BinaryenSetLocalId(), + GetGlobal = _BinaryenGetGlobalId(), + SetGlobal = _BinaryenSetGlobalId(), + Load = _BinaryenLoadId(), + Store = _BinaryenStoreId(), + Const = _BinaryenConstId(), + Unary = _BinaryenUnaryId(), + Binary = _BinaryenBinaryId(), + Select = _BinaryenSelectId(), + Drop = _BinaryenDropId(), + Return = _BinaryenReturnId(), + Host = _BinaryenHostId(), + Nop = _BinaryenNopId(), + Unreachable = _BinaryenUnreachableId(), + AtomicCmpxchg = _BinaryenAtomicCmpxchgId(), + AtomicRMW = _BinaryenAtomicRMWId(), + AtomicWait = _BinaryenAtomicWaitId(), + AtomicWake = _BinaryenAtomicWakeId() +} + export enum UnaryOp { ClzI32 = _BinaryenClzInt32(), CtzI32 = _BinaryenCtzInt32(), @@ -146,13 +177,13 @@ export enum HostOp { HasFeature = _BinaryenHasFeature() } -export enum AtomicRMWOp { // TODO: not yet part of the C-API - Add, - Sub, - And, - Or, - Xor, - Xchg +export enum AtomicRMWOp { + Add = _BinaryenAtomicRMWAdd(), + Sub = _BinaryenAtomicRMWSub(), + And = _BinaryenAtomicRMWAnd(), + Or = _BinaryenAtomicRMWOr(), + Xor = _BinaryenAtomicRMWXor(), + Xchg = _BinaryenAtomicRMWXchg() } export class MemorySegment { @@ -216,8 +247,8 @@ export class Module { try { return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length); } finally { - _free(cStr); _free(cArr); + _free(cStr); } } @@ -274,8 +305,8 @@ export class Module { try { return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (operands).length : 0); } finally { - _free(cStr); _free(cArr); + _free(cStr); } } @@ -299,6 +330,26 @@ export class Module { } } + createAtomicRMW(op: AtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicRMW(this.ref, op, bytes, offset, ptr, value, type); + } + + createAtomicCmpxchg(bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: Type): BinaryenExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicCmpxchg(this.ref, bytes, offset, ptr, expected, replacement, type); + } + + createAtomicWait(ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicWait(this.ref, ptr, expected, timeout, expectedType); + } + + createAtomicWake(ptr: BinaryenExpressionRef, wakeCount: BinaryenExpressionRef): BinaryenExpressionRef { + if (this.noEmit) return 0; + return _BinaryenAtomicWake(this.ref, ptr, wakeCount); + } + // statements createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef { @@ -323,8 +374,8 @@ export class Module { try { return _BinaryenBlock(this.ref, cStr, cArr, children.length, type); } finally { - _free(cStr); _free(cArr); + _free(cStr); } } @@ -383,9 +434,9 @@ export class Module { try { return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value); } finally { - for (i = 0; i < k; ++i) _free(strs[i]); - _free(cArr); _free(cStr); + _free(cArr); + for (i = k - 1; i >= 0; --i) _free(strs[i]); } } @@ -416,7 +467,7 @@ export class Module { // meta - addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenImportRef { + addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenGlobalRef { if (this.noEmit) return 0; const cStr: CString = allocString(name); try { @@ -433,8 +484,8 @@ export class Module { try { return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body); } finally { - _free(cStr); _free(cArr); + _free(cStr); } } @@ -445,8 +496,8 @@ export class Module { try { return _BinaryenAddExport(this.ref, cStr1, cStr2); } finally { - _free(cStr1); _free(cStr2); + _free(cStr1); } } @@ -468,9 +519,9 @@ export class Module { try { return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type); } finally { - _free(cStr1); - _free(cStr2); _free(cStr3); + _free(cStr2); + _free(cStr1); } } @@ -506,11 +557,11 @@ export class Module { try { _BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k); } finally { - _free(cStr); - for (i = 0; i < k; ++i) _free(segs[i]); - _free(cArr1); - _free(cArr2); _free(cArr3); + _free(cArr2); + _free(cArr1); + for (i = k - 1; i >= 0; --i) _free(segs[i]); + _free(cStr); } } @@ -540,6 +591,45 @@ export class Module { } } +export function getExpressionId(expr: BinaryenExpressionRef): ExpressionId { + return _BinaryenExpressionGetId(expr); +} + +export function getExpressionType(expr: BinaryenExpressionRef): Type { + return _BinaryenExpressionGetType(expr); +} + +export function printExpression(expr: BinaryenExpressionRef): void { + return _BinaryenExpressionPrint(expr); +} + +export function getConstValueI32(expr: BinaryenExpressionRef): i32 { + return _BinaryenConstGetValueI32(expr); +} + +export function getConstValueI64Low(expr: BinaryenExpressionRef): i32 { + return _BinaryenConstGetValueI64Low(expr); +} + +export function getConstValueI64High(expr: BinaryenExpressionRef): i32 { + return _BinaryenConstGetValueI64High(expr); +} + +export function getConstValueI64(expr: BinaryenExpressionRef): I64 { + return new I64( + _BinaryenConstGetValueI64Low(expr), + _BinaryenConstGetValueI64High(expr) + ); +} + +export function getConstValueF32(expr: BinaryenExpressionRef): f32 { + return _BinaryenConstGetValueF32(expr); +} + +export function getConstValueF64(expr: BinaryenExpressionRef): f64 { + return _BinaryenConstGetValueF64(expr); +} + export class Relooper { module: Module; @@ -596,7 +686,7 @@ export class Relooper { } // helpers -// TODO: investigate stack allocation? +// can't do stack allocation here: STACKTOP is a global in WASM but a hidden variable in asm.js function allocU8Array(u8s: Uint8Array | null): CArray { if (!u8s) return 0; diff --git a/src/compiler.ts b/src/compiler.ts index fac2107f..d9cee56c 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,7 +1,7 @@ import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType, Relooper } from "./binaryen"; import { PATH_DELIMITER } from "./constants"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; -import { Program, ClassTemplate, Element, ElementKind, Enum, FunctionTemplate, FunctionInstance, Global, Local, Namespace, Parameter } from "./program"; +import { Program, ClassPrototype, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program"; import { CharCode, I64, U64, normalizePath, sb } from "./util"; import { Token } from "./tokenizer"; import { @@ -96,11 +96,11 @@ export class Compiler extends DiagnosticEmitter { options: Options; module: Module; - startFunction: FunctionInstance; + startFunction: Function; startFunctionBody: BinaryenExpressionRef[] = new Array(); currentType: Type = Type.void; - currentFunction: FunctionInstance; + currentFunction: Function; disallowContinue: bool = true; memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL @@ -118,8 +118,8 @@ export class Compiler extends DiagnosticEmitter { this.program = program; this.options = options ? options : new Options(); this.module = this.options.noEmit ? Module.createStub() : Module.create(); - const startFunctionTemplate: FunctionTemplate = new FunctionTemplate(program, "start", null); - const startFunctionInstance: FunctionInstance = new FunctionInstance(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null); + const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", null); + const startFunctionInstance: Function = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null); this.currentFunction = this.startFunction = startFunctionInstance; this.memoryOffset = new U64(2 * (this.options.target == Target.WASM64 ? 8 : 4), 0); // leave space for `null` and heapStart (both of usize type) } @@ -251,7 +251,7 @@ export class Compiler extends DiagnosticEmitter { // otherwise a top-level statement that is part of the start function's body default: { - const previousFunction: FunctionInstance = this.currentFunction; + const previousFunction: Function = this.currentFunction; this.currentFunction = this.startFunction; this.startFunctionBody.push(this.compileStatement(statement)); this.currentFunction = previousFunction; @@ -290,7 +290,7 @@ export class Compiler extends DiagnosticEmitter { let initializeInStart: bool; if (element.hasConstantValue) { if (type.isLongInteger) - initializer = this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi); + initializer = element.constantIntegerValue ? this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) : this.module.createI64(0, 0); else if (type.kind == TypeKind.F32) initializer = this.module.createF32(element.constantFloatValue); else if (type.kind == TypeKind.F64) @@ -298,11 +298,11 @@ export class Compiler extends DiagnosticEmitter { else if (type.isSmallInteger) { if (type.isSignedInteger) { const shift: i32 = type.smallIntegerShift; - initializer = this.module.createI32(element.constantIntegerValue.toI32() << shift >> shift); + initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0); } else - initializer = this.module.createI32(element.constantIntegerValue.toI32() & type.smallIntegerMask); + initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & type.smallIntegerMask: 0); } else - initializer = this.module.createI32(element.constantIntegerValue.toI32()); + initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() : 0); initializeInStart = false; this.module.addGlobal(element.internalName, binaryenType, element.isMutable, initializer); } else if (declaration) { @@ -374,16 +374,16 @@ export class Compiler extends DiagnosticEmitter { compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null, alternativeReportNode: Node | null = null): void { const internalName: string = declaration.internalName; const element: Element | null = this.program.elements.get(internalName); - if (!element || element.kind != ElementKind.FUNCTION) + if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE) throw new Error("unexpected missing function"); const resolvedTypeArguments: Type[] | null = this.program.resolveTypeArguments(declaration.typeParameters, typeArguments, contextualTypeArguments, alternativeReportNode); // reports if (!resolvedTypeArguments) return; - this.compileFunction(element, resolvedTypeArguments, contextualTypeArguments); + this.compileFunction(element, resolvedTypeArguments, contextualTypeArguments); } - compileFunction(template: FunctionTemplate, typeArguments: Type[], contextualTypeArguments: Map | null = null): void { - const instance: FunctionInstance | null = template.instantiate(typeArguments, contextualTypeArguments); + compileFunction(template: FunctionPrototype, typeArguments: Type[], contextualTypeArguments: Map | null = null): void { + const instance: Function | null = template.resolve(typeArguments, contextualTypeArguments); if (!instance || instance.compiled) return; @@ -398,7 +398,7 @@ export class Compiler extends DiagnosticEmitter { instance.compiled = true; // compile statements - const previousFunction: FunctionInstance = this.currentFunction; + const previousFunction: Function = this.currentFunction; this.currentFunction = instance; const stmts: BinaryenExpressionRef[] = this.compileStatements(declaration.statements); this.currentFunction = previousFunction; @@ -468,18 +468,18 @@ export class Compiler extends DiagnosticEmitter { for (let [name, element] of ns.members) { switch (element.kind) { - case ElementKind.CLASS: - if ((noTreeShaking || (element).isExport) && !(element).isGeneric) - this.compileClass(element, []); + case ElementKind.CLASS_PROTOTYPE: + if ((noTreeShaking || (element).isExport) && !(element).isGeneric) + this.compileClass(element, []); break; case ElementKind.ENUM: this.compileEnum(element); break; - case ElementKind.FUNCTION: - if ((noTreeShaking || (element).isExport) && !(element).isGeneric) - this.compileFunction(element, []); + case ElementKind.FUNCTION_PROTOTYPE: + if ((noTreeShaking || (element).isExport) && !(element).isGeneric) + this.compileFunction(element, []); break; case ElementKind.GLOBAL: @@ -506,18 +506,18 @@ export class Compiler extends DiagnosticEmitter { throw new Error("unexpected missing element"); switch (element.kind) { - case ElementKind.CLASS: - if (!(element).isGeneric) - this.compileClass(element, []); + case ElementKind.CLASS_PROTOTYPE: + if (!(element).isGeneric) + this.compileClass(element, []); break; case ElementKind.ENUM: this.compileEnum(element); break; - case ElementKind.FUNCTION: - if (!(element).isGeneric) - this.compileFunction(element, []); + case ElementKind.FUNCTION_PROTOTYPE: + if (!(element).isGeneric) + this.compileFunction(element, []); break; case ElementKind.GLOBAL: @@ -536,15 +536,15 @@ export class Compiler extends DiagnosticEmitter { compileClassDeclaration(declaration: ClassDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map | null = null, alternativeReportNode: Node | null = null): void { const internalName: string = declaration.internalName; const element: Element | null = this.program.elements.get(internalName); - if (!element || element.kind != ElementKind.CLASS) + if (!element || element.kind != ElementKind.CLASS_PROTOTYPE) throw new Error("unexpected missing class"); const resolvedTypeArguments: Type[] | null = this.program.resolveTypeArguments(declaration.typeParameters, typeArguments, contextualTypeArguments, alternativeReportNode); // reports if (!resolvedTypeArguments) return; - this.compileClass(element, resolvedTypeArguments, contextualTypeArguments); + this.compileClass(element, resolvedTypeArguments, contextualTypeArguments); } - compileClass(cls: ClassTemplate, typeArguments: Type[], contextualTypeArguments: Map | null = null) { + compileClass(cls: ClassPrototype, typeArguments: Type[], contextualTypeArguments: Map | null = null) { throw new Error("not implemented"); } @@ -1053,9 +1053,9 @@ export class Compiler extends DiagnosticEmitter { } compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef { - const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments, true); // reports + const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports if (toType && toType != contextualType) { - const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, toType); + const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, toType, false); return this.convertExpression(expr, this.currentType, toType); } return this.compileExpression(expression.expression, contextualType); @@ -1316,9 +1316,13 @@ export class Compiler extends DiagnosticEmitter { } compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef { - const element: Element | null = this.program.resolveElement(expression, this.currentFunction); - if (!element || element.kind != ElementKind.FUNCTION) + const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports + if (!element) return this.module.createUnreachable(); + if (element.kind != ElementKind.FUNCTION_PROTOTYPE) { + // TODO: report 'Cannot invoke an expression whose type lacks a call signature.' + return this.module.createUnreachable(); + } throw new Error("not implemented"); } @@ -1401,7 +1405,7 @@ export class Compiler extends DiagnosticEmitter { // throw new Error("not implemented"); // getter - if (element.kind == ElementKind.FUNCTION && (element).isGetter) + if (element.kind == ElementKind.FUNCTION_PROTOTYPE && (element).isGetter) throw new Error("not implemented"); this.error(DiagnosticCode.Operation_not_supported, expression.range); diff --git a/src/constants.ts b/src/constants.ts index e9f02e73..831a3d3e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,7 +2,7 @@ export const PATH_DELIMITER: string = "/"; export const PARENT_SUBST: string = ".."; -export const GETTER_PREFIX: string = "get:"; -export const SETTER_PREFIX: string = "set:"; +export const GETTER_PREFIX: string = "get "; +export const SETTER_PREFIX: string = "set "; export const INSTANCE_DELIMITER: string = "#"; export const STATIC_DELIMITER: string = "."; diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index a5fd8466..1c870a6f 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -51,10 +51,12 @@ export enum DiagnosticCode { Type_0_is_not_generic = 2315, Type_0_is_not_assignable_to_type_1 = 2322, _this_cannot_be_referenced_in_current_location = 2332, + Property_0_does_not_exist_on_type_1 = 2339, The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357, The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364, Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391, Duplicate_function_implementation = 2393, + Export_declaration_conflicts_with_exported_declaration_of_0 = 2484, The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541, Expected_0_type_arguments_but_got_1 = 2558, File_0_not_found = 6054 @@ -112,10 +114,12 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 2315: return "Type '{0}' is not generic."; case 2322: return "Type '{0}' is not assignable to type '{1}'."; case 2332: return "'this' cannot be referenced in current location."; + case 2339: return "Property '{0}' does not exist on type '{1}'."; case 2357: return "The operand of an increment or decrement operator must be a variable or a property access."; case 2364: return "The left-hand side of an assignment expression must be a variable or a property access."; case 2391: return "Function implementation is missing or not immediately following the declaration."; case 2393: return "Duplicate function implementation."; + case 2484: return "Export declaration conflicts with exported declaration of '{0}'."; case 2541: return "The target of an assignment must be a variable or a property access."; case 2558: return "Expected {0} type arguments, but got {1}."; case 6054: return "File '{0}' not found."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index c44ba842..76f9c690 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -51,10 +51,12 @@ "Type '{0}' is not generic.": 2315, "Type '{0}' is not assignable to type '{1}'.": 2322, "'this' cannot be referenced in current location.": 2332, + "Property '{0}' does not exist on type '{1}'.": 2339, "The operand of an increment or decrement operator must be a variable or a property access.": 2357, "The left-hand side of an assignment expression must be a variable or a property access.": 2364, "Function implementation is missing or not immediately following the declaration.": 2391, "Duplicate function implementation.": 2393, + "Export declaration conflicts with exported declaration of '{0}'.": 2484, "The target of an assignment must be a variable or a property access.": 2541, "Expected {0} type arguments, but got {1}.": 2558, diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 0dcb34df..db1a5710 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -11,10 +11,12 @@ export enum DiagnosticCategory { } export function diagnosticCategoryToString(category: DiagnosticCategory): string { - if (category == DiagnosticCategory.INFO) return "INFO"; - if (category == DiagnosticCategory.WARNING) return "WARNING"; - if (category == DiagnosticCategory.ERROR) return "ERROR"; - return ""; + switch (category) { + case DiagnosticCategory.INFO: return "INFO"; + case DiagnosticCategory.WARNING: return "WARNING"; + case DiagnosticCategory.ERROR: return "ERROR"; + default: return ""; + } } const colorBlue: string = "\u001b[93m"; @@ -23,10 +25,12 @@ const colorRed: string = "\u001b[91m"; const colorReset: string = "\u001b[0m"; export function diagnosticCategoryToColor(category: DiagnosticCategory): string { - if (category == DiagnosticCategory.INFO) return colorBlue; - if (category == DiagnosticCategory.WARNING) return colorYellow; - if (category == DiagnosticCategory.ERROR) return colorRed; - return ""; + switch (category) { + case DiagnosticCategory.INFO: return colorBlue; + case DiagnosticCategory.WARNING: return colorYellow; + case DiagnosticCategory.ERROR: return colorRed; + default: return ""; + } } export class DiagnosticMessage { diff --git a/src/evaluator.ts b/src/evaluator.ts new file mode 100644 index 00000000..7c890c8b --- /dev/null +++ b/src/evaluator.ts @@ -0,0 +1,68 @@ +// TODO: not yet decided whether we'll need this +// https://github.com/WebAssembly/binaryen/pull/1294 +// https://github.com/WebAssembly/binaryen/pull/1295 + +import { Type } from "./types"; +import { I64 } from "./util"; +import { + + NodeKind, + + Expression, + BinaryExpression, + LiteralExpression, + UnaryExpression, + UnaryPostfixExpression, + UnaryPrefixExpression + +} from "./ast"; + +export class Evaluator { + + success: bool = false; + type: Type; + integerValue: I64; + floatValue: f64; + stringValue: string; + + constructor(initialType: Type) { + this.type = initialType; + } + + evaluate(expression: Expression): this { + switch (expression.kind) { + + case NodeKind.BINARY: + this.evaluateBinary(expression); + break; + + case NodeKind.LITERAL: + this.evaluateLiteral(expression); + break; + + case NodeKind.UNARYPREFIX: + this.evaluateUnaryPrefix(expression); + break; + } + return this; + } + + private evaluateBinary(expression: BinaryExpression): this { + // TODO + return this; + } + + private evaluateLiteral(expression: LiteralExpression): this { + // TODO + return this; + } + + private evaluateUnaryPrefix(expression: UnaryPrefixExpression): this { + // TODO + return this; + } +} + +export function evaluate(expression: Expression, contextualType: Type): Evaluator { + return new Evaluator(contextualType).evaluate(expression); +} diff --git a/src/program.ts b/src/program.ts index 2c9ae165..ad5cf91d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -201,113 +201,98 @@ export class Program extends DiagnosticEmitter { private initializeClass(declaration: ClassDeclaration): void { const internalName: string = declaration.internalName; - const template: ClassTemplate = new ClassTemplate(this, internalName, declaration); - if (this.elements.has(internalName)) + if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - this.elements.set(internalName, template); + return; + } + const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration); + this.elements.set(internalName, prototype); if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); else - this.exports.set(internalName, template); + this.exports.set(internalName, prototype); } const memberDeclarations: DeclarationStatement[] = declaration.members; for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { const memberDeclaration: DeclarationStatement = memberDeclarations[j]; - switch (memberDeclaration.kind) { - - case NodeKind.FIELD: - this.initializeField(memberDeclaration, template); - break; - - case NodeKind.METHOD: - this.initializeMethod(memberDeclaration, template); - break; - - default: - throw new Error("unexpected class member"); - } + if (memberDeclaration.kind == NodeKind.FIELD) + this.initializeField(memberDeclaration, prototype); + else if (memberDeclaration.kind == NodeKind.METHOD) + this.initializeMethod(memberDeclaration, prototype); + else + throw new Error("unexpected class member"); } } - private initializeField(declaration: FieldDeclaration, template: ClassTemplate): void { + private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void { const name: string = declaration.identifier.name; - if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { - const internalName: string = declaration.internalName; - if (template.members.has(name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - const global: Global = new Global(this, internalName, declaration, null); - template.members.set(name, global); - } + if (classPrototype.members.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName); + return; + } + const internalName: string = declaration.internalName; + if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { // static fields become globals + const global: Global = new Global(this, internalName, declaration, null); + classPrototype.members.set(name, global); } else { - if (template.fieldDeclarations.has(name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); - else - template.fieldDeclarations.set(name, declaration); + const field: FieldPrototype = new FieldPrototype(classPrototype, internalName, declaration); + classPrototype.members.set(name, field); } } - private initializeMethod(declaration: MethodDeclaration, template: ClassTemplate): void { + private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void { let name: string = declaration.identifier.name; - if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { - const internalName: string = declaration.internalName; - if (template.members.has(name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - const func: FunctionTemplate = new FunctionTemplate(this, internalName, declaration); - let modifiers: Modifier[] | null; - if (modifiers = declaration.modifiers) { - for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) { - const modifier: Modifier = modifiers[i]; - if (modifier.modifierKind == ModifierKind.GET) { - name = GETTER_PREFIX + name; - break; - } else if (modifier.modifierKind == ModifierKind.SET) { - name = SETTER_PREFIX + name; - break; - } - } - } - template.members.set(name, func); - } - } else { - if (template.methodDeclarations.has(name)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); - else - template.methodDeclarations.set(name, declaration); + const internalName: string = declaration.internalName; + if (classPrototype.members.has(name)) { + this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + return; } + const func: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, hasModifier(ModifierKind.STATIC, declaration.modifiers) ? null : classPrototype); + let modifiers: Modifier[] | null; + if (modifiers = declaration.modifiers) { + for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) { + const modifier: Modifier = modifiers[i]; + if (modifier.modifierKind == ModifierKind.GET) { + name = GETTER_PREFIX + name; + break; + } else if (modifier.modifierKind == ModifierKind.SET) { + name = SETTER_PREFIX + name; + break; + } + } + } + classPrototype.members.set(name, func); } private initializeEnum(declaration: EnumDeclaration): void { const internalName: string = declaration.internalName; - const enm: Enum = new Enum(this, internalName, declaration); - if (this.elements.has(internalName)) + if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - this.elements.set(internalName, enm); - if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { - if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - this.exports.set(internalName, enm); - } - const members: EnumValueDeclaration[] = declaration.members; - for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) - this.initializeEnumValue(members[i], enm); + return; } + const enm: Enum = new Enum(this, internalName, declaration); + this.elements.set(internalName, enm); + if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { + if (this.exports.has(internalName)) + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); + else + this.exports.set(internalName, enm); + } + const values: EnumValueDeclaration[] = declaration.members; + for (let i: i32 = 0, k: i32 = values.length; i < k; ++i) + this.initializeEnumValue(values[i], enm); } private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void { const name: string = declaration.identifier.name; const internalName: string = declaration.internalName; - if (enm.members.has(name)) + if (enm.members.has(name)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - const value: EnumValue = new EnumValue(enm, this, internalName, declaration); - enm.members.set(name, value); + return; } + const value: EnumValue = new EnumValue(enm, this, internalName, declaration); + enm.members.set(name, value); } private initializeExports(statement: ExportStatement, queuedExports: Map): void { @@ -318,35 +303,35 @@ export class Program extends DiagnosticEmitter { private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map): void { const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name; - if (queuedExports.has(exportName)) - this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName); - else { - const queuedExport: QueuedExport = new QueuedExport(); - if (internalPath == null) { - queuedExport.isForeign = false; - queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name; - } else { - queuedExport.isForeign = true; - queuedExport.referencedName = (internalPath) + PATH_DELIMITER + member.identifier.name; - } - queuedExport.member = member; - queuedExports.set(exportName, queuedExport); + if (queuedExports.has(exportName)) { + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, exportName); + return; } + const queuedExport: QueuedExport = new QueuedExport(); + if (internalPath == null) { + queuedExport.isForeign = false; + queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name; + } else { + queuedExport.isForeign = true; + queuedExport.referencedName = (internalPath) + PATH_DELIMITER + member.identifier.name; + } + queuedExport.member = member; + queuedExports.set(exportName, queuedExport); } private initializeFunction(declaration: FunctionDeclaration): void { const internalName: string = declaration.internalName; - const template: FunctionTemplate = new FunctionTemplate(this, internalName, declaration); - if (this.elements.has(internalName)) + const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null); + if (this.elements.has(internalName)) { this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else { - this.elements.set(internalName, template); - if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { - if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); - else - this.exports.set(internalName, template); - } + return; + } + this.elements.set(internalName, prototype); + if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { + if (this.exports.has(internalName)) + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); + else + this.exports.set(internalName, prototype); } } @@ -386,16 +371,16 @@ export class Program extends DiagnosticEmitter { private initializeInterface(declaration: InterfaceDeclaration): void { const internalName: string = declaration.internalName; - const template: InterfaceTemplate = new InterfaceTemplate(this, internalName, declaration); + const interfacePrototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration); if (this.elements.has(internalName)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); else - this.elements.set(internalName, template); + this.elements.set(internalName, interfacePrototype); if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); else - this.exports.set(internalName, template); + this.exports.set(internalName, interfacePrototype); } const memberDeclarations: DeclarationStatement[] = declaration.members; for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { @@ -403,11 +388,11 @@ export class Program extends DiagnosticEmitter { switch (memberDeclaration.kind) { case NodeKind.FIELD: - this.initializeField(memberDeclaration, template); + this.initializeField(memberDeclaration, interfacePrototype); break; case NodeKind.METHOD: - this.initializeMethod(memberDeclaration, template); + this.initializeMethod(memberDeclaration, interfacePrototype); break; default: @@ -418,16 +403,16 @@ export class Program extends DiagnosticEmitter { private initializeNamespace(declaration: NamespaceDeclaration): void { const internalName: string = declaration.internalName; - const ns: Namespace = new Namespace(this, internalName, declaration); + const namespace: Namespace = new Namespace(this, internalName, declaration); if (this.elements.has(internalName)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); else { - this.elements.set(internalName, ns); + this.elements.set(internalName, namespace); if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (this.exports.has(internalName)) - this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); + this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName); else - this.exports.set(internalName, ns); + this.exports.set(internalName, namespace); } } const members: Statement[] = declaration.members; @@ -544,12 +529,12 @@ export class Program extends DiagnosticEmitter { return typeArguments; } - resolveElement(expression: Expression, contextualFunction: FunctionInstance): Element | null { + resolveElement(expression: Expression, contextualFunction: Function): Element | null { // this if (expression.kind == NodeKind.THIS) { if (contextualFunction.instanceMethodOf) - return contextualFunction.instanceMethodOf.template; + return contextualFunction.instanceMethodOf; this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); return null; } @@ -566,22 +551,31 @@ export class Program extends DiagnosticEmitter { const programGlobalElement: Element | null = this.elements.get(name); if (programGlobalElement) return programGlobalElement; + this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name); return null; // static or instance property (incl. enum values) or method } else if (expression.kind == NodeKind.PROPERTYACCESS) { - const target: Element | null = this.resolveElement((expression).expression, contextualFunction); + const target: Element | null = this.resolveElement((expression).expression, contextualFunction); // reports + let member: Element | null = null; if (!target) return null; - switch (target.kind) { - case ElementKind.CLASS: - case ElementKind.ENUM: - case ElementKind.NAMESPACE: - } - // TODO + const propertyName: string = (expression).property.name; + if (target.kind == ElementKind.ENUM) + member = (target).members.get(propertyName); + else if (target.kind == ElementKind.CLASS_PROTOTYPE) + member = (target).members.get(propertyName); + else if (target.kind == ElementKind.CLASS) + member = (target).members.get(propertyName); + else if (target.kind == ElementKind.NAMESPACE) + member = (target).members.get(propertyName); + if (member) + return member; + this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (expression).property.name, target.internalName); + return null; } - return null; + throw new Error("not implemented: " + expression.kind); } } @@ -603,25 +597,29 @@ function checkGlobalDecorator(decorators: DecoratorStatement[]): string | null { } export enum ElementKind { + CLASS_PROTOTYPE, CLASS, - CLASSINSTANCE, ENUM, ENUMVALUE, + FIELD_PROTOTYPE, + FIELD, + FUNCTION_PROTOTYPE, FUNCTION, - FUNCTIONINSTANCE, GLOBAL, + INTERFACE_PROTOTYPE, INTERFACE, - INTERFACEINSTANCE, LOCAL, NAMESPACE } +/** Base class of all program elements. */ export abstract class Element { kind: ElementKind; program: Program; internalName: string; globalExportName: string | null = null; + compiled: bool = false; constructor(program: Program, internalName: string) { this.program = program; @@ -629,59 +627,7 @@ export abstract class Element { } } -export class Enum extends Element { - - kind = ElementKind.ENUM; - declaration: EnumDeclaration | null; - members: Map = new Map(); - compiled: bool = false; - - constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) { - super(program, internalName); - this.declaration = declaration; - } - - get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; } - get isGlobalExport(): bool { return this.globalExportName != null; } - get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; } -} - -export class EnumValue extends Element { - - kind = ElementKind.ENUMVALUE; - declaration: EnumValueDeclaration | null; - enum: Enum; - hasConstantValue: bool = false; - constantValue: i32 = 0; - - constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) { - super(program, internalName); - this.enum = enm; - if (!(this.declaration = declaration)) this.hasConstantValue = true; - } -} - -export class Global extends Element { - - kind = ElementKind.GLOBAL; - declaration: VariableLikeDeclarationStatement | null; - type: Type | null; - hasConstantValue: bool = false; - constantIntegerValue: I64 = new I64(0, 0); - constantFloatValue: f64 = 0; - compiled: bool = false; - - constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) { - super(program, internalName); - if (!(this.declaration = declaration)) this.hasConstantValue = true; - this.type = type; // resolved later if `null` - } - - get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; } - get isGlobalExport(): bool { return this.globalExportName != null; } - get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; } -} - +/** A namespace. Also the base class of other namespace-like program elements. */ export class Namespace extends Element { kind = ElementKind.NAMESPACE; @@ -697,6 +643,61 @@ export class Namespace extends Element { get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't exports */ false; } } +/** An enum. */ +export class Enum extends Namespace { + + kind = ElementKind.ENUM; + declaration: EnumDeclaration | null; + members: Map = new Map(); // more specific + + constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) { + super(program, internalName, null); + this.declaration = declaration; + } + + get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; } + get isGlobalExport(): bool { return this.globalExportName != null; } + get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; } +} + +/** An enum value. */ +export class EnumValue extends Element { + + kind = ElementKind.ENUMVALUE; + declaration: EnumValueDeclaration | null; + enum: Enum; + hasConstantValue: bool; + constantValue: i32 = 0; + + constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) { + super(program, internalName); + this.enum = enm; + if (!(this.declaration = declaration)) this.hasConstantValue = true; + } +} + +/** A global variable. */ +export class Global extends Element { + + kind = ElementKind.GLOBAL; + declaration: VariableLikeDeclarationStatement | null; + type: Type | null; + hasConstantValue: bool = false; + constantIntegerValue: I64 | null = null; + constantFloatValue: f64 = 0; + + constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) { + super(program, internalName); + if (!(this.declaration = declaration)) this.hasConstantValue = true; + this.type = type; // resolved later if `null`, also updates constantKind + } + + get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; } + get isGlobalExport(): bool { return this.globalExportName != null; } + get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; } +} + +/** A function parameter. */ export class Parameter { name: string; @@ -708,6 +709,7 @@ export class Parameter { } } +/** A function local. */ export class Local extends Element { kind = ElementKind.LOCAL; @@ -721,28 +723,31 @@ export class Local extends Element { } } -export class FunctionTemplate extends Element { +/** A yet unresolved function prototype. */ +export class FunctionPrototype extends Element { - kind = ElementKind.FUNCTION; + kind = ElementKind.FUNCTION_PROTOTYPE; declaration: FunctionDeclaration | null; - instances: Map; + classPrototype: ClassPrototype | null; + instances: Map = new Map(); isGeneric: bool; - constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null) { + constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) { super(program, internalName); this.declaration = declaration; - this.instances = new Map(); + this.classPrototype = classPrototype; this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins set this } get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; } get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; } + get isInstance(): bool { return this.classPrototype != null; } get isGetter(): bool { return this.declaration ? hasModifier(ModifierKind.GET, this.declaration.modifiers) : /* internals aren't getters */ false; } get isSetter(): bool { return this.declaration ? hasModifier(ModifierKind.SET, this.declaration.modifiers) : /* internals aren't setters */ false; } - instantiate(typeArguments: Type[], contextualTypeArguments: Map | null): FunctionInstance | null { + resolve(typeArguments: Type[], contextualTypeArguments: Map | null): Function | null { const instanceKey: string = typesToString(typeArguments, "", ""); - let instance: FunctionInstance | null = this.instances.get(instanceKey); + let instance: Function | null = this.instances.get(instanceKey); if (instance) return instance; const declaration: FunctionDeclaration | null = this.declaration; @@ -793,31 +798,31 @@ export class FunctionTemplate extends Element { let internalName: string = this.internalName; if (instanceKey.length) internalName += "<" + instanceKey + ">"; - instance = new FunctionInstance(this, internalName, typeArguments, parameters, returnType, null); // TODO: class + instance = new Function(this, internalName, typeArguments, parameters, returnType, null); // TODO: class this.instances.set(instanceKey, instance); return instance; } } -export class FunctionInstance extends Element { +/** A resolved function. */ +export class Function extends Element { - kind = ElementKind.FUNCTIONINSTANCE; - template: FunctionTemplate; + kind = ElementKind.FUNCTION; + template: FunctionPrototype; typeArguments: Type[]; parameters: Parameter[]; returnType: Type; - instanceMethodOf: ClassInstance | null; + instanceMethodOf: Class | null; locals: Map = new Map(); - additionalLocals: Type[] = []; + additionalLocals: Type[] = []; // without parameters breakContext: string | null = null; - compiled: bool = false; contextualTypeArguments: Map = new Map(); private breakMajor: i32 = 0; private breakMinor: i32 = 0; - constructor(template: FunctionTemplate, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: ClassInstance | null) { + constructor(template: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) { super(template.program, internalName); this.template = template; this.typeArguments = typeArguments; @@ -866,20 +871,50 @@ export class FunctionInstance extends Element { } } -export class ClassTemplate extends Namespace { +/** A yet unresolved instance field prototype. */ +export class FieldPrototype extends Element { - kind = ElementKind.CLASS; + kind = ElementKind.FIELD_PROTOTYPE; + declaration: FieldDeclaration | null; + classPrototype: ClassPrototype; + + constructor(classPrototype: ClassPrototype, internalName: string, declaration: FieldDeclaration | null) { + super(classPrototype.program, internalName); + this.classPrototype = classPrototype; + } + + get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; } + get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; } +} + +/** A resolved instance field. */ +export class Field extends Element { + + kind = ElementKind.FIELD; + template: FieldPrototype; + type: Type; + hasConstantValue: bool = false; + constantIntegerValue: I64 | null = null; + constantFloatValue: f64 = 0; + + constructor(template: FieldPrototype, internalName: string, type: Type) { + super(template.program, internalName); + if (!this.template.declaration) this.hasConstantValue = true; + this.type = type; + } +} + +/** A yet unresolved class prototype. */ +export class ClassPrototype extends Namespace { + + kind = ElementKind.CLASS_PROTOTYPE; declaration: ClassDeclaration | null; - fieldDeclarations: Map; - methodDeclarations: Map; - instances: Map; + instances: Map; isGeneric: bool; constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) { super(program, internalName, null); this.declaration = declaration; - this.fieldDeclarations = new Map(); - this.methodDeclarations = new Map(); this.instances = new Map(); this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins can set this } @@ -887,9 +922,9 @@ export class ClassTemplate extends Namespace { get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; } get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; } - instantiate(typeArguments: Type[], contextualTypeArguments: Map | null): ClassInstance { + resolve(typeArguments: Type[], contextualTypeArguments: Map | null): Class { const key: string = typesToString(typeArguments, "", ""); - let instance: ClassInstance | null = this.instances.get(key); + let instance: Class | null = this.instances.get(key); if (instance) return instance; if (!this.declaration) @@ -898,19 +933,20 @@ export class ClassTemplate extends Namespace { } } -export class ClassInstance extends Element { +/** A resolved class. */ +export class Class extends Namespace { - kind = ElementKind.CLASSINSTANCE; - template: ClassTemplate; + kind = ElementKind.CLASS; + declaration: ClassDeclaration | null; + template: ClassPrototype; typeArguments: Type[]; - base: ClassInstance | null; + base: Class | null; type: Type; - compiled: bool = false; contextualTypeArguments: Map = new Map(); - constructor(template: ClassTemplate, internalName: string, typeArguments: Type[], base: ClassInstance | null) { - super(template.program, internalName); + constructor(template: ClassPrototype, internalName: string, typeArguments: Type[], base: Class | null) { + super(template.program, internalName, template.declaration); this.template = template; this.typeArguments = typeArguments; this.base = base; @@ -933,9 +969,10 @@ export class ClassInstance extends Element { } } -export class InterfaceTemplate extends ClassTemplate { +/** A yet unresvoled interface. */ +export class InterfacePrototype extends ClassPrototype { - kind = ElementKind.INTERFACE; + kind = ElementKind.INTERFACE_PROTOTYPE; declaration: InterfaceDeclaration | null; constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null) { @@ -943,12 +980,14 @@ export class InterfaceTemplate extends ClassTemplate { } } -export class InterfaceInstance extends ClassInstance { +/** A resolved interface. */ +export class Interface extends Class { - kind = ElementKind.INTERFACEINSTANCE; - base: InterfaceInstance | null; + kind = ElementKind.INTERFACE; + template: InterfacePrototype; + base: Interface | null; - constructor(template: InterfaceTemplate, internalName: string, typeArguments: Type[], base: InterfaceInstance | null) { + constructor(template: InterfacePrototype, internalName: string, typeArguments: Type[], base: Interface | null) { super(template, internalName, typeArguments, base); } } diff --git a/src/tsconfig.json b/src/tsconfig.json index c7126ccf..9ee738f0 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -25,6 +25,7 @@ "compiler.ts", "diagnosticMessages.generated.ts", "diagnostics.ts", + "evaluator.ts", "glue/js.d.ts", "glue/js.ts", "index.ts", diff --git a/src/types.ts b/src/types.ts index 40095f05..6af8591f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { ClassInstance } from "./program"; +import { Class } from "./program"; import { sb } from "./util"; export const enum TypeKind { @@ -29,7 +29,7 @@ export class Type { kind: TypeKind; size: i32; - classType: ClassInstance | null; + classType: Class | null; nullable: bool = false; nullableType: Type | null = null; // cached, of this type @@ -50,7 +50,7 @@ export class Type { get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; } get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; } - asClass(classType: ClassInstance): Type { + asClass(classType: Class): Type { const ret: Type = new Type(this.kind, this.size); ret.classType = classType; return ret; diff --git a/src/util.ts b/src/util.ts index e2be4b97..88c724dc 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,7 +1,4 @@ export { CharCode, isLineBreak} from "./util/charcode"; - export { I64, U64 } from "./util/i64"; - export { normalize as normalizePath, resolve as resolvePath, dirname } from "./util/path"; - export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb diff --git a/tests/compiler.ts b/tests/compiler.ts index 463153c9..e994e73f 100644 --- a/tests/compiler.ts +++ b/tests/compiler.ts @@ -30,6 +30,10 @@ const files: Map = new Map([ } return -1; } + import { sub } from "../other"; + export function what(): void { + sub(1,2); + } `], ["../other", @@ -54,6 +58,7 @@ do { const program = parser.finish(); const compiler = new Compiler(program); const module = compiler.compile(); +console.log(program.elements.keys()); // module.optimize(); module.validate(); diff --git a/tests/tokenizer.ts b/tests/tokenizer.ts index 32fa7f8a..ad147c9a 100644 --- a/tests/tokenizer.ts +++ b/tests/tokenizer.ts @@ -1,6 +1,6 @@ import "../src/glue/js"; import { Tokenizer, Token } from "../src/tokenizer"; -import { Source } from "../src/reflection"; +import { Source } from "../src/ast"; import * as fs from "fs"; const text = fs.readFileSync(__dirname + "/../src/tokenizer.ts").toString();