diff options
-rw-r--r-- | man/mpick.1 | 11 | ||||
-rw-r--r-- | mpick.c | 114 |
2 files changed, 121 insertions, 4 deletions
diff --git a/man/mpick.1 b/man/mpick.1 index 744fa24..53d5120 100644 --- a/man/mpick.1 +++ b/man/mpick.1 @@ -107,6 +107,8 @@ tests are given by the following EBNF: | prune -- do not match further messages in thread | print -- always true value | skip -- always false value + | <let> + | <ident> <flagprop> ::= child | draft | flagged | info | new | parent | passed | replied | seen | selected | trashed @@ -149,6 +151,15 @@ tests are given by the following EBNF: <str> ::= " ([^"] | "")+ " -- use "" for a single " inside " | $[A-Za-z0-9_]+ -- environment variable + +-- let expressions evaluate the expression following the `in` keyword, +-- the bindings are lazily evaluated. +<let> ::= { let <ident> = <scope> } in <scope> + +-- Inside the scope previously defined idents are replaced with expressions +<scope> ::= <expr> + +<ident> ::= [A-Za-z_][A-Za-z0-9_]+ .Ed .Sh EXIT STATUS .Ex -std diff --git a/mpick.c b/mpick.c index 71e27ee..3dc6660 100644 --- a/mpick.c +++ b/mpick.c @@ -162,6 +162,21 @@ struct pos { size_t linenr; }; +struct binding { + char *name; + size_t nlen; + size_t refcount; + struct expr *expr; + struct binding *next; +}; + +struct scope { + struct binding *bindings; + struct scope *prev; +}; + +struct scope *scopeq = NULL; + static struct thread *thr; static char *argv0; @@ -374,10 +389,103 @@ parse_op() return 0; } -static struct expr *parse_cmp(); +static int +parse_ident(char **sp, size_t *lp) +{ + char *p = pos; + if (!isalpha(*pos) && *pos != '_') + return 0; + p++; + while (*p && (isalnum(*p) || *p == '_')) + p++; + *sp = pos; + *lp = p-pos; + pos = p; + ws(); + return 1; +} + +static struct expr * +parse_binding() +{ + struct scope *sc; + struct binding *b; + char *s; + size_t l = 0; + + if (parse_ident(&s, &l)) { + for (sc = scopeq; sc; sc = sc->prev) { + for (b = sc->bindings; b; b = b->next) { + if (b->nlen == l && strncmp(b->name, s, l) == 0) { + b->refcount++; + return b->expr; + } + } + } + } + // back to the start of the ident if there was one + pos = pos-l; + parse_error_at(NULL, "unknown expression"); + return 0; +} + static struct expr *parse_cond(); static struct expr * +parse_let() +{ + if (!token("let")) + return parse_binding(); + + struct scope *sc; + char *s; + size_t l; + + sc = xcalloc(1, sizeof (struct scope)); + sc->prev = scopeq; + scopeq = sc; + + struct binding *b, *bq; + struct expr *e; + bq = NULL; + for (;;) { + if (!parse_ident(&s, &l)) + parse_error_at(NULL, "missing ident"); + if (!token("=")) + parse_error_at(NULL, "missing ="); + e = parse_cond(); + b = xcalloc(1, sizeof (struct binding)); + b->name = s; + b->nlen = l; + b->expr = e; + if (!sc->bindings) sc->bindings = b; + if (bq) bq->next = b; + bq = b; + if (!token("let")) + break; + } + if (!token("in")) + parse_error_at(NULL, "missing `in`"); + + e = parse_cond(); + + struct binding *bs; + for (b = sc->bindings; b; b = bs) { + bs = b->next; + if (b->refcount == 0) + freeexpr(b->expr); + free(b); + } + + scopeq = sc->prev; + free(sc); + + return e; +} + +static struct expr *parse_cmp(); + +static struct expr * parse_inner() { if (token("prune")) { @@ -403,10 +511,8 @@ parse_inner() return e; parse_error_at(&savepos, "unterminated ("); return 0; - } else { - parse_error("unknown expression at '%.15s'", pos); - return 0; } + return parse_let(); } static int |