Skip to content

Commit

Permalink
Add error code to ParseError
Browse files Browse the repository at this point in the history
  • Loading branch information
adams85 committed Mar 31, 2024
1 parent c01abb0 commit 2d7de87
Show file tree
Hide file tree
Showing 15 changed files with 494 additions and 289 deletions.
1 change: 1 addition & 0 deletions src/Acornima/Acornima.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@

<PropertyGroup>
<PolySharpIncludeGeneratedTypes Condition="'$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1'">
System.Runtime.CompilerServices.CallerArgumentExpressionAttribute;
System.Runtime.CompilerServices.IsExternalInit;
System.Runtime.CompilerServices.SkipLocalsInitAttribute
</PolySharpIncludeGeneratedTypes>
Expand Down
7 changes: 5 additions & 2 deletions src/Acornima/ParseError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ namespace Acornima;

public abstract class ParseError
{
internal delegate ParseError Factory(string description, int index, Position position, string? sourceFile);
internal delegate ParseError Factory(string code, string description, int index, Position position, string? sourceFile);

public string Code { get; }

public string Description { get; }

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

public string? SourceFile { get; }

public ParseError(string description, int index = -1, Position position = default, string? sourceFile = null)
public ParseError(string code, string description, int index = -1, Position position = default, string? sourceFile = null)
{
Code = code ?? throw new ArgumentNullException(nameof(code));
Description = description ?? throw new ArgumentNullException(nameof(description));
Index = index;
Position = position;
Expand Down
5 changes: 0 additions & 5 deletions src/Acornima/ParseErrorException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ protected ParseErrorException(ParseError error, Exception? innerException = null

public string Description => Error.Description;

/// <summary>
/// Zero-based index within the parsed code string. (Can be negative if location information is available.)
/// </summary>
public int Index => Error.Index;

/// <summary>
/// One-based line number. (Can be zero if location information is not available.)
/// </summary>
Expand Down
95 changes: 54 additions & 41 deletions src/Acornima/Parser.Expression.cs

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions src/Acornima/Parser.Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace Acornima;

using static SyntaxErrorMessages;

public partial class Parser
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -323,9 +325,14 @@ private static ReservedWordKind GetReservedWordKind(ReadOnlySpan<char> word, boo

private void HandleReservedWordError(Identifier id)
{
Raise(id.Start, (GetReservedWordKind(id.Name.AsSpan(), _strict, _options.EcmaVersion) & ReservedWordKind.Strict) != 0
? SyntaxErrorMessages.UnexpectedStrictReserved
: SyntaxErrorMessages.UnexpectedReserved);
if ((GetReservedWordKind(id.Name.AsSpan(), _strict, _options.EcmaVersion) & ReservedWordKind.Strict) != 0)
{
Raise(id.Start, UnexpectedStrictReserved);
}
else
{
Raise(id.Start, UnexpectedReserved);
}
}

#if DEBUG
Expand Down
66 changes: 44 additions & 22 deletions src/Acornima/Parser.LVal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace Acornima;

using static Unsafe;
using static SyntaxErrorMessages;

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

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

Expand All @@ -48,7 +49,7 @@ public partial class Parser
// Original acornjs error reporting is different (just falls through to the default case)
if (isBinding)
{
Raise(node.Start, SyntaxErrorMessages.InvalidPropertyBindingPattern);
Raise(node.Start, InvalidPropertyBindingPattern);
}
break;

Expand All @@ -74,7 +75,7 @@ public partial class Parser

if (property.Kind != PropertyKind.Init || property.Value is FunctionExpression)
{
Raise(property.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
Raise(property.Start, InvalidDestructuringTarget);
}

convertedNode = ToAssignable(property.Value, ref NullRef<DestructuringErrors>(), isBinding);
Expand Down Expand Up @@ -102,11 +103,11 @@ public partial class Parser
// Raise(argument.Start, "Rest elements cannot have a default value"); // original acornjs error reporting
if (isParam)
{
Raise(argument.Start, SyntaxErrorMessages.RestDefaultInitializer);
Raise(argument.Start, RestDefaultInitializer);
}
else
{
Raise(node.Start, SyntaxErrorMessages.InvalidDestructuringTarget);
Raise(node.Start, InvalidDestructuringTarget);
}
}

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

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

assignmentProperties[i] = prop;
Expand Down Expand Up @@ -240,7 +241,7 @@ private RestElement ParseRestBinding()
if (_tokenizerOptions._ecmaVersion == EcmaVersion.ES6 && _tokenizer._type != TokenType.Name)
{
// Unexpected(); // original acornjs error reporting
Raise(_tokenizer._start, SyntaxErrorMessages.InvalidDestructuringTarget);
Raise(_tokenizer._start, InvalidDestructuringTarget);
}

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

// As opposed to the original acornjs implementation, we report the position of the rest argument.
Raise(rest.Argument.Start, close == TokenType.ParenRight ? SyntaxErrorMessages.ParamAfterRest : SyntaxErrorMessages.ElementAfterRest);
if (close == TokenType.ParenRight)
{
Raise(rest.Argument.Start, ParamAfterRest);
}
else
{
Raise(rest.Argument.Start, ElementAfterRest);
}
}

Expect(close);
Expand Down Expand Up @@ -438,7 +446,7 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
// RaiseRecoverable(identifier.Start, $"{(isBind ? "Binding " : "Assigning to ")}{identifier.Name} in strict mode"); // original acornjs error reporting
if (identifier.Name is "eval" or "arguments")
{
RaiseRecoverable(identifier.Start, SyntaxErrorMessages.StrictEvalArguments);
RaiseRecoverable(identifier.Start, StrictEvalArguments);
}
else
{
Expand All @@ -451,15 +459,15 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
if (bindingType == BindingType.Lexical && identifier.Name == "let")
{
// RaiseRecoverable(identifier.Start, "let is disallowed as a lexically bound name"); // original acornjs error reporting
Raise(identifier.Start, SyntaxErrorMessages.LetInLexicalBinding);
Raise(identifier.Start, LetInLexicalBinding);
}

if (checkClashes is not null)
{
if (checkClashes.Contains(identifier.Name))
{
// RaiseRecoverable(identifier.Start, "Argument name clash"); // original acornjs error reporting
Raise(identifier.Start, SyntaxErrorMessages.ParamDupe);
Raise(identifier.Start, ParamDupe);
}

checkClashes.Add(identifier.Name);
Expand All @@ -481,7 +489,7 @@ private void CheckLValSimple(Node expr, BindingType bindingType = BindingType.No
if (isBind)
{
// RaiseRecoverable(expr.Start, "Binding member expression"); // original acornjs error reporting
Raise(expr.Start, SyntaxErrorMessages.InvalidPropertyBindingPattern);
Raise(expr.Start, InvalidPropertyBindingPattern);
}
break;

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

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

Expand Down Expand Up @@ -645,14 +653,28 @@ private void HandleLeftHandSideError(Node node, bool isBinding, LeftHandSideKind
}
else
{
Raise(node.Start, lhsKind switch
switch (lhsKind)
{
LeftHandSideKind.Assignment => SyntaxErrorMessages.InvalidLhsInAssignment,
LeftHandSideKind.PrefixUpdate => SyntaxErrorMessages.InvalidLhsInPrefixOp,
LeftHandSideKind.PostfixUpdate => SyntaxErrorMessages.InvalidLhsInPostfixOp,
LeftHandSideKind.ForInOf => SyntaxErrorMessages.InvalidLhsInFor,
_ => SyntaxErrorMessages.InvalidDestructuringTarget,
});
case LeftHandSideKind.Assignment:
Raise(node.Start, InvalidLhsInAssignment);
break;

case LeftHandSideKind.PrefixUpdate:
Raise(node.Start, InvalidLhsInPrefixOp);
break;

case LeftHandSideKind.PostfixUpdate:
Raise(node.Start, InvalidLhsInPostfixOp);
break;

case LeftHandSideKind.ForInOf:
Raise(node.Start, InvalidLhsInFor);
break;

default:
Raise(node.Start, InvalidDestructuringTarget);
break;
}
}
}

Expand Down
Loading

0 comments on commit 2d7de87

Please sign in to comment.