3 * Copyright © 2010 Intel Corporation
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
30 #include "glcpp-parse.h"
32 /* Flex annoyingly generates some functions without making them
33 * static. Let's declare them here. */
34 int glcpp_get_column (yyscan_t yyscanner);
35 void glcpp_set_column (int column_no , yyscan_t yyscanner);
38 #define YY_NO_UNISTD_H
43 #define YY_USER_ACTION \
45 if (parser->has_new_line_number) \
46 yylineno = parser->new_line_number; \
47 if (parser->has_new_source_number) \
48 yylloc->source = parser->new_source_number; \
49 yylloc->first_column = yycolumn + 1; \
50 yylloc->first_line = yylloc->last_line = yylineno; \
52 yylloc->last_column = yycolumn + 1; \
53 parser->has_new_line_number = 0; \
54 parser->has_new_source_number = 0; \
57 #define YY_USER_INIT \
64 #define RETURN_TOKEN(token) \
66 if (token == NEWLINE) \
67 parser->last_token_was_newline = 1; \
69 parser->last_token_was_newline = 0; \
75 %option bison-bridge bison-locations reentrant noyywrap
76 %option extra-type="glcpp_parser_t *"
77 %option prefix="glcpp_"
79 %option never-interactive
81 %x DONE COMMENT UNREACHABLE SKIP DEFINE NEWLINE_CATCHUP
87 HASH ^{HSPACE}*#{HSPACE}*
88 IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]*
89 PP_NUMBER [.]?[0-9]([._a-zA-Z0-9]|[eEpP][-+])*
90 PUNCTUATION [][(){}.&*~!/%<>^|;,=+-]
92 /* The OTHER class is simply a catch-all for things that the CPP
93 parser just doesn't care about. Since flex regular expressions that
94 match longer strings take priority over those matching shorter
95 strings, we have to be careful to avoid OTHER matching and hiding
96 something that CPP does care about. So we simply exclude all
97 characters that appear in any other expressions. */
99 OTHER [^][_#[:space:]#a-zA-Z0-9(){}.&*~!/%<>^|;,=+-]
102 DECIMAL_INTEGER [1-9][0-9]*[uU]?
103 OCTAL_INTEGER 0[0-7]*[uU]?
104 HEXADECIMAL_INTEGER 0[xX][0-9a-fA-F]+[uU]?
108 glcpp_parser_t *parser = yyextra;
110 /* When we lex a multi-line comment, we replace it (as
111 * specified) with a single space. But if the comment spanned
112 * multiple lines, then subsequent parsing stages will not
113 * count correct line numbers. To avoid this problem we keep
114 * track of all newlines that were commented out by a
115 * multi-line comment, and we emit a NEWLINE token for each at
116 * the next legal opportunity, (which is when the lexer would
117 * be emitting a NEWLINE token anyway).
119 if (YY_START == NEWLINE_CATCHUP) {
120 if (parser->commented_newlines)
121 parser->commented_newlines--;
122 if (parser->commented_newlines == 0)
124 RETURN_TOKEN (NEWLINE);
127 /* The handling of the SKIP vs INITIAL start states requires
128 * some special handling. Typically, a lexer would change
129 * start states with statements like "BEGIN SKIP" within the
130 * lexer rules. We can't get away with that here, since we
131 * need the parser to actually evaluate expressions for
132 * directives like "#if".
134 * So, here, in code that will be executed on every call to
135 * the lexer,and before any rules, we examine the skip_stack
136 * as set by the parser to know whether to change from INITIAL
137 * to SKIP or from SKIP back to INITIAL.
139 * Three cases cause us to switch out of the SKIP state and
140 * back to the INITIAL state:
142 * 1. The top of the skip_stack is of type SKIP_NO_SKIP
143 * This means we're still evaluating some #if
144 * hierarchy, but we're on a branch of it where
145 * content should not be skipped (such as "#if 1" or
148 * 2. The skip_stack is NULL meaning that we've reached
151 * 3. The lexing_directive bit is set. This indicates that we are
152 * lexing a pre-processor directive, (such as #if, #elif, or
153 * #else). For the #if and #elif directives we always need to
154 * parse the conditions, (even if otherwise within an #if
155 * 0). And for #else, we want to be able to generate an error
156 * if any garbage follows #else.
158 if (YY_START == INITIAL || YY_START == SKIP) {
159 if (parser->lexing_directive ||
160 parser->skip_stack == NULL ||
161 parser->skip_stack->type == SKIP_NO_SKIP)
169 /* Single-line comments */
173 /* Multi-line comments */
174 <DEFINE,INITIAL>"/*" { yy_push_state(COMMENT, yyscanner); }
176 <COMMENT>[^*\n]*\n { yylineno++; yycolumn = 0; parser->commented_newlines++; }
177 <COMMENT>"*"+[^*/\n]*
178 <COMMENT>"*"+[^*/\n]*\n { yylineno++; yycolumn = 0; parser->commented_newlines++; }
180 yy_pop_state(yyscanner);
181 if (yyextra->space_tokens)
182 RETURN_TOKEN (SPACE);
185 {HASH}version{HSPACE}+ {
186 yylval->str = ralloc_strdup (yyextra, yytext);
187 yyextra->space_tokens = 0;
188 RETURN_TOKEN (HASH_VERSION);
191 /* glcpp doesn't handle #extension, #version, or #pragma directives.
192 * Simply pass them through to the main compiler's lexer/parser. */
193 {HASH}(extension|pragma)[^\n]* {
194 yylval->str = ralloc_strdup (yyextra, yytext);
197 RETURN_TOKEN (OTHER);
200 {HASH}line{HSPACE}+ {
201 RETURN_TOKEN (HASH_LINE);
206 yyextra->lexing_directive = 1;
207 yyextra->space_tokens = 0;
208 RETURN_TOKEN (HASH_IFDEF);
212 yyextra->lexing_directive = 1;
213 yyextra->space_tokens = 0;
214 RETURN_TOKEN (HASH_IFNDEF);
217 {HASH}if/[^_a-zA-Z0-9] {
218 yyextra->lexing_directive = 1;
219 yyextra->space_tokens = 0;
220 RETURN_TOKEN (HASH_IF);
223 {HASH}elif/[^_a-zA-Z0-9] {
224 yyextra->lexing_directive = 1;
225 yyextra->space_tokens = 0;
226 RETURN_TOKEN (HASH_ELIF);
230 yyextra->space_tokens = 0;
231 RETURN_TOKEN (HASH_ELSE);
235 yyextra->space_tokens = 0;
236 RETURN_TOKEN (HASH_ENDIF);
245 for (p = yytext; !isalpha(p[0]); p++); /* skip " # " */
246 p += 5; /* skip "error" */
247 glcpp_error(yylloc, yyextra, "#error%s", p);
250 /* After we see a "#define" we enter the <DEFINE> start state
251 * for the lexer. Within <DEFINE> we are looking for the first
252 * identifier and specifically checking whether the identifier
253 * is followed by a '(' or not, (to lex either a
254 * FUNC_IDENTIFIER or an OBJ_IDENITIFIER token).
256 * While in the <DEFINE> state we also need to explicitly
257 * handle a few other things that may appear before the
260 * * Comments, (handled above with the main support for
263 * * Whitespace (simply ignored)
265 * * Anything else, (not an identifier, not a comment,
266 * and not whitespace). This will generate an error.
268 {HASH}define{HSPACE}+ {
269 yyextra->space_tokens = 0;
270 yy_push_state(DEFINE, yyscanner);
271 RETURN_TOKEN (HASH_DEFINE);
274 /* An identifier immediately followed by '(' */
275 <DEFINE>{IDENTIFIER}/"(" {
276 yy_pop_state(yyscanner);
277 yylval->str = ralloc_strdup (yyextra, yytext);
278 RETURN_TOKEN (FUNC_IDENTIFIER);
281 /* An identifier not immediately followed by '(' */
282 <DEFINE>{IDENTIFIER} {
283 yy_pop_state(yyscanner);
284 yylval->str = ralloc_strdup (yyextra, yytext);
285 RETURN_TOKEN (OBJ_IDENTIFIER);
290 /* Just ignore it. Nothing to do here. */
293 /* '/' not followed by '*', so not a comment. This is an error. */
294 <DEFINE>[/][^*]{NONSPACE}* {
296 glcpp_error(yylloc, yyextra, "#define followed by a non-identifier: %s", yytext);
297 RETURN_TOKEN (INTEGER_STRING);
300 /* A character that can't start an identifier, comment, or
301 * space. This is an error. */
302 <DEFINE>[^_a-zA-Z/[:space:]]{NONSPACE}* {
304 glcpp_error(yylloc, yyextra, "#define followed by a non-identifier: %s", yytext);
305 RETURN_TOKEN (INTEGER_STRING);
309 yyextra->space_tokens = 0;
310 RETURN_TOKEN (HASH_UNDEF);
314 yyextra->space_tokens = 0;
319 yylval->str = ralloc_strdup (yyextra, yytext);
320 RETURN_TOKEN (INTEGER_STRING);
324 yylval->str = ralloc_strdup (yyextra, yytext);
325 RETURN_TOKEN (INTEGER_STRING);
328 {HEXADECIMAL_INTEGER} {
329 yylval->str = ralloc_strdup (yyextra, yytext);
330 RETURN_TOKEN (INTEGER_STRING);
334 RETURN_TOKEN (LEFT_SHIFT);
338 RETURN_TOKEN (RIGHT_SHIFT);
342 RETURN_TOKEN (LESS_OR_EQUAL);
346 RETURN_TOKEN (GREATER_OR_EQUAL);
350 RETURN_TOKEN (EQUAL);
354 RETURN_TOKEN (NOT_EQUAL);
367 glcpp_error(yylloc, yyextra, "Token pasting (##) is illegal in GLES");
368 RETURN_TOKEN (PASTE);
372 RETURN_TOKEN (DEFINED);
376 yylval->str = ralloc_strdup (yyextra, yytext);
377 RETURN_TOKEN (IDENTIFIER);
381 yylval->str = ralloc_strdup (yyextra, yytext);
382 RETURN_TOKEN (OTHER);
386 RETURN_TOKEN (yytext[0]);
390 yylval->str = ralloc_strdup (yyextra, yytext);
391 RETURN_TOKEN (OTHER);
395 if (yyextra->space_tokens) {
396 RETURN_TOKEN (SPACE);
401 if (parser->commented_newlines) {
402 BEGIN NEWLINE_CATCHUP;
404 yyextra->space_tokens = 1;
405 yyextra->lexing_directive = 0;
408 RETURN_TOKEN (NEWLINE);
411 <INITIAL,COMMENT,DEFINE><<EOF>> {
412 if (YY_START == COMMENT)
413 glcpp_error(yylloc, yyextra, "Unterminated comment");
414 if (YY_START == DEFINE)
415 glcpp_error(yylloc, yyextra, "#define without macro name");
416 BEGIN DONE; /* Don't keep matching this rule forever. */
417 yyextra->lexing_directive = 0;
418 if (! parser->last_token_was_newline)
419 RETURN_TOKEN (NEWLINE);
422 /* We don't actually use the UNREACHABLE start condition. We
423 only have this action here so that we can pretend to call some
424 generated functions, (to avoid "defined but not used"
428 yy_top_state(yyextra);
434 glcpp_lex_set_source_string(glcpp_parser_t *parser, const char *shader)
436 yy_scan_string(shader, parser->scanner);