Mercurial > ~dholland > hg > tradcpp > index.cgi
changeset 15:f6177d3ed5c2
handle directives
author | David A. Holland |
---|---|
date | Sun, 19 Dec 2010 21:42:01 -0500 |
parents | 5045b9678bb0 |
children | 9dda765ee85c |
files | Makefile directive.c directive.h eval.h files.c macro.h main.c |
diffstat | 7 files changed, 596 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sun Dec 19 19:51:36 2010 -0500 +++ b/Makefile Sun Dec 19 21:42:01 2010 -0500 @@ -1,7 +1,7 @@ # $NetBSD$ PROG= tradcpp -SRCS= main.c files.c place.c array.c utils.c +SRCS= main.c files.c directive.c place.c array.c utils.c WARNS= 5 .include <bsd.prog.mk>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/directive.c Sun Dec 19 21:42:01 2010 -0500 @@ -0,0 +1,423 @@ +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "utils.h" +#include "mode.h" +#include "place.h" +#include "files.h" +#include "directive.h" +#include "macro.h" +#include "eval.h" + +static const char ws[] = " \t\f\v"; + +struct ifstate { + struct ifstate *prev; + struct place startplace; + bool curtrue; + bool evertrue; + bool seenelse; +}; + +static struct ifstate *ifstate; + +//////////////////////////////////////////////////////////// +// common parsing bits + +static +void +oneword(const char *what, struct place *p2, char *line) +{ + size_t pos; + + pos = strcspn(line, ws); + if (line[pos] != '\0') { + p2->column += pos; + complain(p2, "Garbage after %s argument", what); + complain_fail(); + line[pos] = '\0'; + } +} + +//////////////////////////////////////////////////////////// +// if handling + +static +struct ifstate * +ifstate_create(struct ifstate *prev, struct place *p, bool startstate) +{ + struct ifstate *is; + + is = domalloc(sizeof(*is)); + is->prev = prev; + if (p != NULL) { + is->startplace = *p; + } else { + place_setbuiltin(&is->startplace, 1); + } + is->curtrue = startstate; + is->evertrue = is->curtrue; + is->seenelse = false; + return is; +} + +static +void +ifstate_destroy(struct ifstate *is) +{ + free(is); +} + +static +void +ifstate_push(struct place *p, bool startstate) +{ + ifstate = ifstate_create(ifstate, p, startstate); +} + +static +void +ifstate_pop(void) +{ + struct ifstate *is; + + is = ifstate; + ifstate = ifstate->prev; + ifstate_destroy(is); +} + +static +void +d_if(struct place *p, struct place *p2, char *line, size_t len) +{ + char *expr; + bool val; + + expr = macroexpand(p2, line, len, true); + val = eval(expr); + ifstate_push(p, val); + free(expr); +} + +static +void +d_ifdef(struct place *p, struct place *p2, char *line, size_t len) +{ + oneword("#ifdef", p2, line); + ifstate_push(p, macro_isdefined(line)); +} + +static +void +d_ifndef(struct place *p, struct place *p2, char *line, size_t len) +{ + oneword("#ifndef", p2, line); + ifstate_push(p, !macro_isdefined(line)); +} + +static +void +d_elif(struct place *p, struct place *p2, char *line, size_t len) +{ + char *expr; + + if (ifstate->seenelse) { + complain(p, "#elif after #else"); + complain_fail(); + } + + if (ifstate->evertrue) { + ifstate->curtrue = false; + } else { + expr = macroexpand(p2, line, len, true); + ifstate->curtrue = eval(expr); + ifstate->evertrue = ifstate->curtrue; + free(expr); + } +} + +static +void +d_else(struct place *p, struct place *p2, char *line, size_t len) +{ + if (ifstate->seenelse) { + complain(p, "Multiple #else directives in one conditional"); + complain_fail(); + } + + ifstate->curtrue = !ifstate->evertrue; + ifstate->evertrue = true; + ifstate->seenelse = true; +} + +static +void +d_endif(struct place *p, struct place *p2, char *line, size_t len) +{ + if (ifstate->prev == NULL) { + complain(p, "Unmatched #endif"); + complain_fail(); + } else { + ifstate_pop(); + } +} + +//////////////////////////////////////////////////////////// +// macros + +static +void +d_define(struct place *p, struct place *p2, char *line, size_t len) +{ + size_t pos; + struct place p3; + + /* + * line may be: + * macro expansion + * macro(arg, arg, ...) expansion + */ + + pos = strcspn(line, " \t\f\v("); + if (line[pos] == '(') { + pos++; + pos = pos + strcspn(line+pos, "()"); + if (line[pos] == '(') { + p2->column += pos; + complain(p2, "Left parenthesis in macro parameters"); + complain_fail(); + return; + } + if (line[pos] != ')') { + p2->column += pos; + complain(p2, "Unclosed macro parameter list"); + complain_fail(); + return; + } + pos++; + if (!strchr(ws, line[pos])) { + p2->column += pos; + complain(p2, "Trash after macro parameter list"); + complain_fail(); + return; + } + line[pos++] = '\0'; + } else if (line[pos] == '\0') { + /* nothing */ + } else { + line[pos++] = '\0'; + } + + pos += strspn(line+pos, ws); + + p3 = *p2; + p3.column += pos; + macro_define(p2, line, &p3, line + pos); +} + +static +void +d_undef(struct place *p, struct place *p2, char *line, size_t len) +{ + oneword("#undef", p2, line); + macro_undef(line); +} + +//////////////////////////////////////////////////////////// +// includes + +static +bool +tryinclude(struct place *p, char *line, size_t len) +{ + if (len > 2 && line[0] == '"' && line[len-1] == '"') { + line[len-1] = '\0'; + file_readquote(p, line+1); + return true; + } + if (len > 2 && line[0] == '<' && line[len-1] == '>') { + line[len-1] = '\0'; + file_readbracket(p, line+1); + return true; + } + return false; +} + +static +void +d_include(struct place *p, struct place *p2, char *line, size_t len) +{ + char *text; + + if (tryinclude(p, line, len)) { + return; + } + text = macroexpand(p2, line, len, false); + if (tryinclude(p, text, strlen(text))) { + free(text); + return; + } + free(text); + complain(p, "Illegal #include directive"); + complain_fail(); +} + +static +void +d_line(struct place *p, struct place *p2, char *line, size_t len) +{ + /* XXX */ + complain(p, "Sorry, no #line yet"); +} + +//////////////////////////////////////////////////////////// +// messages + +static +void +d_warning(struct place *p, struct place *p2, char *line, size_t len) +{ + char *msg; + + msg = macroexpand(p2, line, len, false); + complain(p, "#warning: %s", msg); + if (mode.werror) { + complain_fail(); + } + free(msg); +} + +static +void +d_error(struct place *p, struct place *p2, char *line, size_t len) +{ + char *msg; + + msg = macroexpand(p2, line, len, false); + complain(p, "#error: %s", msg); + complain_fail(); + free(msg); +} + +//////////////////////////////////////////////////////////// +// other + +static +void +d_pragma(struct place *p, struct place *p2, char *line, size_t len) +{ + complain(p, "#pragma %s", line); + complain_fail(); +} + +//////////////////////////////////////////////////////////// +// directive table + +static const struct { + const char *name; + bool ifskip; + void (*func)(struct place *, struct place *, char *line, size_t len); +} directives[] = { + { "define", true, d_define }, + { "elif", false, d_elif }, + { "else", false, d_else }, + { "endif", false, d_endif }, + { "error", true, d_error }, + { "if", false, d_if }, + { "ifdef", false, d_ifdef }, + { "ifndef", false, d_ifndef }, + { "include", true, d_include }, + { "line", true, d_line }, + { "pragma", true, d_pragma }, + { "undef", true, d_undef }, + { "warning", true, d_warning }, +}; +static const unsigned numdirectives = HOWMANY(directives); + +static +size_t +notrailingws(char *buf, size_t len) +{ + while (len > 0 && strchr(ws, buf[len-1])) { + buf[--len] = '\0'; + } + return len; +} + +static +void +directive_gotdirective(struct place *p, char *line, size_t linelen) +{ + struct place p2; + size_t len, skip; + unsigned i; + + p2 = *p; + for (i=0; i<numdirectives; i++) { + len = strlen(directives[i].name); + if (!strncmp(line, directives[i].name, len) && + strchr(ws, line[len])) { + if (directives[i].ifskip && !ifstate->curtrue) { + return; + } + skip = len + strspn(line+len, ws); + p2.column += skip; + line += skip; + linelen -= skip; + linelen = notrailingws(line, linelen); + directives[i].func(p, &p2, line, linelen); + return; + } + } + skip = strcspn(line, ws); + complain(p, "Unknown directive #%.*s", (int)skip, line); + complain_fail(); +} + +void +directive_gotline(struct place *p, char *line, size_t len) +{ + size_t skip; + + /* check if we have a directive line */ + skip = strspn(line, ws); + if (line[skip] == '#') { + skip = skip + 1 + strspn(line + skip + 1, ws); + p->column += skip; + directive_gotdirective(p, line+skip, len-skip); + } else if (ifstate->curtrue) { + macro_sendline(p, line, len); + } +} + + +void +directive_goteof(struct place *p) +{ + while (ifstate->prev != NULL) { + complain(p, "Missing #endif"); + complain(&ifstate->startplace, "...opened at this point"); + complain_failed(); + ifstate_pop(); + } + macro_sendeof(p); +} + +//////////////////////////////////////////////////////////// +// module initialization + +void +directive_init(void) +{ + ifstate = ifstate_create(NULL, NULL, true); +} + +void +directive_cleanup(void) +{ + assert(ifstate->prev == NULL); + ifstate_destroy(ifstate); + ifstate = NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/directive.h Sun Dec 19 21:42:01 2010 -0500 @@ -0,0 +1,10 @@ +#include <stddef.h> + +struct place; + +void directive_init(void); +void directive_cleanup(void); + +void directive_gotline(struct place *p, char *line, size_t len); +void directive_goteof(struct place *p); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eval.h Sun Dec 19 21:42:01 2010 -0500 @@ -0,0 +1,1 @@ +bool eval(char *expr);
--- a/files.c Sun Dec 19 19:51:36 2010 -0500 +++ b/files.c Sun Dec 19 21:42:01 2010 -0500 @@ -7,8 +7,10 @@ #include <err.h> #include "array.h" +#include "mode.h" #include "place.h" #include "files.h" +#include "directive.h" struct incdir { const char *name; @@ -84,8 +86,133 @@ //////////////////////////////////////////////////////////// // parsing +static +size_t +findnl(const char *buf, size_t start, size_t limit) +{ + size_t i; + + for (i=start; i<limit; i++) { + if (buf[i] == '\n') { + return i; + } + } + return limit; +} + +static void -file_read(const struct placefile *pf, int fd); +file_read(const struct placefile *pf, int fd, const char *name) +{ + struct place linestartplace, nextlinestartplace, ptmp; + size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp; + ssize_t result; + bool ateof = false; + char *buf; + + place_setfilestart(&linestartplace, pf); + nextlinestartplace = linestartplace; + + bufmax = 128; + bufend = 0; + linestart = 0; + lineend = 0; + buf = domalloc(bufmax); + + while (1) { + if (lineend >= bufend) { + /* do not have a whole line in the buffer; read more */ + if (linestart > 0 && bufend > linestart) { + /* slide to beginning of buffer */ + memmove(buf, buf+linestart, bufend-linestart); + bufend -= linestart; + lineend -= linestart; + linestart = 0; + } + if (bufend >= bufmax) { + /* need bigger buffer */ + bufmax *= 2; + buf = dorealloc(buf, bufmax); + } + + if (ateof) { + /* don't read again, in case it's a socket */ + result = 0; + } else { + result = read(fd, buf+bufend, bufmax - bufend); + } + + if (result == -1) { + /* read error */ + warn("%s", name); + complain_fail(); + } else if (result == 0 && bufend == linestart) { + /* eof */ + ateof = true; + break; + } else if (result == 0) { + /* eof in middle of line */ + ateof = true; + ptmp = linestartplace; + ptmp.column += bufend - linestart; + complain(&ptmp, "No newline at end of file"); + if (mode.werror) { + complain_fail(); + } + assert(bufend < bufmax); + lineend = bufend++; + buf[lineend] = '\n'; + } else { + tmp = bufend; + bufend += (size_t)result; + lineend = findnl(buf, tmp, bufend); + } + /* loop in case we still don't have a whole line */ + continue; + } + + /* have a line */ + assert(buf[lineend] == '\n'); + buf[lineend] = '\0'; + nextlinestart = lineend+1; + nextlinestartplace.line++; + + /* check for CR/NL */ + if (lineend > 0 && buf[lineend-1] == '\r') { + buf[lineend-1] = '\0'; + lineend--; + } + + /* check for continuation line */ + if (lineend > 0 && buf[lineend-1]=='\\') { + lineend--; + tmp = nextlinestart - lineend; + if (bufend > nextlinestart) { + memmove(buf+lineend, buf+nextlinestart, + bufend - nextlinestart); + } + bufend -= tmp; + nextlinestart -= tmp; + lineend = findnl(buf, lineend, bufend); + /* might not have a whole line, so loop */ + continue; + } + + /* line now goes from linestart to lineend */ + assert(buf[lineend] == '\0'); + if (lineend > linestart) { + directive_gotline(&linestartplace, + buf+linestart, lineend-linestart); + } + + linestart = nextlinestart; + lineend = findnl(buf, linestart, bufend); + linestartplace = nextlinestartplace; + } + + directive_goteof(&linestartplace); + free(buf); +} //////////////////////////////////////////////////////////// // path search @@ -120,11 +247,13 @@ { int fd; + /* XXX check for non-regular files */ + fd = open(file, O_RDONLY); if (fd < 0) { return -1; } - /* XXX: do we need to do anything here or is this function pointless?*/ + return fd; } @@ -147,8 +276,8 @@ fd = file_tryopen(file); if (fd >= 0) { pf = place_addfile(place, file, id->issystem); + file_read(pf, fd, file); free(file); - file_read(pf, fd); close(fd); return; } @@ -184,6 +313,6 @@ die(); } pf = place_addfile(place, name, false); - file_read(pf, fd); + file_read(pf, fd, name); close(fd); }
--- a/macro.h Sun Dec 19 19:51:36 2010 -0500 +++ b/macro.h Sun Dec 19 21:42:01 2010 -0500 @@ -1,4 +1,11 @@ struct place; -void macro_define(struct place *, const char *macro, const char *expansion); +void macro_define(struct place *, const char *macro, + struct place *, const char *expansion); void macro_undef(const char *macro); +bool macro_isdefined(const char *macro); + +char *macroexpand(struct place *, char *buf, size_t len, bool honordefined); + +void macro_sendline(struct place *, char *buf, size_t len); +void macro_sendeof(struct place *);
--- a/main.c Sun Dec 19 19:51:36 2010 -0500 +++ b/main.c Sun Dec 19 21:42:01 2010 -0500 @@ -12,6 +12,7 @@ #include "mode.h" #include "place.h" #include "files.h" +#include "directive.h" #include "macro.h" struct mode mode = { @@ -57,6 +58,7 @@ struct commandline_macro { struct place where; + struct place where2; const char *macro; const char *expansion; }; @@ -79,13 +81,14 @@ static void -commandline_macro_add(const struct place *p, - const char *macro, const char *expansion) +commandline_macro_add(const struct place *p, const char *macro, + const struct place *p2, const char *expansion) { struct commandline_macro *cm; cm = domalloc(sizeof(*cm)); cm->where = *p; + cm->where2 = *p2; cm->macro = macro; cm->expansion = expansion; } @@ -94,6 +97,7 @@ void commandline_def(const struct place *p, char *str) { + struct place p2; char *val; val = strchr(str, '='); @@ -101,14 +105,21 @@ *val = '\0'; val++; } - commandline_macro_add(p, str, val ? val : "1"); + + if (val) { + p2 = *p; + p2.column += strlen(str); + } else { + place_setbuiltin(&p2, 1); + } + commandline_macro_add(p, str, &p2, val ? val : "1"); } static void commandline_undef(const struct place *p, char *str) { - commandline_macro_add(p, str, NULL); + commandline_macro_add(p, str, p, NULL); } static @@ -122,7 +133,8 @@ for (i=0; i<num; i++) { cm = array_get(&commandline_macros, i); if (cm->expansion != NULL) { - macro_define(&cm->where, cm->macro, cm->expansion); + macro_define(&cm->where, cm->macro, + &cm->where2, cm->expansion); } else { macro_undef(cm->macro); } @@ -138,7 +150,7 @@ struct place p; place_setbuiltin(&p, num); - macro_define(&p, name, val); + macro_define(&p, name, &p, val); } static @@ -859,6 +871,7 @@ place_init(); files_init(); + directive_init(); } static @@ -867,6 +880,7 @@ { unsigned i, num; + directive_cleanup(); files_cleanup(); place_cleanup();