parser.c - scc - simple c99 compiler
 (HTM) git clone git://git.simple-cc.org/scc
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       parser.c (12747B)
       ---
            1 #include <assert.h>
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <limits.h>
            5 #include <stdarg.h>
            6 #include <stdio.h>
            7 #include <stdlib.h>
            8 #include <string.h>
            9 
           10 #include "make.h"
           11 
           12 #define MAXREPL  30
           13 #define TABSIZ   64
           14 #define MAXTOKEN 256
           15 #define ITEM     128
           16 
           17 typedef struct macro Macro;
           18 
           19 enum inputype {
           20         FTFILE,
           21         FTEXPAN,
           22 };
           23 
           24 struct loc {
           25         char *fname;
           26         int lineno;
           27 };
           28 
           29 struct input {
           30         int siz;
           31         int type;
           32 
           33         FILE *fp;
           34         struct loc loc;
           35 
           36         int pos;
           37         char *buf;
           38 
           39         struct input *prev;
           40 };
           41 
           42 struct macro {
           43         char *name;
           44         char *value;
           45 
           46         struct macro *next;
           47 };
           48 
           49 static struct input *input;
           50 static char token[MAXTOKEN];
           51 static int tok;
           52 static Macro *htab[TABSIZ];
           53 
           54 void
           55 dumpmacros(void)
           56 {
           57         Macro **pp, *p;
           58 
           59         for (pp = htab; pp < &htab[TABSIZ]; ++pp) {
           60                 for (p = *pp; p; p = p->next)
           61                         printf("%s = %s\n", p->name, getmacro(p->name));
           62         }
           63 }
           64 
           65 static Macro *
           66 lookup(char *name)
           67 {
           68         Macro *mp;
           69         int h = hash(name) & TABSIZ-1;
           70 
           71         for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next)
           72                 ;
           73 
           74         if (mp)
           75                 return mp;
           76 
           77         mp = emalloc(sizeof(*mp));
           78         mp->name = estrdup(name);
           79         mp->value = NULL;
           80         mp->next = htab[h];
           81         htab[h] = mp;
           82 
           83         return mp;
           84 }
           85 
           86 void
           87 setmacro(char *name, char *val, int export)
           88 {
           89         int n;
           90         char *buf;
           91         Macro *mp;
           92 
           93         mp = lookup(name);
           94         free(mp->value);
           95         mp->value = estrdup(val);
           96 
           97         if (export && strcmp(name, "SHELL") != 0) {
           98                 n = snprintf(NULL, 0, "%s=%s", name, val);
           99                 buf = emalloc(n+1);
          100                 snprintf(buf, n+1, "%s=%s", name, val);
          101                 putenv(buf);
          102         }
          103 }
          104 
          105 char *
          106 getmacro(char *name)
          107 {
          108         int hide;
          109         char *s, *t;
          110         Macro *mp = lookup(name);
          111 
          112         hide = 0;
          113         if (!strcmp(name, "SHELL") || !strcmp(name, "MAKEFLAGS"))
          114                 hide = 1;
          115 
          116         s = mp->value;
          117         if (!s && !hide)
          118                 s = getenv(name);
          119         if (!s)
          120                 s = "";
          121 
          122         if (eflag && !hide) {
          123                 t = getenv(name);
          124                 if (t)
          125                         s = t;
          126         }
          127 
          128         return s;
          129 }
          130 
          131 static struct loc *
          132 getloc(void)
          133 {
          134         struct input *ip;
          135 
          136         for (ip = input; ip && ip->type != FTFILE; ip = ip->prev)
          137                 ;
          138         if (!ip)
          139                 return NULL;
          140 
          141         return &ip->loc;
          142 }
          143 
          144 
          145 void
          146 error(char *fmt, ...)
          147 {
          148         va_list va;
          149         struct loc *loc;
          150 
          151         fprintf(stderr, "make: error: ");
          152         if ((loc = getloc()) != NULL)
          153                 fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
          154 
          155         va_start(va, fmt);
          156         vfprintf(stderr, fmt, va);
          157         va_end(va);
          158         putc('\n', stderr);
          159 
          160         exit(EXIT_FAILURE);
          161 }
          162 
          163 void
          164 warning(char *fmt, ...)
          165 {
          166         va_list va;
          167         struct loc *loc;
          168 
          169         fprintf(stderr, "make: warning: ");
          170         if ((loc = getloc()) != NULL)
          171                 fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
          172 
          173         va_start(va, fmt);
          174         vfprintf(stderr, fmt, va);
          175         va_end(va);
          176         putc('\n', stderr);
          177 }
          178 
          179 static void
          180 pop(void)
          181 {
          182         struct input *ip = input->prev;
          183 
          184         if (input->type == FTFILE) {
          185                 fclose(input->fp);
          186                 free(input->loc.fname);
          187         }
          188         free(input->buf);
          189         free(input);
          190 
          191         input = ip;
          192 }
          193 
          194 static void
          195 push(int type, ...)
          196 {
          197         int len, pos;
          198         FILE *fp = NULL;
          199         char *buf, *s, *fname = NULL;
          200         va_list va;
          201         struct input *ip;
          202 
          203         va_start(va, type);
          204         switch (type) {
          205         case FTFILE:
          206                 s = va_arg(va, char *);
          207                 fp = va_arg(va, FILE *);
          208                 fname = estrdup(s);
          209                 buf = emalloc(BUFSIZ);
          210                 pos = len = BUFSIZ;
          211                 break;
          212         case FTEXPAN:
          213                 s = va_arg(va, char *);
          214                 buf = estrdup(s);
          215                 pos = 0;
          216                 len = strlen(s);
          217                 break;
          218         }
          219         va_end(va);
          220 
          221         ip = emalloc(sizeof(*ip));
          222         ip->siz = len;
          223         ip->buf = buf;
          224         ip->type = type;
          225         ip->fp = fp;
          226         ip->loc.fname = fname;
          227         ip->loc.lineno = 1;
          228         ip->pos = pos;
          229         ip->prev = input;
          230 
          231         input = ip;
          232 }
          233 
          234 static char *
          235 trim(char *s)
          236 {
          237         size_t len;
          238 
          239         while (isspace(*s))
          240                 s++;
          241 
          242         for (len = strlen(s); len > 0 && isspace(s[len-1]); --len)
          243                 s[len-1] = '\0';
          244 
          245         return s;
          246 }
          247 
          248 static void
          249 include(char *s)
          250 {
          251         int len;
          252         FILE *fp;
          253         char *fil, *t;
          254 
          255         s = trim(s);
          256         fil = expandstring(s, NULL);
          257 
          258         t = trim(fil);
          259         if (strlen(t) != 0) {
          260                 debug("including '%s'", t);
          261                 if ((fp = fopen(t, "r")) == NULL)
          262                         error("opening %s:%s", t, strerror(errno));
          263                 push(FTFILE, t, fp);
          264         }
          265 
          266 end:
          267         free(fil);
          268 }
          269 
          270 static char *
          271 nextline(void)
          272 {
          273         int c;
          274         FILE *fp;
          275         char *s, *lim;
          276 
          277         assert(input->type == FTFILE);
          278 
          279 repeat:
          280         fp = input->fp;
          281         if (feof(fp))
          282                 return NULL;
          283 
          284         lim = &input->buf[input->siz];
          285         for (s = input->buf; s < lim; *s++ = c) {
          286                 c = getc(fp);
          287                 if (c == '\n' || c == EOF) {
          288                         input->loc.lineno++;
          289                         *s++ = '\n';
          290                         break;
          291                 }
          292                 if (c > UCHAR_MAX || c < 0)
          293                         error("invalid character '%c' (%d)", c, c);
          294         }
          295 
          296 
          297         if (s == lim)
          298                 error("too long line");
          299         if (ferror(fp))
          300                 error(strerror(errno));
          301         *s = '\0';
          302 
          303         if (!strcmp(input->buf, ""))
          304                 goto repeat;
          305 
          306         if (strncmp(input->buf, "include", 7) == 0) {
          307                 input->pos = input->siz;
          308                 include(input->buf+7);
          309                 goto repeat;
          310         }
          311 
          312         input->pos = 0;
          313 
          314 
          315         return input->buf;
          316 }
          317 
          318 static int
          319 empty(struct input *ip)
          320 {
          321         return ip->pos == ip->siz || ip->buf[ip->pos] == '\0';
          322 }
          323 
          324 static int
          325 moreinput(void)
          326 {
          327         while (input) {
          328                 if (!empty(input))
          329                         break;
          330 
          331                 switch (input->type) {
          332                 case FTEXPAN:
          333                         pop();
          334                         break;
          335                 case FTFILE:
          336                         if (!nextline())
          337                                 pop();
          338                         break;
          339                 }
          340         }
          341 
          342         return input != NULL;
          343 }
          344 
          345 static int
          346 nextc(void)
          347 {
          348         if (!moreinput())
          349                 return EOF;
          350 
          351         return input->buf[input->pos++];
          352 }
          353 
          354 static int
          355 back(int c)
          356 {
          357         if (c == EOF)
          358                 return c;
          359         assert(input->pos > 0);
          360         return input->buf[--input->pos] = c;
          361 }
          362 
          363 static void
          364 comment(void)
          365 {
          366         int c;
          367 
          368         while ((c = nextc()) != EOF && c != '\n') {
          369                 if (c == '\\' && nextc() == EOF)
          370                         break;
          371         }
          372 }
          373 
          374 static void
          375 skipspaces(void)
          376 {
          377         int c;
          378 
          379         for (c = nextc(); c == ' ' || c == '\t'; c = nextc())
          380                 ;
          381         back(c);
          382 }
          383 
          384 static int
          385 validchar(int c)
          386 {
          387         if (c == EOF)
          388                 return 0;
          389         return c == '.' || c == '/' || c == '_' || c == '-' || isalnum(c);
          390 }
          391 
          392 static int
          393 item(void)
          394 {
          395         int c;
          396         char *s;
          397 
          398         for (s = token; s < &token[MAXTOKEN] - 1; *s++ = c) {
          399                 c = nextc();
          400                 if (!validchar(c))
          401                         break;
          402         }
          403 
          404         if (s >= &token[MAXTOKEN] - 1)
          405                 error("token too long");
          406         if (s == token)
          407                 error("invalid empty token");
          408         *s = '\0';
          409         back(c);
          410 
          411         return ITEM;
          412 }
          413 
          414 static void
          415 expandmacro(char *name, char *repl, char *to)
          416 {
          417         int pos, siz, replsiz, tosiz;
          418         char *s, *t, *p, *buf;
          419 
          420         s = expandstring(getmacro(name), NULL);
          421 
          422         pos = 0;
          423         buf = NULL;
          424         tosiz = strlen(to);
          425         replsiz = strlen(repl);
          426 
          427         t = s;
          428         for (pos = 0; *t; pos += siz) {
          429                 if (replsiz > 0 && strncmp(t, repl, replsiz) == 0) {
          430                         siz = tosiz;
          431                         p = to;
          432                         t += replsiz;
          433                 } else {
          434                         siz = 1;
          435                         p = t;
          436                         t++;
          437                 }
          438 
          439                 buf = erealloc(buf, pos + siz + 1);
          440                 memcpy(buf+pos, p, siz);
          441         }
          442 
          443         if (pos > 0) {
          444                 buf[pos] = '\0';
          445                 push(FTEXPAN, buf);
          446                 free(buf);
          447         }
          448         free(s);
          449 }
          450 
          451 static void
          452 expandsimple(Target *tp)
          453 {
          454         char *s;
          455         Target **p;
          456         int len, c;
          457 
          458         switch (c = nextc()) {
          459         case '@':
          460                 if (!tp || !tp->target)
          461                         return;
          462                 push(FTEXPAN, tp->target);
          463                 break;
          464         case '<':
          465                 if (!tp || !tp->req)
          466                         return;
          467                 push(FTEXPAN, tp->req);
          468                 break;
          469         case '*':
          470                 if (!tp || !tp->target)
          471                         return;
          472                 s = strrchr(tp->target, '.');
          473                 if (!s) {
          474                         push(FTEXPAN, tp->target);
          475                         return;
          476                 }
          477 
          478                 len = s - tp->target;
          479                 s = emalloc(len+1);
          480                 memcpy(s, tp->target, len);
          481                 s[len] = '\0';
          482                 push(FTEXPAN, s);
          483                 free(s);
          484                 break;
          485         case '?':
          486                 if (!tp)
          487                         return;
          488 
          489                 if (tp->req && stamp(tp->req) > tp->stamp) {
          490                         push(FTEXPAN, " ");
          491                         push(FTEXPAN, tp->req);
          492                 }
          493 
          494                 for (p = tp->deps; p && *p; ++p) {
          495                         if (stamp((*p)->name) > tp->stamp) {
          496                                 push(FTEXPAN, " ");
          497                                 push(FTEXPAN, (*p)->name);
          498                         }
          499                 }
          500                 break;
          501         default:
          502                 token[0] = c;
          503                 token[1] = '\0';
          504                 expandmacro(token, "", "");
          505                 break;
          506         }
          507 }
          508 
          509 static void
          510 expansion(Target *tp)
          511 {
          512         int delim, c, repli, toi, namei, st;
          513         char name[MAXTOKEN], repl[MAXREPL], to[MAXREPL];
          514 
          515         c = nextc();
          516         if (c == '(')
          517                 delim = ')';
          518         else if (c == '{')
          519                 delim = '}';
          520         else
          521                 delim = 0;
          522 
          523         if (!delim) {
          524                 back(c);
          525                 expandsimple(tp);
          526         } else {
          527                 st = namei = repli = toi = 0;
          528                 while ((c = nextc()) != EOF) {
          529                         if (c == delim)
          530                                 break;
          531 
          532                         switch (st) {
          533                         case 0:
          534                                 if (c == ':') {
          535                                         st = 1;
          536                                         continue;
          537                                 }
          538                                 if (!validchar(c))
          539                                         error("invalid macro name in expansion");
          540                                 if (namei == MAXTOKEN-1)
          541                                         error("expansion text too long");
          542                                 name[namei++] = c;
          543                                 break;
          544                         case 1:
          545                                 if (c == '=') {
          546                                         st = 2;
          547                                         continue;
          548                                 }
          549                                 if (repli == MAXREPL-1)
          550                                         error("macro replacement too big");
          551                                 repl[repli++] = c;
          552                                 break;
          553                         case 2:
          554                                 if (toi == MAXREPL-1)
          555                                         error("macro substiturion too big");
          556                                 to[toi++] = c;
          557                                 break;
          558                         }
          559                 }
          560 
          561                 if (c == EOF)
          562                         error("found eof while parsing expansion");
          563                 if (st > 0 && (namei == 0 ||  repli == 0 || to == 0))
          564                         error("invalid macro expansion");
          565 
          566                 name[namei] = '\0';
          567                 repl[repli] = '\0';
          568                 to[toi] = '\0';
          569                 expandmacro(name, repl, to);
          570         }
          571 }
          572 
          573 /*
          574  * Horrible hack to do string expansion.
          575  * We cannot use normal push and nextc because that
          576  * would consume characters of the current file too.
          577  * For that reason it cleans the input and it recovers
          578  * it later.
          579  */
          580 char *
          581 expandstring(char *line, Target *tp)
          582 {
          583         int c, n;
          584         char *s;
          585         struct input *ip = input;
          586 
          587         input = NULL;
          588         push(FTEXPAN, line);
          589 
          590         n = 0;
          591         s = NULL;
          592         while ((c = nextc()) != EOF) {
          593                 if (c == '$') {
          594                         if ((c = nextc()) != '$') {
          595                                 back(c);
          596                                 expansion(tp);
          597                                 continue;
          598                         }
          599                 }
          600 
          601                 s = erealloc(s, ++n);
          602                 s[n-1] = c;
          603         }
          604 
          605         s = erealloc(s, n+1);
          606         s[n] = '\0';
          607         input = ip;
          608 
          609         return s;
          610 }
          611 
          612 static int
          613 readchar(void)
          614 {
          615         int c;
          616 
          617         while ((c = nextc()) != EOF) {
          618                 if (c == ' ' || c == '\t')
          619                         continue;
          620                 if (c == '\\') {
          621                         if ((c = nextc()) == '\n')
          622                                 continue;
          623                         back(c);
          624                         c = '\\';
          625                 }
          626                 break;
          627         }
          628 
          629         return c;
          630 }
          631 
          632 static int
          633 next(void)
          634 {
          635         int c;
          636 
          637 repeat:
          638         c = readchar();
          639 
          640         switch (c) {
          641         case EOF:
          642                 strcpy(token, "<EOF>");
          643                 tok = EOF;
          644                 break;
          645         case '$':
          646                 if ((c = nextc()) == '$')
          647                         goto single;
          648                 back(c);
          649                 expansion(NULL);
          650                 goto repeat;
          651         case '#':
          652                 comment();
          653                 c = '\n';
          654         case ';':
          655         case ':':
          656         case '=':
          657         case '\n':
          658         single:
          659                 token[0] = c;
          660                 token[1] = '\0';
          661                 tok = c;
          662                 break;
          663         default:
          664                 if (!validchar(c))
          665                         error("unexpected character '%c'", c);
          666                 back(c);
          667                 tok = item();
          668                 break;
          669         }
          670 
          671         return tok;
          672 }
          673 
          674 static char *
          675 readmacrodef(void)
          676 {
          677         int n, c;
          678         char *line;
          679 
          680         n = 0;
          681         line = NULL;
          682         while ((c = nextc()) != EOF) {
          683                 line = erealloc(line, n+1);
          684                 if (c == '\n')
          685                         break;
          686                 if (c == '#') {
          687                         comment();
          688                         break;
          689                 }
          690                 if (c == '\\') {
          691                         if ((c = nextc()) != '\n') {
          692                                 back(c);
          693                                 c = '\\';
          694                         } else {
          695                                 skipspaces();
          696                                 c = ' ';
          697                         }
          698                 }
          699 
          700                 line[n++] = c;
          701         }
          702         if (c == EOF)
          703                 error("EOF while looking for end of line");
          704         line[n] = '\0';
          705 
          706         return line;
          707 }
          708 
          709 static char *
          710 readcmd(void)
          711 {
          712         int n, c;
          713         char *line;
          714 
          715         skipspaces();
          716 
          717         n = 0;
          718         line = NULL;
          719         while ((c = nextc()) != EOF) {
          720                 line = erealloc(line, n+1);
          721                 if (c == '\n')
          722                         break;
          723                 if (c == '\\') {
          724                         if ((c = nextc()) == '\n') {
          725                                 if ((c = nextc()) != '\t')
          726                                         back(c);
          727                                 continue;
          728                         }
          729                         back(c);
          730                         c = '\\';
          731                 }
          732                 line[n++] = c;
          733         }
          734         if (c == EOF)
          735                 error("EOF while looking for end of command");
          736         line[n] = '\0';
          737 
          738         return line;
          739 }
          740 
          741 static void
          742 rule(char *targets[], int ntargets)
          743 {
          744         int c, i, j, ndeps, nactions;
          745         char **actions, **deps = NULL;
          746 
          747         if (ntargets == 0)
          748                 error("missing target");
          749 
          750         for (ndeps = 0; next() == ITEM; ++ndeps) {
          751                 deps = erealloc(deps, (ndeps+1) * sizeof(char *));
          752                 deps[ndeps] = estrdup(token);
          753         }
          754 
          755         if (tok != '\n' && tok != ';')
          756                 error("garbage at the end of the line");
          757 
          758         nactions = 0;
          759         actions = NULL;
          760         if (tok == ';') {
          761                 nactions++;
          762                 actions = erealloc(actions, nactions * sizeof(char *));
          763                 actions[nactions-1] = readcmd();
          764         }
          765 
          766         for (;;) {
          767                 if ((c = nextc()) == '#') {
          768                         comment();
          769                         continue;
          770                 }
          771                 if (c != '\t')
          772                         break;
          773                 nactions++;
          774                 actions = erealloc(actions, nactions * sizeof(char *));
          775                 actions[nactions-1] = readcmd();
          776         }
          777         back(c);
          778 
          779         for (i = 0; i < ntargets; i++) {
          780                 addtarget(targets[i], ndeps);
          781                 for (j = 0; j < ndeps; j++)
          782                         adddep(targets[i], deps[j]);
          783                 if (nactions > 0)
          784                         addrule(targets[i], actions, nactions);
          785         }
          786 
          787         for (i = 0; i < ndeps; i++)
          788                 free(deps[i]);
          789         free(deps);
          790 
          791         for (i = 0; i < nactions; i++)
          792                 free(actions[i]);
          793         free(actions);
          794 }
          795 
          796 static void
          797 assign(char *macros[], int n)
          798 {
          799         int len, c;
          800         char *defs;
          801 
          802         if (n != 1)
          803                 error("invalid macro definition");
          804 
          805         skipspaces();
          806         defs = readmacrodef();
          807         setmacro(*macros, defs, NOEXPORT);
          808         free(defs);
          809 }
          810 
          811 void
          812 parseinput(void)
          813 {
          814         int i, n;
          815         char **targets;
          816 
          817         while (moreinput()) {
          818                 n = 0;
          819                 targets = NULL;
          820 
          821                 next();
          822                 if (tok == '\n')
          823                         continue;
          824 
          825                 while (tok == ITEM) {
          826                         n++;
          827                         targets = erealloc(targets, n * sizeof(char *));
          828                         targets[n-1] = estrdup(token);
          829                         next();
          830                 }
          831 
          832                 switch (tok) {
          833                 case ':':
          834                         rule(targets, n);
          835                         break;
          836                 case '=':
          837                         assign(targets, n);
          838                         break;
          839                 default:
          840                         error("unexpected token '%s'(%d)", token, tok);
          841                 }
          842 
          843                 for (i = 0; i < n; i++)
          844                         free(targets[i]);
          845                 free(targets);
          846         }
          847 }
          848 
          849 int
          850 parse(char *fname)
          851 {
          852         FILE *fp;
          853 
          854         if (!fname) {
          855                 fp = stdin;
          856                 fname = "<stdin>";
          857         } else if ((fp = fopen(fname, "r")) == NULL) {
          858                 return 0;
          859         }
          860 
          861         debug("parsing %s", fname);
          862         push(FTFILE, fname, fp);
          863         parseinput();
          864 
          865         return 1;
          866 }
          867 
          868 void
          869 inject(char *s)
          870 {
          871         push(FTEXPAN, s);
          872         parseinput();
          873 }