%{
#include "os.h"
#include "lnode.h"
#include "config.h"
#define YYMAXDEPTH 10000
char *local_names[MAX_LOCAL];
int current_number_of_locals = 0;
struct lnode *prog, *heart_beat;
int variable_count;
char *argument_name; /* The name of the current argument. */
/* lexical.l */
extern int current_line;
extern char *current_file;
extern int yylex(void);
/* simulate.c */
extern void fatal(char *fmt, ...);
extern void error(char *fmt, ...);
extern void smart_log(char *error_file, int line, char *what);
extern int num_parse_error;
/* lang.y */
void yyerror(char *str);
static int check_deklared(char *str);
static void free_all_local_names(void);
static void add_local_name(char *str);
static int num_val(struct lnode_number *p);
%}
%token F_THIS_PLAYER F_IF F_IDENTIFIER F_AND F_OR F_STATUS F_SET_TRUE
%token F_SET_FALSE F_CONS F_RETURN F_NOT F_WRITE F_STRING F_ADD_VERB
%token F_ADD_ADJ F_ADD_SUBST F_ADD_ACTION F_MOVE_OBJECT F_PRESENT
%token F_NUMBER F_ASSIGN F_INT F_CALL_OTHER F_ADD F_SUBTRACT F_MULTIPLY
%token F_DIVIDE F_MOD F_LT F_GT F_EQ F_GE F_LE F_ARGUMENT F_FUNCTION
%token F_CLONE_OBJECT F_THIS_OBJECT F_SAVE_OBJECT F_RESTORE_OBJECT F_NE
%token F_ENVIRONMENT F_ADD_EQ F_SUB_EQ F_DIV_EQ F_MULT_EQ F_MOD_EQ
%token F_COMMAND F_SET_LIGHT F_DESTRUCT F_CREATE_WIZARD F_NEGATE F_SAY
%token F_STRLEN F_SUBSCRIPT F_WHILE F_BREAK F_SHUTDOWN F_LOG_FILE
%token F_SSCANF F_SHOUT F_STRING_DECL F_LOCAL_NAME F_FIRST_INVENTORY
%token F_NEXT_INVENTORY F_ENABLE_COMMANDS F_RANDOM F_INPUT_TO F_CRYPT
%token F_LS F_CAT F_FIND_LIVING F_FIND_PLAYER F_TELL_OBJECT F_PEOPLE
%token F_ED F_LIVING F_LOWER_CASE F_ELSE F_CAPITALIZE
%token F_SET_HEART_BEAT F_SNOOP F_TELL_ROOM F_FIND_OBJECT F_WIZLIST
%token F_RM F_CONST0 F_CONST1 F_BLOCK F_TRANSFER F_REGCOMP F_REGEXEC
%token F_LOCALCMD F_SWAP F_CONTINUE F_ADD_WORTH F_TIME
%union
{
struct lnode *lnode;
int number;
char *string;
}
%type <lnode> cond expr1 expr_list expr2 expr3 function_call variable
%type <lnode> statements statement program def return expr4
%type <lnode> string expr0 F_NUMBER expr25 expr27 expr23 expr22 while
%type <lnode> F_LOCAL_NAME block number F_CONST0 F_CONST1
%type <number> name F_SET_FALSE F_SET_TRUE F_THIS_PLAYER F_WRITE F_THIS_OBJECT
%type <number> assign
%type <string> F_IDENTIFIER F_STRING F_ARGUMENT
%%
all: program { prog = $1; };
program: def possible_semi_colon program
{ if ($1 != 0) {
struct lnode_def *p = (struct lnode_def *)$1;
p->next = (struct lnode_def *)$3;
$$ = (struct lnode *)p;
} else
$$ = $3;
}
| /* empty */ { $$ = 0; };
possible_semi_colon: /* empty */
| ';' { yyerror("Extra ';'. Ignored."); };
pre_pro_info: '#' number F_STRING
{ free(current_file); current_file = $3;
current_line = num_val((struct lnode_number *)$2) - 1;
free_lnode($2, 1);
}
possible_extra_number;
number: F_NUMBER | F_CONST0 | F_CONST1;
possible_extra_number: /* empty */ | number { free_lnode($1, 1); };
def: F_IDENTIFIER '(' argument ')' block
{
$$ = (struct lnode *)alloc_lnode_def(F_IDENTIFIER,$1,$5,
current_number_of_locals);
/* alloc_lnode_def frees $1 if name found in string_space */
if (strcmp(((struct lnode_def *)$$)->name, "heart_beat") == 0)
heart_beat = $$;
if (argument_name) free(argument_name);
argument_name = 0; free_all_local_names(); }
| name '(' argument ')' block
{ yyerror("Predefined function name.\n"); $$ = 0; }
| type name_list ';' { $$ = 0; }
| pre_pro_info { $$ = 0; };
req_semi: ';'
| /* empty */ { yyerror("Missing ';'"); };
argument: /* empty */
| F_IDENTIFIER { argument_name = $1; };
type: F_STATUS | F_INT | F_STRING_DECL;
name_list: new_name
| new_name ',' name_list;
new_name: F_IDENTIFIER
{ alloc_lnode_var_def(F_STATUS, $1, variable_count++); };
block: '{' local_declarations statements '}'
{ $$ = (struct lnode *)alloc_lnode_block($3); };
local_declarations: /* empty */
| local_declarations type local_list req_semi ;
local_list: F_IDENTIFIER { add_local_name($1); }
| local_list ',' F_IDENTIFIER { add_local_name($3); }
| F_ARGUMENT { yyerror("Illegal to redeclare argument"); }
| F_LOCAL_NAME { yyerror("Illegal to redeclare local name"); };
statements: /* empty */ { $$ = 0; }
| statement statements
{ if ($1 == 0)
$$ = $2;
else
$$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, $2);
}
| error ';' { yyerror("Illegal statement"); $$ = 0; }
| pre_pro_info { $$ = 0; };
statement: expr0 ';' { $$ = $1; } | cond | while | return ';' { $$ = $1; }
| block
| F_BREAK req_semi { $$ = (struct lnode *)alloc_lnode_single(F_BREAK);
}
| F_CONTINUE req_semi { $$ = (struct lnode *)alloc_lnode_single(F_CONTINUE);
};
while: F_WHILE '(' expr0 ')' statement
{ $$ = (struct lnode *)alloc_lnode_2(F_WHILE, $3, $5); };
expr0: expr1
| variable assign expr1
{ $$ = (struct lnode *)alloc_lnode_2($2, $1, $3); }
| error assign expr1 { yyerror("Illegal LHS"); $$ = 0; }
assign: '=' { $$ = F_ASSIGN; }
| F_ADD_EQ { $$ = F_ADD_EQ; }
| F_SUB_EQ { $$ = F_SUB_EQ; }
| F_MULT_EQ { $$ = F_MULT_EQ; }
| F_MOD_EQ { $$ = F_MOD_EQ; }
| F_DIV_EQ { $$ = F_DIV_EQ; };
return: F_RETURN { $$ = (struct lnode *)alloc_lnode_1(F_RETURN, 0); }
| F_RETURN expr0 { $$ = (struct lnode *)alloc_lnode_1(F_RETURN, $2); };
expr_list: /* empty */ { $$ = 0; }
| expr1
{ $$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, 0); }
| expr1 ',' expr_list
{ $$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, $3); };
expr1: expr2
| expr2 F_OR expr1
{ $$ = (struct lnode *)alloc_lnode_2(F_OR, $1, $3); };
expr2: expr22
| expr22 F_AND expr2
{ $$ = (struct lnode *)alloc_lnode_2(F_AND, $1, $3); };
expr22: expr23
| expr25 F_EQ expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_EQ, $1, $3); }
| expr25 F_NE expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_NE, $1, $3); };
expr23: expr25
| expr25 '>' expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_GT, $1, $3); }
| expr25 F_GE expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_GE, $1, $3); }
| expr25 '<' expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_LT, $1, $3); }
| expr25 F_LE expr25
{ $$ = (struct lnode *)alloc_lnode_2(F_LE, $1, $3); };
expr25: expr27
| expr25 '+' expr27
{ $$ = (struct lnode *)alloc_lnode_2(F_ADD, $1, $3); }
| expr25 '-' expr27
{ $$ = (struct lnode *)alloc_lnode_2(F_SUBTRACT, $1, $3); };
expr27: expr3
| expr27 '*' expr3
{ $$ = (struct lnode *)alloc_lnode_2(F_MULTIPLY, $1, $3); }
| expr27 '%' expr3
{ $$ = (struct lnode *)alloc_lnode_2(F_MOD, $1, $3); }
| expr27 '/' expr3
{ $$ = (struct lnode *)alloc_lnode_2(F_DIVIDE, $1, $3); };
expr3: expr4
| F_NOT expr3
{ $$ = (struct lnode *)alloc_lnode_1(F_NOT, $2); }
| '-' expr3
{ $$ = (struct lnode *)alloc_lnode_1(F_NEGATE, $2); };
expr4: function_call | variable | string | number
| '(' expr0 ')' { $$ = $2; }
| expr4 '[' expr0 ']'
{ $$ = (struct lnode *)alloc_lnode_2(F_SUBSCRIPT, $1, $3); };
string: F_STRING
{ $$ = (struct lnode *)alloc_lnode_name(F_STRING, $1); };
function_call: name '(' expr_list ')'
{ $$ = (struct lnode *)alloc_lnode_1($1, $3); }
| F_IDENTIFIER '(' ')'
{ $$ = (struct lnode *)alloc_lnode_funcall(F_FUNCTION, $1, 0); }
| F_IDENTIFIER '(' expr0 ')'
{ $$ = (struct lnode *)alloc_lnode_funcall(F_FUNCTION, $1, $3); };
variable: F_IDENTIFIER
{ $$ = (struct lnode *)alloc_lnode_number(F_IDENTIFIER,
check_deklared($1));
free($1); }
| F_ARGUMENT { $$ = (struct lnode *)alloc_lnode_single(F_ARGUMENT); }
| F_LOCAL_NAME ;
name: F_THIS_PLAYER { $$ = F_THIS_PLAYER; }
| F_THIS_OBJECT { $$ = F_THIS_OBJECT; }
| F_SET_FALSE { $$ = F_SET_FALSE; }
| F_WRITE { $$ = F_WRITE; }
| F_ADD_VERB { $$ = F_ADD_VERB; }
| F_ADD_SUBST { $$ = F_ADD_SUBST; }
| F_ED { $$ = F_ED; }
| F_ADD_ADJ { $$ = F_ADD_ADJ; }
| F_SHUTDOWN { $$ = F_SHUTDOWN; }
| F_CALL_OTHER { $$ = F_CALL_OTHER; }
| F_CLONE_OBJECT { $$ = F_CLONE_OBJECT; }
| F_ADD_ACTION { $$ = F_ADD_ACTION; }
| F_MOVE_OBJECT { $$ = F_MOVE_OBJECT; }
| F_LOG_FILE { $$ = F_LOG_FILE; }
| F_SET_TRUE { $$ = F_SET_TRUE; }
| F_SAVE_OBJECT { $$ = F_SAVE_OBJECT; }
| F_ENVIRONMENT { $$ = F_ENVIRONMENT; }
| F_PRESENT { $$ = F_PRESENT; }
| F_SET_HEART_BEAT { $$ = F_SET_HEART_BEAT; }
| F_COMMAND { $$ = F_COMMAND; }
| F_CRYPT { $$ = F_CRYPT; }
| F_CAT { $$ = F_CAT; }
| F_LS { $$ = F_LS; }
| F_RM { $$ = F_RM; }
| F_FIND_LIVING { $$ = F_FIND_LIVING; }
| F_FIND_PLAYER { $$ = F_FIND_PLAYER; }
| F_LOWER_CASE { $$ = F_LOWER_CASE; }
| F_TELL_OBJECT { $$ = F_TELL_OBJECT; }
| F_FIND_OBJECT { $$ = F_FIND_OBJECT; }
| F_WIZLIST { $$ = F_WIZLIST; }
| F_TRANSFER { $$ = F_TRANSFER; }
| F_REGCOMP { $$ = F_REGCOMP; }
| F_REGEXEC { $$ = F_REGEXEC; }
| F_LOCALCMD { $$ = F_LOCALCMD; }
| F_SWAP { $$ = F_SWAP; }
| F_ADD_WORTH { $$ = F_ADD_WORTH; }
| F_TIME { $$ = F_TIME; }
| F_PEOPLE { $$ = F_PEOPLE; }
| F_INPUT_TO { $$ = F_INPUT_TO; }
| F_ENABLE_COMMANDS { $$ = F_ENABLE_COMMANDS; }
| F_CAPITALIZE { $$ = F_CAPITALIZE; }
| F_LIVING { $$ = F_LIVING; }
| F_RANDOM { $$ = F_RANDOM; }
| F_SNOOP { $$ = F_SNOOP; }
| F_FIRST_INVENTORY { $$ = F_FIRST_INVENTORY; }
| F_NEXT_INVENTORY { $$ = F_NEXT_INVENTORY; }
| F_SET_LIGHT { $$ = F_SET_LIGHT; }
| F_DESTRUCT { $$ = F_DESTRUCT; }
| F_CREATE_WIZARD { $$ = F_CREATE_WIZARD; }
| F_SAY { $$ = F_SAY; }
| F_TELL_ROOM { $$ = F_TELL_ROOM; }
| F_SHOUT { $$ = F_SHOUT; }
| F_RESTORE_OBJECT { $$ = F_RESTORE_OBJECT; }
| F_SSCANF { $$ = F_SSCANF; }
| F_STRLEN { $$ = F_STRLEN; };
cond: F_IF '(' expr1 ')' statement { $$ = (struct lnode *)alloc_lnode_3(F_IF, $3, $5, 0); }
| F_IF '(' expr1 ')' statement F_ELSE statement
{ $$ = (struct lnode *)alloc_lnode_3(F_IF, $3, $5, $7); };
%%
void yyerror(char *str)
{
if (num_parse_error > 5)
return;
(void)fprintf(stderr, "%s: %s line %d\n", current_file, str,
current_line);
smart_log(current_file, current_line, str);
num_parse_error++;
}
static int check_deklared(char *str)
{
struct lnode_var_def *p;
char buff[100];
for (p = prog_status; p; p = p->next) {
if (strcmp(p->name, str) == 0)
return p->num_var;
}
(void)sprintf(buff, "Variable %s not declared !", str);
yyerror(buff);
return 0;
}
static void free_all_local_names()
{
int i;
for (i=0; i<current_number_of_locals; i++) {
free(local_names[i]);
local_names[i] = 0;
}
current_number_of_locals = 0;
}
static void add_local_name(char *str)
{
if (current_number_of_locals == MAX_LOCAL)
error("Too many local variables (max %d)\n", MAX_LOCAL);
local_names[current_number_of_locals++] = str;
}
static int num_val(struct lnode_number *p)
{
switch(p->type) {
case F_NUMBER:
return p->number;
case F_CONST0:
return 0;
case F_CONST1:
return 1;
default:
fatal("Illegal type of number.\n");
}
return 0;
}