tfe-1.0/area/
tfe-1.0/files/
tfe-1.0/logs/
tfe-1.0/logs/immortal/
tfe-1.0/logs/mob/
tfe-1.0/logs/object/
tfe-1.0/logs/player/
tfe-1.0/logs/room/
tfe-1.0/notes/clans/
tfe-1.0/player/
tfe-1.0/prev/
tfe-1.0/prev/area/
tfe-1.0/prev/player/
tfe-1.0/prev/rooms/
tfe-1.0/rooms/
tfe-1.0/src-gc/
tfe-1.0/src-msvc/
tfe-1.0/src-unix/
tfe-1.0/www/
tfe-1.0/www/html/
#include "sys/types.h"
#include "stdio.h"
#include "stdlib.h"
#include "define.h"
#include "struct.h"


arg_type*     curr_arg;
mem_block*  block_list  = NULL;

char         error_buf  [ MAX_INPUT_LENGTH ];
char*             code;
extra_array*      data;


/*
 *   COMPILER ROUTINES
 */


void compile( program_data* program )
{
  arg_type*         arg;
  arg_type*    arg_list  = NULL;
  arg_type*    arg_last  = NULL;
  mem_block*       list;
  mem_block*   tmp_list;
  int              line;
  char*          letter;

  clear_queue( program );
  delete_list( program->memory );  

  if( program->binary != NULL ) {
    delete program->binary;
    program->binary = NULL;
    }
 
  program->memory = NULL;

  tmp_list   = block_list;
  code       = program->code;
  data       = &program->data;
  block_list = NULL;

  *error_buf = '\0';

  for( ; *error_buf == '\0'; ) {
    if( ( arg = read_op( read_arg( ) ) ) == NULL )
      break; 

    line += 2;
   
    if( *code == ';' )
      code++;

    if( arg_list == NULL )
      arg_list = arg;
    else
      arg_last->next = arg;

    arg_last = arg;
    }

  list       = block_list;
  block_list = tmp_list;

  if( *error_buf != '\0' ) { 
    for( line = 1, letter = program->code; letter != code; letter++ )
      if( *letter == '\n' )
        line += 2;
    program->corrupt = TRUE;
    page_header( var_ch, "*** %s\n\r\n\r", error_buf );
    page_header( var_ch, "*** Error on line %d\n\r", line );
    page_header( var_ch, "*** FAILS TO COMPILE\n\r" );
    delete_list( list );
    if( arg_list != NULL )
      delete arg_list;
    return;
    }

  program->binary  = arg_list;
  program->memory  = list;
  program->active  = 0;
  program->corrupt = FALSE;
}


/*
 *   READ_ARG
 */


arg_type* read_arg( )
{
  arg_type*    arg;
  arg_type*   arg1;
  arg_type*   arg2;
  bool         neg;

  if( *error_buf != '\0' )
    return NULL;

  skip_spaces( code );

  if( *code == '\0' ) 
    return NULL;

  if( *code == '{' ) {
    code++;
    if( ( arg1 = read_op( read_arg( ) ) ) == NULL )
      return NULL;
    for( arg2 = arg1; ; arg2 = arg2->next ) {
      skip_spaces( code );
      if( *code == '}' ) {
        code++;
        return arg1;
        }
      if( ( arg2->next = read_op( read_arg( ) ) ) == NULL ) {
        if( *error_buf == '\0' )
          strcpy( error_buf, "End of statement block without }." );
        delete arg1;
        return NULL;
        }
      }
    }       

  if( ( arg = read_const( code ) ) != NULL )
    return arg;
        
  if( exact_match( code, "end" ) ) {
    arg         = new arg_type;
    arg->family = end;
    return arg;
    }

  if( exact_match( code, "continue" ) ) {
    arg         = new arg_type;
    arg->family = cont;
    return arg;
    }

  if( exact_match( code, "loop(" ) )
    return read_loop( code );

  if( exact_match( code, "if(" ) ) 
    return read_if( code );

  if( neg = ( *code == '!' ) ) 
    code++;

  if( ( arg = read_function( code, neg ) ) != NULL 
    || *error_buf != '\0' )
    return arg;

  if( ( arg = read_variable( code, neg ) ) != NULL  
    || *error_buf != '\0' )
    return arg;

  if( neg )
    code--;

  if( isdigit( *code ) || ( *code == '-' && isdigit( *(code+1) ) ) ) 
    return read_digit( code );

  if( ( arg = read_string( code, *data ) ) != NULL 
    || *error_buf != '\0' )
    return arg;
  
  if( *error_buf == '\0' ) 
    strcpy( error_buf, "Error of unknown type." );

  return NULL;
}


/*
 *   OPERATORS
 */


const class cfunc_type op_list [ ] =
{
  { "=",     &code_set_equal,    NONE,    { INTEGER, INTEGER, NONE,  NONE }  },
  { "+=",    &code_plus_equal,   NONE,    { INTEGER, INTEGER, NONE,  NONE }  },
  { "-=",    &code_minus_equal,  NONE,    { INTEGER, INTEGER, NONE,  NONE }  },
  { "&&",    &code_and,          INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "||",    &code_or,           INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "==",    &code_is_equal,     INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "!=",    &code_not_equal,    INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { ">",     &code_gt,           INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "<",     &code_lt,           INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { ">=",    &code_ge,           INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "<=",    &code_le,           INTEGER, { INTEGER, INTEGER, NONE,  NONE }  },
  { "",      NULL,               NONE,    { NONE,  NONE,  NONE,  NONE }  }
};


bool can_assign( arg_enum t1, arg_enum t2 )
{
  if( t1 == INTEGER )
    return TRUE;

  if( t1 == THING )
    return( t2 == CHARACTER || t2 == OBJECT || t2 == ROOM );

  return( t1 == t2 );  
}


arg_type* read_op( arg_type* arg )
{
  afunc_type*  afunc;
  int              i;

  if( arg == NULL || arg->family == if_clause 
    || arg->family == loop )
    return arg; 

  skip_spaces( code );

  if( *code == ')' || *code == ',' )
    return arg;

  if( *code == ';' ) {
    code++;
    return arg;
    }

  afunc         = new afunc_type;     
  afunc->type   = INTEGER;
  afunc->arg[0] = arg;

  for( i = 0; ; i++ ) {
    if( *op_list[i].name == '\0' ) {
      strcpy( error_buf, "Unknown operator or missing semi-colon." );
      delete afunc;
      return NULL;
      }
    if( exact_match( code, op_list[i].name ) ) 
      break;
    }

  afunc->func = &op_list[i];

  skip_spaces( code );

  if( ( afunc->arg[1] = read_arg( ) ) == NULL ) {
    if( *error_buf == '\0' )
      strcpy( error_buf, "Operator missing second argument." );
    delete afunc;
    return NULL;
    }

  if( i >= 3 && i <= 4 ) 
    return read_op( afunc );

  if( i >= 5 && i <= 6 ) {
    if( afunc->arg[1]->type != arg->type ) {
      sprintf( error_buf,
        "Both sides of operator '%s' must be of identical type.",
        op_list[i].name );
      delete afunc;
      return NULL;
      }
    return afunc;
    }

  if( i != 0 ) {
    if( afunc->arg[1]->type != INTEGER || arg->type != INTEGER ) {
      sprintf( error_buf,
        "Operator '%s' requires integer arguments.", op_list[i].name );
      delete afunc;
      return NULL; 
      }
    }

  if( i < 3 ) {
    if( arg->family != variable ) {
      strcpy( error_buf, "Assigning a value to a non-variable?" );
      delete afunc;
      return NULL;
      }
    if( !can_assign( arg->type, afunc->arg[1]->type ) ) {
      sprintf( error_buf, "Assignment to %s from %s.",
        arg_type_name[ arg->type ], arg_type_name[ afunc->arg[1]->type ] );
      delete afunc;
      return NULL;
      }
    if( *code != ';' ) {
      strcpy( error_buf, "Assignment missing trailing semi-colon." );
      delete afunc;
      return NULL;
      }
    code++;
    }

  return afunc;
}  


/*
 *   CONSTANTS
 */


class Const_Data
{
 public:
  const char**   entry1;
  const char**   entry2;
  int*             size;
  arg_enum         type;

  const char* entry( int j ) const {
    return *(entry1+j*(entry2-entry1));
    };
};


int max_clss   = MAX_CLSS;
int max_dir    = 6;
int max_relig  = MAX_RELIGION;
int max_rflag  = MAX_RFLAG;
int max_skill  = MAX_SKILL;
int max_stat   = 9;


const char* stat_name[] = { "str", "int", "wis", "dex", "con",
  "level", "piety", "class", "align" };


#define max_nation  MAX_ENTRY_NATION
#define max_race    MAX_ENTRY_RACE


const class const_data const_list [] = 
{
  {  &nation_table[0].name, &nation_table[1].name, &max_nation, NATION     },
  {  &clss_table[0].name,   &clss_table[1].name,   &max_clss,   CLASS      },
  {  &dir_table[0].name,    &dir_table[1].name,    &max_dir,    DIRECTION  },
  {  &rflag_name[0],        &rflag_name[1],        &max_rflag,  RFLAG      },
  {  &skill_table[0].name,  &skill_table[1].name,  &max_skill,  SKILL      },
  {  &stat_name[0],         &stat_name[1],         &max_stat,   STAT       },
  {  &race_table[0].name,   &race_table[1].name,   &max_race,   RACE       },
  {  NULL,                  NULL,                  NULL,        NONE       },
};

#undef max_nation
#undef max_race


arg_type* read_const( const char*& code )
{
  arg_type*   arg;
  int        i, j;

  for( i = 0; const_list[i].entry1 != NULL; i++ ) {
    for( j = 0; j < *const_list[i].size; j++ ) {
      if( exact_match( code, const_list[i].entry( j ) ) ) {
        arg         = new arg_type;
        arg->type   = const_list[i].type;
        arg->family = constant;
        arg->value  = (void*) j;
        return arg;
        }
      }
    }

  return NULL;
}


/*
 *   VARIABLES
 */


class Var_Data
{
 public:
  char*        name;
  void*        pointer;
  arg_enum     type;  
};


const class var_data variable_list [] =
{
  { "mob",           &var_mob,            CHARACTER   }, 
  { "rch",           &var_rch,            CHARACTER   }, 
  { "victim",        &var_victim,         CHARACTER   }, 
  { "arg",           &var_arg,            STRING     },
  { "room",          &var_room,           ROOM },
  { "obj",           &var_obj,            OBJECT     },
  { "container",     &var_container,      OBJECT     },
  { "ch",            &var_ch,             CHARACTER   },
  { "i",             &var_i,              INTEGER   },
  { "j",             &var_j,              INTEGER   },
  { "",              NULL,                NONE    }
};


arg_type* read_variable( const char*& code, bool neg )
{
  arg_type*  arg;
  int          i;

  for( i = 0; ; i++ ) {
    if( *variable_list[i].name == '\0' ) 
      return NULL;
    if( exact_match( code, variable_list[i].name ) )
      break;
    }

  arg         = new arg_type;
  arg->type   = ( neg ? INTEGER : variable_list[i].type );
  arg->family = variable;
  arg->value  = (void*) variable_list[i].pointer;
  arg->neg    = neg;

  return arg;
}


/*
 *   LOOPS
 */


const char* loop_name[] = { "all_in_room", "followers" };


loop_type* read_loop( const char*& code )
{
  loop_type* aloop  = new loop_type;
  int            i;

  for( i = 0; i < 2; i++ ) {
    if( exact_match( code, loop_name[i] ) ) {
      if( *code != ')' ) {
        strcpy( error_buf, "Missing ')' after loop." ); 
        delete aloop;
        return NULL;
        }
      code++; 
      aloop->fruit = (loop_enum) i;
      if( ( aloop->aloop = read_arg( ) ) != NULL )
        return aloop;
      if( *error_buf == '\0' ) 
        strcpy( error_buf, "Error in loop." );
      delete aloop;
      return NULL;
      }
    }

  aloop->fruit = loop_unknown;
  aloop->neg   = ( *code == '!' );
  code        += aloop->neg;

  if( ( aloop->condition = read_op( read_arg( ) ) ) == NULL ) {
    if( *error_buf == '\0' ) 
      strcpy( error_buf, "Loop statement with null condition??" );
    delete aloop;
    return NULL;  
    }   

  skip_spaces( code );

  if( *code != ')' ) {
    strcpy( error_buf, "Loop statement missing closing )." );
    delete aloop;
    return NULL;
    }

  code++;

  if( ( aloop->aloop = read_arg( ) ) == NULL ) {
    if( *error_buf == '\0' ) 
      strcpy( error_buf, "Loop statement with null loop." );
    delete aloop;
    return NULL;  
    }   

  return aloop;
}  


/*
 *   IF STATEMENTS
 */


aif_type* read_if( const char*& code )
{
  aif_type*       aif  = new aif_type;
  const char*  letter;

  if( ( aif->condition = read_op( read_arg( ) ) ) == NULL ) {
    if( *error_buf == '\0' ) 
      strcpy( error_buf, "If statement with null condition??" );
    delete aif;
    return NULL;  
    }   

  skip_spaces( code );

  if( *code != ')' ) {
    strcpy( error_buf, "If statement missing closing )." );
    delete aif;
    return NULL;
    }

  code++;

  if( ( aif->yes = read_arg( ) ) == NULL 
    || ( aif->yes->next == NULL
    && ( aif->yes = read_op( aif->yes ) ) == NULL ) ) {
    if( *error_buf == '\0' )
      strcpy( error_buf, "If statement with no effect." );
    delete aif;
    return NULL;
    } 

  letter = code;
  for( ; isspace( *letter ) || *letter == ';'; letter++ );
  
  if( !strncasecmp( "else", letter, 4 ) ) {
    code = letter+4;
    if( ( aif->no = read_arg( ) ) == NULL 
      || ( aif->no->next == NULL
      && ( aif->no = read_op( aif->no ) ) == NULL ) ) {
      if( *error_buf == '\0' )
        strcpy( error_buf, "Else statement with no effect." );
      delete aif;
      return NULL;
      }
    }
 
  return aif;
}


/*
 *   FUNCTIONS
 */


arg_type* read_function( const char*& code, bool neg )
{
  afunc_type*  afunc;
  int           i, j;
  int         length;

  for( i = 0; ; i++ ) {
    if( cfunc_list[i].name[0] == '\0' ) 
      return NULL;
    length = strlen( cfunc_list[i].name );
    if( !strncasecmp( cfunc_list[i].name, code, length ) 
      && code[length] == '(' ) 
      break;
    }

  code       += length+1; 
  afunc       = new afunc_type;     
  afunc->type = ( neg ? INTEGER : cfunc_list[i].type );
  afunc->neg  = neg;
  afunc->func = &cfunc_list[i];

  for( j = 0; ; j++ ) {
    skip_spaces( code );
    if( *code == ')' ) 
      break;
    if( j == 4 ) {
      sprintf( error_buf,
        "Too many arguments for function %s.", cfunc_list[i].name );
      delete afunc;
      return NULL;
      }
    if( ( afunc->arg[j] = read_op( read_arg( ) ) ) == NULL ) {
      if( *error_buf == '\0' )
        strcpy( error_buf, "Function missing closing ')'." );
      delete afunc;
      return NULL;
      }
    if( !can_assign( cfunc_list[i].arg[j], afunc->arg[j]->type ) ) {
      sprintf( error_buf,
        "Passing %s to function %s for argument %d, requires %s.",
        arg_type_name[ afunc->arg[j]->type ],
        cfunc_list[i].name, j+1,
        arg_type_name[ cfunc_list[i].arg[j] ] );
      delete afunc;
      return NULL;
      }
    if( *code == ',' ) 
      code++;
    }

  code++;

  return afunc;
}


/*
 *   STRINGS
 */


arg_type* read_string( const char*& code, extra_array& data )
{
  arg_type*       arg;
  const char*  string;
  char         letter;

  if( *code != '"' && *code != '#' )
    return NULL;

  arg         = new arg_type;
  arg->type   = STRING;
  arg->family = constant;

  string = code+1;

  if( *code == '"' ) {
    for( code++; *code != '"'; code++ ) {
      if( *code == '\0' ) {
        strcpy( error_buf, "Unexpected end of string." );
        delete arg;
        return NULL;
        }
      }
    *((char*)code) = '\0';
    arg->value     = (void*) code_alloc( string );
    *((char*)code) = '"';
    code++;
    return arg;
    }

  for( code++; *code != ',' && !isspace( *code ); code++ ) 
    if( *code == '\0' ) {
      strcpy( error_buf, "Unexpected end of string." );
      delete arg;
      return NULL;
      }

  letter         = *code;
  *((char*)code) = '\0';
  arg->value     = (void*) get_string( string, data );

  if( arg->value == NULL ) {
    sprintf( error_buf, "String not found. (%s)", string ); 
    delete arg;
    arg = NULL;
    } 

  *((char*)code) = letter;

  return arg;
}


/*
 *   NUMBERS
 */


arg_type* itoarg( int i )
{
  arg_type*   arg  = new arg_type;

  arg->type   = INTEGER;
  arg->family = constant;
  arg->value  = (void*) i;

  return arg;
}


arg_type* read_digit( const char*& code )
{
  afunc_type*  afunc;
  int           dice;
  int           plus;
  int           side;
  int              i;
  bool      negative  = FALSE;

  if( *code == '-' ) {
    negative = TRUE;
    code++;
    }

  for( dice = 0; isdigit( *code ); code++ )
    dice = *code+10*dice-'0';

  if( negative )
    dice = -dice;

  if( *code != 'd' )
    return itoarg( dice );

  for( plus = side = 0, code++; isdigit( *code ); code++ )
     side = *code+10*side-'0';

  if( *code == '+' ) 
    for( code++; isdigit( *code ); code++ )
      plus = *code+10*plus-'0';

  afunc         = new afunc_type;
  afunc->type   = INTEGER;
  afunc->family = function;

  afunc->arg[0] = itoarg( dice );
  afunc->arg[1] = itoarg( side );
  afunc->arg[2] = itoarg( plus );

  for( i = 0; ; i++ ) 
    if( !strcasecmp( cfunc_list[i].name, "dice" ) ) {
      afunc->func = &cfunc_list[i];
      break;
      }

  return afunc;
}