Commit 809ec546 by Olli Etuaho

Don't evaluate short-circuited preprocessor expressions

Resubmit with clang build issue fixed. The result of a short-circuited operation is now either 0 or 1. ESSL 3.00 spec section 3.4 mentions that the second operand in a logical && or || preprocessor operation is evaluated only if the first operand doesn't short-circuit the expression. The non-evaluated part of a preprocessor expression may also have undefined identifiers. Make the expression parser follow the spec by ignoring errors that are generated inside short-circuited expressions. This includes undefined identifiers and divide by zero. BUG=angleproject:347 TEST=dEQP-GLES3.functional.shaders.preprocessor.undefined_identifiers.* angle_unittests Change-Id: I4163f96ec46d40ac859ffb39d91b89490041e44d Reviewed-on: https://chromium-review.googlesource.com/297252Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 9a1b49f7
......@@ -122,6 +122,13 @@ struct Context
pp::Lexer* lexer;
pp::Token* token;
int* result;
void startIgnoreErrors() { ++ignoreErrors; }
void endIgnoreErrors() { --ignoreErrors; }
bool isIgnoringErrors() { return ignoreErrors > 0; }
int ignoreErrors;
};
} // namespace
......@@ -162,15 +169,16 @@ extern int ppdebug;
enum yytokentype
{
TOK_CONST_INT = 258,
TOK_OP_OR = 259,
TOK_OP_AND = 260,
TOK_OP_EQ = 261,
TOK_OP_NE = 262,
TOK_OP_LE = 263,
TOK_OP_GE = 264,
TOK_OP_LEFT = 265,
TOK_OP_RIGHT = 266,
TOK_UNARY = 267
TOK_IDENTIFIER = 259,
TOK_OP_OR = 260,
TOK_OP_AND = 261,
TOK_OP_EQ = 262,
TOK_OP_NE = 263,
TOK_OP_LE = 264,
TOK_OP_GE = 265,
TOK_OP_LEFT = 266,
TOK_OP_RIGHT = 267,
TOK_UNARY = 268
};
#endif
......@@ -429,23 +437,23 @@ union yyalloc
#endif /* !YYCOPY_NEEDED */
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 14
#define YYFINAL 15
/* YYLAST -- Last index in YYTABLE. */
#define YYLAST 175
#define YYLAST 176
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 27
#define YYNTOKENS 28
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 3
#define YYNNTS 5
/* YYNRULES -- Number of rules. */
#define YYNRULES 26
#define YYNRULES 29
/* YYNSTATES -- Number of states. */
#define YYNSTATES 52
#define YYNSTATES 55
/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
by yylex, with out-of-bounds checking. */
#define YYUNDEFTOK 2
#define YYMAXUTOK 267
#define YYMAXUTOK 268
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
......@@ -457,16 +465,16 @@ static const yytype_uint8 yytranslate[] =
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 23, 2, 2, 2, 21, 8, 2,
25, 26, 19, 17, 2, 18, 2, 20, 2, 2,
2, 2, 2, 24, 2, 2, 2, 22, 9, 2,
26, 27, 20, 18, 2, 19, 2, 21, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
11, 2, 12, 2, 2, 2, 2, 2, 2, 2,
12, 2, 13, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 7, 2, 2, 2, 2, 2,
2, 2, 2, 2, 8, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 6, 2, 24, 2, 2, 2,
2, 2, 2, 2, 7, 2, 25, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
......@@ -480,16 +488,16 @@ static const yytype_uint8 yytranslate[] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 9, 10, 13, 14, 15, 16, 22
5, 6, 10, 11, 14, 15, 16, 17, 23
};
#if YYDEBUG
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
static const yytype_uint16 yyrline[] =
{
0, 97, 97, 104, 105, 108, 111, 114, 117, 120,
123, 126, 129, 132, 135, 138, 141, 144, 147, 150,
163, 176, 179, 182, 185, 188, 191
0, 105, 105, 111, 112, 118, 118, 139, 139, 160,
163, 166, 169, 172, 175, 178, 181, 184, 187, 190,
193, 196, 199, 222, 245, 248, 251, 254, 257, 260
};
#endif
......@@ -498,11 +506,11 @@ static const yytype_uint8 yyrline[] =
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
{
"$end", "error", "$undefined", "TOK_CONST_INT", "TOK_OP_OR",
"TOK_OP_AND", "'|'", "'^'", "'&'", "TOK_OP_EQ", "TOK_OP_NE", "'<'",
"'>'", "TOK_OP_LE", "TOK_OP_GE", "TOK_OP_LEFT", "TOK_OP_RIGHT", "'+'",
"'-'", "'*'", "'/'", "'%'", "TOK_UNARY", "'!'", "'~'", "'('", "')'",
"$accept", "input", "expression", YY_NULLPTR
"$end", "error", "$undefined", "TOK_CONST_INT", "TOK_IDENTIFIER",
"TOK_OP_OR", "TOK_OP_AND", "'|'", "'^'", "'&'", "TOK_OP_EQ", "TOK_OP_NE",
"'<'", "'>'", "TOK_OP_LE", "TOK_OP_GE", "TOK_OP_LEFT", "TOK_OP_RIGHT",
"'+'", "'-'", "'*'", "'/'", "'%'", "TOK_UNARY", "'!'", "'~'", "'('",
"')'", "$accept", "input", "expression", "$@1", "$@2", YY_NULLPTR
};
#endif
......@@ -511,16 +519,16 @@ static const char *const yytname[] =
(internal) symbol number NUM (which must be that of a token). */
static const yytype_uint16 yytoknum[] =
{
0, 256, 257, 258, 259, 260, 124, 94, 38, 261,
262, 60, 62, 263, 264, 265, 266, 43, 45, 42,
47, 37, 267, 33, 126, 40, 41
0, 256, 257, 258, 259, 260, 261, 124, 94, 38,
262, 263, 60, 62, 264, 265, 266, 267, 43, 45,
42, 47, 37, 268, 33, 126, 40, 41
};
# endif
#define YYPACT_NINF -11
#define YYPACT_NINF -12
#define yypact_value_is_default(Yystate) \
(!!((Yystate) == (-11)))
(!!((Yystate) == (-12)))
#define YYTABLE_NINF -1
......@@ -531,12 +539,12 @@ static const yytype_uint16 yytoknum[] =
STATE-NUM. */
static const yytype_int16 yypact[] =
{
46, -11, 46, 46, 46, 46, 46, 12, 68, -11,
-11, -11, -11, 27, -11, 46, 46, 46, 46, 46,
46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
46, 46, 46, -11, 85, 101, 116, 130, 143, 154,
154, -10, -10, -10, -10, 37, 37, 31, 31, -11,
-11, -11
31, -12, -12, 31, 31, 31, 31, 31, 51, 76,
-12, -12, -12, -12, 53, -12, -12, -12, 31, 31,
31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
31, 31, 31, 31, -12, 31, 31, 124, 138, 26,
149, 149, -11, -11, -11, -11, 154, 154, -8, -8,
-12, -12, -12, 93, 109
};
/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
......@@ -544,24 +552,24 @@ static const yytype_int16 yypact[] =
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
0, 3, 0, 0, 0, 0, 0, 0, 2, 25,
24, 22, 23, 0, 1, 0, 0, 0, 0, 0,
0, 3, 4, 0, 0, 0, 0, 0, 0, 2,
28, 27, 25, 26, 0, 1, 5, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 26, 4, 5, 6, 7, 8, 10,
9, 14, 13, 12, 11, 16, 15, 18, 17, 21,
20, 19
0, 0, 0, 0, 29, 0, 0, 9, 10, 11,
13, 12, 17, 16, 15, 14, 19, 18, 21, 20,
24, 23, 22, 6, 8
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int8 yypgoto[] =
{
-11, -11, -2
-12, -12, -3, -12, -12
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int8 yydefgoto[] =
{
-1, 7, 8
-1, 8, 9, 35, 36
};
/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
......@@ -569,74 +577,74 @@ static const yytype_int8 yydefgoto[] =
number is the opposite. If YYTABLE_NINF, syntax error. */
static const yytype_uint8 yytable[] =
{
9, 10, 11, 12, 13, 26, 27, 28, 29, 30,
31, 32, 14, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 1,
30, 31, 32, 33, 28, 29, 30, 31, 32, 0,
0, 0, 0, 2, 3, 0, 0, 0, 0, 4,
5, 6, 15, 16, 17, 18, 19, 20, 21, 22,
10, 11, 12, 13, 14, 27, 28, 29, 30, 31,
32, 33, 31, 32, 33, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 0, 53, 54, 1, 2, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 3,
4, 15, 0, 0, 0, 5, 6, 7, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 0, 0, 0, 0,
34, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32
33, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 29, 30, 31, 32, 33
};
static const yytype_int8 yycheck[] =
{
2, 3, 4, 5, 6, 15, 16, 17, 18, 19,
20, 21, 0, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 3,
19, 20, 21, 26, 17, 18, 19, 20, 21, -1,
-1, -1, -1, 17, 18, -1, -1, -1, -1, 23,
24, 25, 4, 5, 6, 7, 8, 9, 10, 11,
3, 4, 5, 6, 7, 16, 17, 18, 19, 20,
21, 22, 20, 21, 22, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, -1, 35, 36, 3, 4, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 18,
19, 0, -1, -1, -1, 24, 25, 26, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, -1, -1, -1, -1,
27, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21
22, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 18, 19, 20, 21, 22
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
0, 3, 17, 18, 23, 24, 25, 28, 29, 29,
29, 29, 29, 29, 0, 4, 5, 6, 7, 8,
0, 3, 4, 18, 19, 24, 25, 26, 29, 30,
30, 30, 30, 30, 30, 0, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 26, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29
19, 20, 21, 22, 27, 31, 32, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30
};
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
0, 27, 28, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29
0, 28, 29, 30, 30, 31, 30, 32, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 30, 30, 30, 30, 30
};
/* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
static const yytype_uint8 yyr2[] =
{
0, 2, 1, 1, 3, 3, 3, 3, 3, 3,
0, 2, 1, 1, 1, 0, 4, 0, 4, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 2, 2, 2, 2, 3
3, 3, 3, 3, 3, 2, 2, 2, 2, 3
};
......@@ -1322,7 +1330,6 @@ yyreduce:
{
*(context->result) = static_cast<int>((yyvsp[0]));
YYACCEPT;
}
break;
......@@ -1330,7 +1337,10 @@ yyreduce:
case 4:
{
(yyval) = (yyvsp[-2]) || (yyvsp[0]);
if (!context->isIgnoringErrors())
{
YYABORT;
}
}
break;
......@@ -1338,7 +1348,15 @@ yyreduce:
case 5:
{
(yyval) = (yyvsp[-2]) && (yyvsp[0]);
if ((yyvsp[-1]) != 0)
{
// Ignore errors in the short-circuited part of the expression.
// ESSL3.00 section 3.4:
// If an operand is not evaluated, the presence of undefined identifiers
// in the operand will not cause an error.
// Unevaluated division by zero should not cause an error either.
context->startIgnoreErrors();
}
}
break;
......@@ -1346,7 +1364,15 @@ yyreduce:
case 6:
{
(yyval) = (yyvsp[-2]) | (yyvsp[0]);
if ((yyvsp[-3]) != 0)
{
context->endIgnoreErrors();
(yyval) = static_cast<YYSTYPE>(1);
}
else
{
(yyval) = (yyvsp[-3]) || (yyvsp[0]);
}
}
break;
......@@ -1354,7 +1380,15 @@ yyreduce:
case 7:
{
(yyval) = (yyvsp[-2]) ^ (yyvsp[0]);
if ((yyvsp[-1]) == 0)
{
// Ignore errors in the short-circuited part of the expression.
// ESSL3.00 section 3.4:
// If an operand is not evaluated, the presence of undefined identifiers
// in the operand will not cause an error.
// Unevaluated division by zero should not cause an error either.
context->startIgnoreErrors();
}
}
break;
......@@ -1362,7 +1396,15 @@ yyreduce:
case 8:
{
(yyval) = (yyvsp[-2]) & (yyvsp[0]);
if ((yyvsp[-3]) == 0)
{
context->endIgnoreErrors();
(yyval) = static_cast<YYSTYPE>(0);
}
else
{
(yyval) = (yyvsp[-3]) && (yyvsp[0]);
}
}
break;
......@@ -1370,7 +1412,7 @@ yyreduce:
case 9:
{
(yyval) = (yyvsp[-2]) != (yyvsp[0]);
(yyval) = (yyvsp[-2]) | (yyvsp[0]);
}
break;
......@@ -1378,7 +1420,7 @@ yyreduce:
case 10:
{
(yyval) = (yyvsp[-2]) == (yyvsp[0]);
(yyval) = (yyvsp[-2]) ^ (yyvsp[0]);
}
break;
......@@ -1386,7 +1428,7 @@ yyreduce:
case 11:
{
(yyval) = (yyvsp[-2]) >= (yyvsp[0]);
(yyval) = (yyvsp[-2]) & (yyvsp[0]);
}
break;
......@@ -1394,7 +1436,7 @@ yyreduce:
case 12:
{
(yyval) = (yyvsp[-2]) <= (yyvsp[0]);
(yyval) = (yyvsp[-2]) != (yyvsp[0]);
}
break;
......@@ -1402,7 +1444,7 @@ yyreduce:
case 13:
{
(yyval) = (yyvsp[-2]) > (yyvsp[0]);
(yyval) = (yyvsp[-2]) == (yyvsp[0]);
}
break;
......@@ -1410,7 +1452,7 @@ yyreduce:
case 14:
{
(yyval) = (yyvsp[-2]) < (yyvsp[0]);
(yyval) = (yyvsp[-2]) >= (yyvsp[0]);
}
break;
......@@ -1418,7 +1460,7 @@ yyreduce:
case 15:
{
(yyval) = (yyvsp[-2]) >> (yyvsp[0]);
(yyval) = (yyvsp[-2]) <= (yyvsp[0]);
}
break;
......@@ -1426,7 +1468,7 @@ yyreduce:
case 16:
{
(yyval) = (yyvsp[-2]) << (yyvsp[0]);
(yyval) = (yyvsp[-2]) > (yyvsp[0]);
}
break;
......@@ -1434,7 +1476,7 @@ yyreduce:
case 17:
{
(yyval) = (yyvsp[-2]) - (yyvsp[0]);
(yyval) = (yyvsp[-2]) < (yyvsp[0]);
}
break;
......@@ -1442,7 +1484,7 @@ yyreduce:
case 18:
{
(yyval) = (yyvsp[-2]) + (yyvsp[0]);
(yyval) = (yyvsp[-2]) >> (yyvsp[0]);
}
break;
......@@ -1450,40 +1492,84 @@ yyreduce:
case 19:
{
if ((yyvsp[0]) == 0) {
std::ostringstream stream;
stream << (yyvsp[-2]) << " % " << (yyvsp[0]);
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
} else {
(yyval) = (yyvsp[-2]) << (yyvsp[0]);
}
break;
case 20:
{
(yyval) = (yyvsp[-2]) - (yyvsp[0]);
}
break;
case 21:
{
(yyval) = (yyvsp[-2]) + (yyvsp[0]);
}
break;
case 22:
{
if ((yyvsp[0]) == 0)
{
if (context->isIgnoringErrors())
{
(yyval) = static_cast<YYSTYPE>(0);
}
else
{
std::ostringstream stream;
stream << (yyvsp[-2]) << " % " << (yyvsp[0]);
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
}
}
else
{
(yyval) = (yyvsp[-2]) % (yyvsp[0]);
}
}
break;
case 20:
case 23:
{
if ((yyvsp[0]) == 0) {
std::ostringstream stream;
stream << (yyvsp[-2]) << " / " << (yyvsp[0]);
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
} else {
if ((yyvsp[0]) == 0)
{
if (context->isIgnoringErrors())
{
(yyval) = static_cast<YYSTYPE>(0);
}
else
{
std::ostringstream stream;
stream << (yyvsp[-2]) << " / " << (yyvsp[0]);
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
}
}
else
{
(yyval) = (yyvsp[-2]) / (yyvsp[0]);
}
}
break;
case 21:
case 24:
{
(yyval) = (yyvsp[-2]) * (yyvsp[0]);
......@@ -1491,7 +1577,7 @@ yyreduce:
break;
case 22:
case 25:
{
(yyval) = ! (yyvsp[0]);
......@@ -1499,7 +1585,7 @@ yyreduce:
break;
case 23:
case 26:
{
(yyval) = ~ (yyvsp[0]);
......@@ -1507,7 +1593,7 @@ yyreduce:
break;
case 24:
case 27:
{
(yyval) = - (yyvsp[0]);
......@@ -1515,7 +1601,7 @@ yyreduce:
break;
case 25:
case 28:
{
(yyval) = + (yyvsp[0]);
......@@ -1523,7 +1609,7 @@ yyreduce:
break;
case 26:
case 29:
{
(yyval) = (yyvsp[-1]);
......@@ -1781,6 +1867,15 @@ int yylex(YYSTYPE *lvalp, Context *context)
type = TOK_CONST_INT;
break;
}
case pp::Token::IDENTIFIER:
if (!context->isIgnoringErrors())
{
context->diagnostics->report(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->text);
}
*lvalp = static_cast<YYSTYPE>(-1);
type = TOK_IDENTIFIER;
break;
case pp::Token::OP_OR:
type = TOK_OP_OR;
break;
......@@ -1855,6 +1950,7 @@ bool ExpressionParser::parse(Token *token, int *result)
context.lexer = mLexer;
context.token = token;
context.result = result;
context.ignoreErrors = 0;
int ret = yyparse(&context);
switch (ret)
{
......
......@@ -64,6 +64,13 @@ struct Context
pp::Lexer* lexer;
pp::Token* token;
int* result;
void startIgnoreErrors() { ++ignoreErrors; }
void endIgnoreErrors() { --ignoreErrors; }
bool isIgnoringErrors() { return ignoreErrors > 0; }
int ignoreErrors;
};
} // namespace
%}
......@@ -79,6 +86,7 @@ static void yyerror(Context* context, const char* reason);
%}
%token TOK_CONST_INT
%token TOK_IDENTIFIER
%left TOK_OP_OR
%left TOK_OP_AND
%left '|'
......@@ -96,17 +104,58 @@ static void yyerror(Context* context, const char* reason);
input
: expression {
*(context->result) = static_cast<int>($1);
YYACCEPT;
}
;
expression
: TOK_CONST_INT
| expression TOK_OP_OR expression {
$$ = $1 || $3;
| TOK_IDENTIFIER {
if (!context->isIgnoringErrors())
{
YYABORT;
}
}
| expression TOK_OP_AND expression {
$$ = $1 && $3;
| expression TOK_OP_OR {
if ($1 != 0)
{
// Ignore errors in the short-circuited part of the expression.
// ESSL3.00 section 3.4:
// If an operand is not evaluated, the presence of undefined identifiers
// in the operand will not cause an error.
// Unevaluated division by zero should not cause an error either.
context->startIgnoreErrors();
}
} expression {
if ($1 != 0)
{
context->endIgnoreErrors();
$$ = static_cast<YYSTYPE>(1);
}
else
{
$$ = $1 || $4;
}
}
| expression TOK_OP_AND {
if ($1 == 0)
{
// Ignore errors in the short-circuited part of the expression.
// ESSL3.00 section 3.4:
// If an operand is not evaluated, the presence of undefined identifiers
// in the operand will not cause an error.
// Unevaluated division by zero should not cause an error either.
context->startIgnoreErrors();
}
} expression {
if ($1 == 0)
{
context->endIgnoreErrors();
$$ = static_cast<YYSTYPE>(0);
}
else
{
$$ = $1 && $4;
}
}
| expression '|' expression {
$$ = $1 | $3;
......@@ -148,28 +197,48 @@ expression
$$ = $1 + $3;
}
| expression '%' expression {
if ($3 == 0) {
std::ostringstream stream;
stream << $1 << " % " << $3;
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
} else {
if ($3 == 0)
{
if (context->isIgnoringErrors())
{
$$ = static_cast<YYSTYPE>(0);
}
else
{
std::ostringstream stream;
stream << $1 << " % " << $3;
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
}
}
else
{
$$ = $1 % $3;
}
}
| expression '/' expression {
if ($3 == 0) {
std::ostringstream stream;
stream << $1 << " / " << $3;
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
} else {
if ($3 == 0)
{
if (context->isIgnoringErrors())
{
$$ = static_cast<YYSTYPE>(0);
}
else
{
std::ostringstream stream;
stream << $1 << " / " << $3;
std::string text = stream.str();
context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
context->token->location,
text.c_str());
YYABORT;
}
}
else
{
$$ = $1 / $3;
}
}
......@@ -213,6 +282,15 @@ int yylex(YYSTYPE *lvalp, Context *context)
type = TOK_CONST_INT;
break;
}
case pp::Token::IDENTIFIER:
if (!context->isIgnoringErrors())
{
context->diagnostics->report(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->text);
}
*lvalp = static_cast<YYSTYPE>(-1);
type = TOK_IDENTIFIER;
break;
case pp::Token::OP_OR:
type = TOK_OP_OR;
break;
......@@ -287,6 +365,7 @@ bool ExpressionParser::parse(Token *token, int *result)
context.lexer = mLexer;
context.token = token;
context.result = result;
context.ignoreErrors = 0;
int ret = yyparse(&context);
switch (ret)
{
......
......@@ -697,10 +697,6 @@ TEST_F(IfTest, UndefinedMacro)
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 1),
"syntax error"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 1),
"UNDEFINED"));
......@@ -833,3 +829,65 @@ TEST_F(IfTest, UnterminatedIfdef)
mPreprocessor.lex(&token);
}
// The preprocessor only allows one expression to follow an #if directive.
// Supplying two integer expressions should be an error.
TEST_F(IfTest, ExtraIntExpression)
{
const char *str =
"#if 1 1\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 1), "syntax error"));
pp::Token token;
mPreprocessor.lex(&token);
}
// The preprocessor only allows one expression to follow an #if directive.
// Supplying two expressions where one uses a preprocessor define should be an error.
TEST_F(IfTest, ExtraIdentifierExpression)
{
const char *str =
"#define one 1\n"
"#if 1 one\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::PP_INVALID_EXPRESSION,
pp::SourceLocation(0, 2), "syntax error"));
pp::Token token;
mPreprocessor.lex(&token);
}
// Divide by zero that's not evaluated because of short-circuiting should not cause an error.
TEST_F(IfTest, ShortCircuitedDivideByZero)
{
const char *str =
"#if 1 || (2 / 0)\n"
"pass\n"
"#endif\n";
const char *expected =
"\n"
"pass\n"
"\n";
preprocess(str, expected);
}
// Undefined identifier that's not evaluated because of short-circuiting should not cause an error.
TEST_F(IfTest, ShortCircuitedUndefined)
{
const char *str =
"#if 1 || UNDEFINED\n"
"pass\n"
"#endif\n";
const char *expected =
"\n"
"pass\n"
"\n";
preprocess(str, expected);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment