/* $Id: print.c,v 1.125 2012/02/22 12:29:16 vrsieh Exp $ 
 *
 * Copyright (C) 2007-2009 FAUcc Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <assert.h>
#include <stdio.h>

#include "declaration.h"
#include "scope.h"
#include "expr.h"
#include "stmt.h"
#include "cc1.h"

/*forward*/ static void
print_expr(FILE *fp, struct expr *e);
/*forward*/ static void
print_type_spec(FILE *fp, int level, struct type *t);
/*forward*/ static void
print_type(FILE *fp, int level, struct type *t);
/*forward*/ static void
print_declarator(FILE *fp, int level, struct declaration *d);
/*forward*/ void
print_declaration(FILE *fp, int, struct declaration *);

static int
print_newline(FILE *fp, int sol)
{
	if (! sol) {
		fprintf(fp, "\n");
		sol = 1;
	}
	return sol;
}

static int
print_indent(FILE *fp, int sol, int level)
{
	unsigned int i;

	if (sol) {
		for (i = 0; i < level; i++) {
			fprintf(fp, "\t");
		}
	} else {
		fprintf(fp, " ");
	}
	return 0;
}

static void
print_offsetof_member_designator(FILE *fp, struct expr *e)
{
	switch (e->type) {
	case EXPR_ARROW:
		fprintf(fp, "%s", e->member);
		break;
	case EXPR_DOT:
		print_offsetof_member_designator(fp, e->expr0);
		fprintf(fp, ".");
		fprintf(fp, "%s", e->member);
		break;
	case EXPR_ARRAY:
		print_offsetof_member_designator(fp, e->expr0);
		fprintf(fp, "[");
		print_expr(fp, e->expr1);
		fprintf(fp, "]");
		break;
	default:
		assert(0);
	}
}

static void
print_string(FILE *fp, unsigned int len, const char *_str)
{
	const unsigned char *str = (const unsigned char *) _str;
	unsigned int i;

	for (i = 0; i < len; i++) {
		switch (*str) {
		case '\0': fprintf(fp, "\\0"); break;
		case '\a': fprintf(fp, "\\a"); break;
		case '\b': fprintf(fp, "\\b"); break;
		case '\n': fprintf(fp, "\\n"); break;
		case '\r': fprintf(fp, "\\r"); break;
		case '\t': fprintf(fp, "\\t"); break;
		case '"': fprintf(fp, "\\\""); break;
		case '\\': fprintf(fp, "\\\\"); break;
		default:
			if (32 <= *str && *str <= 127) {
				fprintf(fp, "%c", *str);
			} else {
				fprintf(fp, "\\%03o", *str);
			}
			break;
		}
		str++;
	}
}

static void
print_expr(FILE *fp, struct expr *e)
{
	struct expr *e1;

	switch (e->type) {
	case EXPR_NONE:
		assert(0);
	case EXPR_INTEGER:
		switch (e->type_name->type) {
		case TYPE_INT8:
			fprintf(fp, "0x%x", (char) e->integer); break;
		case TYPE_UINT8:
			fprintf(fp, "0x%xU", (unsigned char) e->integer); break;
		case TYPE_INT16:
			fprintf(fp, "0x%x", (short) e->integer); break;
		case TYPE_UINT16:
			fprintf(fp, "0x%xU", (unsigned short) e->integer); break;
		case TYPE_INT32:
			fprintf(fp, "0x%x", (int) e->integer); break;
		case TYPE_UINT32:
			fprintf(fp, "0x%xU", (unsigned int) e->integer); break;
		case TYPE_INT64:
			fprintf(fp, "0x%llxLL", (long long) e->integer); break;
		case TYPE_UINT64:
			fprintf(fp, "0x%llxULL", e->integer); break;
		default:
			fprintf(stderr, "type_name->type=%d\n", e->type_name->type);
			assert(0);
		}
		break;
	case EXPR_REAL:
		fprintf(fp, "%e", (double) e->real);
		break;
	case EXPR_STRING:
		fprintf(fp, "\"");
		print_string(fp, e->string_len, e->string);
		fprintf(fp, "\"");
		break;
	case EXPR_IDENTIFIER:
		fprintf(fp, "%s", e->declaration->identifier);
		break;
	case EXPR_BRACES:
		fprintf(fp, "{");
		for (e1 = e->first; e1; e1 = e1->next) {
			fprintf(fp, " ");
			print_expr(fp, e1);
			if (e1->next) {
				fprintf(fp, ",");
			}
		}
		fprintf(fp, " }");
		break;
	case EXPR_SIZEOF_TYPE:
		fprintf(fp, "sizeof (");
		print_type(fp, -1, e->type_name);
		fprintf(fp, ")");
		break;
	case EXPR_SIZEOF_EXPR:
		fprintf(fp, "sizeof ");
		print_expr(fp, e->expr0);
		break;
	case EXPR_BUILTIN_CONSTANT_P:
		fprintf(fp, "__builtin_constant_p(");
		print_expr(fp, e->expr0);
		fprintf(fp, ")");
		break;
	case EXPR_BUILTIN_OFFSETOF:
		fprintf(fp, "__builtin_offsetof(");
		print_type(fp, -1, e->type_name);
		fprintf(fp, ", ");
		print_offsetof_member_designator(fp, e->expr0);
		fprintf(fp, ")");
		break;
	case EXPR_BUILTIN_VA_ARG:
		fprintf(fp, "__builtin_va_arg(");
		print_expr(fp, e->expr0);
		fprintf(fp, ", ");
		print_type(fp, -1, e->type_name);
		fprintf(fp, ")");
		break;
	case EXPR_TYPE_CONVERSION:
		fprintf(fp, "(");
		print_type(fp, -1, e->type_name);
		fprintf(fp, ") ");
		print_expr(fp, e->expr0);
		break;
	case EXPR_STAR:
		fprintf(fp, "*");
		print_expr(fp, e->expr0);
		break;
	case EXPR_AMPHERSAND:
		fprintf(fp, "&");
		print_expr(fp, e->expr0);
		break;
	case EXPR_PRE_INC:
		fprintf(fp, "++");
		print_expr(fp, e->expr0);
		break;
	case EXPR_PRE_DEC:
		fprintf(fp, "--");
		print_expr(fp, e->expr0);
		break;
	case EXPR_POST_INC:
		print_expr(fp, e->expr0);
		fprintf(fp, "++");
		break;
	case EXPR_POST_DEC:
		print_expr(fp, e->expr0);
		fprintf(fp, "--");
		break;
	case EXPR_NEG:
		fprintf(fp, "-");
		print_expr(fp, e->expr0);
		break;
	case EXPR_INV:
		fprintf(fp, "~");
		print_expr(fp, e->expr0);
		break;
	case EXPR_NOT:
		fprintf(fp, "! ");
		print_expr(fp, e->expr0);
		break;
	case EXPR_LEFT:
		print_expr(fp, e->expr0);
		fprintf(fp, " << ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_RIGHT:
		print_expr(fp, e->expr0);
		fprintf(fp, " >> ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_EQUAL:
		print_expr(fp, e->expr0);
		fprintf(fp, " == ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_NOT_EQUAL:
		print_expr(fp, e->expr0);
		fprintf(fp, " != ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_LESS:
		print_expr(fp, e->expr0);
		fprintf(fp, " < ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_GREATER:
		print_expr(fp, e->expr0);
		fprintf(fp, " > ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_LESS_EQUAL:
		print_expr(fp, e->expr0);
		fprintf(fp, " <= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_GREATER_EQUAL:
		print_expr(fp, e->expr0);
		fprintf(fp, " >= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_ADD:
		print_expr(fp, e->expr0);
		fprintf(fp, " + ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_SUB:
		print_expr(fp, e->expr0);
		fprintf(fp, " - ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_MUL:
		print_expr(fp, e->expr0);
		fprintf(fp, " * ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_DIV:
		print_expr(fp, e->expr0);
		fprintf(fp, " / ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_MOD:
		print_expr(fp, e->expr0);
		fprintf(fp, " %% ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_AND:
		print_expr(fp, e->expr0);
		fprintf(fp, " & ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_OR:
		print_expr(fp, e->expr0);
		fprintf(fp, " | ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_XOR:
		print_expr(fp, e->expr0);
		fprintf(fp, " ^ ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_SHORT_OR:
		print_expr(fp, e->expr0);
		fprintf(fp, " || ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_SHORT_AND:
		print_expr(fp, e->expr0);
		fprintf(fp, " && ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_LIST:
		for (e1 = e->first; e1; e1 = e1->next) {
			print_expr(fp, e1);
			if (e1->next) {
				fprintf(fp, ", ");
			}
		}
		break;
	case EXPR_DOT:
		print_expr(fp, e->expr0);
		fprintf(fp, ".");
		fprintf(fp, "%s", e->member);
		break;
	case EXPR_ARROW:
		print_expr(fp, e->expr0);
		fprintf(fp, "->");
		fprintf(fp, "%s", e->member);
		break;
	case EXPR_ARRAY:
		print_expr(fp, e->expr0);
		fprintf(fp, "[");
		print_expr(fp, e->expr1);
		fprintf(fp, "]");
		break;
	case EXPR_FUNC:
		print_expr(fp, e->expr0);
		fprintf(fp, "(");
		if (e->expr1) {
			print_expr(fp, e->expr1);
		}
		fprintf(fp, ")");
		break;
	case EXPR_CONDITION:
		print_expr(fp, e->expr0);
		fprintf(fp, " ? ");
		print_expr(fp, e->expr1);
		fprintf(fp, " : ");
		print_expr(fp, e->expr2);
		break;
	case EXPR_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " = ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_LEFT_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " <<= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_RIGHT_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " >>= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_ADD_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " += ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_SUB_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " -= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_MUL_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " *= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_DIV_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " /= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_MOD_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " %%= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_AND_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " &= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_OR_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " |= ");
		print_expr(fp, e->expr1);
		break;
	case EXPR_XOR_ASSIGN:
		print_expr(fp, e->expr0);
		fprintf(fp, " ^= ");
		print_expr(fp, e->expr1);
		break;
	}
}

int
print_stmt(FILE *fp, int sol, int level, struct stmt *s)
{
	struct declaration *d;
	struct stmt *s1;

#if 0
	if (s->reachable) {
		fprintf(fp, "*");
	}
#endif
	switch (s->type) {
	case STMT_NONE:
		assert(0);

	case STMT_NULL:
		sol = print_indent(fp, sol, level);
		fprintf(fp, ";");
		break;

	case STMT_LABEL:
		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level - 1);
		fprintf(fp, "%s:", s->label->identifier);
		sol = 0;
		sol = print_newline(fp, sol);
		sol = print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_CASE:
		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level - 1);
		fprintf(fp, "case ");
		print_expr(fp, s->expr0);
		fprintf(fp, ":");
		sol = 0;
		sol = print_newline(fp, sol);
		sol = print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_DEFAULT:
		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level - 1);
		fprintf(fp, "default:");
		sol = 0;
		sol = print_newline(fp, sol);
		sol = print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_EXPR:
		sol = print_indent(fp, sol, level);
		print_expr(fp, s->expr0);
		fprintf(fp, ";");
		break;

	case STMT_IF:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "if (");
		print_expr(fp, s->expr0);
		fprintf(fp, ")");
		sol = print_stmt(fp, sol, level, s->stmt0);
		if (s->stmt1) {
			sol = print_indent(fp, sol, level);
			fprintf(fp, "else");
			sol = 0;
			sol = print_stmt(fp, sol, level, s->stmt1);
		}
		break;

	case STMT_SWITCH:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "switch (");
		print_expr(fp, s->expr0);
		fprintf(fp, ")");
		sol = 0;
		sol = print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_WHILE:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "while (");
		print_expr(fp, s->expr0);
		fprintf(fp, ")");
		sol = 0;
		sol =  print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_DO_WHILE:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "do");
		sol = print_stmt(fp, sol, level, s->stmt0);
		sol = print_indent(fp, sol, level);
		fprintf(fp, "while (");
		print_expr(fp, s->expr0);
		fprintf(fp, ");");
		sol = 0;
		break;

	case STMT_FOR:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "for (");
		if (s->expr0) {
			print_expr(fp, s->expr0);
		}
		fprintf(fp, "; ");
		if (s->expr1) {
			print_expr(fp, s->expr1);
		}
		fprintf(fp, "; ");
		if (s->expr2) {
			print_expr(fp, s->expr2);
		}
		fprintf(fp, ")");
		sol = 0;
		sol = print_stmt(fp, sol, level, s->stmt0);
		break;

	case STMT_GOTO:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "goto %s;", s->label->identifier);
		sol = 0;
		break;

	case STMT_CONTINUE:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "continue;");
		sol = 0;
		break;

	case STMT_BREAK:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "break;");
		sol = 0;
		break;

	case STMT_RETURN:
		sol = print_indent(fp, sol, level);
		if (s->expr0) {
			fprintf(fp, "return ");
			print_expr(fp, s->expr0);
			fprintf(fp, ";");
		} else {
			fprintf(fp, "return;");
		}
		sol = 0;
		break;

	case STMT_ASM:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "asm (");
		sol = 0;
		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level + 1);
		fprintf(fp, "\"");
		print_string(fp, s->code_len, s->code);
		fprintf(fp, "\"");
		sol = 0;
		if (s->output->first
		 || s->input->first
		 || s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->output->first; c; c = c->next) {
				fprintf(fp, " \"%s\" (", c->string);
				print_expr(fp, c->expr);
				fprintf(fp, ")");
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
		if (s->input->first
		 || s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->input->first; c; c = c->next) {
				fprintf(fp, " \"%s\" (", c->string);
				print_expr(fp, c->expr);
				fprintf(fp, ")");
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
		if (s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->change->first; c; c = c->next) {
				fprintf(fp, " \"%s\"", c->string);
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level);
		fprintf(fp, ");");
		sol = 0;
		break;

	case STMT_VA_START:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "__builtin_va_start(");
		print_expr(fp, s->expr0);
		fprintf(fp, ", ");
		print_expr(fp, s->expr1);
		fprintf(fp, ");");
		sol = 0;
		break;

	case STMT_VA_END:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "__builtin_va_end(");
		print_expr(fp, s->expr0);
		fprintf(fp, ");");
		sol = 0;
		break;
		
	case STMT_BLOCK:
		sol = print_indent(fp, sol, level);
		fprintf(fp, "{");
		sol = 0;
		sol = print_newline(fp, sol);
		for (d = s->scope->declaration_first; d; d = d->next) {
			print_declaration(fp, level + 1, d);
		}
		if (s->scope->declaration_first
		 && s->stmt_first) {
			fprintf(fp, "\n");
			sol = 1;
		}
		for (s1 = s->stmt_first; s1; s1 = s1->next) {
			sol = print_stmt(fp, sol, level + 1, s1);
			sol = print_newline(fp, sol);
		}
		sol = print_indent(fp, sol, level);
		fprintf(fp, "}");
		sol = 0;
		break;

	default:
		assert(0);
	}

	if (s->type != STMT_ASM
	 && s->output) {
		if (s->output->first
		 || s->input->first
		 || s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->output->first; c; c = c->next) {
				fprintf(fp, " \"%s\" (", c->string);
				print_expr(fp, c->expr);
				fprintf(fp, ")");
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
		if (s->input->first
		 || s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->input->first; c; c = c->next) {
				fprintf(fp, " \"%s\" (", c->string);
				print_expr(fp, c->expr);
				fprintf(fp, ")");
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
		if (s->change->first) {
			struct constraint *c;

			sol = print_newline(fp, sol);
			sol = print_indent(fp, sol, level + 1);
			fprintf(fp, ":");
			sol = 0;
			for (c = s->change->first; c; c = c->next) {
				fprintf(fp, " \"%s\"", c->string);
				if (c->next) {
					fprintf(fp, ",");
				}
			}
		}
	}

#if 0
	{
		struct decl_live *l;

		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level + 1);
		fprintf(fp, "live:");
		sol = 0;
		for (l = s->decl_live_first[0]; l; l = l->next) {
			fprintf(fp, " %s", l->decl->identifier);
		}

		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level + 1);
		fprintf(fp, "read:");
		sol = 0;
		for (l = s->decl_read_first; l; l = l->next) {
			fprintf(fp, " %s", l->decl->identifier);
		}

		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level + 1);
		fprintf(fp, "write:");
		sol = 0;
		for (l = s->decl_write_first; l; l = l->next) {
			fprintf(fp, " %s", l->decl->identifier);
		}

		sol = print_newline(fp, sol);
		sol = print_indent(fp, sol, level + 1);
		fprintf(fp, "live:");
		sol = 0;
		for (l = s->decl_live_first[1]; l; l = l->next) {
			fprintf(fp, " %s", l->decl->identifier);
		}
	}
#endif

	return sol;
}

static void
print_type_spec(FILE *fp, int level, struct type *t)
{
	int sol;
	struct declaration *dion;
	unsigned int n;

	sol = 1;
	switch (t->type) {
	default:
		for (n = 0; n < t->mod_const; n++) {
			if (! sol) {
				fprintf(fp, " ");
			}
			fprintf(fp, "const");
			sol = 0;
		}
		for (n = 0; n < t->mod_volatile; n++) {
			if (! sol) {
				fprintf(fp, " ");
			}
			fprintf(fp, "volatile");
			sol = 0;
		}
		for (n = 0; n < t->attr_packed; n++) {
			if (! sol) {
				fprintf(fp, " ");
			}
			fprintf(fp, "__attribute__((__packed__))");
			sol = 0;
		}
		break;
		break;
	case TYPE_POINTER:
	case TYPE_ARRAY:
	case TYPE_FUNCTION:
		break;
	}

	switch (t->type) {
	case TYPE_NONE:
	case TYPE_MAX:
		assert(0);

	case TYPE_ELIPSIS:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "...");
		sol = 0;
		break;

	case TYPE_VA_LIST:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "__builtin_va_list");
		sol = 0;
		break;
	case TYPE_VOID:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "void");
		sol = 0;
		break;
	case TYPE_INT8:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_char == 1) {
			fprintf(fp, "char");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_UINT8:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_char == 1) {
			fprintf(fp, "unsigned char");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_INT16:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_int == 2) {
			fprintf(fp, "int");
		} else if (opt_f_sizeof_short_int == 2) {
			fprintf(fp, "short int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_UINT16:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_int == 2) {
			fprintf(fp, "unsigned int");
		} else if (opt_f_sizeof_short_int == 2) {
			fprintf(fp, "unsigned short int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_INT32:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_int == 4) {
			fprintf(fp, "int");
		} else if (opt_f_sizeof_long_int == 4) {
			fprintf(fp, "long int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_UINT32:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_int == 4) {
			fprintf(fp, "unsigned int");
		} else if (opt_f_sizeof_long_int == 4) {
			fprintf(fp, "long unsigned int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_INT64:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_long_long_int == 8) {
			fprintf(fp, "long long int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_UINT64:
		if (! sol) {
			fprintf(fp, " ");
		}
		if (opt_f_sizeof_long_long_int == 8) {
			fprintf(fp, "unsigned long long int");
		} else {
			assert(0);
		}
		sol = 0;
		break;
	case TYPE_FLOAT32:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "float");
		sol = 0;
		break;
	case TYPE_FLOAT64:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "double");
		sol = 0;
		break;
	case TYPE_FLOAT80:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "long double");
		sol = 0;
		break;
	case TYPE_STRUCT:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "struct");
		sol = 0;
		if (t->identifier) {
			fprintf(fp, " %s", t->identifier);
		}
		if (t->scope
		 && t->scope->declaration_first) {
			fprintf(fp, " {\n");
			sol = 1;
			for (dion = t->scope->declaration_first;
			    dion;
			    dion = dion->next) {
				print_declaration(fp, level + 1, dion);
				sol = 1;
			}
			sol = print_indent(fp, sol, level);
			fprintf(fp, "}");
		}
		break;
	case TYPE_UNION:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "union");
		sol = 0;
		if (t->identifier) {
			fprintf(fp, " %s", t->identifier);
		}
		if (t->scope
		 && t->scope->declaration_first) {
			fprintf(fp, " {\n");
			sol = 1;
			for (dion = t->scope->declaration_first;
			    dion;
			    dion = dion->next) {
				print_declaration(fp, level + 1, dion);
				sol = 1;
			}
			sol = print_indent(fp, sol, level);
			fprintf(fp, "}");
		}
		break;
	case TYPE_ENUM:
		if (! sol) {
			fprintf(fp, " ");
		}
		fprintf(fp, "enum");
		sol = 0;
		if (t->identifier) {
			fprintf(fp, " %s", t->identifier);
		}
		if (t->scope) {
			fprintf(fp, " {\n");
			sol = 1;
			for (dion = t->scope->declaration_first;
			    dion;
			    dion = dion->next) {
				sol = print_indent(fp, sol, level);
				print_declarator(fp, level + 1, dion);
				fprintf(fp, ",");
				sol = print_newline(fp, sol);
			}
			fprintf(fp, "}");
			sol = 0;
		}
		break;
	case TYPE_POINTER:
	case TYPE_ARRAY:
	case TYPE_FUNCTION:
		print_type_spec(fp, level, t->declarator);
		break;
	}
}

static void
print_abs_decl_prefix(FILE *fp, int level, struct type *d)
{
	if (d->declarator) {
		print_abs_decl_prefix(fp, level, d->declarator);
	}
	switch (d->type) {
	default:
		break;
	case TYPE_POINTER:
		/* Hack... */
		if (type_is_array(d->declarator)
		 || type_is_function(d->declarator)) {
			fprintf(fp, "(");
		}
		fprintf(fp, "*");
		if (d->mod_const) {
			fprintf(fp, " const");
		}
		if (d->mod_volatile) {
			fprintf(fp, " volatile");
		}
		if (d->mod_const || d->mod_volatile) {
			fprintf(fp, " ");
		}
		break;
	case TYPE_ARRAY:
		break;
	case TYPE_FUNCTION:
		break;
	}
}

static void
print_abs_decl_suffix(FILE *fp, int level, struct type *d)
{
	struct declaration *p;

	switch (d->type) {
	default:
		break;
	case TYPE_POINTER:
		/* Hack... */
		if (type_is_array(d->declarator)
		 || type_is_function(d->declarator)) {
			fprintf(fp, ")");
		}
		break;
	case TYPE_ARRAY:
		fprintf(fp, "[");
		if (d->dimension) {
			print_expr(fp, d->dimension);
		}
		fprintf(fp, "]");
		break;
	case TYPE_FUNCTION:
		fprintf(fp, "(");
		for (p = d->parameter->declaration_first; p; p = p->next) {
			print_declaration(fp, -1, p);
			if (p->next) {
				fprintf(fp, ", ");
			}
		}
		fprintf(fp, ")");
		break;
	}
	if (d->declarator) {
		print_abs_decl_suffix(fp, level, d->declarator);
	}
}

static void
print_declarator(FILE *fp, int level, struct declaration *d)
{
	print_abs_decl_prefix(fp, level, d->type_name);
	if (d->identifier) {
		fprintf(fp, "%s", d->identifier);
	}
	print_abs_decl_suffix(fp, level, d->type_name);
	if (d->regname) {
		fprintf(fp, " asm(\"%s\")", d->regname);
	} else if (d->storage_register != -1) {
		fprintf(fp, " asm(\"%d\")", d->storage_register);
	}
	if (d->nbits) {
		fprintf(fp, " : ");
		print_expr(fp, d->nbits);
	}
	if (declaration_initializer_get(d)) {
		fprintf(fp, " = ");
		print_expr(fp, declaration_initializer_get(d));
	}
}

static void
print_type(FILE *fp, int level, struct type *ad)
{
	print_type_spec(fp, level, ad);
	fprintf(fp, " ");
	print_abs_decl_prefix(fp, level, ad);
	print_abs_decl_suffix(fp, level, ad);
}

static int
isfunc(struct declaration *d)
{
	if (d->stmt) {
		return 1;
	} else {
		return 0;
	}
}

static void
print_storage(FILE *fp, enum type_storage storage)
{
	switch (storage) {
	case STORAGE_NONE:
	case STORAGE_PARAM:
		break;
	case STORAGE_AUTO:
		/* No need to print 'auto' keyword. */
		break;
	case STORAGE_STATIC:
		fprintf(fp, "static ");
		break;
	case STORAGE_TYPEDEF:
		fprintf(fp, "typedef ");
		break;
	case STORAGE_REGISTER:
		fprintf(fp, "register ");
		break;
	case STORAGE_EXTERN:
		fprintf(fp, "extern ");
		break;
	default: assert(0);
	}
}

static void
print_attr(FILE *fp, unsigned int mod_inline, unsigned int attr_noreturn)
{
	if (mod_inline) {
		fprintf(fp, "inline ");
	}
	if (attr_noreturn) {
		fprintf(fp, "__attribute__((__noreturn__)) ");
	}
}

void
print_declaration(FILE *fp, int level, struct declaration *d)
{
	if (d->storage == STORAGE_ASM) {
		fprintf(fp, "asm (\"%s\");\n", d->code);
		
	} else if (isfunc(d)) {
		print_storage(fp, d->storage);
		print_attr(fp, d->mod_inline, d->attr_noreturn);
		print_type_spec(fp, level, d->type_name);
		fprintf(fp, "\n");
		print_declarator(fp, level, d);
		fprintf(fp, "\n");
		print_stmt(fp, 1, 0, d->stmt);
		fprintf(fp, "\n");
		fprintf(fp, "\n");
	} else {
		if (0 <= level) {
			print_indent(fp, 1, level);
		}
		print_storage(fp, d->storage);
		print_attr(fp, d->mod_inline, d->attr_noreturn);
		print_type_spec(fp, level, d->type_name);
		fprintf(fp, " ");
		print_declarator(fp, level, d);
		if (0 <= level) {
			fprintf(fp, ";\n");
		}
	}
}

void
print(FILE *fp, struct scope *s)
{
	struct declaration *d;

	for (d = s->declaration_first; d; d = d->next) {
		print_declaration(fp, 0, d);
	}
}
