aboutsummaryrefslogtreecommitdiff
path: root/mesalib/src/glsl/glcpp
diff options
context:
space:
mode:
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;
}
}