BSD 4_4_Lite1 development
[unix-history] / usr / src / contrib / rc-1.4 / parse.y
/* parse.y */
/*
* Adapted from rc grammar, v10 manuals, volume 2.
*/
%{
#include "rc.h"
#ifndef lint
#define lint /* hush up gcc -Wall, leave out the dumb sccsid's. */
#endif
static Node *star, *nolist;
Node *parsetree; /* not using yylval because bison declares it as an auto */
%}
%token ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN
%token OROR PIPE REDIR SREDIR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD HUH
%left WHILE ')' ELSE
%left ANDAND OROR '\n'
%left BANG SUBSHELL
%left PIPE
%right '$'
%left SUB
/*
*/
%union {
struct Node *node;
struct Redir redir;
struct Pipe pipe;
struct Dup dup;
struct Word word;
char *keyword;
}
%type <redir> REDIR SREDIR
%type <pipe> PIPE
%type <dup> DUP
%type <word> WORD
%type <keyword> keyword
%type <node> assign body brace case cbody cmd cmdsa cmdsan comword epilog
first line nlwords paren redir sword simple iftail word words
%start rc
%%
rc : line end { parsetree = $1; YYACCEPT; }
| error end { yyerrok; parsetree = NULL; YYABORT; }
/* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */
end : END /* EOF */ { if (!heredoc(1)) YYABORT; } /* flag error if there is a heredoc in the queue */
| '\n' { if (!heredoc(0)) YYABORT; } /* get heredoc on \n */
/* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */
cmdsa : cmd ';'
| cmd '&' { $$ = ($1 != NULL ? mk(nNowait,$1) : $1); }
/* a line is a single command, or a command terminated by ; or & followed by a line (recursive) */
line : cmd
| cmdsa line { $$ = ($1 != NULL ? mk(nBody,$1,$2) : $2); }
/* a body is like a line, only commands may also be terminated by newline */
body : cmd
| cmdsan body { $$ = ($1 == NULL ? $2 : $2 == NULL ? $1 : mk(nBody,$1,$2)); }
cmdsan : cmdsa
| cmd '\n' { $$ = $1; if (!heredoc(0)) YYABORT; } /* get h.d. on \n */
brace : '{' body '}' { $$ = $2; }
paren : '(' body ')' { $$ = $2; }
assign : first '=' word { $$ = mk(nAssign,$1,$3); }
epilog : { $$ = NULL; }
| redir epilog { $$ = mk(nEpilog,$1,$2); }
/* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */
redir : DUP { $$ = mk(nDup,$1.type,$1.left,$1.right); }
| REDIR word { $$ = mk(nRedir,$1.type,$1.fd,$2);
if ($1.type == rHeredoc && !qdoc($2, $$)) YYABORT; /* queue heredocs up */
}
| SREDIR word { $$ = mk(nRedir,$1.type,$1.fd,$2);
if ($1.type == rHeredoc && !qdoc($2, $$)) YYABORT; /* queue heredocs up */
}
case : CASE words ';' { $$ = mk(nCase, $2); }
| CASE words '\n' { $$ = mk(nCase, $2); }
cbody : cmd { $$ = mk(nCbody, $1, NULL); }
| case cbody { $$ = mk(nCbody, $1, $2); }
| cmdsan cbody { $$ = mk(nCbody, $1, $2); }
iftail : cmd %prec ELSE
| brace ELSE optnl cmd { $$ = mk(nElse,$1,$4); }
cmd : /* empty */ %prec WHILE { $$ = NULL; }
| simple
| brace epilog { $$ = mk(nBrace,$1,$2); }
| IF paren optnl iftail { $$ = mk(nIf,$2,$4); }
| FOR '(' word IN words ')' optnl cmd { $$ = mk(nForin,$3,$5,$8); }
| FOR '(' word ')' optnl cmd { $$ = mk(nForin,$3,star,$6); }
| WHILE paren optnl cmd { $$ = mk(nWhile,$2,$4); }
| SWITCH '(' word ')' optnl '{' cbody '}' { $$ = mk(nSwitch,$3,$7); }
| TWIDDLE optcaret word words { $$ = mk(nMatch,$3,$4); }
| cmd ANDAND optnl cmd { $$ = mk(nAndalso,$1,$4); }
| cmd OROR optnl cmd { $$ = mk(nOrelse,$1,$4); }
| cmd PIPE optnl cmd { $$ = mk(nPipe,$2.left,$2.right,$1,$4); }
| redir cmd %prec BANG { $$ = ($2 != NULL ? mk(nPre,$1,$2) : $1); }
| assign cmd %prec BANG { $$ = ($2 != NULL ? mk(nPre,$1,$2) : $1); }
| BANG optcaret cmd { $$ = mk(nBang,$3); }
| SUBSHELL optcaret cmd { $$ = mk(nSubshell,$3); }
| FN words brace { $$ = mk(nNewfn,$2,$3); }
| FN words { $$ = mk(nRmfn,$2); }
optcaret : /* empty */
| '^'
simple : first
| simple word { $$ = ($2 != NULL ? mk(nArgs,$1,$2) : $1); }
| simple redir { $$ = mk(nArgs,$1,$2); }
first : comword
| first '^' sword { $$ = mk(nConcat,$1,$3); }
sword : comword
| keyword { $$ = mk(nWord,$1, NULL); }
word : sword
| word '^' sword { $$ = mk(nConcat,$1,$3); }
comword : '$' sword { $$ = mk(nVar,$2); }
| '$' sword SUB words ')' { $$ = mk(nVarsub,$2,$4); }
| COUNT sword { $$ = mk(nCount,$2); }
| FLAT sword { $$ = mk(nFlat, $2); }
| '`' sword { $$ = mk(nBackq,nolist,$2); }
| '`' brace { $$ = mk(nBackq,nolist,$2); }
| BACKBACK word brace { $$ = mk(nBackq,$2,$3); }
| BACKBACK word sword { $$ = mk(nBackq,$2,$3); }
| '(' nlwords ')' { $$ = $2; }
| REDIR brace { $$ = mk(nNmpipe,$1.type,$1.fd,$2); }
| WORD { $$ = ($1.w[0] == '\'') ? mk(nQword, $1.w+1, NULL) : mk(nWord,$1.w, $1.m); }
keyword : FOR { $$ = "for"; }
| IN { $$ = "in"; }
| WHILE { $$ = "while"; }
| IF { $$ = "if"; }
| SWITCH { $$ = "switch"; }
| FN { $$ = "fn"; }
| ELSE { $$ = "else"; }
| CASE { $$ = "case"; }
| TWIDDLE { $$ = "~"; }
| BANG { $$ = "!"; }
| SUBSHELL { $$ = "@"; }
words : { $$ = NULL; }
| words word { $$ = ($1 != NULL ? ($2 != NULL ? mk(nLappend,$1,$2) : $1) : $2); }
nlwords : { $$ = NULL; }
| nlwords '\n'
| nlwords word { $$ = ($1 != NULL ? ($2 != NULL ? mk(nLappend,$1,$2) : $1) : $2); }
optnl : /* empty */
| optnl '\n'
%%
void initparse() {
star = treecpy(mk(nVar,mk(nWord,"*",NULL)), ealloc);
nolist = treecpy(mk(nVar,mk(nWord,"ifs",NULL)), ealloc);
}