Modify grammar to support Private Fields proposal: (#260)
* Modify grammar to support Private Fields proposal: - Adding optional plugin `classPrivateProperties` - Adding PrivateName type identifier - Adding ClassPrivateProperty to ClassBody - Allow PrivateName in MemberExpression - Allow PrivateName as a reference - Adding tests * Remove unnecesary liberal parameter * Guarding for plugin dependecy for future versioning * update spec.md [skip ci] * move comment [skip ci] * remove unused param [skip ci] * Refactor PrivateName to contain Identifier in name property
This commit is contained in:
committed by
Henry Zhu
parent
6c4acecf00
commit
01da62283c
@@ -99,7 +99,6 @@ export default class ExpressionParser extends LValParser {
|
||||
parseMaybeAssign(noIn?: ?boolean, refShorthandDefaultPos?: ?Pos, afterLeftParse?: Function, refNeedsArrowPos?: ?Pos): N.Expression {
|
||||
const startPos = this.state.start;
|
||||
const startLoc = this.state.startLoc;
|
||||
|
||||
if (this.match(tt._yield) && this.state.inGenerator) {
|
||||
let left = this.parseYield();
|
||||
if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc);
|
||||
@@ -297,7 +296,7 @@ export default class ExpressionParser extends LValParser {
|
||||
} else if (this.eat(tt.dot)) {
|
||||
const node = this.startNodeAt(startPos, startLoc);
|
||||
node.object = base;
|
||||
node.property = this.parseIdentifier(true);
|
||||
node.property = this.hasPlugin("classPrivateProperties") ? this.parseMaybePrivateName() : this.parseIdentifier(true);
|
||||
node.computed = false;
|
||||
base = this.finishNode(node, "MemberExpression");
|
||||
} else if (this.eat(tt.bracketL)) {
|
||||
@@ -525,6 +524,13 @@ export default class ExpressionParser extends LValParser {
|
||||
this.takeDecorators(node);
|
||||
return this.parseClass(node, false);
|
||||
|
||||
case tt.hash:
|
||||
if (this.hasPlugin("classPrivateProperties")) {
|
||||
return this.parseMaybePrivateName();
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
case tt._new:
|
||||
return this.parseNew();
|
||||
|
||||
@@ -547,6 +553,18 @@ export default class ExpressionParser extends LValParser {
|
||||
}
|
||||
}
|
||||
|
||||
parseMaybePrivateName(): N.PrivateName | N.Identifier {
|
||||
const isPrivate = this.eat(tt.hash);
|
||||
|
||||
if (isPrivate) {
|
||||
const node = this.startNode();
|
||||
node.name = this.parseIdentifier(true);
|
||||
return this.finishNode(node, "PrivateName");
|
||||
} else {
|
||||
return this.parseIdentifier(true);
|
||||
}
|
||||
}
|
||||
|
||||
parseFunctionExpression(): N.FunctionExpression | N.MetaProperty {
|
||||
const node = this.startNode();
|
||||
const meta = this.parseIdentifier(true);
|
||||
|
||||
@@ -26,6 +26,7 @@ export default class LValParser extends NodeUtils {
|
||||
if (node) {
|
||||
switch (node.type) {
|
||||
case "Identifier":
|
||||
case "PrivateName":
|
||||
case "ObjectPattern":
|
||||
case "ArrayPattern":
|
||||
case "AssignmentPattern":
|
||||
@@ -227,6 +228,7 @@ export default class LValParser extends NodeUtils {
|
||||
checkClashes: ?{ [key: string]: boolean },
|
||||
contextDescription: string): void {
|
||||
switch (expr.type) {
|
||||
case "PrivateName":
|
||||
case "Identifier":
|
||||
this.checkReservedWord(expr.name, expr.start, false, true);
|
||||
|
||||
|
||||
@@ -650,6 +650,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
// class bodies are implicitly strict
|
||||
const oldStrict = this.state.strict;
|
||||
this.state.strict = true;
|
||||
this.state.inClass = true;
|
||||
|
||||
let hadConstructor = false;
|
||||
let decorators = [];
|
||||
@@ -680,6 +681,13 @@ export default class StatementParser extends ExpressionParser {
|
||||
decorators = [];
|
||||
}
|
||||
|
||||
if (this.hasPlugin("classPrivateProperties") && this.match(tt.hash)) { // Private property
|
||||
this.next();
|
||||
this.parsePropertyName(method);
|
||||
classBody.body.push(this.parsePrivateClassProperty(method));
|
||||
continue;
|
||||
}
|
||||
|
||||
method.static = false;
|
||||
if (this.match(tt.name) && this.state.value === "static") {
|
||||
const key = this.parseIdentifier(true); // eats 'static'
|
||||
@@ -774,9 +782,26 @@ export default class StatementParser extends ExpressionParser {
|
||||
|
||||
node.body = this.finishNode(classBody, "ClassBody");
|
||||
|
||||
this.state.inClass = false;
|
||||
this.state.strict = oldStrict;
|
||||
|
||||
}
|
||||
|
||||
parsePrivateClassProperty(node: N.ClassPrivateProperty): N.ClassPrivateProperty {
|
||||
this.state.inClassProperty = true;
|
||||
|
||||
if (this.match(tt.eq)) {
|
||||
this.next();
|
||||
node.value = this.parseMaybeAssign();
|
||||
} else {
|
||||
node.value = null;
|
||||
}
|
||||
this.semicolon();
|
||||
this.state.inClassProperty = false;
|
||||
return this.finishNode(node, "ClassPrivateProperty");
|
||||
}
|
||||
|
||||
|
||||
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
|
||||
const hasPlugin = this.hasPlugin("classProperties");
|
||||
const noPluginMsg = "You can only use Class Properties when the 'classProperties' plugin is enabled.";
|
||||
|
||||
@@ -405,8 +405,17 @@ export default class Tokenizer extends LocationParser {
|
||||
|
||||
getTokenFromCode(code: number): void {
|
||||
switch (code) {
|
||||
|
||||
case 35: // '#'
|
||||
if (this.hasPlugin("classPrivateProperties") && this.state.inClass) {
|
||||
++this.state.pos; return this.finishToken(tt.hash);
|
||||
} else {
|
||||
this.raise(this.state.pos, `Unexpected character '${codePointToString(code)}'`);
|
||||
}
|
||||
|
||||
// The interpretation of a dot depends on whether it is followed
|
||||
// by a digit or another two dots.
|
||||
|
||||
case 46: // '.'
|
||||
return this.readToken_dot();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export default class State {
|
||||
this.inAsync =
|
||||
this.inPropertyName =
|
||||
this.inType =
|
||||
this.inClass =
|
||||
this.inClassProperty =
|
||||
this.noAnonFunctionType =
|
||||
false;
|
||||
@@ -81,6 +82,7 @@ export default class State {
|
||||
noAnonFunctionType: boolean;
|
||||
inPropertyName: boolean;
|
||||
inClassProperty: boolean;
|
||||
inClass: boolean;
|
||||
|
||||
// Labels in scope.
|
||||
labels: Array<{ kind: ?("loop" | "switch"), statementStart?: number }>;
|
||||
|
||||
@@ -108,6 +108,7 @@ export const types: { [name: string]: TokenType } = {
|
||||
backQuote: new TokenType("`", { startsExpr }),
|
||||
dollarBraceL: new TokenType("${", { beforeExpr, startsExpr }),
|
||||
at: new TokenType("@"),
|
||||
hash: new TokenType("#"),
|
||||
|
||||
// Operators. These carry several kinds of properties to help the
|
||||
// parser use them properly (the presence of these properties is
|
||||
|
||||
23
src/types.js
23
src/types.js
@@ -62,6 +62,13 @@ export type Identifier = PatternBase & {
|
||||
__clone(): Identifier;
|
||||
};
|
||||
|
||||
export type PrivateName = PatternBase & {
|
||||
type: "PrivateName";
|
||||
name: string;
|
||||
|
||||
__clone(): Identifier;
|
||||
};
|
||||
|
||||
// Literals
|
||||
|
||||
export type Literal = RegExpLiteral | NullLiteral | StringLiteral | BooleanLiteral | NumericLiteral;
|
||||
@@ -334,7 +341,7 @@ export type ObjectExpression = NodeBase & {
|
||||
properties: $ReadOnlyArray<ObjectProperty | ObjectMethod | SpreadElement>;
|
||||
};
|
||||
|
||||
export type ObjectOrClassMember = ClassMethod | ClassProperty | ObjectMember;
|
||||
export type ObjectOrClassMember = ClassMethod | ClassProperty | ClassPrivateProperty | ObjectMember;
|
||||
|
||||
export type ObjectMember = ObjectProperty | ObjectMethod;
|
||||
|
||||
@@ -552,7 +559,7 @@ export type ClassMemberBase = NodeBase & HasDecorators & {
|
||||
|
||||
export type Accessibility = "public" | "protected" | "private";
|
||||
|
||||
export type ClassMember = ClassMethod | ClassProperty;
|
||||
export type ClassMember = ClassMethod | ClassProperty | ClassPrivateProperty;
|
||||
|
||||
export type MethodLike = ObjectMethod | FunctionExpression | ClassMethod;
|
||||
|
||||
@@ -584,6 +591,18 @@ export type ClassProperty = ClassMemberBase & {
|
||||
readonly?: true;
|
||||
};
|
||||
|
||||
export type ClassPrivateProperty = ClassMemberBase & {
|
||||
type: "ClassPrivateProperty";
|
||||
key: Identifier;
|
||||
value: ?Expression; // TODO: Not in spec that this is nullable.
|
||||
|
||||
typeAnnotation?: ?FlowTypeAnnotation; // TODO: Not in spec
|
||||
variance?: ?FlowVariance; // TODO: Not in spec
|
||||
|
||||
// TypeScript only: (TODO: Not in spec)
|
||||
readonly?: true;
|
||||
};
|
||||
|
||||
export type OptClassDeclaration = ClassBase & DeclarationBase & HasDecorators & {
|
||||
type: "ClassDeclaration";
|
||||
// TypeScript only
|
||||
|
||||
Reference in New Issue
Block a user