From bebad3784d1e5c13d80bb4c77f77a08af00c0727 Mon Sep 17 00:00:00 2001 From: Chui Tey Date: Mon, 6 Feb 2023 06:45:19 +1000 Subject: [PATCH] #121 Failed parsing nested case statements --- .../Parsers/TSQLCaseExpressionParser.cs | 126 +++++++++++++++--- .../Tokens/Parsers/TSQLTokenParserHelper.cs | 6 + 2 files changed, 115 insertions(+), 17 deletions(-) diff --git a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLCaseExpressionParser.cs b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLCaseExpressionParser.cs index 794fa5b..c32e047 100644 --- a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLCaseExpressionParser.cs +++ b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLCaseExpressionParser.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - +using TSQL.Elements; using TSQL.Tokens; using TSQL.Tokens.Parsers; @@ -22,21 +18,110 @@ public TSQLCaseExpression Parse(ITSQLTokenizer tokenizer) caseExpression.Tokens.Add(tokenizer.Current); - TSQLTokenParserHelper.ReadUntilStop( + tokenizer.MoveNext(); + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( tokenizer, - caseExpression, - new List() { }, - new List() { - TSQLKeywords.END - }, - lookForStatementStarts: false); - - // this is different than the other clauses because the - // stop word is still part of the expression instead of - // being part of the next expression or clause like in - // the other parsers + caseExpression.Tokens); + + var nextToken = tokenizer.Current ?? throw new TSQLParseException("CASE expression is incomplete. Expect WHEN or an input expression"); + + TSQLToken whenToken = null; + + if (!nextToken.IsKeyword(TSQLKeywords.WHEN)) + { + caseExpression.IsSimpleCaseExpression = true; + var valueExpr = new TSQLValueExpressionParser().Parse(tokenizer); + caseExpression.Tokens.AddRange(valueExpr.Tokens); + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + caseExpression.Tokens); + + whenToken = tokenizer.Current; + } + else + { + caseExpression.IsSimpleCaseExpression = false; + whenToken = nextToken; + } + + if (!whenToken.IsKeyword(TSQLKeywords.WHEN)) + { + throw new TSQLParseException("CASE expression is incorrect. It should have a WHEN keyword, instead we have: " + whenToken.Text); + } + + // 'WHEN' keyword + caseExpression.Tokens.Add(whenToken); + + do + { + if (!tokenizer.MoveNext()) + { + throw new TSQLParseException("CASE expression is incomplete. After WHEN, there should be an expression"); + } + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + caseExpression.Tokens); + + var whenExpr = new TSQLValueExpressionParser().Parse(tokenizer); + caseExpression.Tokens.AddRange(whenExpr.Tokens); + + if (!tokenizer.Current.IsKeyword(TSQLKeywords.THEN)) + { + throw new TSQLParseException( + "CASE expression is incomplete. After WHEN, there should be a THEN keyword"); + } + + // 'THEN' keyword + caseExpression.Tokens.Add(tokenizer.Current); + + if (!tokenizer.MoveNext()) + { + throw new TSQLParseException( + "CASE expression is incomplete. After THEN, there should be an expression"); + } + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + caseExpression.Tokens); + + var thenExpr = new TSQLValueExpressionParser().Parse(tokenizer); + caseExpression.Tokens.AddRange(thenExpr.Tokens); + + } while (tokenizer.Current.IsKeyword(TSQLKeywords.WHEN)); + + if (!tokenizer.Current.IsKeyword(TSQLKeywords.ELSE)) + { + throw new TSQLParseException( + "CASE expression is incomplete. There should be an ELSE keyword"); + } + + caseExpression.Tokens.Add(tokenizer.Current); + + if (!tokenizer.MoveNext()) + { + throw new TSQLParseException( + "CASE expression incomplete. There should be an expression after ELSE keyword"); + } + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + caseExpression.Tokens); + + var elseExpr = new TSQLValueExpressionParser().Parse(tokenizer); + caseExpression.Tokens.AddRange(elseExpr.Tokens); + + if (!tokenizer.Current.IsKeyword(TSQLKeywords.END)) + { + throw new TSQLParseException("CASE expression incomplete. There should be an END keyword"); + } + + // 'END' keyword caseExpression.Tokens.Add(tokenizer.Current); + // move past the END keyword tokenizer.MoveNext(); TSQLTokenParserHelper.ReadCommentsAndWhitespace( @@ -45,5 +130,12 @@ public TSQLCaseExpression Parse(ITSQLTokenizer tokenizer) return caseExpression; } + + private TSQLToken ReadNextToken(ITSQLTokenizer tokenizer) + { + if (!tokenizer.MoveNext()) return null; + return tokenizer.Current; + } + } } diff --git a/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs b/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs index 378b2a5..7292a88 100644 --- a/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs +++ b/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs @@ -104,6 +104,12 @@ public static void ReadCommentsAndWhitespace( element.Tokens); } + /// + /// Precondition: tokenizer.Current has not been consumed + /// Postcondition: tokenizer advanced if token consumed + /// + /// + /// public static void ReadCommentsAndWhitespace( ITSQLTokenizer tokenizer, List savedTokens)