/*
 * Copyright (c) 2004 - 2006, Nils R. Weller
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Expression parser
 */
#include "expr.h"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "token.h"
#include "error.h"
#include "defs.h"
#include "type.h"
#include "subexpr.h"
#include "libnwcc.h"
#include "n_libc.h"

void
append_expr(struct expr **head, struct expr **tail, struct expr *e) {
	if (*head == NULL) {
		*head = *tail = e;
	} else {
		(*tail)->next = e;
		e->prev = *tail;
		*tail = (*tail)->next;
	}
}

struct expr *
alloc_expr(void) {
	struct expr	*ret = n_xmalloc(sizeof *ret);
	static struct expr	nullexpr;
	*ret = nullexpr;
	return ret;
}


void	recover(struct token **tok, int delim, int delim2);

static int
ambig_to_binary(struct token *t) {
	int	op = *(int *)t->data;
	switch (op) {
	case TOK_OP_AMB_PLUS:
		op = TOK_OP_PLUS;
		break;
	case TOK_OP_AMB_MINUS:
		op = TOK_OP_MINUS;
		break;
	case TOK_OP_AMB_MULTI:
		op = TOK_OP_MULTI;
		break;
	case TOK_OP_AMB_BAND:
		op = TOK_OP_BAND;
		break;
	case TOK_OP_AMB_COND2:
		op = TOK_OP_COND2;
		break;
	}

	if (!IS_BINARY(op) && op != TOK_OP_COND2) {
		errorfl(t, "Invalid operator ``%s'' - "
			"binary or ternary operator expected", t->ascii);
		return -1;
	}
	return op;
}


int
expr_ends(struct token *t, int delim, int delim2) {
	int	type;

	if (t->type == TOK_OPERATOR) {
		type = *(int *)t->data;
	} else {
		type = t->type;
	}
	if (type == delim || type == delim2) {
		return 1;
	}
	return 0; 
}


/*
 * Get lowest precedence operator in an expression. Note that the
 * conditional operator requires special handling; In
 * foo? bar: baz;
 * ... ? and : act like parentheses, i.e. lower precedence operators
 * such as the comma operator may occur between them.
 */
static struct operator *
get_lowest_prec(struct expr *ex, struct expr **endp) {
	int		lowest = 100;
	int		cond_expr = 0;
	struct expr	*lptr = NULL;

	*endp = NULL;
	for (; ex != NULL; ex = ex->next) {
		struct operator	*tmp;

		if (ex->op == 0 || ex->used) {
			continue;
		}
		tmp = &operators[LOOKUP_OP2(ex->op)];
		if (!cond_expr) {
			if (tmp->prec < lowest) {
				lowest = tmp->prec;
				lptr = ex;
			} else if (tmp->prec == lowest) {
				if (tmp->assoc != OP_ASSOC_RIGHT) {
					lptr = ex;
				}
			}
		}	
		if (ex->op == TOK_OP_COND) {
			++cond_expr;
		} else if (ex->op == TOK_OP_COND2) {
			--cond_expr;
		}	
	}
	*endp = lptr;
	return lptr? (void *)&operators[LOOKUP_OP2(lptr->op)]: (void *)NULL;
}

static struct expr *
bind_operators(struct expr *ex) {
	struct operator	*op;
	struct expr	*endp;
	struct expr	*start = ex;

	if (ex == NULL) return NULL;
	if ((op = get_lowest_prec(ex, &endp)) == NULL) {
		/* No more operators left */
		if (!ex->used) {
			endp = ex;
			endp->used = 1;
		}
		else endp = NULL;
		return endp;
	} else if (op->value == TOK_OP_COND) {
		struct expr	*endp2;
		struct expr	*ex2 = endp->next;
		int		cond1 = 0;

		/*
		 * Need to get second part of conditional operator. This
		 * requires skipping intermediate conditional operators.
		 */
		for (;;) {
			if (ex2 == NULL 
				|| (op = get_lowest_prec(ex2, &endp2))
				== NULL) {
				errorfl(ex2? ex2->tok: endp->tok,
					"Parse error - missing second part"
					" of conditional operator");
				return NULL;
			} else if (op->value == TOK_OP_COND) {
				++cond1;
			} else if (op->value == TOK_OP_COND2) {
				if (cond1 == 0) {
					/* done! */
					break;
				} else {
					--cond1;
				}	
			}	
			ex2 = endp2->next;
		} 

		/*
		 * At this point, endp points to ``?'' and endp2 to ``:''. We
		 * bind them such that:
		 * Given x = ``?''
		 * ... x->left = cond-expr
		 * ... x->right = :
		 * ... x->right->left = expr-nonzero
		 * ... x->right->right = expr-zero
		 */
		endp->used = endp2->used = 1;
		ex = endp->next;
		endp->next = NULL;
		endp->left = bind_operators(start);
		endp->right = endp2; 
		ex2 = endp2->next;
		endp2->next = NULL;
		endp->right->left = bind_operators(ex);
		endp->right->right = bind_operators(ex2);
		return endp;
	}	

	endp->used = 1;

	ex = endp->next;
	endp->next = NULL;

	endp->left = bind_operators(start);
	endp->right = bind_operators(ex);
	return endp;
}

static void
append_init_list(
	struct initializer **init, 
	struct initializer **init_tail,
	struct initializer *i) {
	if (*init == NULL) {
		*init = *init_tail = i;
	} else {
		(*init_tail)->next = i;
		*init_tail = (*init_tail)->next;
	}	
}	

struct initializer *
alloc_initializer(void) {
	static struct initializer	nullinit;
	struct initializer	*ret;
	ret = n_xmalloc(sizeof *ret);
	*ret = nullinit;
	return ret;
}	

static void
conv_init(struct tyval *tv, struct type *toty, struct token *t) {
	struct type		*fromty = tv->type;
	struct type_node	*ltn;
	struct type_node	*rtn;

	if (toty->tlist == NULL) {
		/* Simple basic type conversion */
		if (toty->code != fromty->code) {
			cross_do_conv(tv, toty->code);
		}
		return;
	}

	if (fromty->tlist == NULL || 
		(toty->code != fromty->code
		&& (toty->code != TY_VOID && fromty->code != TY_VOID))) { 
	/* this bullshit doesn't work */		
		return;
	}


	/* 
	 * Must be assignment to pointer since this function is only
	 * called for scalar types
	 */

	if (tv->is_nullptr_const) {
		return;
	}
	if (toty->code == TY_VOID
		&& toty->tlist->type == TN_POINTER_TO
		&& toty->tlist->next == NULL) {
		/* Assignment to void pointer */
		return;
	}	
		

	ltn = toty->tlist;
	rtn = fromty->tlist;

	if (rtn && rtn->type == TN_FUNCTION) {
		if (ltn->type == TN_POINTER_TO) {
			/* Probably pointer to function */
			ltn = ltn->next;
		}
	}	

	for (; ltn != NULL && rtn != NULL;
		ltn = ltn->next, rtn = rtn->next) {
		if (ltn->type != rtn->type) {
			if (rtn->type == TN_ARRAY_OF
				&& rtn == fromty->tlist) {
				/* OK - assign array address to ptr */
				;
			} else {
				errorfl(t,
					"Initializer of incompatible type");
				return;
			}	
		}	
	}

	if (ltn != rtn) {
		/* One type list is longer */
		errorfl(t, "Initializer of incompatible type");
		return;
	}
}

/*
 * Parses an expression and returns a pointer to the parse tree.
 */
struct expr *
parse_expr(struct token **tok, int delim, int delim2, int type) {
	struct token	*t;
	struct token	*tokstart = *tok;
	struct expr	*ret = NULL;
	struct expr	*rettail = NULL;
	struct expr	*ex = NULL;
	struct s_expr	*s_ex;
	struct s_expr	*last_s_ex = NULL;
	static int	nesting;

	++nesting;

#ifdef NO_EXPR
	/* Don't use expression parser */
	recover(tok, delim, delim2);
	if (*tok == NULL) {
		lexerror("Unterminated expression");
		exit(1);
	}
	return NULL;
#endif

	t = *tok;

	if (t->type == TOK_SEMICOLON) {
		/* Empty expression */
		--nesting;
		if ((delim != TOK_SEMICOLON && delim2 != TOK_SEMICOLON)
			|| type == EXPR_INIT || type == EXPR_CONSTINIT) {
			errorfl(t, "Parse error at `%s'", t->ascii);
			return NULL;
		}
		ret = alloc_expr();
		return ret;
	} else if (t->type == TOK_ARRAY_CLOSE
			&& (delim == TOK_ARRAY_CLOSE
				|| delim2 == TOK_ARRAY_CLOSE)) {
		ret = alloc_expr();
		--nesting;
		return ret;
	}


	/*
	 * Strategy: An expression is a number of sub-expressions
	 * connected through binary and ternary operators, so we
	 * just always need to read a sub-expression, a connecting
	 * operator - if any - and then the next sub-expression.
	 */
	while ((s_ex = get_sub_expr(&t, delim, delim2, type)) != NULL) {
		int	op;

		last_s_ex = s_ex;

		ex = alloc_expr();
		ex->op = 0;
		ex->data = s_ex;

		append_expr(&ret, &rettail, ex);

		if (expr_ends(t, delim, delim2)) {
			if ((ex->tok = s_ex->meat) == NULL) {
				if ((ex->tok = s_ex->is_sizeof) == NULL) {
					/* Must be parenthesized expr */
					ex->tok = s_ex->is_expr->tok;
				}
			}	
			break;
		} else if (t->type != TOK_OPERATOR) {
			errorfl(t, "Parse error at `%s'(#2)", t->ascii);
			ex = NULL;
			break;
		}
		/* Must be binary or ternary operator */
		ex = alloc_expr();
		ex->data = NULL;
		op = *(int *)t->data;
		ex->tok = t;
		if (op != TOK_OP_COND) {
			op = ambig_to_binary(t);
	t->data = n_xmemdup(&op, sizeof op); /* XXX is this ok? */	
		}	
		if (type == EXPR_CONST || type == EXPR_CONSTINIT) {
			int		err = 0;
			char	*is_what =
				type == EXPR_CONST? "expression": "initializer";

			if (op == TOK_OP_COMMA) {
				errorfl(t,
					"The comma operator is not allowed "
					"in constant %ss", is_what);
				dump_toklist(*tok);
				exit(0);
				err = 1;
			} else if (IS_ASSIGN_OP(op)) {
				errorfl(t,
					"Assignment operators are not allowed "
					"in constant %ss", is_what);
				err = 1;
			}
			if (err) {
				free(ex);
				ex = NULL;
				break;
			}
		}
		ex->op = op;

		append_expr(&ret, &rettail, ex);

		if (next_token(&t) != 0) {
			--nesting;
			return NULL;
		}
		if (expr_ends(t, delim, delim2)) {
			errorfl(ex->tok, "Syntax error at `%s'",
				ex->tok->ascii);
			break;
		}
	}

	*tok = t;

	if (ex == NULL || !expr_ends(t, delim, delim2)) {
		/* Try to recover from parse errors */
		if (nesting == 1) {
			recover(&tokstart, delim, delim2);
			*tok = tokstart;
		}
		--nesting;
		return NULL;
	}

	--nesting;
	fflush(stdout);

	if ((ret = bind_operators(ret)) == NULL) {
		puts("panic: cannot bind operators");
		exit(1); /* XXX */
	}
	ret->extype = type;
	if ((type == EXPR_CONST || type == EXPR_CONSTINIT)
		&& nesting == 0) {
		if (eval_const_expr(ret) != 0) {
			return NULL;
		}	
	}

	return ret;
}




void
recover(struct token **tok, int delim, int delim2) {
	struct token	*t;
	int		parens = 0;
	int		brackets = 0;
	int		braces = 0;

	if (*tok == NULL) return; 

	if (delim == TOK_PAREN_CLOSE || delim2 == TOK_PAREN_CLOSE) {
		++parens;
	}
	if (delim == TOK_ARRAY_CLOSE || delim2 == TOK_ARRAY_CLOSE) {
		++brackets;
	}
	if (delim == TOK_COMP_CLOSE || delim2 == TOK_COMP_CLOSE) {
		++braces;
	}

	for (t = *tok; t != NULL; t = t->next) {
		int	ty = -1; /* 0 compared even with unused delim :( */

#ifdef DEBUG2
printf(" lol %s\n", t->ascii);
#endif
		if (t->type == TOK_OPERATOR) {
			ty = *(int *)t->data;
		} else if (t->type == TOK_PAREN_OPEN) {
			++parens;
			continue;
		} else if (t->type == TOK_PAREN_CLOSE) {
			/*
			 * The below was --parens < 0, but <=
			 * seems more permissive for parse 
			 * errors - so use that!
			 */
			if (--parens <= 0
				&& (delim == TOK_PAREN_CLOSE
					|| delim2 == TOK_PAREN_CLOSE)) {
				*tok = t;
				return;
			}
			continue;
		} else if (t->type == TOK_ARRAY_OPEN) {
			++brackets;
			continue;
		} else if (t->type == TOK_ARRAY_CLOSE) {
			if (--brackets <= 0
				&& (delim == TOK_ARRAY_CLOSE
					|| delim2 == TOK_ARRAY_CLOSE)) {
				*tok = t;
printf("okay, ret at %s\n", t->ascii);
				return;
			}
			continue;
		} else if (t->type == TOK_COMP_OPEN) {
			++braces;
			continue;
		} else if (t->type == TOK_COMP_CLOSE) {
			if (--braces <= 0
				&& (delim == TOK_COMP_CLOSE
					|| delim2 == TOK_COMP_CLOSE)) {
				*tok = t;
				return;
			}
			continue;
		} else {
			ty = t->type;
		}
		if (ty == delim || ty == delim2) {
			if (ty == TOK_OP_COMMA) {
				/*
				 * We must take into account that someone
				 * could write
				 * int foo = bar(x, y, z);
				 * ... in which case we do not want to exit
				 * after x!
				 */
				if (parens != 0) {
					continue;
				}
			}
			*tok = t;
			return; 
		}
	}
	*tok = NULL;
}



syntax highlighted by Code2HTML, v. 0.9.1