# HG changeset patch # User David A. Holland # Date 1292835319 18000 # Node ID f9792a9ec7047898d56776be2337910f841793fa # Parent c08a947d8f30c9350e505581eaba17d8c43b16c2 macro expansion. diff -r c08a947d8f30 -r f9792a9ec704 macro.c --- a/macro.c Mon Dec 20 01:51:47 2010 -0500 +++ b/macro.c Mon Dec 20 03:55:19 2010 -0500 @@ -5,6 +5,17 @@ #include "mode.h" #include "place.h" #include "macro.h" +#include "output.h" + +struct expansionitem { + bool isstring; + union { + char *string; + unsigned param; + }; +}; +DECLARRAY(expansionitem); +DEFARRAY(expansionitem, ); struct macro { struct place defplace; @@ -13,7 +24,8 @@ char *name; bool hasparams; struct stringarray params; - char *expansion; + struct expansionitemarray expansion; + bool inuse; }; DECLARRAY(macro); DEFARRAY(macro, ); @@ -28,9 +40,75 @@ // macro structure ops static +struct expansionitem * +expansionitem_create_string(const char *string) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrdup(string); + return ei; +} + +static +struct expansionitem * +expansionitem_create_stringlen(const char *string, size_t len) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = true; + ei->string = dostrndup(string, len); + return ei; +} + +static +struct expansionitem * +expansionitem_create_param(unsigned param) +{ + struct expansionitem *ei; + + ei = domalloc(sizeof(*ei)); + ei->isstring = false; + ei->param = param; + return ei; +} + +static +void +expansionitem_destroy(struct expansionitem *ei) +{ + if (ei->isstring) { + free(ei->string); + } + free(ei); +} + +static +bool +expansionitem_eq(const struct expansionitem *ei1, + const struct expansionitem *ei2) +{ + if (ei1->isstring != ei2->isstring) { + return false; + } + if (ei1->isstring) { + if (strcmp(ei1->string, ei2->string) != 0) { + return false; + } + } else { + if (ei1->param != ei2->param) { + return false; + } + } + return true; +} + +static struct macro * macro_create(struct place *p1, const char *name, unsigned hash, - struct place *p2, const char *expansion) + struct place *p2) { struct macro *m; @@ -41,16 +119,20 @@ m->name = dostrdup(name); m->hasparams = false; stringarray_init(&m->params); - m->expansion = dostrdup(expansion); + expansionitemarray_init(&m->expansion); + m->inuse = false; return m; } +DESTROYALL_ARRAY(expansionitem, ); + static void macro_destroy(struct macro *m) { + expansionitemarray_destroyall(&m->expansion); + expansionitemarray_cleanup(&m->expansion); free(m->name); - free(m->expansion); free(m); } @@ -59,6 +141,7 @@ macro_eq(const struct macro *m1, const struct macro *m2) { unsigned num1, num2, i; + struct expansionitem *ei1, *ei2; const char *p1, *p2; if (strcmp(m1->name, m2->name) != 0) { @@ -69,10 +152,20 @@ return false; } - if (strcmp(m1->expansion, m2->expansion) != 0) { + num1 = expansionitemarray_num(&m1->expansion); + num2 = expansionitemarray_num(&m2->expansion); + if (num1 != num2) { return false; } + for (i=0; iexpansion, i); + ei2 = expansionitemarray_get(&m2->expansion, i); + if (!expansionitem_eq(ei1, ei2)) { + return false; + } + } + num1 = stringarray_num(&m1->params); num2 = stringarray_num(&m2->params); if (num1 != num2) { @@ -99,12 +192,10 @@ */ static unsigned -hashfunc(const char *s) +hashfunc(const char *s, size_t len) { uint16_t x1, x2, a; - size_t i, len; - - len = strlen(s); + size_t i; x1 = (uint16_t) (len >> 16); x2 = (uint16_t) (len); @@ -175,14 +266,15 @@ static struct macro * -macrotable_find(const char *name, bool remove) +macrotable_findlen(const char *name, size_t len, bool remove) { unsigned hash; struct macroarray *bucket; struct macro *m, *m2; unsigned i, num; + size_t mlen; - hash = hashfunc(name); + hash = hashfunc(name, len); bucket = macroarrayarray_get(¯os, hash & hashmask); if (bucket == NULL) { return NULL; @@ -193,7 +285,8 @@ if (hash != m->hash) { continue; } - if (!strcmp(name, m->name)) { + mlen = strlen(m->name); + if (len == mlen && !memcmp(name, m->name, len)) { if (remove) { if (i < num-1) { m2 = macroarray_get(bucket, num-1); @@ -209,6 +302,13 @@ } static +struct macro * +macrotable_find(const char *name, bool remove) +{ + return macrotable_findlen(name, strlen(name), remove); +} + +static void macrotable_rehash(void) { @@ -264,7 +364,7 @@ macrotable_rehash(); } - hash = hashfunc(m->name); + hash = hashfunc(m->name, strlen(m->name)); bucket = macroarrayarray_get(¯os, hash & hashmask); if (bucket == NULL) { bucket = macroarray_create(); @@ -280,16 +380,18 @@ static struct macro * macro_define_common_start(struct place *p1, const char *macro, - struct place *p2, const char *expansion) + struct place *p2) { struct macro *m; + unsigned hash; if (!is_identifier(macro)) { complain(p1, "Invalid macro name %s", macro); complain_fail(); } - m = macro_create(p1, macro, hashfunc(macro), p2, expansion); + hash = hashfunc(macro, strlen(macro)); + m = macro_create(p1, macro, hash, p2); return m; } @@ -355,13 +457,72 @@ } } +static +bool +isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) +{ + unsigned num, i; + const char *param; + + num = stringarray_num(&m->params); + for (i=0; iparams, i); + if (strlen(param) == len && !strcmp(name, param)) { + *num_ret = i; + return true; + } + } + return false; +} + +static +void +macro_parse_expansion(struct macro *m, const char *buf) +{ + size_t blockstart, wordstart, pos; + struct expansionitem *ei; + unsigned param; + + pos = blockstart = 0; + while (buf[pos] != '\0') { + pos += strspn(buf+pos, ws); + if (strchr(alnum, buf[pos])) { + wordstart = pos; + pos += strspn(buf+pos, alnum); + if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { + if (pos > blockstart) { + ei = expansionitem_create_stringlen( + buf + blockstart, + blockstart - pos); + expansionitemarray_add(&m->expansion, + ei, NULL); + } + ei = expansionitem_create_param(param); + expansionitemarray_add(&m->expansion, ei,NULL); + blockstart = pos; + continue; + } + continue; + } + pos++; + } + if (pos > blockstart) { + ei = expansionitem_create_stringlen(buf + blockstart, + blockstart - pos); + expansionitemarray_add(&m->expansion, ei, NULL); + } +} + void macro_define_plain(struct place *p1, const char *macro, struct place *p2, const char *expansion) { struct macro *m; + struct expansionitem *ei; - m = macro_define_common_start(p1, macro, p2, expansion); + m = macro_define_common_start(p1, macro, p2); + ei = expansionitem_create_string(expansion); + expansionitemarray_add(&m->expansion, ei, NULL); macro_define_common_end(m); } @@ -372,8 +533,9 @@ { struct macro *m; - m = macro_define_common_start(p1, macro, p3, expansion); + m = macro_define_common_start(p1, macro, p3); macro_parse_parameters(m, p2, params); + macro_parse_expansion(m, expansion); macro_define_common_end(m); } @@ -400,10 +562,494 @@ //////////////////////////////////////////////////////////// // macro expansion -char *macroexpand(struct place *, char *buf, size_t len, bool honordefined); +struct expstate { + bool honordefined; + enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; + struct macro *curmacro; + struct stringarray args; + unsigned argparens; + + bool tobuf; + char *buf; + size_t bufpos, bufmax; +}; + +static struct expstate mainstate; + +static void doexpand(struct expstate *es, struct place *p, + char *buf, size_t len); + +static +void +expstate_init(struct expstate *es, bool tobuf, bool honordefined) +{ + es->honordefined = honordefined; + es->state = ES_NORMAL; + es->curmacro = NULL; + stringarray_init(&es->args); + es->argparens = 0; + es->tobuf = tobuf; + es->buf = NULL; + es->bufpos = 0; + es->bufmax = 0; +} + +static +void +expstate_cleanup(struct expstate *es) +{ + assert(es->state == ES_NORMAL); + stringarray_cleanup(&es->args); + if (es->buf) { + free(es->buf); + } +} + +static +void +expstate_destroyargs(struct expstate *es) +{ + unsigned i, num; + + num = stringarray_num(&es->args); + for (i=0; iargs, i)); + } + stringarray_setsize(&es->args, 0); +} + +static +void +expand_send(struct expstate *es, const char *buf, size_t len) +{ + if (es->tobuf) { + if (es->bufpos + len > es->bufmax) { + if (es->bufmax == 0) { + es->bufmax = 64; + } + while (es->bufpos + len > es->bufmax) { + es->bufmax *= 2; + } + es->buf = dorealloc(es->buf, es->bufmax); + } + memcpy(es->buf + es->bufpos, buf, len); + es->bufpos += len; + } else { + output(buf, len); + } +} + +static +void +expand_send_eof(struct expstate *es) +{ + if (es->tobuf) { + expand_send(es, "", 1); + es->bufpos--; + } else { + output_eof(); + } +} + +static +void +expand_newarg(struct expstate *es, char *buf, size_t len) +{ + char *text; + + text = dostrndup(buf, len); + stringarray_add(&es->args, text, NULL); +} + +static +void +expand_appendarg(struct expstate *es, char *buf, size_t len) +{ + unsigned num; + char *text; + size_t oldlen; + + num = stringarray_num(&es->args); + assert(num > 0); + + text = stringarray_get(&es->args, num - 1); + oldlen = strlen(text); + text = dorealloc(text, oldlen + len + 1); + memcpy(text + oldlen, buf, len); + text[oldlen+len] = '\0'; + stringarray_set(&es->args, num - 1, text); +} + +static +char * +expand_substitute(struct expstate *es) +{ + struct expansionitem *ei; + unsigned i, num; + size_t len; + char *arg; + char *ret; + + len = 0; + num = expansionitemarray_num(&es->curmacro->expansion); + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + len += strlen(ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + len += strlen(arg); + } + } + + ret = domalloc(len+1); + *ret = '\0'; + for (i=0; icurmacro->expansion, i); + if (ei->isstring) { + strcat(ret, ei->string); + } else { + arg = stringarray_get(&es->args, ei->param); + strcat(ret, arg); + } + } + + return ret; +} + +static +void +expand_domacro(struct expstate *es, struct place *p) +{ + struct macro *m; + char *newbuf, *newbuf2; + + if (es->curmacro == NULL) { + /* defined() */ + if (stringarray_num(&es->args) != 1) { + complain(p, "Too many arguments for defined()"); + complain_fail(); + expand_send(es, "0", 1); + return; + } + m = macrotable_find(stringarray_get(&es->args, 0), false); + expand_send(es, (m != NULL) ? "1" : "0", 1); + return; + } + + assert(es->curmacro->inuse == false); + es->curmacro->inuse = true; + + newbuf = expand_substitute(es); + newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false); + free(newbuf); + doexpand(es, p, newbuf2, strlen(newbuf2)); + free(newbuf2); + + es->curmacro->inuse = false; +} + +static +void +expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, buf, len); + break; + break; + case ES_WANTLPAREN: + break; + case ES_NOARG: + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) +{ + struct macro *m; + struct expansionitem *ei; + char *newbuf; -void macro_sendline(struct place *, char *buf, size_t len); -void macro_sendeof(struct place *); + switch (es->state) { + case ES_NORMAL: + if (es->honordefined && + len == 7 && !memcmp(buf, "defined", 7)) { + es->curmacro = NULL; + es->state = ES_WANTLPAREN; + break; + } + m = macrotable_findlen(buf, len, false); + if (m == NULL) { + expand_send(es, buf, len); + } else if (!m->hasparams) { + m->inuse = true; + assert(expansionitemarray_num(&m->expansion) == 1); + ei = expansionitemarray_get(&m->expansion, 0); + assert(ei->isstring); + newbuf = macroexpand(p, ei->string, + strlen(ei->string), false); + doexpand(es, p, newbuf, strlen(newbuf)); + free(newbuf); + m->inuse = false; + } else { + es->curmacro = m; + es->state = ES_WANTLPAREN; + } + break; + case ES_WANTLPAREN: + if (es->curmacro != NULL) { + complain(p, "Expected arguments for macro %s", + es->curmacro->name); + complain_fail(); + } else { + /* "defined foo" means "defined(foo)" */ + expand_newarg(es, buf, len); + es->state = ES_NORMAL; + expand_domacro(es, p); + break; + } + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, buf, len); + break; + case ES_WANTLPAREN: + es->state = ES_NOARG; + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + es->argparens++; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + es->argparens++; + break; + } +} + +static +void +expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, buf, len); + break; + case ES_WANTLPAREN: + if (es->curmacro) { + complain(p, "Expected arguments for macro %s", + es->curmacro->name); + } else { + complain(p, "Expected arguments for defined()"); + } + complain_fail(); + break; + case ES_NOARG: + assert(es->argparens == 0); + es->state = ES_NORMAL; + expand_domacro(es, p); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + es->argparens--; + expand_appendarg(es, buf, len); + } else { + es->state = ES_NORMAL; + expand_domacro(es, p); + } + break; + } +} + +static +void +expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, buf, len); + break; + case ES_WANTLPAREN: + if (es->curmacro) { + complain(p, "Expected arguments for macro %s", + es->curmacro->name); + } else { + complain(p, "Expected arguments for defined()"); + } + complain_fail(); + break; + case ES_NOARG: + assert(es->argparens == 0); + expand_newarg(es, buf, 0); + break; + case ES_HAVEARG: + if (es->argparens > 0) { + expand_appendarg(es, buf, len); + } else { + es->state = ES_NOARG; + } + break; + } +} + +static +void +expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) +{ + switch (es->state) { + case ES_NORMAL: + expand_send(es, buf, len); + break; + case ES_WANTLPAREN: + if (es->curmacro) { + complain(p, "Expected arguments for macro %s", + es->curmacro->name); + } else { + complain(p, "Expected arguments for defined()"); + } + complain_fail(); + break; + case ES_NOARG: + expand_newarg(es, buf, len); + es->state = ES_HAVEARG; + break; + case ES_HAVEARG: + expand_appendarg(es, buf, len); + break; + } +} + +static +void +expand_got_eof(struct expstate *es, struct place *p) +{ + switch (es->state) { + case ES_NORMAL: + expand_send_eof(es); + break; + case ES_WANTLPAREN: + if (es->curmacro) { + complain(p, "Expected arguments for macro %s", + es->curmacro->name); + } else { + complain(p, "Expected arguments for defined()"); + } + complain_fail(); + break; + case ES_NOARG: + case ES_HAVEARG: + if (es->curmacro) { + complain(p, "Unclosed argument list for macro %s", + es->curmacro->name); + } else { + complain(p, "Unclosed argument list for defined()"); + } + complain_fail(); + expstate_destroyargs(es); + break; + } + es->state = ES_NORMAL; + es->curmacro = NULL; + es->argparens = 0; +} + +static +void +doexpand(struct expstate *es, struct place *p, char *buf, size_t len) +{ + size_t x; + + while (len > 0) { + x = strspn(buf, ws); + if (x > 0) { + expand_got_ws(es, p, buf, x); + buf += x; + len -= x; + } + + x = strspn(buf, alnum); + if (x > 0) { + expand_got_word(es, p, buf, x); + buf += x; + len -= x; + continue; + } + + if (buf[0] == '(') { + expand_got_lparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (buf[0] == ')') { + expand_got_rparen(es, p, buf, 1); + buf++; + len--; + continue; + } + + if (buf[0] == ',') { + expand_got_comma(es, p, buf, 1); + buf++; + len--; + continue; + } + + expand_got_other(es, p, buf, 1); + buf++; + len--; + } +} + +char * +macroexpand(struct place *p, char *buf, size_t len, bool honordefined) +{ + struct expstate es; + char *ret; + + expstate_init(&es, true, honordefined); + doexpand(&es, p, buf, len); + expand_got_eof(&es, p); + ret = es.buf; + es.buf = NULL; + expstate_cleanup(&es); + + return ret; +} + +void +macro_sendline(struct place *p, char *buf, size_t len) +{ + doexpand(&mainstate, p, buf, len); +} + +void +macro_sendeof(struct place *p) +{ + expand_got_eof(&mainstate, p); +} //////////////////////////////////////////////////////////// // module initialization @@ -412,10 +1058,12 @@ macros_init(void) { macrotable_init(); + expstate_init(&mainstate, false, false); } void macros_cleanup(void) { + expstate_cleanup(&mainstate); macrotable_cleanup(); } diff -r c08a947d8f30 -r f9792a9ec704 output.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/output.h Mon Dec 20 03:55:19 2010 -0500 @@ -0,0 +1,2 @@ +void output(const char *buf, size_t len); +void output_eof(void);