3 * Copyright © 2010 Intel Corporation
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
32 yyerror (void *scanner, const char *error);
35 _define_object_macro (glcpp_parser_t *parser,
37 token_list_t *replacements);
40 _define_function_macro (glcpp_parser_t *parser,
42 string_list_t *parameters,
43 token_list_t *replacements);
46 _expand_object_macro (glcpp_parser_t *parser, const char *identifier);
49 _expand_function_macro (glcpp_parser_t *parser,
50 const char *identifier,
51 argument_list_t *arguments);
54 _string_list_create (void *ctx);
57 _string_list_append_item (string_list_t *list, const char *str);
60 _string_list_append_list (string_list_t *list, string_list_t *tail);
63 _string_list_contains (string_list_t *list, const char *member, int *index);
66 _string_list_length (string_list_t *list);
69 _argument_list_create (void *ctx);
72 _argument_list_append (argument_list_t *list, token_list_t *argument);
75 _argument_list_length (argument_list_t *list);
78 _argument_list_member_at (argument_list_t *list, int index);
81 _token_list_create (void *ctx);
84 _token_list_append (token_list_t *list, int type, const char *value);
87 _token_list_append_list (token_list_t *list, token_list_t *tail);
90 glcpp_parser_pop_expansion (glcpp_parser_t *parser);
93 _glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, int condition);
96 _glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, const char *type,
100 _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser);
102 #define yylex glcpp_parser_lex
105 glcpp_parser_lex (glcpp_parser_t *parser);
112 argument_list_t *argument_list;
113 string_list_t *string_list;
115 token_list_t *token_list;
118 %parse-param {glcpp_parser_t *parser}
119 %lex-param {glcpp_parser_t *parser}
121 %token DEFINE DEFINED ELIF ELSE ENDIF FUNC_MACRO IDENTIFIER IDENTIFIER_FINALIZED IF IFDEF IFNDEF INTEGER OBJ_MACRO NEWLINE SPACE TOKEN UNDEF
122 %type <ival> expression INTEGER punctuator
123 %type <str> content FUNC_MACRO IDENTIFIER IDENTIFIER_FINALIZED OBJ_MACRO
124 %type <argument_list> argument_list
125 %type <string_list> macro parameter_list
126 %type <token> TOKEN argument_word argument_word_or_comma
127 %type <token_list> argument argument_or_comma replacement_list pp_tokens
133 %left EQUAL NOT_EQUAL
134 %left '<' '>' LESS_OR_EQUAL GREATER_OR_EQUAL
135 %left LEFT_SHIFT RIGHT_SHIFT
140 /* Hard to remove shift/reduce conflicts documented as follows:
142 * 1. '(' after FUNC_MACRO name which is correctly resolved to shift
143 * to form macro invocation rather than reducing directly to
146 * 2. Similarly, '(' after FUNC_MACRO which is correctly resolved to
147 * shift to form macro invocation rather than reducing directly to
150 * 3. Similarly again now that we added argument_or_comma as well.
156 /* We do all printing at the input level. */
159 parser->just_printed_separator = 1;
165 if (parser->skip_stack && parser->skip_stack->type != SKIP_NO_SKIP)
168 if ($2 && strlen ($2) && ! skipping) {
170 int is_not_separator = ((c >= 'a' && c <= 'z') ||
171 (c >= 'A' && c <= 'Z') ||
172 (c >= 'A' && c <= 'Z') ||
173 (c >= '0' && c <= '9') ||
176 if (! parser->just_printed_separator && is_not_separator)
182 if (is_not_separator)
183 parser->just_printed_separator = 0;
185 parser->just_printed_separator = 1;
191 if (parser->need_newline) {
193 parser->just_printed_separator = 1;
194 parser->need_newline = 0;
203 | IDENTIFIER_FINALIZED {
213 $$ = talloc_strdup (parser, "\n");
216 $$ = talloc_asprintf (parser, "%c", $1);
230 FUNC_MACRO '(' argument_list ')' {
231 _expand_function_macro (parser, $1, $3);
234 _expand_object_macro (parser, $1);
241 $$ = _argument_list_create (parser);
244 $$ = _argument_list_create (parser);
245 _argument_list_append ($$, $1);
247 | argument_list ',' argument {
248 _argument_list_append ($1, $3);
255 $$ = _token_list_create (parser);
256 _token_list_append ($$, $1.type, $1.value);
258 | argument argument_word {
259 _token_list_append ($1, $2.type, $2.value);
260 talloc_free ($2.value);
263 | argument '(' argument_or_comma ')' {
264 _token_list_append ($1, '(', "(");
265 _token_list_append_list ($1, $3);
266 _token_list_append ($1, ')', ")");
272 IDENTIFIER { $$.type = IDENTIFIER; $$.value = $1; }
273 | IDENTIFIER_FINALIZED { $$.type = IDENTIFIER_FINALIZED; $$.value = $1; }
275 | FUNC_MACRO { $$.type = FUNC_MACRO; $$.value = $1; }
276 | macro { $$.type = TOKEN; $$.value = xtalloc_strdup (parser, ""); }
279 /* XXX: The body of argument_or_comma is the same as the body
280 * of argument, but with "argument" and "argument_word"
281 * changed to "argument_or_comma" and
282 * "argument_word_or_comma". It would be nice to have less
283 * redundancy here, but I'm not sure how.
285 * It would also be nice to have a less ugly grammar to have
286 * to implement, but such is the C preprocessor.
289 argument_word_or_comma {
290 $$ = _token_list_create (parser);
291 _token_list_append ($$, $1.type, $1.value);
293 | argument_or_comma argument_word_or_comma {
294 _token_list_append ($1, $2.type, $2.value);
297 | argument_or_comma '(' argument_or_comma ')' {
298 _token_list_append ($1, '(', "(");
299 _token_list_append_list ($1, $3);
300 _token_list_append ($1, ')', ")");
305 argument_word_or_comma:
306 IDENTIFIER { $$.type = IDENTIFIER; $$.value = $1; }
307 | IDENTIFIER_FINALIZED { $$.type = IDENTIFIER_FINALIZED; $$.value = $1; }
309 | FUNC_MACRO { $$.type = FUNC_MACRO; $$.value = $1; }
310 | macro { $$.type = TOKEN; $$.value = xtalloc_strdup (parser, ""); }
311 | ',' { $$.type = ','; $$.value = xtalloc_strdup (parser, ","); }
315 DEFINE IDENTIFIER NEWLINE {
316 token_list_t *list = _token_list_create (parser);
317 _define_object_macro (parser, $2, list);
319 | DEFINE IDENTIFIER SPACE replacement_list NEWLINE {
320 _define_object_macro (parser, $2, $4);
322 | DEFINE IDENTIFIER '(' parameter_list ')' replacement_list NEWLINE {
323 _define_function_macro (parser, $2, $4, $6);
325 | IF expression NEWLINE {
326 _glcpp_parser_skip_stack_push_if (parser, $2);
328 | IFDEF IDENTIFIER NEWLINE {
329 string_list_t *macro = hash_table_find (parser->defines, $2);
331 _glcpp_parser_skip_stack_push_if (parser, macro != NULL);
333 | IFNDEF IDENTIFIER NEWLINE {
334 string_list_t *macro = hash_table_find (parser->defines, $2);
336 _glcpp_parser_skip_stack_push_if (parser, macro == NULL);
338 | ELIF expression NEWLINE {
339 _glcpp_parser_skip_stack_change_if (parser, "#elif", $2);
342 _glcpp_parser_skip_stack_change_if (parser, "else", 1);
345 _glcpp_parser_skip_stack_pop (parser);
348 string_list_t *macro = hash_table_find (parser->defines, $2);
350 /* XXX: Need hash table to support a real way
351 * to remove an element rather than prefixing
352 * a new node with data of NULL like this. */
353 hash_table_insert (parser->defines, NULL, $2);
364 | expression OR expression {
367 | expression AND expression {
370 | expression '|' expression {
373 | expression '^' expression {
376 | expression '&' expression {
379 | expression NOT_EQUAL expression {
382 | expression EQUAL expression {
385 | expression GREATER_OR_EQUAL expression {
388 | expression LESS_OR_EQUAL expression {
391 | expression '>' expression {
394 | expression '<' expression {
397 | expression RIGHT_SHIFT expression {
400 | expression LEFT_SHIFT expression {
403 | expression '-' expression {
406 | expression '+' expression {
409 | expression '%' expression {
412 | expression '/' expression {
415 | expression '*' expression {
418 | '!' expression %prec UNARY {
421 | '~' expression %prec UNARY {
424 | '-' expression %prec UNARY {
427 | '+' expression %prec UNARY {
430 | DEFINED IDENTIFIER %prec UNARY {
431 string_list_t *macro = hash_table_find (parser->defines, $2);
438 | '(' expression ')' {
445 $$ = _string_list_create (parser);
448 $$ = _string_list_create (parser);
449 _string_list_append_item ($$, $1);
452 | parameter_list ',' IDENTIFIER {
453 _string_list_append_item ($1, $3);
461 $$ = _token_list_create (parser);
471 $$ = _token_list_create (parser);
472 _token_list_append ($$, $1.type, $1.value);
475 _token_list_append ($1, $2.type, $2.value);
483 _string_list_create (void *ctx)
487 list = xtalloc (ctx, string_list_t);
495 _string_list_append_list (string_list_t *list, string_list_t *tail)
497 if (list->head == NULL) {
498 list->head = tail->head;
500 list->tail->next = tail->head;
503 list->tail = tail->tail;
507 _string_list_append_item (string_list_t *list, const char *str)
511 node = xtalloc (list, string_node_t);
512 node->str = xtalloc_strdup (node, str);
516 if (list->head == NULL) {
519 list->tail->next = node;
526 _string_list_contains (string_list_t *list, const char *member, int *index)
534 for (i = 0, node = list->head; node; i++, node = node->next) {
535 if (strcmp (node->str, member) == 0) {
546 _string_list_length (string_list_t *list)
554 for (node = list->head; node; node = node->next)
561 _argument_list_create (void *ctx)
563 argument_list_t *list;
565 list = xtalloc (ctx, argument_list_t);
573 _argument_list_append (argument_list_t *list, token_list_t *argument)
575 argument_node_t *node;
577 if (argument == NULL || argument->head == NULL)
580 node = xtalloc (list, argument_node_t);
581 node->argument = argument;
585 if (list->head == NULL) {
588 list->tail->next = node;
595 _argument_list_length (argument_list_t *list)
598 argument_node_t *node;
603 for (node = list->head; node; node = node->next)
610 _argument_list_member_at (argument_list_t *list, int index)
612 argument_node_t *node;
619 for (i = 0; i < index; i++) {
626 return node->argument;
632 _token_list_create (void *ctx)
636 list = xtalloc (ctx, token_list_t);
644 _token_list_append (token_list_t *list, int type, const char *value)
648 node = xtalloc (list, token_node_t);
650 node->value = xtalloc_strdup (list, value);
654 if (list->head == NULL) {
657 list->tail->next = node;
664 _token_list_append_list (token_list_t *list, token_list_t *tail)
666 if (list->head == NULL) {
667 list->head = tail->head;
669 list->tail->next = tail->head;
672 list->tail = tail->tail;
676 yyerror (void *scanner, const char *error)
678 fprintf (stderr, "Parse error: %s\n", error);
682 glcpp_parser_create (void)
684 glcpp_parser_t *parser;
686 parser = xtalloc (NULL, glcpp_parser_t);
688 glcpp_lex_init_extra (parser, &parser->scanner);
689 parser->defines = hash_table_ctor (32, hash_table_string_hash,
690 hash_table_string_compare);
691 parser->expansions = NULL;
693 parser->just_printed_separator = 1;
694 parser->need_newline = 0;
696 parser->skip_stack = NULL;
702 glcpp_parser_parse (glcpp_parser_t *parser)
704 return yyparse (parser);
708 glcpp_parser_destroy (glcpp_parser_t *parser)
710 if (parser->need_newline)
712 if (parser->skip_stack)
713 fprintf (stderr, "Error: Unterminated #if\n");
714 glcpp_lex_destroy (parser->scanner);
715 hash_table_dtor (parser->defines);
716 talloc_free (parser);
720 glcpp_parser_is_expanding (glcpp_parser_t *parser, const char *member)
722 expansion_node_t *node;
724 for (node = parser->expansions; node; node = node->next) {
726 strcmp (node->macro->identifier, member) == 0)
736 glcpp_parser_classify_token (glcpp_parser_t *parser,
737 const char *identifier,
738 int *parameter_index)
742 /* Is this token a defined macro? */
743 macro = hash_table_find (parser->defines, identifier);
746 return TOKEN_CLASS_IDENTIFIER;
748 /* Don't consider this a macro if we are already actively
749 * expanding this macro. */
750 if (glcpp_parser_is_expanding (parser, identifier))
751 return TOKEN_CLASS_IDENTIFIER_FINALIZED;
753 /* Definitely a macro. Just need to check if it's function-like. */
754 if (macro->is_function)
755 return TOKEN_CLASS_FUNC_MACRO;
757 return TOKEN_CLASS_OBJ_MACRO;
761 _define_object_macro (glcpp_parser_t *parser,
762 const char *identifier,
763 token_list_t *replacements)
767 macro = xtalloc (parser, macro_t);
769 macro->is_function = 0;
770 macro->parameters = NULL;
771 macro->identifier = talloc_strdup (macro, identifier);
772 macro->replacements = talloc_steal (macro, replacements);
774 hash_table_insert (parser->defines, macro, identifier);
778 _define_function_macro (glcpp_parser_t *parser,
779 const char *identifier,
780 string_list_t *parameters,
781 token_list_t *replacements)
785 macro = xtalloc (parser, macro_t);
787 macro->is_function = 1;
788 macro->parameters = talloc_steal (macro, parameters);
789 macro->identifier = talloc_strdup (macro, identifier);
790 macro->replacements = talloc_steal (macro, replacements);
792 hash_table_insert (parser->defines, macro, identifier);
796 _glcpp_parser_push_expansion (glcpp_parser_t *parser,
798 token_node_t *replacements)
800 expansion_node_t *node;
802 node = xtalloc (parser, expansion_node_t);
805 node->replacements = replacements;
807 node->next = parser->expansions;
808 parser->expansions = node;
812 glcpp_parser_pop_expansion (glcpp_parser_t *parser)
814 expansion_node_t *node;
816 node = parser->expansions;
819 fprintf (stderr, "Internal error: _expansion_list_pop called on an empty list.\n");
823 parser->expansions = node->next;
829 _expand_object_macro (glcpp_parser_t *parser, const char *identifier)
833 macro = hash_table_find (parser->defines, identifier);
834 assert (! macro->is_function);
835 assert (! glcpp_parser_is_expanding (parser, identifier));
837 _glcpp_parser_push_expansion (parser, macro, macro->replacements->head);
841 _expand_function_macro (glcpp_parser_t *parser,
842 const char *identifier,
843 argument_list_t *arguments)
846 token_list_t *expanded;
850 macro = hash_table_find (parser->defines, identifier);
851 assert (macro->is_function);
852 assert (! glcpp_parser_is_expanding (parser, identifier));
854 if (_argument_list_length (arguments) !=
855 _string_list_length (macro->parameters))
858 "Error: macro %s invoked with %d arguments (expected %d)\n",
860 _argument_list_length (arguments),
861 _string_list_length (macro->parameters));
865 expanded = _token_list_create (macro);
867 for (i = macro->replacements->head; i; i = i->next) {
868 if (_string_list_contains (macro->parameters, i->value,
871 token_list_t *argument;
872 argument = _argument_list_member_at (arguments,
874 for (j = argument->head; j; j = j->next)
876 _token_list_append (expanded, j->type,
880 _token_list_append (expanded, i->type, i->value);
884 _glcpp_parser_push_expansion (parser, macro, expanded->head);
888 glcpp_parser_lex (glcpp_parser_t *parser)
890 expansion_node_t *expansion;
891 token_node_t *replacements;
896 /* Who says C can't do efficient tail recursion? */
899 expansion = parser->expansions;
901 if (expansion == NULL)
902 return glcpp_lex (parser->scanner);
904 replacements = expansion->replacements;
906 /* Pop expansion when replacements is exhausted. */
907 if (replacements == NULL) {
908 glcpp_parser_pop_expansion (parser);
912 expansion->replacements = replacements->next;
914 token = replacements->value;
916 /* Implement token pasting. */
917 if (replacements->next && strcmp (replacements->next->value, "##") == 0) {
918 token_node_t *next_node;
920 next_node = replacements->next->next;
922 if (next_node == NULL) {
923 fprintf (stderr, "Error: '##' cannot appear at the end of a macro expansion.\n");
927 token = xtalloc_asprintf (parser, "%s%s",
928 token, next_node->value);
929 expansion->replacements = next_node->next;
933 if (strcmp (token, "(") == 0)
935 else if (strcmp (token, ")") == 0)
938 yylval.str = xtalloc_strdup (parser, token);
940 /* Carefully refuse to expand any finalized identifier. */
941 if (replacements->type == IDENTIFIER_FINALIZED)
942 return IDENTIFIER_FINALIZED;
944 switch (glcpp_parser_classify_token (parser, yylval.str,
947 case TOKEN_CLASS_IDENTIFIER:
950 case TOKEN_CLASS_IDENTIFIER_FINALIZED:
951 return IDENTIFIER_FINALIZED;
953 case TOKEN_CLASS_FUNC_MACRO:
957 case TOKEN_CLASS_OBJ_MACRO:
964 _glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, int condition)
966 skip_type_t current = SKIP_NO_SKIP;
969 if (parser->skip_stack)
970 current = parser->skip_stack->type;
972 node = xtalloc (parser, skip_node_t);
974 if (current == SKIP_NO_SKIP) {
976 node->type = SKIP_NO_SKIP;
978 node->type = SKIP_TO_ELSE;
980 node->type = SKIP_TO_ENDIF;
983 node->next = parser->skip_stack;
984 parser->skip_stack = node;
988 _glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, const char *type,
991 if (parser->skip_stack == NULL) {
992 fprintf (stderr, "Error: %s without #if\n", type);
996 if (parser->skip_stack->type == SKIP_TO_ELSE) {
998 parser->skip_stack->type = SKIP_NO_SKIP;
1000 parser->skip_stack->type = SKIP_TO_ENDIF;
1005 _glcpp_parser_skip_stack_pop (glcpp_parser_t *parser)
1009 if (parser->skip_stack == NULL) {
1010 fprintf (stderr, "Error: #endif without #if\n");
1014 node = parser->skip_stack;
1015 parser->skip_stack = node->next;