summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2018-07-05 14:25:56 (GMT)
committerKitware Robot <kwrobot@kitware.com>2018-07-05 14:26:05 (GMT)
commitca733cdf4532a4a3d05b82c49a5f2fbf994149de (patch)
treea4da4e97e6094b142b75e5575a4eb7d298422b5f
parent5fec622ece869b30560b797e469c93c57d7016b6 (diff)
parent5dbee9d2d0f68e1fc343d04ac00a4a35d43df6fa (diff)
downloadCMake-ca733cdf4532a4a3d05b82c49a5f2fbf994149de.zip
CMake-ca733cdf4532a4a3d05b82c49a5f2fbf994149de.tar.gz
CMake-ca733cdf4532a4a3d05b82c49a5f2fbf994149de.tar.bz2
Merge topic 'math-hex'
5dbee9d2d0 math: Add options to calculate and format output as hexadecimal 8661e7052c math: Diagnose divide-by-zero 7c4c13ffef math: Reject unexpected expression input explicitly 5b0f73a15a math: Use 64-bit integer type for computation 963b8d9f6c math: Add RunCMake.math test to prepare for error cases Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !2056
-rw-r--r--Help/command/math.rst18
-rw-r--r--Help/release/dev/math-hex.rst4
-rw-r--r--Source/LexerParser/cmExprLexer.cxx126
-rw-r--r--Source/LexerParser/cmExprLexer.in.l7
-rw-r--r--Source/LexerParser/cmExprParser.cxx137
-rw-r--r--Source/LexerParser/cmExprParser.y5
-rw-r--r--Source/LexerParser/cmExprParserTokens.h4
-rw-r--r--Source/cmExprParserHelper.cxx38
-rw-r--r--Source/cmExprParserHelper.h11
-rw-r--r--Source/cmMathCommand.cxx69
-rw-r--r--Tests/MathTest/CMakeLists.txt33
-rw-r--r--Tests/MathTest/MathTestExec.cxx40
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/math/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/math/MATH-DivideByZero-result.txt1
-rw-r--r--Tests/RunCMake/math/MATH-DivideByZero-stderr.txt4
-rw-r--r--Tests/RunCMake/math/MATH-DivideByZero.cmake1
-rw-r--r--Tests/RunCMake/math/MATH-DoubleOption-result.txt1
-rw-r--r--Tests/RunCMake/math/MATH-DoubleOption-stderr.txt4
-rw-r--r--Tests/RunCMake/math/MATH-DoubleOption.cmake1
-rw-r--r--Tests/RunCMake/math/MATH-InvalidExpression-result.txt1
-rw-r--r--Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt6
-rw-r--r--Tests/RunCMake/math/MATH-InvalidExpression.cmake1
-rw-r--r--Tests/RunCMake/math/MATH-TooManyArguments-result.txt1
-rw-r--r--Tests/RunCMake/math/MATH-TooManyArguments-stderr.txt4
-rw-r--r--Tests/RunCMake/math/MATH-TooManyArguments.cmake1
-rw-r--r--Tests/RunCMake/math/MATH-WrongArgument-result.txt1
-rw-r--r--Tests/RunCMake/math/MATH-WrongArgument-stderr.txt4
-rw-r--r--Tests/RunCMake/math/MATH-WrongArgument.cmake1
-rw-r--r--Tests/RunCMake/math/MATH.cmake12
-rw-r--r--Tests/RunCMake/math/RunCMakeTest.cmake8
31 files changed, 399 insertions, 149 deletions
diff --git a/Help/command/math.rst b/Help/command/math.rst
index f99dc3d..63af931 100644
--- a/Help/command/math.rst
+++ b/Help/command/math.rst
@@ -5,10 +5,26 @@ Mathematical expressions.
::
- math(EXPR <output-variable> <math-expression>)
+ math(EXPR <output-variable> <math-expression> [OUTPUT_FORMAT <format>])
``EXPR`` evaluates mathematical expression and returns result in the
output variable. Example mathematical expression is ``5 * (10 + 13)``.
Supported operators are ``+``, ``-``, ``*``, ``/``, ``%``, ``|``, ``&``,
``^``, ``~``, ``<<``, ``>>``, and ``(...)``. They have the same meaning
as they do in C code.
+
+Numeric constants are evaluated in decimal or hexadecimal representation.
+
+The result is formatted according to the option "OUTPUT_FORMAT" ,
+where ``<format>`` is one of:
+::
+
+ HEXADECIMAL = Result in output variable will be formatted in C code
+ Hexadecimal notation.
+ DECIMAL = Result in output variable will be formatted in decimal notation.
+
+
+For example::
+
+ math(EXPR value "100 * 0xA" DECIMAL) results in value is set to "1000"
+ math(EXPR value "100 * 0xA" HEXADECIMAL) results in value is set to "0x3e8"
diff --git a/Help/release/dev/math-hex.rst b/Help/release/dev/math-hex.rst
new file mode 100644
index 0000000..16e21ec
--- /dev/null
+++ b/Help/release/dev/math-hex.rst
@@ -0,0 +1,4 @@
+math-hex
+--------
+
+* The :command:`math` command gained options for hexadecimal.
diff --git a/Source/LexerParser/cmExprLexer.cxx b/Source/LexerParser/cmExprLexer.cxx
index 81a1ec5..1548daf 100644
--- a/Source/LexerParser/cmExprLexer.cxx
+++ b/Source/LexerParser/cmExprLexer.cxx
@@ -548,8 +548,8 @@ static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
yyg->yy_hold_char = *yy_cp; \
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
-#define YY_NUM_RULES 15
-#define YY_END_OF_BUFFER 16
+#define YY_NUM_RULES 18
+#define YY_END_OF_BUFFER 19
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@@ -557,29 +557,29 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
-static const flex_int16_t yy_accept[23] =
+static const flex_int16_t yy_accept[29] =
{ 0,
- 0, 0, 16, 15, 6, 8, 13, 14, 4, 2,
- 3, 5, 1, 15, 15, 9, 7, 10, 1, 11,
- 12, 0
+ 0, 0, 19, 17, 1, 18, 8, 10, 15, 16,
+ 6, 4, 5, 7, 2, 2, 17, 17, 11, 9,
+ 12, 2, 0, 13, 14, 3, 3, 0
} ;
static const YY_CHAR yy_ec[256] =
{ 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 1, 1, 4, 5, 1, 6,
+ 7, 8, 9, 1, 10, 1, 11, 12, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 1, 1, 14,
+ 1, 15, 1, 1, 16, 16, 16, 16, 16, 16,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 2, 3, 1, 4,
- 5, 6, 7, 1, 8, 1, 9, 10, 10, 10,
- 10, 10, 10, 10, 10, 10, 10, 1, 1, 11,
- 1, 12, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 13, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 17, 1, 1,
+ 1, 1, 1, 18, 1, 1, 16, 16, 16, 16,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 14, 1, 15, 1, 1, 1, 1,
+ 16, 16, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 17,
+ 1, 1, 1, 19, 1, 20, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -596,40 +596,46 @@ static const YY_CHAR yy_ec[256] =
1, 1, 1, 1, 1
} ;
-static const YY_CHAR yy_meta[16] =
+static const YY_CHAR yy_meta[21] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1
+ 1, 2, 2, 1, 1, 3, 4, 1, 1, 1
} ;
-static const flex_int16_t yy_base[23] =
+static const flex_int16_t yy_base[32] =
{ 0,
- 0, 0, 20, 21, 21, 21, 21, 21, 21, 21,
- 21, 21, 9, 7, 5, 21, 21, 21, 6, 21,
- 21, 21
+ 0, 0, 34, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 16, 9, 18, 11, 35, 35,
+ 35, 11, 0, 35, 35, 0, 0, 35, 23, 26,
+ 28
} ;
-static const flex_int16_t yy_def[23] =
+static const flex_int16_t yy_def[32] =
{ 0,
- 22, 1, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 0
+ 28, 1, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 29, 28, 28, 28, 28, 28,
+ 28, 28, 30, 28, 28, 31, 31, 0, 28, 28,
+ 28
} ;
-static const flex_int16_t yy_nxt[37] =
+static const flex_int16_t yy_nxt[56] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
- 14, 15, 16, 17, 18, 19, 21, 20, 19, 22,
- 3, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22
+ 14, 15, 16, 17, 18, 4, 4, 19, 20, 21,
+ 22, 22, 22, 22, 22, 25, 22, 26, 26, 27,
+ 27, 24, 23, 28, 3, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28
} ;
-static const flex_int16_t yy_chk[37] =
+static const flex_int16_t yy_chk[56] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 19, 15, 14, 13, 3,
- 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
- 22, 22, 22, 22, 22, 22
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 16, 16, 22, 22, 29, 18, 29, 30, 30, 31,
+ 31, 17, 15, 3, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28
} ;
/* The intent behind this definition is that it'll catch
@@ -668,6 +674,8 @@ Modify cmExprLexer.cxx:
/* Include the set of tokens from the parser. */
#include "cmExprParserTokens.h"
+#include <string>
+
/*--------------------------------------------------------------------------*/
#define INITIAL 0
@@ -946,13 +954,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 23 )
+ if ( yy_current_state >= 29 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
++yy_cp;
}
- while ( yy_base[yy_current_state] != 21 );
+ while ( yy_base[yy_current_state] != 35 );
yy_find_action:
yy_act = yy_accept[yy_current_state];
@@ -978,62 +986,74 @@ do_action: /* This label is used only to access EOF actions. */
case 1:
YY_RULE_SETUP
-{ yylvalp->Number = atoi(yytext); return exp_NUMBER; }
+{}
YY_BREAK
case 2:
YY_RULE_SETUP
-{ return exp_PLUS; }
+{ yylvalp->Number = std::stoll(yytext, nullptr, 10); return exp_NUMBER; }
YY_BREAK
case 3:
YY_RULE_SETUP
-{ return exp_MINUS; }
+{ yylvalp->Number = std::stoll(yytext, nullptr, 16); return exp_NUMBER; }
YY_BREAK
case 4:
YY_RULE_SETUP
-{ return exp_TIMES; }
+{ return exp_PLUS; }
YY_BREAK
case 5:
YY_RULE_SETUP
-{ return exp_DIVIDE; }
+{ return exp_MINUS; }
YY_BREAK
case 6:
YY_RULE_SETUP
-{ return exp_MOD; }
+{ return exp_TIMES; }
YY_BREAK
case 7:
YY_RULE_SETUP
-{ return exp_OR; }
+{ return exp_DIVIDE; }
YY_BREAK
case 8:
YY_RULE_SETUP
-{ return exp_AND; }
+{ return exp_MOD; }
YY_BREAK
case 9:
YY_RULE_SETUP
-{ return exp_XOR; }
+{ return exp_OR; }
YY_BREAK
case 10:
YY_RULE_SETUP
-{ return exp_NOT; }
+{ return exp_AND; }
YY_BREAK
case 11:
YY_RULE_SETUP
-{ return exp_SHIFTLEFT; }
+{ return exp_XOR; }
YY_BREAK
case 12:
YY_RULE_SETUP
-{ return exp_SHIFTRIGHT; }
+{ return exp_NOT; }
YY_BREAK
case 13:
YY_RULE_SETUP
-{ return exp_OPENPARENT; }
+{ return exp_SHIFTLEFT; }
YY_BREAK
case 14:
YY_RULE_SETUP
-{ return exp_CLOSEPARENT; }
+{ return exp_SHIFTRIGHT; }
YY_BREAK
case 15:
YY_RULE_SETUP
+{ return exp_OPENPARENT; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{ return exp_CLOSEPARENT; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+{return exp_UNEXPECTED;}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
ECHO;
YY_BREAK
case YY_STATE_EOF(INITIAL):
@@ -1334,7 +1354,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 23 )
+ if ( yy_current_state >= 29 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
@@ -1363,11 +1383,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
- if ( yy_current_state >= 23 )
+ if ( yy_current_state >= 29 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
- yy_is_jam = (yy_current_state == 22);
+ yy_is_jam = (yy_current_state == 28);
(void)yyg;
return yy_is_jam ? 0 : yy_current_state;
diff --git a/Source/LexerParser/cmExprLexer.in.l b/Source/LexerParser/cmExprLexer.in.l
index e5f177a..87237d1 100644
--- a/Source/LexerParser/cmExprLexer.in.l
+++ b/Source/LexerParser/cmExprLexer.in.l
@@ -28,6 +28,8 @@ Modify cmExprLexer.cxx:
/* Include the set of tokens from the parser. */
#include "cmExprParserTokens.h"
+#include <string>
+
/*--------------------------------------------------------------------------*/
%}
@@ -38,8 +40,10 @@ Modify cmExprLexer.cxx:
%pointer
%%
+[ \t] {}
-[0-9][0-9]* { yylvalp->Number = atoi(yytext); return exp_NUMBER; }
+[0-9][0-9]* { yylvalp->Number = std::stoll(yytext, nullptr, 10); return exp_NUMBER; }
+0[xX][0-9a-fA-F][0-9a-fA-F]* { yylvalp->Number = std::stoll(yytext, nullptr, 16); return exp_NUMBER; }
"+" { return exp_PLUS; }
"-" { return exp_MINUS; }
@@ -54,5 +58,6 @@ Modify cmExprLexer.cxx:
">>" { return exp_SHIFTRIGHT; }
"(" { return exp_OPENPARENT; }
")" { return exp_CLOSEPARENT; }
+. {return exp_UNEXPECTED;}
%%
diff --git a/Source/LexerParser/cmExprParser.cxx b/Source/LexerParser/cmExprParser.cxx
index 67664a5..cbb8078 100644
--- a/Source/LexerParser/cmExprParser.cxx
+++ b/Source/LexerParser/cmExprParser.cxx
@@ -89,6 +89,7 @@ Modify cmExprParser.cxx:
#include <stdlib.h>
#include <string.h>
+#include <stdexcept>
/*-------------------------------------------------------------------------*/
#define YYDEBUG 1
@@ -108,7 +109,7 @@ static void cmExpr_yyerror(yyscan_t yyscanner, const char* message);
# pragma warning (disable: 4065) /* Switch statement contains default but no case. */
#endif
-#line 112 "cmExprParser.cxx" /* yacc.c:339 */
+#line 113 "cmExprParser.cxx" /* yacc.c:339 */
# ifndef YY_NULLPTR
# if defined __cplusplus && 201103L <= __cplusplus
@@ -156,7 +157,8 @@ extern int cmExpr_yydebug;
exp_AND = 268,
exp_XOR = 269,
exp_NOT = 270,
- exp_NUMBER = 271
+ exp_NUMBER = 271,
+ exp_UNEXPECTED = 272
};
#endif
/* Tokens. */
@@ -174,6 +176,7 @@ extern int cmExpr_yydebug;
#define exp_XOR 269
#define exp_NOT 270
#define exp_NUMBER 271
+#define exp_UNEXPECTED 272
/* Value type. */
@@ -185,7 +188,7 @@ int cmExpr_yyparse (yyscan_t yyscanner);
/* Copy the second part of user declarations. */
-#line 189 "cmExprParser.cxx" /* yacc.c:358 */
+#line 192 "cmExprParser.cxx" /* yacc.c:358 */
#ifdef short
# undef short
@@ -430,7 +433,7 @@ union yyalloc
#define YYLAST 30
/* YYNTOKENS -- Number of terminals. */
-#define YYNTOKENS 17
+#define YYNTOKENS 18
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 10
/* YYNRULES -- Number of rules. */
@@ -441,7 +444,7 @@ union yyalloc
/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
by yylex, with out-of-bounds checking. */
#define YYUNDEFTOK 2
-#define YYMAXUTOK 271
+#define YYMAXUTOK 272
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
@@ -477,16 +480,16 @@ static const yytype_uint8 yytranslate[] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16
+ 15, 16, 17
};
#if YYDEBUG
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
- 0, 73, 73, 78, 81, 86, 89, 94, 97, 102,
- 105, 108, 113, 116, 119, 124, 127, 130, 133, 138,
- 141, 144, 149, 152
+ 0, 75, 75, 80, 83, 88, 91, 96, 99, 104,
+ 107, 110, 115, 118, 121, 126, 129, 132, 138, 143,
+ 146, 149, 154, 157
};
#endif
@@ -498,8 +501,9 @@ static const char *const yytname[] =
"$end", "error", "$undefined", "exp_PLUS", "exp_MINUS", "exp_TIMES",
"exp_DIVIDE", "exp_MOD", "exp_SHIFTLEFT", "exp_SHIFTRIGHT",
"exp_OPENPARENT", "exp_CLOSEPARENT", "exp_OR", "exp_AND", "exp_XOR",
- "exp_NOT", "exp_NUMBER", "$accept", "start", "exp", "bitwiseor",
- "bitwisexor", "bitwiseand", "shift", "term", "unary", "factor", YY_NULLPTR
+ "exp_NOT", "exp_NUMBER", "\"character\"", "$accept", "start", "exp",
+ "bitwiseor", "bitwisexor", "bitwiseand", "shift", "term", "unary",
+ "factor", YY_NULLPTR
};
#endif
@@ -509,7 +513,7 @@ static const char *const yytname[] =
static const yytype_uint16 yytoknum[] =
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
- 265, 266, 267, 268, 269, 270, 271
+ 265, 266, 267, 268, 269, 270, 271, 272
};
# endif
@@ -579,18 +583,18 @@ static const yytype_int8 yycheck[] =
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
- 0, 3, 4, 10, 16, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 25, 25, 19, 0, 12, 14,
- 13, 8, 9, 3, 4, 5, 6, 7, 11, 20,
- 21, 22, 23, 23, 24, 24, 25, 25, 25
+ 0, 3, 4, 10, 16, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 26, 26, 20, 0, 12, 14,
+ 13, 8, 9, 3, 4, 5, 6, 7, 11, 21,
+ 22, 23, 24, 24, 25, 25, 26, 26, 26
};
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
- 0, 17, 18, 19, 19, 20, 20, 21, 21, 22,
- 22, 22, 23, 23, 23, 24, 24, 24, 24, 25,
- 25, 25, 26, 26
+ 0, 18, 19, 20, 20, 21, 21, 22, 22, 23,
+ 23, 23, 24, 24, 24, 25, 25, 25, 25, 26,
+ 26, 26, 27, 27
};
/* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
@@ -1281,183 +1285,186 @@ yyreduce:
switch (yyn)
{
case 2:
-#line 73 "cmExprParser.y" /* yacc.c:1646 */
+#line 75 "cmExprParser.y" /* yacc.c:1646 */
{
cmExpr_yyget_extra(yyscanner)->SetResult((yyvsp[0].Number));
}
-#line 1289 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1293 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 3:
-#line 78 "cmExprParser.y" /* yacc.c:1646 */
+#line 80 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1297 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1301 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 4:
-#line 81 "cmExprParser.y" /* yacc.c:1646 */
+#line 83 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) | (yyvsp[0].Number);
}
-#line 1305 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1309 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 5:
-#line 86 "cmExprParser.y" /* yacc.c:1646 */
+#line 88 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1313 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1317 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 6:
-#line 89 "cmExprParser.y" /* yacc.c:1646 */
+#line 91 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) ^ (yyvsp[0].Number);
}
-#line 1321 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1325 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 7:
-#line 94 "cmExprParser.y" /* yacc.c:1646 */
+#line 96 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1329 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1333 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 8:
-#line 97 "cmExprParser.y" /* yacc.c:1646 */
+#line 99 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) & (yyvsp[0].Number);
}
-#line 1337 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1341 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 9:
-#line 102 "cmExprParser.y" /* yacc.c:1646 */
+#line 104 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1345 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1349 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 10:
-#line 105 "cmExprParser.y" /* yacc.c:1646 */
+#line 107 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) << (yyvsp[0].Number);
}
-#line 1353 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1357 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 11:
-#line 108 "cmExprParser.y" /* yacc.c:1646 */
+#line 110 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) >> (yyvsp[0].Number);
}
-#line 1361 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1365 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 12:
-#line 113 "cmExprParser.y" /* yacc.c:1646 */
+#line 115 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1369 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1373 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 13:
-#line 116 "cmExprParser.y" /* yacc.c:1646 */
+#line 118 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) + (yyvsp[0].Number);
}
-#line 1377 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1381 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 14:
-#line 119 "cmExprParser.y" /* yacc.c:1646 */
+#line 121 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) - (yyvsp[0].Number);
}
-#line 1385 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1389 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 15:
-#line 124 "cmExprParser.y" /* yacc.c:1646 */
+#line 126 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1393 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1397 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 16:
-#line 127 "cmExprParser.y" /* yacc.c:1646 */
+#line 129 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) * (yyvsp[0].Number);
}
-#line 1401 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1405 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 17:
-#line 130 "cmExprParser.y" /* yacc.c:1646 */
+#line 132 "cmExprParser.y" /* yacc.c:1646 */
{
+ if (yyvsp[0].Number == 0) {
+ throw std::overflow_error("divide by zero");
+ }
(yyval.Number) = (yyvsp[-2].Number) / (yyvsp[0].Number);
}
-#line 1409 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1416 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 18:
-#line 133 "cmExprParser.y" /* yacc.c:1646 */
+#line 138 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-2].Number) % (yyvsp[0].Number);
}
-#line 1417 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1424 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 19:
-#line 138 "cmExprParser.y" /* yacc.c:1646 */
+#line 143 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1425 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1432 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 20:
-#line 141 "cmExprParser.y" /* yacc.c:1646 */
+#line 146 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = + (yyvsp[0].Number);
}
-#line 1433 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1440 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 21:
-#line 144 "cmExprParser.y" /* yacc.c:1646 */
+#line 149 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = - (yyvsp[0].Number);
}
-#line 1441 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1448 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 22:
-#line 149 "cmExprParser.y" /* yacc.c:1646 */
+#line 154 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[0].Number);
}
-#line 1449 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1456 "cmExprParser.cxx" /* yacc.c:1646 */
break;
case 23:
-#line 152 "cmExprParser.y" /* yacc.c:1646 */
+#line 157 "cmExprParser.y" /* yacc.c:1646 */
{
(yyval.Number) = (yyvsp[-1].Number);
}
-#line 1457 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1464 "cmExprParser.cxx" /* yacc.c:1646 */
break;
-#line 1461 "cmExprParser.cxx" /* yacc.c:1646 */
+#line 1468 "cmExprParser.cxx" /* yacc.c:1646 */
default: break;
}
/* User semantic actions sometimes alter yychar, and that requires
@@ -1687,7 +1694,7 @@ yyreturn:
#endif
return yyresult;
}
-#line 157 "cmExprParser.y" /* yacc.c:1906 */
+#line 162 "cmExprParser.y" /* yacc.c:1906 */
/* End of grammar */
diff --git a/Source/LexerParser/cmExprParser.y b/Source/LexerParser/cmExprParser.y
index d1c3a97..510daaa 100644
--- a/Source/LexerParser/cmExprParser.y
+++ b/Source/LexerParser/cmExprParser.y
@@ -18,6 +18,7 @@ Modify cmExprParser.cxx:
#include <stdlib.h>
#include <string.h>
+#include <stdexcept>
/*-------------------------------------------------------------------------*/
#define YYDEBUG 1
@@ -63,6 +64,7 @@ static void cmExpr_yyerror(yyscan_t yyscanner, const char* message);
%token exp_XOR;
%token exp_NOT;
%token exp_NUMBER;
+%token exp_UNEXPECTED "character";
/*-------------------------------------------------------------------------*/
/* grammar */
@@ -128,6 +130,9 @@ term:
$<Number>$ = $<Number>1 * $<Number>3;
}
| term exp_DIVIDE unary {
+ if (yyvsp[0].Number == 0) {
+ throw std::overflow_error("divide by zero");
+ }
$<Number>$ = $<Number>1 / $<Number>3;
}
| term exp_MOD unary {
diff --git a/Source/LexerParser/cmExprParserTokens.h b/Source/LexerParser/cmExprParserTokens.h
index 84b2bbd..00be4e9 100644
--- a/Source/LexerParser/cmExprParserTokens.h
+++ b/Source/LexerParser/cmExprParserTokens.h
@@ -58,7 +58,8 @@ extern int cmExpr_yydebug;
exp_AND = 268,
exp_XOR = 269,
exp_NOT = 270,
- exp_NUMBER = 271
+ exp_NUMBER = 271,
+ exp_UNEXPECTED = 272
};
#endif
/* Tokens. */
@@ -76,6 +77,7 @@ extern int cmExpr_yydebug;
#define exp_XOR 269
#define exp_NOT 270
#define exp_NUMBER 271
+#define exp_UNEXPECTED 272
/* Value type. */
diff --git a/Source/cmExprParserHelper.cxx b/Source/cmExprParserHelper.cxx
index fe7159a..d90d8b9 100644
--- a/Source/cmExprParserHelper.cxx
+++ b/Source/cmExprParserHelper.cxx
@@ -6,6 +6,8 @@
#include <iostream>
#include <sstream>
+#include <stdexcept>
+#include <utility>
int cmExpr_yyparse(yyscan_t yyscanner);
//
@@ -13,6 +15,7 @@ cmExprParserHelper::cmExprParserHelper()
{
this->FileLine = -1;
this->FileName = nullptr;
+ this->Result = 0;
}
cmExprParserHelper::~cmExprParserHelper()
@@ -37,7 +40,33 @@ int cmExprParserHelper::ParseString(const char* str, int verb)
yyscan_t yyscanner;
cmExpr_yylex_init(&yyscanner);
cmExpr_yyset_extra(this, yyscanner);
- int res = cmExpr_yyparse(yyscanner);
+ int res;
+
+ try {
+ res = cmExpr_yyparse(yyscanner);
+ if (res != 0) {
+ std::string e = "cannot parse the expression: \"" + InputBuffer + "\": ";
+ e += ErrorString;
+ e += ".";
+ this->SetError(std::move(e));
+ }
+ } catch (std::runtime_error const& fail) {
+ std::string e =
+ "cannot evaluate the expression: \"" + InputBuffer + "\": ";
+ e += fail.what();
+ e += ".";
+ this->SetError(std::move(e));
+ res = 1;
+ } catch (std::out_of_range const&) {
+ std::string e = "cannot evaluate the expression: \"" + InputBuffer +
+ "\": a numeric value is out of range.";
+ this->SetError(std::move(e));
+ res = 1;
+ } catch (...) {
+ std::string e = "cannot parse the expression: \"" + InputBuffer + "\".";
+ this->SetError(std::move(e));
+ res = 1;
+ }
cmExpr_yylex_destroy(yyscanner);
if (res != 0) {
// str << "CAL_Parser returned: " << res << std::endl;
@@ -85,7 +114,12 @@ void cmExprParserHelper::Error(const char* str)
this->ErrorString = ostr.str();
}
-void cmExprParserHelper::SetResult(int value)
+void cmExprParserHelper::SetResult(KWIML_INT_int64_t value)
{
this->Result = value;
}
+
+void cmExprParserHelper::SetError(std::string errorString)
+{
+ this->ErrorString = std::move(errorString);
+}
diff --git a/Source/cmExprParserHelper.h b/Source/cmExprParserHelper.h
index dcdaca9..d4d95b4 100644
--- a/Source/cmExprParserHelper.h
+++ b/Source/cmExprParserHelper.h
@@ -5,6 +5,8 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include "cm_kwiml.h"
+
#include <string>
#include <vector>
@@ -13,7 +15,7 @@ class cmExprParserHelper
public:
struct ParserType
{
- int Number;
+ KWIML_INT_int64_t Number;
};
cmExprParserHelper();
@@ -24,9 +26,9 @@ public:
int LexInput(char* buf, int maxlen);
void Error(const char* str);
- void SetResult(int value);
+ void SetResult(KWIML_INT_int64_t value);
- int GetResult() { return this->Result; }
+ KWIML_INT_int64_t GetResult() { return this->Result; }
const char* GetError() { return this->ErrorString.c_str(); }
@@ -40,8 +42,9 @@ private:
void Print(const char* place, const char* str);
void CleanupParser();
+ void SetError(std::string errorString);
- int Result;
+ KWIML_INT_int64_t Result;
const char* FileName;
long FileLine;
std::string ErrorString;
diff --git a/Source/cmMathCommand.cxx b/Source/cmMathCommand.cxx
index c1cd1b6..5d1b099 100644
--- a/Source/cmMathCommand.cxx
+++ b/Source/cmMathCommand.cxx
@@ -2,10 +2,11 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmMathCommand.h"
-#include <stdio.h>
-
#include "cmExprParserHelper.h"
#include "cmMakefile.h"
+#include "cm_kwiml.h"
+
+#include <stdio.h>
class cmExecutionStatus;
@@ -27,24 +28,78 @@ bool cmMathCommand::InitialPass(std::vector<std::string> const& args,
bool cmMathCommand::HandleExprCommand(std::vector<std::string> const& args)
{
- if (args.size() != 3) {
+ if ((args.size() != 3) && (args.size() != 5)) {
this->SetError("EXPR called with incorrect arguments.");
return false;
}
+ enum class NumericFormat
+ {
+ UNINITIALIZED,
+ DECIMAL,
+ HEXADECIMAL,
+ };
+
const std::string& outputVariable = args[1];
const std::string& expression = args[2];
+ size_t argumentIndex = 3;
+ NumericFormat outputFormat = NumericFormat::UNINITIALIZED;
+
+ this->Makefile->AddDefinition(outputVariable, "ERROR");
+
+ if (argumentIndex < args.size()) {
+ const std::string messageHint = "sub-command EXPR ";
+ const std::string option = args[argumentIndex++];
+ if (option == "OUTPUT_FORMAT") {
+ if (argumentIndex < args.size()) {
+ const std::string argument = args[argumentIndex++];
+ if (argument == "DECIMAL") {
+ outputFormat = NumericFormat::DECIMAL;
+ } else if (argument == "HEXADECIMAL") {
+ outputFormat = NumericFormat::HEXADECIMAL;
+ } else {
+ std::string error = messageHint + "value \"" + argument +
+ "\" for option \"" + option + "\" is invalid.";
+ this->SetError(error);
+ return false;
+ }
+ } else {
+ std::string error =
+ messageHint + "missing argument for option \"" + option + "\".";
+ this->SetError(error);
+ return false;
+ }
+ } else {
+ std::string error =
+ messageHint + "option \"" + option + "\" is unknown.";
+ this->SetError(error);
+ return false;
+ }
+ }
+
+ if (outputFormat == NumericFormat::UNINITIALIZED) {
+ outputFormat = NumericFormat::DECIMAL;
+ }
cmExprParserHelper helper;
if (!helper.ParseString(expression.c_str(), 0)) {
- std::string e = "cannot parse the expression: \"" + expression + "\": ";
- e += helper.GetError();
- this->SetError(e);
+ this->SetError(helper.GetError());
return false;
}
char buffer[1024];
- sprintf(buffer, "%d", helper.GetResult());
+ const char* fmt;
+ switch (outputFormat) {
+ case NumericFormat::HEXADECIMAL:
+ fmt = "0x%" KWIML_INT_PRIx64;
+ break;
+ case NumericFormat::DECIMAL:
+ CM_FALLTHROUGH;
+ default:
+ fmt = "%" KWIML_INT_PRId64;
+ break;
+ }
+ sprintf(buffer, fmt, helper.GetResult());
this->Makefile->AddDefinition(outputVariable, buffer);
return true;
diff --git a/Tests/MathTest/CMakeLists.txt b/Tests/MathTest/CMakeLists.txt
index f764b3a..5403d29 100644
--- a/Tests/MathTest/CMakeLists.txt
+++ b/Tests/MathTest/CMakeLists.txt
@@ -13,14 +13,35 @@ set(expressions
"-1 + +1"
"+1 - -1"
"+1 - - + + -(-3 + - - +1)"
+ "1000 -12*5"
+ "1000 +12*-5"
+ "1000 -12*-5"
)
-set(FILE_EXPRESSIONS "")
-foreach(expression
- ${expressions})
- math(EXPR expr "${expression}")
- string(APPEND FILE_EXPRESSIONS "TEST_EXPRESSION(${expression}, ${expr})\n")
-endforeach()
+set(FILE_EXPRESSIONS "extern void test_expression(int x, int y, const char * text);\n")
+
+
+macro(add_math_test expression)
+ math(EXPR result ${expression} ${ARGV1} ${ARGV2})
+ set(CODE "test_expression(${expression}, ${result}, \"${expression}\");")
+ string(APPEND FILE_EXPRESSIONS "${CODE}\n")
+endmacro()
+
+macro(add_math_tests)
+ foreach (expression ${expressions})
+ add_math_test(${expression} ${ARGV0} ${ARGV1})
+ endforeach ()
+endmacro()
+
+add_math_tests()
+add_math_tests("OUTPUT_FORMAT" "DECIMAL")
+add_math_tests("OUTPUT_FORMAT" "HEXADECIMAL")
+
+# Avoid the test with negative result and hexadecimal formatting
+# therefore more tests with a negative result
+add_math_test("-12*5")
+add_math_test("12*-5")
+
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/MathTestTests.h.in"
diff --git a/Tests/MathTest/MathTestExec.cxx b/Tests/MathTest/MathTestExec.cxx
index 124eba4..fbcddc4 100644
--- a/Tests/MathTest/MathTestExec.cxx
+++ b/Tests/MathTest/MathTestExec.cxx
@@ -1,21 +1,43 @@
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#define TEST_EXPRESSION(x, y) \
- if ((x) != (y)) { \
- printf("Problem with EXPR: Expression: \"%s\" in C returns %d while in " \
- "CMake returns: %d\n", \
- #x, (x), (y)); \
- res++; \
+int res = 0;
+bool print = false;
+
+void test_expression(int x, int y, const char* text)
+{
+ bool fail = (x) != (y);
+ if (fail) {
+ res++;
+ printf("Problem with EXPR:");
+ }
+ if (fail || print) {
+ printf("Expression: \"%s\" in CMake returns %d", text, (y));
+ if (fail) {
+ printf(" while in C returns: %d", (x));
+ }
+ printf("\n");
}
+}
int main(int argc, char* argv[])
{
- if (argc > 1) {
- printf("Usage: %s\n", argv[0]);
+ if (argc > 2) {
+ printf("Usage: %s [print]\n", argv[0]);
return 1;
}
- int res = 0;
+
+ if (argc > 1) {
+ if (strcmp(argv[1], "print") != 0) {
+ printf("Usage: %s [print]\n", argv[0]);
+ return 1;
+ }
+ print = true;
+ }
+
#include "MathTestTests.h"
+
if (res != 0) {
printf("%s: %d math tests failed\n", argv[0], res);
return 1;
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index dcc7c98..c211f99 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -238,6 +238,7 @@ add_RunCMake_test(include)
add_RunCMake_test(include_directories)
add_RunCMake_test(include_guard)
add_RunCMake_test(list)
+add_RunCMake_test(math)
add_RunCMake_test(message)
add_RunCMake_test(project -DCMake_TEST_RESOURCES=${CMake_TEST_RESOURCES})
add_RunCMake_test(return)
diff --git a/Tests/RunCMake/math/CMakeLists.txt b/Tests/RunCMake/math/CMakeLists.txt
new file mode 100644
index 0000000..12cd3c7
--- /dev/null
+++ b/Tests/RunCMake/math/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8.4)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/math/MATH-DivideByZero-result.txt b/Tests/RunCMake/math/MATH-DivideByZero-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideByZero-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-DivideByZero-stderr.txt b/Tests/RunCMake/math/MATH-DivideByZero-stderr.txt
new file mode 100644
index 0000000..66ad633
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideByZero-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at MATH-DivideByZero.cmake:1 \(math\):
+ math cannot evaluate the expression: "100/0": divide by zero.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-DivideByZero.cmake b/Tests/RunCMake/math/MATH-DivideByZero.cmake
new file mode 100644
index 0000000..3ac161e
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideByZero.cmake
@@ -0,0 +1 @@
+math(EXPR var "100/0")
diff --git a/Tests/RunCMake/math/MATH-DoubleOption-result.txt b/Tests/RunCMake/math/MATH-DoubleOption-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DoubleOption-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-DoubleOption-stderr.txt b/Tests/RunCMake/math/MATH-DoubleOption-stderr.txt
new file mode 100644
index 0000000..767a060
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DoubleOption-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at MATH-DoubleOption.cmake:1 \(math\):
+ math EXPR called with incorrect arguments.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-DoubleOption.cmake b/Tests/RunCMake/math/MATH-DoubleOption.cmake
new file mode 100644
index 0000000..7bcb78e
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DoubleOption.cmake
@@ -0,0 +1 @@
+math(EXPR var "10*10" OUTPUT_FORMAT DECIMAL OUTPUT_FORMAT HEXADECIMAL)
diff --git a/Tests/RunCMake/math/MATH-InvalidExpression-result.txt b/Tests/RunCMake/math/MATH-InvalidExpression-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-InvalidExpression-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt b/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt
new file mode 100644
index 0000000..18ac9f7
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-InvalidExpression-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at MATH-InvalidExpression.cmake:1 \(math\):
+ *math cannot parse the expression: "INVALID": syntax error, unexpected
+ *character, expecting exp_PLUS or exp_MINUS or exp_OPENPARENT or exp_NUMBER
+ *\(1\).
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-InvalidExpression.cmake b/Tests/RunCMake/math/MATH-InvalidExpression.cmake
new file mode 100644
index 0000000..6e37128
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-InvalidExpression.cmake
@@ -0,0 +1 @@
+math(EXPR var "INVALID")
diff --git a/Tests/RunCMake/math/MATH-TooManyArguments-result.txt b/Tests/RunCMake/math/MATH-TooManyArguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-TooManyArguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-TooManyArguments-stderr.txt b/Tests/RunCMake/math/MATH-TooManyArguments-stderr.txt
new file mode 100644
index 0000000..fdcfecf
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-TooManyArguments-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at MATH-TooManyArguments.cmake:1 \(math\):
+ math EXPR called with incorrect arguments.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-TooManyArguments.cmake b/Tests/RunCMake/math/MATH-TooManyArguments.cmake
new file mode 100644
index 0000000..969dc80
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-TooManyArguments.cmake
@@ -0,0 +1 @@
+math(EXPR var "10*10" OUTPUT_FORMAT DECIMAL OUTPUT_FORMAT )
diff --git a/Tests/RunCMake/math/MATH-WrongArgument-result.txt b/Tests/RunCMake/math/MATH-WrongArgument-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-WrongArgument-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-WrongArgument-stderr.txt b/Tests/RunCMake/math/MATH-WrongArgument-stderr.txt
new file mode 100644
index 0000000..bbe54bf
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-WrongArgument-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at MATH-WrongArgument.cmake:1 \(math\):
+ math sub-command EXPR option "OUT" is unknown.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-WrongArgument.cmake b/Tests/RunCMake/math/MATH-WrongArgument.cmake
new file mode 100644
index 0000000..fb6d2e7
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-WrongArgument.cmake
@@ -0,0 +1 @@
+math(EXPR var "10*10" OUT HEX )
diff --git a/Tests/RunCMake/math/MATH.cmake b/Tests/RunCMake/math/MATH.cmake
new file mode 100644
index 0000000..a5f50cd
--- /dev/null
+++ b/Tests/RunCMake/math/MATH.cmake
@@ -0,0 +1,12 @@
+macro(math_test expression expected)
+ math(EXPR evaluated ${expression} ${ARGN})
+ if (NOT evaluated STREQUAL ${expected})
+ message(FATAL_ERROR "wrong math result: ${evaluated} != ${expected}")
+ endif ()
+endmacro()
+
+
+math_test("100 * 10" 1000)
+math_test("100 * 10" 1000 OUTPUT_FORMAT DECIMAL)
+math_test("100 * 0xA" 1000 OUTPUT_FORMAT DECIMAL)
+math_test("100 * 0xA" 0x3e8 OUTPUT_FORMAT HEXADECIMAL)
diff --git a/Tests/RunCMake/math/RunCMakeTest.cmake b/Tests/RunCMake/math/RunCMakeTest.cmake
new file mode 100644
index 0000000..494b813
--- /dev/null
+++ b/Tests/RunCMake/math/RunCMakeTest.cmake
@@ -0,0 +1,8 @@
+include(RunCMake)
+
+run_cmake(MATH)
+run_cmake(MATH-WrongArgument)
+run_cmake(MATH-DoubleOption)
+run_cmake(MATH-TooManyArguments)
+run_cmake(MATH-InvalidExpression)
+run_cmake(MATH-DivideByZero)