/*
 * Copyright (c) 2003 - 2007, 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.
 *
 * This file contains the hierarchial source analyzer, called
 * analyze(). This function uses the global variable
 * ``toklist'' which must point to the complete translation unit
 */

#include "analyze.h"
#include <string.h>
#include <stdlib.h>
#include "token.h"
#include "decl.h"
#include "error.h"
#include "backend.h"
#include "scope.h"
#include "symlist.h"
#include "type.h"
#include "expr.h"
#include "misc.h"
#include "functions.h"
#include "debug.h"
#include "builtins.h"
#include "inlineasm.h"
#include "icode.h"
#include "control.h"
#include "n_libc.h"

static void
check_main(struct token *t, struct type *ty) {
	static struct type 	*maindef = NULL;
	struct ty_func		*tfunc = ty->tlist->tfunc;
	struct sym_entry	*se;
	struct type		*tytmp;

	if (maindef != NULL) return;
	if (ty->name[0] != 'm' || strcmp(ty->name + 1, "ain") != 0) {
		return;
	}
	maindef = ty;

	/* XXX why is it always null??? */
	if (ty->code != TY_INT
		|| ty->tlist->next != NULL) {
		warningfl(t,
	"Illegal return type for main(). ISO C says it must be `int'!");
		return;
	} else if (ty->storage == TOK_KEY_STATIC) {
		errorfl(t, "Static (file scope) declaration of main()");
		return;
	}	

	switch (tfunc->nargs) {
	case 0:
	case -1:
		/* int main(void) or int main() */
		break;
	case 1:
		errorfl(t,
			"main() should take no, two or three arguments");
		break;
	case 2:
	case 3:
		/* XXX check arg1 + 2 = int,char** */

		se = tfunc->scope->slist;
		tytmp = make_basic_type(TY_INT);

		if (compare_types(se->dec->dtype, tytmp,
			CMPTY_SIGN|CMPTY_CONST) != 0) {
			errorfl(t, "First argument of main is not \
`int' as it should be");
		}

		tytmp = n_xmemdup(make_basic_type(TY_CHAR), sizeof *tytmp);
		append_typelist(tytmp, TN_POINTER_TO, 0, NULL, NULL);
		append_typelist(tytmp, TN_POINTER_TO, 0, NULL, NULL);

		if (compare_types(se->next->dec->dtype, tytmp,
			CMPTY_SIGN|CMPTY_CONST) != 0) {
			errorfl(t, "Second argument of main is not \
`char **' as it should be");
		}
		free(tytmp);

		if (tfunc->nargs == 3) {
			/* XXX check arg3 = char** */
		}
	}
}	

/* 
 * Do semantic analysis of translation unit. 
 * Initialize functions, structure, globals subsystems
 * Returns 0 if no errors were found, else 1
 */
int
analyze(struct token **curtok) {
	struct token		*t;
	struct decl		**d;
	struct expr		*ex;
	struct function		*func = NULL;
	struct token		*builtin_tok = NULL;
	struct token		*starttok;
	struct decl		**builtin_decl = NULL;
	static int		level;
	int			braces = 0;
	int			is_func;
	int			i;

	if (++level == 1) {
		static int	ptropval = TOK_OP_AMB_MULTI;
		static int	num = 1;
		static const struct {
			void	*p;
			int	tokval;
		} normal_va_list[] = {
			{ "typedef", TOK_IDENTIFIER },
			{ "char", TOK_IDENTIFIER },
			{ &ptropval, TOK_OPERATOR },
			{ "__builtin_va_list", TOK_IDENTIFIER },
			{ NULL, TOK_SEMICOLON },
			{ NULL, 0 }
		}, amd64_va_list[] = {
			/* typedef struct { */
			{ "typedef", TOK_IDENTIFIER },
			{ "struct", TOK_IDENTIFIER },
			{ NULL, TOK_COMP_OPEN },

			/* unsigned gp_offset; */
			{ "unsigned", TOK_IDENTIFIER },
			{ "gp_offset", TOK_IDENTIFIER },
			{ NULL, TOK_SEMICOLON },

			/* unsigned fp_offset; */
			{ "unsigned", TOK_IDENTIFIER },
			{ "fp_offset", TOK_IDENTIFIER },
			{ NULL, TOK_SEMICOLON },

			/* void *overflow_arg_area; */
			{ "void", TOK_IDENTIFIER },
			{ &ptropval, TOK_OPERATOR },
			{ "overflow_arg_area", TOK_IDENTIFIER },
			{ NULL, TOK_SEMICOLON },

			/* void *reg_save_area; */
			{ "void", TOK_IDENTIFIER },
			{ &ptropval, TOK_OPERATOR },
			{ "reg_save_area", TOK_IDENTIFIER },
			{ NULL, TOK_SEMICOLON },

			/* } __builtin_va_list[1]; */
			{ NULL, TOK_COMP_CLOSE },
			{ "__builtin_va_list", TOK_IDENTIFIER },
			{ NULL, TOK_ARRAY_OPEN },
			{ &num, TY_INT },
			{ NULL, TOK_ARRAY_CLOSE },
			{ NULL, TOK_SEMICOLON },
			{ NULL, 0 }
		}, *vatok;

		/* Initial call; Otherwise called by parse_expr() */
		curscope = &global_scope;

#if 0
		store_token(&builtin_tok, 
			n_xstrdup("typedef"), TOK_IDENTIFIER, 0);
		store_token(&builtin_tok,
			n_xstrdup("char"), TOK_IDENTIFIER, 0);
		i = TOK_OP_AMB_MULTI;
		store_token(&builtin_tok, &i, TOK_OPERATOR, 0);
		store_token(&builtin_tok, "__builtin_va_list",
			TOK_IDENTIFIER, 0);
		store_token(&builtin_tok, 0, TOK_SEMICOLON, 0);
		store_token(&builtin_tok, NULL, 0, 0);
		if ((builtin_decl = parse_decl(&builtin_tok, 0)) != NULL) {
			store_decl_scope(&global_scope, builtin_decl);
		} else {
			warningfl(NULL, "Cannot create builtins");
		}
#endif
		if (backend->arch == ARCH_AMD64) {
			vatok = amd64_va_list;
		} else {
			vatok = normal_va_list;
		} 
		for (i = 0; vatok[i].p || vatok[i].tokval; ++i) {
			void	*p;
			int	*dummyptr = n_xmalloc(sizeof *dummyptr);

			if (vatok[i].tokval == TOK_IDENTIFIER) {
				p = n_xstrdup(vatok[i].p);
			} else if (vatok[i].tokval == TY_INT) {
#if 0
				p = n_xmemdup(vatok[i].p, sizeof(int));
#endif
				p = n_xmalloc(16); /* XXX */
				memcpy(p, vatok[i].p, sizeof(int)); /* XXX */
			} else {
				if (vatok[i].p != NULL) {
					p = vatok[i].p;
				} else {
					p = dummyptr;
				}
			}
			store_token(&builtin_tok, p, vatok[i].tokval, 0, NULL); 
		}
		if ((builtin_decl = parse_decl(&builtin_tok, 0)) != NULL) {
			store_decl_scope(&global_scope, builtin_decl);
			builtin_va_list_type = builtin_decl[0]->dtype;
		} else {
			warningfl(NULL, "Cannot create builtins");
		}
		t = toklist; /* Start at beginning */
	} else {
		t = *curtok; /* Start at current token */
	}
	
	for (; t != NULL; t = t->next) {
		if (t->prev
			&& (t->prev->type == TOK_COMP_OPEN
			|| t->prev->type == TOK_COMP_CLOSE
			|| t->prev->type == TOK_SEMICOLON)) {
			if (!errors) {
				/*
				 * 09/01/07: XXX This token is apparently  
				 * still sometimes used if we have parse
				 * errors!!!!
				 */
				free(t->prev);
				t->prev = NULL;
			}	
		}

		/*
		 * 08/19/07: Check with lookup_symbol() added. Otherwise
		 * stuff like
		 *
		 *    typedef int foo;
		 *    {
		 *       int foo;
		 *       foo = 0;    <--- bad!!
		 *    }
		 *
		 * ... yields a syntax error because ``foo = 0;'' is
		 * interpreted as a declaration
		 */
		if (IS_TYPE(t)
			&& (t->type != TOK_IDENTIFIER
			|| lookup_symbol(curscope, t->data, 1) == NULL)) {
do_decl:
			starttok = t;
			d = parse_decl(&t, DECL_VARINIT);
			if (d == NULL) {
				/*
				 * XXX call recover() instead? 
				 */
				 t = t->next;
				 while (t != NULL
					 && t->type != TOK_SEMICOLON
					 && !IS_KEYWORD(t->type)) {
					t = t->next;
				 }
				 if (t == NULL) {
					break;
				 }
				 if (IS_KEYWORD(t->type)) {
					 t = t->prev;
				 }	 
				 continue;
			} else {
				int	k;

				/*
				 * 07/23/07: Don't free tokens if there's a
				 * VLA involved somewhere, cuz otherwise we
				 * still need to evaluate the expression(s)
				 */
				for (k = 0; d[k] != NULL; ++k) {
					if (d[k]->dtype->is_vla) {
						break;
					}
				}	
				if (d[k] == NULL) {
					free_tokens(starttok, t->prev, FREE_DECL);
				}	
			}

			is_func = 0;
				
			if ((d[0]->dtype->code == TY_STRUCT
				|| d[0]->dtype->code == TY_ENUM
				|| d[0]->dtype->code == TY_UNION)
				&& d[0]->dtype->is_def) {
				if (!d[0]->dtype->is_func) {
					continue;
				}	
			}	
#ifdef DEBUG2
			printf("Parsed declaration successfully!\n");
#endif
			if (t->type == TOK_COMP_OPEN) {
				++braces;
				for (i = 0; d[i] != NULL; ++i) {
					if (d[i]->dtype->is_func) {
						is_func = 1;
					}
				}
				if (is_func && i > 1) {
					/* 
					 * This must be illegal, as in
					 * int foo, bar(void) { ... }
					 */
					errorfl(t,
"Syntax error at %s - function definitions may not be paired with "
"other declarations!", t->ascii);
					/* XXX what to do here? */
				} else if (is_func) {
					if (curscope->parent
						!= &global_scope) {
						/* 
						 * ->parent because
						 * parse_func() already
						 * opens new scope
						 */
						errorfl(t,
			"Nested function definitions are not allowed");
						/*XXX what to do here?*/
					} else {
						d[0]->dtype->is_def = 1;
						func = alloc_function();
						/*func->scope = curscope;*/
						func->proto = d[0];
						func->fty = d[0]->dtype->tlist->tfunc;
						/* XXX complete ... */
						check_main(t, d[0]->dtype);
						curfunc = func;

						/*
						 * Note that in cases like
						 * void (*foo())() { ...
						 * (function returning
						 * function pointer), the
						 * current scope will now
						 * be that of the return
						 * type function declaration.
						 * So it has to be set to that
						 * of the function definition
						 * here
						 */
						curscope = d[0]->dtype->
							tlist->tfunc->scope;
						func->scope = curscope;
					}
				} else {
					errorfl(t,
					"Syntax error at `%s'", t->ascii);
					/* XXX what to do here? */
				}
			}
		} else if (t->type == TOK_KEY_ASM) {
			/* Inline assembler statement */
			struct statement	*st;
			struct inline_asm_stmt	*inl;

			if ((inl = parse_inline_asm(&t)) != NULL) {
				st = alloc_statement();
				st->type = ST_ASM;
				st->data = inl;
				append_statement(&curscope->code,
					&curscope->code_tail, st);
				t = t->next;
			}
		} else if (IS_CONTROL(t->type)) {
			(void) parse_ctrl(curcont, &t, 1, NULL);
#ifdef NO_EXPR
/* 
 * If the expression parser is used, it will parse casts on its own -
 * if it's not, we still want to parse casts in order to debug the
 * code responsible for it!
 */
		} else if (t->type == TOK_PAREN_OPEN
			&& curscope != &global_scope) {
			if (t->next == NULL) {
				errorfl(t, "Unexpected end of file");
				break; /* XXX */
			}
			if (IS_TYPE(t->next)) {
				/* This has got to be a cast */
				t = t->next;
				d = parse_decl(&t, DECL_CAST);
				if (d != NULL) {
#ifdef DEBUG2
					printf("Parsed cast successfully!\n");
#endif
				} else {
					/* Recover from error */
					while (t != NULL
						&& t->type != TOK_SEMICOLON) {
						t = t->next;
					}
					if (t == NULL) {
						break;
					}
				}
			} 
#endif
		} else if (t->type == TOK_COMP_OPEN) {
			/* Opening a compound statement - new scope! */
			struct statement	*st;

			if (curscope == &global_scope) {
				errorfl(t,
				"Invalid compound statement at top level");
				/* Create new scope anyway, to match with } */
			}
			if (curcont != NULL && curcont->stmt == NULL) {
				curcont->compound_body = 1;
				curcont->stmt = alloc_statement();
				curcont->stmt->type = ST_COMP;
				curcont->stmt->data = new_scope(SCOPE_CODE);
				curcont->braces = braces;
			} else {
				st = alloc_statement();
				st->type = ST_COMP;
				append_statement(&curscope->code,
					&curscope->code_tail, st);
				st->data = new_scope(SCOPE_CODE);
			}
			++braces;
		} else if (t->type == TOK_COMP_CLOSE) {
			/* Closing compound statement - leaving scope! */
			close_scope();
			if (--braces == 0 && func) {
#if 0
			if (func && --braces == 0) {
#endif
				/* This is the end of a function definition */
				func->next = funclist;
				funclist = func;
#if 0
				if (funclist == NULL) {
					funclist = funclist_tail = func;
				} else {
					funclist_tail->next = func;
					funclist_tail = func;
				}
#endif

				curfunc = func = NULL;
			}
			if (curcont && braces == curcont->braces) {
				complete_ctrl(&t, curcont);
			}

			if (braces == 0 && level > 1) {
				/* GNU statement-as-expression ends here */
				*curtok = t;
				break;
			}
		} else {
			if (curscope == &global_scope) {
				/* Must be implicit function declaration */
				if (t->type == TOK_SEMICOLON) {
					warningfl(t,
						"Empty expression used outside"
						" of function - illegal in ISO"
						" C");
					continue;
				}	
				goto do_decl;
			}

			if (t->type == TOK_IDENTIFIER) {
				if (try_label(&t, NULL)) {
					/* was label */
					continue;
				}
			}

			ex = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1);
			if (ex != NULL) {
				put_expr_scope(ex);
			}
		}
		if (t == NULL) {
			break;
		}
	}

	if (level == 1) {
		curscope = &global_scope;
	}

	if (braces > 0) {
		errorfl(NULL, "Parse error at end of input - "
			"%d missing `}' tokens", braces);
	}
	--level;
	if (!errors) {
		if (level == 0) {
			for (func = funclist; func != NULL; func = func->next) {
				debug_print_function(func);
				curfunc = func;
				curscope = func->scope;
				func->icode =
					xlate_to_icode(func->scope->code, 1);

				/*
				 * 10/31/07: Added this to make sure that all
				 * registers are completely thrown away when a
				 * function ends. Anything else is just nonsense
				 * and causes subsequent function definitions to
				 * perform unnecessary saves of these registers
				 */
				backend->invalidate_gprs(NULL, 0);
			}
		}
	} else if (level != 0) {
		return -1;
	}	

#if 0
	if (/* check_unused */1) {
		struct decl	*dp;
		int			i;
		struct decl	*darr[] = {
			static_init_vars, static_uninit_vars, NULL
		};

		for (i = 0; darr[i] != NULL; ++i) {
			for (dp = darr[i]; dp != NULL; dp = dp->next) {
				if (dp->dtype->storage != TOK_KEY_EXTERN
					&& dp->references == 0
					&& !is_basic_agg_type(dp->dtype)) {
					warningfl(dp->tok,
						"Unused variable `%s'",
						dp->dtype->name);
				}
			}
		}
	}
#endif
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1