Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
+ if (Expr.startswith("(")) {
+ if (AO != AllowedOperand::Any)
+ return ErrorDiagnostic::get(
+ SM, Expr, "parenthesized expression not permitted here");
+ return parseParenExpr(Expr, LineNumber, Context, SM);
+ }
+
if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {
// Try to parse as a numeric variable use.
Expected<Pattern::VariableProperties> ParseVarResult =
"invalid operand format '" + Expr + "'");
}
+Expected<std::unique_ptr<ExpressionAST>>
+Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
+ FileCheckPatternContext *Context, const SourceMgr &SM) {
+ Expr = Expr.ltrim(SpaceChars);
+ assert(Expr.startswith("("));
+
+ // Parse right operand.
+ Expr.consume_front("(");
+ Expr = Expr.ltrim(SpaceChars);
+ if (Expr.empty())
+ return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
+
+ // Note: parseNumericOperand handles nested opening parentheses.
+ Expected<std::unique_ptr<ExpressionAST>> SubExprResult =
+ parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
+ Expr = Expr.ltrim(SpaceChars);
+ while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) {
+ StringRef OrigExpr = Expr;
+ SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false,
+ LineNumber, Context, SM);
+ Expr = Expr.ltrim(SpaceChars);
+ }
+ if (!SubExprResult)
+ return SubExprResult;
+
+ if (!Expr.consume_front(")")) {
+ return ErrorDiagnostic::get(SM, Expr,
+ "missing ')' at end of nested expression");
+ }
+ return SubExprResult;
+}
+
static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
return LeftOp + RightOp;
}
/// \p Context points to the class instance holding the live string and
/// numeric variables. \returns the class representing that operand in the
/// AST of the expression or an error holding a diagnostic against \p SM
- /// otherwise.
+ /// otherwise. If \p Expr starts with a "(" this function will attempt to
+ /// parse a parenthesized expression.
static Expected<std::unique_ptr<ExpressionAST>>
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
Optional<size_t> LineNumber,
std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr,
Optional<size_t> LineNumber, FileCheckPatternContext *Context,
const SourceMgr &SM);
+
+ /// Parses a parenthesized expression inside \p Expr at line \p LineNumber, or
+ /// before input is parsed if \p LineNumber is None. \p Expr must start with
+ /// a '('. Accepts both literal values and numeric variables. Parameter \p
+ /// Context points to the class instance holding the live string and numeric
+ /// variables. \returns the class representing that operand in the AST of the
+ /// expression or an error holding a diagnostic against \p SM otherwise.
+ static Expected<std::unique_ptr<ExpressionAST>>
+ parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
+ FileCheckPatternContext *Context, const SourceMgr &SM);
};
//===----------------------------------------------------------------------===//
"implicit format conflict between 'FOO' (%u) and "
"'VAR_LOWER_HEX' (%x), need an explicit format specifier",
Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
+
+ // Simple parenthesized expressions:
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1+1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("(1)+1"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+1)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((1)+X)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("((X)+Y)"), Succeeded());
+
+ expectDiagnosticError("missing operand in expression",
+ Tester.parseSubst("(").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("(1").takeError());
+ expectDiagnosticError("missing operand in expression",
+ Tester.parseSubst("(1+").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("(1+1").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("((1+2+3").takeError());
+ expectDiagnosticError("missing ')' at end of nested expression",
+ Tester.parseSubst("((1+2)+3").takeError());
+
+ // Test missing operation between operands:
+ expectDiagnosticError("unsupported operation '('",
+ Tester.parseSubst("(1)(2)").takeError());
+ expectDiagnosticError("unsupported operation '('",
+ Tester.parseSubst("2(X)").takeError());
+
+ // Test more closing than opening parentheses. The diagnostic messages are
+ // not ideal, but for now simply check that we reject invalid input.
+ expectDiagnosticError("invalid operand format ')'",
+ Tester.parseSubst(")").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("1)").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(1+2))").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(2))").takeError());
+ expectDiagnosticError("unsupported operation ')'",
+ Tester.parseSubst("(1))(").takeError());
}
TEST_F(FileCheckTest, ParsePattern) {
Succeeded());
}
+TEST_F(FileCheckTest, MatchParen) {
+ PatternTester Tester;
+ // Check simple parenthesized expressions
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ expectNotFoundError(Tester.match("").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2 + 2)]]"));
+ expectNotFoundError(Tester.match("21").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR + (2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(NUMVAR)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
+
+ // Check nested parenthesized expressions:
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(2))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("22"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+(2+(NUMVAR))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("38"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR))))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("36"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR+((((NUMVAR)))-1)-1]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("34"), Succeeded());
+
+ // Parentheses can also be the first character after the '#':
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#(NUMVAR+2)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+}
+
TEST_F(FileCheckTest, Substitution) {
SourceMgr SM;
FileCheckPatternContext Context;