Implement #if, #else, #elif, and #endif with tests.
authorCarl Worth <cworth@cworth.org>
Fri, 21 May 2010 05:27:07 +0000 (22:27 -0700)
committerCarl Worth <cworth@cworth.org>
Fri, 21 May 2010 05:27:07 +0000 (22:27 -0700)
So far the only expression implemented is a single integer literal,
but obviously that's easy to extend. Various things including nesting
are tested here.

13 files changed:
glcpp-lex.l
glcpp-parse.y
glcpp.h
tests/040-token-pasting.c [new file with mode: 0644]
tests/041-if-0.c [new file with mode: 0644]
tests/042-if-1.c [new file with mode: 0644]
tests/043-if-0-else.c [new file with mode: 0644]
tests/044-if-1-else.c [new file with mode: 0644]
tests/045-if-0-elif.c [new file with mode: 0644]
tests/046-if-1-elsif.c [new file with mode: 0644]
tests/047-if-elif-else.c [new file with mode: 0644]
tests/048-if-nested.c [new file with mode: 0644]
tests/glcpp-test

index 6138a9de12ef48b94062e9454036d8e8a826486c..825ce3d370925457e8cd822bac12587d609684ae 100644 (file)
@@ -36,6 +36,7 @@
 %x ST_DEFINE_OBJ_OR_FUNC
 %x ST_DEFINE_PARAMETER
 %x ST_DEFINE_VALUE
+%x ST_IF
 %x ST_UNDEF
 %x ST_UNDEF_END
 
@@ -44,11 +45,42 @@ NONSPACE    [^[:space:]]
 NEWLINE                [\n]
 HSPACE         [ \t]
 HASH           ^{HSPACE}*#{HSPACE}*
+INTEGER                [0-9]+
 IDENTIFIER     [_a-zA-Z][_a-zA-Z0-9]*
 TOKEN          [^[:space:](),]+
 
 %%
 
+{HASH}if{HSPACE}* {
+       BEGIN ST_IF;
+       return IF;
+}
+
+{HASH}elif{HSPACE}* {
+       BEGIN ST_IF;
+       return ELIF;
+}
+
+<ST_IF>{INTEGER} {
+       yylval.ival = atoi (yytext);
+       return INTEGER;
+}
+
+<ST_IF>{HSPACE}+
+
+<ST_IF>\n {
+       BEGIN INITIAL;
+       return NEWLINE;
+}
+
+{HASH}endif{HSPACE}* {
+       return ENDIF;
+}
+
+{HASH}else{HSPACE}* {
+       return ELSE;
+}
+
 {HASH}undef{HSPACE}* {
        BEGIN ST_UNDEF;
        return UNDEF;
index aa758f7e43956d7d6e9def5e7a1bd4133cfb811b..26432f203254ff29884b74a8b998f21b47f1cc25 100644 (file)
@@ -89,6 +89,16 @@ _token_list_append_list (token_list_t *list, token_list_t *tail);
 static void
 glcpp_parser_pop_expansion (glcpp_parser_t *parser);
 
+static void
+_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, int condition);
+
+static void
+_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, const char *type,
+                                   int condition);
+                       
+static void
+_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser);
+
 #define yylex glcpp_parser_lex
 
 static int
@@ -108,8 +118,8 @@ glcpp_parser_lex (glcpp_parser_t *parser);
 %parse-param {glcpp_parser_t *parser}
 %lex-param {glcpp_parser_t *parser}
 
-%token DEFINE FUNC_MACRO IDENTIFIER IDENTIFIER_FINALIZED OBJ_MACRO NEWLINE SEPARATOR SPACE TOKEN UNDEF
-%type <ival> punctuator
+%token DEFINE ELIF ELSE ENDIF FUNC_MACRO IDENTIFIER IDENTIFIER_FINALIZED IF IFDEF IFNDEF INTEGER OBJ_MACRO NEWLINE SEPARATOR SPACE TOKEN UNDEF
+%type <ival> expression INTEGER punctuator
 %type <str> content FUNC_MACRO IDENTIFIER IDENTIFIER_FINALIZED OBJ_MACRO
 %type <argument_list> argument_list
 %type <string_list> macro parameter_list
@@ -143,8 +153,12 @@ input:
        }
 |      input content {
                int is_token;
+               int skipping = 0;
+
+               if (parser->skip_stack && parser->skip_stack->type != SKIP_NO_SKIP)
+                       skipping = 1;
 
-               if ($2 && strlen ($2)) {
+               if ($2 && strlen ($2) && ! skipping) {
                        int c = $2[0];
                        int is_not_separator = ((c >= 'a' && c <= 'z') ||
                                                (c >= 'A' && c <= 'Z') ||
@@ -301,6 +315,28 @@ directive:
 |      DEFINE IDENTIFIER '(' parameter_list ')' replacement_list NEWLINE {
                _define_function_macro (parser, $2, $4, $6);
        }
+|      IF expression NEWLINE {
+               _glcpp_parser_skip_stack_push_if (parser, $2);
+       }
+|      IFDEF IDENTIFIER NEWLINE {
+               string_list_t *macro = hash_table_find (parser->defines, $2);
+               talloc_free ($2);
+               _glcpp_parser_skip_stack_push_if (parser, macro != NULL);
+       }
+|      IFNDEF IDENTIFIER NEWLINE {
+               string_list_t *macro = hash_table_find (parser->defines, $2);
+               talloc_free ($2);
+               _glcpp_parser_skip_stack_push_if (parser, macro == NULL);
+       }
+|      ELIF expression NEWLINE {
+               _glcpp_parser_skip_stack_change_if (parser, "#elif", $2);
+       }
+|      ELSE {
+               _glcpp_parser_skip_stack_change_if (parser, "else", 1);
+       }
+|      ENDIF {
+               _glcpp_parser_skip_stack_pop (parser);
+       }
 |      UNDEF IDENTIFIER {
                string_list_t *macro = hash_table_find (parser->defines, $2);
                if (macro) {
@@ -314,6 +350,13 @@ directive:
        }
 ;
 
+/* XXX: Need to fill out with all operators. */
+expression:
+       INTEGER {
+               $$ = $1;
+       }
+;
+
 parameter_list:
        /* empty */ {
                $$ = _string_list_create (parser);
@@ -567,6 +610,8 @@ glcpp_parser_create (void)
        parser->just_printed_separator = 1;
        parser->need_newline = 0;
 
+       parser->skip_stack = NULL;
+
        return parser;
 }
 
@@ -581,6 +626,8 @@ glcpp_parser_destroy (glcpp_parser_t *parser)
 {
        if (parser->need_newline)
                printf ("\n");
+       if (parser->skip_stack)
+               fprintf (stderr, "Error: Unterminated #if\n");
        glcpp_lex_destroy (parser->scanner);
        hash_table_dtor (parser->defines);
        talloc_free (parser);
@@ -829,3 +876,59 @@ glcpp_parser_lex (glcpp_parser_t *parser)
                break;
        }
 }
+
+static void
+_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, int condition)
+{
+       skip_type_t current = SKIP_NO_SKIP;
+       skip_node_t *node;
+
+       if (parser->skip_stack)
+               current = parser->skip_stack->type;
+
+       node = xtalloc (parser, skip_node_t);
+
+       if (current == SKIP_NO_SKIP) {
+               if (condition)
+                       node->type = SKIP_NO_SKIP;
+               else
+                       node->type = SKIP_TO_ELSE;
+       } else {
+               node->type = SKIP_TO_ENDIF;
+       }
+
+       node->next = parser->skip_stack;
+       parser->skip_stack = node;
+}
+
+static void
+_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, const char *type,
+                                   int condition)
+{
+       if (parser->skip_stack == NULL) {
+               fprintf (stderr, "Error: %s without #if\n", type);
+               exit (1);
+       }
+
+       if (parser->skip_stack->type == SKIP_TO_ELSE) {
+               if (condition)
+                       parser->skip_stack->type = SKIP_NO_SKIP;
+       } else {
+               parser->skip_stack->type = SKIP_TO_ENDIF;
+       }
+}
+                       
+static void
+_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser)
+{
+       skip_node_t *node;
+
+       if (parser->skip_stack == NULL) {
+               fprintf (stderr, "Error: #endif without #if\n");
+               exit (1);
+       }
+
+       node = parser->skip_stack;
+       parser->skip_stack = node->next;
+       talloc_free (node);
+}
diff --git a/glcpp.h b/glcpp.h
index 1537109ada6a449f6695a43cfc1f66f771102d5b..33ece8f92b114fa5165a2700811128826d7113b1 100644 (file)
--- a/glcpp.h
+++ b/glcpp.h
@@ -95,12 +95,24 @@ typedef struct expansion_node {
        struct expansion_node *next;
 } expansion_node_t;
 
+typedef enum skip_type {
+       SKIP_NO_SKIP,
+       SKIP_TO_ELSE,
+       SKIP_TO_ENDIF
+} skip_type_t;
+
+typedef struct skip_node {
+       skip_type_t type;
+       struct skip_node *next;
+} skip_node_t;
+
 struct glcpp_parser {
        yyscan_t scanner;
        struct hash_table *defines;
        expansion_node_t *expansions;
        int just_printed_separator;
        int need_newline;
+       skip_node_t *skip_stack;
 };
 
 void
diff --git a/tests/040-token-pasting.c b/tests/040-token-pasting.c
new file mode 100644 (file)
index 0000000..caab3ba
--- /dev/null
@@ -0,0 +1,2 @@
+#define paste(a,b) a ## b
+paste(one , token)
diff --git a/tests/041-if-0.c b/tests/041-if-0.c
new file mode 100644 (file)
index 0000000..2cab677
--- /dev/null
@@ -0,0 +1,5 @@
+success_1
+#if 0
+failure
+#endif
+success_2
diff --git a/tests/042-if-1.c b/tests/042-if-1.c
new file mode 100644 (file)
index 0000000..874a25c
--- /dev/null
@@ -0,0 +1,5 @@
+success_1
+#if 1
+success_2
+#endif
+success_3
diff --git a/tests/043-if-0-else.c b/tests/043-if-0-else.c
new file mode 100644 (file)
index 0000000..323351f
--- /dev/null
@@ -0,0 +1,7 @@
+success_1
+#if 0
+failure
+#else
+success_2
+#endif
+success_3
diff --git a/tests/044-if-1-else.c b/tests/044-if-1-else.c
new file mode 100644 (file)
index 0000000..28dfc25
--- /dev/null
@@ -0,0 +1,7 @@
+success_1
+#if 1
+success_2
+#else
+failure
+#endif
+success_3
diff --git a/tests/045-if-0-elif.c b/tests/045-if-0-elif.c
new file mode 100644 (file)
index 0000000..e50f686
--- /dev/null
@@ -0,0 +1,11 @@
+success_1
+#if 0
+failure_1
+#elif 0
+failure_2
+#elif 1
+success_3
+#elif 1
+failure_3
+#endif
+success_4
diff --git a/tests/046-if-1-elsif.c b/tests/046-if-1-elsif.c
new file mode 100644 (file)
index 0000000..130515a
--- /dev/null
@@ -0,0 +1,11 @@
+success_1
+#if 1
+success_2
+#elif 0
+failure_1
+#elif 1
+failure_2
+#elif 0
+failure_3
+#endif
+success_3
diff --git a/tests/047-if-elif-else.c b/tests/047-if-elif-else.c
new file mode 100644 (file)
index 0000000..e8f0838
--- /dev/null
@@ -0,0 +1,11 @@
+success_1
+#if 0
+failure_1
+#elif 0
+failure_2
+#elif 0
+failure_3
+#else
+success_2
+#endif
+success_3
diff --git a/tests/048-if-nested.c b/tests/048-if-nested.c
new file mode 100644 (file)
index 0000000..fc4679c
--- /dev/null
@@ -0,0 +1,11 @@
+success_1
+#if 0
+failure_1
+#if 1
+failure_2
+#else
+failure_3
+#endif
+failure_4
+#endif
+success_2
index 25685eeabe5448bb0da76a882def22ccdbb6bac1..022a23671216f713be74c1b71b8fdbfa155ece9c 100755 (executable)
@@ -5,5 +5,5 @@ for test in *.c; do
     ../glcpp < $test > $test.out
     gcc -E $test -o $test.gcc
     grep -v '^#' < $test.gcc > $test.expected
-    diff -u $test.expected $test.out
+    diff -B -u $test.expected $test.out
 done