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:
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'