~emersion/mrsh

92d0c493bc04858bcabff85140e9d69cb8dae1d6 — Drew DeVault 1 year, 10 months ago 28130e1
Partially implement arithmetic expansion

The only expansion missing is assignment expansion, which requires more
discussion.

Additionally, the parser is missing variable support, e.g. $((var+1)).
4 files changed, 167 insertions(+), 2 deletions(-)

M Makefile
M shell/arithm.c
A test/arithm.sh
M test/meson.build
M Makefile => Makefile +2 -0
@@ 19,6 19,8 @@ public_includes=\

tests=\
		test/conformance/if.sh \
		test/arithm.sh \
		test/async.sh \
		test/case.sh \
		test/command.sh \
		test/for.sh \

M shell/arithm.c => shell/arithm.c +134 -2
@@ 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:
		// TODO
		*result = 42;
		break;
	}
	return true;
}

A test/arithm.sh => test/arithm.sh +30 -0
@@ 0,0 1,30 @@
#!/bin/sh -eu

echo "1 =" $((1))
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))
# https://github.com/emersion/mrsh/issues/86
#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))

# Associativity
# https://github.com/emersion/mrsh/issues/53
echo "1+2+3 =" $((1+2+3))
#echo "5-1-2 =" $((5-1-2))
echo "1+2*3 =" $((1+2*3))
echo "2*3+1 =" $((2*3+1))
#echo "2*(3+1) =" $((2*(3+1)))

M test/meson.build => test/meson.build +1 -0
@@ 4,6 4,7 @@ ref_sh = find_program('sh', required: false)
test_files = [
	'conformance/if.sh',

	'arithm.sh',
	'async.sh',
	'case.sh',
	'command.sh',