#include <config.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+
#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 <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <stdbool.h>
-#include <ctype.h>
-
-//#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;
- bool has_arguments : 1;
- size_t n_arguments;
- symbol_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;
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 pp_conditional_t *conditional_stack;
-token_t pp_token;
-static bool resolve_escape_sequences = false;
-static bool do_print_spaces = true;
-static bool do_expansions;
-static bool skip_mode;
-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 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;
*/
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;
}
/**
*
* @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 = '\\';
}
/**
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
}
*/
static inline bool is_octal_digit(int chr)
{
- switch(chr) {
+ switch (chr) {
case '0':
case '1':
case '2':
* 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;
*
* @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 '\\';
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;
}
goto end_of_string;
default:
- obstack_1grow(&symbol_obstack, (char) CC);
+ obstack_grow_symbol(&symbol_obstack, input.c);
next_char();
break;
}
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");
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);
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;
}
goto end_of_char_constant;
default:
- obstack_1grow(&symbol_obstack, (char) CC);
+ obstack_1grow(&symbol_obstack, (char) input.c);
next_char();
break;
}
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.type = TP_CHARACTER_CONSTANT;
- pp_token.v.string.begin = string;
- pp_token.v.string.size = size;
+ pp_token.kind = TP_CHARACTER_CONSTANT;
+ pp_token.string.string = make_string(string, size);
+
+ if (size == 0) {
+ parse_error("empty character constant");
+ }
}
#define SYMBOL_CHARS_WITHOUT_E_P \
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 */
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;
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;
}
}
+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;
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;
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;
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;
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)
-{
- if(do_print_spaces)
- counted_spaces++;
-
- 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)
-{
- if(do_print_spaces)
- counted_spaces++;
-
- 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
case '7':
case '8':
case '9':
- put_back(CC);
- CC = '.';
+ put_back(input.c);
+ input.c = '.';
parse_number();
return;
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 '&':
MAYBE('=', TP_SLASHEQUAL)
case '*':
next_char();
+ info.had_whitespace = true;
skip_multiline_comment();
goto restart;
case '/':
next_char();
+ info.had_whitespace = true;
skip_line_comment();
goto restart;
ELSE('/')
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('%')
case '#':
MAYBE_PROLOG
MAYBE('#', TP_HASHHASH)
- ELSE('#')
+ ELSE_CODE(
+ pp_token.kind = '#';
+ )
case '?':
case '[':
case ';':
case ',':
case '\\':
- pp_token.type = CC;
+ pp_token.kind = input.c;
next_char();
return;
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;
}
}
{
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;
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);
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 >= 8) {
- 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 (skip_mode)
return;
- if (pp_token.type != '\n') {
- print_spaces();
- input.had_non_space = true;
+ 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();
}
}
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;
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;
}
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;
eat_pp(TP_define);
assert(obstack_object_size(&pp_obstack) == 0);
- if (pp_token.type != TP_IDENTIFIER) {
- errorf(&pp_token.source_position,
+ 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]));
/* 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) */
- if (CC == '(') {
+ if (input.c == '(') {
/* eat the '(' */
next_preprocessing_token();
/* get next token after '(' */
next_preprocessing_token();
while (true) {
- switch (pp_token.type) {
+ 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");
goto error_out;
}
break;
case TP_IDENTIFIER:
- obstack_ptr_grow(&pp_obstack, pp_token.v.symbol);
+ obstack_ptr_grow(&pp_obstack, pp_token.identifier.symbol);
next_preprocessing_token();
- if (pp_token.type == ',') {
+ if (pp_token.kind == ',') {
next_preprocessing_token();
break;
}
- if (pp_token.type != ')') {
- errorf(&pp_token.source_position,
+ if (pp_token.kind != ')') {
+ errorf(&pp_token.base.source_position,
"expected ',' or ')' after identifier, got '%t'",
&pp_token);
goto error_out;
}
break;
case ')':
+ next_preprocessing_token();
goto finish_argument_list;
default:
- errorf(&pp_token.source_position,
+ 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_arguments = true;
- new_definition->n_arguments
- = obstack_object_size(&pp_obstack) / sizeof(new_definition->arguments[0]);
- new_definition->arguments = obstack_finish(&pp_obstack);
+ 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();
}
/* 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);
+ 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);
{
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);
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();
}
-/* skip spaces advancing at the start of the next preprocessing token */
-static void skip_spaces(void)
-{
- while (true) {
- switch (CC) {
- case ' ':
- case '\t':
- if(do_print_spaces)
- counted_spaces++;
- next_char();
- continue;
- case '/':
- next_char();
- if (CC == '/') {
- next_char();
- skip_line_comment();
- continue;
- } else if (CC == '*') {
- next_char();
- skip_multiline_comment();
- continue;
- } else {
- put_back(CC);
- CC = '/';
- }
- return;
- default:
- return;
- }
- }
-}
-
static const char *parse_headername(void)
{
/* 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;
+ }
- /* skip spaces so we reach start of next preprocessing token */
- skip_spaces();
-
- assert(obstack_object_size(&input_obstack) == 0);
+ assert(obstack_object_size(&symbol_obstack) == 0);
/* check wether we have a "... or <... headername */
- switch (CC) {
+ 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(
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(
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 identify_string(headername);
+}
- return headername;
+static bool do_include(bool system_include, const char *headername)
+{
+ 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;
+ }
+ }
+
+ 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 */
-
- print_spaces();
-
+ skip_whitespace();
+ bool system_include = input.c == '<';
const char *headername = parse_headername();
if (headername == NULL) {
eat_pp_directive();
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 false;
* 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 */
- do_print_spaces = true;
- do_expansions = true;
+ 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;
static void parse_ifdef_ifndef_directive(void)
{
- bool is_ifndef = (pp_token.type == TP_ifndef);
+ 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.source_position;
+ conditional->source_position = pp_token.base.source_position;
conditional->skip = true;
return;
}
- if (pp_token.type != TP_IDENTIFIER) {
- errorf(&pp_token.source_position,
+ 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.v.symbol;
+ symbol_t *symbol = pp_token.identifier.symbol;
pp_definition_t *pp_definition = symbol->pp_definition;
next_preprocessing_token();
- if (pp_token.type != '\n') {
- errorf(&pp_token.source_position,
+ if (!info.at_line_begin) {
+ errorf(&pp_token.base.source_position,
"extra tokens at end of #%s",
is_ifndef ? "ifndef" : "ifdef");
eat_pp_directive();
}
pp_conditional_t *conditional = push_conditional();
- conditional->source_position = pp_token.source_position;
+ conditional->source_position = pp_token.base.source_position;
conditional->condition = condition;
if (!condition) {
{
eat_pp(TP_else);
- if (pp_token.type != '\n') {
+ if (!info.at_line_begin) {
if (!skip_mode) {
- warningf(&pp_token.source_position, "extra tokens at end of #else");
+ 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.source_position, "#else without prior #if");
+ errorf(&pp_token.base.source_position, "#else without prior #if");
return;
}
if (conditional->in_else) {
- errorf(&pp_token.source_position,
+ errorf(&pp_token.base.source_position,
"#else after #else (condition started %P)",
conditional->source_position);
skip_mode = true;
if (!conditional->skip) {
skip_mode = conditional->condition;
}
- conditional->source_position = pp_token.source_position;
+ conditional->source_position = pp_token.base.source_position;
}
static void parse_endif_directive(void)
{
eat_pp(TP_endif);
- if (pp_token.type != '\n') {
+ if (!info.at_line_begin) {
if (!skip_mode) {
- warningf(&pp_token.source_position,
- "extra tokens at end of #endif");
+ warningf(WARN_OTHER, &pp_token.base.source_position, "extra tokens at end of #endif");
}
eat_pp_directive();
}
pp_conditional_t *conditional = conditional_stack;
if (conditional == NULL) {
- errorf(&pp_token.source_position, "#endif without prior #if");
+ errorf(&pp_token.base.source_position, "#endif without prior #if");
return;
}
static void parse_preprocessing_directive(void)
{
- do_print_spaces = false;
- do_expansions = false;
+ in_pp_directive = true;
eat_pp('#');
if (skip_mode) {
- switch(pp_token.type) {
+ switch (pp_token.kind) {
case TP_ifdef:
case TP_ifndef:
parse_ifdef_ifndef_directive();
break;
}
} else {
- switch(pp_token.type) {
+ switch (pp_token.kind) {
case TP_define:
parse_define_directive();
break;
case TP_endif:
parse_endif_directive();
break;
- case TP_include: {
- bool in_new_source = parse_include_directive();
- /* no need to do anything if source file switched */
- if (in_new_source)
- return;
- break;
- }
- case '\n':
- /* the nop directive */
+ case TP_include:
+ parse_include_directive();
break;
default:
- errorf(&pp_token.source_position,
+ 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;
}
}
- do_print_spaces = true;
- do_expansions = true;
+ in_pp_directive = false;
+ assert(info.at_line_begin);
+}
- /* eat '\n' */
- assert(pp_token.type == '\n' || pp_token.type == TP_EOF);
- next_preprocessing_token();
+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;
}
-#define GCC_COMPAT_MODE
+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)
init_symbol_table();
init_tokens();
+ obstack_init(&config_obstack);
obstack_init(&pp_obstack);
obstack_init(&input_obstack);
+ strset_init(&stringset);
+
+ setup_include_path();
- const char *filename = "t.c";
- if (argc > 1)
- filename = argv[1];
+ /* 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 \"<built-in>\"\n", out);
- fputs("# 1 \"<command-line>\"\n", out);
-#endif
+ fprintf(out, "# 1 \"<built-in>\"\n");
+ fprintf(out, "# 1 \"<command-line>\"\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();