/* Lexical analisis Ksi.
   Copyright (C) 2001, 2002 Michal Moskal (malekith@pld-linux.org)

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include "ksi.h"
#include "ggc.h"	/* ggc_alloc_string() */

static FILE *yyin;
static char *line_ptr;

/* helpers, a'la ctype */
static inline int ws(int ch)
{
	return ch == ' ' || ch == '\t';
}

static int id_starter(int ch)
{
	return ((ch|' ') >= 'a' && (ch|' ') <= 'z') || 
		ch == '_' || ch == '$' || ch == '@';
}

static int id_body(int ch)
{
	return ((ch|' ') >= 'a' && (ch|' ') <= 'z') ||
	       ch == '_' || ch == '$' || ch == '@' ||
	       (ch >= '0' && ch <= '9');
}

static char *get_raw_line(void)
{
	static char *line;
	static int line_buf_size;
	static int eof;

	if (eof)
		return NULL;
		
	if (line == NULL) 
		line = xmalloc(line_buf_size = 512);
	
	
	line[line_buf_size-2] = '\n';
	
	if (fgets(line, line_buf_size, yyin) == NULL) {
		free(line);
		eof = 1;
		return NULL;
	}
	
	while (line[line_buf_size-2] != '\n') {
		/* need to read more */
		line = xrealloc(line, line_buf_size * 2);
		line[line_buf_size * 2 - 2] = '\n';
		if (fgets(line + line_buf_size - 1, line_buf_size + 1, 
			  yyin) == NULL) {
			eof = 1;
			break;
		}
		line_buf_size *= 2;
	}

	lineno++;

	return line;
}

static int version_seen = 0;
static char *preprocess(const char *p);

static void define_string(const char *name, const char *val)
{
	tree id;

	id = get_identifier(name);
	
	IDENTIFIER_SYSTEM_MACRO(id) = ggc_alloc_string(val, -1);
}

struct predefined_opt {
	struct predefined_opt *next;
	const char *opt;
};

struct predefined_opt *predefined_opts;

void kpp_define_opt(const char *p)
{
	struct predefined_opt *o = xmalloc(sizeof(*o));
	
	o->next = predefined_opts;
	predefined_opts = o;
	o->opt = p;
}

static void kpp_defined_opts(void)
{
	const char *name, *defval = "1", *val, *p;
	struct predefined_opt *ptr = NULL, *tmp;

	while (predefined_opts) {
		tmp = predefined_opts;
		predefined_opts = predefined_opts->next;
		tmp->next = ptr;
		ptr = tmp;
	}
	
	while (ptr) {
		p = ptr->opt;
		ptr = ptr->next;
		name = p;
		while (*p && *p != '=')
			p++;
		
		if (*p)
			val = p + 1;
		else
			val = defval;

		name = ggc_alloc_string(name, p - name);
		define_string(name, val);
	}
}

static void define_preprocessed(const char *name, const char *val)
{
	define_string(name, preprocess(val));
}

static void define_int(const char *name, int x)
{
	char buf[100];

	sprintf(buf, "%d", x);
	define_string(name, buf);
}

static void define_macro(char *p)
{
	char *name, *val;
	tree id;

	name = p;
	
	while (*p && !ws(*p))
		p++;
		
	while (ws(*p))
		*p++ = 0;
	val = p;

	id = get_identifier(name);
	IDENTIFIER_USER_MACRO(id) = ggc_alloc_string(val, -1);
}

static void process_directive(char *line)
{
	char *p = line, *end;

	while (ws(*p))
		p++;
		
	while (*p && !ws(*p))
		p++;
		
	while (ws(*p))
		*p++ = 0;
		
	if (*p) {
		end = strchr(p, 0) - 1;
		/* trim white spaces */
		while (ws(*end))
			*end-- = 0;
	}
	
	
	if (strcmp("version", line) == 0) {
		if (version_seen)
			fatal_error("second !version tag found");
		if (strcmp(p, "1") != 0)
			fatal_error("unsupported (non-1) version of "
				    "input file: '%s'", p);
		version_seen++;
		return;
	} 

	if (!version_seen)
		fatal_error("got directive before !version (!%s)", line);
	
	if (strcmp("define", line) == 0) {
		define_macro(p);
	} else
		fatal_error("unknown directive: !%s", line);
}

static const char *expand_macro(const char **pp)
{
	const char *macro = NULL, *name;
	const char *p = *pp + 2;
	tree id;

	while (id_body(*p))
		p++;

	name = ggc_alloc_string(*pp + 1, p - (*pp + 1));
	id = get_identifier(name);

	if (**pp == '.')
		macro = IDENTIFIER_SYSTEM_MACRO(id);
	else
		macro = IDENTIFIER_USER_MACRO(id);
	
	if (!macro)
		fatal_error("undefined macro %s used", name); 

	*pp = p;

	return macro;
}

static char *preprocess(const char *p)
{
	static char *target_line = NULL;
	static int target_size = 0;
	int in_string = 0;
	int stringize;
	int target_pos = 0;
	const char *macro;
	int len;
	
	if (p == NULL)
		return NULL;	/* EOF */

	if (target_line == 0)
		target_line = xmalloc((target_size = 512) + 10);
	
	while (*p) {
		switch (*p) {
		case '"':
			in_string = !in_string;
			break;
		case '\\':
			target_line[target_pos++] = *p++;
			break;
		case '\'':
			if (in_string)
				break;
			target_line[target_pos++] = *p++;
			if (*p == '\\')
				target_line[target_pos++] = *p++;
			break;
		case '#':
			if (!in_string)
				p = "";		/* remove comment */
			break;
		case '/':
			if (!in_string && p[1] == '/')
				p = "";
			break;
		case '.':
		case '^':
		case '%':
			if ((stringize = (*p == '%'))) {
				if (p[1] == '.' || p[1] == '^')
					p++;
				else
					break;
			}
			
			if (in_string || !id_starter(p[1])) {
				if (stringize)
					p--;	/* undo */
				break;
			}
			
			macro = expand_macro(&p);
			len = strlen(macro) + stringize * 2;
			if (target_pos + len >= target_size) {
				while (target_pos + len >= target_size)
					target_size *= 2;
				target_line = xrealloc(target_line, 
						       target_size + 10);
			}
			if (stringize) {
				target_line[target_pos++] = '"';
				len -= 2;
			}
			memcpy(target_line + target_pos, macro, len);
			target_pos += len;
			if (stringize)
				target_line[target_pos++] = '"';
			break;
		default:
			break;
		}
		
		/* don't go after \0 */
		if (*p) {
			target_line[target_pos++] = *p++;
			if (target_pos >= target_size) {
				target_line = xrealloc(target_line, 
						      (target_size *= 2) + 10);
				if (target_line == NULL)
					fatal_error("out of core");
			}
		}
	}

	target_line[target_pos] = 0;
	
	return target_line; 
}

/* get line, preprocess it, skip comments */
static char *get_line(void)
{
	char *p, *start = NULL, *end;
	
	p = preprocess(get_raw_line());

	if (p == NULL)
		return NULL;	/* EOF */
		
	start = p;
	while (ws(*start))
		start++;
	
	if (*p) {
		end = strchr(p, 0) - 1;
		if (*end == '\n')
			*end = 0;
	}
	
	if (*start == '!') {
		process_directive(start + 1);
		*p = 0;	/* return empty line */
	} else if (*start && !version_seen)
		fatal_error("got input before !version (%s)", p);
	
	return p; 
}

/* get next character, return ' ' for empty lines and comments */
static int get_ch(void)
{
	if (line_ptr == NULL || *line_ptr == 0) {
		line_ptr = get_line();
	
		if (line_ptr == NULL)
			fatal_error("unexpected EOF");
		
		if (*line_ptr == 0 || *line_ptr == '#' ||
		    (line_ptr[0] == '/' && line_ptr[1] == '/'))
		    	*line_ptr = 0;
		return ' ';
	} else
		return *line_ptr++;
}

static int get_non_ws(void)
{
	int c;

	while (ws(c = get_ch()))
		{}
	
	return c;
}

int get_separator()
{
	int c = get_ch();

	if (c == ')' || c == ']')
		return c;

	if (ws(c)) {
		c = get_non_ws();
		if (c == ')' || c == ']')
			return c;
		line_ptr--;
		return ' ';
	}
		
	fatal_error("expected ')', ']' or white space, found '%c'", c);

	return 0;
}

const char *arg_name(int arg)
{
	switch (arg) {
	case arg_int:
		return "int";
	case arg_uint:
		return "uint";
	case arg_float:
		return "float";
	case arg_string:
		return "string";
	case arg_id:
		return "id";
	case arg_kw:
		return "keyword";
	case arg_list_beg:
		return "[";
	case arg_list_end:
		return "]";
	default:
		return "BAD_ARG";
	}
}

static int hex(int c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
	c |= ' ';
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	return -1;
}

static int parse_escape(char **ptr)
{
	int code = 0;
	
	switch (**ptr) {
	case 'n':
		code = '\n';
		break;
	case 't':
		code = '\t';
		break;
	case 'r':
		code = '\r';
		break;
	case 'e':
		code = 27;
		break;
	case '"':
	case '\'':
	case '\\':
		code = **ptr;
		break;
	case '0':
		return 0;	/* unlinke C! */
	case 'x':
	case 'X':
		code = hex((*ptr)[1]) * 16 + hex((*ptr)[2]);
		if (code < 0)
			fatal_error("invalid hex escape '\\x%c%c'",
				    (*ptr)[1], (*ptr)[2]);
		(*ptr) += 2;
		break;

	case 0:
		fatal_error("newline after \\");
		break;
	default:
		fatal_error("invalid escape '\\%c'", **ptr);
		break;
	}

	(*ptr)++;
	return code;
}

static void parse_string(struct arg *ret)
{
#define BUFSIZE 999
	char buf[BUFSIZE+1];
	char *p, *data = NULL;
	int len = -1;
	int n, rec_flag = 0;

restart:
	n = 0;
	
	for (p = line_ptr; *p; n++)
		if (*p == '"')
			break;
		else if (*p == '\\') {
			p++;
			parse_escape(&p);
		} else
			p++;
	
	if (!*p)
		fatal_error("unterminated string");

	if (rec_flag) {
		/* we were called recursivly */
		if (data == buf) {
			if (len + n > BUFSIZE) {
				data = xmalloc(len + n);
				memcpy(data, buf, len + 1);
			}
		} else {
			data = xrealloc(data, len + n);
		}
		p = data + len - 1;
		len += n;
	} else {
		len = n + 1;
		if (len > BUFSIZE)
			data = xmalloc(len);
		else
			data = buf;
		p = data;
	}
	
	for (;;)
		if (*line_ptr == '"')
			break;
		else if (*line_ptr == '\\') {
			line_ptr++;
			*p++ = parse_escape(&line_ptr);
		} else
			*p++ = *line_ptr++;
	*p = 0;
	
	line_ptr++;

	if (*line_ptr == '.') {
		int c;

		line_ptr++;
		c = get_non_ws();
		if (c != '"')
			fatal_error("expected `\"' after `\".', not %c", c);
		
		rec_flag++;
		goto restart;
	}
	
	ret->sel = arg_string;
	ret->u.val_string.data = ggc_alloc_string(data, len);
	ret->u.val_string.len = len;
	if (data != buf)
		free(data);
}

static void parse_kw(struct arg *ret)
{
	char *p;
	keyword_t kw;

	for (p = line_ptr; *p; p++)
		if (!id_body(*p))
			break;
	
	/* first check if it's a keyword */
	kw = lookup_keyword(line_ptr, p - line_ptr);
	if (kw == kw__error)
		fatal_error("unrecognised keyword '%.*s'", 
			    (int)(p - line_ptr), line_ptr);
	/* we've got keyword */
	ret->sel = arg_kw;
	ret->u.val_kw = kw;

	line_ptr = p;
}

static void parse_id(struct arg *ret)
{
	char *p;
	
	for (p = line_ptr--; *p; p++)
		if (!id_body(*p))
			break;
	
	ret->sel = arg_id;
	ret->u.val_id = ggc_alloc_string(line_ptr, p - line_ptr);

	line_ptr = p;
}


static int one_digit(int c)
{
	if (c >= '0' && c <= '9')
		return c - '0';
		
	c |= ' ';
	if (c >= 'a' && c <= 'z')
		return c - 'a';
		
	return 999;
}

static void str2int(char **pp,
		    unsigned HOST_WIDE_INT *low,
		    unsigned HOST_WIDE_INT *high, int base)
{
#define TOTAL_PARTS ((HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR) * 2)
	unsigned int parts[TOTAL_PARTS];
	char *p;
	int i, n;
	int neg = 0, overflow = 0, seen_digit = 0;
	
	for (i = 0; i < TOTAL_PARTS; i++)
		parts[i] = 0;
		
	p = *pp;

	if (base < 0) {
		neg++;
		base = -base;
	}
		
	for (;;) {
		n = one_digit(*p);
		if (n >= base)
			break;
		p++;
		seen_digit++;
		for (i = 0; i < TOTAL_PARTS; i++) {
			parts[i] *= base;
			if (i) {
				parts[i]
				    += (parts[i - 1] >>
					HOST_BITS_PER_CHAR);
				parts[i - 1]
				    &= (1 << HOST_BITS_PER_CHAR) - 1;
			} else
				parts[0] += n;
		}

		/* If the highest-order part overflows (gets larger than
		   a host char will hold) then the whole number has
		   overflowed.  Record this and truncate the highest-order
		   part. */
		if ((parts[TOTAL_PARTS - 1] >> HOST_BITS_PER_CHAR) != 0) {
			overflow++;
			parts[TOTAL_PARTS - 1] &= (1 << HOST_BITS_PER_CHAR) - 1;
		}
	}
	
	if (neg) {
		int c = 1;
		
		for (i = 0; i < TOTAL_PARTS; i++) {
			parts[i] = (~parts[i]) & 
				   ((1 << HOST_BITS_PER_CHAR) - 1);
			if (c)
				parts[i]++;
			if (parts[i] & (1 << HOST_BITS_PER_CHAR))
				c = 1;
			else
				c = 0;
		}
	}
	
	*high = *low = 0;

	for (i = 0; i < HOST_BITS_PER_WIDE_INT / HOST_BITS_PER_CHAR; i++) {
		*high |= ((HOST_WIDE_INT) parts[i + (HOST_BITS_PER_WIDE_INT
						     / HOST_BITS_PER_CHAR)]
			  << (i * HOST_BITS_PER_CHAR));
		*low |=
		    (HOST_WIDE_INT) parts[i] << (i * HOST_BITS_PER_CHAR);
	}
	

	if (!seen_digit)
		ice();
	if (overflow)
		fatal_error("integer overflow: %.*s", 
			    (int)(p - *pp), *pp);
		
	*pp = p;
}

static void might_be_float(struct arg *ret, char *start, int c)
{
	int fl = 0, len;
	char buf[200];
	
	if (*line_ptr == '.') {
		do
			line_ptr++;
		while (*line_ptr >= '0' && *line_ptr <= '9');
		
		fl++;
	} 
	
	if ((*line_ptr|' ') == 'e') {
		do
			line_ptr++;
		while (*line_ptr >= '0' && *line_ptr <= '9');
		
		fl++;
	}
	
	if (!fl)
		return;
	
	ret->sel = arg_float;
	len = line_ptr - start;
	if (c) {
		if (len + 1 > (int)sizeof(buf)) {
			char *tmp;
			tmp = xmalloc(len + 1);
			tmp[0] = c;
			memcpy(tmp+1, start, len);
			ret->u.val_float = ggc_alloc_string(tmp, len + 1);
			free(tmp);
		} else {
			buf[0] = c;
			memcpy(buf+1, start, len);
			ret->u.val_float = ggc_alloc_string(buf, len + 1);
		}
	} else {
		ret->u.val_float = ggc_alloc_string(start, len);
	}
}

struct arg *get_arg(int allowed, struct arg *ret)
{
	int c;
	char *start;

	c = get_non_ws();
	
	ret->sel = 0;
	ret->u.val_int.low = 0;
	ret->u.val_int.high = 0;
	
	start = line_ptr - 1;
	if (c == '0') {
		switch (*line_ptr) {
		case 0:
			ret->sel = arg_int;
			break;
		case 'x':
		case 'X':
			ret->sel = arg_uint;
			str2int(&line_ptr, 
				&ret->u.val_uint.low, 
				&ret->u.val_uint.high, 16);
			break;
		default:
			if (*line_ptr >= '1' && *line_ptr <= '9') {
				ret->sel = arg_uint; 
				str2int(&line_ptr, 
					&ret->u.val_uint.low, 
					&ret->u.val_uint.high, 8);
			} else
				ret->sel = arg_int;
			break;
		}
	
		might_be_float(ret, start, 0);
	} else if (c >= '1' && c <= '9') {
		ret->sel = arg_int;
		line_ptr--;
		str2int(&line_ptr, 
			&ret->u.val_int.low, 
			&ret->u.val_int.high, 10);
			
		might_be_float(ret, start, 0);
	} else if (c == '-') {
		c = get_ch();
		/* 0 not allowed, we don't want to negate unsigned numbers */
		if (c >= '1' && c <= '9') {
			ret->sel = arg_int;
			line_ptr--;
			str2int(&line_ptr, 
				&ret->u.val_int.low, 
				&ret->u.val_int.high, -10);

			might_be_float(ret, start, '-');
		} else
			fatal_error("junk ('%c') after '-'", c);
	} else if (c == '\'') {
		ret->sel = arg_uint;
		switch (*line_ptr) {
		case 0:
		case '\'':
			fatal_error("illegal character after \"'\"");
			break;
		case '\\':
			line_ptr++;
			ret->u.val_uint.low = parse_escape(&line_ptr);
			break;
		default:
			ret->u.val_uint.low = (unsigned char)*line_ptr++;
			break;
		}
		if (*line_ptr != '\'')
			fatal_error("no closing \"'\" (found '%c')", *line_ptr);
		line_ptr++;
	} else if (c == '"') {
		parse_string(ret);
	} else if (id_starter(c)) {
		parse_id(ret);
	} else if (c == '(') {
		parse_kw(ret);
	} else if (c == '[') {
		ret->sel = arg_list_beg;
	} else if (c == ']') {
		ret->sel = arg_list_end;
	} else
		fatal_error("illegal character '%c'", c);
	
	if (ret->sel & allowed)
		return ret;
	
	/* oops, we cannot use this... */
	error("expected one of:");
	{
		int f = 1;

		while (f < 0x8000000) {
			if (f & allowed)
				error(" - %s", arg_name(f));
			f <<= 1;
		}
	}
	
	fatal_error("got %s, bailing out", arg_name(ret->sel));

	return NULL;
}

extern tree dbx_last_input_file;

const char *ksi_init_parse(const char *filename)
{
	if (filename == NULL || strcmp(filename, "-") == 0) {
		filename = "stdin";
		yyin = stdin;
	} else
		yyin = fopen(filename, "r");
	
	if (yyin == NULL)
		fatal_io_error("cannot open input file '%s'", filename);

	lineno = 1;

	if (dbx_last_input_file == NULL)
		dbx_last_input_file = get_identifier(filename);
	
	init_types();

	return filename;
}

void finish_parse(void);

void finish_parse()
{
	fini_types();
}

char *kpp_output;
FILE *kpp_out_fd;

int yyparse()
{
	if (flag_just_preprocess) {
		char *line;
		while ((line = get_line()))
			fprintf(kpp_out_fd, "%s\n", line);
	} else {
		/* now we might switch to regular input processing */
		parse_unit();
	}
	
	return 0;
}

void init_term(struct term_state *ctx, keyword_t kw)
{
	ctx->kw = kw;
	ctx->arg_no = 0;
	ctx->next_sep = 0;
}

void fini_term(struct term_state *ctx)
{
	int s = (ctx->next_sep ? ctx->next_sep : get_separator());

	if (s == ' ')
		fatal_error("(%s ...) takes just %d arg(s)", 
			    keyword_name(ctx->kw), ctx->arg_no);
	else if (s != ')')
		fatal_error("no closing ')' for (%s ...) (found '%c')", 
			    keyword_name(ctx->kw), s);
}

void arg0_term(keyword_t kw)
{
	struct term_state ctx;

	init_term(&ctx, kw);
	fini_term(&ctx);
}

void get_term_arg(struct term_state *ctx, int allowed, struct arg *a)
{
	int s;
	
	ctx->arg_no++;
	
	if (ctx->next_sep) {
		s = ctx->next_sep;
		ctx->next_sep = 0;
	} else
		s = get_separator();
		
	if (s == ')')
		fatal_error("(%s ...) needs more then %d args", 
			    keyword_name(ctx->kw), ctx->arg_no - 1);
	else if (s != ' ')
		fatal_error("expected whitespace in (%s ...) (found '%c')", 
			    keyword_name(ctx->kw), s);
	
	get_arg(allowed, a);
}

tree get_id(struct term_state *ctx)
{
	struct arg a;
	
	get_term_arg(ctx, arg_id, &a);

	return get_identifier(a.u.val_id);
}

int optional_term_arg_present(struct term_state *ctx)
{
	return ((ctx->next_sep = get_separator()) == ' ');
}

keyword_t get_keyword(struct term_state *ctx)
{
	struct arg a;
	keyword_t kw;

	get_term_arg(ctx, arg_kw, &a);
	kw = a.u.val_kw;

	return kw;
}

void expect_keyword(struct term_state *ctx, keyword_t exp)
{
	keyword_t kw = get_keyword(ctx);

	if (exp != kw)
		fatal_error("found (%s ...) where (%s ...) was expected", 
			    keyword_name(kw), keyword_name(exp));
}

void init_list(struct list_state *ctx)
{
	ctx->arg_no = 0;
	ctx->eol = 0;
}

void expect_list(struct list_state *ctx)
{
	struct arg a;

	get_arg(arg_list_beg, &a);
	init_list(ctx);
}

/* return -1 if none, 0 if present */
int get_list_arg(struct list_state *ctx, int allowed, struct arg *a)
{
	if (ctx->eol)
		return -1;

	ctx->arg_no++;

	get_arg(allowed | arg_list_end, a);

	if (a->sel == arg_list_end) {
		ctx->eol++;
		return -1;
	}

	return 0;
}

keyword_t get_keyword_from_list(struct list_state *ctx)
{
	struct arg a;
	keyword_t kw;

	if (get_list_arg(ctx, arg_kw, &a))
		return kw__error;
	kw = a.u.val_kw;

	return kw;
}

void expect_keyword_in_list(struct list_state *ctx, keyword_t exp)
{
	keyword_t kw = get_keyword_from_list(ctx);

	if (exp != kw)
		fatal_error("found (%s ...) where (%s ...) was expected", 
			    keyword_name(kw), keyword_name(exp));
}

/*
 * Initialize preprocessor symbols.
 */
void kpp_init(void)
{
	define_int("ksi_version", 1);
	define_int("kpp", 1);

	define_int("c_char_bits", CHAR_TYPE_SIZE);
	define_int("c_short_bits", SHORT_TYPE_SIZE);
	define_int("c_int_bits", INT_TYPE_SIZE);
	define_int("c_long_bits", LONG_TYPE_SIZE);
	define_int("c_long_long_bits", LONG_LONG_TYPE_SIZE);
	define_int("c_float_bits", FLOAT_TYPE_SIZE);
	define_int("c_double_bits", DOUBLE_TYPE_SIZE);
	define_int("c_long_double_bits", LONG_DOUBLE_TYPE_SIZE);
	define_int("c_ptr_bits", POINTER_SIZE);
	
	define_int("c_char_bytes", CHAR_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_short_bytes", SHORT_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_int_bytes", INT_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_long_bytes", LONG_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_long_long_bytes", LONG_LONG_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_float_bytes", FLOAT_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_double_bytes", DOUBLE_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_long_double_bytes", LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT);
	define_int("c_ptr_bytes", POINTER_SIZE / BITS_PER_UNIT);
	
	define_preprocessed("c_int", "(int .c_int_bits)");
	define_preprocessed("c_char", "(int .c_char_bits)");
	define_preprocessed("c_short", "(int .c_short_bits)");
	define_preprocessed("c_long", "(int .c_long_bits)");
	define_preprocessed("c_long_long", "(int .c_long_long_bits)");
	define_preprocessed("c_double", "(float .c_double_bits)");
	define_preprocessed("c_float", "(float .c_float_bits)");
	define_preprocessed("c_long_double", "(float .c_long_double_bits)");
	define_preprocessed("c_ptr", "(ptr (uint 8))");
	define_preprocessed("c_ptr_off", "(int .c_ptr_bits)");
	define_preprocessed("c_uptr_off", "(uint .c_ptr_bits)");
	
	define_preprocessed("c_uint", "(uint .c_int_bits)");
	define_preprocessed("c_uchar", "(uint .c_char_bits)");
	define_preprocessed("c_ushort", "(uint .c_short_bits)");
	define_preprocessed("c_ulong", "(uint .c_long_bits)");
	define_preprocessed("c_ulong_long", "(uint .c_long_long_bits)");

	/* shortcuts */
	define_string("int8", "(int 8)");
	define_string("int16", "(int 16)");
	define_string("int32", "(int 32)");
	define_string("int64", "(int 64)");
	define_string("int128", "(int 128)");

	define_string("uint8", "(uint 8)");
	define_string("uint16", "(uint 16)");
	define_string("uint32", "(uint 32)");
	define_string("uint64", "(uint 64)");
	define_string("uint128", "(uint 128)");
	
	define_string("ptr", "(ptr (uint 8))");
	define_string("bool", "(bool)");
	define_string("void", "(void)");
	
	define_preprocessed("ptr_off", ".c_ptr_off");
	define_preprocessed("uptr_off", ".c_uptr_off");
	define_preprocessed("sizetype", ".c_uptr_off");

	define_string("float32", "(float 32)");
	define_string("float64", "(float 64)");
	define_string("float96", "(float 96)");
	define_string("float128", "(float 128)");
	
	define_string("null", "(const (ptr (uint 8)) 0)");
	define_string("true", "(const (bool) 1)");
	define_string("false", "(const (bool) 0)");
	
	define_string("string", "const (ptr (uint 8))");
	
	define_string("local", "[(local)]"); /* same as (nil) */
	define_string("public", "[(public)]");
	define_string("extern", "[(extern)]");

	/* endian stuff */
	define_int("bytes_big_endian", BYTES_BIG_ENDIAN);
	define_int("words_big_endian", WORDS_BIG_ENDIAN);
	if (BYTES_BIG_ENDIAN)
		define_int("bytes_big_endian_defined", 1);
	if (WORDS_BIG_ENDIAN)
		define_int("words_big_endian_defined", 1);
	define_preprocessed("bytes_big_endian_bool", 
			    "(const (bool) .bytes_big_endian)");
	define_preprocessed("words_big_endian_bool", 
			    "(const (bool) .words_big_endian)");

	kpp_defined_opts();

	if (kpp_output == NULL || strcmp(kpp_output, "-") == 0)
		kpp_out_fd = stdout;
	else {
		if ((kpp_out_fd = fopen(kpp_output, "w")) == NULL)
			fatal_error("cannot open kpp output file `%s'", 
				    kpp_output);
	}
}
