%{
/*
Copyright(C) 1990, Marcus J. Ranum, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.
*/
/*
EXPECTED CONFLICTS::conflicts: 1 shift/reduce on the if/else. This is OK.
grammar for ubermud compiler. there are lots of cases where 'cute'
tricks are played to minimize the number of instructions that get written
into a program - this is unfortunate, but necessary, since every byte that
can be saved out of an object will decrease the size of the universe.
frequently used ops, like EVAL, ASGN, BLTIN, and ELEVAL use this technique
as much as possible. it makes the code a bit more convoluted, but it
should prove worthwhile.
WARNING: severely icky-poo looking code ahead. The debug statments
are a ROYAL pain in the butt, but you will come to love them if you ever
need to debug this guy. If you make ANY changes, keep the debug statements
up-to-date, or you will be killed in a horrible way.
*/
/*
#define COMPILEDEBUG
*/
#include <stdio.h>
#include <ctype.h>
#include "ubermud.h"
#include "externs.h"
/*
scratch buffers in which to compile and assemble input.
this is done in a somewhat roundabout manner, to maintain a static area
for compilation.
*/
static char *currfunc; /* kludge - used to return function name */
static long currenum; /* kludge - used to return function elem # */
static int currfuncop; /* used to return assign op of funcs */
static int stringnum;
static int stringoff;
static char stringbuf[STRBUFSIZ];
static int instbuf[INSBUFSIZ];
static Prog bprg = { 0, 0, instbuf, stringbuf, 0, INSBUFSIZ, 0 };
/* ya caught me! a GLOBAL! */
Prog *yaccprog = &bprg;
static int infunct = 0;
/*
shortened name for the current instruction address - I suppose some
day someone might want to assemble 2 programs at once, by re-pointing
yaccprog, so we deref this through a pointer.
*/
#define ASMPC (yaccprog->p_pc)
/* compiler state exception flag */
static int compiler_state = COMPILE_OK;
static char *infuncmsg = "operation only permitted within function";
%}
%token NUM STR IDENT
%token OUTOFSPACE BLTIN
%token IF IN FOR FORARG ELSE FUNC RETURN VNULL
%right ASGN
%left REF
%left OR
%left AND
%left GT GTE LT LTE EQ NE
%left ADD SUB
%left MUL DIV
%right UNARY
%left NOT
%%
program:
/* nothing */
| program statement
{
(void)assemble(yaccprog,OP_STOP);
/*
sizes here are used only for error control
when running the assembled static program.
they get fixed during resetparser(), which
should get called in the main loop.
*/
yaccprog->p_siz = ASMPC;
yaccprog->s_siz = stringoff;
return(compiler_state);
}
| program funcdef
{
/*
this instruction will only be reached if an
explicit return; or return(expr) is not reached
first. it ensures that something (a non-value)
will be left on the stack for posterity
*/
#ifdef COMPILEDEBUG
printf("OP_RETF@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_RETF);
/*
sizes here are used to calculate how much space
is allocated if this prog gets copied into a
static function. this is very important, as it
ensures minimal space wastage. these values must
get reset in resetparser(), during the main loop.
*/
yaccprog->p_siz = ASMPC;
yaccprog->p_siz = ASMPC;
yaccprog->s_siz = stringoff;
infunct = 0;
if(compiler_state == COMPILE_OK)
return(COMPILE_FUNC);
return(compiler_state);
}
;
asgn:
'$' IDENT ASGN expr
{
$$ = $4;
#ifdef COMPILEDEBUG
printf("OP_ASGN@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_ASGN);
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$2],ASMPC);
#endif
(void)assemble(yaccprog,$2);
}
|
MUL element ASGN expr %prec UNARY
{
/* *#44.bar = expr; */
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_ECASGN@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_ECASGN);
}
|
element ASGN expr
{
/* #44.bar = expr; */
#ifdef COMPILEDEBUG
printf("OP_EASGN@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_EASGN);
}
;
statement:
expr ';'
{
#ifdef COMPILEDEBUG
printf("OP_POP@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_POP);
}
| RETURN ';'
{
if(!infunct) {
yyerror(infuncmsg);
compiler_state = COMPILE_BAD;
} else {
#ifdef COMPILEDEBUG
printf("OP_RETNV@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_RETNV);
}
}
| RETURN expr ';'
{
$$ = $2;
if(!infunct) {
yyerror(infuncmsg);
compiler_state = COMPILE_BAD;
} else {
#ifdef COMPILEDEBUG
printf("OP_RETV@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_RETV);
}
}
| ';'
| ifheader condition statement stop
{
yaccprog->p_mem[$1 + 1] = $3; /* if part */
yaccprog->p_mem[$1 + 2] = 0; /* else part (none) */
yaccprog->p_mem[$1 + 3] = $4; /* continuation */
#ifdef COMPILEDEBUG
printf("IF@%d backpatch, ifpart:%d@%d, else:%d@%d, cont:%d@%d\n",
$1, yaccprog->p_mem[$1+1],$1+1,yaccprog->p_mem[$1+2],$1+2,
yaccprog->p_mem[$1+3],$1+3);
#endif
}
| ifheader condition statement stop ELSE statement stop
{
yaccprog->p_mem[$1 + 1] = $3; /* if part */
yaccprog->p_mem[$1 + 2] = $6; /* else part */
yaccprog->p_mem[$1 + 3] = $7; /* continuation */
#ifdef COMPILEDEBUG
printf("IF@%d backpatch, ifpart:%d@%d, else:%d@%d, cont:%d@%d\n",
$1, yaccprog->p_mem[$1+1],$1+1,yaccprog->p_mem[$1+2],$1+2,
yaccprog->p_mem[$1+3],$1+3);
#endif
}
| forheader '$' IDENT IN condition statement stop
{
yaccprog->p_mem[$1 + 1] = $3; /* iterator sym name */
yaccprog->p_mem[$1 + 2] = $6; /* do part */
yaccprog->p_mem[$1 + 3] = $7; /* continuation */
#ifdef COMPILEDEBUG
printf("FOR@%d backpatch, sym:%s@%d, do:%d@%d, cont:%d@%d\n",
$1, &stringbuf[$3],$1+1,yaccprog->p_mem[$1+2],$1+2,
yaccprog->p_mem[$1+3],$1+3);
#endif
}
| forargheader '$' IDENT statement stop
{
yaccprog->p_mem[$1 + 1] = $3; /* iterator sym name */
yaccprog->p_mem[$1 + 2] = $5; /* continuation */
#ifdef COMPILEDEBUG
printf("FORARG@%d backpatch, iter:%s@%d, cont:%d@%d\n",
$1, &stringbuf[$3],$1+1,yaccprog->p_mem[$1+2],$1+2);
#endif
}
| '{' statements '}'
{
$$ = $2;
}
;
condition:
'(' expr ')'
{
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_STOP@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_STOP);
}
;
stop:
/* nothing */
{
#ifdef COMPILEDEBUG
printf("OP_STOP@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_STOP);
$$ = ASMPC;
}
;
statements:
/* nothing */
{
$$ = ASMPC;
}
| statements statement
;
refnum:
'@'
{
#ifdef COMPILEDEBUG
printf("OP_ROOTPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_ROOTPUSH);
}
| '#' NUM
{
#ifdef COMPILEDEBUG
printf("OP_RPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_RPUSH);
#ifdef COMPILEDEBUG
printf("%d@%d\n",$2,ASMPC);
#endif
(void)assemble(yaccprog,$2);
}
| '#' IDENT
{
if(!strcmp(&stringbuf[$2],"self")) {
#ifdef COMPILEDEBUG
printf("OP_SELFPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_SELFPUSH);
} else
if(!strcmp(&stringbuf[$2],"caller")) {
#ifdef COMPILEDEBUG
printf("OP_CALLPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_CALLPUSH);
} else
if(!strcmp(&stringbuf[$2],"actor")) {
#ifdef COMPILEDEBUG
printf("OP_ACTPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_ACTPUSH);
} else {
yyerror("error. must specify self|actor|caller\n");
compiler_state = COMPILE_BAD;
}
}
;
element_head:
refnum
| '$' IDENT
{
#ifdef COMPILEDEBUG
printf("OP_EVAL@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_EVAL);
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$2],ASMPC);
#endif
(void)assemble(yaccprog,$2);
}
| '$' NUM
{
if(!infunct) {
yyerror(infuncmsg);
compiler_state = COMPILE_BAD;
} else {
#ifdef COMPILEDEBUG
printf("OP_PPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_PPUSH);
#ifdef COMPILEDEBUG
printf("%d@%d\n",$2,ASMPC);
#endif
(void)assemble(yaccprog,$2);
}
}
;
element:
element_head '.' IDENT
{
#ifdef COMPILEDEBUG
printf("OP_ELPUSH@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_ELPUSH);
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$3],ASMPC);
#endif
(void)assemble(yaccprog,$3);
}
| element_head '.' '(' expr ')'
{
#ifdef COMPILEDEBUG
printf("OP_STRVAR@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_STRVAR);
}
| element '.' IDENT
{
#ifdef COMPILEDEBUG
printf("OP_EEVAL@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_EEVAL);
#ifdef COMPILEDEBUG
printf("OP_ELPUSH@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_ELPUSH);
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$3],ASMPC);
#endif
(void)assemble(yaccprog,$3);
}
| element '.' '(' push_eval_kludge expr ')'
{
#ifdef COMPILEDEBUG
printf("OP_STRVAR@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_STRVAR);
}
;
push_eval_kludge:
/*
nothing - what this does is ensures that the element is
pushed AND evaluated BEFORE the expression is pushed and
evaluated. this is somewhat gross, but then the whole
string->variable hack is somewhat gross.
*/
{
#ifdef COMPILEDEBUG
printf("OP_EEVAL@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_EEVAL);
}
;
evaluated_element:
element
{
#ifdef COMPILEDEBUG
printf("OP_EEVAL@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_EEVAL);
}
| '$' IDENT
{
#ifdef COMPILEDEBUG
printf("OP_EVAL@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_EVAL); /* replace w/ contents */
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$2],ASMPC);
#endif
(void)assemble(yaccprog,$2); /* variable name */
}
| '$' NUM
{
if(!infunct) {
yyerror(infuncmsg);
compiler_state = COMPILE_BAD;
} else {
#ifdef COMPILEDEBUG
printf("OP_PPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_PPUSH);
#ifdef COMPILEDEBUG
printf("%d@%d\n",$2,ASMPC);
#endif
(void)assemble(yaccprog,$2);
}
}
;
expr:
NUM
{
#ifdef COMPILEDEBUG
printf("OP_NPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_NPUSH);
#ifdef COMPILEDEBUG
printf("%d@%d\n",$1,ASMPC);
#endif
(void)assemble(yaccprog,$1);
}
| STR
{
#ifdef COMPILEDEBUG
printf("OP_SPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_SPUSH);
#ifdef COMPILEDEBUG
printf("%s@%d\n",&stringbuf[$1],ASMPC);
#endif
(void)assemble(yaccprog,$1);
}
| VNULL
{
#ifdef COMPILEDEBUG
printf("OP_NULPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_NULPUSH);
}
| '$' '#'
{
if(!infunct) {
yyerror(infuncmsg);
compiler_state = COMPILE_BAD;
} else
#ifdef COMPILEDEBUG
printf("OP_CPUSH@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_CPUSH);
}
| refnum
| evaluated_element
| asgn
| BLTIN beginargs '(' arglist ')'
{
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_BLTIN@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_BLTIN);
#ifdef COMPILEDEBUG
printf("bltin #%d@%d\n",$1,ASMPC);
#endif
(void)assemble(yaccprog,$1);
#ifdef COMPILEDEBUG
printf("%d args@%d\n",$4,ASMPC);
#endif
(void)assemble(yaccprog,$4);
}
| evaluated_element '(' arglist ')'
{
#ifdef COMPILEDEBUG
printf("OP_CALL@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_CALL);
#ifdef COMPILEDEBUG
printf("%d args@%d\n",$3,ASMPC);
#endif
(void)assemble(yaccprog,$3);
}
| '(' expr ')'
{
$$ = $2;
}
| expr ADD expr
{
#ifdef COMPILEDEBUG
printf("OP_ADD@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_ADD);
}
| expr SUB expr
{
#ifdef COMPILEDEBUG
printf("OP_SUB@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_SUB);
}
| expr MUL expr
{
#ifdef COMPILEDEBUG
printf("OP_MUL@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_MUL);
}
| expr DIV expr
{
#ifdef COMPILEDEBUG
printf("OP_DIV@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_DIV);
}
| SUB expr %prec UNARY
{
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_NEG@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_NEG); /* negate top of stack */
}
| REF element %prec UNARY
{
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_REF@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_REF);
}
| expr EQ expr
{
#ifdef COMPILEDEBUG
printf("OP_EQ@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_EQ);
}
| expr NE expr
{
#ifdef COMPILEDEBUG
printf("OP_NE@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_NE);
}
| expr LT expr
{
#ifdef COMPILEDEBUG
printf("OP_LT@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_LT);
}
| expr LTE expr
{
#ifdef COMPILEDEBUG
printf("OP_LTE@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_LTE);
}
| expr GT expr
{
#ifdef COMPILEDEBUG
printf("OP_GT@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_GT);
}
| expr GTE expr
{
#ifdef COMPILEDEBUG
printf("OP_GTE@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_GTE);
}
| expr AND expr
{
#ifdef COMPILEDEBUG
printf("OP_AND@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_AND);
}
| expr OR expr
{
#ifdef COMPILEDEBUG
printf("OP_OR@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_OR);
}
| NOT expr %prec UNARY
{
$$ = $2;
#ifdef COMPILEDEBUG
printf("OP_NOT@%d\n",ASMPC);
#endif
(void)assemble(yaccprog,OP_NOT); /* invert top of stack */
}
;
forheader:
FOR
{
#ifdef COMPILEDEBUG
printf("OP_FOR (header only)@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_FOR);
(void)assemble(yaccprog,OP_STOP);
(void)assemble(yaccprog,OP_STOP);
(void)assemble(yaccprog,OP_STOP);
}
;
forargheader:
FORARG
{
#ifdef COMPILEDEBUG
printf("OP_FORARG (header only)@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_FORARG);
(void)assemble(yaccprog,OP_STOP);
(void)assemble(yaccprog,OP_STOP);
}
;
ifheader:
IF
{
#ifdef COMPILEDEBUG
printf("OP_IF (header only)@%d\n",ASMPC);
#endif
$$ = assemble(yaccprog,OP_IF);
(void)assemble(yaccprog,OP_STOP);
(void)assemble(yaccprog,OP_STOP);
(void)assemble(yaccprog,OP_STOP);
}
;
beginargs:
/* nothing */
{
$$ = ASMPC;
}
;
funcdef:
funcheader '@' '.' IDENT statement
{
/*
this is gross, but the best way to handle it.
store a pointer to the name of the currently
defined function, to allow easy linking. this
COULD be passed back in the compiled program,
but then it would have to be stripped out.
so, do it like this. Sue me.
*/
currfunc = &stringbuf[$4];
currenum = (long)0;
}
| funcheader '#' NUM '.' IDENT statement
{
currfunc = &stringbuf[$5];
currenum = (long)$3;
currfuncop = OP_EASGN;
}
| funcheader MUL '#' NUM '.' IDENT statement
{
currfunc = &stringbuf[$6];
currenum = (long)$4;
currfuncop = OP_ECASGN;
}
;
arglist:
/* nothing */
{
$$ = 0;
}
| expr
{
$$ = 1;
}
| arglist ',' expr
{
$$ = $1 + 1;
}
;
funcheader:
FUNC
{
infunct = 1;
}
;
%%
FILE *yyin = { stdin };
int yylineno = 1;
void
setyyinput(f)
FILE *f;
{
yyin = f;
}
putinstringbuf(b)
char *b;
{
register char *op = &stringbuf[stringoff];
int ooff = stringoff;
while(*b && stringoff < sizeof(stringbuf)) {
if(isprint(*b)) {
*op++ = *b;
stringoff++;
if(stringoff >= sizeof(stringbuf))
return(-1);
}
b++;
}
stringoff++;
*op = '\0';
return(ooff);
}
assemble(p,op)
Prog *p;
int op;
{
if(p->p_pc >= p->p_siz) {
yyerror("program too big\n");
compiler_state = COMPILE_OOE;
return(0);
}
p->p_mem[p->p_pc] = op;
p->p_pc++;
return(p->p_pc - 1);
}
/*
return the element # of the currently defined function
*/
long
funcnum()
{
return(currenum);
}
/*
return the assignment op type of the currently defined function
*/
funcop()
{
return(currfuncop);
}
/*
return the name of the currently defined function
*/
char *
funcname()
{
return(currfunc);
}
/*
reset the string buffer pointers and so on. wipe the static memory
prior to a parser run.
*/
void
resetparser()
{
infunct = 0;
stringnum = 0;
stringoff = 0;
/* kludgy - reset the max size of the compiler's program buffer */
ASMPC = 0;
yaccprog->p_siz = INSBUFSIZ;
yaccprog->s_siz = STRBUFSIZ;
currfunc = 0;
compiler_state = COMPILE_OK;
}
/*
search the string buffer for an existing copy of string. in case it is
already there, return the offset (cut down on duplication). if not,
return -1
*/
static int
searchbuf(s)
char *s;
{
register char *p = stringbuf;
register char *kp;
int strt;
int srch = 0;
while(srch < stringoff) {
kp = s;
strt = srch;
/* match as match can */
while(*p == *kp && *kp != '\0' && srch < stringoff - 1) {
p++;
kp++;
srch++;
}
/* is it a match ? */
if(*p == '\0' && *kp == '\0' && srch < stringoff - 1) {
#ifdef ASMDEBUG
printf("string matched at %d\n",strt);
#endif
return(strt);
}
/* it is not a match, skip to next NULL, and continue */
while(*p != '\0') {
p++;
srch++;
}
/* and skip the NULL */
srch++;
if(srch < stringoff - 1)
p++;
}
return(-1);
}
static int
lookup(s,bval)
char *s;
int *bval;
{
int start = 0;
int ret;
static struct kwordz{
char *kw;
int rval;
int bltin; /* # of builtin if builtin */
} keyz[] = {
/* MUST BE IN LEXICAL SORT ORDER !!!!!! */
"NULL", VNULL, -1,
"atoi", BLTIN, 0,
"atoobj", BLTIN, 1,
"catfile", BLTIN, 2,
"chmod", BLTIN, 3,
"chown", BLTIN, 4,
"disconnect", BLTIN, 5,
"echo", BLTIN, 6,
"echoto", BLTIN, 7,
"else", ELSE, -1,
"errno", BLTIN, 8,
"error", BLTIN, 9,
"foreach", FOR, -1,
"foreacharg", FORARG, -1,
"func", FUNC, -1,
"geteuid", BLTIN, 10,
"getuid", BLTIN, 11,
"if", IF, -1,
"in", IN, -1,
"islist", BLTIN, 12,
"isnum", BLTIN, 13,
"isobj", BLTIN, 14,
"isstr", BLTIN, 15,
"listadd", BLTIN, 16,
"listappend", BLTIN, 17,
"listcount", BLTIN, 18,
"listdrop", BLTIN, 19,
"listelem", BLTIN, 20,
"listmerge", BLTIN, 21,
"listnew", BLTIN, 22,
"listprepend", BLTIN, 16, /* alias add/prepend */
"listsearch", BLTIN, 23,
"listsetelem", BLTIN, 24,
"log", BLTIN, 25,
"match", BLTIN, 26,
"objectdestroy", BLTIN, 27,
"objectelements", BLTIN, 28,
"objectnew", BLTIN, 29,
"objectowner", BLTIN, 30,
"random", BLTIN, 31,
"regcmp", BLTIN, 32,
"regexp", BLTIN, 33,
"return", RETURN, -1,
"setruid", BLTIN, 34,
"setuid", BLTIN, 35,
"shutdown", BLTIN, 36,
"str", BLTIN, 37,
"strlen", BLTIN, 38,
"strtime", BLTIN, 39,
"time", BLTIN, 40,
0,0,0
};
int end = (sizeof(keyz)/sizeof(struct kwordz)) - 2;
int p = end/2;
*bval = -1;
while(start <= end) {
ret = strcmp(s,keyz[p].kw);
if(ret == 0) {
*bval = keyz[p].bltin;
return(keyz[p].rval);
}
if(ret > 0)
start = p + 1;
else
end = p - 1;
p = start + ((end - start)/2);
}
return(-1);
}
yylex()
{
char in[BUFSIZ];
char *p = in;
int c;
/* handle whitespace */
while(isspace(c = fgetc(yyin)))
if(c == '\n')
yylineno++;
/* handle EOF */
if(c == EOF) {
return(0);
}
/* save current char - it is valuable */
*p++ = c;
/* handle NUM */
if(isdigit(c)) {
int num;
num = c - '0';
while(isdigit(c = fgetc(yyin)))
num = (num * 10) + (c - '0');
(void)ungetc(c,yyin);
if(c == '\n')
yylineno--;
yylval = num;
return(NUM);
}
/* handle keywords or idents/builtins */
if(isalpha(c) || c == '_') {
int cnt = 0;
int rv;
int bltin;
while((c = fgetc(yyin)) != EOF && (isalnum(c) || c == '_')) {
if(++cnt + 1 >= MAXIDENTLEN)
yyerror("identifier too long");
*p++ = c;
}
(void)ungetc(c,yyin);
*p = '\0';
if((rv = lookup(in,&bltin)) != -1) {
if(rv == BLTIN)
yylval = bltin;
return(rv);
}
/* if the string is already in the buffer, return that */
if(stringoff > 0 && (yylval = searchbuf(in)) != -1) {
return(IDENT);
}
if(stringoff + cnt > sizeof(stringbuf)) {
yyerror("out of string space!");
compiler_state = COMPILE_OOS;
return(OUTOFSPACE);
}
bcopy(in,&stringbuf[stringoff],cnt + 2);
yylval = stringoff;
stringoff += cnt + 2;
stringbuf[stringoff - 1] = '\0';
return(IDENT);
}
/* handle quoted strings */
if(c == '"' || c == '\'') {
int cnt = 0;
int quot = c;
/* strip start quote by resetting ptr */
p = in;
/* match quoted strings */
while((c = fgetc(yyin)) != EOF && c != quot) {
if(!isascii(c))
continue;
if(++cnt + 1 >= sizeof(in))
yyerror("string too long");
/* we have to guard the line count */
if(c == '\n')
yylineno++;
if(c == '\\') {
int b;
switch(b = fgetc(yyin)) {
case EOF:
yyerror("EOF in quoted string");
break;
case 't':
*p++ = '\t';
break;
case ';':
*p++ = MATCH_CHAR;
break;
case 'n':
*p++ = '\n';
break;
default:
*p++ = b;
}
} else {
*p++ = c;
}
}
if(c == EOF)
yyerror("EOF in quoted string");
*p = '\0';
/* if the string is already in the buffer, return that */
if(stringoff > 0 && (yylval = searchbuf(in)) != -1) {
return(STR);
}
if(stringoff + cnt > sizeof(stringbuf)) {
yyerror("out of string space!");
compiler_state = COMPILE_OOS;
return(OUTOFSPACE);
}
bcopy(in,&stringbuf[stringoff],cnt + 2);
yylval = stringoff;
stringoff += cnt + 2;
stringbuf[stringoff - 1] = '\0';
return(STR);
}
switch(c) {
case '=':
{
int t;
t = fgetc(yyin);
if(t != '=') {
(void)ungetc(t,yyin);
return(ASGN);
}
}
return(EQ);
case '>':
{
int t;
t = fgetc(yyin);
if(t != '=') {
(void)ungetc(t,yyin);
return(GT);
}
}
return(GTE);
case '<':
{
int t;
t = fgetc(yyin);
if(t != '=') {
(void)ungetc(t,yyin);
return(LT);
}
}
return(LTE);
case '!':
{
int t;
t = fgetc(yyin);
if(t != '=') {
(void)ungetc(t,yyin);
return(NOT);
}
}
return(NE);
case '&':
{
int t;
t = fgetc(yyin);
if(t != '&') {
(void)ungetc(t,yyin);
return(REF);
}
}
return(AND);
case '|':
{
int t;
t = fgetc(yyin);
if(t != '|') {
(void)ungetc(t,yyin);
return(t);
}
}
return(OR);
case '/':
return(DIV);
case '*':
return(MUL);
case '-':
return(SUB);
case '+':
return(ADD);
}
/* punt */
if(c == '\n')
yylineno++;
return(c);
}