More careful parsing of braces and brackets inside array element lists

This commit is contained in:
Alexander Luzgarev 2018-04-01 18:29:53 +02:00
parent 52a85be26c
commit 01dda0cf6f
4 changed files with 133 additions and 20 deletions

View File

@ -253,5 +253,102 @@ namespace Parser.Tests
Assert.AreEqual(f.InputDescription, null); Assert.AreEqual(f.InputDescription, null);
Assert.AreEqual(text, actual.FullText); Assert.AreEqual(text, actual.FullText);
} }
[Test]
public void ParseCellArrayWithCellArrayLiteralInside()
{
var text = "{1 2 a {3}}";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
var a = (CellArrayLiteralExpressionNode) actual;
Assert.AreEqual(4, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseCellArrayWithCellArrayAccessInside()
{
var text = "{1 2 a{3}}";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
var a = (CellArrayLiteralExpressionNode) actual;
Assert.AreEqual(3, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseCellArrayWithElementInBracketsInside()
{
var text = "{1 2 a (3)}";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
var a = (CellArrayLiteralExpressionNode) actual;
Assert.AreEqual(4, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseCellArrayWithFunctionCallInside()
{
var text = "{1 2 a(3)}";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<CellArrayLiteralExpressionNode>(actual);
var a = (CellArrayLiteralExpressionNode) actual;
Assert.AreEqual(3, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseArrayWithCellArrayLiteralInside()
{
var text = "[1 2 a {3}]";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
var a = (ArrayLiteralExpressionNode) actual;
Assert.AreEqual(4, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseArrayWithCellArrayAccessInside()
{
var text = "[1 2 a{3}]";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
var a = (ArrayLiteralExpressionNode) actual;
Assert.AreEqual(3, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseArrayWithElementInBracketsInside()
{
var text = "[1 2 a (3)]";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
var a = (ArrayLiteralExpressionNode) actual;
Assert.AreEqual(4, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
[Test]
public void ParseArrayWithFunctionCallInside()
{
var text = "[1 2 a(3)]";
var sut = CreateParser(text);
var actual = sut.ParseExpression();
Assert.IsInstanceOf<ArrayLiteralExpressionNode>(actual);
var a = (ArrayLiteralExpressionNode) actual;
Assert.AreEqual(3, a.Elements.Elements.Count);
Assert.AreEqual(text, actual.FullText);
}
} }
} }

View File

@ -282,28 +282,32 @@ namespace Parser
throw new ParsingException($"Unexpected token {CurrentToken.PureToken} at {CurrentToken.PureToken.Position}."); throw new ParsingException($"Unexpected token {CurrentToken.PureToken} at {CurrentToken.PureToken.Position}.");
} }
private ExpressionNode ParsePostfix(ExpressionNode expression) private ExpressionNode ParsePostfix(ParseOptions options, ExpressionNode expression)
{ {
while (true) while (true)
{ {
var token = CurrentToken; var token = CurrentToken;
switch(token.Kind) { switch(token.Kind) {
case TokenKind.OpeningBrace: // cell array element access case TokenKind.OpeningBrace: // cell array element access
if (expression.TrailingTrivia.Any()) if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
{ {
return expression; return expression;
} }
var openingBrace = EatToken(); var openingBrace = EatToken();
var index = ParseExpression(); var indices = ParseCellArrayElementList();
var closingBrace = EatToken(TokenKind.ClosingBrace); var closingBrace = EatToken(TokenKind.ClosingBrace);
expression = Factory.CellArrayElementAccessExpression( expression = Factory.CellArrayElementAccessExpression(
expression, expression,
Factory.Token(openingBrace), Factory.Token(openingBrace),
index, indices,
Factory.Token(closingBrace) Factory.Token(closingBrace)
); );
break; break;
case TokenKind.OpeningBracket: // function call case TokenKind.OpeningBracket: // function call
if (options.ParsingArrayElements && expression.TrailingTrivia.Any())
{
return expression;
}
var openingBracket = EatToken(); var openingBracket = EatToken();
var parameters = ParseFunctionCallParameterList(); var parameters = ParseFunctionCallParameterList();
var closingBracket = EatToken(TokenKind.ClosingBracket); var closingBracket = EatToken(TokenKind.ClosingBracket);
@ -355,7 +359,7 @@ namespace Parser
} }
} }
var expression = ParseExpression(); var expression = ParseExpression(new ParseOptions {ParsingArrayElements = true});
if (expression != null) if (expression != null)
{ {
nodes.Add(expression); nodes.Add(expression);
@ -381,7 +385,7 @@ namespace Parser
} }
} }
nodes.Add(ParseExpression()); nodes.Add(ParseExpression(new ParseOptions {ParsingArrayElements = true}));
} }
return Factory.ArrayElementList(nodes); return Factory.ArrayElementList(nodes);
@ -420,7 +424,7 @@ namespace Parser
closeParen); closeParen);
} }
private ExpressionNode ParseTerm() private ExpressionNode ParseTerm(ParseOptions options)
{ {
var token = CurrentToken; var token = CurrentToken;
ExpressionNode expression = null; ExpressionNode expression = null;
@ -460,12 +464,24 @@ namespace Parser
{ {
return null; return null;
} }
return ParsePostfix(expression); return ParsePostfix(options, expression);
}
internal struct ParseOptions
{
public bool ParsingArrayElements { get; set; }
public static ParseOptions Default = new ParseOptions { ParsingArrayElements = false };
} }
public ExpressionNode ParseExpression() public ExpressionNode ParseExpression()
{ {
return ParseSubExpression(Precedence.Expression); return ParseExpression(ParseOptions.Default);
}
private ExpressionNode ParseExpression(ParseOptions options)
{
return ParseSubExpression(options, Precedence.Expression);
} }
private bool IsUnaryOperator(TokenKind kind) private bool IsUnaryOperator(TokenKind kind)
@ -533,8 +549,8 @@ namespace Parser
throw new ArgumentException(nameof(kind)); throw new ArgumentException(nameof(kind));
} }
} }
private ExpressionNode ParseSubExpression(Precedence precedence) private ExpressionNode ParseSubExpression(ParseOptions options, Precedence precedence)
{ {
ExpressionNode lhs = null; ExpressionNode lhs = null;
if (IsUnaryOperator(CurrentToken.Kind)) if (IsUnaryOperator(CurrentToken.Kind))
@ -542,12 +558,12 @@ namespace Parser
var operation = EatToken(); var operation = EatToken();
var unaryTokenKind = ConvertToUnaryTokenKind(operation.Kind); var unaryTokenKind = ConvertToUnaryTokenKind(operation.Kind);
var newPrecedence = GetPrecedence(unaryTokenKind); var newPrecedence = GetPrecedence(unaryTokenKind);
var operand = ParseSubExpression(newPrecedence); var operand = ParseSubExpression(options, newPrecedence);
lhs = Factory.UnaryPrefixOperationExpression(Factory.Token(operation), operand); lhs = Factory.UnaryPrefixOperationExpression(Factory.Token(operation), operand);
} }
else else
{ {
lhs = ParseTerm(); lhs = ParseTerm(options);
} }
while (true) while (true)
{ {
@ -566,7 +582,7 @@ namespace Parser
} }
EatToken(); EatToken();
var rhs = ParseSubExpression(newPrecedence); var rhs = ParseSubExpression(options, newPrecedence);
if (rhs == null && token.Kind == TokenKind.Colon) // for parsing things like a{:} if (rhs == null && token.Kind == TokenKind.Colon) // for parsing things like a{:}
{ {
rhs = Factory.EmptyExpression(); rhs = Factory.EmptyExpression();

View File

@ -264,15 +264,15 @@ namespace Parser
public CellArrayElementAccessExpressionNode CellArrayElementAccessExpression( public CellArrayElementAccessExpressionNode CellArrayElementAccessExpression(
ExpressionNode cellArray, ExpressionNode cellArray,
TokenNode openingBrace, TokenNode openingBrace,
ExpressionNode index, ArrayElementListNode indices,
TokenNode closingBrace) TokenNode closingBrace)
{ {
var children = new List<SyntaxNode> {cellArray, openingBrace, index, closingBrace}; var children = new List<SyntaxNode> {cellArray, openingBrace, indices, closingBrace};
var result = new CellArrayElementAccessExpressionNode( var result = new CellArrayElementAccessExpressionNode(
children, children,
cellArray, cellArray,
openingBrace, openingBrace,
index, indices,
closingBrace); closingBrace);
SetParent(result); SetParent(result);
return result; return result;

View File

@ -340,19 +340,19 @@ namespace Parser
{ {
public ExpressionNode CellArray { get; } public ExpressionNode CellArray { get; }
public TokenNode OpeningBrace { get; } public TokenNode OpeningBrace { get; }
public ExpressionNode Index { get; } public ArrayElementListNode Indices { get; }
public TokenNode ClosingBrace { get; } public TokenNode ClosingBrace { get; }
public CellArrayElementAccessExpressionNode( public CellArrayElementAccessExpressionNode(
List<SyntaxNode> children, List<SyntaxNode> children,
ExpressionNode cellArray, ExpressionNode cellArray,
TokenNode openingBrace, TokenNode openingBrace,
ExpressionNode index, ArrayElementListNode indices,
TokenNode closingBrace) : base(children) TokenNode closingBrace) : base(children)
{ {
CellArray = cellArray; CellArray = cellArray;
OpeningBrace = openingBrace; OpeningBrace = openingBrace;
Index = index; Indices = indices;
ClosingBrace = closingBrace; ClosingBrace = closingBrace;
} }
} }