about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-11-08 16:24:51 +0100
committerLeah Neukirchen <leah@vuxu.org>2017-11-08 16:24:51 +0100
commit7f683af905ca5ee6d332dd0954383e8bf7d52b3c (patch)
tree9ce6eae7fadf7bf3e90529dfc2d81131dd51a3e1
parent7cad7b37eb81158b5956dbcf6bdc722b5ffa8f4f (diff)
downloadlr-7f683af905ca5ee6d332dd0954383e8bf7d52b3c.tar.gz
lr-7f683af905ca5ee6d332dd0954383e8bf7d52b3c.tar.xz
lr-7f683af905ca5ee6d332dd0954383e8bf7d52b3c.zip
add ternary (conditional) operator
-rw-r--r--NEWS.md1
-rw-r--r--README.md1
-rw-r--r--lr.11
-rw-r--r--lr.c36
4 files changed, 35 insertions, 4 deletions
diff --git a/NEWS.md b/NEWS.md
index 8fc633f..5dd0ffe 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,7 @@
 ## HEAD
 
 * Feature: new option `-B` for breadth first traversal.
+* Feature: new syntax `? :` for ternary operator.
 
 ## 1.1 (2017-10-29)
 
diff --git a/README.md b/README.md
index 3fc6e25..8d59713 100644
--- a/README.md
+++ b/README.md
@@ -140,6 +140,7 @@ Default: `n`.
 
 	<expr>     ::= <expr> || <expr>  -- disjunction
 	             | <expr> && <expr>  -- conjunction
+	             | <expr> ? <expr> : <expr>  -- ternary operator
 	             | ! <expr>          -- negation
 	             | ( <expr )
 	             | <timeprop> <numop> <dur>
diff --git a/lr.1 b/lr.1
index 8c13121..a51a95b 100644
--- a/lr.1
+++ b/lr.1
@@ -285,6 +285,7 @@ tests are given by the following EBNF:
 .Bd -literal
 <expr>     ::= <expr> || <expr>  -- disjunction
              | <expr> && <expr>  -- conjunction
+             | <expr> ? <expr> : <expr>  -- ternary operator
              | ! <expr>          -- negation
              | ( <expr )
              | <timeprop> <numop> <dur>
diff --git a/lr.c b/lr.c
index 5e82a84..f9c1ca5 100644
--- a/lr.c
+++ b/lr.c
@@ -170,6 +170,7 @@ struct fileinfo {
 enum op {
 	EXPR_OR = 1,
 	EXPR_AND,
+	EXPR_COND,
 	EXPR_NOT,
 	EXPR_LT,
 	EXPR_LE,
@@ -235,7 +236,7 @@ struct expr {
 		char *string;
 		int64_t num;
 		regex_t *regex;
-	} a, b;
+	} a, b, c;
 };
 
 static char *pos;
@@ -407,7 +408,7 @@ parse_op()
 }
 
 static struct expr *parse_cmp();
-static struct expr *parse_or();
+static struct expr *parse_cond();
 
 static struct expr *
 parse_inner()
@@ -434,7 +435,7 @@ parse_inner()
 		not->a.expr = e;
 		return not;
 	} else if (token("(")) {
-		struct expr *e = parse_or();
+		struct expr *e = parse_cond();
 		if (token(")"))
 			return e;
 		parse_error("missing ) at '%.15s'", pos);
@@ -875,10 +876,33 @@ parse_or()
 }
 
 static struct expr *
+parse_cond()
+{
+	struct expr *e1 = parse_or();
+
+	if (token("?")) {
+		struct expr *e2 = parse_or();
+		if (token(":")) {
+			struct expr *e3 = parse_cond();
+			struct expr *r = mkexpr(EXPR_COND);
+			r->a.expr = e1;
+			r->b.expr = e2;
+			r->c.expr = e3;
+
+			return r;
+		} else {
+			parse_error(": expected at '%.15s'", pos);
+		}
+	}
+
+	return e1;
+}
+
+static struct expr *
 parse_expr(const char *s)
 {
 	pos = (char *)s;
-	struct expr *e = parse_or();
+	struct expr *e = parse_cond();
 	if (*pos)
 		parse_error("trailing garbage at '%.15s'", pos);
 	return e;
@@ -1195,6 +1219,10 @@ eval(struct expr *e, struct fileinfo *fi)
 		return eval(e->a.expr, fi) || eval(e->b.expr, fi);
 	case EXPR_AND:
 		return eval(e->a.expr, fi) && eval(e->b.expr, fi);
+	case EXPR_COND:
+		return eval(e->a.expr, fi)
+		    ? eval(e->b.expr, fi)
+		    : eval(e->c.expr, fi);
 	case EXPR_NOT:
 		return !eval(e->a.expr, fi);
 	case EXPR_PRUNE: