#include <ctype.h> #include <stdio.h> #include "copyright.h" #include "config.h" #include "version.h" #include "db.h" #include "interface.h" #include "inst.h" #include "externs.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 IF_STACK is a stack for holding previous IF statements. Everytime a THEN is encountered, the next address is inserted into the code before the most recent IF. */ /* Of course I modified the crap out of it to handle for, do, and while loops. now the type field holds a SORCE_whatever to tell where the loop came from, and LOOP parses accordingly to tidy up --Doran*/ /* Also, if you change any primitive names, you need to look through the 'name dependency' and change those accordingly. */ /* name dependancy *//* not all are used */ #define IF_NAME "IF" #define CALL_NAME "CALL" #define READ_NAME "READ" #define EXIT_NAME "EXIT" #define JMP_NAME "JMP" #define PROGRAM_NAME "PROGRAM " #define EXECUTE_NAME "EXECUTE" #define SLEEP_NAME "SLEEP" #define VAR_NAME "VAR" #define LOOP_NAME "LOOP" #define NOP_NAME "NOP" #define FOR_CHECK_NAME "FOR_CHECK " #define FOR_ADD_NAME "FOR_ADD " #define FOR_POP_NAME "FOR_POP " static hash_tab primitive_list[COMP_HASH_SIZE]; /* #defines for types of addresses shoved on the if stack */ #define SOURCE_ERROR -1 #define SOURCE_IF 0 #define SOURCE_FOR 1 #define SOURCE_DO 2 #define SOURCE_WHILE_ALPHA 3 #define SOURCE_WHILE_BETA 4 /* 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. */ typedef struct intermediate { int no; /* which number instruction this is */ inst in; /* instruction itself */ struct intermediate *next; /* next instruction */ } intermediate; typedef struct if_stack { int source; intermediate *place; struct if_stack *next; } if_stack; if_stack *ifs; /* 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. */ typedef struct proc_list { char *name; intermediate *code; struct proc_list *next; } proc_list; proc_list *procs; static int nowords; /* number of words compiled */ static intermediate *curr_word; /* word currently being compiled */ static intermediate *first_word; /* first word of the list */ static intermediate *curr_proc; /* first word of current procedure */ /* variable names. The index into variables give you what position * the variable holds. */ static char *variables[MAX_VAR] = { "ME", "LOC", "TRIGGER" }; static line *curr_line; /* current line */ static int lineno; /* current line number */ static char *next_char; /* next char * */ static dbref player, program; /* globalized player and program */ /* 1 if error occured */ static int compile_err; int primitive(char *s); /* returns primitive_number if primitive */ void advance_line(); void free_prog(inst *, int); char *next_token(); char *next_token_raw(); intermediate *next_word(char *); intermediate *process_special(char *); intermediate *primitive_word(char *); intermediate *string_word(char *); intermediate *number_word(char *); intermediate *floating_word(char *); intermediate *object_word(char *); intermediate *quoted_word(char *); intermediate *call_word(char *); intermediate *var_word(char *); char *do_string(); void do_comment(); intermediate *new_inst(); intermediate *find_if(); void cleanup(); void add_proc(char *, intermediate *); void addif(intermediate *, int from); int query_if(); int for_nest(); int add_variable(char *); int special(char *); int call(char *); int quoted(char *); int object_check(char *); int string(char *); int variable(char *); int get_primitive(char *); void copy_program(); void set_start(); char *line_copy = NULL; int macrosubs; /* Safeguard for macro-subst. infinite loops */ /* 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? */ /* abort compile macro */ #define abort_compile(C) \ { \ char _buf[BUFFER_LEN]; \ sprintf(_buf, "Error in line %d: %s", lineno, C); \ if (line_copy) \ { \ free (line_copy); \ line_copy = NULL; \ } \ if (player != NOTHING) notify(player, player, _buf); \ else \ log_muf("MUF compiler warning in program %ld:\n%s\n", program, _buf); \ cleanup(); \ compile_err++; \ free_prog(DBFETCH(program)->sp.program.code, \ DBFETCH(program)->sp.program.siz); \ return 0; \ } /* for void functions */ #define v_abort_compile(C) \ { \ char _buf[BUFFER_LEN]; \ sprintf(_buf, "Error in line %d: %s", lineno, C); \ if (line_copy) \ { \ free (line_copy); \ line_copy = NULL; \ } \ if (player != NOTHING) notify(player, player, _buf); \ else\ log_muf("MUF compiler warning in program %ld:\n%s\n", program, _buf); \ cleanup(); \ compile_err++; \ free_prog(DBFETCH(program)->sp.program.code, \ DBFETCH(program)->sp.program.siz); \ return; \ } extern frame *frame_list; /* returns true for numbers of form [x.x] <series of digits> */ int floating(char *s) { if (!s) return 0; while (isspace(*s)) s++; if (*s == '+' || *s == '-') s++; if ((*s < '0' || *s > '9') || !index(s, '.')) return 0; return 1; } #ifdef PREP char *alloc_string(char *string1); char *expand_def(char *defname); void kill_def(char *defname); void insert_def(char *defname, char *deff); void purge_defs(void); void include_defs(dbref i); void init_defs(void); void do_directive(char *direct); char *alloc_string(char *string1) { char *s; /* NULL, "" -> NULL */ if (string1 == 0 || *string1 == '\0') return 0; if ((s = (char *) malloc(strlen(string1) + 1)) == 0) { abort(); } strcpy(s, string1); return s; } char *envpropstr(dbref *where, char *propname) { char *temp; while (*where != NOTHING) { temp = (char *) get_property_data(*where, propname, ACCESS_OT); if (temp && *temp) return temp; *where = DBFETCH(*where)->location; } return NULL; } void do_directive(char *direct) /* handle compiler directives */ { char temp[BUFFER_LEN]; char *tmpname; char *tmpptr; int i = 0; int j; strcpy(temp, ++direct); if (!(temp[0])) { v_abort_compile("I don't understand that compiler directive!"); } if (!string_compare(temp, "define")) { tmpname = (char *) next_token_raw(); if (!tmpname) v_abort_compile("Unexpected end of file looking for $define name."); i = 0; while ((tmpptr = (char *) next_token_raw()) && (string_compare(tmpptr, "$enddef"))) { strcpy((&temp[i]), tmpptr); i += strlen(&temp[i]); if (*tmpptr == BEGINSTRING) temp[i++] = ENDSTRING; temp[i++] = ' '; free(tmpptr); if (i > (BUFFER_LEN / 2)) v_abort_compile("$define definition too long."); } temp[--i] = '\0'; if (!tmpptr) v_abort_compile("Unexpected end of file in $define definition."); free(tmpptr); (void) insert_def(tmpname, temp); free(tmpname); } else if (!string_compare(temp, "enddef")) { v_abort_compile("$enddef without a previous matching $define."); } else if (!string_compare(temp, "def")) { tmpname = (char *) next_token_raw(); if (!tmpname) v_abort_compile("Unexpected end of file looking for $define name."); (void) insert_def(tmpname, next_char); while (*next_char) next_char++; advance_line(); free(tmpname); } else if (!string_compare(temp, "include")) { struct match_data md; tmpname = (char *) next_token_raw(); if (!tmpname) v_abort_compile("Unexpected end of file while doing $include."); { char tempa[BUFFER_LEN], tempb[BUFFER_LEN]; strcpy(tempa, match_args); strcpy(tempb, match_cmdname); init_match(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("I don't understand what object you want to $include."); include_defs((dbref) i); } else if (!string_compare(temp, "undef")) { tmpname = (char *) next_token_raw(); if (!tmpname) v_abort_compile("Unexpected end of file looking for name to $undef."); kill_def(tmpname); free(tmpname); } else if (!string_compare(temp, "echo")) { tmpname = (char *) next_token(); if (!tmpname) v_abort_compile("Unexpected end of file looking for string to $echo."); notify(player, player, tmpname); free(tmpname); } else if (!string_compare(temp, "ifdef")) { tmpname = (char *) next_token_raw(); if (!tmpname) v_abort_compile("Unexpected end of file looking for $ifdef condition."); 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(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 (j) { i = 0; while ((tmpptr = (char *) next_token_raw()) && (i || ((string_compare(tmpptr, "$else")) && (string_compare(tmpptr, "$endif"))))) { if (!string_compare(tmpptr, "$ifdef")) i++; else if (!string_compare(tmpptr, "$ifndef")) i++; else if (!string_compare(tmpptr, "$endif")) i--; } if (!tmpptr) { v_abort_compile("Unexpected end of file in $ifdef clause."); } free(tmpptr); } } else if (!string_compare(temp, "ifndef")) { tmpname = (char *) next_token_raw(); if (!tmpname) { v_abort_compile("Unexpected end of file looking for $ifndef condition."); } 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(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 (!j) { i = 0; while ((tmpptr = (char *) next_token_raw()) && (i || ((string_compare(tmpptr, "$else")) && (string_compare(tmpptr, "$endif"))))) { if (!string_compare(tmpptr, "$ifdef")) i++; else if (!string_compare(tmpptr, "$ifndef")) i++; else if (!string_compare(tmpptr, "$endif")) i--; } if (!tmpptr) { v_abort_compile("Unexpected end of file in $ifndef clause."); } free(tmpptr); } } else if (!string_compare(temp, "else")) { i = 0; while ((tmpptr = (char *) next_token_raw()) && (i || (string_compare(tmpptr, "$endif")))) { if (!string_compare(tmpptr, "$ifdef")) i++; else if (!string_compare(tmpptr, "$ifndef")) i++; else if (!string_compare(tmpptr, "$endif")) i--; free(tmpptr); } if (!tmpptr) { v_abort_compile("Unexpected end of file in $else clause."); } free(tmpptr); } else if (!string_compare(temp, "endif")) { } else { v_abort_compile("Unrecognized compiler directive."); } } #define DEFHASHSIZE (256) /* Table for compiler $defines */ static hash_tab defhash[DEFHASHSIZE]; char *expand_def(char *defname) { hash_data *exp = find_hash(defname, defhash, DEFHASHSIZE); if (!exp) { if (*defname == BEGINMACRO) { return ((char *) macro_expansion(macrotop, &defname[1])); } else { return (NULL); } } return (alloc_string((char *) exp->pval)); } void kill_def(char *defname) { hash_data *exp = find_hash(defname, defhash, DEFHASHSIZE); if (exp) { free(exp->pval); (void) free_hash(defname, defhash, DEFHASHSIZE); } } void insert_def(char *defname, char *deff) { hash_data hd; (void) kill_def(defname); hd.pval = (void *) alloc_string(deff); (void) add_hash(defname, hd, defhash, DEFHASHSIZE); } void purge_defs(void) { kill_hash(defhash, DEFHASHSIZE, 1); } void include_defs(dbref i) { propdir *ptr; char *one, *two; ptr = find_property(i, "_defs", ACCESS_OT); if(ptr && ptr->child) { for(ptr = ptr->child; ptr; ptr = ptr->next) { if(ptr->data) { one = uncompress(ptr->name); two = uncompress(ptr->data); insert_def(one, two); } } } } void init_defs(void) { /* What version of the server is this? */ (void) insert_def((char *) "__version", (char *) VERSION); /* make defines for compatability to removed primitives */ /* (void) insert_def((char *) "desc", (char *) "\"_/de\" getpropstr"); (void) insert_def((char *) "succ", (char *) "\"_/sc\" getpropstr"); (void) insert_def((char *) "fail", (char *) "\"_/fl\" getpropstr"); (void) insert_def((char *) "drop", (char *) "\"_/dr\" getpropstr"); (void) insert_def((char *) "osucc", (char *) "\"_/osc\" getpropstr"); (void) insert_def((char *) "ofail", (char *) "\"_/ofl\" getpropstr"); (void) insert_def((char *) "odrop", (char *) "\"_/odr\" getpropstr"); (void) insert_def((char *) "setdesc", (char *) "\"_/de\" swap 0 addprop"); (void) insert_def((char *) "setsucc", (char *) "\"_/sc\" swap 0 addprop"); (void) insert_def((char *) "setfail", (char *) "\"_/fl\" swap 0 addprop"); (void) insert_def((char *) "setdrop", (char *) "\"_/dr\" swap 0 addprop"); (void) insert_def((char *) "setosucc", (char *) "\"_/osc\" swap 0 addprop"); (void) insert_def((char *) "setofail", (char *) "\"_/ofl\" swap 0 addprop"); (void) insert_def((char *) "setodrop", (char *) "\"_/odr\" swap 0 addprop"); (void) insert_def((char *) "notify_except", (char *) "1 swap notify_exclude"); */ /* Create standard server defines */ (void) insert_def((char *) "strip", (char *) "striplead striptail"); (void) insert_def((char *) "background", (char *) "0 sleep"); (void) insert_def((char *) "repeat", (char *) "loop"); (void) insert_def((char *) "instring", (char *) "tolower swap tolower swap instr"); (void) insert_def((char *) "rinstring", (char *) "tolower swap tolower swap rinstr"); /* include any defines set in #0's _defs/ propdir. */ include_defs((dbref) 0); } #endif /* PREP */ /* overall control code. Does piece-meal tokenization parsing and backward checking. */ void do_compile(dbref player_in, dbref program_in) { char *token; char buf[BUFFER_LEN]; intermediate *new_word; sprintf (buf, "Program %s has been recompiled by %s.", unparse_name(program_in), unparse_name(player_in)); bump_frames(buf, program_in, player_in); /* set all global variables */ #ifdef PREP init_defs(); #endif nowords = 0; curr_word = first_word = curr_proc = 0; player = player_in; program = program_in; lineno = 1; curr_line = DBFETCH(program)->sp.program.first; if (curr_line) next_char = curr_line -> this_line; first_word = curr_word = NULL; procs = 0; compile_err = 0; ifs = 0; /* free old stuff */ free_prog(DBFETCH(program)->sp.program.code, DBFETCH(program)->sp.program.siz); if (!curr_line) v_abort_compile("Missing program text."); /* do compilation */ while ((token = next_token())) { new_word = next_word(token); /* test for errors */ if (compile_err) return; if (new_word) { if (!first_word) first_word = curr_word = new_word; else { curr_word -> next = new_word; curr_word = curr_word -> next; } } while (curr_word && curr_word -> next) curr_word = curr_word -> next; free(token); } if (curr_proc) v_abort_compile("Unexpected end of file."); if (!procs) v_abort_compile("Missing procedure definition."); /* do copying over */ copy_program(); if (compile_err) return; set_start(); cleanup(); } intermediate *next_word(char *token) { intermediate *new_word; char buf[BUFFER_LEN]; if (!token) return 0; if (special(token)) new_word = process_special(token); else if (variable(token)) new_word = var_word(token); else if (primitive(token)) new_word = primitive_word(token); else if (string(token)) new_word = string_word(token + 1); else if (floating(token)) new_word = floating_word(token); else if (number(token)) new_word = number_word(token); else if (object_check(token)) new_word = object_word(token); else if (quoted(token)) new_word = quoted_word(token + 1); else if (call(token)) new_word = call_word(token); else { sprintf(buf, "Unrecognized word %s.", token); abort_compile(buf); } if (new_word) new_word -> in.linenum = lineno; return new_word; } /* Little routine to do the line_copy handling right */ void advance_line() { curr_line = curr_line -> next; lineno++; macrosubs = 0; if (line_copy) { free (line_copy); line_copy = NULL; } if (curr_line) next_char = (line_copy = dup_string(curr_line -> this_line)); else next_char = (line_copy = NULL); } #ifdef PREP /* Skips comments, grabs strings, returns NULL when no more tokens to grab. */ char *next_token_raw() { static char buf[BUFFER_LEN]; int i; if (!curr_line) return (char *) 0; if (!next_char) return (char *) 0; /* skip white space */ while (*next_char && isspace(*next_char)) next_char++; if (!(*next_char)) { advance_line(); return next_token_raw(); } /* take care of comments */ if (*next_char == BEGINCOMMENT) { do_comment(); return next_token_raw(); } if (*next_char == BEGINSTRING) return do_string(); for (i = 0; *next_char && !isspace(*next_char); i++) { buf[i] = *next_char; next_char++; } buf[i] = '\0'; return alloc_string(buf); } char *next_token() { static char *expansion, *temp; temp = (char *) next_token_raw(); if (!temp) return NULL; if (temp[0] == BEGINDIRECTIVE) { do_directive(temp); free(temp); return next_token(); } if (temp[0] == BEGINESCAPE) { if (temp[1]) { return (++temp); } else { return (temp); } } if ((expansion = expand_def(temp))) { free(temp); if (++macrosubs > SUBSTITUTIONS) { abort_compile("Too many macro substitutions."); } else { temp = (char *) malloc(strlen(next_char) + strlen(expansion) + 21); strcpy(temp, expansion); strcat(temp, next_char); free((void *) expansion); if (line_copy) { free((void *) line_copy); } next_char = line_copy = temp; return next_token(); } } else { return (temp); } } #else /* Skips comments, grabs strings, returns NULL when no more tokens to grab. */ char * next_token() { char buf[BUFFER_LEN]; char *expansion, *temp; int i; if (!curr_line) return (char *) 0; if (!next_char) return (char *) 0; /* skip white space */ while (*next_char && isspace(*next_char)) next_char++; if (!(*next_char)) { advance_line(); if (!curr_line) return (char *) 0; else return next_token(); } /* take care of comments */ if (*next_char == BEGINCOMMENT) { do_comment(); return next_token(); } if (*next_char == BEGINSTRING) return do_string(); /* macro */ if (*next_char == BEGINMACRO) { next_char++; for (i = 0; *next_char && !isspace(*next_char); i++) { buf[i] = *next_char; next_char++; } buf[i] = '\0'; if (!(expansion = (char *)macro_expansion(macrotop, buf))) { abort_compile ("Macro not defined."); } else { if (++macrosubs > SUBSTITUTIONS) { abort_compile ("Too many macro substitutions."); } else { temp = (char *) malloc(strlen(next_char) + strlen(expansion) + 21); strcpy(temp, expansion); strcat(temp, next_char); free (expansion); free (line_copy); next_char = line_copy = temp; return next_token(); } } } /* ordinary token */ for (i = 0; *next_char && !isspace(*next_char); i++) { buf[i] = *next_char; next_char++; } buf[i] = '\0'; return dup_string(buf); } #endif /* PREP */ /* skip comments */ void do_comment() { while (*next_char && *next_char != ENDCOMMENT) next_char++; if (!(*next_char)) { advance_line(); if (!curr_line) { v_abort_compile("Unterminated comment."); } do_comment(); } else { next_char++; if (!(*next_char)) advance_line(); } } /* return string */ char * do_string() { char buf[BUFFER_LEN]; int i = 0, quoted1 = 0; buf[i] = *next_char; next_char++; i++; while ((quoted1 || *next_char != ENDSTRING) && *next_char) if (*next_char == '\\' && !quoted1) { quoted1++; next_char++; } else { buf[i] = *next_char; i++; next_char++; quoted1 = 0; } if (!*next_char) { abort_compile("Unterminated string found at end of line."); } next_char++; buf[i] = '\0'; return dup_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. */ intermediate * process_special(char *token) { char buf[BUFFER_LEN]; char *tok; intermediate *new; if (!string_compare(token, ":")) { char *proc_name; if (curr_proc) abort_compile("Definition within definition."); proc_name = next_token(); if (!proc_name) abort_compile("Unexpected end of file within procedure."); tok = next_token(); new = next_word(tok); if (tok) free(tok); if (!new) { sprintf(buf, "Error in definition of %s.", proc_name); free(proc_name); abort_compile(buf); } curr_proc = new; add_proc(proc_name, new); free(proc_name); return new; } else if (!string_compare(token, ";")) { if (ifs) abort_compile("Unexpected end of procedure definition."); if (!curr_proc) abort_compile("Procedure end without body."); curr_proc = 0; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(EXIT_NAME); /* name dependency */ return new; } else if (!string_compare(token, "IF")) { intermediate *curr; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; new -> in.data.call = 0; new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(IF_NAME); /* name dependency */ addif(new, SOURCE_IF); return new; } else if (!string_compare(token, "ELSE")) { intermediate *eef; intermediate *curr; intermediate *after; eef = find_if(); if (!eef) abort_compile("ELSE without IF."); new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; new -> in.data.call = 0; new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(JMP_NAME); /* name dependency */ addif(new, SOURCE_IF); /* treated as if when next then comes up */ tok = next_token(); curr -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); eef -> in.data.number = after -> no; return new; } else if (!string_compare(token, "THEN")) { /* can't use 'if' because it's a reserved word */ intermediate *eef; if (query_if() != SOURCE_IF) abort_compile("THEN improperly nested."); eef = find_if(); if (!eef) abort_compile("THEN without IF."); tok = next_token(); new = next_word(tok); if (tok) free(tok); if (!new) abort_compile("Unexpected end of program."); eef -> in.data.number = new -> no; return new; } else if (!string_compare(token, "DO")) { intermediate *after; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(NOP_NAME); /* name dependency */ addif(new, SOURCE_DO); tok = next_token(); new -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); return new; } else if (!string_compare(token, "BEGIN")) { intermediate *after; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(NOP_NAME); /* name dependency */ addif(new, SOURCE_WHILE_ALPHA); tok = next_token(); new -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); return new; } else if (!string_compare(token, "WHILE")) { intermediate *curr; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; new -> in.data.call = 0; new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(IF_NAME); /* name dependency */ addif(new, SOURCE_WHILE_BETA); return new; } else if (!string_compare(token, "FOR")) { intermediate *after; intermediate *curr; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(FOR_ADD_NAME); /* name dependency */ new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(NOP_NAME); /* name dependency */ addif(curr, SOURCE_FOR); tok = next_token(); curr -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); return new; } else if (!string_compare(token, "LOOP")) { /* can't use 'if' because it's a reserved word */ intermediate *eef; intermediate *wheel; intermediate *curr; intermediate *after; if (query_if() == SOURCE_IF || query_if() == SOURCE_ERROR) abort_compile("LOOP improperly nested."); switch(query_if()) { case SOURCE_WHILE_BETA: eef = find_if(); if (query_if() != SOURCE_WHILE_ALPHA) abort_compile("Improperly nested loop in conditional of WHILE."); wheel = find_if(); if (!wheel) abort_compile("WHILE ... LOOP without BEGIN."); new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; new -> in.data.number = wheel -> no; new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(JMP_NAME); /* name dependency */ tok = next_token(); curr -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); eef -> in.data.number = new -> no + 2; break; case SOURCE_DO: eef = find_if(); new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; new -> in.data.number = eef -> no; new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(LOOP_NAME); /* name dependency */ tok = next_token(); curr -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); break; case SOURCE_FOR: eef = find_if(); new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(FOR_CHECK_NAME); new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_ADD; curr -> in.data.number = eef -> no; curr -> next = new_inst(); curr = curr -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(LOOP_NAME); /* name dependency */ tok = next_token(); curr -> next = after = next_word(tok); if (tok) free(tok); if (!after) abort_compile("Unexpected end of program."); break; case SOURCE_WHILE_ALPHA: abort_compile("WHILE statement missing."); break; default: { char BUF[80]; sprintf(BUF, "Unexpected IF_STACK type: %d.", query_if()); abort_compile(BUF); } break; } return new; } #ifdef BREAK_CONTINUE else if (!string_compare(token, "BREAK")) { /* can't use 'if' because it's a reserved word */ intermediate *eef; intermediate *curr; eef = find_if(); if (!eef) abort_compile("Can't have a BREAK outside of a loop."); new = new_inst(); new->no = nowords++; new->in.type = PROG_ADD; /* new->in.line = lineno; */ new->in.data.number = 0; new->next = new_inst(); curr = new->next; curr->no = nowords++; curr->in.type = PROG_PRIMITIVE; /* curr->in.line = lineno; */ curr->in.data.number = IN_JMP; /* addwhile(new); */ addif(new, SOURCE_WHILE_BETA); return new; } else if (!string_compare(token, "CONTINUE")) { /* can't use 'if' because it's a reserved word */ intermediate *beef; intermediate *curr; beef = find_if(); if (!beef) abort_compile("Can't CONTINUE outside of a loop."); new = new_inst(); new->no = nowords++; new->in.type = PROG_ADD; /* new->in.line = lineno; */ new->in.data.number = beef->no; new->next = new_inst(); curr = new->next; curr->no = nowords++; curr->in.type = PROG_PRIMITIVE; /* curr->in.line = lineno; */ curr->in.data.number = IN_JMP; return new; } #endif /* PREP */ else if (!string_compare(token, "CALL")) { intermediate *curr; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(CALL_NAME); /* name dependency */ new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_OBJECT; curr -> in.data.objref = program; curr -> next = new_inst(); curr = curr -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(PROGRAM_NAME); /* name dependency */ return new; } else if (!string_compare(token, "EXIT")) { intermediate *curr; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(EXIT_NAME); /* name dependency */ if (for_nest() != 0) { new -> in.type = PROG_INTEGER; new -> in.data.number = for_nest(); new -> next = new_inst(); curr = new -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(FOR_POP_NAME); /* name dependency */ curr -> next = new_inst(); curr = curr -> next; curr -> no = nowords++; curr -> in.type = PROG_PRIMITIVE; curr -> in.data.number = get_primitive(EXIT_NAME); /* name dependency */ } return new; } #ifdef PUBLIC else if (!string_compare(token, "PUBLIC")) { struct PROC_LIST *p; struct publics *pub; if (curr_proc) abort_compile("Public declaration within procedure."); tok = next_token(); if ((!tok) || !call(tok)) abort_compile("Subroutine unknown in PUBLIC declaration."); for (p = procs; p; p = p->next) if (!string_compare(p->name, tok)) break; if (!p) abort_compile("Subroutine unknown in PUBLIC declaration."); if (!currpubs) { currpubs = (struct publics *) malloc(sizeof(struct publics)); currpubs->next = NULL; currpubs->subname = (char *) strdup(tok); if (tok) free((void *) tok); currpubs->addr.no = p->code->no; return 0; } for (pub = currpubs; pub;) { if (!string_compare(tok, pub->subname)) { abort_compile("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 *) strdup(tok); if (tok) free((void *) tok); pub->addr.no = p->code->no; pub = NULL; } } } return 0; } #endif /* PUBLIC */ else if (!string_compare(token, "VAR")) { if (curr_proc) abort_compile("Variable declared within procedure."); tok = next_token(); if (!tok || !add_variable(tok)) abort_compile("Variable limit exceeded."); if (tok) free(tok); return 0; } else { sprintf(buf, "Unrecognized special form %s found on line %d.", token, lineno); abort_compile(buf); } } /* return primitive word. */ intermediate * primitive_word(char *token) { intermediate *new; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_PRIMITIVE; new -> in.data.number = get_primitive(token); return new; } /* return self pushing word (string) */ intermediate *string_word(char *token) { intermediate *new; new = new_inst(); new->no = nowords++; new->in.type = PROG_STRING; new->in.data.string = dup_string(token); return new; } /* return self pushing word (number) */ intermediate *number_word(char *token) { intermediate *new; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_INTEGER; new -> in.data.number = strtol(token, NULL, 0); return new; } /* return self pushing word (floating) */ intermediate *floating_word(char *token) { intermediate *new; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_FLOAT; new -> in.data.fnum = atof(token); return new; } /* do a subroutine call --- push address onto stack, then make a primitive CALL. */ intermediate *call_word(char *token) { intermediate *new; proc_list *p; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; for (p = procs; p; p = p -> next) if (!string_compare(p -> name, token)) break; new -> in.data.number = p -> code -> no; new -> next = new_inst(); new -> next -> no = nowords++; new -> next -> in.type = PROG_PRIMITIVE; new -> next -> in.data.number = get_primitive(EXECUTE_NAME); /* name dependency */ return new; } intermediate *quoted_word(char *token) { intermediate *new; proc_list *p; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_ADD; for (p = procs; p; p = p -> next) if (!string_compare(p -> name, token)) break; new -> in.data.number = p -> code -> no; return new; } /* returns number corresponding to variable number. We assume that it DOES exist */ intermediate *var_word(char *token) { intermediate *new; int var_no; new = new_inst(); new -> no = nowords++; new -> in.type = PROG_VAR; for (var_no = 0; var_no < MAX_VAR; var_no++) if (!string_compare(token, variables[var_no])) break; new -> in.data.number = var_no; return new; } /* check if object is in database before putting it in */ intermediate *object_word(char *token) { intermediate *new; int objno; objno = atol(token + 1); new = new_inst(); new -> no = nowords++; new -> in.type = PROG_OBJECT; new -> in.data.objref = objno; return new; } /* support routines for internal data structures. */ /* add procedure to procedures list */ void add_proc(char *proc_name, intermediate *place) { proc_list *new; new = (proc_list *) malloc(sizeof(proc_list)); new -> name = dup_string(proc_name); new -> code = place; new -> next = procs; procs = new; } /* add if to if stack */ void addif(intermediate *place, int from) { if_stack *new; new = (if_stack *) malloc(sizeof(if_stack)); new->place = place; new->next = ifs; new->source = from; ifs = new; } /* queries the type of the top element on the if stack */ /* non destructive */ int query_if() { if (!ifs) return SOURCE_ERROR; return(ifs->source); } /* checks nested for depth */ int for_nest() { int result = 0; if_stack *temp = ifs; for(;temp;temp = temp->next) if (temp->source == SOURCE_FOR) result++; return (result); } /* pops topmost if off the stack */ intermediate *find_if() { intermediate *temp; if_stack *tofree; if (!ifs) return 0; temp = ifs->place; tofree = ifs; ifs = ifs->next; free(tofree); return temp; } /* adds variable. Return 0 if no space left */ int add_variable(char *varname) { int i; for (i = RES_VAR; i < MAX_VAR; i++) if (!variables[i]) break; if (i == MAX_VAR) return 0; variables[i] = dup_string(varname); return i; } /* predicates for procedure calls */ int special(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, "CALL") && string_compare(token, "FOR") && string_compare(token, "BEGIN") && string_compare(token, "WHILE") && #ifdef BREAK_CONTINUE string_compare(token, "BREAK") && string_compare(token, "CONTINUE") && #endif string_compare(token, "DO") && string_compare(token, "LOOP") && string_compare(token, "EXIT") && string_compare(token, "VAR"))); } /* see if procedure call */ int call(char *token) { proc_list *i; for (i = 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(char *token) { return ( *token == '\'' && call(token + 1)); } /* see if it's an object # */ int object_check(char *token) { if (*token == '#' && number(token + 1)) return 1; else return 0; } /* see if string */ int string(char *token) { return ( token[0] == '"' ); } int variable(char *token) { int i; for (i = 0; i < MAX_VAR && variables[i]; i++) if (!string_compare(token, variables[i])) return 1; return 0; } /* see if token is primitive */ int primitive(char *token) { return get_primitive(token); } /* return primitive instruction */ int get_primitive(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 cleanup() { intermediate *wd, *tempword; if_stack *eef, *tempif; proc_list *p, *tempp; int i; for (wd = first_word; wd; wd = tempword) { tempword = wd -> next; if (wd -> in.type == PROG_STRING) if (wd -> in.data.string) free(wd->in.data.string); free(wd); } first_word = 0; for (eef = ifs; eef; eef = tempif) { tempif = eef -> next; free(eef); } ifs = NULL; for (p = procs; p; p = tempp) { tempp = p -> next; free(p -> name); free(p); } procs = 0; #ifdef PREP purge_defs(); #endif for (i = RES_VAR; i < MAX_VAR && variables[i]; i++) { free(variables[i]); variables[i] = 0; } } /* copy program to an array */ void copy_program() { /* Everything should be peachy keen now, so we don't do any error checking */ intermediate *curr; inst *code; int i; if (!first_word) v_abort_compile("Nothing to compile."); code = (inst *) malloc(sizeof(inst) * (nowords + 1)); i = 0; for (curr = first_word; curr; curr = curr -> next) { code[i].type = curr -> in.type; code[i].linenum = curr -> in.linenum; switch (code[i].type) { case PROG_PRIMITIVE: case PROG_INTEGER: case PROG_FLOAT: case PROG_VAR: code[i].data.number = curr -> in.data.number; break; case PROG_STRING: code[i].data.string = curr -> in.data.string ? dup_string(curr->in.data.string) : 0; break; case PROG_OBJECT: code[i].data.objref = curr -> in.data.objref; break; case PROG_ADD: code[i].data.call = code + curr -> in.data.number; break; default: v_abort_compile("Unknown type compile! Internal error."); break; } i++; } DBSTORE(program, sp.program.code, code); } void set_start() { DBSTORE(program, sp.program.siz, nowords); DBSTORE(program, sp.program.start, (DBFETCH(program)->sp.program.code + procs -> code -> no)); } /* allocate and initialize data linked structure. */ intermediate * new_inst() { intermediate *new; new = (intermediate *) malloc(sizeof (intermediate)); new -> next = 0; new -> no = 0; new -> in.type = 0; new -> in.linenum = 0; new -> in.data.number = 0; return new; } void free_prog(inst *c, int siz) { int i; for (i = 0; i < siz; i++) if (c[i].type == PROG_STRING && c[i].data.string) free(c[i].data.string); if (c) free(c); DBSTORE(program, sp.program.code, 0); DBSTORE(program, sp.program.siz, 0); } 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() { kill_hash(primitive_list, COMP_HASH_SIZE, 0); return; } void init_primitives() { int i; fprintf(stderr, "Initializing primitives %d thru %d\n", BASE_MIN, BASE_MAX); clear_primitives(); for (i = BASE_MIN; i <= BASE_MAX; i++) { add_primitive(i); #ifdef NOISY_PRIMS fprintf(stderr, "%d : %s\n",i,base_inst[i-BASE_MIN]); #endif } } void uncompile_program(dbref i, dbref player1, char *buf) { /* free program */ bump_frames(buf, i, player1); free_prog(DBFETCH(i)->sp.program.code, DBFETCH(i)->sp.program.siz); /* cleanpubs(DBFETCH(i)->sp.program.pubs); */ /* DBFETCH(i)->sp.program.pubs = NULL; */ DBFETCH(i)->sp.program.first = 0; DBFETCH(i)->sp.program.curr_line = 0; DBFETCH(i)->sp.program.siz = 0; DBFETCH(i)->sp.program.code = 0; DBFETCH(i)->sp.program.start = 0; } void do_uncompile(__DO_PROTO) /* Add program matching to this */ { dbref i; char buf[100]; if (!Wizard(player)) { notify(player, player, "Permission denied."); return; } for (i = 0; i < db_top; i++) { if (Typeof(i) == TYPE_PROGRAM) { sprintf (buf, "Program %s uncompiled by %s", unparse_name(i), unparse_name(player)); uncompile_program(i, player, buf); } } notify(player, player, "All programs decompiled."); }