home *** CD-ROM | disk | FTP | other *** search
- /* parse.y */
-
- /*
- * Adapted from rc grammar, v10 manuals, volume 2.
- */
-
- %{
- #include "stddef.h"
- #include "node.h"
- #include "lex.h"
- #include "tree.h"
- #include "heredoc.h"
- #include "parse.h"
- #include "utils.h"
- #undef NULL
- #define NULL 0
- #undef lint
- #define lint /* hush up gcc -Wall, leave out the dumb sccsid's. */
- static Node *star, *nolist;
- Node *parsetree; /* not using yylval because bison declares it as an auto */
- %}
-
- %term BANG DUP ELSE END FN FOR HUH IF IN LBRACK PIPE RBRACK
- %term REDIR STAR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD
-
- %left IF WHILE FOR SWITCH ')' ELSE
- %left ANDAND OROR
- %left BANG SUBSHELL
- %left PIPE
- %left '^'
- %right '$' COUNT FLAT
- %left SUB
- %left '`' BACKBACK
-
- %union {
- struct Node *node;
- struct Redir redir;
- struct Pipe pipe;
- struct Dup dup;
- struct Word word;
- char *keyword;
- }
-
- %type <redir> REDIR
- %type <pipe> PIPE
- %type <dup> DUP
- %type <word> WORD
- %type <keyword> keyword
- %type <node> assign body brace cmd cmdsa cmdsan comword epilog
- first line paren redir simple iftail word words
-
- %start rc
-
- %%
-
- rc : line end { parsetree = $1; return 1; }
- | error end { yyerrok; parsetree = NULL; return -1; }
-
- /* an rc line may end in end-of-file as well as newline, e.g., rc -c 'ls' */
- end : END /* EOF */ { heredoc(1); } /* flag error if there is a heredoc in the queue */
- | '\n' { heredoc(0); } /* get heredoc on \n */
-
- /* a cmdsa is a command followed by ampersand or newline (used in "line" and "body") */
- cmdsa : cmd ';'
- | cmd '&' { $$ = ($1 != NULL ? newnode(NOWAIT,$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 ? newnode(BODY,$1,$2) : $2); }
-
- /* a body is like a line, only commands may also be terminated by newline */
- body : cmd
- | cmdsan body { $$ = ($1 != NULL ? newnode(BODY,$1,$2) : $2); }
-
- cmdsan : cmdsa
- | cmd '\n' { $$ = $1; heredoc(0); } /* get h.d. on \n */
-
- brace : '{' body '}' { $$ = $2; }
-
- paren : '(' body ')' { $$ = $2; }
-
- assign : first '=' word { $$ = newnode(ASSIGN,$1,$3); }
-
- epilog : { $$ = NULL; }
- | redir epilog { $$ = newnode(EPILOG,$1,$2); }
-
- /* a redirection is a dup (e.g., >[1=2]) or a file redirection. (e.g., > /dev/null) */
- redir : DUP { $$ = newnode(rDUP,$1.type,$1.left,$1.right); }
- | REDIR word { $$ = newnode(rREDIR,$1.type,$1.fd,$2);
- if ($1.type == HEREDOC) qdoc($2, $$); /* queue heredocs up */
- }
-
- /* the tail of an if statement can be a command, or a braced command followed by else on the same line */
- iftail : cmd %prec IF
- | brace ELSE cmd { $$ = newnode(rELSE,$1,$3); }
-
- /* skipnl() skips newlines and comments between an operator and its operand */
- cmd : { $$ = NULL; }
- | simple
- | brace epilog { $$ = ($2 == NULL ? $1 : newnode(BRACE,$1,$2)); }
- | IF paren { skipnl(); } iftail { $$ = newnode(rIF,$2,$4); }
- | FOR '(' word IN words ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,$5,$8); }
- | FOR '(' word ')' { skipnl(); } cmd { $$ = newnode(FORIN,$3,star,$6); }
- | WHILE paren { skipnl(); } cmd { $$ = newnode(rWHILE,$2,$4); }
- | SWITCH '(' word ')' { skipnl(); } brace { $$ = newnode(rSWITCH,$3,$6); }
- | TWIDDLE word words { $$ = newnode(MATCH,$2,$3); }
- | cmd ANDAND { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rANDAND,$1,$4) : $4); }
- | cmd OROR { skipnl(); } cmd { $$ = ($1 != NULL ? newnode(rOROR,$1,$4) : $4); }
- | cmd PIPE { skipnl(); } cmd { $$ = newnode(rPIPE,$2.left,$2.right,$1,$4); }
- | redir cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); }
- | assign cmd %prec BANG { $$ = ($2 != NULL ? newnode(PRE,$1,$2) : $1); }
- | BANG cmd { $$ = newnode(rBANG,$2); }
- | SUBSHELL cmd { $$ = newnode(rSUBSHELL,$2); }
- | FN words brace { $$ = newnode(NEWFN,$2,$3); }
- | FN words { $$ = newnode(RMFN,$2); }
-
- simple : first
- | simple word { $$ = ($2 != NULL ? newnode(ARGS,$1,$2) : $1); }
- | simple redir { $$ = newnode(ARGS,$1,$2); }
-
- first : comword
- | first '^' word { $$ = newnode(CONCAT,$1,$3); }
-
- word : comword
- | keyword { $$ = newnode(rWORD,$1, NULL); }
- | word '^' word { $$ = newnode(CONCAT,$1,$3); }
-
- comword : '$' word { $$ = newnode(VAR,$2); }
- | '$' word SUB words ')' { $$ = newnode(VARSUB,$2,$4); }
- | COUNT word { $$ = newnode(rCOUNT,$2); }
- | FLAT word { $$ = newnode(rFLAT, $2); }
- | WORD { $$ = newnode(rWORD,$1.w, $1.m); }
- | '`' word { $$ = newnode(BACKQ,nolist,$2); }
- | '`' brace { $$ = newnode(BACKQ,nolist,$2); }
- | BACKBACK word brace { $$ = newnode(BACKQ,$2,$3); }
- | BACKBACK word word { $$ = newnode(BACKQ,$2,$3); }
- | '(' words ')' { $$ = $2; }
- | REDIR brace { $$ = newnode(NMPIPE,$1.type,$1.fd,$2); }
-
- keyword : FOR { $$ = "for"; }
- | IN { $$ = "in"; }
- | WHILE { $$ = "while"; }
- | IF { $$ = "if"; }
- | SWITCH { $$ = "switch"; }
- | FN { $$ = "fn"; }
- | ELSE { $$ = "else"; }
- | TWIDDLE { $$ = "~"; }
- | BANG { $$ = "!"; }
- | SUBSHELL { $$ = "@"; }
-
- words : { $$ = NULL; }
- | words word { $$ = ($1 != NULL ? ($2 != NULL ? newnode(LAPPEND,$1,$2) : $1) : $2); }
-
- %%
-
- void initparse() {
- star = treecpy(newnode(VAR,newnode(rWORD,"*",NULL)), ealloc);
- nolist = treecpy(newnode(VAR,newnode(rWORD,"ifs",NULL)), ealloc);
- }
-