#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 */