* a numeric operand, or
* an expression followed by an operator and a numeric operand.
- A numeric operand is a previously defined numeric variable, or an integer
- literal and have a 64-bit precision. The supported operators are ``+`` and
- ``-``. Spaces are accepted before, after and between any of these elements.
- Overflow and underflow are rejected. There is currently no support for
- operator precendence, but parentheses can be used to change the evaluation
- order.
+ A numeric operand is a previously defined numeric variable, an integer
+ literal, or a function. Spaces are accepted before, after and between any of
+ these elements. Numeric operands have 64-bit precision. Overflow and underflow
+ are rejected. There is no support for operator precendence, but parentheses
+ can be used to change the evaluation order.
+
+The supported operators are:
+
+ * ``+`` - Returns the sum of its two operands.
+ * ``-`` - Returns the difference of its two operands.
+
+The syntax of a function call is ``<name>(<arguments>)`` where:
+
+* ``name`` is a predefined string literal. Accepted values are:
+
+ * add - Returns the sum of its two operands.
+ * max - Returns the largest of its two operands.
+ * min - Returns the smallest of its two operands.
+ * sub - Returns the difference of its two operands.
+
+* ``<arguments>`` is a comma seperated list of expressions.
For example:
}
}
+Expected<ExpressionValue> llvm::max(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (LeftOperand.isNegative() && RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ int64_t RightValue = cantFail(RightOperand.getSignedValue());
+ return ExpressionValue(std::max(LeftValue, RightValue));
+ }
+
+ if (!LeftOperand.isNegative() && !RightOperand.isNegative()) {
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ return ExpressionValue(std::max(LeftValue, RightValue));
+ }
+
+ if (LeftOperand.isNegative())
+ return RightOperand;
+
+ return LeftOperand;
+}
+
+Expected<ExpressionValue> llvm::min(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand)
+ return RightOperand;
+
+ return LeftOperand;
+}
+
Expected<ExpressionValue> NumericVariableUse::eval() const {
Optional<ExpressionValue> Value = Variable->getValue();
if (Value)
if (Str.empty())
return ErrorDiagnostic::get(SM, Str, "empty variable name");
- bool ParsedOneChar = false;
- unsigned I = 0;
+ size_t I = 0;
bool IsPseudo = Str[0] == '@';
// Global vars start with '$'.
if (Str[0] == '$' || IsPseudo)
++I;
- for (unsigned E = Str.size(); I != E; ++I) {
- if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
- return ErrorDiagnostic::get(SM, Str, "invalid variable name");
+ if (!isValidVarNameStart(Str[I++]))
+ return ErrorDiagnostic::get(SM, Str, "invalid variable name");
+ for (size_t E = Str.size(); I != E; ++I)
// Variable names are composed of alphanumeric characters and underscores.
if (Str[I] != '_' && !isAlnum(Str[I]))
break;
- ParsedOneChar = true;
- }
StringRef Name = Str.take_front(I);
Str = Str.substr(I);
// Try to parse as a numeric variable use.
Expected<Pattern::VariableProperties> ParseVarResult =
parseVariable(Expr, SM);
- if (ParseVarResult)
+ if (ParseVarResult) {
+ // Try to parse a function call.
+ if (Expr.ltrim(SpaceChars).startswith("(")) {
+ if (AO != AllowedOperand::Any)
+ return ErrorDiagnostic::get(SM, ParseVarResult->Name,
+ "unexpected function call");
+
+ return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context,
+ SM);
+ }
+
return parseNumericVariableUse(ParseVarResult->Name,
ParseVarResult->IsPseudo, LineNumber,
Context, SM);
+ }
+
if (AO == AllowedOperand::LineVar)
return ParseVarResult.takeError();
// Ignore the error and retry parsing as a literal.
std::move(*RightOpResult));
}
+Expected<std::unique_ptr<ExpressionAST>>
+Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName,
+ Optional<size_t> LineNumber,
+ FileCheckPatternContext *Context, const SourceMgr &SM) {
+ Expr = Expr.ltrim(SpaceChars);
+ assert(Expr.startswith("("));
+
+ auto OptFunc = StringSwitch<Optional<binop_eval_t>>(FuncName)
+ .Case("add", operator+)
+ .Case("max", max)
+ .Case("min", min)
+ .Case("sub", operator-)
+ .Default(None);
+
+ if (!OptFunc)
+ return ErrorDiagnostic::get(
+ SM, FuncName, Twine("call to undefined function '") + FuncName + "'");
+
+ Expr.consume_front("(");
+ Expr = Expr.ltrim(SpaceChars);
+
+ // Parse call arguments, which are comma separated.
+ SmallVector<std::unique_ptr<ExpressionAST>, 4> Args;
+ while (!Expr.empty() && !Expr.startswith(")")) {
+ if (Expr.startswith(","))
+ return ErrorDiagnostic::get(SM, Expr, "missing argument");
+
+ // Parse the argument, which is an arbitary expression.
+ StringRef OuterBinOpExpr = Expr;
+ Expected<std::unique_ptr<ExpressionAST>> Arg =
+ parseNumericOperand(Expr, AllowedOperand::Any, LineNumber, Context, SM);
+ while (Arg && !Expr.empty()) {
+ Expr = Expr.ltrim(SpaceChars);
+ // Have we reached an argument terminator?
+ if (Expr.startswith(",") || Expr.startswith(")"))
+ break;
+
+ // Arg = Arg <op> <expr>
+ Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber,
+ Context, SM);
+ }
+
+ // Prefer an expression error over a generic invalid argument message.
+ if (!Arg)
+ return Arg.takeError();
+ Args.push_back(std::move(*Arg));
+
+ // Have we parsed all available arguments?
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front(","))
+ break;
+
+ Expr = Expr.ltrim(SpaceChars);
+ if (Expr.startswith(")"))
+ return ErrorDiagnostic::get(SM, Expr, "missing argument");
+ }
+
+ if (!Expr.consume_front(")"))
+ return ErrorDiagnostic::get(SM, Expr,
+ "missing ')' at end of call expression");
+
+ const unsigned NumArgs = Args.size();
+ if (NumArgs == 2)
+ return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]),
+ std::move(Args[1]));
+
+ // TODO: Support more than binop_eval_t.
+ return ErrorDiagnostic::get(SM, FuncName,
+ Twine("function '") + FuncName +
+ Twine("' takes 2 arguments but ") +
+ Twine(NumArgs) + " given");
+}
+
Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
DefinedNumericVariable = None;
ExpressionFormat ExplicitFormat = ExpressionFormat();
- // Parse format specifier.
+ // Parse format specifier (NOTE: ',' is also an argument seperator).
size_t FormatSpecEnd = Expr.find(',');
- if (FormatSpecEnd != StringRef::npos) {
+ size_t FunctionStart = Expr.find('(');
+ if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) {
Expr = Expr.ltrim(SpaceChars);
if (!Expr.consume_front("%"))
return ErrorDiagnostic::get(
const ExpressionValue &Rhs);
Expected<ExpressionValue> operator-(const ExpressionValue &Lhs,
const ExpressionValue &Rhs);
+Expected<ExpressionValue> max(const ExpressionValue &Lhs,
+ const ExpressionValue &Rhs);
+Expected<ExpressionValue> min(const ExpressionValue &Lhs,
+ const ExpressionValue &Rhs);
/// Base class representing the AST of a given expression.
class ExpressionAST {
FileCheckPatternContext *Context, const SourceMgr &SM);
enum class AllowedOperand { LineVar, LegacyLiteral, Any };
/// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
- /// before input is parsed if \p LineNumber is None. Accepts both literal
- /// values and numeric variables, depending on the value of \p AO. Parameter
- /// \p Context points to the class instance holding the live string and
- /// numeric variables. \returns the class representing that operand in the
+ /// before input is parsed if \p LineNumber is None. Accepts literal values,
+ /// numeric variables and function calls, depending on the value of \p AO.
+ /// 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. If \p Expr starts with a "(" this function will attempt to
/// parse a parenthesized expression.
static Expected<std::unique_ptr<ExpressionAST>>
parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
+
+ /// Parses \p Expr for an argument list belonging to a call to function \p
+ /// FuncName at line \p LineNumber, or before input is parsed if \p LineNumber
+ /// is None. Parameter \p FuncLoc is the source location used for diagnostics.
+ /// Parameter \p Context points to the class instance holding the live string
+ /// and numeric variables. \returns the class representing that call in the
+ /// AST of the expression or an error holding a diagnostic against \p SM
+ /// otherwise.
+ static Expected<std::unique_ptr<ExpressionAST>>
+ parseCallExpr(StringRef &Expr, StringRef FuncName,
+ Optional<size_t> LineNumber, FileCheckPatternContext *Context,
+ const SourceMgr &SM);
};
//===----------------------------------------------------------------------===//
11 // CHECK-NEXT: {{^}}[[#%u,UNSI]]
12 // CHECK-NEXT: {{^}}[[#%u,UNSI+1]]
10 // CHECK-NEXT: {{^}}[[#%u,UNSI-1]]
+15 // CHECK-NEXT: {{^}}[[#%u,add(UNSI,4)]]
+11 // CHECK-NEXT: {{^}}[[#%u,max(UNSI,7)]]
+99 // CHECK-NEXT: {{^}}[[#%u,max(UNSI,99)]]
+7 // CHECK-NEXT: {{^}}[[#%u,min(UNSI,7)]]
+11 // CHECK-NEXT: {{^}}[[#%u,min(UNSI,99)]]
+8 // CHECK-NEXT: {{^}}[[#%u,sub(UNSI,3)]]
c // CHECK-NEXT: {{^}}[[#%x,LHEX]]
d // CHECK-NEXT: {{^}}[[#%x,LHEX+1]]
b // CHECK-NEXT: {{^}}[[#%x,LHEX-1]]
1a // CHECK-NEXT: {{^}}[[#%x,LHEX+0xe]]
1a // CHECK-NEXT: {{^}}[[#%x,LHEX+0xE]]
+e // CHECK-NEXT: {{^}}[[#%x,add(LHEX,2)]]
+ff // CHECK-NEXT: {{^}}[[#%x,max(LHEX,0xff)]]
+a // CHECK-NEXT: {{^}}[[#%x,min(LHEX,0xa)]]
+a // CHECK-NEXT: {{^}}[[#%x,sub(LHEX,2)]]
D // CHECK-NEXT: {{^}}[[#%X,UHEX]]
E // CHECK-NEXT: {{^}}[[#%X,UHEX+1]]
C // CHECK-NEXT: {{^}}[[#%X,UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xE]]
+F // CHECK-NEXT: {{^}}[[#%X,add(UHEX,2)]]
+FF // CHECK-NEXT: {{^}}[[#%X,max(UHEX,0xff)]]
+A // CHECK-NEXT: {{^}}[[#%X,min(UHEX,0xa)]]
+B // CHECK-NEXT: {{^}}[[#%X,sub(UHEX,2)]]
-30 // CHECK-NEXT: {{^}}[[#%d,SIGN]]
-29 // CHECK-NEXT: {{^}}[[#%d,SIGN+1]]
-31 // CHECK-NEXT: {{^}}[[#%d,SIGN-1]]
42 // CHECK-NEXT: {{^}}[[#%d,SIGN+72]]
+-29 // CHECK-NEXT: {{^}}[[#%d,add(SIGN,1)]]
+-17 // CHECK-NEXT: {{^}}[[#%d,max(SIGN,-17)]]
+-30 // CHECK-NEXT: {{^}}[[#%d,min(SIGN,-17)]]
+-31 // CHECK-NEXT: {{^}}[[#%d,sub(SIGN,1)]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIa]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIb]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIc]]
10 // CHECK-NEXT: {{^}}[[# %u , UNSI -1]]
10 // CHECK-NEXT: {{^}}[[# %u , UNSI - 1]]
10 // CHECK-NEXT: {{^}}[[# %u , UNSI - 1 ]]
+13 // CHECK-NEXT: {{^}}[[#%u, add(UNSI,2)]]
+13 // CHECK-NEXT: {{^}}[[# %u, add(UNSI,2)]]
+13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI,2)]]
+13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI, 2)]]
+13 // CHECK-NEXT: {{^}}[[# %u , add( UNSI, 2)]]
+13 // CHECK-NEXT: {{^}}[[# %u , add( UNSI,2)]]
+13 // CHECK-NEXT: {{^}}[[# %u , add(UNSI,2) ]]
+13 // CHECK-NEXT: {{^}}[[# %u , add (UNSI,2)]]
+104 // CHECK-NEXT: {{^}}[[# %u , UNSI + sub( add (100 , UNSI+ 1 ), 20) +1 ]]
; Numeric expressions in implicit matching format and default matching rule using
; variables defined on other lines.
11 // CHECK-NEXT: {{^}}[[#UNSI]]
12 // CHECK-NEXT: {{^}}[[#UNSI+1]]
10 // CHECK-NEXT: {{^}}[[#UNSI-1]]
+99 // CHECK-NEXT: {{^}}[[#max(UNSI,99)]]
+7 // CHECK-NEXT: {{^}}[[#min(UNSI,7)]]
c // CHECK-NEXT: {{^}}[[#LHEX]]
d // CHECK-NEXT: {{^}}[[#LHEX+1]]
b // CHECK-NEXT: {{^}}[[#LHEX-1]]
1a // CHECK-NEXT: {{^}}[[#LHEX+0xe]]
1a // CHECK-NEXT: {{^}}[[#LHEX+0xE]]
+ff // CHECK-NEXT: {{^}}[[#max(LHEX,255)]]
+a // CHECK-NEXT: {{^}}[[#min(LHEX,10)]]
D // CHECK-NEXT: {{^}}[[#UHEX]]
E // CHECK-NEXT: {{^}}[[#UHEX+1]]
C // CHECK-NEXT: {{^}}[[#UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xE]]
+FF // CHECK-NEXT: {{^}}[[#max(UHEX,255)]]
+A // CHECK-NEXT: {{^}}[[#min(UHEX,10)]]
-30 // CHECK-NEXT: {{^}}[[#SIGN]]
-29 // CHECK-NEXT: {{^}}[[#SIGN+1]]
-31 // CHECK-NEXT: {{^}}[[#SIGN-1]]
UNDERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:29: error: unable to substitute variable or numeric expression
UNDERFLOW-MSG-NEXT: {{U}}NDERFLOW-NEXT: TINYVAR: {{\[\[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000\]\]}}
UNDERFLOW-MSG-NEXT: {{^}} ^{{$}}
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-MISSING-CLOSING-BRACKET --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CALL-MISSING-CLOSING-BRACKET-MSG %s
+
+CALL MISSING CLOSING BRACKET
+30
+CALL-MISSING-CLOSING-BRACKET-LABEL: CALL MISSING CLOSING BRACKET
+CALL-MISSING-CLOSING-BRACKET-NEXT: [[#add(NUMVAR,3]]
+CALL-MISSING-CLOSING-BRACKET-MSG: numeric-expression.txt:[[#@LINE-1]]:51: error: missing ')' at end of call expression
+CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{C}}ALL-MISSING-CLOSING-BRACKET-NEXT: {{\[\[#add\(NUMVAR,3\]\]}}
+CALL-MISSING-CLOSING-BRACKET-MSG-NEXT: {{^}} ^{{$}}
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-MISSING-ARGUMENT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CALL-MISSING-ARGUMENT-MSG %s
+
+CALL MISSING ARGUMENT
+30
+CALL-MISSING-ARGUMENT-LABEL: CALL MISSING ARGUMENT
+CALL-MISSING-ARGUMENT-NEXT: [[#add(NUMVAR,)]]
+CALL-MISSING-ARGUMENT-MSG: numeric-expression.txt:[[#@LINE-1]]:43: error: missing argument
+CALL-MISSING-ARGUMENT-MSG-NEXT: {{C}}ALL-MISSING-ARGUMENT-NEXT: {{\[\[#add\(NUMVAR,\)\]\]}}
+CALL-MISSING-ARGUMENT-MSG-NEXT: {{^}} ^{{$}}
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-WRONG-ARGUMENT-COUNT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CALL-WRONG-ARGUMENT-COUNT-MSG %s
+
+CALL WRONG ARGUMENT COUNT
+30
+CALL-WRONG-ARGUMENT-COUNT-LABEL: CALL WRONG ARGUMENT COUNT
+CALL-WRONG-ARGUMENT-COUNT-NEXT: [[#add(NUMVAR)]]
+CALL-WRONG-ARGUMENT-COUNT-MSG: numeric-expression.txt:[[#@LINE-1]]:36: error: function 'add' takes 2 arguments but 1 given
+CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{C}}ALL-WRONG-ARGUMENT-COUNT-NEXT: {{\[\[#add\(NUMVAR\)\]\]}}
+CALL-WRONG-ARGUMENT-COUNT-MSG-NEXT: {{^}} ^{{$}}
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-UNDEFINED-FUNCTION --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CALL-UNDEFINED-FUNCTION-MSG %s
+
+CALL UNDEFINED FUNCTION
+30
+CALL-UNDEFINED-FUNCTION-LABEL: CALL UNDEFINED FUNCTION
+CALL-UNDEFINED-FUNCTION-NEXT: [[#bogus_function(NUMVAR)]]
+CALL-UNDEFINED-FUNCTION-MSG: numeric-expression.txt:[[#@LINE-1]]:34: error: call to undefined function 'bogus_function'
+CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{C}}ALL-UNDEFINED-FUNCTION-NEXT: {{\[\[#bogus_function\(NUMVAR\)\]\]}}
+CALL-UNDEFINED-FUNCTION-MSG-NEXT: {{^}} ^{{$}}
public:
PatternTester() {
- std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ"};
+ std::vector<StringRef> GlobalDefines = {"#FOO=42", "BAR=BAZ", "#add=7"};
// An ASSERT_FALSE would make more sense but cannot be used in a
// constructor.
EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
Tester.parseSubst("(2))").takeError());
expectDiagnosticError("unsupported operation ')'",
Tester.parseSubst("(1))(").takeError());
+
+ // Valid expression with function call.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO,3)"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add (FOO,3)"), Succeeded());
+ // Valid expression with nested function call.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min(BAR,10))"), Succeeded());
+ // Valid expression with function call taking expression as argument.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, (BAR+10) + 3)"),
+ Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(FOO, min (BAR,10) + 3)"),
+ Succeeded());
+ // Valid expression with variable named the same as a function.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add+FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+add"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("add(add,add)+add"), Succeeded());
+
+ // Malformed call syntax.
+ expectDiagnosticError("missing ')' at end of call expression",
+ Tester.parseSubst("add(FOO,(BAR+7)").takeError());
+ expectDiagnosticError("missing ')' at end of call expression",
+ Tester.parseSubst("add(FOO,min(BAR,7)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(FOO,)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(,FOO)").takeError());
+ expectDiagnosticError("missing argument",
+ Tester.parseSubst("add(FOO,,3)").takeError());
+
+ // Valid call, but to an unknown function.
+ expectDiagnosticError("call to undefined function 'bogus_function'",
+ Tester.parseSubst("bogus_function(FOO,3)").takeError());
+ expectDiagnosticError("call to undefined function '@add'",
+ Tester.parseSubst("@add(2,3)").takeError());
+ expectDiagnosticError("call to undefined function '$add'",
+ Tester.parseSubst("$add(2,3)").takeError());
+ expectDiagnosticError("call to undefined function 'FOO'",
+ Tester.parseSubst("FOO(2,3)").takeError());
+ expectDiagnosticError("call to undefined function 'FOO'",
+ Tester.parseSubst("FOO (2,3)").takeError());
+
+ // Valid call, but with incorrect argument count.
+ expectDiagnosticError("function 'add' takes 2 arguments but 1 given",
+ Tester.parseSubst("add(FOO)").takeError());
+ expectDiagnosticError("function 'add' takes 2 arguments but 3 given",
+ Tester.parseSubst("add(FOO,3,4)").takeError());
+
+ // Valid call, but not part of a valid expression.
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("2add(FOO,2)").takeError());
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("FOO add(FOO,2)").takeError());
+ expectDiagnosticError("unsupported operation 'a'",
+ Tester.parseSubst("add(FOO,2)add(FOO,2)").takeError());
}
TEST_F(FileCheckTest, ParsePattern) {
EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
}
+TEST_F(FileCheckTest, MatchBuiltinFunctions) {
+ PatternTester Tester;
+ // Esnure #NUMVAR has the expected value.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
+ expectNotFoundError(Tester.match("FAIL").takeError());
+ expectNotFoundError(Tester.match("").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ // Check each builtin function generates the expected result.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(NUMVAR,13)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("31"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#sub(NUMVAR,7)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("11"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,5)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#max(NUMVAR,99)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("99"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,5)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("5"), Succeeded());
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#min(NUMVAR,99)]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+
+ // Check nested function calls.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(min(7,2),max(4,10))]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("12"), Succeeded());
+
+ // Check function call that uses a variable of the same name.
+ Tester.initNextPattern();
+ ASSERT_FALSE(Tester.parsePattern("[[#add(add,add)+min (add,3)+add]]"));
+ EXPECT_THAT_EXPECTED(Tester.match("24"), Succeeded());
+}
+
TEST_F(FileCheckTest, Substitution) {
SourceMgr SM;
FileCheckPatternContext Context;