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

int current_line;
int total_lines;	/* Used to compute average compiled lines/s */
char *current_file;
extern char *argument_name;
int number(), lookup_table(), 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

/*
 * using a lookup table cuts the size of the lexer down
 */
struct tl {
      char *name;
      int ret_val;
};
static struct tl table[]  = {
{"remote_command"	, F_REMOTE_COMMAND },
{"ls"			, F_LS },
{"cat"			, F_CAT },
{"grep"			, F_GREP },
{"rm"			, F_RM },
{"log_file"		, F_LOG_FILE },
{"else"			, F_ELSE },
{"reallocate"		, F_REALLOCATE },
{"index"		, F_INDEX },
{"searcha"		, F_SEARCHA },
{"in_editor"		, F_IN_EDITOR },
{"contents"		, F_CONTENTS },
{"find_living"		, F_FIND_LIVING },
{"find_player"		, F_FIND_PLAYER },
{"previous_object"	, F_PREVIOUS_OBJECT },
{"inherit"		, F_INHERIT },
{"tell_object"		, F_TELL_OBJECT },
{"query_host_name"	, F_QUERY_HOST_NAME },
{"tail"			, F_TAIL },
{"file_name"		, F_FILE_NAME },
{"query_verb"		, F_QUERY_VERB },
{"this_verb"		, F_QUERY_VERB },
{"snoop"		, F_SNOOP },
{"input_to"		, F_INPUT_TO },
{"rmdir"		, F_RMDIR },
{"mkdir"		, F_MKDIR },
{"crypt"		, F_CRYPT },
{"first_inventory"	, F_FIRST_INVENTORY },
{"regcomp"		, F_REGCOMP },
{"regexec"		, F_REGEXEC },
{"transfer"		, F_TRANSFER },
{"next_inventory"	, F_NEXT_INVENTORY },
{"string"		, F_STRING_DECL },
{"shout"			, F_SHOUT },
{"sscanf"		, F_SSCANF },
{"extract"		, F_EXTRACT },
{"ed"			, F_ED },
{"localcmd"		, F_LOCALCMD },
{"swap"			, F_SWAP },
{"explode"		, F_EXPLODE },
{"break"			, F_BREAK },
{"intp"			, F_INTP },
{"pointerp"		, F_POINTERP },
{"objectp"		, F_OBJECTP },
{"stringp"		, F_STRINGP },
{"users"			, F_USERS },
{"ctime"			, F_CTIME },
{"disable_commands"	, F_DISABLE_COMMANDS },
{"allocate"		, F_ALLOCATE },
{"combine_free_list"	, F_COMBINE_FREE_LIST },
{"continue"		, F_CONTINUE },
{"while"			, F_WHILE },
{"capitalize"		, F_CAPITALIZE },
{"living"		, F_LIVING },
{"interactive"		, F_INTERACTIVE },
{"lower_case"		, F_LOWER_CASE },
{"set_heart_beat"	, F_SET_HEART_BEAT },
{"strlen"		, F_STRLEN },
{"say"			, F_SAY },
{"tell_room"		, F_TELL_ROOM },
{"find_object"		, F_FIND_OBJECT },
{"add_worth"		, F_ADD_WORTH },
{"time"			, F_TIME },
{"create_wizard"		, F_CREATE_WIZARD },
{"destruct"		, F_DESTRUCT },
{"static"		, F_STATIC },
{"environment" 		, F_ENVIRONMENT },
{"save_object"		, F_SAVE_OBJECT },
{"enable_commands"	, F_ENABLE_COMMANDS },
{"sizeof"		, F_SIZEOF },
{"random" 		, F_RANDOM },
{"restore_object"	, F_RESTORE_OBJECT },
{"this_object"		, F_THIS_OBJECT },
{"clone_object"		, F_CLONE_OBJECT },
{"this_player"		, F_THIS_PLAYER },
{"if"			, F_IF },
{"status"		, F_STATUS },
{"object"		, F_STATUS },
{"return"		, F_RETURN },
{"write"			, F_WRITE },
{"add_verb"		, F_ADD_VERB },
{"add_xverb"		, F_ADD_XVERB },
{"add_adj"		, F_ADD_ADJ },
{"add_subst"		, F_ADD_SUBST },
{"add_action"		, F_ADD_ACTION },
{"move_object"		, F_MOVE_OBJECT },
{"present"		, F_PRESENT },
{"call_out"		, F_CALL_OUT },
{"remove_call_out"	, F_REMOVE_CALL_OUT },
{"find_call_out"		, F_FIND_CALL_OUT },
{"creator"		, F_CREATOR },
{"caller"		, F_CALLER },
{"query_ip_number"	, F_QUERY_IP_NUMBER },
{"command"		, F_COMMAND },
{"set_light"		, F_SET_LIGHT },
{"int"			, F_INT },
{"call_other"		, F_CALL_OTHER },
{"shutdown"		, F_SHUTDOWN },
{"typeof"		, F_TYPEOF },
{"cp"			, F_CP },
{"rename"		, F_RENAME },
{"read_file"		, F_READ_FILE },
{"query_snoop"		, F_QUERY_SNOOP },
{"write_file"   	, F_WRITE_FILE },
{"file_size"    	, F_FILE_SIZE },
{"parse_command"     	, F_PARSE_COMMAND },
{"implode"   		, F_IMPLODE },
{"query_idle"   	, F_QUERY_IDLE },
{"do"			, F_DO },
{"for"	 		, F_FOR },
{"filter_objects" 	, F_FILTER_OBJECTS },
{"query_load_average" 	, F_QUERY_LOAD_AVERAGE },
{"catch"		, F_CATCH },
{"throw"		, F_THROW },
{"set_bit"	    	, F_SET_BIT },
{"clear_bit"   		, F_CLEAR_BIT },
{"test_bit" 		, F_TEST_BIT },
{"notify_fail" 		, F_NOTIFY_FAIL },
{"set_living_name"      , F_SET_LIVING_NAME },
{ 0, 0}
 };

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]|\\\")*\")
%%
";"			{ 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.lnode = (struct lnode *)alloc_lnode_number(F_NUMBER,
							    yytext[1]);
          return F_NUMBER; }
{ident}		{ return lookup_table(yytext); }
{string}	{ return string(yytext); }
[\t ]		{ break; }
"\n"	{ current_line++;
 	  if (current_line & L_MASK) {
 	      yyerror("Too many lines.\n");
 	      current_line = 0;
 	  }
	total_lines++;
 	}
.	{ char buff[100]; sprintf(buff, "Illegal character '%c'", yytext[0]);
	  yyerror(buff); }
%%

extern YYSTYPE yylval;

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

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

    for (i=0; table[i].name; i++) {
        if (*str != table[i].name[0]) continue;
        if (strcmp(str, table[i].name) == 0) return(table[i].ret_val);      
    }
    for (i=current_number_of_locals-1; i>=0; i--) {
	if (strcmp(local_names[i], str) == 0) {
	    yylval.lnode = (struct lnode *)alloc_lnode_number(F_LOCAL_NAME, i);
	    return F_LOCAL_NAME;
	}
    }
    if (argument_name && strcmp(str, argument_name) == 0)
	return F_ARGUMENT;
    yylval.string = string_copy(str);
    return F_IDENTIFIER;
}

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

static char cbuf[100];

char * cmdname(c)
int c;
{
    int i;

    switch (c) {
	case F_LOCAL_NAME: return "Local var def";
	case F_ARGUMENT:   return "First Argument";
	}

    for (i=0; table[i].name; i++) {
        if (c == table[i].ret_val) return table[i].name;
    }
    sprintf(cbuf, "Identifier? (%d)", c);
    return cbuf;
}
    
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') {
	    *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.lnode = (struct lnode *)alloc_lnode_single(F_CONST0);
	return F_CONST0;
    }
    if (i == 1) {
	yylval.lnode = (struct lnode *)alloc_lnode_single(F_CONST1);
	return F_CONST1;
    }
    yylval.lnode =(struct lnode *)alloc_lnode_number(F_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;
}

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