Implement #if, #else, #elif, and #endif with tests.

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.
This commit is contained in:
Carl Worth 2010-05-20 22:27:07 -07:00
parent d8327e575d
commit b20d33c5c6
13 changed files with 221 additions and 4 deletions

View 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;

View 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 ($2 && strlen ($2)) {
if (parser->skip_stack && parser->skip_stack->type != SKIP_NO_SKIP)
skipping = 1;
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);
}

12
glcpp.h
View file

@ -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

View file

@ -0,0 +1,2 @@
#define paste(a,b) a ## b
paste(one , token)

5
tests/041-if-0.c Normal file
View file

@ -0,0 +1,5 @@
success_1
#if 0
failure
#endif
success_2

5
tests/042-if-1.c Normal file
View file

@ -0,0 +1,5 @@
success_1
#if 1
success_2
#endif
success_3

7
tests/043-if-0-else.c Normal file
View file

@ -0,0 +1,7 @@
success_1
#if 0
failure
#else
success_2
#endif
success_3

7
tests/044-if-1-else.c Normal file
View file

@ -0,0 +1,7 @@
success_1
#if 1
success_2
#else
failure
#endif
success_3

11
tests/045-if-0-elif.c Normal file
View file

@ -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

11
tests/046-if-1-elsif.c Normal file
View file

@ -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

11
tests/047-if-elif-else.c Normal file
View file

@ -0,0 +1,11 @@
success_1
#if 0
failure_1
#elif 0
failure_2
#elif 0
failure_3
#else
success_2
#endif
success_3

11
tests/048-if-nested.c Normal file
View file

@ -0,0 +1,11 @@
success_1
#if 0
failure_1
#if 1
failure_2
#else
failure_3
#endif
failure_4
#endif
success_2

View file

@ -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