blob: f08c35cc3d8a9d2a79e0257fca010d1cc2647324 [file] [log] [blame]
/* $Header$
*
* Copyright (c) 1989, Larry Wall
*
* You may distribute under the terms of the GNU General Public License
* as specified in the README file that comes with the perl 3.0 kit.
*
* $Log$
* Revision 1.2 2004/09/01 14:33:24 criswell
* Migrating test suite out of the source tree.
*
* Revision 1.1 2004/02/17 22:21:15 criswell
* Initial commit of the perl Malloc Benchmark. I've cheated a little by
* generating the yacc output files and committing them directly, but it was
* easier than disabling the Bison Voodoo that gets executed by default.
*
* Revision 4.0 91/03/20 01:05:51 lwall
* 4.0 baseline.
*
*/
#include "EXTERN.h"
#include "perl.h"
#include "perly.h"
extern char *tokename[];
extern int yychar;
static int cmd_tosave();
static int arg_tosave();
static int spat_tosave();
static bool saw_return;
SUBR *
make_sub(name,cmd)
char *name;
CMD *cmd;
{
register SUBR *sub;
STAB *stab = stabent(name,TRUE);
Newz(101,sub,1,SUBR);
if (stab_sub(stab)) {
if (dowarn) {
CMD *oldcurcmd = curcmd;
if (cmd)
curcmd = cmd;
warn("Subroutine %s redefined",name);
curcmd = oldcurcmd;
}
if (stab_sub(stab)->cmd) {
cmd_free(stab_sub(stab)->cmd);
stab_sub(stab)->cmd = Nullcmd;
afree(stab_sub(stab)->tosave);
}
Safefree(stab_sub(stab));
}
stab_sub(stab) = sub;
sub->filestab = curcmd->c_filestab;
saw_return = FALSE;
tosave = anew(Nullstab);
tosave->ary_fill = 0; /* make 1 based */
(void)cmd_tosave(cmd,FALSE); /* this builds the tosave array */
sub->tosave = tosave;
if (saw_return) {
struct compcmd mycompblock;
mycompblock.comp_true = cmd;
mycompblock.comp_alt = Nullcmd;
cmd = add_label(savestr("_SUB_"),make_ccmd(C_BLOCK,Nullarg,mycompblock));
saw_return = FALSE;
cmd->c_flags |= CF_TERM;
}
sub->cmd = cmd;
if (perldb) {
STR *str;
STR *tmpstr = str_mortal(&str_undef);
sprintf(buf,"%s:%ld",stab_val(curcmd->c_filestab)->str_ptr,
(long)subline);
str = str_make(buf,0);
str_cat(str,"-");
sprintf(buf,"%ld",(long)curcmd->c_line);
str_cat(str,buf);
name = str_get(subname);
stab_fullname(tmpstr,stab);
hstore(stab_xhash(DBsub), tmpstr->str_ptr, tmpstr->str_cur, str, 0);
str_set(subname,"main");
}
subline = 0;
return sub;
}
SUBR *
make_usub(name, ix, subaddr, filename)
char *name;
int ix;
int (*subaddr)();
char *filename;
{
register SUBR *sub;
STAB *stab = stabent(name,allstabs);
if (!stab) /* unused function */
return Null(SUBR*);
Newz(101,sub,1,SUBR);
if (stab_sub(stab)) {
if (dowarn)
warn("Subroutine %s redefined",name);
if (stab_sub(stab)->cmd) {
cmd_free(stab_sub(stab)->cmd);
stab_sub(stab)->cmd = Nullcmd;
afree(stab_sub(stab)->tosave);
}
Safefree(stab_sub(stab));
}
stab_sub(stab) = sub;
sub->filestab = fstab(filename);
sub->usersub = subaddr;
sub->userindex = ix;
return sub;
}
make_form(stab,fcmd)
STAB *stab;
FCMD *fcmd;
{
if (stab_form(stab)) {
FCMD *tmpfcmd;
FCMD *nextfcmd;
for (tmpfcmd = stab_form(stab); tmpfcmd; tmpfcmd = nextfcmd) {
nextfcmd = tmpfcmd->f_next;
if (tmpfcmd->f_expr)
arg_free(tmpfcmd->f_expr);
if (tmpfcmd->f_unparsed)
str_free(tmpfcmd->f_unparsed);
if (tmpfcmd->f_pre)
Safefree(tmpfcmd->f_pre);
Safefree(tmpfcmd);
}
}
stab_form(stab) = fcmd;
}
CMD *
block_head(tail)
register CMD *tail;
{
CMD *head;
register int opt;
register int last_opt = 0;
register STAB *last_stab = Nullstab;
register int count = 0;
register CMD *switchbeg = Nullcmd;
if (tail == Nullcmd) {
return tail;
}
head = tail->c_head;
for (tail = head; tail; tail = tail->c_next) {
/* save one measly dereference at runtime */
if (tail->c_type == C_IF) {
if (!(tail->ucmd.ccmd.cc_alt = tail->ucmd.ccmd.cc_alt->c_next))
tail->c_flags |= CF_TERM;
}
else if (tail->c_type == C_EXPR) {
ARG *arg;
if (tail->ucmd.acmd.ac_expr)
arg = tail->ucmd.acmd.ac_expr;
else
arg = tail->c_expr;
if (arg) {
if (arg->arg_type == O_RETURN)
tail->c_flags |= CF_TERM;
else if (arg->arg_type == O_ITEM && arg[1].arg_type == A_CMD)
tail->c_flags |= CF_TERM;
}
}
if (!tail->c_next)
tail->c_flags |= CF_TERM;
if (tail->c_expr && (tail->c_flags & CF_OPTIMIZE) == CFT_FALSE)
opt_arg(tail,1, tail->c_type == C_EXPR);
/* now do a little optimization on case-ish structures */
switch(tail->c_flags & (CF_OPTIMIZE|CF_FIRSTNEG|CF_INVERT)) {
case CFT_ANCHOR:
if (stabent("*",FALSE)) { /* bad assumption here!!! */
opt = 0;
break;
}
/* FALL THROUGH */
case CFT_STROP:
opt = (tail->c_flags & CF_NESURE) ? CFT_STROP : 0;
break;
case CFT_CCLASS:
opt = CFT_STROP;
break;
case CFT_NUMOP:
opt = (tail->c_slen == O_NE ? 0 : CFT_NUMOP);
if ((tail->c_flags&(CF_NESURE|CF_EQSURE)) != (CF_NESURE|CF_EQSURE))
opt = 0;
break;
default:
opt = 0;
}
if (opt && opt == last_opt && tail->c_stab == last_stab)
count++;
else {
if (count >= 3) { /* is this the breakeven point? */
if (last_opt == CFT_NUMOP)
make_nswitch(switchbeg,count);
else
make_cswitch(switchbeg,count);
}
if (opt) {
count = 1;
switchbeg = tail;
}
else
count = 0;
}
last_opt = opt;
last_stab = tail->c_stab;
}
if (count >= 3) { /* is this the breakeven point? */
if (last_opt == CFT_NUMOP)
make_nswitch(switchbeg,count);
else
make_cswitch(switchbeg,count);
}
return head;
}
/* We've spotted a sequence of CMDs that all test the value of the same
* spat. Thus we can insert a SWITCH in front and jump directly
* to the correct one.
*/
make_cswitch(head,count)
register CMD *head;
int count;
{
register CMD *cur;
register CMD **loc;
register int i;
register int min = 255;
register int max = 0;
/* make a new head in the exact same spot */
New(102,cur, 1, CMD);
#ifdef STRUCTCOPY
*cur = *head;
#else
Copy(head,cur,1,CMD);
#endif
Zero(head,1,CMD);
head->c_type = C_CSWITCH;
head->c_next = cur; /* insert new cmd at front of list */
head->c_stab = cur->c_stab;
Newz(103,loc,258,CMD*);
loc++; /* lie a little */
while (count--) {
if ((cur->c_flags & CF_OPTIMIZE) == CFT_CCLASS) {
for (i = 0; i <= 255; i++) {
if (!loc[i] && cur->c_short->str_ptr[i>>3] & (1 << (i&7))) {
loc[i] = cur;
if (i < min)
min = i;
if (i > max)
max = i;
}
}
}
else {
i = *cur->c_short->str_ptr & 255;
if (!loc[i]) {
loc[i] = cur;
if (i < min)
min = i;
if (i > max)
max = i;
}
}
cur = cur->c_next;
}
max++;
if (min > 0)
Copy(&loc[min],&loc[0], max - min, CMD*);
loc--;
min--;
max -= min;
for (i = 0; i <= max; i++)
if (!loc[i])
loc[i] = cur;
Renew(loc,max+1,CMD*); /* chop it down to size */
head->ucmd.scmd.sc_offset = min;
head->ucmd.scmd.sc_max = max;
head->ucmd.scmd.sc_next = loc;
}
make_nswitch(head,count)
register CMD *head;
int count;
{
register CMD *cur = head;
register CMD **loc;
register int i;
register int min = 32767;
register int max = -32768;
int origcount = count;
double value; /* or your money back! */
short changed; /* so triple your money back! */
while (count--) {
i = (int)str_gnum(cur->c_short);
value = (double)i;
if (value != cur->c_short->str_u.str_nval)
return; /* fractional values--just forget it */
changed = i;
if (changed != i)
return; /* too big for a short */
if (cur->c_slen == O_LE)
i++;
else if (cur->c_slen == O_GE) /* we only do < or > here */
i--;
if (i < min)
min = i;
if (i > max)
max = i;
cur = cur->c_next;
}
count = origcount;
if (max - min > count * 2 + 10) /* too sparse? */
return;
/* now make a new head in the exact same spot */
New(104,cur, 1, CMD);
#ifdef STRUCTCOPY
*cur = *head;
#else
Copy(head,cur,1,CMD);
#endif
Zero(head,1,CMD);
head->c_type = C_NSWITCH;
head->c_next = cur; /* insert new cmd at front of list */
head->c_stab = cur->c_stab;
Newz(105,loc, max - min + 3, CMD*);
loc++;
max -= min;
max++;
while (count--) {
i = (int)str_gnum(cur->c_short);
i -= min;
switch(cur->c_slen) {
case O_LE:
i++;
case O_LT:
for (i--; i >= -1; i--)
if (!loc[i])
loc[i] = cur;
break;
case O_GE:
i--;
case O_GT:
for (i++; i <= max; i++)
if (!loc[i])
loc[i] = cur;
break;
case O_EQ:
if (!loc[i])
loc[i] = cur;
break;
}
cur = cur->c_next;
}
loc--;
min--;
max++;
for (i = 0; i <= max; i++)
if (!loc[i])
loc[i] = cur;
head->ucmd.scmd.sc_offset = min;
head->ucmd.scmd.sc_max = max;
head->ucmd.scmd.sc_next = loc;
}
CMD *
append_line(head,tail)
register CMD *head;
register CMD *tail;
{
if (tail == Nullcmd)
return head;
if (!tail->c_head) /* make sure tail is well formed */
tail->c_head = tail;
if (head != Nullcmd) {
tail = tail->c_head; /* get to start of tail list */
if (!head->c_head)
head->c_head = head; /* start a new head list */
while (head->c_next) {
head->c_next->c_head = head->c_head;
head = head->c_next; /* get to end of head list */
}
head->c_next = tail; /* link to end of old list */
tail->c_head = head->c_head; /* propagate head pointer */
}
while (tail->c_next) {
tail->c_next->c_head = tail->c_head;
tail = tail->c_next;
}
return tail;
}
CMD *
dodb(cur)
CMD *cur;
{
register CMD *cmd;
register CMD *head = cur->c_head;
STR *str;
if (!head)
head = cur;
if (!head->c_line)
return cur;
str = afetch(stab_xarray(curcmd->c_filestab),(int)head->c_line,FALSE);
if (str == &str_undef || str->str_nok)
return cur;
str->str_u.str_nval = (double)head->c_line;
str->str_nok = 1;
Newz(106,cmd,1,CMD);
str_magic(str, curcmd->c_filestab, 0, Nullch, 0);
str->str_magic->str_u.str_cmd = cmd;
cmd->c_type = C_EXPR;
cmd->ucmd.acmd.ac_stab = Nullstab;
cmd->ucmd.acmd.ac_expr = Nullarg;
cmd->c_expr = make_op(O_SUBR, 2,
stab2arg(A_WORD,DBstab),
Nullarg,
Nullarg);
cmd->c_flags |= CF_COND|CF_DBSUB|CFT_D0;
cmd->c_line = head->c_line;
cmd->c_label = head->c_label;
cmd->c_filestab = curcmd->c_filestab;
cmd->c_stash = curstash;
return append_line(cmd, cur);
}
CMD *
make_acmd(type,stab,cond,arg)
int type;
STAB *stab;
ARG *cond;
ARG *arg;
{
register CMD *cmd;
Newz(107,cmd,1,CMD);
cmd->c_type = type;
cmd->ucmd.acmd.ac_stab = stab;
cmd->ucmd.acmd.ac_expr = arg;
cmd->c_expr = cond;
if (cond)
cmd->c_flags |= CF_COND;
if (cmdline == NOLINE)
cmd->c_line = curcmd->c_line;
else {
cmd->c_line = cmdline;
cmdline = NOLINE;
}
cmd->c_filestab = curcmd->c_filestab;
cmd->c_stash = curstash;
if (perldb)
cmd = dodb(cmd);
return cmd;
}
CMD *
make_ccmd(type,arg,cblock)
int type;
ARG *arg;
struct compcmd cblock;
{
register CMD *cmd;
Newz(108,cmd, 1, CMD);
cmd->c_type = type;
cmd->c_expr = arg;
cmd->ucmd.ccmd.cc_true = cblock.comp_true;
cmd->ucmd.ccmd.cc_alt = cblock.comp_alt;
if (arg)
cmd->c_flags |= CF_COND;
if (cmdline == NOLINE)
cmd->c_line = curcmd->c_line;
else {
cmd->c_line = cmdline;
cmdline = NOLINE;
}
cmd->c_filestab = curcmd->c_filestab;
cmd->c_stash = curstash;
if (perldb)
cmd = dodb(cmd);
return cmd;
}
CMD *
make_icmd(type,arg,cblock)
int type;
ARG *arg;
struct compcmd cblock;
{
register CMD *cmd;
register CMD *alt;
register CMD *cur;
register CMD *head;
struct compcmd ncblock;
Newz(109,cmd, 1, CMD);
head = cmd;
cmd->c_type = type;
cmd->c_expr = arg;
cmd->ucmd.ccmd.cc_true = cblock.comp_true;
cmd->ucmd.ccmd.cc_alt = cblock.comp_alt;
if (arg)
cmd->c_flags |= CF_COND;
if (cmdline == NOLINE)
cmd->c_line = curcmd->c_line;
else {
cmd->c_line = cmdline;
cmdline = NOLINE;
}
cmd->c_filestab = curcmd->c_filestab;
cmd->c_stash = curstash;
cur = cmd;
alt = cblock.comp_alt;
while (alt && alt->c_type == C_ELSIF) {
cur = alt;
alt = alt->ucmd.ccmd.cc_alt;
}
if (alt) { /* a real life ELSE at the end? */
ncblock.comp_true = alt;
ncblock.comp_alt = Nullcmd;
alt = append_line(cur,make_ccmd(C_ELSE,Nullarg,ncblock));
cur->ucmd.ccmd.cc_alt = alt;
}
else
alt = cur; /* no ELSE, so cur is proxy ELSE */
cur = cmd;
while (cmd) { /* now point everyone at the ELSE */
cur = cmd;
cmd = cur->ucmd.ccmd.cc_alt;
cur->c_head = head;
if (cur->c_type == C_ELSIF)
cur->c_type = C_IF;
if (cur->c_type == C_IF)
cur->ucmd.ccmd.cc_alt = alt;
if (cur == alt)
break;
cur->c_next = cmd;
}
if (perldb)
cur = dodb(cur);
return cur;
}
void
opt_arg(cmd,fliporflop,acmd)
register CMD *cmd;
int fliporflop;
int acmd;
{
register ARG *arg;
int opt = CFT_EVAL;
int sure = 0;
ARG *arg2;
int context = 0; /* 0 = normal, 1 = before &&, 2 = before || */
int flp = fliporflop;
if (!cmd)
return;
if (!(arg = cmd->c_expr)) {
cmd->c_flags &= ~CF_COND;
return;
}
/* Can we turn && and || into if and unless? */
if (acmd && !cmd->ucmd.acmd.ac_expr && !(cmd->c_flags & CF_TERM) &&
(arg->arg_type == O_AND || arg->arg_type == O_OR) ) {
dehoist(arg,1);
arg[2].arg_type &= A_MASK; /* don't suppress eval */
dehoist(arg,2);
cmd->ucmd.acmd.ac_expr = arg[2].arg_ptr.arg_arg;
cmd->c_expr = arg[1].arg_ptr.arg_arg;
if (arg->arg_type == O_OR)
cmd->c_flags ^= CF_INVERT; /* || is like unless */
arg->arg_len = 0;
free_arg(arg);
arg = cmd->c_expr;
}
/* Turn "if (!expr)" into "unless (expr)" */
if (!(cmd->c_flags & CF_TERM)) { /* unless return value wanted */
while (arg->arg_type == O_NOT) {
dehoist(arg,1);
cmd->c_flags ^= CF_INVERT; /* flip sense of cmd */
cmd->c_expr = arg[1].arg_ptr.arg_arg; /* hoist the rest of expr */
free_arg(arg);
arg = cmd->c_expr; /* here we go again */
}
}
if (!arg->arg_len) { /* sanity check */
cmd->c_flags |= opt;
return;
}
/* for "cond .. cond" we set up for the initial check */
if (arg->arg_type == O_FLIP)
context |= 4;
/* for "cond && expr" and "cond || expr" we can ignore expr, sort of */
morecontext:
if (arg->arg_type == O_AND)
context |= 1;
else if (arg->arg_type == O_OR)
context |= 2;
if (context && (arg[flp].arg_type & A_MASK) == A_EXPR) {
arg = arg[flp].arg_ptr.arg_arg;
flp = 1;
if (arg->arg_type == O_AND || arg->arg_type == O_OR)
goto morecontext;
}
if ((context & 3) == 3)
return;
if (arg[flp].arg_flags & (AF_PRE|AF_POST)) {
cmd->c_flags |= opt;
if (acmd && !cmd->ucmd.acmd.ac_expr && !(cmd->c_flags & CF_TERM)
&& cmd->c_expr->arg_type == O_ITEM) {
arg[flp].arg_flags &= ~AF_POST; /* prefer ++$foo to $foo++ */
arg[flp].arg_flags |= AF_PRE; /* if value not wanted */
}
return; /* side effect, can't optimize */
}
if (arg->arg_type == O_ITEM || arg->arg_type == O_FLIP ||
arg->arg_type == O_AND || arg->arg_type == O_OR) {
if ((arg[flp].arg_type & A_MASK) == A_SINGLE) {
opt = (str_true(arg[flp].arg_ptr.arg_str) ? CFT_TRUE : CFT_FALSE);
cmd->c_short = str_smake(arg[flp].arg_ptr.arg_str);
goto literal;
}
else if ((arg[flp].arg_type & A_MASK) == A_STAB ||
(arg[flp].arg_type & A_MASK) == A_LVAL) {
cmd->c_stab = arg[flp].arg_ptr.arg_stab;
if (!context)
arg[flp].arg_ptr.arg_stab = Nullstab;
opt = CFT_REG;
literal:
if (!context) { /* no && or ||? */
arg_free(arg);
cmd->c_expr = Nullarg;
}
if (!(context & 1))
cmd->c_flags |= CF_EQSURE;
if (!(context & 2))
cmd->c_flags |= CF_NESURE;
}
}
else if (arg->arg_type == O_MATCH || arg->arg_type == O_SUBST ||
arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST) {
if ((arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
(arg[2].arg_type & A_MASK) == A_SPAT &&
arg[2].arg_ptr.arg_spat->spat_short ) {
cmd->c_stab = arg[1].arg_ptr.arg_stab;
cmd->c_short = str_smake(arg[2].arg_ptr.arg_spat->spat_short);
cmd->c_slen = arg[2].arg_ptr.arg_spat->spat_slen;
if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ALL &&
!(arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ONCE) &&
(arg->arg_type == O_MATCH || arg->arg_type == O_NMATCH) )
sure |= CF_EQSURE; /* (SUBST must be forced even */
/* if we know it will work.) */
if (arg->arg_type != O_SUBST) {
arg[2].arg_ptr.arg_spat->spat_short = Nullstr;
arg[2].arg_ptr.arg_spat->spat_slen = 0; /* only one chk */
}
sure |= CF_NESURE; /* normally only sure if it fails */
if (arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST)
cmd->c_flags |= CF_FIRSTNEG;
if (context & 1) { /* only sure if thing is false */
if (cmd->c_flags & CF_FIRSTNEG)
sure &= ~CF_NESURE;
else
sure &= ~CF_EQSURE;
}
else if (context & 2) { /* only sure if thing is true */
if (cmd->c_flags & CF_FIRSTNEG)
sure &= ~CF_EQSURE;
else
sure &= ~CF_NESURE;
}
if (sure & (CF_EQSURE|CF_NESURE)) { /* if we know anything*/
if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_SCANFIRST)
opt = CFT_SCAN;
else
opt = CFT_ANCHOR;
if (sure == (CF_EQSURE|CF_NESURE) /* really sure? */
&& arg->arg_type == O_MATCH
&& context & 4
&& fliporflop == 1) {
spat_free(arg[2].arg_ptr.arg_spat);
arg[2].arg_ptr.arg_spat = Nullspat; /* don't do twice */
}
else
cmd->c_spat = arg[2].arg_ptr.arg_spat;
cmd->c_flags |= sure;
}
}
}
else if (arg->arg_type == O_SEQ || arg->arg_type == O_SNE ||
arg->arg_type == O_SLT || arg->arg_type == O_SGT) {
if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
if (arg[2].arg_type == A_SINGLE) {
char *junk = str_get(arg[2].arg_ptr.arg_str);
cmd->c_stab = arg[1].arg_ptr.arg_stab;
cmd->c_short = str_smake(arg[2].arg_ptr.arg_str);
cmd->c_slen = cmd->c_short->str_cur+1;
switch (arg->arg_type) {
case O_SLT: case O_SGT:
sure |= CF_EQSURE;
cmd->c_flags |= CF_FIRSTNEG;
break;
case O_SNE:
cmd->c_flags |= CF_FIRSTNEG;
/* FALL THROUGH */
case O_SEQ:
sure |= CF_NESURE|CF_EQSURE;
break;
}
if (context & 1) { /* only sure if thing is false */
if (cmd->c_flags & CF_FIRSTNEG)
sure &= ~CF_NESURE;
else
sure &= ~CF_EQSURE;
}
else if (context & 2) { /* only sure if thing is true */
if (cmd->c_flags & CF_FIRSTNEG)
sure &= ~CF_EQSURE;
else
sure &= ~CF_NESURE;
}
if (sure & (CF_EQSURE|CF_NESURE)) {
opt = CFT_STROP;
cmd->c_flags |= sure;
}
}
}
}
else if (arg->arg_type == O_EQ || arg->arg_type == O_NE ||
arg->arg_type == O_LE || arg->arg_type == O_GE ||
arg->arg_type == O_LT || arg->arg_type == O_GT) {
if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
if (arg[2].arg_type == A_SINGLE) {
cmd->c_stab = arg[1].arg_ptr.arg_stab;
if (dowarn) {
STR *str = arg[2].arg_ptr.arg_str;
if ((!str->str_nok && !looks_like_number(str)))
warn("Possible use of == on string value");
}
cmd->c_short = str_nmake(str_gnum(arg[2].arg_ptr.arg_str));
cmd->c_slen = arg->arg_type;
sure |= CF_NESURE|CF_EQSURE;
if (context & 1) { /* only sure if thing is false */
sure &= ~CF_EQSURE;
}
else if (context & 2) { /* only sure if thing is true */
sure &= ~CF_NESURE;
}
if (sure & (CF_EQSURE|CF_NESURE)) {
opt = CFT_NUMOP;
cmd->c_flags |= sure;
}
}
}
}
else if (arg->arg_type == O_ASSIGN &&
(arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
arg[1].arg_ptr.arg_stab == defstab &&
arg[2].arg_type == A_EXPR ) {
arg2 = arg[2].arg_ptr.arg_arg;
if (arg2->arg_type == O_ITEM && arg2[1].arg_type == A_READ) {
opt = CFT_GETS;
cmd->c_stab = arg2[1].arg_ptr.arg_stab;
if (!(stab_io(arg2[1].arg_ptr.arg_stab)->flags & IOF_ARGV)) {
free_arg(arg2);
arg[2].arg_ptr.arg_arg = Nullarg;
free_arg(arg);
cmd->c_expr = Nullarg;
}
}
}
else if (arg->arg_type == O_CHOP &&
(arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) ) {
opt = CFT_CHOP;
cmd->c_stab = arg[1].arg_ptr.arg_stab;
free_arg(arg);
cmd->c_expr = Nullarg;
}
if (context & 4)
opt |= CF_FLIP;
cmd->c_flags |= opt;
if (cmd->c_flags & CF_FLIP) {
if (fliporflop == 1) {
arg = cmd->c_expr; /* get back to O_FLIP arg */
New(110,arg[3].arg_ptr.arg_cmd, 1, CMD);
Copy(cmd, arg[3].arg_ptr.arg_cmd, 1, CMD);
New(111,arg[4].arg_ptr.arg_cmd,1,CMD);
Copy(cmd, arg[4].arg_ptr.arg_cmd, 1, CMD);
opt_arg(arg[4].arg_ptr.arg_cmd,2,acmd);
arg->arg_len = 2; /* this is a lie */
}
else {
if ((opt & CF_OPTIMIZE) == CFT_EVAL)
cmd->c_flags = (cmd->c_flags & ~CF_OPTIMIZE) | CFT_UNFLIP;
}
}
}
CMD *
add_label(lbl,cmd)
char *lbl;
register CMD *cmd;
{
if (cmd)
cmd->c_label = lbl;
return cmd;
}
CMD *
addcond(cmd, arg)
register CMD *cmd;
register ARG *arg;
{
cmd->c_expr = arg;
cmd->c_flags |= CF_COND;
return cmd;
}
CMD *
addloop(cmd, arg)
register CMD *cmd;
register ARG *arg;
{
void while_io();
cmd->c_expr = arg;
cmd->c_flags |= CF_COND|CF_LOOP;
if (!(cmd->c_flags & CF_INVERT))
while_io(cmd); /* add $_ =, if necessary */
if (cmd->c_type == C_BLOCK)
cmd->c_flags &= ~CF_COND;
else {
arg = cmd->ucmd.acmd.ac_expr;
if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_CMD)
cmd->c_flags &= ~CF_COND; /* "do {} while" happens at least once */
if (arg && (arg->arg_flags & AF_DEPR) &&
(arg->arg_type == O_SUBR || arg->arg_type == O_DBSUBR) )
cmd->c_flags &= ~CF_COND; /* likewise for "do subr() while" */
}
return cmd;
}
CMD *
invert(cmd)
CMD *cmd;
{
register CMD *targ = cmd;
if (targ->c_head)
targ = targ->c_head;
if (targ->c_flags & CF_DBSUB)
targ = targ->c_next;
targ->c_flags ^= CF_INVERT;
return cmd;
}
yyerror(s)
char *s;
{
char tmpbuf[258];
char tmp2buf[258];
char *tname = tmpbuf;
if (bufptr > oldoldbufptr && bufptr - oldoldbufptr < 200 &&
oldoldbufptr != oldbufptr && oldbufptr != bufptr) {
while (isspace(*oldoldbufptr))
oldoldbufptr++;
strncpy(tmp2buf, oldoldbufptr, bufptr - oldoldbufptr);
tmp2buf[bufptr - oldoldbufptr] = '\0';
sprintf(tname,"next 2 tokens \"%s\"",tmp2buf);
}
else if (bufptr > oldbufptr && bufptr - oldbufptr < 200 &&
oldbufptr != bufptr) {
while (isspace(*oldbufptr))
oldbufptr++;
strncpy(tmp2buf, oldbufptr, bufptr - oldbufptr);
tmp2buf[bufptr - oldbufptr] = '\0';
sprintf(tname,"next token \"%s\"",tmp2buf);
}
else if (yychar > 256)
tname = "next token ???";
else if (!yychar)
(void)strcpy(tname,"at EOF");
else if (yychar < 32)
(void)sprintf(tname,"next char ^%c",yychar+64);
else if (yychar == 127)
(void)strcpy(tname,"at EOF");
else
(void)sprintf(tname,"next char %c",yychar);
(void)sprintf(buf, "%s in file %s at line %d, %s\n",
s,stab_val(curcmd->c_filestab)->str_ptr,curcmd->c_line,tname);
if (curcmd->c_line == multi_end && multi_start < multi_end)
sprintf(buf+strlen(buf),
" (Might be a runaway multi-line %c%c string starting on line %d)\n",
multi_open,multi_close,multi_start);
if (in_eval)
str_cat(stab_val(stabent("@",TRUE)),buf);
else
fputs(buf,stderr);
if (++error_count >= 10)
fatal("%s has too many errors.\n",
stab_val(curcmd->c_filestab)->str_ptr);
}
void
while_io(cmd)
register CMD *cmd;
{
register ARG *arg = cmd->c_expr;
STAB *asgnstab;
/* hoist "while (<channel>)" up into command block */
if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_READ) {
cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
cmd->c_flags |= CFT_GETS; /* and set it to do the input */
cmd->c_stab = arg[1].arg_ptr.arg_stab;
if (stab_io(arg[1].arg_ptr.arg_stab)->flags & IOF_ARGV) {
cmd->c_expr = l(make_op(O_ASSIGN, 2, /* fake up "$_ =" */
stab2arg(A_LVAL,defstab), arg, Nullarg));
}
else {
free_arg(arg);
cmd->c_expr = Nullarg;
}
}
else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_INDREAD) {
cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
cmd->c_flags |= CFT_INDGETS; /* and set it to do the input */
cmd->c_stab = arg[1].arg_ptr.arg_stab;
free_arg(arg);
cmd->c_expr = Nullarg;
}
else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_GLOB) {
if ((cmd->c_flags & CF_OPTIMIZE) == CFT_ARRAY)
asgnstab = cmd->c_stab;
else
asgnstab = defstab;
cmd->c_expr = l(make_op(O_ASSIGN, 2, /* fake up "$foo =" */
stab2arg(A_LVAL,asgnstab), arg, Nullarg));
cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
}
}
CMD *
wopt(cmd)
register CMD *cmd;
{
register CMD *tail;
CMD *newtail;
register int i;
if (cmd->c_expr && (cmd->c_flags & CF_OPTIMIZE) == CFT_FALSE)
opt_arg(cmd,1, cmd->c_type == C_EXPR);
while_io(cmd); /* add $_ =, if necessary */
/* First find the end of the true list */
tail = cmd->ucmd.ccmd.cc_true;
if (tail == Nullcmd)
return cmd;
New(112,newtail, 1, CMD); /* guaranteed continue */
for (;;) {
/* optimize "next" to point directly to continue block */
if (tail->c_type == C_EXPR &&
tail->ucmd.acmd.ac_expr &&
tail->ucmd.acmd.ac_expr->arg_type == O_NEXT &&
(tail->ucmd.acmd.ac_expr->arg_len == 0 ||
(cmd->c_label &&
strEQ(cmd->c_label,
tail->ucmd.acmd.ac_expr[1].arg_ptr.arg_str->str_ptr) )))
{
arg_free(tail->ucmd.acmd.ac_expr);
tail->ucmd.acmd.ac_expr = Nullarg;
tail->c_type = C_NEXT;
if (cmd->ucmd.ccmd.cc_alt != Nullcmd)
tail->ucmd.ccmd.cc_alt = cmd->ucmd.ccmd.cc_alt;
else
tail->ucmd.ccmd.cc_alt = newtail;
tail->ucmd.ccmd.cc_true = Nullcmd;
}
else if (tail->c_type == C_IF && !tail->ucmd.ccmd.cc_alt) {
if (cmd->ucmd.ccmd.cc_alt != Nullcmd)
tail->ucmd.ccmd.cc_alt = cmd->ucmd.ccmd.cc_alt;
else
tail->ucmd.ccmd.cc_alt = newtail;
}
else if (tail->c_type == C_CSWITCH || tail->c_type == C_NSWITCH) {
if (cmd->ucmd.ccmd.cc_alt != Nullcmd) {
for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
if (!tail->ucmd.scmd.sc_next[i])
tail->ucmd.scmd.sc_next[i] = cmd->ucmd.ccmd.cc_alt;
}
else {
for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
if (!tail->ucmd.scmd.sc_next[i])
tail->ucmd.scmd.sc_next[i] = newtail;
}
}
if (!tail->c_next)
break;
tail = tail->c_next;
}
/* if there's a continue block, link it to true block and find end */
if (cmd->ucmd.ccmd.cc_alt != Nullcmd) {
tail->c_next = cmd->ucmd.ccmd.cc_alt;
tail = tail->c_next;
for (;;) {
/* optimize "next" to point directly to continue block */
if (tail->c_type == C_EXPR &&
tail->ucmd.acmd.ac_expr &&
tail->ucmd.acmd.ac_expr->arg_type == O_NEXT &&
(tail->ucmd.acmd.ac_expr->arg_len == 0 ||
(cmd->c_label &&
strEQ(cmd->c_label,
tail->ucmd.acmd.ac_expr[1].arg_ptr.arg_str->str_ptr) )))
{
arg_free(tail->ucmd.acmd.ac_expr);
tail->ucmd.acmd.ac_expr = Nullarg;
tail->c_type = C_NEXT;
tail->ucmd.ccmd.cc_alt = newtail;
tail->ucmd.ccmd.cc_true = Nullcmd;
}
else if (tail->c_type == C_IF && !tail->ucmd.ccmd.cc_alt) {
tail->ucmd.ccmd.cc_alt = newtail;
}
else if (tail->c_type == C_CSWITCH || tail->c_type == C_NSWITCH) {
for (i = tail->ucmd.scmd.sc_max; i >= 0; i--)
if (!tail->ucmd.scmd.sc_next[i])
tail->ucmd.scmd.sc_next[i] = newtail;
}
if (!tail->c_next)
break;
tail = tail->c_next;
}
for ( ; tail->c_next; tail = tail->c_next) ;
}
/* Here's the real trick: link the end of the list back to the beginning,
* inserting a "last" block to break out of the loop. This saves one or
* two procedure calls every time through the loop, because of how cmd_exec
* does tail recursion.
*/
tail->c_next = newtail;
tail = newtail;
if (!cmd->ucmd.ccmd.cc_alt)
cmd->ucmd.ccmd.cc_alt = tail; /* every loop has a continue now */
#ifndef lint
(void)bcopy((char *)cmd, (char *)tail, sizeof(CMD));
#endif
tail->c_type = C_EXPR;
tail->c_flags ^= CF_INVERT; /* turn into "last unless" */
tail->c_next = tail->ucmd.ccmd.cc_true; /* loop directly back to top */
tail->ucmd.acmd.ac_expr = make_op(O_LAST,0,Nullarg,Nullarg,Nullarg);
tail->ucmd.acmd.ac_stab = Nullstab;
return cmd;
}
CMD *
over(eachstab,cmd)
STAB *eachstab;
register CMD *cmd;
{
/* hoist "for $foo (@bar)" up into command block */
cmd->c_flags &= ~CF_OPTIMIZE; /* clear optimization type */
cmd->c_flags |= CFT_ARRAY; /* and set it to do the iteration */
cmd->c_stab = eachstab;
cmd->c_short = str_new(0); /* just to save a field in struct cmd */
cmd->c_short->str_u.str_useful = -1;
return cmd;
}
cmd_free(cmd)
register CMD *cmd;
{
register CMD *tofree;
register CMD *head = cmd;
while (cmd) {
if (cmd->c_type != C_WHILE) { /* WHILE block is duplicated */
if (cmd->c_label) {
Safefree(cmd->c_label);
cmd->c_label = Nullch;
}
if (cmd->c_short) {
str_free(cmd->c_short);
cmd->c_short = Nullstr;
}
if (cmd->c_expr) {
arg_free(cmd->c_expr);
cmd->c_expr = Nullarg;
}
}
switch (cmd->c_type) {
case C_WHILE:
case C_BLOCK:
case C_ELSE:
case C_IF:
if (cmd->ucmd.ccmd.cc_true) {
cmd_free(cmd->ucmd.ccmd.cc_true);
cmd->ucmd.ccmd.cc_true = Nullcmd;
}
break;
case C_EXPR:
if (cmd->ucmd.acmd.ac_expr) {
arg_free(cmd->ucmd.acmd.ac_expr);
cmd->ucmd.acmd.ac_expr = Nullarg;
}
break;
}
tofree = cmd;
cmd = cmd->c_next;
if (tofree != head) /* to get Saber to shut up */
Safefree(tofree);
if (cmd && cmd == head) /* reached end of while loop */
break;
}
Safefree(head);
}
arg_free(arg)
register ARG *arg;
{
register int i;
for (i = 1; i <= arg->arg_len; i++) {
switch (arg[i].arg_type & A_MASK) {
case A_NULL:
if (arg->arg_type == O_TRANS) {
Safefree(arg[i].arg_ptr.arg_cval);
arg[i].arg_ptr.arg_cval = Nullch;
}
break;
case A_LEXPR:
if (arg->arg_type == O_AASSIGN &&
arg[i].arg_ptr.arg_arg->arg_type == O_LARRAY) {
char *name =
stab_name(arg[i].arg_ptr.arg_arg[1].arg_ptr.arg_stab);
if (strnEQ("_GEN_",name, 5)) /* array for foreach */
hdelete(defstash,name,strlen(name));
}
/* FALL THROUGH */
case A_EXPR:
arg_free(arg[i].arg_ptr.arg_arg);
arg[i].arg_ptr.arg_arg = Nullarg;
break;
case A_CMD:
cmd_free(arg[i].arg_ptr.arg_cmd);
arg[i].arg_ptr.arg_cmd = Nullcmd;
break;
case A_WORD:
case A_STAB:
case A_LVAL:
case A_READ:
case A_GLOB:
case A_ARYLEN:
case A_LARYLEN:
case A_ARYSTAB:
case A_LARYSTAB:
break;
case A_SINGLE:
case A_DOUBLE:
case A_BACKTICK:
str_free(arg[i].arg_ptr.arg_str);
arg[i].arg_ptr.arg_str = Nullstr;
break;
case A_SPAT:
spat_free(arg[i].arg_ptr.arg_spat);
arg[i].arg_ptr.arg_spat = Nullspat;
break;
}
}
free_arg(arg);
}
spat_free(spat)
register SPAT *spat;
{
register SPAT *sp;
HENT *entry;
if (spat->spat_runtime) {
arg_free(spat->spat_runtime);
spat->spat_runtime = Nullarg;
}
if (spat->spat_repl) {
arg_free(spat->spat_repl);
spat->spat_repl = Nullarg;
}
if (spat->spat_short) {
str_free(spat->spat_short);
spat->spat_short = Nullstr;
}
if (spat->spat_regexp) {
regfree(spat->spat_regexp);
spat->spat_regexp = Null(REGEXP*);
}
/* now unlink from spat list */
for (entry = defstash->tbl_array['_']; entry; entry = entry->hent_next) {
register HASH *stash;
STAB *stab = (STAB*)entry->hent_val;
if (!stab)
continue;
stash = stab_hash(stab);
if (!stash || stash->tbl_spatroot == Null(SPAT*))
continue;
if (stash->tbl_spatroot == spat)
stash->tbl_spatroot = spat->spat_next;
else {
for (sp = stash->tbl_spatroot;
sp && sp->spat_next != spat;
sp = sp->spat_next)
;
if (sp)
sp->spat_next = spat->spat_next;
}
}
Safefree(spat);
}
/* Recursively descend a command sequence and push the address of any string
* that needs saving on recursion onto the tosave array.
*/
static int
cmd_tosave(cmd,willsave)
register CMD *cmd;
int willsave; /* willsave passes down the tree */
{
register CMD *head = cmd;
int shouldsave = FALSE; /* shouldsave passes up the tree */
int tmpsave;
register CMD *lastcmd = Nullcmd;
while (cmd) {
if (cmd->c_expr)
shouldsave |= arg_tosave(cmd->c_expr,willsave);
switch (cmd->c_type) {
case C_WHILE:
if (cmd->ucmd.ccmd.cc_true) {
tmpsave = cmd_tosave(cmd->ucmd.ccmd.cc_true,willsave);
/* Here we check to see if the temporary array generated for
* a foreach needs to be localized because of recursion.
*/
if (tmpsave && (cmd->c_flags & CF_OPTIMIZE) == CFT_ARRAY) {
if (lastcmd &&
lastcmd->c_type == C_EXPR &&
lastcmd->c_expr) {
ARG *arg = lastcmd->c_expr;
if (arg->arg_type == O_ASSIGN &&
arg[1].arg_type == A_LEXPR &&
arg[1].arg_ptr.arg_arg->arg_type == O_LARRAY &&
strnEQ("_GEN_",
stab_name(
arg[1].arg_ptr.arg_arg[1].arg_ptr.arg_stab),
5)) { /* array generated for foreach */
(void)localize(arg);
}
}
/* in any event, save the iterator */
(void)apush(tosave,cmd->c_short);
}
shouldsave |= tmpsave;
}
break;
case C_BLOCK:
case C_ELSE:
case C_IF:
if (cmd->ucmd.ccmd.cc_true)
shouldsave |= cmd_tosave(cmd->ucmd.ccmd.cc_true,willsave);
break;
case C_EXPR:
if (cmd->ucmd.acmd.ac_expr)
shouldsave |= arg_tosave(cmd->ucmd.acmd.ac_expr,willsave);
break;
}
lastcmd = cmd;
cmd = cmd->c_next;
if (cmd && cmd == head) /* reached end of while loop */
break;
}
return shouldsave;
}
static int
arg_tosave(arg,willsave)
register ARG *arg;
int willsave;
{
register int i;
int shouldsave = FALSE;
for (i = arg->arg_len; i >= 1; i--) {
switch (arg[i].arg_type & A_MASK) {
case A_NULL:
break;
case A_LEXPR:
case A_EXPR:
shouldsave |= arg_tosave(arg[i].arg_ptr.arg_arg,shouldsave);
break;
case A_CMD:
shouldsave |= cmd_tosave(arg[i].arg_ptr.arg_cmd,shouldsave);
break;
case A_WORD:
case A_STAB:
case A_LVAL:
case A_READ:
case A_GLOB:
case A_ARYLEN:
case A_SINGLE:
case A_DOUBLE:
case A_BACKTICK:
break;
case A_SPAT:
shouldsave |= spat_tosave(arg[i].arg_ptr.arg_spat);
break;
}
}
switch (arg->arg_type) {
case O_RETURN:
saw_return = TRUE;
break;
case O_EVAL:
case O_SUBR:
shouldsave = TRUE;
break;
}
if (willsave)
(void)apush(tosave,arg->arg_ptr.arg_str);
return shouldsave;
}
static int
spat_tosave(spat)
register SPAT *spat;
{
int shouldsave = FALSE;
if (spat->spat_runtime)
shouldsave |= arg_tosave(spat->spat_runtime,FALSE);
if (spat->spat_repl) {
shouldsave |= arg_tosave(spat->spat_repl,FALSE);
}
return shouldsave;
}