rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit 7a643120366f1ea24eac5ccb759d4df616e7789b
parent 2dd0aa249c9bac4e5c616d1f386dc4271fdbab46
Author: Toby Goodwin <toby@paccrat.org>
Date:   Mon, 19 Mar 2018 20:55:56 +0000

implement desugaring version (again, lost due to git problem)

Diffstat:
MChangeLog | 8++++++++
Mrc.h | 2+-
Mtree.c | 47++++++++++++++++++++++++++++++++++++++++-------
Mtrip.rc | 3++-
4 files changed, 51 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -911,3 +911,11 @@ Changes since rc-1.5b2 Bug: the grammar disallowed local assignments and redirections in the true branch of `if` when there was an `else` clause (thanks Dražen Borković and Bert Münnich; github #31). + +2018-03-17 + + Feature: implement the `flag` builtin. + +2018-03-19 + + Feature: implement `if not`. diff --git a/rc.h b/rc.h @@ -371,7 +371,7 @@ extern volatile sig_atomic_t slow; /* tree.c */ -extern Node *mk(int /*nodetype*/,...); +extern Node *mk(enum nodetype, ...); extern Node *treecpy(Node *, void *(*)(size_t)); extern void treefree(Node *); diff --git a/tree.c b/tree.c @@ -2,9 +2,47 @@ #include "rc.h" +/* convert if followed by ifnot to else */ +static Node *desugar_ifnot(Node *n) { + if (n->type == nBody && n->u[1].p && n->u[1].p->type == nIfnot) { + /* (body (if c x) (if-not y)) => (body (if c (else x y))) */ + if (n->u[0].p->type == nIf) { + Node *yes = n->u[0].p; + Node *no = n->u[1].p; + Node *els = nalloc(offsetof(Node, u[2])); + els->type = nElse; + els->u[1].p = no->u[0].p; + els->u[0].p = yes->u[1].p; + yes->u[1].p = els; + n->u[1].p = NULL; + } else goto fail; + } else if (n->type == nBody && + n->u[1].p && n->u[1].p->type == nBody && + n->u[1].p->u[0].p && + n->u[1].p->u[0].p->type == nIfnot) { + /* (body (if c x) (body (if-not y) z)) => + (body (if c (else x y)) z) */ + if (n->u[0].p->type == nIf) { + Node *yes = n->u[0].p; + Node *no = n->u[1].p->u[0].p; + Node *els = nalloc(offsetof(Node, u[2])); + els->type = nElse; + els->u[1].p = no->u[0].p; + els->u[0].p = yes->u[1].p; + yes->u[1].p = els; + n->u[1].p = n->u[1].p->u[1].p; + } else goto fail; + } + + return n; +fail: + rc_error("`if not' must follow `if'"); + return NULL; +} + /* make a new node, pass it back to yyparse. Used to generate the parsetree. */ -extern Node *mk(int /*nodetype*/ t,...) { +extern Node *mk(enum nodetype t,...) { va_list ap; Node *n; va_start(ap, t); @@ -60,13 +98,8 @@ extern Node *mk(int /*nodetype*/ t,...) { break; } n->type = t; - if (t == nBody && n->u[1].p && n->u[1].p->type == nIfnot) { - if (n->u[0].p->type == nIf) { - fprint(2, "here i am!\n"); - } else - rc_error("`if not' must follow `if'"); - } + n = desugar_ifnot(n); va_end(ap); return n; } diff --git a/trip.rc b/trip.rc @@ -706,7 +706,8 @@ if (~ $status 0) { # exercise "if not" submatch 'if (false) echo foo; if not echo bar' 'bar' 'if not 1' -submatch 'if (false) echo foo; echo qux; if not echo bar' 'rc: `if not'' must follow `if''' 'if not 2' +submatch 'if (false) echo -n foo; if not echo -n bar; echo qux' 'barqux' 'if not 2' +submatch 'if (false) echo foo; echo qux; if not echo bar' 'rc: `if not'' must follow `if''' 'if not 3' # exercise flag builtin submatch 'flag' 'rc: not enough arguments to flag' 'flag no args'