summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--develop.c3
-rw-r--r--footobar.c1
-rw-r--r--lex.c1
-rw-r--r--parse.y4
-rw-r--r--rc.h4
-rw-r--r--tree.c46
-rw-r--r--trip.rc12
-rw-r--r--walk.c15
9 files changed, 81 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 8f2726d..7d81d82 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -912,6 +912,12 @@ Changes since rc-1.5b2
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`.
+
Feature: default values for $nl and $tab (github #43).
diff --git a/develop.c b/develop.c
index 0ebec7f..0bffde2 100644
--- a/develop.c
+++ b/develop.c
@@ -118,6 +118,9 @@ void dump(Node *n, int indent) {
case nIf:
dump_2("nIf", n, indent);
break;
+ case nIfnot:
+ dump_1("nIfnot", n, indent);
+ break;
case nLappend:
dump_2("nLappend", n, indent);
break;
diff --git a/footobar.c b/footobar.c
index d6f6dfd..0af2891 100644
--- a/footobar.c
+++ b/footobar.c
@@ -67,6 +67,7 @@ static bool Tconv(Format *f, int ignore) {
case nElse: fmtprint(f, "{%T}else %T", n->u[0].p, n->u[1].p); break;
case nNewfn: fmtprint(f, "fn %T {%T}", n->u[0].p, n->u[1].p); break;
case nIf: fmtprint(f, "if(%T)%T", n->u[0].p, n->u[1].p); break;
+ case nIfnot: fmtprint(f, "if not %T", n->u[0].p); break;
case nOrelse: fmtprint(f, "%T||%T", n->u[0].p, n->u[1].p); break;
case nArgs: fmtprint(f, "%T %T", n->u[0].p, n->u[1].p); break;
case nSwitch: fmtprint(f, "switch(%T){%T}", n->u[0].p, n->u[1].p); break;
diff --git a/lex.c b/lex.c
index 195e8f6..58c3df5 100644
--- a/lex.c
+++ b/lex.c
@@ -159,6 +159,7 @@ top: while ((c = gchar()) == ' ' || c == '\t')
if (*buf == 'f' && buf[1] == 'n') return FN;
if (*buf == 'i' && buf[1] == 'n') return IN;
}
+ if (streq(buf, "not")) return NOT;
if (streq(buf, "for")) return FOR;
if (streq(buf, "else")) return ELSE;
if (streq(buf, "switch")) return SWITCH;
diff --git a/parse.y b/parse.y
index 55f7e80..b3f1255 100644
--- a/parse.y
+++ b/parse.y
@@ -13,7 +13,7 @@ static Node *star, *nolist;
Node *parsetree; /* not using yylval because bison declares it as an auto */
%}
-%token ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN
+%token ANDAND BACKBACK BANG CASE COUNT DUP ELSE END FLAT FN FOR IF IN NOT
%token OROR PIPE REDIR SREDIR SUB SUBSHELL SWITCH TWIDDLE WHILE WORD HUH
%left '^' '='
@@ -106,6 +106,7 @@ cmd : /* empty */ %prec WHILE { $$ = NULL; }
| simple
| brace epilog { $$ = mk(nBrace,$1,$2); }
| IF paren optnl iftail { $$ = mk(nIf,$2,$4); }
+ | IF NOT optnl cmd { $$ = mk(nIfnot,$4); }
| FOR '(' word IN words ')' optnl cmd { $$ = mk(nForin,$3,$5,$8); }
| FOR '(' word ')' optnl cmd { $$ = mk(nForin,$3,star,$6); }
| WHILE paren optnl cmd { $$ = mk(nWhile,$2,$4); }
@@ -158,6 +159,7 @@ keyword : FOR { $$ = "for"; }
| IN { $$ = "in"; }
| WHILE { $$ = "while"; }
| IF { $$ = "if"; }
+ | NOT { $$ = "not"; }
| SWITCH { $$ = "switch"; }
| FN { $$ = "fn"; }
| ELSE { $$ = "else"; }
diff --git a/rc.h b/rc.h
index 82e7d84..7ae0693 100644
--- a/rc.h
+++ b/rc.h
@@ -36,7 +36,7 @@ typedef union Edata Edata;
typedef enum nodetype {
nAndalso, nAssign, nBackq, nBang, nBody, nCbody, nNowait, nBrace,
nConcat, nCount, nElse, nFlat, nDup, nEpilog, nNewfn, nForin, nIf,
- nOrelse, nPipe, nPre, nRedir, nRmfn, nArgs, nSubshell, nCase,
+ nIfnot, nOrelse, nPipe, nPre, nRedir, nRmfn, nArgs, nSubshell, nCase,
nSwitch, nMatch, nVar, nVarsub, nWhile, nWord, nLappend, nNmpipe
} nodetype;
@@ -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
index 60b42e3..d167a7c 100644
--- 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);
@@ -26,11 +64,11 @@ extern Node *mk(int /*nodetype*/ t,...) {
break;
case nBang: case nNowait:
case nCount: case nFlat: case nRmfn: case nSubshell:
- case nVar: case nCase:
+ case nVar: case nCase: case nIfnot:
n = nalloc(offsetof(Node, u[1]));
n->u[0].p = va_arg(ap, Node *);
break;
- case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
+ case nAndalso: case nAssign: case nBackq: case nBody: case nBrace: case nConcat:
case nElse: case nEpilog: case nIf: case nNewfn: case nCbody:
case nOrelse: case nPre: case nArgs: case nSwitch:
case nMatch: case nVarsub: case nWhile: case nLappend:
@@ -60,6 +98,8 @@ extern Node *mk(int /*nodetype*/ t,...) {
break;
}
n->type = t;
+
+ n = desugar_ifnot(n);
va_end(ap);
return n;
}
diff --git a/trip.rc b/trip.rc
index bb5643f..c89d89a 100644
--- a/trip.rc
+++ b/trip.rc
@@ -17,8 +17,11 @@ fn expect {
fn submatch {
if (!~ $#* 3)
fail incorrect invocation of submatch
- prompt=$nl if (!~ `` $nl {$rc -ic $1>[2=1]} $2)
+ got = `` $nl { prompt=$nl $rc -ic $1>[2=1] }
+ if (!~ $got $2) {
+ echo got $got expected $2
fail $3
+ }
}
fn sigexit sigint sigquit sigsegv
fn sigexit {
@@ -675,7 +678,7 @@ if (!~ $^L 'a c e f') {
}
# test support for unquoted =
-submatch 'echo foo = bar' 'foo = bar' 'unquoted equals 2'
+submatch 'echo foo = bar' 'foo = bar' 'unquoted equals 1'
submatch 'echo foo=bar' 'foo=bar' 'unquoted equals 2'
submatch 'echo foo=' 'foo=' 'unquoted equals 3'
submatch 'echo =bar; whatis -v echo' 'echo=bar' 'unquoted equals 4'
@@ -701,6 +704,11 @@ if (~ $status 0) {
fail '"rc -e" exits with zero status when backquote command returns non-zero'
}
+# exercise "if not"
+submatch 'if (false) echo foo; if not echo bar' 'bar' 'if not 1'
+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'
submatch 'flag a b c' 'usage: flag f [ + | - ]' 'flag 3 args'
diff --git a/walk.c b/walk.c
index cb734fe..b9218ca 100644
--- a/walk.c
+++ b/walk.c
@@ -18,6 +18,7 @@ static bool isallpre(Node *);
static bool dofork(bool);
static void dopipe(Node *);
static void loop_body(Node* n);
+static int if_state = 2; /* last if, for "if not" interactive top-level */
/* Tail-recursive version of walk() */
@@ -33,6 +34,7 @@ top: sigchk();
set(TRUE);
return TRUE;
}
+ if (parent && n->type != nIfnot) if_state = 2;
switch (n->type) {
case nArgs: case nBackq: case nConcat: case nCount:
case nFlat: case nLappend: case nRedir: case nVar:
@@ -96,10 +98,17 @@ top: sigchk();
true_cmd = true_cmd->u[0].p;
}
cond = TRUE;
- if (!walk(n->u[0].p, TRUE))
- true_cmd = false_cmd; /* run the else clause */
+ if_state = walk(n->u[0].p, TRUE);
cond = oldcond;
- WALK(true_cmd, parent);
+ WALK(if_state ? true_cmd : false_cmd, parent);
+ }
+ case nIfnot: {
+ if (if_state == 2)
+ rc_error("`if not' must follow `if'");
+ if (if_state == 0)
+ walk(n->u[0].p, TRUE);
+ if_state = 2;
+ break;
}
case nWhile: {
Jbwrap break_jb;