1 2

[PATCH] Partially implement arithmetic expansion

Details
Message ID
<20190131180300.5622-1-sir@cmpwn.com>
DKIM signature
pass
Download raw message
Patch: +155 -3
The only expansion missing is assignment expansion, which requires more
discussion. The problems with it are:

1. We need to get mrsh_run_expression access to the mrsh_state for
   assignment expressions to work
2. The parser does not handle many assignment expressions correctly,
   e.g. $((var+=1)) or $((var++))

Additionally, the parser is missing variable support, e.g. $((var+1)).
---
Note that this depends on Cristian's work.

 parser/arithm.c  |   2 +-
 shell/arithm.c   | 136 ++++++++++++++++++++++++++++++++++++++++++++++-
 test/arithm.sh   |  19 +++++++
 test/meson.build |   1 +
 4 files changed, 155 insertions(+), 3 deletions(-)
 create mode 100644 test/arithm.sh

diff --git a/parser/arithm.c b/parser/arithm.c
index 4bf2579..c53ebfd 100644
--- a/parser/arithm.c
+++ b/parser/arithm.c
@@ -170,7 +170,7 @@ static struct mrsh_arithm_expr *factor(struct mrsh_parser *state) {
 	enum mrsh_arithm_binop_type type;
 	if (parse_char(state, '*')) {
 		type = MRSH_ARITHM_BINOP_ASTERISK;
-	} else if (parse_char(state, '-')) {
+	} else if (parse_char(state, '/')) {
 		type = MRSH_ARITHM_BINOP_SLASH;
 	} else if (parse_char(state, '%')) {
 		type = MRSH_ARITHM_BINOP_PERCENT;
diff --git a/shell/arithm.c b/shell/arithm.c
index 093d7ea..821bff3 100644
--- a/shell/arithm.c
+++ b/shell/arithm.c
@@ -1,7 +1,139 @@
+#include <assert.h>
 #include <mrsh/shell.h>
 
+static bool mrsh_run_arithm_binop(
+		struct mrsh_arithm_binop *binop, long *result) {
+	long left, right;
+	if (!mrsh_run_arithm_expr(binop->left, &left)) {
+		return false;
+	}
+	if (!mrsh_run_arithm_expr(binop->right, &right)) {
+		return false;
+	}
+	switch (binop->type) {
+	case MRSH_ARITHM_BINOP_ASTERISK:
+		*result = left * right;
+		return true;
+	case MRSH_ARITHM_BINOP_SLASH:
+		*result = left / right;
+		return true;
+	case MRSH_ARITHM_BINOP_PERCENT:
+		*result = left % right;
+		return true;
+	case MRSH_ARITHM_BINOP_PLUS:
+		*result = left + right;
+		return true;
+	case MRSH_ARITHM_BINOP_MINUS:
+		*result = left - right;
+		return true;
+	case MRSH_ARITHM_BINOP_DLESS:
+		*result = left << right;
+		return true;
+	case MRSH_ARITHM_BINOP_DGREAT:
+		*result = left >> right;
+		return true;
+	case MRSH_ARITHM_BINOP_LESS:
+		*result = left < right;
+		return true;
+	case MRSH_ARITHM_BINOP_LESSEQ:
+		*result = left <= right;
+		return true;
+	case MRSH_ARITHM_BINOP_GREAT:
+		*result = left > right;
+		return true;
+	case MRSH_ARITHM_BINOP_GREATEQ:
+		*result = left >= right;
+		return true;
+	case MRSH_ARITHM_BINOP_DEQ:
+		*result = left == right;
+		return true;
+	case MRSH_ARITHM_BINOP_BANGEQ:
+		*result = left != right;
+		return true;
+	case MRSH_ARITHM_BINOP_AND:
+		*result = left & right;
+		return true;
+	case MRSH_ARITHM_BINOP_CIRC:
+		*result = left ^ right;
+		return true;
+	case MRSH_ARITHM_BINOP_OR:
+		*result = left | right;
+		return true;
+	case MRSH_ARITHM_BINOP_DAND:
+		*result = left && right;
+		return true;
+	case MRSH_ARITHM_BINOP_DOR:
+		*result = left || right;
+		return true;
+	}
+	assert(false && "Unknown binary arithmetic operation");
+}
+
+static bool mrsh_run_arithm_unop(
+		struct mrsh_arithm_unop *unop, long *result) {
+	long val;
+	if (!mrsh_run_arithm_expr(unop->body, &val)) {
+		return false;
+	}
+	switch (unop->type) {
+	case MRSH_ARITHM_UNOP_PLUS:;
+		/* no-op */
+		return true;
+	case MRSH_ARITHM_UNOP_MINUS:;
+		*result = -val;
+		return true;
+	case MRSH_ARITHM_UNOP_TILDE:;
+		*result = ~val;
+		return true;
+	case MRSH_ARITHM_UNOP_BANG:;
+		*result = !val;
+		return true;
+	}
+	assert(false && "Unknown unary arithmetic operation");
+}
+
+static bool mrsh_run_arithm_cond(
+		struct mrsh_arithm_cond *cond, long *result) {
+	long condition, body, _else;
+	if (!mrsh_run_arithm_expr(cond->condition, &condition)) {
+		return false;
+	}
+	if (condition) {
+		if (!mrsh_run_arithm_expr(cond->body, &body)) {
+			return false;
+		}
+		*result = body;
+	} else {
+		if (!mrsh_run_arithm_expr(cond->else_part, &_else)) {
+			return false;
+		}
+		*result = _else;
+	}
+	return true;
+}
+
 bool mrsh_run_arithm_expr(struct mrsh_arithm_expr *expr, long *result) {
-	// TODO
-	*result = 42;
+	switch (expr->type) {
+	case MRSH_ARITHM_LITERAL:;
+		struct mrsh_arithm_literal *literal =
+			(struct mrsh_arithm_literal *)expr;
+		*result = literal->value;
+		break;
+	case MRSH_ARITHM_BINOP:;
+		struct mrsh_arithm_binop *binop =
+			(struct mrsh_arithm_binop *)expr;
+		return mrsh_run_arithm_binop(binop, result);
+	case MRSH_ARITHM_UNOP:;
+		struct mrsh_arithm_unop *unop =
+			(struct mrsh_arithm_unop *)expr;
+		return mrsh_run_arithm_unop(unop, result);
+	case MRSH_ARITHM_COND:;
+		struct mrsh_arithm_cond *cond =
+			(struct mrsh_arithm_cond *)expr;
+		return mrsh_run_arithm_cond(cond, result);
+	default:
+		*result = 42;
+		break;
+	}
 	return true;
 }
diff --git a/test/arithm.sh b/test/arithm.sh
new file mode 100644
index 0000000..bb39621
--- /dev/null
+++ b/test/arithm.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+echo "2*5 ->" $((2*5))
+echo "2/5 ->" $((2/5))
+echo "2%5 ->" $((2%5))
+echo "2+5 ->" $((2+5))
+echo "2-5 ->" $((2-5))
+echo "2<<5 -> " $((2<<5))
+echo "2>>5 -> " $((2>>5))
+echo "2<5 -> " $((2<5))
+echo "2<=5 -> " $((2<=5))
+echo "2>5 -> " $((2>5))
+echo "2>=5 -> " $((2>=5))
+echo "2==5 -> " $((2==5))
+echo "2!=5 -> " $((2!=5))
+echo "2&5 -> " $((2&5))
+echo "2^5 -> " $((2^5))
+echo "2|5 -> " $((2|5))
+echo "2&&5 -> " $((2&&5))
+echo "2||5 -> " $((2||5))
diff --git a/test/meson.build b/test/meson.build
index 4f74e3d..4b4d5ed 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -4,6 +4,7 @@ ref_sh = find_program('sh', required: false)
 test_files = [
 	'conformance/if.sh',
 
+	'arithm.sh',
 	'case.sh',
 	'for.sh',
 	'function.sh',
-- 
2.20.1
Details
Message ID
<-g6wdj5rWJ409XQKKfOEQcLgDABcZwqXpz0JtmQJmX2dfgD_pA8zmnVMM3m3URdJ8wJBljbS9pAv9H_TABjghThGbJ8L1jIvy-O3YeDIDVg=@emersion.fr>
In-Reply-To
<20190131180300.5622-1-sir@cmpwn.com> (view parent)
DKIM signature
pass
Download raw message
This looks pretty good to me!

On Thursday, January 31, 2019 7:03 PM, Drew DeVault <sir@cmpwn.com> wrote:
> The only expansion missing is assignment expansion, which requires more
> discussion. The problems with it are:
>
> 1.  We need to get mrsh_run_expression access to the mrsh_state for
>     assignment expressions to work

It seems reasonable to me to change the API to allow this.

> 2.  The parser does not handle many assignment expressions correctly,
>     e.g. $((var+=1)) or $((var++))
>
>     Additionally, the parser is missing variable support, e.g. $((var+1)).

https://github.com/emersion/mrsh/issues/85

Will try to get this fixed soon™.

> Note that this depends on Cristian's work.

This has now been merged.