/*
 * sparse/expression.c
 *
 * Copyright (C) 2003 Transmeta Corp.
 *               2003-2004 Linus Torvalds
 *
 *  Licensed under the Open Software License version 1.1
 *
 * This is the expression parsing part of parsing C.
 */
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>

#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "scope.h"
#include "expression.h"
#include "target.h"
#include "char.h"

static int match_oplist(SCTX_ int op, ...)
{
	va_list args;
	int nextop;

	va_start(args, op);
	do {
		nextop = va_arg(args, int);
	} while (nextop != 0 && nextop != op);
	va_end(args);

	return nextop != 0;
}

static struct token *comma_expression(SCTX_ struct token *, struct expression **);

struct token *parens_expression(SCTX_ struct token *token, struct expression **expr, const char *where)
{
	token = expect(sctx_ token, '(', where);
	if (match_op(token, '{')) {
		struct expression *e = alloc_expression(sctx_ token, EXPR_STATEMENT);
		struct statement *stmt = alloc_statement(sctx_ token, STMT_COMPOUND);
		*expr = e;
		e->statement = stmt;
		start_symbol_scope(sctx );
		token = compound_statement(sctx_ token->next, stmt);
		end_symbol_scope(sctx);
		token = expect(sctx_ token, '}', "at end of statement expression");
	} else
		token = parse_expression(sctx_ token, expr);
	return expect(sctx_ token, ')', where);
}

/*
 * Handle __func__, __FUNCTION__ and __PRETTY_FUNCTION__ token
 * conversion
 */
static struct symbol *handle_func(SCTX_ struct token *token)
{
	struct ident *ident = token->ident;
	struct symbol *decl, *array;
	struct string *string;
	int len;

	if (ident != (struct ident *)&sctxp __func___ident &&
	    ident != (struct ident *)&sctxp __FUNCTION___ident &&
	    ident != (struct ident *)&sctxp __PRETTY_FUNCTION___ident)
		return NULL;

	if (!sctxp current_fn || !sctxp current_fn->ident)
		return NULL;

	/* OK, it's one of ours */
	array = alloc_symbol(sctx_ token, SYM_ARRAY);
	array->ctype.base_type = &sctxp char_ctype;
	array->ctype.alignment = 1;
	array->endpos = token;
	decl = alloc_symbol(sctx_ token, SYM_NODE);
	decl->ctype.base_type = array;
	decl->ctype.alignment = 1;
	decl->ctype.modifiers = MOD_STATIC;
	decl->endpos = token;

	/* function-scope, but in NS_SYMBOL */
	bind_symbol(sctx_ decl, ident, NS_LABEL);
	decl->namespace = NS_SYMBOL;

	len = sctxp current_fn->ident->len;
	string = __alloc_string(sctx_ len + 1);
	memcpy(string->data, sctxp current_fn->ident->name, len);
	string->data[len] = 0;
	string->length = len + 1;

	decl->initializer = alloc_expression(sctx_ token, EXPR_STRING);
	decl->initializer->string = string;
	decl->initializer->ctype = decl;
	decl->array_size = alloc_const_expression(sctx_  token, len + 1);
	array->array_size = decl->array_size;
	decl->bit_size = array->bit_size = bytes_to_bits(sctx_ len + 1);

	return decl;
}

static struct token *parse_type(SCTX_ struct token *token, struct expression **tree)
{
	struct symbol *sym;
	*tree = alloc_expression(sctx_ token, EXPR_TYPE);
	(*tree)->flags = Int_const_expr; /* sic */
	token = typename(sctx_ token, &sym, NULL);
	if (sym->ident)
		sparse_error(sctx_ token->pos,
			     "type expression should not include identifier "
			     "\"%s\"", sym->ident->name);
	(*tree)->symbol = sym;
	return token;
}

static struct token *builtin_types_compatible_p_expr(SCTX_ struct token *token,
						     struct expression **tree)
{
	struct expression *expr = alloc_expression(sctx_ 
		token, EXPR_COMPARE);
	expr->flags = Int_const_expr;
	expr->op = SPECIAL_EQUAL;
	token = token->next;
	if (!match_op(token, '('))
		return expect(sctx_ token, '(',
			      "after __builtin_types_compatible_p");
	token = token->next;
	token = parse_type(sctx_ token, &expr->left);
	if (!match_op(token, ','))
		return expect(sctx_ token, ',',
			      "in __builtin_types_compatible_p");
	token = token->next;
	token = parse_type(sctx_ token, &expr->right);
	if (!match_op(token, ')'))
		return expect(sctx_ token, ')',
			      "at end of __builtin_types_compatible_p");
	token = token->next;
	
	*tree = expr;
	return token;
}

static struct token *builtin_offsetof_expr(SCTX_ struct token *token,
					   struct expression **tree)
{
	struct expression *expr = NULL;
	struct expression **p = &expr;
	struct symbol *sym;
	int op = '.';

	token = token->next;
	if (!match_op(token, '('))
		return expect(sctx_ token, '(', "after __builtin_offset");

	token = token->next;
	token = typename(sctx_ token, &sym, NULL);
	if (sym->ident)
		sparse_error(sctx_ token->pos,
			     "type expression should not include identifier "
			     "\"%s\"", sym->ident->name);

	if (!match_op(token, ','))
		return expect(sctx_ token, ',', "in __builtin_offset");

	while (1) {
		struct expression *e;
		switch (op) {
		case ')':
			expr->in = sym;
			*tree = expr;
		default:
			return expect(sctx_ token, ')', "at end of __builtin_offset");
		case SPECIAL_DEREFERENCE:
			e = alloc_expression(sctx_ token, EXPR_OFFSETOF);
			e->flags = Int_const_expr;
			e->op = '[';
			*p = e;
			p = &e->down;
			/* fall through */
		case '.':
			token = token->next;
			e = alloc_expression(sctx_ token, EXPR_OFFSETOF);
			e->flags = Int_const_expr;
			e->op = '.';
			if (token_type(token) != TOKEN_IDENT) {
				sparse_error(sctx_ token->pos, "Expected member name");
				return token;
			}
			e->ident = token->ident;
			token = token->next;
			break;
		case '[':
			token = token->next;
			e = alloc_expression(sctx_ token, EXPR_OFFSETOF);
			e->flags = Int_const_expr;
			e->op = '[';
			token = parse_expression(sctx_ token, &e->index);
			token = expect(sctx_ token, ']',
					"at end of array dereference");
			if (!e->index)
				return token;
		}
		*p = e;
		p = &e->down;
		op = token_type(token) == TOKEN_SPECIAL ? token->special : 0;
	}
}

#ifndef ULLONG_MAX
#define ULLONG_MAX (~0ULL)
#endif

static unsigned long long parse_num(SCTX_ const char *nptr, char **end)
{
	if (nptr[0] == '0' && tolower(nptr[1]) == 'b')
		return strtoull(&nptr[2], end, 2);
	return strtoull(nptr, end, 0);
}

static void get_number_value(SCTX_ struct expression *expr, struct token *token)
{
	const char *str = token->number;
	unsigned long long value;
	char *end;
	int size = 0, want_unsigned = 0;
	int overflow = 0, do_warn = 0;
	int try_unsigned = 1;
	int bits;

	errno = 0;
	value = parse_num(sctx_ str, &end);
	if (end == str)
		goto Float;
	if (value == ULLONG_MAX && errno == ERANGE)
		overflow = 1;
	while (1) {
		char c = *end++;
		if (!c) {
			break;
		} else if (c == 'u' || c == 'U') {
			if (want_unsigned)
				goto Enoint;
			want_unsigned = 1;
		} else if (c == 'l' || c == 'L') {
			if (size)
				goto Enoint;
			size = 1;
			if (*end == c) {
				size = 2;
				end++;
			}
		} else
			goto Float;
	}
	if (overflow)
		goto Eoverflow;
	/* OK, it's a valid integer */
	/* decimals can be unsigned only if directly specified as such */
	if (str[0] != '0' && !want_unsigned)
		try_unsigned = 0;
	if (!size) {
		bits = sctxp bits_in_int - 1;
		if (!(value & (~1ULL << bits))) {
			if (!(value & (1ULL << bits))) {
				goto got_it;
			} else if (try_unsigned) {
				want_unsigned = 1;
				goto got_it;
			}
		}
		size = 1;
		do_warn = 1;
	}
	if (size < 2) {
		bits = sctxp bits_in_long - 1;
		if (!(value & (~1ULL << bits))) {
			if (!(value & (1ULL << bits))) {
				goto got_it;
			} else if (try_unsigned) {
				want_unsigned = 1;
				goto got_it;
			}
			do_warn |= 2;
		}
		size = 2;
		do_warn |= 1;
	}
	bits = sctxp bits_in_longlong - 1;
	if (value & (~1ULL << bits))
		goto Eoverflow;
	if (!(value & (1ULL << bits)))
		goto got_it;
	if (!try_unsigned)
		warning(sctx_ expr->pos->pos, "decimal constant %s is too big for long long",
			show_token(sctx_ token));
	want_unsigned = 1;
got_it:
	if (do_warn)
		warning(sctx_ expr->pos->pos, "constant %s is so big it is%s%s%s",
			show_token(sctx_ token),
			want_unsigned ? " unsigned":"",
			size > 0 ? " long":"",
			size > 1 ? " long":"");
	if (do_warn & 2)
		warning(sctx_ expr->pos->pos,
			"decimal constant %s is between LONG_MAX and ULONG_MAX."
			" For C99 that means long long, C90 compilers are very "
			"likely to produce unsigned long (and a warning) here",
			show_token(sctx_ token));
        expr->type = EXPR_VALUE;
	expr->flags = Int_const_expr;
        expr->ctype = ctype_integer(sctx_ size, want_unsigned);
        expr->value = value;
	return;
Eoverflow:
	error_die(sctx_ expr->pos->pos, "constant %s is too big even for unsigned long long",
			show_token(sctx_ token));
	return;
Float:
	expr->fvalue = string_to_ld(sctx_ str, &end);
	if (str == end)
		goto Enoint;

	if (*end && end[1])
		goto Enoint;

	if (*end == 'f' || *end == 'F')
		expr->ctype = &sctxp float_ctype;
	else if (*end == 'l' || *end == 'L')
		expr->ctype = &sctxp ldouble_ctype;
	else if (!*end)
		expr->ctype = &sctxp double_ctype;
	else
		goto Enoint;

	expr->flags = Float_literal;
	expr->type = EXPR_FVALUE;
	return;

Enoint:
	error_die(sctx_ expr->pos->pos, "constant %s is not a valid number", show_token(sctx_ token));
}

struct token *primary_expression(SCTX_ struct token *token, struct expression **tree)
{
	struct expression *expr = NULL;

	switch (token_type(token)) {
	case TOKEN_CHAR ... TOKEN_WIDE_CHAR_EMBEDDED_3:
		expr = alloc_expression(sctx_ token, EXPR_VALUE);   
		expr->flags = Int_const_expr;
		expr->ctype = token_type(token) < TOKEN_WIDE_CHAR ? &sctxp int_ctype : &sctxp long_ctype;
		get_char_constant(sctx_ token, &expr->value);
		token = token->next;
		break;

	case TOKEN_NUMBER:
		expr = alloc_expression(sctx_ token, EXPR_VALUE);
		get_number_value(sctx_ expr, token); /* will see if it's an integer */
		token = token->next;
		break;

	case TOKEN_ZERO_IDENT: {
		expr = alloc_expression(sctx_ token, EXPR_SYMBOL);
		expr->flags = Int_const_expr;
		expr->ctype = &sctxp int_ctype;
		expr->symbol = &sctxp zero_int;
		expr->symbol_name = token->ident;
		token = token->next;
		break;
	}

	case TOKEN_IDENT: {
		struct symbol *sym = lookup_symbol(sctx_ token->ident, NS_SYMBOL | NS_TYPEDEF);
		struct token *next = token->next;

		if (!sym) {
			sym = handle_func(sctx_ token);
			if (token->ident == (struct ident *)&sctxp __builtin_types_compatible_p_ident) {
				token = builtin_types_compatible_p_expr(sctx_ token, &expr);
				break;
			}
			if (token->ident == (struct ident *)&sctxp __builtin_offsetof_ident) {
				token = builtin_offsetof_expr(sctx_ token, &expr);
				break;
			}
		} else if (sym->enum_member) {
			expr = alloc_expression(sctx_ token, EXPR_VALUE);
			*expr = *sym->initializer;
			/* we want the right position reported, thus the copy */
			expr->pos = token;
			expr->flags = Int_const_expr;
			token = next;
			break;
		}

		expr = alloc_expression(sctx_ token, EXPR_SYMBOL);

		/*
		 * We support types as real first-class citizens, with type
		 * comparisons etc:
		 *
		 *	if (typeof(a) == int) ..
		 */
		if (sym && sym->namespace == NS_TYPEDEF) {
			sparse_error(sctx_ token->pos, "typename in expression");
			sym = NULL;
		}
		expr->symbol_name = token->ident;
		expr->symbol = sym;
		token = next;
		break;
	}

	case TOKEN_STRING:
	case TOKEN_WIDE_STRING:
		expr = alloc_expression(sctx_ token, EXPR_STRING);
		token = get_string_constant(sctx_ token, expr);
		break;

	case TOKEN_SPECIAL:
		if (token->special == '(') {
			expr = alloc_expression(sctx_ token, EXPR_PREOP);
			expr->op = '(';
			token = parens_expression(sctx_ token, &expr->unop, "in expression");
			if (expr->unop)
				expr->flags = expr->unop->flags;
			break;
		}
		if (token->special == '[' && lookup_type(sctx_ token->next)) {
			expr = alloc_expression(sctx_ token, EXPR_TYPE);
			expr->flags = Int_const_expr; /* sic */
			token = typename(sctx_ token->next, &expr->symbol, NULL);
			token = expect(sctx_ token, ']', "in type expression");
			break;
		}
			
	default:
		;
	}
	*tree = expr;
	return token;
}

static struct token *expression_list(SCTX_ struct token *token, struct expression_list **list)
{
	while (!match_op(token, ')')) {
		struct expression *expr = NULL;
		token = assignment_expression(sctx_ token, &expr);
		if (!expr)
			break;
		add_expression(sctx_ list, expr);
		if (!match_op(token, ','))
			break;
		token = token->next;
	}
	return token;
}

/*
 * extend to deal with the ambiguous C grammar for parsing
 * a cast expressions followed by an initializer.
 */
static struct token *postfix_expression(SCTX_ struct token *token, struct expression **tree, struct expression *cast_init_expr)
{
	struct expression *expr = cast_init_expr;

	if (!expr)
		token = primary_expression(sctx_ token, &expr);

	while (expr && token_type(token) == TOKEN_SPECIAL) {
		switch (token->special) {
		case '[': {			/* Array dereference */
			struct expression *deref = alloc_expression(sctx_ token, EXPR_PREOP);
			struct expression *add = alloc_expression(sctx_ token, EXPR_BINOP);

			deref->op = '*';
			deref->unop = add;

			add->op = '+';
			add->left = expr;
			token = parse_expression(sctx_ token->next, &add->right);
			token = expect(sctx_ token, ']', "at end of array dereference");
			expr = deref;
			continue;
		}
		case SPECIAL_INCREMENT:		/* Post-increment */
		case SPECIAL_DECREMENT:	{	/* Post-decrement */
			struct expression *post = alloc_expression(sctx_ token, EXPR_POSTOP);
			post->op = token->special;
			post->unop = expr;
			expr = post;
			token = token->next;
			continue;
		}
		case SPECIAL_DEREFERENCE: {	/* Structure pointer member dereference */
			/* "x->y" is just shorthand for "(*x).y" */
			struct expression *inner = alloc_expression(sctx_ token, EXPR_PREOP);
			inner->op = '*';
			inner->unop = expr;
			expr = inner;
		}
		/* Fall through!! */
		case '.': {			/* Structure member dereference */
			struct expression *deref = alloc_expression(sctx_ token, EXPR_DEREF);
			deref->op = '.';
			deref->deref = expr;
			token = token->next;
			if (token_type(token) != TOKEN_IDENT) {
				sparse_error(sctx_ token->pos, "Expected member name");
				break;
			}
			deref->member = token->ident;
			token = token->next;
			expr = deref;
			continue;
		}

		case '(': {			/* Function call */
			struct expression *call = alloc_expression(sctx_ token, EXPR_CALL);
			call->op = '(';
			call->fn = expr;
			token = expression_list(sctx_ token->next, &call->args);
			token = expect(sctx_ token, ')', "in function call");
			expr = call;
			continue;
		}

		default:
			break;
		}
		break;
	}
	*tree = expr;
	return token;
}

static struct token *cast_expression(SCTX_ struct token *token, struct expression **tree);
static struct token *unary_expression(SCTX_ struct token *token, struct expression **tree);

static struct token *type_info_expression(SCTX_ struct token *token,
	struct expression **tree, int type)
{
	struct expression *expr = alloc_expression(sctx_ token, type);
	struct token *p;

	*tree = expr;
	expr->flags = Int_const_expr; /* XXX: VLA support will need that changed */
	token = token->next;
	if (!match_op(token, '(') || !lookup_type(sctx_ token->next))
		return unary_expression(sctx_ token, &expr->cast_expression);
	p = token;
	token = typename(sctx_ token->next, &expr->cast_type, NULL);

	if (!match_op(token, ')')) {
		static const char * error[] = {
			[EXPR_SIZEOF] = "at end of sizeof",
			[EXPR_ALIGNOF] = "at end of __alignof__",
			[EXPR_PTRSIZEOF] = "at end of __sizeof_ptr__"
		};
		return expect(sctx_ token, ')', error[type]);
	}

	token = token->next;
	/*
	 * C99 ambiguity: the typename might have been the beginning
	 * of a typed initializer expression..
	 */
	if (match_op(token, '{')) {
		struct expression *cast = alloc_expression(sctx_ p, EXPR_CAST);
		cast->cast_type = expr->cast_type;
		expr->cast_type = NULL;
		expr->cast_expression = cast;
		token = initializer(sctx_ &cast->cast_expression, token);
		token = postfix_expression(sctx_ token, &expr->cast_expression, cast);
	}
	return token;
}

static struct token *unary_expression(SCTX_ struct token *token, struct expression **tree)
{
	if (token_type(token) == TOKEN_IDENT) {
		struct ident *ident = token->ident;
		if (ident->reserved) {
		  /*static */const struct {
				struct ident *id;
				int type;
			} type_information[] = {
				{ (struct ident *)&sctxp sizeof_ident, EXPR_SIZEOF },
				{ (struct ident *)&sctxp __alignof___ident, EXPR_ALIGNOF },
				{ (struct ident *)&sctxp __alignof_ident, EXPR_ALIGNOF },
				{ (struct ident *)&sctxp __sizeof_ptr___ident, EXPR_PTRSIZEOF },
			};
			int i;
			for (i = 0; i < ARRAY_SIZE(type_information); i++) {
				if (ident == type_information[i].id)
					return type_info_expression(sctx_ token, tree, type_information[i].type);
			}
		}
	}

	if (token_type(token) == TOKEN_SPECIAL) {
		if (match_oplist(sctx_ token->special,
		    SPECIAL_INCREMENT, SPECIAL_DECREMENT,
		    '&', '*', 0)) {
		    	struct expression *unop;
			struct expression *unary;
			struct token *next;

			next = cast_expression(sctx_ token->next, &unop);
			if (!unop) {
				sparse_error(sctx_ token->pos, "Syntax error in unary expression");
				*tree = NULL;
				return next;
			}
			unary = alloc_expression(sctx_ token, EXPR_PREOP);
			unary->op = token->special;
			unary->unop = unop;
			*tree = unary;
			return next;
		}
		/* possibly constant ones */
		if (match_oplist(sctx_ token->special, '+', '-', '~', '!', 0)) {
		    	struct expression *unop;
			struct expression *unary;
			struct token *next;

			next = cast_expression(sctx_ token->next, &unop);
			if (!unop) {
				sparse_error(sctx_ token->pos, "Syntax error in unary expression");
				*tree = NULL;
				return next;
			}
			unary = alloc_expression(sctx_ token, EXPR_PREOP);
			unary->op = token->special;
			unary->unop = unop;
			unary->flags = unop->flags & Int_const_expr;
			*tree = unary;
			return next;
		}
		/* Gcc extension: &&label gives the address of a label */
		if (match_op(token, SPECIAL_LOGICAL_AND) &&
		    token_type(token->next) == TOKEN_IDENT) {
			struct expression *label = alloc_expression(sctx_ token, EXPR_LABEL);
			struct symbol *sym = label_symbol(sctx_ token->next);
			if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) {
				sym->ctype.modifiers |= MOD_ADDRESSABLE;
				add_symbol(sctx_ &(sctxp function_computed_target_list), sym);
			}
			label->label_symbol = sym;
			*tree = label;
			return token->next->next;
		}
						
	}
			
	return postfix_expression(sctx_ token, tree, NULL);
}

/*
 * Ambiguity: a '(' can be either a cast-expression or
 * a primary-expression depending on whether it is followed
 * by a type or not. 
 *
 * additional ambiguity: a "cast expression" followed by
 * an initializer is really a postfix-expression.
 */
static struct token *cast_expression(SCTX_ struct token *token, struct expression **tree)
{
	if (match_op(token, '(')) {
		struct token *next = token->next;
		if (lookup_type(sctx_ next)) {
			struct expression *cast = alloc_expression(sctx_ next, EXPR_CAST);
			struct expression *v;
			struct symbol *sym;
			int is_force;

			token = typename(sctx_ next, &sym, &is_force);
			cast->cast_type = sym;
			token = expect(sctx_ token, ')', "at end of cast operator");
			if (match_op(token, '{')) {
				if (is_force)
					warning(sctx_ sym->pos->pos,
						"[force] in compound literal");
				token = initializer(sctx_ &cast->cast_expression, token);
				return postfix_expression(sctx_ token, tree, cast);
			}
			*tree = cast;
			if (is_force)
				cast->type = EXPR_FORCE_CAST;
			token = cast_expression(sctx_ token, &v);
			if (!v)
				return token;
			cast->cast_expression = v;
			if (v->flags & Int_const_expr)
				cast->flags = Int_const_expr;
			else if (v->flags & Float_literal) /* and _not_ int */
				cast->flags = Int_const_expr | Float_literal;
			return token;
		}
	}
	return unary_expression(sctx_ token, tree);
}

/*
 * Generic left-to-right binop parsing
 *
 * This _really_ needs to be inlined, because that makes the inner
 * function call statically deterministic rather than a totally
 * unpredictable indirect call. But gcc-3 is so "clever" that it
 * doesn't do so by default even when you tell it to inline it.
 *
 * Making it a macro avoids the inlining problem, and also means
 * that we can pass in the op-comparison as an expression rather
 * than create a data structure for it.
 */

#define LR_BINOP_EXPRESSION(__token, tree, type, inner, compare)	\
	struct expression *left = NULL;					\
	struct token * next = inner(sctx_ __token, &left);		\
									\
	if (left) {							\
		while (token_type(next) == TOKEN_SPECIAL) {		\
			struct expression *top, *right = NULL;		\
			int op = next->special;				\
									\
			if (!(compare))					\
				goto out;				\
			top = alloc_expression(sctx_ next, type);		\
			next = inner(sctx_ next->next, &right);		\
			if (!right) {					\
				sparse_error(sctx_ next->pos, "No right hand side of '%s'-expression", show_special(sctx_ op));	\
				break;					\
			}						\
			top->flags = left->flags & right->flags		\
						& Int_const_expr;	\
			top->op = op;					\
			top->left = left;				\
			top->right = right;				\
			left = top;					\
		}							\
	}								\
out:									\
	*tree = left;							\
	return next;							\

static struct token *multiplicative_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, cast_expression,
		(op == '*') || (op == '/') || (op == '%')
	);
}

static struct token *additive_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, multiplicative_expression,
		(op == '+') || (op == '-')
	);
}

static struct token *shift_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, additive_expression,
		(op == SPECIAL_LEFTSHIFT) || (op == SPECIAL_RIGHTSHIFT)
	);
}

static struct token *relational_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_COMPARE, shift_expression,
		(op == '<') || (op == '>') ||
		(op == SPECIAL_LTE) || (op == SPECIAL_GTE)
	);
}

static struct token *equality_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_COMPARE, relational_expression,
		(op == SPECIAL_EQUAL) || (op == SPECIAL_NOTEQUAL)
	);
}

static struct token *bitwise_and_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, equality_expression,
		(op == '&')
	);
}

static struct token *bitwise_xor_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, bitwise_and_expression,
		(op == '^')
	);
}

static struct token *bitwise_or_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_BINOP, bitwise_xor_expression,
		(op == '|')
	);
}

static struct token *logical_and_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_LOGICAL, bitwise_or_expression,
		(op == SPECIAL_LOGICAL_AND)
	);
}

static struct token *logical_or_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_LOGICAL, logical_and_expression,
		(op == SPECIAL_LOGICAL_OR)
	);
}

struct token *conditional_expression(SCTX_ struct token *token, struct expression **tree)
{
	token = logical_or_expression(sctx_ token, tree);
	if (*tree && match_op(token, '?')) {
		struct expression *expr = alloc_expression(sctx_ token, EXPR_CONDITIONAL);
		expr->op = token->special;
		expr->left = *tree;
		*tree = expr;
		token = parse_expression(sctx_ token->next, &expr->cond_true);
		token = expect(sctx_ token, ':', "in conditional expression");
		token = conditional_expression(sctx_ token, &expr->cond_false);
		if (expr->left && expr->cond_false) {
			int is_const = expr->left->flags &
					expr->cond_false->flags &
					Int_const_expr;
			if (expr->cond_true)
				is_const &= expr->cond_true->flags;
			expr->flags = is_const;
		}
	}
	return token;
}

struct token *assignment_expression(SCTX_ struct token *token, struct expression **tree)
{
	token = conditional_expression(sctx_ token, tree);
	if (*tree && token_type(token) == TOKEN_SPECIAL) {
		static const int assignments[] = {
			'=',
			SPECIAL_ADD_ASSIGN, SPECIAL_SUB_ASSIGN,
			SPECIAL_MUL_ASSIGN, SPECIAL_DIV_ASSIGN,
			SPECIAL_MOD_ASSIGN, SPECIAL_SHL_ASSIGN,
			SPECIAL_SHR_ASSIGN, SPECIAL_AND_ASSIGN,
			SPECIAL_OR_ASSIGN,  SPECIAL_XOR_ASSIGN };
		int i, op = token->special;
		for (i = 0; i < ARRAY_SIZE(assignments); i++)
			if (assignments[i] == op) {
				struct expression * expr = alloc_expression(sctx_ token, EXPR_ASSIGNMENT);
				expr->left = *tree;
				expr->op = op;
				*tree = expr;
				return assignment_expression(sctx_ token->next, &expr->right);
			}
	}
	return token;
}

static struct token *comma_expression(SCTX_ struct token *token, struct expression **tree)
{
	LR_BINOP_EXPRESSION(
		token, tree, EXPR_COMMA, assignment_expression,
		(op == ',')
	);
}

struct token *parse_expression(SCTX_ struct token *token, struct expression **tree)
{
	return comma_expression(sctx_ token,tree);
}
