From e9a55c2b3302ad0bd64d47a2268669c362cd0aef Mon Sep 17 00:00:00 2001 From: Joshua Phillips Date: Sat, 13 Dec 2008 18:04:20 +0000 Subject: [PATCH] Converted tabs to spaces, build into build/ dir Converted all tabs to four spaces. Altered build system to build into a build/ directory, keeping the source tree nice and clean. --- .gitignore | 4 +- SConscript | 16 + SConstruct | 67 +- bstr.c | 454 ++++++------ c_btypes.h | 20 +- cc.c | 18 +- cc.h | 6 +- cpp.c | 2242 ++++++++++++++++++++++++++++++------------------------------ cpp.h | 44 +- cpp_main.c | 66 +- errors.c | 36 +- parse.c | 1156 +++++++++++++++---------------- scanner.c | 582 ++++++++-------- scanner.h | 30 +- stree.c | 530 +++++++------- stree.h | 40 +- 16 files changed, 2658 insertions(+), 2653 deletions(-) create mode 100644 SConscript rewrite SConstruct (87%) rewrite bstr.c (72%) rewrite cpp.c (83%) rewrite cpp_main.c (91%) rewrite parse.c (79%) rewrite scanner.c (76%) rewrite stree.c (79%) diff --git a/.gitignore b/.gitignore index aa9e6fe..0025b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ -*.o -cc -cpp +build/ gmon.out core .sconsign.dblite diff --git a/SConscript b/SConscript new file mode 100644 index 0000000..808f1d5 --- /dev/null +++ b/SConscript @@ -0,0 +1,16 @@ +import os.path +Import(['env']) + +# Table of programs and source files + +programs = { + 'cpp': ['cpp.c', 'bstr.c', 'errors.c', 'cpp_main.c'], + 'cc' : ['cpp.c', 'bstr.c', 'errors.c', 'scanner.c', 'stree.c', 'cc.c', 'parse.c'], +} + +objects = {} +for program in programs: + for source in programs[program]: + if source not in objects: + objects[source] = env.Object(source) + env.Program(program, [objects[i] for i in programs[program]]) diff --git a/SConstruct b/SConstruct dissimilarity index 87% index f391d11..1b23cc5 100644 --- a/SConstruct +++ b/SConstruct @@ -1,38 +1,29 @@ -# Build options -opts = Options('scache.conf') -opts.AddOptions( - PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'), - PathOption('DESTDIR', 'Set the installation root', '/'), - ('INCLUDE_DIRS', 'Set the default include directories to build into the compiler', '/include:/usr/include:/usr/local/include:/opt/include'), - ('CC', 'Set the C compiler to use'), - ('LINK', 'Set the linker to use'), - ('CFLAGS', 'Change the flags for the compiler', '-Wall -g -ffunction-sections'), - ('LINKFLAGS', 'Change the flags for the linker', '-Wl,--gc-sections'), - BoolOption('verbose', 'Show full commands during the build process', False), -) - -env = Environment(options = opts) -Help(opts.GenerateHelpText(env)) -opts.Save('scache.conf', env) - -# Build rules -if not env['verbose']: - env['CCCOMSTR'] = ' Compiling $TARGET' - env['LINKCOMSTR'] = ' Linking $TARGET' - env['ARCOMSTR'] = ' Archiving $TARGET' - env['RANLIBCOMSTR'] = ' Indexing $TARGET' - -#p = env.Program('cpp', cpp_sources) - -objects = {} -def get_objects(*source_list): - object_list = [] - for i in source_list: - if i not in objects: - objects[i] = env.Object(i) - object_list.append(objects[i]) - return object_list - -cpp_program = env.Program('cpp', get_objects('cpp.c', 'bstr.c', 'errors.c', 'cpp_main.c')) -cc_program = env.Program('cc', get_objects('cpp.c', 'bstr.c', 'errors.c', 'scanner.c', 'stree.c', - 'cc.c', 'parse.c')) +# Build options +opts = Options('scache.conf') +opts.AddOptions( + PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'), + PathOption('DESTDIR', 'Set the installation root', '/'), + ('BUILDDIR', 'Set the sub-directory to put object files in', 'build'), + ('INCLUDE_DIRS', 'Set the default include directories to build into the compiler', '/include:/usr/include:/usr/local/include:/opt/include'), + ('CC', 'Set the C compiler to use'), + ('LINK', 'Set the linker to use'), + ('CFLAGS', 'Change the flags for the compiler', '-Wall -g -ffunction-sections'), + ('LINKFLAGS', 'Change the flags for the linker', '-Wl,--gc-sections'), + BoolOption('verbose', 'Show full commands during the build process', False), +) + +env = Environment(options = opts) +Help(opts.GenerateHelpText(env)) +opts.Save('scache.conf', env) + +# Pretty coloured output +if not env['verbose']: + env['CCCOMSTR'] = ' Compiling $TARGET' + env['LINKCOMSTR'] = ' Linking $TARGET' + env['ARCOMSTR'] = ' Archiving $TARGET' + env['RANLIBCOMSTR'] = ' Indexing $TARGET' + +SConscript('SConscript', + exports = ['env'], + build_dir = env['BUILDDIR'], + duplicate = 0) diff --git a/bstr.c b/bstr.c dissimilarity index 72% index 2675999..39cb605 100644 --- a/bstr.c +++ b/bstr.c @@ -1,227 +1,227 @@ -#define _GNU_SOURCE -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "ctype.h" -#include "bstr.h" - -char *strchrnul(const char *str, int c) -{ - char *p = strchr(str, c); - if (!p) - p = (char *) str + strlen(str); - return p; -} - -char *strdcat(char **dest, const char *src) -{ - int la, lb; - char *p; - la = *dest ? strlen(*dest) : 0; - lb = strlen(src); - p = realloc(*dest, la + lb + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest + la, src, lb + 1); - return *dest; -} - -char *strdncat(char **dest, const char *src, size_t n) -{ - int la, lb; - char *p; - la = *dest ? strlen(*dest) : 0; - lb = strlen(src); - if (lb > n) - lb = n; - p = realloc(*dest, la + lb + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest + la, src, lb); - (*dest)[la + lb] = '\0'; - return *dest; -} - -char *strdcpy(char **dest, const char *src) -{ - int l; - char *p; - l = strlen(src); - p = realloc(*dest, l + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest, src, l + 1); - return *dest; -} - -char *strdncpy(char **dest, const char *src, size_t n) -{ - int l; - char *p; - l = strlen(src); - if (l > n) - l = n; - p = realloc(*dest, l + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest, src, l); - (*dest)[l] = '\0'; - return *dest; -} - -char *strdcatc(char **dest, int ch) -{ - int l; - char *p; - l = *dest ? strlen(*dest) : 0; - p = realloc(*dest, l + 2); - if (!p) - return NULL; - *dest = p; - (*dest)[l] = ch; - (*dest)[l + 1] = '\0'; - return *dest; -} - -char *strldcat(char **dest, int *dest_len, const char *src) -{ - int lb; - char *p; - lb = strlen(src); - p = realloc(*dest, *dest_len + lb + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest + *dest_len, src, lb + 1); - *dest_len += lb; - return *dest; -} - -char *strldncat(char **dest, int *dest_len, const char *src, size_t n) -{ - int lb; - char *p; - lb = strlen(src); - if (lb > n) - lb = n; - p = realloc(*dest, *dest_len + lb + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest + *dest_len, src, lb); - (*dest)[*dest_len + lb] = '\0'; - *dest_len += lb; - return *dest; -} - -char *strldcpy(char **dest, int *dest_len, const char *src, int src_len) -{ - char *p; - p = realloc(*dest, src_len + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest, src, src_len + 1); - *dest_len = src_len; - return *dest; -} - -char *strldncpy(char **dest, int *dest_len, const char *src, size_t n) -{ - int l; - char *p; - l = strlen(src); - if (l > n) - l = n; - p = realloc(*dest, l + 1); - if (!p) - return NULL; - *dest = p; - memcpy(*dest, src, l); - (*dest)[l] = '\0'; - *dest_len = l; - return *dest; -} - -char *strldcatc(char **dest, int *dest_len, int ch) -{ - char *p; - p = realloc(*dest, *dest_len + 2); - if (!p) - return NULL; - *dest = p; - (*dest)[*dest_len] = ch; - (*dest)[*dest_len + 1] = '\0'; - (*dest_len)++; - return *dest; -} - -char *sdprintf(const char *fmt, ...) -{ - char *p; - va_list ap; - va_start(ap, fmt); - p = vsdprintf(fmt, ap); - va_end(ap); - return p; -} - -char *vsdprintf(const char *fmt, va_list ap) -{ - char buf[2], *p; - int n; - n = vsnprintf(buf, 2, fmt, ap); - p = malloc(n + 1); - if (!p) - return NULL; - vsprintf(p, fmt, ap); - return p; -} - -void strtolower(char *str) -{ - while (*str){ - *str = tolower(*str); - str++; - } -} - -void strtoupper(char *str) -{ - while (*str){ - *str = toupper(*str); - str++; - } -} - -char *pathname(const char *str) -{ - char *p = strrchr(str, '/'); - if (!p) - return strdup("."); - else - return strndup(str, p - str); -} - -char *filesuffix(const char *str) -{ - const char *p = str + strlen(str) - 1; - while (p >= str){ - if (*p == '.'){ - // Suffix, or hidden file? - if (p == str || p[-1] == '/'){ - // Hidden file. :-( - return strdup(""); - } else { - return strdup(p + 1); - } - } else - p--; - } - return strdup(""); -} - +#define _GNU_SOURCE +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "ctype.h" +#include "bstr.h" + +char *strchrnul(const char *str, int c) +{ + char *p = strchr(str, c); + if (!p) + p = (char *) str + strlen(str); + return p; +} + +char *strdcat(char **dest, const char *src) +{ + int la, lb; + char *p; + la = *dest ? strlen(*dest) : 0; + lb = strlen(src); + p = realloc(*dest, la + lb + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest + la, src, lb + 1); + return *dest; +} + +char *strdncat(char **dest, const char *src, size_t n) +{ + int la, lb; + char *p; + la = *dest ? strlen(*dest) : 0; + lb = strlen(src); + if (lb > n) + lb = n; + p = realloc(*dest, la + lb + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest + la, src, lb); + (*dest)[la + lb] = '\0'; + return *dest; +} + +char *strdcpy(char **dest, const char *src) +{ + int l; + char *p; + l = strlen(src); + p = realloc(*dest, l + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest, src, l + 1); + return *dest; +} + +char *strdncpy(char **dest, const char *src, size_t n) +{ + int l; + char *p; + l = strlen(src); + if (l > n) + l = n; + p = realloc(*dest, l + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest, src, l); + (*dest)[l] = '\0'; + return *dest; +} + +char *strdcatc(char **dest, int ch) +{ + int l; + char *p; + l = *dest ? strlen(*dest) : 0; + p = realloc(*dest, l + 2); + if (!p) + return NULL; + *dest = p; + (*dest)[l] = ch; + (*dest)[l + 1] = '\0'; + return *dest; +} + +char *strldcat(char **dest, int *dest_len, const char *src) +{ + int lb; + char *p; + lb = strlen(src); + p = realloc(*dest, *dest_len + lb + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest + *dest_len, src, lb + 1); + *dest_len += lb; + return *dest; +} + +char *strldncat(char **dest, int *dest_len, const char *src, size_t n) +{ + int lb; + char *p; + lb = strlen(src); + if (lb > n) + lb = n; + p = realloc(*dest, *dest_len + lb + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest + *dest_len, src, lb); + (*dest)[*dest_len + lb] = '\0'; + *dest_len += lb; + return *dest; +} + +char *strldcpy(char **dest, int *dest_len, const char *src, int src_len) +{ + char *p; + p = realloc(*dest, src_len + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest, src, src_len + 1); + *dest_len = src_len; + return *dest; +} + +char *strldncpy(char **dest, int *dest_len, const char *src, size_t n) +{ + int l; + char *p; + l = strlen(src); + if (l > n) + l = n; + p = realloc(*dest, l + 1); + if (!p) + return NULL; + *dest = p; + memcpy(*dest, src, l); + (*dest)[l] = '\0'; + *dest_len = l; + return *dest; +} + +char *strldcatc(char **dest, int *dest_len, int ch) +{ + char *p; + p = realloc(*dest, *dest_len + 2); + if (!p) + return NULL; + *dest = p; + (*dest)[*dest_len] = ch; + (*dest)[*dest_len + 1] = '\0'; + (*dest_len)++; + return *dest; +} + +char *sdprintf(const char *fmt, ...) +{ + char *p; + va_list ap; + va_start(ap, fmt); + p = vsdprintf(fmt, ap); + va_end(ap); + return p; +} + +char *vsdprintf(const char *fmt, va_list ap) +{ + char buf[2], *p; + int n; + n = vsnprintf(buf, 2, fmt, ap); + p = malloc(n + 1); + if (!p) + return NULL; + vsprintf(p, fmt, ap); + return p; +} + +void strtolower(char *str) +{ + while (*str){ + *str = tolower(*str); + str++; + } +} + +void strtoupper(char *str) +{ + while (*str){ + *str = toupper(*str); + str++; + } +} + +char *pathname(const char *str) +{ + char *p = strrchr(str, '/'); + if (!p) + return strdup("."); + else + return strndup(str, p - str); +} + +char *filesuffix(const char *str) +{ + const char *p = str + strlen(str) - 1; + while (p >= str){ + if (*p == '.'){ + // Suffix, or hidden file? + if (p == str || p[-1] == '/'){ + // Hidden file. :-( + return strdup(""); + } else { + return strdup(p + 1); + } + } else + p--; + } + return strdup(""); +} + diff --git a/c_btypes.h b/c_btypes.h index 2d95cc0..e13fe6c 100644 --- a/c_btypes.h +++ b/c_btypes.h @@ -8,13 +8,13 @@ // for a basic type such as 'int', or 'const unsigned long' // but for more complex types (structs, pointers), it can't. enum btype_base_type { - BT_VOID = 0, - BT_CHAR, - BT_INT, - BT_FLOAT, - BT_DOUBLE, - BT_POINTER, // pointer to type in btype::node - BT_STRUCT, // struct OR union (btype::node points to STF_TAG node) + BT_VOID = 0, + BT_CHAR, + BT_INT, + BT_FLOAT, + BT_DOUBLE, + BT_POINTER, // pointer to type in btype::node + BT_STRUCT, // struct OR union (btype::node points to STF_TAG node) }; // qualifiers and type-qualifiers @@ -28,9 +28,9 @@ enum btype_base_type { #define TQ_RESTRICT (1 << 7) struct btype { - enum btype_base_type base_type; - int qualifiers; // TQ_* constants - struct stree *node; // stree node for type + enum btype_base_type base_type; + int qualifiers; // TQ_* constants + struct stree *node; // stree node for type }; #endif diff --git a/cc.c b/cc.c index caa40be..a4d536f 100644 --- a/cc.c +++ b/cc.c @@ -8,16 +8,16 @@ int main(int argc, char **argv) { - struct cc cc_obj, *cc = &cc_obj; + struct cc cc_obj, *cc = &cc_obj; - lex_create(&cc->lex); - cpp_include_file(&cc->lex.cpp, "", stdin, false); - lex_start(&cc->lex); - cc_parse(cc); - stree_dump(cc, cc->stree, stdout); - stree_destroy(cc->stree); - lex_delete(&cc->lex); + lex_create(&cc->lex); + cpp_include_file(&cc->lex.cpp, "", stdin, false); + lex_start(&cc->lex); + cc_parse(cc); + stree_dump(cc, cc->stree, stdout); + stree_destroy(cc->stree); + lex_delete(&cc->lex); - return 0; + return 0; } diff --git a/cc.h b/cc.h index 7a1bd9b..92be351 100644 --- a/cc.h +++ b/cc.h @@ -7,9 +7,9 @@ struct cc; #include "stree.h" struct cc { - struct lexer lex; - struct stree *stree; - struct stree *scope; + struct lexer lex; + struct stree *stree; + struct stree *scope; }; #endif diff --git a/cpp.c b/cpp.c dissimilarity index 83% index e743524..d1a5082 100644 --- a/cpp.c +++ b/cpp.c @@ -1,1121 +1,1121 @@ -#include "cpp.h" -#include "errors.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "bstr.h" -#include "stdarg.h" -#include "stdbool.h" -#include "ctype.h" -#include "sys/types.h" -#include "sys/stat.h" -#include "assert.h" - -#define CONFIG_INCLUDE_DIRS "/usr/include" - -//#define trace(...) fprintf(stderr, __VA_ARGS__) -#define trace(...) - -// A value from an expression -// (in #if etc.) -struct exprval { - int value; -}; - -void cpp_delete_macro(struct cpp *cpp, struct macro *m); -void cpp_lex(struct cpp *cpp); - -int cpp_get_col(struct cpp *cpp, char *p) -{ - return p + 1 - cpp->line_buf; -} - -#define cpp_error(cpp, p, ...) cpp_message(cpp, p, NULL, "error: " __VA_ARGS__) -#define cpp_warning(cpp, p, ...) cpp_message(cpp, p, NULL, "warning: " __VA_ARGS__) -#define cpp_error_loc(cpp, sloc, ...) cpp_message(cpp, NULL, sloc, "error: " __VA_ARGS__) -#define cpp_warning_loc(cpp, sloc, ...) cpp_message(cpp, NULL, sloc, "warning: " __VA_ARGS__) -void cpp_message(struct cpp *cpp, char *p, struct sloc *sloc, const char *fmt, ...) -{ - va_list ap; - struct sloc my_sloc; - if (!sloc){ - my_sloc = cpp->line_loc; - my_sloc.col = cpp_get_col(cpp, p); - sloc = &my_sloc; - } - fprintf(stderr, "%s:%d: ", - sloc->name, - sloc->line/*, - sloc->col*/); - // NOTE: don't print col number, because after macro substitution, - // the column number becomes incorrect. Fix=??? - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); -} - -void cpp_init(struct cpp *cpp) -{ - cpp->include_top = NULL; - cpp->stale_files = NULL; - cpp->line_buf = NULL; - cpp->macros = NULL; - cpp->include_dirs = estrdup(CONFIG_INCLUDE_DIRS); -} - -void cpp_delete(struct cpp *cpp) -{ - struct incfile *incf, *incf_prev; - incf = cpp->include_top; - while (incf){ - incf_prev = incf->prev; - free(incf); - incf = incf_prev; - } - incf = cpp->stale_files; - while (incf){ - incf_prev = incf->prev; - free(incf); - incf = incf_prev; - } - free(cpp->line_buf); - free(cpp->include_dirs); - while (cpp->macros){ - cpp_delete_macro(cpp, cpp->macros); - } -} - -struct macro *cpp_create_macro(const char *name) -{ - struct macro *m; - m = emalloc(sizeof(struct macro) + strlen(name)); - m->next = NULL; - m->args = NULL; - m->has_args = false; - m->text = NULL; - strcpy(m->name, name); - return m; -} - -struct macro *cpp_create_push_macro(struct cpp *cpp, const char *name) -{ - struct macro *m; - m = cpp_create_macro(name); - m->next = cpp->macros; - cpp->macros = m; - return m; -} - -struct macro *cpp_find_macro(struct cpp *cpp, const char *name) -{ - struct macro *m; - m = cpp->macros; - while (m && strcmp(m->name, name)){ - m = m->next; - } - return m; -} - -struct macro *cpp_find_macro_arg(struct macro *m, const char *name) -{ - struct macro *m_arg; - m_arg = m->args; - while (m_arg && strcmp(m_arg->name, name)){ - m_arg = m_arg->next; - } - return m_arg; -} - -static int macro_count_args(struct macro *m) -{ - struct macro *m_arg; - int n; - m_arg = m->args; - n = 0; - while (m_arg){ - n++; - m_arg = m_arg->next; - } - return n; -} - -void cpp_delete_macro(struct cpp *cpp, struct macro *m) -{ - struct macro *m_i, *m_arg, *m_arg_next; - m_i = cpp->macros; - if (m_i == m){ - cpp->macros = m->next; - } else { - while (m_i && m_i->next != m){ - m_i = m_i->next; - } - if (m_i){ - m_i->next = m->next; - } - } - m_arg = m->args; - while (m_arg){ - m_arg_next = m_arg->next; - free(m_arg->text); - free(m_arg); - m_arg = m_arg_next; - } - free(m->text); - free(m); -} - - -// push a file on the include stack -void cpp_include_file(struct cpp *cpp, const char *name, FILE *f, bool must_close) -{ - struct incfile *incf; - incf = emalloc(sizeof(struct incfile) + strlen(name)); - incf->f = f; - strcpy(incf->name, name); - incf->line = 0; - incf->must_close = must_close; - incf->eof = false; - incf->cond_d = incf->cond_td = 0; - // push on include stack - incf->prev = cpp->include_top; - cpp->include_top = incf; -} - -static bool get_dir(char **dirlist, char **output) -{ - if (**dirlist){ - char *p = strchrnul(*dirlist, ':'); - strdncpy(output, *dirlist, p - *dirlist); - *dirlist = p; - return true; - } else { - return false; - } -} - -// open a file and push it on the include stack -bool cpp_open_include_file(struct cpp *cpp, const char *name) -{ - FILE *f; - char *dirlist, *attempt_file = NULL; - struct stat stat_struct; - - // try current directory - if (!stat(name, &stat_struct)){ - f = fopen(name, "r"); - assert(f != NULL); - cpp_include_file(cpp, name, f, true); - return true; - } - - // try include dirs - dirlist = cpp->include_dirs; - while (get_dir(&dirlist, &attempt_file)){ - strdcatc(&attempt_file, '/'); - strdcat(&attempt_file, name); - if (!stat(attempt_file, &stat_struct)){ - f = fopen(attempt_file, "r"); - assert(f != NULL); - cpp_include_file(cpp, attempt_file, f, true); - free(attempt_file); - return true; - } - } - free(attempt_file); - return false; -} - -void cpp_pop_include(struct cpp *cpp) -{ - struct incfile *incf; - incf = cpp->include_top; - if (incf){ - cpp->include_top = incf->prev; - incf->prev = cpp->stale_files; - cpp->stale_files = incf; - if (incf->must_close == true){ - fclose(incf->f); - } - } -} - -void cpp_clear_line(struct cpp *cpp) -{ - if (cpp->line_buf) - strdcpy(&cpp->line_buf, ""); -} - -void cpp_delete_line(struct cpp *cpp) -{ - // TODO: can we, erm, keep the buffer?! - free(cpp->line_buf); - cpp->line_buf = NULL; -} - -void cpp_read_line(struct cpp *cpp) -{ - struct incfile *incf; - char **pstr = &cpp->line_buf; - int ch; - - incf = cpp->include_top; - if (incf && incf->eof){ - cpp_pop_include(cpp); - incf = cpp->include_top; - } - if (incf){ - strdcpy(pstr, ""); - do { - ch = fgetc(incf->f); - if (ch == EOF){ - incf->eof = true; - } else if (ch == '\\'){ - // backslashed newline? - ch = fgetc(incf->f); - if (ch == '\n'){ - // yes - incf->line++; - strdcatc(pstr, ' '); // insert a space to ensure token separation - ch = '@'; // this could be anything - } else { - // no - strdcatc(pstr, '\\'); - strdcatc(pstr, ch); - } - } else if (ch != '\n'){ - strdcatc(pstr, ch); - } - } while (ch != EOF && ch != '\n'); - incf->line++; - cpp->line_loc.name = incf->name; - cpp->line_loc.line = incf->line; - } else { - cpp_delete_line(cpp); - } -} - -// read another line and stick it on the end of the buffer -void cpp_append_line(struct cpp *cpp) -{ - char *old_buf; - old_buf = cpp->line_buf; - cpp->line_buf = NULL; - cpp_read_line(cpp); - if (cpp->line_buf){ - strdcatc(&old_buf, ' '); - strdcat(&old_buf, cpp->line_buf); - free(cpp->line_buf); - } - cpp->line_buf = old_buf; -} - -static void white(char **p, char *set) -{ - while (**p && strchr(set, **p)){ - (*p)++; - } -} - -// read a number -bool cpp_lex_number(struct cpp *cpp, char **p_start, char **output) -{ - char *p = *p_start; - - if (*p == '0'){ - // octal or hexadecimal - p++; - if (tolower(*p) == 'x'){ - // hexadecimal - p++; - while (isdigit(*p) || (tolower(*p) >= 'a' && tolower(*p) <= 'f')){ - p++; - } - } else { - // octal - p++; - while (*p >= '0' && *p <= '7'){ - p++; - } - } - } else if (*p >= '0' && *p <= '9'){ - // decimal - while (*p >= '0' && *p <= '9'){ - p++; - } - if (*p == '.'){ - // decimal part - p++; - while (*p >= '0' && *p <= '9'){ - p++; - } - } - if (tolower(*p) == 'e'){ - // exponent part - p++; - while (*p >= '0' && *p <= '9'){ - p++; - } - } - } else { - // not a number at all - return false; - } - strdncat(output, *p_start, p - *p_start); - *p_start = p; - return true; -} - -// read a string literal -bool cpp_lex_string(struct cpp *cpp, char *quotes, char **p_start, char **output) -{ - char *p = *p_start, quote; - char *p_last_start; - if (*p && strchr(quotes, *p)){ - quote = *p; - p_last_start = *p_start; - p++; - if (output){ - strdcpy(output, ""); - } - while (*p && *p != quote){ - if (*p == '\\'){ - if (p[1] == quote || p[1] == '\\'){ - // escaped quotation mark or backslash - p += 2; - if (output){ - strdncat(output, p_last_start, p - p_last_start); - } - p_last_start = p; - } else { - p++; - } - } else { - p++; - } - } - if (*p != quote){ - cpp_error(cpp, *p_start, "unterminated %s literal", - (quote == '\'') ? "character" : "string"); - } else { - p++; - } - if (output){ - strdncat(output, p_last_start, p - p_last_start); - } - *p_start = p; - return true; - } else { - return false; - } -} - -// read an identifier -bool cpp_lex_ident(struct cpp *cpp, char **p_start, char **output) -{ - char *p = *p_start; - if (*p && (isalpha(*p) || *p == '_')){ - while (*p && (isalnum(*p) || *p == '_')){ - p++; - } - strdncpy(output, *p_start, p - *p_start); - *p_start = p; - return true; - } else { - return false; - } -} - -bool cpp_muted(struct cpp *cpp) -{ - struct incfile *incf; - incf = cpp->include_top; - if (incf){ - if (incf->cond_d > incf->cond_td){ - return true; - } - } - return false; -} - -void cpp_parse_include(struct cpp *cpp, char *p, char *p_start) -{ - char *inc_str = NULL, *p_str_start; - white(&p, " \t"); - p_str_start = p; - if (cpp_lex_string(cpp, "<\"", &p, &inc_str)){ - white(&p, " \t"); - if (*p){ - cpp_warning(cpp, p, "junk at end of #include directive"); - } - if (strlen(inc_str) == 2){ - cpp_error(cpp, p_str_start, "empty filename in #include"); - } else { - inc_str[strlen(inc_str) - 1] = '\0'; - if (!cpp_open_include_file(cpp, inc_str + 1)){ - cpp_error(cpp, p_str_start, "%s: no such file or directory", inc_str + 1); - } - free(inc_str); - } - cpp_clear_line(cpp); - } else { - cpp_error(cpp, p_start, "#include expects \"FILENAME\" or "); - } -} - -bool cpp_parse_macro_param(struct cpp *cpp, char **p, struct macro *m, struct macro ***p_next) -{ - char *param_name = NULL; - struct sloc arg_loc; - struct macro *m_arg; - bool result; - - arg_loc = cpp->line_loc; - arg_loc.col = cpp_get_col(cpp, *p); - if (cpp_lex_ident(cpp, p, ¶m_name)){ - m_arg = cpp_find_macro_arg(m, param_name); - if (m_arg){ - cpp_error_loc(cpp, &arg_loc, "repeated macro parameter \"%s\"", param_name); - } else { - m_arg = cpp_create_macro(param_name); - **p_next = m_arg; - *p_next = &m_arg->next; - } - result = true; - } else { - result = false; - } - free(param_name); - return result; -} - -void cpp_parse_define(struct cpp *cpp, char *p, char *p_start) -{ - char *macro_name = NULL; - struct macro *m, **p_next; - struct sloc name_sloc; - bool failed = false; - - white(&p, " \t"); - name_sloc = cpp->line_loc; - name_sloc.col = cpp_get_col(cpp, p); - if (cpp_lex_ident(cpp, &p, ¯o_name)){ - m = cpp_find_macro(cpp, macro_name); - if (m){ - cpp_warning_loc(cpp, &name_sloc, "\"%s\" redefined", macro_name); - cpp_warning_loc(cpp, &m->sloc, "previous definition was here"); - cpp_delete_macro(cpp, m); - } - m = cpp_create_push_macro(cpp, macro_name); - free(macro_name); - m->sloc = name_sloc; - if (*p == '('){ - // macro has parameters - m->has_args = true; - p++; - p_next = &m->args; - white(&p, " \t"); - if (*p != ')'){ - do { - if (!strncmp(p, "...", 3)){ - // variadic macro - struct macro *m_arg = cpp_create_macro("__VA_ARGS__"); - *p_next = m_arg; - p_next = &m_arg->next; - p += 3; - white(&p, " \t"); - break; - } else if (!cpp_parse_macro_param(cpp, &p, m, &p_next)){ - failed = true; - } - white(&p, " \t"); - } while ((*p == ',') ? ( - p++, - white(&p, " \t"), - true) : false); - } - *p_next = NULL; - if (*p == ')'){ - p++; - } else { - cpp_error(cpp, p, "missing ')' in macro parameter list"); - } - } - white(&p, " \t"); - strdcpy(&m->text, p); - cpp_clear_line(cpp); - if (failed){ - cpp_delete_macro(cpp, m); - } - } else { - cpp_error(cpp, p_start, "no macro name given in #define directive"); - } -} - -void cpp_parse_undef(struct cpp *cpp, char *p, char *p_start) -{ - char *tok = NULL; - struct macro *m; - white(&p, " \t"); - if (cpp_lex_ident(cpp, &p, &tok)){ - white(&p, " \t"); - if (*p){ - cpp_warning(cpp, p, "junk at end of #undef directive"); - } - m = cpp_find_macro(cpp, tok); - if (m){ - cpp_delete_macro(cpp, m); - } - free(tok); - cpp_clear_line(cpp); - } else { - cpp_error(cpp, p_start, "no macro name given in #undef directive"); - } -} - -void cpp_do_condition(struct cpp *cpp, bool condition) -{ - if (!cpp_muted(cpp) && condition){ - cpp->include_top->cond_td++; - } - cpp->include_top->cond_d++; - cpp_clear_line(cpp); -} - -void cpp_parse_ifdef(struct cpp *cpp, char *p, char *p_start) -{ - char *tok = NULL; - white(&p, " \t"); - if (cpp_lex_ident(cpp, &p, &tok)){ - white(&p, " \t"); - if (*p){ - cpp_warning(cpp, p, "junk at end of #ifdef directive"); - } - cpp_do_condition(cpp, !!cpp_find_macro(cpp, tok)); - free(tok); - } else { - cpp_error(cpp, p_start, "no macro name given in #ifdef directive"); - } -} - -// parse a C number - oct, hex or dec -static void parse_cnumber(const char *str, struct exprval *presult) -{ - unsigned long long result = 0; - const char *p = str; - if (*p == '0'){ - // octal or hexadecimal - p++; - if (tolower(*p) == 'x'){ - // hexadecimal - p++; - while (isdigit(*p) || (tolower(*p) >= 'a' && tolower(*p) <= 'f')){ - if (isdigit(*p)){ - result = (result << 4) | (*p - '0'); - } else { - result = (result << 4) | ((tolower(*p) - 'a') + 10); - } - p++; - } - } else { - // octal - p++; - while (*p >= '0' && *p <= '7'){ - result = (result << 3) | (*p - '0'); - p++; - } - } - } else if (*p >= '0' && *p <= '9'){ - // decimal - while (*p >= '0' && *p <= '9'){ - result = (result * 10) + (*p - '0'); - p++; - } - if (*p == '.'){ - // decimal part - p++; - while (*p >= '0' && *p <= '9'){ - // TODO: use value (floats, etc.) - p++; - } - } - if (tolower(*p) == 'e'){ - // exponent part - p++; - while (*p >= '0' && *p <= '9'){ - // TODO: use value (floats, etc.) - p++; - } - } - } - presult->value = result; -} - -void cpp_factor(struct cpp *cpp, char **p, struct exprval *result) -{ - char *factor_str = NULL; - - if(cpp_lex_number(cpp, p, &factor_str)){ - // it's a number - parse_cnumber(factor_str, result); - free(factor_str); - } else if (**p == '!'){ - // logical not - (*p)++; - cpp_factor(cpp, p, result); - result->value = !result->value; - } else if (!strncmp(*p, "defined", 7)){ - // eg. #if defined(FOO) - char *macro_name = NULL; - struct macro *m; - (*p) += 7; - white(p, " \t"); - if (**p != '('){ - cpp_error(cpp, *p, "'(' expected"); - } else { - (*p)++; - } - white(p, " \t"); - if (cpp_lex_ident(cpp, p, ¯o_name)){ - m = cpp_find_macro(cpp, macro_name); - result->value = !!m; - free(macro_name); - } else { - cpp_error(cpp, *p, "identifier (macro name, for \"defined\") expected"); - } - white(p, " \t"); - if (**p != ')'){ - cpp_error(cpp, *p, "')' expected"); - } else { - (*p)++; - } - } else { - cpp_error(cpp, *p, "expression expected"); - } -} - -void cpp_and_expr(struct cpp *cpp, char **p, struct exprval *result) -{ - cpp_factor(cpp, p, result); - white(p, " \t"); - while ((*p)[0] == '&' && (*p)[1] == '&'){ - struct exprval rhs; - (*p) += 2; - white(p, " \t"); - cpp_factor(cpp, p, &rhs); - white(p, " \t"); - result->value = result->value && rhs.value; - } -} - -void cpp_or_expr(struct cpp *cpp, char **p, struct exprval *result) -{ - cpp_and_expr(cpp, p, result); - white(p, " \t"); - while ((*p)[0] == '|' && (*p)[1] == '|'){ - struct exprval rhs; - (*p) += 2; - white(p, " \t"); - cpp_and_expr(cpp, p, &rhs); - white(p, " \t"); - result->value = result->value || rhs.value; - } -} - -void cpp_expr(struct cpp *cpp, char **p, struct exprval *result) -{ - cpp_or_expr(cpp, p, result); -} - -void cpp_parse_if(struct cpp *cpp, char *p, char *p_start) -{ - struct exprval result; - white(&p, " \t"); - cpp_expr(cpp, &p, &result); - cpp_do_condition(cpp, !!result.value); -} - -void cpp_parse_ifndef(struct cpp *cpp, char *p, char *p_start) -{ - char *tok = NULL; - white(&p, " \t"); - if (cpp_lex_ident(cpp, &p, &tok)){ - white(&p, " \t"); - if (*p){ - cpp_warning(cpp, p, "junk at end of #ifdef directive"); - } - cpp_do_condition(cpp, !cpp_find_macro(cpp, tok)); - free(tok); - } else { - cpp_error(cpp, p_start, "no macro name given in #ifdef directive"); - } -} - -void cpp_parse_else(struct cpp *cpp, char *p, char *p_start) -{ - white(&p, " \t"); - if (!cpp_muted(cpp) && *p){ - cpp_warning(cpp, p, "junk at end of #else directive"); - } - if (cpp->include_top->cond_d == 0){ - cpp_error(cpp, p_start, "#else without matching #if or #ifdef"); - } else { - if (cpp->include_top->cond_d == cpp->include_top->cond_td + 1){ - // false -> true - cpp->include_top->cond_td++; - } else if (!cpp_muted(cpp)){ - // true -> false - cpp->include_top->cond_td--; - } - } - cpp_delete_line(cpp); -} - -void cpp_parse_endif(struct cpp *cpp, char *p, char *p_start) -{ - white(&p, " \t"); - if (!cpp_muted(cpp) && *p){ - cpp_warning(cpp, p, "junk at end of #endif directive"); - } - if (cpp->include_top->cond_d == 0){ - cpp_error(cpp, p_start, "#endif without matching #if or #ifdef"); - } else { - if (!cpp_muted(cpp)){ - cpp->include_top->cond_td--; - } - cpp->include_top->cond_d--; - } - if (cpp_muted(cpp)) - cpp_delete_line(cpp); - else - cpp_clear_line(cpp); -} - -static struct { - bool conditional_related; - const char *str; - void (* func)(struct cpp *cpp, char *p, char *p_start); -} directives[] = { - {false, "define", cpp_parse_define}, - {false, "undef", cpp_parse_undef}, - {false, "include", cpp_parse_include}, - {true, "if", cpp_parse_if}, - {true, "ifdef", cpp_parse_ifdef}, - {true, "ifndef", cpp_parse_ifndef}, - {true, "else", cpp_parse_else}, - {true, "endif", cpp_parse_endif}, -}; - -bool cpp_macro_args(struct cpp *cpp, char **line_buf, char **p, struct macro *m) -{ - const char *p_start; - struct macro *m_arg; - int n, depth, p_n, p_start_n; - if (m->has_args){ - white(p, " \t"); - if (**p != '('){ - return false; // give up - } - (*p)++; - - // read arguments - m_arg = m->args; - n = 0; - while (m_arg){ - white(p, " \t"); - p_start = *p; - depth = 1; - if (!strcmp(m_arg->name, "__VA_ARGS__")){ - // XXX: it's silly having this here - it's almost the same - // as the one further down - while (**p != '\0' && depth > 0){ - if (cpp_lex_string(cpp, "\"'", p, NULL)){ - // it's a string - let's not interpret '(' and ')' in strings! - } else if (**p == '('){ - depth++; - } else if (**p == ')'){ - depth--; - } - if (depth > 0){ - (*p)++; - } - } - } else { - for (;;){ - while (**p != '\0' && depth > 0 && (**p != ',' || depth > 1)){ - if (cpp_lex_string(cpp, "\"'", p, NULL)){ - // it's a string - let's not interpret '(' and ')' in strings! - } else if (**p == '('){ - depth++; - } else if (**p == ')'){ - depth--; - } - if (depth > 0){ - (*p)++; - } - } - if (**p == '\0' && cpp->include_top){ - // there's a line break in the middle of it - p_n = *p - *line_buf; - p_start_n = p_start - *line_buf; - cpp_append_line(cpp); - *p = *line_buf + p_n; - p_start = *line_buf + p_start_n; - } else { - break; - } - } - } - strdncpy(&m_arg->text, p_start, *p - p_start); - //printf("arg=%s\n", m_arg->text); - if (*p > p_start){ - n++; - } - if ((**p == ')' && m_arg->next) - || (**p == ',' && !m_arg->next)){ - cpp_error(cpp, *p, "macro \"%s\" requires %d arguments, but only %d given", m->name, macro_count_args(m), n); - break; - } else if (**p == '\0'){ - if (cpp->include_top){ - } else { - cpp_error(cpp, *p, "unterminated argument list invoking macro \"%s\"", m->name); - break; - } - } else if (**p == ','){ - (*p)++; - } - m_arg = m_arg->next; - } - (*p)++; - return true; - } else { - return true; - } -} - -// the spaghetti code is served -char *cpp_macro_arg_subst(struct cpp *cpp, struct macro *m) -{ - char *result = NULL, *s = NULL, *p, *p0, *p_before, *sp; - struct macro *m_arg; - int stringize = 0; - - p = m->text; - p0 = p; - while (*p){ - if (*p == '#'){ - if (p[1] == '#'){ - // token-paste - if (p > p0){ - strdncat(&result, p0, p - 1 - p0); - } - p += 2; - while (*p == ' ' || *p == '\t'){ - p++; - } - p0 = p; - // remove whitespace after previous tok - sp = result + strlen(result); - while (sp > result && (sp[-1] == ' ' || sp[-1] == '\t')){ - sp--; - } - *sp = '\0'; - } else { - // stringize - if (p > p0){ - strdncat(&result, p0, p - p0); - } - stringize = 1; - p++; - p0 = p; - while (*p == ' ' || *p == '\t'){ - p++; - } - if (isalpha(*p) || *p == '_'){ - goto identifier; - } else { - cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); - stringize = 0; - } - } - } else if (isalpha(*p) || *p == '_'){ - // identifier - macro arg name? - identifier: - p_before = p; - p++; - while (isalnum(*p) || isdigit(*p) || *p == '_'){ - p++; - } - strdncpy(&s, p_before, p - p_before); - m_arg = cpp_find_macro_arg(m, s); - if (!m_arg){ - if (stringize){ - cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); - stringize = 0; - } - continue; - } - // macro-arg substitution - if (p_before > p0){ - strdncat(&result, p0, p_before - p0); - } - strdcatc(&result, ' '); - if (stringize){ - strdcatc(&result, '"'); - } - if (m_arg->text) - strdcat(&result, m_arg->text); - if (stringize){ - strdcatc(&result, '"'); - stringize = 0; - } - p0 = p; - } else { - p++; - } - } - if (stringize){ - cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); - } - strdncat(&result, p0, p - p0); - free(s); - return result; -} - -struct macro_stack { - struct macro_stack *prev; - struct macro *m; -}; - -void cpp_substitute_macros(struct cpp *cpp, char **line_buf, struct macro_stack *ms) -{ - char *p, *p_start, *ident = NULL; - char *substituted_text, *new_line_buf = NULL; - struct macro *m; - struct macro_stack stack_item, *ms_ptr; - int p_start_n, p_end_n; - p = *line_buf; - while (*p){ - p_start = p; - if (cpp_lex_ident(cpp, &p, &ident)){ - // is it a macro? - m = cpp_find_macro(cpp, ident); - // is it in the stack? - if (m){ - ms_ptr = ms; - while (ms_ptr && ms_ptr->m != m){ - ms_ptr = ms_ptr->prev; - } - if (ms_ptr){ - m = NULL; // yes it is. Let's forget about it. - } - } - p_start_n = p_start - *line_buf; - if (m && cpp_macro_args(cpp, line_buf, &p, m)){ - p_start = *line_buf + p_start_n; - substituted_text = cpp_macro_arg_subst(cpp, m); - // now we have to substitute the substituted text - stack_item.prev = ms; - stack_item.m = m; - cpp_substitute_macros(cpp, &substituted_text, &stack_item); - strdncat(&new_line_buf, *line_buf, p_start - *line_buf); - strdcat(&new_line_buf, substituted_text); - free(substituted_text); - p_end_n = strlen(new_line_buf); // de-pointerize p_start - strdcat(&new_line_buf, p); - // replace the line buffer with our new one (with the substitution) - free(*line_buf); - *line_buf = new_line_buf; - new_line_buf = NULL; - p = *line_buf + p_end_n; - } - } else if (cpp_lex_string(cpp, "\"'", &p, &ident)){ - // nothing needs to be done - // but at least we've passed the string, so we - // won't expand macros in it - } else { - p++; - } - } - free(ident); -} - -void cpp_remove_comments(struct cpp *cpp) -{ - char *p = cpp->line_buf, *p_start; - int p_start_n, p_n; - while (*p){ - if (p[0] == '/' && p[1] == '/'){ - // single-line comment - memset(p, ' ', strlen(p)); - p += strlen(p); - } else if (p[0] == '/' && p[1] == '*'){ - // multi-line comment - p_start = p; - p += 2; - for (;;){ - if (p[0] == '*' && p[1] == '/'){ - // end of comment - p += 2; - memset(p_start, ' ', p - p_start); - break; - } else if (*p == '\0'){ - // we need to read another line! - // as you can see, this isn't very elegant - p_start_n = p_start - cpp->line_buf; - p_n = p - cpp->line_buf; - cpp_append_line(cpp); - p_start = cpp->line_buf + p_start_n; - p = cpp->line_buf + p_n; - } else { - p++; - } - } - } else { - p++; - } - } -} - -void cpp_process_line(struct cpp *cpp) -{ - char *p, *tok = NULL, *p_start; - int i; - bool done; - if (!cpp->line_buf) - return; - cpp_remove_comments(cpp); - p = cpp->line_buf; - white(&p, " \t"); - if (*p == '#'){ - // preprocessor directive - p_start = p; - p++; - white(&p, " \t"); - done = false; - cpp_lex_ident(cpp, &p, &tok); - if (tok){ - for (i=0; i<(sizeof directives/sizeof *directives); i++){ - if (!strcmp(tok, directives[i].str)){ - if (!cpp_muted(cpp) || directives[i].conditional_related){ - directives[i].func(cpp, p, p_start); - } - done = true; - } - } - if (!done && !cpp_muted(cpp)){ - cpp_error(cpp, p_start, "invalid preprocessing directive #%s", tok); - } - } else { - cpp_clear_line(cpp); - } - free(tok); - } else { - if (!cpp_muted(cpp)){ - cpp_substitute_macros(cpp, &cpp->line_buf, NULL); - } - } - if (cpp_muted(cpp)){ - cpp_delete_line(cpp); - } -} +#include "cpp.h" +#include "errors.h" +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "bstr.h" +#include "stdarg.h" +#include "stdbool.h" +#include "ctype.h" +#include "sys/types.h" +#include "sys/stat.h" +#include "assert.h" + +#define CONFIG_INCLUDE_DIRS "/usr/include" + +//#define trace(...) fprintf(stderr, __VA_ARGS__) +#define trace(...) + +// A value from an expression +// (in #if etc.) +struct exprval { + int value; +}; + +void cpp_delete_macro(struct cpp *cpp, struct macro *m); +void cpp_lex(struct cpp *cpp); + +int cpp_get_col(struct cpp *cpp, char *p) +{ + return p + 1 - cpp->line_buf; +} + +#define cpp_error(cpp, p, ...) cpp_message(cpp, p, NULL, "error: " __VA_ARGS__) +#define cpp_warning(cpp, p, ...) cpp_message(cpp, p, NULL, "warning: " __VA_ARGS__) +#define cpp_error_loc(cpp, sloc, ...) cpp_message(cpp, NULL, sloc, "error: " __VA_ARGS__) +#define cpp_warning_loc(cpp, sloc, ...) cpp_message(cpp, NULL, sloc, "warning: " __VA_ARGS__) +void cpp_message(struct cpp *cpp, char *p, struct sloc *sloc, const char *fmt, ...) +{ + va_list ap; + struct sloc my_sloc; + if (!sloc){ + my_sloc = cpp->line_loc; + my_sloc.col = cpp_get_col(cpp, p); + sloc = &my_sloc; + } + fprintf(stderr, "%s:%d: ", + sloc->name, + sloc->line/*, + sloc->col*/); + // NOTE: don't print col number, because after macro substitution, + // the column number becomes incorrect. Fix=??? + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +void cpp_init(struct cpp *cpp) +{ + cpp->include_top = NULL; + cpp->stale_files = NULL; + cpp->line_buf = NULL; + cpp->macros = NULL; + cpp->include_dirs = estrdup(CONFIG_INCLUDE_DIRS); +} + +void cpp_delete(struct cpp *cpp) +{ + struct incfile *incf, *incf_prev; + incf = cpp->include_top; + while (incf){ + incf_prev = incf->prev; + free(incf); + incf = incf_prev; + } + incf = cpp->stale_files; + while (incf){ + incf_prev = incf->prev; + free(incf); + incf = incf_prev; + } + free(cpp->line_buf); + free(cpp->include_dirs); + while (cpp->macros){ + cpp_delete_macro(cpp, cpp->macros); + } +} + +struct macro *cpp_create_macro(const char *name) +{ + struct macro *m; + m = emalloc(sizeof(struct macro) + strlen(name)); + m->next = NULL; + m->args = NULL; + m->has_args = false; + m->text = NULL; + strcpy(m->name, name); + return m; +} + +struct macro *cpp_create_push_macro(struct cpp *cpp, const char *name) +{ + struct macro *m; + m = cpp_create_macro(name); + m->next = cpp->macros; + cpp->macros = m; + return m; +} + +struct macro *cpp_find_macro(struct cpp *cpp, const char *name) +{ + struct macro *m; + m = cpp->macros; + while (m && strcmp(m->name, name)){ + m = m->next; + } + return m; +} + +struct macro *cpp_find_macro_arg(struct macro *m, const char *name) +{ + struct macro *m_arg; + m_arg = m->args; + while (m_arg && strcmp(m_arg->name, name)){ + m_arg = m_arg->next; + } + return m_arg; +} + +static int macro_count_args(struct macro *m) +{ + struct macro *m_arg; + int n; + m_arg = m->args; + n = 0; + while (m_arg){ + n++; + m_arg = m_arg->next; + } + return n; +} + +void cpp_delete_macro(struct cpp *cpp, struct macro *m) +{ + struct macro *m_i, *m_arg, *m_arg_next; + m_i = cpp->macros; + if (m_i == m){ + cpp->macros = m->next; + } else { + while (m_i && m_i->next != m){ + m_i = m_i->next; + } + if (m_i){ + m_i->next = m->next; + } + } + m_arg = m->args; + while (m_arg){ + m_arg_next = m_arg->next; + free(m_arg->text); + free(m_arg); + m_arg = m_arg_next; + } + free(m->text); + free(m); +} + + +// push a file on the include stack +void cpp_include_file(struct cpp *cpp, const char *name, FILE *f, bool must_close) +{ + struct incfile *incf; + incf = emalloc(sizeof(struct incfile) + strlen(name)); + incf->f = f; + strcpy(incf->name, name); + incf->line = 0; + incf->must_close = must_close; + incf->eof = false; + incf->cond_d = incf->cond_td = 0; + // push on include stack + incf->prev = cpp->include_top; + cpp->include_top = incf; +} + +static bool get_dir(char **dirlist, char **output) +{ + if (**dirlist){ + char *p = strchrnul(*dirlist, ':'); + strdncpy(output, *dirlist, p - *dirlist); + *dirlist = p; + return true; + } else { + return false; + } +} + +// open a file and push it on the include stack +bool cpp_open_include_file(struct cpp *cpp, const char *name) +{ + FILE *f; + char *dirlist, *attempt_file = NULL; + struct stat stat_struct; + + // try current directory + if (!stat(name, &stat_struct)){ + f = fopen(name, "r"); + assert(f != NULL); + cpp_include_file(cpp, name, f, true); + return true; + } + + // try include dirs + dirlist = cpp->include_dirs; + while (get_dir(&dirlist, &attempt_file)){ + strdcatc(&attempt_file, '/'); + strdcat(&attempt_file, name); + if (!stat(attempt_file, &stat_struct)){ + f = fopen(attempt_file, "r"); + assert(f != NULL); + cpp_include_file(cpp, attempt_file, f, true); + free(attempt_file); + return true; + } + } + free(attempt_file); + return false; +} + +void cpp_pop_include(struct cpp *cpp) +{ + struct incfile *incf; + incf = cpp->include_top; + if (incf){ + cpp->include_top = incf->prev; + incf->prev = cpp->stale_files; + cpp->stale_files = incf; + if (incf->must_close == true){ + fclose(incf->f); + } + } +} + +void cpp_clear_line(struct cpp *cpp) +{ + if (cpp->line_buf) + strdcpy(&cpp->line_buf, ""); +} + +void cpp_delete_line(struct cpp *cpp) +{ + // TODO: can we, erm, keep the buffer?! + free(cpp->line_buf); + cpp->line_buf = NULL; +} + +void cpp_read_line(struct cpp *cpp) +{ + struct incfile *incf; + char **pstr = &cpp->line_buf; + int ch; + + incf = cpp->include_top; + if (incf && incf->eof){ + cpp_pop_include(cpp); + incf = cpp->include_top; + } + if (incf){ + strdcpy(pstr, ""); + do { + ch = fgetc(incf->f); + if (ch == EOF){ + incf->eof = true; + } else if (ch == '\\'){ + // backslashed newline? + ch = fgetc(incf->f); + if (ch == '\n'){ + // yes + incf->line++; + strdcatc(pstr, ' '); // insert a space to ensure token separation + ch = '@'; // this could be anything + } else { + // no + strdcatc(pstr, '\\'); + strdcatc(pstr, ch); + } + } else if (ch != '\n'){ + strdcatc(pstr, ch); + } + } while (ch != EOF && ch != '\n'); + incf->line++; + cpp->line_loc.name = incf->name; + cpp->line_loc.line = incf->line; + } else { + cpp_delete_line(cpp); + } +} + +// read another line and stick it on the end of the buffer +void cpp_append_line(struct cpp *cpp) +{ + char *old_buf; + old_buf = cpp->line_buf; + cpp->line_buf = NULL; + cpp_read_line(cpp); + if (cpp->line_buf){ + strdcatc(&old_buf, ' '); + strdcat(&old_buf, cpp->line_buf); + free(cpp->line_buf); + } + cpp->line_buf = old_buf; +} + +static void white(char **p, char *set) +{ + while (**p && strchr(set, **p)){ + (*p)++; + } +} + +// read a number +bool cpp_lex_number(struct cpp *cpp, char **p_start, char **output) +{ + char *p = *p_start; + + if (*p == '0'){ + // octal or hexadecimal + p++; + if (tolower(*p) == 'x'){ + // hexadecimal + p++; + while (isdigit(*p) || (tolower(*p) >= 'a' && tolower(*p) <= 'f')){ + p++; + } + } else { + // octal + p++; + while (*p >= '0' && *p <= '7'){ + p++; + } + } + } else if (*p >= '0' && *p <= '9'){ + // decimal + while (*p >= '0' && *p <= '9'){ + p++; + } + if (*p == '.'){ + // decimal part + p++; + while (*p >= '0' && *p <= '9'){ + p++; + } + } + if (tolower(*p) == 'e'){ + // exponent part + p++; + while (*p >= '0' && *p <= '9'){ + p++; + } + } + } else { + // not a number at all + return false; + } + strdncat(output, *p_start, p - *p_start); + *p_start = p; + return true; +} + +// read a string literal +bool cpp_lex_string(struct cpp *cpp, char *quotes, char **p_start, char **output) +{ + char *p = *p_start, quote; + char *p_last_start; + if (*p && strchr(quotes, *p)){ + quote = *p; + p_last_start = *p_start; + p++; + if (output){ + strdcpy(output, ""); + } + while (*p && *p != quote){ + if (*p == '\\'){ + if (p[1] == quote || p[1] == '\\'){ + // escaped quotation mark or backslash + p += 2; + if (output){ + strdncat(output, p_last_start, p - p_last_start); + } + p_last_start = p; + } else { + p++; + } + } else { + p++; + } + } + if (*p != quote){ + cpp_error(cpp, *p_start, "unterminated %s literal", + (quote == '\'') ? "character" : "string"); + } else { + p++; + } + if (output){ + strdncat(output, p_last_start, p - p_last_start); + } + *p_start = p; + return true; + } else { + return false; + } +} + +// read an identifier +bool cpp_lex_ident(struct cpp *cpp, char **p_start, char **output) +{ + char *p = *p_start; + if (*p && (isalpha(*p) || *p == '_')){ + while (*p && (isalnum(*p) || *p == '_')){ + p++; + } + strdncpy(output, *p_start, p - *p_start); + *p_start = p; + return true; + } else { + return false; + } +} + +bool cpp_muted(struct cpp *cpp) +{ + struct incfile *incf; + incf = cpp->include_top; + if (incf){ + if (incf->cond_d > incf->cond_td){ + return true; + } + } + return false; +} + +void cpp_parse_include(struct cpp *cpp, char *p, char *p_start) +{ + char *inc_str = NULL, *p_str_start; + white(&p, " \t"); + p_str_start = p; + if (cpp_lex_string(cpp, "<\"", &p, &inc_str)){ + white(&p, " \t"); + if (*p){ + cpp_warning(cpp, p, "junk at end of #include directive"); + } + if (strlen(inc_str) == 2){ + cpp_error(cpp, p_str_start, "empty filename in #include"); + } else { + inc_str[strlen(inc_str) - 1] = '\0'; + if (!cpp_open_include_file(cpp, inc_str + 1)){ + cpp_error(cpp, p_str_start, "%s: no such file or directory", inc_str + 1); + } + free(inc_str); + } + cpp_clear_line(cpp); + } else { + cpp_error(cpp, p_start, "#include expects \"FILENAME\" or "); + } +} + +bool cpp_parse_macro_param(struct cpp *cpp, char **p, struct macro *m, struct macro ***p_next) +{ + char *param_name = NULL; + struct sloc arg_loc; + struct macro *m_arg; + bool result; + + arg_loc = cpp->line_loc; + arg_loc.col = cpp_get_col(cpp, *p); + if (cpp_lex_ident(cpp, p, ¶m_name)){ + m_arg = cpp_find_macro_arg(m, param_name); + if (m_arg){ + cpp_error_loc(cpp, &arg_loc, "repeated macro parameter \"%s\"", param_name); + } else { + m_arg = cpp_create_macro(param_name); + **p_next = m_arg; + *p_next = &m_arg->next; + } + result = true; + } else { + result = false; + } + free(param_name); + return result; +} + +void cpp_parse_define(struct cpp *cpp, char *p, char *p_start) +{ + char *macro_name = NULL; + struct macro *m, **p_next; + struct sloc name_sloc; + bool failed = false; + + white(&p, " \t"); + name_sloc = cpp->line_loc; + name_sloc.col = cpp_get_col(cpp, p); + if (cpp_lex_ident(cpp, &p, ¯o_name)){ + m = cpp_find_macro(cpp, macro_name); + if (m){ + cpp_warning_loc(cpp, &name_sloc, "\"%s\" redefined", macro_name); + cpp_warning_loc(cpp, &m->sloc, "previous definition was here"); + cpp_delete_macro(cpp, m); + } + m = cpp_create_push_macro(cpp, macro_name); + free(macro_name); + m->sloc = name_sloc; + if (*p == '('){ + // macro has parameters + m->has_args = true; + p++; + p_next = &m->args; + white(&p, " \t"); + if (*p != ')'){ + do { + if (!strncmp(p, "...", 3)){ + // variadic macro + struct macro *m_arg = cpp_create_macro("__VA_ARGS__"); + *p_next = m_arg; + p_next = &m_arg->next; + p += 3; + white(&p, " \t"); + break; + } else if (!cpp_parse_macro_param(cpp, &p, m, &p_next)){ + failed = true; + } + white(&p, " \t"); + } while ((*p == ',') ? ( + p++, + white(&p, " \t"), + true) : false); + } + *p_next = NULL; + if (*p == ')'){ + p++; + } else { + cpp_error(cpp, p, "missing ')' in macro parameter list"); + } + } + white(&p, " \t"); + strdcpy(&m->text, p); + cpp_clear_line(cpp); + if (failed){ + cpp_delete_macro(cpp, m); + } + } else { + cpp_error(cpp, p_start, "no macro name given in #define directive"); + } +} + +void cpp_parse_undef(struct cpp *cpp, char *p, char *p_start) +{ + char *tok = NULL; + struct macro *m; + white(&p, " \t"); + if (cpp_lex_ident(cpp, &p, &tok)){ + white(&p, " \t"); + if (*p){ + cpp_warning(cpp, p, "junk at end of #undef directive"); + } + m = cpp_find_macro(cpp, tok); + if (m){ + cpp_delete_macro(cpp, m); + } + free(tok); + cpp_clear_line(cpp); + } else { + cpp_error(cpp, p_start, "no macro name given in #undef directive"); + } +} + +void cpp_do_condition(struct cpp *cpp, bool condition) +{ + if (!cpp_muted(cpp) && condition){ + cpp->include_top->cond_td++; + } + cpp->include_top->cond_d++; + cpp_clear_line(cpp); +} + +void cpp_parse_ifdef(struct cpp *cpp, char *p, char *p_start) +{ + char *tok = NULL; + white(&p, " \t"); + if (cpp_lex_ident(cpp, &p, &tok)){ + white(&p, " \t"); + if (*p){ + cpp_warning(cpp, p, "junk at end of #ifdef directive"); + } + cpp_do_condition(cpp, !!cpp_find_macro(cpp, tok)); + free(tok); + } else { + cpp_error(cpp, p_start, "no macro name given in #ifdef directive"); + } +} + +// parse a C number - oct, hex or dec +static void parse_cnumber(const char *str, struct exprval *presult) +{ + unsigned long long result = 0; + const char *p = str; + if (*p == '0'){ + // octal or hexadecimal + p++; + if (tolower(*p) == 'x'){ + // hexadecimal + p++; + while (isdigit(*p) || (tolower(*p) >= 'a' && tolower(*p) <= 'f')){ + if (isdigit(*p)){ + result = (result << 4) | (*p - '0'); + } else { + result = (result << 4) | ((tolower(*p) - 'a') + 10); + } + p++; + } + } else { + // octal + p++; + while (*p >= '0' && *p <= '7'){ + result = (result << 3) | (*p - '0'); + p++; + } + } + } else if (*p >= '0' && *p <= '9'){ + // decimal + while (*p >= '0' && *p <= '9'){ + result = (result * 10) + (*p - '0'); + p++; + } + if (*p == '.'){ + // decimal part + p++; + while (*p >= '0' && *p <= '9'){ + // TODO: use value (floats, etc.) + p++; + } + } + if (tolower(*p) == 'e'){ + // exponent part + p++; + while (*p >= '0' && *p <= '9'){ + // TODO: use value (floats, etc.) + p++; + } + } + } + presult->value = result; +} + +void cpp_factor(struct cpp *cpp, char **p, struct exprval *result) +{ + char *factor_str = NULL; + + if(cpp_lex_number(cpp, p, &factor_str)){ + // it's a number + parse_cnumber(factor_str, result); + free(factor_str); + } else if (**p == '!'){ + // logical not + (*p)++; + cpp_factor(cpp, p, result); + result->value = !result->value; + } else if (!strncmp(*p, "defined", 7)){ + // eg. #if defined(FOO) + char *macro_name = NULL; + struct macro *m; + (*p) += 7; + white(p, " \t"); + if (**p != '('){ + cpp_error(cpp, *p, "'(' expected"); + } else { + (*p)++; + } + white(p, " \t"); + if (cpp_lex_ident(cpp, p, ¯o_name)){ + m = cpp_find_macro(cpp, macro_name); + result->value = !!m; + free(macro_name); + } else { + cpp_error(cpp, *p, "identifier (macro name, for \"defined\") expected"); + } + white(p, " \t"); + if (**p != ')'){ + cpp_error(cpp, *p, "')' expected"); + } else { + (*p)++; + } + } else { + cpp_error(cpp, *p, "expression expected"); + } +} + +void cpp_and_expr(struct cpp *cpp, char **p, struct exprval *result) +{ + cpp_factor(cpp, p, result); + white(p, " \t"); + while ((*p)[0] == '&' && (*p)[1] == '&'){ + struct exprval rhs; + (*p) += 2; + white(p, " \t"); + cpp_factor(cpp, p, &rhs); + white(p, " \t"); + result->value = result->value && rhs.value; + } +} + +void cpp_or_expr(struct cpp *cpp, char **p, struct exprval *result) +{ + cpp_and_expr(cpp, p, result); + white(p, " \t"); + while ((*p)[0] == '|' && (*p)[1] == '|'){ + struct exprval rhs; + (*p) += 2; + white(p, " \t"); + cpp_and_expr(cpp, p, &rhs); + white(p, " \t"); + result->value = result->value || rhs.value; + } +} + +void cpp_expr(struct cpp *cpp, char **p, struct exprval *result) +{ + cpp_or_expr(cpp, p, result); +} + +void cpp_parse_if(struct cpp *cpp, char *p, char *p_start) +{ + struct exprval result; + white(&p, " \t"); + cpp_expr(cpp, &p, &result); + cpp_do_condition(cpp, !!result.value); +} + +void cpp_parse_ifndef(struct cpp *cpp, char *p, char *p_start) +{ + char *tok = NULL; + white(&p, " \t"); + if (cpp_lex_ident(cpp, &p, &tok)){ + white(&p, " \t"); + if (*p){ + cpp_warning(cpp, p, "junk at end of #ifdef directive"); + } + cpp_do_condition(cpp, !cpp_find_macro(cpp, tok)); + free(tok); + } else { + cpp_error(cpp, p_start, "no macro name given in #ifdef directive"); + } +} + +void cpp_parse_else(struct cpp *cpp, char *p, char *p_start) +{ + white(&p, " \t"); + if (!cpp_muted(cpp) && *p){ + cpp_warning(cpp, p, "junk at end of #else directive"); + } + if (cpp->include_top->cond_d == 0){ + cpp_error(cpp, p_start, "#else without matching #if or #ifdef"); + } else { + if (cpp->include_top->cond_d == cpp->include_top->cond_td + 1){ + // false -> true + cpp->include_top->cond_td++; + } else if (!cpp_muted(cpp)){ + // true -> false + cpp->include_top->cond_td--; + } + } + cpp_delete_line(cpp); +} + +void cpp_parse_endif(struct cpp *cpp, char *p, char *p_start) +{ + white(&p, " \t"); + if (!cpp_muted(cpp) && *p){ + cpp_warning(cpp, p, "junk at end of #endif directive"); + } + if (cpp->include_top->cond_d == 0){ + cpp_error(cpp, p_start, "#endif without matching #if or #ifdef"); + } else { + if (!cpp_muted(cpp)){ + cpp->include_top->cond_td--; + } + cpp->include_top->cond_d--; + } + if (cpp_muted(cpp)) + cpp_delete_line(cpp); + else + cpp_clear_line(cpp); +} + +static struct { + bool conditional_related; + const char *str; + void (* func)(struct cpp *cpp, char *p, char *p_start); +} directives[] = { + {false, "define", cpp_parse_define}, + {false, "undef", cpp_parse_undef}, + {false, "include", cpp_parse_include}, + {true, "if", cpp_parse_if}, + {true, "ifdef", cpp_parse_ifdef}, + {true, "ifndef", cpp_parse_ifndef}, + {true, "else", cpp_parse_else}, + {true, "endif", cpp_parse_endif}, +}; + +bool cpp_macro_args(struct cpp *cpp, char **line_buf, char **p, struct macro *m) +{ + const char *p_start; + struct macro *m_arg; + int n, depth, p_n, p_start_n; + if (m->has_args){ + white(p, " \t"); + if (**p != '('){ + return false; // give up + } + (*p)++; + + // read arguments + m_arg = m->args; + n = 0; + while (m_arg){ + white(p, " \t"); + p_start = *p; + depth = 1; + if (!strcmp(m_arg->name, "__VA_ARGS__")){ + // XXX: it's silly having this here - it's almost the same + // as the one further down + while (**p != '\0' && depth > 0){ + if (cpp_lex_string(cpp, "\"'", p, NULL)){ + // it's a string - let's not interpret '(' and ')' in strings! + } else if (**p == '('){ + depth++; + } else if (**p == ')'){ + depth--; + } + if (depth > 0){ + (*p)++; + } + } + } else { + for (;;){ + while (**p != '\0' && depth > 0 && (**p != ',' || depth > 1)){ + if (cpp_lex_string(cpp, "\"'", p, NULL)){ + // it's a string - let's not interpret '(' and ')' in strings! + } else if (**p == '('){ + depth++; + } else if (**p == ')'){ + depth--; + } + if (depth > 0){ + (*p)++; + } + } + if (**p == '\0' && cpp->include_top){ + // there's a line break in the middle of it + p_n = *p - *line_buf; + p_start_n = p_start - *line_buf; + cpp_append_line(cpp); + *p = *line_buf + p_n; + p_start = *line_buf + p_start_n; + } else { + break; + } + } + } + strdncpy(&m_arg->text, p_start, *p - p_start); + //printf("arg=%s\n", m_arg->text); + if (*p > p_start){ + n++; + } + if ((**p == ')' && m_arg->next) + || (**p == ',' && !m_arg->next)){ + cpp_error(cpp, *p, "macro \"%s\" requires %d arguments, but only %d given", m->name, macro_count_args(m), n); + break; + } else if (**p == '\0'){ + if (cpp->include_top){ + } else { + cpp_error(cpp, *p, "unterminated argument list invoking macro \"%s\"", m->name); + break; + } + } else if (**p == ','){ + (*p)++; + } + m_arg = m_arg->next; + } + (*p)++; + return true; + } else { + return true; + } +} + +// the spaghetti code is served +char *cpp_macro_arg_subst(struct cpp *cpp, struct macro *m) +{ + char *result = NULL, *s = NULL, *p, *p0, *p_before, *sp; + struct macro *m_arg; + int stringize = 0; + + p = m->text; + p0 = p; + while (*p){ + if (*p == '#'){ + if (p[1] == '#'){ + // token-paste + if (p > p0){ + strdncat(&result, p0, p - 1 - p0); + } + p += 2; + while (*p == ' ' || *p == '\t'){ + p++; + } + p0 = p; + // remove whitespace after previous tok + sp = result + strlen(result); + while (sp > result && (sp[-1] == ' ' || sp[-1] == '\t')){ + sp--; + } + *sp = '\0'; + } else { + // stringize + if (p > p0){ + strdncat(&result, p0, p - p0); + } + stringize = 1; + p++; + p0 = p; + while (*p == ' ' || *p == '\t'){ + p++; + } + if (isalpha(*p) || *p == '_'){ + goto identifier; + } else { + cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); + stringize = 0; + } + } + } else if (isalpha(*p) || *p == '_'){ + // identifier - macro arg name? + identifier: + p_before = p; + p++; + while (isalnum(*p) || isdigit(*p) || *p == '_'){ + p++; + } + strdncpy(&s, p_before, p - p_before); + m_arg = cpp_find_macro_arg(m, s); + if (!m_arg){ + if (stringize){ + cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); + stringize = 0; + } + continue; + } + // macro-arg substitution + if (p_before > p0){ + strdncat(&result, p0, p_before - p0); + } + strdcatc(&result, ' '); + if (stringize){ + strdcatc(&result, '"'); + } + if (m_arg->text) + strdcat(&result, m_arg->text); + if (stringize){ + strdcatc(&result, '"'); + stringize = 0; + } + p0 = p; + } else { + p++; + } + } + if (stringize){ + cpp_error_loc(cpp, &m->sloc, "'#' not followed by macro-argument"); + } + strdncat(&result, p0, p - p0); + free(s); + return result; +} + +struct macro_stack { + struct macro_stack *prev; + struct macro *m; +}; + +void cpp_substitute_macros(struct cpp *cpp, char **line_buf, struct macro_stack *ms) +{ + char *p, *p_start, *ident = NULL; + char *substituted_text, *new_line_buf = NULL; + struct macro *m; + struct macro_stack stack_item, *ms_ptr; + int p_start_n, p_end_n; + p = *line_buf; + while (*p){ + p_start = p; + if (cpp_lex_ident(cpp, &p, &ident)){ + // is it a macro? + m = cpp_find_macro(cpp, ident); + // is it in the stack? + if (m){ + ms_ptr = ms; + while (ms_ptr && ms_ptr->m != m){ + ms_ptr = ms_ptr->prev; + } + if (ms_ptr){ + m = NULL; // yes it is. Let's forget about it. + } + } + p_start_n = p_start - *line_buf; + if (m && cpp_macro_args(cpp, line_buf, &p, m)){ + p_start = *line_buf + p_start_n; + substituted_text = cpp_macro_arg_subst(cpp, m); + // now we have to substitute the substituted text + stack_item.prev = ms; + stack_item.m = m; + cpp_substitute_macros(cpp, &substituted_text, &stack_item); + strdncat(&new_line_buf, *line_buf, p_start - *line_buf); + strdcat(&new_line_buf, substituted_text); + free(substituted_text); + p_end_n = strlen(new_line_buf); // de-pointerize p_start + strdcat(&new_line_buf, p); + // replace the line buffer with our new one (with the substitution) + free(*line_buf); + *line_buf = new_line_buf; + new_line_buf = NULL; + p = *line_buf + p_end_n; + } + } else if (cpp_lex_string(cpp, "\"'", &p, &ident)){ + // nothing needs to be done + // but at least we've passed the string, so we + // won't expand macros in it + } else { + p++; + } + } + free(ident); +} + +void cpp_remove_comments(struct cpp *cpp) +{ + char *p = cpp->line_buf, *p_start; + int p_start_n, p_n; + while (*p){ + if (p[0] == '/' && p[1] == '/'){ + // single-line comment + memset(p, ' ', strlen(p)); + p += strlen(p); + } else if (p[0] == '/' && p[1] == '*'){ + // multi-line comment + p_start = p; + p += 2; + for (;;){ + if (p[0] == '*' && p[1] == '/'){ + // end of comment + p += 2; + memset(p_start, ' ', p - p_start); + break; + } else if (*p == '\0'){ + // we need to read another line! + // as you can see, this isn't very elegant + p_start_n = p_start - cpp->line_buf; + p_n = p - cpp->line_buf; + cpp_append_line(cpp); + p_start = cpp->line_buf + p_start_n; + p = cpp->line_buf + p_n; + } else { + p++; + } + } + } else { + p++; + } + } +} + +void cpp_process_line(struct cpp *cpp) +{ + char *p, *tok = NULL, *p_start; + int i; + bool done; + if (!cpp->line_buf) + return; + cpp_remove_comments(cpp); + p = cpp->line_buf; + white(&p, " \t"); + if (*p == '#'){ + // preprocessor directive + p_start = p; + p++; + white(&p, " \t"); + done = false; + cpp_lex_ident(cpp, &p, &tok); + if (tok){ + for (i=0; i<(sizeof directives/sizeof *directives); i++){ + if (!strcmp(tok, directives[i].str)){ + if (!cpp_muted(cpp) || directives[i].conditional_related){ + directives[i].func(cpp, p, p_start); + } + done = true; + } + } + if (!done && !cpp_muted(cpp)){ + cpp_error(cpp, p_start, "invalid preprocessing directive #%s", tok); + } + } else { + cpp_clear_line(cpp); + } + free(tok); + } else { + if (!cpp_muted(cpp)){ + cpp_substitute_macros(cpp, &cpp->line_buf, NULL); + } + } + if (cpp_muted(cpp)){ + cpp_delete_line(cpp); + } +} diff --git a/cpp.h b/cpp.h index d37d8dd..16b65af 100644 --- a/cpp.h +++ b/cpp.h @@ -6,40 +6,40 @@ // A location in a source file struct sloc { - const char *name; - int line; - int col; + const char *name; + int line; + int col; }; // A source file struct incfile { - struct incfile *prev; - FILE *f; - int line; - bool must_close; // must close f after use? - bool eof; - int cond_d, cond_td; // condition depth/condition true depth - char name[1]; + struct incfile *prev; + FILE *f; + int line; + bool must_close; // must close f after use? + bool eof; + int cond_d, cond_td; // condition depth/condition true depth + char name[1]; }; // A macro, or a macro argument struct macro { - struct macro *next; - struct macro *args; // argument list - bool has_args; // if has_args && !args then it's a macro like FOO() - char *text; // what the macro expands to - struct sloc sloc; // location of definition - char name[1]; // macro/macro argument name + struct macro *next; + struct macro *args; // argument list + bool has_args; // if has_args && !args then it's a macro like FOO() + char *text; // what the macro expands to + struct sloc sloc; // location of definition + char name[1]; // macro/macro argument name }; // Preprocessor state struct cpp { - struct incfile *include_top; - struct incfile *stale_files; - char *line_buf; - struct sloc line_loc; - struct macro *macros; // all defined macros - char *include_dirs; + struct incfile *include_top; + struct incfile *stale_files; + char *line_buf; + struct sloc line_loc; + struct macro *macros; // all defined macros + char *include_dirs; }; void cpp_init(struct cpp *cpp); diff --git a/cpp_main.c b/cpp_main.c dissimilarity index 91% index 8e2f96d..90f0634 100644 --- a/cpp_main.c +++ b/cpp_main.c @@ -1,33 +1,33 @@ -#include "cpp.h" - -int main(int argc, char **argv) -{ - struct cpp s_cpp, *cpp = &s_cpp; - const char *old_name = NULL; - int old_linenum = 0; - - cpp_init(cpp); - cpp_include_file(cpp, "", stdin, false); - do { - cpp_read_line(cpp); - if (cpp->line_buf){ - cpp_process_line(cpp); - if (cpp->line_buf){ - if (old_name != cpp->line_loc.name - || old_linenum != cpp->line_loc.line - 1){ - old_name = cpp->line_loc.name; - old_linenum = cpp->line_loc.line; - printf("# %d \"%s\"\n", old_linenum, old_name); - } else { - old_linenum++; - } - fputs(cpp->line_buf, stdout); - fputc('\n', stdout); - } - } - } while (cpp->include_top); - cpp_delete(cpp); - - return 0; -} - +#include "cpp.h" + +int main(int argc, char **argv) +{ + struct cpp s_cpp, *cpp = &s_cpp; + const char *old_name = NULL; + int old_linenum = 0; + + cpp_init(cpp); + cpp_include_file(cpp, "", stdin, false); + do { + cpp_read_line(cpp); + if (cpp->line_buf){ + cpp_process_line(cpp); + if (cpp->line_buf){ + if (old_name != cpp->line_loc.name + || old_linenum != cpp->line_loc.line - 1){ + old_name = cpp->line_loc.name; + old_linenum = cpp->line_loc.line; + printf("# %d \"%s\"\n", old_linenum, old_name); + } else { + old_linenum++; + } + fputs(cpp->line_buf, stdout); + fputc('\n', stdout); + } + } + } while (cpp->include_top); + cpp_delete(cpp); + + return 0; +} + diff --git a/errors.c b/errors.c index bc3c224..9afd68d 100644 --- a/errors.c +++ b/errors.c @@ -7,31 +7,31 @@ noreturn void _die(const char *func, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "[%s] ", func); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); - exit(1); + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "[%s] ", func); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); } void *emalloc(size_t size) { - void *p = malloc(size); - if (!p){ - die("memory full"); - } - return p; + void *p = malloc(size); + if (!p){ + die("memory full"); + } + return p; } char *estrdup(const char *str) { - char *p; - p = strdup(str); - if (!p){ - die("memory full"); - } - return p; + char *p; + p = strdup(str); + if (!p){ + die("memory full"); + } + return p; } diff --git a/parse.c b/parse.c dissimilarity index 79% index 1105e1d..3aa3ef7 100644 --- a/parse.c +++ b/parse.c @@ -1,578 +1,578 @@ -#include "parse.h" -#include "cc.h" -#include "scanner.h" -#include "errors.h" -#include "stdlib.h" -#include "stdio.h" -#include "stdarg.h" - -// make-it-easier macros -#define LEX (&cc->lex) // struct lexer * -#define TOK (cc->lex.tok) // tok_t - -struct stree *cc_parse_expr(struct cc *cc); -bool cc_parse_type(struct cc *cc, struct btype *type_info); -struct stree *cc_parse_pointer_type(struct cc *cc, struct btype *type_info); - -// throw a 'foo expected but bar found' error -void cc_expect(struct cc *cc, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "%s:%d: error: ", LEX->tok_sloc.name, LEX->tok_sloc.line); - vfprintf(stderr, fmt, ap); - va_end(ap); - fprintf(stderr, " expected but \"%s\" found\n", lex_get_tok_str(LEX, TOK, LEX->tok_str)); -} - -// throw a compile error with explicit source code location (va_list form) -void cc_error_loc_v(struct cc *cc, struct sloc *sloc, const char *fmt, va_list ap) -{ - fprintf(stderr, "%s:%d: error: ", sloc->name, sloc->line); - vfprintf(stderr, fmt, ap); - fputc('\n', stderr); -} - -// throw a compile error with explicit source code location (... form) -void cc_error_loc(struct cc *cc, struct sloc *sloc, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - cc_error_loc_v(cc, sloc, fmt, ap); - va_end(ap); -} - -// throw a compile error at current token -void cc_error(struct cc *cc, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - cc_error_loc_v(cc, &LEX->tok_sloc, fmt, ap); - va_end(ap); -} - -// If the next token is in the given set of tokens, -// then accept it, otherwise throw an error. -// 'description' describes what is wanted. This could be -// a token or a non-terminal name (eg. "type" or "expression"). -// Token set is a zero-terminated list of tokens. Eg.: -// > cc_accept(cc, "operator", '+', '-', TOK_SHL, 0); -// would throw "operator expected, but '*' found" if the next -// token wasn't either '+', '-' or TOK_SHL (<<) -void cc_accept(struct cc *cc, const char *description, ...) -{ - va_list ap; - tok_t tok; - va_start(ap, description); - while ((tok = va_arg(ap, int))){ - if (TOK == tok){ - // ok - lex_next(LEX); - return; - } - } - cc_expect(cc, "%s", description); -} - -// keep eating tokens until the current token is in the given set. -// The set is a zero-terminated list of tokens, eg.: -// > cc_sync(cc, ')', ';', 0); -void cc_sync(struct cc *cc, ...) -{ - va_list ap; - tok_t tok; - for (;;){ - va_start(ap, cc); - while ((tok = va_arg(ap, int))){ - if (TOK == tok) - return; - } - va_end(ap); - lex_next(LEX); - } -} - -static struct { - int prec; // operator precedence level (1=highest) - tok_t tok; // operator token -} operator_table[] = { - {1, '*'}, {1, '/'}, {1, '%'}, // multiplicative - {2, '+'}, {2, '-'}, // additive - {3, TOK_SHL}, {3, TOK_SHR}, // shiftive? - {4, '<'}, {4, TOK_LE}, {4, '>'}, {4, TOK_GE}, // comparison - {5, TOK_EQ}, {5, TOK_NE}, // equality - {6, '&'}, // bitwise and - {7, '^'}, // bitwise xor - {8, '|'}, // bitwise or - {9, TOK_LAND}, // logical and - {10, TOK_LOR}, // logical or - {11, '?'}, // ternary - {12, '='}, {12, TOK_EADD}, {12, TOK_ESUB}, // augmented assignment - {12, TOK_EMUL}, {12, TOK_EDIV}, {12, TOK_EMOD},// . - {12, TOK_ESHL}, {12, TOK_ESHR}, {12, TOK_EAND},// . - {12, TOK_EXOR}, {12, TOK_EOR}, // . - {13, ','}, // comma operator -}; - -static int get_precedence(tok_t tok) -{ - int i; - for (i=0; i<(sizeof operator_table / sizeof *operator_table); i++){ - if (operator_table[i].tok == tok){ - return operator_table[i].prec; - } - } - return 0; -} - -// return true if parser state is in global scope -// (i.e. what is being passed is at global (file) scope) -static bool cc_isglobal(struct cc *cc) -{ - return (cc->scope == cc->stree); -} - -// return the current function node -static struct stree *cc_get_current_func(struct cc *cc) -{ - // TODO! - return NULL; -} - -struct stree *cc_parse_factor(struct cc *cc) -{ - struct stree *fac; - if (lex_is_ident(LEX, TOK)){ - fac = stree_create(); - fac->form = STF_FACTOR; - fac->tok = lex_get_tok_str(LEX, TOK, NULL)[0]; // TODO - lex_next(LEX); - return fac; - } else if (TOK == '('){ - lex_next(LEX); - fac = cc_parse_expr(cc); - cc_accept(cc, "\")\"", ')', 0); - return fac; - } else { - cc_expect(cc, "expression"); - return NULL; - } -} - -struct stree *cc_parse_expr(struct cc *cc) -{ - struct stree *lhs, *rhs, *op_node, *node, *node_parent, *node_right; - int prec, node_prec, op_tok; - lhs = cc_parse_factor(cc); - if (!lhs) - return NULL; - prec = get_precedence(TOK); - while (prec != 0){ - op_tok = TOK; - lex_next(LEX); - rhs = cc_parse_factor(cc); - if (!rhs){ - return lhs; - } - // find where to insert it - node = lhs; - node_parent = NULL; - while (node && node->form == STF_BINOP){ - node_prec = get_precedence(node->tok); - if (prec < node_prec){ - // carry on descending - node_right = stree_right(node); - if (node_right){ - node_parent = node; - node = node_right; - } else { - break; - } - } else { - break; - } - } - if (node){ - // insert between node and node_parent - op_node = stree_create(); - op_node->form = STF_BINOP; - op_node->tok = op_tok; - if (node_parent){ - stree_remove_child(node); - stree_append_child(op_node, node); - stree_append_child(op_node, rhs); - stree_append_child(node_parent, op_node); - } else { - stree_append_child(op_node, node); - stree_append_child(op_node, rhs); - lhs = op_node; - } - } else { - die("not sure"); - } - prec = get_precedence(TOK); - } - return lhs; -} - -// parse struct or union (they're syntactically identical) -bool cc_parse_struct(struct cc *cc, struct btype *struct_type_info) -{ - struct stree *prev_tag, *tag; - struct btype type_info, original_type_info; - struct stree *type_node, *sym_node; - tag = stree_create(); - tag->form = STF_TAG; - tag->sloc = LEX->tok_sloc; - lex_next(LEX); - if (lex_is_ident(LEX, TOK)){ - // prev_tag = stree_find(cc->scope, STF_TAG, TOK); TODO - tag->tok = TOK; - tag->sloc = LEX->tok_sloc; - lex_next(LEX); - } - if (TOK == '{'){ - // struct definition - lex_next(LEX); - while (TOK != 0 && TOK != '}'){ - if (cc_parse_type(cc, &original_type_info)){ - do { - type_info = original_type_info; - type_node = cc_parse_pointer_type(cc, &type_info); - if (!lex_is_ident(LEX, TOK)){ - cc_expect(cc, "identifier"); - if (type_node){ - stree_destroy(type_node); - } - return false; // XXX: LEAKS? - } - sym_node = stree_create(); - sym_node->form = STF_VARIABLE; - sym_node->btype = type_info; - sym_node->tok = TOK; - sym_node->sloc = LEX->tok_sloc; - if (type_node){ - stree_append_child(sym_node, type_node); - } - stree_append_child(tag, sym_node); - lex_next(LEX); - // TODO: check for duplicate fields - } while (TOK == ',' ? (lex_next(LEX), true) : false); - } else { - // ??? - cc_expect(cc, "field or \"}\""); - } - cc_accept(cc, "\";\"", ';', 0); - } - cc_accept(cc, "\"}\"", '}', 0); - - - } else { - } - stree_append_child(cc->scope, tag); - struct_type_info->base_type = BT_STRUCT; - struct_type_info->node = tag; - return true; -} - -bool cc_parse_type(struct cc *cc, struct btype *type_info) -{ - type_info->base_type = 0; - type_info->qualifiers = 0; - switch (TOK){ - case TOK_void: - type_info->base_type = BT_VOID; // well, it's a type, isn't it? - // (HINT: the check for variables of type void is not done here) - break; - case TOK_char: - type_info->base_type = BT_CHAR; - break; - case TOK_int: - type_info->base_type = BT_INT; - break; - case TOK_float: - type_info->base_type = BT_FLOAT; - break; - case TOK_double: - type_info->base_type = BT_DOUBLE; - break; - case TOK_struct: - case TOK_union: - return cc_parse_struct(cc, type_info); - default: - return false; - } - lex_next(LEX); - return true; -} - -// Parse a pointer type: next token must be after initial type declaration -// i.e.: int *foo; -// ^--current token: '*' -// Returns a sub-stree of additional type information. -// THIS MUST BE PUT SOMEWHERE IN A TREE. -// *type_info will be modified to represent the new pointer type -struct stree *cc_parse_pointer_type(struct cc *cc, struct btype *type_info) -{ - struct stree *type_node = NULL, *new_type_node; - while (TOK == '*'){ - // create a type node to store the excess type information - new_type_node = stree_create(); - new_type_node->form = STF_TYPE; - new_type_node->btype = *type_info; - if (type_node){ - stree_append_child(type_node, new_type_node); - } - type_node = new_type_node; - // create pointer type - type_info->base_type = BT_POINTER; - type_info->qualifiers = 0; // TODO: parse qualifiers - type_info->node = new_type_node; - // eat '*' - lex_next(LEX); - } - return type_node; -} - -// Parse function parameters -// void foo(int a, int b); -// ^-- current token: 'int' -// returns an STF_PARAMETERS node full of parameters, or -// NULL if there aren't any. -struct stree *cc_parse_parameters(struct cc *cc) -{ - struct stree *parameters = NULL; - if (TOK == TOK_void){ - // just 'void' - no parameters - lex_next(LEX); - } else if (TOK != ')'){ - parameters = stree_create(); // create parameter list - parameters->form = STF_PARAMETERS; - do { - struct btype type_info; - struct stree *type_node, *param_node; - if (TOK == TOK_ELLIPSIS){ - // variadic function - param_node = stree_create(); - param_node->form = STF_VARIABLE; - param_node->tok = TOK_ELLIPSIS; - if (stree_child_count(parameters) == 0){ - cc_error(cc, "a named argument is required before \"...\""); - } - lex_next(LEX); - if (TOK != ')'){ - cc_error(cc, "variadic function's \"...\" must be placed at the end of the parameter list"); - cc_sync(cc, ')', ';', 0); - } - } else { - // parse parameter's type - if (!cc_parse_type(cc, &type_info)){ - cc_expect(cc, "type"); - } - type_node = cc_parse_pointer_type(cc, &type_info); - // create parameter node - param_node = stree_create(); - param_node->form = STF_VARIABLE; - param_node->btype = type_info; - // save extra type info, if there is any - if (type_node){ - stree_append_child(param_node, type_node); - } - // does the parameter have a name yet? it may not. - if (lex_is_ident(LEX, TOK)){ - // parameter has a name - param_node->tok = TOK; - lex_next(LEX); - } - // if it doesn't, that's ok too - // as long as it's not a function definition; - // we don't know yet, though. - } - // Now, put the parameter in the list - stree_append_child(parameters, param_node); - } while (TOK == ',' ? (lex_next(LEX), true) : false); - } - cc_accept(cc, "\")\"", ')', 0); - return parameters; -} - -// return true if the two types are identical -bool type_identical(struct btype *type1, struct btype *type2) -{ -top: - if (type1->base_type != type2->base_type) - return false; - // TODO: check qualifiers? - if (type1->base_type == BT_POINTER){ - // compare pointed types - type1 = &type1->node->btype; - type2 = &type2->node->btype; - goto top; - } - return true; // all checks passed -} - -// compare two functions, func1 and func2, for compatibility -void cc_compare_functions(struct cc *cc, struct stree *func1, struct stree *func2) -{ - struct stree *param_list_1, *param_list_2, *param_1, *param_2; - int n; - if (!type_identical(&func1->btype, &func2->btype)){ - cc_error_loc(cc, &func2->sloc, "conflicting types for \"%s\"", - lex_get_tok_str(LEX, func2->tok, NULL)); - cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", - lex_get_tok_str(LEX, func1->tok, NULL)); - } - // check parameters - param_list_1 = stree_get_child_by_form(func1, STF_PARAMETERS); - param_list_2 = stree_get_child_by_form(func2, STF_PARAMETERS); - if (stree_child_count(param_list_1) != stree_child_count(param_list_2)){ - cc_error_loc(cc, &func2->sloc, "conflicting number of parameters for \"%s\"", - lex_get_tok_str(LEX, func2->tok, NULL)); - cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", - lex_get_tok_str(LEX, func1->tok, NULL)); - } else { - param_1 = NULL; - param_2 = NULL; - n = 0; - while (stree_next_child(param_list_1, ¶m_1), - stree_next_child(param_list_2, ¶m_2), - (param_1 && param_2)){ - n++; - if (param_1->tok == TOK_ELLIPSIS || param_2->tok == TOK_ELLIPSIS){ - // variadic function - if (param_1->tok == TOK_ELLIPSIS && param_2->tok == TOK_ELLIPSIS){ - // that's ok - } else { - cc_error_loc(cc, &func2->sloc, "\"%s\" is %svariadic, unlike previous declaration", - lex_get_tok_str(LEX, func2->tok, NULL), - param_1->tok == TOK_ELLIPSIS ? "not " : ""); - cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", - lex_get_tok_str(LEX, func1->tok, NULL)); - } - } - // compare parameter types - else if (!type_identical(¶m_1->btype, ¶m_2->btype)){ - cc_error_loc(cc, &func2->sloc, "conflicting types for parameter %d of \"%s\"", - n, lex_get_tok_str(LEX, func2->tok, NULL)); - cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", - lex_get_tok_str(LEX, func1->tok, NULL)); - } - // merge name information - if (param_2->tok) - param_1->tok = param_2->tok; - } - } -} - -void cc_parse_decl_function(struct cc *cc, struct stree **psym_node) -{ - struct stree *parameters, *prev_decl; - struct stree *sym_node = *psym_node; - if (!cc_isglobal(cc)){ - struct stree *current_func; - current_func = cc_get_current_func(cc); - cc_error(cc, "nested functions are not permitted: \"%s\" inside \"%s\"", - lex_get_tok_str(LEX, sym_node->tok, NULL), - current_func ? lex_get_tok_str(LEX, current_func->tok, NULL) : "???"); - } - // find previous declaration of this symbol - prev_decl = stree_find_local(cc->scope, STF_FUNCTION, sym_node->tok); - if (!prev_decl) prev_decl = stree_find_local(cc->scope, STF_VARIABLE, sym_node->tok); - if (prev_decl){ - if (prev_decl->form == STF_FUNCTION){ - // must check that declarations match later - } else { - cc_error_loc(cc, &sym_node->sloc, - "\"%s\" declared as different kind of symbol", - lex_get_tok_str(LEX, sym_node->tok, NULL)); - cc_error_loc(cc, &prev_decl->sloc, - "previous declaration of \"%s\" was here", - lex_get_tok_str(LEX, sym_node->tok, NULL)); - } - } - sym_node->form = STF_FUNCTION; - // parse parameters - lex_next(LEX); // eat '(' - parameters = cc_parse_parameters(cc); - if (parameters){ - stree_append_child(sym_node, parameters); - } - if (prev_decl && prev_decl->form == STF_FUNCTION){ - // make sure declarations match - cc_compare_functions(cc, prev_decl, sym_node); - // either way, we don't want two copies, so delete the later one - stree_destroy(sym_node); - *psym_node = NULL; - } -} - -void cc_parse_decl_variable(struct cc *cc, struct stree *sym_node) -{ - struct stree *prev_defn; - if (sym_node->btype.base_type == BT_VOID){ - cc_error_loc(cc, &sym_node->sloc, "cannot declare variable \"%s\" of type void: impossible without vacuum pump", - lex_get_tok_str(LEX, sym_node->tok, NULL)); - } - prev_defn = stree_find_local(cc->scope, STF_VARIABLE, sym_node->tok); - if (prev_defn){ - cc_error_loc(cc, &sym_node->sloc, "redefinition of \"%s\"", - lex_get_tok_str(LEX, sym_node->tok, NULL)); - cc_error_loc(cc, &prev_defn->sloc, - "previous definition of \"%s\" was here", - lex_get_tok_str(LEX, sym_node->tok, NULL)); - } - sym_node->form = STF_VARIABLE; -} - -void cc_parse_decl(struct cc *cc, struct stree *insert_here) -{ - struct btype type_info, original_type_info; - struct stree *type_node, *sym_node = NULL; - if (cc_parse_type(cc, &original_type_info)){ - do { - // restore the type before the '*'s (pointer types) - type_info = original_type_info; - type_node = cc_parse_pointer_type(cc, &type_info); - if (!lex_is_ident(LEX, TOK)){ - cc_expect(cc, "identifier"); - if (type_node){ - stree_destroy(type_node); - } - return; - } - - // variable declaration or definition - // or function declaration or definition - sym_node = stree_create(); - sym_node->btype = type_info; - sym_node->tok = TOK; - sym_node->sloc = LEX->tok_sloc; - lex_next(LEX); - // put the type info somewhere - if (type_node){ - stree_append_child(sym_node, type_node); - } - if (TOK == '('){ - // it is a function - cc_parse_decl_function(cc, &sym_node); - } else { - // it is a variable - cc_parse_decl_variable(cc, sym_node); - } - if (sym_node){ - stree_append_child(insert_here, sym_node); - } - } while (TOK == ',' ? (lex_next(LEX), true) : false); - } else { - } -} - -void cc_parse(struct cc *cc) -{ - cc->stree = cc->scope = stree_create(); - while (TOK != 0){ - cc_parse_decl(cc, cc->scope); - cc_accept(cc, "\";\"", ';', 0); - } -} +#include "parse.h" +#include "cc.h" +#include "scanner.h" +#include "errors.h" +#include "stdlib.h" +#include "stdio.h" +#include "stdarg.h" + +// make-it-easier macros +#define LEX (&cc->lex) // struct lexer * +#define TOK (cc->lex.tok) // tok_t + +struct stree *cc_parse_expr(struct cc *cc); +bool cc_parse_type(struct cc *cc, struct btype *type_info); +struct stree *cc_parse_pointer_type(struct cc *cc, struct btype *type_info); + +// throw a 'foo expected but bar found' error +void cc_expect(struct cc *cc, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: error: ", LEX->tok_sloc.name, LEX->tok_sloc.line); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, " expected but \"%s\" found\n", lex_get_tok_str(LEX, TOK, LEX->tok_str)); +} + +// throw a compile error with explicit source code location (va_list form) +void cc_error_loc_v(struct cc *cc, struct sloc *sloc, const char *fmt, va_list ap) +{ + fprintf(stderr, "%s:%d: error: ", sloc->name, sloc->line); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); +} + +// throw a compile error with explicit source code location (... form) +void cc_error_loc(struct cc *cc, struct sloc *sloc, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + cc_error_loc_v(cc, sloc, fmt, ap); + va_end(ap); +} + +// throw a compile error at current token +void cc_error(struct cc *cc, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + cc_error_loc_v(cc, &LEX->tok_sloc, fmt, ap); + va_end(ap); +} + +// If the next token is in the given set of tokens, +// then accept it, otherwise throw an error. +// 'description' describes what is wanted. This could be +// a token or a non-terminal name (eg. "type" or "expression"). +// Token set is a zero-terminated list of tokens. Eg.: +// > cc_accept(cc, "operator", '+', '-', TOK_SHL, 0); +// would throw "operator expected, but '*' found" if the next +// token wasn't either '+', '-' or TOK_SHL (<<) +void cc_accept(struct cc *cc, const char *description, ...) +{ + va_list ap; + tok_t tok; + va_start(ap, description); + while ((tok = va_arg(ap, int))){ + if (TOK == tok){ + // ok + lex_next(LEX); + return; + } + } + cc_expect(cc, "%s", description); +} + +// keep eating tokens until the current token is in the given set. +// The set is a zero-terminated list of tokens, eg.: +// > cc_sync(cc, ')', ';', 0); +void cc_sync(struct cc *cc, ...) +{ + va_list ap; + tok_t tok; + for (;;){ + va_start(ap, cc); + while ((tok = va_arg(ap, int))){ + if (TOK == tok) + return; + } + va_end(ap); + lex_next(LEX); + } +} + +static struct { + int prec; // operator precedence level (1=highest) + tok_t tok; // operator token +} operator_table[] = { + {1, '*'}, {1, '/'}, {1, '%'}, // multiplicative + {2, '+'}, {2, '-'}, // additive + {3, TOK_SHL}, {3, TOK_SHR}, // shiftive? + {4, '<'}, {4, TOK_LE}, {4, '>'}, {4, TOK_GE}, // comparison + {5, TOK_EQ}, {5, TOK_NE}, // equality + {6, '&'}, // bitwise and + {7, '^'}, // bitwise xor + {8, '|'}, // bitwise or + {9, TOK_LAND}, // logical and + {10, TOK_LOR}, // logical or + {11, '?'}, // ternary + {12, '='}, {12, TOK_EADD}, {12, TOK_ESUB}, // augmented assignment + {12, TOK_EMUL}, {12, TOK_EDIV}, {12, TOK_EMOD},// . + {12, TOK_ESHL}, {12, TOK_ESHR}, {12, TOK_EAND},// . + {12, TOK_EXOR}, {12, TOK_EOR}, // . + {13, ','}, // comma operator +}; + +static int get_precedence(tok_t tok) +{ + int i; + for (i=0; i<(sizeof operator_table / sizeof *operator_table); i++){ + if (operator_table[i].tok == tok){ + return operator_table[i].prec; + } + } + return 0; +} + +// return true if parser state is in global scope +// (i.e. what is being passed is at global (file) scope) +static bool cc_isglobal(struct cc *cc) +{ + return (cc->scope == cc->stree); +} + +// return the current function node +static struct stree *cc_get_current_func(struct cc *cc) +{ + // TODO! + return NULL; +} + +struct stree *cc_parse_factor(struct cc *cc) +{ + struct stree *fac; + if (lex_is_ident(LEX, TOK)){ + fac = stree_create(); + fac->form = STF_FACTOR; + fac->tok = lex_get_tok_str(LEX, TOK, NULL)[0]; // TODO + lex_next(LEX); + return fac; + } else if (TOK == '('){ + lex_next(LEX); + fac = cc_parse_expr(cc); + cc_accept(cc, "\")\"", ')', 0); + return fac; + } else { + cc_expect(cc, "expression"); + return NULL; + } +} + +struct stree *cc_parse_expr(struct cc *cc) +{ + struct stree *lhs, *rhs, *op_node, *node, *node_parent, *node_right; + int prec, node_prec, op_tok; + lhs = cc_parse_factor(cc); + if (!lhs) + return NULL; + prec = get_precedence(TOK); + while (prec != 0){ + op_tok = TOK; + lex_next(LEX); + rhs = cc_parse_factor(cc); + if (!rhs){ + return lhs; + } + // find where to insert it + node = lhs; + node_parent = NULL; + while (node && node->form == STF_BINOP){ + node_prec = get_precedence(node->tok); + if (prec < node_prec){ + // carry on descending + node_right = stree_right(node); + if (node_right){ + node_parent = node; + node = node_right; + } else { + break; + } + } else { + break; + } + } + if (node){ + // insert between node and node_parent + op_node = stree_create(); + op_node->form = STF_BINOP; + op_node->tok = op_tok; + if (node_parent){ + stree_remove_child(node); + stree_append_child(op_node, node); + stree_append_child(op_node, rhs); + stree_append_child(node_parent, op_node); + } else { + stree_append_child(op_node, node); + stree_append_child(op_node, rhs); + lhs = op_node; + } + } else { + die("not sure"); + } + prec = get_precedence(TOK); + } + return lhs; +} + +// parse struct or union (they're syntactically identical) +bool cc_parse_struct(struct cc *cc, struct btype *struct_type_info) +{ + struct stree *prev_tag, *tag; + struct btype type_info, original_type_info; + struct stree *type_node, *sym_node; + tag = stree_create(); + tag->form = STF_TAG; + tag->sloc = LEX->tok_sloc; + lex_next(LEX); + if (lex_is_ident(LEX, TOK)){ + // prev_tag = stree_find(cc->scope, STF_TAG, TOK); TODO + tag->tok = TOK; + tag->sloc = LEX->tok_sloc; + lex_next(LEX); + } + if (TOK == '{'){ + // struct definition + lex_next(LEX); + while (TOK != 0 && TOK != '}'){ + if (cc_parse_type(cc, &original_type_info)){ + do { + type_info = original_type_info; + type_node = cc_parse_pointer_type(cc, &type_info); + if (!lex_is_ident(LEX, TOK)){ + cc_expect(cc, "identifier"); + if (type_node){ + stree_destroy(type_node); + } + return false; // XXX: LEAKS? + } + sym_node = stree_create(); + sym_node->form = STF_VARIABLE; + sym_node->btype = type_info; + sym_node->tok = TOK; + sym_node->sloc = LEX->tok_sloc; + if (type_node){ + stree_append_child(sym_node, type_node); + } + stree_append_child(tag, sym_node); + lex_next(LEX); + // TODO: check for duplicate fields + } while (TOK == ',' ? (lex_next(LEX), true) : false); + } else { + // ??? + cc_expect(cc, "field or \"}\""); + } + cc_accept(cc, "\";\"", ';', 0); + } + cc_accept(cc, "\"}\"", '}', 0); + + + } else { + } + stree_append_child(cc->scope, tag); + struct_type_info->base_type = BT_STRUCT; + struct_type_info->node = tag; + return true; +} + +bool cc_parse_type(struct cc *cc, struct btype *type_info) +{ + type_info->base_type = 0; + type_info->qualifiers = 0; + switch (TOK){ + case TOK_void: + type_info->base_type = BT_VOID; // well, it's a type, isn't it? + // (HINT: the check for variables of type void is not done here) + break; + case TOK_char: + type_info->base_type = BT_CHAR; + break; + case TOK_int: + type_info->base_type = BT_INT; + break; + case TOK_float: + type_info->base_type = BT_FLOAT; + break; + case TOK_double: + type_info->base_type = BT_DOUBLE; + break; + case TOK_struct: + case TOK_union: + return cc_parse_struct(cc, type_info); + default: + return false; + } + lex_next(LEX); + return true; +} + +// Parse a pointer type: next token must be after initial type declaration +// i.e.: int *foo; +// ^--current token: '*' +// Returns a sub-stree of additional type information. +// THIS MUST BE PUT SOMEWHERE IN A TREE. +// *type_info will be modified to represent the new pointer type +struct stree *cc_parse_pointer_type(struct cc *cc, struct btype *type_info) +{ + struct stree *type_node = NULL, *new_type_node; + while (TOK == '*'){ + // create a type node to store the excess type information + new_type_node = stree_create(); + new_type_node->form = STF_TYPE; + new_type_node->btype = *type_info; + if (type_node){ + stree_append_child(type_node, new_type_node); + } + type_node = new_type_node; + // create pointer type + type_info->base_type = BT_POINTER; + type_info->qualifiers = 0; // TODO: parse qualifiers + type_info->node = new_type_node; + // eat '*' + lex_next(LEX); + } + return type_node; +} + +// Parse function parameters +// void foo(int a, int b); +// ^-- current token: 'int' +// returns an STF_PARAMETERS node full of parameters, or +// NULL if there aren't any. +struct stree *cc_parse_parameters(struct cc *cc) +{ + struct stree *parameters = NULL; + if (TOK == TOK_void){ + // just 'void' - no parameters + lex_next(LEX); + } else if (TOK != ')'){ + parameters = stree_create(); // create parameter list + parameters->form = STF_PARAMETERS; + do { + struct btype type_info; + struct stree *type_node, *param_node; + if (TOK == TOK_ELLIPSIS){ + // variadic function + param_node = stree_create(); + param_node->form = STF_VARIABLE; + param_node->tok = TOK_ELLIPSIS; + if (stree_child_count(parameters) == 0){ + cc_error(cc, "a named argument is required before \"...\""); + } + lex_next(LEX); + if (TOK != ')'){ + cc_error(cc, "variadic function's \"...\" must be placed at the end of the parameter list"); + cc_sync(cc, ')', ';', 0); + } + } else { + // parse parameter's type + if (!cc_parse_type(cc, &type_info)){ + cc_expect(cc, "type"); + } + type_node = cc_parse_pointer_type(cc, &type_info); + // create parameter node + param_node = stree_create(); + param_node->form = STF_VARIABLE; + param_node->btype = type_info; + // save extra type info, if there is any + if (type_node){ + stree_append_child(param_node, type_node); + } + // does the parameter have a name yet? it may not. + if (lex_is_ident(LEX, TOK)){ + // parameter has a name + param_node->tok = TOK; + lex_next(LEX); + } + // if it doesn't, that's ok too + // as long as it's not a function definition; + // we don't know yet, though. + } + // Now, put the parameter in the list + stree_append_child(parameters, param_node); + } while (TOK == ',' ? (lex_next(LEX), true) : false); + } + cc_accept(cc, "\")\"", ')', 0); + return parameters; +} + +// return true if the two types are identical +bool type_identical(struct btype *type1, struct btype *type2) +{ +top: + if (type1->base_type != type2->base_type) + return false; + // TODO: check qualifiers? + if (type1->base_type == BT_POINTER){ + // compare pointed types + type1 = &type1->node->btype; + type2 = &type2->node->btype; + goto top; + } + return true; // all checks passed +} + +// compare two functions, func1 and func2, for compatibility +void cc_compare_functions(struct cc *cc, struct stree *func1, struct stree *func2) +{ + struct stree *param_list_1, *param_list_2, *param_1, *param_2; + int n; + if (!type_identical(&func1->btype, &func2->btype)){ + cc_error_loc(cc, &func2->sloc, "conflicting types for \"%s\"", + lex_get_tok_str(LEX, func2->tok, NULL)); + cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", + lex_get_tok_str(LEX, func1->tok, NULL)); + } + // check parameters + param_list_1 = stree_get_child_by_form(func1, STF_PARAMETERS); + param_list_2 = stree_get_child_by_form(func2, STF_PARAMETERS); + if (stree_child_count(param_list_1) != stree_child_count(param_list_2)){ + cc_error_loc(cc, &func2->sloc, "conflicting number of parameters for \"%s\"", + lex_get_tok_str(LEX, func2->tok, NULL)); + cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", + lex_get_tok_str(LEX, func1->tok, NULL)); + } else { + param_1 = NULL; + param_2 = NULL; + n = 0; + while (stree_next_child(param_list_1, ¶m_1), + stree_next_child(param_list_2, ¶m_2), + (param_1 && param_2)){ + n++; + if (param_1->tok == TOK_ELLIPSIS || param_2->tok == TOK_ELLIPSIS){ + // variadic function + if (param_1->tok == TOK_ELLIPSIS && param_2->tok == TOK_ELLIPSIS){ + // that's ok + } else { + cc_error_loc(cc, &func2->sloc, "\"%s\" is %svariadic, unlike previous declaration", + lex_get_tok_str(LEX, func2->tok, NULL), + param_1->tok == TOK_ELLIPSIS ? "not " : ""); + cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", + lex_get_tok_str(LEX, func1->tok, NULL)); + } + } + // compare parameter types + else if (!type_identical(¶m_1->btype, ¶m_2->btype)){ + cc_error_loc(cc, &func2->sloc, "conflicting types for parameter %d of \"%s\"", + n, lex_get_tok_str(LEX, func2->tok, NULL)); + cc_error_loc(cc, &func1->sloc, "previous declaration of \"%s\" was here", + lex_get_tok_str(LEX, func1->tok, NULL)); + } + // merge name information + if (param_2->tok) + param_1->tok = param_2->tok; + } + } +} + +void cc_parse_decl_function(struct cc *cc, struct stree **psym_node) +{ + struct stree *parameters, *prev_decl; + struct stree *sym_node = *psym_node; + if (!cc_isglobal(cc)){ + struct stree *current_func; + current_func = cc_get_current_func(cc); + cc_error(cc, "nested functions are not permitted: \"%s\" inside \"%s\"", + lex_get_tok_str(LEX, sym_node->tok, NULL), + current_func ? lex_get_tok_str(LEX, current_func->tok, NULL) : "???"); + } + // find previous declaration of this symbol + prev_decl = stree_find_local(cc->scope, STF_FUNCTION, sym_node->tok); + if (!prev_decl) prev_decl = stree_find_local(cc->scope, STF_VARIABLE, sym_node->tok); + if (prev_decl){ + if (prev_decl->form == STF_FUNCTION){ + // must check that declarations match later + } else { + cc_error_loc(cc, &sym_node->sloc, + "\"%s\" declared as different kind of symbol", + lex_get_tok_str(LEX, sym_node->tok, NULL)); + cc_error_loc(cc, &prev_decl->sloc, + "previous declaration of \"%s\" was here", + lex_get_tok_str(LEX, sym_node->tok, NULL)); + } + } + sym_node->form = STF_FUNCTION; + // parse parameters + lex_next(LEX); // eat '(' + parameters = cc_parse_parameters(cc); + if (parameters){ + stree_append_child(sym_node, parameters); + } + if (prev_decl && prev_decl->form == STF_FUNCTION){ + // make sure declarations match + cc_compare_functions(cc, prev_decl, sym_node); + // either way, we don't want two copies, so delete the later one + stree_destroy(sym_node); + *psym_node = NULL; + } +} + +void cc_parse_decl_variable(struct cc *cc, struct stree *sym_node) +{ + struct stree *prev_defn; + if (sym_node->btype.base_type == BT_VOID){ + cc_error_loc(cc, &sym_node->sloc, "cannot declare variable \"%s\" of type void: impossible without vacuum pump", + lex_get_tok_str(LEX, sym_node->tok, NULL)); + } + prev_defn = stree_find_local(cc->scope, STF_VARIABLE, sym_node->tok); + if (prev_defn){ + cc_error_loc(cc, &sym_node->sloc, "redefinition of \"%s\"", + lex_get_tok_str(LEX, sym_node->tok, NULL)); + cc_error_loc(cc, &prev_defn->sloc, + "previous definition of \"%s\" was here", + lex_get_tok_str(LEX, sym_node->tok, NULL)); + } + sym_node->form = STF_VARIABLE; +} + +void cc_parse_decl(struct cc *cc, struct stree *insert_here) +{ + struct btype type_info, original_type_info; + struct stree *type_node, *sym_node = NULL; + if (cc_parse_type(cc, &original_type_info)){ + do { + // restore the type before the '*'s (pointer types) + type_info = original_type_info; + type_node = cc_parse_pointer_type(cc, &type_info); + if (!lex_is_ident(LEX, TOK)){ + cc_expect(cc, "identifier"); + if (type_node){ + stree_destroy(type_node); + } + return; + } + + // variable declaration or definition + // or function declaration or definition + sym_node = stree_create(); + sym_node->btype = type_info; + sym_node->tok = TOK; + sym_node->sloc = LEX->tok_sloc; + lex_next(LEX); + // put the type info somewhere + if (type_node){ + stree_append_child(sym_node, type_node); + } + if (TOK == '('){ + // it is a function + cc_parse_decl_function(cc, &sym_node); + } else { + // it is a variable + cc_parse_decl_variable(cc, sym_node); + } + if (sym_node){ + stree_append_child(insert_here, sym_node); + } + } while (TOK == ',' ? (lex_next(LEX), true) : false); + } else { + } +} + +void cc_parse(struct cc *cc) +{ + cc->stree = cc->scope = stree_create(); + while (TOK != 0){ + cc_parse_decl(cc, cc->scope); + cc_accept(cc, "\";\"", ';', 0); + } +} diff --git a/scanner.c b/scanner.c dissimilarity index 76% index a02e4a7..78d15fa 100644 --- a/scanner.c +++ b/scanner.c @@ -1,291 +1,291 @@ -#include "scanner.h" -#include "errors.h" -#include "cpp.h" -#include "ctype.h" -#include "bstr.h" -#include "string.h" -#include "assert.h" - -static char *keywords[] = { -#define DEF(x) #x, -#include "tokens.inc" -#undef DEF -}; - -static char *punctuation[] = { -#define PUNCT(x, str) str, -#include "tokens.inc" -#undef PUNCT -}; - -void lex_error(struct lexer *lex, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "%s:%d: error: ", lex->tok_sloc.name, lex->tok_sloc.line); - vfprintf(stderr, fmt, ap); - va_end(ap); - fputc('\n', stderr); -} - -// return token number for a keyword -static int find_keyword(const char *str) -{ - // binary search - int l, u, try; - char *try_str; - int compare; - l = TOK_FIRSTK + 1; - u = TOK_LASTK - 1; - do { - try = (l + u) / 2; - try_str = keywords[try - (TOK_FIRSTK + 1)]; - compare = strcmp(str, try_str); - if (compare == 0){ - return try; - } else if (compare < 0){ - u = try - 1; - } else if (compare > 0){ - l = try + 1; - } - } while (l <= u); - return 0; -} - -void lex_create(struct lexer *lex) -{ - lex->pch = NULL; - lex->tok = 0; - lex->tok_str = NULL; - lex->tok_str_len = 0; - lex->next_ident_tok = TOK_IDENT; - memset(lex->ident_hashtab, 0, IDENT_HASH_SIZE * sizeof(struct ident *)); - cpp_init(&lex->cpp); -} - -void lex_delete(struct lexer *lex) -{ - int i; - struct ident *id, *id_prev; - for (i=0; iident_hashtab[i]; - while (id){ - id_prev = id->hash_prev; - free(id); - id = id_prev; - } - } - free(lex->tok_str); - cpp_delete(&lex->cpp); -} - -// generate a (fairly simple) hash for a string -static int hash_str(const char *str, int hash_size) -{ - int hash_value = 0; - while (*str){ - hash_value *= *str; - str++; - } - return hash_value % hash_size; -} - -struct ident *lex_get_ident_hashed(struct lexer *lex, const char *str, int hash) -{ - struct ident *ident; - ident = lex->ident_hashtab[hash]; - while (ident && strcmp(ident->str, str)){ - ident = ident->hash_prev; - } - return ident; -} - -// get, or create, a 'struct ident' -struct ident *lex_get_ident(struct lexer *lex, const char *str) -{ - int hash = hash_str(str, IDENT_HASH_SIZE); - struct ident *ident = lex_get_ident_hashed(lex, str, hash); - if (!ident){ - // create a new one - ident = emalloc(sizeof(struct ident) + strlen(str)); - ident->hash_prev = lex->ident_hashtab[hash]; - lex->ident_hashtab[hash] = ident; - ident->tok = lex->next_ident_tok++; - strcpy(ident->str, str); - } - return ident; -} - -// get a 'struct ident', but don't create it -struct ident *lex_get_ident_nocreate(struct lexer *lex, const char *str) -{ - int hash = hash_str(str, IDENT_HASH_SIZE); - return lex_get_ident_hashed(lex, str, hash); -} - -void lex_getline(struct lexer *lex) -{ -top: - cpp_read_line(&lex->cpp); - if (lex->cpp.line_buf){ - cpp_process_line(&lex->cpp); - if (lex->cpp.line_buf){ - lex->pch = lex->cpp.line_buf; - if (!*lex->pch){ - // blank line - goto top; - } - } else { - lex->pch = NULL; - } - } else { - lex->pch = NULL; - } -} - -void lex_start(struct lexer *lex) -{ - lex_getline(lex); - lex_next(lex); -} - -void lex_white(struct lexer *lex) -{ - lex->pch += strspn(lex->pch, " \t\n"); -} - -void lex_next(struct lexer *lex) -{ - if (!lex->pch || !*lex->pch){ - lex_getline(lex); - } - if (!lex->pch){ - lex->tok = 0; - lex->tok_sloc = lex->cpp.line_loc; - return; - } - lex_white(lex); - lex->tok_sloc = lex->cpp.line_loc; - if (isalpha(lex->pch[0]) || lex->pch[0] == '_'){ - // identifier or keyword - char *p_start = lex->pch, *id_str = NULL; - tok_t tok; - struct ident *ident; - while (isalnum(lex->pch[0]) || lex->pch[0] == '_'){ - lex->pch++; - } - strdncpy(&id_str, p_start, lex->pch - p_start); - tok = find_keyword(id_str); - if (tok != 0){ - lex->tok = tok; - } else { - // identifier - ident = lex_get_ident(lex, id_str); - lex->tok = ident->tok; - } - free(id_str); - } else if (lex->pch[0] == '"' || lex->pch[0] == '\''){ - char quote = lex->pch[0], **str_data = &lex->tok_str; - int *pstr_data_len = &lex->tok_str_len; - lex->pch++; - while (lex->pch[0] && lex->pch[0] != quote){ - strldcatc(str_data, pstr_data_len, lex->pch[0]); - lex->pch++; - } - if (lex->pch[0] == quote){ - lex->pch++; - } else { - lex_error(lex, "unterminated string literal"); - } - if (quote == '"'){ - lex->tok = TOK_STR; - } else { - lex->tok = TOK_CHARSTR; - } - } else { - // scan punctuation table - // HOT code! optimize! - int i, longest_match = 0, longest_match_len = 0, pch_len = strlen(lex->pch), punct_len; - for (i=TOK_FIRST_PUNCT+1; i pch_len || punct_len < longest_match_len){ - continue; - } - if (!strncmp(lex->pch, punctuation[i - (TOK_FIRST_PUNCT + 1)], punct_len)){ - assert(punct_len > longest_match_len); - longest_match = i; - longest_match_len = punct_len; - } - } - if (longest_match){ - lex->pch += longest_match_len; - lex->tok = longest_match; - } else // single-character token? - if (strchr("><=!-&|+*/%^.;:~(){}[],", lex->pch[0])){ - lex->tok = lex->pch[0]; - lex->pch++; - } else { - lex_error(lex, "invalid character in input file: %c", lex->pch[0]); - lex->tok = 0; - } - } -} - -// return a string for a token -// 'tok_str' may be null, but you won't get the contents of -// strings. The return value is a static string. Don't call lex_get_tok_str -// or lex_delete etc. until you've finished with the return value! -char *lex_get_tok_str(struct lexer *lex, tok_t tok, char *tok_str) -{ - static char buf[3]; - if (tok == 0){ - return ""; - } else if (tok <= 255){ - sprintf(buf, "%c", tok); - return buf; - } else if (tok > TOK_FIRSTK && tok < TOK_LASTK){ - return keywords[tok - (TOK_FIRSTK + 1)]; - } else if (tok > TOK_FIRST_PUNCT && tok < TOK_LAST_PUNCT){ - return punctuation[tok - (TOK_FIRST_PUNCT + 1)]; - } else if (tok >= TOK_IDENT){ - // this is difficult, because they're all in a hash table - // thankfully, we won't have to do this much - int i; - for (i=0; iident_hashtab[i]; - while (ident && ident->tok != tok){ - ident = ident->hash_prev; - } - if (ident){ - return ident->str; - } - } - return NULL; - } else { // TODO: strings and punctuation-like tokens - return NULL; - } -} - -bool lex_is_ident(struct lexer *lex, tok_t tok) -{ - return (tok >= TOK_IDENT && tok < lex->next_ident_tok); -} - -#if 0 -int main(int argc, char **argv) -{ - struct lexer lex; - lex_create(&lex); - cpp_include_file(&lex.cpp, "", stdin, false); - lex_start(&lex); - while (lex.tok){ - printf("%s ", lex_get_tok_str(&lex, lex.tok, lex.tok_str)); - lex_next(&lex); - } - - lex_delete(&lex); - return 0; -} -#endif - +#include "scanner.h" +#include "errors.h" +#include "cpp.h" +#include "ctype.h" +#include "bstr.h" +#include "string.h" +#include "assert.h" + +static char *keywords[] = { +#define DEF(x) #x, +#include "tokens.inc" +#undef DEF +}; + +static char *punctuation[] = { +#define PUNCT(x, str) str, +#include "tokens.inc" +#undef PUNCT +}; + +void lex_error(struct lexer *lex, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d: error: ", lex->tok_sloc.name, lex->tok_sloc.line); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +// return token number for a keyword +static int find_keyword(const char *str) +{ + // binary search + int l, u, try; + char *try_str; + int compare; + l = TOK_FIRSTK + 1; + u = TOK_LASTK - 1; + do { + try = (l + u) / 2; + try_str = keywords[try - (TOK_FIRSTK + 1)]; + compare = strcmp(str, try_str); + if (compare == 0){ + return try; + } else if (compare < 0){ + u = try - 1; + } else if (compare > 0){ + l = try + 1; + } + } while (l <= u); + return 0; +} + +void lex_create(struct lexer *lex) +{ + lex->pch = NULL; + lex->tok = 0; + lex->tok_str = NULL; + lex->tok_str_len = 0; + lex->next_ident_tok = TOK_IDENT; + memset(lex->ident_hashtab, 0, IDENT_HASH_SIZE * sizeof(struct ident *)); + cpp_init(&lex->cpp); +} + +void lex_delete(struct lexer *lex) +{ + int i; + struct ident *id, *id_prev; + for (i=0; iident_hashtab[i]; + while (id){ + id_prev = id->hash_prev; + free(id); + id = id_prev; + } + } + free(lex->tok_str); + cpp_delete(&lex->cpp); +} + +// generate a (fairly simple) hash for a string +static int hash_str(const char *str, int hash_size) +{ + int hash_value = 0; + while (*str){ + hash_value *= *str; + str++; + } + return hash_value % hash_size; +} + +struct ident *lex_get_ident_hashed(struct lexer *lex, const char *str, int hash) +{ + struct ident *ident; + ident = lex->ident_hashtab[hash]; + while (ident && strcmp(ident->str, str)){ + ident = ident->hash_prev; + } + return ident; +} + +// get, or create, a 'struct ident' +struct ident *lex_get_ident(struct lexer *lex, const char *str) +{ + int hash = hash_str(str, IDENT_HASH_SIZE); + struct ident *ident = lex_get_ident_hashed(lex, str, hash); + if (!ident){ + // create a new one + ident = emalloc(sizeof(struct ident) + strlen(str)); + ident->hash_prev = lex->ident_hashtab[hash]; + lex->ident_hashtab[hash] = ident; + ident->tok = lex->next_ident_tok++; + strcpy(ident->str, str); + } + return ident; +} + +// get a 'struct ident', but don't create it +struct ident *lex_get_ident_nocreate(struct lexer *lex, const char *str) +{ + int hash = hash_str(str, IDENT_HASH_SIZE); + return lex_get_ident_hashed(lex, str, hash); +} + +void lex_getline(struct lexer *lex) +{ +top: + cpp_read_line(&lex->cpp); + if (lex->cpp.line_buf){ + cpp_process_line(&lex->cpp); + if (lex->cpp.line_buf){ + lex->pch = lex->cpp.line_buf; + if (!*lex->pch){ + // blank line + goto top; + } + } else { + lex->pch = NULL; + } + } else { + lex->pch = NULL; + } +} + +void lex_start(struct lexer *lex) +{ + lex_getline(lex); + lex_next(lex); +} + +void lex_white(struct lexer *lex) +{ + lex->pch += strspn(lex->pch, " \t\n"); +} + +void lex_next(struct lexer *lex) +{ + if (!lex->pch || !*lex->pch){ + lex_getline(lex); + } + if (!lex->pch){ + lex->tok = 0; + lex->tok_sloc = lex->cpp.line_loc; + return; + } + lex_white(lex); + lex->tok_sloc = lex->cpp.line_loc; + if (isalpha(lex->pch[0]) || lex->pch[0] == '_'){ + // identifier or keyword + char *p_start = lex->pch, *id_str = NULL; + tok_t tok; + struct ident *ident; + while (isalnum(lex->pch[0]) || lex->pch[0] == '_'){ + lex->pch++; + } + strdncpy(&id_str, p_start, lex->pch - p_start); + tok = find_keyword(id_str); + if (tok != 0){ + lex->tok = tok; + } else { + // identifier + ident = lex_get_ident(lex, id_str); + lex->tok = ident->tok; + } + free(id_str); + } else if (lex->pch[0] == '"' || lex->pch[0] == '\''){ + char quote = lex->pch[0], **str_data = &lex->tok_str; + int *pstr_data_len = &lex->tok_str_len; + lex->pch++; + while (lex->pch[0] && lex->pch[0] != quote){ + strldcatc(str_data, pstr_data_len, lex->pch[0]); + lex->pch++; + } + if (lex->pch[0] == quote){ + lex->pch++; + } else { + lex_error(lex, "unterminated string literal"); + } + if (quote == '"'){ + lex->tok = TOK_STR; + } else { + lex->tok = TOK_CHARSTR; + } + } else { + // scan punctuation table + // HOT code! optimize! + int i, longest_match = 0, longest_match_len = 0, pch_len = strlen(lex->pch), punct_len; + for (i=TOK_FIRST_PUNCT+1; i pch_len || punct_len < longest_match_len){ + continue; + } + if (!strncmp(lex->pch, punctuation[i - (TOK_FIRST_PUNCT + 1)], punct_len)){ + assert(punct_len > longest_match_len); + longest_match = i; + longest_match_len = punct_len; + } + } + if (longest_match){ + lex->pch += longest_match_len; + lex->tok = longest_match; + } else // single-character token? + if (strchr("><=!-&|+*/%^.;:~(){}[],", lex->pch[0])){ + lex->tok = lex->pch[0]; + lex->pch++; + } else { + lex_error(lex, "invalid character in input file: %c", lex->pch[0]); + lex->tok = 0; + } + } +} + +// return a string for a token +// 'tok_str' may be null, but you won't get the contents of +// strings. The return value is a static string. Don't call lex_get_tok_str +// or lex_delete etc. until you've finished with the return value! +char *lex_get_tok_str(struct lexer *lex, tok_t tok, char *tok_str) +{ + static char buf[3]; + if (tok == 0){ + return ""; + } else if (tok <= 255){ + sprintf(buf, "%c", tok); + return buf; + } else if (tok > TOK_FIRSTK && tok < TOK_LASTK){ + return keywords[tok - (TOK_FIRSTK + 1)]; + } else if (tok > TOK_FIRST_PUNCT && tok < TOK_LAST_PUNCT){ + return punctuation[tok - (TOK_FIRST_PUNCT + 1)]; + } else if (tok >= TOK_IDENT){ + // this is difficult, because they're all in a hash table + // thankfully, we won't have to do this much + int i; + for (i=0; iident_hashtab[i]; + while (ident && ident->tok != tok){ + ident = ident->hash_prev; + } + if (ident){ + return ident->str; + } + } + return NULL; + } else { // TODO: strings and punctuation-like tokens + return NULL; + } +} + +bool lex_is_ident(struct lexer *lex, tok_t tok) +{ + return (tok >= TOK_IDENT && tok < lex->next_ident_tok); +} + +#if 0 +int main(int argc, char **argv) +{ + struct lexer lex; + lex_create(&lex); + cpp_include_file(&lex.cpp, "", stdin, false); + lex_start(&lex); + while (lex.tok){ + printf("%s ", lex_get_tok_str(&lex, lex.tok, lex.tok_str)); + lex_next(&lex); + } + + lex_delete(&lex); + return 0; +} +#endif + diff --git a/scanner.h b/scanner.h index baaaba6..df8a04c 100644 --- a/scanner.h +++ b/scanner.h @@ -8,20 +8,20 @@ typedef int tok_t; #define IDENT_HASH_SIZE 64 struct ident { - struct ident *hash_prev; - tok_t tok; - char str[1]; + struct ident *hash_prev; + tok_t tok; + char str[1]; }; struct lexer { - struct cpp cpp; - char *pch; - tok_t tok; - char *tok_str; - int tok_str_len; - struct sloc tok_sloc; - int next_ident_tok; - struct ident *ident_hashtab[IDENT_HASH_SIZE]; + struct cpp cpp; + char *pch; + tok_t tok; + char *tok_str; + int tok_str_len; + struct sloc tok_sloc; + int next_ident_tok; + struct ident *ident_hashtab[IDENT_HASH_SIZE]; }; void lex_create(struct lexer *lex); @@ -34,18 +34,18 @@ bool lex_is_ident(struct lexer *lex, tok_t tok); // Token numbers #include "tokens.inc" enum { - TOK_FIRSTK = 0x200, + TOK_FIRSTK = 0x200, #define DEF(x) TOK_ ## x, #include "tokens.inc" #undef DEF - TOK_LASTK, + TOK_LASTK, }; enum { - TOK_FIRST_PUNCT = 0x100, + TOK_FIRST_PUNCT = 0x100, #define PUNCT(x, str) TOK_ ## x, #include "tokens.inc" #undef PUNCT - TOK_LAST_PUNCT, + TOK_LAST_PUNCT, }; #endif diff --git a/stree.c b/stree.c dissimilarity index 79% index 5b2e4ed..343fa5f 100644 --- a/stree.c +++ b/stree.c @@ -1,265 +1,265 @@ -#include "stree.h" -#include "scanner.h" -#include "cc.h" -#include "errors.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "assert.h" -#include "bstr.h" - -static const char *form_strtab[] = { - "NONE", - "BINOP", - "UNOP", - "FACTOR", - "TAG", - "TYPE", - "FUNCTION", - "VARIABLE", - "PARAMETERS", -}; - -static int next_id = 1; - -const char *stree_form_str(int form) -{ - return form_strtab[form - STF_NONE]; -} - -struct stree *stree_create(void) -{ - struct stree *st; - st = emalloc(sizeof(struct stree)); - memset(st, 0, sizeof (struct stree)); - st->id = next_id++; - return st; -} - -void stree_append_child(struct stree *st_parent, struct stree *st_child) -{ - struct stree *st_child_prev = st_parent->child; - if (!st_parent || !st_child) - return; - st_child->parent = st_parent; - if (st_child_prev){ - // insert at end of child list - while (st_child_prev->next){ - st_child_prev = st_child_prev->next; - } - st_child_prev->next = st_child; - st_child->prev = st_child_prev; - st_child->next = NULL; - } else { - // first child - st_parent->child = st_child; - st_child->prev = NULL; - st_child->next = NULL; - } -} - -void stree_remove_child(struct stree *st_child) -{ - struct stree *st_parent = st_child->parent; - if (st_child->prev){ - st_child->prev->next = st_child->next; - } else { - st_parent->child = st_child->next; - } - if (st_child->next){ - st_child->next->prev = st_child->prev; - } - st_child->prev = NULL; - st_child->next = NULL; - st_child->parent = NULL; -} - -void stree_destroy(struct stree *st) -{ - if (st){ - // destroy children - struct stree *st_child = st->child, - *st_child_next; - - while (st_child){ - st_child_next = st_child->next; - stree_destroy(st_child); - st_child = st_child_next; - } - - free(st); - } -} - -void stree_next_child(struct stree *st, struct stree **pchild) -{ - if (*pchild == NULL){ - *pchild = st->child; - } else { - *pchild = (*pchild)->next; - } -} - -// form a string representing 'node', for debug output -static char *make_type_str(struct stree *node) -{ - char *s = NULL; - // is the type info valid? - switch (node->form){ - case STF_VARIABLE: - case STF_FUNCTION: - case STF_TYPE: - // XXX: erm, use arrays perhaps? - if (node->btype.qualifiers & TQ_RESTRICT) - strdcat(&s, "restrict "); - if (node->btype.qualifiers & TQ_VOLATILE) - strdcat(&s, "volatile "); - if (node->btype.qualifiers & TQ_CONST) - strdcat(&s, "const "); - if (node->btype.qualifiers & TQ_SIGNED) - strdcat(&s, "signed "); - if (node->btype.qualifiers & TQ_UNSIGNED) - strdcat(&s, "unsigned "); - if (node->btype.qualifiers & TQ_LONG_LONG) - strdcat(&s, "long long "); - if (node->btype.qualifiers & TQ_LONG) - strdcat(&s, "long "); - if (node->btype.qualifiers & TQ_SHORT) - strdcat(&s, "short "); - switch (node->btype.base_type){ - case BT_VOID: - strdcat(&s, "void"); - break; - case BT_CHAR: - strdcat(&s, "char"); - break; - case BT_INT: - strdcat(&s, "int"); - break; - case BT_FLOAT: - strdcat(&s, "float"); - break; - case BT_DOUBLE: - strdcat(&s, "double"); - break; - case BT_POINTER: - strdcat(&s, "pointer"); - // TODO: pointing to what? - break; - case BT_STRUCT: - strdcat(&s, "struct"); - // TODO: more informations! - break; - } - break; - default: - break; - } - return s; -} - -static void stree_dump_internal(struct cc *cc, struct stree_stack *stack, FILE *output) -{ - struct stree_stack stack_item; - { - // dump this node - struct stree_stack *p = stack; - assert(p); - // locate bottom of stack - while (p->prev){ - p = p->prev; - } - while (p && p->next){ - if (p->st_node->next){ - fprintf(output, " | "); - } else { - fprintf(output, " "); - } - p = p->next; - } - if (stack->st_node->next){ - fprintf(output, " +--"); - } else { - fprintf(output, " L--"); - } - { - char *type_str = make_type_str(stack->st_node); - fprintf(output, "%d:%s [%s]%s%s\n", stack->st_node->id, - stree_form_str(stack->st_node->form), - lex_get_tok_str(&cc->lex, stack->st_node->tok, NULL), - type_str ? " : " : "", - type_str ? type_str : ""); - free(type_str); - } - } - // and the children - stack_item.prev = stack; - stack_item.next = NULL; - stack->next = &stack_item; - stack_item.st_node = stack->st_node->child; - while (stack_item.st_node){ - stree_dump_internal(cc, &stack_item, output); - stack_item.st_node = stack_item.st_node->next; - } - stack->next = NULL; // pop stack -} - - -void stree_dump(struct cc *cc, struct stree *st_root, FILE *output) -{ - struct stree_stack stack_item; - if (!st_root){ - fprintf(output, "[TREE:NULL]\n"); - return; - } - stack_item.st_node = st_root; - stack_item.prev = NULL; - stack_item.next = NULL; - stree_dump_internal(cc, &stack_item, output); -} - -struct stree *stree_right(struct stree *st) -{ - struct stree *p = st->child; - while (p && p->next){ - p = p->next; - } - return p; -} - -int stree_child_count(struct stree *st) -{ - int n = 0; - struct stree *child_st; - if (!st){ - return 0; - } - child_st = st->child; - while (child_st){ - n++; - child_st = child_st->next; - } - return n; -} - -struct stree *stree_find_local(struct stree *scope, enum stree_form form, tok_t tok) -{ - if (!scope) - return NULL; - struct stree *p = scope->child; - while (p && !(p->form == form && p->tok == tok)){ - p = p->next; - } - return p; -} - -struct stree *stree_get_child_by_form(struct stree *st, enum stree_form form) -{ - struct stree *child_st; - child_st = st->child; - while (child_st && child_st->form != form){ - child_st = child_st->next; - } - return child_st; -} - +#include "stree.h" +#include "scanner.h" +#include "cc.h" +#include "errors.h" +#include "stdlib.h" +#include "stdio.h" +#include "string.h" +#include "assert.h" +#include "bstr.h" + +static const char *form_strtab[] = { + "NONE", + "BINOP", + "UNOP", + "FACTOR", + "TAG", + "TYPE", + "FUNCTION", + "VARIABLE", + "PARAMETERS", +}; + +static int next_id = 1; + +const char *stree_form_str(int form) +{ + return form_strtab[form - STF_NONE]; +} + +struct stree *stree_create(void) +{ + struct stree *st; + st = emalloc(sizeof(struct stree)); + memset(st, 0, sizeof (struct stree)); + st->id = next_id++; + return st; +} + +void stree_append_child(struct stree *st_parent, struct stree *st_child) +{ + struct stree *st_child_prev = st_parent->child; + if (!st_parent || !st_child) + return; + st_child->parent = st_parent; + if (st_child_prev){ + // insert at end of child list + while (st_child_prev->next){ + st_child_prev = st_child_prev->next; + } + st_child_prev->next = st_child; + st_child->prev = st_child_prev; + st_child->next = NULL; + } else { + // first child + st_parent->child = st_child; + st_child->prev = NULL; + st_child->next = NULL; + } +} + +void stree_remove_child(struct stree *st_child) +{ + struct stree *st_parent = st_child->parent; + if (st_child->prev){ + st_child->prev->next = st_child->next; + } else { + st_parent->child = st_child->next; + } + if (st_child->next){ + st_child->next->prev = st_child->prev; + } + st_child->prev = NULL; + st_child->next = NULL; + st_child->parent = NULL; +} + +void stree_destroy(struct stree *st) +{ + if (st){ + // destroy children + struct stree *st_child = st->child, + *st_child_next; + + while (st_child){ + st_child_next = st_child->next; + stree_destroy(st_child); + st_child = st_child_next; + } + + free(st); + } +} + +void stree_next_child(struct stree *st, struct stree **pchild) +{ + if (*pchild == NULL){ + *pchild = st->child; + } else { + *pchild = (*pchild)->next; + } +} + +// form a string representing 'node', for debug output +static char *make_type_str(struct stree *node) +{ + char *s = NULL; + // is the type info valid? + switch (node->form){ + case STF_VARIABLE: + case STF_FUNCTION: + case STF_TYPE: + // XXX: erm, use arrays perhaps? + if (node->btype.qualifiers & TQ_RESTRICT) + strdcat(&s, "restrict "); + if (node->btype.qualifiers & TQ_VOLATILE) + strdcat(&s, "volatile "); + if (node->btype.qualifiers & TQ_CONST) + strdcat(&s, "const "); + if (node->btype.qualifiers & TQ_SIGNED) + strdcat(&s, "signed "); + if (node->btype.qualifiers & TQ_UNSIGNED) + strdcat(&s, "unsigned "); + if (node->btype.qualifiers & TQ_LONG_LONG) + strdcat(&s, "long long "); + if (node->btype.qualifiers & TQ_LONG) + strdcat(&s, "long "); + if (node->btype.qualifiers & TQ_SHORT) + strdcat(&s, "short "); + switch (node->btype.base_type){ + case BT_VOID: + strdcat(&s, "void"); + break; + case BT_CHAR: + strdcat(&s, "char"); + break; + case BT_INT: + strdcat(&s, "int"); + break; + case BT_FLOAT: + strdcat(&s, "float"); + break; + case BT_DOUBLE: + strdcat(&s, "double"); + break; + case BT_POINTER: + strdcat(&s, "pointer"); + // TODO: pointing to what? + break; + case BT_STRUCT: + strdcat(&s, "struct"); + // TODO: more informations! + break; + } + break; + default: + break; + } + return s; +} + +static void stree_dump_internal(struct cc *cc, struct stree_stack *stack, FILE *output) +{ + struct stree_stack stack_item; + { + // dump this node + struct stree_stack *p = stack; + assert(p); + // locate bottom of stack + while (p->prev){ + p = p->prev; + } + while (p && p->next){ + if (p->st_node->next){ + fprintf(output, " | "); + } else { + fprintf(output, " "); + } + p = p->next; + } + if (stack->st_node->next){ + fprintf(output, " +--"); + } else { + fprintf(output, " L--"); + } + { + char *type_str = make_type_str(stack->st_node); + fprintf(output, "%d:%s [%s]%s%s\n", stack->st_node->id, + stree_form_str(stack->st_node->form), + lex_get_tok_str(&cc->lex, stack->st_node->tok, NULL), + type_str ? " : " : "", + type_str ? type_str : ""); + free(type_str); + } + } + // and the children + stack_item.prev = stack; + stack_item.next = NULL; + stack->next = &stack_item; + stack_item.st_node = stack->st_node->child; + while (stack_item.st_node){ + stree_dump_internal(cc, &stack_item, output); + stack_item.st_node = stack_item.st_node->next; + } + stack->next = NULL; // pop stack +} + + +void stree_dump(struct cc *cc, struct stree *st_root, FILE *output) +{ + struct stree_stack stack_item; + if (!st_root){ + fprintf(output, "[TREE:NULL]\n"); + return; + } + stack_item.st_node = st_root; + stack_item.prev = NULL; + stack_item.next = NULL; + stree_dump_internal(cc, &stack_item, output); +} + +struct stree *stree_right(struct stree *st) +{ + struct stree *p = st->child; + while (p && p->next){ + p = p->next; + } + return p; +} + +int stree_child_count(struct stree *st) +{ + int n = 0; + struct stree *child_st; + if (!st){ + return 0; + } + child_st = st->child; + while (child_st){ + n++; + child_st = child_st->next; + } + return n; +} + +struct stree *stree_find_local(struct stree *scope, enum stree_form form, tok_t tok) +{ + if (!scope) + return NULL; + struct stree *p = scope->child; + while (p && !(p->form == form && p->tok == tok)){ + p = p->next; + } + return p; +} + +struct stree *stree_get_child_by_form(struct stree *st, enum stree_form form) +{ + struct stree *child_st; + child_st = st->child; + while (child_st && child_st->form != form){ + child_st = child_st->next; + } + return child_st; +} + diff --git a/stree.h b/stree.h index c6772d9..759fc28 100644 --- a/stree.h +++ b/stree.h @@ -9,33 +9,33 @@ struct stree; #include "c_btypes.h" enum stree_form { - // stree node 'form's - STF_NONE, - STF_BINOP, - STF_UNOP, - STF_FACTOR, - STF_TAG, // tag (struct/union/enum) - STF_TYPE, // type information, NOT a typedef - STF_FUNCTION, - STF_VARIABLE, // variable, field or parameter - STF_PARAMETERS, // function parameter list - // WARNING: Add strings to form_strtab in stree.c - // when adding constants here! + // stree node 'form's + STF_NONE, + STF_BINOP, + STF_UNOP, + STF_FACTOR, + STF_TAG, // tag (struct/union/enum) + STF_TYPE, // type information, NOT a typedef + STF_FUNCTION, + STF_VARIABLE, // variable, field or parameter + STF_PARAMETERS, // function parameter list + // WARNING: Add strings to form_strtab in stree.c + // when adding constants here! }; struct stree { - struct stree *parent, *prev, *next, *child; - int id; // unique id for debugging - struct sloc sloc; // location of definition - enum stree_form form; - tok_t tok; - struct btype btype; + struct stree *parent, *prev, *next, *child; + int id; // unique id for debugging + struct sloc sloc; // location of definition + enum stree_form form; + tok_t tok; + struct btype btype; }; // used for recursion struct stree_stack { - struct stree *st_node; - struct stree_stack *prev, *next; + struct stree *st_node; + struct stree_stack *prev, *next; }; struct stree *stree_create(void); -- 2.11.4.GIT