diff options
| -rw-r--r-- | ChangeLog | 6 | ||||
| -rw-r--r-- | develop.c | 3 | ||||
| -rw-r--r-- | footobar.c | 1 | ||||
| -rw-r--r-- | lex.c | 1 | ||||
| -rw-r--r-- | parse.y | 4 | ||||
| -rw-r--r-- | rc.h | 4 | ||||
| -rw-r--r-- | tree.c | 44 | ||||
| -rw-r--r-- | trip.rc | 12 | ||||
| -rw-r--r-- | walk.c | 15 |
9 files changed, 80 insertions, 10 deletions
@@ -912,8 +912,14 @@ 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). 2018-03-20 @@ -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; @@ -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; @@ -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; @@ -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"; } @@ -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 *); @@ -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,7 +64,7 @@ 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; @@ -60,6 +98,8 @@ extern Node *mk(int /*nodetype*/ t,...) { break; } n->type = t; + + n = desugar_ifnot(n); va_end(ap); return n; } @@ -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' @@ -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; |