/* next error will still return 1 by default */
%{
#include "y.tab.h"
#define FLEX_SCANNER
#include "lint.h"
#include "string.h"
#include "config.h"
#include "exec.h"
#include "interpret.h"
#include "instrs.h"

int strict_types_flag;  /* not used with this lexical analyzer... */
int current_line;
int total_lines;	/* Used to compute average compiled lines/s */
char *current_file;
extern char *argument_name;
int number(), lookupword(), string(), atoi();

/*
 * Flex doesn't handle negative characters, but this change will at least
 * not crash the game.
 */
#ifdef YY_FATAL_ERROR
#undef YY_FATAL_ERROR
#define YY_FATAL_ERROR(msg) \
      { \
      fputs( msg, stderr ); \
      putc( '\n', stderr ); \
      error(msg); \
      }
#endif

#ifdef FLEX
#define yywrap() 1
#endif

/*
 * The number of arguments stated below, are used by the compiler.
 * If min == max, then no information has to be coded about the
 * actual number of arguments. Otherwise, the actual number of arguments
 * will be stored in the byte after the instruction.
 * A maximum value of -1 means unlimited maximum value.
 *
 * If an argument has type 0 (T_INVALID) specified, then no checks will
 * be done at run time.
 */
static struct keyword {
    char *word;
    short  token;
    char min_args;	/* Minimum number of arguments. */
    char max_args;	/* Maximum number of arguments. */
    short ret_type;	/* The return type used by the compiler. */
    char arg_type1;	/* Type of argument 1 */
    char arg_type2;	/* Type of argument 2 */
} predefs[] = {
#include "efuns.i"
};

/*
I have no idea what these were supposed to do -- Raistlin 
{"add_adj"              , F_ADD_ADJ },
{"add_subst"		, F_ADD_SUBST },
*/

#define NELEM(a) (sizeof (a) / sizeof (a)[0])

static struct keyword reswords[] = {
{ "break",		F_BREAK, },
{ "catch",		F_CATCH, },
{ "continue",		F_CONTINUE, },
{ "do",			F_DO, },
{ "else",		F_ELSE, },
{ "for",		F_FOR, },
{ "if",			F_IF, },
{ "inherit",		F_INHERIT, },
{ "int",		F_INT, },
{ "mixed",		F_MIXED, },
{ "nomask",		F_NO_MASK, },
{ "object",		F_OBJECT, },
{ "parse_command",	F_PARSE_COMMAND, },
{ "private",		F_PRIVATE, },
{ "protected",		F_PROTECTED, },
{ "public",             F_PUBLIC, },
{ "return",		F_RETURN, },
{ "sscanf",		F_SSCANF, },
{ "static",		F_STATIC, },
{ "status",		F_STATUS, },
{ "string",		F_STRING_DECL, },
{ "void",		F_VOID, },
{ "while",		F_WHILE, },
};

extern char *local_names[];
extern int current_number_of_locals;

extern char *string_copy();
void pre_processor_info();
%}
#ifdef LEX
%e 1500
%p 5000
%n 1000
%k 1000
%a 2500
%o 3000
#endif

dec		(0|[1-9][0-9]*)
oct		(0[0-7]+)
hex		(0[xX][0-9a-fA-F]+)
ident		([_a-zA-Z][_a-zA-Z0-9]*)
string		(\"([^"\\\n]|\\\n|\\.)*\")

%%
";"			{ return ';'; }
"("			{ return '('; }
")"			{ return ')'; }
","			{ return ','; }
"{"			{ return '{'; }
"}"			{ return '}'; }
"++"			{ return F_INC; }
"--"			{ return F_DEC; }
"?"			{ return '?'; }
":"			{ return ':'; }
"~"			{ return '~'; }
"&&"			{ return F_LAND; }
"&"			{ return '&'; }
"&="			{ return F_AND_EQ; }
"||"			{ return F_LOR; }
"|"			{ return '|'; }
"|="			{ return F_OR_EQ; }
"^"			{ return '^'; }
"^="			{ return F_XOR_EQ; }
"<<"			{ return F_LSH; }
"<<="			{ return F_LSH_EQ; }
">>"			{ return F_RSH; }
">>="			{ return F_RSH_EQ; }
"!"			{ return F_NOT; }
"->"			{ return F_ARROW; }
"="			{ return '='; }
"-"			{ return '-'; }
"-="			{ return F_SUB_EQ; }
"+"			{ return '+'; }
"+="			{ return F_ADD_EQ; }
"*"			{ return '*'; }
"*="			{ return F_MULT_EQ; }
"%"			{ return '%'; }
"%="			{ return F_MOD_EQ; }
"/"			{ return '/'; }
"/="			{ return F_DIV_EQ; }
"<"			{ return '<'; }
">"			{ return '>'; }
">="			{ return F_GE; }
"<="			{ return F_LE; }
"=="			{ return F_EQ; }
"!="			{ return F_NE; }
"::"			{ return F_COLON_COLON; }
^#.*$   { pre_processor_info(yytext); }
"["	{ return '['; }
"]"	{ return ']'; }
{dec}   { return dec(yytext); }
{hex}   { return hex(yytext); }
{oct}   { return oct(yytext); }
'.'	{ yylval.number = yytext[1];
          return F_NUMBER; }
{ident}		{ 
                   int i;
                   if ((i = lookup_resword(yytext)) >= 0)
                      return i;
                   return ident(yytext); 
                }
{string}	{ return string(yytext); }
[\t ]		{ break; }
"\n"	{ current_line++;
	  total_lines++;
          store_line_number_info();
 	}
.	{ char buff[100]; sprintf(buff, "Illegal character '%c'", yytext[0]);
	  yyerror(buff); }
%%

extern YYSTYPE yylval;

#ifdef LEX
int yywrap() { return 1; }
#endif

/*
 * debugging, gives the name of a single.
 */

int string(str)
    char *str;
{
    char *p;

    p = (char *)string_copy(str);
    yylval.string = p;
    for (str++; str[1] != '\0'; str++, p++) {
        if (str[0] == '\\' && str[1] == '\n')
           str++, p--; /* ignore \n's at the end of a line  -- R&R */
        else
        if (str[0] == '\\' && str[1] == 'n') {
	    *p = '\n';
	    str++;
	} else if (str[0] == '\\' && str[1] == 't') {
	    *p = '\t';
	    str++;
	} else if (str[0] == '\\' && str[1] == '"') {	/* LA */
	    *p = '"';					/* LA */
	    str++;					/* LA */
	    if (!str[1]) {
		yyerror("Unterminated string");
		break;
	    }
 	} else if (str[0] == '\\') {
 	    *p = str[1];
 	    str++;
	} else
	    *p = *str;
    }
    *p = '\0';
    return F_STRING;
}

int dec(str)
    char *str;
{
    int i;

    i = atoi(str);
    return number(i);
}

int hex(str)
    char *str;
{
    int i;

    sscanf(str, "%x", &i);
    return number(i);
}

int oct(str)
    char *str;
{
    int i;

    sscanf(str, "%x", &i);
    return number(i);
}

int number(i)
    int i;
{
    if (i == 0) {
        yylval.number = 0; 
	return F_CONST0;
    }
    if (i == 1) {
	yylval.number = 1;
	return F_CONST1;
    }
    yylval.number = i;
    return F_NUMBER;
}

void start_new_file(f)
    FILE *f;
{
	extern int yyprevious;
#ifdef FLEX
    yy_init = 1;
#endif
#ifdef LEX
    extern int yyprevious;	/* KLUDGE! */
    NLSTATE;
    yysptr = yysbuf;
#endif
    yyin = f;
}

void pre_processor_info(str)
    char *str;
{
    int line,i;
    char file[100];
    if (sscanf(str, "# %d \"%s\"", &line, file) != 2)
	return;
    i = strlen(file);
    if (file[i-1] == '"')
	file[i-1] = '\0';
    free(current_file);
    current_file = string_copy(file);
    current_line = line - 1;
}

#if 0
#ifdef LEX
void fix_vars()
{
	extern int yyprevious;
	current_line = 0;
	yyprevious = 10;
	yylineno = 1;
	yysptr = yysbuf;
}
#endif
#endif

struct instr instrs[256];

static void add_instr_name(name, n)
    char *name;
    int n;
{
    instrs[n - F_OFFSET].name = name;
}

void init_num_args()
{
    int i, n;

    for(i=0; i<NELEM(predefs); i++) {
	n = predefs[i].token - F_OFFSET;
	if (n < 0 || n > NELEM(instrs))
	    fatal("Token %s has illegal value %d.\n", predefs[i].word, n);
	instrs[n].min_arg = predefs[i].min_args;
	instrs[n].max_arg = predefs[i].max_args;
	instrs[n].name = predefs[i].word;
	instrs[n].type[0] = predefs[i].arg_type1;
	instrs[n].type[1] = predefs[i].arg_type2;
	instrs[n].ret_type = predefs[i].ret_type;
    }
    add_instr_name("<", F_LT);
    add_instr_name(">", F_GT);
    add_instr_name("<=", F_LE);
    add_instr_name(">=", F_GE);
    add_instr_name("==", F_EQ);
    add_instr_name("+=", F_ADD_EQ);
    add_instr_name("!", F_NOT);
    add_instr_name("index", F_INDEX_INSTR);
    add_instr_name("push_indexed_lvalue", F_PUSH_INDEXED_LVALUE);
    add_instr_name("identifier", F_IDENTIFIER);
    add_instr_name("local", F_LOCAL_NAME);
    add_instr_name("indirect", F_INDIRECT);
    add_instr_name("number", F_NUMBER);
    add_instr_name("push_local_variable_lvalue", F_PUSH_LOCAL_VARIABLE_LVALUE);
    add_instr_name("const1", F_CONST1);
    add_instr_name("subtract", F_SUBTRACT);
    add_instr_name("assign", F_ASSIGN);
    add_instr_name("pop", F_POP_VALUE);
    add_instr_name("const0", F_CONST0);
    add_instr_name("jump_when_zero", F_JUMP_WHEN_ZERO);
    add_instr_name("jump_when_non_zero", F_JUMP_WHEN_NON_ZERO);
    add_instr_name("||", F_LOR);
    add_instr_name("&&", F_LAND);
    add_instr_name("-=", F_SUB_EQ);
    add_instr_name("jump", F_JUMP);
    add_instr_name("return", F_RETURN);
    add_instr_name("sscanf", F_SSCANF);
    add_instr_name("string", F_STRING);
    add_instr_name("call", F_CALL_FUNCTION_BY_ADDRESS);
    add_instr_name("aggregate", F_AGGREGATE);
    add_instr_name("push_identifier_lvalue", F_PUSH_IDENTIFIER_LVALUE);
    add_instr_name("+", F_ADD);
    add_instr_name("!=", F_NE);
    add_instr_name("dup", F_DUP);
    add_instr_name("catch", F_CATCH);
    add_instr_name("neg", F_NEGATE);
}

char *get_f_name(n)
    int n;
{
    if (instrs[n-F_OFFSET].name)
	return instrs[n-F_OFFSET].name;
    return "<OTHER>";
}

static int
lookupword(s, words, h)
char *s;
struct keyword *words;
int h;
{
    int i, l, r;

    l = 0;
    for(;;) {
      i = (l+h)/2;
      r = strcmp(s, words[i].word);
      if (r == 0)
          return words[i].token;
      else if (l == i)
          return -1;
      else if (r < 0)
          h = i;
      else
          l = i;
    }
}

static int lookup_resword(s)
    char *s;
{
    return lookupword(s, reswords, NELEM(reswords));
}

int lookup_predef(s)
    char *s;
{
    return lookupword(s, predefs, NELEM(predefs));
}

static int islocal(str)
    char *str;
{
    int i;

    for (i=current_number_of_locals-1; i>=0; i--) {
	if (strcmp(local_names[i], str) == 0)
            return i;
    }
    return -1;
}

static int ident(str)
    char *str;
{
    int i;
   
    i = islocal(str);
    if (i >= 0) {
        yylval.number = i;
        return F_LOCAL_NAME;
    }
    yylval.string = string_copy(str);
    return F_IDENTIFIER;
}