%token Tfor Tin Twhile Tif Telse Tswitch Tcase Tcasebody Ttwiddle Tbang Tsubshell Tfunc %token Tredir Tdup Tpipe Tindex %token Tbasic Targs Tword Twords Tparen Tblock %define parse.error verbose %{ #include "rc.h" int yylex(void); void yyerror(const char *); %} /* operator precendence: lowest first */ %left Tif Tfor Tswitch Tcase ')' Twhile Telse %left Tandand Toror '\n' %left Tbang Tsubshell %left Tpipe; %left '^'; %right '$' Tcount Tjoin %left Tindex /* semantic types */ %union{ struct Tree *tree; } %type line cmds cmdsln body paren block ifbody casebody case assign epilog redir; %type cmd basic executable nonkeyword keyword word words wordsnl atom; %type Tfor Tin Twhile Tif Telse Tswitch Tcase Ttwiddle Tbang Tsubshell Tfunc; %type Tword Tredir Tpipe Tdup; /* grammar */ %start rc %% rc: /*empty*/ { return 0; } | line '\n' { return compile($1); } line: cmd | cmds line { $$ = maketree2(';', $1, $2); } body: cmd | cmdsln body { $$ = maketree2(';', $1, $2); } paren: '(' body ')' { $$ = maketree1(Tparen, $2); } block: '{' body '}' { $$ = maketree1(Tblock, $2); } cmds: cmd ';' | cmd '&' { $$ = maketree1('&', $1); } cmdsln: cmds | cmd '\n' ifbody: cmd %prec Telse { $$ = maketree2(Tif, nil, $1); } | block Telse nl cmd { $$ = maketree3(Tif, nil, $1, $2); } case: Tcase words ';' nl cmdsln { $$ = hangchild2($1, $2, 0, $5, 1);} casebody: /* empty */ { $$ = nil; } | case casebody { $$ = (!$2) ? $1 : maketree2(';', $1, $2); } assign: executable '=' word { $$ = maketree2('=', $1, $3); } redir: Tdup | Tredir word { $$ = hangchild1($1, $2, 0); } epilog: /* empty */ { $$ = nil; } | redir epilog { $$ = hangchild1($1, $2, 1); } cmd: /* empty */ %prec Twhile { $$ = nil; } | basic { $$ = maketree1(Tbasic, $1); } | block epilog { $$ = hangepilog($1, $2); } | cmd Tpipe nl cmd { $$ = hangchild2($2, $1, 0, $4, 1); } | cmd Tandand nl cmd { $$ = maketree2(Tandand, $1, $4); } | cmd Toror nl cmd { $$ = maketree2(Toror, $1, $4); } | redir cmd %prec Tbang { $$ = hangchild1($1, $2, 1); } | assign cmd %prec Tbang { $$ = hangchild1($1, $2, 2); } | Tbang cmd { $$ = maketree1(Tbang, $2); } | Tsubshell cmd { $$ = maketree1(Tsubshell, $2); } | Tfor '(' word ')' nl cmd { $$ = hangchild3($1, $3, nil, $6); } | Tfor '(' word Tin words ')' nl cmd { $$ = hangchild3($1, $3, $5, $8); } | Twhile paren nl cmd { $$ = hangchild2($1, $2, 0, $4, 1); } | Tif paren nl ifbody { $$ = hangchild1($2, $2, 0); } | Tswitch '(' word ')' nl '{' nl casebody '}' { $$ = hangchild2($1, $3, 0, $8, 1); } basic: executable | basic word { $$ = maketree2(Targs, $1, $2); } | basic redir { $$ = maketree2(Targs, $1, $2); } atom: nonkeyword | keyword { $$ = maketree1(Tword, $1); } word: atom | word '^' atom { $$ = maketree2('^', $1, $3); } executable: nonkeyword | executable '^' atom { $$ = maketree2('^', $1, $3); } nonkeyword: Tword | '$' atom { $$ = maketree1('$', $2); } | '$' atom Tindex words ')' { $$ = maketree2(Tindex, $2, $4); } | '(' wordsnl ')' { $$ = $2; } | Tcount atom { $$ = maketree1(Tcount, $2); } | Tjoin atom { $$ = maketree1(Tjoin, $2); } | '`' block { $$ = maketree1('`', $2); } //| Tredir block { $$ = hangchild1($1, $2, 0); $$->type = Tpipefd; } keyword: Tfor|Tin|Twhile|Tif|Telse|Tswitch|Tcase|Tbang|Tsubshell|Tfunc words: /* empty */ { $$ = nil; } | words word { $$ = maketree2(Twords, $1, $2); } wordsnl: /* empty */ { $$ = nil; } | wordsnl '\n' /* empty */ | wordsnl word {$$ = (!$1) ? ((!$2) ? nil : $2) : ((!$2) ? $1 : maketree2(Twords, $1, $2)); } nl: /*empty*/ | nl '\n' %%