#include <stdio.h> #include <fcntl.h> #include <errno.h> #include <ctype.h> #include "lint.h" #include "interpret.h" /* we have to place it before lang.h to define struct svalue */ #include "lang.h" #include "string.h" #include "config.h" #include "exec.h" #include "lex.h" #include "instrs.h" #include "patchlevel.h" #include "stralloc.h" #if defined(hpux) && !defined(__GNUC__) /* This compilers handling of (char) is broken */ #define CHAR_EOF EOF #else #define CHAR_EOF ((char)EOF) #endif int current_line; int total_lines; /* Used to compute average compiled lines/s */ char *current_file; int pragma_strict_types; /* Force usage of strict types. */ int pragma_save_types; /* Save argument types after compilation */ int pragma_combine_strings; /* perform addition of constant strings at * compile time. */ char *last_lex_string; struct lpc_predef_s *lpc_predefs=NULL; extern char *argument_name; static INLINE int number PROT((int)); static INLINE int string PROT((char *)); static void handle_define PROT((char *)); static void add_define PROT((char *, int, char *)); #ifdef __GNUC__ /* old gcc versions, like on a NeXT, want it like this... */ static void add_permanent_define PROT((char *, int, char *, int)); #else /* other compilers, like DICE, insist on this... */ static void add_permanent_define PROT((char *, int, char *, char)); #endif static int expand_define PROT((void)); struct defn; static int _expand_define PROT((struct defn*)); static void add_input PROT((char *)); static INLINE void myungetc PROT((int)); static int cond_get_exp PROT((int, struct svalue *)); static int exgetc(); static char *get_current_file(), *get_current_line(), *get_version(); static int yyin_des; static char *linebufstart; static char *linebufend; static char saved_char; /* the one that gets overwritten at the start of * the last line in the buffer by the '\0' .*/ static int lex_fatal; static char **inc_list; static int inc_list_size; static char *auto_include_string = (char *)0; static int auto_include_start; #define EXPANDMAX 25000 static int nexpands; extern char *local_names[]; extern int current_number_of_locals; #ifndef tolower extern int tolower PROT((int)); #endif static void lexerror PROT((char *)); #define MAXLINE 2048 static char yytext[MAXLINE]; static struct ident *lookup_define(); static struct ifstate { struct ifstate *next; int state; } *iftop = 0; #define EXPECT_ELSE 1 #define EXPECT_ENDIF 2 static struct incstate { struct incstate *next; int yyin_des; int line; char *file; long linebufoffset; int pragma_strict_types; char saved_char; } *inctop = 0; #define DEFMAX 12000 #define DEFBUF_1STLEN (DEFMAX+MAXLINE+1)/* allow DEFMAX + an input line + 0 */ #define MAX_TOTAL_BUF 400000 static char *defbuf = 0; static unsigned long defbuf_len = 0; static char *outp; static struct s_reswords reswords[] = { { "break", F_BREAK, }, { "case", F_CASE, }, { "catch", F_CATCH, }, { "closure", F_CLOSURE_DECL, }, { "continue", F_CONTINUE, }, { "default", F_DEFAULT, }, { "do", F_DO, }, { "else", F_ELSE, }, { "float", F_FLOAT_DECL, }, { "for", F_FOR, }, { "if", F_IF, }, { "inherit", F_INHERIT, }, { "int", F_INT, }, { "mapping", F_MAPPING, }, { "mixed", F_MIXED, }, { "nomask", F_NO_MASK, }, { "object", F_OBJECT, }, { "parse_command", F_PARSE_COMMAND, }, { "private", F_PRIVATE, }, { "protected", F_PROTECTED, }, { "public", F_PUBLIC, }, { "return", F_RETURN, }, { "sscanf", F_SSCANF, }, { "static", F_STATIC, }, { "status", F_STATUS, }, { "string", F_STRING_DECL, }, { "switch", F_SWITCH, }, { "symbol", F_SYMBOL_DECL, }, { "varargs", F_VARARGS, }, { "virtual", F_VIRTUAL, }, { "void", F_VOID, }, { "while", F_WHILE, }, }; struct ident *ident_table[ITABLE_SIZE]; #if ITABLE_SIZE == 256 #define identhash(s) chashstr(s, 12) #else #define identhash(s) hashstr(s, 12, ITABLE_SIZE) #endif /* * 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 checked by the compiler if type checking is enabled, * and always at runtime. */ #include "efun_defs.c" struct ident *make_shared_identifier(s, n) char * s; int n; { struct ident *curr, *prev; int h; char *str; #if defined(LEXDEBUG) printf("make_shared_identifier called:%s\n",s); #endif h = identhash(s); curr = ident_table[h]; prev = 0; while (curr) { #if defined(LEXDEBUG) printf("checking %s.\n", curr->name); #endif if (!strcmp(curr->name, s)) { /* found it */ #if defined(LEXDEBUG) printf("found.\n"); #endif if (prev) { /* not at head of list */ prev->next = curr->next; curr->next = ident_table[h]; ident_table[h] = curr; } if (n > curr->type) { struct ident *inferior=curr; #if defined(LEXDEBUG) printf("shifting down inferior.\n"); #endif if (curr = (struct ident*)xalloc(sizeof *curr)) { curr->name = inferior->name; curr->next = inferior->next; curr->type = I_TYPE_UNKNOWN; curr->inferior = inferior; curr->hash = h; ident_table[h] = curr; } } return curr; } prev = curr; curr = curr->next; } /* not found, create new one */ str = make_shared_string(s); if (!str) return 0; curr = (struct ident*)xalloc(sizeof *curr); if (!curr) { free_string(str); return 0; } curr->name = str; curr->next = ident_table[h]; curr->type = I_TYPE_UNKNOWN; curr->inferior = 0; curr->hash = h; ident_table[h] = curr; return curr; } void free_shared_identifier(p) struct ident * p; { struct ident *curr, **q; int h; char *s; h = p->hash; q = &ident_table[h]; curr = *q; s = p->name; #if defined(LEXDEBUG) printf("freeing '%s'\n",s); fflush(stdout); #endif #ifdef DEBUG while (curr) { if (!strcmp(curr->name, s)) { /* found matching name */ #else for(;;) { if (curr->name == s) { /* found matching name */ #endif struct ident *first = curr; #ifdef DEBUG while (curr) { #else for(;;) { #endif if (curr == p) { /* this is the right one */ if (first == curr) { if (curr->inferior) { curr->inferior->next = curr->next; *q = curr->inferior; xfree((char *)curr); return; /* success */ } else { *q = curr->next; free_string(curr->name); xfree((char *)curr); return; /* success */ } } else { *q = curr->inferior; xfree((char *)curr); return; /* success */ } } q = &curr->inferior; curr = *q; } #ifdef DEBUG fatal("free_shared_identifier: type not found!\n"); #endif } q = &curr->next; curr = *q; } /* not found */ #ifdef DEBUG fatal("free_shared_identifier: name not found!\n"); #endif } void merge(name, dest) char *name, *dest; { char *from; int dlen; strcpy(dest, current_file); if (from = strrchr(dest, '/')) { /* strip filename */ *from = 0; dlen = from - dest; } else { /* current_file was the file_name */ /* include from the root directory */ *dest = 0; dlen = 0; } from = name; if (*from == '/') { *dest=0; /* absolute path */ dlen = 0; do from++; while (*from == '/'); } 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; dlen = 0; } else { *tmp = 0; dlen = tmp - dest; } from +=3; /* skip "../" */ } else if(!strncmp(from, "./", 2)) { from += 2; } else { /* append first component to dest */ char *q; if(*dest) { /* only if dest is not empty !! */ dest[dlen] = '/'; dest[++dlen] = '\0'; } q = strchr(from, '/'); if(q) { /* from has 2 or more components */ while(*from=='/') /* find the start */ from++; strncpy(dest+dlen, from, q - from); dest[dlen += q-from] = '\0'; for(from = q+1;*from=='/';from++) ; } else { /* this was the last component */ strcat(dest, from); break; } } } } #if 1 && defined(atarist) && defined(__GNUC_INLINE__) /* this is a speed hack */ /* always read '\r', this doesn't matter. */ #undef getc #define CR_IN_INPUT #define getc(__fp) \ ( (--__fp->_cnt >= 0) ? (*__fp->_ptr++) : _filbuf(__fp) ) #endif #define myfilbuf() (*outp?0:_myfilbuf()) static /* NO inline */ char * _myfilbuf() { int i; char *p; *outp = saved_char; if (linebufend - outp) memcpy(outp-MAXLINE, outp, linebufend - outp); outp -= MAXLINE; *(outp-1) = '\n'; p = linebufstart; /* == linebufend - MAXLINE */ i = read(yyin_des, p, MAXLINE); if (i < MAXLINE) { if (i < 0) { i = 0; } p += i; if (p - outp ? p[-1] != '\n' : current_line == 1) *p++ = '\n'; *p++ = EOF; return outp; } p += i; while (*--p != '\n'); /* find last newline */ if (p < linebufstart) { lexerror("line too long"); *(p = linebufend-1) = '\n'; } p++; saved_char = *p; *p = 0; return outp; } static INLINE char mygetc() { #if 0 fprintf(stderr, "c='%c' %x", *outp, *outp); #endif #if defined(LEXDEBUG) putc(*outp, stderr); #if 1 fflush(stdout); #endif #endif return *outp++; } static INLINE void myungetc(c) int c; { *--outp = c; } static INLINE int gobble(c) char c; { if (c == mygetc()) return 1; --outp; return 0; } static void lexerror(s) char *s; { yyerror(s); lex_fatal++; } static int skip_to(token, atoken) char *token, *atoken; { char *p, *q; char c; char nl = '\n'; int nest; p = outp; for(nest = 0;;) { #if 0 /* we don't compile code here */ store_line_number_info(); #endif current_line++; total_lines++; c = *p++; if (c == '#') { while(lexwhite(*p++)); q = --p; while (isalunum(*p++)); c = *--p; *p = 0; if (c != nl) { while (*++p != nl); } p++; /*fprintf(stderr, "skip checks %s\n", q);*/ if (strcmp(q, "if") == 0 || strcmp(q, "ifdef") == 0 || strcmp(q, "ifndef") == 0) { nest++; } else if (nest > 0) { if (strcmp(q, "endif") == 0) nest--; } else { if (strcmp(q, token) == 0) { *(p-1) = nl; outp = p; if (!*p) { _myfilbuf(); } return 1; } else if (atoken) { if (strcmp(q, atoken) == 0) { *(p-1) = nl; outp = p; if (!*p) { _myfilbuf(); } return 0; } else if (strcmp(q, "elif") == 0) { current_line--; total_lines--; q[0] = nl; q[1] = '#'; q[4] = c; outp = q+1; return 0; } } } } else { /*fprintf(stderr, "skipping (%d) %c", c, c);*/ if ( c == CHAR_EOF) { outp = p - 2; current_line--; total_lines--; lexerror("Unexpected end of file while skipping"); return 1; } while (c != nl) c = *p++; } if (!*p) { outp = p; p = _myfilbuf(); } } } static void handle_cond(c) int c; { struct ifstate *p; /*fprintf(stderr, "cond %d\n", c);*/ if (c || skip_to("else", "endif")) { p = (struct ifstate *)xalloc(sizeof(struct ifstate)); p->next = iftop; iftop = p; p->state = c ? EXPECT_ELSE : EXPECT_ENDIF; } } static int inc_open(buf, name, delim) char *buf, *name, delim; { int fd; int i; char *p; if (delim == '"') { merge(name, buf); if ((fd = ixopen(buf, O_RDONLY)) > 0) return fd; if (errno == EMFILE) lexerror("File descriptors exhausted"); if (errno == ENFILE) lexerror("File table overflow"); } /* * 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, inc_list[i], name); fd = ixopen(buf, O_RDONLY); if (fd >= 0) return fd; if (errno == EMFILE) lexerror("File descriptors exhausted"); if (errno == ENFILE) lexerror("File table overflow"); } return -1; } static void realloc_defbuf() { char * old_defbuf = defbuf; long old_defbuf_len = defbuf_len; outp -= &defbuf[defbuf_len] - (char*)0; if (defbuf_len > (MAX_TOTAL_BUF >> 1) ) { defbuf_len = MAX_TOTAL_BUF; } else { defbuf_len <<= 1; } defbuf = xalloc(defbuf_len); memcpy(defbuf+defbuf_len-old_defbuf_len, old_defbuf, old_defbuf_len); xfree(old_defbuf); outp += &defbuf[defbuf_len] - (char*)0; } static INLINE void handle_include(name) char *name; { char *p; char buf[1024]; int fd; struct incstate *is; int delim; long linebufoffset; char *old_outp; int in_buffer = 0; /*fprintf(stderr, "handle include '%s'\n", name);*/ #if 0 if (nbuf) { lexerror("Internal preprocessor error"); return; } #endif old_outp = outp; while (*name != '"' && *name != '<') { char c; struct ident *d; for (p = name; isalunum(*p); p++); c = *p; *p = 0; d = lookup_define(name); *p = c; if (in_buffer) { outp = p; } else { myungetc('\n'); add_input(p); in_buffer = 1; } if (!d || !_expand_define(&d->u.define) ) { yyerror("Missing leading \" or < in #include"); return; } name = outp; while (lexwhite(*name)) name++; } delim = *name++ == '"' ? '"' : '>'; for(p = name; *p && *p != delim; p++) ; if (!*p) { yyerror("Missing trailing \" or > in #include"); outp = old_outp; return; } *p = 0; if (delim == '"') { char *q; q = p + 1; for (;;) { while(lexwhite(*q)) q++; if (!*q || *q == '\n') break; while (*q != delim) { char *r, c; struct ident *d; for (r = q; isalunum(*r); r++); c = *r; *r = 0; d = lookup_define(q); *r = c; if (in_buffer) { outp = r; if (name != yytext) { if ( (p - name) >= MAXLINE - 1) { yyerror("Include name too long."); outp = old_outp; return; } *p = 0; strcpy(yytext, name); p += yytext - name; name = yytext; } } else { myungetc('\n'); add_input(r); in_buffer = 1; } if (!d || !_expand_define(&d->u.define) ) { yyerror("Missing leading \" in #include"); outp = old_outp; return; } q = outp; while (lexwhite(*q)) q++; } while(*++q && *q != delim) { if ( (p - name) >= MAXLINE - 1) { yyerror("Include name too long."); outp = old_outp; return; } *p++ = *q; } if (!*q++) { yyerror("Missing trailing \" in #include"); outp = old_outp; return; } } } outp = old_outp; *p = 0; if ((p - name) > sizeof(buf) - 100) { yyerror("Include name too long."); return; } linebufoffset = linebufstart - &defbuf[defbuf_len]; if (outp - defbuf < 3*MAXLINE) { realloc_defbuf(); fprintf(stderr, "reallocating defbuf.\n"); if (outp - defbuf < 2*MAXLINE) { lexerror("Maximum total buffer size exceeded"); return; } } if ((fd = inc_open(buf, name, delim)) >= 0) { store_include_info(name); is = (struct incstate *)xalloc(sizeof(struct incstate)); if (!is) { lexerror("Out of memory"); return; } is->yyin_des = yyin_des; is->line = current_line; is->file = current_file; is->linebufoffset = linebufoffset; is->saved_char = saved_char; is->next = inctop; is->pragma_strict_types = pragma_strict_types; current_file = xalloc(strlen(buf)+1); if (!current_file) { current_file = is->file; xfree((char*)is); lexerror("Out of memory"); return; } strcpy(current_file, buf); pragma_strict_types = 0; instrs[F_CALL_OTHER-F_OFFSET].ret_type = TYPE_ANY; inctop = is; current_line = 1; linebufend = outp - 1; /* allow trailing zero */ linebufstart = linebufend - MAXLINE; *(outp = linebufend) = 0; yyin_des = fd; _myfilbuf(); /*fprintf(stderr, "pushed to %s\n", buf);*/ } else { sprintf(buf, "Cannot #include %s", name); yyerror(buf); } } static void skip_comment() { register char c, *p; p = outp; for(;;) { while((c = *p++) != '*') { if (c == '\n') { store_line_number_info(); nexpands=0; if ((c = *p) == CHAR_EOF) { outp = p - 1; lexerror("End of file in a comment"); return; } current_line++; if (!c) { outp = p; p = _myfilbuf(); } } } do { if ((c = *p++) == '/') { outp = p; return; } if (c == '\n') { store_line_number_info(); nexpands=0; if ((c = *p) == CHAR_EOF) { outp = p - 1; lexerror("End of file in a comment"); return; } current_line++; if (!c) { outp = p; p = _myfilbuf(); } c = '\0'; } } while(c == '*'); } } static char * skip_pp_comment(p) char *p; { char c; for (;;) { c = *p++; if (c == '\n') { store_line_number_info(); nexpands=0; current_line++; if (!*p) { outp = p; p = _myfilbuf(); } return p; } } } #define TRY(c, t) if (*yyp == (c)) {yyp++; outp = yyp; return t;} static void deltrail(sp) char *sp; { char *p; p = sp; if (!*p) { lexerror("Illegal # command"); } else { while(*p && !isspace(*p)) p++; *p = 0; } } #define SAVEC \ if (yyp < yytext+MAXLINE-5)\ *yyp++ = c;\ else {\ lexerror("Line too long");\ break;\ } static void handle_pragma(str) char *str; { #if defined(LEXDEBUG) printf("handle pragma:'%s'\n",str); #endif if (strcmp(str, "strict_types") == 0) { pragma_strict_types = 2; instrs[F_CALL_OTHER-F_OFFSET].ret_type = TYPE_UNKNOWN; } else if (strcmp(str, "save_types") == 0) { pragma_save_types = 1; } else if (strcmp(str, "combine_strings") == 0) { pragma_combine_strings = 1; } else if (strcmp(str, "no_combine_strings") == 0) { pragma_combine_strings = 0; } else if (strcmp(str, "strong_types") == 0) { pragma_strict_types = 1; instrs[F_CALL_OTHER-F_OFFSET].ret_type = TYPE_ANY; #if defined( DEBUG ) && defined ( TRACE_CODE ) } else if (strcmp(str, "set_code_window") == 0) { extern void set_code_window(); set_code_window(); } else if (strcmp(str, "show_code_window") == 0) { extern void show_code_window(); show_code_window(); #endif } } static struct ident *all_defines = 0, *permanent_defines = 0, *undefined_permanent_defines = 0; static INLINE int number(i) int i; { #ifdef LEXDEBUG printf("returning number %d.\n", i); #endif yylval.number = i; return F_NUMBER; } static void add_lex_string(str) char *str; { mp_int len1; char *tmp; len1 = strlen(last_lex_string); tmp = alloca(len1 + strlen(str) + 1); strcpy(tmp, last_lex_string); strcpy(tmp + len1, str); free_string(last_lex_string); last_lex_string = make_shared_string(tmp); } int yylex(); static INLINE int string(str) char *str; { if (last_lex_string) { add_lex_string(str); return yylex(); } else { last_lex_string = make_shared_string(str); } return F_STRING; } static INLINE int yylex1() { register char *yyp; register char c; yyp = outp; #if 0 if (lex_fatal) { return -1; } #endif for(;;) { switch(c = *yyp++) { case CHAR_EOF: if (inctop) { static char call_other_return_types[] = { TYPE_ANY, TYPE_ANY, TYPE_UNKNOWN }; struct incstate *p; p = inctop; close(yyin_des); /*fprintf(stderr, "popping to %s\n", p->file);*/ xfree(current_file); nexpands=0; current_file = p->file; current_line = p->line + 1; pragma_strict_types = p->pragma_strict_types; instrs[F_CALL_OTHER-F_OFFSET].ret_type = call_other_return_types[pragma_strict_types]; yyin_des = p->yyin_des; saved_char = p->saved_char; inctop = p->next; *linebufend = '\n'; yyp = linebufend + 1; linebufstart = &defbuf[defbuf_len] + p->linebufoffset; linebufend = linebufstart + MAXLINE; xfree((char *)p); if (!*yyp) { outp = yyp; yyp = _myfilbuf(); } store_include_end(); break; } if (iftop) { struct ifstate *p = iftop; yyerror(p->state == EXPECT_ENDIF ? "Missing #endif" : "Missing #else"); while(iftop) { p = iftop; iftop = p->next; xfree((char *)p); } } outp = yyp-1; return -1; case '\n': { store_line_number_info(); nexpands=0; current_line++; total_lines++; if (!*yyp) { outp = yyp; yyp = _myfilbuf(); } } break; case '\r': *(yyp-1) = *(yyp-2); break; case ' ': case '\t': case '\f': case '\v': break; case '+': switch(c=*yyp++) { case '+': outp=yyp; return F_INC; case '=': yylval.number = F_ADD_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; default: yyp--; } outp = yyp; return '+'; case '-': switch(c=*yyp++) { case '>': outp=yyp; return F_ARROW; case '-': outp=yyp; return F_DEC; case '=': yylval.number = F_SUB_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; default: yyp--; } outp = yyp; return '-'; case '&': switch(c=*yyp++) { case '&': outp=yyp; return F_LAND; case '=': yylval.number = F_AND_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; default: yyp--; } outp = yyp; return '&'; case '|': switch(c=*yyp++) { case '|': outp=yyp; return F_LOR; case '=': yylval.number = F_OR_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; default: yyp--; } outp = yyp; return '|'; case '^': if (*yyp == '=') { yyp++; yylval.number = F_XOR_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } outp = yyp; return '^'; case '<': c = *yyp++;; if (c == '<') { if (*yyp == '=') { yyp++; yylval.number = F_LSH_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } outp=yyp; return F_LSH; } if (c == '=') { outp=yyp; return F_LE; } yyp--; outp=yyp; return '<'; case '>': c = *yyp++; if (c == '>') { if (*yyp == '=') { yyp++; yylval.number = F_RSH_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } outp=yyp; return F_RSH; } if (c == '=') { outp=yyp; return F_GE; } yyp--; outp=yyp; return '>'; case '*': if (*yyp == '=') { yyp++; yylval.number = F_MULT_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } outp=yyp; return '*'; case '%': if (*yyp == '=') { yyp++; yylval.number = F_MOD_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } outp=yyp; return '%'; case '/': c = *yyp++; if (c == '*') { outp=yyp; skip_comment(); yyp=outp; if (lex_fatal) { return -1; } break; } if (c == '/') { yyp = skip_pp_comment(yyp); break; } if (c == '=') { yylval.number = F_DIV_EQ-F_OFFSET; outp=yyp; return F_ASSIGN; } yyp--; outp=yyp; return '/'; case '=': TRY('=', F_EQ); yylval.number = F_ASSIGN-F_OFFSET; outp=yyp; return F_ASSIGN; case ';': case '(': case ')': case ',': case '{': case '}': case '~': case '[': case ']': case '?': outp=yyp; return c; case '!': TRY('=', F_NE); outp=yyp; return F_NOT; case ':': TRY(':', F_COLON_COLON); outp=yyp; return ':'; case '.': TRY('.',F_RANGE); goto badlex; case '#': if (*yyp == '\'') { extern struct function *simul_efunp; extern struct object *master_ob; struct ident *p; char *wordstart = ++yyp; int efun_override; do c=*yyp++; while (isalunum(c)); c = *--yyp; /* the assignment is good for the data flow analysis :-} */ if (yyp == wordstart) { extern int symbol_operator PROT((char *, char **)); int i; if ((i = symbol_operator(yyp, &outp)) < 0) yyerror("Missing function name after #'"); yylval.closure.number = i + CLOSURE_EFUN_OFFS; return F_CLOSURE; } efun_override = 0; if (yyp - wordstart == 4 && !strncmp(wordstart, "efun::", 6) ) { efun_override = 1; wordstart = yyp += 2; do c=*yyp++; while (isalunum(c)); c = *--yyp; } outp = yyp; *yyp = 0; p = make_shared_identifier(wordstart, I_TYPE_GLOBAL); *yyp = c; if (!p) { lexerror("Out of memory"); return 0; } while (p->type > I_TYPE_GLOBAL) { if (p->type == I_TYPE_RESWORD) { int code; switch(code = p->u.code) { default: /* There aren't efuns with reswords as names, and * it is impossible to define local / global vars * or functions with such a name. Thus, !p->inferior . */ yyerrorf( "No closure associated with reserved word '%s'", p->name ); code = CLOSURE_EFUN_OFFS; break; case F_IF: code = F_BRANCH_WHEN_ZERO-F_OFFSET+CLOSURE_EFUN_OFFS; break; case F_DO: code = F_BBRANCH_WHEN_NON_ZERO-F_OFFSET+CLOSURE_EFUN_OFFS; break; case F_WHILE: /* the politically correct code / / was already taken, see above. */ code = F_BBRANCH_WHEN_ZERO-F_OFFSET+CLOSURE_EFUN_OFFS; break; case F_CONTINUE: code = F_BRANCH-F_OFFSET+CLOSURE_EFUN_OFFS; break; case F_BREAK: case F_RETURN: code += -F_OFFSET + CLOSURE_EFUN_OFFS; break; } yylval.closure.number = code; return F_CLOSURE; } if ( !(p = p->inferior) ) break; } if (!p || p->type < I_TYPE_GLOBAL) { if (p && p->type == I_TYPE_UNKNOWN) free_shared_identifier(p); c = *yyp; *yyp = 0; yyerrorf("Undefined function: %s", wordstart); *yyp = c; yylval.closure.number = CLOSURE_EFUN_OFFS; return F_CLOSURE; } if (efun_override && p->u.global.sim_efun >= 0 && simul_efunp[p->u.global.sim_efun].flags & TYPE_MOD_NO_MASK && p->u.global.efun && master_ob) { struct svalue *res; push_constant_string("nomask simul_efun"); push_volatile_string(current_file); push_shared_string(p->name); res = apply_master_ob("privilege_violation", 3); if (!res || res->type != T_NUMBER || res->u.number < 0) { yyerrorf( "Privilege violation: nomask simul_efun %s", p->name ); efun_override = 0; } else if (!res->u.number) { efun_override = 0; } } switch(0) { default: if (!efun_override) { if (p->u.global.function >= 0) { int i; i = p->u.global.function; yylval.closure.number = i; if (i >= CLOSURE_IDENTIFIER_OFFS) yyerrorf( "Too high function index of %s for #'", p->name ); break; } if (p->u.global.sim_efun >= 0) { yylval.closure.number = p->u.global.sim_efun + CLOSURE_SIMUL_EFUN_OFFS; break; } } if (p->u.global.efun >= 0) { yylval.closure.number = p->u.global.efun + CLOSURE_EFUN_OFFS; if (yylval.closure.number > LAST_INSTRUCTION_CODE + CLOSURE_EFUN_OFFS) { yylval.closure.number = efun_aliases[ yylval.closure.number - CLOSURE_EFUN_OFFS - LAST_INSTRUCTION_CODE - 1 ] + CLOSURE_EFUN_OFFS; } break; } if (p->u.global.variable >= 0) { yylval.closure.number = p->u.global.variable + CLOSURE_IDENTIFIER_OFFS; break; } c = *yyp; *yyp = 0; yyerrorf("Undefined function: %s", wordstart); *yyp = c; yylval.closure.number = CLOSURE_EFUN_OFFS; break; } return F_CLOSURE; } else if (*(yyp-2) == '\n' && !nexpands) { char *sp = 0; int quote; outp=yyp; yyp = yytext; do { c = mygetc(); } while (lexwhite(c)); for(quote = 0;;) { if (c == '"') quote ^= 1; while(!quote && c == '/') { /*gc - handle comments cpp-like! 1.6.91 @@@*/ char c2; if ( (c2 = mygetc()) == '*') { skip_comment(); c=mygetc(); } else if (c2 == '/') { outp = skip_pp_comment(outp); current_line--; c = '\n'; } else { --outp; break; } } if (!sp && lexwhite(c)) sp = yyp; if (c == '\n') { break; } SAVEC; c = mygetc(); } if (sp) { *sp++ = 0; while(lexwhite(*sp)) sp++; } else { sp = yyp; } *yyp = 0; if (strcmp("include", yytext) == 0) { handle_include(sp); } else {myfilbuf();if (strcmp("define", yytext) == 0) { handle_define(sp); } else if (strcmp("if", yytext) == 0) { int cond; struct svalue sv; myungetc('\n'); add_input(sp); cond = cond_get_exp(0, &sv); free_svalue(&sv); if (mygetc() != '\n') { yyerror("Condition too complex in #if"); while (mygetc() != '\n'); } 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 (*yytext == 'e' && yytext[1] == 'l' && ( (yytext[2] == 's' && yytext[3] == 'e') || (yytext[2] == 'i' && yytext[3] == 'f') ) && !yytext[4]) { if (iftop && iftop->state == EXPECT_ELSE) { struct ifstate *p = iftop; /*fprintf(stderr, "found else\n");*/ iftop = p->next; xfree((char *)p); skip_to("endif", (char *)0); } else { yyerror("Unexpected #else"); } } else if (strcmp("endif", yytext) == 0) { if (iftop && (iftop->state == EXPECT_ENDIF || iftop->state == EXPECT_ELSE)) { struct ifstate *p = iftop; /*fprintf(stderr, "found endif\n");*/ iftop = p->next; xfree((char *)p); } else { yyerror("Unexpected #endif"); } } else if (strcmp("undef", yytext) == 0) { struct ident *p, **q; int h; deltrail(sp); #if 0 fprintf(stderr, "#undef '%s'\n", sp); #endif h = identhash(sp); for(q = &ident_table[h]; p=*q; q=&p->next) { if (strcmp(sp, p->name)) continue; if (p->type != I_TYPE_DEFINE) break; /* failure */ if (!p->u.define.permanent) { #if defined(LEXDEBUG) fprintf(stderr, "#undef define '%s' %d '%s'\n", p->name, p->u.define.nargs, p->u.define.exps.str); fflush(stderr); #endif if (p->inferior) { p->inferior->next = p->next; *q = p->inferior; } else { *q = p->next; free_string(p->name); } xfree(p->u.define.exps.str); p->name = 0; /* mark for later freeing by all_defines */ /* success */ break; } else { if (p->inferior) { p->inferior->next = p->next; *q = p->inferior; increment_string_ref(p->name); } else { *q = p->next; } p->inferior = undefined_permanent_defines; undefined_permanent_defines = p; /* success */ break; } } } else if (strcmp("echo", yytext) == 0) { fprintf(stderr, "%s\n", sp); } else if (strcmp("pragma", yytext) == 0) { deltrail(sp); handle_pragma(sp); } else { yyerror("Unrecognised # directive"); }} store_line_number_info(); nexpands=0; current_line++; total_lines++; yyp = outp; if (lex_fatal) { return -1; } break; } else goto badlex; case '\'': c = *yyp++; if (c == '\\') { switch(c = *yyp++) { case '\n': case CHAR_EOF: yyp--; /* this will be noted as error below */ break; case 'a': c = '\007'; break; case 'b': c = '\b'; break; case 'f': c = '\014'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; } if (*yyp++ != '\'') { yyp--; yyerror("Illegal character constant"); } } else if (*yyp++ != '\'' || c == '\'' && (*yyp == '(' || isalunum(*yyp) || *yyp == '\'') ) { char *wordstart; int quotes = 1; yyp -= 2; while (*yyp == '\'') { quotes++; yyp++; } wordstart = yyp; if (!isalpha(*yyp)) { if (*yyp == '(' && yyp[1] == '{') { outp = yyp + 2; yylval.number = quotes; return F_QUOTED_AGGREGATE; } yyerror("Illegal character constant"); outp = yyp; return F_NUMBER; } while(isalunum(*++yyp)); c = *yyp; *yyp = 0; yylval.symbol.name = make_shared_string(wordstart); *yyp = c; yylval.symbol.quotes = quotes; outp = yyp; return F_SYMBOL; } yylval.number = c; outp = yyp; return F_NUMBER; case '"': { char *p = yyp; yyp = yytext; for(;;) { c = *p++; if (c == '\n') { outp = p-1; /* myfilbuf(); not needed */ lexerror("Newline in string"); return string(""); } SAVEC; if (c == '"') { *--yyp = 0; break; } if (c == '\\') { yyp--; switch(c = *p++) { case '\r': if (*p++ != '\n') { p--; *yyp++ = c; break; } case '\n': store_line_number_info(); current_line++; total_lines++; if (*p == CHAR_EOF ) { lexerror("End of file in string"); return string(""); } if (!*p) { outp = p; p = _myfilbuf(); } if (*p++ != '\r') p--; break; case 'a': *yyp++ = '\007'; break; case 'b': *yyp++ = '\b'; break; case 'f': *yyp++ = '\014'; break; case 'n': *yyp++ = '\n'; break; case 'r': *yyp++ = '\r'; break; case 't': *yyp++ = '\t'; break; default : *yyp++ = c; } } } outp = p; return string(yytext); } case '0': c = *yyp++; if ( c == 'X' || c == 'x' ) { unsigned long l; /* strtol() gets the sign bit wrong, strtoul() isn't portable enough. */ l = 0; --yyp; while(leXdigit(c = *++yyp)) { if (c > '9') c -= 'a' - ('9' + 1); l <<= 4; l += c - '0'; } outp=yyp; return number(l); } yyp--; if (!lexdigit(c) && (c != '.' || yyp[1] == '.') ) { outp=yyp; return number(0); } c = '0'; /* fall through */ case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': { char *numstart=yyp; long l; l = c - '0'; while(lexdigit(c = *yyp++)) l = (((l << 2)+l) << 1) + #if defined(atarist) || defined(sun) /* everybody with ascii is invited to join in... */ (c & 0xf ); /* can be done in the same step as the type conversion */ #else (c - '0'); #endif #ifdef FLOATS if (c == '.' && *yyp != '.') { while(lexdigit(*yyp++)); c = *--yyp; *yyp = 0; yylval.float_number = atof(numstart-1); *yyp = c; outp=yyp; return F_FLOAT; } #endif /* FLOATS */ --yyp; outp = yyp; return number(l); } case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H': case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P': case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X': case 'Y':case 'Z':case 'a':case 'b':case 'c':case 'd':case 'e':case 'f': case 'g':case 'h':case 'i':case 'j':case 'k':case 'l':case 'm':case 'n': case 'o':case 'p':case 'q':case 'r':case 's':case 't':case 'u':case 'v': case 'w':case 'x':case 'y':case 'z':case '_': { struct ident *p; char *wordstart = yyp-1; do c=*yyp++; while (isalunum(c)); c = *--yyp; /* the assignment is good for the data flow analysis :-} */ *yyp = 0; p = make_shared_identifier(wordstart, I_TYPE_UNKNOWN); *yyp = c; if (!p) { lexerror("Out of memory"); return 0; } #if 0 printf("ident type is %d\n",p->type); #endif switch(p->type) { case I_TYPE_DEFINE: outp=yyp; _expand_define(&p->u.define); if (lex_fatal) { return -1; } yyp=outp; continue; case I_TYPE_RESWORD: outp = yyp; return p->u.code; case I_TYPE_LOCAL: yylval.number = p->u.local; outp = yyp; return F_LOCAL; default: yylval.ident = p; outp = yyp; return F_IDENTIFIER; } } default: goto badlex; } } badlex: if (lex_fatal) return -1; { char buff[100]; sprintf(buff, "Illegal character (hex %02x) '%c'", c, c); yyerror(buff); outp = yyp; return ' '; } } int yylex() { int r; #ifdef LEXDEBUG yytext[0] = 0; #endif r = yylex1(); #ifdef LEXDEBUG fprintf(stderr, "lex=%d(%s) ", r, yytext); #endif return r; } extern YYSTYPE yylval; void end_new_file() { while (inctop) { struct incstate *p; p = inctop; close(yyin_des); xfree(current_file); current_file = p->file; yyin_des = p->yyin_des; inctop = p->next; xfree((char *)p); } while(iftop) { struct ifstate *p; p = iftop; iftop = p->next; xfree((char *)p); } if (defbuf_len > DEFBUF_1STLEN) { xfree(defbuf); defbuf_len = 0; } if (last_lex_string) { free_string(last_lex_string); last_lex_string = 0; } } void lex_close() { int i; struct incstate *p; static char buf[] = "File descriptors exhausted, include nesting: 12345678"; for (i = 0, p = inctop; p; p = p->next) i++; sprintf(buf + sizeof buf - 8, "%d", i); end_new_file(); outp = ("##")+1; lexerror(buf); } void start_new_file(fd) int fd; { free_defines(); yyin_des = fd; if (!defbuf_len) { defbuf = xalloc(DEFBUF_1STLEN); defbuf_len = DEFBUF_1STLEN; } *(outp = linebufend = (linebufstart = defbuf + DEFMAX) + MAXLINE) = 0; current_line = 1; /* already used in _myfilbuf() */ _myfilbuf(); lex_fatal = 0; if (auto_include_string) { add_input(auto_include_string); current_line = auto_include_start; } pragma_strict_types = 0; /* I would prefer !o_flag /Lars */ instrs[F_CALL_OTHER-F_OFFSET].ret_type = TYPE_ANY; pragma_save_types = 0; nexpands = 0; } struct ident *all_efuns = 0; void init_num_args() { extern char master_name[]; int i, n; struct lpc_predef_s *tmpf; char mtext[MLEN]; static short binary_operators[] = { F_ADD, F_SUBTRACT, F_MULTIPLY, F_DIVIDE, F_MOD, F_LT, F_GT, F_EQ, F_GE, F_LE, F_NE, F_OR, F_XOR, F_LSH, F_RSH, F_INDEX, F_RINDEX, F_EXTRACT2, }; static short ternary_operators[] = { F_RANGE, F_NR_RANGE, F_RR_RANGE, F_RN_RANGE, F_MAP_INDEX, }; for (i=0; i<ITABLE_SIZE; i++) ident_table[i] = 0; for(n=0; n<NELEM(instrs); n++) { struct ident *p; if (instrs[n].Default == -1) continue; p = make_shared_identifier(instrs[n].name, I_TYPE_GLOBAL); if (!p) fatal("Out of memory\n"); p->type = I_TYPE_GLOBAL; p->u.global.efun = n; p->u.global.sim_efun = -1; p->u.global.function = -2; p->u.global.variable = -2; p->next_all = all_efuns; all_efuns = p; } for (i=0; i<NELEM(reswords); i++) { struct ident *p; p = make_shared_identifier(reswords[i].name, I_TYPE_RESWORD); if (!p) fatal("Out of memory\n"); p->type = I_TYPE_RESWORD; p->u.code = reswords[i].code; } for (i=0; i<NELEM(binary_operators); i++) { n = binary_operators[i] - F_OFFSET; instrs[n].min_arg = instrs[n].max_arg = 2; instrs[n].Default = 0; instrs[n].ret_type = TYPE_ANY; } for (i=0; i<NELEM(ternary_operators); i++) { n = ternary_operators[i] - F_OFFSET; instrs[n].min_arg = instrs[n].max_arg = 3; instrs[n].Default = 0; instrs[n].ret_type = TYPE_ANY; } n = F_AND - F_OFFSET; instrs[n].min_arg = instrs[n].max_arg = 2; instrs[n].ret_type = TYPE_ANY; n = F_COMPL - F_OFFSET; instrs[n].min_arg = instrs[n].max_arg = 1; instrs[n].Default = 0; instrs[n].ret_type = TYPE_ANY; n = F_NOT - F_OFFSET; instrs[n].min_arg = instrs[n].max_arg = 1; instrs[n].ret_type = TYPE_ANY; add_permanent_define("LPC3", -1, string_copy(""), 0); #ifdef COMPAT_MODE add_permanent_define("COMPAT_FLAG", -1, string_copy(""), 0); #endif mtext[0] = '"'; mtext[1] = '/'; strcpy(mtext+2, master_name); strcat(mtext+2, "\""); add_permanent_define("__MASTER_OBJECT__", -1, string_copy(mtext), 0); add_permanent_define("__FILE__", -1, (char *)get_current_file, 1); add_permanent_define("__LINE__", -1, (char *)get_current_line, 1); add_permanent_define("__VERSION__", -1, (char *)get_version, 1); for (tmpf=lpc_predefs; tmpf; tmpf=tmpf->next) { char namebuf[NSIZE]; *mtext='\0'; sscanf(tmpf->flag,"%[^=]=%[ -~=]",namebuf,mtext); if ( strlen(namebuf) >= NSIZE ) fatal("NSIZE exceeded\n"); if ( strlen(mtext) >= MLEN ) fatal("MLEN exceeded\n"); add_permanent_define(namebuf,-1,string_copy(mtext), 0); } } char *get_f_name(n) int n; { if (instrs[n].name) return instrs[n].name; else { static char buf[30]; sprintf(buf, "<OTHER %d>", n); return buf; } } #define NARGS 25 #define MARKS '@' #define SKIPWHITE while(lexwhite(*p)) p++ #define GETALPHA(p, q, m) \ while(isalunum(*p)) {\ *q = *p++;\ if (q < (m))\ q++;\ else {\ lexerror("Name too long");\ return;\ }\ }\ *q++ = 0 static char cmygetc() { int c; for(;;) { c = mygetc(); if (c == '/') { if (gobble('*')) skip_comment(); else return c; } else return c; } } static void refill() { char *p; int c; p = yytext; do { c = cmygetc(); if (p < yytext+MAXLINE-5) *p++ = c; else { lexerror("Line too long"); break; } } while(c != '\n'); myfilbuf(); p[-1] = ' '; *p = 0; nexpands=0; current_line++; store_line_number_info(); } static void handle_define(yyt) 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); if (*p == '(') { /* if "function macro" */ int arg; int inid; char *ids; p++; /* skip '(' */ SKIPWHITE; if (*p == ')') { arg = 0; } else { for(arg = 0; arg < NARGS; ) { q = args[arg]; GETALPHA(p, q, &args[arg][NSIZE-1]); 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 ')' */ for(inid = 0, q = mtext; *p; ) { if (isalunum(*p)) { if (!inid) { inid++; ids = p; } } else { if (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 (*p++ == MARKS) *++q = MARKS; if (q < mtext+MLEN-2) q++; else { lexerror("Macro text too long"); return; } if (!*p) { if (p[-2] == '\\') { q -= 2; refill(); p = yytext; } else if (p[-2] == '\r' && p[-3] == '\\' ) { q -= 3; refill(); p = yytext; } } } *--q = 0; add_define(namebuf, arg, mtext); } else { for(q = mtext; *p; ) { *q = *p++; if (q < mtext+MLEN-2) q++; else { lexerror("Macro text too long"); return; } if (!*p) { if (p[-2] == '\\') { q -= 2; refill(); p = yytext; } else if (p[-2] == '\r' && p[-3] == '\\' ) { q -= 3; refill(); p = yytext; } } } *--q = 0; add_define(namebuf, -1, mtext); } return; } static void add_input(p) char *p; { int l = strlen(p); #if defined(LEXDEBUG) if (l > 0) fprintf(stderr, "add '%s'\n", p); #endif outp -= l; if (outp < &defbuf[10]) { outp += l; lexerror("Macro expansion buffer overflow"); return; } strncpy(outp, p, l); } static void add_define(name, nargs, exps) char *name, *exps; int nargs; { struct ident *p; p = make_shared_identifier(name, I_TYPE_DEFINE); if (!p) { lexerror("Out of memory"); return; } if (p->type != I_TYPE_UNKNOWN) { if (nargs != p->u.define.nargs || p->u.define.special || strcmp(exps,p->u.define.exps.str) != 0) { char buf[200+NSIZE]; sprintf(buf, "Redefinition of #define %s", name); yyerror(buf); } return; } p->type = I_TYPE_DEFINE; p->u.define.nargs = nargs; p->u.define.permanent = 0; p->u.define.special = 0; if ( !(p->u.define.exps.str = xalloc(strlen(exps)+1)) ) { free_shared_identifier(p); lexerror("Out of memory"); return; } strcpy(p->u.define.exps.str, exps); p->next_all = all_defines; all_defines = p; #if defined(LEXDEBUG) fprintf(stderr, "define '%s' %d '%s'\n", name, nargs, exps); #endif } static void add_permanent_define(name, nargs, exps, special) char *name, *exps, special; int nargs; { struct ident *p; p = make_shared_identifier(name, I_TYPE_DEFINE); if (!p) { error("Out of memory\n"); } if (p->type != I_TYPE_UNKNOWN) { if (nargs != p->u.define.nargs || p->u.define.special || strcmp(exps,p->u.define.exps.str) != 0) { error("Redefinition of #define %s", name); } return; } p->type = I_TYPE_DEFINE; p->u.define.nargs = nargs; p->u.define.permanent = 1; p->u.define.special = special; p->u.define.exps.str = exps; p->next_all = permanent_defines; permanent_defines = p; } void free_defines() { struct ident *p, *q; for(p = all_defines; p; p = q) { q = p->next_all; if (p->name) { xfree(p->u.define.exps.str); free_shared_identifier(p); } else { /* has been undef'd. */ xfree((char *)p); } } all_defines = 0; for (p = undefined_permanent_defines; p; p = q) { struct ident *curr, **prev; q = p->next; p->next = 0; prev = &ident_table[p->hash]; while (curr = *prev) { if (curr->name == p->name) { /* found it */ p->next = curr->next; free_string(p->name); break; } prev = &curr->next; } /* not found, create new one */ p->inferior = curr; *prev = p; } undefined_permanent_defines = 0; nexpands = 0; } static struct ident * lookup_define(s) char * s; { struct ident *curr, *prev; int h; h = identhash(s); curr = ident_table[h]; prev = 0; while (curr) { if (!strcmp(curr->name, s)) { /* found it */ if (prev) { /* not at head of list */ prev->next = curr->next; curr->next = ident_table[h]; ident_table[h] = curr; } if (curr->type == I_TYPE_DEFINE) return curr; return 0; } prev = curr; curr = curr->next; } /* not found */ return 0; } #define SKIPW \ for(;;) {\ do {\ c = cmygetc();\ } while(lexwhite(c));\ if (c == '\n') {\ myfilbuf();\ store_line_number_info();\ current_line++;\ total_lines++;\ } else break;\ }\ /* Check if yytext is a macro and expand if it is. */ static int expand_define() { struct ident *p; p = lookup_define(yytext); if (!p) { return 0; } return _expand_define(&p->u.define); } static int _expand_define(p) struct defn *p; { char expbuf[DEFMAX]; char *args[NARGS]; char buf[DEFMAX]; char *q, *e, *b; char *r; if (nexpands++ > EXPANDMAX) { lexerror("Too many macro expansions"); return 0; } if (p->nargs == -1) { if (!p->special) { add_input(p->exps.str); } else { e = (*p->exps.fun)(); add_input(e); xfree(e); } } else { int c, parcnt = 0, dquote = 0, squote = 0; int n; SKIPW; if (c != '(') { yyerror("Missing '(' in macro call"); return 0; } SKIPW; if (c == ')') n = 0; else { r = outp; *--r = c; q = expbuf; args[0] = q; for(n = 0;;) { if (q >= expbuf + DEFMAX - 5) { lexerror("Macro argument overflow"); return 0; } switch(c = *r++) { case '"' : if (!squote) dquote ^= 1; *q++ = c; continue; case '#': *q++ = c; if (!squote && !dquote && *r == '\'') { r++; *q++ = '\''; if (isalunum(c = *r)) { do { *q++ = c; ++r; } while (isalunum(c = *r)); } else { extern int symbol_operator PROT((char *, char **)); char *end; if (symbol_operator(r, &end) < 0) { yyerror("Missing function name after #'"); } strncpy(q, r, end - r); q += end - r; r = end; } } continue; case '\'': if ( !dquote && (!isalunum(*r) || r[1] == '\'') && (*r != '(' || r[1] != '{') ) { squote ^= 1; } *q++ = c; continue; case '(' : if (!squote && !dquote) parcnt++; *q++ = c; continue; case ')' : if (!squote && !dquote) { parcnt--; if (parcnt < 0) { *q++ = 0; n++; break; } } *q++ = c; continue; case '\\': *q++ = c; if (squote || dquote) { c = *r++; if (c == '\r') c = *r++; if (c == '\n') { /* nope! This wracks consistency! */ store_line_number_info(); current_line++; total_lines++; if (!*r) { outp = r; r = _myfilbuf(); } q--; /* alas, long strings should work. */ continue; } if (c == CHAR_EOF) { /* can't quote THAT */ r--; continue; } *q++ = c; } continue; case '\n': store_line_number_info(); current_line++; total_lines++; *q++ = ' '; if (!*r) { outp = r; r = _myfilbuf(); } if (squote || dquote) { lexerror("Newline in string"); return 0; } continue; case ',': if (!parcnt && !dquote && !squote) { *q++ = 0; args[++n] = q; if (n == NARGS - 1) { lexerror("Maximum macro argument count exceeded"); return 0; } continue; } *q++ = c; continue; case CHAR_EOF: lexerror("Unexpected end of file"); return 0; case '/': if (!squote && !dquote) { if ( (c = *r++) == '*') { outp = r; skip_comment(); r = outp; } else { --r; *q++ = '/'; } continue; } default: *q++ = c; continue; } /* end switch */ break; } /* end for */ outp = r; } if (n != p->nargs) { yyerror("Wrong number of macro arguments"); return 0; } /* Do expansion */ b = buf; e = p->exps.str; while(*e) { if (*e == MARKS) { if (*++e == MARKS) *b++ = *e++; else { for(q = args[*e++ - MARKS - 1]; *q; ) { *b++ = *q++; if (b >= buf+DEFMAX) { lexerror("Macro expansion overflow"); return 0; } } } } else { *b++ = *e++; if (b >= buf+DEFMAX) { lexerror("Macro expansion overflow"); return 0; } } } *b++ = 0; add_input(buf); } return 1; } /* Stuff to evaluate expression. I havn't really checked it. /LA ** Written by "J\"orn Rennecke" <amylaar@cs.tu-berlin.de> */ #define SKPW do c = mygetc(); while(lexwhite(c)); myungetc(c) static int exgetc() { register char c,*yyp; c=mygetc(); for (;;) { if ( isalpha(c) || c=='_' ) { yyp=yytext; do { SAVEC; c=mygetc(); } while ( isalunum(c) ); myungetc(c); *yyp='\0'; if (strcmp(yytext, "defined") == 0) { /* handle the defined "function" in #if */ do c = mygetc(); while(lexwhite(c)); if (c != '(') { yyerror("Missing ( in defined"); continue; } do c = mygetc(); while(lexwhite(c)); yyp=yytext; while ( isalunum(c) ) { SAVEC; c=mygetc(); } *yyp='\0'; while(lexwhite(c)) c = mygetc(); if (c != ')') { yyerror("Missing ) in defined"); continue; } SKPW; if (lookup_define(yytext)) add_input(" 1 "); else add_input(" 0 "); } else { if (!expand_define()) add_input(" 0 "); } c=mygetc(); } else if (c == '\\' && *outp == '\n') { int quote; outp++; yyp = yytext; for(quote = 0;;) { c = mygetc(); if (c == '"') quote ^= 1; while(!quote && c == '/') { /* handle comments cpp-like */ char c2; if ( (c2 = mygetc()) == '*') { skip_comment(); c=mygetc(); } else if (c2 == '/') { outp = skip_pp_comment(outp); current_line--; c = '\n'; } else { --outp; break; } } SAVEC; if (c == '\n') { break; } } *yyp = '\0'; current_line++; total_lines++; add_input(yytext); nexpands = 0; } else { break; } } return c; } #define BNOT 1 #define LNOT 2 #define UMINUS 3 #define UPLUS 4 #define MULT 1 #define DIV 2 #define MOD 3 #define BPLUS 4 #define BMINUS 5 #define LSHIFT 6 #define RSHIFT 7 #define LESS 8 #define LEQ 9 #define GREAT 10 #define GEQ 11 #define EQ 12 #define NEQ 13 #define BAND 14 #define XOR 15 #define BOR 16 #define LAND 17 #define LOR 18 #define QMARK 19 /* lookup table for characters >= ' ' and <= '~'. * 0 for no operator, else index into optab2. */ static char _optab[]= {0,6,0,0,0,46,50,0,0,0,2,18,0,14,0,10,0,0,0,0,0,0,0,0,0,0,0,0,22,42,32,68, 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,64,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,57,0,1}; /* optab2[index-1] : operation code for unary operator, 0 for none. * optab[index+0 .. +3 .. +6 ...] : * two character binary operators: second character, operation code, priority * one character binary operator & end: 0, operation code, priority * end: 0, 0 */ static char optab2[]= { BNOT,0,0, MULT,11, LNOT,'=',NEQ,7,0,0, DIV,11, UMINUS,0,BMINUS,10, UPLUS,0,BPLUS,10, 0,'<',LSHIFT,9,'=',LEQ,8,0,LESS,8, 0,'>',RSHIFT,9,'=',GEQ,8,0,GREAT,8, 0,'=',EQ,7,0,0, MOD,11, 0,'&',LAND,3,0,BAND,6,0,'|',LOR,2,0,BOR,4, 0,0,XOR,5,0,0,QMARK,1 }; #define optab1 (_optab-' ') static int cond_get_exp(priority, svp) int priority; struct svalue *svp; { int c; int value,value2,x; struct svalue sv2; svp->type = T_INVALID; do c=exgetc(); while ( lexwhite(c) ); if ( c=='(' ) { value = cond_get_exp(0, svp); do c=exgetc(); while ( lexwhite(c) ); if ( c!=')' ) { yyerror("bracket not paired in #if"); if (c == '\n') myungetc('\n'); } } else if ( ispunct(c) ) { if (c == '"') { char *p, *q; svp->type = T_STRING; svp->x.string_type = STRING_MALLOC; q = p = outp; for (;;) { c = *p++; if (c == '"') { break; } if (c == '\n') { yyerror("unexpected end of string in #if"); svp->u.string = string_copy(""); return 0; } if (c == '\\') c = *p++; if (c == '\n') { current_line++; *--p = '"'; break; } *q++ = c; } *q = '\0'; svp->u.string = string_copy(outp); outp = p; } else { x=optab1[c]; if (!x) { yyerror("illegal character in #if"); return 0; } value = cond_get_exp(12, svp); switch ( optab2[x-1] ) { case BNOT : value = ~value; break; case LNOT : value = !value; break; case UMINUS: value = -value; break; case UPLUS : value = value; break; default : yyerror("illegal unary operator in #if"); free_svalue(svp); svp->type = T_NUMBER; return 0; } if (svp->type != T_NUMBER) { yyerror("illegal type to unary operator in #if"); free_svalue(svp); svp->type = T_NUMBER; return 0; } svp->u.number = value; } } else { int base; if ( !lexdigit(c) ) { if (c == '\n') { yyerror("missing expression in #if"); myungetc('\n'); } else yyerror("illegal character in #if"); return 0; } value=0; if ( c!='0' ) base=10; else { c=mygetc(); if ( c=='x' || c=='X' ) { base=16; c=mygetc(); } else base=8; } for(;;) { if ( isdigit(c) ) x = -'0'; else if ( isupper(c) ) x = -'A'+10; else if ( islower(c) ) x = -'a'+10; else break; x+=c; if ( x > base ) break; value=value*base+x; c=mygetc(); } myungetc(c); svp->type = T_NUMBER; svp->u.number = value; } for (;;) { do c=exgetc(); while ( lexwhite(c) ); if ( !ispunct(c) ) break; if (c == '"') { myungetc('"'); c = '+'; } x=optab1[c]; if (!x) break; value2=mygetc(); for(;;x+=3) { if ( !optab2[x] ) { myungetc(value2); if ( !optab2[x+1] ) { yyerror("illegal operator use in #if"); return 0; } break; } if ( value2==optab2[x] ) break; } if ( priority >= optab2[x+2] ) { if( optab2[x] ) myungetc(value2); break; } value2=cond_get_exp(optab2[x+2], &sv2); if (svp->type == T_NUMBER && sv2.type == T_NUMBER) { switch ( optab2[x+1] ) { case MULT : value *= value2; break; case DIV : if (!value2) lexerror("Division by zero"); else value /= value2; break; case MOD : if (!value2) lexerror("Division by zero"); else value %= value2; break; case BPLUS : value += value2; break; case BMINUS : value -= value2; break; case LSHIFT : value <<= value2; break; case RSHIFT : value >>= value2; break; case LESS : value = value < value2; break; case LEQ : value = value <= value2; break; case GREAT : value = value > value2; break; case GEQ : value = value >= value2; break; case EQ : value = value == value2; break; case NEQ : value = value != value2; break; case BAND : value &= value2; break; case XOR : value ^= value2; break; case BOR : value |= value2; break; case LAND : value = value && value2; break; case LOR : value = value || value2; break; case QMARK : do c=exgetc(); while( lexwhite(c) ); if ( c!=':' ) { yyerror("'?' without ':' in #if"); myungetc(c); return 0; } if ( value ) { *svp = sv2; cond_get_exp(1, &sv2); free_svalue(&sv2); value = value2; } else value=cond_get_exp(1, svp); break; } } else if (svp->type == T_STRING && sv2.type == T_STRING) { x = optab2[x+1]; if (x == BPLUS) { char *str; str = xalloc(strlen(svp->u.string) + strlen(sv2.u.string) + 1); strcpy(str, svp->u.string); strcat(str, sv2.u.string); free_string_svalue(svp); free_string_svalue(&sv2); svp->u.string = str; } else { value = strcmp(svp->u.string, sv2.u.string); free_string_svalue(svp); svp->type = T_NUMBER; free_string_svalue(&sv2); switch (x) { case LESS : value = value < 0; break; case LEQ : value = value <= 0; break; case GREAT : value = value > 0; break; case GEQ : value = value >= 0; break; case EQ : value = value == 0; break; case NEQ : value = value != 0; break; default: yyerror("illegal operator use in #if"); return 0; } svp->u.number = value; } } else { yyerror("operands in #if won't match"); free_svalue(svp); svp->type = T_NUMBER; free_svalue(&sv2); return 0; } } myungetc(c); return value; } void set_inc_list(sv) struct svalue *sv; { int i; struct vector *v; char *p; if (sv == 0) { fprintf(stderr, "There must be a function 'define_include_dirs' in master.c.\n"); fprintf(stderr, "This function should return an array of all directories to be searched\n"); fprintf(stderr, "for include files.\n"); exit(1); } if (sv->type != T_POINTER) { fprintf(stderr, "'define_include_dirs' in master.c did not return an array.\n"); exit(1); } v = sv->u.vec; /* cheaper to malloc 1..4 bytes too much than to test for size 0 */ inc_list = (char **)xalloc(v->size * sizeof (char *)+1); inc_list_size = v->size; for (i=0; i < v->size; i++) { if (v->item[i].type != T_STRING) { fprintf(stderr, "Illegal value returned from 'define_include_dirs' in master.c\n"); exit(1); } p = v->item[i].u.string; if (*p == '/') p++; /* * Even make sure that the game administrator has not made an error. */ if (!legal_path(p)) { fprintf(stderr, "'define_include_dirs' must give paths without any '..'\n"); exit(1); } inc_list[i] = make_shared_string(p); } } void clear_auto_include_string() { if (auto_include_string) { xfree(auto_include_string); auto_include_string = 0; } } void set_auto_include_string(s) char *s; { clear_auto_include_string(); auto_include_string = xalloc(strlen(s)+2); *auto_include_string = '\n'; strcpy(auto_include_string+1, s); for (auto_include_start = 0; *s; auto_include_start -= *s++ == '\n'); } static char *get_current_file() { char *buf; buf = xalloc(strlen(current_file)+4); sprintf(buf, "\"/%s\"", current_file); return buf; } static char *get_current_line() { char *buf; buf = xalloc(12); sprintf(buf, "%d", current_line); return buf; } static char *get_version() { char *buf; buf = xalloc(16); sprintf(buf, "\"%5.5s%s\"", GAME_VERSION, PATCH_LEVEL); return buf; } void remove_unknown_identifier() { int i; struct ident *id, *next; for (i = ITABLE_SIZE; --i >= 0; ) { id = ident_table[i]; for ( ; id; id = next) { next = id->next; if (id->type == I_TYPE_UNKNOWN) free_shared_identifier(id); } } } #ifdef MALLOC_smalloc void count_lex_refs() { int i; struct ident *id; char **p; /* Identifier */ for (i = ITABLE_SIZE; --i >= 0; ) { id = ident_table[i]; for ( ; id; id = id->next) { count_ref_from_string(id->name); note_malloced_block_ref((char *)id); } } for (id = permanent_defines; id; id = id->next_all) { if (!id->u.define.special) note_malloced_block_ref(id->u.define.exps.str); } /* include list */ note_malloced_block_ref((char *)inc_list); for (p = inc_list, i = inc_list_size; --i >= 0; p++) count_ref_from_string(*p); if (auto_include_string) note_malloced_block_ref(auto_include_string); if (defbuf_len) note_malloced_block_ref(defbuf); } #endif /* MALLOC_smalloc */