From c6c36613e636df1329d4067286bbee8f74526ea2 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Thu, 14 Dec 2017 11:55:35 +0100 Subject: [PATCH] Accessor parsing; Cleanup --- src/ast.ts | 8 +++- src/builtins.ts | 19 ++++++-- src/compiler.ts | 39 +++++++++++----- src/decompiler.ts | 2 +- src/diagnosticMessages.generated.ts | 8 ++++ src/diagnosticMessages.json | 4 ++ src/diagnostics.ts | 16 +++++-- src/evaluator.ts | 68 ---------------------------- src/index.ts | 3 ++ src/module.ts | 2 +- src/parser.ts | 61 ++++++++++++++++++++----- src/program.ts | 2 +- src/tokenizer.ts | 3 +- src/types.ts | 3 +- src/util.ts | 4 -- src/util/sb.ts | 2 + std/assembly.d.ts | 2 +- std/assembly.json | 1 + std/portable.d.ts | 2 +- std/portable.js | 6 +-- tests/compiler/tlsf.ts | 2 - tests/compiler/tsconfig.json | 6 +++ tests/parser.js | 3 +- tests/parser/class.ts | 3 +- tests/parser/class.ts.fixture.ts | 3 +- tests/parser/namespace.ts | 2 +- tests/parser/namespace.ts.fixture.ts | 2 +- tests/parser/tsconfig.json | 6 +++ tests/parser/var.ts | 6 ++- tests/parser/var.ts.fixture.ts | 7 ++- 30 files changed, 171 insertions(+), 124 deletions(-) delete mode 100644 src/evaluator.ts delete mode 100644 src/util.ts create mode 100644 src/util/sb.ts create mode 100644 tests/compiler/tsconfig.json create mode 100644 tests/parser/tsconfig.json diff --git a/src/ast.ts b/src/ast.ts index e444e255..352e5dcf 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -68,16 +68,20 @@ import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER, PARENT_SUBST, STATIC_DELIMITER, INSTANCE_DELIMITER } from "./constants"; import { Token, Tokenizer, operatorTokenToString, Range } from "./tokenizer"; -import { CharCode, I64, normalizePath, resolvePath } from "./util"; +import { CharCode } from "./util/charcode"; +import { I64 } from "./util/i64"; +import { normalize as normalizePath, resolve as resolvePath } from "./util/path"; export { Range } from "./tokenizer"; +/** Base class of all AST nodes. */ export abstract class Node { kind: NodeKind; range: Range; parent: Node | null = null; + /** Serializes this node to its TypeScript representation. */ abstract serialize(sb: string[]): void; } @@ -1374,7 +1378,7 @@ export class FunctionDeclaration extends DeclarationStatement { sb.push(", "); this.parameters[i].serialize(sb); } - if (this.returnType) { + if (this.returnType && !hasModifier(ModifierKind.SET, this.modifiers)) { sb.push("): "); (this.returnType).serialize(sb); } else diff --git a/src/builtins.ts b/src/builtins.ts index 5c0f4c4b..d13fd244 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -1,4 +1,4 @@ -import { Compiler, Target, typeToNativeType, typeToNativeOne } from "./compiler"; +import { Compiler, Target, typeToNativeType, typeToNativeOne, typeToNativeZero } from "./compiler"; import { DiagnosticCode } from "./diagnostics"; import { Node, Expression } from "./ast"; import { Type } from "./types"; @@ -490,15 +490,26 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty case "assert": // assert(isTrue: bool) -> void compiler.currentType = Type.void; - if (!validateCall(compiler, typeArguments, 0, operands, 1, reportNode)) + if (typeArguments.length != 0) { + compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, "0", typeArguments.length.toString(10)); return module.createUnreachable(); + } + if (operands.length < 1) { + compiler.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, "1", operands.length.toString(10)); + return module.createUnreachable(); + } + if (operands.length > 2) { + compiler.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, "2", operands.length.toString(10)); + return module.createUnreachable(); + } arg0 = compiler.compileExpression(operands[0], Type.i32); // reports + arg1 = operands.length > 1 ? compiler.compileExpression(operands[1], usizeType) : typeToNativeZero(module, usizeType); // TODO: string type compiler.currentType = Type.void; return compiler.options.noAssert ? module.createNop() : module.createIf( module.createUnary(UnaryOp.EqzI32, arg0), - module.createUnreachable() + module.createUnreachable() // TODO: report message to embedder ); case "parseInt": // takes a pointer to the string @@ -541,7 +552,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty return 0; } -/** Validates a call to a built-in function. */ +/** Pre-validates a call to a built-in function. */ function validateCall(compiler: Compiler, typeArguments: Type[], expectedTypeArguments: i32, operands: Expression[], expectedOperands: i32, reportNode: Node): bool { if (typeArguments.length != expectedTypeArguments) { compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), typeArguments.length.toString(10)); diff --git a/src/compiler.ts b/src/compiler.ts index 73bcd025..4eadec75 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -31,7 +31,6 @@ import { EnumValue } from "./program"; -import { I64, U64, sb } from "./util"; import { Token } from "./tokenizer"; import { @@ -103,7 +102,10 @@ import { TypeKind, } from "./types"; +import { I64, U64 } from "./util/i64"; +import { sb } from "./util/sb"; +/** Compilation target. */ export enum Target { /** WebAssembly with 32-bit pointers. */ WASM32, @@ -111,6 +113,7 @@ export enum Target { WASM64 } +/** Compiler options. */ export class Options { /** WebAssembly target. Defaults to {@link Target.WASM32}. */ target: Target = Target.WASM32; @@ -122,6 +125,7 @@ export class Options { noAssert: bool = false; } +/** Indicates the desired kind of a conversion. */ const enum ConversionKind { /** No conversion. */ NONE, @@ -131,6 +135,7 @@ const enum ConversionKind { EXPLICIT } +/** Compiler interface. */ export class Compiler extends DiagnosticEmitter { /** Program reference. */ @@ -323,8 +328,10 @@ export class Compiler extends DiagnosticEmitter { if (!type) { if (!declaration) throw new Error("unexpected missing declaration"); - if (!declaration.type) - return false; // TODO: infer type? currently reported by parser + if (!declaration.type) { // TODO: infer type + this.error(DiagnosticCode.Type_expected, declaration.identifier.range); + return false; + } type = this.program.resolveType(declaration.type); // reports if (!type) return false; @@ -336,6 +343,7 @@ export class Compiler extends DiagnosticEmitter { let initializer: ExpressionRef; let initializeInStart: bool = false; if (global.hasConstantValue) { + assert(type != null); if (type.isLongInteger) initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0); else if (type.kind == TypeKind.F32) @@ -513,17 +521,17 @@ export class Compiler extends DiagnosticEmitter { // create the function type let k: i32 = instance.parameters.length; - const binaryenResultType: NativeType = typeToNativeType(instance.returnType); - const binaryenParamTypes: NativeType[] = new Array(k); + const nativeResultType: NativeType = typeToNativeType(instance.returnType); + const nativeParamTypes: NativeType[] = new Array(k); const signatureNameParts: string[] = new Array(k + 1); for (let i: i32 = 0; i < k; ++i) { - binaryenParamTypes[i] = typeToNativeType(instance.parameters[i].type); + nativeParamTypes[i] = typeToNativeType(instance.parameters[i].type); signatureNameParts[i] = typeToSignatureNamePart(instance.parameters[i].type); } signatureNameParts[k] = typeToSignatureNamePart(instance.returnType); - let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes); + let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(nativeResultType, nativeParamTypes); if (!typeRef) - typeRef = this.module.addFunctionType(signatureNameParts.join(""), binaryenResultType, binaryenParamTypes); + typeRef = this.module.addFunctionType(signatureNameParts.join(""), nativeResultType, nativeParamTypes); // create the function const internalName: string = instance.internalName; @@ -673,6 +681,7 @@ export class Compiler extends DiagnosticEmitter { // memory + /** Adds a static memory segment with the specified data. */ addMemorySegment(buffer: Uint8Array): MemorySegment { if (this.memoryOffset.lo & 7) { // align to 8 bytes so any native data type is aligned here this.memoryOffset.or32(7); @@ -686,6 +695,7 @@ export class Compiler extends DiagnosticEmitter { // types + // TODO: try to get rid of this determineExpressionType(expression: Expression, contextualType: Type): Type { const previousType: Type = this.currentType; const previousNoEmit: bool = this.module.noEmit; @@ -945,6 +955,8 @@ export class Compiler extends DiagnosticEmitter { if (declaration.initializer) initializers.push(this.compileAssignment(declaration.identifier, declaration.initializer, Type.void)); } + } else { + this.error(DiagnosticCode.Type_expected, declaration.identifier.range); } } return initializers.length ? this.module.createBlock(null, initializers, NativeType.None) : this.module.createNop(); @@ -1160,11 +1172,14 @@ export class Compiler extends DiagnosticEmitter { expr = mod.createUnary(UnaryOp.ConvertI64_F32, expr); else expr = mod.createUnary(UnaryOp.ConvertU64_F32, expr); - } else + } else { + if (!fromType.isSmallInteger) + losesInformation = true; if (fromType.isSignedInteger) expr = mod.createUnary(UnaryOp.ConvertI32_F32, expr); else expr = mod.createUnary(UnaryOp.ConvertU32_F32, expr); + } // int to f64 } else { @@ -1205,8 +1220,8 @@ export class Compiler extends DiagnosticEmitter { else expr = mod.createUnary(UnaryOp.ExtendU32, expr); - // i32 to smaller/change of signage i32 - } else if (toType.isSmallInteger && (fromType.size > toType.size || (fromType.size == toType.size && fromType.kind != toType.kind))) { + // i32 or smaller to even smaller int + } else if (toType.isSmallInteger && fromType.size > toType.size) { losesInformation = true; if (toType.isSignedInteger) { expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); @@ -1813,7 +1828,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.createI64(intValue.lo, intValue.hi); if (contextualType.isSmallInteger) return this.module.createI32(intValue.toI32()); - this.currentType = Type.i32; + this.currentType = contextualType.isSignedInteger ? Type.i32 : Type.u32; return this.module.createI32(intValue.toI32()); } diff --git a/src/decompiler.ts b/src/decompiler.ts index 07f0d34a..5f654009 100644 --- a/src/decompiler.ts +++ b/src/decompiler.ts @@ -14,7 +14,7 @@ import { readString } from "./module"; -import { I64 } from "./util"; +import { I64 } from "./util/i64"; // TODO :-) diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 7b2bac02..6323826a 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -20,6 +20,9 @@ export enum DiagnosticCode { Statements_are_not_allowed_in_ambient_contexts = 1036, Initializers_are_not_allowed_in_ambient_contexts = 1039, _0_modifier_cannot_be_used_here = 1042, + A_set_accessor_must_have_exactly_one_parameter = 1049, + A_set_accessor_parameter_cannot_have_an_initializer = 1052, + A_get_accessor_cannot_have_parameters = 1054, Type_parameters_cannot_appear_on_a_constructor_declaration = 1092, Type_annotation_cannot_appear_on_a_constructor_declaration = 1093, An_accessor_cannot_have_type_parameters = 1094, @@ -42,6 +45,7 @@ export enum DiagnosticCode { String_literal_expected = 1141, Line_break_not_permitted_here = 1142, Declaration_expected = 1146, + _const_declarations_must_be_initialized = 1155, Unterminated_regular_expression_literal = 1161, Binary_digit_expected = 1177, Octal_digit_expected = 1178, @@ -95,6 +99,9 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 1036: return "Statements are not allowed in ambient contexts."; case 1039: return "Initializers are not allowed in ambient contexts."; case 1042: return "'{0}' modifier cannot be used here."; + case 1049: return "A 'set' accessor must have exactly one parameter."; + case 1052: return "A 'set' accessor parameter cannot have an initializer."; + case 1054: return "A 'get' accessor cannot have parameters."; case 1092: return "Type parameters cannot appear on a constructor declaration."; case 1093: return "Type annotation cannot appear on a constructor declaration."; case 1094: return "An accessor cannot have type parameters."; @@ -117,6 +124,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 1141: return "String literal expected."; case 1142: return "Line break not permitted here."; case 1146: return "Declaration expected."; + case 1155: return "'const' declarations must be initialized."; case 1161: return "Unterminated regular expression literal."; case 1177: return "Binary digit expected."; case 1178: return "Octal digit expected."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index bd73d801..dcdbb6b7 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -19,6 +19,9 @@ "Statements are not allowed in ambient contexts.": 1036, "Initializers are not allowed in ambient contexts.": 1039, "'{0}' modifier cannot be used here.": 1042, + "A 'set' accessor must have exactly one parameter.": 1049, + "A 'set' accessor parameter cannot have an initializer.": 1052, + "A 'get' accessor cannot have parameters.": 1054, "Type parameters cannot appear on a constructor declaration.": 1092, "Type annotation cannot appear on a constructor declaration.": 1093, "An accessor cannot have type parameters.": 1094, @@ -41,6 +44,7 @@ "String literal expected.": 1141, "Line break not permitted here.": 1142, "Declaration expected.": 1146, + "'const' declarations must be initialized.": 1155, "Unterminated regular expression literal.": 1161, "Binary digit expected.": 1177, "Octal digit expected.": 1178, diff --git a/src/diagnostics.ts b/src/diagnostics.ts index c1b6c494..b56b9250 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -1,6 +1,7 @@ import { Range } from "./ast"; -import { CharCode, isLineBreak, sb } from "./util"; import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated"; +import { CharCode, isLineBreak } from "./util/charcode"; +import { sb } from "./util/sb"; export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated"; @@ -71,6 +72,12 @@ export class DiagnosticMessage { this.range = range; return this; } + + toString(): string { + if (this.range) + return diagnosticCategoryToString(this.category) + " " + this.code + ": \"" + this.message + "\" in " + this.range.source.path + " @ " + this.range.start + "," + this.range.end; + return diagnosticCategoryToString(this.category) + " " + this.code + ": " + this.message; + } } export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: bool = false, showContext: bool = false): string { @@ -146,6 +153,7 @@ export function formatDiagnosticContext(range: Range, useColors: bool = false): export abstract class DiagnosticEmitter { diagnostics: DiagnosticMessage[]; + silentDiagnostics: bool = false; constructor(diagnostics: DiagnosticMessage[] | null = null) { this.diagnostics = diagnostics ? diagnostics : new Array(); @@ -154,8 +162,10 @@ export abstract class DiagnosticEmitter { emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) { const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range); this.diagnostics.push(message); - console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary - console.log(new Error("stack").stack); + if (!this.silentDiagnostics) { + console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary + console.log(new Error("stack").stack); + } } error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void { diff --git a/src/evaluator.ts b/src/evaluator.ts deleted file mode 100644 index 7c890c8b..00000000 --- a/src/evaluator.ts +++ /dev/null @@ -1,68 +0,0 @@ -// 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/index.ts b/src/index.ts index 0c8d793a..4ec1311e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,9 @@ compile(parser) -> module + [check diagnostics again] + [output module] + */ import { Module } from "./module"; diff --git a/src/module.ts b/src/module.ts index d28108da..512a95b9 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,5 +1,5 @@ -import { I64, U64 } from "./util"; import { Target } from "./compiler"; +import { I64, U64 } from "./util/i64"; export type ModuleRef = usize; export type FunctionTypeRef = usize; diff --git a/src/parser.ts b/src/parser.ts index 2bce10a9..0172623b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -10,7 +10,8 @@ import { Program } from "./program"; import { Tokenizer, Token, Range } from "./tokenizer"; import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics"; -import { normalizePath, I64 } from "./util"; +import { I64 } from "./util/i64"; +import { normalize as normalizePath } from "./util/path"; import { Node, @@ -386,10 +387,8 @@ export class Parser extends DiagnosticEmitter { const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range()); let type: TypeNode | null = null; - if (tn.skip(Token.COLON)) { + if (tn.skip(Token.COLON)) type = this.parseType(tn); - } else - this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable let initializer: Expression | null = null; if (tn.skip(Token.EQUALS)) { @@ -398,6 +397,11 @@ export class Parser extends DiagnosticEmitter { initializer = this.parseExpression(tn, Precedence.COMMA + 1); if (!initializer) return null; + } else { + if (hasModifier(ModifierKind.CONST, parentModifiers)) + this.error(DiagnosticCode._const_declarations_must_be_initialized, identifier.range); + else if (!type) // neither type nor initializer + this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable } return Statement.createVariableDeclaration(identifier, type, initializer, parentModifiers, parentDecorators, Range.join(identifier.range, tn.range())); } @@ -567,13 +571,28 @@ export class Parser extends DiagnosticEmitter { const parameters: Parameter[] | null = this.parseParameters(tn); if (!parameters) return null; + let isSetter: bool = hasModifier(ModifierKind.SET, modifiers); + if (isSetter) { + if (parameters.length != 1) + this.error(DiagnosticCode.A_set_accessor_must_have_exactly_one_parameter, identifier.range); // recoverable + if (parameters.length && parameters[0].initializer) + this.error(DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer, identifier.range); // recoverable + } + let isGetter: bool = hasModifier(ModifierKind.GET, modifiers); + if (isGetter && parameters.length) + this.error(DiagnosticCode.A_get_accessor_cannot_have_parameters, identifier.range); // recoverable let returnType: TypeNode | null = null; if (tn.skip(Token.COLON)) { - returnType = this.parseType(tn); + returnType = this.parseType(tn, isSetter); if (!returnType) return null; - } else - this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable + } else { + if (isSetter) { + if (parameters.length) + returnType = parameters[0].type; + } else + this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable + } const isDeclare: bool = hasModifier(ModifierKind.DECLARE, modifiers); let statements: Statement[] | null = null; if (tn.skip(Token.OPENBRACE)) { @@ -675,10 +694,15 @@ export class Parser extends DiagnosticEmitter { else if (tn.skip(Token.ABSTRACT)) modifiers = addModifier(Statement.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers); - if (tn.skip(Token.GET)) + let isGetter: bool = false; + let isSetter: bool = false; + if (tn.skip(Token.GET)) { modifiers = addModifier(Statement.createModifier(ModifierKind.GET, tn.range()), modifiers); - else if (tn.skip(Token.SET)) + isGetter = true; + } else if (tn.skip(Token.SET)) { // can't be both modifiers = addModifier(Statement.createModifier(ModifierKind.SET, tn.range()), modifiers); + isSetter = true; + } if (tn.skip(Token.IDENTIFIER)) { const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range()); @@ -695,13 +719,26 @@ export class Parser extends DiagnosticEmitter { let parameters = this.parseParameters(tn); if (!parameters) return null; + if (isGetter && parameters.length) + this.error(DiagnosticCode.A_get_accessor_cannot_have_parameters, identifier.range); + if (isSetter) { + if (parameters.length != 1) + this.error(DiagnosticCode.A_set_accessor_must_have_exactly_one_parameter, identifier.range); + if (parameters.length && parameters[0].initializer) + this.error(DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer, identifier.range); + } let returnType: TypeNode | null = null; if (tn.skip(Token.COLON)) { - returnType = this.parseType(tn); + returnType = this.parseType(tn, isSetter); if (!returnType) return null; - } else - this.error(DiagnosticCode.Type_expected, tn.range()); // recoverable + } else { + if (isSetter) { + if (parameters.length) + returnType = parameters[0].type; + } else + this.error(DiagnosticCode.Type_expected, tn.range()); // recoverable + } let statements: Statement[] | null = null; if (tn.skip(Token.OPENBRACE)) { if (parentIsDeclare) diff --git a/src/program.ts b/src/program.ts index d5379153..5f8e03bc 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3,7 +3,7 @@ import { Target, typeToNativeType } from "./compiler"; import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; import { Type, typesToString } from "./types"; -import { I64 } from "./util"; +import { I64 } from "./util/i64"; import { ModifierKind, diff --git a/src/tokenizer.ts b/src/tokenizer.ts index 9b5af916..697a1481 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -21,7 +21,8 @@ import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics"; import { Source } from "./ast"; -import { I64, CharCode, isLineBreak } from "./util"; +import { CharCode, isLineBreak } from "./util/charcode"; +import { I64 } from "./util/i64"; export enum Token { diff --git a/src/types.ts b/src/types.ts index 65c59517..fb367dc2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ import { Class } from "./program"; -import { sb } from "./util"; +import { sb } from "./util/sb"; export const enum TypeKind { @@ -103,6 +103,7 @@ export class Type { static readonly f32: Type = new Type(TypeKind.F32, 32); static readonly f64: Type = new Type(TypeKind.F64, 64); static readonly void: Type = new Type(TypeKind.VOID, 0); + static readonly infer: Type = Type.void; } export function typesToString(types: Type[], prefix: string = "<", postfix: string = ">"): string { diff --git a/src/util.ts b/src/util.ts deleted file mode 100644 index 88c724dc..00000000 --- a/src/util.ts +++ /dev/null @@ -1,4 +0,0 @@ -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/src/util/sb.ts b/src/util/sb.ts new file mode 100644 index 00000000..be4d44dd --- /dev/null +++ b/src/util/sb.ts @@ -0,0 +1,2 @@ +/** Shared string builder. */ +export const sb: string[] = []; diff --git a/std/assembly.d.ts b/std/assembly.d.ts index 73910164..1e4cd97e 100644 --- a/std/assembly.d.ts +++ b/std/assembly.d.ts @@ -89,7 +89,7 @@ declare function isNaN(value: T): bool; /** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */ declare function isFinite(value: T): bool; /** Traps if the specified value evaluates to `false`. */ -declare function assert(isTrue: bool): void; +declare function assert(isTrue: bool, message?: string): void; /** Parses an integer string to a 64-bit float. */ declare function parseInt(str: string, radix?: i32): f64; /** Parses a string to a 64-bit float. */ diff --git a/std/assembly.json b/std/assembly.json index 1472a2cc..fba0bf2c 100644 --- a/std/assembly.json +++ b/std/assembly.json @@ -2,6 +2,7 @@ "extends": "../tsconfig-base.json", "compilerOptions": { "target": "esnext", + "module": "commonjs", "noLib": true, "types": [], "rootDirs": [ diff --git a/std/portable.d.ts b/std/portable.d.ts index 95ec1941..bb576da0 100644 --- a/std/portable.d.ts +++ b/std/portable.d.ts @@ -51,7 +51,7 @@ declare function unreachable(): any; // sic /** Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa. */ declare function changetype(value: T1): T2; /** Traps if the specified value evaluates to `false`. */ -declare function assert(isTrue: bool): void; +declare function assert(isTrue: bool, message?: string): void; /** Parses an integer string to a 64-bit float. */ declare function parseInt(str: string, radix?: i32): f64; /** Parses a floating point string to a 64-bit float. */ diff --git a/std/portable.js b/std/portable.js index f8d23c01..f96536ad 100644 --- a/std/portable.js +++ b/std/portable.js @@ -19,14 +19,14 @@ UnreachableError.prototype.message = "unreachable"; globalScope["unreachable"] = function unreachable() { throw new UnreachableError(); }; -function AssertionError() { +function AssertionError(message) { + this.message = message || "assertion failed"; this.stack = new Error().stack; } AssertionError.prototype = new Error; AssertionError.prototype.name = "AssertionError"; -AssertionError.prototype.message = "assertion failed"; -globalScope["assert"] = function assert(isTrue) { if (!isTrue) throw new AssertionError(); }; +globalScope["assert"] = function assert(isTrue, message) { if (!isTrue) throw new AssertionError(message); }; globalScope["changetype"] = function changetype(value) { return value; } String["fromCharCodes"] = function fromCharCodes(arr) { return String.fromCharCode.apply(String, arr); } diff --git a/tests/compiler/tlsf.ts b/tests/compiler/tlsf.ts index 56e9a8d7..c959c3dd 100644 --- a/tests/compiler/tlsf.ts +++ b/tests/compiler/tlsf.ts @@ -2,8 +2,6 @@ // Useful as a compiler test in this state, but nothing more. // based upon: https://github.com/mattconte/tlsf/blob/master/tlsf.c (BSD) -/// - /** Finds the index of the least bit set. */ function fls(word: u32): i32 { return !word ? -1: 31 - clz(word); diff --git a/tests/compiler/tsconfig.json b/tests/compiler/tsconfig.json new file mode 100644 index 00000000..54138117 --- /dev/null +++ b/tests/compiler/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../std/assembly.json", + "include": [ + "./*.ts" + ] +} diff --git a/tests/parser.js b/tests/parser.js index 67b47d0e..c11eab4b 100644 --- a/tests/parser.js +++ b/tests/parser.js @@ -18,12 +18,13 @@ glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => { console.log(chalk.default.whiteBright("Testing parser/" + filename)); var parser = new Parser(); + parser.silentDiagnostics = true; var sourceText = fs.readFileSync(__dirname + "/parser/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, ""); parser.parseFile(sourceText, filename, true); var sb = []; parser.program.sources[0].serialize(sb); - var actual = sb.join(""); + var actual = sb.join("") + parser.diagnostics.map(diagnostic => "// " + diagnostic + "\n").join("");; var fixture = filename + ".fixture.ts"; if (isCreate) { diff --git a/tests/parser/class.ts b/tests/parser/class.ts index 82159670..9f664979 100644 --- a/tests/parser/class.ts +++ b/tests/parser/class.ts @@ -4,8 +4,9 @@ export class Test { static staticFunction(): void { } get instanceGetter(): i32 { + return 0; } - static set staticSetter(v: i32): i32 { + static set staticSetter(v: i32) { } instanceField: i32; static staticField: i32; diff --git a/tests/parser/class.ts.fixture.ts b/tests/parser/class.ts.fixture.ts index 53053378..79f44b89 100644 --- a/tests/parser/class.ts.fixture.ts +++ b/tests/parser/class.ts.fixture.ts @@ -4,8 +4,9 @@ instanceFunction(): void { static staticFunction(): void { } get instanceGetter(): i32 { +return 0; } -static set staticSetter(v: i32): i32 { +static set staticSetter(v: i32) { } instanceField: i32; static staticField: i32; diff --git a/tests/parser/namespace.ts b/tests/parser/namespace.ts index 0d0bf369..f5242b01 100644 --- a/tests/parser/namespace.ts +++ b/tests/parser/namespace.ts @@ -2,7 +2,7 @@ declare namespace A { namespace B { export namespace C { let aVar: i32; - const aConst: i32; + const aConst: i32 = 0; function aFunc(): void {} enum AnEnum {} class AClass {} diff --git a/tests/parser/namespace.ts.fixture.ts b/tests/parser/namespace.ts.fixture.ts index b3c6fe3a..2855800a 100644 --- a/tests/parser/namespace.ts.fixture.ts +++ b/tests/parser/namespace.ts.fixture.ts @@ -2,7 +2,7 @@ declare namespace A { namespace B { export namespace C { let aVar: i32; -const aConst: i32; +const aConst: i32 = 0; function aFunc(): void { } enum AnEnum { diff --git a/tests/parser/tsconfig.json b/tests/parser/tsconfig.json new file mode 100644 index 00000000..54138117 --- /dev/null +++ b/tests/parser/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../std/assembly.json", + "include": [ + "./*.ts" + ] +} diff --git a/tests/parser/var.ts b/tests/parser/var.ts index 1b93002d..5cfb2d6a 100644 --- a/tests/parser/var.ts +++ b/tests/parser/var.ts @@ -1,3 +1,7 @@ var a: i32; let b: i32; -const c: i32; +const c: i32 = 0; +let d = 2; + +let e; // type expected +const f: i32; // must be initialized diff --git a/tests/parser/var.ts.fixture.ts b/tests/parser/var.ts.fixture.ts index 38af1b61..095fa3e2 100644 --- a/tests/parser/var.ts.fixture.ts +++ b/tests/parser/var.ts.fixture.ts @@ -1,3 +1,8 @@ let a: i32; let b: i32; -const c: i32; +const c: i32 = 0; +let d = 2; +let e; +const f: i32; +// ERROR 1110: "Type expected." in var.ts @ 59,59 +// ERROR 1155: "'const' declarations must be initialized." in var.ts @ 84,85