#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;
}