URI:
       tparse.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tparse.c (10522B)
       ---
            1 #include "rc.h"
            2 #include "io.h"
            3 #include "fns.h"
            4 
            5 static tree*        body(int tok, int *ptok);
            6 static tree*        brace(int tok);
            7 static tree*        cmd(int tok, int *ptok);
            8 static tree*        cmd2(int tok, int *ptok);
            9 static tree*        cmd3(int tok, int *ptok);
           10 static tree*        cmds(int tok, int *ptok, int nlok);
           11 static tree*        epilog(int tok, int *ptok);
           12 static int        iswordtok(int tok);
           13 static tree*        line(int tok, int *ptok);
           14 static tree*        paren(int tok);
           15 static tree*        yyredir(int tok, int *ptok);
           16 static tree*        yyword(int tok, int *ptok, int eqok);
           17 static tree*        word1(int tok, int *ptok);
           18 static tree*        words(int tok, int *ptok);
           19 
           20 static jmp_buf yyjmp;
           21 
           22 static int
           23 dropnl(int tok)
           24 {
           25         while(tok == ' ' || tok == '\n')
           26                 tok = yylex();
           27         return tok;
           28 }
           29 
           30 static int
           31 dropsp(int tok)
           32 {
           33         while(tok == ' ')
           34                 tok = yylex();
           35         return tok;
           36 }
           37 
           38 static void
           39 syntax(int tok)
           40 {
           41         USED(tok);
           42         yyerror("syntax error");
           43         longjmp(yyjmp, 1);
           44 }
           45 
           46 int
           47 parse(void)
           48 {
           49         tree *t;
           50         int tok;
           51 
           52         if(setjmp(yyjmp))
           53                 return 1;
           54 
           55         // rc:                                { return 1;}
           56         // |        line '\n'                {return !compile($1);}
           57 
           58         tok = dropsp(yylex());
           59         if(tok == EOF)
           60                 return 1;
           61         t = line(tok, &tok);
           62         if(tok != '\n')
           63                 yyerror("missing newline at end of line");
           64         yylval.tree = t;
           65         return !compile(t);
           66 }
           67 
           68 static tree*
           69 line(int tok, int *ptok)
           70 {
           71         return cmds(tok, ptok, 0);
           72 }
           73 
           74 static tree*
           75 body(int tok, int *ptok)
           76 {
           77         return cmds(tok, ptok, 1);
           78 }
           79 
           80 static tree*
           81 cmds(int tok, int *ptok, int nlok)
           82 {
           83         tree *t, **last, *t2;
           84 
           85         // line:        cmd
           86         // |        cmdsa line                {$$=tree2(';', $1, $2);}
           87         // cmdsa:        cmd ';'
           88         // |        cmd '&'                        {$$=tree1('&', $1);}
           89 
           90         // body:        cmd
           91         // |        cmdsan body                {$$=tree2(';', $1, $2);}
           92         // cmdsan:        cmdsa
           93         // |        cmd '\n'
           94 
           95         t = nil;
           96         last = nil;
           97         for(;;) {
           98                 t2 = cmd(tok, &tok);
           99                 if(tok == '&')
          100                         t2 = tree1('&', t2);
          101                 if(t2 != nil) {
          102                         // slot into list t
          103                         if(last == nil) {
          104                                 t = t2;
          105                                 last = &t;
          106                         } else {
          107                                 *last = tree2(';', *last, t2);
          108                                 last = &(*last)->child[1];
          109                         }
          110                 }
          111                 if(tok != ';' && tok != '&' && (!nlok || tok != '\n'))
          112                         break;
          113                 tok = yylex();
          114         }
          115         *ptok = tok;
          116         return t;
          117 }
          118 
          119 static tree*
          120 brace(int tok)
          121 {
          122         tree *t;
          123 
          124         // brace:        '{' body '}'                {$$=tree1(BRACE, $2);}
          125 
          126         tok = dropsp(tok);
          127         if(tok != '{')
          128                 syntax(tok);
          129         t = body(yylex(), &tok);
          130         if(tok != '}')
          131                 syntax(tok);
          132         return tree1(BRACE, t);
          133 }
          134 
          135 static tree*
          136 paren(int tok)
          137 {
          138         tree *t;
          139 
          140         // paren:        '(' body ')'                {$$=tree1(PCMD, $2);}
          141 
          142         tok = dropsp(tok);
          143         if(tok != '(')
          144                 syntax(tok);
          145         t = body(yylex(), &tok);
          146         if(tok != ')')
          147                 syntax(tok);
          148         return tree1(PCMD, t);
          149 }
          150 
          151 static tree*
          152 epilog(int tok, int *ptok)
          153 {
          154         tree *t, *r;
          155 
          156         // epilog:                                {$$=0;}
          157         // |        redir epilog                {$$=mung2($1, $1->child[0], $2);}
          158 
          159         if(tok != REDIR && tok != DUP) {
          160                 *ptok = tok;
          161                 return nil;
          162         }
          163 
          164         r = yyredir(tok, &tok);
          165         t = epilog(tok, &tok);
          166         *ptok = tok;
          167         return mung2(r, r->child[0], t);
          168 }
          169 
          170 static tree*
          171 yyredir(int tok, int *ptok)
          172 {
          173         tree *r, *w;
          174 
          175         // redir:        REDIR word                {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);}
          176         // |        DUP
          177 
          178         switch(tok) {
          179         default:
          180                 syntax(tok);
          181         case DUP:
          182                 r = yylval.tree;
          183                 *ptok = dropsp(yylex());
          184                 break;
          185         case REDIR:
          186                 r = yylval.tree;
          187                 w = yyword(yylex(), &tok, 1);
          188                 *ptok = dropsp(tok);
          189                 r = mung1(r, r->rtype==HERE?heredoc(w):w);
          190                 break;
          191         }
          192         return r;
          193 }
          194 
          195 static tree*
          196 cmd(int tok, int *ptok)
          197 {
          198         int op;
          199         tree *t1, *t2;
          200 
          201         // |        cmd ANDAND cmd                {$$=tree2(ANDAND, $1, $3);}
          202         // |        cmd OROR cmd                {$$=tree2(OROR, $1, $3);}
          203 
          204         tok = dropsp(tok);
          205         t1 = cmd2(tok, &tok);
          206         while(tok == ANDAND || tok == OROR) {
          207                 op = tok;
          208                 t2 = cmd2(dropnl(yylex()), &tok);
          209                 t1 = tree2(op, t1, t2);
          210         }
          211         *ptok = tok;
          212         return t1;
          213 }
          214 
          215 static tree*
          216 cmd2(int tok, int *ptok)
          217 {
          218         tree *t1, *t2, *t3;
          219 
          220         // |        cmd PIPE cmd                {$$=mung2($2, $1, $3);}
          221         t1 = cmd3(tok, &tok);
          222         while(tok == PIPE) {
          223                 t2 = yylval.tree;
          224                 t3 = cmd3(dropnl(yylex()), &tok);
          225                 t1 = mung2(t2, t1, t3);
          226         }
          227         *ptok = tok;
          228         return t1;
          229 }
          230 
          231 static tree*
          232 cmd3(int tok, int *ptok)
          233 {
          234         tree *t1, *t2, *t3, *t4;
          235 
          236         tok = dropsp(tok);
          237         switch(tok) {
          238         case ';':
          239         case '&':
          240         case '\n':
          241                 *ptok = tok;
          242                 return nil;
          243 
          244         case IF:
          245                 // |        IF paren {skipnl();} cmd        {$$=mung2($1, $2, $4);}
          246                 // |        IF NOT {skipnl();} cmd        {$$=mung1($2, $4);}
          247                 t1 = yylval.tree;
          248                 tok = dropsp(yylex());
          249                 if(tok == NOT) {
          250                         t1 = yylval.tree;
          251                         t2 = cmd(dropnl(yylex()), ptok);
          252                         return mung1(t1, t2);
          253                 }
          254                 t2 = paren(tok);
          255                 t3 = cmd(dropnl(yylex()), ptok);
          256                 return mung2(t1, t2, t3);
          257 
          258         case FOR:
          259                 // |        FOR '(' word IN words ')' {skipnl();} cmd
          260                 //                {$$=mung3($1, $3, $5 ? $5 : tree1(PAREN, $5), $8);}
          261                 // |        FOR '(' word ')' {skipnl();} cmd
          262                 //                {$$=mung3($1, $3, (tree *)0, $6);}
          263                 t1 = yylval.tree;
          264                 tok = dropsp(yylex());
          265                 if(tok != '(')
          266                         syntax(tok);
          267                 t2 = yyword(yylex(), &tok, 1);
          268                 switch(tok) {
          269                 default:
          270                         syntax(tok);
          271                 case ')':
          272                         t3 = nil;
          273                         break;
          274                 case IN:
          275                         t3 = words(yylex(), &tok);
          276                         if(t3 == nil)
          277                                 t3 = tree1(PAREN, nil);
          278                         if(tok != ')')
          279                                 syntax(tok);
          280                         break;
          281                 }
          282                 t4 = cmd(dropnl(yylex()), ptok);
          283                 return mung3(t1, t2, t3, t4);
          284 
          285         case WHILE:
          286                 // |        WHILE paren {skipnl();} cmd
          287                 //                {$$=mung2($1, $2, $4);}
          288                 t1 = yylval.tree;
          289                 t2 = paren(yylex());
          290                 t3 = cmd(dropnl(yylex()), ptok);
          291                 return mung2(t1, t2, t3);
          292 
          293         case SWITCH:
          294                 // |        SWITCH word {skipnl();} brace
          295                 //                {$$=tree2(SWITCH, $2, $4);}
          296                 t1 = yyword(yylex(), &tok, 1);
          297                 tok = dropnl(tok); // doesn't work in yacc grammar but works here!
          298                 t2 = brace(tok);
          299                 *ptok = dropsp(yylex());
          300                 return tree2(SWITCH, t1, t2);
          301 
          302         case FN:
          303                 // |        FN words brace                {$$=tree2(FN, $2, $3);}
          304                 // |        FN words                {$$=tree1(FN, $2);}
          305                 t1 = words(yylex(), &tok);
          306                 if(tok != '{') {
          307                         *ptok = tok;
          308                         return tree1(FN, t1);
          309                 }
          310                 t2 = brace(tok);
          311                 *ptok = dropsp(yylex());
          312                 return tree2(FN, t1, t2);
          313 
          314         case TWIDDLE:
          315                 // |        TWIDDLE word words        {$$=mung2($1, $2, $3);}
          316                 t1 = yylval.tree;
          317                 t2 = yyword(yylex(), &tok, 1);
          318                 t3 = words(tok, ptok);
          319                 return mung2(t1, t2, t3);
          320 
          321         case BANG:
          322         case SUBSHELL:
          323                 // |        BANG cmd                {$$=mung1($1, $2);}
          324                 // |        SUBSHELL cmd                {$$=mung1($1, $2);}
          325                 // Note: cmd2: ! x | y is !{x | y} not {!x} | y.
          326                 t1 = yylval.tree;
          327                 return mung1(t1, cmd2(yylex(), ptok));
          328 
          329         case REDIR:
          330         case DUP:
          331                 // |        redir cmd  %prec BANG        {$$=mung2($1, $1->child[0], $2);}
          332                 // Note: cmd2: {>x echo a | tr a-z A-Z} writes A to x.
          333                 t1 = yyredir(tok, &tok);
          334                 t2 = cmd2(tok, ptok);
          335                 return mung2(t1, t1->child[0], t2);
          336 
          337         case '{':
          338                 // |        brace epilog                {$$=epimung($1, $2);}
          339                 t1 = brace(tok);
          340                 tok = dropsp(yylex());
          341                 t2 = epilog(tok, ptok);
          342                 return epimung(t1, t2);
          343         }
          344 
          345         if(!iswordtok(tok)) {
          346                 *ptok = tok;
          347                 return nil;
          348         }
          349 
          350         // cmd: ...
          351         // |        simple                        {$$=simplemung($1);}
          352         // |        assign cmd %prec BANG        {$$=mung3($1, $1->child[0], $1->child[1], $2);}
          353         // assign:        first '=' word                {$$=tree2('=', $1, $3);}
          354         // Note: first is same as word except for disallowing all the leading keywords,
          355         // but all those keywords have been picked off in the switch above.
          356         // Except NOT, but disallowing that in yacc was likely a mistake anyway:
          357         // there's no ambiguity in not=1 or not x y z.
          358         t1 = yyword(tok, &tok, 0);
          359         if(tok == '=') {
          360                 // assignment
          361                 // Note: cmd2: {x=1 true | echo $x} echoes 1.
          362                 t1 = tree2('=', t1, yyword(yylex(), &tok, 1));
          363                 t2 = cmd2(tok, ptok);
          364                 return mung3(t1, t1->child[0], t1->child[1], t2);
          365         }
          366 
          367         // simple:        first
          368         // |        simple word                {$$=tree2(ARGLIST, $1, $2);}
          369         // |        simple redir                {$$=tree2(ARGLIST, $1, $2);}
          370         for(;;) {
          371                 if(tok == REDIR || tok == DUP) {
          372                         t1 = tree2(ARGLIST, t1, yyredir(tok, &tok));
          373                 } else if(iswordtok(tok)) {
          374                         t1 = tree2(ARGLIST, t1, yyword(tok, &tok, 1));
          375                 } else {
          376                         break;
          377                 }
          378         }
          379         *ptok = tok;
          380         return simplemung(t1);
          381 }
          382 
          383 static tree*
          384 words(int tok, int *ptok)
          385 {
          386         tree *t;
          387 
          388         // words:                                {$$=(tree*)0;}
          389         // |        words word                {$$=tree2(WORDS, $1, $2);}
          390 
          391         t = nil;
          392         tok = dropsp(tok);
          393         while(iswordtok(tok))
          394                 t = tree2(WORDS, t, yyword(tok, &tok, 1));
          395         *ptok = tok;
          396         return t;
          397 }
          398 
          399 static tree*
          400 yyword(int tok, int *ptok, int eqok)
          401 {
          402         tree *t;
          403 
          404         // word:        keyword                        {lastword=1; $1->type=WORD;}
          405         // |        comword
          406         // |        word '^' word                {$$=tree2('^', $1, $3);}
          407         // comword: '$' word                {$$=tree1('$', $2);}
          408         // |        '$' word SUB words ')'        {$$=tree2(SUB, $2, $4);}
          409         // |        '"' word                {$$=tree1('"', $2);}
          410         // |        COUNT word                {$$=tree1(COUNT, $2);}
          411         // |        WORD
          412         // |        '`' brace                {$$=tree1('`', $2);}
          413         // |        '(' words ')'                {$$=tree1(PAREN, $2);}
          414         // |        REDIR brace                {$$=mung1($1, $2); $$->type=PIPEFD;}
          415         // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
          416         //
          417         // factored into:
          418         //
          419         // word: word1
          420         // | word '^' word1
          421         //
          422         // word1: keyword | comword
          423 
          424         t = word1(tok, &tok);
          425         if(tok == '=' && !eqok)
          426                 goto out;
          427         for(;;) {
          428                 if(iswordtok(tok)) {
          429                         // No free carats around parens.
          430                         if(t->type == PAREN || tok == '(')
          431                                 syntax(tok);
          432                         t = tree2('^', t, word1(tok, &tok));
          433                         continue;
          434                 }
          435                 tok = dropsp(tok);
          436                 if(tok == '^') {
          437                         t = tree2('^', t, word1(yylex(), &tok));
          438                         continue;
          439                 }
          440                 break;
          441         }
          442 out:
          443         *ptok = dropsp(tok);
          444         return t;
          445 }
          446 
          447 static tree*
          448 word1(int tok, int *ptok)
          449 {
          450         tree *w, *sub, *t;
          451 
          452         tok = dropsp(tok);
          453         switch(tok) {
          454         default:
          455                 syntax(tok);
          456 
          457         case WORD:
          458         case FOR:
          459         case IN:
          460         case WHILE:
          461         case IF:
          462         case NOT:
          463         case TWIDDLE:
          464         case BANG:
          465         case SUBSHELL:
          466         case SWITCH:
          467         case FN:
          468                 // |        WORD
          469                 // keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN
          470                 t = yylval.tree;
          471                 t->type = WORD;
          472                 *ptok = yylex();
          473                 return t;
          474 
          475         case '=':
          476                 *ptok = yylex();
          477                 return token("=", WORD);
          478 
          479         case '$':
          480                 // comword: '$' word1                {$$=tree1('$', $2);}
          481                 // |        '$' word1 SUB words ')'        {$$=tree2(SUB, $2, $4);}
          482                 w = word1(yylex(), &tok);
          483                 if(tok == '(') {
          484                         sub = words(yylex(), &tok);
          485                         if(tok != ')')
          486                                 syntax(tok);
          487                         *ptok = yylex();
          488                         return tree2(SUB, w, sub);
          489                 }
          490                 *ptok = tok;
          491                 return tree1('$', w);
          492 
          493         case '"':
          494                 // |        '"' word1                {$$=tree1('"', $2);}
          495                 return tree1('"', word1(yylex(), ptok));
          496 
          497         case COUNT:
          498                 // |        COUNT word1                {$$=tree1(COUNT, $2);}
          499                 return tree1(COUNT, word1(yylex(), ptok));
          500 
          501         case '`':
          502                 // |        '`' brace                {$$=tree1('`', $2);}
          503                 t = tree1('`', brace(yylex()));
          504                 *ptok = yylex();
          505                 return t;
          506 
          507         case '(':
          508                 // |        '(' words ')'                {$$=tree1(PAREN, $2);}
          509                 t = tree1(PAREN, words(yylex(), &tok));
          510                 if(tok != ')')
          511                         syntax(tok);
          512                 *ptok = yylex();
          513                 return t;
          514 
          515         case REDIRW:
          516                 // |        REDIRW brace                {$$=mung1($1, $2); $$->type=PIPEFD;}
          517                 t = yylval.tree;
          518                 t = mung1(t, brace(yylex()));
          519                 t->type = PIPEFD;
          520                 *ptok = yylex();
          521                 return t;
          522         }
          523 }
          524 
          525 static int
          526 iswordtok(int tok)
          527 {
          528         switch(tok) {
          529         case FOR:
          530         case IN:
          531         case WHILE:
          532         case IF:
          533         case NOT:
          534         case TWIDDLE:
          535         case BANG:
          536         case SUBSHELL:
          537         case SWITCH:
          538         case FN:
          539         case '$':
          540         case '"':
          541         case COUNT:
          542         case WORD:
          543         case '`':
          544         case '(':
          545         case REDIRW:
          546         case '=':
          547                 return 1;
          548         }
          549         return 0;
          550 }