X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=preprocessor.c;h=cc6308bbe7fde49bd560f6b468fbecd16ac14e0c;hb=9bf914ca0a1473ee629d30d8a6a65cfbd633712b;hp=012d90bd74a1c05bfbf01034d7c4ada9f0bc596f;hpb=ae52b69dfadbd0835e01f55167d979cb6a9a6305;p=cparser diff --git a/preprocessor.c b/preprocessor.c index 012d90b..cc6308b 100644 --- a/preprocessor.c +++ b/preprocessor.c @@ -1,107 +1,141 @@ #include +#include +#include +#include +#include +#include + #include "token_t.h" #include "symbol_t.h" #include "adt/util.h" #include "adt/error.h" +#include "adt/strutil.h" +#include "adt/strset.h" #include "lang_features.h" #include "diagnostic.h" #include "string_rep.h" +#include "input.h" -#include -#include -#include -#include -#include - -//#define DEBUG_CHARS #define MAX_PUTBACK 3 #define INCLUDE_LIMIT 199 /* 199 is for gcc "compatibility" */ +struct pp_argument_t { + size_t list_len; + token_t *token_list; +}; + struct pp_definition_t { symbol_t *symbol; source_position_t source_position; pp_definition_t *parent_expansion; size_t expand_pos; - bool is_variadic : 1; - bool is_expanding : 1; - size_t argument_count; - token_t *arguments; + bool is_variadic : 1; + bool is_expanding : 1; + bool has_parameters : 1; + size_t n_parameters; + symbol_t *parameters; + + /* replacement */ size_t list_len; - token_t *replacement_list; + token_t *token_list; + +}; + +typedef struct pp_conditional_t pp_conditional_t; +struct pp_conditional_t { + source_position_t source_position; + bool condition; + bool in_else; + bool skip; /**< conditional in skip mode (then+else gets skipped) */ + pp_conditional_t *parent; }; typedef struct pp_input_t pp_input_t; struct pp_input_t { FILE *file; - int c; - char buf[1024+MAX_PUTBACK]; - const char *bufend; - const char *bufpos; + input_t *input; + utf32 c; + utf32 buf[1024+MAX_PUTBACK]; + const utf32 *bufend; + const utf32 *bufpos; source_position_t position; - bool had_non_space; pp_input_t *parent; + unsigned output_line; +}; + +/** additional info about the current token */ +typedef struct add_token_info_t { + /** whitespace from beginning of line to the token */ + unsigned whitespace; + /** there has been any whitespace before the token */ + bool had_whitespace; + /** the token is at the beginning of the line */ + bool at_line_begin; +} add_token_info_t; + +typedef struct searchpath_entry_t searchpath_entry_t; +struct searchpath_entry_t { + const char *path; + searchpath_entry_t *next; }; -pp_input_t input; -#define CC input.c +static pp_input_t input; static pp_input_t *input_stack; static unsigned n_inputs; static struct obstack input_obstack; -token_t pp_token; -static bool resolve_escape_sequences = false; -static bool do_print_spaces = true; -static FILE *out; -static struct obstack pp_obstack; -static unsigned counted_newlines; -static unsigned counted_spaces; -static const char *printed_input_name = NULL; -static pp_definition_t *current_expansion = NULL; -static bool do_expansions; +static pp_conditional_t *conditional_stack; + +static token_t pp_token; +static bool resolve_escape_sequences = false; +static bool ignore_unknown_chars = true; +static bool in_pp_directive; +static bool skip_mode; +static FILE *out; +static struct obstack pp_obstack; +static struct obstack config_obstack; +static const char *printed_input_name = NULL; +static source_position_t expansion_pos; +static pp_definition_t *current_expansion = NULL; +static strset_t stringset; +static preprocessor_token_kind_t last_token = TP_ERROR; + +static searchpath_entry_t *searchpath; + +static add_token_info_t info; static inline void next_char(void); static void next_preprocessing_token(void); static void print_line_directive(const source_position_t *pos, const char *add); -static void print_spaces(void); -static bool open_input(const char *filename) +static void switch_input(FILE *file, const char *filename) { - FILE *file = fopen(filename, "r"); - if (file == NULL) - return false; - input.file = file; + input.input = input_from_stream(file, NULL); input.bufend = NULL; input.bufpos = NULL; - input.had_non_space = false; + input.output_line = 0; input.position.input_name = filename; - input.position.linenr = 1; + input.position.lineno = 1; /* indicate that we're at a new input */ print_line_directive(&input.position, input_stack != NULL ? "1" : NULL); - counted_newlines = 0; - counted_spaces = 0; - - /* read first char and first token */ - next_char(); + /* place a virtual '\n' so we realize we're at line begin */ + input.position.lineno = 0; + input.c = '\n'; next_preprocessing_token(); - - return true; } static void close_input(void) { - /* ensure we have a newline at EOF */ - if (input.had_non_space) { - fputc('\n', out); - } - + input_free(input.input); assert(input.file != NULL); fclose(input.file); + input.input = NULL; input.file = NULL; input.bufend = NULL; input.bufpos = NULL; @@ -154,23 +188,23 @@ static void pop_restore_input(void) */ static void parse_error(const char *msg) { - errorf(&pp_token.source_position, "%s", msg); + errorf(&pp_token.base.source_position, "%s", msg); } static inline void next_real_char(void) { assert(input.bufpos <= input.bufend); if (input.bufpos >= input.bufend) { - size_t s = fread(input.buf + MAX_PUTBACK, 1, - sizeof(input.buf) - MAX_PUTBACK, input.file); - if(s == 0) { - CC = EOF; + size_t const n = decode(input.input, input.buf + MAX_PUTBACK, lengthof(input.buf) - MAX_PUTBACK); + if (n == 0) { + input.c = EOF; return; } input.bufpos = input.buf + MAX_PUTBACK; - input.bufend = input.buf + MAX_PUTBACK + s; + input.bufend = input.bufpos + n; } - CC = *input.bufpos++; + input.c = *input.bufpos++; + ++input.position.colno; } /** @@ -178,44 +212,42 @@ static inline void next_real_char(void) * * @param pc the character to put back */ -static inline void put_back(int pc) +static inline void put_back(utf32 const pc) { assert(input.bufpos > input.buf); *(--input.bufpos - input.buf + input.buf) = (char) pc; - -#ifdef DEBUG_CHARS - printf("putback '%c'\n", pc); -#endif + --input.position.colno; } #define MATCH_NEWLINE(code) \ case '\r': \ next_char(); \ - if(CC == '\n') { \ + if (input.c == '\n') { \ + case '\n': \ next_char(); \ } \ - ++input.position.linenr; \ - code \ - case '\n': \ - next_char(); \ - ++input.position.linenr; \ + info.whitespace = 0; \ + ++input.position.lineno; \ + input.position.colno = 1; \ code -#define eat(c_type) do { assert(CC == c_type); next_char(); } while(0) +#define eat(c_type) (assert(input.c == c_type), next_char()) static void maybe_concat_lines(void) { eat('\\'); - switch(CC) { - MATCH_NEWLINE(return;) + switch (input.c) { + MATCH_NEWLINE( + return; + ) default: break; } - put_back(CC); - CC = '\\'; + put_back(input.c); + input.c = '\\'; } /** @@ -227,42 +259,42 @@ static inline void next_char(void) next_real_char(); /* filter trigraphs and concatenated lines */ - if(UNLIKELY(CC == '\\')) { + if (UNLIKELY(input.c == '\\')) { maybe_concat_lines(); goto end_of_next_char; } - if(LIKELY(CC != '?')) + if (LIKELY(input.c != '?')) goto end_of_next_char; next_real_char(); - if(LIKELY(CC != '?')) { - put_back(CC); - CC = '?'; + if (LIKELY(input.c != '?')) { + put_back(input.c); + input.c = '?'; goto end_of_next_char; } next_real_char(); - switch(CC) { - case '=': CC = '#'; break; - case '(': CC = '['; break; - case '/': CC = '\\'; maybe_concat_lines(); break; - case ')': CC = ']'; break; - case '\'': CC = '^'; break; - case '<': CC = '{'; break; - case '!': CC = '|'; break; - case '>': CC = '}'; break; - case '-': CC = '~'; break; + switch (input.c) { + case '=': input.c = '#'; break; + case '(': input.c = '['; break; + case '/': input.c = '\\'; maybe_concat_lines(); break; + case ')': input.c = ']'; break; + case '\'': input.c = '^'; break; + case '<': input.c = '{'; break; + case '!': input.c = '|'; break; + case '>': input.c = '}'; break; + case '-': input.c = '~'; break; default: - put_back(CC); + put_back(input.c); put_back('?'); - CC = '?'; + input.c = '?'; break; } end_of_next_char:; #ifdef DEBUG_CHARS - printf("nchar '%c'\n", CC); + printf("nchar '%c'\n", input.c); #endif } @@ -275,7 +307,7 @@ end_of_next_char:; */ static inline bool is_octal_digit(int chr) { - switch(chr) { + switch (chr) { case '0': case '1': case '2': @@ -294,7 +326,8 @@ static inline bool is_octal_digit(int chr) * Returns the value of a digit. * The only portable way to do it ... */ -static int digit_value(int digit) { +static int digit_value(int digit) +{ switch (digit) { case '0': return 0; case '1': return 1; @@ -328,53 +361,44 @@ static int digit_value(int digit) { * * @param first_digit the already read first digit */ -static int parse_octal_sequence(const int first_digit) +static utf32 parse_octal_sequence(const utf32 first_digit) { assert(is_octal_digit(first_digit)); - int value = digit_value(first_digit); - if (!is_octal_digit(CC)) return value; - value = 8 * value + digit_value(CC); + utf32 value = digit_value(first_digit); + if (!is_octal_digit(input.c)) return value; + value = 8 * value + digit_value(input.c); next_char(); - if (!is_octal_digit(CC)) return value; - value = 8 * value + digit_value(CC); + if (!is_octal_digit(input.c)) return value; + value = 8 * value + digit_value(input.c); next_char(); + return value; - if(char_is_signed) { - return (signed char) value; - } else { - return (unsigned char) value; - } } /** * Parses a hex character sequence. */ -static int parse_hex_sequence(void) +static utf32 parse_hex_sequence(void) { - int value = 0; - while(isxdigit(CC)) { - value = 16 * value + digit_value(CC); + utf32 value = 0; + while (isxdigit(input.c)) { + value = 16 * value + digit_value(input.c); next_char(); } - - if(char_is_signed) { - return (signed char) value; - } else { - return (unsigned char) value; - } + return value; } /** * Parse an escape sequence. */ -static int parse_escape_sequence(void) +static utf32 parse_escape_sequence(void) { eat('\\'); - int ec = CC; + utf32 const ec = input.c; next_char(); - switch(ec) { + switch (ec) { case '"': return '"'; case '\'': return '\''; case '\\': return '\\'; @@ -400,39 +424,68 @@ static int parse_escape_sequence(void) case EOF: parse_error("reached end of file while parsing escape sequence"); return EOF; - default: - parse_error("unknown escape sequence"); + /* \E is not documented, but handled, by GCC. It is acceptable according + * to §6.11.4, whereas \e is not. */ + case 'E': + case 'e': + if (c_mode & _GNUC) + return 27; /* hopefully 27 is ALWAYS the code for ESCAPE */ + break; + case 'u': + case 'U': + parse_error("universal character parsing not implemented yet"); return EOF; + default: + break; } + /* §6.4.4.4:8 footnote 64 */ + parse_error("unknown escape sequence"); + return EOF; +} + +static const char *identify_string(char *string) +{ + const char *result = strset_insert(&stringset, string); + if (result != string) { + obstack_free(&symbol_obstack, string); + } + return result; +} + +static string_t make_string(char *string, size_t len) +{ + const char *result = identify_string(string); + return (string_t) {result, len}; } static void parse_string_literal(void) { - const unsigned start_linenr = input.position.linenr; + const unsigned start_linenr = input.position.lineno; eat('"'); - int tc; - while(1) { - switch(CC) { - case '\\': - if(resolve_escape_sequences) { + while (true) { + switch (input.c) { + case '\\': { + utf32 tc; + if (resolve_escape_sequences) { tc = parse_escape_sequence(); obstack_1grow(&symbol_obstack, (char) tc); } else { - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); } break; + } case EOF: { source_position_t source_position; - source_position.input_name = pp_token.source_position.input_name; - source_position.linenr = start_linenr; + source_position.input_name = pp_token.base.source_position.input_name; + source_position.lineno = start_linenr; errorf(&source_position, "string has no end"); - pp_token.type = TP_ERROR; + pp_token.kind = TP_ERROR; return; } @@ -441,7 +494,7 @@ static void parse_string_literal(void) goto end_of_string; default: - obstack_1grow(&symbol_obstack, (char) CC); + obstack_grow_symbol(&symbol_obstack, input.c); next_char(); break; } @@ -450,34 +503,34 @@ static void parse_string_literal(void) end_of_string: /* add finishing 0 to the string */ obstack_1grow(&symbol_obstack, '\0'); - const size_t size = (size_t)obstack_object_size(&symbol_obstack); - const char *const string = obstack_finish(&symbol_obstack); + const size_t size = (size_t)obstack_object_size(&symbol_obstack); + char *const string = obstack_finish(&symbol_obstack); -#if 0 /* TODO hash */ - /* check if there is already a copy of the string */ - result = strset_insert(&stringset, string); - if(result != string) { - obstack_free(&symbol_obstack, string); - } -#else - const char *const result = string; -#endif + pp_token.kind = TP_STRING_LITERAL; + pp_token.string.string = make_string(string, size); +} - pp_token.type = TP_STRING_LITERAL; - pp_token.v.string.begin = result; - pp_token.v.string.size = size; +/** + * Parse a wide string literal and set lexer_token. + */ +static void parse_wide_string_literal(void) +{ + parse_string_literal(); + if (pp_token.kind == TP_STRING_LITERAL) + pp_token.kind = TP_WIDE_STRING_LITERAL; } static void parse_wide_character_constant(void) { eat('\''); - int found_char = 0; - while(1) { - switch(CC) { - case '\\': - found_char = parse_escape_sequence(); + while (true) { + switch (input.c) { + case '\\': { + const utf32 tc = parse_escape_sequence(); + obstack_grow_symbol(&symbol_obstack, tc); break; + } MATCH_NEWLINE( parse_error("newline while parsing character constant"); @@ -490,97 +543,37 @@ static void parse_wide_character_constant(void) case EOF: parse_error("EOF while parsing character constant"); - pp_token.type = TP_ERROR; + pp_token.kind = TP_ERROR; return; default: - if(found_char != 0) { - parse_error("more than 1 characters in character " - "constant"); - goto end_of_wide_char_constant; - } else { - found_char = CC; - next_char(); - } - break; - } - } - -end_of_wide_char_constant: - pp_token.type = TP_WIDE_CHARACTER_CONSTANT; - /* TODO... */ -} - -static void parse_wide_string_literal(void) -{ - const unsigned start_linenr = input.position.linenr; - - assert(CC == '"'); - next_char(); - - while(1) { - switch(CC) { - case '\\': { - wchar_rep_t tc = parse_escape_sequence(); - obstack_grow(&symbol_obstack, &tc, sizeof(tc)); - break; - } - - case EOF: { - source_position_t source_position; - source_position.input_name = pp_token.source_position.input_name; - source_position.linenr = start_linenr; - errorf(&source_position, "string has no end"); - pp_token.type = TP_ERROR; - return; - } - - case '"': - next_char(); - goto end_of_string; - - default: { - wchar_rep_t tc = CC; - obstack_grow(&symbol_obstack, &tc, sizeof(tc)); + obstack_grow_symbol(&symbol_obstack, input.c); next_char(); break; } - } } -end_of_string:; - /* add finishing 0 to the string */ - static const wchar_rep_t nul = L'\0'; - obstack_grow(&symbol_obstack, &nul, sizeof(nul)); - - const size_t size - = (size_t)obstack_object_size(&symbol_obstack) / sizeof(wchar_rep_t); - const wchar_rep_t *const string = obstack_finish(&symbol_obstack); +end_of_wide_char_constant: + obstack_1grow(&symbol_obstack, '\0'); + size_t size = (size_t) obstack_object_size(&symbol_obstack)-1; + char *string = obstack_finish(&symbol_obstack); + pp_token.kind = TP_WIDE_CHARACTER_CONSTANT; + pp_token.string.string = make_string(string, size); -#if 0 /* TODO hash */ - /* check if there is already a copy of the string */ - const wchar_rep_t *const result = strset_insert(&stringset, string); - if(result != string) { - obstack_free(&symbol_obstack, string); + if (size == 0) { + parse_error("empty character constant"); } -#else - const wchar_rep_t *const result = string; -#endif - - pp_token.type = TP_WIDE_STRING_LITERAL; - pp_token.v.wide_string.begin = result; - pp_token.v.wide_string.size = size; } static void parse_character_constant(void) { - const unsigned start_linenr = input.position.linenr; + const unsigned start_linenr = input.position.lineno; eat('\''); int tc; - while(1) { - switch(CC) { + while (true) { + switch (input.c) { case '\\': tc = parse_escape_sequence(); obstack_1grow(&symbol_obstack, (char) tc); @@ -593,10 +586,10 @@ static void parse_character_constant(void) case EOF: { source_position_t source_position; - source_position.input_name = pp_token.source_position.input_name; - source_position.linenr = start_linenr; + source_position.input_name = pp_token.base.source_position.input_name; + source_position.lineno = start_linenr; errorf(&source_position, "EOF while parsing character constant"); - pp_token.type = TP_ERROR; + pp_token.kind = TP_ERROR; return; } @@ -605,7 +598,7 @@ static void parse_character_constant(void) goto end_of_char_constant; default: - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); break; @@ -613,12 +606,16 @@ static void parse_character_constant(void) } end_of_char_constant:; - const size_t size = (size_t)obstack_object_size(&symbol_obstack); - const char *const string = obstack_finish(&symbol_obstack); + obstack_1grow(&symbol_obstack, '\0'); + const size_t size = (size_t)obstack_object_size(&symbol_obstack); + char *const string = obstack_finish(&symbol_obstack); + + pp_token.kind = TP_CHARACTER_CONSTANT; + pp_token.string.string = make_string(string, size); - pp_token.type = TP_CHARACTER_CONSTANT; - pp_token.v.string.begin = string; - pp_token.v.string.size = size; + if (size == 0) { + parse_error("empty character constant"); + } } #define SYMBOL_CHARS_WITHOUT_E_P \ @@ -701,7 +698,7 @@ static void expand_next(void) pp_definition_t *definition = current_expansion; restart: - if(definition->list_len == 0 + if (definition->list_len == 0 || definition->expand_pos >= definition->list_len) { /* we're finished with the current macro, move up 1 level in the * expansion stack */ @@ -710,7 +707,7 @@ restart: definition->is_expanding = false; /* it was the outermost expansion, parse normal pptoken */ - if(parent == NULL) { + if (parent == NULL) { current_expansion = NULL; next_preprocessing_token(); return; @@ -719,15 +716,16 @@ restart: current_expansion = definition; goto restart; } - pp_token = definition->replacement_list[definition->expand_pos]; + pp_token = definition->token_list[definition->expand_pos]; + pp_token.base.source_position = expansion_pos; ++definition->expand_pos; - if(pp_token.type != TP_IDENTIFIER) + if (pp_token.kind != TP_IDENTIFIER) return; /* if it was an identifier then we might need to expand again */ - pp_definition_t *symbol_definition = pp_token.v.symbol->pp_definition; - if(symbol_definition != NULL && !symbol_definition->is_expanding) { + pp_definition_t *symbol_definition = pp_token.identifier.symbol->pp_definition; + if (symbol_definition != NULL && !symbol_definition->is_expanding) { symbol_definition->parent_expansion = definition; symbol_definition->expand_pos = 0; symbol_definition->is_expanding = true; @@ -737,16 +735,116 @@ restart: } } +static void skip_line_comment(void) +{ + while (true) { + switch (input.c) { + case EOF: + return; + + case '\r': + case '\n': + return; + + default: + next_char(); + break; + } + } +} + +static void skip_multiline_comment(void) +{ + unsigned start_linenr = input.position.lineno; + while (true) { + switch (input.c) { + case '/': + next_char(); + if (input.c == '*') { + /* TODO: nested comment, warn here */ + } + break; + case '*': + next_char(); + if (input.c == '/') { + next_char(); + info.whitespace += input.position.colno-1; + return; + } + break; + + MATCH_NEWLINE( + info.at_line_begin |= !in_pp_directive; + break; + ) + + case EOF: { + source_position_t source_position; + source_position.input_name = pp_token.base.source_position.input_name; + source_position.lineno = start_linenr; + errorf(&source_position, "at end of file while looking for comment end"); + return; + } + + default: + next_char(); + break; + } + } +} + +static void skip_whitespace(void) +{ + while (true) { + switch (input.c) { + case ' ': + case '\t': + next_char(); + continue; + + MATCH_NEWLINE( + info.at_line_begin = true; + return; + ) + + case '/': + next_char(); + if (input.c == '/') { + next_char(); + skip_line_comment(); + continue; + } else if (input.c == '*') { + next_char(); + skip_multiline_comment(); + continue; + } else { + put_back(input.c); + input.c = '/'; + } + return; + default: + return; + } + } +} + +static void eat_pp(int type) +{ + (void) type; + assert(pp_token.kind == type); + next_preprocessing_token(); +} + static void parse_symbol(void) { - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); - while(1) { - switch(CC) { + while (true) { + switch (input.c) { DIGITS SYMBOL_CHARS - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); break; @@ -760,11 +858,11 @@ end_symbol: char *string = obstack_finish(&symbol_obstack); /* might be a wide string or character constant ( L"string"/L'c' ) */ - if(CC == '"' && string[0] == 'L' && string[1] == '\0') { + if (input.c == '"' && string[0] == 'L' && string[1] == '\0') { obstack_free(&symbol_obstack, string); parse_wide_string_literal(); return; - } else if(CC == '\'' && string[0] == 'L' && string[1] == '\0') { + } else if (input.c == '\'' && string[0] == 'L' && string[1] == '\0') { obstack_free(&symbol_obstack, string); parse_wide_character_constant(); return; @@ -772,35 +870,27 @@ end_symbol: symbol_t *symbol = symbol_table_insert(string); - pp_token.type = symbol->pp_ID; - pp_token.v.symbol = symbol; + pp_token.kind = symbol->pp_ID; + pp_token.identifier.symbol = symbol; /* we can free the memory from symbol obstack if we already had an entry in * the symbol table */ - if(symbol->string != string) { + if (symbol->string != string) { obstack_free(&symbol_obstack, string); } - - pp_definition_t *pp_definition = symbol->pp_definition; - if(do_expansions && pp_definition != NULL) { - pp_definition->expand_pos = 0; - pp_definition->is_expanding = true, - current_expansion = pp_definition; - expand_next(); - } } static void parse_number(void) { - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); - while(1) { - switch(CC) { + while (true) { + switch (input.c) { case '.': DIGITS SYMBOL_CHARS_WITHOUT_E_P - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); break; @@ -808,10 +898,10 @@ static void parse_number(void) case 'p': case 'E': case 'P': - obstack_1grow(&symbol_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); - if(CC == '+' || CC == '-') { - obstack_1grow(&symbol_obstack, (char) CC); + if (input.c == '+' || input.c == '-') { + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); } break; @@ -826,121 +916,57 @@ end_number: size_t size = obstack_object_size(&symbol_obstack); char *string = obstack_finish(&symbol_obstack); - pp_token.type = TP_NUMBER; - pp_token.v.string.begin = string; - pp_token.v.string.size = size; -} - -static void skip_multiline_comment(void) -{ - unsigned start_linenr = input.position.linenr; - - while(1) { - switch(CC) { - case '/': - next_char(); - if (CC == '*') { - /* TODO: nested comment, warn here */ - } - break; - case '*': - next_char(); - if(CC == '/') { - next_char(); - return; - } - break; - - MATCH_NEWLINE( - if(do_print_spaces) { - counted_newlines++; - counted_spaces = 0; - } - break; - ) - - case EOF: { - source_position_t source_position; - source_position.input_name = pp_token.source_position.input_name; - source_position.linenr = start_linenr; - errorf(&source_position, "at end of file while looking for comment end"); - return; - } - - default: - next_char(); - break; - } - } -} - -static void skip_line_comment(void) -{ - while(1) { - switch(CC) { - case EOF: - return; - - case '\n': - case '\r': - return; - - default: - next_char(); - break; - } - } + pp_token.kind = TP_NUMBER; + pp_token.number.number = make_string(string, size); } - #define MAYBE_PROLOG \ next_char(); \ - while(1) { \ - switch(CC) { + while (true) { \ + switch (input.c) { #define MAYBE(ch, set_type) \ case ch: \ next_char(); \ - pp_token.type = set_type; \ + pp_token.kind = set_type; \ return; #define ELSE_CODE(code) \ default: \ - code; \ + code \ + return; \ } \ - } /* end of while(1) */ \ - break; + } #define ELSE(set_type) \ ELSE_CODE( \ - pp_token.type = set_type; \ - return; \ + pp_token.kind = set_type; \ ) static void next_preprocessing_token(void) { - if(current_expansion != NULL) { + if (current_expansion != NULL) { expand_next(); return; } - pp_token.source_position = input.position; - + info.at_line_begin = false; + info.had_whitespace = false; restart: - switch(CC) { + pp_token.base.source_position = input.position; + switch (input.c) { case ' ': case '\t': - if(do_print_spaces) - counted_spaces++; - next_char(); + ++info.whitespace; + info.had_whitespace = true; + next_char(); goto restart; MATCH_NEWLINE( - counted_newlines++; - counted_spaces = 0; - pp_token.type = '\n'; - return; + info.at_line_begin = true; + info.had_whitespace = true; + goto restart; ) SYMBOL_CHARS @@ -971,8 +997,8 @@ restart: case '7': case '8': case '9': - put_back(CC); - CC = '.'; + put_back(input.c); + input.c = '.'; parse_number(); return; @@ -980,10 +1006,9 @@ restart: MAYBE_PROLOG MAYBE('.', TP_DOTDOTDOT) ELSE_CODE( - put_back(CC); - CC = '.'; - pp_token.type = '.'; - return; + put_back(input.c); + input.c = '.'; + pp_token.kind = '.'; ) ELSE('.') case '&': @@ -1015,15 +1040,13 @@ restart: MAYBE('=', TP_SLASHEQUAL) case '*': next_char(); + info.had_whitespace = true; skip_multiline_comment(); - if(do_print_spaces) - counted_spaces++; goto restart; case '/': next_char(); + info.had_whitespace = true; skip_line_comment(); - if(do_print_spaces) - counted_spaces++; goto restart; ELSE('/') case '%': @@ -1036,10 +1059,9 @@ restart: MAYBE_PROLOG MAYBE(':', TP_HASHHASH) ELSE_CODE( - put_back(CC); - CC = '%'; - pp_token.type = '#'; - return; + put_back(input.c); + input.c = '%'; + pp_token.kind = '#'; ) ELSE('#') ELSE('%') @@ -1081,7 +1103,9 @@ restart: case '#': MAYBE_PROLOG MAYBE('#', TP_HASHHASH) - ELSE('#') + ELSE_CODE( + pp_token.kind = '#'; + ) case '?': case '[': @@ -1094,7 +1118,7 @@ restart: case ';': case ',': case '\\': - pp_token.type = CC; + pp_token.kind = input.c; next_char(); return; @@ -1102,20 +1126,25 @@ restart: if (input_stack != NULL) { close_input(); pop_restore_input(); - counted_newlines = 0; - counted_spaces = 0; - /* hack to output correct line number */ + fputc('\n', out); print_line_directive(&input.position, "2"); - next_preprocessing_token(); + goto restart; } else { - pp_token.type = TP_EOF; + pp_token.base.source_position.lineno++; + info.at_line_begin = true; + pp_token.kind = TP_EOF; } return; default: next_char(); - errorf(&pp_token.source_position, "unknown character '%c' found\n", CC); - pp_token.type = TP_ERROR; + if (!ignore_unknown_chars) { + errorf(&pp_token.base.source_position, + "unknown character '%c' found\n", input.c); + pp_token.kind = TP_ERROR; + } else { + pp_token.kind = input.c; + } return; } } @@ -1124,7 +1153,7 @@ static void print_quoted_string(const char *const string) { fputc('"', out); for (const char *c = string; *c != 0; ++c) { - switch(*c) { + switch (*c) { case '"': fputs("\\\"", out); break; case '\\': fputs("\\\\", out); break; case '\a': fputs("\\a", out); break; @@ -1136,8 +1165,8 @@ static void print_quoted_string(const char *const string) case '\v': fputs("\\v", out); break; case '\?': fputs("\\?", out); break; default: - if(!isprint(*c)) { - fprintf(out, "\\%03o", *c); + if (!isprint(*c)) { + fprintf(out, "\\%03o", (unsigned)*c); break; } fputc(*c, out); @@ -1149,72 +1178,80 @@ static void print_quoted_string(const char *const string) static void print_line_directive(const source_position_t *pos, const char *add) { - fprintf(out, "# %d ", pos->linenr); + fprintf(out, "# %u ", pos->lineno); print_quoted_string(pos->input_name); if (add != NULL) { fputc(' ', out); fputs(add, out); } - fputc('\n', out); printed_input_name = pos->input_name; + input.output_line = pos->lineno-1; } -static void print_spaces(void) +static void emit_newlines(void) { - if (counted_newlines >= 9) { - if (input.had_non_space) { - fputc('\n', out); - } - print_line_directive(&pp_token.source_position, NULL); - counted_newlines = 0; + unsigned delta = pp_token.base.source_position.lineno - input.output_line; + + if (delta >= 9) { + fputc('\n', out); + print_line_directive(&pp_token.base.source_position, NULL); + fputc('\n', out); } else { - for (unsigned i = 0; i < counted_newlines; ++i) + for (unsigned i = 0; i < delta; ++i) { fputc('\n', out); - counted_newlines = 0; + } } - for (unsigned i = 0; i < counted_spaces; ++i) - fputc(' ', out); - counted_spaces = 0; + input.output_line = pp_token.base.source_position.lineno; } static void emit_pp_token(void) { - if (pp_token.type != '\n') { - print_spaces(); - input.had_non_space = true; + if (skip_mode) + return; + + if (info.at_line_begin) { + emit_newlines(); + + for (unsigned i = 0; i < info.whitespace; ++i) + fputc(' ', out); + + } else if (info.had_whitespace || + tokens_would_paste(last_token, pp_token.kind)) { + fputc(' ', out); } - switch(pp_token.type) { + switch (pp_token.kind) { case TP_IDENTIFIER: - fputs(pp_token.v.symbol->string, out); + fputs(pp_token.identifier.symbol->string, out); break; case TP_NUMBER: - fputs(pp_token.v.string.begin, out); + fputs(pp_token.number.number.begin, out); break; + case TP_WIDE_STRING_LITERAL: + fputc('L', out); case TP_STRING_LITERAL: fputc('"', out); - fputs(pp_token.v.string.begin, out); + fputs(pp_token.string.string.begin, out); fputc('"', out); break; - case '\n': + case TP_WIDE_CHARACTER_CONSTANT: + fputc('L', out); + case TP_CHARACTER_CONSTANT: + fputc('\'', out); + fputs(pp_token.string.string.begin, out); + fputc('\'', out); break; default: - print_pp_token_type(out, pp_token.type); + print_pp_token_kind(out, pp_token.kind); break; } -} - -static void eat_pp(preprocessor_token_type_t type) -{ - (void) type; - assert(pp_token.type == type); - next_preprocessing_token(); + last_token = pp_token.kind; } static void eat_pp_directive(void) { - while(pp_token.type != '\n' && pp_token.type != TP_EOF) { + while (!info.at_line_begin) { next_preprocessing_token(); } } @@ -1222,29 +1259,13 @@ static void eat_pp_directive(void) static bool strings_equal(const string_t *string1, const string_t *string2) { size_t size = string1->size; - if(size != string2->size) + if (size != string2->size) return false; const char *c1 = string1->begin; const char *c2 = string2->begin; - for(size_t i = 0; i < size; ++i, ++c1, ++c2) { - if(*c1 != *c2) - return false; - } - return true; -} - -static bool wide_strings_equal(const wide_string_t *string1, - const wide_string_t *string2) -{ - size_t size = string1->size; - if(size != string2->size) - return false; - - const wchar_rep_t *c1 = string1->begin; - const wchar_rep_t *c2 = string2->begin; - for(size_t i = 0; i < size; ++i, ++c1, ++c2) { - if(*c1 != *c2) + for (size_t i = 0; i < size; ++i, ++c1, ++c2) { + if (*c1 != *c2) return false; } return true; @@ -1252,24 +1273,17 @@ static bool wide_strings_equal(const wide_string_t *string1, static bool pp_tokens_equal(const token_t *token1, const token_t *token2) { - if(token1->type != token2->type) + if (token1->kind != token2->kind) return false; - switch(token1->type) { - case TP_HEADERNAME: - /* TODO */ - return false; + switch (token1->kind) { case TP_IDENTIFIER: - return token1->v.symbol == token2->v.symbol; + return token1->identifier.symbol == token2->identifier.symbol; case TP_NUMBER: case TP_CHARACTER_CONSTANT: case TP_STRING_LITERAL: - return strings_equal(&token1->v.string, &token2->v.string); + return strings_equal(&token1->string.string, &token2->string.string); - case TP_WIDE_CHARACTER_CONSTANT: - case TP_WIDE_STRING_LITERAL: - return wide_strings_equal(&token1->v.wide_string, - &token2->v.wide_string); default: return true; } @@ -1278,14 +1292,14 @@ static bool pp_tokens_equal(const token_t *token1, const token_t *token2) static bool pp_definitions_equal(const pp_definition_t *definition1, const pp_definition_t *definition2) { - if(definition1->list_len != definition2->list_len) + if (definition1->list_len != definition2->list_len) return false; size_t len = definition1->list_len; - const token_t *t1 = definition1->replacement_list; - const token_t *t2 = definition2->replacement_list; - for(size_t i = 0; i < len; ++i, ++t1, ++t2) { - if(!pp_tokens_equal(t1, t2)) + const token_t *t1 = definition1->token_list; + const token_t *t2 = definition2->token_list; + for (size_t i = 0; i < len; ++i, ++t1, ++t2) { + if (!pp_tokens_equal(t1, t2)) return false; } return true; @@ -1294,14 +1308,14 @@ static bool pp_definitions_equal(const pp_definition_t *definition1, static void parse_define_directive(void) { eat_pp(TP_define); + assert(obstack_object_size(&pp_obstack) == 0); - if(pp_token.type != TP_IDENTIFIER) { - errorf(&pp_token.source_position, - "expected identifier after #define, got '%T'", &pp_token); - eat_pp_directive(); - return; + if (pp_token.kind != TP_IDENTIFIER || info.at_line_begin) { + errorf(&pp_token.base.source_position, + "expected identifier after #define, got '%t'", &pp_token); + goto error_out; } - symbol_t *symbol = pp_token.v.symbol; + symbol_t *symbol = pp_token.identifier.symbol; pp_definition_t *new_definition = obstack_alloc(&pp_obstack, sizeof(new_definition[0])); @@ -1311,22 +1325,55 @@ static void parse_define_directive(void) /* this is probably the only place where spaces are significant in the * lexer (except for the fact that they separate tokens). #define b(x) * is something else than #define b (x) */ - //token_t *arguments = NULL; - if(CC == '(') { + if (input.c == '(') { + /* eat the '(' */ + next_preprocessing_token(); + /* get next token after '(' */ next_preprocessing_token(); - while(pp_token.type != ')') { - if(pp_token.type == TP_DOTDOTDOT) { + + while (true) { + switch (pp_token.kind) { + case TP_DOTDOTDOT: new_definition->is_variadic = true; next_preprocessing_token(); - if(pp_token.type != ')') { + if (pp_token.kind != ')') { errorf(&input.position, "'...' not at end of macro argument list"); - continue; + goto error_out; + } + break; + case TP_IDENTIFIER: + obstack_ptr_grow(&pp_obstack, pp_token.identifier.symbol); + next_preprocessing_token(); + + if (pp_token.kind == ',') { + next_preprocessing_token(); + break; + } + + if (pp_token.kind != ')') { + errorf(&pp_token.base.source_position, + "expected ',' or ')' after identifier, got '%t'", + &pp_token); + goto error_out; } - } else if(pp_token.type != TP_IDENTIFIER) { + break; + case ')': next_preprocessing_token(); + goto finish_argument_list; + default: + errorf(&pp_token.base.source_position, + "expected identifier, '...' or ')' in #define argument list, got '%t'", + &pp_token); + goto error_out; } } + + finish_argument_list: + new_definition->has_parameters = true; + new_definition->n_parameters + = obstack_object_size(&pp_obstack) / sizeof(new_definition->parameters[0]); + new_definition->parameters = obstack_finish(&pp_obstack); } else { next_preprocessing_token(); } @@ -1334,20 +1381,19 @@ static void parse_define_directive(void) /* construct a new pp_definition on the obstack */ assert(obstack_object_size(&pp_obstack) == 0); size_t list_len = 0; - while(pp_token.type != '\n' && pp_token.type != TP_EOF) { + while (!info.at_line_begin) { obstack_grow(&pp_obstack, &pp_token, sizeof(pp_token)); ++list_len; next_preprocessing_token(); } - new_definition->list_len = list_len; - new_definition->replacement_list = obstack_finish(&pp_obstack); + new_definition->list_len = list_len; + new_definition->token_list = obstack_finish(&pp_obstack); pp_definition_t *old_definition = symbol->pp_definition; - if(old_definition != NULL) { - if(!pp_definitions_equal(old_definition, new_definition)) { - warningf(&input.position, "multiple definition of macro '%Y' (first defined %P)", - symbol, &old_definition->source_position); + if (old_definition != NULL) { + if (!pp_definitions_equal(old_definition, new_definition)) { + warningf(WARN_OTHER, &input.position, "multiple definition of macro '%Y' (first defined %P)", symbol, &old_definition->source_position); } else { /* reuse the old definition */ obstack_free(&pp_obstack, new_definition); @@ -1356,51 +1402,56 @@ static void parse_define_directive(void) } symbol->pp_definition = new_definition; + return; + +error_out: + if (obstack_object_size(&pp_obstack) > 0) { + char *ptr = obstack_finish(&pp_obstack); + obstack_free(&pp_obstack, ptr); + } + eat_pp_directive(); } static void parse_undef_directive(void) { eat_pp(TP_undef); - if(pp_token.type != TP_IDENTIFIER) { + if (pp_token.kind != TP_IDENTIFIER) { errorf(&input.position, - "expected identifier after #undef, got '%T'", &pp_token); + "expected identifier after #undef, got '%t'", &pp_token); eat_pp_directive(); return; } - symbol_t *symbol = pp_token.v.symbol; + symbol_t *symbol = pp_token.identifier.symbol; symbol->pp_definition = NULL; next_preprocessing_token(); - if(pp_token.type != '\n') { - warningf(&input.position, "extra tokens at end of #undef directive"); + if (!info.at_line_begin) { + warningf(WARN_OTHER, &input.position, "extra tokens at end of #undef directive"); } - /* eat until '\n' */ eat_pp_directive(); } static const char *parse_headername(void) { - /* behind an #include we can have the special headername lexems, check - * for them here */ - - /* skip spaces */ - while (CC == ' ' || CC == '\t') { - next_char(); + /* behind an #include we can have the special headername lexems. + * They're only allowed behind an #include so they're not recognized + * by the normal next_preprocessing_token. We handle them as a special + * exception here */ + if (info.at_line_begin) { + parse_error("expected headername after #include"); + return NULL; } - assert(obstack_object_size(&input_obstack) == 0); + assert(obstack_object_size(&symbol_obstack) == 0); - switch (CC) { + /* check wether we have a "... or <... headername */ + switch (input.c) { case '<': - /* for now until we have proper searchpath handling */ - obstack_1grow(&input_obstack, '.'); - obstack_1grow(&input_obstack, '/'); - next_char(); while (true) { - switch (CC) { + switch (input.c) { case EOF: /* fallthrough */ MATCH_NEWLINE( @@ -1411,19 +1462,15 @@ static const char *parse_headername(void) next_char(); goto finished_headername; } - obstack_1grow(&input_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); } /* we should never be here */ case '"': - /* for now until we have proper searchpath handling */ - obstack_1grow(&input_obstack, '.'); - obstack_1grow(&input_obstack, '/'); - next_char(); while (true) { - switch (CC) { + switch (input.c) { case EOF: /* fallthrough */ MATCH_NEWLINE( @@ -1434,99 +1481,345 @@ static const char *parse_headername(void) next_char(); goto finished_headername; } - obstack_1grow(&input_obstack, (char) CC); + obstack_1grow(&symbol_obstack, (char) input.c); next_char(); } /* we should never be here */ default: - /* TODO: do normale pp_token parsing and concatenate results */ + /* TODO: do normal pp_token parsing and concatenate results */ panic("pp_token concat include not implemented yet"); } finished_headername: - obstack_1grow(&input_obstack, '\0'); - char *headername = obstack_finish(&input_obstack); + obstack_1grow(&symbol_obstack, '\0'); + char *headername = obstack_finish(&symbol_obstack); /* TODO: iterate search-path to find the file */ - next_preprocessing_token(); + skip_whitespace(); - return headername; + return identify_string(headername); } -static void parse_include_directive(void) +static bool do_include(bool system_include, const char *headername) { - /* don't eat the TP_include here! - * we need an alternative parsing for the next token */ + if (!system_include) { + /* for "bla" includes first try current dir + * TODO: this isn't correct, should be the directory of the source file + */ + FILE *file = fopen(headername, "r"); + if (file != NULL) { + switch_input(file, headername); + return true; + } + } - print_spaces(); + size_t headername_len = strlen(headername); + assert(obstack_object_size(&pp_obstack) == 0); + /* check searchpath */ + for (searchpath_entry_t *entry = searchpath; entry != NULL; + entry = entry->next) { + const char *path = entry->path; + size_t len = strlen(path); + obstack_grow(&pp_obstack, path, len); + if (path[len-1] != '/') + obstack_1grow(&pp_obstack, '/'); + obstack_grow(&pp_obstack, headername, headername_len+1); + + char *complete_path = obstack_finish(&pp_obstack); + FILE *file = fopen(complete_path, "r"); + if (file != NULL) { + const char *filename = identify_string(complete_path); + switch_input(file, filename); + return true; + } + obstack_free(&pp_obstack, complete_path); + } + return false; +} + +static bool parse_include_directive(void) +{ + /* don't eat the TP_include here! + * we need an alternative parsing for the next token */ + skip_whitespace(); + bool system_include = input.c == '<'; const char *headername = parse_headername(); if (headername == NULL) { eat_pp_directive(); - return; + return false; } - if (pp_token.type != '\n' && pp_token.type != TP_EOF) { - warningf(&pp_token.source_position, + if (!info.at_line_begin) { + warningf(WARN_OTHER, &pp_token.base.source_position, "extra tokens at end of #include directive"); eat_pp_directive(); } if (n_inputs > INCLUDE_LIMIT) { - errorf(&pp_token.source_position, "#include nested too deeply"); + errorf(&pp_token.base.source_position, "#include nested too deeply"); /* eat \n or EOF */ next_preprocessing_token(); - return; + return false; } + /* we have to reenable space counting and macro expansion here, + * because it is still disabled in directive parsing, + * but we will trigger a preprocessing token reading of the new file + * now and need expansions/space counting */ + in_pp_directive = false; + /* switch inputs */ + emit_newlines(); push_input(); - bool res = open_input(headername); + bool res = do_include(system_include, headername); if (!res) { - errorf(&pp_token.source_position, + errorf(&pp_token.base.source_position, "failed including '%s': %s", headername, strerror(errno)); pop_restore_input(); + return false; + } + + return true; +} + +static pp_conditional_t *push_conditional(void) +{ + pp_conditional_t *conditional + = obstack_alloc(&pp_obstack, sizeof(*conditional)); + memset(conditional, 0, sizeof(*conditional)); + + conditional->parent = conditional_stack; + conditional_stack = conditional; + + return conditional; +} + +static void pop_conditional(void) +{ + assert(conditional_stack != NULL); + conditional_stack = conditional_stack->parent; +} + +static void check_unclosed_conditionals(void) +{ + while (conditional_stack != NULL) { + pp_conditional_t *conditional = conditional_stack; + + if (conditional->in_else) { + errorf(&conditional->source_position, "unterminated #else"); + } else { + errorf(&conditional->source_position, "unterminated condition"); + } + pop_conditional(); + } +} + +static void parse_ifdef_ifndef_directive(void) +{ + bool is_ifndef = (pp_token.kind == TP_ifndef); + bool condition; + next_preprocessing_token(); + + if (skip_mode) { + eat_pp_directive(); + pp_conditional_t *conditional = push_conditional(); + conditional->source_position = pp_token.base.source_position; + conditional->skip = true; return; } + + if (pp_token.kind != TP_IDENTIFIER || info.at_line_begin) { + errorf(&pp_token.base.source_position, + "expected identifier after #%s, got '%t'", + is_ifndef ? "ifndef" : "ifdef", &pp_token); + eat_pp_directive(); + + /* just take the true case in the hope to avoid further errors */ + condition = true; + } else { + symbol_t *symbol = pp_token.identifier.symbol; + pp_definition_t *pp_definition = symbol->pp_definition; + next_preprocessing_token(); + + if (!info.at_line_begin) { + errorf(&pp_token.base.source_position, + "extra tokens at end of #%s", + is_ifndef ? "ifndef" : "ifdef"); + eat_pp_directive(); + } + + /* evaluate wether we are in true or false case */ + condition = is_ifndef ? pp_definition == NULL : pp_definition != NULL; + } + + pp_conditional_t *conditional = push_conditional(); + conditional->source_position = pp_token.base.source_position; + conditional->condition = condition; + + if (!condition) { + skip_mode = true; + } } -static void parse_preprocessing_directive(void) +static void parse_else_directive(void) { - do_print_spaces = false; - do_expansions = false; - eat_pp('#'); + eat_pp(TP_else); - switch(pp_token.type) { - case TP_define: - parse_define_directive(); - break; - case TP_undef: - parse_undef_directive(); - break; - case TP_include: - parse_include_directive(); - /* no need to parse ending '\n' */ - do_print_spaces = true; - do_expansions = true; + if (!info.at_line_begin) { + if (!skip_mode) { + warningf(WARN_OTHER, &pp_token.base.source_position, "extra tokens at end of #else"); + } + eat_pp_directive(); + } + + pp_conditional_t *conditional = conditional_stack; + if (conditional == NULL) { + errorf(&pp_token.base.source_position, "#else without prior #if"); return; - default: - errorf(&pp_token.source_position, - "invalid preprocessing directive #%T", &pp_token); + } + + if (conditional->in_else) { + errorf(&pp_token.base.source_position, + "#else after #else (condition started %P)", + conditional->source_position); + skip_mode = true; + return; + } + + conditional->in_else = true; + if (!conditional->skip) { + skip_mode = conditional->condition; + } + conditional->source_position = pp_token.base.source_position; +} + +static void parse_endif_directive(void) +{ + eat_pp(TP_endif); + + if (!info.at_line_begin) { + if (!skip_mode) { + warningf(WARN_OTHER, &pp_token.base.source_position, "extra tokens at end of #endif"); + } eat_pp_directive(); - break; } - do_print_spaces = true; - do_expansions = true; + pp_conditional_t *conditional = conditional_stack; + if (conditional == NULL) { + errorf(&pp_token.base.source_position, "#endif without prior #if"); + return; + } - /* eat '\n' */ - assert(pp_token.type == '\n' || pp_token.type == TP_EOF); - next_preprocessing_token(); + if (!conditional->skip) { + skip_mode = false; + } + pop_conditional(); } -#define GCC_COMPAT_MODE +static void parse_preprocessing_directive(void) +{ + in_pp_directive = true; + eat_pp('#'); + + if (skip_mode) { + switch (pp_token.kind) { + case TP_ifdef: + case TP_ifndef: + parse_ifdef_ifndef_directive(); + break; + case TP_else: + parse_else_directive(); + break; + case TP_endif: + parse_endif_directive(); + break; + default: + eat_pp_directive(); + break; + } + } else { + switch (pp_token.kind) { + case TP_define: + parse_define_directive(); + break; + case TP_undef: + parse_undef_directive(); + break; + case TP_ifdef: + case TP_ifndef: + parse_ifdef_ifndef_directive(); + break; + case TP_else: + parse_else_directive(); + break; + case TP_endif: + parse_endif_directive(); + break; + case TP_include: + parse_include_directive(); + break; + default: + if (info.at_line_begin) { + /* the nop directive "#" */ + break; + } + errorf(&pp_token.base.source_position, + "invalid preprocessing directive #%t", &pp_token); + eat_pp_directive(); + break; + } + } + + in_pp_directive = false; + assert(info.at_line_begin); +} + +static void prepend_include_path(const char *path) +{ + searchpath_entry_t *entry = OALLOCZ(&config_obstack, searchpath_entry_t); + entry->path = path; + entry->next = searchpath; + searchpath = entry; +} + +static void setup_include_path(void) +{ + /* built-in paths */ + prepend_include_path("/usr/include"); + + /* parse environment variable */ + const char *cpath = getenv("CPATH"); + if (cpath != NULL && *cpath != '\0') { + const char *begin = cpath; + const char *c; + do { + c = begin; + while (*c != '\0' && *c != ':') + ++c; + + size_t len = c-begin; + if (len == 0) { + /* for gcc compatibility (Matze: I would expect that + * nothing happens for an empty entry...) */ + prepend_include_path("."); + } else { + char *string = obstack_alloc(&config_obstack, len+1); + memcpy(string, begin, len); + string[len] = '\0'; + + prepend_include_path(string); + } + + begin = c+1; + /* skip : */ + if (*begin == ':') + ++begin; + } while(*c != '\0'); + } +} int pptest_main(int argc, char **argv); int pptest_main(int argc, char **argv) @@ -1534,47 +1827,110 @@ int pptest_main(int argc, char **argv) init_symbol_table(); init_tokens(); + obstack_init(&config_obstack); obstack_init(&pp_obstack); obstack_init(&input_obstack); - - const char *filename = "t.c"; - if (argc > 1) - filename = argv[1]; + strset_init(&stringset); + + setup_include_path(); + + /* simplistic commandline parser */ + const char *filename = NULL; + for (int i = 1; i < argc; ++i) { + const char *opt = argv[i]; + if (streq(opt, "-I")) { + prepend_include_path(argv[++i]); + continue; + } else if (streq(opt, "-E")) { + /* ignore */ + } else if (opt[0] == '-') { + fprintf(stderr, "Unknown option '%s'\n", opt); + } else { + if (filename != NULL) + fprintf(stderr, "Multiple inputs not supported\n"); + filename = argv[i]; + } + } + if (filename == NULL) { + fprintf(stderr, "No input specified\n"); + return 1; + } out = stdout; -#ifdef GCC_COMPAT_MODE - /* this is here so we can directly compare "gcc -E" output and our output */ + /* just here for gcc compatibility */ fprintf(out, "# 1 \"%s\"\n", filename); - fputs("# 1 \"\"\n", out); - fputs("# 1 \"\"\n", out); -#endif + fprintf(out, "# 1 \"\"\n"); + fprintf(out, "# 1 \"\"\n"); - bool ok = open_input(filename); - assert(ok); + FILE *file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Couldn't open input '%s'\n", filename); + return 1; + } + switch_input(file, filename); - while(true) { - /* we're at a line begin */ - if(pp_token.type == '#') { + while (true) { + if (pp_token.kind == '#' && info.at_line_begin) { parse_preprocessing_directive(); - } else { - /* parse+emit a line */ - while(pp_token.type != '\n') { - if(pp_token.type == TP_EOF) - goto end_of_main_loop; - emit_pp_token(); - next_preprocessing_token(); + continue; + } else if (pp_token.kind == TP_EOF) { + goto end_of_main_loop; + } else if (pp_token.kind == TP_IDENTIFIER && !in_pp_directive) { + symbol_t *symbol = pp_token.identifier.symbol; + pp_definition_t *pp_definition = symbol->pp_definition; + if (pp_definition != NULL && !pp_definition->is_expanding) { + expansion_pos = pp_token.base.source_position; + if (pp_definition->has_parameters) { + source_position_t position = pp_token.base.source_position; + add_token_info_t old_info = info; + next_preprocessing_token(); + add_token_info_t new_info = info; + + /* no opening brace -> no expansion */ + if (pp_token.kind == '(') { + eat_pp('('); + + /* parse arguments (TODO) */ + while (pp_token.kind != TP_EOF && pp_token.kind != ')') + next_preprocessing_token(); + } else { + token_t next_token = pp_token; + /* restore identifier token */ + pp_token.kind = TP_IDENTIFIER; + pp_token.identifier.symbol = symbol; + pp_token.base.source_position = position; + info = old_info; + emit_pp_token(); + + info = new_info; + pp_token = next_token; + continue; + } + info = old_info; + } + pp_definition->expand_pos = 0; + pp_definition->is_expanding = true; + current_expansion = pp_definition; + expand_next(); + continue; } - emit_pp_token(); - next_preprocessing_token(); } + + emit_pp_token(); + next_preprocessing_token(); } end_of_main_loop: + fputc('\n', out); + check_unclosed_conditionals(); close_input(); obstack_free(&input_obstack, NULL); obstack_free(&pp_obstack, NULL); + obstack_free(&config_obstack, NULL); + + strset_destroy(&stringset); exit_tokens(); exit_symbol_table();