%{ #include <stdio.h> #include <ctype.h> /* hack */ #define DONT_REDEFINE_MALLOC_DAMMIT #include "config.h" #include "mud.h" #include "cmd.h" #include "sbuf.h" #include "vars.h" #include "u.h" /* input read from a line */ static char *yyin = (char *)0; static char *p_who; static Nod *node(); static Nod *compiled; /* root of compiled program */ static Nod *tmpsyms; /* root of scope symbol table */ static Nod *ins_stmt(); static int bltcmdreturned = -1; /* kludge */ static Nod bltcmdretnod; static int returning = 0; %} %union { Nod *nval; char *cval; int ival; } %token NUM STR IDENT IF IN FOR FORARG ELSE VNULL %token ARGLIST CALL CALLCNT CALLIDENT EVAL STMT TASGN TEVAL %token NEGATE VOMIT OID BLTIN BLTINCMD RETURN SUID %right ASGN %left OR %left AND %left GT GTE LT LTE EQ NE %left ADD SUB %left MUL DIV %right UNARY %left NOT %type <nval> asgn statement statements condition %type <nval> refnum element_head element expr arglist %type <cval> STR IDENT %type <ival> NUM %% program: statements { compiled = $1; } | error { say(p_who,"program run aborted\n",(char *)0); compiled = NNULL; } ; statements: /* nothing */ { $$ = (Nod *)0; } | statements statement ';' { $$ = ins_stmt($1,node(STMT,NNULL,$2)); } ; asgn: '$' IDENT ASGN expr { $$ = node(TASGN,$4,NNULL); $$->nv.cv = $2; } | element ASGN expr { $$ = node(ASGN,$3,$1); $$->nv.cv = (char *)0; } | '(' IDENT ')' element ASGN expr { $$ = node(ASGN,$6,$4); $$->nv.cv = $2; } ; statement: /* NOTHING */ { $$ = node(VNULL,NNULL,NNULL); $$->nv.iv = UERR_NONE; } | expr | IF condition statement ELSE statement { $$ = node(IF,$2,node(ELSE,$3,$5)); } | IF condition statement { $$ = node(IF,$2,node(ELSE,$3,NNULL)); } | FOR '$' IDENT IN condition statement { $$ = node(FOR,$5,$6); $$->nv.cv = $3; } | FORARG '$' IDENT statement { $$ = node(FORARG,$4,NNULL); $$->nv.cv = $3; } | RETURN expr { $$ = node(RETURN,$2,NNULL); } | RETURN { $$ = node(RETURN,node(VNULL,NNULL,NNULL),NNULL); } | '{' statements '}' { $$ = $2; } | refnum ':' '{' statements '}' { $$ = node(SUID,$1,$4); } ; condition: '(' expr ')' { $$ = $2; } ; refnum: '#' NUM { char obuf[MAXOID]; $$ = node(OID,NNULL,NNULL); $$->nv.cv = tmpstr(itoa($2,obuf)); } | '#' NUM '@' IDENT { char obuf[MAXOID]; int l; /* must assemble parsed object-id - icky, but it works */ $$ = node(OID,NNULL,NNULL); (void)itoa($2,obuf); if((l = (strlen(obuf) + strlen($4) + 2)) > MAXOID) { $$ = node(VNULL,NNULL,NNULL); $$->nv.iv = UERR_BADOID; } else { $$ = node(OID,NNULL,NNULL); $$->nv.cv = (char *)tmpalloc(l); sprintf($$->nv.cv,"%s@%s",obuf,$4); } } | '#' IDENT { $$ = node(OID,NNULL,NNULL); $$->nv.cv = $2; } | '#' STR { $$ = node(OID,NNULL,NNULL); $$->nv.cv = $2; } ; element_head: refnum | '$' IDENT { $$ = node(TEVAL,NNULL,NNULL); $$->nv.cv = $2; } | '$' NUM { $$ = node(CALLIDENT,NNULL,NNULL); $$->nv.iv = $2; } ; element: element_head '.' IDENT { $$ = node(IDENT,$1,node(STR,NNULL,NNULL)); $$->l->nv.cv = $3; } | element '.' IDENT { $$ = node(IDENT,$1,node(STR,NNULL,NNULL)); $$->l->nv.cv = $3; } | element_head '.' '(' expr ')' { $$ = node(IDENT,$1,$4); } | element '.' '(' expr ')' { $$ = node(IDENT,$1,$4); } ; expr: NUM { $$ = node(NUM,NNULL,NNULL); $$->nv.iv = $1; } | STR { $$ = node(STR,NNULL,NNULL); $$->nv.cv = $1; } | VNULL { $$ = node(VNULL,NNULL,NNULL); $$->nv.iv = UERR_USER; } | refnum | '$' '#' { $$ = node(CALLCNT,NNULL,NNULL); } | '$' NUM { $$ = node(CALLIDENT,NNULL,NNULL); $$->nv.iv = $2; } | '$' IDENT { $$ = node(TEVAL,NNULL,NNULL); $$->nv.cv = $2; } | asgn | element '(' arglist ')' { $$ = node(CALL,$1,$3); } | IDENT '(' arglist ')' { $$ = node(BLTIN,NNULL,$3); $$->nv.cv = $1; } | '@' IDENT '(' arglist ')' { $$ = node(BLTINCMD,NNULL,$4); $$->nv.cv = $2; } | element { $$ = node(EVAL,$1,NNULL); } | '(' expr ')' { $$ = $2; } | expr ADD expr { $$ = node(ADD,$3,$1); } | expr SUB expr { $$ = node(SUB,$3,$1); } | expr MUL expr { $$ = node(MUL,$3,$1); } | expr DIV expr { $$ = node(DIV,$3,$1); } | SUB expr %prec UNARY { $$ = node(NEGATE,NNULL,$2); } | expr EQ expr { $$ = node(EQ,$3,$1); } | expr NE expr { $$ = node(NE,$3,$1); } | expr AND expr { $$ = node(AND,$3,$1); } | expr OR expr { $$ = node(OR,$3,$1); } | expr LT expr { $$ = node(LT,$3,$1); } | expr LTE expr { $$ = node(LTE,$3,$1); } | expr GT expr { $$ = node(GT,$3,$1); } | expr GTE expr { $$ = node(GTE,$3,$1); } | NOT expr %prec UNARY { $$ = node(NOT,NNULL,$2); } ; arglist: /* nothing */ { $$ = (Nod *)0; } | expr { $$ = node(ARGLIST,NNULL,$1); } | arglist ',' expr { $$ = ins_stmt($1,node(ARGLIST,NNULL,$3)); } ; %% /* append an expression to a list. needed because of how we parse */ static Nod * ins_stmt(nl,n) Nod *nl; Nod *n; { Nod *np = nl; while(np != NNULL && np->r != NNULL) np = np->r; if(np == NNULL) return(n); np->r = n; n->r = NNULL; return(nl); } static int lookup(s) char *s; { int start = 0; int ret; static struct kwordz { char *kw; int rval; } keyz[] = { "NULL", VNULL, "else", ELSE, "foreach", FOR, "foreacharg", FORARG, "if", IF, "in", IN, "return", RETURN, 0,0 }; int end = (sizeof(keyz)/sizeof(struct kwordz)) - 2; int p = end/2; while(start <= end) { ret = strcmp(s,keyz[p].kw); if(ret == 0) return(keyz[p].rval); if(ret > 0) start = p + 1; else end = p - 1; p = start + ((end - start)/2); } return(-1); } static int follow(expect,ifyes,ifno) int expect; int ifyes; int ifno; { if(*yyin == expect) { yyin++; return(ifyes); } return(ifno); } void parser_setinput(s) char *s; { yyin = s; } int parser_compile(who) char *who; { p_who = who; compiled = NNULL; yyparse(); p_who = (char *)0; if(compiled != NNULL) return(1); return(0); } int parser_run(who,aswho,ac,av) char *who; char *aswho; int ac; char *av[]; { int rv; Nod *savret; returning = 0; if(add_run_level() > MAXRECURSIONS) { say(who,"Too many recursions. Program ends.\n",(char *)0); return(0); } tmpsyms = NNULL; p_who = who; savret = compiled; rv = eval(compiled,who,aswho,ac,av); /* if returned buffer is empty, adopt whatever was left in root Nod */ if(!returning) eval_adopt(savret,&bltcmdretnod,0); p_who = (char *)0; return(rv); } /* simple lexical analyser that reads input from a string (yyin) */ static int yylex() { char lexbuf[MUDBUF]; char *p = lexbuf; if(yyin == (char *)0) return(0); /* handle whitespace */ while(isspace(*yyin)) yyin++; if(*yyin == '\0') { yyin = (char *)0; return(0); } /* handle NUM */ if(isdigit(*yyin)) { int num = *yyin - '0'; yyin++; while(isdigit(*yyin)) { num = (num * 10) + (*yyin - '0'); yyin++; } yylval.ival = num; return(NUM); } /* handle keywords or idents/builtins */ if(isalpha(*yyin) || *yyin == '_') { int cnt = 0; int rv; *p++ = *yyin++; while(*yyin != '\0' && (isalnum(*yyin) || *yyin == '_')) { if(++cnt + 1 >= 20) { say(p_who,"identifier too long\n",(char *)0); return(VOMIT); } *p++ = *yyin++; } *p = '\0'; if((rv = lookup(lexbuf)) != -1) return(rv); yylval.cval = tmpstr(lexbuf); return(IDENT); } /* handle quoted strings */ if(*yyin == '"' || *yyin == '\'') { int cnt = 0; int quot = *yyin++; /* match quoted strings */ while(*yyin != '\0' && *yyin != quot) { if(!isascii(*yyin)) { yyin++; continue; } if(++cnt + 1 >= sizeof(lexbuf)) { say(p_who,"string too long\n",(char *)0); return(VOMIT); } if(*yyin == '\\') { yyin++; switch(*yyin) { case '\0': say(p_who,"EOF in string\n",(char *)0); return(VOMIT); case 't': *p++ = '\t'; break; #ifdef U_NEWLINES case 'n': *p++ = '\n'; break; #endif default: *p++ = *yyin; break; } } else { *p++ = *yyin; } yyin++; } if(*yyin == '\0') { say(p_who,"EOF in string\n",(char *)0); return(VOMIT); } if(*yyin == quot) yyin++; *p = '\0'; yylval.cval = tmpstr(lexbuf); return(STR); } yyin++; switch(*(yyin - 1)) { case '=': return(follow('=',EQ,ASGN)); case '>': return(follow('=',GTE,GT)); case '<': return(follow('=',LTE,LT)); case '!': return(follow('=',NE,NOT)); case '&': return(follow('&',AND,'&')); case '|': return(follow('|',OR,'|')); case '/': return(DIV); case '*': return(MUL); case '-': return(SUB); case '+': return(ADD); } return(*(yyin - 1)); } static int yyerror(s) char *s; { say(p_who,s,"\n",(char *)0); } static Nod * node(t,r,l) int t; Nod *r; Nod *l; { Nod *ret; if((ret = (Nod *)tmpalloc(sizeof(Nod))) == (Nod *)0) { exit(1); } ret->l = l; ret->r = r; ret->nv.t = t; ret->nv.cv = (char *)0; return(ret); } static Nod * eval_gettmp(nam,tab,who,aswho) char *nam; Nod *tab; char *who; char *aswho; { static Nod fak; while(tab != NNULL) { if(!strcmp(nam,tab->nv.cv)) return(tab); tab = tab->r; } /* didn't find it? overload the usual suspects */ if(!strcmp(nam,"here")) { fak.rv.cv = ut_loc(aswho); fak.rv.t = OID; return(&fak); } if(!strcmp(nam,"actor")) { fak.rv.cv = run_actor(); fak.rv.t = OID; return(&fak); } if(!strcmp(nam,"me") || !strcmp(nam,"self")) { fak.rv.cv = aswho; fak.rv.t = OID; return(&fak); } if(!strcmp(nam,"subv") || !strcmp(nam,"objv")) { if((fak.rv.cv = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0) fak.rv.cv = "it"; fak.rv.t = STR; return(&fak); } if(!strcmp(nam,"Subv") || !strcmp(nam,"Objv")) { if((fak.rv.cv = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0) fak.rv.cv = "It"; fak.rv.t = STR; return(&fak); } if(!strcmp(nam,"posv")) { if((fak.rv.cv = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0) fak.rv.cv = "its"; fak.rv.t = STR; return(&fak); } if(!strcmp(nam,"Posv")) { if((fak.rv.cv = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0) fak.rv.cv = "Its"; fak.rv.t = STR; return(&fak); } return(NNULL); } /* kludge to permit passing back values from normal cmds. if this is passed back in a U call, we set the returned value in the node appropriately. */ void eval_cmd_returnint(val) int val; { bltcmdretnod.rv.t = bltcmdreturned = NUM; bltcmdretnod.rv.iv = val; } /* kludge to permit passing back values from normal cmds. here we pass back a string */ void eval_cmd_returnstr(val) int val; { bltcmdretnod.rv.t = bltcmdreturned = STR; bltcmdretnod.rv.cv = tmpstr(val); } /* kludge to permit passing back values from normal cmds. here we pass back an OBJ */ void eval_cmd_returnoid(val) int val; { bltcmdretnod.rv.t = bltcmdreturned = OID; bltcmdretnod.rv.cv = tmpstr(val); } int eval_cmd_returnedtrue() { if(bltcmdreturned == -1) return(0); return(eval_castbool(&bltcmdretnod)); } /* recursively evaluate nodes, placing returned values in .rv */ eval(n,w,aw,ac,av) Nod *n; /* root node */ char *w; /* who */ char *aw; /* aswho */ int ac; /* initial arg count */ char *av[]; /* arg vector */ { if(n == NNULL) return(1); switch(n->nv.t) { /* evaluate statements (a linked list), and throw results away */ case STMT: while(1) { if(eval(n->l,w,aw,ac,av) != 0) return(1); if(n->r == NNULL || returning) break; n = n->r; } if(returning) eval_adopt(&bltcmdretnod,n,0); else { n->rv.t = VNULL; n->rv.iv = UERR_NONE; } return(0); /* call a set of commands as someone else */ case SUID: if(eval(n->r,w,aw,ac,av) != 0) return(1); if(n->r->rv.t != OID) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; return(0); } if(ut_flagged(aw,var_wiz) || ut_isobjown(aw,n->r->rv.cv)) { char *sava; sava = run_actor(); run_setactor(n->r->rv.cv); if(eval(n->l,n->r->rv.cv,n->r->rv.cv,ac,av) != 0) return(1); run_setactor(sava); eval_adopt(n->l,n,0); } n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); /* function or macro call. */ case CALL: /* stack a new temporary variable context and restore after call */ /* we must needs duplicate a lot of the functionality of EVAL here but can't do it exactly because we need to get the type of the thing we're calling (be it cmd or U or whatever) */ if(n->r == NNULL || n->r->r == NNULL) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; return(0); } /* if this is not the case, we have a scrambled parse tree */ if(n->r->nv.t != IDENT) { n->rv.t = VNULL; n->rv.iv = UERR_FATAL; return(1); } if(eval(n->r->l,w,aw,ac,av) != 0) return(1); if(eval(n->r->r,w,aw,ac,av) != 0) return(1); if(n->r->r->rv.t != OID) { n->rv.t = VNULL; n->rv.iv = n->r->r->rv.iv; return(0); } /* now get the attribute of the object in question */ if((n->rv.cv = var_namatch(n->r->l->rv.cv)) == (char *)0) n->rv.cv = n->r->l->rv.cv; n->rv.cv = ut_getatt(n->r->r->rv.cv,1,(char *)0,n->rv.cv,(char *)0); if(n->rv.cv == (char *)0) { n->rv.t = VNULL; n->rv.iv = UERR_NOATTR; return(0); } /* permissions for the small minded - check IF it exists (faster) */ if(!ut_flagged(aw,var_wiz) && !var_ispublic(n->rv.cv,w,aw,n->r->r->rv.cv) && !ut_isobjown(aw,n->r->r->rv.cv)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); } returning = 0; /* is it a cmd? if so just call through */ if(attistype(n->rv.cv,typ_cmd)) { char *nav[MAXARG]; int nac = 0; Nod *argp; /* setup argvec from arglist */ nav[nac++] = n->r->l->rv.cv; for(argp = n->l;argp != NNULL; argp = argp->r) { if(eval(argp->l,w,aw,ac,av) != 0) return(1); nav[nac++] = eval_caststr(argp->l); if(nac >= MAXARG - 1) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(0); } } nav[nac] = (char *)0; n->rv.t = NUM; n->rv.iv = run(w,aw,attdata(n->rv.cv),nac,nav,0); returning = 0; return(0); } /* is it a U program? if so compile and call */ if(attistype(n->rv.cv,typ_u)) { char *nav[MAXARG]; int nac = 0; Nod *argp; Nod *symsav; /* pointer to old symbol table */ parser_setinput(attdata(n->rv.cv)); if(!parser_compile(w)) { n->rv.t = VNULL; n->rv.iv = UERR_SYNTAX; return(0); } /* build argvec */ nav[nac++] = n->r->l->rv.cv; for(argp = n->l;argp != NNULL; argp = argp->r) { if(eval(argp->l,w,aw,ac,av) != 0) return(1); nav[nac++] = eval_caststr(argp->l); if(nac >= MAXARG - 1) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(0); } } nav[nac] = (char *)0; n->rv.t = NUM; symsav = tmpsyms; tmpsyms = NNULL; /* new sym table, local scope */ n->rv.iv = parser_run(w,aw,nac,nav,0); tmpsyms = symsav; /* pop back old sym table */ if(returning) eval_adopt(&bltcmdretnod,n,0); returning = 0; return(0); } /* feh */ n->rv.t = VNULL; n->rv.iv = UERR_NOFUNC; return(0); /* builtin op - call through to builtin function */ case BLTIN: if(n->nv.cv[0] != '\0') { Bltin *bp; if((bp = u_bltlookup(n->nv.cv)) != (Bltin *)0) { Nod *argp; Nod *avec[MAXARG]; int acnt = 0; int rv; for(argp = n->l;argp != NNULL; argp = argp->r) { if(eval(argp->l,w,aw,ac,av) != 0) return(1); avec[acnt++] = argp->l; if(acnt >= MAXARG - 1) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(0); } } /* function does not take varargs. therefore, check */ if(bp->flgs & BLT_FIXARG && acnt != bp->argc) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(1); } /* wizard only ? */ if(bp->flgs & BLT_WIZONLY && !ut_flagged(aw,var_wiz)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(1); } /* call through function. function sets its own return value in process (presumably). we set return value to zero, so functions can return it as a success signal, if need be. */ n->rv.t = NUM; n->rv.iv = 0; if((rv = (*bp->func)(w,aw,acnt,avec,n)) != UERR_NONE) { n->rv.t = VNULL; n->rv.iv = rv; return(0); } return(0); } } n->rv.t = VNULL; n->rv.iv = UERR_NOFUNC; return(0); /* builtin command - call to normal U-macro builtin */ case BLTINCMD: if(n->nv.cv[0] != '\0') { Cmd *cp; if((cp = u_cmdlookup(n->nv.cv)) != (Cmd *)0) { Nod *argp; char *nav[MAXARG]; int nac = 0; nav[nac++] = n->nv.cv; for(argp = n->l;argp != NNULL; argp = argp->r) { if(eval(argp->l,w,aw,ac,av) != 0) return(1); nav[nac++] = eval_caststr(argp->l); if(nac >= MAXARG - 1) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(0); } } nav[nac] = (char *)0; if(cp->flgs & CM_FIXARG && nac != cp->argc) { n->rv.t = VNULL; n->rv.iv = UERR_ARGCNT; return(0); } /* wizard only ? */ if(cp->flgs & CM_PRIV && !ut_flagged(aw,var_wiz)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); } #ifdef PLAYERONLY if(cp->flgs & CM_NOPLY && !ut_flagged(aw,var_wiz) && !ut_flagged(w,var_isplay)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); } #endif /* call through function. function sets its own return value in process (presumably). we set return value to zero, so functions can return it as a success signal, if need be. */ bltcmdreturned = -1; /* if the function returns nonzero, it failed we assume, so we assign the return value to VNULL and trap the return code as an error number and HOPE the guy who wrote the cmd got it right. */ n->rv.t = NUM; if((n->rv.iv = (*cp->func)(nac,nav,w,aw)) != UERR_NONE) n->rv.t = VNULL; else n->rv.iv = 0; /* cmd tried to explicitly pass back value.. */ if(bltcmdreturned != -1) eval_adopt(&bltcmdretnod,n,0); return(0); } } n->rv.t = VNULL; n->rv.iv = UERR_NOFUNC; return(0); /* parameter count - easy */ case CALLCNT: n->rv.t = NUM; n->rv.iv = ac; return(0); /* parameter by number */ case CALLIDENT: if(n->nv.iv < 0 || n->nv.iv >= ac) { n->rv.iv = UERR_NOATTR; n->rv.t = VNULL; return(0); } n->rv.t = STR; n->rv.cv = av[n->nv.iv]; return(0); /* conditional operator IF nodes are built with an ELSE node on the left. we need to look down the branches of the ELSE node. $$ = node(IF,$2,node(ELSE,$3,$5)); */ case IF: if(n->l == NNULL || n->l->nv.t != ELSE) return(1); if(n->r != NNULL && eval(n->r,w,aw,ac,av) != 0) return(1); if(n->r != NNULL && eval_castbool(n->r)) { if(n->l->r != NNULL && eval(n->l->r,w,aw,ac,av) != 0) return(1); } else { if(n->l->l != NNULL && eval(n->l->l,w,aw,ac,av) != 0) return(1); } return(0); /* iterate a list. note that the list condition gets evaluated exactly once. */ case FOR: if(eval(n->r,w,aw,ac,av)) return(1); if(n->r->rv.t != STR) { n->rv.t = VNULL; n->rv.iv = UERR_BADLST; return(0); } if(n->r->rv.cv[0] != '\0') { char *lp; Sbuf sb; Nod *tn; lp = n->r->rv.cv; sbuf_initstatic(&sb); while((lp = lstnextsbuf(lp,&sb)) != (char *)0) { if((tn = eval_gettmp(n->nv.cv,tmpsyms,w,aw)) == (Nod *)0) tmpsyms = tn = node(0,tmpsyms,NNULL); tn->nv.cv = n->nv.cv; tn->rv.t = STR; tn->rv.cv = sbuf_buf(&sb); if(eval(n->l,w,aw,ac,av)) return(1); } sbuf_freestatic(&sb); } n->rv.t = VNULL; n->rv.iv = UERR_NONE; return(0); /* iterate all our paramters */ case FORARG: if(n->nv.cv[0] != '\0') { Nod *tn; int c; for(c = 1; c < ac; c++) { if((tn = eval_gettmp(n->nv.cv,tmpsyms,w,aw)) == (Nod *)0) tmpsyms = tn = node(0,tmpsyms,NNULL); tn->nv.cv = n->nv.cv; tn->rv.t = STR; tn->rv.cv = av[c]; if(eval(n->r,w,aw,ac,av)) return(1); } } n->rv.t = VNULL; n->rv.iv = UERR_NONE; return(0); /* return from call */ case RETURN: if(eval(n->r,w,aw,ac,av) != 0) return(1); eval_adopt(n->r,n,0); eval_adopt(n,&bltcmdretnod,0); bltcmdreturned = n->rv.t; returning = 1; return(0); /* assignment operation to NON temporary. unfortunately a lot of this logic is duplicated from vars.c, but it's all subtly different. */ case ASGN: if(eval(n->r,w,aw,ac,av) != 0) return(1); /* l contains coding of where to store */ if(n->l->nv.t != IDENT) { n->rv.t = VNULL; n->rv.iv = UERR_FATAL; return(1); } /* eval attr to be assigned to */ if(eval(n->l->r,w,aw,ac,av) || eval(n->l->l,w,aw,ac,av)) return(1); if(n->l->r->rv.t != OID) { n->rv.t = VNULL; n->rv.iv = n->l->r->rv.iv; return(0); } /* actually perform the assignment and modify the database */ if(n->r->rv.t == VNULL) { /* assignment to NULL unsets value */ n->rv.iv = var_unset_internal(w,aw,n->l->r->rv.cv,n->l->l->rv.cv,0); } else { char nbuf[MAXOID]; char *tp = typ_str; char *dp = n->r->rv.cv; if(n->r->rv.t == NUM) { tp = typ_int; dp = itoa(n->r->rv.iv,nbuf); } /* cast overrides all */ if(n->nv.cv != (char *)0) { /* only wizards can give unknown types */ if(!fndtyp(n->nv.cv) && !ut_flagged(aw,var_wiz)) { n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); } tp = n->nv.cv; } n->rv.iv = var_set_internal(w,aw,n->l->r->rv.cv,tp,n->l->l->rv.cv,dp,0); } if(n->rv.iv != UERR_NONE) { n->rv.t = VNULL; return(0); } eval_adopt(n->r,n,0); return(0); /* assignment operation to temporary */ case TASGN: if(n->r == NNULL) { n->rv.t = VNULL; n->rv.iv = UERR_FATAL; return(1); } if(eval(n->r,w,aw,ac,av) == 0) { Nod *tn; if((tn = eval_gettmp(n->nv.cv,tmpsyms,w,aw)) == (Nod *)0) { tmpsyms = tn = node(0,tmpsyms,NNULL); eval_adopt(n->r,tn,0); tn->nv.cv = n->nv.cv; /* set name */ } else { eval_adopt(n->r,tn,0); /* reset existing */ } eval_adopt(tn,n,0); } else { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; } return(0); /* look up a temporary */ case TEVAL: if(n->nv.cv != (char *)0 && n->nv.cv[0] != '\0') { Nod *tn; if((tn = eval_gettmp(n->nv.cv,tmpsyms,w,aw)) != (Nod *)0) { eval_adopt(tn,n,0); return(0); } } n->rv.t = VNULL; n->rv.iv = UERR_NOATTR; return(0); /* look up an element value and return the contents */ case EVAL: /* both elemental nodes should be ready to eval */ if(n->r == NNULL || n->r->r == NNULL) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; return(0); } /* if this is not the case, we have a scrambled parse tree */ if(n->r->nv.t != IDENT) { n->rv.t = VNULL; n->rv.iv = UERR_FATAL; return(1); } if(eval(n->r->r,w,aw,ac,av) || eval(n->r->l,w,aw,ac,av)) return(1); if(n->r->r->rv.t != OID && n->r->r->rv.t != STR) { n->rv.t = VNULL; n->rv.iv = n->r->r->rv.iv; return(0); } /* now get the attribute of the object in question */ n->rv.cv = ut_getatt(n->r->r->rv.cv,1,(char *)0,n->r->l->rv.cv,(char *)0); if(n->rv.cv == (char *)0) { n->rv.t = VNULL; n->rv.iv = UERR_NOATTR; return(0); } /* permissions for the small minded - check IF it exists (faster) */ if(!ut_flagged(aw,var_wiz) && !var_ispublic(n->r->l->rv.cv,w,aw,n->r->r->rv.cv) && !ut_isobjown(aw,n->r->r->rv.cv)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); } /* *ATTEMPT* to give it SOME kind of type */ if(attistype(n->rv.cv,typ_int)) { n->rv.t = NUM; n->rv.iv = atoi(attdata(n->rv.cv)); } else if(attistype(n->rv.cv,typ_flag)) { n->rv.t = NUM; n->rv.iv = 1; } else { n->rv.t = STR; n->rv.cv = attdata(n->rv.cv); } return(0); /* chained evaluation. grammar ensures this had *better* be an OID */ case IDENT: if(eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->l->rv.t != STR && n->l->rv.t != OID) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; return(0); } if((n->r->rv.t != STR && n->r->rv.t != OID) || n->r->rv.cv == (char *)0) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; return(0); } n->rv.cv = ut_getatt(n->r->rv.cv,0,typ_obj,n->l->rv.cv,(char *)0); if(n->rv.cv == (char *)0) { n->rv.t = VNULL; n->rv.iv = UERR_NOATTR; return(0); } if(!ut_flagged(aw,var_wiz) && !var_ispublic(n->l->rv.cv,w,aw,n->r->rv.cv) && !ut_isobjown(aw,n->r->rv.cv)) { n->rv.t = VNULL; n->rv.iv = UERR_PERM; return(0); } n->rv.t = OID; return(0); /* basic sanity check on an object specifier */ case OID: if(!ut_isgoodid(n->nv.cv)) { n->rv.t = VNULL; n->rv.iv = UERR_BADOID; } n->rv.t = OID; n->rv.cv = n->nv.cv; return(0); /* addition operation. string addition is overloaded */ case ADD: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->l->rv.t == NUM && n->r->rv.t == NUM) { n->rv.t = NUM; n->rv.iv = n->l->rv.iv + n->r->rv.iv; return(0); } /* overload cast promote to string */ if(n->l->rv.t == STR) { int l1 = strlen(n->l->rv.cv); int l2; char *x; x = eval_caststr(n->r); l2 = strlen(x); n->rv.t = STR; n->rv.cv = tmpalloc((unsigned)(l1 + l2 + 1)); bcopy(n->l->rv.cv,n->rv.cv,l1); bcopy(x,n->rv.cv + l1,l2); *(n->rv.cv + l1 + l2) = '\0'; return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* subtraction operation. numeric only */ case SUB: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->l->rv.t == NUM && n->r->nv.t == NUM) { n->rv.t = NUM; n->rv.iv = n->l->rv.iv - n->r->rv.iv; return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* multiply - used for numerics only */ case MUL: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->l->rv.t == NUM && n->r->nv.t == NUM) { n->rv.t = NUM; n->rv.iv = n->l->rv.iv * n->r->rv.iv; return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* division with check for div by zero - numerics only */ case DIV: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->l->rv.t == NUM && n->r->nv.t == NUM) { n->rv.t = NUM; if(n->r->rv.iv == 0) { n->rv.t = VNULL; n->rv.iv = UERR_ZDIV; return(0); } n->rv.iv = n->l->rv.iv / n->r->rv.iv; return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* equality */ case EQ: case NE: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); n->rv.t = NUM; if(n->l->rv.t == NUM && n->r->rv.t == NUM) { if(n->nv.t == EQ) n->rv.iv = (n->l->rv.iv == n->r->rv.iv); else n->rv.iv = (n->l->rv.iv != n->r->rv.iv); return(0); } if((n->l->rv.t == STR || n->l->rv.t == OID) && (n->r->rv.t == STR || n->r->rv.t == OID)) { int xx = strcmp(n->r->rv.cv,n->l->rv.cv); if(n->nv.t == EQ) n->rv.iv = xx == 0 ? 1 : 0; else n->rv.iv = xx != 0 ? 1 : 0; return(0); } if(n->l->rv.t == VNULL && n->r->rv.t == VNULL) { if(n->nv.t == EQ) n->rv.iv = 1; else n->rv.iv = 0; return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* grouping operations */ case AND: case OR: n->rv.t = NUM; if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); if(n->nv.t == AND) n->rv.iv = (eval_castbool(n->l) && eval_castbool(n->r)); else n->rv.iv = (eval_castbool(n->l) || eval_castbool(n->r)); return(0); /* ordering operation. string ordering is overloaded. */ case LT: case LTE: case GT: case GTE: if(n->l == NNULL || n->r == NNULL || eval(n->r,w,aw,ac,av) || eval(n->l,w,aw,ac,av)) return(1); n->rv.t = NUM; if(n->l->rv.t == NUM && n->r->rv.t == NUM) { if(n->nv.t == LTE) n->rv.iv = (n->l->rv.iv <= n->r->rv.iv); else if(n->nv.t == LT) n->rv.iv = (n->l->rv.iv < n->r->rv.iv); else if(n->nv.t == GT) n->rv.iv = (n->l->rv.iv > n->r->rv.iv); else n->rv.iv = (n->l->rv.iv >= n->r->rv.iv); return(0); } if(n->l->rv.t == STR && n->r->rv.t == STR) { int x = strcmp(n->l->rv.cv,n->r->rv.cv); if(n->nv.t == LTE) n->rv.iv = (x <= 0); else if(n->nv.t == LT) n->rv.iv = (x < 0); else if(n->nv.t == GT) n->rv.iv = (x > 0); else n->rv.iv = (x >= 0); return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* negate numeric value */ case NEGATE: if(n->l == NNULL || eval(n->l,w,aw,ac,av)) return(1); n->rv.t = NUM; if(n->l->rv.t == NUM) { n->rv.t = NUM; n->rv.iv = -(n->l->rv.iv); return(0); } n->rv.t = VNULL; n->rv.iv = UERR_TYPE; return(0); /* invert truth sense */ case NOT: if(n->l == NNULL || eval(n->l,w,aw,ac,av)) return(1); n->rv.t = NUM; n->rv.iv = !(eval_castbool(n->l)); return(0); /* pose a NULL */ case VNULL: n->rv.t = n->nv.t; n->rv.iv = n->nv.iv; return(0); /* numeric literal */ case NUM: n->rv.t = n->nv.t; n->rv.iv = n->nv.iv; return(0); /* string literal */ case STR: n->rv.t = n->nv.t; n->rv.cv = n->nv.cv; return(0); /* error */ default: n->rv.t = VNULL; n->rv.iv = UERR_FATAL; return(1); } }