aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/glsl/glcpp
diff options
context:
space:
mode:
authormarha <marha@users.sourceforge.net>2014-08-11 21:14:48 +0200
committermarha <marha@users.sourceforge.net>2014-08-11 21:14:48 +0200
commitfdbedba4d50e1b28b0249c83ba11c029f096e400 (patch)
treea80ccb6275fd99644c79c67856e9c1bf1b323d62 /mesalib/src/glsl/glcpp
parentb33b8d8ae86876b50df96881b96074b3fe177cce (diff)
downloadvcxsrv-fdbedba4d50e1b28b0249c83ba11c029f096e400.tar.gz
vcxsrv-fdbedba4d50e1b28b0249c83ba11c029f096e400.tar.bz2
vcxsrv-fdbedba4d50e1b28b0249c83ba11c029f096e400.zip
fontconfig libX11 libxcb libxcb/xcb-proto mesa xserver xkeyboard-config git update 11 Aug 2014
xserver commit 3714f5401690b288045090c4bcd9cb01c6e4860e libxcb commit 966fba6ba4838949d0727dfa45eeb9392d1f85d9 libxcb/xcb-proto commit 4b384d2a015c50d0e93dcacda4b8260a3fd37640 xkeyboard-config commit 651a00ab656a1754b9183a383970a735209bbb50 libX11 commit 368a6401c6a3275d3497fec38a3dcbc38cd9df60 libXdmcp commit fe8eab93e9bcdbe8bb8052434bb5e676e3a0ee8f libXext commit efdcbb7634501e1117d422636a0a75d7ea84b16b libfontenc commit 0037a42107b952c9d903719615747e760e4e7247 libXinerama commit edd95182b26eb5d576d4878c559e0f17dddaa909 libXau commit 1e4635be11154dd8262f37b379511bd627defa2a xkbcomp commit d4e02a09258063c6d024c3ccd42d6b22212e6e18 pixman commit 6d2cf40166d81bfc63108504c8022dc4fec37ff5 xextproto commit 66afec3f49e8eb0d4c2e9af7088fc3116d4bafd7 randrproto commit a4a6694c059d74247c16527eef4a0ec9f56bbef6 glproto commit f84853d97d5749308992412a215fa518b6536eb3 mkfontscale commit 47908fd7a0d061fdcd21e3498da4e223ca9136d9 xwininfo commit 017b3736489985999d8dcf4d9e473e1fd6dd3647 libXft commit 214f9b5306d833e2787c75fe41dfdc9228fcb738 libXmu commit 22d9c590901e121936f50dee97dc60c4f7defb63 libxtrans commit a57a7f62242e1ea972b81414741729bf3dbae0a4 fontconfig commit 841753a93f0e5698663b7931b8456e7b96259f54 mesa commit f24be7340162c6a831b392d46d6637e9656e7a8a
Diffstat (limited to 'mesalib/src/glsl/glcpp')
-rw-r--r--mesalib/src/glsl/glcpp/glcpp-lex.l449
-rw-r--r--mesalib/src/glsl/glcpp/glcpp-parse.y426
-rw-r--r--mesalib/src/glsl/glcpp/glcpp.c6
-rw-r--r--mesalib/src/glsl/glcpp/glcpp.h6
-rw-r--r--mesalib/src/glsl/glcpp/pp.c96
5 files changed, 732 insertions, 251 deletions
diff --git a/mesalib/src/glsl/glcpp/glcpp-lex.l b/mesalib/src/glsl/glcpp/glcpp-lex.l
index a1a8e76af..98d500ec0 100644
--- a/mesalib/src/glsl/glcpp/glcpp-lex.l
+++ b/mesalib/src/glsl/glcpp/glcpp-lex.l
@@ -52,14 +52,107 @@ void glcpp_set_column (int column_no , yyscan_t yyscanner);
yylloc->last_column = yycolumn + 1; \
parser->has_new_line_number = 0; \
parser->has_new_source_number = 0; \
- } while(0);
+ } while(0);
#define YY_USER_INIT \
do { \
yylineno = 1; \
- yycolumn = 1; \
+ yycolumn = 0; \
yylloc->source = 0; \
} while(0)
+
+/* It's ugly to have macros that have return statements inside of
+ * them, but flex-based lexer generation is all built around the
+ * return statement.
+ *
+ * To mitigate the ugliness, we defer as much of the logic as possible
+ * to an actual function, not a macro (see
+ * glcpplex_update_state_per_token) and we make the word RETURN
+ * prominent in all of the macros which may return.
+ *
+ * The most-commonly-used macro is RETURN_TOKEN which will perform all
+ * necessary state updates based on the provided token,, then
+ * conditionally return the token. It will not return a token if the
+ * parser is currently skipping tokens, (such as within #if
+ * 0...#else).
+ *
+ * The RETURN_TOKEN_NEVER_SKIP macro is a lower-level variant that
+ * makes the token returning unconditional. This is needed for things
+ * like #if and the tokens of its condition, (since these must be
+ * evaluated by the parser even when otherwise skipping).
+ *
+ * Finally, RETURN_STRING_TOKEN is a simple convenience wrapper on top
+ * of RETURN_TOKEN that performs a string copy of yytext before the
+ * return.
+ */
+#define RETURN_TOKEN_NEVER_SKIP(token) \
+ do { \
+ if (glcpp_lex_update_state_per_token (parser, token)) \
+ return token; \
+ } while (0)
+
+#define RETURN_TOKEN(token) \
+ do { \
+ if (! parser->skipping) { \
+ RETURN_TOKEN_NEVER_SKIP(token); \
+ } \
+ } while(0)
+
+#define RETURN_STRING_TOKEN(token) \
+ do { \
+ if (! parser->skipping) { \
+ yylval->str = ralloc_strdup (yyextra, yytext); \
+ RETURN_TOKEN_NEVER_SKIP (token); \
+ } \
+ } while(0)
+
+
+/* Update all state necessary for each token being returned.
+ *
+ * Here we'll be tracking newlines and spaces so that the lexer can
+ * alter its behavior as necessary, (for example, '#' has special
+ * significance if it is the first non-whitespace, non-comment token
+ * in a line, but does not otherwise).
+ *
+ * NOTE: If this function returns FALSE, then no token should be
+ * returned at all. This is used to suprress duplicate SPACE tokens.
+ */
+static int
+glcpp_lex_update_state_per_token (glcpp_parser_t *parser, int token)
+{
+ /* After the first non-space token in a line, we won't
+ * allow any '#' to introduce a directive. */
+ if (token == NEWLINE) {
+ parser->first_non_space_token_this_line = 1;
+ } else if (token != SPACE) {
+ parser->first_non_space_token_this_line = 0;
+ }
+
+ /* Track newlines just to know whether a newline needs
+ * to be inserted if end-of-file comes early. */
+ if (token == NEWLINE) {
+ parser->last_token_was_newline = 1;
+ } else {
+ parser->last_token_was_newline = 0;
+ }
+
+ /* Track spaces to avoid emitting multiple SPACE
+ * tokens in a row. */
+ if (token == SPACE) {
+ if (! parser->last_token_was_space) {
+ parser->last_token_was_space = 1;
+ return 1;
+ } else {
+ parser->last_token_was_space = 1;
+ return 0;
+ }
+ } else {
+ parser->last_token_was_space = 0;
+ return 1;
+ }
+}
+
+
%}
%option bison-bridge bison-locations reentrant noyywrap
@@ -67,14 +160,19 @@ void glcpp_set_column (int column_no , yyscan_t yyscanner);
%option prefix="glcpp_"
%option stack
%option never-interactive
+%option warn nodefault
+
+ /* Note: When adding any start conditions to this list, you must also
+ * update the "Internal compiler error" catch-all rule near the end of
+ * this file. */
-%x DONE COMMENT UNREACHABLE SKIP DEFINE NEWLINE_CATCHUP
+%x COMMENT DEFINE DONE HASH NEWLINE_CATCHUP UNREACHABLE
SPACE [[:space:]]
NONSPACE [^[:space:]]
-NEWLINE [\n]
HSPACE [ \t]
-HASH ^{HSPACE}*#{HSPACE}*
+HASH #
+NEWLINE (\r\n|\n\r|\r|\n)
IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]*
PP_NUMBER [.]?[0-9]([._a-zA-Z0-9]|[eEpP][-+])*
PUNCTUATION [][(){}.&*~!/%<>^|;,=+-]
@@ -111,270 +209,357 @@ HEXADECIMAL_INTEGER 0[xX][0-9a-fA-F]+[uU]?
parser->commented_newlines--;
if (parser->commented_newlines == 0)
BEGIN INITIAL;
- return NEWLINE;
+ RETURN_TOKEN_NEVER_SKIP (NEWLINE);
}
- /* The handling of the SKIP vs INITIAL start states requires
- * some special handling. Typically, a lexer would change
- * start states with statements like "BEGIN SKIP" within the
- * lexer rules. We can't get away with that here, since we
- * need the parser to actually evaluate expressions for
- * directives like "#if".
+ /* Set up the parser->skipping bit here before doing any lexing.
+ *
+ * This bit controls whether tokens are skipped, (as implemented by
+ * RETURN_TOKEN), such as between "#if 0" and "#endif".
*
- * So, here, in code that will be executed on every call to
- * the lexer,and before any rules, we examine the skip_stack
- * as set by the parser to know whether to change from INITIAL
- * to SKIP or from SKIP back to INITIAL.
+ * The parser maintains a skip_stack indicating whether we should be
+ * skipping, (and nested levels of #if/#ifdef/#ifndef/#endif) will
+ * push and pop items from the stack.
*
- * Three cases cause us to switch out of the SKIP state and
- * back to the INITIAL state:
+ * Here are the rules for determining whether we are skipping:
*
- * 1. The top of the skip_stack is of type SKIP_NO_SKIP
- * This means we're still evaluating some #if
- * hierarchy, but we're on a branch of it where
- * content should not be skipped (such as "#if 1" or
- * "#else" or so).
+ * 1. If the skip stack is NULL, we are outside of all #if blocks
+ * and we are not skipping.
*
- * 2. The skip_stack is NULL meaning that we've reached
- * the last #endif.
+ * 2. If the skip stack is non-NULL, the type of the top node in
+ * the stack determines whether to skip. A type of
+ * SKIP_NO_SKIP is used for blocks wheere we are emitting
+ * tokens, (such as between #if 1 and #endif, or after the
+ * #else of an #if 0, etc.).
*
- * 3. The lexing_directive bit is set. This indicates that we are
- * lexing a pre-processor directive, (such as #if, #elif, or
- * #else). For the #if and #elif directives we always need to
- * parse the conditions, (even if otherwise within an #if
- * 0). And for #else, we want to be able to generate an error
- * if any garbage follows #else.
+ * 3. The lexing_directive bit overrides the skip stack. This bit
+ * is set when we are actively lexing the expression for a
+ * pre-processor condition, (such as #if, #elif, or #else). In
+ * this case, even if otherwise skipping, we need to emit the
+ * tokens for this condition so that the parser can evaluate
+ * the expression. (For, #else, there's no expression, but we
+ * emit tokens so the parser can generate a nice error message
+ * if there are any tokens here).
*/
- if (YY_START == INITIAL || YY_START == SKIP) {
- if (parser->lexing_directive ||
- parser->skip_stack == NULL ||
- parser->skip_stack->type == SKIP_NO_SKIP)
- {
- BEGIN INITIAL;
- } else {
- BEGIN SKIP;
- }
+ if (parser->skip_stack &&
+ parser->skip_stack->type != SKIP_NO_SKIP &&
+ ! parser->lexing_directive)
+ {
+ parser->skipping = 1;
+ } else {
+ parser->skipping = 0;
}
/* Single-line comments */
-"//"[^\n]* {
+<INITIAL,DEFINE,HASH>"//"[^\r\n]* {
}
/* Multi-line comments */
-"/*" { yy_push_state(COMMENT, yyscanner); }
-<COMMENT>[^*\n]*
-<COMMENT>[^*\n]*\n { yylineno++; yycolumn = 0; parser->commented_newlines++; }
-<COMMENT>"*"+[^*/\n]*
-<COMMENT>"*"+[^*/\n]*\n { yylineno++; yycolumn = 0; parser->commented_newlines++; }
+<INITIAL,DEFINE,HASH>"/*" { yy_push_state(COMMENT, yyscanner); }
+<COMMENT>[^*\r\n]*
+<COMMENT>[^*\r\n]*{NEWLINE} { yylineno++; yycolumn = 0; parser->commented_newlines++; }
+<COMMENT>"*"+[^*/\r\n]*
+<COMMENT>"*"+[^*/\r\n]*{NEWLINE} { yylineno++; yycolumn = 0; parser->commented_newlines++; }
<COMMENT>"*"+"/" {
yy_pop_state(yyscanner);
- if (yyextra->space_tokens)
- return SPACE;
+ /* In the <HASH> start condition, we don't want any SPACE token. */
+ if (yyextra->space_tokens && YY_START != HASH)
+ RETURN_TOKEN (SPACE);
+}
+
+{HASH} {
+
+ /* If the '#' is the first non-whitespace, non-comment token on this
+ * line, then it introduces a directive, switch to the <HASH> start
+ * condition.
+ *
+ * Otherwise, this is just punctuation, so return the HASH_TOKEN
+ * token. */
+ if (parser->first_non_space_token_this_line) {
+ BEGIN HASH;
+ }
+
+ RETURN_TOKEN_NEVER_SKIP (HASH_TOKEN);
}
-{HASH}version{HSPACE}+ {
- yylval->str = ralloc_strdup (yyextra, yytext);
+<HASH>version{HSPACE}+ {
+ BEGIN INITIAL;
yyextra->space_tokens = 0;
- return HASH_VERSION;
+ RETURN_STRING_TOKEN (VERSION_TOKEN);
+}
+
+ /* Swallow empty #pragma directives, (to avoid confusing the
+ * downstream compiler). */
+<HASH>pragma{HSPACE}*/{NEWLINE} {
+ BEGIN INITIAL;
}
/* glcpp doesn't handle #extension, #version, or #pragma directives.
* Simply pass them through to the main compiler's lexer/parser. */
-{HASH}(extension|pragma)[^\n]* {
- if (parser->commented_newlines)
- BEGIN NEWLINE_CATCHUP;
- yylval->str = ralloc_strdup (yyextra, yytext);
- yylineno++;
- yycolumn = 0;
- return OTHER;
+<HASH>(extension|pragma)[^\r\n]* {
+ BEGIN INITIAL;
+ RETURN_STRING_TOKEN (PRAGMA);
}
-{HASH}line{HSPACE}+ {
- return HASH_LINE;
+<HASH>line{HSPACE}+ {
+ BEGIN INITIAL;
+ RETURN_TOKEN (LINE);
}
-<SKIP,INITIAL>{
-{HASH}ifdef {
+<HASH>{NEWLINE} {
+ BEGIN INITIAL;
+ RETURN_TOKEN_NEVER_SKIP (NEWLINE);
+}
+
+ /* For the pre-processor directives, we return these tokens
+ * even when we are otherwise skipping. */
+<HASH>ifdef {
+ BEGIN INITIAL;
yyextra->lexing_directive = 1;
yyextra->space_tokens = 0;
- return HASH_IFDEF;
+ RETURN_TOKEN_NEVER_SKIP (IFDEF);
}
-{HASH}ifndef {
+<HASH>ifndef {
+ BEGIN INITIAL;
yyextra->lexing_directive = 1;
yyextra->space_tokens = 0;
- return HASH_IFNDEF;
+ RETURN_TOKEN_NEVER_SKIP (IFNDEF);
}
-{HASH}if/[^_a-zA-Z0-9] {
+<HASH>if/[^_a-zA-Z0-9] {
+ BEGIN INITIAL;
yyextra->lexing_directive = 1;
yyextra->space_tokens = 0;
- return HASH_IF;
+ RETURN_TOKEN_NEVER_SKIP (IF);
}
-{HASH}elif/[^_a-zA-Z0-9] {
+<HASH>elif/[^_a-zA-Z0-9] {
+ BEGIN INITIAL;
yyextra->lexing_directive = 1;
yyextra->space_tokens = 0;
- return HASH_ELIF;
+ RETURN_TOKEN_NEVER_SKIP (ELIF);
}
-{HASH}else {
+<HASH>else {
+ BEGIN INITIAL;
yyextra->space_tokens = 0;
- return HASH_ELSE;
+ RETURN_TOKEN_NEVER_SKIP (ELSE);
}
-{HASH}endif {
+<HASH>endif {
+ BEGIN INITIAL;
yyextra->space_tokens = 0;
- return HASH_ENDIF;
-}
+ RETURN_TOKEN_NEVER_SKIP (ENDIF);
}
-<SKIP>[^\n] {
- if (parser->commented_newlines)
- BEGIN NEWLINE_CATCHUP;
+<HASH>error[^\r\n]* {
+ BEGIN INITIAL;
+ RETURN_STRING_TOKEN (ERROR_TOKEN);
}
-{HASH}error.* {
- char *p;
- for (p = yytext; !isalpha(p[0]); p++); /* skip " # " */
- p += 5; /* skip "error" */
- glcpp_error(yylloc, yyextra, "#error%s", p);
+ /* After we see a "#define" we enter the <DEFINE> start state
+ * for the lexer. Within <DEFINE> we are looking for the first
+ * identifier and specifically checking whether the identifier
+ * is followed by a '(' or not, (to lex either a
+ * FUNC_IDENTIFIER or an OBJ_IDENITIFIER token).
+ *
+ * While in the <DEFINE> state we also need to explicitly
+ * handle a few other things that may appear before the
+ * identifier:
+ *
+ * * Comments, (handled above with the main support for
+ * comments).
+ *
+ * * Whitespace (simply ignored)
+ *
+ * * Anything else, (not an identifier, not a comment,
+ * and not whitespace). This will generate an error.
+ */
+<HASH>define{HSPACE}* {
+ if (! parser->skipping) {
+ BEGIN DEFINE;
+ yyextra->space_tokens = 0;
+ RETURN_TOKEN (DEFINE_TOKEN);
+ }
}
-{HASH}define{HSPACE}+ {
+<HASH>undef {
+ BEGIN INITIAL;
yyextra->space_tokens = 0;
- yy_push_state(DEFINE, yyscanner);
- return HASH_DEFINE;
+ RETURN_TOKEN (UNDEF);
}
+<HASH>{HSPACE}+ {
+ /* Nothing to do here. Importantly, don't leave the <HASH>
+ * start condition, since it's legal to have space between the
+ * '#' and the directive.. */
+}
+
+ /* This will catch any non-directive garbage after a HASH */
+<HASH>{NONSPACE} {
+ BEGIN INITIAL;
+ RETURN_TOKEN (GARBAGE);
+}
+
+ /* An identifier immediately followed by '(' */
<DEFINE>{IDENTIFIER}/"(" {
- yy_pop_state(yyscanner);
- yylval->str = ralloc_strdup (yyextra, yytext);
- return FUNC_IDENTIFIER;
+ BEGIN INITIAL;
+ RETURN_STRING_TOKEN (FUNC_IDENTIFIER);
}
+ /* An identifier not immediately followed by '(' */
<DEFINE>{IDENTIFIER} {
- yy_pop_state(yyscanner);
- yylval->str = ralloc_strdup (yyextra, yytext);
- return OBJ_IDENTIFIER;
+ BEGIN INITIAL;
+ RETURN_STRING_TOKEN (OBJ_IDENTIFIER);
}
-{HASH}undef {
- yyextra->space_tokens = 0;
- return HASH_UNDEF;
+ /* Whitespace */
+<DEFINE>{HSPACE}+ {
+ /* Just ignore it. Nothing to do here. */
}
-{HASH} {
- yyextra->space_tokens = 0;
- return HASH;
+ /* '/' not followed by '*', so not a comment. This is an error. */
+<DEFINE>[/][^*]{NONSPACE}* {
+ BEGIN INITIAL;
+ glcpp_error(yylloc, yyextra, "#define followed by a non-identifier: %s", yytext);
+ RETURN_STRING_TOKEN (INTEGER_STRING);
+}
+
+ /* A character that can't start an identifier, comment, or
+ * space. This is an error. */
+<DEFINE>[^_a-zA-Z/[:space:]]{NONSPACE}* {
+ BEGIN INITIAL;
+ glcpp_error(yylloc, yyextra, "#define followed by a non-identifier: %s", yytext);
+ RETURN_STRING_TOKEN (INTEGER_STRING);
}
{DECIMAL_INTEGER} {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return INTEGER_STRING;
+ RETURN_STRING_TOKEN (INTEGER_STRING);
}
{OCTAL_INTEGER} {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return INTEGER_STRING;
+ RETURN_STRING_TOKEN (INTEGER_STRING);
}
{HEXADECIMAL_INTEGER} {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return INTEGER_STRING;
+ RETURN_STRING_TOKEN (INTEGER_STRING);
}
"<<" {
- return LEFT_SHIFT;
+ RETURN_TOKEN (LEFT_SHIFT);
}
">>" {
- return RIGHT_SHIFT;
+ RETURN_TOKEN (RIGHT_SHIFT);
}
"<=" {
- return LESS_OR_EQUAL;
+ RETURN_TOKEN (LESS_OR_EQUAL);
}
">=" {
- return GREATER_OR_EQUAL;
+ RETURN_TOKEN (GREATER_OR_EQUAL);
}
"==" {
- return EQUAL;
+ RETURN_TOKEN (EQUAL);
}
"!=" {
- return NOT_EQUAL;
+ RETURN_TOKEN (NOT_EQUAL);
}
"&&" {
- return AND;
+ RETURN_TOKEN (AND);
}
"||" {
- return OR;
+ RETURN_TOKEN (OR);
+}
+
+"++" {
+ RETURN_TOKEN (PLUS_PLUS);
+}
+
+"--" {
+ RETURN_TOKEN (MINUS_MINUS);
}
"##" {
- if (parser->is_gles)
- glcpp_error(yylloc, yyextra, "Token pasting (##) is illegal in GLES");
- return PASTE;
+ if (! parser->skipping) {
+ if (parser->is_gles)
+ glcpp_error(yylloc, yyextra, "Token pasting (##) is illegal in GLES");
+ RETURN_TOKEN (PASTE);
+ }
}
"defined" {
- return DEFINED;
+ RETURN_TOKEN (DEFINED);
}
{IDENTIFIER} {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return IDENTIFIER;
+ RETURN_STRING_TOKEN (IDENTIFIER);
}
{PP_NUMBER} {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return OTHER;
+ RETURN_STRING_TOKEN (OTHER);
}
{PUNCTUATION} {
- return yytext[0];
+ RETURN_TOKEN (yytext[0]);
}
{OTHER}+ {
- yylval->str = ralloc_strdup (yyextra, yytext);
- return OTHER;
+ RETURN_STRING_TOKEN (OTHER);
}
{HSPACE} {
if (yyextra->space_tokens) {
- return SPACE;
+ RETURN_TOKEN (SPACE);
}
}
-<SKIP,INITIAL>\n {
+ /* We preserve all newlines, even between #if 0..#endif, so no
+ skipping.. */
+<*>{NEWLINE} {
if (parser->commented_newlines) {
BEGIN NEWLINE_CATCHUP;
+ } else {
+ BEGIN INITIAL;
}
+ yyextra->space_tokens = 1;
yyextra->lexing_directive = 0;
yylineno++;
yycolumn = 0;
- return NEWLINE;
+ RETURN_TOKEN_NEVER_SKIP (NEWLINE);
}
- /* Handle missing newline at EOF. */
-<INITIAL><<EOF>> {
+<INITIAL,COMMENT,DEFINE,HASH><<EOF>> {
+ if (YY_START == COMMENT)
+ glcpp_error(yylloc, yyextra, "Unterminated comment");
BEGIN DONE; /* Don't keep matching this rule forever. */
yyextra->lexing_directive = 0;
- return NEWLINE;
+ if (! parser->last_token_was_newline)
+ RETURN_TOKEN (NEWLINE);
}
+ /* This is a catch-all to avoid the annoying default flex action which
+ * matches any character and prints it. If any input ever matches this
+ * rule, then we have made a mistake above and need to fix one or more
+ * of the preceding patterns to match that input. */
+
+<*>. {
+ glcpp_error(yylloc, yyextra, "Internal compiler error: Unexpected character: %s", yytext);
+
/* We don't actually use the UNREACHABLE start condition. We
- only have this action here so that we can pretend to call some
+ only have this block here so that we can pretend to call some
generated functions, (to avoid "defined but not used"
warnings. */
-<UNREACHABLE>. {
- unput('.');
- yy_top_state(yyextra);
+ if (YY_START == UNREACHABLE) {
+ unput('.');
+ yy_top_state(yyextra);
+ }
}
%%
diff --git a/mesalib/src/glsl/glcpp/glcpp-parse.y b/mesalib/src/glsl/glcpp/glcpp-parse.y
index 084078eb0..a61697394 100644
--- a/mesalib/src/glsl/glcpp/glcpp-parse.y
+++ b/mesalib/src/glsl/glcpp/glcpp-parse.y
@@ -57,6 +57,9 @@ _string_list_append_item (string_list_t *list, const char *str);
static int
_string_list_contains (string_list_t *list, const char *member, int *index);
+static const char *
+_string_list_has_duplicate (string_list_t *list);
+
static int
_string_list_length (string_list_t *list);
@@ -105,18 +108,25 @@ _parser_active_list_pop (glcpp_parser_t *parser);
static int
_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier);
+typedef enum {
+ EXPANSION_MODE_IGNORE_DEFINED,
+ EXPANSION_MODE_EVALUATE_DEFINED
+} expansion_mode_t;
+
/* Expand list, and begin lexing from the result (after first
* prefixing a token of type 'head_token_type').
*/
static void
_glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser,
int head_token_type,
- token_list_t *list);
+ token_list_t *list,
+ expansion_mode_t mode);
/* Perform macro expansion in-place on the given list. */
static void
_glcpp_parser_expand_token_list (glcpp_parser_t *parser,
- token_list_t *list);
+ token_list_t *list,
+ expansion_mode_t mode);
static void
_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser,
@@ -164,14 +174,18 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value);
%lex-param {glcpp_parser_t *parser}
%expect 0
-%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE FUNC_IDENTIFIER OBJ_IDENTIFIER HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_LINE HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE
+
+ /* We use HASH_TOKEN, DEFINE_TOKEN and VERSION_TOKEN (as opposed to
+ * HASH, DEFINE, and VERSION) to avoid conflicts with other symbols,
+ * (such as the <HASH> and <DEFINE> start conditions in the lexer). */
+%token DEFINED ELIF_EXPANDED HASH_TOKEN DEFINE_TOKEN FUNC_IDENTIFIER OBJ_IDENTIFIER ELIF ELSE ENDIF ERROR_TOKEN IF IFDEF IFNDEF LINE PRAGMA UNDEF VERSION_TOKEN GARBAGE IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE PLUS_PLUS MINUS_MINUS
%token PASTE
%type <ival> INTEGER operator SPACE integer_constant
%type <expression_value> expression
-%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
+%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER ERROR_TOKEN PRAGMA
%type <string_list> identifier_list
-%type <token> preprocessing_token conditional_token
-%type <token_list> pp_tokens replacement_list text_line conditional_tokens
+%type <token> preprocessing_token
+%type <token_list> pp_tokens replacement_list text_line
%left OR
%left AND
%left '|'
@@ -184,6 +198,8 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value);
%left '*' '/' '%'
%right UNARY
+%debug
+
%%
input:
@@ -192,27 +208,14 @@ input:
;
line:
- control_line {
- ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
- }
-| HASH_LINE {
- glcpp_parser_resolve_implicit_version(parser);
- } pp_tokens NEWLINE {
-
- if (parser->skip_stack == NULL ||
- parser->skip_stack->type == SKIP_NO_SKIP)
- {
- _glcpp_parser_expand_and_lex_from (parser,
- LINE_EXPANDED, $3);
- }
- }
+ control_line
+| SPACE control_line
| text_line {
_glcpp_parser_print_expanded_token_list (parser, $1);
ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
ralloc_free ($1);
}
| expanded_line
-| HASH non_directive
;
expanded_line:
@@ -259,29 +262,48 @@ define:
;
control_line:
- HASH_DEFINE {
+ control_line_success {
+ ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "\n");
+ }
+| control_line_error
+| HASH_TOKEN LINE {
+ glcpp_parser_resolve_implicit_version(parser);
+ } pp_tokens NEWLINE {
+
+ if (parser->skip_stack == NULL ||
+ parser->skip_stack->type == SKIP_NO_SKIP)
+ {
+ _glcpp_parser_expand_and_lex_from (parser,
+ LINE_EXPANDED, $4,
+ EXPANSION_MODE_IGNORE_DEFINED);
+ }
+ }
+;
+
+control_line_success:
+ HASH_TOKEN DEFINE_TOKEN {
glcpp_parser_resolve_implicit_version(parser);
} define
-| HASH_UNDEF {
+| HASH_TOKEN UNDEF {
glcpp_parser_resolve_implicit_version(parser);
} IDENTIFIER NEWLINE {
macro_t *macro;
- if (strcmp("__LINE__", $3) == 0
- || strcmp("__FILE__", $3) == 0
- || strcmp("__VERSION__", $3) == 0)
+ if (strcmp("__LINE__", $4) == 0
+ || strcmp("__FILE__", $4) == 0
+ || strcmp("__VERSION__", $4) == 0)
glcpp_error(& @1, parser, "Built-in (pre-defined)"
" macro names can not be undefined.");
- macro = hash_table_find (parser->defines, $3);
+ macro = hash_table_find (parser->defines, $4);
if (macro) {
- hash_table_remove (parser->defines, $3);
+ hash_table_remove (parser->defines, $4);
ralloc_free (macro);
}
- ralloc_free ($3);
+ ralloc_free ($4);
}
-| HASH_IF {
+| HASH_TOKEN IF {
glcpp_parser_resolve_implicit_version(parser);
- } conditional_tokens NEWLINE {
+ } pp_tokens NEWLINE {
/* Be careful to only evaluate the 'if' expression if
* we are not skipping. When we are skipping, we
* simply push a new 0-valued 'if' onto the skip
@@ -293,7 +315,8 @@ control_line:
parser->skip_stack->type == SKIP_NO_SKIP)
{
_glcpp_parser_expand_and_lex_from (parser,
- IF_EXPANDED, $3);
+ IF_EXPANDED, $4,
+ EXPANSION_MODE_EVALUATE_DEFINED);
}
else
{
@@ -301,7 +324,7 @@ control_line:
parser->skip_stack->type = SKIP_TO_ENDIF;
}
}
-| HASH_IF NEWLINE {
+| HASH_TOKEN IF NEWLINE {
/* #if without an expression is only an error if we
* are not skipping */
if (parser->skip_stack == NULL ||
@@ -311,21 +334,21 @@ control_line:
}
_glcpp_parser_skip_stack_push_if (parser, & @1, 0);
}
-| HASH_IFDEF {
+| HASH_TOKEN IFDEF {
glcpp_parser_resolve_implicit_version(parser);
} IDENTIFIER junk NEWLINE {
- macro_t *macro = hash_table_find (parser->defines, $3);
- ralloc_free ($3);
+ macro_t *macro = hash_table_find (parser->defines, $4);
+ ralloc_free ($4);
_glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL);
}
-| HASH_IFNDEF {
+| HASH_TOKEN IFNDEF {
glcpp_parser_resolve_implicit_version(parser);
} IDENTIFIER junk NEWLINE {
- macro_t *macro = hash_table_find (parser->defines, $3);
- ralloc_free ($3);
- _glcpp_parser_skip_stack_push_if (parser, & @2, macro == NULL);
+ macro_t *macro = hash_table_find (parser->defines, $4);
+ ralloc_free ($4);
+ _glcpp_parser_skip_stack_push_if (parser, & @3, macro == NULL);
}
-| HASH_ELIF conditional_tokens NEWLINE {
+| HASH_TOKEN ELIF pp_tokens NEWLINE {
/* Be careful to only evaluate the 'elif' expression
* if we are not skipping. When we are skipping, we
* simply change to a 0-valued 'elif' on the skip
@@ -337,7 +360,8 @@ control_line:
parser->skip_stack->type == SKIP_TO_ELSE)
{
_glcpp_parser_expand_and_lex_from (parser,
- ELIF_EXPANDED, $2);
+ ELIF_EXPANDED, $3,
+ EXPANSION_MODE_EVALUATE_DEFINED);
}
else if (parser->skip_stack &&
parser->skip_stack->has_else)
@@ -350,7 +374,7 @@ control_line:
"elif", 0);
}
}
-| HASH_ELIF NEWLINE {
+| HASH_TOKEN ELIF NEWLINE {
/* #elif without an expression is an error unless we
* are skipping. */
if (parser->skip_stack &&
@@ -370,7 +394,7 @@ control_line:
glcpp_warning(& @1, parser, "ignoring illegal #elif without expression");
}
}
-| HASH_ELSE { parser->lexing_directive = 1; } NEWLINE {
+| HASH_TOKEN ELSE { parser->lexing_directive = 1; } NEWLINE {
if (parser->skip_stack &&
parser->skip_stack->has_else)
{
@@ -383,24 +407,39 @@ control_line:
parser->skip_stack->has_else = true;
}
}
-| HASH_ENDIF {
+| HASH_TOKEN ENDIF {
_glcpp_parser_skip_stack_pop (parser, & @1);
} NEWLINE
-| HASH_VERSION integer_constant NEWLINE {
+| HASH_TOKEN VERSION_TOKEN integer_constant NEWLINE {
if (parser->version_resolved) {
glcpp_error(& @1, parser, "#version must appear on the first line");
}
- _glcpp_parser_handle_version_declaration(parser, $2, NULL, true);
+ _glcpp_parser_handle_version_declaration(parser, $3, NULL, true);
}
-| HASH_VERSION integer_constant IDENTIFIER NEWLINE {
+| HASH_TOKEN VERSION_TOKEN integer_constant IDENTIFIER NEWLINE {
if (parser->version_resolved) {
glcpp_error(& @1, parser, "#version must appear on the first line");
}
- _glcpp_parser_handle_version_declaration(parser, $2, $3, true);
+ _glcpp_parser_handle_version_declaration(parser, $3, $4, true);
}
-| HASH NEWLINE {
+| HASH_TOKEN NEWLINE {
glcpp_parser_resolve_implicit_version(parser);
}
+| HASH_TOKEN PRAGMA NEWLINE {
+ ralloc_asprintf_rewrite_tail (&parser->output, &parser->output_length, "#%s", $2);
+ }
+;
+
+control_line_error:
+ HASH_TOKEN ERROR_TOKEN NEWLINE {
+ glcpp_error(& @1, parser, "#%s", $2);
+ }
+| HASH_TOKEN DEFINE_TOKEN NEWLINE {
+ glcpp_error (& @1, parser, "#define without macro name");
+ }
+| HASH_TOKEN GARBAGE pp_tokens NEWLINE {
+ glcpp_error (& @1, parser, "Illegal non-directive after #");
+ }
;
integer_constant:
@@ -612,12 +651,6 @@ text_line:
| pp_tokens NEWLINE
;
-non_directive:
- pp_tokens NEWLINE {
- yyerror (& @1, parser, "Invalid tokens after #");
- }
-;
-
replacement_list:
/* empty */ { $$ = NULL; }
| pp_tokens
@@ -630,31 +663,6 @@ junk:
}
;
-conditional_token:
- /* Handle "defined" operator */
- DEFINED IDENTIFIER {
- int v = hash_table_find (parser->defines, $2) ? 1 : 0;
- $$ = _token_create_ival (parser, INTEGER, v);
- }
-| DEFINED '(' IDENTIFIER ')' {
- int v = hash_table_find (parser->defines, $3) ? 1 : 0;
- $$ = _token_create_ival (parser, INTEGER, v);
- }
-| preprocessing_token
-;
-
-conditional_tokens:
- /* Exactly the same as pp_tokens, but using conditional_token */
- conditional_token {
- $$ = _token_list_create (parser);
- _token_list_append ($$, $1);
- }
-| conditional_tokens conditional_token {
- $$ = $1;
- _token_list_append ($$, $2);
- }
-;
-
pp_tokens:
preprocessing_token {
parser->space_tokens = 1;
@@ -680,6 +688,10 @@ preprocessing_token:
$$ = _token_create_ival (parser, $1, $1);
$$->location = yylloc;
}
+| DEFINED {
+ $$ = _token_create_ival (parser, DEFINED, DEFINED);
+ $$->location = yylloc;
+ }
| OTHER {
$$ = _token_create_str (parser, OTHER, $1);
$$->location = yylloc;
@@ -722,6 +734,8 @@ operator:
| ',' { $$ = ','; }
| '=' { $$ = '='; }
| PASTE { $$ = PASTE; }
+| PLUS_PLUS { $$ = PLUS_PLUS; }
+| MINUS_MINUS { $$ = MINUS_MINUS; }
;
%%
@@ -777,6 +791,25 @@ _string_list_contains (string_list_t *list, const char *member, int *index)
return 0;
}
+/* Return duplicate string in list (if any), NULL otherwise. */
+const char *
+_string_list_has_duplicate (string_list_t *list)
+{
+ string_node_t *node, *dup;
+
+ if (list == NULL)
+ return NULL;
+
+ for (node = list->head; node; node = node->next) {
+ for (dup = node->next; dup; dup = dup->next) {
+ if (strcmp (node->str, dup->str) == 0)
+ return node->str;
+ }
+ }
+
+ return NULL;
+}
+
int
_string_list_length (string_list_t *list)
{
@@ -1123,14 +1156,21 @@ _token_print (char **out, size_t *len, token_t *token)
case PASTE:
ralloc_asprintf_rewrite_tail (out, len, "##");
break;
- case COMMA_FINAL:
- ralloc_asprintf_rewrite_tail (out, len, ",");
+ case PLUS_PLUS:
+ ralloc_asprintf_rewrite_tail (out, len, "++");
+ break;
+ case MINUS_MINUS:
+ ralloc_asprintf_rewrite_tail (out, len, "--");
+ break;
+ case DEFINED:
+ ralloc_asprintf_rewrite_tail (out, len, "defined");
break;
case PLACEHOLDER:
/* Nothing to print. */
break;
default:
assert(!"Error: Don't know how to print token.");
+
break;
}
}
@@ -1308,12 +1348,16 @@ glcpp_parser_create (const struct gl_extensions *extensions, gl_api api)
parser->active = NULL;
parser->lexing_directive = 0;
parser->space_tokens = 1;
+ parser->last_token_was_newline = 0;
+ parser->last_token_was_space = 0;
+ parser->first_non_space_token_this_line = 1;
parser->newline_as_space = 0;
parser->in_control_line = 0;
parser->paren_count = 0;
parser->commented_newlines = 0;
parser->skip_stack = NULL;
+ parser->skipping = 0;
parser->lex_from_list = NULL;
parser->lex_from_node = NULL;
@@ -1459,15 +1503,143 @@ _token_list_create_with_one_integer (void *ctx, int ival)
return _token_list_create_with_one_ival (ctx, INTEGER, ival);
}
+/* Evaluate a DEFINED token node (based on subsequent tokens in the list).
+ *
+ * Note: This function must only be called when "node" is a DEFINED token,
+ * (and will abort with an assertion failure otherwise).
+ *
+ * If "node" is followed, (ignoring any SPACE tokens), by an IDENTIFIER token
+ * (optionally preceded and followed by '(' and ')' tokens) then the following
+ * occurs:
+ *
+ * If the identifier is a defined macro, this function returns 1.
+ *
+ * If the identifier is not a defined macro, this function returns 0.
+ *
+ * In either case, *last will be updated to the last node in the list
+ * consumed by the evaluation, (either the token of the identifier or the
+ * token of the closing parenthesis).
+ *
+ * In all other cases, (such as "node is the final node of the list", or
+ * "missing closing parenthesis", etc.), this function generates a
+ * preprocessor error, returns -1 and *last will not be set.
+ */
+static int
+_glcpp_parser_evaluate_defined (glcpp_parser_t *parser,
+ token_node_t *node,
+ token_node_t **last)
+{
+ token_node_t *argument, *defined = node;
+
+ assert (node->token->type == DEFINED);
+
+ node = node->next;
+
+ /* Ignore whitespace after DEFINED token. */
+ while (node && node->token->type == SPACE)
+ node = node->next;
+
+ if (node == NULL)
+ goto FAIL;
+
+ if (node->token->type == IDENTIFIER || node->token->type == OTHER) {
+ argument = node;
+ } else if (node->token->type == '(') {
+ node = node->next;
+
+ /* Ignore whitespace after '(' token. */
+ while (node && node->token->type == SPACE)
+ node = node->next;
+
+ if (node == NULL || (node->token->type != IDENTIFIER &&
+ node->token->type != OTHER))
+ {
+ goto FAIL;
+ }
+
+ argument = node;
+
+ node = node->next;
+
+ /* Ignore whitespace after identifier, before ')' token. */
+ while (node && node->token->type == SPACE)
+ node = node->next;
+
+ if (node == NULL || node->token->type != ')')
+ goto FAIL;
+ } else {
+ goto FAIL;
+ }
+
+ *last = node;
+
+ return hash_table_find (parser->defines,
+ argument->token->value.str) ? 1 : 0;
+
+FAIL:
+ glcpp_error (&defined->token->location, parser,
+ "\"defined\" not followed by an identifier");
+ return -1;
+}
+
+/* Evaluate all DEFINED nodes in a given list, modifying the list in place.
+ */
+static void
+_glcpp_parser_evaluate_defined_in_list (glcpp_parser_t *parser,
+ token_list_t *list)
+{
+ token_node_t *node, *node_prev, *replacement, *last = NULL;
+ int value;
+
+ if (list == NULL)
+ return;
+
+ node_prev = NULL;
+ node = list->head;
+
+ while (node) {
+
+ if (node->token->type != DEFINED)
+ goto NEXT;
+
+ value = _glcpp_parser_evaluate_defined (parser, node, &last);
+ if (value == -1)
+ goto NEXT;
+
+ replacement = ralloc (list, token_node_t);
+ replacement->token = _token_create_ival (list, INTEGER, value);
+
+ /* Splice replacement node into list, replacing from "node"
+ * through "last". */
+ if (node_prev)
+ node_prev->next = replacement;
+ else
+ list->head = replacement;
+ replacement->next = last->next;
+ if (last == list->tail)
+ list->tail = replacement;
+
+ node = replacement;
+
+ NEXT:
+ node_prev = node;
+ node = node->next;
+ }
+}
+
/* Perform macro expansion on 'list', placing the resulting tokens
* into a new list which is initialized with a first token of type
* 'head_token_type'. Then begin lexing from the resulting list,
* (return to the current lexing source when this list is exhausted).
+ *
+ * See the documentation of _glcpp_parser_expand_token_list for a description
+ * of the "mode" parameter.
*/
static void
_glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser,
int head_token_type,
- token_list_t *list)
+ token_list_t *list,
+ expansion_mode_t mode)
{
token_list_t *expanded;
token_t *token;
@@ -1475,7 +1647,7 @@ _glcpp_parser_expand_and_lex_from (glcpp_parser_t *parser,
expanded = _token_list_create (parser);
token = _token_create_ival (parser, head_token_type, head_token_type);
_token_list_append (expanded, token);
- _glcpp_parser_expand_token_list (parser, list);
+ _glcpp_parser_expand_token_list (parser, list, mode);
_token_list_append_list (expanded, list);
glcpp_parser_lex_from (parser, expanded);
}
@@ -1538,12 +1710,15 @@ _glcpp_parser_apply_pastes (glcpp_parser_t *parser, token_list_t *list)
* *last to the last node in the list that was consumed by the
* expansion. Specifically, *last will be set as follows: as the
* token of the closing right parenthesis.
+ *
+ * See the documentation of _glcpp_parser_expand_token_list for a description
+ * of the "mode" parameter.
*/
static token_list_t *
_glcpp_parser_expand_function (glcpp_parser_t *parser,
token_node_t *node,
- token_node_t **last)
-
+ token_node_t **last,
+ expansion_mode_t mode)
{
macro_t *macro;
const char *identifier;
@@ -1612,7 +1787,8 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser,
expanded_argument = _token_list_copy (parser,
argument);
_glcpp_parser_expand_token_list (parser,
- expanded_argument);
+ expanded_argument,
+ mode);
_token_list_append_list (substituted,
expanded_argument);
} else {
@@ -1652,11 +1828,15 @@ _glcpp_parser_expand_function (glcpp_parser_t *parser,
*
* As the token of the closing right parenthesis in the case of
* function-like macro expansion.
+ *
+ * See the documentation of _glcpp_parser_expand_token_list for a description
+ * of the "mode" parameter.
*/
static token_list_t *
_glcpp_parser_expand_node (glcpp_parser_t *parser,
token_node_t *node,
- token_node_t **last)
+ token_node_t **last,
+ expansion_mode_t mode)
{
token_t *token = node->token;
const char *identifier;
@@ -1664,14 +1844,6 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser,
/* We only expand identifiers */
if (token->type != IDENTIFIER) {
- /* We change any COMMA into a COMMA_FINAL to prevent
- * it being mistaken for an argument separator
- * later. */
- if (token->type == ',') {
- token->type = COMMA_FINAL;
- token->value.ival = COMMA_FINAL;
- }
-
return NULL;
}
@@ -1723,7 +1895,7 @@ _glcpp_parser_expand_node (glcpp_parser_t *parser,
return replacement;
}
- return _glcpp_parser_expand_function (parser, node, last);
+ return _glcpp_parser_expand_function (parser, node, last, mode);
}
/* Push a new identifier onto the parser's active list.
@@ -1782,11 +1954,28 @@ _parser_active_list_contains (glcpp_parser_t *parser, const char *identifier)
/* Walk over the token list replacing nodes with their expansion.
* Whenever nodes are expanded the walking will walk over the new
* nodes, continuing to expand as necessary. The results are placed in
- * 'list' itself;
+ * 'list' itself.
+ *
+ * The "mode" argument controls the handling of any DEFINED tokens that
+ * result from expansion as follows:
+ *
+ * EXPANSION_MODE_IGNORE_DEFINED: Any resulting DEFINED tokens will be
+ * left in the final list, unevaluated. This is the correct mode
+ * for expanding any list in any context other than a
+ * preprocessor conditional, (#if or #elif).
+ *
+ * EXPANSION_MODE_EVALUATE_DEFINED: Any resulting DEFINED tokens will be
+ * evaluated to 0 or 1 tokens depending on whether the following
+ * token is the name of a defined macro. If the DEFINED token is
+ * not followed by an (optionally parenthesized) identifier, then
+ * an error will be generated. This the correct mode for
+ * expanding any list in the context of a preprocessor
+ * conditional, (#if or #elif).
*/
static void
_glcpp_parser_expand_token_list (glcpp_parser_t *parser,
- token_list_t *list)
+ token_list_t *list,
+ expansion_mode_t mode)
{
token_node_t *node_prev;
token_node_t *node, *last = NULL;
@@ -1801,15 +1990,23 @@ _glcpp_parser_expand_token_list (glcpp_parser_t *parser,
node_prev = NULL;
node = list->head;
+ if (mode == EXPANSION_MODE_EVALUATE_DEFINED)
+ _glcpp_parser_evaluate_defined_in_list (parser, list);
+
while (node) {
while (parser->active && parser->active->marker == node)
_parser_active_list_pop (parser);
- expansion = _glcpp_parser_expand_node (parser, node, &last);
+ expansion = _glcpp_parser_expand_node (parser, node, &last, mode);
if (expansion) {
token_node_t *n;
+ if (mode == EXPANSION_MODE_EVALUATE_DEFINED) {
+ _glcpp_parser_evaluate_defined_in_list (parser,
+ expansion);
+ }
+
for (n = node; n != last->next; n = n->next)
while (parser->active &&
parser->active->marker == n)
@@ -1862,7 +2059,7 @@ _glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser,
if (list == NULL)
return;
- _glcpp_parser_expand_token_list (parser, list);
+ _glcpp_parser_expand_token_list (parser, list, EXPANSION_MODE_IGNORE_DEFINED);
_token_list_trim_trailing_space (list);
@@ -1923,6 +2120,10 @@ _define_object_macro (glcpp_parser_t *parser,
{
macro_t *macro, *previous;
+ /* We define pre-defined macros before we've started parsing the
+ * actual file. So if there's no location defined yet, that's what
+ * were doing and we don't want to generate an error for using the
+ * reserved names. */
if (loc != NULL)
_check_for_reserved_macro_name(parser, loc, identifier);
@@ -1955,9 +2156,16 @@ _define_function_macro (glcpp_parser_t *parser,
token_list_t *replacements)
{
macro_t *macro, *previous;
+ const char *dup;
_check_for_reserved_macro_name(parser, loc, identifier);
+ /* Check for any duplicate parameter names. */
+ if ((dup = _string_list_has_duplicate (parameters)) != NULL) {
+ glcpp_error (loc, parser, "Duplicate macro parameter \"%s\"",
+ dup);
+ }
+
macro = ralloc (parser, macro_t);
ralloc_steal (macro, parameters);
ralloc_steal (macro, replacements);
@@ -2020,11 +2228,11 @@ glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser)
if (ret == NEWLINE)
parser->in_control_line = 0;
}
- else if (ret == HASH_DEFINE ||
- ret == HASH_UNDEF || ret == HASH_IF ||
- ret == HASH_IFDEF || ret == HASH_IFNDEF ||
- ret == HASH_ELIF || ret == HASH_ELSE ||
- ret == HASH_ENDIF || ret == HASH)
+ else if (ret == DEFINE_TOKEN ||
+ ret == UNDEF || ret == IF ||
+ ret == IFDEF || ret == IFNDEF ||
+ ret == ELIF || ret == ELSE ||
+ ret == ENDIF || ret == HASH_TOKEN)
{
parser->in_control_line = 1;
}
@@ -2117,7 +2325,7 @@ _glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc,
const char *type, int condition)
{
if (parser->skip_stack == NULL) {
- glcpp_error (loc, parser, "%s without #if\n", type);
+ glcpp_error (loc, parser, "#%s without #if\n", type);
return;
}
@@ -2170,6 +2378,8 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
if (extensions != NULL) {
if (extensions->OES_EGL_image_external)
add_builtin_define(parser, "GL_OES_EGL_image_external", 1);
+ if (extensions->OES_standard_derivatives)
+ add_builtin_define(parser, "GL_OES_standard_derivatives", 1);
}
} else {
add_builtin_define(parser, "GL_ARB_draw_buffers", 1);
diff --git a/mesalib/src/glsl/glcpp/glcpp.c b/mesalib/src/glsl/glcpp/glcpp.c
index 07b1500b6..ca188015c 100644
--- a/mesalib/src/glsl/glcpp/glcpp.c
+++ b/mesalib/src/glsl/glcpp/glcpp.c
@@ -124,6 +124,7 @@ enum {
const static struct option
long_options[] = {
{"disable-line-continuations", no_argument, 0, DISABLE_LINE_CONTINUATIONS_OPT },
+ {"debug", no_argument, 0, 'd'},
{0, 0, 0, 0 }
};
@@ -140,11 +141,14 @@ main (int argc, char *argv[])
init_fake_gl_context (&gl_ctx);
- while ((c = getopt_long(argc, argv, "", long_options, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "d", long_options, NULL)) != -1) {
switch (c) {
case DISABLE_LINE_CONTINUATIONS_OPT:
gl_ctx.Const.DisableGLSLLineContinuations = true;
break;
+ case 'd':
+ glcpp_parser_debug = 1;
+ break;
default:
usage ();
exit (1);
diff --git a/mesalib/src/glsl/glcpp/glcpp.h b/mesalib/src/glsl/glcpp/glcpp.h
index 64b487202..70aa14b6e 100644
--- a/mesalib/src/glsl/glcpp/glcpp.h
+++ b/mesalib/src/glsl/glcpp/glcpp.h
@@ -29,7 +29,7 @@
#include "main/mtypes.h"
-#include "../ralloc.h"
+#include "util/ralloc.h"
#include "program/hash_table.h"
@@ -177,11 +177,15 @@ struct glcpp_parser {
active_list_t *active;
int lexing_directive;
int space_tokens;
+ int last_token_was_newline;
+ int last_token_was_space;
+ int first_non_space_token_this_line;
int newline_as_space;
int in_control_line;
int paren_count;
int commented_newlines;
skip_node_t *skip_stack;
+ int skipping;
token_list_t *lex_from_list;
token_node_t *lex_from_node;
char *output;
diff --git a/mesalib/src/glsl/glcpp/pp.c b/mesalib/src/glsl/glcpp/pp.c
index 4a623f81e..a54bcbe16 100644
--- a/mesalib/src/glsl/glcpp/pp.c
+++ b/mesalib/src/glsl/glcpp/pp.c
@@ -70,6 +70,42 @@ glcpp_warning (YYLTYPE *locp, glcpp_parser_t *parser, const char *fmt, ...)
&parser->info_log_length, "\n");
}
+/* Given str, (that's expected to start with a newline terminator of some
+ * sort), return a pointer to the first character in str after the newline.
+ *
+ * A newline terminator can be any of the following sequences:
+ *
+ * "\r\n"
+ * "\n\r"
+ * "\n"
+ * "\r"
+ *
+ * And the longest such sequence will be skipped.
+ */
+static const char *
+skip_newline (const char *str)
+{
+ const char *ret = str;
+
+ if (ret == NULL)
+ return ret;
+
+ if (*ret == '\0')
+ return ret;
+
+ if (*ret == '\r') {
+ ret++;
+ if (*ret && *ret == '\n')
+ ret++;
+ } else if (*ret == '\n') {
+ ret++;
+ if (*ret && *ret == '\r')
+ ret++;
+ }
+
+ return ret;
+}
+
/* Remove any line continuation characters in the shader, (whether in
* preprocessing directives or in GLSL code).
*/
@@ -78,10 +114,49 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
{
char *clean = ralloc_strdup(ctx, "");
const char *backslash, *newline, *search_start;
+ const char *cr, *lf;
+ char newline_separator[3];
int collapsed_newlines = 0;
search_start = shader;
+ /* Determine what flavor of newlines this shader is using. GLSL
+ * provides for 4 different possible ways to separate lines, (using
+ * one or two characters):
+ *
+ * "\n" (line-feed, like Linux, Unix, and new Mac OS)
+ * "\r" (carriage-return, like old Mac files)
+ * "\r\n" (carriage-return + line-feed, like DOS files)
+ * "\n\r" (line-feed + carriage-return, like nothing, really)
+ *
+ * This code explicitly supports a shader that uses a mixture of
+ * newline terminators and will properly handle line continuation
+ * backslashes followed by any of the above.
+ *
+ * But, since we must also insert additional newlines in the output
+ * (for any collapsed lines) we attempt to maintain consistency by
+ * examining the first encountered newline terminator, and using the
+ * same terminator for any newlines we insert.
+ */
+ cr = strchr(search_start, '\r');
+ lf = strchr(search_start, '\n');
+
+ newline_separator[0] = '\n';
+ newline_separator[1] = '\0';
+ newline_separator[2] = '\0';
+
+ if (cr == NULL) {
+ /* Nothing to do. */
+ } else if (lf == NULL) {
+ newline_separator[0] = '\r';
+ } else if (lf == cr + 1) {
+ newline_separator[0] = '\r';
+ newline_separator[1] = '\n';
+ } else if (cr == lf + 1) {
+ newline_separator[0] = '\n';
+ newline_separator[1] = '\r';
+ }
+
while (true) {
backslash = strchr(search_start, '\\');
@@ -91,17 +166,24 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
* line numbers.
*/
if (collapsed_newlines) {
- newline = strchr(search_start, '\n');
+ cr = strchr (search_start, '\r');
+ lf = strchr (search_start, '\n');
+ if (cr && lf)
+ newline = cr < lf ? cr : lf;
+ else if (cr)
+ newline = cr;
+ else
+ newline = lf;
if (newline &&
(backslash == NULL || newline < backslash))
{
ralloc_strncat(&clean, shader,
newline - shader + 1);
while (collapsed_newlines) {
- ralloc_strcat(&clean, "\n");
+ ralloc_strcat(&clean, newline_separator);
collapsed_newlines--;
}
- shader = newline + 1;
+ shader = skip_newline (newline);
search_start = shader;
}
}
@@ -116,15 +198,11 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
* advance the shader pointer to the character after the
* newline.
*/
- if (backslash[1] == '\n' ||
- (backslash[1] == '\r' && backslash[2] == '\n'))
+ if (backslash[1] == '\r' || backslash[1] == '\n')
{
collapsed_newlines++;
ralloc_strncat(&clean, shader, backslash - shader);
- if (backslash[1] == '\n')
- shader = backslash + 2;
- else
- shader = backslash + 3;
+ shader = skip_newline (backslash + 1);
search_start = shader;
}
}