Mercurial > projects > ldc
diff dmd2/macro.c @ 758:f04dde6e882c
Added initial D2 support, D2 frontend and changes to codegen to make things compile.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 01:38:48 +0100 |
parents | |
children | 638d16625da2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmd2/macro.c Tue Nov 11 01:38:48 2008 +0100 @@ -0,0 +1,459 @@ + +// Copyright (c) 1999-2006 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +/* Simple macro text processor. + */ + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <ctype.h> +#include <assert.h> + +#if IN_GCC || IN_LLVM +#include "mem.h" +#else +#if _WIN32 +#include "..\root\mem.h" +#elif POSIX +#include "../root/mem.h" +#else +#error "fix this" +#endif +#endif + +#include "root.h" +#include "macro.h" + +#define isidstart(c) (isalpha(c) || (c) == '_') +#define isidchar(c) (isalnum(c) || (c) == '_') + +unsigned char *memdup(unsigned char *p, size_t len) +{ + return (unsigned char *)memcpy(mem.malloc(len), p, len); +} + +Macro::Macro(unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) +{ + next = NULL; + +#if 1 + this->name = name; + this->namelen = namelen; + + this->text = text; + this->textlen = textlen; +#else + this->name = name; + this->namelen = namelen; + + this->text = text; + this->textlen = textlen; +#endif + inuse = 0; +} + + +Macro *Macro::search(unsigned char *name, size_t namelen) +{ Macro *table; + + //printf("Macro::search(%.*s)\n", namelen, name); + for (table = this; table; table = table->next) + { + if (table->namelen == namelen && + memcmp(table->name, name, namelen) == 0) + { + //printf("\tfound %d\n", table->textlen); + break; + } + } + return table; +} + +Macro *Macro::define(Macro **ptable, unsigned char *name, size_t namelen, unsigned char *text, size_t textlen) +{ + //printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text); + + Macro *table; + + //assert(ptable); + for (table = *ptable; table; table = table->next) + { + if (table->namelen == namelen && + memcmp(table->name, name, namelen) == 0) + { + table->text = text; + table->textlen = textlen; + return table; + } + } + table = new Macro(name, namelen, text, textlen); + table->next = *ptable; + *ptable = table; + return table; +} + +/********************************************************** + * Given buffer p[0..end], extract argument marg[0..marglen]. + * Params: + * n 0: get entire argument + * 1..9: get nth argument + * -1: get 2nd through end + */ + +unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsigned *pmarglen, int n) +{ + /* Scan forward for matching right parenthesis. + * Nest parentheses. + * Skip over $( and $) + * Skip over "..." and '...' strings inside HTML tags. + * Skip over <!-- ... --> comments. + * Skip over previous macro insertions + * Set marglen. + */ + unsigned parens = 1; + unsigned char instring = 0; + unsigned incomment = 0; + unsigned intag = 0; + unsigned inexp = 0; + unsigned argn = 0; + + unsigned v = 0; + + Largstart: +#if 1 + // Skip first space, if any, to find the start of the macro argument + if (v < end && isspace(p[v])) + v++; +#else + // Skip past spaces to find the start of the macro argument + for (; v < end && isspace(p[v]); v++) + ; +#endif + *pmarg = p + v; + + for (; v < end; v++) + { unsigned char c = p[v]; + + switch (c) + { + case ',': + if (!inexp && !instring && !incomment && parens == 1) + { + argn++; + if (argn == 1 && n == -1) + { v++; + goto Largstart; + } + if (argn == n) + break; + if (argn + 1 == n) + { v++; + goto Largstart; + } + } + continue; + + case '(': + if (!inexp && !instring && !incomment) + parens++; + continue; + + case ')': + if (!inexp && !instring && !incomment && --parens == 0) + { + break; + } + continue; + + case '"': + case '\'': + if (!inexp && !incomment && intag) + { + if (c == instring) + instring = 0; + else if (!instring) + instring = c; + } + continue; + + case '<': + if (!inexp && !instring && !incomment) + { + if (v + 6 < end && + p[v + 1] == '!' && + p[v + 2] == '-' && + p[v + 3] == '-') + { + incomment = 1; + v += 3; + } + else if (v + 2 < end && + isalpha(p[v + 1])) + intag = 1; + } + continue; + + case '>': + if (!inexp) + intag = 0; + continue; + + case '-': + if (!inexp && + !instring && + incomment && + v + 2 < end && + p[v + 1] == '-' && + p[v + 2] == '>') + { + incomment = 0; + v += 2; + } + continue; + + case 0xFF: + if (v + 1 < end) + { + if (p[v + 1] == '{') + inexp++; + else if (p[v + 1] == '}') + inexp--; + } + continue; + + default: + continue; + } + break; + } + if (argn == 0 && n == -1) + *pmarg = p + v; + *pmarglen = p + v - *pmarg; + //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg); + return v; +} + + +/***************************************************** + * Expand macro in place in buf. + * Only look at the text in buf from start to end. + */ + +void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, + unsigned char *arg, unsigned arglen) +{ +#if 0 + printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, *pend, arglen, arg); + printf("Buf is: '%.*s'\n", *pend - start, buf->data + start); +#endif + + static int nest; + if (nest > 100) // limit recursive expansion + return; + nest++; + + unsigned end = *pend; + assert(start <= end); + assert(end <= buf->offset); + + /* First pass - replace $0 + */ + arg = memdup(arg, arglen); + for (unsigned u = start; u + 1 < end; ) + { + unsigned char *p = buf->data; // buf->data is not loop invariant + + /* Look for $0, but not $$0, and replace it with arg. + */ + if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+')) + { + if (u > start && p[u - 1] == '$') + { // Don't expand $$0, but replace it with $0 + buf->remove(u - 1, 1); + end--; + u += 1; // now u is one past the closing '1' + continue; + } + + unsigned char c = p[u + 1]; + int n = (c == '+') ? -1 : c - '0'; + + unsigned char *marg; + unsigned marglen; + extractArgN(arg, arglen, &marg, &marglen, n); + if (marglen == 0) + { // Just remove macro invocation + //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); + buf->remove(u, 2); + end -= 2; + } + else if (c == '+') + { + // Replace '$+' with 'arg' + //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); + buf->remove(u, 2); + buf->insert(u, marg, marglen); + end += marglen - 2; + + // Scan replaced text for further expansion + unsigned mend = u + marglen; + expand(buf, u, &mend, NULL, 0); + end += mend - (u + marglen); + u = mend; + } + else + { + // Replace '$1' with '\xFF{arg\xFF}' + //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg); + buf->data[u] = 0xFF; + buf->data[u + 1] = '{'; + buf->insert(u + 2, marg, marglen); + buf->insert(u + 2 + marglen, "\xFF}", 2); + end += -2 + 2 + marglen + 2; + + // Scan replaced text for further expansion + unsigned mend = u + 2 + marglen; + expand(buf, u + 2, &mend, NULL, 0); + end += mend - (u + 2 + marglen); + u = mend; + } + //printf("u = %d, end = %d\n", u, end); + //printf("#%.*s#\n", end, &buf->data[0]); + continue; + } + + u++; + } + + /* Second pass - replace other macros + */ + for (unsigned u = start; u + 4 < end; ) + { + unsigned char *p = buf->data; // buf->data is not loop invariant + + /* A valid start of macro expansion is $(c, where c is + * an id start character, and not $$(c. + */ + if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2])) + { + //printf("\tfound macro start '%c'\n", p[u + 2]); + unsigned char *name = p + u + 2; + unsigned namelen = 0; + + unsigned char *marg; + unsigned marglen; + + unsigned v; + /* Scan forward to find end of macro name and + * beginning of macro argument (marg). + */ + for (v = u + 2; v < end; v++) + { unsigned char c = p[v]; + + if (!isidchar(c)) + { // We've gone past the end of the macro name. + namelen = v - (u + 2); + break; + } + } + + v += extractArgN(p + v, end - v, &marg, &marglen, 0); + assert(v <= end); + + if (v < end) + { // v is on the closing ')' + if (u > start && p[u - 1] == '$') + { // Don't expand $$(NAME), but replace it with $(NAME) + buf->remove(u - 1, 1); + end--; + u = v; // now u is one past the closing ')' + continue; + } + + Macro *m = search(name, namelen); + if (m) + { +#if 0 + if (m->textlen && m->text[0] == ' ') + { m->text++; + m->textlen--; + } +#endif + if (m->inuse && marglen == 0) + { // Remove macro invocation + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + } + else if (m->inuse && arglen == marglen && memcmp(arg, marg, arglen) == 0) + { // Recursive expansion; just leave in place + + } + else + { + //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text); +#if 1 + marg = memdup(marg, marglen); + // Insert replacement text + buf->spread(v + 1, 2 + m->textlen + 2); + buf->data[v + 1] = 0xFF; + buf->data[v + 2] = '{'; + memcpy(buf->data + v + 3, m->text, m->textlen); + buf->data[v + 3 + m->textlen] = 0xFF; + buf->data[v + 3 + m->textlen + 1] = '}'; + + end += 2 + m->textlen + 2; + + // Scan replaced text for further expansion + m->inuse++; + unsigned mend = v + 1 + 2+m->textlen+2; + expand(buf, v + 1, &mend, marg, marglen); + end += mend - (v + 1 + 2+m->textlen+2); + m->inuse--; + + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + u += mend - (v + 1); +#else + // Insert replacement text + buf->insert(v + 1, m->text, m->textlen); + end += m->textlen; + + // Scan replaced text for further expansion + m->inuse++; + unsigned mend = v + 1 + m->textlen; + expand(buf, v + 1, &mend, marg, marglen); + end += mend - (v + 1 + m->textlen); + m->inuse--; + + buf->remove(u, v + 1 - u); + end -= v + 1 - u; + u += mend - (v + 1); +#endif + mem.free(marg); + //printf("u = %d, end = %d\n", u, end); + //printf("#%.*s#\n", end - u, &buf->data[u]); + continue; + } + } + else + { + // Replace $(NAME) with nothing + buf->remove(u, v + 1 - u); + end -= (v + 1 - u); + continue; + } + } + } + u++; + } + mem.free(arg); + *pend = end; + nest--; +}