Skip to content

Commit 2d7de87

Browse files
committed
Add error code to ParseError
1 parent c01abb0 commit 2d7de87

15 files changed

+494
-289
lines changed

src/Acornima/Acornima.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676

7777
<PropertyGroup>
7878
<PolySharpIncludeGeneratedTypes Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1'">
79+
System.Runtime.CompilerServices.CallerArgumentExpressionAttribute;
7980
System.Runtime.CompilerServices.IsExternalInit;
8081
System.Runtime.CompilerServices.SkipLocalsInitAttribute
8182
</PolySharpIncludeGeneratedTypes>

src/Acornima/ParseError.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ namespace Acornima;
44

55
public abstract class ParseError
66
{
7-
internal delegate ParseError Factory(string description, int index, Position position, string? sourceFile);
7+
internal delegate ParseError Factory(string code, string description, int index, Position position, string? sourceFile);
8+
9+
public string Code { get; }
810

911
public string Description { get; }
1012

@@ -31,8 +33,9 @@ public abstract class ParseError
3133

3234
public string? SourceFile { get; }
3335

34-
public ParseError(string description, int index = -1, Position position = default, string? sourceFile = null)
36+
public ParseError(string code, string description, int index = -1, Position position = default, string? sourceFile = null)
3537
{
38+
Code = code ?? throw new ArgumentNullException(nameof(code));
3639
Description = description ?? throw new ArgumentNullException(nameof(description));
3740
Index = index;
3841
Position = position;

src/Acornima/ParseErrorException.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@ protected ParseErrorException(ParseError error, Exception? innerException = null
1414

1515
public string Description => Error.Description;
1616

17-
/// <summary>
18-
/// Zero-based index within the parsed code string. (Can be negative if location information is available.)
19-
/// </summary>
20-
public int Index => Error.Index;
21-
2217
/// <summary>
2318
/// One-based line number. (Can be zero if location information is not available.)
2419
/// </summary>

src/Acornima/Parser.Expression.cs

Lines changed: 54 additions & 41 deletions
Large diffs are not rendered by default.

src/Acornima/Parser.Helpers.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
namespace Acornima;
1010

11+
using static SyntaxErrorMessages;
12+
1113
public partial class Parser
1214
{
1315
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -323,9 +325,14 @@ private static ReservedWordKind GetReservedWordKind(ReadOnlySpan<char> word, boo
323325

324326
private void HandleReservedWordError(Identifier id)
325327
{
326-
Raise(id.Start, (GetReservedWordKind(id.Name.AsSpan(), _strict, _options.EcmaVersion) & ReservedWordKind.Strict) != 0
327-
? SyntaxErrorMessages.UnexpectedStrictReserved
328-
: SyntaxErrorMessages.UnexpectedReserved);
328+
if ((GetReservedWordKind(id.Name.AsSpan(), _strict, _options.EcmaVersion) & ReservedWordKind.Strict) != 0)
329+
{
330+
Raise(id.Start, UnexpectedStrictReserved);
331+
}
332+
else
333+
{
334+
Raise(id.Start, UnexpectedReserved);
335+
}
329336
}
330337

331338
#if DEBUG

src/Acornima/Parser.LVal.cs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace Acornima;
1010

1111
using static Unsafe;
12+
using static SyntaxErrorMessages;
1213

1314
// https://github.com/acornjs/acorn/blob/8.11.3/acorn/src/lval.js
1415

@@ -33,7 +34,7 @@ public partial class Parser
3334
if (InAsync() && node.As<Identifier>().Name == "await")
3435
{
3536
// Raise(node.Start, "Can not use 'await' as identifier inside an async function"); // original acornjs error reporting
36-
Raise(node.Start, SyntaxErrorMessages.AwaitBindingIdentifier);
37+
Raise(node.Start, AwaitBindingIdentifier);
3738
}
3839
break;
3940

@@ -48,7 +49,7 @@ public partial class Parser
4849
// Original acornjs error reporting is different (just falls through to the default case)
4950
if (isBinding)
5051
{
51-
Raise(node.Start, SyntaxErrorMessages.InvalidPropertyBindingPattern);
52+
Raise(node.Start, InvalidPropertyBindingPattern);
5253
}
5354
break;
5455

@@ -74,7 +75,7 @@ public partial class Parser
7475

7576
if (property.Kind != PropertyKind.Init || property.Value is FunctionExpression)
7677
{
77-
Raise(property.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
78+
Raise(property.Start, InvalidDestructuringTarget);
7879
}
7980

8081
convertedNode = ToAssignable(property.Value, ref NullRef<DestructuringErrors>(), isBinding);
@@ -102,11 +103,11 @@ public partial class Parser
102103
// Raise(argument.Start, "Rest elements cannot have a default value"); // original acornjs error reporting
103104
if (isParam)
104105
{
105-
Raise(argument.Start, SyntaxErrorMessages.RestDefaultInitializer);
106+
Raise(argument.Start, RestDefaultInitializer);
106107
}
107108
else
108109
{
109-
Raise(node.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
110+
Raise(node.Start, InvalidDestructuringTarget);
110111
}
111112
}
112113

@@ -119,7 +120,7 @@ public partial class Parser
119120
if (assignmentExpression.Operator != Operator.Assignment)
120121
{
121122
// Raise(assignmentExpression.Left.End, "Only '=' operator can be used for specifying default value."); // original acornjs error reporting
122-
Raise(assignmentExpression.Left.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
123+
Raise(assignmentExpression.Left.Start, InvalidDestructuringTarget);
123124
}
124125

125126
convertedNode = ToAssignable(assignmentExpression.Left, ref NullRef<DestructuringErrors>(), isBinding, lhsKind: lhsKind);
@@ -173,7 +174,7 @@ private NodeList<Node> ToAssignableProperties(in NodeList<Node> properties, bool
173174
&& (restElement.Argument.Type is NodeType.ArrayPattern or NodeType.ObjectPattern))
174175
{
175176
// Raise(restElement.Argument.Start, "Unexpected token"); // original acornjs error reporting
176-
Raise(restElement.Argument.Start, SyntaxErrorMessages.InvalidRestAssignmentPattern);
177+
Raise(restElement.Argument.Start, InvalidRestAssignmentPattern);
177178
}
178179

179180
assignmentProperties[i] = prop;
@@ -240,7 +241,7 @@ private RestElement ParseRestBinding()
240241
if (_tokenizerOptions._ecmaVersion == EcmaVersion.ES6 && _tokenizer._type != TokenType.Name)
241242
{
242243
// Unexpected(); // original acornjs error reporting
243-
Raise(_tokenizer._start, SyntaxErrorMessages.InvalidDestructuringTarget);
244+
Raise(_tokenizer._start, InvalidDestructuringTarget);
244245
}
245246

246247
var argument = ParseBindingAtom();
@@ -320,7 +321,14 @@ private Node ParseBindingAtom()
320321
// Raise(_tokenizer._start, "Comma is not permitted after the rest element"); // original acornjs error reporting
321322

322323
// As opposed to the original acornjs implementation, we report the position of the rest argument.
323-
Raise(rest.Argument.Start, close == TokenType.ParenRight ? SyntaxErrorMessages.ParamAfterRest : SyntaxErrorMessages.ElementAfterRest);
324+
if (close == TokenType.ParenRight)
325+
{
326+
Raise(rest.Argument.Start, ParamAfterRest);
327+
}
328+
else
329+
{
330+
Raise(rest.Argument.Start, ElementAfterRest);
331+
}
324332
}
325333

326334
Expect(close);
@@ -438,7 +446,7 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
438446
// RaiseRecoverable(identifier.Start, $"{(isBind ? "Binding " : "Assigning to ")}{identifier.Name} in strict mode"); // original acornjs error reporting
439447
if (identifier.Name is "eval" or "arguments")
440448
{
441-
RaiseRecoverable(identifier.Start, SyntaxErrorMessages.StrictEvalArguments);
449+
RaiseRecoverable(identifier.Start, StrictEvalArguments);
442450
}
443451
else
444452
{
@@ -451,15 +459,15 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
451459
if (bindingType == BindingType.Lexical && identifier.Name == "let")
452460
{
453461
// RaiseRecoverable(identifier.Start, "let is disallowed as a lexically bound name"); // original acornjs error reporting
454-
Raise(identifier.Start, SyntaxErrorMessages.LetInLexicalBinding);
462+
Raise(identifier.Start, LetInLexicalBinding);
455463
}
456464

457465
if (checkClashes is not null)
458466
{
459467
if (checkClashes.Contains(identifier.Name))
460468
{
461469
// RaiseRecoverable(identifier.Start, "Argument name clash"); // original acornjs error reporting
462-
Raise(identifier.Start, SyntaxErrorMessages.ParamDupe);
470+
Raise(identifier.Start, ParamDupe);
463471
}
464472

465473
checkClashes.Add(identifier.Name);
@@ -481,7 +489,7 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
481489
if (isBind)
482490
{
483491
// RaiseRecoverable(expr.Start, "Binding member expression"); // original acornjs error reporting
484-
Raise(expr.Start, SyntaxErrorMessages.InvalidPropertyBindingPattern);
492+
Raise(expr.Start, InvalidPropertyBindingPattern);
485493
}
486494
break;
487495

@@ -490,7 +498,7 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
490498
if (isBind)
491499
{
492500
// RaiseRecoverable(parenthesizedExpression.Start, "Binding parenthesized expression"); // original acornjs error reporting
493-
Raise(parenthesizedExpression.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
501+
Raise(parenthesizedExpression.Start, InvalidDestructuringTarget);
494502
}
495503

496504
// NOTE: Original acornjs implementation does a recursive call here, but we can optimize that into a loop to keep the call stack shallow.
@@ -617,7 +625,7 @@ private void DeclareName(string name, BindingType bindingType, int pos)
617625
if (redeclared)
618626
{
619627
// RaiseRecoverable(pos, $"Identifier '{name}' has already been declared"); // original acornjs error reporting
620-
Raise(pos, string.Format(SyntaxErrorMessages.VarRedeclaration, name));
628+
Raise(pos, VarRedeclaration, new object[] { name });
621629
}
622630
}
623631

@@ -645,14 +653,28 @@ private void HandleLeftHandSideError(Node node, bool isBinding, LeftHandSideKind
645653
}
646654
else
647655
{
648-
Raise(node.Start, lhsKind switch
656+
switch (lhsKind)
649657
{
650-
LeftHandSideKind.Assignment => SyntaxErrorMessages.InvalidLhsInAssignment,
651-
LeftHandSideKind.PrefixUpdate => SyntaxErrorMessages.InvalidLhsInPrefixOp,
652-
LeftHandSideKind.PostfixUpdate => SyntaxErrorMessages.InvalidLhsInPostfixOp,
653-
LeftHandSideKind.ForInOf => SyntaxErrorMessages.InvalidLhsInFor,
654-
_ => SyntaxErrorMessages.InvalidDestructuringTarget,
655-
});
658+
case LeftHandSideKind.Assignment:
659+
Raise(node.Start, InvalidLhsInAssignment);
660+
break;
661+
662+
case LeftHandSideKind.PrefixUpdate:
663+
Raise(node.Start, InvalidLhsInPrefixOp);
664+
break;
665+
666+
case LeftHandSideKind.PostfixUpdate:
667+
Raise(node.Start, InvalidLhsInPostfixOp);
668+
break;
669+
670+
case LeftHandSideKind.ForInOf:
671+
Raise(node.Start, InvalidLhsInFor);
672+
break;
673+
674+
default:
675+
Raise(node.Start, InvalidDestructuringTarget);
676+
break;
677+
}
656678
}
657679
}
658680

0 commit comments

Comments
 (0)