From 01dda0cf6f0c347f13f7e8aab100b5b7bfccd025 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sun, 1 Apr 2018 18:29:53 +0200 Subject: [PATCH] More careful parsing of braces and brackets inside array element lists --- Parser.Tests/MParserShould.cs | 97 +++++++++++++++++++++++++++++++++++ Parser/MParser.cs | 44 +++++++++++----- Parser/SyntaxFactory.cs | 6 +-- Parser/SyntaxNode.cs | 6 +-- 4 files changed, 133 insertions(+), 20 deletions(-) diff --git a/Parser.Tests/MParserShould.cs b/Parser.Tests/MParserShould.cs index 75aa33b..ad5ef5d 100644 --- a/Parser.Tests/MParserShould.cs +++ b/Parser.Tests/MParserShould.cs @@ -253,5 +253,102 @@ namespace Parser.Tests Assert.AreEqual(f.InputDescription, null); 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(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(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(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(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(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(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(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(actual); + var a = (ArrayLiteralExpressionNode) actual; + Assert.AreEqual(3, a.Elements.Elements.Count); + Assert.AreEqual(text, actual.FullText); + } + } } \ No newline at end of file diff --git a/Parser/MParser.cs b/Parser/MParser.cs index 631726d..1891dc8 100644 --- a/Parser/MParser.cs +++ b/Parser/MParser.cs @@ -282,28 +282,32 @@ namespace Parser 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) { var token = CurrentToken; switch(token.Kind) { case TokenKind.OpeningBrace: // cell array element access - if (expression.TrailingTrivia.Any()) + if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) { return expression; } var openingBrace = EatToken(); - var index = ParseExpression(); + var indices = ParseCellArrayElementList(); var closingBrace = EatToken(TokenKind.ClosingBrace); expression = Factory.CellArrayElementAccessExpression( expression, Factory.Token(openingBrace), - index, + indices, Factory.Token(closingBrace) ); break; case TokenKind.OpeningBracket: // function call + if (options.ParsingArrayElements && expression.TrailingTrivia.Any()) + { + return expression; + } var openingBracket = EatToken(); var parameters = ParseFunctionCallParameterList(); var closingBracket = EatToken(TokenKind.ClosingBracket); @@ -355,7 +359,7 @@ namespace Parser } } - var expression = ParseExpression(); + var expression = ParseExpression(new ParseOptions {ParsingArrayElements = true}); if (expression != null) { nodes.Add(expression); @@ -381,7 +385,7 @@ namespace Parser } } - nodes.Add(ParseExpression()); + nodes.Add(ParseExpression(new ParseOptions {ParsingArrayElements = true})); } return Factory.ArrayElementList(nodes); @@ -420,7 +424,7 @@ namespace Parser closeParen); } - private ExpressionNode ParseTerm() + private ExpressionNode ParseTerm(ParseOptions options) { var token = CurrentToken; ExpressionNode expression = null; @@ -460,12 +464,24 @@ namespace Parser { 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() { - return ParseSubExpression(Precedence.Expression); + return ParseExpression(ParseOptions.Default); + } + + private ExpressionNode ParseExpression(ParseOptions options) + { + return ParseSubExpression(options, Precedence.Expression); } private bool IsUnaryOperator(TokenKind kind) @@ -533,8 +549,8 @@ namespace Parser throw new ArgumentException(nameof(kind)); } } - - private ExpressionNode ParseSubExpression(Precedence precedence) + + private ExpressionNode ParseSubExpression(ParseOptions options, Precedence precedence) { ExpressionNode lhs = null; if (IsUnaryOperator(CurrentToken.Kind)) @@ -542,12 +558,12 @@ namespace Parser var operation = EatToken(); var unaryTokenKind = ConvertToUnaryTokenKind(operation.Kind); var newPrecedence = GetPrecedence(unaryTokenKind); - var operand = ParseSubExpression(newPrecedence); + var operand = ParseSubExpression(options, newPrecedence); lhs = Factory.UnaryPrefixOperationExpression(Factory.Token(operation), operand); } else { - lhs = ParseTerm(); + lhs = ParseTerm(options); } while (true) { @@ -566,7 +582,7 @@ namespace Parser } EatToken(); - var rhs = ParseSubExpression(newPrecedence); + var rhs = ParseSubExpression(options, newPrecedence); if (rhs == null && token.Kind == TokenKind.Colon) // for parsing things like a{:} { rhs = Factory.EmptyExpression(); diff --git a/Parser/SyntaxFactory.cs b/Parser/SyntaxFactory.cs index 7c65d14..478d1ba 100644 --- a/Parser/SyntaxFactory.cs +++ b/Parser/SyntaxFactory.cs @@ -264,15 +264,15 @@ namespace Parser public CellArrayElementAccessExpressionNode CellArrayElementAccessExpression( ExpressionNode cellArray, TokenNode openingBrace, - ExpressionNode index, + ArrayElementListNode indices, TokenNode closingBrace) { - var children = new List {cellArray, openingBrace, index, closingBrace}; + var children = new List {cellArray, openingBrace, indices, closingBrace}; var result = new CellArrayElementAccessExpressionNode( children, cellArray, openingBrace, - index, + indices, closingBrace); SetParent(result); return result; diff --git a/Parser/SyntaxNode.cs b/Parser/SyntaxNode.cs index 57ecadf..57a7fdb 100644 --- a/Parser/SyntaxNode.cs +++ b/Parser/SyntaxNode.cs @@ -340,19 +340,19 @@ namespace Parser { public ExpressionNode CellArray { get; } public TokenNode OpeningBrace { get; } - public ExpressionNode Index { get; } + public ArrayElementListNode Indices { get; } public TokenNode ClosingBrace { get; } public CellArrayElementAccessExpressionNode( List children, ExpressionNode cellArray, TokenNode openingBrace, - ExpressionNode index, + ArrayElementListNode indices, TokenNode closingBrace) : base(children) { CellArray = cellArray; OpeningBrace = openingBrace; - Index = index; + Indices = indices; ClosingBrace = closingBrace; } }