aboutsummaryrefslogtreecommitdiff
path: root/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'expr.c')
-rw-r--r--expr.c874
1 files changed, 874 insertions, 0 deletions
diff --git a/expr.c b/expr.c
new file mode 100644
index 0000000..92cd4d9
--- /dev/null
+++ b/expr.c
@@ -0,0 +1,874 @@
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "util.h"
+#include "decl.h"
+#include "eval.h"
+#include "expr.h"
+#include "init.h"
+#include "pp.h"
+#include "scope.h"
+#include "token.h"
+#include "type.h"
+
+static struct expression *
+mkexpr(enum expressionkind k, struct type *t, enum expressionflags flags)
+{
+ struct expression *e;
+
+ e = xmalloc(sizeof(*e));
+ e->type = flags & EXPRFLAG_LVAL || !t ? t : typeunqual(t, NULL);
+ e->flags = flags;
+ e->kind = k;
+ e->next = NULL;
+
+ return e;
+}
+
+static struct expression *
+mkconstexpr(struct type *t, uint64_t n)
+{
+ struct expression *e;
+
+ e = mkexpr(EXPRCONST, t, 0);
+ e->constant.i = n;
+
+ return e;
+}
+
+void
+delexpr(struct expression *e)
+{
+ free(e);
+}
+
+/* 6.3.2.1 Conversion of arrays and function designators */
+static struct expression *
+decay(struct expression *e)
+{
+ struct expression *old = e;
+
+ switch (e->type->kind) {
+ case TYPEARRAY:
+ e = mkexpr(EXPRUNARY, mkpointertype(old->type->base), EXPRFLAG_DECAYED);
+ e->unary.op = TBAND;
+ e->unary.base = old;
+ break;
+ case TYPEFUNC:
+ e = mkexpr(EXPRUNARY, mkpointertype(old->type), EXPRFLAG_DECAYED);
+ e->unary.op = TBAND;
+ e->unary.base = old;
+ break;
+ }
+
+ return e;
+}
+
+static struct type *
+inttype(uint64_t val, bool decimal, char *end)
+{
+ static struct {
+ struct type *type;
+ const char *end1, *end2;
+ } limits[] = {
+ {&typeint, "", NULL},
+ {&typeuint, "u", NULL},
+ {&typelong, "l", NULL},
+ {&typeulong, "ul", "lu"},
+ {&typellong, "ll", NULL},
+ {&typeullong, "ull", "llu"},
+ };
+ static const uint64_t max[] = {
+ [1] = 0xff,
+ [2] = 0xffff,
+ [4] = 0xffffffff,
+ [8] = 0xffffffffffffffff,
+ };
+ struct type *t;
+ size_t i, step;
+
+ for (i = 0; end[i]; ++i)
+ end[i] = tolower(end[i]);
+ for (i = 0; i < LEN(limits); ++i) {
+ if (strcmp(end, limits[i].end1) == 0)
+ break;
+ if (limits[i].end2 && strcmp(end, limits[i].end2) == 0)
+ break;
+ }
+ if (i == LEN(limits))
+ error(&tok.loc, "invalid integer constant suffix '%s'", end);
+ step = i % 2 || decimal ? 2 : 1;
+ for (; i < LEN(limits); i += step) {
+ t = limits[i].type;
+ if (val <= max[t->size] >> t->basic.issigned)
+ return t;
+ }
+ error(&tok.loc, "no suitable type for constant '%s'", tok.lit);
+}
+
+static int
+isodigit(int c)
+{
+ return '0' <= c && c <= '8';
+}
+
+static int
+unescape(char **p)
+{
+ int c;
+ char *s = *p;
+
+ if (*s == '\\') {
+ ++s;
+ switch (*s) {
+ case '\'':
+ case '"':
+ case '?':
+ case '\\': c = *s; ++s; break;
+ case 'a': c = '\a'; ++s; break;
+ case 'b': c = '\b'; ++s; break;
+ case 'f': c = '\f'; ++s; break;
+ case 'n': c = '\n'; ++s; break;
+ case 'r': c = '\r'; ++s; break;
+ case 't': c = '\t'; ++s; break;
+ case 'v': c = '\v'; ++s; break;
+ case 'x':
+ ++s;
+ assert(isxdigit(*s));
+ c = 0;
+ do c = c * 16 + (*s > '9' ? tolower(*s) - 'a' : *s - '0');
+ while (isxdigit(*++s));
+ break;
+ default:
+ assert(isodigit(*s));
+ c = 0;
+ do c = c * 8 + (*s - '0');
+ while (isodigit(*++s));
+ }
+ } else {
+ c = *s++;
+ }
+ *p = s;
+ return c;
+}
+
+/* 6.5 Expressions */
+static struct expression *
+primaryexpr(struct scope *s)
+{
+ struct expression *e;
+ struct declaration *d;
+ char *src, *dst, *end;
+ int base;
+
+ switch (tok.kind) {
+ case TIDENT:
+ d = scopegetdecl(s, tok.lit, 1);
+ if (!d)
+ error(&tok.loc, "undeclared identifier: %s", tok.lit);
+ e = mkexpr(EXPRIDENT, d->type, d->kind == DECLOBJECT ? EXPRFLAG_LVAL : 0);
+ e->ident.decl = d;
+ if (d->kind != DECLBUILTIN)
+ e = decay(e);
+ next();
+ break;
+ case TSTRINGLIT:
+ e = mkexpr(EXPRSTRING, mkarraytype(&typechar, 0), EXPRFLAG_LVAL);
+ e->string.size = 0;
+ e->string.data = NULL;
+ do {
+ e->string.data = xreallocarray(e->string.data, e->string.size + strlen(tok.lit), 1);
+ dst = e->string.data + e->string.size;
+ for (src = tok.lit + 1; *src != '"'; ++dst)
+ *dst = unescape(&src);
+ e->string.size = dst - e->string.data;
+ next();
+ } while (tok.kind == TSTRINGLIT);
+ e->type->array.length = e->string.size + 1;
+ e->type->size = e->type->array.length * e->type->base->size;
+ e = decay(e);
+ break;
+ case TCHARCONST:
+ src = tok.lit + 1;
+ e = mkconstexpr(&typeint, unescape(&src));
+ if (*src != '\'')
+ error(&tok.loc, "character constant contains more than one character: %c", *src);
+ next();
+ break;
+ case TNUMBER:
+ e = mkexpr(EXPRCONST, NULL, 0);
+ base = tok.lit[0] != '0' ? 10 : tolower(tok.lit[1]) == 'x' ? 16 : 8;
+ if (strpbrk(tok.lit, base == 16 ? ".pP" : ".eE")) {
+ /* floating constant */
+ errno = 0;
+ e->constant.f = strtod(tok.lit, &end);
+ if (errno)
+ error(&tok.loc, "invalid floating constant '%s': %s", tok.lit, strerror(errno));
+ if (!end[0])
+ e->type = &typedouble;
+ else if (tolower(end[0]) == 'f' && !end[1])
+ e->type = &typefloat;
+ else if (tolower(end[0]) == 'l' && !end[1])
+ e->type = &typelongdouble;
+ else
+ error(&tok.loc, "invalid floating constant suffix '%s'", *end);
+ } else {
+ /* integer constant */
+ errno = 0;
+ e->constant.i = strtoull(tok.lit, &end, 0);
+ if (errno)
+ error(&tok.loc, "invalid integer constant '%s': %s", tok.lit, strerror(errno));
+ e->type = inttype(e->constant.i, base == 10, end);
+ }
+ next();
+ break;
+ case TLPAREN:
+ next();
+ e = expr(s);
+ expect(TRPAREN, "after expression");
+ break;
+ case T_GENERIC:
+ error(&tok.loc, "generic selection is not yet supported");
+ default:
+ error(&tok.loc, "expected primary expression");
+ }
+
+ return e;
+}
+
+static void
+lvalueconvert(struct expression *e)
+{
+ e->type = typeunqual(e->type, NULL);
+ e->flags &= ~EXPRFLAG_LVAL;
+}
+
+static struct type *
+commonreal(struct expression **e1, struct expression **e2)
+{
+ struct type *t;
+
+ t = typecommonreal((*e1)->type, (*e2)->type);
+ *e1 = exprconvert(*e1, t);
+ *e2 = exprconvert(*e2, t);
+
+ return t;
+}
+
+static struct expression *
+mkbinaryexpr(struct location *loc, enum tokenkind op, struct expression *l, struct expression *r)
+{
+ struct expression *e;
+ struct type *t = NULL;
+ enum typeproperty lp, rp;
+
+ lvalueconvert(l);
+ lvalueconvert(r);
+ lp = typeprop(l->type);
+ rp = typeprop(r->type);
+ switch (op) {
+ case TLOR:
+ case TLAND:
+ if (!(lp & PROPSCALAR))
+ error(loc, "left-hand-side of logical operator must be scalar");
+ if (!(rp & PROPSCALAR))
+ error(loc, "right-hand-side of logical operator must be scalar");
+ l = exprconvert(l, &typebool);
+ r = exprconvert(r, &typebool);
+ t = &typeint;
+ break;
+ case TEQL:
+ case TNEQ:
+ case TLESS:
+ case TGREATER:
+ case TLEQ:
+ case TGEQ:
+ if (lp & PROPARITH && rp & PROPARITH) {
+ commonreal(&l, &r);
+ } else {
+ // XXX: check pointer types
+ }
+ t = &typeint;
+ break;
+ case TBOR:
+ case TXOR:
+ case TBAND:
+ t = commonreal(&l, &r);
+ break;
+ case TADD:
+ if (lp & PROPARITH && rp & PROPARITH) {
+ t = commonreal(&l, &r);
+ } else if (l->type->kind == TYPEPOINTER && rp & PROPINT) {
+ t = l->type;
+ r = mkbinaryexpr(loc, TMUL, exprconvert(r, &typeulong), mkconstexpr(&typeulong, t->base->size));
+ } else if (lp & PROPINT && r->type->kind == TYPEPOINTER) {
+ t = r->type;
+ } else {
+ error(loc, "invalid operands to '+' operator");
+ }
+ break;
+ case TSUB:
+ if (lp & PROPARITH && rp & PROPARITH) {
+ t = commonreal(&l, &r);
+ } else if (l->type->kind == TYPEPOINTER && rp & PROPINT) {
+ t = l->type;
+ r = mkbinaryexpr(loc, TMUL, exprconvert(r, &typeulong), mkconstexpr(&typeulong, t->base->size));
+ } else if (l->type->kind == TYPEPOINTER && r->type->kind == TYPEPOINTER && typecompatible(typeunqual(l->type->base, NULL), typeunqual(r->type->base, NULL))) {
+ l = mkbinaryexpr(loc, TDIV, exprconvert(l, &typeulong), mkconstexpr(&typeulong, l->type->base->size));
+ r = mkbinaryexpr(loc, TDIV, exprconvert(r, &typeulong), mkconstexpr(&typeulong, r->type->base->size));
+ t = &typelong;
+ } else {
+ error(loc, "invalid operands to '-' operator");
+ }
+ break;
+ case TMOD:
+ if (!(lp & PROPINT) || !(rp & PROPINT))
+ error(loc, "operands to '%%' operator must be integer");
+ t = commonreal(&l, &r);
+ break;
+ case TMUL:
+ case TDIV:
+ if (!(lp & PROPARITH) || !(rp & PROPARITH))
+ error(loc, "operands to '%c' operator must be arithmetic", op == TMUL ? '*' : '/');
+ t = commonreal(&l, &r);
+ break;
+ case TSHL:
+ case TSHR:
+ if (!(lp & PROPINT) || !(rp & PROPINT))
+ error(loc, "operands to '%s' operator must be integer", op == TSHL ? "<<" : ">>");
+ l = exprconvert(l, typeintpromote(l->type));
+ r = exprconvert(r, typeintpromote(r->type));
+ t = l->type;
+ break;
+ default:
+ fatal("internal error: unknown binary operator %d", op);
+ }
+ e = mkexpr(EXPRBINARY, t, 0);
+ e->binary.op = op;
+ e->binary.l = l;
+ e->binary.r = r;
+
+ return e;
+}
+
+static struct expression *
+postfixexpr(struct scope *s, struct expression *r)
+{
+ struct expression *e, *arr, *idx, *tmp, **end;
+ struct type *t;
+ enum typequalifier tq;
+ struct parameter *p;
+ struct member *m;
+ char *name;
+ enum tokenkind op;
+ bool lvalue;
+
+ if (!r)
+ r = primaryexpr(s);
+ for (;;) {
+ switch (tok.kind) {
+ case TLBRACK: /* subscript */
+ next();
+ arr = r;
+ idx = expr(s);
+ if (arr->type->kind != TYPEPOINTER) {
+ if (idx->type->kind != TYPEPOINTER)
+ error(&tok.loc, "either array or index must be pointer type");
+ tmp = arr;
+ arr = idx;
+ idx = tmp;
+ }
+ if (arr->type->base->incomplete)
+ error(&tok.loc, "array is pointer to incomplete type");
+ lvalueconvert(idx);
+ if (!(typeprop(idx->type) & PROPINT))
+ error(&tok.loc, "index is not an integer type");
+ tmp = mkbinaryexpr(&tok.loc, TADD, arr, idx);
+
+ e = mkexpr(EXPRUNARY, arr->type->base, EXPRFLAG_LVAL);
+ e->unary.op = TMUL;
+ e->unary.base = tmp;
+ expect(TRBRACK, "after array index");
+ e = decay(e);
+ break;
+ case TLPAREN: /* function call */
+ next();
+ if (r->kind == EXPRIDENT && r->ident.decl->kind == DECLBUILTIN) {
+ if (r->ident.decl == &builtinvastart) {
+ e = mkexpr(EXPRBUILTIN, &typevoid, 0);
+ e->builtin.kind = BUILTINVASTART;
+ e->builtin.arg = exprconvert(assignexpr(s), &typevalistptr);
+ expect(TCOMMA, "after va_list");
+ free(expect(TIDENT, "after ','"));
+ // XXX: check that this was actually a parameter name?
+ expect(TRPAREN, "after parameter identifier");
+ } else if (r->ident.decl == &builtinvaend) {
+ e = mkexpr(EXPRBUILTIN, &typevoid, 0);
+ e->builtin.kind = BUILTINVAEND;
+ exprconvert(assignexpr(s), &typevalistptr);
+ expect(TRPAREN, "after va_list");
+ } else if (r->ident.decl == &builtinoffsetof) {
+ t = typename(s);
+ expect(TCOMMA, "after type name");
+ name = expect(TIDENT, "after ','");
+ if (t->kind != TYPESTRUCT && t->kind != TYPEUNION)
+ error(&tok.loc, "type is not a struct/union type");
+ m = typemember(t, name);
+ if (!m)
+ error(&tok.loc, "struct/union has no member named '%s'", name);
+ e = mkconstexpr(&typeulong, m->offset);
+ free(name);
+ expect(TRPAREN, "after member name");
+ } else {
+ fatal("internal error; unknown builtin");
+ }
+ break;
+ }
+ lvalueconvert(r);
+ if (r->type->kind != TYPEPOINTER || r->type->base->kind != TYPEFUNC)
+ error(&tok.loc, "called object is not a function");
+ t = r->type->base;
+ e = mkexpr(EXPRCALL, t->base, 0);
+ e->call.func = r;
+ e->call.args = NULL;
+ e->call.nargs = 0;
+ p = t->func.params;
+ end = &e->call.args;
+ for (;;) {
+ if (tok.kind == TRPAREN)
+ break;
+ if (e->call.args)
+ expect(TCOMMA, "or ')' after function call argument");
+ if (!p && !t->func.isvararg && t->func.isprototype)
+ error(&tok.loc, "too many arguments for function call");
+ *end = assignexpr(s);
+ if (!t->func.isprototype || (t->func.isvararg && !p))
+ *end = exprconvert(*end, typeargpromote((*end)->type));
+ else
+ *end = exprconvert(*end, p->type);
+ end = &(*end)->next;
+ ++e->call.nargs;
+ if (p)
+ p = p->next;
+ }
+ if (!t->func.isprototype && p)
+ error(&tok.loc, "not enough arguments for function call");
+ e = decay(e);
+ next();
+ break;
+ case TPERIOD:
+ e = mkexpr(EXPRUNARY, mkpointertype(r->type), 0);
+ e->unary.op = TBAND;
+ e->unary.base = r;
+ r = e;
+ /* fallthrough */
+ case TARROW:
+ op = tok.kind;
+ if (r->type->kind != TYPEPOINTER)
+ error(&tok.loc, "arrow operator must be applied to pointer to struct/union");
+ tq = QUALNONE;
+ t = typeunqual(r->type->base, &tq);
+ if (t->kind != TYPESTRUCT && t->kind != TYPEUNION)
+ error(&tok.loc, "arrow operator must be applied to pointer to struct/union");
+ next();
+ if (tok.kind != TIDENT)
+ error(&tok.loc, "expected identifier after '->' operator");
+ lvalue = op == TARROW || r->unary.base->flags & EXPRFLAG_LVAL;
+ r = exprconvert(r, mkpointertype(&typechar));
+ m = typemember(t, tok.lit);
+ if (!m)
+ error(&tok.loc, "struct/union has no member named '%s'", tok.lit);
+ r = mkbinaryexpr(&tok.loc, TADD, r, mkconstexpr(&typeulong, m->offset));
+ r = exprconvert(r, mkpointertype(mkqualifiedtype(m->type, tq)));
+ e = mkexpr(EXPRUNARY, r->type->base, lvalue ? EXPRFLAG_LVAL : 0);
+ e->unary.op = TMUL;
+ e->unary.base = r;
+ e = decay(e);
+ next();
+ break;
+ case TINC:
+ case TDEC:
+ e = mkexpr(EXPRINCDEC, r->type, 0);
+ e->incdec.op = tok.kind;
+ e->incdec.base = r;
+ e->incdec.post = 1;
+ next();
+ break;
+ default:
+ return r;
+ }
+ r = e;
+ }
+}
+
+static struct expression *castexpr(struct scope *);
+
+static struct expression *
+unaryexpr(struct scope *s)
+{
+ enum tokenkind op;
+ struct expression *e, *l;
+ struct type *t;
+
+ op = tok.kind;
+ switch (op) {
+ case TINC:
+ case TDEC:
+ next();
+ l = unaryexpr(s);
+ if (!(l->flags & EXPRFLAG_LVAL))
+ error(&tok.loc, "operand of %srement operator must be an lvalue", op == TINC ? "inc" : "dec");
+ /*
+ if (l->qualifiers & QUALCONST)
+ error(&tok.loc, "operand of %srement operator is const qualified", op == TINC ? "inc" : "dec");
+ */
+ e = mkexpr(EXPRINCDEC, l->type, 0);
+ e->incdec.op = op;
+ e->incdec.base = l;
+ e->incdec.post = 0;
+ break;
+ case TBAND:
+ next();
+ e = castexpr(s);
+ if (!(e->flags & EXPRFLAG_DECAYED)) {
+ l = e;
+ e = mkexpr(EXPRUNARY, NULL, 0);
+ e->unary.op = op;
+ e->unary.base = l;
+ e->type = mkpointertype(l->type);
+ }
+ break;
+ case TMUL:
+ next();
+ l = castexpr(s);
+ if (l->type->kind != TYPEPOINTER)
+ error(&tok.loc, "cannot dereference non-pointer");
+ e = mkexpr(EXPRUNARY, l->type->base, EXPRFLAG_LVAL);
+ e->unary.op = op;
+ e->unary.base = l;
+ e = decay(e);
+ break;
+ case TADD:
+ next();
+ e = castexpr(s);
+ e = exprconvert(e, typeintpromote(e->type));
+ break;
+ case TSUB:
+ next();
+ e = castexpr(s);
+ e = exprconvert(e, typeintpromote(e->type));
+ e = mkbinaryexpr(&tok.loc, TSUB, mkconstexpr(&typeint, 0), e);
+ break;
+ case TBNOT:
+ next();
+ e = castexpr(s);
+ e = exprconvert(e, typeintpromote(e->type));
+ e = mkbinaryexpr(&tok.loc, TXOR, e, mkconstexpr(e->type, -1));
+ break;
+ case TLNOT:
+ next();
+ e = castexpr(s);
+ lvalueconvert(e);
+ if (!(typeprop(e->type) & PROPSCALAR))
+ error(&tok.loc, "operator '!' must have scalar operand");
+ e = mkbinaryexpr(&tok.loc, TEQL, e, mkconstexpr(&typeint, 0));
+ break;
+ case TSIZEOF:
+ case T_ALIGNOF:
+ next();
+ if (consume(TLPAREN)) {
+ t = typename(s);
+ if (!t) {
+ e = expr(s);
+ if (e->flags & EXPRFLAG_DECAYED)
+ e = e->unary.base;
+ t = e->type;
+ }
+ expect(TRPAREN, "after type name");
+ } else if (op == TSIZEOF) {
+ e = unaryexpr(s);
+ if (e->flags & EXPRFLAG_DECAYED)
+ e = e->unary.base;
+ t = e->type;
+ } else {
+ error(&tok.loc, "expected ')' after '_Alignof'");
+ }
+ e = mkconstexpr(&typeulong, op == TSIZEOF ? t->size : t->align);
+ break;
+ default:
+ e = postfixexpr(s, NULL);
+ }
+
+ return e;
+}
+
+static struct expression *
+castexpr(struct scope *s)
+{
+ struct type *t;
+ struct expression *r, *e, **end;
+
+ end = &r;
+ while (consume(TLPAREN)) {
+ t = typename(s);
+ if (!t) {
+ e = expr(s);
+ expect(TRPAREN, "after expression to match '('");
+ *end = postfixexpr(s, e);
+ return r;
+ }
+ expect(TRPAREN, "after type name");
+ if (tok.kind == TLBRACE) {
+ e = mkexpr(EXPRCOMPOUND, t, EXPRFLAG_LVAL);
+ e->compound.init = parseinit(s, t);
+ e = decay(e);
+ *end = postfixexpr(s, e);
+ return r;
+ }
+ e = mkexpr(EXPRCAST, t, 0);
+ // XXX check types 6.5.4
+ *end = e;
+ end = &e->cast.e;
+ }
+ *end = unaryexpr(s);
+
+ return r;
+}
+
+static int
+isequality(enum tokenkind t)
+{
+ return t == TEQL || t == TNEQ;
+}
+
+static int
+isrelational(enum tokenkind t)
+{
+ return t == TLESS || t == TGREATER || t == TLEQ || t == TGEQ;
+}
+
+static int
+isshift(enum tokenkind t)
+{
+ return t == TSHL || t == TSHR;
+}
+
+static int
+isadditive(enum tokenkind t)
+{
+ return t == TADD || t == TSUB;
+}
+
+static int
+ismultiplicative(enum tokenkind t)
+{
+ return t == TMUL || t == TDIV || t == TMOD;
+}
+
+static struct expression *
+binaryexpr(struct scope *s, size_t i)
+{
+ static const struct {
+ int tok;
+ int (*fn)(enum tokenkind);
+ } prec[] = {
+ // XXX: bitmask?
+ {.tok = TLOR},
+ {.tok = TLAND},
+ {.tok = TBOR},
+ {.tok = TXOR},
+ {.tok = TBAND},
+ {.fn = isequality},
+ {.fn = isrelational},
+ {.fn = isshift},
+ {.fn = isadditive},
+ {.fn = ismultiplicative},
+ };
+ struct expression *e, *l, *r;
+ struct location loc;
+ enum tokenkind op;
+
+ if (i >= LEN(prec))
+ return castexpr(s);
+ l = binaryexpr(s, i + 1);
+ while (tok.kind == prec[i].tok || (prec[i].fn && prec[i].fn(tok.kind))) {
+ op = tok.kind;
+ loc = tok.loc;
+ next();
+ r = binaryexpr(s, i + 1);
+ e = mkbinaryexpr(&loc, op, l, r);
+ l = e;
+ }
+
+ return l;
+}
+
+static bool
+nullpointer(struct expression *e)
+{
+ if (e->kind != EXPRCONST)
+ return false;
+ if (!(typeprop(e->type) & PROPINT) && (e->type->kind != TYPEPOINTER || e->type->base != &typevoid))
+ return false;
+ return e->constant.i == 0;
+}
+
+static struct expression *
+condexpr(struct scope *s)
+{
+ struct expression *r, *e;
+ struct type *t, *f;
+ enum typequalifier tq;
+
+ r = binaryexpr(s, 0);
+ if (!consume(TQUESTION))
+ return r;
+ e = mkexpr(EXPRCOND, NULL, 0);
+ e->cond.e = exprconvert(r, &typebool);
+ e->cond.t = expr(s);
+ expect(TCOLON, "in conditional expression");
+ e->cond.f = condexpr(s);
+ lvalueconvert(e->cond.t);
+ lvalueconvert(e->cond.f);
+ t = e->cond.t->type;
+ f = e->cond.f->type;
+ if (t == f) {
+ e->type = t;
+ } else if (typeprop(t) & PROPARITH && typeprop(f) & PROPARITH) {
+ e->type = commonreal(&e->cond.t, &e->cond.f);
+ } else if (t == &typevoid && f == &typevoid) {
+ e->type = &typevoid;
+ } else {
+ e->cond.t = eval(e->cond.t);
+ e->cond.f = eval(e->cond.f);
+ if (nullpointer(e->cond.t) && f->kind == TYPEPOINTER) {
+ e->type = f;
+ } else if (nullpointer(e->cond.f) && t->kind == TYPEPOINTER) {
+ e->type = t;
+ } else if (t->kind == TYPEPOINTER && f->kind == TYPEPOINTER) {
+ tq = QUALNONE;
+ t = typeunqual(t->base, &tq);
+ f = typeunqual(f->base, &tq);
+ if (t == &typevoid || f == &typevoid) {
+ e->type = &typevoid;
+ } else {
+ if (!typecompatible(t, f))
+ error(&tok.loc, "operands of conditional operator must have compatible types");
+ e->type = typecomposite(t, f);
+ }
+ e->type = mkpointertype(mkqualifiedtype(e->type, tq));
+ } else {
+ error(&tok.loc, "invalid operands to conditional operator");
+ }
+ }
+
+ return e;
+}
+
+struct expression *
+constexpr(struct scope *s)
+{
+ return eval(condexpr(s));
+}
+
+uint64_t
+intconstexpr(struct scope *s)
+{
+ struct expression *e;
+
+ e = constexpr(s);
+ if (e->kind != EXPRCONST || !(typeprop(e->type) & PROPINT))
+ error(&tok.loc, "not an integer constant expression");
+ return e->constant.i;
+}
+
+struct expression *
+assignexpr(struct scope *s)
+{
+ struct expression *e, *l, *r, *tmp = NULL, **res = &e;
+ enum tokenkind op;
+
+ l = condexpr(s);
+ if (l->kind == EXPRBINARY || l->kind == EXPRCOMMA || l->kind == EXPRCAST)
+ return l;
+ switch (tok.kind) {
+ case TASSIGN: op = TNONE; break;
+ case TMULASSIGN: op = TMUL; break;
+ case TDIVASSIGN: op = TDIV; break;
+ case TMODASSIGN: op = TMOD; break;
+ case TADDASSIGN: op = TADD; break;
+ case TSUBASSIGN: op = TSUB; break;
+ case TSHLASSIGN: op = TSHL; break;
+ case TSHRASSIGN: op = TSHR; break;
+ case TBANDASSIGN: op = TBAND; break;
+ case TXORASSIGN: op = TXOR; break;
+ case TBORASSIGN: op = TBOR; break;
+ default:
+ return l;
+ }
+ next();
+ r = assignexpr(s);
+ if (op) {
+ /* rewrite `E1 OP= E2` as `T = &E1, *T = *T OP E2`, where T is a temporary slot */
+ tmp = mkexpr(EXPRTEMP, mkpointertype(l->type), EXPRFLAG_LVAL);
+ tmp->temp = NULL;
+ e = mkexpr(EXPRCOMMA, l->type, 0);
+ e->comma.exprs = mkexpr(EXPRASSIGN, tmp->type, 0);
+ e->comma.exprs->assign.l = tmp;
+ e->comma.exprs->assign.r = mkexpr(EXPRUNARY, e->type, 0);
+ e->comma.exprs->assign.r->unary.op = TBAND;
+ e->comma.exprs->assign.r->unary.base = l;
+ res = &e->comma.exprs->next;
+ l = mkexpr(EXPRUNARY, l->type, 0);
+ l->unary.op = TMUL;
+ l->unary.base = tmp;
+ r = mkbinaryexpr(&tok.loc, op, l, r);
+ }
+ r = exprconvert(r, l->type);
+ *res = mkexpr(EXPRASSIGN, l->type, 0);
+ (*res)->assign.l = l;
+ (*res)->assign.r = r;
+
+ return e;
+}
+
+struct expression *
+expr(struct scope *s)
+{
+ struct expression *r, *e, **end;
+
+ end = &r;
+ for (;;) {
+ e = assignexpr(s);
+ *end = e;
+ end = &e->next;
+ if (tok.kind != TCOMMA)
+ break;
+ next();
+ }
+ if (!r->next)
+ return r;
+ e = mkexpr(EXPRCOMMA, e->type, 0);
+ e->comma.exprs = r;
+
+ return e;
+}
+
+struct expression *
+exprconvert(struct expression *e, struct type *t)
+{
+ struct expression *cast;
+
+ if (typecompatible(e->type, t))
+ return e;
+ cast = mkexpr(EXPRCAST, t, 0);
+ cast->cast.e = e;
+
+ return cast;
+}