/*
* Compile.c (This is really a tokenizer, not a compiler)
*/
#include "copyright.h"
#include "config.h"
#include "db.h"
#include "props.h"
#include "interface.h"
#include "inst.h"
#include "externs.h"
#include "params.h"
#include "tune.h"
#include "match.h"
#include "interp.h"
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
/* This file contains code for doing "byte-compilation" of
mud-forth programs. As such, it contains many internal
data structures and other such which are not found in other
parts of TinyMUCK. */
/* The CONTROL_STACK is a stack for holding previous control statements.
This is used to resolve forward references for IF/THEN and loops, as well
as a placeholder for back references for loops. */
#define CTYPE_IF 1
#define CTYPE_ELSE 2
#define CTYPE_BEGIN 3
#define CTYPE_FOR 4 /* Get it? CTYPE_FOUR!! HAHAHAHAHA -Fre'ta */
/* C-4? *BOOM!* -Revar */
#define CTYPE_WHILE 5
#define CTYPE_TRY 6 /* reserved for exception handling */
#define CTYPE_CATCH 7 /* reserved for exception handling */
/* These would be constants, but their value isn't known until runtime. */
static int IN_FORITER;
static int IN_FOREACH;
static int IN_FORPOP;
static int IN_FOR;
static int IN_TRYPOP;
static hash_tab primitive_list[COMP_HASH_SIZE];
struct CONTROL_STACK {
short type;
struct INTERMEDIATE *place;
struct CONTROL_STACK *next;
struct CONTROL_STACK *extra;
};
/* This structure is an association list that contains both a procedure
name and the place in the code that it belongs. A lookup to the procedure
will see both it's name and it's number and so we can generate a
reference to it. Since I want to disallow co-recursion, I will not allow
forward referencing.
*/
struct PROC_LIST {
const char *name;
int returntype;
struct INTERMEDIATE *code;
struct PROC_LIST *next;
};
/* The intermediate code is code generated as a linked list
when there is no hint or notion of how much code there
will be, and to help resolve all references.
There is always a pointer to the current word that is
being compiled kept.
*/
#define INTMEDFLG_DIVBYZERO 1
#define INTMEDFLG_MODBYZERO 2
#define INTMEDFLG_INTRY 4
struct INTERMEDIATE {
int no; /* which number instruction this is */
struct inst in; /* instruction itself */
short line; /* line number of instruction */
short flags;
struct INTERMEDIATE *next; /* next instruction */
};
/* The state structure for a compile. */
typedef struct COMPILE_STATE_T {
struct CONTROL_STACK *control_stack;
struct PROC_LIST *procs;
int nowords; /* number of words compiled */
struct INTERMEDIATE *curr_word; /* word being compiled */
struct INTERMEDIATE *first_word; /* first word of the list */
struct INTERMEDIATE *curr_proc; /* first word of curr. proc. */
struct publics *currpubs;
int nested_fors;
int nested_trys;
/* Address resolution data. Used to relink addresses after compile. */
struct INTERMEDIATE **addrlist; /* list of addresses to resolve */
int *addroffsets; /* list of offsets from instrs */
int addrmax; /* size of current addrlist array */
int addrcount; /* number of allocated addresses */
/* variable names. The index into cstat->variables give you what position
* the variable holds.
*/
const char *variables[MAX_VAR];
int variabletypes[MAX_VAR];
const char *localvars[MAX_VAR];
int localvartypes[MAX_VAR];
const char *scopedvars[MAX_VAR];
int scopedvartypes[MAX_VAR];
struct line *curr_line; /* current line */
int lineno; /* current line number */
int start_comment; /* Line last comment started at */
int force_comment; /* Only attempt certain compile. */
const char *next_char; /* next char * */
dbref player, program; /* player and program for this compile */
int compile_err; /* 1 if error occured */
char *line_copy;
int macrosubs; /* Safeguard for macro-subst. infinite loops */
int descr; /* the descriptor that initiated compiling */
int force_err_display; /* If true, always show compiler errors. */
struct INTERMEDIATE *nextinst;
hash_tab defhash[DEFHASHSIZE];
} COMPSTATE;
int primitive(const char *s); /* returns primitive_number if
* primitive */
#define free_prog(i) free_prog_real(i,__FILE__,__LINE__);
void free_prog_real(dbref,const char*, const int);
const char *next_token(COMPSTATE *);
const char *next_token_raw(COMPSTATE *);
struct INTERMEDIATE *next_word(COMPSTATE *, const char *);
struct INTERMEDIATE *process_special(COMPSTATE *, const char *);
struct INTERMEDIATE *primitive_word(COMPSTATE *, const char *);
struct INTERMEDIATE *string_word(COMPSTATE *, const char *);
struct INTERMEDIATE *number_word(COMPSTATE *, const char *);
struct INTERMEDIATE *float_word(COMPSTATE *, const char *);
struct INTERMEDIATE *object_word(COMPSTATE *, const char *);
struct INTERMEDIATE *quoted_word(COMPSTATE *, const char *);
struct INTERMEDIATE *call_word(COMPSTATE *, const char *);
struct INTERMEDIATE *var_word(COMPSTATE *, const char *);
struct INTERMEDIATE *lvar_word(COMPSTATE *, const char *);
struct INTERMEDIATE *svar_word(COMPSTATE *, const char *);
const char *do_string(COMPSTATE *);
void do_comment(COMPSTATE *, int);
void do_directive(COMPSTATE *, char *direct);
struct prog_addr *alloc_addr(COMPSTATE *, int, struct inst *);
struct INTERMEDIATE *prealloc_inst(COMPSTATE * cstat);
struct INTERMEDIATE *new_inst(COMPSTATE *);
void cleanpubs(struct publics *mypub);
void clean_mcpbinds(struct mcp_binding *mcpbinds);
void cleanup(COMPSTATE *);
void add_proc(COMPSTATE *, const char *, struct INTERMEDIATE *, int rettype);
void add_control_structure(COMPSTATE *, int typ, struct INTERMEDIATE *);
void add_loop_exit(COMPSTATE *, struct INTERMEDIATE *);
int in_loop(COMPSTATE * cstat);
int innermost_control_type(COMPSTATE * cstat);
int count_trys_inside_loop(COMPSTATE* cstat);
struct INTERMEDIATE *locate_control_structure(COMPSTATE* cstat, int type1, int type2);
struct INTERMEDIATE *innermost_control_place(COMPSTATE * cstat, int type1);
struct INTERMEDIATE *pop_control_structure(COMPSTATE * cstat, int type1, int type2);
struct INTERMEDIATE *pop_loop_exit(COMPSTATE *);
void resolve_loop_addrs(COMPSTATE *, int where);
int add_variable(COMPSTATE *, const char *, int valtype);
int add_localvar(COMPSTATE *, const char *, int valtype);
int add_scopedvar(COMPSTATE *, const char *, int valtype);
int special(const char *);
int call(COMPSTATE *, const char *);
int quoted(COMPSTATE *, const char *);
int object(const char *);
int string(const char *);
int variable(COMPSTATE *, const char *);
int localvar(COMPSTATE *, const char *);
int scopedvar(COMPSTATE *, const char *);
void copy_program(COMPSTATE *);
void set_start(COMPSTATE *);
void free_intermediate_node(struct INTERMEDIATE *wd);
/* Character defines */
#define BEGINCOMMENT '('
#define ENDCOMMENT ')'
#define BEGINSTRING '"'
#define ENDSTRING '"'
#define BEGINMACRO '.'
#define BEGINDIRECTIVE '$'
#define BEGINESCAPE '\\'
#define SUBSTITUTIONS 20 /* How many nested macros will we allow? */
void
do_abort_compile(COMPSTATE * cstat, const char *c)
{
static char _buf[BUFFER_LEN];
if (cstat->start_comment) {
snprintf(_buf, sizeof(_buf), "Error in line %d: %s Comment starting at line %d.", cstat->lineno, c, cstat->start_comment);
cstat->start_comment = 0;
} else {
snprintf(_buf, sizeof(_buf), "Error in line %d: %s", cstat->lineno, c);
}
if (cstat->line_copy) {
free((void *) cstat->line_copy);
cstat->line_copy = NULL;
}
if (((FLAGS(cstat->player) & INTERACTIVE) && !(FLAGS(cstat->player) & READMODE)) ||
cstat->force_err_display) {
notify_nolisten(cstat->player, _buf, 1);
} else {
log_muf("%s(#%d) [%s(#%d)] %s(#%d) %s\n",
NAME(OWNER(cstat->program)), OWNER(cstat->program),
NAME(cstat->program), cstat->program,
NAME(cstat->player), cstat->player,
_buf
);
}
cstat->compile_err++;
if (cstat->compile_err > 1) {
return;
}
if (cstat->nextinst) {
struct INTERMEDIATE* ptr;
while (cstat->nextinst)
{
ptr = cstat->nextinst;
cstat->nextinst = ptr->next;
free(ptr);
}
cstat->nextinst = NULL;
}
cleanup(cstat);
cleanpubs(cstat->currpubs);
cstat->currpubs = NULL;
free_prog(cstat->program);
cleanpubs(PROGRAM_PUBS(cstat->program));
PROGRAM_SET_PUBS(cstat->program, NULL);
clean_mcpbinds(PROGRAM_MCPBINDS(cstat->program));
PROGRAM_SET_MCPBINDS(cstat->program, NULL);
PROGRAM_SET_PROFTIME(cstat->program, 0, 0);
}
/* abort compile macro */
#define abort_compile(ST,C) { do_abort_compile(ST,C); return 0; }
/* abort compile for void functions */
#define v_abort_compile(ST,C) { do_abort_compile(ST,C); return; }
void compiler_warning(COMPSTATE* cstat, char* text, ...)
{
char buf[BUFFER_LEN];
va_list vl;
va_start(vl, text);
vsnprintf(buf, sizeof(buf), text, vl);
va_end(vl);
notify_nolisten(cstat->player, buf, 1);
}
/*****************************************************************/
#define ADDRLIST_ALLOC_CHUNK_SIZE 256
int
get_address(COMPSTATE* cstat, struct INTERMEDIATE* dest, int offset)
{
int i;
if (!cstat->addrlist)
{
cstat->addrcount = 0;
cstat->addrmax = ADDRLIST_ALLOC_CHUNK_SIZE;
cstat->addrlist = (struct INTERMEDIATE**)
malloc(cstat->addrmax * sizeof(struct INTERMEDIATE*));
cstat->addroffsets = (int*)
malloc(cstat->addrmax * sizeof(int));
}
for (i = 0; i < cstat->addrcount; i++)
if (cstat->addrlist[i] == dest && cstat->addroffsets[i] == offset)
return i;
if (cstat->addrcount >= cstat->addrmax)
{
cstat->addrmax += ADDRLIST_ALLOC_CHUNK_SIZE;
cstat->addrlist = (struct INTERMEDIATE**)
realloc(cstat->addrlist, cstat->addrmax * sizeof(struct INTERMEDIATE*));
cstat->addroffsets = (int*)
realloc(cstat->addroffsets, cstat->addrmax * sizeof(int));
}
cstat->addrlist[cstat->addrcount] = dest;
cstat->addroffsets[cstat->addrcount] = offset;
return cstat->addrcount++;
}
void
fix_addresses(COMPSTATE* cstat)
{
struct INTERMEDIATE* ptr;
struct publics* pub;
int count = 0;
/* renumber the instruction chain */
for (ptr = cstat->first_word; ptr; ptr = ptr->next)
ptr->no = count++;
/* repoint publics to targets */
for (pub = cstat->currpubs; pub; pub = pub->next)
pub->addr.no = cstat->addrlist[pub->addr.no]->no +
cstat->addroffsets[pub->addr.no];
/* repoint addresses to targets */
for (ptr = cstat->first_word; ptr; ptr = ptr->next)
{
switch (ptr->in.type) {
case PROG_ADD:
case PROG_IF:
case PROG_TRY:
case PROG_JMP:
case PROG_EXEC:
ptr->in.data.number = cstat->addrlist[ptr->in.data.number]->no +
cstat->addroffsets[ptr->in.data.number];
break;
default:
break;
}
}
}
void
free_addresses(COMPSTATE* cstat)
{
cstat->addrcount = 0;
cstat->addrmax = 0;
if (cstat->addrlist)
free(cstat->addrlist);
if (cstat->addroffsets)
free(cstat->addroffsets);
cstat->addrlist = NULL;
}
/*****************************************************************/
void
fixpubs(struct publics *mypubs, struct inst *offset)
{
while (mypubs) {
mypubs->addr.ptr = offset + mypubs->addr.no;
mypubs = mypubs->next;
}
}
int
size_pubs(struct publics *mypubs)
{
int bytes = 0;
while (mypubs) {
bytes += sizeof(*mypubs);
mypubs = mypubs->next;
}
return bytes;
}
char *
expand_def(COMPSTATE * cstat, const char *defname)
{
hash_data *exp = find_hash(defname, cstat->defhash, DEFHASHSIZE);
if (!exp) {
if (*defname == BEGINMACRO) {
return (macro_expansion(macrotop, &defname[1]));
} else {
return (NULL);
}
}
return (string_dup((char *) exp->pval));
}
void
kill_def(COMPSTATE * cstat, const char *defname)
{
hash_data *exp = find_hash(defname, cstat->defhash, DEFHASHSIZE);
if (exp) {
free(exp->pval);
(void) free_hash(defname, cstat->defhash, DEFHASHSIZE);
}
}
void
insert_def(COMPSTATE * cstat, const char *defname, const char *deff)
{
hash_data hd;
(void) kill_def(cstat, defname);
hd.pval = (void *) string_dup(deff);
(void) add_hash(defname, hd, cstat->defhash, DEFHASHSIZE);
}
void
insert_intdef(COMPSTATE * cstat, const char *defname, int deff)
{
char buf[sizeof(int) * 3];
snprintf(buf, sizeof(buf), "%d", deff);
insert_def(cstat, defname, buf);
}
void
purge_defs(COMPSTATE * cstat)
{
kill_hash(cstat->defhash, DEFHASHSIZE, 1);
}
void
include_defs(COMPSTATE * cstat, dbref i)
{
char dirname[BUFFER_LEN];
char temp[BUFFER_LEN];
const char *tmpptr;
PropPtr j, pptr;
strcpy(dirname, "/_defs/");
j = first_prop(i, dirname, &pptr, temp);
while (j) {
strcpy(dirname, "/_defs/");
strcatn(dirname, sizeof(dirname), temp);
tmpptr = uncompress(get_property_class(i, dirname));
if (tmpptr && *tmpptr)
insert_def(cstat, temp, (char *) tmpptr);
j = next_prop(pptr, j, temp);
}
}
void
include_internal_defs(COMPSTATE * cstat)
{
/* Create standard server defines */
insert_def(cstat, "__version", VERSION);
insert_def(cstat, "__muckname", tp_muckname);
insert_intdef(cstat, "__fuzzball__", 1);
insert_def(cstat, "strip", "striplead striptail");
insert_def(cstat, "instring", "tolower swap tolower swap instr");
insert_def(cstat, "rinstring", "tolower swap tolower swap rinstr");
insert_intdef(cstat, "bg_mode", BACKGROUND);
insert_intdef(cstat, "fg_mode", FOREGROUND);
insert_intdef(cstat, "pr_mode", PREEMPT);
insert_intdef(cstat, "max_variable_count", MAX_VAR);
insert_intdef(cstat, "sorttype_caseinsens", SORTTYPE_CASEINSENS);
insert_intdef(cstat, "sorttype_descending", SORTTYPE_DESCENDING);
insert_intdef(cstat, "sorttype_case_ascend", SORTTYPE_CASE_ASCEND);
insert_intdef(cstat, "sorttype_nocase_ascend", SORTTYPE_NOCASE_ASCEND);
insert_intdef(cstat, "sorttype_case_descend", SORTTYPE_CASE_DESCEND);
insert_intdef(cstat, "sorttype_nocase_descend", SORTTYPE_NOCASE_DESCEND);
insert_intdef(cstat, "sorttype_shuffle", SORTTYPE_SHUFFLE);
/* Make defines for compatability to removed primitives */
insert_def(cstat, "desc", "\"_/de\" getpropstr");
insert_def(cstat, "succ", "\"_/sc\" getpropstr");
insert_def(cstat, "fail", "\"_/fl\" getpropstr");
insert_def(cstat, "drop", "\"_/dr\" getpropstr");
insert_def(cstat, "osucc", "\"_/osc\" getpropstr");
insert_def(cstat, "ofail", "\"_/ofl\" getpropstr");
insert_def(cstat, "odrop", "\"_/odr\" getpropstr");
insert_def(cstat, "setdesc", "\"_/de\" swap 0 addprop");
insert_def(cstat, "setsucc", "\"_/sc\" swap 0 addprop");
insert_def(cstat, "setfail", "\"_/fl\" swap 0 addprop");
insert_def(cstat, "setdrop", "\"_/dr\" swap 0 addprop");
insert_def(cstat, "setosucc", "\"_/osc\" swap 0 addprop");
insert_def(cstat, "setofail", "\"_/ofl\" swap 0 addprop");
insert_def(cstat, "setodrop", "\"_/odr\" swap 0 addprop");
insert_def(cstat, "preempt", "pr_mode setmode");
insert_def(cstat, "background", "bg_mode setmode");
insert_def(cstat, "foreground", "fg_mode setmode");
insert_def(cstat, "notify_except", "1 swap notify_exclude");
insert_def(cstat, "event_wait", "0 array_make event_waitfor");
insert_def(cstat, "tread", "\"__tread\" timer_start { \"TIMER.__tread\" \"READ\" }list event_waitfor swap pop \"READ\" strcmp if \"\" 0 else read 1 \"__tread\" timer_stop then");
insert_def(cstat, "truename", "name");
/* MUF Error defines */
insert_def(cstat, "err_divzero?", "0 is_set?");
insert_def(cstat, "err_nan?", "1 is_set?");
insert_def(cstat, "err_imaginary?", "2 is_set?");
insert_def(cstat, "err_fbounds?", "3 is_set?");
insert_def(cstat, "err_ibounds?", "4 is_set?");
/* Array convenience defines */
insert_def(cstat, "}array", "} array_make");
insert_def(cstat, "}list", "} array_make");
insert_def(cstat, "}dict", "} 2 / array_make_dict");
insert_def(cstat, "}join", "} array_make \"\" array_join");
insert_def(cstat, "}cat", "} array_make array_interpret");
insert_def(cstat, "}tell", "} array_make me @ 1 array_make array_notify");
insert_def(cstat, "[]", "array_getitem");
insert_def(cstat, "->[]", "array_setitem");
insert_def(cstat, "[]<-", "array_appenditem");
insert_def(cstat, "[..]", "array_getrange");
insert_def(cstat, "array_diff", "2 array_ndiff");
insert_def(cstat, "array_union", "2 array_nunion");
insert_def(cstat, "array_intersect", "2 array_nintersect");
/* GUI dialog types */
insert_def(cstat, "d_simple", "\"simple\"");
insert_def(cstat, "d_tabbed", "\"tabbed\"");
insert_def(cstat, "d_helper", "\"helper\"");
/* GUI control types */
insert_def(cstat, "c_menu", "\"menu\"");
insert_def(cstat, "c_datum", "\"datum\"");
insert_def(cstat, "c_label", "\"text\"");
insert_def(cstat, "c_image", "\"image\"");
insert_def(cstat, "c_hrule", "\"hrule\"");
insert_def(cstat, "c_vrule", "\"vrule\"");
insert_def(cstat, "c_button", "\"button\"");
insert_def(cstat, "c_checkbox", "\"checkbox\"");
insert_def(cstat, "c_radiobtn", "\"radio\"");
insert_def(cstat, "c_password", "\"password\"");
insert_def(cstat, "c_edit", "\"edit\"");
insert_def(cstat, "c_multiedit", "\"multiedit\"");
insert_def(cstat, "c_combobox", "\"combobox\"");
insert_def(cstat, "c_spinner", "\"spinner\"");
insert_def(cstat, "c_scale", "\"scale\"");
insert_def(cstat, "c_listbox", "\"listbox\"");
insert_def(cstat, "c_tree", "\"tree\"");
insert_def(cstat, "c_frame", "\"frame\"");
insert_def(cstat, "c_notebook", "\"notebook\"");
/* Backwards compatibility for old GUI dialog creation prims */
insert_def(cstat, "gui_dlog_simple", "d_simple swap 0 array_make_dict gui_dlog_create");
insert_def(cstat, "gui_dlog_tabbed", "d_tabbed swap \"panes\" over array_keys array_make \"names\" 4 rotate array_vals array_make 2 array_make_dict gui_dlog_create");
insert_def(cstat, "gui_dlog_helper", "d_helper swap \"panes\" over array_keys array_make \"names\" 4 rotate array_vals array_make 2 array_make_dict gui_dlog_create");
/* Regex */
insert_intdef(cstat, "reg_icase", MUF_RE_ICASE);
insert_intdef(cstat, "reg_all", MUF_RE_ALL);
insert_intdef(cstat, "reg_extended", MUF_RE_EXTENDED);
}
void
init_defs(COMPSTATE * cstat)
{
/* initialize hash table */
int i;
for (i = 0; i < DEFHASHSIZE; i++) {
cstat->defhash[i] = NULL;
}
/* Create standard server defines */
include_internal_defs(cstat);
/* Include any defines set in #0's _defs/ propdir. */
include_defs(cstat, (dbref) 0);
/* Include any defines set in program owner's _defs/ propdir. */
include_defs(cstat, OWNER(cstat->program));
}
void
uncompile_program(dbref i)
{
/* free program */
(void) dequeue_prog(i, 1);
free_prog(i);
cleanpubs(PROGRAM_PUBS(i));
PROGRAM_SET_PUBS(i, NULL);
clean_mcpbinds(PROGRAM_MCPBINDS(i));
PROGRAM_SET_MCPBINDS(i, NULL);
PROGRAM_SET_PROFTIME(i, 0, 0);
PROGRAM_SET_CODE(i, NULL);
PROGRAM_SET_SIZ(i, 0);
PROGRAM_SET_START(i, NULL);
}
void
do_uncompile(dbref player)
{
dbref i;
if (!Wizard(OWNER(player))) {
notify_nolisten(player, "Permission denied.", 1);
return;
}
for (i = 0; i < db_top; i++) {
if (Typeof(i) == TYPE_PROGRAM) {
uncompile_program(i);
}
}
notify_nolisten(player, "All programs decompiled.", 1);
}
void
free_unused_programs()
{
dbref i;
time_t now = time(NULL);
for (i = 0; i < db_top; i++) {
if ((Typeof(i) == TYPE_PROGRAM) && !(FLAGS(i) & (ABODE | INTERNAL)) &&
(now - DBFETCH(i)->ts.lastused > tp_clean_interval) && (PROGRAM_INSTANCES(i) == 0)) {
uncompile_program(i);
}
}
}
/* Various flags for the IMMEDIATE instructions */
#define IMMFLAG_REFERENCED 1 /* Referenced by a jump */
/* Checks code for valid fetch-and-clear optim changes, and does them. */
void
MaybeOptimizeVarsAt(COMPSTATE * cstat, struct INTERMEDIATE* first, int AtNo, int BangNo)
{
struct INTERMEDIATE* curr = first->next;
struct INTERMEDIATE* ptr;
int farthest = 0;
int i;
int lvarflag = 0;
if (first->flags & INTMEDFLG_INTRY)
return;
if (first->in.type == PROG_LVAR_AT || first->in.type == PROG_LVAR_AT_CLEAR)
lvarflag = 1;
for(; curr; curr = curr->next) {
if (curr->flags & INTMEDFLG_INTRY)
return;
switch(curr->in.type) {
case PROG_PRIMITIVE:
/* Don't trust any physical @ or !'s in the code, someone
may be indirectly referencing the scoped variable */
/* Don't trust any explicit jmp's in the code. */
if ((curr->in.data.number == AtNo) ||
(curr->in.data.number == BangNo) ||
(curr->in.data.number == IN_JMP))
{
return;
}
if (lvarflag) {
/* For lvars, don't trust the following prims... */
/* EXITs escape the code path without leaving lvar scope. */
/* EXECUTEs escape the code path without leaving lvar scope. */
/* CALLs cause re-entrancy problems. */
if (curr->in.data.number == IN_RET ||
curr->in.data.number == IN_EXECUTE ||
curr->in.data.number == IN_CALL)
{
return;
}
}
break;
case PROG_LVAR_AT:
case PROG_LVAR_AT_CLEAR:
if (lvarflag) {
if (curr->in.data.number == first->in.data.number) {
/* Can't optimize if references to the variable found before a var! */
return;
}
}
break;
case PROG_SVAR_AT:
case PROG_SVAR_AT_CLEAR:
if (!lvarflag) {
if (curr->in.data.number == first->in.data.number) {
/* Can't optimize if references to the variable found before a var! */
return;
}
}
break;
case PROG_LVAR_BANG:
if (lvarflag) {
if (first->in.data.number == curr->in.data.number) {
if (curr->no <= farthest) {
/* cannot optimize as we are within a branch */
return;
} else {
/* Optimize it! */
first->in.type = PROG_LVAR_AT_CLEAR;
return;
}
}
}
break;
case PROG_SVAR_BANG:
if (!lvarflag) {
if (first->in.data.number == curr->in.data.number) {
if (curr->no <= farthest) {
/* cannot optimize as we are within a branch */
return;
} else {
/* Optimize it! */
first->in.type = PROG_SVAR_AT_CLEAR;
return;
}
}
}
break;
case PROG_EXEC:
if (lvarflag) {
/* Don't try to optimize lvars over execs */
return;
}
break;
case PROG_IF:
case PROG_TRY:
case PROG_JMP:
ptr = cstat->addrlist[curr->in.data.number];
i = cstat->addroffsets[curr->in.data.number];
while (ptr->next && i-->0)
ptr = ptr->next;
if (ptr->no <= first->no) {
/* Can't optimize as we've exited the code branch the @ is in. */
return;
}
if (ptr->no > farthest)
farthest = ptr->no;
break;
case PROG_FUNCTION:
/* Don't try to optimize over functions */
return;
}
}
}
void
RemoveNextIntermediate(COMPSTATE * cstat, struct INTERMEDIATE* curr)
{
struct INTERMEDIATE* tmp;
int i;
if (!curr->next) {
return;
}
tmp = curr->next;
for (i = 0; i < cstat->addrcount; i++) {
if (cstat->addrlist[i] == tmp) {
cstat->addrlist[i] = curr;
}
}
curr->next = curr->next->next;
free_intermediate_node(tmp);
cstat->nowords--;
}
void
RemoveIntermediate(COMPSTATE * cstat, struct INTERMEDIATE* curr)
{
if (!curr->next) {
return;
}
curr->no = curr->next->no;
curr->in.line = curr->next->in.line;
curr->in.type = curr->next->in.type;
switch(curr->in.type) {
case PROG_STRING:
curr->in.data.string = curr->next->in.data.string;
break;
case PROG_FLOAT:
curr->in.data.fnumber = curr->next->in.data.fnumber;
break;
case PROG_FUNCTION:
curr->in.data.mufproc = curr->next->in.data.mufproc;
break;
case PROG_ADD:
curr->in.data.addr = curr->next->in.data.addr;
break;
case PROG_IF:
case PROG_TRY:
case PROG_JMP:
case PROG_EXEC:
curr->in.data.call = curr->next->in.data.call;
break;
default:
curr->in.data.number = curr->next->in.data.number;
break;
}
curr->next->in.type = PROG_INTEGER;
curr->next->in.data.number = 0;
RemoveNextIntermediate(cstat, curr);
}
int
ContiguousIntermediates(int* Flags, struct INTERMEDIATE* ptr, int count)
{
while (count-->0) {
if (!ptr) {
return 0;
}
if ((Flags[ptr->no] & IMMFLAG_REFERENCED)) {
return 0;
}
ptr = ptr->next;
}
return 1;
}
int
IntermediateIsPrimitive(struct INTERMEDIATE* ptr, int primnum)
{
if (ptr && ptr->in.type == PROG_PRIMITIVE) {
if (ptr->in.data.number == primnum) {
return 1;
}
}
return 0;
}
int
IntermediateIsInteger(struct INTERMEDIATE* ptr, int val)
{
if (ptr && ptr->in.type == PROG_INTEGER) {
if (ptr->in.data.number == val) {
return 1;
}
}
return 0;
}
int
IntermediateIsString(struct INTERMEDIATE* ptr, const char* val)
{
const char* myval;
if (ptr && ptr->in.type == PROG_STRING) {
myval = ptr->in.data.string? ptr->in.data.string->data : "";
if (!strcmp(myval, val)) {
return 1;
}
}
return 0;
}
int
OptimizeIntermediate(COMPSTATE * cstat, int force_err_display)
{
struct INTERMEDIATE* curr;
int* Flags;
int i;
int count = 0;
int old_instr_count = cstat->nowords;
int AtNo = get_primitive("@"); /* Wince */
int BangNo = get_primitive("!");
int SwapNo = get_primitive("swap");
int RotNo = get_primitive("rot");
int NotNo = get_primitive("not");
int StrcmpNo = get_primitive("strcmp");
int EqualsNo = get_primitive("=");
int PlusNo = get_primitive("+");
int MinusNo = get_primitive("-");
int MultNo = get_primitive("*");
int DivNo = get_primitive("/");
int ModNo = get_primitive("%");
int DecrNo = get_primitive("--");
int IncrNo = get_primitive("++");
/* Code assumes everything is setup nicely, if not, bad things will happen */
if (!cstat->first_word)
return 0;
/* renumber the instruction chain */
for (curr = cstat->first_word; curr; curr = curr->next)
curr->no = count++;
if ((Flags = (int*)malloc(sizeof(int) * count)) == 0)
return 0;
memset(Flags, 0, sizeof(int) * count);
/* Mark instructions which jumps reference */
for(curr = cstat->first_word; curr; curr = curr->next) {
switch(curr->in.type) {
case PROG_ADD:
case PROG_IF:
case PROG_TRY:
case PROG_JMP:
case PROG_EXEC:
i = cstat->addrlist[curr->in.data.number]->no +
cstat->addroffsets[curr->in.data.number];
Flags[i] |= IMMFLAG_REFERENCED;
break;
}
}
for(curr = cstat->first_word; curr; ) {
int advance = 1;
switch(curr->in.type) {
case PROG_LVAR:
/* lvar ! ==> lvar! */
/* lvar @ ==> lvar@ */
if (curr->next && curr->next->in.type == PROG_PRIMITIVE) {
if (curr->next->in.data.number == AtNo) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
curr->in.type = PROG_LVAR_AT;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
if (curr->next->in.data.number == BangNo) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
curr->in.type = PROG_LVAR_BANG;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
break;
case PROG_SVAR:
/* svar ! ==> svar! */
/* svar @ ==> svar@ */
if (curr->next && curr->next->in.type == PROG_PRIMITIVE) {
if (curr->next->in.data.number == AtNo) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
curr->in.type = PROG_SVAR_AT;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
if (curr->next->in.data.number == BangNo) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
curr->in.type = PROG_SVAR_BANG;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
break;
case PROG_STRING:
/* "" strcmp 0 = ==> not */
if (IntermediateIsString(curr, "")) {
if (ContiguousIntermediates(Flags, curr->next, 3)) {
if (IntermediateIsPrimitive(curr->next, StrcmpNo)) {
if (IntermediateIsInteger(curr->next->next, 0)) {
if (IntermediateIsPrimitive(curr->next->next->next, EqualsNo)) {
if (curr->in.data.string)
free((void *) curr->in.data.string);
curr->in.type = PROG_PRIMITIVE;
curr->in.data.number = NotNo;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
}
}
break;
case PROG_INTEGER:
/* consolidate constant integer calculations */
if (ContiguousIntermediates(Flags, curr->next, 2)) {
if (curr->next->in.type == PROG_INTEGER) {
/* Int Int + ==> Sum */
if (IntermediateIsPrimitive(curr->next->next, PlusNo)) {
curr->in.data.number += curr->next->in.data.number;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
/* Int Int - ==> Diff */
if (IntermediateIsPrimitive(curr->next->next, MinusNo)) {
curr->in.data.number -= curr->next->in.data.number;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
/* Int Int * ==> Prod */
if (IntermediateIsPrimitive(curr->next->next, MultNo)) {
curr->in.data.number *= curr->next->in.data.number;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
/* Int Int / ==> Div */
if (IntermediateIsPrimitive(curr->next->next, DivNo)) {
if (curr->next->in.data.number == 0)
{
if (!(curr->next->next->flags & INTMEDFLG_DIVBYZERO))
{
curr->next->next->flags |= INTMEDFLG_DIVBYZERO;
if (force_err_display)
{
compiler_warning(
cstat,
"Warning on line %i: Divide by zero",
curr->next->next->in.line
);
}
}
}
else
{
curr->in.data.number /= curr->next->in.data.number;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
}
break;
}
/* Int Int % ==> Div */
if (IntermediateIsPrimitive(curr->next->next, ModNo)) {
if (curr->next->in.data.number == 0)
{
if (!(curr->next->next->flags & INTMEDFLG_MODBYZERO))
{
curr->next->next->flags |= INTMEDFLG_MODBYZERO;
if (force_err_display)
{
compiler_warning(
cstat,
"Warning on line %i: Modulus by zero",
curr->next->next->in.line
);
}
}
}
else
{
curr->in.data.number %= curr->next->in.data.number;
RemoveNextIntermediate(cstat, curr);
RemoveNextIntermediate(cstat, curr);
advance = 0;
}
break;
}
}
}
/* 0 = ==> not */
if (IntermediateIsInteger(curr, 0)) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
if (IntermediateIsPrimitive(curr->next, EqualsNo)) {
curr->in.type = PROG_PRIMITIVE;
curr->in.data.number = NotNo;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
/* 1 + ==> ++ */
if (IntermediateIsInteger(curr, 1)) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
if (IntermediateIsPrimitive(curr->next, PlusNo)) {
curr->in.type = PROG_PRIMITIVE;
curr->in.data.number = IncrNo;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
/* 1 - ==> -- */
if (IntermediateIsInteger(curr, 1)) {
if (ContiguousIntermediates(Flags, curr->next, 1)) {
if (IntermediateIsPrimitive(curr->next, MinusNo)) {
curr->in.type = PROG_PRIMITIVE;
curr->in.data.number = DecrNo;
RemoveNextIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
break;
case PROG_PRIMITIVE:
/* rot rot swap ==> swap rot */
if (IntermediateIsPrimitive(curr, RotNo)) {
if (ContiguousIntermediates(Flags, curr->next, 2)) {
if (IntermediateIsPrimitive(curr->next, RotNo)) {
if (IntermediateIsPrimitive(curr->next->next, SwapNo)) {
curr->in.data.number = SwapNo;
curr->next->in.data.number = RotNo;
RemoveNextIntermediate(cstat, curr->next);
advance = 0;
break;
}
}
}
}
/* not not if ==> if */
if (IntermediateIsPrimitive(curr, NotNo)) {
if (ContiguousIntermediates(Flags, curr->next, 2)) {
if (IntermediateIsPrimitive(curr->next, NotNo)) {
if (curr->next->next->in.type == PROG_IF) {
RemoveIntermediate(cstat, curr);
RemoveIntermediate(cstat, curr);
advance = 0;
break;
}
}
}
}
break;
}
if (advance) {
curr = curr->next;
}
}
/* Turn all var@'s which have a following var! into a var@-clear */
for(curr = cstat->first_word; curr; curr = curr->next)
if (curr->in.type == PROG_SVAR_AT || curr->in.type == PROG_LVAR_AT)
MaybeOptimizeVarsAt(cstat, curr, AtNo, BangNo);
free(Flags);
return (old_instr_count - cstat->nowords);
}
/* Genericized Optimizer ideas:
*
* const int OI_ANY = -121314; // arbitrary unlikely-to-be-needed value.
*
* typedef enum {
* OI_KEEP,
* OI_CHGVAL,
* OI_CHGTYPE,
* OI_REPLACE,
* OI_DELETE
* } OI_ACTION;
*
* OPTIM* option_new();
* void optim_free(OPTIM* optim);
* void optim_add_raw (OPTIM* optim, struct INTERMEDIATE* originst,
* OI_ACTION action, struct INTERMEDIATE* newinst);
* void optim_add_type (OPTIM* optim, int origtype,
* OI_ACTION action, int newtype);
* void optim_add_prim (OPTIM* optim, const char* origprim,
* OI_ACTION action, int newval);
* void optim_add_int (OPTIM* optim, int origval,
* OI_ACTION action, int newval);
* void optim_add_str (OPTIM* optim, const char* origval,
* OI_ACTION action, int newval);
*
*
* OPTIM* optim = optim_new(cstat);
* optim_add_str (optim, "", OI_DELETE, 0);
* optim_add_prim(optim, "strcmp", OI_CHGVAL, get_primitive("not"));
* optim_add_int (optim, 0, OI_DELETE, 0);
* optim_add_prim(optim, "=", OI_DELETE, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_str(optim, "", OI_DELETE, 0);
* optim_add_prim(optim, "strcmp", OI_DELETE, 0);
* optim_add_prim(optim, "not", OI_KEEP, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_prim(optim, "rot", OI_CHGVAL, get_primitive("swap"));
* optim_add_prim(optim, "rot", OI_KEEP, 0);
* optim_add_prim(optim, "swap", OI_DELETE, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_type(optim, PROG_SVAR, OI_CHGTYPE, PROG_SVAR_AT);
* optim_add_prim(optim, "@", OI_DELETE, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_type(optim, PROG_SVAR, OI_CHGTYPE, PROG_SVAR_BANG);
* optim_add_prim(optim, "!", OI_DELETE, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_int (optim, 0, OI_DELETE, 0);
* optim_add_prim(optim, "=", OI_CHGVAL, get_primitive("not"));
*
* OPTIM* optim = optim_new(cstat);
* optim_add_prim(optim, "not", OI_DELETE, 0);
* optim_add_prim(optim, "not", OI_DELETE, 0);
* optim_add_type(optim, PROG_IF, OI_KEEP, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_int (optim, 0, OI_DELETE, 0);
* optim_add_type(optim, PROG_IF, OI_CHGTYPE, PROG_JMP);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_int (optim, 1, OI_DELETE, 0);
* optim_add_type(optim, PROG_IF, OI_DELETE, 0);
*
* OPTIM* optim = optim_new(cstat);
* optim_add_int (optim, 1, OI_DELETE, 0);
* optim_add_prim(optim, "+", OI_CHGVAL, get_primitive("++"));
*
* OPTIM* optim = optim_new(cstat);
* optim_add_int (optim, 1, OI_DELETE, 0);
* optim_add_prim(optim, "-", OI_CHGVAL, get_primitive("--"));
*
*/
/* overall control code. Does piece-meal tokenization parsing and
backward checking. */
void
do_compile(int descr, dbref player_in, dbref program_in, int force_err_display)
{
const char *token;
struct INTERMEDIATE *new_word;
int i;
COMPSTATE cstat;
/* set all compile state variables */
cstat.force_err_display = force_err_display;
cstat.descr = descr;
cstat.control_stack = 0;
cstat.procs = 0;
cstat.nowords = 0;
cstat.curr_word = cstat.first_word = NULL;
cstat.curr_proc = NULL;
cstat.currpubs = NULL;
cstat.nested_fors = 0;
cstat.nested_trys = 0;
cstat.addrcount = 0;
cstat.addrmax = 0;
for (i = 0; i < MAX_VAR; i++) {
cstat.variables[i] = NULL;
cstat.variabletypes[i] = 0;
cstat.localvars[i] = NULL;
cstat.localvartypes[i] = 0;
cstat.scopedvars[i] = NULL;
cstat.scopedvartypes[i] = 0;
}
cstat.curr_line = PROGRAM_FIRST(program_in);
cstat.lineno = 1;
cstat.start_comment = 0;
cstat.force_comment = tp_muf_comments_strict? 1 : 0;
cstat.next_char = NULL;
if (cstat.curr_line)
cstat.next_char = cstat.curr_line->this_line;
cstat.player = player_in;
cstat.program = program_in;
cstat.compile_err = 0;
cstat.line_copy = NULL;
cstat.macrosubs = 0;
cstat.nextinst = NULL;
cstat.addrlist = NULL;
cstat.addroffsets = NULL;
init_defs(&cstat);
cstat.variables[0] = "ME";
cstat.variabletypes[0] = PROG_OBJECT;
cstat.variables[1] = "LOC";
cstat.variabletypes[1] = PROG_OBJECT;
cstat.variables[2] = "TRIGGER";
cstat.variabletypes[2] = PROG_OBJECT;
cstat.variables[3] = "COMMAND";
cstat.variabletypes[3] = PROG_STRING;
/* free old stuff */
(void) dequeue_prog(cstat.program, 1);
free_prog(cstat.program);
cleanpubs(PROGRAM_PUBS(cstat.program));
PROGRAM_SET_PUBS(cstat.program, NULL);
clean_mcpbinds(PROGRAM_MCPBINDS(cstat.program));
PROGRAM_SET_MCPBINDS(cstat.program, NULL);
PROGRAM_SET_PROFTIME(cstat.program, 0, 0);
PROGRAM_SET_PROFSTART(cstat.program, time(NULL));
PROGRAM_SET_PROF_USES(cstat.program, 0);
if (!cstat.curr_line)
v_abort_compile(&cstat, "Missing program text.");
/* do compilation */
while ((token = next_token(&cstat))) {
/* test for errors */
if (cstat.compile_err) {
free((void *) token);
return;
}
new_word = next_word(&cstat, token);
/* test for errors */
if (cstat.compile_err) {
free((void *) token);
return;
}
if (new_word) {
if (!cstat.first_word)
cstat.first_word = cstat.curr_word = new_word;
else {
cstat.curr_word->next = new_word;
cstat.curr_word = cstat.curr_word->next;
}
}
while (cstat.curr_word && cstat.curr_word->next)
cstat.curr_word = cstat.curr_word->next;
free((void *) token);
}
if (cstat.curr_proc)
v_abort_compile(&cstat, "Unexpected end of file.");
if (!cstat.procs)
v_abort_compile(&cstat, "Missing procedure definition.");
if (tp_optimize_muf) {
int maxpasses = 5;
int passcount = 0;
int optimcount = 0;
int optcnt = 0;
do {
optcnt = OptimizeIntermediate(&cstat, force_err_display);
optimcount += optcnt;
passcount++;
} while (optcnt > 0 && --maxpasses > 0);
if (force_err_display && optimcount > 0) {
char buf[BUFFER_LEN];
snprintf(buf, sizeof(buf), "Program optimized by %d instructions in %d passes.", optimcount, passcount);
notify_nolisten(cstat.player, buf, 1);
}
}
/* do copying over */
fix_addresses(&cstat);
copy_program(&cstat);
fixpubs(cstat.currpubs, PROGRAM_CODE(cstat.program));
PROGRAM_SET_PUBS(cstat.program, cstat.currpubs);
if (cstat.nextinst) {
struct INTERMEDIATE* ptr;
while (cstat.nextinst)
{
ptr = cstat.nextinst;
cstat.nextinst = ptr->next;
free(ptr);
}
cstat.nextinst = NULL;
}
if (cstat.compile_err)
return;
set_start(&cstat);
cleanup(&cstat);
/* Set PROGRAM_INSTANCES to zero (cuz they don't get set elsewhere) */
PROGRAM_SET_INSTANCES(cstat.program, 0);
/* restart AUTOSTART program. */
if ((FLAGS(cstat.program) & ABODE) && TrueWizard(OWNER(cstat.program)))
add_muf_queue_event(-1, OWNER(cstat.program), NOTHING, NOTHING,
cstat.program, "Startup", "Queued Event.", 0);
if (force_err_display)
notify_nolisten(cstat.player, "Program compiled successfully.", 1);
}
struct INTERMEDIATE *
next_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *new_word;
static char buf[BUFFER_LEN];
if (!token)
return 0;
if (call(cstat, token))
new_word = call_word(cstat, token);
else if (scopedvar(cstat, token))
new_word = svar_word(cstat, token);
else if (localvar(cstat, token))
new_word = lvar_word(cstat, token);
else if (variable(cstat, token))
new_word = var_word(cstat, token);
else if (special(token))
new_word = process_special(cstat, token);
else if (primitive(token))
new_word = primitive_word(cstat, token);
else if (string(token))
new_word = string_word(cstat, token + 1);
else if (number(token))
new_word = number_word(cstat, token);
else if (ifloat(token))
new_word = float_word(cstat, token);
else if (object(token))
new_word = object_word(cstat, token);
else if (quoted(cstat, token))
new_word = quoted_word(cstat, token + 1);
else {
snprintf(buf, sizeof(buf), "Unrecognized word %s.", token);
abort_compile(cstat, buf);
}
return new_word;
}
/* Little routine to do the line_copy handling right */
void
advance_line(COMPSTATE * cstat)
{
cstat->curr_line = cstat->curr_line->next;
cstat->lineno++;
cstat->macrosubs = 0;
if (cstat->line_copy) {
free((void *) cstat->line_copy);
cstat->line_copy = NULL;
}
if (cstat->curr_line)
cstat->next_char = (cstat->line_copy = alloc_string(cstat->curr_line->this_line));
else
cstat->next_char = (cstat->line_copy = NULL);
}
/* Skips comments, grabs strings, returns NULL when no more tokens to grab. */
const char *
next_token_raw(COMPSTATE * cstat)
{
static char buf[BUFFER_LEN];
int i;
if (!cstat->curr_line)
return (char *) 0;
if (!cstat->next_char)
return (char *) 0;
/* skip white space */
while (*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++;
if (!(*cstat->next_char)) {
advance_line(cstat);
return next_token_raw(cstat);
}
/* take care of comments */
if (*cstat->next_char == BEGINCOMMENT) {
cstat->start_comment = cstat->lineno;
if (cstat->force_comment == 1) {
do_comment(cstat, -1);
} else {
do_comment(cstat, 0);
}
cstat->start_comment = 0;
return next_token_raw(cstat);
}
if (*cstat->next_char == BEGINSTRING)
return do_string(cstat);
for (i = 0; *cstat->next_char && !isspace(*cstat->next_char); i++) {
buf[i] = *cstat->next_char;
cstat->next_char++;
}
buf[i] = '\0';
return alloc_string(buf);
}
const char *
next_token(COMPSTATE * cstat)
{
char *expansion, *temp;
temp = (char *) next_token_raw(cstat);
if (!temp)
return NULL;
if (temp[0] == BEGINDIRECTIVE) {
do_directive(cstat, temp);
free(temp);
return next_token(cstat);
}
if (temp[0] == BEGINESCAPE) {
if (temp[1]) {
expansion = temp;
temp = (char *) malloc(strlen(expansion));
strcpy(temp, (expansion + 1));
free(expansion);
}
return (temp);
}
if ((expansion = expand_def(cstat, temp))) {
free(temp);
if (++cstat->macrosubs > SUBSTITUTIONS) {
abort_compile(cstat, "Too many macro substitutions.");
} else {
int templen = strlen(cstat->next_char) + strlen(expansion) + 21;
temp = (char *) malloc(templen);
strcpy(temp, expansion);
strcatn(temp, templen, cstat->next_char);
free((void *) expansion);
if (cstat->line_copy) {
free((void *) cstat->line_copy);
}
cstat->next_char = cstat->line_copy = temp;
return next_token(cstat);
}
} else {
return (temp);
}
}
/* Old-style comment parser */
int do_old_comment(COMPSTATE * cstat)
{
while (*cstat->next_char && *cstat->next_char != ENDCOMMENT)
cstat->next_char++;
if (!(*cstat->next_char)) {
advance_line(cstat);
if (!cstat->curr_line) {
return 1;
}
return do_old_comment(cstat);
} else {
cstat->next_char++;
if (!(*cstat->next_char))
advance_line(cstat);
}
return 0;
}
/* skip comments, recursive style */
int do_new_comment(COMPSTATE * cstat, int depth)
{
int retval = 0;
int in_str = 0;
const char *ptr;
if (!*cstat->next_char || *cstat->next_char != BEGINCOMMENT)
return 2;
if (depth >= 7 /*arbitrary*/)
return 3;
cstat->next_char++; /* Advance past BEGINCOMMENT */
while (*cstat->next_char != ENDCOMMENT) {
if (!(*cstat->next_char)) {
do {
advance_line(cstat);
if (!cstat->curr_line) {
return 1;
}
} while(!(*cstat->next_char));
} else if (*cstat->next_char == BEGINCOMMENT) {
retval = do_new_comment(cstat, depth+1);
if (retval) {
return retval;
}
} else {
cstat->next_char++;
}
};
cstat->next_char++; /* Advance past ENDCOMMENT */
ptr = cstat->next_char;
while (*ptr) {
if (in_str) {
if (*ptr == ENDSTRING) {
in_str = 0;
}
} else {
if (*ptr == BEGINSTRING) {
in_str = 1;
} else if (*ptr == ENDSTRING) {
in_str = 1;
break;
}
}
ptr++;
}
if (in_str) {
compiler_warning(
cstat,
"Warning on line %i: Unterminated string may indicate unterminated comment. Comment starts on line %i.",
cstat->lineno, cstat->start_comment
);
}
if (!(*cstat->next_char))
advance_line(cstat);
if (depth && !cstat->curr_line) /* EOF? Don't care if done (depth==0) */
return 1;
return 0;
}
/* skip comments */
void
do_comment(COMPSTATE * cstat, int depth)
{
unsigned int next_char = 0; /* Save state if needed. */
int lineno = 0;
struct line *curr_line = NULL;
int macrosubs = 0;
int retval = 0;
if (!depth && !cstat->force_comment) {
next_char = cstat->line_copy?
cstat->next_char - cstat->line_copy : 0;
macrosubs = cstat->macrosubs;
lineno = cstat->lineno;
curr_line = cstat->curr_line;
}
if (!depth) {
if ((retval = do_new_comment(cstat, 0))) {
if (cstat->force_comment) {
switch (retval) {
case 1:
v_abort_compile(cstat, "Unterminated comment.");
break;
case 2:
v_abort_compile(cstat, "Expected comment.");
break;
case 3:
v_abort_compile(cstat, "Comments nested too deep (more than 7 levels).");
break;
}
return;
} else {
/* Set back up, drop through for retry. */
if (cstat->line_copy) {
free((void *) cstat->line_copy);
cstat->line_copy = NULL;
}
cstat->curr_line = curr_line;
cstat->macrosubs = macrosubs;
cstat->lineno = lineno;
if (cstat->curr_line) {
cstat->next_char = (cstat->line_copy = alloc_string(cstat->curr_line->this_line)) + next_char;
} else {
cstat->next_char = (cstat->line_copy = NULL);
}
}
} else {
/* Comment hunt worked, new-style. */
return;
}
}
if (do_old_comment(cstat)) {
v_abort_compile(cstat, "Unterminated comment.");
}
}
int
is_preprocessor_conditional(const char* tmpptr)
{
if (!string_compare(tmpptr, "$ifdef"))
return 1;
else if (!string_compare(tmpptr, "$ifndef"))
return 1;
else if (!string_compare(tmpptr, "$iflib"))
return 1;
else if (!string_compare(tmpptr, "$ifnlib"))
return 1;
else if (!string_compare(tmpptr, "$ifver"))
return 1;
else if (!string_compare(tmpptr, "$iflibver"))
return 1;
else if (!string_compare(tmpptr, "$ifnver"))
return 1;
else if (!string_compare(tmpptr, "$ifnlibver"))
return 1;
else if (!string_compare(tmpptr, "$ifcancall"))
return 1;
else if (!string_compare(tmpptr, "$ifncancall"))
return 1;
return 0;
}
/* handle compiler directives */
void
do_directive(COMPSTATE * cstat, char *direct)
{
char temp[BUFFER_LEN];
char *tmpname = NULL;
char *tmpptr = NULL;
int i = 0;
int j;
strcpy(temp, ++direct);
if (!(temp[0])) {
v_abort_compile(cstat, "I don't understand that compiler directive!");
}
if (!string_compare(temp, "define")) {
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file looking for $define name.");
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(string_compare(tmpptr, "$enddef"))) {
char *cp;
for (cp = tmpptr; i < (BUFFER_LEN / 2) && *cp;) {
if (*tmpptr == BEGINSTRING && cp != tmpptr &&
(*cp == ENDSTRING || *cp == BEGINESCAPE)) {
temp[i++] = BEGINESCAPE;
}
temp[i++] = *cp++;
}
if (*tmpptr == BEGINSTRING)
temp[i++] = ENDSTRING;
temp[i++] = ' ';
free(tmpptr);
if (i > (BUFFER_LEN / 2))
v_abort_compile(cstat, "$define definition too long.");
}
if (i)
i--;
temp[i] = '\0';
if (!tmpptr)
v_abort_compile(cstat, "Unexpected end of file in $define definition.");
free(tmpptr);
(void) insert_def(cstat, tmpname, temp);
free(tmpname);
} else if (!string_compare(temp, "cleardefs")) {
char nextToken[BUFFER_LEN];
purge_defs(cstat); /* Get rid of all defs first. */
include_internal_defs(cstat); /* Always include internal defs. */
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
strcpy(nextToken, cstat->next_char);
tmpname = nextToken;
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
if (!tmpname || !*tmpname || MLevel(OWNER(cstat->program)) < 4)
{
include_defs(cstat, OWNER(cstat->program));
include_defs(cstat, (dbref) 0);
}
} else if (!string_compare(temp, "enddef")) {
v_abort_compile(cstat, "$enddef without a previous matching $define.");
} else if (!string_compare(temp, "def")) {
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file looking for $def name.");
(void) insert_def(cstat, tmpname, cstat->next_char);
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
free(tmpname);
} else if (!string_compare(temp, "pubdef")) {
char *holder = NULL;
tmpname = (char *) next_token_raw(cstat);
holder = tmpname;
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file looking for $pubdef name.");
if (string_compare(tmpname, ":") &&
(index(tmpname, '/') ||
index(tmpname, ':') ||
Prop_SeeOnly(tmpname) ||
Prop_Hidden(tmpname) ||
Prop_System(tmpname)))
{
free(tmpname);
v_abort_compile(cstat, "Invalid $pubdef name. No /, :, @ nor ~ are allowed.");
} else {
if (!string_compare(tmpname, ":")) {
remove_property(cstat->program, "/_defs");
} else {
const char *defstr = NULL;
char propname[BUFFER_LEN];
int doitset = 1;
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
defstr = cstat->next_char;
if (*tmpname == '\\') {
char *temppropstr = NULL;
(void) *tmpname++;
snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
temppropstr = (char *) get_property_class(cstat->program, propname);
if (temppropstr ) {
doitset = 0;
}
} else {
snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
}
if (doitset) {
if (defstr && *defstr) {
add_property(cstat->program, propname, defstr, 0);
} else {
remove_property(cstat->program, propname);
}
}
}
}
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
free(holder);
} else if (!string_compare(temp, "libdef")) {
char *holder = NULL;
tmpname = (char *) next_token_raw(cstat);
holder = tmpname;
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file looking for $libdef name.");
if (index(tmpname, '/') ||
index(tmpname, ':') ||
Prop_SeeOnly(tmpname) ||
Prop_Hidden(tmpname) ||
Prop_System(tmpname))
{
free(tmpname);
v_abort_compile(cstat, "Invalid $libdef name. No /, :, @, nor ~ are allowed.");
} else {
char propname[BUFFER_LEN];
char defstr[BUFFER_LEN];
int doitset = 1;
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
if (*tmpname == '\\') {
char *temppropstr = NULL;
(void) *tmpname++;
snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
temppropstr = (char *) get_property_class(cstat->program, propname);
if (temppropstr ) {
doitset = 0;
}
} else {
snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
}
snprintf(defstr, sizeof(defstr), "#%i \"%s\" call", cstat->program, tmpname);
if (doitset) {
if (defstr && *defstr) {
add_property(cstat->program, propname, defstr, 0);
} else {
remove_property(cstat->program, propname);
}
}
}
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
free(holder);
} else if (!string_compare(temp, "include")) {
struct match_data md;
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file while doing $include.");
{
char tempa[BUFFER_LEN], tempb[BUFFER_LEN];
strcpy(tempa, match_args);
strcpy(tempb, match_cmdname);
init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
match_registered(&md);
match_absolute(&md);
match_me(&md);
i = (int) match_result(&md);
strcpy(match_args, tempa);
strcpy(match_cmdname, tempb);
}
free(tmpname);
if (((dbref) i == NOTHING) || (i < 0) || (i >= db_top)
|| (Typeof(i) == TYPE_GARBAGE))
v_abort_compile(cstat, "I don't understand what object you want to $include.");
include_defs(cstat, (dbref) i);
} else if (!string_compare(temp, "undef")) {
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file looking for name to $undef.");
kill_def(cstat, tmpname);
free(tmpname);
} else if (!string_compare(temp, "echo")) {
notify_nolisten(cstat->player, cstat->next_char, 1);
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
} else if (!string_compare(temp, "abort")) {
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
tmpname = (char *) cstat->next_char;
if (tmpname && *tmpname) {
v_abort_compile(cstat, tmpname);
} else {
v_abort_compile(cstat, "Forced abort for the compile.");
}
} else if (!string_compare(temp, "version")) {
tmpname = (char *) next_token_raw(cstat);
if (!ifloat(tmpname))
v_abort_compile(cstat, "Expected a floating point number for the version.");
add_property(cstat->program, "_version", tmpname, 0);
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
free(tmpname);
} else if (!string_compare(temp, "lib-version")) {
tmpname = (char *) next_token_raw(cstat);
if (!ifloat(tmpname))
v_abort_compile(cstat, "Expected a floating point number for the version.");
add_property(cstat->program, "_lib-version", tmpname, 0);
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
free(tmpname);
} else if (!string_compare(temp, "author")) {
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
tmpname = (char *) cstat->next_char;
while (*cstat->next_char)
cstat->next_char++;
add_property(cstat->program, "_author", tmpname, 0);
advance_line(cstat);
} else if (!string_compare(temp, "note")) {
while(*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++; /* eating leading spaces */
tmpname = (char *) cstat->next_char;
while (*cstat->next_char)
cstat->next_char++;
add_property(cstat->program, "_note", tmpname, 0);
advance_line(cstat);
} else if (!string_compare(temp, "ifdef") || !string_compare(temp, "ifndef")) {
int invert_flag = !string_compare(temp, "ifndef");
tmpname = (char *) next_token_raw(cstat);
if (!tmpname) {
v_abort_compile(cstat, "Unexpected end of file looking for $ifdef condition.");
}
if (*tmpname == '"') {
strcpy(temp, tmpname+1);
} else {
strcpy(temp, tmpname);
}
free(tmpname);
for (i = 1; temp[i] && (temp[i] != '=') && (temp[i] != '>') && (temp[i] != '<'); i++) ;
tmpname = &(temp[i]);
i = (temp[i] == '>') ? 1 : ((temp[i] == '=') ? 0 : ((temp[i] == '<') ? -1 : -2));
*tmpname = '\0';
tmpname++;
tmpptr = (char *) expand_def(cstat, temp);
if (i == -2) {
j = (!tmpptr);
if (tmpptr)
free(tmpptr);
} else {
if (!tmpptr) {
j = 1;
} else {
j = string_compare(tmpptr, tmpname);
j = !((!i && !j) || ((i * j) > 0));
free(tmpptr);
}
}
if (invert_flag) {
j = !j;
}
if (j) {
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(i || ((string_compare(tmpptr, "$else"))
&& (string_compare(tmpptr, "$endif"))))) {
if (is_preprocessor_conditional(tmpptr))
i++;
else if (!string_compare(tmpptr, "$endif"))
i--;
free(tmpptr);
}
if (!tmpptr) {
v_abort_compile(cstat, "Unexpected end of file in $ifdef clause.");
}
free(tmpptr);
}
} else if (!string_compare(temp, "ifcancall") || !string_compare(temp, "ifncancall")) {
struct match_data md;
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file for ifcancall.");
if (string_compare(tmpname, "this"))
{
char tempa[BUFFER_LEN], tempb[BUFFER_LEN];
strcpy(tempa, match_args);
strcpy(tempb, match_cmdname);
init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
match_registered(&md);
match_absolute(&md);
match_me(&md);
i = (int) match_result(&md);
strcpy(match_args, tempa);
strcpy(match_cmdname, tempb);
} else {
i = cstat->program;
}
free(tmpname);
if (((dbref) i == NOTHING) || (i < 0) || (i >= db_top) || (Typeof(i) == TYPE_GARBAGE))
v_abort_compile(cstat, "I don't understand what program you want to check in ifcancall.");
tmpname = (char *) next_token_raw(cstat);
if (!tmpname || !*tmpname)
{
if (tmpptr) {
free(tmpptr);
}
v_abort_compile(cstat, "I don't understand what function you want to check for.");
}
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
if (!PROGRAM_CODE(i)) {
struct line *tmpline;
tmpline = PROGRAM_FIRST(i);
PROGRAM_SET_FIRST(i, ((struct line *) read_program(i)));
do_compile(cstat->descr, OWNER(i), i, 0);
free_prog_text(PROGRAM_FIRST(i));
PROGRAM_SET_FIRST(i, tmpline);
}
j = 0;
if (MLevel(OWNER(i)) > 0 &&
(MLevel(OWNER(cstat->program)) >= 4 || OWNER(i) == OWNER(cstat->program) || Linkable(i))
) {
struct publics *pbs;
pbs = PROGRAM_PUBS(i);
while (pbs) {
if (!string_compare(tmpname, pbs->subname))
break;
pbs = pbs->next;
}
if (pbs && MLevel(OWNER(cstat->program)) >= pbs->mlev)
j = 1;
}
free(tmpname);
if (!string_compare(temp, "ifncancall")) {
j = !j;
}
if (!j) {
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(i || ((string_compare(tmpptr, "$else"))
&& (string_compare(tmpptr, "$endif"))))) {
if (is_preprocessor_conditional(tmpptr))
i++;
else if (!string_compare(tmpptr, "$endif"))
i--;
free(tmpptr);
}
if (!tmpptr) {
v_abort_compile(cstat, "Unexpected end of file in $ifcancall clause.");
}
free(tmpptr);
}
} else if (!string_compare(temp, "ifver") || !string_compare(temp, "iflibver") ||
!string_compare(temp, "ifnver") || !string_compare(temp, "ifnlibver")) {
struct match_data md;
double verflt = 0;
double checkflt = 0;
int needFree = 0;
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file while doing $ifver.");
if (string_compare(tmpname, "this"))
{
char tempa[BUFFER_LEN], tempb[BUFFER_LEN];
strcpy(tempa, match_args);
strcpy(tempb, match_cmdname);
init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
match_registered(&md);
match_absolute(&md);
match_me(&md);
i = (int) match_result(&md);
strcpy(match_args, tempa);
strcpy(match_cmdname, tempb);
} else {
i = cstat->program;
}
free(tmpname);
if (((dbref) i == NOTHING) || (i < 0) || (i >= db_top) || (Typeof(i) == TYPE_GARBAGE))
v_abort_compile(cstat, "I don't understand what object you want to check with $ifver.");
if (!string_compare(temp, "ifver") || !string_compare(temp, "ifnver")) {
tmpptr = (char *) get_property_class(i, "_version");
} else {
tmpptr = (char *) get_property_class(i, "_lib-version");
}
if (!tmpptr || !*tmpptr) {
tmpptr = (char*)malloc(4 * sizeof(char));
strcpy(tmpptr, "0.0");
needFree = 1;
} else {
uncompress(tmpptr);
}
tmpname = (char *) next_token_raw(cstat);
if (!tmpname || !*tmpname) {
free(tmpptr);
free(tmpname);
v_abort_compile(cstat, "I don't understand what version you want to compare to with $ifver.");
}
if (!tmpptr || !ifloat(tmpptr)) {
verflt = 0.0;
} else {
sscanf(tmpptr, "%lg", &verflt);
}
if ( needFree )
free(tmpptr);
if (!tmpname || !ifloat(tmpname)) {
checkflt = 0.0;
} else {
sscanf(tmpname, "%lg", &checkflt);
}
free(tmpname);
while (*cstat->next_char)
cstat->next_char++;
advance_line(cstat);
j = checkflt <= verflt;
if (!string_compare(temp, "ifnver") || !string_compare(temp, "ifnlibver")) {
j = !j;
}
if (!j) {
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(i || ((string_compare(tmpptr, "$else"))
&& (string_compare(tmpptr, "$endif"))))) {
if (is_preprocessor_conditional(tmpptr))
i++;
else if (!string_compare(tmpptr, "$endif"))
i--;
free(tmpptr);
}
if (!tmpptr) {
v_abort_compile(cstat, "Unexpected end of file in $ifver clause.");
}
free(tmpptr);
}
} else if (!string_compare(temp, "iflib") || !string_compare(temp, "ifnlib")) {
struct match_data md;
char tempa[BUFFER_LEN], tempb[BUFFER_LEN];
tmpname = (char *) next_token_raw(cstat);
if (!tmpname)
v_abort_compile(cstat, "Unexpected end of file in $iflib/$ifnlib clause.");
strcpy(tempa, match_args);
strcpy(tempb, match_cmdname);
init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
match_registered(&md);
match_absolute(&md);
match_me(&md);
i = (int) match_result(&md);
strcpy(match_args, tempa);
strcpy(match_cmdname, tempb);
free(tmpname);
if ((((dbref) i == NOTHING) || (i < 0) || (i >= db_top)
|| (Typeof(i) == TYPE_GARBAGE)) ? 0 : (Typeof(i) == TYPE_PROGRAM)
) {
j = 1;
} else {
j = 0;
}
if (!string_compare(temp, "ifnlib")) {
j = !j;
}
if (!j) {
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(i || ((string_compare(tmpptr, "$else"))
&& (string_compare(tmpptr, "$endif"))))) {
if (is_preprocessor_conditional(tmpptr))
i++;
else if (!string_compare(tmpptr, "$endif"))
i--;
free(tmpptr);
}
if (!tmpptr) {
v_abort_compile(cstat, "Unexpected end of file in $iflib clause.");
}
free(tmpptr);
}
} else if (!string_compare(temp, "else")) {
i = 0;
while ((tmpptr = (char *) next_token_raw(cstat)) &&
(i || (string_compare(tmpptr, "$endif")))) {
if (is_preprocessor_conditional(tmpptr))
i++;
else if (!string_compare(tmpptr, "$endif"))
i--;
free(tmpptr);
}
if (!tmpptr) {
v_abort_compile(cstat, "Unexpected end of file in $else clause.");
}
free(tmpptr);
} else if (!string_compare(temp, "endif")) {
} else if (!string_compare(temp, "pragma")) {
/* TODO - move pragmas to its own section for easy expansion. */
while (*cstat->next_char && isspace(*cstat->next_char))
cstat->next_char++;
if (!*cstat->next_char || !(tmpptr = (char *)next_token_raw(cstat)))
v_abort_compile(cstat, "Pragma requires at least one argument.");
if (!string_compare(tmpptr, "comment_strict")) {
/* Do non-recursive comments (old style) */
cstat->force_comment = 1;
} else if (!string_compare(tmpptr, "comment_recurse")) {
/* Do recursive comments ((new) style) */
cstat->force_comment = 2;
} else if (!string_compare(tmpptr, "comment_loose")) {
/* Try to compile with recursive and non-recursive comments
doing recursive first, then strict on a comment-based
compile error. Only throw an error if both fail. This is
the default mode. */
cstat->force_comment = 0;
} else {
/* If the pragma is not recognized, it is ignored, with a warning. */
compiler_warning(
cstat,
"Warning on line %i: Pragma %.64s unrecognized. Ignoring.",
cstat->lineno, tmpptr
);
while (*cstat->next_char)
cstat->next_char++;
}
free(tmpptr);
if (*cstat->next_char) {
compiler_warning(
cstat,
"Warning on line %i: Ignoring extra pragma arguments: %.256s",
cstat->lineno, cstat->next_char
);
advance_line(cstat);
}
} else {
v_abort_compile(cstat, "Unrecognized compiler directive.");
}
}
/* return string */
const char *
do_string(COMPSTATE * cstat)
{
static char buf[BUFFER_LEN];
int i = 0, quoted = 0;
buf[i] = *cstat->next_char;
cstat->next_char++;
i++;
while ((quoted || *cstat->next_char != ENDSTRING) && *cstat->next_char) {
if (*cstat->next_char == '\\' && !quoted) {
quoted++;
cstat->next_char++;
} else if (*cstat->next_char == 'r' && quoted) {
buf[i++] = '\r';
cstat->next_char++;
quoted = 0;
} else if (*cstat->next_char == '[' && quoted) {
buf[i++] = ESCAPE_CHAR;
cstat->next_char++;
quoted = 0;
} else {
buf[i] = *cstat->next_char;
i++;
cstat->next_char++;
quoted = 0;
}
}
if (!*cstat->next_char) {
abort_compile(cstat, "Unterminated string found at end of line.");
}
cstat->next_char++;
buf[i] = '\0';
return alloc_string(buf);
}
/* process special. Performs special processing.
It sets up FOR and IF structures. Remember --- for those,
we've got to set aside an extra argument space. */
struct INTERMEDIATE *
process_special(COMPSTATE * cstat, const char *token)
{
static char buf[BUFFER_LEN];
const char *tok;
struct INTERMEDIATE *nu;
if (!string_compare(token, ":")) {
const char *proc_name;
int argsflag = 0;
if (cstat->curr_proc)
abort_compile(cstat, "Definition within definition.");
proc_name = next_token(cstat);
if (!proc_name)
abort_compile(cstat, "Unexpected end of file within procedure.");
strcpy(buf, proc_name);
if (proc_name)
free((void *) proc_name);
proc_name = buf;
if (*proc_name && buf[strlen(buf)-1] == '[') {
argsflag = 1;
buf[strlen(buf)-1] = '\0';
if (!*proc_name)
abort_compile(cstat, "Bad procedure name.");
}
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_FUNCTION;
nu->in.line = cstat->lineno;
nu->in.data.mufproc = (struct muf_proc_data*)malloc(sizeof(struct muf_proc_data));
nu->in.data.mufproc->procname = string_dup(proc_name);
nu->in.data.mufproc->vars = 0;
nu->in.data.mufproc->args = 0;
nu->in.data.mufproc->varnames = NULL;
cstat->curr_proc = nu;
if (argsflag) {
const char* varspec;
const char* varname;
int argsdone = 0;
int outflag = 0;
do {
varspec = next_token(cstat);
if (!varspec)
abort_compile(cstat, "Unexpected end of file within procedure arguments declaration.");
if (!strcmp(varspec, "]")) {
argsdone = 1;
} else if (!strcmp(varspec, "--")) {
outflag = 1;
} else if (!outflag) {
varname = index(varspec, ':');
if (varname) {
varname++;
} else {
varname = varspec;
}
if (*varname) {
if (add_scopedvar(cstat, varname, PROG_UNTYPED) < 0)
abort_compile(cstat, "Variable limit exceeded.");
nu->in.data.mufproc->vars++;
nu->in.data.mufproc->args++;
}
}
if (varspec) {
free((void *) varspec);
}
} while(!argsdone);
}
add_proc(cstat, proc_name, nu, PROG_UNTYPED);
return nu;
} else if (!string_compare(token, ";")) {
int i, varcnt;
if (cstat->control_stack)
abort_compile(cstat, "Unexpected end of procedure definition.");
if (!cstat->curr_proc)
abort_compile(cstat, "Procedure end without body.");
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_RET;
varcnt = cstat->curr_proc->in.data.mufproc->vars;
if (varcnt) {
cstat->curr_proc->in.data.mufproc->varnames =
(const char**)calloc(varcnt, sizeof(char*));
for (i = 0; i < varcnt; i++) {
cstat->curr_proc->in.data.mufproc->varnames[i] = cstat->scopedvars[i];
cstat->scopedvars[i] = 0;
}
}
cstat->curr_proc = 0;
return nu;
} else if (!string_compare(token, "IF")) {
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_IF;
nu->in.line = cstat->lineno;
nu->in.data.call = 0;
add_control_structure(cstat, CTYPE_IF, nu);
return nu;
} else if (!string_compare(token, "ELSE")) {
struct INTERMEDIATE *eef;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_IF:
break;
case CTYPE_TRY:
abort_compile(cstat, "Unterminated TRY-CATCH block at ELSE.");
break;
case CTYPE_CATCH:
abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at ELSE.");
break;
case CTYPE_FOR:
case CTYPE_BEGIN:
abort_compile(cstat, "Unterminated Loop at ELSE.");
break;
default:
abort_compile(cstat, "ELSE without IF.");
break;
}
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_JMP;
nu->in.line = cstat->lineno;
nu->in.data.call = 0;
eef = pop_control_structure(cstat, CTYPE_IF, 0);
add_control_structure(cstat, CTYPE_ELSE, nu);
eef->in.data.number = get_address(cstat, nu, 1);
return nu;
} else if (!string_compare(token, "THEN")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *eef;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_IF:
case CTYPE_ELSE:
break;
case CTYPE_TRY:
abort_compile(cstat, "Unterminated TRY-CATCH block at THEN.");
break;
case CTYPE_CATCH:
abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at THEN.");
break;
case CTYPE_FOR:
case CTYPE_BEGIN:
abort_compile(cstat, "Unterminated Loop at THEN.");
break;
default:
abort_compile(cstat, "THEN without IF.");
break;
}
prealloc_inst(cstat);
eef = pop_control_structure(cstat, CTYPE_IF, CTYPE_ELSE);
eef->in.data.number = get_address(cstat, cstat->nextinst, 0);
return NULL;
} else if (!string_compare(token, "BEGIN")) {
prealloc_inst(cstat);
add_control_structure(cstat, CTYPE_BEGIN, cstat->nextinst);
return NULL;
} else if (!string_compare(token, "FOR")) {
struct INTERMEDIATE *new2, *new3;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_FOR;
new2 = (nu->next = new_inst(cstat));
new2->no = cstat->nowords++;
new2->in.type = PROG_PRIMITIVE;
new2->in.line = cstat->lineno;
new2->in.data.number = IN_FORITER;
new3 = (new2->next = new_inst(cstat));
new3->no = cstat->nowords++;
new3->in.line = cstat->lineno;
new3->in.type = PROG_IF;
new3->in.data.number = 0;
add_control_structure(cstat, CTYPE_FOR, new2);
cstat->nested_fors++;
return nu;
} else if (!string_compare(token, "FOREACH")) {
struct INTERMEDIATE *new2, *new3;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_FOREACH;
new2 = (nu->next = new_inst(cstat));
new2->no = cstat->nowords++;
new2->in.type = PROG_PRIMITIVE;
new2->in.line = cstat->lineno;
new2->in.data.number = IN_FORITER;
new3 = (new2->next = new_inst(cstat));
new3->no = cstat->nowords++;
new3->in.line = cstat->lineno;
new3->in.type = PROG_IF;
new3->in.data.number = 0;
add_control_structure(cstat, CTYPE_FOR, new2);
cstat->nested_fors++;
return nu;
} else if (!string_compare(token, "UNTIL")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *eef;
struct INTERMEDIATE *curr;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_FOR:
cstat->nested_fors--;
case CTYPE_BEGIN:
break;
case CTYPE_TRY:
abort_compile(cstat, "Unterminated TRY-CATCH block at UNTIL.");
break;
case CTYPE_CATCH:
abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at UNTIL.");
break;
case CTYPE_IF:
case CTYPE_ELSE:
abort_compile(cstat, "Unterminated IF-THEN at UNTIL.");
break;
default:
abort_compile(cstat, "Loop start not found for UNTIL.");
break;
}
prealloc_inst(cstat);
resolve_loop_addrs(cstat, get_address(cstat, cstat->nextinst, 1));
eef = pop_control_structure(cstat, CTYPE_BEGIN, CTYPE_FOR);
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_IF;
nu->in.line = cstat->lineno;
nu->in.data.number = get_address(cstat, eef, 0);
if (ctrltype == CTYPE_FOR) {
curr = (nu->next = new_inst(cstat));
curr->no = cstat->nowords++;
curr->in.type = PROG_PRIMITIVE;
curr->in.line = cstat->lineno;
curr->in.data.number = IN_FORPOP;
}
return nu;
} else if (!string_compare(token, "WHILE")) {
struct INTERMEDIATE *curr;
int trycount;
if (!in_loop(cstat))
abort_compile(cstat, "Can't have a WHILE outside of a loop.");
trycount = count_trys_inside_loop(cstat);
nu = curr = NULL;
while (trycount-->0) {
if (!nu) {
nu = curr = new_inst(cstat);
} else {
nu = (nu->next = new_inst(cstat));
}
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_TRYPOP;
}
if (nu) {
nu = (nu->next = new_inst(cstat));
} else {
curr = nu = new_inst(cstat);
}
nu->no = cstat->nowords++;
nu->in.type = PROG_IF;
nu->in.line = cstat->lineno;
nu->in.data.number = 0;
add_loop_exit(cstat, nu);
return curr;
} else if (!string_compare(token, "BREAK")) {
int trycount;
struct INTERMEDIATE *curr;
if (!in_loop(cstat))
abort_compile(cstat, "Can't have a BREAK outside of a loop.");
trycount = count_trys_inside_loop(cstat);
nu = curr = NULL;
while (trycount-->0) {
if (!nu) {
nu = curr = new_inst(cstat);
} else {
nu = (nu->next = new_inst(cstat));
}
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_TRYPOP;
}
if (nu) {
nu = (nu->next = new_inst(cstat));
} else {
curr = nu = new_inst(cstat);
}
nu->no = cstat->nowords++;
nu->in.type = PROG_JMP;
nu->in.line = cstat->lineno;
nu->in.data.number = 0;
add_loop_exit(cstat, nu);
return curr;
} else if (!string_compare(token, "CONTINUE")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *beef;
struct INTERMEDIATE *curr;
int trycount;
if (!in_loop(cstat))
abort_compile(cstat, "Can't CONTINUE outside of a loop.");
beef = locate_control_structure(cstat, CTYPE_FOR, CTYPE_BEGIN);
trycount = count_trys_inside_loop(cstat);
nu = curr = NULL;
while (trycount-->0) {
if (!nu) {
nu = curr = new_inst(cstat);
} else {
nu = (nu->next = new_inst(cstat));
}
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_TRYPOP;
}
if (nu) {
nu = (nu->next = new_inst(cstat));
} else {
curr = nu = new_inst(cstat);
}
nu->no = cstat->nowords++;
nu->in.type = PROG_JMP;
nu->in.line = cstat->lineno;
nu->in.data.number = get_address(cstat, beef, 0);
return curr;
} else if (!string_compare(token, "REPEAT")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *eef;
struct INTERMEDIATE *curr;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_FOR:
cstat->nested_fors--;
case CTYPE_BEGIN:
break;
case CTYPE_TRY:
abort_compile(cstat, "Unterminated TRY-CATCH block at REPEAT.");
break;
case CTYPE_CATCH:
abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at REPEAT.");
break;
case CTYPE_IF:
case CTYPE_ELSE:
abort_compile(cstat, "Unterminated IF-THEN at REPEAT.");
break;
default:
abort_compile(cstat, "Loop start not found for REPEAT.");
break;
}
prealloc_inst(cstat);
resolve_loop_addrs(cstat, get_address(cstat, cstat->nextinst, 1));
eef = pop_control_structure(cstat, CTYPE_BEGIN, CTYPE_FOR);
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_JMP;
nu->in.line = cstat->lineno;
nu->in.data.number = get_address(cstat, eef, 0);
if (ctrltype == CTYPE_FOR) {
curr = (nu->next = new_inst(cstat));
curr->no = cstat->nowords++;
curr->in.type = PROG_PRIMITIVE;
curr->in.line = cstat->lineno;
curr->in.data.number = IN_FORPOP;
}
return nu;
} else if (!string_compare(token, "TRY")) {
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_TRY;
nu->in.line = cstat->lineno;
nu->in.data.number = 0;
add_control_structure(cstat, CTYPE_TRY, nu);
cstat->nested_trys++;
return nu;
} else if (!string_compare(token, "CATCH") || !string_compare(token, "CATCH_DETAILED")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *eef;
struct INTERMEDIATE *curr;
struct INTERMEDIATE *jump;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_TRY:
break;
case CTYPE_FOR:
case CTYPE_BEGIN:
abort_compile(cstat, "Unterminated Loop at CATCH.");
break;
case CTYPE_IF:
case CTYPE_ELSE:
abort_compile(cstat, "Unterminated IF-THEN at CATCH.");
break;
case CTYPE_CATCH:
default:
abort_compile(cstat, "No TRY found for CATCH.");
break;
}
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_TRYPOP;
jump = (nu->next = new_inst(cstat));
jump->no = cstat->nowords++;
jump->in.type = PROG_JMP;
jump->in.line = cstat->lineno;
jump->in.data.number = 0;
curr = (jump->next = new_inst(cstat));
curr->no = cstat->nowords++;
curr->in.type = PROG_PRIMITIVE;
curr->in.line = cstat->lineno;
curr->in.data.number = IN_CATCH;
if (!string_compare(token, "CATCH_DETAILED")) {
curr->in.data.number = IN_CATCH_DETAILED;
}
eef = pop_control_structure(cstat, CTYPE_TRY, 0);
cstat->nested_trys--;
eef->in.data.number = get_address(cstat, curr, 0);
add_control_structure(cstat, CTYPE_CATCH, jump);
return nu;
} else if (!string_compare(token, "ENDCATCH")) {
/* can't use 'if' because it's a reserved word */
struct INTERMEDIATE *eef;
int ctrltype = innermost_control_type(cstat);
switch (ctrltype) {
case CTYPE_CATCH:
break;
case CTYPE_FOR:
case CTYPE_BEGIN:
abort_compile(cstat, "Unterminated Loop at ENDCATCH.");
break;
case CTYPE_IF:
case CTYPE_ELSE:
abort_compile(cstat, "Unterminated IF-THEN at ENDCATCH.");
break;
case CTYPE_TRY:
default:
abort_compile(cstat, "No CATCH found for ENDCATCH.");
break;
}
prealloc_inst(cstat);
eef = pop_control_structure(cstat, CTYPE_CATCH, 0);
eef->in.data.number = get_address(cstat, cstat->nextinst, 0);
return NULL;
} else if (!string_compare(token, "CALL")) {
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_PRIMITIVE;
nu->in.line = cstat->lineno;
nu->in.data.number = IN_CALL;
return nu;
} else if (!string_compare(token, "WIZCALL") || !string_compare(token, "PUBLIC")) {
struct PROC_LIST *p;
struct publics *pub;
int wizflag = 0;
if (!string_compare(token, "WIZCALL"))
wizflag = 1;
if (cstat->curr_proc)
abort_compile(cstat, "PUBLIC or WIZCALL declaration within procedure.");
tok = next_token(cstat);
if ((!tok) || !call(cstat, tok))
abort_compile(cstat, "Subroutine unknown in PUBLIC or WIZCALL declaration.");
for (p = cstat->procs; p; p = p->next)
if (!string_compare(p->name, tok))
break;
if (!p)
abort_compile(cstat, "Subroutine unknown in PUBLIC or WIZCALL declaration.");
if (!cstat->currpubs) {
cstat->currpubs = (struct publics *) malloc(sizeof(struct publics));
cstat->currpubs->next = NULL;
cstat->currpubs->subname = (char *) string_dup(tok);
if (tok)
free((void *) tok);
cstat->currpubs->addr.no = get_address(cstat, p->code, 0);
cstat->currpubs->mlev = wizflag ? 4 : 1;
return 0;
}
for (pub = cstat->currpubs; pub;) {
if (!string_compare(tok, pub->subname)) {
abort_compile(cstat, "Function already declared public.");
} else {
if (pub->next) {
pub = pub->next;
} else {
pub->next = (struct publics *) malloc(sizeof(struct publics));
pub = pub->next;
pub->next = NULL;
pub->subname = (char *) string_dup(tok);
if (tok)
free((void *) tok);
pub->addr.no = get_address(cstat, p->code, 0);
pub->mlev = wizflag ? 4 : 1;
pub = NULL;
}
}
}
return 0;
} else if (!string_compare(token, "VAR")) {
if (cstat->curr_proc) {
tok = next_token(cstat);
if (!tok)
abort_compile(cstat, "Unexpected end of program.");
if (add_scopedvar(cstat, tok, PROG_UNTYPED) < 0)
abort_compile(cstat, "Variable limit exceeded.");
if (tok)
free((void *) tok);
cstat->curr_proc->in.data.mufproc->vars++;
} else {
tok = next_token(cstat);
if (!tok)
abort_compile(cstat, "Unexpected end of program.");
if (!add_variable(cstat, tok, PROG_UNTYPED))
abort_compile(cstat, "Variable limit exceeded.");
if (tok)
free((void *) tok);
}
return 0;
} else if (!string_compare(token, "VAR!")) {
if (cstat->curr_proc) {
struct INTERMEDIATE *nu;
tok = next_token(cstat);
if (!tok)
abort_compile(cstat, "Unexpected end of program.");
if (add_scopedvar(cstat, tok, PROG_UNTYPED) < 0)
abort_compile(cstat, "Variable limit exceeded.");
if (tok)
free((void *) tok);
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_SVAR_BANG;
nu->in.line = cstat->lineno;
nu->in.data.number = cstat->curr_proc->in.data.mufproc->vars++;
return nu;
} else {
abort_compile(cstat, "VAR! used outside of procedure.");
}
return 0;
} else if (!string_compare(token, "LVAR")) {
if (cstat->curr_proc)
abort_compile(cstat, "Local variable declared within procedure.");
tok = next_token(cstat);
if (!tok || (add_localvar(cstat, tok, PROG_UNTYPED) == -1))
abort_compile(cstat, "Local variable limit exceeded.");
if (tok)
free((void *) tok);
return 0;
} else {
snprintf(buf, sizeof(buf), "Unrecognized special form %s found. (%d)", token, cstat->lineno);
abort_compile(cstat, buf);
}
}
/* return primitive word. */
struct INTERMEDIATE *
primitive_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu, *cur;
int pnum, loop;
pnum = get_primitive(token);
cur = nu = new_inst(cstat);
if (pnum == IN_RET || pnum == IN_JMP) {
for (loop = 0; loop < cstat->nested_trys; loop++) {
cur->no = cstat->nowords++;
cur->in.type = PROG_PRIMITIVE;
cur->in.line = cstat->lineno;
cur->in.data.number = IN_TRYPOP;
cur->next = new_inst(cstat);
cur = cur->next;
}
for (loop = 0; loop < cstat->nested_fors; loop++) {
cur->no = cstat->nowords++;
cur->in.type = PROG_PRIMITIVE;
cur->in.line = cstat->lineno;
cur->in.data.number = IN_FORPOP;
cur->next = new_inst(cstat);
cur = cur->next;
}
}
cur->no = cstat->nowords++;
cur->in.type = PROG_PRIMITIVE;
cur->in.line = cstat->lineno;
cur->in.data.number = pnum;
return nu;
}
/* return self pushing word (string) */
struct INTERMEDIATE *
string_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_STRING;
nu->in.line = cstat->lineno;
nu->in.data.string = alloc_prog_string(token);
return nu;
}
/* return self pushing word (float) */
struct INTERMEDIATE *
float_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_FLOAT;
nu->in.line = cstat->lineno;
sscanf(token, "%lg", &(nu->in.data.fnumber));
return nu;
}
/* return self pushing word (number) */
struct INTERMEDIATE *
number_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_INTEGER;
nu->in.line = cstat->lineno;
nu->in.data.number = atoi(token);
return nu;
}
/* do a subroutine call --- push address onto stack, then make a primitive
CALL.
*/
struct INTERMEDIATE *
call_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
struct PROC_LIST *p;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_EXEC;
nu->in.line = cstat->lineno;
for (p = cstat->procs; p; p = p->next)
if (!string_compare(p->name, token))
break;
nu->in.data.number = get_address(cstat, p->code, 0);
return nu;
}
struct INTERMEDIATE *
quoted_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
struct PROC_LIST *p;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_ADD;
nu->in.line = cstat->lineno;
for (p = cstat->procs; p; p = p->next)
if (!string_compare(p->name, token))
break;
nu->in.data.number = get_address(cstat, p->code, 0);
return nu;
}
/* returns number corresponding to variable number.
We assume that it DOES exist */
struct INTERMEDIATE *
var_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
int i, var_no;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_VAR;
nu->in.line = cstat->lineno;
for (var_no = i = 0; i < MAX_VAR; i++) {
if (!cstat->variables[i])
break;
if (!string_compare(token, cstat->variables[i]))
var_no = i;
}
nu->in.data.number = var_no;
return nu;
}
struct INTERMEDIATE *
svar_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
int i, var_no;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_SVAR;
nu->in.line = cstat->lineno;
for (i = var_no = 0; i < MAX_VAR; i++) {
if (!cstat->scopedvars[i])
break;
if (!string_compare(token, cstat->scopedvars[i]))
var_no = i;
}
nu->in.data.number = var_no;
return nu;
}
struct INTERMEDIATE *
lvar_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
int i, var_no;
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_LVAR;
nu->in.line = cstat->lineno;
for (i = var_no = 0; i < MAX_VAR; i++) {
if (!cstat->localvars[i])
break;
if (!string_compare(token, cstat->localvars[i]))
var_no = i;
}
nu->in.data.number = var_no;
return nu;
}
/* check if object is in database before putting it in */
struct INTERMEDIATE *
object_word(COMPSTATE * cstat, const char *token)
{
struct INTERMEDIATE *nu;
int objno;
objno = atol(token + 1);
nu = new_inst(cstat);
nu->no = cstat->nowords++;
nu->in.type = PROG_OBJECT;
nu->in.line = cstat->lineno;
nu->in.data.objref = objno;
return nu;
}
/* support routines for internal data structures. */
/* add procedure to procedures list */
void
add_proc(COMPSTATE * cstat, const char *proc_name, struct INTERMEDIATE *place, int rettype)
{
struct PROC_LIST *nu;
nu = (struct PROC_LIST *) malloc(sizeof(struct PROC_LIST));
nu->name = alloc_string(proc_name);
nu->returntype = rettype;
nu->code = place;
nu->next = cstat->procs;
cstat->procs = nu;
}
/* add if to control stack */
void
add_control_structure(COMPSTATE * cstat, int typ, struct INTERMEDIATE *place)
{
struct CONTROL_STACK *nu;
nu = (struct CONTROL_STACK *) malloc(sizeof(struct CONTROL_STACK));
nu->place = place;
nu->type = typ;
nu->next = cstat->control_stack;
nu->extra = 0;
cstat->control_stack = nu;
}
/* add while to current loop's list of exits remaining to be resolved. */
void
add_loop_exit(COMPSTATE * cstat, struct INTERMEDIATE *place)
{
struct CONTROL_STACK *nu;
struct CONTROL_STACK *loop;
loop = cstat->control_stack;
while (loop && loop->type != CTYPE_BEGIN && loop->type != CTYPE_FOR) {
loop = loop->next;
}
if (!loop)
return;
nu = (struct CONTROL_STACK *) malloc(sizeof(struct CONTROL_STACK));
nu->place = place;
nu->type = CTYPE_WHILE;
nu->next = 0;
nu->extra = loop->extra;
loop->extra = nu;
}
/* Returns true if a loop start is in the control structure stack. */
int
in_loop(COMPSTATE * cstat)
{
struct CONTROL_STACK *loop;
loop = cstat->control_stack;
while (loop && loop->type != CTYPE_BEGIN && loop->type != CTYPE_FOR) {
loop = loop->next;
}
return (loop != NULL);
}
/* Returns the type of the innermost nested control structure. */
int
innermost_control_type(COMPSTATE * cstat)
{
struct CONTROL_STACK *ctrl;
ctrl = cstat->control_stack;
if (!ctrl)
return 0;
return ctrl->type;
}
/* Returns number of TRYs before topmost Loop */
int
count_trys_inside_loop(COMPSTATE* cstat)
{
struct CONTROL_STACK *loop;
int count = 0;
loop = cstat->control_stack;
while (loop) {
if (loop->type == CTYPE_FOR || loop->type == CTYPE_BEGIN) {
break;
}
if (loop->type == CTYPE_TRY) {
count++;
}
loop = loop->next;
}
return count;
}
/* returns topmost begin or for off the stack */
struct INTERMEDIATE *
locate_control_structure(COMPSTATE* cstat, int type1, int type2)
{
struct CONTROL_STACK *loop;
loop = cstat->control_stack;
while (loop) {
if (loop->type == type1 || loop->type == type2) {
return loop->place;
}
loop = loop->next;
}
return 0;
}
/* checks if topmost loop stack item is a for */
struct INTERMEDIATE *
innermost_control_place(COMPSTATE * cstat, int type1)
{
struct CONTROL_STACK *ctrl;
ctrl = cstat->control_stack;
if (!ctrl)
return 0;
if (ctrl->type != type1)
return 0;
return ctrl->place;
}
/* Pops off the innermost control structure and returns the place. */
struct INTERMEDIATE *
pop_control_structure(COMPSTATE * cstat, int type1, int type2)
{
struct CONTROL_STACK *ctrl;
struct INTERMEDIATE *place;
ctrl = cstat->control_stack;
if (!ctrl)
return NULL;
if (ctrl->type != type1 && ctrl->type != type2)
return NULL;
place = ctrl->place;
cstat->control_stack = ctrl->next;
free(ctrl);
return place;
}
/* pops first while off the innermost control structure, if it's a loop. */
struct INTERMEDIATE *
pop_loop_exit(COMPSTATE * cstat)
{
struct INTERMEDIATE *temp;
struct CONTROL_STACK *tofree;
struct CONTROL_STACK *parent;
parent = cstat->control_stack;
if (!parent)
return 0;
if (parent->type != CTYPE_BEGIN && parent->type != CTYPE_FOR)
return 0;
if (!parent->extra)
return 0;
if (parent->extra->type != CTYPE_WHILE)
return 0;
temp = parent->extra->place;
tofree = parent->extra;
parent->extra = parent->extra->extra;
free((void *) tofree);
return temp;
}
void
resolve_loop_addrs(COMPSTATE * cstat, int where)
{
struct INTERMEDIATE *eef;
while ((eef = pop_loop_exit(cstat)))
eef->in.data.number = where;
eef = innermost_control_place(cstat, CTYPE_FOR);
if (eef) {
eef->next->in.data.number = where;
}
}
/* adds variable. Return 0 if no space left */
int
add_variable(COMPSTATE * cstat, const char *varname, int valtype)
{
int i;
for (i = RES_VAR; i < MAX_VAR; i++)
if (!cstat->variables[i])
break;
if (i == MAX_VAR)
return 0;
cstat->variables[i] = alloc_string(varname);
cstat->variabletypes[i] = valtype;
return i;
}
/* adds local variable. Return 0 if no space left */
int
add_scopedvar(COMPSTATE * cstat, const char *varname, int valtype)
{
int i;
for (i = 0; i < MAX_VAR; i++)
if (!cstat->scopedvars[i])
break;
if (i == MAX_VAR)
return -1;
cstat->scopedvars[i] = alloc_string(varname);
cstat->scopedvartypes[i] = valtype;
return i;
}
/* adds local variable. Return 0 if no space left */
int
add_localvar(COMPSTATE * cstat, const char *varname, int valtype)
{
int i;
for (i = 0; i < MAX_VAR; i++)
if (!cstat->localvars[i])
break;
if (i == MAX_VAR)
return -1;
cstat->localvars[i] = alloc_string(varname);
cstat->localvartypes[i] = valtype;
return i;
}
/* predicates for procedure calls */
int
special(const char *token)
{
return (token && !(string_compare(token, ":")
&& string_compare(token, ";")
&& string_compare(token, "IF")
&& string_compare(token, "ELSE")
&& string_compare(token, "THEN")
&& string_compare(token, "BEGIN")
&& string_compare(token, "FOR")
&& string_compare(token, "FOREACH")
&& string_compare(token, "UNTIL")
&& string_compare(token, "WHILE")
&& string_compare(token, "BREAK")
&& string_compare(token, "CONTINUE")
&& string_compare(token, "REPEAT")
&& string_compare(token, "TRY")
&& string_compare(token, "CATCH")
&& string_compare(token, "CATCH_DETAILED")
&& string_compare(token, "ENDCATCH")
&& string_compare(token, "CALL")
&& string_compare(token, "PUBLIC")
&& string_compare(token, "WIZCALL")
&& string_compare(token, "LVAR")
&& string_compare(token, "VAR!")
&& string_compare(token, "VAR")));
}
/* see if procedure call */
int
call(COMPSTATE * cstat, const char *token)
{
struct PROC_LIST *i;
for (i = cstat->procs; i; i = i->next)
if (!string_compare(i->name, token))
return 1;
return 0;
}
/* see if it's a quoted procedure name */
int
quoted(COMPSTATE * cstat, const char *token)
{
return (*token == '\'' && call(cstat, token + 1));
}
/* see if it's an object # */
int
object(const char *token)
{
if (*token == NUMBER_TOKEN && number(token + 1))
return 1;
else
return 0;
}
/* see if string */
int
string(const char *token)
{
return (token[0] == '"');
}
int
variable(COMPSTATE * cstat, const char *token)
{
int i;
for (i = 0; i < MAX_VAR && cstat->variables[i]; i++)
if (!string_compare(token, cstat->variables[i]))
return 1;
return 0;
}
int
scopedvar(COMPSTATE * cstat, const char *token)
{
int i;
for (i = 0; i < MAX_VAR && cstat->scopedvars[i]; i++)
if (!string_compare(token, cstat->scopedvars[i]))
return 1;
return 0;
}
int
localvar(COMPSTATE * cstat, const char *token)
{
int i;
for (i = 0; i < MAX_VAR && cstat->localvars[i]; i++)
if (!string_compare(token, cstat->localvars[i]))
return 1;
return 0;
}
/* see if token is primitive */
int
primitive(const char *token)
{
int primnum;
primnum = get_primitive(token);
return (primnum && primnum <= BASE_MAX - PRIMS_INTERNAL_CNT);
}
/* return primitive instruction */
int
get_primitive(const char *token)
{
hash_data *hd;
if ((hd = find_hash(token, primitive_list, COMP_HASH_SIZE)) == NULL)
return 0;
else {
return (hd->ival);
}
}
/* clean up as nicely as we can. */
void
cleanpubs(struct publics *mypub)
{
struct publics *tmppub;
while (mypub) {
tmppub = mypub->next;
free(mypub->subname);
free(mypub);
mypub = tmppub;
}
}
void
clean_mcpbinds(struct mcp_binding *mypub)
{
struct mcp_binding *tmppub;
while (mypub) {
tmppub = mypub->next;
free(mypub->pkgname);
free(mypub->msgname);
free(mypub);
mypub = tmppub;
}
}
void
append_intermediate_chain(struct INTERMEDIATE *chain, struct INTERMEDIATE *add)
{
while (chain->next)
chain = chain->next;
chain->next = add;
}
void
free_intermediate_node(struct INTERMEDIATE *wd)
{
int varcnt, j;
if (wd->in.type == PROG_STRING) {
if (wd->in.data.string)
free((void *) wd->in.data.string);
}
if (wd->in.type == PROG_FUNCTION) {
free((void*)wd->in.data.mufproc->procname);
varcnt = wd->in.data.mufproc->vars;
if (wd->in.data.mufproc->varnames) {
for (j = 0; j < varcnt; j++) {
free((void*)wd->in.data.mufproc->varnames[j]);
}
free((void*)wd->in.data.mufproc->varnames);
}
free((void*)wd->in.data.mufproc);
}
free((void *) wd);
}
void
free_intermediate_chain(struct INTERMEDIATE *wd)
{
struct INTERMEDIATE* tempword;
while (wd) {
tempword = wd->next;
free_intermediate_node(wd);
wd = tempword;
}
}
void
cleanup(COMPSTATE * cstat)
{
/* struct INTERMEDIATE *wd, *tempword; */
struct CONTROL_STACK *eef, *tempif;
struct PROC_LIST *p, *tempp;
int i;
free_intermediate_chain(cstat->first_word);
cstat->first_word = 0;
for (eef = cstat->control_stack; eef; eef = tempif) {
tempif = eef->next;
free((void *) eef);
}
cstat->control_stack = 0;
for (p = cstat->procs; p; p = tempp) {
tempp = p->next;
free((void *) p->name);
free((void *) p);
}
cstat->procs = 0;
purge_defs(cstat);
free_addresses(cstat);
for (i = RES_VAR; i < MAX_VAR && cstat->variables[i]; i++) {
free((void *) cstat->variables[i]);
cstat->variables[i] = 0;
}
for (i = 0; i < MAX_VAR && cstat->scopedvars[i]; i++) {
free((void *) cstat->scopedvars[i]);
cstat->scopedvars[i] = 0;
}
for (i = 0; i < MAX_VAR && cstat->localvars[i]; i++) {
free((void *) cstat->localvars[i]);
cstat->localvars[i] = 0;
}
}
/* copy program to an array */
void
copy_program(COMPSTATE * cstat)
{
/*
* Everything should be peachy keen now, so we don't do any error checking
*/
struct INTERMEDIATE *curr;
struct inst *code;
int i, j, varcnt;
if (!cstat->first_word)
v_abort_compile(cstat, "Nothing to compile.");
code = (struct inst *) malloc(sizeof(struct inst) * (cstat->nowords + 1));
i = 0;
for (curr = cstat->first_word; curr; curr = curr->next) {
code[i].type = curr->in.type;
code[i].line = curr->in.line;
switch (code[i].type) {
case PROG_PRIMITIVE:
case PROG_INTEGER:
case PROG_SVAR:
case PROG_SVAR_AT:
case PROG_SVAR_AT_CLEAR:
case PROG_SVAR_BANG:
case PROG_LVAR:
case PROG_LVAR_AT:
case PROG_LVAR_AT_CLEAR:
case PROG_LVAR_BANG:
case PROG_VAR:
code[i].data.number = curr->in.data.number;
break;
case PROG_FLOAT:
code[i].data.fnumber = curr->in.data.fnumber;
break;
case PROG_STRING:
code[i].data.string = curr->in.data.string ?
alloc_prog_string(curr->in.data.string->data) : 0;
break;
case PROG_FUNCTION:
code[i].data.mufproc = (struct muf_proc_data*)malloc(sizeof(struct muf_proc_data));
code[i].data.mufproc->procname = string_dup(curr->in.data.mufproc->procname);
code[i].data.mufproc->vars = varcnt = curr->in.data.mufproc->vars;
code[i].data.mufproc->args = curr->in.data.mufproc->args;
if (varcnt) {
if (curr->in.data.mufproc->varnames) {
code[i].data.mufproc->varnames = (const char**)calloc(varcnt, sizeof(char*));
for (j = 0; j < varcnt; j++) {
code[i].data.mufproc->varnames[j] = string_dup(curr->in.data.mufproc->varnames[j]);
}
} else {
code[i].data.mufproc->varnames = NULL;
}
} else {
code[i].data.mufproc->varnames = NULL;
}
break;
case PROG_OBJECT:
code[i].data.objref = curr->in.data.objref;
break;
case PROG_ADD:
code[i].data.addr = alloc_addr(cstat, curr->in.data.number, code);
break;
case PROG_IF:
case PROG_JMP:
case PROG_EXEC:
case PROG_TRY:
code[i].data.call = code + curr->in.data.number;
break;
default:
v_abort_compile(cstat, "Unknown type compile! Internal error.");
break;
}
i++;
}
PROGRAM_SET_CODE(cstat->program, code);
}
void
set_start(COMPSTATE * cstat)
{
PROGRAM_SET_SIZ(cstat->program, cstat->nowords);
/* address instr no is resolved before this gets called. */
PROGRAM_SET_START(cstat->program, (PROGRAM_CODE(cstat->program) + cstat->procs->code->no));
}
/* allocate and initialize data linked structure. */
struct INTERMEDIATE *
alloc_inst(void)
{
struct INTERMEDIATE *nu;
nu = (struct INTERMEDIATE *) malloc(sizeof(struct INTERMEDIATE));
nu->next = 0;
nu->no = 0;
nu->line = 0;
nu->flags = 0;
nu->in.type = 0;
nu->in.line = 0;
nu->in.data.number = 0;
return nu;
}
struct INTERMEDIATE *
prealloc_inst(COMPSTATE * cstat)
{
struct INTERMEDIATE *ptr;
struct INTERMEDIATE *nu;
/* only allocate at most one extra instr */
if (cstat->nextinst)
return NULL;
nu = alloc_inst();
nu->flags |= (cstat->nested_trys > 0) ? INTMEDFLG_INTRY : 0;
if (!cstat->nextinst) {
cstat->nextinst = nu;
} else {
for (ptr = cstat->nextinst; ptr->next; ptr = ptr->next);
ptr->next = nu;
}
nu->no = cstat->nowords;
return nu;
}
struct INTERMEDIATE *
new_inst(COMPSTATE * cstat)
{
struct INTERMEDIATE *nu;
nu = cstat->nextinst;
if (!nu) {
nu = alloc_inst();
}
cstat->nextinst = nu->next;
nu->next = NULL;
nu->flags |= (cstat->nested_trys > 0) ? INTMEDFLG_INTRY : 0;
return nu;
}
/* allocate an address */
struct prog_addr *
alloc_addr(COMPSTATE * cstat, int offset, struct inst *codestart)
{
struct prog_addr *nu;
nu = (struct prog_addr *) malloc(sizeof(struct prog_addr));
nu->links = 1;
nu->progref = cstat->program;
nu->data = codestart + offset;
return nu;
}
void
free_prog_real(dbref prog, const char *file, const int line)
{
int i;
struct inst *c = PROGRAM_CODE(prog);
int siz = PROGRAM_SIZ(prog);
if (c) {
if (PROGRAM_INSTANCES(prog)) {
fprintf(stderr, "Freeing program %s with %d instances reported from %s:%d\n",
unparse_object(GOD, prog), PROGRAM_INSTANCES(prog),file,line);
}
i = scan_instances(prog);
if (i) {
fprintf(stderr, "Freeing program %s with %d instances found from %s:%d\n",
unparse_object(GOD, prog), i,file,line);
}
for (i = 0; i < siz; i++) {
if (c[i].type == PROG_ADD) {
if (c[i].data.addr->links != 1) {
fprintf(stderr, "Freeing address in %s with link count %d from %s:%d\n",
unparse_object(GOD, prog), c[i].data.addr->links,file,line);
}
free(c[i].data.addr);
}
else
{
CLEAR(c + i);
}
}
free((void *) c);
}
PROGRAM_SET_CODE(prog, 0);
PROGRAM_SET_SIZ(prog, 0);
PROGRAM_SET_START(prog, 0);
}
long
size_prog(dbref prog)
{
struct inst *c;
long i, j, varcnt, siz, byts = 0;
c = PROGRAM_CODE(prog);
if (!c)
return 0;
siz = PROGRAM_SIZ(prog);
for (i = 0L; i < siz; i++) {
byts += sizeof(*c);
if (c[i].type == PROG_FUNCTION) {
byts += strlen(c[i].data.mufproc->procname) + 1;
varcnt = c[i].data.mufproc->vars;
if (c[i].data.mufproc->varnames) {
for (j = 0; j < varcnt; j++) {
byts += strlen(c[i].data.mufproc->varnames[j]) + 1;
}
byts += sizeof(char**) * varcnt;
}
byts += sizeof(struct muf_proc_data);
} else if (c[i].type == PROG_STRING && c[i].data.string) {
byts += strlen(c[i].data.string->data) + 1;
byts += sizeof(struct shared_string);
} else if ((c[i].type == PROG_ADD))
byts += sizeof(struct prog_addr);
}
byts += size_pubs(PROGRAM_PUBS(prog));
return byts;
}
static void
add_primitive(int val)
{
hash_data hd;
hd.ival = val;
if (add_hash(base_inst[val - BASE_MIN], hd, primitive_list, COMP_HASH_SIZE) == NULL)
panic("Out of memory");
else
return;
}
void
clear_primitives(void)
{
kill_hash(primitive_list, COMP_HASH_SIZE, 0);
return;
}
void
init_primitives(void)
{
int i;
clear_primitives();
for (i = BASE_MIN; i <= BASE_MAX; i++) {
add_primitive(i);
}
IN_FORPOP = get_primitive(" FORPOP");
IN_FORITER = get_primitive(" FORITER");
IN_FOR = get_primitive(" FOR");
IN_FOREACH = get_primitive(" FOREACH");
IN_TRYPOP = get_primitive(" TRYPOP");
log_status("MUF: %d primitives exist.\n", BASE_MAX);
}