Fix treatment of trailing semicolons

This commit is contained in:
Alexander Luzgarev 2020-07-17 09:53:19 +02:00
parent b2c6f8cadd
commit 62713311cc
13 changed files with 175 additions and 30 deletions

View File

@ -37,12 +37,33 @@ namespace Parser.Tests
var actual = sut.Parse(); var actual = sut.Parse();
var statement = actual.Root.Body.Statements[0].AsNode(); var statement = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement); Assert.IsType<ExpressionStatementSyntaxNode>(statement);
if (statement is null) Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement!).Expression);
{ }
throw new System.Exception();
}
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement).Expression); [Fact]
public void ParseExpressionStatementWithSemicolon()
{
var text = "2 + 3;";
var sut = GetSut(text);
var actual = sut.Parse();
Assert.Single(actual.Root.Body.Statements);
var statement = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement);
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement!).Expression);
}
[Fact]
public void ParseExpressionStatementWithSemicolonAfterNewLine()
{
var text = "2 + 3\n;";
var sut = GetSut(text);
var actual = sut.Parse();
Assert.Equal(2, actual.Root.Body.Statements.Count);
var statement1 = actual.Root.Body.Statements[0].AsNode();
Assert.IsType<ExpressionStatementSyntaxNode>(statement1);
Assert.IsType<BinaryOperationExpressionSyntaxNode>(((ExpressionStatementSyntaxNode)statement1!).Expression);
var statement2 = actual.Root.Body.Statements[1].AsToken();
Assert.Equal(TokenKind.SemicolonToken, statement2.Kind);
} }
[Fact] [Fact]

View File

@ -258,7 +258,8 @@ namespace Parser.Binding
private BoundExpressionStatement BindExpressionStatement(ExpressionStatementSyntaxNode node) private BoundExpressionStatement BindExpressionStatement(ExpressionStatementSyntaxNode node)
{ {
var expression = BindExpression(node.Expression); var expression = BindExpression(node.Expression);
return ExpressionStatement(node, expression); var discardResult = node.Semicolon is not null;
return ExpressionStatement(node, expression, discardResult);
} }
private BoundExpression BindExpression(ExpressionSyntaxNode node) private BoundExpression BindExpression(ExpressionSyntaxNode node)

View File

@ -25,9 +25,12 @@ namespace Parser.Binding
return new BoundBlockStatement(syntax, statements); return new BoundBlockStatement(syntax, statements);
} }
public static BoundExpressionStatement ExpressionStatement(SyntaxNode syntax, BoundExpression expression) public static BoundExpressionStatement ExpressionStatement(
SyntaxNode syntax,
BoundExpression expression,
bool discardResult)
{ {
return new BoundExpressionStatement(syntax, expression); return new BoundExpressionStatement(syntax, expression, discardResult);
} }
public static BoundIfStatement IfStatement( public static BoundIfStatement IfStatement(

View File

@ -118,15 +118,18 @@ namespace Parser.Binding
public class BoundExpressionStatement : BoundStatement public class BoundExpressionStatement : BoundStatement
{ {
public BoundExpression Expression { get; } public BoundExpressionStatement(SyntaxNode syntax, BoundExpression expression, bool discardResult)
public override BoundNodeKind Kind => BoundNodeKind.ExpressionStatement;
public BoundExpressionStatement(SyntaxNode syntax, BoundExpression expression)
: base(syntax) : base(syntax)
{ {
Expression = expression; Expression = expression;
DiscardResult = discardResult;
} }
public BoundExpression Expression { get; }
public bool DiscardResult { get; }
public override BoundNodeKind Kind => BoundNodeKind.ExpressionStatement;
} }
public class BoundForStatement : BoundStatement public class BoundForStatement : BoundStatement

View File

@ -147,7 +147,7 @@ namespace Parser.Binding
return node; return node;
} }
return ExpressionStatement(node.Syntax, expression); return ExpressionStatement(node.Syntax, expression, node.DiscardResult);
} }
public virtual BoundStatement RewriteEmptyStatement(BoundEmptyStatement node) public virtual BoundStatement RewriteEmptyStatement(BoundEmptyStatement node)

View File

@ -210,7 +210,8 @@ namespace Parser
private MObject? EvaluateExpressionStatement(BoundExpressionStatement node) private MObject? EvaluateExpressionStatement(BoundExpressionStatement node)
{ {
return EvaluateExpression(node.Expression); var result = EvaluateExpression(node.Expression);
return node.DiscardResult ? null : result;
} }
private MObject? EvaluateExpression(BoundExpression node) private MObject? EvaluateExpression(BoundExpression node)

View File

@ -845,7 +845,28 @@ namespace Parser.Internal
throw new Exception("Expression statement cannot be empty."); throw new Exception("Expression statement cannot be empty.");
} }
return Factory.ExpressionStatementSyntax(expression); if (CurrentToken.Kind == TokenKind.SemicolonToken && !TriviaContainsNewLine(expression.TrailingTrivia))
{
var semicolon = EatToken();
return Factory.ExpressionStatementSyntax(
expression,
Factory.TrailingSemicolonSyntax(semicolon));
}
return Factory.ExpressionStatementSyntax(expression, semicolon: null);
}
private bool TriviaContainsNewLine(IReadOnlyList<SyntaxTrivia> trivia)
{
foreach(var t in trivia)
{
if (t.Text.Contains('\n'))
{
return true;
}
}
return false;
} }
private AttributeAssignmentSyntaxNode? ParseAttributeAssignment() private AttributeAssignmentSyntaxNode? ParseAttributeAssignment()
@ -1179,11 +1200,6 @@ namespace Parser.Internal
} }
} }
if (CurrentToken.Kind == TokenKind.OpenSquareBracketToken)
{
return ParseExpressionStatement();
}
if (CurrentToken.Kind == TokenKind.SemicolonToken) if (CurrentToken.Kind == TokenKind.SemicolonToken)
{ {
return Factory.EmptyStatementSyntax(EatToken()); return Factory.EmptyStatementSyntax(EatToken());

View File

@ -78,9 +78,14 @@ namespace Parser.Internal
return new TryCatchStatementSyntaxNode(tryKeyword, tryBody, catchClause, endKeyword); return new TryCatchStatementSyntaxNode(tryKeyword, tryBody, catchClause, endKeyword);
} }
public ExpressionStatementSyntaxNode ExpressionStatementSyntax(ExpressionSyntaxNode expression) public ExpressionStatementSyntaxNode ExpressionStatementSyntax(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon)
{ {
return new ExpressionStatementSyntaxNode(expression); return new ExpressionStatementSyntaxNode(expression, semicolon);
}
public TrailingSemicolonSyntaxNode TrailingSemicolonSyntax(SyntaxToken semicolon)
{
return new TrailingSemicolonSyntaxNode(semicolon);
} }
public EmptyStatementSyntaxNode EmptyStatementSyntax(SyntaxToken semicolon) public EmptyStatementSyntaxNode EmptyStatementSyntax(SyntaxToken semicolon)

View File

@ -779,18 +779,23 @@ namespace Parser.Internal
internal class ExpressionStatementSyntaxNode : StatementSyntaxNode internal class ExpressionStatementSyntaxNode : StatementSyntaxNode
{ {
internal readonly ExpressionSyntaxNode _expression; internal readonly ExpressionSyntaxNode _expression;
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression): base(TokenKind.ExpressionStatement) internal readonly TrailingSemicolonSyntaxNode? _semicolon;
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon): base(TokenKind.ExpressionStatement)
{ {
Slots = 1; Slots = 2;
this.AdjustWidth(expression); this.AdjustWidth(expression);
_expression = expression; _expression = expression;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
} }
internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TokenDiagnostic[] diagnostics): base(TokenKind.ExpressionStatement, diagnostics) internal ExpressionStatementSyntaxNode(ExpressionSyntaxNode expression, TrailingSemicolonSyntaxNode? semicolon, TokenDiagnostic[] diagnostics): base(TokenKind.ExpressionStatement, diagnostics)
{ {
Slots = 1; Slots = 2;
this.AdjustWidth(expression); this.AdjustWidth(expression);
_expression = expression; _expression = expression;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
} }
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position) internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position)
@ -800,14 +805,52 @@ namespace Parser.Internal
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics) public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{ {
return new ExpressionStatementSyntaxNode(_expression, diagnostics); return new ExpressionStatementSyntaxNode(_expression, _semicolon, diagnostics);
} }
public override GreenNode? GetSlot(int i) public override GreenNode? GetSlot(int i)
{ {
return i switch return i switch
{ {
0 => _expression, _ => null 0 => _expression, 1 => _semicolon, _ => null
}
;
}
}
internal class TrailingSemicolonSyntaxNode : SyntaxNode
{
internal readonly SyntaxToken _semicolon;
internal TrailingSemicolonSyntaxNode(SyntaxToken semicolon): base(TokenKind.TrailingSemicolon)
{
Slots = 1;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal TrailingSemicolonSyntaxNode(SyntaxToken semicolon, TokenDiagnostic[] diagnostics): base(TokenKind.TrailingSemicolon, diagnostics)
{
Slots = 1;
this.AdjustWidth(semicolon);
_semicolon = semicolon;
}
internal override Parser.SyntaxNode CreateRed(Parser.SyntaxNode parent, int position)
{
return new Parser.TrailingSemicolonSyntaxNode(parent, this, position);
}
public override GreenNode SetDiagnostics(TokenDiagnostic[] diagnostics)
{
return new TrailingSemicolonSyntaxNode(_semicolon, diagnostics);
}
public override GreenNode? GetSlot(int i)
{
return i switch
{
0 => _semicolon, _ => null
} }
; ;

View File

@ -88,6 +88,10 @@
</Class> </Class>
<Class Name="ExpressionStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ExpressionStatement"> <Class Name="ExpressionStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="ExpressionStatement">
<Field Type="ExpressionSyntaxNode" Name="expression" /> <Field Type="ExpressionSyntaxNode" Name="expression" />
<Field Type="TrailingSemicolonSyntaxNode" Name="semicolon" Nullable="true" />
</Class>
<Class Name="TrailingSemicolonSyntaxNode" BaseClass="SyntaxNode" Kind="TrailingSemicolon">
<Field Type="SyntaxToken" Name="semicolon" />
</Class> </Class>
<Class Name="EmptyStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="EmptyStatement"> <Class Name="EmptyStatementSyntaxNode" BaseClass="StatementSyntaxNode" Kind="EmptyStatement">
<Field Type="SyntaxToken" Name="semicolon" /> <Field Type="SyntaxToken" Name="semicolon" />

View File

@ -852,6 +852,7 @@ namespace Parser
public class ExpressionStatementSyntaxNode : StatementSyntaxNode public class ExpressionStatementSyntaxNode : StatementSyntaxNode
{ {
private SyntaxNode? _expression; private SyntaxNode? _expression;
private SyntaxNode? _semicolon;
internal ExpressionStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position) internal ExpressionStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)
{ {
} }
@ -865,11 +866,20 @@ namespace Parser
} }
} }
public TrailingSemicolonSyntaxNode? Semicolon
{
get
{
var red = this.GetRed(ref this._semicolon, 1);
return red is null ? default : (TrailingSemicolonSyntaxNode)red;
}
}
internal override SyntaxNode? GetNode(int i) internal override SyntaxNode? GetNode(int i)
{ {
return i switch return i switch
{ {
0 => GetRed(ref _expression!, 0), _ => null 0 => GetRed(ref _expression!, 0), 1 => GetRed(ref _semicolon, 1), _ => null
} }
; ;
@ -881,6 +891,36 @@ namespace Parser
} }
} }
public class TrailingSemicolonSyntaxNode : SyntaxNode
{
internal TrailingSemicolonSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)
{
}
public SyntaxToken Semicolon
{
get
{
return new SyntaxToken(this, ((Parser.Internal.TrailingSemicolonSyntaxNode)_green)._semicolon, this.GetChildPosition(0));
}
}
internal override SyntaxNode? GetNode(int i)
{
return i switch
{
_ => null
}
;
}
public override void Accept(SyntaxVisitor visitor)
{
visitor.VisitTrailingSemicolon(this);
}
}
public class EmptyStatementSyntaxNode : StatementSyntaxNode public class EmptyStatementSyntaxNode : StatementSyntaxNode
{ {
internal EmptyStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position) internal EmptyStatementSyntaxNode(SyntaxNode parent, Internal.GreenNode green, int position): base(parent, green, position)

View File

@ -83,6 +83,11 @@ namespace Parser
DefaultVisit(node); DefaultVisit(node);
} }
public virtual void VisitTrailingSemicolon(TrailingSemicolonSyntaxNode node)
{
DefaultVisit(node);
}
public virtual void VisitEmptyStatement(EmptyStatementSyntaxNode node) public virtual void VisitEmptyStatement(EmptyStatementSyntaxNode node)
{ {
DefaultVisit(node); DefaultVisit(node);

View File

@ -135,6 +135,9 @@
// a list of syntax nodes and/or tokens. // a list of syntax nodes and/or tokens.
List, List,
// a semicolon that marks expression statements with discarded results.
TrailingSemicolon,
// STATEMENTS // STATEMENTS
// The name ends with "Declaration" or "Statement". // The name ends with "Declaration" or "Statement".