9front - general discussion about 9front
 help / color / mirror / Atom feed
* 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).