#define elif else if #define PAGES 256 #define CIEL_PAGESIZE 4096 #define MAXLEN 256 #define XSTR(e) #e #define STR(e) XSTR(e) typedef unsigned char u8; typedef signed char s8; typedef unsigned short u16; typedef signed short s16; typedef unsigned int u32; typedef signed int s32; #if defined(_WIN32) || defined(__i386) || defined(__ARM_ARCH) typedef unsigned long long u64; typedef signed long long s64; #define U64(x) (x ## ull) #else typedef unsigned long u64; typedef signed long s64; #define U64(x) (x ## ul) #endif #if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) || defined(_WIN64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) typedef u64 size_t; typedef s64 ssize_t; #else typedef u32 size_t; typedef s32 ssize_t; #endif #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L _Static_assert(sizeof(u8) == 1, "u8 is incorrect"); _Static_assert(sizeof(s8) == 1, "s8 is incorrect"); _Static_assert(sizeof(u16) == 2, "u16 is incorrect"); _Static_assert(sizeof(s16) == 2, "s16 is incorrect"); _Static_assert(sizeof(u32) == 4, "u32 is incorrect"); _Static_assert(sizeof(s32) == 4, "s32 is incorrect"); _Static_assert(sizeof(u64) == 8, "u64 is incorrect"); _Static_assert(sizeof(s64) == 8, "s64 is incorrect"); _Static_assert(sizeof(ssize_t) == sizeof(void *), "ssize_t is incorrect"); _Static_assert(sizeof(size_t) == sizeof(void *), "size_t is incorrect"); #endif #ifndef COSMOPOLITAN_H_ ssize_t read(int fildes, void *buf, size_t nbyte); ssize_t write(int fildes, const void *buf, size_t nbyte); void *malloc(size_t size); void free(void *ptr); #define NULL (void*)0 typedef int bool; #endif struct slice { char *str; s32 len; }; static bool sliceeq(struct slice a, struct slice b) { s32 i; if (a.len != b.len) return 0; if (a.str == b.str) return 1; for (i = 0; i < (a.len + 3) / 4; i++) { if (((u32 *)a.str)[i] != ((u32 *)b.str)[i]) return 0; } return 1; } static u64 udiv(u64 a, u64 b) { return b ? a / b : 0; } static s64 sdiv(s64 a, s64 b) { return b ? a / b : 0; } static u64 umod(u64 a, u64 b) { return b ? a % b : 0; } static s64 smod(s64 a, s64 b) { return b ? a % b + ((a ^ b) < 0 ? b: 0) : 0; } static u64 upow(u64 b, u64 e) { u64 r = 1; while (e) { if (e & 1) r *= b; e >>= 1, b *= b; } return r; } static s64 spow(s64 b, s64 e) { if (b == 1) return 1; elif (b == -1) return e & 1 ? -1 : 1; if (e < 0) return 0; return upow(b, e); } static u64 ushl(u64 a, u64 b) { return b < 64 ? a << b : 0; } static u64 ushr(u64 a, u64 b) { return b < 64 ? a >> b : 0; } static s64 sshl(s64 a, s64 b) { if (b < -63) return a < 0 ? -1 : 0; elif (b < 0) return a >> b; elif (b < 63) return a << b; else return 0; } static s64 sshr(s64 a, s64 b) { if (b < -63) return 0; elif (b < 0) return a << b; elif (b < 63) return a >> b; else return a < 0 ? -1 : 0; } static u64 rol(u64 a, u64 b) { return b ? (a << (b & 63)) | (a >> (64 - (b & 63))) : a; } static u64 ror(u64 a, u64 b) { return b ? (a >> (b & 63)) | (a << (64 - (b & 63))) : a; } static bool isalpha_(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } static bool isnum(char c) { return c >= '0' && c <= '9'; } static bool isident(char c, bool notfirst) { if (notfirst) return isalpha_(c) || c == '_' || isnum(c); else return isalpha_(c) || c == '_' || c == '\\'; } static bool isspace_(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static bool isopen(char c) { return c == '{' || c == '[' || c == '('; } static bool isclose(char c) { return c == ')' || c == ']' || c == '}'; } static bool isop(char c) { return c >= '!' && c <= '~' && !(isident(c, 1) || isopen(c) || isclose(c)); } static ssize_t zlen(const char *str) { ssize_t len = 0; while (str[len]) len++; return len; } static bool writeok = 1; static void write_(int fd, const char *str, ssize_t len) { if (len > 0 && writeok) writeok = write(fd, str, len) == len; } static void writestr(int fd, const char *str) { write_(fd, str, zlen(str)); } static void writepair(int fd, struct slice p) { write_(fd, p.str, p.len); } static void writech(int fd, char c) { write_(fd, &c, 1); } static void writeline(int fd, const char *str) { writestr(fd, str), writech(1, '\n'); } static void writeu64(int fd, u64 x) { char str[21], *start = str + 20; for (*start = 0; *--start = x % 10 + '0', x /= 10; ); writestr(fd, start); } static void writeint(int fd, s32 x) { writeu64(fd, x < 0 ? writech(fd, '-'), 0 - (u64)x : (u64)x); } static void writepad(int fd, s32 x, s32 digits) { while (--digits) if ((x /= 10) == 0) writech(fd, ' '); } enum result {OK, BAD, EXCEPTION, OOM}; char *pages[PAGES]; s32 pagelen = 0; struct slice iden; static enum result pushc(char c) { if (pagelen > PAGES) return OOM; if (!iden.str) iden.str = pages[pagelen++] = malloc(CIEL_PAGESIZE); if (!iden.str) return OOM; if (iden.len >= MAXLEN) return EXCEPTION; iden.str[iden.len++] = c; return OK; } static struct slice nextiden() { struct slice ret = iden; if (!iden.len) return ret; iden.str += iden.len, iden.len = 0; while ((size_t)iden.str & 3) *iden.str++ = 0; if (iden.str - pages[pagelen - 1] > CIEL_PAGESIZE - MAXLEN) iden.str = NULL; return ret; } static u32 uhash(u32 h) { return ++h, h ^= h >> 16, h *= 0xA66C52B5, h; } static u32 shash(struct slice s) { u32 h = 1; s32 i; for (i = 0; i < s.len; i++) h = uhash(h + s.str[i]); return h ? h : 1; } struct entry { u32 h; s32 len; char *str; void *v; }; struct entry *space = NULL; size_t hsize = 0, hmask = 0, hused = 0, hn = 0; enum hmode {HLOOK, HPUSH, HPULL}; static enum result hinit(size_t n) { size_t i; struct entry empty = {0}; hn = n, hsize = 1 << n, space = malloc(hsize * sizeof(struct entry)); for (i = 0; i < hsize && space; i++) space[i] = empty; return hmask = hsize - 1, hused = 0, space ? OK : OOM; } static bool entryeq(struct entry a, struct entry b) { struct slice sa, sb; sa.str = a.str, sa.len = a.len, sb.str = b.str, sb.len = b.len; return sliceeq(sa, sb); } static bool hall(enum hmode m, struct slice k, void **v, u32 h) { size_t i, j; struct entry a; a.h = h = h ? h : shash(k), a.len = k.len, a.str = k.str, a.v = v ? *v : 0; for (i = 0; i < hsize; i++) { size_t ind = (h + i) & hmask; struct entry b = space[ind]; if (!b.h && m == HPUSH) hused++, space[ind] = a; if (!b.h) return m == HPUSH; if (a.h == b.h && entryeq(a, b)) { if (m == HPULL) { for (j = 1; j < hsize; j++) { size_t jnd = (h + i + j) & hmask; if (b = space[jnd], b.h) { size_t bdib = (jnd - b.h) & hmask; if (!bdib) break; space[ind] = space[jnd], ind = jnd; } } hused--, space[ind].h = 0; } elif (m == HPUSH) space[ind].v = *v; elif (m == HLOOK) *v = b.v; return 1; } if (i) { size_t adib = (ind - a.h) & hmask, bdib = (ind - b.h) & hmask; if (bdib < adib && m == HPUSH) space[ind] = a, a = b; elif (bdib < adib) return 0; } } return 0; } static enum result hpush(struct slice k, void *v) { return hall(HPUSH, k, &v, 0) ? OK : EXCEPTION; } static bool hlook(struct slice k, void **v) { return hall(HLOOK, k, v, 0); } static enum result hpull(struct slice k) { return hall(HPULL, k, NULL, 0) ? OK : EXCEPTION; } static enum result rehash() { size_t i; struct entry *oldspace = space; enum result hres = hinit(hn + 1); if (hres != OK) return hres; for (i = 0; i < hsize / 2; i++) { struct entry e = oldspace[i]; struct slice k; k.str = e.str, k.len = e.len; if (e.h && !hall(HPUSH, k, &e.v, e.h)) return EXCEPTION; } return free(oldspace), OK; } #define OPS \ X(TOPCOM, 1, right) X(TOPSET, 2, 0) X(TOPLOR, 3, left || right) \ X(TOPLXOR, 4, !!left ^ !!right) X(TOPLAND, 5, left && right) \ X(TUNLNOT, 6, 0) \ X(TOPEQ, 7, left == right) X(TOPGE, 7, left >= right) \ X(TOPGT, 7, left > right) X(TOPLE, 7, left <= right) \ X(TOPLT, 7, left < right) X(TOPNE, 7, left != right) \ X(TOPOR, 8, left | right) X(TOPXOR, 9, left ^ right) \ X(TOPAND, 10, left & right) X(TOPSHL, 11, ushl(left, right)) \ X(TOPSHR, 11, ushr(left, right)) X(TOPADD, 12, left + right) \ X(TOPSUB, 12, left - right) X(TOPDIV, 13, udiv(left, right)) \ X(TOPMOD, 13, umod(left, right)) X(TOPMUL, 13, left * right) \ X(TUNBOOL, 14, 0) X(TUNINV, 14, 0) X(TUNNEG, 14, 0) \ X(TUNNOT, 14, 0) X(TUNPOS, 14, 0) \ X(TOPPOW, 15, upow(left, right)) X(TOPTYPE, 16, left) \ X(TOPDIV_S, 0, sdiv(left, right)) \ X(TOPGE_S, 0, (s64)left >= (s64)right) \ X(TOPGT_S, 0, (s64)left > (s64)right) \ X(TOPLE_S, 0, (s64)left <= (s64)right) \ X(TOPLT_S, 0, (s64)left < (s64)right) \ X(TOPMOD_S, 0, smod(left, right)) X(TOPPOW_S, 0, spow(left, right)) \ X(TOPSHL_S, 0, sshl(left, right)) X(TOPSHR_S, 0, sshr(left, right)) enum ttype { TTUNK, TTSPACE, TTID, TTYPE, TTNUM, TTOP, TTOPEN, TTCLOSE, TTEND }; enum ctype { TUNK, TSIGNED, TUNSIGNED, TNUMBIN, TNUMOCT, TNUMDEC, TNUMHEX, #define X(a, b, c) a, OPS CTYPE_END #undef X }; static const s8 precedence[] = { #define X(a, b, c) b, OPS 0 #undef X }; struct token { struct token *next, *prev; struct token *rpnnext; struct token *pops; s32 row, col, depth; struct slice id; enum ttype tt; enum ctype ct; u64 value, modulo; }; struct token *head = NULL, *tail = NULL, *rpnhead = NULL, *rpntail = NULL; struct token *stack = NULL; static enum result pusht(struct token tok) { struct token *htok = malloc(sizeof(struct token)); if (!htok) return OOM; *htok = tok; if (!head) head = htok; if (tail) tail->next = htok, htok->prev = tail; tail = htok; return OK; } static enum result popt(struct token *out) { if (!head) return EXCEPTION; if (out) *out = *tail; if (head == tail) free(head), head = NULL, tail = NULL; elif (tail) tail = tail->prev, free(tail->next); return OK; } static struct token *popstack() { struct token *res = stack; if (stack) stack = stack->pops, res->pops = NULL; return res; } static bool isop1(char c) { return c == '%' || c == '(' || c == ')' || c == '+' || c == ',' || c == '-' || c == '/' || c == ';'; } static bool isop2(char c) { return c == '!' || c == '&' || c == '*' || c == '<' || c == '=' || c == '>' || c == '^' || c == '|' || c == '~'; } static bool isop3(char c) { return c == '!' || c == '<' || c == '>'; } static bool isanyop(char a, char b, char c) { return isop1(b) || isop1(c) || (b == a && isop2(a)) || (b == '=' && isop3(a)); } static bool isunary(enum ctype ct) { return ct == TUNLNOT || (ct >= TUNBOOL && ct <= TUNPOS); } static bool isright(enum ctype ct) { return isunary(ct) || ct == TOPSET || ct == TOPPOW; } const char *fid = "identifiers cannot be longer than " STR(MAXLEN) " bytes", *fnotid = "left-hand operand must be an identifier", *fnan = "not a number", *fopsign = "operands have different signedness", *fmiscl = "missing \")\"", *fopmod = "operands have different modulos", *farg = "malformed argc/argv", *fall = "failed to allocate memory", *fnumpre = "invalid numeric prefix", *fstart = "invalid starting token", *fout8 = "out of range for UTF-8", *ueof = "unexpected end of file", *femp = "empty token", *fnot = "no token", *fbignum = "number too large", *fundef = "undefined", *funex = "unexpected", *fuchar = "unknown character", *funkop = "unknown operator", *finc8 = "incomplete UTF-8", *fmisop = "missing \"(\"", *fmop = "missing operand", *ffread = "read failed", *ftype = "invalid type", *finv8 = "invalid UTF-8"; u8 c0 = 0, c1 = 0, c2 = 0; s32 row = 1, col = 1; s32 utf8 = 0; char buf[4096]; ssize_t bufind = 0, bufread = 0, bytesread = 0; static void writepos(int fd) { writeint(fd, row), writech(fd, ':'), writeint(fd, col), writech(fd, ' '); } static enum result usererror(const char *str) { return writepos(2), writeline(2, str), BAD; } static enum result tokerror(const char *str, const struct token tok) { row = tok.row, col = tok.col, writepos(2), writestr(2, str); return writestr(2, ": \""), writepair(2, tok.id), writestr(2, "\"\n"), BAD; } static enum result parsedec(struct slice id, u64 *out) { s32 i, c = id.str[0]; u64 value = 0; if (id.len > 20) return EXCEPTION; if (id.len == 20 && c > '1' && c <= '9') return EXCEPTION; for (i = 0, c = id.str[i]; i < id.len; c = id.str[++i]) { if (c < '0' || c > '9') return BAD; value = value * 10 + (c - '0'); } if (id.len == 20 && value >> 63 != 1) return EXCEPTION; return *out = value, OK; } static enum result parsenum(struct token *tok) { struct slice id = tok->id; u64 value = 0; s32 i, c = id.str[0]; if (tok->ct == TNUMBIN) { if (id.len > 66) return EXCEPTION; for (i = 2, c = id.str[i]; i < id.len; c = id.str[++i]) { if (c != '0' && c != '1') return BAD; value = value * 2 + (c - '0'); } } elif (tok->ct == TNUMOCT) { if (id.len > 34) return EXCEPTION; for (i = 2, c = id.str[i]; i < id.len; c = id.str[++i]) { if (c < '0' || c > '7') return BAD; value = value * 8 + (c - '0'); } } elif (tok->ct == TNUMDEC) { enum result pres = parsedec(id, &value); if (pres != OK) return pres; } elif (tok->ct == TNUMHEX) { if (id.len > 18) return EXCEPTION; for (i = 2, c = id.str[i]; i < id.len; c = id.str[++i]) { if (c >= '0' && c <= '9') value = value * 16 + c - '0'; elif (c >= 'A' && c <= 'F') value = value * 16 + c - 'A' + 10; elif (c >= 'a' && c <= 'f') value = value * 16 + c - 'a' + 10; else return BAD; } } else return EXCEPTION; if (tok->ct != TNUMDEC && i == 2) return BAD; return tok->value = value, OK; } static enum result utfeat(u8 c) { bool cont = c >= 0x80 && c < 0xC0; if (utf8 && cont) utf8--; elif ((utf8 && !cont) || cont) return usererror(finv8); elif (c < 0x80) return usererror(fuchar); elif (c >= 0xC0 && c < 0xE0) utf8 = 1; elif (c >= 0xE0 && c < 0xF0) utf8 = 2; elif (c >= 0xF0 && c < 0xF6) utf8 = 3; else return usererror(fout8); return OK; } static bool nextchar(int fd) { if (++bufind >= bufread) { bytesread = bufread = read(fd, buf, 4096), bufind = 0; if (bufread < 1) return 0; } return c0 = buf[bufind], bytesread = 1; } enum result tokenize(int fd) { enum ttype tt = TTUNK, newtype = TTUNK; struct token tok = {0}; s32 idrow = row, idcol = col, oldutf8 = 0; while (bytesread = 0, c0 || nextchar(fd)) { if (c1 == '\r' && c0 == '\n') goto next; oldutf8 = utf8; if (isspace_(c0)) newtype = TTSPACE; elif (isident(c0, tt == TTID)) newtype = TTID; elif (isop(c0)) newtype = TTOP; elif (isopen(c0)) newtype = TTOPEN; elif (isclose(c0)) newtype = TTCLOSE; elif (isnum(c0)) newtype = TTNUM; elif (bytesread && utfeat(c0)) return BAD; else newtype = TTID; if (tt == TTNUM && newtype == TTID); elif (tt == TTUNK) tt = newtype; elif (newtype != tt || isanyop(c2, c1, c0)) break; if (tt == TTSPACE || pushc(c0) == OK); elif (iden.len >= MAXLEN) return usererror(fid); else return OOM; if (c0 == '\r' || c0 == '\n') col=1, row++; elif (c0 < 0x80 || c0 >= 0xE0) col++; next: c2 = c1, c1 = c0, c0 = 0, bytesread = 0; } if (bufread < 0) return writeline(2, ffread), EXCEPTION; elif (newtype != TTUNK); elif (bytesread == 0) tt = TTEND; if (oldutf8) return usererror(finc8); tok.id = nextiden(), tok.tt = tt, tok.row = idrow, tok.col = idcol; return pusht(tok); } enum result shunt(struct token *tok) { if (!tok) { while (stack) { if (stack->tt == TTOPEN) return tokerror(fmiscl, *stack); if (!rpntail) rpntail = popstack(); rpntail = rpntail->rpnnext = popstack(); } } elif (!rpntail) { if (tok->tt == TTID || tok->tt == TTNUM) rpnhead = rpntail = tok; elif (tok->tt == TTOPEN || (tok->tt == TTOP && isunary(tok->ct))) { tok->pops = stack, stack = tok; } else return tokerror(fstart, *tok); } else { if (tok->tt == TTID || tok->tt == TTNUM || tok->tt == TTYPE) { rpntail = rpntail->rpnnext = tok; } elif (tok->tt == TTOPEN) tok->pops = stack, stack = tok; elif (tok->tt == TTCLOSE) { while (stack && stack->tt != TTOPEN) { rpntail = rpntail->rpnnext = popstack(); } if (!stack) return tokerror(fmisop, *tok); popstack(); } elif (tok->tt == TTOP) { s32 pre = precedence[tok->ct - TOPCOM] + isright(tok->ct); while (stack && stack->tt == TTOP) { if (isunary(tok->ct) && stack->ct == TOPPOW) break; elif (pre > precedence[stack->ct - TOPCOM]) break; rpntail = rpntail->rpnnext = popstack(); } tok->pops = stack, stack = tok; } else return tokerror("TODO", *tok), EXCEPTION; } return OK; } static enum ctype decideop(char a, char b) { static const char *checks = "^^>>;\0+\0\0\0<=\0\0,\0*\0|\0**=\0<\0&&\0\0~\0" "<<\0\0%\0&\0\0\0-\0>=>\0==~~/\0!!!\0||^\0!="; static const enum ctype mapping[] = { TOPLXOR,TOPSHR,TOPTYPE,TOPADD,TUNK,TOPLE,TUNK,TOPCOM,TOPMUL,TOPOR,TOPPOW, TOPSET,TOPLT,TOPLAND,TUNK,TUNINV,TOPSHL,TUNK,TOPMOD,TOPAND,TUNK,TOPSUB, TOPGE,TOPGT,TOPEQ,TUNLNOT,TOPDIV,TUNBOOL,TUNNOT,TOPLOR,TOPXOR,TOPNE }; u32 i = uhash(uhash(0x3561F0B7 + a) + b) & 31; return (a != checks[2 * i] || b != checks[2 * i + 1]) ? TUNK : mapping[i]; } static enum ctype decideoplong(char a, char b, char c, char d) { static const char *checks = "bor\0pow\0eq\0\0le\0\0shl\0\0\0\0\0not\0or\0\0mod\0band\0\0\0\0\0\0\0" "\0mul\0\0\0\0\0\0\0\0\0bnotne\0\0pos\0shr\0bool\0\0\0\0xor\0add\0inv\0" "com\0ge\0\0lt\0\0set\0and\0neg\0sub\0div\0gt\0\0bxor\0\0\0\0type"; static const enum ctype mapping[] = { TOPOR,TOPPOW,TOPEQ,TOPLE,TOPSHL,TUNK,TUNLNOT,TOPLOR,TOPMOD,TOPAND,TUNK, TUNK,TOPMUL,TUNK,TUNK,TUNNOT,TOPNE,TUNPOS,TOPSHR,TUNBOOL,TUNK,TOPLXOR, TOPADD,TUNINV,TOPCOM,TOPGE,TOPLT,TOPSET,TOPLAND,TUNNEG,TOPSUB,TOPDIV, TOPGT,TOPXOR,TUNK,TOPTYPE }; u32 i = uhash(uhash(uhash(uhash(0xC0FF1042 + a) + b) + c) + d) % 36; const char *ch = checks + 4 * i; if (a != ch[0] || b != ch[1] || c != ch[2] || d != ch[3]) return TUNK; return mapping[i]; } enum result parse() { enum result res = OK, pres = OK, sres = OK; s32 depth = 0; bool hint_op = 0, was_op = 0, expect_unempty = 0; bool expect_type = 0, expect_nonid = 0, expect_operand = 0; while ((res = tokenize(0)) == OK) { struct token tok; s32 ss = 0; if (!tail) return writeline(2, fnot), EXCEPTION; tok = *tail; if (tok.tt == TTEND) break; elif (tok.tt == TTSPACE) goto next; elif (tok.id.len < 1) return tokerror(femp, tok), EXCEPTION; else ss = 256 * tok.id.str[0] + tok.id.str[1]; was_op = tok.tt == TTOP; if (tok.tt == TTID && expect_type && !expect_nonid) { struct slice idnum; u64 value = 0; if (tok.id.str[0] == 'u') tok.ct = TUNSIGNED; elif (tok.id.str[0] == 's') tok.ct = TSIGNED; elif (tok.id.str[0] == 'm') tok.ct = TUNSIGNED; else badtype: return tokerror(ftype, tok); idnum.str = tok.id.str + 1, idnum.len = tok.id.len - 1; if ((pres = parsedec(idnum, &value)) != OK) goto badtype; if (tok.id.str[0] != 'm' && value - 1 > 63) goto badtype; tok.modulo = (tok.id.str[0] == 'm') ? value : ushl(1, value); tok.tt = TTYPE; } elif (tok.tt == TTID && tok.id.str[0] == '\\') { char a = tok.id.str[1], b = tok.id.str[2], c = tok.id.str[3]; char d = tok.id.len > 4 ? tok.id.str[4] : '\0'; tok.tt = TTOP, tok.ct = decideoplong(a, b, c, d); if (tok.ct == TUNK || tok.id.len > 5) return tokerror(funkop, tok); } elif (tok.tt == TTID) { } elif (tok.tt == TTOP) { tok.ct = decideop(tok.id.str[0], tok.id.str[1]); if (tok.ct == TOPADD && !hint_op) tok.ct = TUNPOS; elif (tok.ct == TOPSUB && !hint_op) tok.ct = TUNNEG; elif (tok.ct == TUNK || tok.id.len > 2) return tokerror(funkop, tok); } elif (tok.tt == TTNUM && !expect_nonid) { if (ss == 256 * '0' + 'b') tok.ct = TNUMBIN; elif (ss == 256 * '0' + 'o') tok.ct = TNUMOCT; elif (ss == 256 * '0' + 'x') tok.ct = TNUMHEX; elif (ss == 256 * '0') tok.ct = TNUMDEC; elif (tok.id.str[0] == '0') return tokerror(fnumpre, tok); else tok.ct = TNUMDEC; pres = parsenum(&tok); if (pres == BAD) return tokerror(fnan, tok); elif (pres == EXCEPTION) return tokerror(fbignum, tok); } elif (tok.tt == TTOPEN) { if (ss == 256 * '(') depth++; else return tokerror(fuchar, tok); } elif (tok.tt == TTCLOSE) { if (ss == 256 * ')') depth--; else return tokerror(fuchar, tok); if (expect_unempty) goto unexpected; } else goto unexpected; next: tok.depth = depth; *tail = tok; if (tok.tt == TTSPACE) continue; if (expect_nonid && isunary(tok.ct)) goto unexpected; if (expect_type && tok.tt != TTYPE) goto unexpected; expect_nonid = tok.tt == TTID || tok.tt == TTNUM; expect_type = tok.ct == TOPTYPE; expect_unempty = tok.tt == TTOPEN; hint_op = expect_nonid || tok.tt == TTCLOSE || tok.tt == TTYPE; if (expect_nonid || tok.tt == TTYPE) expect_operand = 0; elif (isunary(tok.ct)) expect_operand = 1; elif ((was_op || tok.tt == TTCLOSE) && expect_operand) goto unexpected; elif (was_op) expect_operand = 1; if ((sres = shunt(tail)) != OK) return sres; } if (res == OK) { if (expect_operand) return usererror(ueof); return shunt(NULL); } return (res == OOM) ? writeline(2, fall), OOM : res; unexpected: return tokerror(funex, *tail); } static enum result operate1(struct token op, struct token *out) { struct token *ta = popstack(); u64 value, right = ta->value; switch (op.ct) { case TUNLNOT: value = !right; break; case TUNNOT: value = !right; break; case TUNBOOL: value = !!right; break; case TUNINV: value = ~right; break; case TUNPOS: value = right; break; case TUNNEG: value = 0 - right; break; default: return tokerror("TODO unary operator", op), EXCEPTION; } out->value = value, out->ct = ta->ct, out->modulo = ta->modulo; return OK; } static enum ctype signop(enum ctype ct) { switch (ct) { case TOPLT: return TOPLT_S; case TOPGT: return TOPGT_S; case TOPLE: return TOPLE_S; case TOPGE: return TOPGE_S; case TOPSHL: return TOPSHL_S; case TOPSHR: return TOPSHR_S; case TOPDIV: return TOPDIV_S; case TOPMOD: return TOPMOD_S; case TOPPOW: return TOPPOW_S; default: return ct; } } static enum result operate2(struct token op, struct token *out) { struct token *tb = popstack(), *ta = popstack(), *other = tb; u64 value, left = ta->value, right = tb->value; u64 modleft = ta->modulo, modright = tb->modulo; u64 signleft = ta->ct == TSIGNED, signright = tb->ct == TSIGNED; bool inherit = (ta->ct >= TNUMBIN && ta->ct <= TNUMHEX) || (tb->ct >= TNUMBIN && tb->ct <= TNUMHEX); if (op.ct == TOPTYPE || inherit); elif (modleft != modright) return tokerror(fopmod, op); elif (signleft != signright) return tokerror(fopsign, op); if (signleft || signright) op.ct = signop(op.ct); switch (op.ct) { #define X(a, b, c) case a: value = c; break; OPS #undef X default: return tokerror("TODO binary operator", op), EXCEPTION; } if (other->ct >= TNUMBIN && other->ct <= TNUMHEX) other = ta; out->value = value, out->ct = other->ct, out->modulo = other->modulo; return OK; } static enum result assign(struct token *out) { enum result hres; struct token *tb = popstack(), *ta = popstack(); if (!hlook(ta->id, (void **)&ta)) { if ((hres = hpush(ta->id, ta)) != OK) return hres; if (hused >= hsize * 3 / 4 && (hres = rehash()) == OOM) { return writeline(2, fall), OOM; } elif (hres != OK) return hres; } elif (ta->modulo != tb->modulo) return tokerror(fopmod, *ta); elif (ta->ct == TSIGNED && tb->ct == TUNSIGNED) return tokerror(fopsign, *ta); elif (ta->ct == TUNSIGNED && tb->ct == TSIGNED) return tokerror(fopsign, *ta); *out = *ta, out->tt = TTNUM; ta->value = tb->value, ta->modulo = tb->modulo, ta->ct = tb->ct; return OK; } static void modulate(struct token *tok) { u64 halfmod = tok->modulo ? tok->modulo / 2 : U64(1) << 63; tok->value = tok->modulo ? tok->value % tok->modulo : tok->value; if (tok->ct == TSIGNED && tok->value >= halfmod) tok->value -= tok->modulo; } enum result evaluate() { struct token *p = rpnhead; enum result ores = hinit(1); if (ores == OOM) return writeline(2, fall), OOM; while (p) { struct token tok = *p, *v; if (tok.tt == TTNUM || tok.tt == TTYPE || tok.tt == TTID) { p->pops = stack, stack = p; } elif (tok.tt == TTOP) { struct token out = {0}; out.tt = TTNUM, out.ct = TUNSIGNED; if (!stack) return tokerror(fmop, tok); if (stack->tt == TTID) { if (!hlook(stack->id, (void **)&v)) { return tokerror(fundef, *stack); } stack->value = v->value, stack->modulo = v->modulo; stack->ct = v->ct; } if (isunary(tok.ct)) { if ((ores = operate1(tok, &out)) != OK) return ores; } elif (!stack->pops) return tokerror(fmop, tok); elif (tok.ct == TOPSET) { if (stack->pops->tt != TTID) return tokerror(fnotid, tok); if ((ores = assign(&out)) != OK) return ores; } elif (stack->pops->tt == TTID) { if (!hlook(stack->pops->id, (void **)&v)) { return tokerror(fundef, *stack->pops); } stack->pops->value = v->value, stack->pops->modulo = v->modulo; stack->pops->ct = v->ct; if ((ores = operate2(tok, &out)) != OK) return ores; } elif ((ores = operate2(tok, &out)) != OK) return ores; modulate(&out); out.pops = stack; if (pusht(out) == OOM) return writeline(2, fall), OOM; stack = tail; } else return tokerror("TODO type", tok), EXCEPTION; p = tok.rpnnext; } if (stack) writeu64(1, stack->value), writech(1, '\n'); if (stack && stack->pops) return tokerror(funex, *stack->pops), EXCEPTION; return OK; } int main(int argc, char **argv) { int ret = 0; if (argc <= 0 || !argv || !argv[0]) return writeline(2, farg), BAD; pages[0] = iden.str = NULL; iden.len = 0; if ((ret = parse()) == OK) ret = evaluate(); if (space) free(space); while (popt(NULL) == OK); while (pagelen-->0) free(pages[pagelen]); return ret; }