Support parameter properties; Minor formatting

This commit is contained in:
dcodeIO
2018-03-25 00:21:58 +01:00
parent c80bf35747
commit 38a025950e
17 changed files with 314 additions and 234 deletions

View File

@ -1047,6 +1047,8 @@ export class ParameterNode extends Node {
type: CommonTypeNode;
/** Initializer expression, if present. */
initializer: Expression | null;
/** Implicit field declaration, if applicable. */
implicitFieldDeclaration: FieldDeclaration | null = null;
}
/** Represents a function signature. */
@ -1583,6 +1585,9 @@ export class ExpressionStatement extends Statement {
/** Represents a field declaration within a `class`. */
export class FieldDeclaration extends VariableLikeDeclarationStatement {
kind = NodeKind.FIELDDECLARATION;
/** Parameter index within the constructor, if applicable. */
parameterIndex: i32 = -1;
}
/** Represents a `for` statement. */

View File

@ -108,7 +108,8 @@ import {
ArrayLiteralExpression,
StringLiteralExpression,
UnaryPostfixExpression,
UnaryPrefixExpression
UnaryPrefixExpression,
FieldDeclaration
} from "./ast";
import {
@ -5999,21 +6000,25 @@ export function makeAllocate(compiler: Compiler, classInstance: Class, reportNod
if (member.kind == ElementKind.FIELD) {
let field = <Field>member;
let fieldType = field.type;
let nativeFieldType = fieldType.toNativeType();
let fieldDeclaration = field.prototype.declaration;
assert(!field.isAny(CommonFlags.CONST));
if (fieldDeclaration.initializer) { // use initializer
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
compiler.compileExpression(fieldDeclaration.initializer, fieldType), // reports
fieldType.toNativeType(),
nativeFieldType,
field.memoryOffset
));
} else { // initialize with zero
// TODO: might be unnecessary if the ctor initializes the field
initializers.push(module.createStore(field.type.byteSize,
let parameterIndex = (<FieldDeclaration>field.prototype.declaration).parameterIndex;
initializers.push(module.createStore(fieldType.byteSize,
module.createGetLocal(tempLocal.index, nativeSizeType),
field.type.toNativeZero(module),
field.type.toNativeType(),
parameterIndex >= 0 // initialized via parameter
? module.createGetLocal(1 + parameterIndex, nativeFieldType)
: fieldType.toNativeZero(module),
nativeFieldType,
field.memoryOffset
));
}

View File

@ -72,6 +72,7 @@ export enum DiagnosticCode {
Decorators_are_not_valid_here = 1206,
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
A_class_may_only_extend_another_class = 1311,
A_parameter_property_cannot_be_declared_using_a_rest_parameter = 1317,
Duplicate_identifier_0 = 2300,
Cannot_find_name_0 = 2304,
Module_0_has_no_exported_member_1 = 2305,
@ -176,6 +177,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1206: return "Decorators are not valid here.";
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
case 1311: return "A class may only extend another class.";
case 1317: return "A parameter property cannot be declared using a rest parameter.";
case 2300: return "Duplicate identifier '{0}'.";
case 2304: return "Cannot find name '{0}'.";
case 2305: return "Module '{0}' has no exported member '{1}'.";

View File

@ -65,6 +65,7 @@
"Decorators are not valid here.": 1206,
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
"A class may only extend another class.": 1311,
"A parameter property cannot be declared using a rest parameter.": 1317,
"Duplicate identifier '{0}'.": 2300,
"Cannot find name '{0}'.": 2304,

View File

@ -799,8 +799,11 @@ export class ASTBuilder {
sb.push(" {\n");
let indentLevel = ++this.indentLevel;
for (let i = 0, k = members.length; i < k; ++i) {
indent(sb, indentLevel);
this.visitNodeAndTerminate(members[i]);
let member = members[i];
if (member.kind != NodeKind.FIELDDECLARATION || (<FieldDeclaration>member).parameterIndex < 0) {
indent(sb, indentLevel);
this.visitNodeAndTerminate(member);
}
}
indent(sb, --this.indentLevel);
sb.push("}");
@ -1368,6 +1371,10 @@ export class ASTBuilder {
serializeParameter(node: ParameterNode): void {
var sb = this.sb;
var kind = node.parameterKind;
var implicitFieldDeclaration = node.implicitFieldDeclaration;
if (implicitFieldDeclaration) {
this.serializeAccessModifiers(implicitFieldDeclaration);
}
if (kind == ParameterKind.REST) {
sb.push("...");
}

View File

@ -967,7 +967,8 @@ export class Parser extends DiagnosticEmitter {
}
parseParameters(
tn: Tokenizer
tn: Tokenizer,
isConstructor: bool = false
): ParameterNode[] | null {
// at '(': (Parameter (',' Parameter)*)? ')'
@ -979,7 +980,7 @@ export class Parser extends DiagnosticEmitter {
if (tn.peek() != Token.CLOSEPAREN) {
do {
let param = this.parseParameter(tn);
let param = this.parseParameter(tn, isConstructor);
if (!param) return null;
if (seenRest && !reportedRest) {
this.error(
@ -1022,17 +1023,63 @@ export class Parser extends DiagnosticEmitter {
parseParameter(
tn: Tokenizer,
suppressErrors: bool = false
isConstructor: bool = false
): ParameterNode | null {
// before: '...'? Identifier '?'? (':' Type)? ('=' Expression)?
// before: ('public' | 'private' | 'protected' | '...')? Identifier '?'? (':' Type)? ('=' Expression)?
var isRest = false;
var isOptional = false;
var startRange: Range | null = null;
if (tn.skip(Token.DOT_DOT_DOT)) {
isRest = true;
var accessFlags: CommonFlags = CommonFlags.NONE;
if (tn.skip(Token.PUBLIC)) {
startRange = tn.range();
if (!isConstructor) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
startRange, "public"
);
}
accessFlags |= CommonFlags.PUBLIC;
} else if (tn.skip(Token.PROTECTED)) {
startRange = tn.range();
if (!isConstructor) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
startRange, "protected"
);
}
accessFlags |= CommonFlags.PROTECTED;
} else if (tn.skip(Token.PRIVATE)) {
startRange = tn.range();
if (!isConstructor) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
startRange, "private"
);
}
accessFlags |= CommonFlags.PRIVATE;
}
if (tn.skip(Token.READONLY)) {
if (!startRange) startRange = tn.range();
if (!isConstructor) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
startRange, "readonly"
);
}
accessFlags |= CommonFlags.READONLY;
}
if (tn.skip(Token.DOT_DOT_DOT)) {
if (accessFlags) {
this.error(
DiagnosticCode.A_parameter_property_cannot_be_declared_using_a_rest_parameter,
tn.range()
);
} else {
startRange = tn.range();
}
isRest = true;
}
if (tn.skip(Token.IDENTIFIER)) {
if (!isRest) startRange = tn.range();
@ -1071,7 +1118,7 @@ export class Parser extends DiagnosticEmitter {
initializer = this.parseExpression(tn, Precedence.COMMA + 1);
if (!initializer) return null;
}
return Node.createParameter(
let param = Node.createParameter(
identifier,
type,
initializer,
@ -1082,6 +1129,8 @@ export class Parser extends DiagnosticEmitter {
: ParameterKind.DEFAULT,
Range.join(<Range>startRange, tn.range())
);
param.flags |= accessFlags;
return param;
} else {
this.error(
DiagnosticCode.Identifier_expected,
@ -1400,14 +1449,7 @@ export class Parser extends DiagnosticEmitter {
}
var members = new Array<DeclarationStatement>();
if (!tn.skip(Token.CLOSEBRACE)) {
do {
let member = this.parseClassMember(tn, flags);
if (!member) return null;
members.push(<DeclarationStatement>member);
} while (!tn.skip(Token.CLOSEBRACE));
}
return Node.createClassDeclaration(
var declaration = Node.createClassDeclaration(
identifier,
typeParameters,
extendsType,
@ -1417,11 +1459,20 @@ export class Parser extends DiagnosticEmitter {
flags,
tn.range(startPos, tn.pos)
);
if (!tn.skip(Token.CLOSEBRACE)) {
do {
let member = this.parseClassMember(tn, declaration);
if (!member) return null;
member.parent = declaration;
members.push(<DeclarationStatement>member);
} while (!tn.skip(Token.CLOSEBRACE));
}
return declaration;
}
parseClassMember(
tn: Tokenizer,
parentFlags: CommonFlags
parent: ClassDeclaration
): DeclarationStatement | null {
// before:
@ -1440,7 +1491,7 @@ export class Parser extends DiagnosticEmitter {
decorators.push(<DecoratorNode>decorator);
}
var flags = parentFlags & CommonFlags.AMBIENT; // inherit
var flags = parent.flags & CommonFlags.AMBIENT; // inherit
if (tn.skip(Token.PUBLIC)) {
flags |= CommonFlags.PUBLIC;
@ -1466,7 +1517,7 @@ export class Parser extends DiagnosticEmitter {
} else {
flags |= CommonFlags.INSTANCE;
}
if (parentFlags & CommonFlags.GENERIC) {
if (parent.flags & CommonFlags.GENERIC) {
flags |= CommonFlags.GENERIC_CONTEXT;
}
}
@ -1575,10 +1626,32 @@ export class Parser extends DiagnosticEmitter {
// method: '(' Parameters (':' Type)? '{' Statement* '}' ';'?
if (tn.skip(Token.OPENPAREN)) {
let signatureStart = tn.tokenPos;
let parameters = this.parseParameters(tn);
let parameters = this.parseParameters(tn, isConstructor);
if (!parameters) return null;
if (isGetter) {
if (isConstructor) {
for (let i = 0, k = parameters.length; i < k; ++i) {
let parameter = parameters[i];
if (parameter.isAny(
CommonFlags.PUBLIC |
CommonFlags.PROTECTED |
CommonFlags.PRIVATE |
CommonFlags.READONLY
)) {
let implicitFieldDeclaration = Node.createFieldDeclaration(
parameter.name,
parameter.type,
null, // initialized via parameter
null,
parameter.flags | CommonFlags.INSTANCE,
parameter.range
);
implicitFieldDeclaration.parameterIndex = i;
implicitFieldDeclaration.parent = parent;
parameter.implicitFieldDeclaration = implicitFieldDeclaration;
parent.members.push(implicitFieldDeclaration);
}
}
} else if (isGetter) {
if (parameters.length) {
this.error(
DiagnosticCode.A_get_accessor_cannot_have_parameters,