Accessor parsing; Cleanup

This commit is contained in:
dcodeIO 2017-12-14 11:55:35 +01:00
parent 99b0fdf7a8
commit c6c36613e6
30 changed files with 171 additions and 124 deletions

View File

@ -68,16 +68,20 @@
import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER, PARENT_SUBST, STATIC_DELIMITER, INSTANCE_DELIMITER } from "./constants"; import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER, PARENT_SUBST, STATIC_DELIMITER, INSTANCE_DELIMITER } from "./constants";
import { Token, Tokenizer, operatorTokenToString, Range } from "./tokenizer"; 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"; export { Range } from "./tokenizer";
/** Base class of all AST nodes. */
export abstract class Node { export abstract class Node {
kind: NodeKind; kind: NodeKind;
range: Range; range: Range;
parent: Node | null = null; parent: Node | null = null;
/** Serializes this node to its TypeScript representation. */
abstract serialize(sb: string[]): void; abstract serialize(sb: string[]): void;
} }
@ -1374,7 +1378,7 @@ export class FunctionDeclaration extends DeclarationStatement {
sb.push(", "); sb.push(", ");
this.parameters[i].serialize(sb); this.parameters[i].serialize(sb);
} }
if (this.returnType) { if (this.returnType && !hasModifier(ModifierKind.SET, this.modifiers)) {
sb.push("): "); sb.push("): ");
(<TypeNode>this.returnType).serialize(sb); (<TypeNode>this.returnType).serialize(sb);
} else } else

View File

@ -1,4 +1,4 @@
import { Compiler, Target, typeToNativeType, typeToNativeOne } from "./compiler"; import { Compiler, Target, typeToNativeType, typeToNativeOne, typeToNativeZero } from "./compiler";
import { DiagnosticCode } from "./diagnostics"; import { DiagnosticCode } from "./diagnostics";
import { Node, Expression } from "./ast"; import { Node, Expression } from "./ast";
import { Type } from "./types"; import { Type } from "./types";
@ -490,15 +490,26 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
case "assert": // assert(isTrue: bool) -> void case "assert": // assert(isTrue: bool) -> void
compiler.currentType = Type.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(); 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 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; compiler.currentType = Type.void;
return compiler.options.noAssert return compiler.options.noAssert
? module.createNop() ? module.createNop()
: module.createIf( : module.createIf(
module.createUnary(UnaryOp.EqzI32, arg0), module.createUnary(UnaryOp.EqzI32, arg0),
module.createUnreachable() module.createUnreachable() // TODO: report message to embedder
); );
case "parseInt": // takes a pointer to the string case "parseInt": // takes a pointer to the string
@ -541,7 +552,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty
return 0; 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 { function validateCall(compiler: Compiler, typeArguments: Type[], expectedTypeArguments: i32, operands: Expression[], expectedOperands: i32, reportNode: Node): bool {
if (typeArguments.length != expectedTypeArguments) { if (typeArguments.length != expectedTypeArguments) {
compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), typeArguments.length.toString(10)); compiler.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, reportNode.range, expectedTypeArguments.toString(10), typeArguments.length.toString(10));

View File

@ -31,7 +31,6 @@ import {
EnumValue EnumValue
} from "./program"; } from "./program";
import { I64, U64, sb } from "./util";
import { Token } from "./tokenizer"; import { Token } from "./tokenizer";
import { import {
@ -103,7 +102,10 @@ import {
TypeKind, TypeKind,
} from "./types"; } from "./types";
import { I64, U64 } from "./util/i64";
import { sb } from "./util/sb";
/** Compilation target. */
export enum Target { export enum Target {
/** WebAssembly with 32-bit pointers. */ /** WebAssembly with 32-bit pointers. */
WASM32, WASM32,
@ -111,6 +113,7 @@ export enum Target {
WASM64 WASM64
} }
/** Compiler options. */
export class Options { export class Options {
/** WebAssembly target. Defaults to {@link Target.WASM32}. */ /** WebAssembly target. Defaults to {@link Target.WASM32}. */
target: Target = Target.WASM32; target: Target = Target.WASM32;
@ -122,6 +125,7 @@ export class Options {
noAssert: bool = false; noAssert: bool = false;
} }
/** Indicates the desired kind of a conversion. */
const enum ConversionKind { const enum ConversionKind {
/** No conversion. */ /** No conversion. */
NONE, NONE,
@ -131,6 +135,7 @@ const enum ConversionKind {
EXPLICIT EXPLICIT
} }
/** Compiler interface. */
export class Compiler extends DiagnosticEmitter { export class Compiler extends DiagnosticEmitter {
/** Program reference. */ /** Program reference. */
@ -323,8 +328,10 @@ export class Compiler extends DiagnosticEmitter {
if (!type) { if (!type) {
if (!declaration) if (!declaration)
throw new Error("unexpected missing declaration"); throw new Error("unexpected missing declaration");
if (!declaration.type) if (!declaration.type) { // TODO: infer type
return false; // TODO: infer type? currently reported by parser this.error(DiagnosticCode.Type_expected, declaration.identifier.range);
return false;
}
type = this.program.resolveType(declaration.type); // reports type = this.program.resolveType(declaration.type); // reports
if (!type) if (!type)
return false; return false;
@ -336,6 +343,7 @@ export class Compiler extends DiagnosticEmitter {
let initializer: ExpressionRef; let initializer: ExpressionRef;
let initializeInStart: bool = false; let initializeInStart: bool = false;
if (global.hasConstantValue) { if (global.hasConstantValue) {
assert(type != null);
if (type.isLongInteger) if (type.isLongInteger)
initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0); initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0);
else if (type.kind == TypeKind.F32) else if (type.kind == TypeKind.F32)
@ -513,17 +521,17 @@ export class Compiler extends DiagnosticEmitter {
// create the function type // create the function type
let k: i32 = instance.parameters.length; let k: i32 = instance.parameters.length;
const binaryenResultType: NativeType = typeToNativeType(instance.returnType); const nativeResultType: NativeType = typeToNativeType(instance.returnType);
const binaryenParamTypes: NativeType[] = new Array(k); const nativeParamTypes: NativeType[] = new Array(k);
const signatureNameParts: string[] = new Array(k + 1); const signatureNameParts: string[] = new Array(k + 1);
for (let i: i32 = 0; i < k; ++i) { 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[i] = typeToSignatureNamePart(instance.parameters[i].type);
} }
signatureNameParts[k] = typeToSignatureNamePart(instance.returnType); signatureNameParts[k] = typeToSignatureNamePart(instance.returnType);
let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(binaryenResultType, binaryenParamTypes); let typeRef: FunctionTypeRef = this.module.getFunctionTypeBySignature(nativeResultType, nativeParamTypes);
if (!typeRef) if (!typeRef)
typeRef = this.module.addFunctionType(signatureNameParts.join(""), binaryenResultType, binaryenParamTypes); typeRef = this.module.addFunctionType(signatureNameParts.join(""), nativeResultType, nativeParamTypes);
// create the function // create the function
const internalName: string = instance.internalName; const internalName: string = instance.internalName;
@ -673,6 +681,7 @@ export class Compiler extends DiagnosticEmitter {
// memory // memory
/** Adds a static memory segment with the specified data. */
addMemorySegment(buffer: Uint8Array): MemorySegment { addMemorySegment(buffer: Uint8Array): MemorySegment {
if (this.memoryOffset.lo & 7) { // align to 8 bytes so any native data type is aligned here if (this.memoryOffset.lo & 7) { // align to 8 bytes so any native data type is aligned here
this.memoryOffset.or32(7); this.memoryOffset.or32(7);
@ -686,6 +695,7 @@ export class Compiler extends DiagnosticEmitter {
// types // types
// TODO: try to get rid of this
determineExpressionType(expression: Expression, contextualType: Type): Type { determineExpressionType(expression: Expression, contextualType: Type): Type {
const previousType: Type = this.currentType; const previousType: Type = this.currentType;
const previousNoEmit: bool = this.module.noEmit; const previousNoEmit: bool = this.module.noEmit;
@ -945,6 +955,8 @@ export class Compiler extends DiagnosticEmitter {
if (declaration.initializer) if (declaration.initializer)
initializers.push(this.compileAssignment(declaration.identifier, <Expression>declaration.initializer, Type.void)); initializers.push(this.compileAssignment(declaration.identifier, <Expression>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(); 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); expr = mod.createUnary(UnaryOp.ConvertI64_F32, expr);
else else
expr = mod.createUnary(UnaryOp.ConvertU64_F32, expr); expr = mod.createUnary(UnaryOp.ConvertU64_F32, expr);
} else } else {
if (!fromType.isSmallInteger)
losesInformation = true;
if (fromType.isSignedInteger) if (fromType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI32_F32, expr); expr = mod.createUnary(UnaryOp.ConvertI32_F32, expr);
else else
expr = mod.createUnary(UnaryOp.ConvertU32_F32, expr); expr = mod.createUnary(UnaryOp.ConvertU32_F32, expr);
}
// int to f64 // int to f64
} else { } else {
@ -1205,8 +1220,8 @@ export class Compiler extends DiagnosticEmitter {
else else
expr = mod.createUnary(UnaryOp.ExtendU32, expr); expr = mod.createUnary(UnaryOp.ExtendU32, expr);
// i32 to smaller/change of signage i32 // i32 or smaller to even smaller int
} else if (toType.isSmallInteger && (fromType.size > toType.size || (fromType.size == toType.size && fromType.kind != toType.kind))) { } else if (toType.isSmallInteger && fromType.size > toType.size) {
losesInformation = true; losesInformation = true;
if (toType.isSignedInteger) { if (toType.isSignedInteger) {
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift)); 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); return this.module.createI64(intValue.lo, intValue.hi);
if (contextualType.isSmallInteger) if (contextualType.isSmallInteger)
return this.module.createI32(intValue.toI32()); return this.module.createI32(intValue.toI32());
this.currentType = Type.i32; this.currentType = contextualType.isSignedInteger ? Type.i32 : Type.u32;
return this.module.createI32(intValue.toI32()); return this.module.createI32(intValue.toI32());
} }

View File

@ -14,7 +14,7 @@ import {
readString readString
} from "./module"; } from "./module";
import { I64 } from "./util"; import { I64 } from "./util/i64";
// TODO :-) // TODO :-)

View File

@ -20,6 +20,9 @@ export enum DiagnosticCode {
Statements_are_not_allowed_in_ambient_contexts = 1036, Statements_are_not_allowed_in_ambient_contexts = 1036,
Initializers_are_not_allowed_in_ambient_contexts = 1039, Initializers_are_not_allowed_in_ambient_contexts = 1039,
_0_modifier_cannot_be_used_here = 1042, _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_parameters_cannot_appear_on_a_constructor_declaration = 1092,
Type_annotation_cannot_appear_on_a_constructor_declaration = 1093, Type_annotation_cannot_appear_on_a_constructor_declaration = 1093,
An_accessor_cannot_have_type_parameters = 1094, An_accessor_cannot_have_type_parameters = 1094,
@ -42,6 +45,7 @@ export enum DiagnosticCode {
String_literal_expected = 1141, String_literal_expected = 1141,
Line_break_not_permitted_here = 1142, Line_break_not_permitted_here = 1142,
Declaration_expected = 1146, Declaration_expected = 1146,
_const_declarations_must_be_initialized = 1155,
Unterminated_regular_expression_literal = 1161, Unterminated_regular_expression_literal = 1161,
Binary_digit_expected = 1177, Binary_digit_expected = 1177,
Octal_digit_expected = 1178, 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 1036: return "Statements are not allowed in ambient contexts.";
case 1039: return "Initializers 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 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 1092: return "Type parameters cannot appear on a constructor declaration.";
case 1093: return "Type annotation 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."; 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 1141: return "String literal expected.";
case 1142: return "Line break not permitted here."; case 1142: return "Line break not permitted here.";
case 1146: return "Declaration expected."; case 1146: return "Declaration expected.";
case 1155: return "'const' declarations must be initialized.";
case 1161: return "Unterminated regular expression literal."; case 1161: return "Unterminated regular expression literal.";
case 1177: return "Binary digit expected."; case 1177: return "Binary digit expected.";
case 1178: return "Octal digit expected."; case 1178: return "Octal digit expected.";

View File

@ -19,6 +19,9 @@
"Statements are not allowed in ambient contexts.": 1036, "Statements are not allowed in ambient contexts.": 1036,
"Initializers are not allowed in ambient contexts.": 1039, "Initializers are not allowed in ambient contexts.": 1039,
"'{0}' modifier cannot be used here.": 1042, "'{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 parameters cannot appear on a constructor declaration.": 1092,
"Type annotation cannot appear on a constructor declaration.": 1093, "Type annotation cannot appear on a constructor declaration.": 1093,
"An accessor cannot have type parameters.": 1094, "An accessor cannot have type parameters.": 1094,
@ -41,6 +44,7 @@
"String literal expected.": 1141, "String literal expected.": 1141,
"Line break not permitted here.": 1142, "Line break not permitted here.": 1142,
"Declaration expected.": 1146, "Declaration expected.": 1146,
"'const' declarations must be initialized.": 1155,
"Unterminated regular expression literal.": 1161, "Unterminated regular expression literal.": 1161,
"Binary digit expected.": 1177, "Binary digit expected.": 1177,
"Octal digit expected.": 1178, "Octal digit expected.": 1178,

View File

@ -1,6 +1,7 @@
import { Range } from "./ast"; import { Range } from "./ast";
import { CharCode, isLineBreak, sb } from "./util";
import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated"; import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
import { CharCode, isLineBreak } from "./util/charcode";
import { sb } from "./util/sb";
export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated"; export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
@ -71,6 +72,12 @@ export class DiagnosticMessage {
this.range = range; this.range = range;
return this; 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 { 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 { export abstract class DiagnosticEmitter {
diagnostics: DiagnosticMessage[]; diagnostics: DiagnosticMessage[];
silentDiagnostics: bool = false;
constructor(diagnostics: DiagnosticMessage[] | null = null) { constructor(diagnostics: DiagnosticMessage[] | null = null) {
this.diagnostics = diagnostics ? <DiagnosticMessage[]>diagnostics : new Array(); this.diagnostics = diagnostics ? <DiagnosticMessage[]>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) { 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); const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
this.diagnostics.push(message); this.diagnostics.push(message);
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary if (!this.silentDiagnostics) {
console.log(<string>new Error("stack").stack); console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
console.log(<string>new Error("stack").stack);
}
} }
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void { error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {

View File

@ -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(<BinaryExpression>expression);
break;
case NodeKind.LITERAL:
this.evaluateLiteral(<LiteralExpression>expression);
break;
case NodeKind.UNARYPREFIX:
this.evaluateUnaryPrefix(<UnaryPrefixExpression>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);
}

View File

@ -17,6 +17,9 @@
compile(parser) -> module compile(parser) -> module
[check diagnostics again]
[output module]
*/ */
import { Module } from "./module"; import { Module } from "./module";

View File

@ -1,5 +1,5 @@
import { I64, U64 } from "./util";
import { Target } from "./compiler"; import { Target } from "./compiler";
import { I64, U64 } from "./util/i64";
export type ModuleRef = usize; export type ModuleRef = usize;
export type FunctionTypeRef = usize; export type FunctionTypeRef = usize;

View File

@ -10,7 +10,8 @@
import { Program } from "./program"; import { Program } from "./program";
import { Tokenizer, Token, Range } from "./tokenizer"; import { Tokenizer, Token, Range } from "./tokenizer";
import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics"; import { DiagnosticCode, DiagnosticEmitter } from "./diagnostics";
import { normalizePath, I64 } from "./util"; import { I64 } from "./util/i64";
import { normalize as normalizePath } from "./util/path";
import { import {
Node, Node,
@ -386,10 +387,8 @@ export class Parser extends DiagnosticEmitter {
const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range()); const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range());
let type: TypeNode | null = null; let type: TypeNode | null = null;
if (tn.skip(Token.COLON)) { if (tn.skip(Token.COLON))
type = this.parseType(tn); type = this.parseType(tn);
} else
this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable
let initializer: Expression | null = null; let initializer: Expression | null = null;
if (tn.skip(Token.EQUALS)) { if (tn.skip(Token.EQUALS)) {
@ -398,6 +397,11 @@ export class Parser extends DiagnosticEmitter {
initializer = this.parseExpression(tn, Precedence.COMMA + 1); initializer = this.parseExpression(tn, Precedence.COMMA + 1);
if (!initializer) if (!initializer)
return null; 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())); 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); const parameters: Parameter[] | null = this.parseParameters(tn);
if (!parameters) if (!parameters)
return null; 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; let returnType: TypeNode | null = null;
if (tn.skip(Token.COLON)) { if (tn.skip(Token.COLON)) {
returnType = this.parseType(tn); returnType = this.parseType(tn, isSetter);
if (!returnType) if (!returnType)
return null; return null;
} else } else {
this.error(DiagnosticCode.Type_expected, tn.range(tn.pos)); // recoverable 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); const isDeclare: bool = hasModifier(ModifierKind.DECLARE, modifiers);
let statements: Statement[] | null = null; let statements: Statement[] | null = null;
if (tn.skip(Token.OPENBRACE)) { if (tn.skip(Token.OPENBRACE)) {
@ -675,10 +694,15 @@ export class Parser extends DiagnosticEmitter {
else if (tn.skip(Token.ABSTRACT)) else if (tn.skip(Token.ABSTRACT))
modifiers = addModifier(Statement.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers); 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); 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); modifiers = addModifier(Statement.createModifier(ModifierKind.SET, tn.range()), modifiers);
isSetter = true;
}
if (tn.skip(Token.IDENTIFIER)) { if (tn.skip(Token.IDENTIFIER)) {
const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range()); const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range());
@ -695,13 +719,26 @@ export class Parser extends DiagnosticEmitter {
let parameters = this.parseParameters(tn); let parameters = this.parseParameters(tn);
if (!parameters) if (!parameters)
return null; 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; let returnType: TypeNode | null = null;
if (tn.skip(Token.COLON)) { if (tn.skip(Token.COLON)) {
returnType = this.parseType(tn); returnType = this.parseType(tn, isSetter);
if (!returnType) if (!returnType)
return null; return null;
} else } else {
this.error(DiagnosticCode.Type_expected, tn.range()); // recoverable if (isSetter) {
if (parameters.length)
returnType = parameters[0].type;
} else
this.error(DiagnosticCode.Type_expected, tn.range()); // recoverable
}
let statements: Statement[] | null = null; let statements: Statement[] | null = null;
if (tn.skip(Token.OPENBRACE)) { if (tn.skip(Token.OPENBRACE)) {
if (parentIsDeclare) if (parentIsDeclare)

View File

@ -3,7 +3,7 @@ import { Target, typeToNativeType } from "./compiler";
import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants"; import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants";
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
import { Type, typesToString } from "./types"; import { Type, typesToString } from "./types";
import { I64 } from "./util"; import { I64 } from "./util/i64";
import { import {
ModifierKind, ModifierKind,

View File

@ -21,7 +21,8 @@
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter, formatDiagnosticMessage } from "./diagnostics";
import { Source } from "./ast"; import { Source } from "./ast";
import { I64, CharCode, isLineBreak } from "./util"; import { CharCode, isLineBreak } from "./util/charcode";
import { I64 } from "./util/i64";
export enum Token { export enum Token {

View File

@ -1,5 +1,5 @@
import { Class } from "./program"; import { Class } from "./program";
import { sb } from "./util"; import { sb } from "./util/sb";
export const enum TypeKind { export const enum TypeKind {
@ -103,6 +103,7 @@ export class Type {
static readonly f32: Type = new Type(TypeKind.F32, 32); static readonly f32: Type = new Type(TypeKind.F32, 32);
static readonly f64: Type = new Type(TypeKind.F64, 64); static readonly f64: Type = new Type(TypeKind.F64, 64);
static readonly void: Type = new Type(TypeKind.VOID, 0); 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 { export function typesToString(types: Type[], prefix: string = "<", postfix: string = ">"): string {

View File

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

2
src/util/sb.ts Normal file
View File

@ -0,0 +1,2 @@
/** Shared string builder. */
export const sb: string[] = [];

2
std/assembly.d.ts vendored
View File

@ -89,7 +89,7 @@ declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */ /** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */
declare function isFinite<T = f32 | f64>(value: T): bool; declare function isFinite<T = f32 | f64>(value: T): bool;
/** Traps if the specified value evaluates to `false`. */ /** 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. */ /** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64; declare function parseInt(str: string, radix?: i32): f64;
/** Parses a string to a 64-bit float. */ /** Parses a string to a 64-bit float. */

View File

@ -2,6 +2,7 @@
"extends": "../tsconfig-base.json", "extends": "../tsconfig-base.json",
"compilerOptions": { "compilerOptions": {
"target": "esnext", "target": "esnext",
"module": "commonjs",
"noLib": true, "noLib": true,
"types": [], "types": [],
"rootDirs": [ "rootDirs": [

2
std/portable.d.ts vendored
View File

@ -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. */ /** Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa. */
declare function changetype<T1,T2>(value: T1): T2; declare function changetype<T1,T2>(value: T1): T2;
/** Traps if the specified value evaluates to `false`. */ /** 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. */ /** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64; declare function parseInt(str: string, radix?: i32): f64;
/** Parses a floating point string to a 64-bit float. */ /** Parses a floating point string to a 64-bit float. */

View File

@ -19,14 +19,14 @@ UnreachableError.prototype.message = "unreachable";
globalScope["unreachable"] = function unreachable() { throw new UnreachableError(); }; globalScope["unreachable"] = function unreachable() { throw new UnreachableError(); };
function AssertionError() { function AssertionError(message) {
this.message = message || "assertion failed";
this.stack = new Error().stack; this.stack = new Error().stack;
} }
AssertionError.prototype = new Error; AssertionError.prototype = new Error;
AssertionError.prototype.name = "AssertionError"; 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; } globalScope["changetype"] = function changetype(value) { return value; }
String["fromCharCodes"] = function fromCharCodes(arr) { return String.fromCharCode.apply(String, arr); } String["fromCharCodes"] = function fromCharCodes(arr) { return String.fromCharCode.apply(String, arr); }

View File

@ -2,8 +2,6 @@
// Useful as a compiler test in this state, but nothing more. // Useful as a compiler test in this state, but nothing more.
// based upon: https://github.com/mattconte/tlsf/blob/master/tlsf.c (BSD) // based upon: https://github.com/mattconte/tlsf/blob/master/tlsf.c (BSD)
/// <reference path="../../assembly.d.ts" />
/** Finds the index of the least bit set. */ /** Finds the index of the least bit set. */
function fls(word: u32): i32 { function fls(word: u32): i32 {
return !word ? -1: 31 - clz<i32>(word); return !word ? -1: 31 - clz<i32>(word);

View File

@ -0,0 +1,6 @@
{
"extends": "../../std/assembly.json",
"include": [
"./*.ts"
]
}

View File

@ -18,12 +18,13 @@ glob.sync(filter, { cwd: __dirname + "/parser" }).forEach(filename => {
console.log(chalk.default.whiteBright("Testing parser/" + filename)); console.log(chalk.default.whiteBright("Testing parser/" + filename));
var parser = new Parser(); 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, ""); var sourceText = fs.readFileSync(__dirname + "/parser/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, "");
parser.parseFile(sourceText, filename, true); parser.parseFile(sourceText, filename, true);
var sb = []; var sb = [];
parser.program.sources[0].serialize(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"; var fixture = filename + ".fixture.ts";
if (isCreate) { if (isCreate) {

View File

@ -4,8 +4,9 @@ export class Test<T> {
static staticFunction(): void { static staticFunction(): void {
} }
get instanceGetter(): i32 { get instanceGetter(): i32 {
return 0;
} }
static set staticSetter(v: i32): i32 { static set staticSetter(v: i32) {
} }
instanceField: i32; instanceField: i32;
static staticField: i32; static staticField: i32;

View File

@ -4,8 +4,9 @@ instanceFunction(): void {
static staticFunction(): void { static staticFunction(): void {
} }
get instanceGetter(): i32 { get instanceGetter(): i32 {
return 0;
} }
static set staticSetter(v: i32): i32 { static set staticSetter(v: i32) {
} }
instanceField: i32; instanceField: i32;
static staticField: i32; static staticField: i32;

View File

@ -2,7 +2,7 @@ declare namespace A {
namespace B { namespace B {
export namespace C { export namespace C {
let aVar: i32; let aVar: i32;
const aConst: i32; const aConst: i32 = 0;
function aFunc(): void {} function aFunc(): void {}
enum AnEnum {} enum AnEnum {}
class AClass {} class AClass {}

View File

@ -2,7 +2,7 @@ declare namespace A {
namespace B { namespace B {
export namespace C { export namespace C {
let aVar: i32; let aVar: i32;
const aConst: i32; const aConst: i32 = 0;
function aFunc(): void { function aFunc(): void {
} }
enum AnEnum { enum AnEnum {

View File

@ -0,0 +1,6 @@
{
"extends": "../../std/assembly.json",
"include": [
"./*.ts"
]
}

View File

@ -1,3 +1,7 @@
var a: i32; var a: i32;
let b: i32; let b: i32;
const c: i32; const c: i32 = 0;
let d = 2;
let e; // type expected
const f: i32; // must be initialized

View File

@ -1,3 +1,8 @@
let a: i32; let a: i32;
let b: 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