#include "rc.h" /* TODO: better error messages */ // ----------------------------------------------------------------------- // global data static int lastt, nextt=EOF; static Tree *node; /* if token was lexed as a tree node (redirs and words), its here */ /* anything that is not listed will automatically terminate parsing the given command */ static uchar prectab[256] = { [Tif] = 1, [Tfor] = 1, [Tswitch] = 1, /* NOTE: we give else lower precedence than if [Telse] = 1, */ [Tandand] = 2, [Toror] = 2, [Tbang] = 3, [Tsubshell] = 3, [Tpipe] = 4, [Tcarot] = 5, [Tdol] = 6, [Tcount] = 6, [Tquote] = 6, [Tsub] = 7, }; // ----------------------------------------------------------------------- // helpers static int lookahead(void) { int tok; if (nextt != EOF) return nextt; tok = lex(&node); return nextt = tok; } static int advance(void) { int tok = lookahead(); lastt = nextt, nextt = EOF; node = nil; return tok; } static int nextis(int tok) { if (lookahead() == tok) { advance(); return 1; } return 0; } // ----------------------------------------------------------------------- // subparsers /* forward declarations */ static Tree *word(void); static Tree *words(void); static Tree* body(int c); static Tree *comword(void); static Tree *cmd(int prec); /* implementations */ /* * TODO: * i don't like all this branching. * think of a better way */ static Tree* case_or_cmd(int c) { Tree *t; if (!c || !nextis(Tcase)) return cmd(1); t = words(); if (!nextis(';') && !nextis('\n')) rcerror("case: missing terminator: recieved %d", nextt); t = tree2(Tcase, t, body(0)); pfmt(errio, "%t\n", t); return t; } static Tree* body(int c) { int tok; Tree *l, *r; skipnl(); l = case_or_cmd(c); loop: switch((tok=lookahead())){ case '&': l = tree1('&', l); /* fallthrough */ case ';': case '\n': advance(); /* fallthrough */ case Tcase: if ((r = case_or_cmd(c))) { l = tree2(';', l, r); goto loop; } /* fallthrough */ default: ; } return l; } static Tree* brace(int c) { Tree *t; if (!nextis('{')) rcerror("brace: expected { found: %c", nextt); t = tree1(Tbrace, body(c)); if (!nextis('}')) rcerror("brace: expected } found: %c", nextt); return t; } static Tree* paren(void) { Tree *t; if (!nextis('(')) rcerror("not a paren"); t = tree1(Tparen, body(0)); if (!nextis(')')) rcerror("unmatched paren"); return t; } /* TODO: fill in */ static Tree* heredoc(Tree* t) { return t; } static Tree* redir(void) { int tok; Tree *t; switch (tok = lookahead()) { case Tdup: t = node; advance(); break; case Tredir: t = node; advance(); t = hang1(t, (t->redir.type == Rhere) ? heredoc(word()) : word()); break; default: t = nil; } return t; } static Tree* epilog(void) { Tree *t, *tt; t = redir(); while((tt = redir())) t = hang2(t, t->child[0], tt); return t; } static Tree* sword(void) { int tok; if (Kstart < (tok=lookahead()) && tok < Kend) return node; return comword(); } static Tree* word(void) { int tok; Tree *t; t = sword(); while(nextis('^')) t = tree2('^', t, sword()); return t; } static Tree* words(void) { Tree *t, *tt; t = word(); while((tt=word())) t = tree2(Twords, t, tt); return t; } /* * NOTE: we divergence from Duff's yacc grammar here. * he has [dol|count|"]->word, we have [dol|count]->sword * calling sword ensures we don't cat strings * this was done in Tom's version by setting precedence */ static Tree* comword(void) { int tok; Tree *t, *tt; switch(tok=lookahead()){ case Tdol: advance(); t = sword(); if(nextis('(')) { t = tree2(Tsub, t, words()); if (!nextis(')')) rcerror("malformed index expression"); } return tree1(Tdol, t); case Tquote: return tree1(Tquote, sword()); case Tcount: advance(); return tree1(Tcount, sword()); case Ttick: advance(); return tree1(Ttick, brace(0)); case Tlparen: return paren(); case Tredir: advance(); t = hang1(node, brace(0)); t->type = Tpipefd; return t; case Tword: t = node; advance(); return t; } return nil; } static Tree* first(void) { int tok; Tree *t; t = comword(); while(nextis('^')) { t = tree2('^', t, word()); } return t; } /* simple _or_ assignment */ static Tree* simple_or_assign(void) { int tok; Tree *t, *tt; /* can't continue */ if (!(t = first())) return nil; /* is an assignment */ assign: if(nextis('=')) { tt = word(); return tree3(Teq, t, tt, cmd(prectab[Tbang])); } /* is a 'simple' */ simple: switch ((tok=lookahead())) { case Tredir: case Tdup: t = tree2(Targs, t, redir()); goto simple; default: if ((tt = word())) { t = tree2(Targs, t, tt); goto simple; } /* fallthrough */ } return simplehang(t); } static Tree* opand(void) { int tok; Tree *t, *tt; switch(tok=lookahead()) { case Tif: advance(); t = paren(); skipnl(); tt = cmd(prectab[Tif]); if (nextis(Telse)) { skipnl(); t = tree3(Tif, t, tt, cmd(prectab[Tif])); } else t = tree3(Tif, t, tt, nil); return t; case Telse: rcerror("invalid hanging else"); break; case Tswitch: advance(); t = word(); skipnl(); tt = brace(1); t = tree2(Tswitch, t, tt); return t; case Tfor: advance(); if (!nextis('(')) rcerror("for: missing opening paren"); t = word(); if (nextis(Tin)) { advance(); tt = words(); t = tree3(Tfor, t, tt, nil); } else t = tree3(Tfor, t, nil, nil); if (!nextis(')')) rcerror("for: missing closing paren"); skipnl(); tt = cmd(prectab[Tfor]); t->child[2] = tt; return t; case Twhile: advance(); t = paren(); skipnl(); tt = cmd(1); return tree2(Twhile, t, tt); case Tfunc: advance(); t = words(); if ((tok=lookahead()) == '{') { tt = brace(0); t = tree2(Tfunc, t, tt); } else t = tree1(Tfunc, t); return t; case Tsubshell: advance(); t = tree1(Tsubshell, cmd(prectab[Tsubshell])); return t; case Tbang: advance(); t = tree1(Tbang, cmd(prectab[Tbang])); return t; case Ttwiddle: advance(); tt = word(); t = tree2(Ttwiddle, tt, words()); return t; case Tlbrace: t = brace(0); tt = epilog(); return epihang(t, tt); case Tredir: /* fallthrough */ case Tdup: t = redir(); tt = cmd(prectab[Tbang]); t = hang2(t, t->child[0], tt); return t; } return simple_or_assign(); } static Tree * cmd(int prec) { int np, tok; Tree *l, *r, *p; if (!(l = opand())) return nil; for(;;) { tok = lookahead(); np = prectab[tok]; if (np < prec) break; p = node; advance(); r = cmd(np+1); if (tok == Tpipe) l = hang2(p, l, r); else l = tree2(tok, l, r); } return l; } // ----------------------------------------------------------------------- // main function int parse(void) { int tok; Tree *t, *tt; t = cmd(1); loop: switch(tok=lookahead()) { case '&': t = tree1('&', t); /* fallthrough */ case ';': advance(); tt = cmd(1); t = tree2(';', t, tt); goto loop; case '\n': advance(); case EOF: pfmt(errio, "%t\n", t); break; default: if (tok > 0x20) rcerror("unrecognized token: %c[%d]", tok, tok); else rcerror("unrecognized token: %d", tok, tok); } return tok != EOF; }