From 6f2cac23a7e759c464ef52103fac929e1eeb6b10 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Sat, 9 Oct 2021 10:30:50 -0700 Subject: feat(rc): added shell --- sys/cmd/rc/parse.c | 496 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) create mode 100644 sys/cmd/rc/parse.c (limited to 'sys/cmd/rc/parse.c') diff --git a/sys/cmd/rc/parse.c b/sys/cmd/rc/parse.c new file mode 100644 index 0000000..b61ac3c --- /dev/null +++ b/sys/cmd/rc/parse.c @@ -0,0 +1,496 @@ +#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; +} -- cgit v1.2.1