1#include <assert.h>2#include <ctype.h>3#include <errno.h>4#include <limits.h>5#include <setjmp.h>6#include <stdio.h>7#include <stdlib.h>8#include <string.h>910#include <scc/cstd.h>11#include <scc/scc.h>12#include "cc1.h"1314#define REPLACECHAR 0xFFFD15#define NOMULTICHAR 016#define MULTICHAR 117#define UTF8_MAX 41819int yytoken;20struct yystype yylval;21char yytext[STRINGSIZ+3];22unsigned short yylen;23int yyspace, lexmode = CCMODE;24unsigned lineno;25char filenam[FILENAME_MAX];2627int namespace = NS_IDEN;28static int safe;29Input *input;3031void32setloc(char *fname, unsigned line)33{34 size_t len;3536 if (fname) {37 if ((len = strlen(fname)) >= FILENAME_MAX)38 die("cc1: %s: file name too long", fname);39 memmove(filenam, fname, len);40 filenam[len] = '\0';4142 /*43 * There are cases where we want to call setloc()44 * with the data in input, and then we have t be45 * careful about freeing input->filenam46 */47 if (fname != input->filenam) {48 free(input->filenam);49 input->filenam = xstrdup(fname);50 }51 }5253 lineno = input->lineno = line;54}5556int57addinput(int type, void *arg, int fail)58{59 FILE *fp;60 char *extp, *fname, *buffer, *infile;61 int infileln;62 Macro *mp;63 Symbol *sym, **hs;64 Input *newip, *curip = input;6566 if (curip)67 curip->lineno = lineno;6869 switch (type) {70 case IMACRO:71 fp = NULL;72 mp = arg;73 sym = mp->sym;74 fname = mp->fname;75 buffer = mp->buffer;76 DBG("INPUT expanding macro %s", sym->name);77 break;78 case IPARAM:79 fp = NULL;80 mp = NULL;81 buffer = arg;82 fname = filenam;83 DBG("INPUT macro parameter '%s'", buffer);84 break;85 case IFILE:86 fname = arg;87 mp = NULL;88 buffer = NULL;8990 if ((fp = fopen(fname, "r")) == NULL) {91 if (!fail)92 return 0;93 die("cc1: %s: %s", fname, strerror(errno));94 }95 if (curip && onlyheader) {96 infile = curip->filenam;97 infileln = strlen(infile);98 if (extp = strrchr(infile, '.'))99 infileln -= strlen(extp);100 printf("%.*s.o: %s %s\n",101 infileln, infile, infile, fname);102 }103 lineno = 0;104 DBG("INPUT file input '%s'", fname);105 break;106 case ISTDIN:107 fp = stdin;108 mp = NULL;109 fname = "<stdin>";110 buffer = NULL;111 lineno = 0;112 DBG("INPUT file input 'stdin'");113 break;114 default:115 abort();116 }117118 if (!buffer) {119 buffer = xmalloc(INPUTSIZ);120 buffer[0] = '\0';121 } else {122 buffer = xstrdup(buffer);123 }124125 if (input)126 unsethideset(input);127128 newip = xmalloc(sizeof(*newip));129 newip->next = curip;130 newip->macro = mp;131 newip->p = newip->begin = newip->line = buffer;132 newip->filenam = NULL;133 newip->lineno = 0;134 newip->fp = fp;135 newip->flags = type;136 memset(newip->hideset, 0, sizeof(newip->hideset));137 input = newip;138139 hs = NULL;140 if (curip)141 hs = curip->hideset;142 if (type == IMACRO)143 hs = mp->hideset;144 if (type == IPARAM && curip)145 hs = curip->macro->hideset;146 if (hs) {147 memcpy(input->hideset, hs, sizeof(input->hideset));148 sethideset(input);149 }150151 setloc(fname, lineno);152 return 1;153}154155void156delinput(void)157{158 Input *ip = input;159160 switch (ip->flags & ITYPE) {161 case IFILE:162 DBG("INPUT file finished '%s'", ip->filenam);163 if (fclose(ip->fp))164 die("cc1: %s: %s", ip->filenam, strerror(errno));165 break;166 case IMACRO:167 DBG("INPUT macro %s finished", ip->macro->sym->name);168 delmacro(ip->macro);169 break;170 case IPARAM:171 DBG("INPUT macro param finished");172 break;173 case ISTDIN:174 DBG("INPUT stdin finished");175 break;176 default:177 abort();178 }179 unsethideset(input);180181 input = ip->next;182 free(ip->filenam);183 free(ip->line);184 free(ip);185 if (input) {186 sethideset(input);187 setloc(input->filenam, input->lineno);188 }189}190191static void192newline(void)193{194 if (++lineno == 0)195 die("cc1: %s: file too long", filenam);196}197198/*199 * Read the next character from the input file, counting number of lines200 * and joining lines escaped with \201 */202static int203readchar(void)204{205 FILE *fp = input->fp;206 int c;207208repeat:209 switch (c = getc(fp)) {210 case '\\':211 if ((c = getc(fp)) == '\n') {212 newline();213 goto repeat;214 }215 ungetc(c, fp);216 c = '\\';217 break;218 case '\n':219 newline();220 break;221 case EOF:222 break;223 }224225 return c;226}227228/*229 * discard a C comment. This function is only called from readline230 * because it is impossible to have a comment in a macro, because231 * comments are always discarded before processing any cpp directive232 */233static void234comment(int type)235{236 int c;237238 c = readchar();239repeat:240 for ( ; c != EOF && c != type; c = readchar())241 ;242243 if (c == EOF) {244 errorp("unterminated comment");245 return;246 }247248 if (type == '*' && (c = readchar()) != '/')249 goto repeat;250}251252/*253 * readline is used to read a full logic line from a file.254 * It discards comments and check that the line fits in255 * the input buffer256 */257static int258readline(void)259{260 char *bp, *lim;261 int c, peekc = 0, delim = 0;262263 if (feof(input->fp)) {264 input->flags |= IEOF;265 *input->p = '\0';266 return 0;267 }268269 *input->line = '\0';270 lim = &input->line[INPUTSIZ-1];271 for (bp = input->line; bp < lim-1; *bp++ = c) {272 c = (peekc) ? peekc : readchar();273 peekc = 0;274 if (c == '\n' || c == EOF)275 break;276 if (c == '\\') {277 peekc = readchar();278 if (peekc == '\n' || peekc == EOF)279 continue;280 if (bp == lim-2)281 break;282 *bp++ = c;283 c = peekc;284 peekc = 0;285 continue;286 }287288 if (delim && c == delim)289 delim = 0;290 else if (!delim && (c == '"' || c == '\''))291 delim = c;292 if (c != '/' || delim)293 continue;294295 /* check for /* or // */296 peekc = readchar();297 if (peekc != '*' && peekc != '/')298 continue;299300 if (peekc == '/') {301 comment('\n');302 break;303 } else {304 comment('*');305 c = ' ';306 }307 peekc = 0;308 }309310 input->begin = input->p = input->line;311 if (bp == lim-1) {312 errorp("line too long");313 --bp;314 }315 *bp++ = '\n';316 *bp = '\0';317318 return 1;319}320321/*322 * moreinput gets more bytes to be passed to the lexer.323 * It can take more bytes from macro expansions or324 * directly reading from files. When a cpp directive325 * is processed the line is discarded because it must not326 * be passed to the lexer327 */328int329moreinput(void)330{331 int wasexpand = 0;332333repeat:334 if (!input)335 return 0;336337 if (*input->p == '\0') {338 int t = input->flags & ITYPE;339 if (t == IPARAM) {340 input->flags |= IEOF;341 return 0;342 }343 if (t == IMACRO) {344 wasexpand = 1;345 input->flags |= IEOF;346 }347 if (input->flags & IEOF) {348 delinput();349 goto repeat;350 }351 if (!readline()) {352 *input->p = '\0';353 goto repeat;354 }355 if (cpp())356 goto repeat;357 }358359 if (onlycpp && !wasexpand)360 ppragmaln();361 return 1;362}363364static void365tok2str(void)366{367 if ((yylen = input->p - input->begin) >= sizeof(yytext))368 error("token too big");369 memcpy(yytext, input->begin, yylen);370 yytext[yylen] = '\0';371 input->begin = input->p;372}373374static Symbol *375readint(int base, int sign, Symbol *sym)376{377 char *s = yytext;378 Type *tp = sym->type;379 struct limits *lim;380 unsigned long long u, val, max;381 int c;382383 lim = getlimits(tp);384 max = lim->max.i;385 if (*s == '0')386 ++s;387 if (toupper(*s) == 'X')388 ++s;389390 for (u = 0; isxdigit(c = *s++); u = u*base + val) {391 static char letters[] = "0123456789ABCDEF";392 val = strchr(letters, toupper(c)) - letters;393 repeat:394 if (u <= max/base && u*base <= max - val)395 continue;396 if (tp->prop & TSIGNED) {397 if (tp == inttype)398 tp = (base==10) ? longtype : uinttype;399 else if (tp == longtype)400 tp = (base==10) ? llongtype : ulongtype;401 else if (tp == llongtype && base != 10)402 tp = ullongtype;403 else404 goto overflow;405 } else {406 if (tp == uinttype)407 tp = (sign==UNSIGNED) ? ulongtype : longtype;408 else if (tp == ulongtype)409 tp = (sign==UNSIGNED) ? ullongtype : llongtype;410 else411 goto overflow;412 }413 sym->type = tp;414 lim = getlimits(tp);415 max = lim->max.i;416 goto repeat;417 }418419 if (tp->prop & TSIGNED)420 sym->u.i = u;421 else422 sym->u.u = u;423424 return sym;425426overflow:427 errorp("overflow in integer constant");428 return sym;429}430431static int432integer(int base)433{434 Type *tp;435 Symbol *sym;436 unsigned size, sign;437438 for (size = sign = 0; ; ++input->p) {439 switch (toupper(*input->p)) {440 case 'L':441 if (size == LLONG)442 goto wrong_type;443 size = (size == LONG) ? LLONG : LONG;444 continue;445 case 'U':446 if (sign == UNSIGNED)447 goto wrong_type;448 sign = UNSIGNED;449 continue;450 default:451 goto convert;452 wrong_type:453 error("invalid suffix in integer constant");454 }455 }456457convert:458 tok2str();459 tp = ctype(INT, sign, size);460 sym = newsym(NS_IDEN, NULL);461 sym->type = tp;462 sym->flags |= SCONSTANT;463 yylval.sym = readint(base, sign, sym);464 return CONSTANT;465}466467static void468digits(int base)469{470 char *p;471 int c;472473 for (p = input->p; c = *p; ++p) {474 switch (base) {475 case 8:476 if (!strchr("01234567", c))477 goto end;478 break;479 case 10:480 if (!isdigit(c))481 goto end;482 break;483 case 16:484 if (!isxdigit(c))485 goto end;486 break;487 }488 }489end:490 input->p = p;491}492493static int494number(void)495{496 Type *tp;497 Symbol *sym;498 int ch, base;499 long double ld;500501 if (*input->p != '0') {502 base = 10;503 } else {504 if (toupper(*++input->p) == 'X') {505 ++input->p;506 base = 16;507 } else {508 base = 8;509 }510 }511 digits(base);512513 if (*input->p != '.')514 return integer(base);515516 sym = newsym(NS_IDEN, NULL);517518 ld = strtold(input->begin, &input->p);519 switch (toupper(*input->p)) {520 case 'F':521 ++input->p;522 tp = floattype;523 sym->u.f = ld;524 break;525 case 'L':526 ++input->p;527 tp = ldoubletype;528 sym->u.ld = ld;529 break;530 default:531 tp = doubletype;532 sym->u.d = ld;533 break;534 }535536 tok2str();537 sym->type = tp;538 sym->flags |= SCONSTANT;539 yylval.sym = sym;540 return CONSTANT;541}542543static Rune544escape(int multi)545{546 Rune c;547 int uni, d, i, cnt, base;548549 switch (*++input->p) {550 case 'a':551 return '\a';552 case 'b':553 return '\b';554 case 'f':555 return '\f';556 case 'n':557 return '\n';558 case 'r':559 return '\r';560 case 't':561 return '\t';562 case 'v':563 return '\v';564 case '"':565 return '"';566 case '\'':567 return '\'';568 case '\\':569 return '\\';570 case '\?':571 return '\?';572 case 'U':573 cnt = 8;574 goto check_universal;575 case 'u':576 cnt = 4;577 check_universal:578 if (!multi)579 warn("multi-character character constant");580 ++input->p;581 uni = 1;582 base = 16;583 break;584 case 'x':585 ++input->p;586 uni = 0;587 cnt = 2;588 base = 16;589 break;590 case '0':591 case '1':592 case '2':593 case '3':594 case '4':595 case '5':596 case '6':597 case '7':598 uni = 0;599 cnt = 3;600 base = 8;601 break;602 default:603 warn("unknown escape sequence");604 return ' ';605 }606607 for (c = i = 0; i < cnt && isxdigit(*input->p); ++i) {608 static char digits[] = "0123456789ABCDEF";609 char *p = strchr(digits, toupper(*input->p));610611 if (!p || (d = p - digits) > base)612 break;613 c *= base;614 c += d;615 ++input->p;616 }617 --input->p;618619 if (base == 16 && i != cnt) {620 if (uni) {621 errorp("incorrect digit for universal character constant");622 c = REPLACECHAR;623 } else {624 errorp("\\x used with no following hex digits");625 c = ' ';626 }627 }628629 if (!uni)630 return c;631632 if (c < 0xa0 && c != 0x24 && c != 0x40 && c != 0x60633 || c >= 0xD800 && c <= 0xDFFF634 || c >= 0x110000) {635 errorp("invalid universal character constant");636 c = REPLACECHAR;637 }638639 return c;640}641642static int643validutf8(Rune wc, int *nbytes)644{645 static struct range {646 unsigned long begin, end;647 int valid;648 int nbytes;649 } ranges[] = {650 {0, 0x80, 1, 1},651 {0x80, 0x800, 1, 2},652 {0x800, 0xD800, 1, 3},653 {0xD800, 0xDD00, 0, 3},654 {0xDD00, 0x10000, 1, 3},655 {0x10000, 0x110000, 1, 4},656 {0x110000, -1ul, 0, 0},657 };658 struct range *bp;659660 for (bp = ranges; bp->begin > wc || bp->end <= wc; ++661bp)662 ;663 *nbytes = bp->nbytes;664665 return bp->valid;666}667668static Rune669utf8rune(void)670{671 Rune wc;672 int i, sh, n;673 unsigned oc, c;674 unsigned char *s = (unsigned char *) input->p;675676 /* fast track for ascii */677 if ((c = *s) < 0x80)678 return c;679680 /* out of sequence multibyte? */681 if ((c & 0xc0) != 0xc0)682 goto invalid;683684 sh = 1;685 wc = 0;686 oc = c << 1;687688 for (i = 0; i < UTF8_MAX; ++i) {689 c = s[1];690 if ((c & 0xc0) != 0x80)691 goto invalid;692 ++s;693694 wc <<= 6;695 wc |= c & 0x3f;696 oc <<= 1;697 sh++;698699 if ((oc & 0x80) == 0) {700 oc = (oc & 0xff) >> sh;701 wc |= oc << (sh-1) * 6;702703 if (!validutf8(wc, &n) || sh != n)704 goto invalid;705 goto return_code;706 }707 }708709invalid:710 errorp("invalid multibyte sequence");711 wc = REPLACECHAR;712713return_code:714 input->p = s;715 return wc;716}717718static Rune719decode(int multi)720{721 Rune r;722723 if (*input->p == '\\') {724 r = escape(multi);725 return r;726 }727728 return multi ? utf8rune() : *input->p;729}730731static int732character(void)733{734 int i, multi = NOMULTICHAR;735 Rune r;736 Type *tp;737 Symbol *sym;738 long long d;739 struct limits *lim;740741 tp = chartype;742 if (*input->p == 'L') {743 multi = MULTICHAR;744 input->p++;745 tp = wchartype;746 }747748 lim = getlimits(tp);749750 d = 0;751 input->p++;752 for (i = 0; *input->p != '\''; i++) {753 r = decode(multi);754 if (r > lim->max.i)755 warn("character too large for enclosing character literal type");756 d |= r;757 input->p++;758 }759 input->p++;760761 if (i == 0)762 errorp("empty character constant");763 if (i > 1)764 warn("multi-character character constant");765766 if (tp == chartype) {767 if (tp->prop & TSIGNED && d > lim->max.i)768 d -= getlimits(uchartype)->max.i + 1;769 tp = inttype;770 }771772 sym = newsym(NS_IDEN, NULL);773 sym->u.i = d;774 sym->type = tp;775 yylval.sym = sym;776 tok2str();777778 return CONSTANT;779}780781static int782rstring(void)783{784 char *beg = input->p++;785 Rune c, buff[STRINGSIZ+1], *bp = buff;786787 for (++input->p; ; ++input->p) {788 switch (*input->p) {789 case '\0':790 errorp("missing terminating '\"' character");791 case '"':792 goto end_loop;793 }794795 c = decode(MULTICHAR);796 if (input->p - beg == STRINGSIZ + 1) {797 /* too long, ignore everything until next quote */798 for (++input->p; *input->p != '"'; ++input->p) {799 if (*input->p == '\\')800 ++input->p;801 if (*input->p == '\0')802 break;803 }804 --bp;805 errorp("string too long");806 break;807 }808 *bp++ = c;809 }810811end_loop:812 *bp++ = '\0';813 input->p++;814815 yylval.sym = newrstring(buff, bp - buff);816 tok2str();817818 return STRING;819}820821static int822sstring(void)823{824 int c;825 struct limits *lim;826 char buff[STRINGSIZ+1], *bp = buff, *beg = input->p;827828 for (++input->p; ; ++input->p) {829 switch (*input->p) {830 case '\0':831 errorp("missing terminating '\"' character");832 case '"':833 goto end_loop;834 }835836 c = decode(NOMULTICHAR);837 if (input->p - beg == STRINGSIZ + 1) {838 /* too long, ignore everything until next quote */839 for (++input->p; *input->p != '"'; ++input->p) {840 if (*input->p == '\\')841 ++input->p;842 if (*input->p == '\0')843 break;844 }845 --bp;846 errorp("string too long");847 break;848 }849 *bp++ = c;850 }851852end_loop:853 *bp++ = '\0';854 input->p++;855856 yylval.sym = newstring(buff, bp - buff);857 tok2str();858859 return STRING;860}861862static int863string(void)864{865 if (*input->p == 'L')866 return rstring();867 return sstring();868}869870static int871iden(void)872{873 Symbol *sym;874 char *p, *begin;875876 if (input->p[0] == 'L' && input->p[1] == '\'')877 return character();878879 if (input->p[0] == 'L' && input->p[1] == '"')880 return string();881882 begin = input->p;883 for (p = begin; isalnum(*p) || *p == '_'; ++p)884 ;885 input->p = p;886 tok2str();887888 sym = lookup(NS_CPP, yytext, NOALLOC);889 if ((yylval.cppsym = sym) != NULL) {890 if (expand(sym))891 return next();892 }893894 sym = lookup(namespace, yytext, ALLOC);895 yylval.sym = sym;896 if (sym->flags & SCONSTANT)897 return CONSTANT;898 if (sym->token != IDEN)899 yylval.token = sym->u.token;900 return sym->token;901}902903static int904follow(int expect, int ifyes, int ifno)905{906 if (*input->p++ == expect)907 return ifyes;908 --input->p;909 return ifno;910}911912static int913minus(void)914{915 switch (*input->p++) {916 case '-':917 return DEC;918 case '>':919 return INDIR;920 case '=':921 return SUB_EQ;922 default:923 --input->p;924 return '-';925 }926}927928static int929plus(void)930{931 switch (*input->p++) {932 case '+':933 return INC;934 case '=':935 return ADD_EQ;936 default:937 --input->p;938 return '+';939 }940}941942static int943relational(int op, int equal, int shift, int assig)944{945 int c;946947 if ((c = *input->p++) == '=')948 return equal;949 if (c == op)950 return follow('=', assig, shift);951 --input->p;952 return op;953}954955static int956logic(int op, int equal, int logic)957{958 int c;959960 if ((c = *input->p++) == '=')961 return equal;962 if (c == op)963 return logic;964 --input->p;965 return op;966}967968static int969dot(void)970{971 int c;972973 if (isdigit(*input->p))974 return number();975 if ((c = *input->p) != '.')976 return '.';977 if ((c = *++input->p) != '.')978 error("incorrect token '..'");979 ++input->p;980 return ELLIPSIS;981}982983static int984operator(void)985{986 int t;987988 switch (t = *input->p++) {989 case '<':990 t = relational('<', LE, SHL, SHL_EQ);991 break;992 case '>':993 t = relational('>', GE, SHR, SHR_EQ);994 break;995 case '&':996 t = logic('&', AND_EQ, AND);997 break;998 case '|':999 t = logic('|', OR_EQ, OR);1000 break;1001 case '=':1002 t = follow('=', EQ, '=');1003 break;1004 case '^':1005 t = follow('=', XOR_EQ, '^');1006 break;1007 case '*':1008 t = follow('=', MUL_EQ, '*');1009 break;1010 case '/':1011 t = follow('=', DIV_EQ, '/');1012 break;1013 case '%':1014 t = follow('=', MOD_EQ, '%');1015 break;1016 case '!':1017 t = follow('=', NE, '!');1018 break;1019 case '-':1020 t = minus();1021 break;1022 case '+':1023 t = plus();1024 break;1025 case '.':1026 t = dot();1027 break;1028 }1029 tok2str();1030 return t;1031}10321033/* TODO: Ensure that namespace is NS_IDEN after a recovery */10341035/*1036 * skip all the spaces until the next token. When we are in1037 * CPPMODE \n is not considered a whitespace1038 */1039static int1040skipspaces(void)1041{1042 int c;10431044 if (!input)1045 return EOF;10461047 for (;;) {1048 switch (c = *input->p) {1049 case '\0':1050 if (!moreinput())1051 return EOF;1052 break;1053 case '\n':1054 case ' ':1055 case '\t':1056 case '\v':1057 case '\r':1058 case '\f':1059 yyspace = c;1060 if (c == '\n' && lexmode == CPPMODE)1061 goto return_byte;1062 ++input->p;1063 break;1064 default:1065 goto return_byte;1066 }1067 }10681069return_byte:1070 input->begin = input->p;1071 return c;1072}10731074static int1075lex(void)1076{1077 int c;10781079repeat:1080 switch (c = skipspaces()) {1081 case EOF:1082 return EOFTOK;1083 case NOEXPAND:1084 case EXPAND:1085 disexpand += (c == NOEXPAND) ? +1 : -1;1086 input->p++;1087 goto repeat;1088 case '_':1089 return iden();1090 case '"':1091 return string();1092 case '\'':1093 return character();1094 default:1095 if (isalpha(c))1096 return iden();1097 if (isdigit(c))1098 return number();1099 return operator();1100 }1101}11021103int1104next(void)1105{1106 yytoken = lex();11071108 if (yytoken == EOFTOK) {1109 strcpy(yytext, "<EOF>");1110 if (cppctx && !input)1111 errorp("#endif expected");1112 }11131114 DBG("TOKEN %s", yytext);1115 return yytoken;1116}11171118void1119expect(int tok)1120{1121 if (yytoken != tok) {1122 if (isgraph(tok))1123 errorp("expected '%c' before '%s'", tok, yytext);1124 else1125 errorp("unexpected '%s'", yytext);1126 } else {1127 next();1128 }1129}11301131int1132ahead(void)1133{1134 skipspaces();1135 return *input->begin;1136}11371138void1139setsafe(int type)1140{1141 safe = type;1142}11431144void1145discard(void)1146{1147 extern jmp_buf recover;1148 int c;11491150 if (!input)1151 exit(EXIT_FAILURE);1152 for (c = yytoken; ; c = *input->p++) {1153 switch (safe) {1154 case END_COMP:1155 if (c == '}')1156 goto jump;1157 goto semicolon;1158 case END_COND:1159 if (c == ')')1160 goto jump;1161 break;1162 case END_LDECL:1163 if (c == ',')1164 goto jump;1165 case END_DECL:1166 semicolon:1167 if (c == ';')1168 goto jump;1169 break;1170 }1171 if ((c == '\0' || c == EOFTOK) && !moreinput())1172 exit(EXIT_FAILURE);1173 }1174jump:1175 input->begin = input->p;1176 yytoken = c;1177 yytext[0] = c;1178 yytext[1] = '\0';1179 exit(EXIT_FAILURE);11801181 /*1182 * FIXME: We don't have a proper recover mechanism at this moment1183 * and we don't set the recover point ever, so executing this1184 * longjmp will generate surely a segmentation fault, so it does1185 * not make sense to do it. We just exit until we can find time1186 * to solve this problem.1187 */1188 longjmp(recover, 1);1189}