--- /dev/null
+%{
+/*
+ * 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_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 *";
+
+/*
+ * Amount of space to reserve for the primary instruction output.
+ */
+static int gensel_reserve_space = 32;
+
+/*
+ * 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
+
+/*
+ * 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_IMMS8 5
+#define GENSEL_PATT_IMMU8 6
+#define GENSEL_PATT_IMMS16 7
+#define GENSEL_PATT_IMMU16 8
+#define GENSEL_PATT_LOCAL 9
+
+/*
+ * 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_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 a single clause for a rule.
+ */
+static void gensel_output_clause(gensel_clause_t clause)
+{
+ char *code;
+ int index;
+
+ /* Cache the instruction pointer into "inst" */
+ printf("\t\tinst = (%s)(gen->posn.ptr);\n", gensel_inst_type);
+ printf("\t\tif(!jit_cache_check_for_n(&(gen->posn), %d))\n",
+ gensel_reserve_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 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("reg");
+ else if(index == 1)
+ printf("reg2");
+ else
+ printf("reg3");
+ }
+ break;
+
+ case GENSEL_PATT_IMM:
+ 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 == '\n')
+ {
+ putc(*code, stdout);
+ putc('\t', stdout);
+ ++code;
+ }
+ else
+ {
+ putc(*code, stdout);
+ ++code;
+ }
+ }
+ printf("\n");
+
+ /* Copy "inst" back into the generation context */
+ 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;
+
+ /* 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 = "??";
+ 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)) != 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, 1, ", arg1);
+ 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_nint_constant", 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)
+ {
+ 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_IMM:
+ case GENSEL_PATT_IMMS8:
+ case GENSEL_PATT_IMMU8:
+ case GENSEL_PATT_IMMS16:
+ case GENSEL_PATT_IMMU16:
+ {
+ printf("\t\timm_value = %s->is_nint_constant;\n", arg);
+ }
+ break;
+
+ case GENSEL_PATT_LOCAL:
+ {
+ printf("\t\tlocal_offset = %s->frame_offset;\n", arg);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ 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) != 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) != 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);
+ }
+ }
+ gensel_output_clause(clause);
+ printf("\t}\n");
+ first = 0;
+ clause = clause->next;
+ }
+}
+
+/*
+ * List of opcodes that are supported by the input rules.
+ */
+static char **supported = 0;
+static int num_supported = 0;
+
+/*
+ * Add an opcode to the supported list.
+ */
+static void gensel_add_supported(char *name)
+{
+ supported = (char **)realloc
+ (supported, (num_supported + 1) * sizeof(char *));
+ if(!supported)
+ {
+ exit(1);
+ }
+ supported[num_supported++] = name;
+}
+
+/*
+ * Output the list of supported opcodes.
+ */
+static void gensel_output_supported(void)
+{
+ int index;
+ for(index = 0; index < num_supported; ++index)
+ {
+ 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_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_TERNARY "`ternary'"
+%token K_STACK "`stack'"
+%token K_INST_TYPE "`%inst_type'"
+
+/*
+ * Define the yylval types of the various non-terminals.
+ */
+%type <name> IDENTIFIER
+%type <code> CODE_BLOCK
+%type <options> Options OptionList Option PatternElement
+%type <clauses> Clauses Clause
+%type <pattern> Pattern
+
+%expect 0
+
+%start Start
+%%
+
+Start
+ : /* empty */
+ | Rules
+ ;
+
+Rules
+ : Rule
+ | Rules Rule
+ ;
+
+Rule
+ : IDENTIFIER ':' Options Clauses {
+ printf("case %s:\n{\n", $1);
+ printf("\t%s inst;\n", gensel_inst_type);
+ gensel_declare_regs($4.head, $3);
+ if(($3 & GENSEL_OPT_SPILL_BEFORE) != 0)
+ {
+ printf("\t_jit_regs_spill_all(gen);\n");
+ }
+ gensel_output_clauses($4.head, $3);
+ if(($3 & (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\t_jit_gen_spill_reg(gen, reg, -1, insn->dest);\n");
+ printf("\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");
+ gensel_free_clauses($4.head);
+ gensel_add_supported($1);
+ }
+ | K_INST_TYPE IDENTIFIER {
+ gensel_inst_type = $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_TERNARY { $$ = GENSEL_OPT_TERNARY; }
+ | K_STACK { $$ = GENSEL_OPT_STACK; }
+ ;
+
+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
+ : PatternElement {
+ $$.elem[0] = $1;
+ $$.elem[1] = GENSEL_PATT_END;
+ $$.size = 1;
+ }
+ | Pattern ',' 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_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;
+}
--- /dev/null
+%{
+/*
+ * gen-sel-scanner.l - Lex input file for the "gen-sel" scanner.
+ *
+ * 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 "gen-sel-parser.h"
+#include <config.h>
+#ifdef HAVE_STRING_H
+ #include <string.h>
+#elif defined(HAVE_STRINGS_H)
+ #include <strings.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+ #include <stdlib.h>
+#endif
+
+extern YYSTYPE yylval;
+
+/*
+ * Current file and line number.
+ */
+char *gensel_filename = "";
+long gensel_linenum = 1;
+
+/*
+ * Return a token code from the lexical analyser.
+ */
+#define RETURNTOK(x) return (x)
+
+/*
+ * Forward declarations.
+ */
+static void gensel_skip_comment(void);
+static char *gensel_read_block(void);
+
+/*
+ * Duplicate a string.
+ */
+static char *gensel_strdup(const char *str)
+{
+ char *new_str;
+ if(!str)
+ {
+ return 0;
+ }
+ new_str = (char *)malloc(strlen(str) + 1);
+ if(!new_str)
+ {
+ return 0;
+ }
+ strcpy(new_str, str);
+ return new_str;
+}
+
+%}
+
+%option outfile="lex.yy.c"
+%option noyywrap
+%option nounput
+
+DIGIT [0-9]
+IDALPHA [a-zA-Z_]
+WHITE [ \t\v\r\f]
+
+%%
+
+"->" { RETURNTOK(K_PTR); }
+"reg" { RETURNTOK(K_REG); }
+"lreg" { RETURNTOK(K_LREG); }
+"freg" { RETURNTOK(K_FREG); }
+"imm" { RETURNTOK(K_IMM); }
+"imms8" { RETURNTOK(K_IMMS8); }
+"immu8" { RETURNTOK(K_IMMU8); }
+"imms16" { RETURNTOK(K_IMMS16); }
+"immu16" { RETURNTOK(K_IMMU16); }
+"local" { RETURNTOK(K_LOCAL); }
+"spill_before" { RETURNTOK(K_SPILL_BEFORE); }
+"binary" { RETURNTOK(K_BINARY); }
+"unary" { RETURNTOK(K_UNARY); }
+"ternary" { RETURNTOK(K_TERNARY); }
+"stack" { RETURNTOK(K_STACK); }
+"%inst_type" { RETURNTOK(K_INST_TYPE); }
+
+{IDALPHA}({DIGIT}|{IDALPHA})* {
+ yylval.name = gensel_strdup(yytext);
+ if(!(yylval.name))
+ {
+ exit(1);
+ }
+ RETURNTOK(IDENTIFIER);
+ }
+
+{WHITE}+ ;
+
+\n { ++gensel_linenum; }
+
+"{" { yylval.code.filename = gensel_filename;
+ yylval.code.linenum = gensel_linenum;
+ yylval.code.block = gensel_read_block();
+ RETURNTOK(CODE_BLOCK); }
+
+"/*" { gensel_skip_comment(); }
+
+. { RETURNTOK(((int)(yytext[0])) & 0xFF); }
+
+%%
+
+/*
+ * Skip a comment in the input stream.
+ */
+static void gensel_skip_comment(void)
+{
+ int ch;
+ for(;;)
+ {
+ ch = input();
+ if(ch == EOF)
+ {
+ break;
+ }
+ else if(ch == '*')
+ {
+ ch = input();
+ while(ch == '*')
+ {
+ ch = input();
+ }
+ if(ch == EOF || ch == '/')
+ {
+ break;
+ }
+ else if(ch == '\n')
+ {
+ ++gensel_linenum;
+ }
+ }
+ else if(ch == '\n')
+ {
+ ++gensel_linenum;
+ }
+ }
+}
+
+/*
+ * Add a character to a reallocatable buffer.
+ */
+#define ADD_CH(c) \
+ do { \
+ if((buflen + 1) >= bufmax) \
+ { \
+ buf = (char *)realloc(buf, bufmax + 64); \
+ if(!buf) \
+ { \
+ exit(1); \
+ } \
+ bufmax += 64; \
+ } \
+ buf[buflen++] = (char)c; \
+ buf[buflen] = (char)'\0'; \
+ } while (0)
+
+/*
+ * Read a literal code block from the input stream.
+ */
+static char *gensel_read_block(void)
+{
+ char *buf = 0;
+ int buflen = 0;
+ int bufmax = 0;
+ int ch;
+ int level = 1;
+ ADD_CH('{');
+ for(;;)
+ {
+ ch = input();
+ if(ch == EOF)
+ {
+ fprintf(stderr, "Unexpected EOF in code block\n");
+ exit(1);
+ }
+ ADD_CH(ch);
+ if(ch == '{')
+ {
+ ++level;
+ }
+ else if(ch == '\n')
+ {
+ ++gensel_linenum;
+ }
+ else if(ch == '}')
+ {
+ --level;
+ if(level == 0)
+ {
+ break;
+ }
+ }
+ }
+ return buf;
+}