%{
/*
 * gen-sel-parser.y - Bison grammar for the "gen-sel" program.
 *
 * Copyright (C) 2004  Southern Storm Software, Pty Ltd.
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <config.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
	#include <string.h>
#elif defined(HAVE_STRINGS_H)
	#include <strings.h>
#endif
#ifdef HAVE_STDLIB_H
	#include <stdlib.h>
#endif

/*
 * Imports from the lexical analyser.
 */
extern int yylex(void);
extern void yyrestart(FILE *file);
#ifdef YYTEXT_POINTER
extern char *yytext;
#else
extern char yytext[];
#endif

/*
 * Report error messages from the parser.
 */
static void yyerror(char *msg)
{
	puts(msg);
}

/*
 * Current file and line number.
 */
extern char *gensel_filename;
extern long gensel_linenum;

/*
 * Instruction type for the "inst" variable.
 */
static char *gensel_inst_type = "unsigned char *";
static int gensel_new_inst_type = 0;

/*
 * Amount of space to reserve for the primary instruction output.
 */
static int gensel_reserve_space = 32;
static int gensel_reserve_more_space = 128;

/*
 * First register in a stack arrangement.
 */
static int gensel_first_stack_reg = 8;	/* st0 under x86 */

/*
 * Option values.
 */
#define	GENSEL_OPT_SPILL_BEFORE			0x0001
#define	GENSEL_OPT_BINARY				0x0002
#define	GENSEL_OPT_UNARY				0x0004
#define	GENSEL_OPT_TERNARY				0x0008
#define	GENSEL_OPT_STACK				0x0010
#define	GENSEL_OPT_UNARY_BRANCH			0x0020
#define	GENSEL_OPT_BINARY_BRANCH		0x0040
#define	GENSEL_OPT_ONLY					0x0080
#define	GENSEL_OPT_MANUAL				0x0100
#define	GENSEL_OPT_UNARY_NOTE			0x0200
#define	GENSEL_OPT_BINARY_NOTE			0x0400
#define	GENSEL_OPT_MORE_SPACE			0x0800

/*
 * Pattern values.
 */
#define	GENSEL_PATT_END					0
#define	GENSEL_PATT_REG					1
#define	GENSEL_PATT_LREG				2
#define	GENSEL_PATT_FREG				3
#define	GENSEL_PATT_IMM					4
#define	GENSEL_PATT_IMMZERO				5
#define	GENSEL_PATT_IMMS8				6
#define	GENSEL_PATT_IMMU8				7
#define	GENSEL_PATT_IMMS16				8
#define	GENSEL_PATT_IMMU16				9
#define	GENSEL_PATT_LOCAL				10

/*
 * Information about clauses.
 */
typedef struct gensel_clause *gensel_clause_t;
struct gensel_clause
{
	int				pattern[8];
	char		   *filename;
	long			linenum;
	char		   *code;
	gensel_clause_t	next;
};

/*
 * Free a list of clauses.
 */
static void gensel_free_clauses(gensel_clause_t clauses)
{
	gensel_clause_t next;
	while(clauses != 0)
	{
		next = clauses->next;
		free(clauses->code);
		free(clauses);
		clauses = next;
	}
}

/*
 * Declare the register variables that are needed for a set of clauses.
 */
static void gensel_declare_regs(gensel_clause_t clauses, int options)
{
	int have_reg1 = 0;
	int have_reg2 = 0;
	int have_reg3 = 0;
	int have_imm = 0;
	int have_local = 0;
	int index;
	while(clauses != 0)
	{
		for(index = 0; clauses->pattern[index] != GENSEL_PATT_END; ++index)
		{
			switch(clauses->pattern[index])
			{
				case GENSEL_PATT_REG:
				case GENSEL_PATT_LREG:
				case GENSEL_PATT_FREG:
				{
					if(index == 0)
						have_reg1 = 1;
					else if(index == 1)
						have_reg2 = 1;
					else
						have_reg3 = 1;
				}
				break;

				case GENSEL_PATT_IMMZERO: break;

				case GENSEL_PATT_IMM:
				case GENSEL_PATT_IMMS8:
				case GENSEL_PATT_IMMU8:
				case GENSEL_PATT_IMMS16:
				case GENSEL_PATT_IMMU16:
				{
					have_imm = 1;
				}
				break;

				case GENSEL_PATT_LOCAL:
				{
					have_local = 1;
				}
				break;
			}
		}
		clauses = clauses->next;
	}
	if(have_reg1)
	{
		printf("\tint reg;\n");
	}
	if(have_reg2 && (options & GENSEL_OPT_STACK) == 0)
	{
		printf("\tint reg2;\n");
	}
	if(have_reg3 && (options & GENSEL_OPT_STACK) == 0)
	{
		printf("\tint reg3;\n");
	}
	if(have_imm)
	{
		printf("\tjit_nint imm_value;\n");
	}
	if(have_local)
	{
		printf("\tjit_nint local_offset;\n");
	}
}

/*
 * Output the code within a clause.
 */
static void gensel_output_clause_code(gensel_clause_t clause)
{
	char *code;
	int index;

	/* Output the line number information from the original file */
	printf("#line %ld \"%s\"\n", clause->linenum, clause->filename);

	/* Output the clause code */
	printf("\t\t");
	code = clause->code;
	while(*code != '\0')
	{
		if(*code == '$' && code[1] >= '1' && code[1] <= '9')
		{
			index = code[1] - '1';
			switch(clause->pattern[index])
			{
				case GENSEL_PATT_REG:
				case GENSEL_PATT_LREG:
				case GENSEL_PATT_FREG:
				{
					if(index == 0)
						printf("_jit_reg_info[reg].cpu_reg");
					else if(index == 1)
						printf("_jit_reg_info[reg2].cpu_reg");
					else
						printf("_jit_reg_info[reg3].cpu_reg");
				}
				break;

				case GENSEL_PATT_IMM:
				case GENSEL_PATT_IMMZERO:
				case GENSEL_PATT_IMMS8:
				case GENSEL_PATT_IMMU8:
				case GENSEL_PATT_IMMS16:
				case GENSEL_PATT_IMMU16:
				{
					printf("imm_value");
				}
				break;

				case GENSEL_PATT_LOCAL:
				{
					printf("local_offset");
				}
				break;
			}
			code += 2;
		}
		else if(*code == '%' && code[1] >= '1' && code[1] <= '9')
		{
			index = code[1] - '1';
			switch(clause->pattern[index])
			{
				case GENSEL_PATT_REG:
				case GENSEL_PATT_LREG:
				case GENSEL_PATT_FREG:
				{
					if(index == 0)
						printf("_jit_reg_info[_jit_reg_info[reg].other_reg].cpu_reg");
					else if(index == 1)
						printf("_jit_reg_info[_jit_reg_info[reg2].other_reg].cpu_reg");
					else
						printf("_jit_reg_info[_jit_reg_info[reg3].other_reg].cpu_reg");
				}
				break;
			}
			code += 2;
		}
		else if(*code == '\n')
		{
			putc(*code, stdout);
			putc('\t', stdout);
			++code;
		}
		else
		{
			putc(*code, stdout);
			++code;
		}
	}
	printf("\n");
}

/*
 * Output a single clause for a rule.
 */
static void gensel_output_clause(gensel_clause_t clause, int options)
{
	/* Cache the instruction pointer into "inst" */
	if(gensel_new_inst_type)
	{
		printf("\t\tjit_gen_load_inst_ptr(gen, inst);\n");
	}
	else
	{
		printf("\t\tinst = (%s)(gen->posn.ptr);\n", gensel_inst_type);
		printf("\t\tif(!jit_cache_check_for_n(&(gen->posn), %d))\n",
			   (((options & GENSEL_OPT_MORE_SPACE) == 0)
			   		? gensel_reserve_space : gensel_reserve_more_space));
		printf("\t\t{\n");
		printf("\t\t\tjit_cache_mark_full(&(gen->posn));\n");
		printf("\t\t\treturn;\n");
		printf("\t\t}\n");
	}

	/* Output the clause code */
	gensel_output_clause_code(clause);

	/* Copy "inst" back into the generation context */
	if(gensel_new_inst_type)
	{
		printf("\t\tjit_gen_save_inst_ptr(gen, inst);\n");
	}
	else
	{
		printf("\t\tgen->posn.ptr = (unsigned char *)inst;\n");
	}
}

/*
 * Output the clauses for a rule.
 */
static void gensel_output_clauses(gensel_clause_t clauses, int options)
{
	const char *arg1;
	const char *arg2;
	const char *arg3;
	const char *arg;
	const char *reg;
	const char *flag1;
	const char *flag2;
	const char *flag3;
	const char *flag;
	int destroy1;
	int destroy2;
	int destroy3;
	int destroy;
	gensel_clause_t clause;
	int first, index;
	int check_index;

	/* If the clause is manual, then output it as-is */
	if((options & GENSEL_OPT_MANUAL) != 0)
	{
		gensel_output_clause_code(clauses);
		return;
	}

	/* Determine the location of this instruction's arguments */
	if((options & GENSEL_OPT_TERNARY) != 0)
	{
		arg1 = "insn->dest";
		arg2 = "insn->value1";
		arg3 = "insn->value2";
		flag1 = "DEST";
		flag2 = "VALUE1";
		flag3 = "VALUE2";
		destroy1 = 0;
		destroy2 = 0;
		destroy3 = 0;
	}
	else
	{
		arg1 = "insn->value1";
		arg2 = "insn->value2";
		arg3 = "??";
		flag1 = "VALUE1";
		flag2 = "VALUE2";
		flag3 = "??";
		if((options & (GENSEL_OPT_BINARY_BRANCH |
					   GENSEL_OPT_UNARY_BRANCH |
					   GENSEL_OPT_BINARY_NOTE |
					   GENSEL_OPT_UNARY_NOTE)) != 0)
		{
			destroy1 = 0;
		}
		else
		{
			destroy1 = 1;
		}
		destroy2 = 0;
		destroy3 = 0;
	}

	/* If all of the clauses start with a register, then load the first
	   value into a register before we start checking cases */
	check_index = 0;
	if((options & (GENSEL_OPT_BINARY | GENSEL_OPT_UNARY |
				   GENSEL_OPT_BINARY_BRANCH | GENSEL_OPT_UNARY_BRANCH |
				   GENSEL_OPT_BINARY_NOTE | GENSEL_OPT_UNARY_NOTE)) != 0 &&
	   (options & GENSEL_OPT_STACK) == 0)
	{
		clause = clauses;
		while(clause != 0)
		{
			if(clause->pattern[0] != GENSEL_PATT_REG &&
			   clause->pattern[0] != GENSEL_PATT_LREG &&
			   clause->pattern[0] != GENSEL_PATT_FREG)
			{
				break;
			}
			clause = clause->next;
		}
		if(!clause)
		{
			printf("\treg = _jit_regs_load_value(gen, %s, %d, ",
				   arg1, destroy1);
			printf("(insn->flags & (JIT_INSN_%s_NEXT_USE | "
								   "JIT_INSN_%s_LIVE)));\n", flag1, flag1);
			check_index = 1;
		}
	}

	/* Output the clause checking and dispatching code */
	clause = clauses;
	first = 1;
	while(clause != 0)
	{
		if(clause->next)
		{
			if(first)
				printf("\tif(");
			else
				printf("\telse if(");
			for(index = check_index; clause->pattern[index]; ++index)
			{
				if(index == 0)
					arg = arg1;
				else if(index == 1)
					arg = arg2;
				else
					arg = arg3;
				switch(clause->pattern[index])
				{
					case GENSEL_PATT_REG:
					case GENSEL_PATT_LREG:
					case GENSEL_PATT_FREG:
					{
						printf("%s->in_register", arg);
					}
					break;

					case GENSEL_PATT_IMM:
					{
						printf("%s->is_constant", arg);
					}
					break;

					case GENSEL_PATT_IMMZERO:
					{
						printf("%s->is_nint_constant && ", arg);
						printf("%s->address == 0", arg);
					}
					break;

					case GENSEL_PATT_IMMS8:
					{
						printf("%s->is_nint_constant && ", arg);
						printf("%s->address >= -128 && ", arg);
						printf("%s->address <= 127", arg);
					}
					break;

					case GENSEL_PATT_IMMU8:
					{
						printf("%s->is_nint_constant && ", arg);
						printf("%s->address >= 0 && ", arg);
						printf("%s->address <= 255", arg);
					}
					break;

					case GENSEL_PATT_IMMS16:
					{
						printf("%s->is_nint_constant && ", arg);
						printf("%s->address >= -32768 && ", arg);
						printf("%s->address <= 32767", arg);
					}
					break;

					case GENSEL_PATT_IMMU16:
					{
						printf("%s->is_nint_constant && ", arg);
						printf("%s->address >= 0 && ", arg);
						printf("%s->address <= 65535", arg);
					}
					break;

					case GENSEL_PATT_LOCAL:
					{
						printf("%s->in_frame && !(%s->in_register)", arg, arg);
					}
					break;
				}
				if(clause->pattern[index + 1])
				{
					printf(" && ");
				}
			}
			printf(")\n\t{\n");
		}
		else if(first)
		{
			printf("\t{\n");
		}
		else
		{
			printf("\telse\n\t{\n");
		}
		if((options & GENSEL_OPT_STACK) == 0 || clause->next)
		{
			for(index = check_index; clause->pattern[index]; ++index)
			{
				if(index == 0)
				{
					arg = arg1;
					reg = "reg";
					flag = flag1;
					destroy = destroy1;
				}
				else if(index == 1)
				{
					arg = arg2;
					reg = "reg2";
					flag = flag2;
					destroy = destroy2;
				}
				else
				{
					arg = arg3;
					reg = "reg3";
					flag = flag3;
					destroy = destroy3;
				}
				switch(clause->pattern[index])
				{
					case GENSEL_PATT_REG:
					case GENSEL_PATT_LREG:
					case GENSEL_PATT_FREG:
					{
						printf("\t\t%s = _jit_regs_load_value(gen, %s, %d, ",
							   reg, arg, destroy);
						printf("(insn->flags & (JIT_INSN_%s_NEXT_USE | "
											   "JIT_INSN_%s_LIVE)));\n",
							   flag, flag);
					}
					break;
	
					case GENSEL_PATT_IMMZERO: break;

					case GENSEL_PATT_IMM:
					case GENSEL_PATT_IMMS8:
					case GENSEL_PATT_IMMU8:
					case GENSEL_PATT_IMMS16:
					case GENSEL_PATT_IMMU16:
					{
						printf("\t\timm_value = %s->address;\n", arg);
					}
					break;
	
					case GENSEL_PATT_LOCAL:
					{
						printf("\t\tlocal_offset = %s->frame_offset;\n", arg);
					}
					break;
				}
			}
		}
		else
		{
			if((options & GENSEL_OPT_ONLY) != 0)
			{
				printf("\t\tif(!_jit_regs_is_top(gen, insn->value1) ||\n");
				printf("\t\t   _jit_regs_num_used(gen, %d) != 1)\n",
					   gensel_first_stack_reg);
				printf("\t\t{\n");
				printf("\t\t\t_jit_regs_spill_all(gen);\n");
				printf("\t\t}\n");
			}
			if((options & GENSEL_OPT_TERNARY) != 0)
			{
				printf("\t\treg = _jit_regs_load_to_top_three\n");
				printf("\t\t\t(gen, insn->dest, insn->value1, insn->value2,\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_DEST_NEXT_USE | "
											   "JIT_INSN_DEST_LIVE)),\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_VALUE1_NEXT_USE | "
											   "JIT_INSN_VALUE1_LIVE)),\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_VALUE2_NEXT_USE | "
											   "JIT_INSN_VALUE2_LIVE)), "
											   "%d);\n",
					   gensel_first_stack_reg);
			}
			else if((options & (GENSEL_OPT_BINARY |
								GENSEL_OPT_BINARY_BRANCH)) != 0)
			{
				printf("\t\treg = _jit_regs_load_to_top_two\n");
				printf("\t\t\t(gen, insn->value1, insn->value2,\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_VALUE1_NEXT_USE | "
											   "JIT_INSN_VALUE1_LIVE)),\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_VALUE2_NEXT_USE | "
											   "JIT_INSN_VALUE2_LIVE)), "
											   "%d);\n",
					   gensel_first_stack_reg);
			}
			else if((options & (GENSEL_OPT_UNARY |
								GENSEL_OPT_UNARY_BRANCH |
								GENSEL_OPT_UNARY_NOTE)) != 0)
			{
				printf("\t\treg = _jit_regs_load_to_top\n");
				printf("\t\t\t(gen, insn->value1,\n");
				printf("\t\t\t\t(insn->flags & (JIT_INSN_VALUE1_NEXT_USE | "
											   "JIT_INSN_VALUE1_LIVE)), "
											   "%d);\n",
					   gensel_first_stack_reg);
			}
		}
		if((options & (GENSEL_OPT_BINARY_BRANCH |
					   GENSEL_OPT_UNARY_BRANCH)) != 0)
		{
			/* Spill all other registers back to their original positions */
			printf("\t\t_jit_regs_spill_all(gen);\n");
		}
		gensel_output_clause(clause, options);
		printf("\t}\n");
		first = 0;
		clause = clause->next;
	}
}

/*
 * List of opcodes that are supported by the input rules.
 */
static char **supported = 0;
static char **supported_options = 0;
static int num_supported = 0;

/*
 * Add an opcode to the supported list.
 */
static void gensel_add_supported(char *name, char *option)
{
	supported = (char **)realloc
		(supported, (num_supported + 1) * sizeof(char *));
	if(!supported)
	{
		exit(1);
	}
	supported[num_supported] = name;
	supported_options = (char **)realloc
		(supported_options, (num_supported + 1) * sizeof(char *));
	if(!supported_options)
	{
		exit(1);
	}
	supported_options[num_supported++] = option;
}

/*
 * Output the list of supported opcodes.
 */
static void gensel_output_supported(void)
{
	int index;
	for(index = 0; index < num_supported; ++index)
	{
		if(supported_options[index])
		{
			if(supported_options[index][0] == '!')
			{
				printf("#ifndef %s\n", supported_options[index] + 1);
			}
			else
			{
				printf("#ifdef %s\n", supported_options[index]);
			}
			printf("case %s:\n", supported[index]);
			printf("#endif\n");
		}
		else
		{
			printf("case %s:\n", supported[index]);
		}
	}
	printf("\treturn 1;\n\n");
}

%}

/*
 * Define the structure of yylval.
 */
%union {
	char		   *name;
	struct
	{
		char	   *filename;
		long		linenum;
		char	   *block;
	}				code;
	int				options;
	struct
	{
		int			elem[8];
		int			size;

	}				pattern;
	struct
	{
		struct gensel_clause *head;
		struct gensel_clause *tail;

	}				clauses;
}

/*
 * Primitive lexical tokens and keywords.
 */
%token IDENTIFIER			"an identifier"
%token CODE_BLOCK			"a code block"
%token K_PTR				"`->'"
%token K_REG				"word register"
%token K_LREG				"long register"
%token K_FREG				"float register"
%token K_IMM				"immediate value"
%token K_IMMZERO			"immediate zero value"
%token K_IMMS8				"immediate signed 8-bit value"
%token K_IMMU8				"immediate unsigned 8-bit value"
%token K_IMMS16				"immediate signed 16-bit value"
%token K_IMMU16				"immediate unsigned 16-bit value"
%token K_LOCAL				"local variable"
%token K_SPILL_BEFORE		"`spill_before'"
%token K_BINARY				"`binary'"
%token K_UNARY				"`unary'"
%token K_UNARY_BRANCH		"`unary_branch'"
%token K_BINARY_BRANCH		"`binary_branch'"
%token K_UNARY_NOTE			"`unary_note'"
%token K_BINARY_NOTE		"`binary_note'"
%token K_TERNARY			"`ternary'"
%token K_STACK				"`stack'"
%token K_ONLY				"`only'"
%token K_MORE_SPACE			"`more_space'"
%token K_MANUAL				"`manual'"
%token K_INST_TYPE			"`%inst_type'"

/*
 * Define the yylval types of the various non-terminals.
 */
%type <name>				IDENTIFIER IfClause IdentifierList
%type <code>				CODE_BLOCK
%type <options>				Options OptionList Option PatternElement
%type <clauses>				Clauses Clause
%type <pattern>				Pattern Pattern2

%expect 0

%start Start
%%

Start
	: /* empty */
	| Rules
	;

Rules
	: Rule
	| Rules Rule
	;

Rule
	: IdentifierList IfClause ':' Options Clauses {
				if($2)
				{
					if(($2)[0] == '!')
					{
						printf("#ifndef %s\n\n", $2 + 1);
					}
					else
					{
						printf("#ifdef %s\n\n", $2);
					}
				}
				printf("case %s:\n{\n", $1);
				if(($4 & GENSEL_OPT_MANUAL) == 0)
				{
					printf("\t%s inst;\n", gensel_inst_type);
				}
				gensel_declare_regs($5.head, $4);
				if(($4 & GENSEL_OPT_SPILL_BEFORE) != 0)
				{
					printf("\t_jit_regs_spill_all(gen);\n");
				}
				gensel_output_clauses($5.head, $4);
				if(($4 & (GENSEL_OPT_BINARY | GENSEL_OPT_UNARY)) != 0)
				{
					printf("\tif((insn->flags & JIT_INSN_DEST_NEXT_USE) != 0)\n");
					printf("\t{\n");
					printf("\t\t_jit_regs_set_value(gen, reg, insn->dest, 0);\n");
					printf("\t}\n");
					printf("\telse\n");
					printf("\t{\n");
					printf("\t\tint other_reg;\n");
					printf("\t\tif(gen->contents[reg].is_long_start)\n");
					printf("\t\t{\n");
					printf("\t\t\tother_reg = _jit_reg_info[reg].other_reg;\n");
					printf("\t\t}\n");
					printf("\t\telse\n");
					printf("\t\t{\n");
					printf("\t\t\tother_reg = -1;\n");
					printf("\t\t}\n");
					printf("\t\t_jit_gen_spill_reg(gen, reg, other_reg, insn->dest);\n");
					printf("\t\tif(insn->dest->has_global_register)\n");
					printf("\t\t\tinsn->dest->in_global_register = 1;\n");
					printf("\t\telse\n");
					printf("\t\t\tinsn->dest->in_frame = 1;\n");
					printf("\t\t_jit_regs_free_reg(gen, reg, 1);\n");
					printf("\t}\n");
				}
				printf("}\nbreak;\n\n");
				if($2)
				{
					printf("#endif /* %s */\n\n", $2);
				}
				gensel_free_clauses($5.head);
				gensel_add_supported($1, $2);
			}
	| K_INST_TYPE IDENTIFIER	{
				gensel_inst_type = $2;
				gensel_new_inst_type = 1;
			}
	;

IdentifierList
	: IDENTIFIER				{ $$ = $1; }
	| IdentifierList ',' IDENTIFIER	{
				char *result = (char *)malloc(strlen($1) + strlen($3) + 16);
				if(!result)
				{
					exit(1);
				}
				strcpy(result, $1);
				strcat(result, ":\ncase ");
				strcat(result, $3);
				free($1);
				free($3);
				$$ = result;
			}
	;

IfClause
	: /* empty */				{ $$ = 0; }
	| '(' IDENTIFIER ')'		{ $$ = $2; }
	;

Options
	: /* empty */				{ $$ = 0; }
	| OptionList				{ $$ = $1; }
	;

OptionList
	: Option					{ $$ = $1; }
	| OptionList ',' Option		{ $$ = $1 | $3; }
	;

Option
	: K_SPILL_BEFORE			{ $$ = GENSEL_OPT_SPILL_BEFORE; }
	| K_BINARY					{ $$ = GENSEL_OPT_BINARY; }
	| K_UNARY					{ $$ = GENSEL_OPT_UNARY; }
	| K_UNARY_BRANCH			{ $$ = GENSEL_OPT_UNARY_BRANCH; }
	| K_BINARY_BRANCH			{ $$ = GENSEL_OPT_BINARY_BRANCH; }
	| K_UNARY_NOTE				{ $$ = GENSEL_OPT_UNARY_NOTE; }
	| K_BINARY_NOTE				{ $$ = GENSEL_OPT_BINARY_NOTE; }
	| K_TERNARY					{ $$ = GENSEL_OPT_TERNARY; }
	| K_STACK					{ $$ = GENSEL_OPT_STACK; }
	| K_ONLY					{ $$ = GENSEL_OPT_ONLY; }
	| K_MORE_SPACE				{ $$ = GENSEL_OPT_MORE_SPACE; }
	| K_MANUAL					{ $$ = GENSEL_OPT_MANUAL; }
	;

Clauses
	: Clause					{ $$ = $1; }
	| Clauses Clause			{
				$1.tail->next = $2.head;
				$$.head = $1.head;
				$$.tail = $2.tail;
			}
	;

Clause
	: '[' Pattern ']' K_PTR CODE_BLOCK	{
				gensel_clause_t clause;
				int index;
				clause = (gensel_clause_t)malloc(sizeof(struct gensel_clause));
				if(!clause)
				{
					exit(1);
				}
				for(index = 0; index < 8; ++index)
				{
					clause->pattern[index] = $2.elem[index];
				}
				clause->filename = $5.filename;
				clause->linenum = $5.linenum;
				clause->code = $5.block;
				clause->next = 0;
				$$.head = clause;
				$$.tail = clause;
			}
	;

Pattern
	: /* empty */		{
				$$.elem[0] = GENSEL_PATT_END;
				$$.size = 0;
			}
	| Pattern2			{ $$ = $1; }
	;

Pattern2
	: PatternElement				{
				$$.elem[0] = $1;
				$$.elem[1] = GENSEL_PATT_END;
				$$.size = 1;
			}
	| Pattern2 ',' PatternElement	{
				$$.elem[$1.size] = $3;
				$$.elem[$1.size + 1] = GENSEL_PATT_END;
				$$.size = $1.size + 1;
			}
	;

PatternElement
	: K_REG						{ $$ = GENSEL_PATT_REG; }
	| K_LREG					{ $$ = GENSEL_PATT_LREG; }
	| K_FREG					{ $$ = GENSEL_PATT_FREG; }
	| K_IMM						{ $$ = GENSEL_PATT_IMM; }
	| K_IMMZERO					{ $$ = GENSEL_PATT_IMMZERO; }
	| K_IMMS8					{ $$ = GENSEL_PATT_IMMS8; }
	| K_IMMU8					{ $$ = GENSEL_PATT_IMMU8; }
	| K_IMMS16					{ $$ = GENSEL_PATT_IMMS16; }
	| K_IMMU16					{ $$ = GENSEL_PATT_IMMU16; }
	| K_LOCAL					{ $$ = GENSEL_PATT_LOCAL; }
	;

%%

#define	COPYRIGHT_MSG	\
" * Copyright (C) 2004  Southern Storm Software, Pty Ltd.\n" \
" *\n" \
" * This program is free software; you can redistribute it and/or modify\n" \
" * it under the terms of the GNU General Public License as published by\n" \
" * the Free Software Foundation; either version 2 of the License, or\n" \
" * (at your option) any later version.\n" \
" *\n" \
" * This program is distributed in the hope that it will be useful,\n" \
" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n" \
" * GNU General Public License for more details.\n" \
" *\n" \
" * You should have received a copy of the GNU General Public License\n" \
" * along with this program; if not, write to the Free Software\n" \
" * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n"
 
int main(int argc, char *argv[])
{
	FILE *file;
	if(argc != 2)
	{
		fprintf(stderr, "Usage: %s input.sel >output.slc\n", argv[0]);
		return 1;
	}
	file = fopen(argv[1], "r");
	if(!file)
	{
		perror(argv[1]);
		return 1;
	}
	printf("/%c Automatically generated from %s - DO NOT EDIT %c/\n",
		   '*', argv[1], '*');
	printf("/%c\n%s%c/\n\n", '*', COPYRIGHT_MSG, '*');
	printf("#if defined(JIT_INCLUDE_RULES)\n\n");
	gensel_filename = argv[1];
	gensel_linenum = 1;
	yyrestart(file);
	if(yyparse())
	{
		fclose(file);
		return 1;
	}
	fclose(file);
	printf("#elif defined(JIT_INCLUDE_SUPPORTED)\n\n");
	gensel_output_supported();
	printf("#endif\n");
	return 0;
}
