aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/rc/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cmd/rc/parse.c')
-rw-r--r--sys/cmd/rc/parse.c496
1 files changed, 496 insertions, 0 deletions
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;
+}