/* * File: lex.c * * Revision: * 93-06-27 (Robocoder): * Adjusted the meaning of the EXPECT_* flags; * EXPECT_ELSE ... means the last condition was false, so we want to find * an alternative or the end of the conditional block * EXPECT_ENDIF ... means the last condition was true, so we want to find * the end of the conditional block * Added #elif preprocessor command * Fixed get_text_block bug so no text returned "" * Added get_array_block()...using @@ENDMARKER to return array of strings */ #define SUPPRESS_COMPILER_INLINES #include "std.h" #include "file_incl.h" #include "lpc_incl.h" #include "compiler.h" #if defined(WIN32) && !defined(MINGW) # include "grammar_tab.h" #else # include "grammar.tab.h" #endif #include "scratchpad.h" #include "preprocess.h" #include "md.h" #include "hash.h" #include "file.h" #include "main.h" #include "cc.h" #include "master.h" #define NELEM(a) (sizeof (a) / sizeof((a)[0])) #define LEX_EOF ((unsigned char) EOF) char lex_ctype[256] = {0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0}; #define is_wspace(c) lex_ctype[(unsigned char)(c)] int current_line; /* line number in this file */ int current_line_base; /* number of lines from other files */ int current_line_saved; /* last line in this file where line num info was saved */ int total_lines; /* Used to compute average compiled lines/s */ char *current_file; int current_file_id; /* Bit flags for pragmas in effect */ int pragmas; int num_parse_error; /* Number of errors in the parser. */ lpc_predef_t *lpc_predefs = NULL; static int yyin_desc; int lex_fatal; static char **inc_list; static int inc_list_size; static int defines_need_freed = 0; static char *last_nl; static int nexpands = 0; #define EXPANDMAX 25000 static char *expands[EXPANDMAX]; static int expand_depth = 0; char yytext[MAXLINE]; char *outp; typedef struct incstate_s { struct incstate_s *next; int yyin_desc; int line; char *file; int file_id; char *last_nl; char *outp; } incstate_t; static incstate_t *inctop = 0; /* prevent unbridled recursion */ #define MAX_INCLUDE_DEPTH 32 static int incnum; /* If more than this is needed, the code needs help :-) */ #define MAX_FUNCTION_DEPTH 10 static function_context_t function_context_stack[MAX_FUNCTION_DEPTH]; static int last_function_context; function_context_t *current_function_context = 0; int arrow_efun, evaluate_efun, this_efun, to_float_efun, to_int_efun, new_efun; /* * The number of arguments stated below, are used by the compiler. * If min == max, then no information has to be coded about the * actual number of arguments. Otherwise, the actual number of arguments * will be stored in the byte after the instruction. * A maximum value of -1 means unlimited maximum value. * * If an argument has type 0 (T_INVALID) specified, then no checks will * be done at run time. * * The argument types are currently not checked by the compiler, * only by the runtime. */ keyword_t predefs[] = #include "efun_defs.c" const char *option_defs[] = #include "option_defs.c" static keyword_t reswords[] = { #ifdef DEBUG {"__TREE__", L_TREE, 0 }, #endif #ifdef ARRAY_RESERVED_WORD {"array", L_ARRAY, 0 }, #endif {"asm", 0, 0}, {"break", L_BREAK, 0}, #ifndef NO_BUFFER_TYPE {"buffer", L_BASIC_TYPE, TYPE_BUFFER}, #endif {"case", L_CASE, 0}, {"catch", L_CATCH, 0}, #ifdef STRUCT_CLASS {"class", L_CLASS, 0}, #endif #ifdef COMPAT_32 {"closure", L_BASIC_TYPE, TYPE_FUNCTION}, #endif {"continue", L_CONTINUE, 0}, {"default", L_DEFAULT, 0}, {"do", L_DO, 0}, {"efun", L_EFUN, 0}, {"else", L_ELSE, 0}, {"float", L_BASIC_TYPE, TYPE_REAL}, {"for", L_FOR, 0}, {"foreach", L_FOREACH, 0}, {"function", L_BASIC_TYPE, TYPE_FUNCTION}, {"if", L_IF, 0}, {"in", L_IN, 0}, {"inherit", L_INHERIT, 0}, {"int", L_BASIC_TYPE, TYPE_NUMBER}, {"mapping", L_BASIC_TYPE, TYPE_MAPPING}, {"mixed", L_BASIC_TYPE, TYPE_ANY}, {"new", L_NEW, 0}, {"nomask", L_TYPE_MODIFIER, DECL_NOMASK}, #ifdef SENSIBLE_MODIFIERS {"nosave", L_TYPE_MODIFIER, DECL_NOSAVE}, #endif {"object", L_BASIC_TYPE, TYPE_OBJECT}, {"parse_command", L_PARSE_COMMAND, 0}, {"private", L_TYPE_MODIFIER, DECL_PRIVATE}, {"protected", L_TYPE_MODIFIER, DECL_PROTECTED}, #ifdef SENSIBLE_MODIFIERS {"public", L_TYPE_MODIFIER, DECL_PUBLIC}, #else {"public", L_TYPE_MODIFIER, DECL_VISIBLE}, #endif #ifdef REF_RESERVED_WORD {"ref", L_REF, 0 }, #endif {"return", L_RETURN, 0}, {"sscanf", L_SSCANF, 0}, #ifndef SENSIBLE_MODIFIERS {"static", L_TYPE_MODIFIER, DECL_NOSAVE | DECL_PROTECTED }, #endif #ifdef HAS_STATUS_TYPE {"status", L_BASIC_TYPE, TYPE_NUMBER}, #endif {"string", L_BASIC_TYPE, TYPE_STRING}, #ifdef STRUCT_STRUCT {"struct", L_CLASS, 0}, #endif {"switch", L_SWITCH, 0}, {"time_expression", L_TIME_EXPRESSION, 0}, {"varargs", L_TYPE_MODIFIER, FUNC_VARARGS }, #ifdef VIRTUAL_RESERVED_WORD {"virtual", L_TYPE_MODIFIER, 0 }, #endif {"void", L_BASIC_TYPE, TYPE_VOID}, {"while", L_WHILE, 0}, }; static ident_hash_elem_t **ident_hash_table; static ident_hash_elem_t **ident_hash_head; static ident_hash_elem_t **ident_hash_tail; static ident_hash_elem_t *ident_dirty_list = 0; instr_t instrs[MAX_INSTRS]; #define TERM_ADD_INPUT 1 #define TERM_INCLUDE 2 #define TERM_START 4 typedef struct linked_buf_s { struct linked_buf_s *prev; char term_type; char buf[DEFMAX]; char *buf_end; char *outp; char *last_nl; } linked_buf_t; static linked_buf_t head_lbuf = { NULL, TERM_START }; static linked_buf_t *cur_lbuf; static void handle_define (char *); static void free_defines (void); static void add_define (const char *, int, const char *); static void add_predefine (const char *, int, const char *); static int expand_define (void); static void add_input (const char *); static int cond_get_exp (int); static void merge (char *name, char *dest); static void add_quoted_predefine (const char *, const char *); static void lexerror (const char *); static int skip_to (const char *, const char *); static void handle_cond (int); static int inc_open (char *, char *, int); static void include_error (const char *, int); static void handle_include (char *, int); static int get_terminator (char *); static int get_array_block (char *); static int get_text_block (char *); static void skip_line (void); static void skip_comment (void); static void deltrail (char *); static void handle_pragma (char *); static int cmygetc (void); static void refill (void); static void refill_buffer (void); static int exgetc (void); static int old_func (void); static ident_hash_elem_t *quick_alloc_ident_entry (void); static void yyerrorp (const char *); #define LEXER #include "preprocess.c" int lookup_predef(const char * name) { int x; for(x = 0; x < (sizeof(predefs) / sizeof(keyword_t)); x++) { if(strcmp(name, predefs[x].word) == 0) { return x; } } return -1; } static void merge (char * name, char * dest) { char *from; strcpy(dest, current_file); if ((from = strrchr(dest, '/'))) /* strip filename */ *from = 0; else /* current_file was the file_name */ /* include from the root directory */ *dest = 0; from = name; while (*from == '/') { from++; *dest = 0; /* absolute path */ } while (*from) { if (!strncmp(from, "../", 3)) { char *tmp; if (*dest == 0) /* including from above mudlib is NOT allowed */ break; tmp = strrchr(dest, '/'); if (tmp == NULL) /* 1 component in dest */ *dest = 0; else *tmp = 0; from += 3; /* skip "../" */ } else if (!strncmp(from, "./", 2)) { from += 2; } else { /* append first component to dest */ char *q; if (*dest) strcat(dest, "/"); /* only if dest is not empty !! */ q = strchr(from, '/'); if (q) { /* from has 2 or more components */ while (*from == '/') /* find the start */ from++; strncat(dest, from, q - from); for (from = q + 1; *from == '/'; from++); } else { /* this was the last component */ strcat(dest, from); break; } } } } static void yyerrorp (const char * s) { char buf[200]; sprintf(buf, s, '#'); yyerror(buf); lex_fatal++; } static void lexerror (const char * s) { yyerror(s); lex_fatal++; } static int skip_to (const char * token, const char * atoken) { char b[20], *p; unsigned char c; register char *yyp = outp, *startp; char *b_end = b + 19; int nest; for (nest = 0;;) { if ((c = *yyp++) == '#') { while (is_wspace(c = *yyp++)); startp = yyp - 1; for (p = b; !isspace(c) && c != LEX_EOF; c = *yyp++) { if (p < b_end) *p++ = c; else break; } *p = 0; if (!strcmp(b, "if") || !strcmp(b, "ifdef") || !strcmp(b, "ifndef")) { nest++; } else if (nest > 0) { if (!strcmp(b, "endif")) nest--; } else { if (!strcmp(b, token)) { outp = startp; *--outp = '#'; return 1; } else if (atoken && !strcmp(b, atoken)) { outp = startp; *--outp = '#'; return 0; } else if (!strcmp(b, "elif")) { outp = startp; *--outp = '#'; return (atoken == 0); } } } while (c != '\n' && c != LEX_EOF) c = *yyp++; if (c == LEX_EOF) { lexerror("Unexpected end of file while looking for #endif"); outp = yyp - 1; return 1; } current_line++; total_lines++; if (yyp == last_nl + 1) { outp = yyp; refill_buffer(); yyp = outp; } } } static int inc_open (char * buf, char * name, int check_local) { int i, f; char *p; const char *tmp; if (check_local) { merge(name, buf); tmp = check_valid_path(buf, master_ob, "include", 0); if (tmp && (f = open(tmp, O_RDONLY)) != -1) return f; } /* * Search all include dirs specified. */ for (p = strchr(name, '.'); p; p = strchr(p + 1, '.')) { if (p[1] == '.') return -1; } for (i = 0; i < inc_list_size; i++) { sprintf(buf, "%s/%s", inc_list[i], name); tmp = check_valid_path(buf, master_ob, "include", 0); if (tmp && (f = open(tmp, O_RDONLY)) != -1) { return f; } } return -1; } static void include_error (const char * msg, int global) { current_line--; if (global) { int saved_pragmas = pragmas; pragmas &= ~PRAGMA_ERROR_CONTEXT; lexerror(msg); pragmas = saved_pragmas; } else { yyerror(msg); } current_line++; } static void handle_include (char * name, int global) { char *p, *nameptr; static char buf[MAXLINE]; incstate_t *is; int delim, f; if (*name != '"' && *name != '<') { defn_t *d; if ((d = lookup_define(name)) && d->nargs == -1) { char *q; q = d->exps; while (uisspace(*q)) q++; handle_include(q, global); } else { include_error("Missing leading \" or < in #include", global); } return; } name = string_copy(name, "handle_include"); push_malloced_string(name); delim = *name++ == '"' ? '"' : '>'; for (p = name; *p && *p != delim; p++); if (!*p) { pop_stack(); include_error("Missing trailing \" or > in #include", global); return; } if (strlen(name) > sizeof(buf) - 100) { pop_stack(); include_error("Include name too long.", global); return; } *p = 0; if (++incnum == MAX_INCLUDE_DEPTH) { include_error("Maximum include depth exceeded.", global); } else if ((f = inc_open(buf, name, delim == '"')) != -1) { is = ALLOCATE(incstate_t, TAG_COMPILER, "handle_include: 1"); is->yyin_desc = yyin_desc; is->line = current_line; is->file = current_file; is->file_id = current_file_id; is->last_nl = last_nl; is->next = inctop; is->outp = outp; inctop = is; current_line--; save_file_info(current_file_id, current_line - current_line_saved); current_line_base += current_line; current_line_saved = 0; current_line = 1; current_file = make_shared_string(buf); current_file_id = add_program_file(buf, 0); yyin_desc = f; refill_buffer(); } else { sprintf(buf, "Cannot #include %s", name); include_error(buf, global); } pop_stack(); } static int get_terminator (char * terminator) { unsigned char c; int j = 0; while (((c = *outp++) != LEX_EOF) && (isalnum(c) || c == '_')) terminator[j++] = c; terminator[j] = '\0'; while (is_wspace(c) && c != LEX_EOF) c = *outp++; if (c == LEX_EOF) return 0; if (c == '\n') { current_line++; if (outp == last_nl + 1) refill_buffer(); } else { outp--; } return j; } #define MAXCHUNK (MAXLINE*4) #define NUMCHUNKS (DEFMAX/MAXCHUNK) #define NEWCHUNK(line) \ if (len == MAXCHUNK - 1) { \ line[curchunk][MAXCHUNK - 1] = '\0'; \ if (curchunk == NUMCHUNKS - 1) { \ res = -2; \ break; \ } \ line[++curchunk] = \ (char *)DXALLOC(MAXCHUNK, TAG_COMPILER, "array/text chunk"); \ len = 0; \ } static int get_array_block (char * term) { int termlen; /* length of terminator */ char *array_line[NUMCHUNKS];/* allocate memory in chunks */ int header, len; /* header length; position in chunk */ int startpos, startchunk; /* start of line */ int curchunk, res; /* current chunk; this function's result */ int i; /* an index counter */ unsigned char c; /* a char */ char *yyp = outp; /* * initialize */ termlen = strlen(term); array_line[0] = (char *) DXALLOC(MAXCHUNK, TAG_COMPILER, "array_block"); array_line[0][0] = '('; array_line[0][1] = '{'; array_line[0][2] = '"'; array_line[0][3] = '\0'; header = 1; len = 3; startpos = 3; startchunk = 0; curchunk = 0; res = 0; while (1) { while (((c = *yyp++) != '\n') && (c != LEX_EOF)) { NEWCHUNK(array_line); if (c == '"' || c == '\\') { array_line[curchunk][len++] = '\\'; NEWCHUNK(array_line); } array_line[curchunk][len++] = c; } if (c == '\n' && (yyp == last_nl + 1)) { outp = yyp; refill_buffer(); yyp = outp; } /* * null terminate current chunk */ array_line[curchunk][len] = '\0'; if (res) { outp = yyp; break; } /* * check for terminator */ if ((!strncmp(array_line[startchunk] + startpos, term, termlen)) && (!uisalnum(*(array_line[startchunk] + startpos + termlen))) && (*(array_line[startchunk] + startpos + termlen) != '_')) { /* * handle lone terminator on line */ if (strlen(array_line[startchunk] + startpos) == termlen) { current_line++; outp = yyp; } else { /* * put back trailing portion after terminator */ outp = --yyp; /* some operating systems give EOF only once */ for (i = curchunk; i > startchunk; i--) add_input(array_line[i]); add_input(array_line[startchunk] + startpos + termlen); } /* * remove terminator from last chunk */ array_line[startchunk][startpos - header] = '\0'; /* * insert array block into input stream */ *--outp = ')'; *--outp = '}'; for (i = startchunk; i >= 0; i--) add_input(array_line[i]); res = 1; break; } else { /* * only report end of file in array block, if not an include file */ if (c == LEX_EOF && inctop == 0) { res = -1; outp = yyp; break; } if (c == '\n') { current_line++; } /* * make sure there's room in the current chunk for terminator (ie * it's simpler if we don't have to deal with a terminator that * spans across chunks) fudge for "\",\"TERMINAL?\0", where '?' * is unknown */ if (len + termlen + 5 > MAXCHUNK) { if (curchunk == NUMCHUNKS - 1) { res = -2; outp = yyp; break; } array_line[++curchunk] = (char *) DXALLOC(MAXCHUNK, TAG_COMPILER, "array_block"); len = 0; } /* * header */ array_line[curchunk][len++] = '"'; array_line[curchunk][len++] = ','; array_line[curchunk][len++] = '"'; array_line[curchunk][len] = '\0'; startchunk = curchunk; startpos = len; header = 2; } } /* * free chunks */ for (i = curchunk; i >= 0; i--) FREE(array_line[i]); return res; } static int get_text_block (char * term) { int termlen; /* length of terminator */ char *text_line[NUMCHUNKS]; /* allocate memory in chunks */ int len; /* position in chunk */ int startpos, startchunk; /* start of line */ int curchunk, res; /* current chunk; this function's result */ int i; /* an index counter */ unsigned char c; /* a char */ register char *yyp = outp; /* * initialize */ termlen = strlen(term); text_line[0] = (char *) DXALLOC(MAXCHUNK, TAG_COMPILER, "text_block"); text_line[0][0] = '"'; text_line[0][1] = '\0'; len = 1; startpos = 1; startchunk = 0; curchunk = 0; res = 0; while (1) { while (((c = *yyp++) != '\n') && (c != LEX_EOF)) { NEWCHUNK(text_line); if (c == '"' || c == '\\') { text_line[curchunk][len++] = '\\'; NEWCHUNK(text_line); } text_line[curchunk][len++] = c; } if (c == '\n' && yyp == last_nl + 1) { outp = yyp; refill_buffer(); yyp = outp; } /* * null terminate current chunk */ text_line[curchunk][len] = '\0'; if (res) { outp = yyp; break; } /* * check for terminator */ if ((!strncmp(text_line[startchunk] + startpos, term, termlen)) && (!uisalnum(*(text_line[startchunk] + startpos + termlen))) && (*(text_line[startchunk] + startpos + termlen) != '_')) { if (strlen(text_line[startchunk] + startpos) == termlen) { current_line++; outp = yyp; } else { char *p, *q; /* * put back trailing portion after terminator */ outp = --yyp; /* some operating systems give EOF only once */ for (i = curchunk; i > startchunk; i--) { /* Ick. go back and unprotect " and \ */ p = text_line[i]; while (*p && *p != '\\') p++; if (*p) { q = p++; do { *q++ = *p++; if (*p == '\\') p++; } while (*p); *q = 0; } add_input(text_line[i]); } p = text_line[startchunk] + startpos + termlen; while (*p && *p != '\\') p++; if (*p) { q = p++; do { *q++ = *p++; if (*p == '\\') p++; } while (*p); *q = 0; } add_input(text_line[startchunk] + startpos + termlen); } /* * remove terminator from last chunk */ text_line[startchunk][startpos] = '\0'; /* * insert text block into input stream */ *--outp = '\0'; *--outp = '"'; for (i = startchunk; i >= 0; i--) add_input(text_line[i]); res = 1; break; } else { /* * only report end of file in text block, if not an include file */ if (c == LEX_EOF && inctop == 0) { res = -1; outp = yyp; break; } if (c == '\n') { current_line++; } /* * make sure there's room in the current chunk for terminator (ie * it's simpler if we don't have to deal with a terminator that * spans across chunks) fudge for "\\nTERMINAL?\0", where '?' is * unknown */ if (len + termlen + 4 > MAXCHUNK) { if (curchunk == NUMCHUNKS - 1) { res = -2; outp = yyp; break; } text_line[++curchunk] = (char *) DXALLOC(MAXCHUNK, TAG_COMPILER, "text_block"); len = 0; } /* * header */ text_line[curchunk][len++] = '\\'; text_line[curchunk][len++] = 'n'; text_line[curchunk][len] = '\0'; startchunk = curchunk; startpos = len; } } /* * free chunks */ for (i = curchunk; i >= 0; i--) FREE(text_line[i]); return res; } static void skip_line() { unsigned char c; register char *yyp = outp; while (((c = *yyp++) != '\n') && (c != LEX_EOF)); /* Next read of this '\n' will do refill_buffer() if neccesary */ if (c == '\n') yyp--; outp = yyp; } static void skip_comment() { unsigned char c = '*'; register char *yyp = outp; for (;;) { while ((c = *yyp++) != '*') { if (c == LEX_EOF) { outp = --yyp; lexerror("End of file in a comment"); return; } if (c == '\n') { nexpands = 0; current_line++; if (yyp == last_nl + 1) { outp = yyp; refill_buffer(); yyp = outp; } } } if (*(yyp - 2) == '/') yywarn("/* found in comment."); do { if ((c = *yyp++) == '/') { outp = yyp; return; } if (c == '\n') { nexpands = 0; current_line++; if (yyp == last_nl + 1) { outp = yyp; refill_buffer(); yyp = outp; } } } while (c == '*'); } } static void deltrail (char * sp) { char *p; p = sp; if (!*p) { lexerror("Illegal # command"); } else { while (*p && !uisspace(*p)) p++; *p = 0; } } #define SAVEC \ if (yyp < yytext+MAXLINE-5)\ *yyp++ = c;\ else {\ lexerror("Line too long");\ break;\ } typedef struct { const char *name; int value; } pragma_t; static pragma_t our_pragmas[] = { { "strict_types", PRAGMA_STRICT_TYPES }, { "save_types", PRAGMA_SAVE_TYPES }, { "warnings", PRAGMA_WARNINGS }, { "optimize", PRAGMA_OPTIMIZE }, { "show_error_context", PRAGMA_ERROR_CONTEXT }, { 0, 0 } }; static void handle_pragma (char * str) { int i; int no_flag; if (strncmp(str, "no_", 3) == 0) { str += 3; no_flag = 1; } else no_flag = 0; for (i = 0; our_pragmas[i].name; i++) { if (strcmp(our_pragmas[i].name, str) == 0) { if (no_flag) { pragmas &= ~our_pragmas[i].value; } else { pragmas |= our_pragmas[i].value; } return; } } yywarn("Unknown #pragma, ignored."); } char *show_error_context() { static char buf[60]; extern int yychar; char sub_context[25]; register char *yyp, *yyp2; int len; if ((unsigned char)outp[-1] == LEX_EOF) { strcpy(buf, " at the end of the file\n"); return buf; } if (yychar == -1) strcpy(buf, " around "); else strcpy(buf, " before "); yyp = outp; yyp2 = sub_context; len = 20; while (len--) { if (*yyp == '\n') { if (len == 19) strcat(buf, "the end of line"); break; } else if ((unsigned char)*yyp == LEX_EOF) { if (len == 19) strcat(buf, "the end of file"); break; } *yyp2++ = *yyp++; } *yyp2 = 0; if (yyp2 != sub_context) strcat(buf, sub_context); strcat(buf, "\n"); return buf; } #ifdef WIN32 int correct_read(int handle, char *buf, unsigned int count) { unsigned int tmp,size=0; do { tmp=read(handle,buf,count); if (tmp < 0) return tmp; if (tmp == 0) return size; size+=tmp; count-=tmp; buf+=tmp; } while (count>0); return size; } #else #define correct_read read #endif static void refill_buffer() { if (cur_lbuf != &head_lbuf) { if (outp >= cur_lbuf->buf_end && cur_lbuf->term_type == TERM_ADD_INPUT) { /* In this case it cur_lbuf cannot have been allocated due to #include */ linked_buf_t *prev_lbuf = cur_lbuf->prev; FREE(cur_lbuf); cur_lbuf = prev_lbuf; outp = cur_lbuf->outp; last_nl = cur_lbuf->last_nl; if (cur_lbuf->term_type == TERM_ADD_INPUT || (outp != last_nl + 1)) return; } } /* Here we are sure that we need more from the file */ /* Assume outp is one beyond a newline at last_nl */ /* or after an #include .... */ { char *end; char *p; int size; if (!inctop) { /* First check if there's enough space at the end */ end = cur_lbuf->buf + DEFMAX; if (end - cur_lbuf->buf_end > MAXLINE + 5) { p = cur_lbuf->buf_end; } else { /* No more space at the end */ size = cur_lbuf->buf_end - outp + 1; /* Include newline */ memcpy(outp - MAXLINE - 1, outp - 1, size); outp -= MAXLINE; p = outp + size - 1; } size = correct_read(yyin_desc, p, MAXLINE); cur_lbuf->buf_end = p += size; if (size < MAXLINE) { *(last_nl = p) = LEX_EOF; if(*(last_nl-1) != '\n'){ if(size +1 > MAXLINE) yyerror("No newline at end of file."); *p++ = '\n'; *(last_nl = p) = LEX_EOF; } return; } while (*--p != '\n'); if (p == outp - 1) { lexerror("Line too long."); *(last_nl = cur_lbuf->buf_end - 1) = '\n'; return; } last_nl = p; return; } else { int flag = 0; /* We are reading from an include file */ /* Is there enough space? */ end = inctop->outp; /* See if we are the last include in a different linked buffer */ if (cur_lbuf->term_type == TERM_INCLUDE && !(end >= cur_lbuf->buf && end < cur_lbuf->buf + DEFMAX)) { end = cur_lbuf->buf_end; flag = 1; } size = end - outp + 1; /* Include newline */ if (outp - cur_lbuf->buf > 2 * MAXLINE) { memcpy(outp - MAXLINE - 1, outp - 1, size); outp -= MAXLINE; p = outp + size - 1; } else { /* No space, need to allocate new buffer */ linked_buf_t *new_lbuf; char *new_outp; if (!(new_lbuf = ALLOCATE(linked_buf_t, TAG_COMPILER, "refill_bufer"))) { lexerror("Out of memory when allocating new buffer.\n"); return; } cur_lbuf->last_nl = last_nl; cur_lbuf->outp = outp; new_lbuf->prev = cur_lbuf; new_lbuf->term_type = TERM_INCLUDE; new_outp = new_lbuf->buf + DEFMAX - MAXLINE - size - 5; memcpy(new_outp - 1, outp - 1, size); cur_lbuf = new_lbuf; outp = new_outp; p = outp + size - 1; flag = 1; } size = correct_read(yyin_desc, p, MAXLINE); end = p += size; if (flag) cur_lbuf->buf_end = p; if (size < MAXLINE) { *(last_nl = p) = LEX_EOF; if(*(last_nl-1) != '\n'){ if(size +1 > MAXLINE) yyerror("No newline at end of file."); *p++ = '\n'; *(last_nl = p) = LEX_EOF; } return; } while (*--p != '\n'); if (p == outp - 1) { lexerror("Line too long."); *(last_nl = end - 1) = '\n'; return; } last_nl = p; return; } } } static int function_flag = 0; INLINE void push_function_context() { function_context_t *fc; parse_node_t *node; if (last_function_context == MAX_FUNCTION_DEPTH - 1) { yyerror("Function pointers nested too deep."); return; } fc = &function_context_stack[++last_function_context]; fc->num_parameters = 0; fc->num_locals = 0; node = new_node_no_line(); node->l.expr = node; node->r.expr = 0; node->kind = 0; fc->values_list = node; fc->bindable = 0; fc->parent = current_function_context; current_function_context = fc; } void pop_function_context() { current_function_context = current_function_context->parent; last_function_context--; } static int old_func() { add_input(" "); add_input(yytext); push_function_context(); return L_FUNCTION_OPEN; } #define return_assign(opcode) { yylval.number = opcode; return L_ASSIGN; } #define return_order(opcode) { yylval.number = opcode; return L_ORDER; } /* To halt execution when a #breakpoint line is encountered, set a breakpoint at this function */ static void lex_breakpoint() { } int yylex() { static char partial[MAXLINE + 5]; /* extra 5 for safety buffer */ static char terminator[MAXLINE + 5]; int is_float; float myreal; char *partp; register char *yyp; unsigned char c; yytext[0] = 0; partp = partial; partial[0] = 0; for (;;) { if (lex_fatal) { return -1; } switch (c = *outp++) { case LEX_EOF: if (inctop) { incstate_t *p; p = inctop; close(yyin_desc); save_file_info(current_file_id, current_line - current_line_saved); current_line_saved = p->line - 1; /* add the lines from this file, and readjust to be relative to the file we're returning to */ current_line_base += current_line - current_line_saved; free_string(current_file); nexpands = 0; if (outp >= cur_lbuf->buf_end) { linked_buf_t *prev_lbuf; if ((prev_lbuf = cur_lbuf->prev)) { FREE(cur_lbuf); cur_lbuf = prev_lbuf; } } current_file = p->file; current_file_id = p->file_id; current_line = p->line; yyin_desc = p->yyin_desc; last_nl = p->last_nl; outp = p->outp; inctop = p->next; incnum--; FREE((char *) p); outp[-1] = '\n'; if (outp == last_nl + 1) refill_buffer(); break; } if (iftop) { ifstate_t *p = iftop; yyerror(p->state == EXPECT_ENDIF ? "Missing #endif" : "Missing #else/#elif"); while (iftop) { p = iftop; iftop = p->next; FREE((char *) p); } } outp--; return -1; case '\r': yywarn("^M"); break; case '\t': #ifdef WARN_TAB yywarn(""); #endif break; case '\n': nexpands = 0; current_line++; total_lines++; if (outp == last_nl + 1) refill_buffer(); case ' ': case '\f': case '\v': break; case '+': { switch(*outp++) { case '+': return L_INC; case '=': return_assign(F_ADD_EQ); default: outp--; return '+'; } } case '-': { switch(*outp++) { case '>': return L_ARROW; case '-': return L_DEC; case '=': return_assign(F_SUB_EQ); default: outp--; return '-'; } } case '&': { switch(*outp++) { case '&': return L_LAND; case '=': return_assign(F_AND_EQ); default: outp--; return '&'; } } case '|': { switch(*outp++) { case '|': return L_LOR; case '=': return_assign(F_OR_EQ); default: outp--; return '|'; } } case '^': { if (*outp++ == '=') return_assign(F_XOR_EQ); outp--; return '^'; } case '<': { switch(*outp++) { case '<': { if (*outp++ == '=') return_assign(F_LSH_EQ); outp--; return L_LSH; } case '=': return_order(F_LE); default: outp--; return '<'; } } case '>': { switch(*outp++) { case '>': { if (*outp++ == '=') return_assign(F_RSH_EQ); outp--; return L_RSH; } case '=': return_order(F_GE); default: outp--; return_order(F_GT); } } case '*': { if (*outp++ == '=') return_assign(F_MULT_EQ); outp--; return '*'; } case '%': { if (*outp++ == '=') return_assign(F_MOD_EQ); outp--; return '%'; } case '/': switch(*outp++) { case '*': skip_comment(); break; case '/': skip_line(); break; case '=': return_assign(F_DIV_EQ); default: outp--; return '/'; } break; case '=': if (*outp++ == '=') return L_EQ; outp--; yylval.number = F_ASSIGN; return L_ASSIGN; case '(': yyp = outp; #ifdef WOMBLES c = *yyp++; #else while (isspace(c = *yyp++)) { if (c == '\n') { current_line++; if (yyp == last_nl + 1) { outp = yyp; refill_buffer(); yyp = outp; } } } #endif switch(c) { case '{' : { outp = yyp; return L_ARRAY_OPEN; } case '[' : { outp = yyp; return L_MAPPING_OPEN; } case ':' : { if ((c = *yyp++) == ':') { outp = yyp -= 2; return '('; } else { while (isspace(c)) { if (c == '\n') { if ((yyp == last_nl + 1)) { outp = yyp; refill_buffer(); yyp = outp; } current_line++; } c = *yyp++; } outp = yyp; if (isalpha(c) || c == '_') { function_flag = 1; goto parse_identifier; } outp--; push_function_context(); return L_FUNCTION_OPEN; } } default: { outp = yyp - 1; return '('; } } case '$': if (!current_function_context) { yyerror("$var illegal outside of function pointer."); return '$'; } if (current_function_context->num_parameters == -2) { yyerror("$var illegal inside anonymous function pointer."); return '$'; } else { if (!isdigit(c = *outp++)) { outp--; return '$'; } yyp = yytext; SAVEC; for (;;) { if (!isdigit(c = *outp++)) break; SAVEC; } outp--; *yyp = 0; yylval.number = atol(yytext) - 1; if (yylval.number < 0) yyerror("In function parameter $num, num must be >= 1."); else if (yylval.number > 254) yyerror("only 255 parameters allowed."); else if (yylval.number >= current_function_context->num_parameters) current_function_context->num_parameters = yylval.number + 1; return L_PARAMETER; } case ')': case '{': case '}': case '[': case ']': case ';': case ',': case '~': #ifndef USE_TRIGRAPHS case '?': return c; #else return c; /* * You're probably asking, what the heck are trigraphs? * The character set of C source is contained within seven-bit * ASCII, a superset of ISO 646-1983 Invariant Code Set; * to allow programs to be represented in the reduced set, * certain single characters are replaced by a corresponding * trigraph (3 character) sequence. These are: * ??= # ??( [ ??< { * ??/ \ ??) ] ??> } * ??' ^ ??! | ??- ~ */ case '?': if (*outp++ != '?') { outp--; return '?'; } switch (*outp++) { case '=': return '#'; case '/': return '\\'; case '\'': return '^'; case '(': return '['; case ')': return ']'; case '!': return '|'; case '<': return '{'; case '>': return '}'; case '-': return '~'; default: outp -= 2; return '?'; } #endif case '!': if (*outp++ == '=') return L_NE; outp--; return L_NOT; case ':': if (*outp++ == ':') return L_COLON_COLON; outp--; return ':'; case '.': if (*outp++ == '.') { if (*outp++ == '.') return L_DOT_DOT_DOT; outp--; return L_RANGE; } outp--; goto badlex; case '#': if (*(outp - 2) == '\n') { char *sp = 0; int quote; while (is_wspace(c = *outp++)); yyp = yytext; for (quote = 0;;) { if (c == '"') quote ^= 1; else if (c == '/' && !quote) { if (*outp == '*') { outp++; skip_comment(); c = *outp++; } else if (*outp == '/') { outp++; skip_line(); c = *outp++; } } if (!sp && isspace(c)) sp = yyp; if (c == '\n' || c == LEX_EOF) break; SAVEC; c = *outp++; } if (sp) { *sp++ = 0; while (uisspace(*sp)) sp++; } else { sp = yyp; } *yyp = 0; if (!strcmp("include", yytext)) { current_line++; if (c == LEX_EOF) { *(last_nl = --outp) = LEX_EOF; outp[-1] = '\n'; } handle_include(sp, 0); break; } else { if (outp == last_nl + 1) refill_buffer(); if (strcmp("define", yytext) == 0) { handle_define(sp); } else if (strcmp("if", yytext) == 0) { int cond; *--outp = '\0'; add_input(sp); cond = cond_get_exp(0); if (*outp++) { yyerror("Condition too complex in #if"); while (*outp++); } else handle_cond(cond); } else if (strcmp("ifdef", yytext) == 0) { deltrail(sp); handle_cond(lookup_define(sp) != 0); } else if (strcmp("ifndef", yytext) == 0) { deltrail(sp); handle_cond(lookup_define(sp) == 0); } else if (strcmp("elif", yytext) == 0) { handle_elif(sp); } else if (strcmp("else", yytext) == 0) { handle_else(); } else if (strcmp("endif", yytext) == 0) { handle_endif(); } else if (strcmp("undef", yytext) == 0) { defn_t *d; deltrail(sp); if ((d = lookup_define(sp))) { if (d->flags & DEF_IS_PREDEF) yyerror("Illegal to #undef a predefined value."); else d->flags |= DEF_IS_UNDEFINED; } } else if (strcmp("echo", yytext) == 0) { debug_message("%s\n", sp); } else if (strcmp("error", yytext) == 0) { char buf[MAXLINE+1]; strcpy(buf, yytext); strcat(buf, "\n"); yyerror(buf); } else if (strcmp("warn", yytext) == 0) { char buf[MAXLINE+1]; strcpy(buf, yytext); strcat(buf, "\n"); yywarn(buf); } else if (strcmp("pragma", yytext) == 0) { handle_pragma(sp); } else if (strcmp("breakpoint", yytext) == 0) { lex_breakpoint(); } else { yyerror("Unrecognised # directive"); } *--outp = '\n'; break; } } else { #ifdef COMPAT_32 if (*outp == '\'') { outp++; return L_LAMBDA; } #endif goto badlex; } case '\'': if (*outp++ == '\\') { switch(*outp++) { case 'n': yylval.number = '\n'; break; case 't': yylval.number = '\t'; break; case 'r': yylval.number = '\r'; break; case 'b': yylval.number = '\b'; break; case 'a': yylval.number = '\x07'; break; case 'e': yylval.number = '\x1b'; break; case '\'': yylval.number = '\''; break; case '"': yylval.number = '"'; break; case '\\': yylval.number = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': outp--; yylval.number = strtol(outp, &outp, 8); if (yylval.number > 255) { yywarn("Illegal character constant."); yylval.number = 'x'; } break; case 'x': if (!isxdigit((unsigned char)*outp)) { yylval.number = 'x'; yywarn("\\x must be followed by a valid hex value; interpreting as 'x' instead."); } else { yylval.number = strtol(outp, &outp, 16); if (yylval.number > 255) { yywarn("Illegal character constant."); yylval.number = 'x'; } } break; case '\n': yylval.number = '\n'; current_line++; total_lines++; if ((outp = last_nl + 1)) refill_buffer(); break; default: yywarn("Unknown \\ escape."); yylval.number = *(outp - 1); break; } } else { yylval.number = *(outp - 1); } if (*outp++ != '\'') { outp--; yyerror("Illegal character constant"); yylval.number = 0; } return L_NUMBER; case '@': { int rc; int tmp; if ((tmp = *outp++) != '@') { /* check for Robocoder's @@ block */ outp--; } if (!get_terminator(terminator)) { lexerror("Illegal terminator"); break; } if (tmp == '@') { rc = get_array_block(terminator); if (rc > 0) { /* outp is pointing at "({" for histortical reasons */ outp += 2; return L_ARRAY_OPEN; } else if (rc == -1) { lexerror("End of file in array block"); return LEX_EOF; } else { /* if rc == -2 */ yyerror("Array block exceeded maximum length"); } } else { rc = get_text_block(terminator); if (rc > 0) { int n; /* * make string token and clean up */ yylval.string = scratch_copy_string(outp); n = strlen(outp) + 1; outp += n; return L_STRING; } else if (rc == -1) { lexerror("End of file in text block"); return LEX_EOF; } else { /* if (rc == -2) */ yyerror("Text block exceeded maximum length"); } } } break; case '"': { int l; register unsigned char *to = scr_tail + 1; if ((l = scratch_end - to) > 255) l = 255; while (l--) { switch(c = *outp++) { case LEX_EOF: lexerror("End of file in string"); return LEX_EOF; case '"': *to++ = 0; if (!l && (to == scratch_end)) { char *res = scratch_large_alloc(to - scr_tail - 1); strcpy(res, (char *) (scr_tail + 1)); yylval.string = res; return L_STRING; } scr_last = scr_tail + 1; scr_tail = to; *to = to - scr_last; yylval.string = (char *) scr_last; return L_STRING; case '\n': current_line++; total_lines++; if (outp == last_nl + 1) refill_buffer(); *to++ = '\n'; break; case '\\': /* Don't copy the \ in yet */ switch((unsigned char)*outp++) { case '\n': current_line++; total_lines++; if (outp == last_nl + 1) refill_buffer(); l++; /* Nothing is copied */ break; case LEX_EOF: lexerror("End of file in string"); return LEX_EOF; case 'n': *to++ = '\n'; break; case 't': *to++ = '\t'; break; case 'r': *to++ = '\r'; break; case 'b': *to++ = '\b'; break; case 'a': *to++ = '\x07'; break; case 'e': *to++ = '\x1b'; break; case '"': *to++ = '"'; break; case '\\': *to++ = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int tmp; outp--; tmp = strtol(outp, &outp, 8); if (tmp > 255) { yywarn("Illegal character constant in string."); tmp = 'x'; } *to++ = tmp; break; } case 'x': { int tmp; if (!isxdigit((unsigned char)*outp)) { *to++ = 'x'; yywarn("\\x must be followed by a valid hex value; interpreting as 'x' instead."); } else { tmp = strtol(outp, &outp, 16); if (tmp > 255) { yywarn("Illegal character constant."); tmp = 'x'; } *to++ = tmp; } break; } default: *to++ = *(outp - 1); yywarn("Unknown \\ escape."); } break; default: *to++ = c; } } /* Not enough space, we now copy the rest into yytext */ l = MAXLINE - (to - scr_tail); yyp = yytext; while (l--) { switch(c = *outp++) { case LEX_EOF: lexerror("End of file in string"); return LEX_EOF; case '"': { char *res; *yyp++ = '\0'; res = scratch_large_alloc((yyp - yytext) + (to - scr_tail) - 1); strncpy(res, (char *) (scr_tail + 1), (to - scr_tail) - 1); strcpy(res + (to - scr_tail) - 1, yytext); yylval.string = res; return L_STRING; } case '\n': current_line++; total_lines++; if (outp == last_nl + 1) refill_buffer(); *yyp++ = '\n'; break; case '\\': /* Don't copy the \ in yet */ switch((unsigned char)*outp++) { case '\n': current_line++; total_lines++; if (outp == last_nl + 1) refill_buffer(); l++; /* Nothing is copied */ break; case LEX_EOF: lexerror("End of file in string"); return LEX_EOF; case 'n': *yyp++ = '\n'; break; case 't': *yyp++ = '\t'; break; case 'r': *yyp++ = '\r'; break; case 'b': *yyp++ = '\b'; break; case 'a': *yyp++ = '\x07'; break; case 'e': *yyp++ = '\x1b'; break; case '"': *yyp++ = '"'; break; case '\\': *yyp++ = '\\'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int tmp; outp--; tmp = strtol(outp, &outp, 8); if (tmp > 255) { yywarn("Illegal character constant in string."); tmp = 'x'; } *yyp++ = tmp; break; } case 'x': { int tmp; if (!isxdigit((unsigned char)*outp)) { *yyp++ = 'x'; yywarn("\\x must be followed by a valid hex value; interpreting as 'x' instead."); } else { tmp = strtol(outp, &outp, 16); if (tmp > 255) { yywarn("Illegal character constant."); tmp = 'x'; } *yyp++ = tmp; } break; } default: *yyp++ = *(outp - 1); yywarn("Unknown \\ escape."); } break; default: *yyp++ = c; } } /* Not even enough length, declare too long string error */ lexerror("String too long"); *yyp++ = '\0'; { char *res; res = scratch_large_alloc((yyp - yytext) + (to - scr_tail) - 1); strncpy(res, (char *) (scr_tail + 1), (to - scr_tail) - 1); strcpy(res + (to - scr_tail) - 1, yytext); yylval.string = res; return L_STRING; } } case '0': c = *outp++; if (c == 'X' || c == 'x') { yyp = yytext; for (;;) { c = *outp++; SAVEC; if (!isxdigit((unsigned char)c)) break; } outp--; yylval.number = strtol(yytext, (char **) NULL, 0x10); return L_NUMBER; } outp--; c = '0'; /* fall through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': is_float = 0; yyp = yytext; *yyp++ = c; for (;;) { c = *outp++; if (c == '.') { if (!is_float) { is_float = 1; } else { is_float = 0; outp--; break; } } else if (!isdigit((unsigned char)c)) break; SAVEC; } outp--; *yyp = 0; if (is_float) { sscanf(yytext, "%f", &myreal); yylval.real = (float)myreal; return L_REAL; } else { yylval.number = atol(yytext); return L_NUMBER; } default: if (isalpha((unsigned char)c) || c == '_') { int r; parse_identifier: yyp = yytext; *yyp++ = c; for (;;) { c = *outp++; if (!isalnum((unsigned char)c) && (c != '_')) break; SAVEC; } *yyp = 0; if (c == '#') { if (*outp++ != '#') lexerror("Single '#' in identifier -- use '##' for token pasting"); outp -= 2; if (!expand_define()) { if (partp + (r = strlen(yytext)) + (function_flag ? 3 : 0) - partial > MAXLINE) lexerror("Pasted token is too long"); if (function_flag) { strcpy(partp, "(: "); partp += 3; } strcpy(partp, yytext); partp += r; outp += 2; } } else if (partp != partial) { outp--; if (!expand_define()) add_input(yytext); while ((c = *outp++) == ' '); outp--; add_input(partial); partp = partial; partial[0] = 0; } else { outp--; if (!expand_define()) { ident_hash_elem_t *ihe; if ((ihe = lookup_ident(yytext))) { if (ihe->token & IHE_RESWORD) { if (function_flag) { function_flag = 0; add_input(yytext); push_function_context(); return L_FUNCTION_OPEN; } yylval.number = ihe->sem_value; return ihe->token & TOKEN_MASK; } if (function_flag) { int val; function_flag = 0; while ((c = *outp++) == ' '); outp--; /* Note that this gets code like: * #define x :) * #define y , * function f = (: foo x; * function g = (: bar y 1 :); * * wrong. But that is almost pathological. */ if (c != ':' && c != ',') return old_func(); if ((val=ihe->dn.local_num) >= 0) { if (c == ',') return old_func(); yylval.number = (val << 8) | FP_L_VAR; } else if ((val=ihe->dn.global_num) >= 0) { if (c == ',') return old_func(); yylval.number = (val << 8) | FP_G_VAR; } else if ((val=ihe->dn.function_num) >=0) { yylval.number = (val << 8)|FP_LOCAL; } else if ((val=ihe->dn.simul_num) >=0) { yylval.number = (val << 8)|FP_SIMUL; } else if ((val=ihe->dn.efun_num) >=0) { yylval.number = (val << 8)|FP_EFUN; } else return old_func(); return L_NEW_FUNCTION_OPEN; } yylval.ihe = ihe; return L_DEFINED_NAME; } yylval.string = scratch_copy(yytext); return L_IDENTIFIER; } if (function_flag) { function_flag = 0; add_input("(:"); } } break; } goto badlex; } } badlex: { #ifdef DEBUG char buff[100]; sprintf(buff, "Illegal character (hex %02x) '%c'", (unsigned) c, (char) c); yyerror(buff); #endif return ' '; } } extern YYSTYPE yylval; void end_new_file() { while (inctop) { incstate_t *p; p = inctop; close(yyin_desc); free_string(current_file); current_file = p->file; yyin_desc = p->yyin_desc; inctop = p->next; FREE((char *) p); } inctop = 0; while (iftop) { ifstate_t *p; p = iftop; iftop = p->next; FREE((char *) p); } if (defines_need_freed) { free_defines(); defines_need_freed = 0; } if (cur_lbuf != &head_lbuf) { linked_buf_t *prev_lbuf; while (cur_lbuf != &head_lbuf) { prev_lbuf = cur_lbuf->prev; FREE ((char *) cur_lbuf); cur_lbuf = prev_lbuf; } } } static void add_quoted_predefine (const char * def, const char * val) { char save_buf[MAXLINE]; strcpy(save_buf, "\""); strcat(save_buf, val); strcat(save_buf, "\""); add_predefine(def, -1, save_buf); } void add_predefines() { char save_buf[80]; int i; lpc_predef_t *tmpf; add_predefine("MUDOS", -1, ""); add_predefine("FLUFFOS", -1, ""); #ifdef WIN32 add_predefine("__WIN32__", -1, ""); #endif #ifdef RUSAGE add_predefine("__HAS_RUSAGE__", -1, ""); #endif #ifdef M64 add_predefine("__M64__", -1, ""); #endif #ifdef PACKAGE_DB add_predefine("__PACKAGE_DB__", -1, ""); #endif #ifdef GET_CHAR_IS_BUFFERED add_predefine("__GET_CHAR_IS_BUFFERED__", -1, ""); #endif #ifdef PACKAGE_DSLIB add_predefine("__DSLIB__", -1, ""); #endif #ifdef PACKAGE_DWLIB add_predefine("__DWLIB__", -1, ""); #endif #ifdef FD_SETSIZE sprintf(save_buf, "%d", FD_SETSIZE); #else sprintf(save_buf, "%d", 64); #endif add_predefine("__FD_SETSIZE__", -1, save_buf); get_version(save_buf); add_quoted_predefine("__VERSION__", save_buf); sprintf(save_buf, "%d", external_port[0].port); add_predefine("__PORT__", -1, save_buf); for (i = 0; i < 2 * NUM_OPTION_DEFS; i += 2) { add_predefine(option_defs[i], -1, option_defs[i+1]); } add_quoted_predefine("__ARCH__", ARCH); add_quoted_predefine("__COMPILER__", COMPILER); add_quoted_predefine("__OPTIMIZATION__", OPTIMIZE); /* Backwards Compat */ #ifndef CDLIB add_quoted_predefine("MUD_NAME", MUD_NAME); #endif #ifdef F_ED add_predefine("HAS_ED", -1, ""); #endif #ifdef F_PRINTF add_predefine("HAS_PRINTF", -1, ""); #endif #if (defined(RUSAGE) || defined(GET_PROCESS_STATS) || defined(TIMES)) add_predefine("HAS_RUSAGE", -1, ""); #endif #ifdef DEBUG_MACRO add_predefine("HAS_DEBUG_LEVEL", -1, ""); #endif for (tmpf = lpc_predefs; tmpf; tmpf = tmpf->next) { char namebuf[NSIZE]; char mtext[MLEN]; *mtext = '\0'; sscanf(tmpf->flag, "%[^=]=%[ -~=]", namebuf, mtext); if (strlen(namebuf) >= NSIZE) fatal("NSIZE exceeded"); if (strlen(mtext) >= MLEN) fatal("MLEN exceeded"); add_predefine(namebuf, -1, mtext); } sprintf(save_buf, "%ld", sizeof(long)); add_predefine("SIZEOFINT", -1, save_buf); sprintf(save_buf, "%ld", LONG_MAX); add_predefine("MAX_INT", -1, save_buf); } void start_new_file (int f) { if (defines_need_freed) { free_defines(); } defines_need_freed = 1; if (current_file) { char *dir; char *tmp; int ln; ln = strlen(current_file); dir = (char *) DMALLOC(ln + 4, TAG_COMPILER, "start_new_file"); dir[0] = '"'; dir[1] = '/'; memcpy(dir + 2, current_file, ln); dir[ln + 2] = '"'; dir[ln + 3] = 0; add_define("__FILE__", -1, dir); tmp = strrchr(dir, '/'); tmp[1] = '"'; tmp[2] = 0; add_define("__DIR__", -1, dir); FREE(dir); } yyin_desc = f; lex_fatal = 0; last_function_context = -1; current_function_context = 0; cur_lbuf = &head_lbuf; cur_lbuf->outp = cur_lbuf->buf_end = outp = cur_lbuf->buf + (DEFMAX >> 1); *(last_nl = outp - 1) = '\n'; pragmas = DEFAULT_PRAGMAS; nexpands = 0; incnum = 0; current_line = 1; current_line_base = 0; current_line_saved = 0; function_flag = 0; if (*GLOBAL_INCLUDE_FILE) { char *gifile; /* need a writable copy */ gifile = alloc_cstring(GLOBAL_INCLUDE_FILE, "global include"); handle_include(gifile, 1); FREE(gifile); } else refill_buffer(); } const char *query_instr_name (int instr) { const char *name; static char num_buf[20]; name = instrs[instr].name; if (name) { if (name[0] == '_') name++; return name; } else { sprintf(num_buf, "efun%d", instr); return num_buf; } } #define add_instr_name(w, x, y, z) int_add_instr_name(w, y, z) static void int_add_instr_name (const char * name, int n, short t) { instrs[n].name = name; instrs[n].ret_type = t; } static void init_instrs() { int i, n; for (i = 0; i < BASE; i++) { instrs[i].ret_type = -1; } for (i = 0; i < NELEM(predefs); i++) { n = predefs[i].token; if (n & F_ALIAS_FLAG) { predefs[i].token ^= F_ALIAS_FLAG; } else { instrs[n].min_arg = predefs[i].min_args; instrs[n].max_arg = predefs[i].max_args; instrs[n].name = predefs[i].word; instrs[n].type[0] = predefs[i].arg_type1; instrs[n].type[1] = predefs[i].arg_type2; instrs[n].type[2] = predefs[i].arg_type3; instrs[n].type[3] = predefs[i].arg_type4; instrs[n].Default = predefs[i].Default; instrs[n].ret_type = predefs[i].ret_type; instrs[n].arg_index = predefs[i].arg_index; } } /* * eoperators have a return type now. T_* is used instead of TYPE_* * since operators can return multiple types. */ #ifdef NO_BUFFER_TYPE #define OR_BUFFER #else #define OR_BUFFER | T_BUFFER #endif add_instr_name("<", "c_lt();\n", F_LT, T_NUMBER); add_instr_name(">", "c_gt();\n", F_GT, T_NUMBER); add_instr_name("<=", "c_le();\n", F_LE, T_NUMBER); add_instr_name(">=", "c_ge();\n", F_GE, T_NUMBER); add_instr_name("==", "f_eq();\n", F_EQ, T_NUMBER); add_instr_name("+=", "c_add_eq(0);\n", F_ADD_EQ, T_ANY); add_instr_name("(void)+=", "c_add_eq(1);\n", F_VOID_ADD_EQ, T_NUMBER); add_instr_name("!", "c_not();\n", F_NOT, T_NUMBER); add_instr_name("&", "f_and();\n", F_AND, T_ARRAY | T_NUMBER); add_instr_name("&=", "f_and_eq();\n", F_AND_EQ, T_NUMBER); add_instr_name("index", "c_index();\n", F_INDEX, T_ANY); add_instr_name("member", "c_member(%i);\n", F_MEMBER, T_ANY); add_instr_name("new_empty_class", "c_new_class(%i, 0);\n", F_NEW_EMPTY_CLASS, T_ANY); add_instr_name("new_class", "c_new_class(%i, 1);\n", F_NEW_CLASS, T_ANY); add_instr_name("rindex", "c_rindex();\n", F_RINDEX, T_ANY); add_instr_name("loop_cond_local", "C_LOOP_COND_LV(%i, %i); if (lpc_int)\n", F_LOOP_COND_LOCAL, -1); add_instr_name("loop_cond_number", "C_LOOP_COND_NUM(%i, %i); if (lpc_int)\n", F_LOOP_COND_NUMBER, -1); add_instr_name("loop_incr", "C_LOOP_INCR(%i);\n", F_LOOP_INCR, -1); add_instr_name("foreach", 0, F_FOREACH, -1); add_instr_name("exit_foreach", "c_exit_foreach();\n", F_EXIT_FOREACH, -1); add_instr_name("expand_varargs", 0, F_EXPAND_VARARGS, -1); add_instr_name("next_foreach", "c_next_foreach();\n", F_NEXT_FOREACH, -1); add_instr_name("member_lvalue", "c_member_lvalue(%i);\n", F_MEMBER_LVALUE, T_LVALUE); add_instr_name("index_lvalue", "push_indexed_lvalue(0);\n", F_INDEX_LVALUE, T_LVALUE|T_LVALUE_BYTE); add_instr_name("rindex_lvalue", "push_indexed_lvalue(1);\n", F_RINDEX_LVALUE, T_LVALUE|T_LVALUE_BYTE); add_instr_name("nn_range_lvalue", "push_lvalue_range(0x00);\n", F_NN_RANGE_LVALUE, T_LVALUE_RANGE); add_instr_name("nr_range_lvalue", "push_lvalue_range(0x01);\n", F_NR_RANGE_LVALUE, T_LVALUE_RANGE); add_instr_name("rr_range_lvalue", "push_lvalue_range(0x11);\n", F_RR_RANGE_LVALUE, T_LVALUE_RANGE); add_instr_name("rn_range_lvalue", "push_lvalue_range(0x10);\n", F_RN_RANGE_LVALUE, T_LVALUE_RANGE); add_instr_name("nn_range", "f_range(0x00);\n", F_NN_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("rr_range", "f_range(0x11);\n", F_RR_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("nr_range", "f_range(0x01);\n", F_NR_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("rn_range", "f_range(0x10);\n", F_RN_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("re_range", "f_extract_range(1);\n", F_RE_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("ne_range", "f_extract_range(0);\n", F_NE_RANGE, T_ARRAY|T_STRING OR_BUFFER); add_instr_name("global", "C_GLOBAL(%i);\n", F_GLOBAL, T_ANY); add_instr_name("local", "C_LOCAL(%i);\n", F_LOCAL, T_ANY); add_instr_name("make_ref", "c_make_ref(%i);\n", F_MAKE_REF, T_REF); add_instr_name("kill_refs", "c_kill_refs(%i);\n", F_KILL_REFS, T_ANY); add_instr_name("ref", "C_REF(%i);\n", F_REF, T_ANY); add_instr_name("ref_lvalue", "C_REF_LVALUE(%i);\n", F_REF_LVALUE, T_LVALUE); add_instr_name("transfer_local", "C_TRANSFER_LOCAL(%i);\n", F_TRANSFER_LOCAL, T_ANY); add_instr_name("number", 0, F_NUMBER, T_NUMBER); add_instr_name("real", 0, F_REAL, T_REAL); add_instr_name("local_lvalue", "C_LVALUE(fp + %i);\n", F_LOCAL_LVALUE, T_LVALUE); add_instr_name("while_dec", "C_WHILE_DEC(%i); if (lpc_int)\n", F_WHILE_DEC, -1); add_instr_name("const1", "push_number(1);\n", F_CONST1, T_NUMBER); add_instr_name("subtract", "c_subtract();\n", F_SUBTRACT, T_NUMBER | T_REAL | T_ARRAY); add_instr_name("(void)assign", "c_void_assign();\n", F_VOID_ASSIGN, T_NUMBER); add_instr_name("(void)assign_local", "c_void_assign_local(fp + %i);\n", F_VOID_ASSIGN_LOCAL, T_NUMBER); add_instr_name("assign", "c_assign();\n", F_ASSIGN, T_ANY); add_instr_name("branch", 0, F_BRANCH, -1); add_instr_name("bbranch", 0, F_BBRANCH, -1); add_instr_name("byte", 0, F_BYTE, T_NUMBER); add_instr_name("-byte", 0, F_NBYTE, T_NUMBER); add_instr_name("branch_ne", 0, F_BRANCH_NE, -1); add_instr_name("branch_ge", 0, F_BRANCH_GE, -1); add_instr_name("branch_le", 0, F_BRANCH_LE, -1); add_instr_name("branch_eq", 0, F_BRANCH_EQ, -1); add_instr_name("bbranch_lt", 0, F_BBRANCH_LT, -1); add_instr_name("bbranch_when_zero", 0, F_BBRANCH_WHEN_ZERO, -1); add_instr_name("bbranch_when_non_zero", 0, F_BBRANCH_WHEN_NON_ZERO, -1); add_instr_name("branch_when_zero", 0, F_BRANCH_WHEN_ZERO, -1); add_instr_name("branch_when_non_zero", 0, F_BRANCH_WHEN_NON_ZERO, -1); add_instr_name("pop", "pop_stack();\n", F_POP_VALUE, -1); add_instr_name("const0", "push_number(0);\n", F_CONST0, T_NUMBER); #ifdef F_JUMP_WHEN_ZERO add_instr_name("jump_when_zero", F_JUMP_WHEN_ZERO, -1); add_instr_name("jump_when_non_zero", F_JUMP_WHEN_NON_ZERO, -1); #endif #ifdef F_LOR add_instr_name("||", 0, F_LOR, -1); add_instr_name("&&", 0, F_LAND, -1); #endif add_instr_name("-=", "f_sub_eq();\n", F_SUB_EQ, T_ANY); #ifdef F_JUMP add_instr_name("jump", F_JUMP, -1); #endif add_instr_name("return_zero", "c_return_zero();\nreturn;\n", F_RETURN_ZERO, -1); add_instr_name("return", "c_return();\nreturn;\n", F_RETURN, -1); add_instr_name("sscanf", "c_sscanf(%i);\n", F_SSCANF, T_NUMBER); add_instr_name("parse_command", "c_parse_command(%i);\n", F_PARSE_COMMAND, T_NUMBER); add_instr_name("string", 0, F_STRING, T_STRING); add_instr_name("short_string", 0, F_SHORT_STRING, T_STRING); add_instr_name("call", "c_call(%i, %i);\n", F_CALL_FUNCTION_BY_ADDRESS, T_ANY); add_instr_name("call_inherited", "c_call_inherited(%i, %i, %i);\n", F_CALL_INHERITED, T_ANY); add_instr_name("aggregate_assoc", "C_AGGREGATE_ASSOC(%i);\n", F_AGGREGATE_ASSOC, T_MAPPING); #ifdef DEBUG add_instr_name("break_point", "break_point();\n", F_BREAK_POINT, -1); #endif add_instr_name("aggregate", "C_AGGREGATE(%i);\n", F_AGGREGATE, T_ARRAY); add_instr_name("(::)", 0, F_FUNCTION_CONSTRUCTOR, T_FUNCTION); /* sorry about this one */ add_instr_name("simul_efun", "call_simul_efun(%i, (lpc_int = %i + num_varargs, num_varargs = 0, lpc_int));\n", F_SIMUL_EFUN, T_ANY); add_instr_name("global_lvalue", "C_LVALUE(¤t_object->variables[variable_index_offset + %i]);\n", F_GLOBAL_LVALUE, T_LVALUE); add_instr_name("|", "f_or();\n", F_OR, T_ARRAY | T_NUMBER); add_instr_name("<<", "f_lsh();\n", F_LSH, T_NUMBER); add_instr_name(">>", "f_rsh();\n", F_RSH, T_NUMBER); add_instr_name(">>=", "f_rsh_eq();\n", F_RSH_EQ, T_NUMBER); add_instr_name("<<=", "f_lsh_eq();\n", F_LSH_EQ, T_NUMBER); add_instr_name("^", "f_xor();\n", F_XOR, T_NUMBER); add_instr_name("^=", "f_xor_eq();\n", F_XOR_EQ, T_NUMBER); add_instr_name("|=", "f_or_eq();\n", F_OR_EQ, T_NUMBER); add_instr_name("+", "c_add();\n", F_ADD, T_ANY); add_instr_name("!=", "f_ne();\n", F_NE, T_NUMBER); add_instr_name("catch", 0, F_CATCH, T_ANY); add_instr_name("end_catch", 0, F_END_CATCH, -1); add_instr_name("-", "c_negate();\n", F_NEGATE, T_NUMBER | T_REAL); add_instr_name("~", "c_compl();\n", F_COMPL, T_NUMBER); add_instr_name("++x", "c_pre_inc();\n", F_PRE_INC, T_NUMBER | T_REAL); add_instr_name("--x", "c_pre_dec();\n", F_PRE_DEC, T_NUMBER | T_REAL); add_instr_name("*", "c_multiply();\n", F_MULTIPLY, T_REAL | T_NUMBER | T_MAPPING); add_instr_name("*=", "f_mult_eq();\n", F_MULT_EQ, T_REAL | T_NUMBER | T_MAPPING); add_instr_name("/", "c_divide();\n", F_DIVIDE, T_REAL | T_NUMBER); add_instr_name("/=", "f_div_eq();\n", F_DIV_EQ, T_NUMBER | T_REAL); add_instr_name("%", "c_mod();\n", F_MOD, T_NUMBER); add_instr_name("%=", "f_mod_eq();\n", F_MOD_EQ, T_NUMBER); add_instr_name("inc(x)", "c_inc();\n", F_INC, -1); add_instr_name("dec(x)", "c_dec();\n", F_DEC, -1); add_instr_name("x++", "c_post_inc();\n", F_POST_INC, T_NUMBER | T_REAL); add_instr_name("x--", "c_post_dec();\n", F_POST_DEC, T_NUMBER | T_REAL); add_instr_name("switch", 0, F_SWITCH, -1); add_instr_name("time_expression", 0, F_TIME_EXPRESSION, -1); add_instr_name("end_time_expression", 0, F_END_TIME_EXPRESSION, T_NUMBER); } #define get_next_char(c) if ((c = *outp++) == '\n' && outp == last_nl + 1) refill_buffer() #define GETALPHA(p, q, m, e) \ if (*p == '_' || uisalpha(*p)) {\ while(isalunum((unsigned char)*p)) {\ *q = *p++;\ if (q < (m))\ q++;\ else {\ lexerror("Name too long");\ return;\ }\ }\ } else lexerror(e);\ *q++ = 0 static int cmygetc() { int c; for (;;) { get_next_char(c); if (c == '/') { switch(*outp++) { case '*': skip_comment(); break; case '/': skip_line(); break; default: outp--; return c; } } else { return c; } } } static void refill() { char *p; unsigned char c; p = yytext; do { c = *outp++; if (p < yytext + MAXLINE - 5) *p++ = c; else { lexerror("Line too long"); break; } } while (c != '\n' && c != LEX_EOF); if ((c == '\n') && (outp == last_nl + 1)) refill_buffer(); p[-1] = ' '; *p = 0; nexpands = 0; current_line++; } static void handle_define (char * yyt) { char namebuf[NSIZE]; char args[NARGS][NSIZE]; char mtext[MLEN]; char *p, *q; p = yyt; strcat(p, " "); q = namebuf; GETALPHA(p, q, namebuf + NSIZE - 1, "Invalid macro name"); if (*p == '(') { /* if "function macro" */ int squote, dquote; int arg; int inid; char *ids = (char *) NULL; p++; /* skip '(' */ SKIPWHITE; if (*p == ')') { arg = 0; } else { for (arg = 0; arg < NARGS;) { q = args[arg]; GETALPHA(p, q, args[arg] + NSIZE - 1, "Invalid macro argument"); arg++; SKIPWHITE; if (*p == ')') break; if (*p++ != ',') { yyerror("Missing ',' in #define parameter list"); return; } SKIPWHITE; } if (arg == NARGS) { lexerror("Too many macro arguments"); return; } } p++; /* skip ')' */ q = mtext; *q++ = ' '; squote = dquote = 0; for (inid = 0; *p;) { if (isalunum((unsigned char)*p)) { /* FIXME */ if (!inid) { inid++; ids = p; } } else { if (!squote && !dquote && inid) { int idlen = p - ids; int n, l; for (n = 0; n < arg; n++) { l = strlen(args[n]); if (l == idlen && strncmp(args[n], ids, l) == 0) { q -= idlen; *q++ = MARKS; *q++ = n + MARKS + 1; break; } } inid = 0; } } *q = *p; if (*q == '\\' && *++p) { if (q < mtext + MLEN - 2) q++; else { lexerror("Macro text too long"); return; } *q = *p; /* skip the quote checks if we just had a \ */ } else if (*q == '"') { dquote ^= !squote; } else if (*q == '\'') { squote ^= !dquote; } if (*p++ == MARKS) *++q = MARKS; if (q < mtext + MLEN - 2) q++; else { lexerror("Macro text too long"); return; } if (!*p && p[-2] == '\\') { q -= 2; refill(); p = yytext; } } *--q = 0; add_define(namebuf, arg, mtext); } else if (is_wspace(*p) || (*p == '\\')) { for (q = mtext; *p;) { *q = *p++; if (q < mtext + MLEN - 2) q++; else { lexerror("Macro text too long"); return; } if (!*p && p[-2] == '\\') { q -= 2; refill(); p = yytext; } } *--q = 0; add_define(namebuf, -1, mtext); } else { lexerror("Illegal macro symbol"); } return; } /* IDEA: linked buffers, to allow "unlimited" buffer expansion */ static void add_input (const char * p) { int l = strlen(p); if (l >= DEFMAX - 10) { lexerror("Macro expansion buffer overflow"); return; } if (outp < l + 5 + cur_lbuf->buf) { /* Not enough space, so let's move it up another linked_buf */ linked_buf_t *new_lbuf; char *q, *new_outp, *buf; int size; q = outp; while (*q != '\n' && (unsigned char)*q != LEX_EOF) q++; /* Incorporate EOF later */ if (*q != '\n' || ((q - outp) + l) >= DEFMAX - 11) { lexerror("Macro expansion buffer overflow"); return; } size = (q - outp) + l + 1; cur_lbuf->outp = q + 1; cur_lbuf->last_nl = last_nl; new_lbuf = ALLOCATE(linked_buf_t, TAG_COMPILER, "add_input"); new_lbuf->term_type = TERM_ADD_INPUT; new_lbuf->prev = cur_lbuf; buf = new_lbuf->buf; cur_lbuf = new_lbuf; last_nl = (new_lbuf->buf_end = buf + DEFMAX - 2) - 1; new_outp = new_lbuf->outp = buf + DEFMAX - 2 - size; memcpy(new_outp, p, l); memcpy(new_outp + l, outp, (q - outp) + 1); outp = new_outp; *(last_nl + 1) = 0; return; } outp -= l; strncpy(outp, p, l); } #ifdef DEBUGMALLOC_EXTENSIONS void mark_all_defines() { int i; defn_t *tmp; for (i = 0; i < inc_list_size; i++) EXTRA_REF(BLOCK(inc_list[i]))++; for (i = 0; i < DEFHASH; i++) { tmp = defns[i]; while (tmp) { DO_MARK(tmp, TAG_PREDEFINES); DO_MARK(tmp->name, TAG_PREDEFINES); DO_MARK(tmp->exps, TAG_PREDEFINES); tmp = tmp->next; } } } #endif static void add_predefine (const char * name, int nargs, const char * exps) { defn_t *p; int h; if ((p = lookup_define(name))) { if (nargs != p->nargs || strcmp(exps, p->exps)) { char buf[200 + NSIZE]; sprintf(buf, "redefinition of #define %s\n", name); yywarn(buf); } p->exps = (char *)DREALLOC(p->exps, strlen(exps) + 1, TAG_PREDEFINES, "add_define: redef"); strcpy(p->exps, exps); p->nargs = nargs; } else { p = ALLOCATE(defn_t, TAG_PREDEFINES, "add_define: def"); p->name = (char *) DXALLOC(strlen(name) + 1, TAG_PREDEFINES, "add_define: def name"); strcpy(p->name, name); p->exps = (char *) DXALLOC(strlen(exps) + 1, TAG_PREDEFINES, "add_define: def exps"); strcpy(p->exps, exps); p->flags = DEF_IS_PREDEF; p->nargs = nargs; h = defhash(name); p->next = defns[h]; defns[h] = p; } } static void free_defines() { defn_t *p, *q; int i; for (i = 0; i < DEFHASH; i++) { for (p = defns[i]; p; p = q) { /* predefines are at the end of the list */ if (p->flags & DEF_IS_PREDEF) { break; } q = p->next; FREE(p->name); FREE(p->exps); FREE((char *) p); } defns[i] = p; /* in case they undefined a predef */ while (p) { p->flags &= ~DEF_IS_UNDEFINED; p = p->next; } } nexpands = 0; } #define SKIPW \ do {\ c = cmygetc();\ } while (is_wspace(c)); static int extract_args (char ** argv, char * argb) { int argc = 0, dquote = 0, parcnt = 0, squote = 0; char *out; unsigned char c; SKIPW; if (c != '(') { yyerror("Missing '(' in macro call"); if (c == '\n' && outp == last_nl + 1) refill_buffer(); return -1; } SKIPW; if (c == ')') return 0; argv[0] = out = argb; while (argc < NARGS) { switch (c) { case '\"': if (!squote) dquote ^= 1; break; case '\'': if (!dquote) squote ^= 1; break; case '\\': if (squote || dquote) { *out++ = c; get_next_char(c); } break; case '(': if (!squote && !dquote) parcnt++; break; case ')': if (!squote && !dquote) parcnt--; break; case '\n': if (outp == last_nl + 1) refill_buffer(); if (squote || dquote) { lexerror("Newline in string"); return -1; } /* Change this to a space so we don't count it more than once */ current_line++; total_lines++; c = ' '; break; case LEX_EOF: lexerror("Unexpected end of file"); return -1; } /* negative parcnt means we're done collecting args */ if (parcnt < 0 || (c == ',' && !parcnt && !dquote && !squote)) { /* strip off trailing whitespace char if there was one */ if (uisspace(*(out - 1))) *(out - 1) = 0; else *out++ = 0; if (parcnt < 0) return argc + 1; argv[++argc] = out; } else { /* don't save leading whitespace and don't accumulate trailing whitespace */ if (!uisspace(c) || dquote || squote || (out > argv[argc] && !uisspace(*(out - 1)))) { if (out >= argb + DEFMAX - NARGS) { lexerror("Macro argument overflow"); return -1; } *out++ = c; } } if (!squote && !dquote) c = cmygetc(); else get_next_char(c); } lexerror("Maximum macro argument count exceeded"); return -1; } /* Check if yytext is a macro and expand if it is. */ static char *expand_define2 (char * text) { int argc = 0, i, paste = 0, pasting = 0; defn_t *macro; char expbuf[DEFMAX], *argv[NARGS], *expand_buffer, *in, *out, *freeme = 0; /* special handling for __LINE__ macro */ if (!strcmp(text, "__LINE__")) { expand_buffer = (char *)DXALLOC(20, TAG_COMPILER, "expand_define2"); sprintf(expand_buffer, "%i", current_line); return expand_buffer; } /* have we already expanded this macro? */ for (i = 0; i < expand_depth; i++) { if (!strcmp(expands[i], text)) return 0; } expands[expand_depth++] = text; if (nexpands++ > EXPANDMAX) { expand_depth--; lexerror("Too many macro expansions"); return 0; } macro = lookup_define(text); if (!macro) { expand_depth--; return 0; } if (macro->nargs >= 0) { if ((argc = extract_args(argv, expbuf)) == -1) { expand_depth--; return 0; } if (argc != macro->nargs) { expand_depth--; yyerror("Wrong number of macro arguments"); return 0; } } if (!argc) { expand_buffer = (char *)DXALLOC(strlen(macro->exps) + 1, TAG_COMPILER, "expand_define2"); strcpy(expand_buffer, macro->exps); expand_depth--; return expand_buffer; } /* Perform expansion with args */ in = macro->exps; out = expand_buffer = (char *)DXALLOC(DEFMAX, TAG_COMPILER, "expand_define2"); #define SAVECHAR(x) SAFE(\ if (out + 1 < expand_buffer + DEFMAX) {\ *out++ = (x);\ } else {\ if (freeme) FREE(freeme);\ FREE(expand_buffer);\ lexerror("Macro expansion overflow");\ expand_depth--;\ return 0;\ }) #define SAVESTR(x, y) SAFE(\ if (out + (y) < expand_buffer + DEFMAX) {\ memcpy(out, (x), (y));\ out += (y);\ } else {\ if (freeme) FREE(freeme);\ FREE(expand_buffer);\ lexerror("Macro expansion overflow");\ expand_depth--;\ return 0;\ }\ if (freeme) {\ FREE(freeme);\ freeme = 0;\ }) while (*in) { char *skip = in + 1; if (*in == MARKS && *++in != MARKS) { char *exp; exp = argv[*in++ - MARKS - 1]; if (paste) { paste = 0; *(out - 1) = '\"'; while (*exp) { switch (*exp) { case '\"': SAVECHAR('\\'); break; case '\\': SAVECHAR(*exp); exp++; break; } SAVECHAR(*exp); exp++; } SAVECHAR('\"'); } else { int len; /* don't expand if token pasting with ## */ if (!pasting && (freeme = expand_define2(exp)) != 0) exp = freeme; len = strlen(exp); SAVESTR(exp, len); } } else { paste = (*in == '#'); /* FIXME: Here we need to recursively expand any macros that we * come across. This cannot possibly be done the way lex.c is * currently architected. I've tried. This all needs to be * redone anyway, but this just going to have to do for now :-( */ SAVECHAR(*in); in++; } /* skip over whitespace and see if we've got ## for token pasting. if * we do, skip over it and continue at the first non-whitespace char. * note that comments are not considered whitespace here like they * should be. */ pasting = 0; while (*skip && uisspace(*skip)) skip++; if (*skip == '#' && *(skip + 1) == '#') { skip += 2; /* guaranteed by add_define to not have end of input before non-whitespace */ while (uisspace(*skip)) skip++; in = skip; pasting = (*in == MARKS && *(in + 1) != MARKS); } } *out = 0; expand_depth--; return expand_buffer; } int expand_define (void) { char *expand_buffer; if ((expand_buffer = expand_define2(yytext)) != 0) { add_input(expand_buffer); FREE(expand_buffer); return 1; } return 0; } /* Stuff to evaluate expression. I havn't really checked it. /LA ** Written by "J\"orn Rennecke" */ #define SKPW do c = *outp++; while(is_wspace(c)); outp-- static int exgetc() { unsigned char c; char *yyp; c = *outp++; while (isalpha(c) || c == '_') { yyp = yytext; do { SAVEC; c = *outp++; } while (isalunum(c)); outp--; *yyp = '\0'; if (strcmp(yytext, "defined") == 0 || strcmp(yytext, "efun_defined") == 0) { int efund = (yytext[0] == 'e'); int flag; /* handle the defined "function" in #if */ do c = *outp++; while (is_wspace(c)); if (c != '(') { yyerror("Missing ( in defined"); continue; } do c = *outp++; while (is_wspace(c)); yyp = yytext; while (isalunum(c)) { SAVEC; c = *outp++; } *yyp = '\0'; while (is_wspace(c)) c = *outp++; if (c != ')') { yyerror("Missing ) in defined"); continue; } SKPW; if (efund) { ident_hash_elem_t *ihe = lookup_ident(yytext); flag = (ihe && ihe->dn.efun_num != -1); } else { flag = (lookup_define(yytext) != 0); } if (flag) add_input(" 1 "); else add_input(" 0 "); } else { if (!expand_define()) add_input(" 0 "); } c = *outp++; } return c; } void set_inc_list (char * list) { int i, size; char *p; if (list == 0) { fprintf(stderr, "The config string 'include dirs' must bet set.\n"); fprintf(stderr, "It should contain a list of all directories to be searched\n"); fprintf(stderr, "for include files, separated by a ':'.\n"); exit(-1); } size = 1; p = list; while (1) { p = strchr(p, ':'); if (!p) break; size++; p++; } inc_list = CALLOCATE(size, char *, TAG_INC_LIST, "set_inc_list"); inc_list_size = size; for (i = size - 1; i >= 0; i--) { p = strrchr(list, ':'); if (p) { *p = '\0'; p++; } else { if (i) { fprintf(stderr, "Fatal error in set_inc_list: bad state.\n"); exit(1); } p = list; } if (*p == '/') p++; /* * Even make sure that the mud administrator has not made an error. */ if (!legal_path(p)) { fprintf(stderr, "'include dirs' must give paths without any '..'\n"); exit(-1); } inc_list[i] = make_shared_string(p); } for (i = 0; i < size - 1; i++) list[strlen(list)] = ':'; } char *main_file_name() { incstate_t *is; if (inctop == 0) return current_file; is = inctop; while (is->next) is = is->next; return is->file; } /* identifier hash table stuff, size must be an even power of two */ #define IDENT_HASH_SIZE 1024 #define IdentHash(s) (whashstr((s)) & (IDENT_HASH_SIZE - 1)) /* The identifier table is hashed for speed. The hash chains are circular * linked lists, so that we can rotate them, since identifier lookup is * rather irregular (i.e. we're likely to be asked about the same one * quite a number of times in a row). This isn't as fast as moving entries * to the front but is done this way for two reasons: * * 1. this allows us to keep permanent identifiers consecutive and clean * up faster * 2. it would only be faster in cases where two identifiers with the same * hash value are used often within close proximity in the source. * This should be rare, esp since the hash table is fairly sparse. * * ident_hash_table[hash] points to our current position (last lookup) * ident_hash_head[hash] points to the first permanent identifier * ident_hash_tail[hash] points to the last one * ident_dirty_list is a linked list of identifiers that need to be cleaned * when we're done; this happens if you define a global or function with * the same name as an efun or sefun. */ #define CHECK_ELEM(x, y, z) if (!strcmp((x)->name, (y))) { \ if (((x)->token & IHE_RESWORD) || ((x)->sem_value)) { z } \ else return 0; } ident_hash_elem_t *lookup_ident (const char * name) { int h = IdentHash(name); ident_hash_elem_t *hptr, *hptr2; if ((hptr = ident_hash_table[h])) { CHECK_ELEM(hptr, name, return hptr;); hptr2 = hptr->next; while (hptr2 != hptr) { CHECK_ELEM(hptr2, name, ident_hash_table[h] = hptr2; return hptr2;); hptr2 = hptr2->next; } } return 0; } ident_hash_elem_t *find_or_add_perm_ident (const char * name) { int h = IdentHash(name); ident_hash_elem_t *hptr, *hptr2; if ((hptr = ident_hash_table[h])) { if (!strcmp(hptr->name, name)) return hptr; hptr2 = hptr->next; while (hptr2 != hptr) { if (!strcmp(hptr2->name, name)) return hptr2; hptr2 = hptr2->next; } hptr = ALLOCATE(ident_hash_elem_t, TAG_PERM_IDENT, "find_or_add_perm_ident:1"); hptr->next = ident_hash_head[h]->next; ident_hash_head[h]->next = hptr; if (ident_hash_head[h] == ident_hash_tail[h]) ident_hash_tail[h] = hptr; } else { hptr = (ident_hash_table[h] = ALLOCATE(ident_hash_elem_t, TAG_PERM_IDENT, "find_or_add_perm_ident:2")); ident_hash_head[h] = hptr; ident_hash_tail[h] = hptr; hptr->next = hptr; } hptr->name = name; hptr->token = 0; hptr->sem_value = 0; hptr->dn.simul_num = -1; hptr->dn.local_num = -1; hptr->dn.global_num = -1; hptr->dn.efun_num = -1; hptr->dn.function_num = -1; hptr->dn.class_num = -1; return hptr; } typedef struct lname_linked_buf_s { struct lname_linked_buf_s *next; char block[4096]; } lname_linked_buf_t; lname_linked_buf_t *lnamebuf = 0; int lb_index = 4096; static char *alloc_local_name (const char * name) { int len = strlen(name)+1; char *res; if (lb_index + len > 4096) { lname_linked_buf_t *new_buf; new_buf = ALLOCATE(lname_linked_buf_t, TAG_COMPILER, "alloc_local_name"); new_buf->next = lnamebuf; lnamebuf = new_buf; lb_index = 0; } res = &(lnamebuf->block[lb_index]); strcpy(res, name); lb_index += len; return res; } int num_free = 0; typedef struct ident_hash_elem_list_s { struct ident_hash_elem_list_s *next; ident_hash_elem_t items[128]; } ident_hash_elem_list_t; ident_hash_elem_list_t *ihe_list = 0; #if 0 void dump_ihe (ident_hash_elem_t * ihe, int noisy) { int sv = 0; if (ihe->token & IHE_RESWORD) { if (noisy) printf("%s ", ihe->name); } else { if (noisy) printf("%s[", ihe->name); if (ihe->dn.function_num != -1) { if (noisy) printf("f"); sv++; } if (ihe->dn.simul_num != -1) { if (noisy) printf("s"); sv++; } if (ihe->dn.efun_num != -1) { if (noisy) printf("e"); sv++; } if (ihe->dn.local_num != -1) { if (noisy) printf("l"); sv++; } if (ihe->dn.global_num != -1) { if (noisy) printf("g"); sv++; } if (ihe->sem_value != sv) { if (noisy) { printf("(*%i*)", ihe->sem_value - sv); } else dump_ihe(ihe, 1); } if (noisy) printf("] "); } } void debug_dump_ident_hash_table (int noisy) { int zeros = 0; int i; ident_hash_elem_t *ihe, *ihe2; if (noisy) printf("\n\nIdentifier Hash Table:\n"); for (i = 0; i < IDENT_HASH_SIZE; i++) { ihe = ident_hash_table[i]; if (!ihe) zeros++; else { if (zeros && noisy) printf("<%i zeros>\n", zeros); zeros = 0; dump_ihe(ihe, noisy); ihe2 = ihe->next; while (ihe2 != ihe) { dump_ihe(ihe2, noisy); ihe2 = ihe2->next; } if (noisy) printf("\n"); } } if (zeros && noisy) printf("<%i zeros>\n", zeros); } #endif void free_unused_identifiers() { ident_hash_elem_list_t *ihel, *next; lname_linked_buf_t *lnb, *lnbn; int i; /* clean up dirty idents */ while (ident_dirty_list) { if (ident_dirty_list->dn.function_num != -1) { ident_dirty_list->dn.function_num = -1; ident_dirty_list->sem_value--; } if (ident_dirty_list->dn.global_num != -1) { ident_dirty_list->dn.global_num = -1; ident_dirty_list->sem_value--; } if (ident_dirty_list->dn.class_num != -1) { ident_dirty_list->dn.class_num = -1; ident_dirty_list->sem_value--; } ident_dirty_list = ident_dirty_list->next_dirty; } for (i = 0; i < IDENT_HASH_SIZE; i++) if ((ident_hash_table[i] = ident_hash_head[i])) ident_hash_tail[i]->next = ident_hash_head[i]; ihel = ihe_list; while (ihel) { next = ihel->next; FREE(ihel); ihel = next; } ihe_list = 0; num_free = 0; lnb = lnamebuf; while (lnb) { lnbn = lnb->next; FREE(lnb); lnb = lnbn; } lnamebuf = 0; lb_index = 4096; #if 0 debug_dump_ident_hash_table(0); #endif } static ident_hash_elem_t *quick_alloc_ident_entry() { if (num_free) { num_free--; return &(ihe_list->items[num_free]); } else { ident_hash_elem_list_t *ihel; ihel = ALLOCATE(ident_hash_elem_list_t, TAG_COMPILER, "quick_alloc_ident_entry"); ihel->next = ihe_list; ihe_list = ihel; num_free = 127; return &(ihe_list->items[127]); } } ident_hash_elem_t * find_or_add_ident (const char * name, int flags) { int h = IdentHash(name); ident_hash_elem_t *hptr, *hptr2; if ((hptr = ident_hash_table[h])) { if (!strcmp(hptr->name, name)) { if ((hptr->token & IHE_PERMANENT) && (flags & FOA_GLOBAL_SCOPE) && (hptr->dn.function_num==-1)&&(hptr->dn.global_num==-1) && (hptr->dn.class_num==-1)) { hptr->next_dirty = ident_dirty_list; ident_dirty_list = hptr; } return hptr; } hptr2 = hptr->next; while (hptr2 != hptr) { if (!strcmp(hptr2->name, name)) { if ((hptr2->token & IHE_PERMANENT)&&(flags & FOA_GLOBAL_SCOPE) && (hptr2->dn.function_num==-1)&&(hptr2->dn.global_num==-1) && (hptr2->dn.class_num == -1)) { hptr2->next_dirty = ident_dirty_list; ident_dirty_list = hptr2; } ident_hash_table[h] = hptr2; /* rotate */ return hptr2; } hptr2 = hptr2->next; } } hptr = quick_alloc_ident_entry(); if (!(hptr2 = ident_hash_tail[h]) && !(hptr2 = ident_hash_table[h])) { ident_hash_table[h] = hptr->next = hptr; } else { hptr->next = hptr2->next; hptr2->next = hptr; } if (flags & FOA_NEEDS_MALLOC) { hptr->name = alloc_local_name(name); } else { hptr->name = name; } hptr->token = 0; hptr->sem_value = 0; hptr->dn.simul_num = -1; hptr->dn.local_num = -1; hptr->dn.global_num = -1; hptr->dn.efun_num = -1; hptr->dn.function_num = -1; hptr->dn.class_num = -1; return hptr; } static void add_keyword_t (const char * name, keyword_t * entry) { int h = IdentHash(name); if (ident_hash_table[h]) { entry->next = ident_hash_head[h]->next; ident_hash_head[h]->next = (ident_hash_elem_t *)entry; if (ident_hash_head[h] == ident_hash_tail[h]) ident_hash_tail[h] = (ident_hash_elem_t *)entry; } else { ident_hash_head[h] = (ident_hash_elem_t *)entry; ident_hash_tail[h] = (ident_hash_elem_t *)entry; ident_hash_table[h] = (ident_hash_elem_t *)entry; entry->next = (ident_hash_elem_t *)entry; } entry->token |= IHE_RESWORD; } void init_identifiers() { int i; ident_hash_elem_t *ihe; init_instrs(); /* allocate all three tables together */ ident_hash_table = CALLOCATE(IDENT_HASH_SIZE * 3, ident_hash_elem_t *, TAG_IDENT_TABLE, "init_identifiers"); ident_hash_head = (ident_hash_elem_t **)&ident_hash_table[IDENT_HASH_SIZE]; ident_hash_tail = (ident_hash_elem_t **)&ident_hash_table[2*IDENT_HASH_SIZE]; /* clean all three tables */ for (i=0; itoken |= IHE_EFUN; ihe->sem_value++; ihe->dn.efun_num = i; } }