[Mesa-dev] [PATCH 2/3] glsl: glcpp: Move handling of #line directives from lexer to parser.

Kenneth Graunke kenneth at whitecape.org
Wed Jun 20 02:15:18 PDT 2012


On 06/09/2012 04:41 PM, Carl Worth wrote:
> The GLSL specification requires that #line directives be interpreted
> after macro expansion. Our existing implementation of #line macros in
> the lexer prevents conformance on this point.
> 
> Moving the handling of #line from the lexer to the parser gives us the
> macro expansion we need. An additional benefit is that the
> preprocessor also now supports comments on the same line as #line
> directives.
> 
> Finally, the preprocessor now emits the (fully-macro-expanded) #line
> directives into the output. This allows the full GLSL compiler to also
> see and interpret these directives so it can also generate correct
> line numbers in error messages.
> ---
>  src/glsl/glcpp/glcpp-lex.l                    |   49 +++++++------------------
>  src/glsl/glcpp/glcpp-parse.y                  |   33 ++++++++++++++++-
>  src/glsl/glcpp/glcpp.h                        |    4 ++
>  src/glsl/glcpp/tests/091-hash-line.c.expected |    8 ++--
>  4 files changed, 54 insertions(+), 40 deletions(-)

Hi Carl,

Sorry for the extremely late review.  (Lost this one in the inbox.)
Comments below...

> diff --git a/src/glsl/glcpp/glcpp-lex.l b/src/glsl/glcpp/glcpp-lex.l
> index b34f2c0..7ab58cb 100644
> --- a/src/glsl/glcpp/glcpp-lex.l
> +++ b/src/glsl/glcpp/glcpp-lex.l
> @@ -40,12 +40,18 @@ void glcpp_set_column (int  column_no , yyscan_t yyscanner);
>  
>  #define YY_NO_INPUT
>  
> -#define YY_USER_ACTION                                          \
> -   do {                                                         \
> -      yylloc->first_column = yycolumn + 1;                      \
> -      yylloc->first_line = yylineno;                            \
> -      yycolumn += yyleng;                                       \
> -   } while(0);
> +#define YY_USER_ACTION							\
> +	do {								\
> +		if (parser->has_new_line_number)			\
> +			yylineno = parser->new_line_number;		\
> +		if (parser->has_new_source_number)			\
> +			yylloc->source = parser->new_source_number;	\

Could yylineno and yylloc->source be set directly from the rules, rather
than using flags and setting them here?  It seems like they could, but I
imagine you tried that and it doesn't work.

> +		yylloc->first_column = yycolumn + 1;			\
> +		yylloc->first_line = yylineno;				\
> +		yycolumn += yyleng;					\
> +		parser->has_new_line_number = 0;			\
> +		parser->has_new_source_number = 0;			\
> + } while(0);
>  
>  #define YY_USER_INIT			\
>  	do {				\
> @@ -129,35 +135,8 @@ HEXADECIMAL_INTEGER	0[xX][0-9a-fA-F]+[uU]?
>  	return OTHER;
>  }
>  
> -{HASH}line{HSPACE}+{DIGITS}{HSPACE}+{DIGITS}{HSPACE}*$ {
> -	/* Eat characters until the first digit is
> -	 * encountered
> -	 */
> -	char *ptr = yytext;
> -	while (!isdigit(*ptr))
> -		ptr++;
> -
> -	/* Subtract one from the line number because
> -	 * yylineno is zero-based instead of
> -	 * one-based.
> -	 */
> -	yylineno = strtol(ptr, &ptr, 0) - 1;
> -	yylloc->source = strtol(ptr, NULL, 0);
> -}
> -
> -{HASH}line{HSPACE}+{DIGITS}{HSPACE}*$ {
> -	/* Eat characters until the first digit is
> -	 * encountered
> -	 */
> -	char *ptr = yytext;
> -	while (!isdigit(*ptr))
> -		ptr++;
> -
> -	/* Subtract one from the line number because
> -	 * yylineno is zero-based instead of
> -	 * one-based.
> -	 */
> -	yylineno = strtol(ptr, &ptr, 0) - 1;
> +{HASH}line {
> +	return HASH_LINE;
>  }
>  
>  <SKIP,INITIAL>{
> diff --git a/src/glsl/glcpp/glcpp-parse.y b/src/glsl/glcpp/glcpp-parse.y
> index ee00180..cc4af16 100644
> --- a/src/glsl/glcpp/glcpp-parse.y
> +++ b/src/glsl/glcpp/glcpp-parse.y
> @@ -162,7 +162,7 @@ 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 HASH_DEFINE_OBJ HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING NEWLINE OTHER PLACEHOLDER SPACE
> +%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE_FUNC HASH_DEFINE_OBJ 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
>  %token PASTE
>  %type <ival> expression INTEGER operator SPACE integer_constant
>  %type <str> IDENTIFIER INTEGER_STRING OTHER
> @@ -208,6 +208,24 @@ expanded_line:
>  |	ELIF_EXPANDED expression NEWLINE {
>  		_glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2);
>  	}
> +|	LINE_EXPANDED integer_constant NEWLINE {
> +		parser->has_new_line_number = 1;
> +		parser->new_line_number = $2;
> +		ralloc_asprintf_rewrite_tail (&parser->output,
> +					      &parser->output_length,
> +					      "#line %" PRIiMAX,
> +					      $2);
> +	}
> +|	LINE_EXPANDED integer_constant integer_constant NEWLINE {
> +		parser->has_new_line_number = 1;
> +		parser->new_line_number = $2;
> +		parser->has_new_source_number = 1;
> +		parser->new_source_number = $3;
> +		ralloc_asprintf_rewrite_tail (&parser->output,
> +					      &parser->output_length,
> +					      "#line %" PRIiMAX " %" PRIiMAX,
> +					      $2, $3);
> +	}
>  ;
>  
>  control_line:
> @@ -228,6 +246,14 @@ control_line:
>  		}
>  		ralloc_free ($2);
>  	}
> +|	HASH_LINE pp_tokens NEWLINE {
> +		if (parser->skip_stack == NULL ||
> +		    parser->skip_stack->type == SKIP_NO_SKIP)
> +		{
> +			_glcpp_parser_expand_and_lex_from (parser,
> +							   LINE_EXPANDED, $2);
> +		}
> +	}

Hm.  I originally thought #line took effect regardless of any #if trees,
but it looks like that's not the case.  Feeding GCC's cpp the following
yields 20, and no mention of 10:

#ifdef FOO
#line 10
#else
#line 20
#endif

So I think this is right.

>  |	HASH_IF conditional_tokens NEWLINE {
>  		/* Be careful to only evaluate the 'if' expression if
>  		 * we are not skipping. When we are skipping, we
> @@ -1120,6 +1146,11 @@ glcpp_parser_create (const struct gl_extensions *extensions, int api)
>  	parser->info_log_length = 0;
>  	parser->error = 0;
>  
> +	parser->has_new_line_number = 0;
> +	parser->new_line_number = 1;
> +	parser->has_new_source_number = 0;
> +	parser->new_source_number = 0;
> +
>  	/* Add pre-defined macros. */
>  	add_builtin_define(parser, "GL_ARB_draw_buffers", 1);
>  	add_builtin_define(parser, "GL_ARB_texture_rectangle", 1);
> diff --git a/src/glsl/glcpp/glcpp.h b/src/glsl/glcpp/glcpp.h
> index 2d7cad2..9d4005c 100644
> --- a/src/glsl/glcpp/glcpp.h
> +++ b/src/glsl/glcpp/glcpp.h
> @@ -177,6 +177,10 @@ struct glcpp_parser {
>  	size_t output_length;
>  	size_t info_log_length;
>  	int error;
> +	int has_new_line_number;
> +	int new_line_number;
> +	int has_new_source_number;
> +	int new_source_number;
>  };

Could you please #include <stdbool.h> and use 'bool' instead of 'int'
for the has_* variables?  This is not only a bit nicer to read, it
causes the compiler to generate proper warnings if you do something
silly like assign GL_FLAT to a boolean.

>  struct gl_extensions;
> diff --git a/src/glsl/glcpp/tests/091-hash-line.c.expected b/src/glsl/glcpp/tests/091-hash-line.c.expected
> index e663398..ea29149 100644
> --- a/src/glsl/glcpp/tests/091-hash-line.c.expected
> +++ b/src/glsl/glcpp/tests/091-hash-line.c.expected
> @@ -3,11 +3,11 @@
>  1:0(1): preprocessor error: #error source 1, line 0 error
>  2:30(1): preprocessor error: #error source 2, line 30 error
>  
> +#line 0
>  
> +#line 25
>  
> +#line 0 1
>  
> -
> -
> -
> -
> +#line 30 2

Other than those minor comments, this looks good.  Thanks for doing
this!  Way better than my hack.

For the series:
Reviewed-by: Kenneth Graunke <kenneth at whitecape.org>


More information about the mesa-dev mailing list