Skip to content

Commit dabc61f

Browse files
authored
Support es2022 class properties (#205)
1 parent c8719da commit dabc61f

12 files changed

+1089
-603
lines changed

src/Esprima/Ast/Nodes.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ public enum Nodes
2929
MemberExpression,
3030
NewExpression,
3131
ObjectExpression,
32+
PrivateIdentifier,
3233
Program,
3334
Property,
35+
PropertyDefinition,
3436
RestElement,
3537
ReturnStatement,
3638
SequenceExpression,

src/Esprima/Ast/PrivateIdentifier.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Esprima.Utils;
2+
3+
namespace Esprima.Ast
4+
{
5+
public sealed class PrivateIdentifier : Expression
6+
{
7+
public readonly string? Name;
8+
9+
public PrivateIdentifier(string? name) : base(Nodes.PrivateIdentifier)
10+
{
11+
Name = name;
12+
}
13+
14+
public override NodeCollection ChildNodes => NodeCollection.Empty;
15+
16+
protected internal override void Accept(AstVisitor visitor)
17+
{
18+
visitor.VisitPrivateIdentifier(this);
19+
}
20+
}
21+
}

src/Esprima/Ast/PropertyDefinition.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Esprima.Utils;
2+
3+
namespace Esprima.Ast
4+
{
5+
public sealed class PropertyDefinition : ClassProperty
6+
{
7+
public readonly bool Static;
8+
9+
public PropertyDefinition(
10+
Expression key,
11+
bool computed,
12+
Expression value,
13+
bool isStatic)
14+
: base(Nodes.PropertyDefinition)
15+
{
16+
Static = isStatic;
17+
Key = key;
18+
Computed = computed;
19+
Value = value;
20+
}
21+
22+
protected internal override void Accept(AstVisitor visitor)
23+
{
24+
visitor.VisitPropertyDefinition(this);
25+
}
26+
}
27+
}

src/Esprima/Ast/PropertyKind.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public enum PropertyKind
1111
Set = 4,
1212
Init = 8,
1313
Constructor = 16,
14-
Method = 32
14+
Method = 32,
15+
Property = 64
1516
};
1617
}

src/Esprima/JavascriptParser.cs

Lines changed: 103 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,10 @@ private Expression ParsePrimaryExpression()
666666
{
667667
expr = ParseClassExpression();
668668
}
669+
else if (MatchKeyword("new"))
670+
{
671+
expr = ParseNewExpression();
672+
}
669673
else if (MatchImportCall())
670674
{
671675
expr = ParseImportCall();
@@ -834,7 +838,7 @@ private FunctionExpression ParsePropertyMethodAsyncFunction(bool isGenerator)
834838
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, isGenerator, hasStrictDirective, true));
835839
}
836840

837-
private Expression ParseObjectPropertyKey()
841+
private Expression ParseObjectPropertyKey(Boolean isPrivate = false)
838842
{
839843
var node = CreateNode();
840844
var token = NextToken();
@@ -866,7 +870,7 @@ private Expression ParseObjectPropertyKey()
866870
case TokenType.BooleanLiteral:
867871
case TokenType.NullLiteral:
868872
case TokenType.Keyword:
869-
key = Finalize(node, new Identifier((string?) token.Value));
873+
key = isPrivate ? Finalize(node, new PrivateIdentifier((string?) token.Value)) : Finalize(node, new Identifier((string?) token.Value));
870874
break;
871875

872876
case TokenType.Punctuator:
@@ -964,7 +968,7 @@ private Property ParseObjectProperty(Token hasProto)
964968
kind = PropertyKind.Init;
965969
computed = Match("[");
966970
key = ParseObjectPropertyKey();
967-
value = ParseGeneratorMethod();
971+
value = ParseGeneratorMethod(isAsync);
968972
method = true;
969973
}
970974
else
@@ -1420,6 +1424,29 @@ private Identifier ParseIdentifierName()
14201424
return Finalize(node, new Identifier((string?) token.Value));
14211425
}
14221426

1427+
private Expression ParseIdentifierOrPrivateIdentifierName()
1428+
{
1429+
var isPrivateField = false;
1430+
1431+
var node = CreateNode();
1432+
1433+
var token = NextToken();
1434+
1435+
if (Equals(token.Value, "#"))
1436+
{
1437+
token = NextToken();
1438+
token.Value = '#' + (string?)token.Value;
1439+
isPrivateField = true;
1440+
}
1441+
1442+
if (!IsIdentifierName(token))
1443+
{
1444+
return ThrowUnexpectedToken<Identifier>(token);
1445+
}
1446+
1447+
return isPrivateField ? Finalize(node, new PrivateIdentifier((string?) token.Value)) : Finalize(node, new Identifier((string?) token.Value));
1448+
}
1449+
14231450
private Expression ParseNewExpression()
14241451
{
14251452
var node = CreateNode();
@@ -1672,7 +1699,7 @@ private Expression ParseLeftHandSideExpressionAllowCall()
16721699
Expect(".");
16731700
}
16741701

1675-
var property = ParseIdentifierName();
1702+
var property = ParseIdentifierOrPrivateIdentifierName();
16761703
expr = Finalize(StartNode(startToken), new StaticMemberExpression(expr, property, optional));
16771704
}
16781705
else
@@ -4164,7 +4191,7 @@ private static bool QualifiedPropertyName(Token token)
41644191
TokenType.NullLiteral => true,
41654192
TokenType.NumericLiteral => true,
41664193
TokenType.Keyword => true,
4167-
TokenType.Punctuator => Equals(token.Value, "["),
4194+
TokenType.Punctuator => Equals(token.Value, "[") || Equals(token.Value, "#"),
41684195
_ => false
41694196
};
41704197
}
@@ -4212,7 +4239,7 @@ private FunctionExpression ParseSetterMethod()
42124239
return Finalize(node, new FunctionExpression(null, NodeList.From(ref formalParameters.Parameters), method, isGenerator, hasStrictDirective, false));
42134240
}
42144241

4215-
private FunctionExpression ParseGeneratorMethod()
4242+
private FunctionExpression ParseGeneratorMethod(bool isAsync = false)
42164243
{
42174244
var node = CreateNode();
42184245

@@ -4224,7 +4251,7 @@ private FunctionExpression ParseGeneratorMethod()
42244251
var method = ParsePropertyMethod(parameters, out var hasStrictDirective);
42254252
_context.AllowYield = previousAllowYield;
42264253

4227-
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, true, hasStrictDirective, false));
4254+
return Finalize(node, new FunctionExpression(null, NodeList.From(ref parameters.Parameters), method, true, hasStrictDirective, isAsync));
42284255
}
42294256

42304257
// https://tc39.github.io/ecma262/#sec-generator-function-definitions
@@ -4321,12 +4348,13 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
43214348

43224349
var kind = PropertyKind.None;
43234350
Expression? key = null;
4324-
FunctionExpression? value = null;
4351+
Expression? value = null;
43254352
var computed = false;
43264353
var method = false;
43274354
var isStatic = false;
43284355
var isAsync = false;
4329-
var isGenerator = false;
4356+
var isGenerator = false;
4357+
var isPrivate = false;
43304358

43314359
if (Match("*"))
43324360
{
@@ -4336,10 +4364,17 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
43364364
else
43374365
{
43384366
computed = Match("[");
4339-
key = ParseObjectPropertyKey();
4367+
if (Match("#"))
4368+
{
4369+
isPrivate = true;
4370+
NextToken();
4371+
token = _lookahead;
4372+
}
4373+
key = ParseObjectPropertyKey(isPrivate);
43404374
var id = key switch
43414375
{
43424376
Identifier identifier => identifier.Name,
4377+
PrivateIdentifier privateIdentifier => privateIdentifier.Name,
43434378
Literal literal => literal.StringValue, // "constructor"
43444379
_ => null
43454380
};
@@ -4352,9 +4387,21 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
43524387
if (Match("*"))
43534388
{
43544389
NextToken();
4390+
if (Match("#"))
4391+
{
4392+
isPrivate = true;
4393+
NextToken();
4394+
token = _lookahead;
4395+
}
43554396
}
43564397
else
43574398
{
4399+
if (Match("#"))
4400+
{
4401+
isPrivate = true;
4402+
NextToken();
4403+
token = _lookahead;
4404+
}
43584405
key = ParseObjectPropertyKey();
43594406
}
43604407
}
@@ -4368,11 +4415,17 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
43684415
if (isGenerator)
43694416
{
43704417
NextToken();
4418+
}
4419+
4420+
if (Match("#"))
4421+
{
4422+
isPrivate = true;
4423+
NextToken();
43714424
}
43724425

43734426
token = _lookahead;
43744427
computed = Match("[");
4375-
key = ParseObjectPropertyKey();
4428+
key = ParseObjectPropertyKey(isPrivate);
43764429
if (token.Type == TokenType.Identifier && (string?) token.Value == "constructor")
43774430
{
43784431
TolerateUnexpectedToken(token, Messages.ConstructorIsAsync);
@@ -4386,26 +4439,49 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
43864439
{
43874440
if (lookaheadPropertyKey && (string?) token.Value == "get")
43884441
{
4389-
kind = PropertyKind.Get;
4442+
kind = PropertyKind.Get;
4443+
if (Match("#"))
4444+
{
4445+
isPrivate = true;
4446+
NextToken();
4447+
token = _lookahead;
4448+
}
43904449
computed = Match("[");
4391-
key = ParseObjectPropertyKey();
4450+
key = ParseObjectPropertyKey(isPrivate);
43924451
_context.AllowYield = false;
43934452
value = ParseGetterMethod();
43944453
}
43954454
else if (lookaheadPropertyKey && (string?) token.Value == "set")
43964455
{
43974456
kind = PropertyKind.Set;
4457+
if (Match("#"))
4458+
{
4459+
isPrivate = true;
4460+
NextToken();
4461+
token = _lookahead;
4462+
}
43984463
computed = Match("[");
4399-
key = ParseObjectPropertyKey();
4400-
value = ParseSetterMethod();
4464+
key = ParseObjectPropertyKey(isPrivate);
4465+
value = ParseSetterMethod();
4466+
}
4467+
else if (!Match("("))
4468+
{
4469+
kind = PropertyKind.Property;
4470+
computed = false;
4471+
4472+
if (Match("="))
4473+
{
4474+
NextToken();
4475+
value = IsolateCoverGrammar(this.parseAssignmentExpression);
4476+
}
44014477
}
44024478
}
44034479
else if (token.Type == TokenType.Punctuator && (string?) token.Value == "*" && lookaheadPropertyKey)
44044480
{
44054481
kind = PropertyKind.Init;
44064482
computed = Match("[");
4407-
key = ParseObjectPropertyKey();
4408-
value = ParseGeneratorMethod();
4483+
key = ParseObjectPropertyKey(isPrivate);
4484+
value = ParseGeneratorMethod(isAsync);
44094485
method = true;
44104486
}
44114487

@@ -4441,7 +4517,7 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
44414517

44424518
if (!isStatic && IsPropertyKey(key!, "constructor"))
44434519
{
4444-
if (kind != PropertyKind.Method || !method || value!.Generator)
4520+
if (kind != PropertyKind.Method || !method || ((FunctionExpression)value!).Generator)
44454521
{
44464522
ThrowUnexpectedToken(token, Messages.ConstructorSpecialMethod);
44474523
}
@@ -4457,10 +4533,15 @@ private ClassProperty ParseClassElement(ref bool hasConstructor)
44574533

44584534
kind = PropertyKind.Constructor;
44594535
}
4460-
}
4461-
4462-
4463-
return Finalize(node, new MethodDefinition(key!, computed, value!, kind, isStatic));
4536+
}
4537+
4538+
if (kind == PropertyKind.Property)
4539+
{
4540+
ConsumeSemicolon();
4541+
return Finalize(node, new PropertyDefinition(key!, computed, value!, isStatic));
4542+
}
4543+
4544+
return Finalize(node, new MethodDefinition(key!, computed, (FunctionExpression)value!, kind, isStatic));
44644545
}
44654546

44664547
private ArrayList<ClassProperty> ParseClassElementList()

0 commit comments

Comments
 (0)