* new rc parser: do we want it?
@ 2020-10-07 3:15 ori
2020-10-07 3:54 ` [9front] " sl
2020-10-07 19:02 ` Anthony Martin
0 siblings, 2 replies; 3+ messages in thread
From: ori @ 2020-10-07 3:15 UTC (permalink / raw)
To: 9front
So, I sat down today and got rsc's new rc parser
working in 9front. There were a couple of small
tweaks needed to make our `sep{...} syntax work,
but other than that, it was mostly uneventful.
The new parser gives us 2 nice things:
echo `{echo foo}.c
has the same free caret behavior as:
echo $foo.c
and unquoted '=' is allowed after the
first word:
echo a=b
But mostly, it prevents us from diverging from
plan9port.
Do we want this?
If yes, can I get some people to test it out
with their scripts, and people with a longer
memory than I to make sure that I accounted
for all of our changes.
Patch below:
diff -r f7dfc146779d sys/src/cmd/rc/code.c
--- a/sys/src/cmd/rc/code.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/code.c Tue Oct 06 20:14:21 2020 -0700
@@ -37,6 +37,16 @@
int
compile(tree *t)
{
+ if(flag['D']) {
+ struct io *s;
+ s = openstr();
+ pfmt(s, "compile: %u\n", t);
+ write(2, s->strp, strlen((char*)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 -r f7dfc146779d sys/src/cmd/rc/exec.c
--- a/sys/src/cmd/rc/exec.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/exec.c Tue Oct 06 20:14:21 2020 -0700
@@ -164,7 +164,7 @@
code bootstrap[17];
char num[12], *rcmain;
int i;
- argc = getflags(argc, argv, "SsrdiIlxepvVc:1m:1[command]", 1);
+ argc = getflags(argc, argv, "DSYsrdiIlxepvVc:1m:1[command]", 1);
if(argc==-1)
usage("[file [arg ...]]");
if(argv[0][0]=='-')
@@ -927,7 +927,7 @@
promptstr="% ";
}
Noerror();
- if(yyparse()){
+ if((flag['Y'] ? yyparse : parse)()){
if(!p->iflag || p->eof && !Eintr()){
if(p->cmdfile)
free(p->cmdfile);
diff -r f7dfc146779d sys/src/cmd/rc/fns.h
--- a/sys/src/cmd/rc/fns.h Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/fns.h Tue Oct 06 20:14:21 2020 -0700
@@ -64,3 +64,4 @@
void yyerror(char*);
int yylex(void);
int yyparse(void);
+int parse(void);
diff -r f7dfc146779d sys/src/cmd/rc/havefork.c
--- a/sys/src/cmd/rc/havefork.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/havefork.c Tue Oct 06 20:14:21 2020 -0700
@@ -212,7 +212,9 @@
break;
default:
addwaitpid(pid);
- Waitfor(pid, 1);
+ /* interrupts don't get us out */
+ while(Waitfor(pid, 1) < 0)
+ ;
runq->pc = runq->code[runq->pc].i;
break;
}
diff -r f7dfc146779d sys/src/cmd/rc/io.c
--- a/sys/src/cmd/rc/io.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/io.c Tue Oct 06 20:14:21 2020 -0700
@@ -48,7 +48,10 @@
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 *));
diff -r f7dfc146779d sys/src/cmd/rc/io.h
--- a/sys/src/cmd/rc/io.h Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/io.h Tue Oct 06 20:14:21 2020 -0700
@@ -22,6 +22,7 @@
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 -r f7dfc146779d sys/src/cmd/rc/lex.c
--- a/sys/src/cmd/rc/lex.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/lex.c Tue Oct 06 20:14:21 2020 -0700
@@ -112,15 +112,17 @@
doprompt = 0;
}
-void
+int
skipwhite(void)
{
- int c;
+ int c, 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) {
@@ -130,9 +132,12 @@
advance();
}
}
- if(c==' ' || c=='\t')
+ if(c==' ' || c=='\t') {
+ skipped = 1;
advance();
- else return;
+ }
+ else
+ return skipped;
}
}
@@ -203,7 +208,7 @@
{
int c, d = nextc();
char *w = tok;
- struct tree *t;
+ tree *t;
yylval.tree = 0;
/*
* Embarassing sneakiness: if the last token read was a quoted or unquoted
@@ -212,11 +217,11 @@
* 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=='"'){
@@ -225,7 +230,8 @@
}
}
inquote = 0;
- skipwhite();
+ if(skipwhite() && !flag['Y'])
+ return ' ';
switch(c = advance()){
case EOF:
lastdol = 0;
@@ -246,7 +252,8 @@
case '&':
lastdol = 0;
if(nextis('&')){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "&&");
return ANDAND;
}
@@ -255,7 +262,8 @@
case '|':
lastdol = 0;
if(nextis(c)){
- skipnl();
+ if(flag['Y'])
+ skipnl();
strcpy(tok, "||");
return OROR;
}
@@ -344,8 +352,13 @@
}
*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 -r f7dfc146779d sys/src/cmd/rc/mkfile
--- a/sys/src/cmd/rc/mkfile Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/mkfile Tue Oct 06 20:14:21 2020 -0700
@@ -9,6 +9,7 @@
here.$O\
io.$O\
lex.$O\
+ parse.$O\
pcmd.$O\
pfnc.$O\
simple.$O\
diff -r f7dfc146779d sys/src/cmd/rc/parse.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/cmd/rc/parse.c Tue Oct 06 20:14:21 2020 -0700
@@ -0,0 +1,555 @@
+#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 grammar but works here!
+ t2 = brace(tok);
+ *ptok = dropsp(yylex());
+ return tree2(SWITCH, t1, t2);
+
+ 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 carats 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('`', nil, $2);}
+ // | '`' word brace {$$=tree1('`', $2, $3);}
+ w = nil;
+ tok = yylex();
+ if(tok != '{')
+ w = yyword(tok, &tok, 1);
+ t = tree2('`', w, brace(tok));
+ *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 -r f7dfc146779d sys/src/cmd/rc/pcmd.c
--- a/sys/src/cmd/rc/pcmd.c Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/pcmd.c Tue Oct 06 20:14:21 2020 -0700
@@ -32,7 +32,7 @@
break;
case '^': pfmt(f, "%t^%t", c0, c1);
break;
- case '`': pfmt(f, "`%t%t", c0, c1);
+ case '`': pfmt(f, "`%t%t%t", c0, c1, c2);
break;
case ANDAND: pfmt(f, "%t && %t", c0, c1);
break;
@@ -146,3 +146,121 @@
break;
}
}
+
+void
+pcmdu(io *f, tree *t) /* unambiguous */
+{
+ if(t==0) {
+ pfmt(f, "<nil>");
+ 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 %u)", c0, c1);
+ 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 -r f7dfc146779d sys/src/cmd/rc/syn.y
--- a/sys/src/cmd/rc/syn.y Mon Oct 05 14:10:12 2020 -0700
+++ b/sys/src/cmd/rc/syn.y Tue Oct 06 20:14:21 2020 -0700
@@ -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<tree> line paren brace body cmdsa cmdsan assign epilog redir
%type<tree> cmd simple first word comword keyword words
%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN
-%type<tree> WORD REDIR DUP PIPE
+%type<tree> WORD REDIR REDIRW DUP PIPE
%%
rc: { return 1;}
| line '\n' {return !compile($1);}
@@ -45,7 +45,7 @@
| IF NOT {skipnl();} cmd {$$=mung1($2, $4);}
| FOR '(' word IN words ')' {skipnl();} cmd
/*
- * if ``words'' is nil, we need a tree element to distinguish between
+ * if ``words'' is nil, we need a tree element to distinguish between
* for(i in ) and for(i), the former being a loop over the empty set
* and the latter being the implicit argument loop. so if $5 is nil
* (the empty set), we represent it as "()". don't parenthesize non-nil
@@ -73,7 +73,7 @@
simple: first
| simple word {$$=tree2(ARGLIST, $1, $2);}
| simple redir {$$=tree2(ARGLIST, $1, $2);}
-first: comword
+first: comword
| first '^' word {$$=tree2('^', $1, $3);}
word: keyword {lastword=1; $1->type=WORD;}
| comword
@@ -86,7 +86,7 @@
| '`' brace {$$=tree2('`', (struct tree*)0, $2);}
| '`' word brace {$$=tree2('`', $2, $3);}
| '(' 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);}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [9front] new rc parser: do we want it?
2020-10-07 3:15 new rc parser: do we want it? ori
@ 2020-10-07 3:54 ` sl
2020-10-07 19:02 ` Anthony Martin
1 sibling, 0 replies; 3+ messages in thread
From: sl @ 2020-10-07 3:54 UTC (permalink / raw)
To: 9front
> and unquoted '=' is allowed after the
> first word:
>
> echo a=b
fwiw, when aiju suggested this a couple years ago i made a
big stink about how this breaks the predictable quoting
rules, but if rsc is dispensing with regularity, i don't
know what to say anymore. i suppose it's just one more thing to
remember.
sl
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: new rc parser: do we want it?
2020-10-07 3:15 new rc parser: do we want it? ori
2020-10-07 3:54 ` [9front] " sl
@ 2020-10-07 19:02 ` Anthony Martin
1 sibling, 0 replies; 3+ messages in thread
From: Anthony Martin @ 2020-10-07 19:02 UTC (permalink / raw)
To: 9front
ori@eigenstate.org once said:
> But mostly, it prevents us from diverging from
> plan9port.
>
> Do we want this?
Yes. I've been using it locally ever since it went into plan9port.
It's a small improvement and I think it's important that rc(1) is
essentially the same everywhere.
Cheers,
Anthony
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2020-10-07 19:03 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07 3:15 new rc parser: do we want it? ori
2020-10-07 3:54 ` [9front] " sl
2020-10-07 19:02 ` Anthony Martin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).