diff --git a/sys/man/1/rc b/sys/man/1/rc index 2dd2a1dd..f5b880a7 100644 --- a/sys/man/1/rc +++ b/sys/man/1/rc @@ -114,7 +114,7 @@ The simplest kind of argument is the unquoted word: a sequence of one or more characters none of which is a blank, tab, newline, or any of the following: .EX - # ; & | ^ $ = ` ' { } ( ) < > + # ; & | ^ $ ` ' { } ( ) < > .EE An unquoted word that contains any of the characters .B * @@ -281,28 +281,10 @@ then one operand must have one component, and the other must be non-empty, and concatenation is distributive. .PD .SS Free Carets -In most circumstances, -.I rc +.I Rc will insert the .B ^ operator automatically between words that are not separated by white space. -Whenever one of -.B $ -.B ' -.B ` -follows a quoted or unquoted word or an unquoted word follows a quoted word -with no intervening blanks or tabs, -a -.B ^ -is inserted between the two. -If an unquoted word immediately follows a -.BR $ -and contains a character other than an alphanumeric, underscore, -or -.BR * , -a -.B ^ -is inserted before the first such character. Thus .IP .B cc -$flags $stem.c @@ -365,7 +347,7 @@ or .I Fd1 is a previously opened file descriptor and .I fd0 -becomes a new copy (in the sense of +becomes a new copy (in the sense of .IR dup (2)) of it. A file descriptor may be closed by writing @@ -484,7 +466,7 @@ is executed. The .I command is executed once for each -.IR argument +.IR argument with that argument assigned to .IR name . If the argument list is omitted, @@ -989,5 +971,3 @@ changes .BR $status . .PP Functions containing here documents don't work. -.PP -Free carets don't get inserted next to keywords. diff --git a/sys/src/cmd/rc/checkparse b/sys/src/cmd/rc/checkparse new file mode 100644 index 00000000..f2a7986e --- /dev/null +++ b/sys/src/cmd/rc/checkparse @@ -0,0 +1,22 @@ +#!/bin/rc + +aflag=false +if(~ $1 -a){ + aflag=true + shift +} + +files=$* +if(~ $#files 0){ + files=`{ + echo ./test.rc + grep -l '^#!/bin/rc' /bin/* /bin/*/* >/dev/null >[2=1] + } +} + +for(i in $files){ + if(! diff <{./o.rc -DY $i >[2=1]} <{./o.rc -D $i >[2=1]}){ + echo '^^^' $i + ~ $aflag false && exit bad + } +} diff --git a/sys/src/cmd/rc/code.c b/sys/src/cmd/rc/code.c index cddab625..57eb74c8 100644 --- a/sys/src/cmd/rc/code.c +++ b/sys/src/cmd/rc/code.c @@ -40,6 +40,16 @@ stuffdot(int a) int compile(tree *t) { + if(flag['D']){ + struct io *s; + s = openstr(); + pfmt(s, "compile: %u\n", t); + write(2, s->strp, strlen(s->strp)); + closeio(s); + if(eflagok) // made it out of rcmain - stop executing commands, just print them + t = nil; + } + ncode = 100; codebuf = (code *)emalloc(ncode*sizeof codebuf[0]); codep = 0; diff --git a/sys/src/cmd/rc/exec.c b/sys/src/cmd/rc/exec.c index 9c4c9beb..fa617768 100644 --- a/sys/src/cmd/rc/exec.c +++ b/sys/src/cmd/rc/exec.c @@ -181,7 +181,8 @@ main(int argc, char *argv[]) ARGBEGIN { case 'd': case 'e': case 'i': case 'l': case 'p': case 'r': case 's': case 'v': - case 'x': case 'I': case 'V': + case 'x': case 'D': case 'I': case 'V': + case 'Y': flag[ARGC()] = flagset; break; case 'c': @@ -950,7 +951,7 @@ Xrdcmds(void) promptstr="% "; } Noerror(); - if(yyparse()){ + if((flag['Y'] ? yyparse : parse)()){ if(!p->iflag || p->eof && !Eintr()){ if(p->cmdfile) efree(p->cmdfile); diff --git a/sys/src/cmd/rc/fns.h b/sys/src/cmd/rc/fns.h index d0519d6c..d340bebe 100644 --- a/sys/src/cmd/rc/fns.h +++ b/sys/src/cmd/rc/fns.h @@ -64,3 +64,4 @@ int wordchr(Rune); void yyerror(char*); int yylex(void); int yyparse(void); +int parse(void); diff --git a/sys/src/cmd/rc/io.c b/sys/src/cmd/rc/io.c index e2db21d2..0c08d395 100644 --- a/sys/src/cmd/rc/io.c +++ b/sys/src/cmd/rc/io.c @@ -48,7 +48,10 @@ pfmt(io *f, char *fmt, ...) pstr(f, va_arg(ap, char *)); break; case 't': - pcmd(f, va_arg(ap, struct tree *)); + pcmd(f, va_arg(ap, tree *)); + break; + case 'u': + pcmdu(f, va_arg(ap, tree *)); break; case 'v': pval(f, va_arg(ap, struct word *)); @@ -271,7 +274,7 @@ io* opencore(char *s, int len) { io *f = new(struct io); - uchar *buf = emalloc(len); + char *buf = emalloc(len); f->fd = -1 /*open("/dev/null", 0)*/; f->bufp = f->strp = buf; diff --git a/sys/src/cmd/rc/io.h b/sys/src/cmd/rc/io.h index 62b463b7..9dcbda93 100644 --- a/sys/src/cmd/rc/io.h +++ b/sys/src/cmd/rc/io.h @@ -3,8 +3,8 @@ struct io{ int fd; - uchar *bufp, *ebuf, *strp; - uchar buf[NBUF]; + char *bufp, *ebuf, *strp; + char buf[NBUF]; uchar output; /* flag */ }; io *err; @@ -25,6 +25,7 @@ void pquo(io*, char*); void pwrd(io*, char*); void pstr(io*, char*); void pcmd(io*, tree*); +void pcmdu(io*, tree*); void pval(io*, word*); void pfnc(io*, thread*); void pfmt(io*, char*, ...); diff --git a/sys/src/cmd/rc/lex.c b/sys/src/cmd/rc/lex.c index 74e56ad9..e09fd0e8 100644 --- a/sys/src/cmd/rc/lex.c +++ b/sys/src/cmd/rc/lex.c @@ -113,16 +113,19 @@ pprompt(void) doprompt = 0; } -void +int skipwhite(void) { Rune c; + int skipped; + skipped = 0; for(;;){ c = nextc(); /* Why did this used to be if(!inquote && c=='#') ?? */ if(c=='#'){ incomm = 1; + skipped = 1; for(;;){ c = nextc(); if(c=='\n' || c==EOF) { @@ -132,9 +135,12 @@ skipwhite(void) advance(); } } - if(c==' ' || c=='\t') + if(c==' ' || c=='\t'){ + skipped = 1; advance(); - else return; + } + else + return skipped; } } @@ -187,7 +193,7 @@ yylex(void) { Rune c, d = nextc(); char *w = tok; - struct tree *t; + tree *t; yylval.tree = 0; /* @@ -197,11 +203,11 @@ yylex(void) * consume the `('. Otherwise, if the next character is the first * character of a simple or compound word, we insert a `^' before it. */ - if(lastword){ + if(lastword && flag['Y']){ lastword = 0; if(d=='('){ advance(); - strcpy(tok, "( [SUB]"); + strcpy(tok, "("); return SUB; } if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ @@ -209,7 +215,8 @@ yylex(void) return '^'; } } - skipwhite(); + if(skipwhite() && !flag['Y']) + return ' '; switch(c = advance()){ case EOF: lastdol = 0; @@ -230,7 +237,8 @@ yylex(void) case '&': lastdol = 0; if(nextis('&')){ - skipnl(); + if(flag['Y']) + skipnl(); strcpy(tok, "&&"); return ANDAND; } @@ -239,7 +247,8 @@ yylex(void) case '|': lastdol = 0; if(nextis(c)){ - skipnl(); + if(flag['Y']) + skipnl(); strcpy(tok, "||"); return OROR; } @@ -328,8 +337,13 @@ yylex(void) } *w='\0'; yylval.tree = t; - if(t->type==PIPE) + if(t->type==PIPE && flag['Y']) skipnl(); + if(t->type==REDIR){ + skipwhite(); + if(nextc() == '{') + t->type = REDIRW; + } return t->type; case '\'': lastdol = 0; diff --git a/sys/src/cmd/rc/mkfile b/sys/src/cmd/rc/mkfile index 2bdfa2fa..c3c5d350 100644 --- a/sys/src/cmd/rc/mkfile +++ b/sys/src/cmd/rc/mkfile @@ -8,6 +8,7 @@ COMMONOFILES=\ here.$O\ io.$O\ lex.$O\ + parse.$O\ pcmd.$O\ pfnc.$O\ simple.$O\ diff --git a/sys/src/cmd/rc/parse.c b/sys/src/cmd/rc/parse.c new file mode 100644 index 00000000..aaf64e02 --- /dev/null +++ b/sys/src/cmd/rc/parse.c @@ -0,0 +1,552 @@ +#include "rc.h" +#include "io.h" +#include "fns.h" + +static tree* body(int tok, int *ptok); +static tree* brace(int tok); +static tree* cmd(int tok, int *ptok); +static tree* cmd2(int tok, int *ptok); +static tree* cmd3(int tok, int *ptok); +static tree* cmds(int tok, int *ptok, int nlok); +static tree* epilog(int tok, int *ptok); +static int iswordtok(int tok); +static tree* line(int tok, int *ptok); +static tree* paren(int tok); +static tree* yyredir(int tok, int *ptok); +static tree* yyword(int tok, int *ptok, int eqok); +static tree* word1(int tok, int *ptok); +static tree* words(int tok, int *ptok); + +static jmp_buf yyjmp; + +static int +dropnl(int tok) +{ + while(tok == ' ' || tok == '\n') + tok = yylex(); + return tok; +} + +static int +dropsp(int tok) +{ + while(tok == ' ') + tok = yylex(); + return tok; +} + +static void +syntax(int tok) +{ + USED(tok); + yyerror("syntax error"); + longjmp(yyjmp, 1); +} + +int +parse(void) +{ + tree *t; + int tok; + + if(setjmp(yyjmp)) + return 1; + + // rc: { return 1;} + // | line '\n' {return !compile($1);} + + tok = dropsp(yylex()); + if(tok == EOF) + return 1; + t = line(tok, &tok); + if(tok != '\n') + yyerror("missing newline at end of line"); + yylval.tree = t; + return !compile(t); +} + +static tree* +line(int tok, int *ptok) +{ + return cmds(tok, ptok, 0); +} + +static tree* +body(int tok, int *ptok) +{ + return cmds(tok, ptok, 1); +} + +static tree* +cmds(int tok, int *ptok, int nlok) +{ + tree *t, **last, *t2; + + // line: cmd + // | cmdsa line {$$=tree2(';', $1, $2);} + // cmdsa: cmd ';' + // | cmd '&' {$$=tree1('&', $1);} + + // body: cmd + // | cmdsan body {$$=tree2(';', $1, $2);} + // cmdsan: cmdsa + // | cmd '\n' + + t = nil; + last = nil; + for(;;) { + t2 = cmd(tok, &tok); + if(tok == '&') + t2 = tree1('&', t2); + if(t2 != nil) { + // slot into list t + if(last == nil) { + t = t2; + last = &t; + } else { + *last = tree2(';', *last, t2); + last = &(*last)->child[1]; + } + } + if(tok != ';' && tok != '&' && (!nlok || tok != '\n')) + break; + tok = yylex(); + } + *ptok = tok; + return t; +} + +static tree* +brace(int tok) +{ + tree *t; + + // brace: '{' body '}' {$$=tree1(BRACE, $2);} + + tok = dropsp(tok); + if(tok != '{') + syntax(tok); + t = body(yylex(), &tok); + if(tok != '}') + syntax(tok); + return tree1(BRACE, t); +} + +static tree* +paren(int tok) +{ + tree *t; + + // paren: '(' body ')' {$$=tree1(PCMD, $2);} + + tok = dropsp(tok); + if(tok != '(') + syntax(tok); + t = body(yylex(), &tok); + if(tok != ')') + syntax(tok); + return tree1(PCMD, t); +} + +static tree* +epilog(int tok, int *ptok) +{ + tree *t, *r; + + // epilog: {$$=0;} + // | redir epilog {$$=mung2($1, $1->child[0], $2);} + + if(tok != REDIR && tok != DUP) { + *ptok = tok; + return nil; + } + + r = yyredir(tok, &tok); + t = epilog(tok, &tok); + *ptok = tok; + return mung2(r, r->child[0], t); +} + +static tree* +yyredir(int tok, int *ptok) +{ + tree *r, *w; + + // redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} + // | DUP + + switch(tok) { + default: + syntax(tok); + case DUP: + r = yylval.tree; + *ptok = dropsp(yylex()); + break; + case REDIR: + r = yylval.tree; + w = yyword(yylex(), &tok, 1); + *ptok = dropsp(tok); + r = mung1(r, r->rtype==HERE?heredoc(w):w); + break; + } + return r; +} + +static tree* +cmd(int tok, int *ptok) +{ + int op; + tree *t1, *t2; + + // | cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} + // | cmd OROR cmd {$$=tree2(OROR, $1, $3);} + + tok = dropsp(tok); + t1 = cmd2(tok, &tok); + while(tok == ANDAND || tok == OROR){ + op = tok; + t2 = cmd2(dropnl(yylex()), &tok); + t1 = tree2(op, t1, t2); + } + *ptok = tok; + return t1; +} + +static tree* +cmd2(int tok, int *ptok) +{ + tree *t1, *t2, *t3; + + // | cmd PIPE cmd {$$=mung2($2, $1, $3);} + t1 = cmd3(tok, &tok); + while(tok == PIPE){ + t2 = yylval.tree; + t3 = cmd3(dropnl(yylex()), &tok); + t1 = mung2(t2, t1, t3); + } + *ptok = tok; + return t1; +} + +static tree* +cmd3(int tok, int *ptok) +{ + tree *t1, *t2, *t3, *t4; + + tok = dropsp(tok); + switch(tok){ + case ';': + case '&': + case '\n': + *ptok = tok; + return nil; + + case IF: + // | IF paren {skipnl();} cmd {$$=mung2($1, $2, $4);} + // | IF NOT {skipnl();} cmd {$$=mung1($2, $4);} + t1 = yylval.tree; + tok = dropsp(yylex()); + if(tok == NOT) { + t1 = yylval.tree; + t2 = cmd(dropnl(yylex()), ptok); + return mung1(t1, t2); + } + t2 = paren(tok); + t3 = cmd(dropnl(yylex()), ptok); + return mung2(t1, t2, t3); + + case FOR: + // | FOR '(' word IN words ')' {skipnl();} cmd + // {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);} + // | FOR '(' word ')' {skipnl();} cmd + // {$$=mung3($1, $3, (tree *)0, $6);} + t1 = yylval.tree; + tok = dropsp(yylex()); + if(tok != '(') + syntax(tok); + t2 = yyword(yylex(), &tok, 1); + switch(tok) { + default: + syntax(tok); + case ')': + t3 = nil; + break; + case IN: + t3 = words(yylex(), &tok); + if(t3 == nil) + t3 = tree1(PAREN, nil); + if(tok != ')') + syntax(tok); + break; + } + t4 = cmd(dropnl(yylex()), ptok); + return mung3(t1, t2, t3, t4); + + case WHILE: + // | WHILE paren {skipnl();} cmd + // {$$=mung2($1, $2, $4);} + t1 = yylval.tree; + t2 = paren(yylex()); + t3 = cmd(dropnl(yylex()), ptok); + return mung2(t1, t2, t3); + + case SWITCH: + // | SWITCH word {skipnl();} brace + // {$$=tree2(SWITCH, $2, $4);} + t1 = yyword(yylex(), &tok, 1); + tok = dropnl(tok); // doesn't work in yacc grammer but works here! + t2 = brace(tok); + *ptok = dropsp(yylex()); + return tree2(SWITCH, t1, t2); + // Note: cmd: a && for(x) y && b is a && {for (x) {y && b}}. + return cmd(tok, ptok); + + case FN: + // | FN words brace {$$=tree2(FN, $2, $3);} + // | FN words {$$=tree1(FN, $2);} + t1 = words(yylex(), &tok); + if(tok != '{') { + *ptok = tok; + return tree1(FN, t1); + } + t2 = brace(tok); + *ptok = dropsp(yylex()); + return tree2(FN, t1, t2); + + case TWIDDLE: + // | TWIDDLE word words {$$=mung2($1, $2, $3);} + t1 = yylval.tree; + t2 = yyword(yylex(), &tok, 1); + t3 = words(tok, ptok); + return mung2(t1, t2, t3); + + case BANG: + case SUBSHELL: + // | BANG cmd {$$=mung1($1, $2);} + // | SUBSHELL cmd {$$=mung1($1, $2);} + // Note: cmd2: ! x | y is !{x | y} not {!x} | y. + t1 = yylval.tree; + return mung1(t1, cmd2(yylex(), ptok)); + + case REDIR: + case DUP: + // | redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} + // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x. + t1 = yyredir(tok, &tok); + t2 = cmd2(tok, ptok); + return mung2(t1, t1->child[0], t2); + + case '{': + // | brace epilog {$$=epimung($1, $2);} + t1 = brace(tok); + tok = dropsp(yylex()); + t2 = epilog(tok, ptok); + return epimung(t1, t2); + } + + if(!iswordtok(tok)) { + *ptok = tok; + return nil; + } + + // cmd: ... + // | simple {$$=simplemung($1);} + // | assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} + // assign: first '=' word {$$=tree2('=', $1, $3);} + // Note: first is same as word except for disallowing all the leading keywords, + // but all those keywords have been picked off in the switch above. + // Except NOT, but disallowing that in yacc was likely a mistake anyway: + // there's no ambiguity in not=1 or not x y z. + t1 = yyword(tok, &tok, 0); + if(tok == '=') { + // assignment + // Note: cmd2: {x=1 true | echo $x} echoes 1. + t1 = tree2('=', t1, yyword(yylex(), &tok, 1)); + t2 = cmd2(tok, ptok); + return mung3(t1, t1->child[0], t1->child[1], t2); + } + + // simple: first + // | simple word {$$=tree2(ARGLIST, $1, $2);} + // | simple redir {$$=tree2(ARGLIST, $1, $2);} + for(;;) { + if(tok == REDIR || tok == DUP) { + t1 = tree2(ARGLIST, t1, yyredir(tok, &tok)); + } else if(iswordtok(tok)) { + t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1)); + } else { + break; + } + } + *ptok = tok; + return simplemung(t1); +} + +static tree* +words(int tok, int *ptok) +{ + tree *t; + + // words: {$$=(tree*)0;} + // | words word {$$=tree2(WORDS, $1, $2);} + + t = nil; + tok = dropsp(tok); + while(iswordtok(tok)) + t = tree2(WORDS, t, yyword(tok, &tok, 1)); + *ptok = tok; + return t; +} + +static tree* +yyword(int tok, int *ptok, int eqok) +{ + tree *t; + + // word: keyword {lastword=1; $1->type=WORD;} + // | comword + // | word '^' word {$$=tree2('^', $1, $3);} + // comword: '$' word {$$=tree1('$', $2);} + // | '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} + // | '"' word {$$=tree1('"', $2);} + // | COUNT word {$$=tree1(COUNT, $2);} + // | WORD + // | '`' brace {$$=tree1('`', $2);} + // | '(' words ')' {$$=tree1(PAREN, $2);} + // | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} + // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN + // + // factored into: + // + // word: word1 + // | word '^' word1 + // + // word1: keyword | comword + + t = word1(tok, &tok); + if(tok == '=' && !eqok) + goto out; + for(;;){ + if(iswordtok(tok)){ + // No free carets around parens. + if(t->type == PAREN || tok == '(') + syntax(tok); + t = tree2('^', t, word1(tok, &tok)); + continue; + } + tok = dropsp(tok); + if(tok == '^'){ + t = tree2('^', t, word1(yylex(), &tok)); + continue; + } + break; + } +out: + *ptok = dropsp(tok); + return t; +} + +static tree* +word1(int tok, int *ptok) +{ + tree *w, *sub, *t; + + tok = dropsp(tok); + switch(tok) { + default: + syntax(tok); + + case WORD: + case FOR: + case IN: + case WHILE: + case IF: + case NOT: + case TWIDDLE: + case BANG: + case SUBSHELL: + case SWITCH: + case FN: + // | WORD + // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN + t = yylval.tree; + t->type = WORD; + *ptok = yylex(); + return t; + + case '=': + *ptok = yylex(); + return token("=", WORD); + + case '$': + // comword: '$' word1 {$$=tree1('$', $2);} + // | '$' word1 SUB words ')' {$$=tree2(SUB, $2, $4);} + w = word1(yylex(), &tok); + if(tok == '(') { + sub = words(yylex(), &tok); + if(tok != ')') + syntax(tok); + *ptok = yylex(); + return tree2(SUB, w, sub); + } + *ptok = tok; + return tree1('$', w); + + case '"': + // | '"' word1 {$$=tree1('"', $2);} + return tree1('"', word1(yylex(), ptok)); + + case COUNT: + // | COUNT word1 {$$=tree1(COUNT, $2);} + return tree1(COUNT, word1(yylex(), ptok)); + + case '`': + // | '`' brace {$$=tree1('`', $2);} + t = tree1('`', brace(yylex())); + *ptok = yylex(); + return t; + + case '(': + // | '(' words ')' {$$=tree1(PAREN, $2);} + t = tree1(PAREN, words(yylex(), &tok)); + if(tok != ')') + syntax(tok); + *ptok = yylex(); + return t; + + case REDIRW: + // | REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;} + t = yylval.tree; + t = mung1(t, brace(yylex())); + t->type = PIPEFD; + *ptok = yylex(); + return t; + } +} + +static int +iswordtok(int tok) +{ + switch(tok) { + case FOR: + case IN: + case WHILE: + case IF: + case NOT: + case TWIDDLE: + case BANG: + case SUBSHELL: + case SWITCH: + case FN: + case '$': + case '"': + case COUNT: + case WORD: + case '`': + case '(': + case REDIRW: + case '=': + return 1; + } + return 0; +} diff --git a/sys/src/cmd/rc/pcmd.c b/sys/src/cmd/rc/pcmd.c index 384c888e..b3ed2f7d 100644 --- a/sys/src/cmd/rc/pcmd.c +++ b/sys/src/cmd/rc/pcmd.c @@ -146,3 +146,121 @@ pcmd(io *f, tree *t) break; } } + +void +pcmdu(io *f, tree *t) /* unambiguous */ +{ + if(t==0){ + pfmt(f, ""); + return; + } + + switch(t->type){ + default: pfmt(f, "(bad %d %p %p %p)", t->type, c0, c1, c2); + break; + case '$': pfmt(f, "($ %u)", c0); + break; + case '"': pfmt(f, "($\" %u)", c0); + break; + case '&': pfmt(f, "(& %u)", c0); + break; + case '^': pfmt(f, "(^ %u %u)", c0, c1); + break; + case '`': pfmt(f, "(` %u)", c0); + break; + case ANDAND: pfmt(f, "(&& %u %u)", c0, c1); + break; + case BANG: pfmt(f, "(! %u)", c0); + break; + case BRACE: pfmt(f, "(brace %u)", c0); + break; + case COUNT: pfmt(f, "($# %u)", c0); + break; + case FN: pfmt(f, "(fn %u %u)", c0, c1); + break; + case IF: pfmt(f, "(if %u %u)", c0, c1); + break; + case NOT: pfmt(f, "(if not %u)", c0); + break; + case OROR: pfmt(f, "(|| %u %u)", c0, c1); + break; + case PCMD: + case PAREN: pfmt(f, "(paren %u)", c0); + break; + case SUB: pfmt(f, "($sub %u %u)", c0, c1); + break; + case SIMPLE: pfmt(f, "(simple %u)", c0); + break; + case SUBSHELL: pfmt(f, "(@ %u)", c0); + break; + case SWITCH: pfmt(f, "(switch %u %u)", c0, c1); + break; + case TWIDDLE: pfmt(f, "(~ %u %u)", c0, c1); + break; + case WHILE: pfmt(f, "(while %u %u)", c0, c1); + break; + case ARGLIST: + pfmt(f, "(arglist %u %u)", c0, c1); + break; + case ';': + pfmt(f, "(; %u %u)", c0, c1); + break; + case WORDS: + pfmt(f, "(words %u %u)", c0, c1); + break; + case FOR: + pfmt(f, "(for %u %u %u)", c0, c1, c2); + break; + case WORD: + if(t->quoted) + pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + if(t->rtype==DUPFD) + pfmt(f, "(>[%d=%d]", t->fd1, t->fd0); /* yes, fd1, then fd0; read lex.c */ + else + pfmt(f, "(>[%d=]", t->fd0); /*)*/ + pfmt(f, " %u)", c1); + break; + case PIPEFD: + case REDIR: + pfmt(f, "("); + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + case RDWR: + pchr(f, '<'); + if(t->rtype==RDWR) + pchr(f, '>'); + if(t->fd0!=0) + pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + break; + } + if(t->rtype == HERE) + pfmt(f, "HERE %u)", c1); + else + pfmt(f, "%u %u)", c0, c1); + break; + case '=': + pfmt(f, "(%u=%u %u)", c0, c1, c2); + break; + case PIPE: + pfmt(f, "(|"); + if(t->fd1==0){ + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, " %u %u", c0, c1); + break; + } +} diff --git a/sys/src/cmd/rc/syn.y b/sys/src/cmd/rc/syn.y index e64a36dd..5c98ef80 100644 --- a/sys/src/cmd/rc/syn.y +++ b/sys/src/cmd/rc/syn.y @@ -1,5 +1,5 @@ %term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN -%term WORD REDIR DUP PIPE SUB +%term WORD REDIR REDIRW DUP PIPE SUB %term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ /* operator priorities -- lowest first */ %left IF WHILE FOR SWITCH ')' NOT @@ -19,7 +19,7 @@ %type line paren brace body cmdsa cmdsan assign epilog redir %type cmd simple first word comword keyword words %type NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN -%type WORD REDIR DUP PIPE +%type WORD REDIR REDIRW DUP PIPE %% rc: { return 1;} | line '\n' {return !compile($1);} @@ -85,7 +85,7 @@ comword: '$' word {$$=tree1('$', $2);} | WORD | '`' brace {$$=tree1('`', $2);} | '(' words ')' {$$=tree1(PAREN, $2);} -| REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} +| REDIRW brace {$$=mung1($1, $2); $$->type=PIPEFD;} keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN words: {$$=(struct tree*)0;} | words word {$$=tree2(WORDS, $1, $2);} diff --git a/sys/src/cmd/rc/test.rc b/sys/src/cmd/rc/test.rc new file mode 100644 index 00000000..4a33d87c --- /dev/null +++ b/sys/src/cmd/rc/test.rc @@ -0,0 +1,93 @@ +# test for parser + +a +a b +a|b +a | b +{a; b; c} +x=y a && b || c +x=y a | b | c +x=y for(i) a | b +>x for(i) a | b +>x a || b && c +a >x || b && c +a | for(i) b | c +fn x {y; z} | b && c +if (x) y +if not z +`{} >x >[1=2]y >[3=] z z x | y + +# rejected now, was like parens were spaces before. +# echo Formatting Venti arenas and indices (this takes a while). + + +# echo $STATLINE(1)^$STATLINE(3)' '$STATLINE(2)' '$STATLINE(4)' '$LSLINE(6)