jehanne/sys/src/cmd/rc/code.c

514 lines
9.1 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
*/
#include "rc.h"
#include "io.h"
#include "exec.h"
#include "fns.h"
#include "getflags.h"
#define c0 t->child[0]
#define c1 t->child[1]
#define c2 t->child[2]
code *codebuf;
int codep, ncode;
#define emitf(x) ((void)(codep!=ncode || morecode()), codebuf[codep].f = (x), codep++)
#define emiti(x) ((void)(codep!=ncode || morecode()), codebuf[codep].i = (x), codep++)
#define emits(x) ((void)(codep!=ncode || morecode()), codebuf[codep].s = (x), codep++)
void stuffdot(int);
char *fnstr(tree*);
void outcode(tree*, int);
void codeswitch(tree*, int);
int iscase(tree*);
code *codecopy(code*);
void codefree(code*);
int
morecode(void)
{
ncode+=100;
codebuf = (code *)erealloc((char *)codebuf, ncode*sizeof codebuf[0]);
return 0;
}
void
stuffdot(int a)
{
if(a<0 || codep<=a)
panic("Bad address %d in stuffdot", a);
codebuf[a].i = codep;
}
int
compile(tree *t)
{
ncode = 100;
codebuf = (code *)emalloc(ncode*sizeof codebuf[0]);
codep = 0;
emiti(0); /* reference count */
outcode(t, flag['e']?1:0);
if(nerror){
free(codebuf);
return 0;
}
readhere();
emitf(Xreturn);
emitf(0);
return 1;
}
void
cleanhere(char *f)
{
emitf(Xdelhere);
emits(estrdup(f));
}
char*
fnstr(tree *t)
{
io *f = openstr();
void *v;
pfmt(f, "%t", t);
v = f->strp;
f->strp = 0;
closeio(f);
return v;
}
void
outcode(tree *t, int eflag)
{
static int line;
int p, q;
tree *tt;
char *f;
if(t==0)
return;
if(t->type!=NOT && t->type!=';')
runq->iflast = 0;
if(t->line != line){
line = t->line;
emitf(Xsrcline);
emiti(line);
}
switch(t->type){
default:
pfmt(err, "bad type %d in outcode\n", t->type);
break;
case '$':
emitf(Xmark);
outcode(c0, eflag);
emitf(Xdol);
break;
case '"':
emitf(Xmark);
emitf(Xmark);
outcode(c0, eflag);
emitf(Xdol);
emitf(Xqw);
break;
case SUB:
emitf(Xmark);
outcode(c0, eflag);
emitf(Xmark);
outcode(c1, eflag);
emitf(Xsub);
break;
case '&':
emitf(Xasync);
p = emiti(0);
outcode(c0, eflag);
emitf(Xexit);
stuffdot(p);
break;
case ';':
outcode(c0, eflag);
outcode(c1, eflag);
break;
case '^':
emitf(Xmark);
outcode(c1, eflag);
emitf(Xmark);
outcode(c0, eflag);
emitf(Xconc);
break;
case '`':
emitf(Xmark);
if(c0){
outcode(c0, 0);
emitf(Xglob);
} else {
emitf(Xmark);
emitf(Xword);
emits(estrdup(ENV_IFS));
emitf(Xdol);
}
emitf(Xbackq);
p = emiti(0);
outcode(c1, 0);
emitf(Xexit);
stuffdot(p);
break;
case ANDAND:
outcode(c0, 0);
emitf(Xtrue);
p = emiti(0);
outcode(c1, eflag);
stuffdot(p);
break;
case ARGLIST:
outcode(c1, eflag);
outcode(c0, eflag);
break;
case BANG:
outcode(c0, eflag);
emitf(Xbang);
break;
case PCMD:
case BRACE:
outcode(c0, eflag);
break;
case COUNT:
emitf(Xmark);
outcode(c0, eflag);
emitf(Xcount);
break;
case FN:
emitf(Xmark);
outcode(c0, eflag);
if(c1){
emitf(Xfn);
p = emiti(0);
emits(fnstr(c1));
if((f = curfile(runq)) != nil){
emitf(Xsrcfile);
emits(estrdup(f));
}
emitf(Xsrcline);
emiti(runq->lexline);
outcode(c1, eflag);
emitf(Xunlocal); /* get rid of $* */
emitf(Xreturn);
stuffdot(p);
}
else
emitf(Xdelfn);
break;
case IF:
outcode(c0, 0);
emitf(Xif);
p = emiti(0);
outcode(c1, eflag);
emitf(Xwastrue);
stuffdot(p);
break;
case NOT:
if(!runq->iflast)
yyerror("`if not' does not follow `if(...)'");
emitf(Xifnot);
p = emiti(0);
outcode(c0, eflag);
stuffdot(p);
break;
case OROR:
outcode(c0, 0);
emitf(Xfalse);
p = emiti(0);
outcode(c1, eflag);
stuffdot(p);
break;
case PAREN:
outcode(c0, eflag);
break;
case SIMPLE:
emitf(Xmark);
outcode(c0, eflag);
emitf(Xsimple);
if(eflag)
emitf(Xeflag);
break;
case SUBSHELL:
emitf(Xsubshell);
p = emiti(0);
outcode(c0, eflag);
emitf(Xexit);
stuffdot(p);
if(eflag)
emitf(Xeflag);
break;
case SWITCH:
codeswitch(t, eflag);
break;
case TWIDDLE:
emitf(Xmark);
outcode(c1, eflag);
emitf(Xmark);
outcode(c0, eflag);
emitf(Xmatch);
if(eflag)
emitf(Xeflag);
break;
case WHILE:
q = codep;
outcode(c0, 0);
if(q==codep)
emitf(Xsettrue); /* empty condition == while(true) */
emitf(Xtrue);
p = emiti(0);
outcode(c1, eflag);
emitf(Xjump);
emiti(q);
stuffdot(p);
break;
case WORDS:
outcode(c1, eflag);
outcode(c0, eflag);
break;
case FOR:
emitf(Xmark);
if(c1){
outcode(c1, eflag);
emitf(Xglob);
}
else{
emitf(Xmark);
emitf(Xword);
emits(estrdup("*"));
emitf(Xdol);
}
emitf(Xmark); /* dummy value for Xlocal */
emitf(Xmark);
outcode(c0, eflag);
emitf(Xlocal);
p = emitf(Xfor);
q = emiti(0);
outcode(c2, eflag);
emitf(Xjump);
emiti(p);
stuffdot(q);
emitf(Xunlocal);
break;
case WORD:
if(t->quoted){
emitf(Xword);
emits(estrdup(t->str));
} else {
if((q = Globsize(t->str)) > 0){
emitf(Xglobs);
emits(estrdup(t->str));
emiti(q);
} else {
emitf(Xword);
emits(deglob(estrdup(t->str)));
}
}
break;
case DUP:
if(t->rtype==DUPFD){
emitf(Xdup);
emiti(t->fd0);
emiti(t->fd1);
}
else{
emitf(Xclose);
emiti(t->fd0);
}
outcode(c1, eflag);
emitf(Xpopredir);
break;
case PIPEFD:
emitf(Xpipefd);
emiti(t->rtype);
p = emiti(0);
outcode(c0, eflag);
emitf(Xexit);
stuffdot(p);
break;
case REDIR:
emitf(Xmark);
outcode(c0, eflag);
emitf(Xglob);
switch(t->rtype){
case APPEND:
emitf(Xappend);
break;
case WRITE:
emitf(Xwrite);
break;
case READ:
case HERE:
emitf(Xread);
break;
case RDWR:
emitf(Xrdwr);
break;
}
emiti(t->fd0);
outcode(c1, eflag);
emitf(Xpopredir);
break;
case '=':
tt = t;
for(;t && t->type=='=';t = c2);
if(t){ /* var=value cmd */
for(t = tt;t->type=='=';t = c2){
emitf(Xmark);
outcode(c1, eflag);
emitf(Xmark);
outcode(c0, eflag);
emitf(Xlocal); /* push var for cmd */
}
outcode(t, eflag); /* gen. code for cmd */
for(t = tt; t->type == '='; t = c2)
emitf(Xunlocal); /* pop var */
}
else{ /* var=value */
for(t = tt;t;t = c2){
emitf(Xmark);
outcode(c1, eflag);
emitf(Xmark);
outcode(c0, eflag);
emitf(Xassign); /* set var permanently */
}
}
t = tt; /* so tests below will work */
break;
case PIPE:
emitf(Xpipe);
emiti(t->fd0);
emiti(t->fd1);
p = emiti(0);
q = emiti(0);
outcode(c0, eflag);
emitf(Xexit);
stuffdot(p);
outcode(c1, eflag);
emitf(Xreturn);
stuffdot(q);
emitf(Xpipewait);
break;
}
if(t->type!=NOT && t->type!=';')
runq->iflast = t->type==IF;
else if(c0) runq->iflast = c0->type==IF;
}
/*
* switch code looks like this:
* Xmark
* (get switch value)
* Xjump 1f
* out: Xjump leave
* 1: Xmark
* (get case values)
* Xcase 1f
* (commands)
* Xjump out
* 1: Xmark
* (get case values)
* Xcase 1f
* (commands)
* Xjump out
* 1:
* leave:
* Xpopm
*/
void
codeswitch(tree *t, int eflag)
{
int leave; /* patch jump address to leave switch */
int out; /* jump here to leave switch */
int nextcase; /* patch jump address to next case */
tree *tt;
if(c1->child[0]==nil
|| c1->child[0]->type!=';'
|| !iscase(c1->child[0]->child[0])){
yyerror("case missing in switch");
return;
}
emitf(Xmark);
outcode(c0, eflag);
emitf(Xjump);
nextcase = emiti(0);
out = emitf(Xjump);
leave = emiti(0);
stuffdot(nextcase);
t = c1->child[0];
while(t->type==';'){
tt = c1;
emitf(Xmark);
for(t = c0->child[0];t->type==ARGLIST;t = c0) outcode(c1, eflag);
emitf(Xcase);
nextcase = emiti(0);
t = tt;
for(;;){
if(t->type==';'){
if(iscase(c0)) break;
outcode(c0, eflag);
t = c1;
}
else{
if(!iscase(t)) outcode(t, eflag);
break;
}
}
emitf(Xjump);
emiti(out);
stuffdot(nextcase);
}
stuffdot(leave);
emitf(Xpopm);
}
int
iscase(tree *t)
{
if(t->type!=SIMPLE)
return 0;
do t = c0; while(t->type==ARGLIST);
return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0;
}
code*
codecopy(code *cp)
{
cp[0].i++;
return cp;
}
void
codefree(code *cp)
{
code *p;
if(--cp[0].i!=0)
return;
for(p = cp+1;p->f;p++){
if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite
|| p->f==Xrdwr
|| p->f==Xasync || p->f==Xbackq || p->f==Xcase || p->f==Xfalse
|| p->f==Xfor || p->f==Xjump
|| p->f==Xsrcline
|| p->f==Xsubshell || p->f==Xtrue) p++;
else if(p->f==Xdup || p->f==Xpipefd) p+=2;
else if(p->f==Xpipe) p+=4;
else if(p->f==Xglobs || p->f==Xsrcfile) free(p[1].s), p+=2;
else if(p->f==Xword || p->f==Xdelhere) free((++p)->s);
else if(p->f==Xfn){
free(p[2].s);
p+=2;
}
}
free(cp);
}