~emersion/mrsh

ref: 41ead0f046b94515afa805ed67da4e82d38b028f mrsh/shell/arithm.c -rw-r--r-- 6.1 KiB
41ead0f0Simon Ser build: disable -Wformat-overflow 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#include <assert.h>
#include <mrsh/shell.h>
#include <stdlib.h>

static bool run_variable(struct mrsh_state *state, const char *name, long *val,
		uint32_t *attribs) {
	const char *str = mrsh_env_get(state, name, attribs);
	if (str == NULL) {
		if ((state->options & MRSH_OPT_NOUNSET)) {
			fprintf(stderr, "%s: %s: unbound variable\n",
					state->frame->argv[0], name);
			return false;
		}
		*val = 0; // POSIX is not clear what to do in this case
	} else {
		char *end;
		*val = strtod(str, &end);
		if (end == str || end[0] != '\0') {
			fprintf(stderr, "%s: %s: not a number: %s\n",
					state->frame->argv[0], name, str);
			return false;
		}
	}
	return true;
}

static bool run_arithm_binop(struct mrsh_state *state,
		struct mrsh_arithm_binop *binop, long *result) {
	long left, right;
	if (!mrsh_run_arithm_expr(state, binop->left, &left)) {
		return false;
	}
	if (!mrsh_run_arithm_expr(state, binop->right, &right)) {
		return false;
	}
	switch (binop->type) {
	case MRSH_ARITHM_BINOP_ASTERISK:
		*result = left * right;
		return true;
	case MRSH_ARITHM_BINOP_SLASH:
		if (right == 0) {
			fprintf(stderr, "%s: division by zero: %ld/%ld\n",
				state->frame->argv[0], left, right);
			return false;
		}
		*result = left / right;
		return true;
	case MRSH_ARITHM_BINOP_PERCENT:
		if (right == 0) {
			fprintf(stderr, "%s: division by zero: %ld%%%ld\n",
				state->frame->argv[0], left, right);
			return false;
		}
		*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 run_arithm_unop(struct mrsh_state *state,
		struct mrsh_arithm_unop *unop, long *result) {
	long val;
	if (!mrsh_run_arithm_expr(state, 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 run_arithm_cond(struct mrsh_state *state,
		struct mrsh_arithm_cond *cond, long *result) {
	long condition;
	if (!mrsh_run_arithm_expr(state, cond->condition, &condition)) {
		return false;
	}
	if (condition) {
		if (!mrsh_run_arithm_expr(state, cond->body, result)) {
			return false;
		}
	} else {
		if (!mrsh_run_arithm_expr(state, cond->else_part, result)) {
			return false;
		}
	}
	return true;
}

static long run_arithm_assign_op(enum mrsh_arithm_assign_op op,
		long cur, long val, long *result) {
	switch (op) {
	case MRSH_ARITHM_ASSIGN_NONE:
		*result = val;
		return true;
	case MRSH_ARITHM_ASSIGN_ASTERISK:
		*result = cur * val;
		return true;
	case MRSH_ARITHM_ASSIGN_SLASH:
		if (val == 0) {
			fprintf(stderr, "division by zero: %ld/%ld\n", cur, val);
			return false;
		}
		*result = cur / val;
		return true;
	case MRSH_ARITHM_ASSIGN_PERCENT:
		if (val == 0) {
			fprintf(stderr, "division by zero: %ld%%%ld\n", cur, val);
			return false;
		}
		*result = cur % val;
		return true;
	case MRSH_ARITHM_ASSIGN_PLUS:
		*result = cur + val;
		return true;
	case MRSH_ARITHM_ASSIGN_MINUS:
		*result = cur - val;
		return true;
	case MRSH_ARITHM_ASSIGN_DLESS:
		*result = cur << val;
		return true;
	case MRSH_ARITHM_ASSIGN_DGREAT:
		*result = cur >> val;
		return true;
	case MRSH_ARITHM_ASSIGN_AND:
		*result = cur & val;
		return true;
	case MRSH_ARITHM_ASSIGN_CIRC:
		*result = cur ^ val;
		return true;
	case MRSH_ARITHM_ASSIGN_OR:
		*result = cur | val;
		return true;
	}
	assert(false);
}

static bool run_arithm_assign(struct mrsh_state *state,
		struct mrsh_arithm_assign *assign, long *result) {
	long val;
	if (!mrsh_run_arithm_expr(state, assign->value, &val)) {
		return false;
	}
	long cur = 0;
	uint32_t attribs = MRSH_VAR_ATTRIB_NONE;
	if (assign->op != MRSH_ARITHM_ASSIGN_NONE) {
		if (!run_variable(state, assign->name, &cur, &attribs)) {
			return false;
		}
	}
	if (!run_arithm_assign_op(assign->op, cur, val, result)) {
		return false;
	}

	char buf[32];
	snprintf(buf, sizeof(buf), "%ld", *result);
	mrsh_env_set(state, assign->name, buf, attribs);

	return true;
}

bool mrsh_run_arithm_expr(struct mrsh_state *state,
		struct mrsh_arithm_expr *expr, long *result) {
	switch (expr->type) {
	case MRSH_ARITHM_LITERAL:;
		struct mrsh_arithm_literal *literal =
			(struct mrsh_arithm_literal *)expr;
		*result = literal->value;
		return true;
	case MRSH_ARITHM_VARIABLE:;
		struct mrsh_arithm_variable *variable =
			(struct mrsh_arithm_variable *)expr;
		return run_variable(state, variable->name, result, NULL);
	case MRSH_ARITHM_BINOP:;
		struct mrsh_arithm_binop *binop =
			(struct mrsh_arithm_binop *)expr;
		return run_arithm_binop(state, binop, result);
	case MRSH_ARITHM_UNOP:;
		struct mrsh_arithm_unop *unop =
			(struct mrsh_arithm_unop *)expr;
		return run_arithm_unop(state, unop, result);
	case MRSH_ARITHM_COND:;
		struct mrsh_arithm_cond *cond =
			(struct mrsh_arithm_cond *)expr;
		return run_arithm_cond(state, cond, result);
	case MRSH_ARITHM_ASSIGN:;
		struct mrsh_arithm_assign *assign =
			(struct mrsh_arithm_assign *)expr;
		return run_arithm_assign(state, assign, result);
	}
	assert(false);
}