blob: 961973fdd68df930267ab66050d27103f49d7a90 [file] [log] [blame]
Gavin Howard6556eed2018-02-27 17:10:27 -07001/*
Gavin Howardb5904bf2018-02-20 13:28:18 -07002 * *****************************************************************************
Gavin Howard5715b042018-02-12 16:11:42 -07003 *
Gavin Howardb5904bf2018-02-20 13:28:18 -07004 * Copyright 2018 Gavin D. Howard
Gavin Howard5715b042018-02-12 16:11:42 -07005 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 *
Gavin Howardb5904bf2018-02-20 13:28:18 -070017 * *****************************************************************************
Gavin Howard5715b042018-02-12 16:11:42 -070018 *
19 * Code to execute bc programs.
20 *
21 */
22
Gavin Howard27fdfb92018-03-21 07:56:59 -060023#include <assert.h>
Gavin Howard8a596d42018-01-15 15:46:01 -070024#include <stdint.h>
25#include <stdlib.h>
26#include <string.h>
27
Gavin Howardd525df82018-02-22 13:34:29 -070028#include <limits.h>
Gavin Howard82cdc6f2018-02-20 14:23:19 -070029#include <unistd.h>
Gavin Howard8a596d42018-01-15 15:46:01 -070030
Gavin Howarda54cabf2018-03-28 14:50:33 -060031#include <io.h>
Gavin Howard13ccc4a2018-02-15 14:09:41 -070032#include <program.h>
33#include <parse.h>
Gavin Howard29493062018-03-20 19:57:37 -060034#include <bc.h>
Gavin Howard8a596d42018-01-15 15:46:01 -070035
Gavin Howard3f68df72018-03-22 20:30:27 -060036BcStatus bc_program_search(BcProgram *p, BcResult *result,
Gavin Howard375263e2018-09-04 15:10:18 -060037 BcNum **ret, bool var)
Gavin Howard3affc802018-02-27 19:57:38 -070038{
39 BcStatus status;
Gavin Howard4b9bb2b2018-03-23 08:11:11 -060040 BcEntry entry, *entry_ptr;
Gavin Howard3affc802018-02-27 19:57:38 -070041 BcVec *vec;
42 BcVecO *veco;
Gavin Howard42d7c6d2018-03-20 14:09:01 -060043 size_t idx, ip_idx;
Gavin Howard57323432018-03-14 20:06:35 -060044 BcAuto *a;
Gavin Howard37296462018-09-07 15:57:03 -060045 // We use this because it has a union of BcNum and BcVec.
46 BcResult data;
Gavin Howard3affc802018-02-27 19:57:38 -070047
Gavin Howard42d7c6d2018-03-20 14:09:01 -060048 for (ip_idx = 0; ip_idx < p->stack.len - 1; ++ip_idx) {
Gavin Howard3affc802018-02-27 19:57:38 -070049
Gavin Howard97eb8562018-03-21 08:04:29 -060050 BcFunc *func;
Gavin Howardce8fb792018-09-04 15:08:36 -060051 BcInstPtr *ip = bc_vec_item_rev(&p->stack, ip_idx);
Gavin Howard97eb8562018-03-21 08:04:29 -060052
Gavin Howard42d7c6d2018-03-20 14:09:01 -060053 if (ip->func == BC_PROGRAM_READ || ip->func == BC_PROGRAM_MAIN) continue;
Gavin Howard3affc802018-02-27 19:57:38 -070054
55 func = bc_vec_item(&p->funcs, ip->func);
56
Gavin Howard3898d192018-03-14 20:44:33 -060057 for (idx = 0; idx < func->autos.len; ++idx) {
Gavin Howard4b9bb2b2018-03-23 08:11:11 -060058
Gavin Howard3898d192018-03-14 20:44:33 -060059 a = bc_vec_item(&func->autos, idx);
Gavin Howard4b9bb2b2018-03-23 08:11:11 -060060
Gavin Howard3898d192018-03-14 20:44:33 -060061 if (!strcmp(a->name, result->data.id.name)) {
Gavin Howard3affc802018-02-27 19:57:38 -070062
Gavin Howard375263e2018-09-04 15:10:18 -060063 BcResult *r = bc_vec_item(&p->results, ip->len + idx);
Gavin Howard3898d192018-03-14 20:44:33 -060064
Gavin Howard39b64082018-09-02 00:10:18 -060065 if (!a->var != !var) return BC_STATUS_EXEC_BAD_TYPE;
Gavin Howard375263e2018-09-04 15:10:18 -060066 *ret = &r->data.num;
Gavin Howard3898d192018-03-14 20:44:33 -060067
68 return BC_STATUS_SUCCESS;
69 }
Gavin Howard57323432018-03-14 20:06:35 -060070 }
Gavin Howard3affc802018-02-27 19:57:38 -070071 }
72
Gavin Howarde3c7c8e2018-03-23 08:15:46 -060073 vec = var ? &p->vars : &p->arrays;
74 veco = var ? &p->var_map : &p->array_map;
Gavin Howard3affc802018-02-27 19:57:38 -070075
76 entry.name = result->data.id.name;
77 entry.idx = vec->len;
78
Gavin Howardea932f02018-03-23 09:53:40 -060079 status = bc_veco_insert(veco, &entry, &idx);
Gavin Howard3affc802018-02-27 19:57:38 -070080
Gavin Howardea932f02018-03-23 09:53:40 -060081 if (status != BC_STATUS_VEC_ITEM_EXISTS) {
Gavin Howard3affc802018-02-27 19:57:38 -070082
Gavin Howard39b64082018-09-02 00:10:18 -060083 size_t len = strlen(entry.name) + 1;
Gavin Howarda1ff02f2018-03-07 12:32:25 -070084
Gavin Howardea932f02018-03-23 09:53:40 -060085 if (status) return status;
Gavin Howard3affc802018-02-27 19:57:38 -070086
Gavin Howardea932f02018-03-23 09:53:40 -060087 if (!(result->data.id.name = malloc(len))) return BC_STATUS_MALLOC_FAIL;
Gavin Howardea932f02018-03-23 09:53:40 -060088 strcpy(result->data.id.name, entry.name);
Gavin Howarda1ff02f2018-03-07 12:32:25 -070089
Gavin Howard39b64082018-09-02 00:10:18 -060090 if (var) status = bc_num_init(&data.data.num, BC_NUM_DEF_SIZE);
Gavin Howardea932f02018-03-23 09:53:40 -060091 else status = bc_vec_init(&data.data.array, sizeof(BcNum), bc_num_free);
Gavin Howard3affc802018-02-27 19:57:38 -070092
Gavin Howard37296462018-09-07 15:57:03 -060093 if (status) goto num_err;
Gavin Howarda628aa22018-09-12 13:52:45 -060094 if ((status = bc_vec_push(vec, 1, &data.data))) goto err;
Gavin Howard3affc802018-02-27 19:57:38 -070095 }
Gavin Howard3affc802018-02-27 19:57:38 -070096
97 entry_ptr = bc_veco_item(veco, idx);
Gavin Howard375263e2018-09-04 15:10:18 -060098 *ret = bc_vec_item(vec, entry_ptr->idx);
Gavin Howard3affc802018-02-27 19:57:38 -070099
100 return BC_STATUS_SUCCESS;
Gavin Howard37296462018-09-07 15:57:03 -0600101
102err:
103 if (var) bc_num_free(&data.data.num);
104 else bc_vec_free(&data.data.array);
105num_err:
106 free(result->data.id.name);
107 return status;
Gavin Howard3affc802018-02-27 19:57:38 -0700108}
109
Gavin Howard39b64082018-09-02 00:10:18 -0600110BcStatus bc_program_num(BcProgram *p, BcResult *result, BcNum **num, bool hex) {
Gavin Howard84095072018-02-27 15:36:58 -0700111
Gavin Howard4b9bb2b2018-03-23 08:11:11 -0600112 BcStatus status = BC_STATUS_SUCCESS;
Gavin Howard84095072018-02-27 15:36:58 -0700113
114 switch (result->type) {
115
Gavin Howard45ff3572018-05-15 16:33:58 -0600116 case BC_RESULT_TEMP:
Gavin Howard84095072018-02-27 15:36:58 -0700117 case BC_RESULT_SCALE:
118 {
119 *num = &result->data.num;
120 break;
121 }
122
123 case BC_RESULT_CONSTANT:
124 {
Gavin Howardf51b1892018-09-04 10:38:51 -0600125 char **s = bc_vec_item(&p->constants, result->data.id.idx);
126 size_t base, len = strlen(*s);
Gavin Howard84095072018-02-27 15:36:58 -0700127
Gavin Howardb09d9b12018-03-23 09:57:07 -0600128 if ((status = bc_num_init(&result->data.num, len))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700129
Gavin Howard1dc4f092018-09-21 10:46:52 -0600130 base = hex && len == 1 ? BC_NUM_MAX_IBASE : p->ibase_t;
Gavin Howard84095072018-02-27 15:36:58 -0700131
Gavin Howard45ff3572018-05-15 16:33:58 -0600132 if ((status = bc_num_parse(&result->data.num, *s, &p->ibase, base))) {
Gavin Howard9ba6c252018-03-14 01:44:38 -0600133 bc_num_free(&result->data.num);
134 return status;
135 }
Gavin Howard84095072018-02-27 15:36:58 -0700136
137 *num = &result->data.num;
Gavin Howard45ff3572018-05-15 16:33:58 -0600138 result->type = BC_RESULT_TEMP;
Gavin Howard84095072018-02-27 15:36:58 -0700139
140 break;
141 }
142
Gavin Howard375263e2018-09-04 15:10:18 -0600143 case BC_RESULT_ARRAY_ELEM:
144 {
145 BcVec *vec;
146
147 if ((status = bc_program_search(p, result, num, 0))) return status;
148
149 vec = (BcVec*) *num;
150
151 if (vec->len <= result->data.id.idx) {
152 status = bc_array_expand(vec, result->data.id.idx + 1);
153 if (status) return status;
154 }
155
156 *num = bc_vec_item(vec, result->data.id.idx);
157
158 break;
159 }
160
Gavin Howard84095072018-02-27 15:36:58 -0700161 case BC_RESULT_VAR:
Gavin Howardabcc5e02018-03-14 16:02:20 -0600162 case BC_RESULT_ARRAY:
Gavin Howard84095072018-02-27 15:36:58 -0700163 {
Gavin Howard375263e2018-09-04 15:10:18 -0600164 status = bc_program_search(p, result, num, result->type == BC_RESULT_VAR);
Gavin Howard84095072018-02-27 15:36:58 -0700165 break;
166 }
167
168 case BC_RESULT_LAST:
169 {
170 *num = &p->last;
171 break;
172 }
173
174 case BC_RESULT_IBASE:
175 {
176 *num = &p->ibase;
177 break;
178 }
179
180 case BC_RESULT_OBASE:
181 {
182 *num = &p->obase;
183 break;
184 }
185
186 case BC_RESULT_ONE:
187 {
188 *num = &p->one;
189 break;
190 }
191
192 default:
193 {
Gavin Howardb09d9b12018-03-23 09:57:07 -0600194 // This is here to prevent compiler warnings in release mode.
Gavin Howard2168fb82018-03-24 10:40:37 -0600195 *num = &result->data.num;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600196 assert(false);
Gavin Howard84095072018-02-27 15:36:58 -0700197 break;
198 }
199 }
200
201 return status;
202}
Gavin Howard6d89b5d2018-02-26 13:21:34 -0700203
Gavin Howard3f68df72018-03-22 20:30:27 -0600204BcStatus bc_program_binaryOpPrep(BcProgram *p, BcResult **left, BcNum **lval,
Gavin Howard584db5b2018-09-21 10:28:32 -0600205 BcResult **right, BcNum **rval, bool assign)
Gavin Howard84095072018-02-27 15:36:58 -0700206{
207 BcStatus status;
Gavin Howardef1b52e2018-03-20 14:02:57 -0600208 bool hex;
Gavin Howard84095072018-02-27 15:36:58 -0700209
Gavin Howard45ff3572018-05-15 16:33:58 -0600210 assert(p && left && lval && right && rval && BC_PROGRAM_CHECK_RESULTS(p, 2));
Gavin Howard84095072018-02-27 15:36:58 -0700211
Gavin Howardf51b1892018-09-04 10:38:51 -0600212 *right = bc_vec_item_rev(&p->results, 0);
213 *left = bc_vec_item_rev(&p->results, 1);
Gavin Howard84095072018-02-27 15:36:58 -0700214
Gavin Howard4c8ff2d2018-09-18 15:24:07 -0600215 if ((*left)->type == BC_RESULT_ARRAY ||
216 (*left)->type == BC_RESULT_ARRAY_AUTO ||
217 (*right)->type == BC_RESULT_ARRAY ||
218 (*right)->type == BC_RESULT_ARRAY_AUTO)
219 {
220 return BC_STATUS_EXEC_BAD_TYPE;
221 }
222
Gavin Howard584db5b2018-09-21 10:28:32 -0600223 hex = assign && ((*left)->type == BC_RESULT_IBASE ||
224 (*right)->type == BC_RESULT_OBASE);
Gavin Howard2168fb82018-03-24 10:40:37 -0600225
Gavin Howardf51b1892018-09-04 10:38:51 -0600226 if ((status = bc_program_num(p, *left, lval, false))) return status;
227 if ((status = bc_program_num(p, *right, rval, hex))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700228
Gavin Howardee938c02018-09-21 12:27:44 -0600229 // We run this again under these conditions in case any vector has been
230 // reallocated out from under the BcNums or arrays we had.
231 if (((*left)->type == (*right)->type) &&
232 ((*left)->type == BC_RESULT_VAR || (*left)->type == BC_RESULT_ARRAY_ELEM))
233 {
234 status = bc_program_num(p, *left, lval, false);
235 }
236
237 return status;
Gavin Howard84095072018-02-27 15:36:58 -0700238}
239
Gavin Howard3f68df72018-03-22 20:30:27 -0600240BcStatus bc_program_binaryOpRetire(BcProgram *p, BcResult *result,
241 BcResultType type)
Gavin Howard5fb956f2018-03-05 10:57:16 -0700242{
Gavin Howard5fb956f2018-03-05 10:57:16 -0700243 result->type = type;
Gavin Howard0eb2a372018-03-24 09:52:55 -0600244 bc_vec_pop(&p->results);
245 bc_vec_pop(&p->results);
Gavin Howarda628aa22018-09-12 13:52:45 -0600246 return bc_vec_push(&p->results, 1, result);
Gavin Howard84095072018-02-27 15:36:58 -0700247}
248
Gavin Howard38bea812018-09-20 12:01:25 -0600249BcStatus bc_program_unaryOpPrep(BcProgram *p, BcResult **result,
250 BcNum **val, bool array_allowed)
251{
Gavin Howard0c0d1922018-03-21 17:00:21 -0600252 assert(p && result && val && BC_PROGRAM_CHECK_RESULTS(p, 1));
Gavin Howard4c8ff2d2018-09-18 15:24:07 -0600253
Gavin Howardf51b1892018-09-04 10:38:51 -0600254 *result = bc_vec_item_rev(&p->results, 0);
Gavin Howard4c8ff2d2018-09-18 15:24:07 -0600255
Gavin Howard38bea812018-09-20 12:01:25 -0600256 if (((*result)->type == BC_RESULT_ARRAY_AUTO ||
257 (*result)->type == BC_RESULT_ARRAY) && !array_allowed)
Gavin Howard4c8ff2d2018-09-18 15:24:07 -0600258 {
259 return BC_STATUS_EXEC_BAD_TYPE;
260 }
261
Gavin Howardf51b1892018-09-04 10:38:51 -0600262 return bc_program_num(p, *result, val, false);
Gavin Howard84095072018-02-27 15:36:58 -0700263}
264
Gavin Howard3f68df72018-03-22 20:30:27 -0600265BcStatus bc_program_unaryOpRetire(BcProgram *p, BcResult *result,
266 BcResultType type)
Gavin Howard5fb956f2018-03-05 10:57:16 -0700267{
Gavin Howard5fb956f2018-03-05 10:57:16 -0700268 result->type = type;
Gavin Howard0eb2a372018-03-24 09:52:55 -0600269 bc_vec_pop(&p->results);
Gavin Howarda628aa22018-09-12 13:52:45 -0600270 return bc_vec_push(&p->results, 1, result);
Gavin Howard84095072018-02-27 15:36:58 -0700271}
272
Gavin Howard3f68df72018-03-22 20:30:27 -0600273BcStatus bc_program_op(BcProgram *p, uint8_t inst) {
Gavin Howard84095072018-02-27 15:36:58 -0700274
275 BcStatus status;
Gavin Howard584db5b2018-09-21 10:28:32 -0600276 BcResult *opd1, *opd2, res;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600277 BcNum *num1, *num2;
Gavin Howard6e0f3c52018-08-27 17:28:22 -0600278 BcNumBinaryOp op;
Gavin Howard84095072018-02-27 15:36:58 -0700279
Gavin Howard584db5b2018-09-21 10:28:32 -0600280 status = bc_program_binaryOpPrep(p, &opd1, &num1, &opd2, &num2, false);
Gavin Howard84095072018-02-27 15:36:58 -0700281 if (status) return status;
Gavin Howard45ff3572018-05-15 16:33:58 -0600282 if ((status = bc_num_init(&res.data.num, BC_NUM_DEF_SIZE))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700283
Gavin Howardf51b1892018-09-04 10:38:51 -0600284 op = bc_program_ops[inst - BC_INST_POWER];
Gavin Howard45ff3572018-05-15 16:33:58 -0600285 if ((status = op(num1, num2, &res.data.num, p->scale))) goto err;
286 if ((status = bc_program_binaryOpRetire(p, &res, BC_RESULT_TEMP))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700287
288 return status;
289
290err:
Gavin Howard45ff3572018-05-15 16:33:58 -0600291 bc_num_free(&res.data.num);
Gavin Howard84095072018-02-27 15:36:58 -0700292 return status;
293}
294
Gavin Howard3f68df72018-03-22 20:30:27 -0600295BcStatus bc_program_read(BcProgram *p) {
Gavin Howard84095072018-02-27 15:36:58 -0700296
297 BcStatus status;
298 BcParse parse;
299 char *buffer;
Gavin Howard84095072018-02-27 15:36:58 -0700300 BcInstPtr ip;
Gavin Howard8317a792018-09-04 22:34:53 -0600301 size_t size = BC_BUF_SIZE;
Gavin Howardc891dcf2018-09-04 15:12:22 -0600302 BcFunc *func = bc_vec_item(&p->funcs, BC_PROGRAM_READ);
Gavin Howard84095072018-02-27 15:36:58 -0700303
Gavin Howard84095072018-02-27 15:36:58 -0700304 func->code.len = 0;
305
Gavin Howardc891dcf2018-09-04 15:12:22 -0600306 if (!(buffer = malloc(size + 1))) return BC_STATUS_MALLOC_FAIL;
Gavin Howard81990302018-03-29 21:35:31 -0600307 if ((status = bc_io_getline(&buffer, &size)))goto io_err;
Gavin Howard84095072018-02-27 15:36:58 -0700308
Gavin Howardb09d9b12018-03-23 09:57:07 -0600309 if ((status = bc_parse_init(&parse, p))) goto io_err;
Gavin Howard9697ea82018-09-20 11:15:37 -0600310 bc_lex_file(&parse.lex, bc_program_stdin_name);
Gavin Howard68f2bae2018-03-26 13:02:27 -0600311 if ((status = bc_lex_text(&parse.lex, buffer))) goto exec_err;
Gavin Howard84095072018-02-27 15:36:58 -0700312
Gavin Howard79be14a2018-09-12 16:39:27 -0600313 status = bc_parse_expr(&parse, &func->code, BC_PARSE_NOREAD,
Gavin Howard38c48192018-09-14 08:57:05 -0600314 bc_parse_next_read);
Gavin Howardc891dcf2018-09-04 15:12:22 -0600315 if (status) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700316
Gavin Howardf4167dc2018-03-26 14:19:28 -0600317 if (parse.lex.token.type != BC_LEX_NEWLINE &&
318 parse.lex.token.type != BC_LEX_EOF)
319 {
320 status = BC_STATUS_EXEC_BAD_READ_EXPR;
Gavin Howard84095072018-02-27 15:36:58 -0700321 goto exec_err;
322 }
323
Gavin Howarda1601ae2018-03-17 19:43:28 -0600324 ip.func = BC_PROGRAM_READ;
Gavin Howard84095072018-02-27 15:36:58 -0700325 ip.idx = 0;
Gavin Howard0c0d1922018-03-21 17:00:21 -0600326 ip.len = p->results.len;
Gavin Howard84095072018-02-27 15:36:58 -0700327
Gavin Howarda628aa22018-09-12 13:52:45 -0600328 if ((status = bc_vec_push(&p->stack, 1, &ip))) goto exec_err;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600329 if ((status = bc_program_exec(p))) goto exec_err;
Gavin Howardceaf64d2018-03-05 11:20:34 -0700330
Gavin Howard0eb2a372018-03-24 09:52:55 -0600331 bc_vec_pop(&p->stack);
Gavin Howardceaf64d2018-03-05 11:20:34 -0700332
Gavin Howard84095072018-02-27 15:36:58 -0700333exec_err:
Gavin Howard84095072018-02-27 15:36:58 -0700334 bc_parse_free(&parse);
Gavin Howard84095072018-02-27 15:36:58 -0700335io_err:
Gavin Howard84095072018-02-27 15:36:58 -0700336 free(buffer);
Gavin Howard84095072018-02-27 15:36:58 -0700337 return status;
338}
339
Gavin Howard3f68df72018-03-22 20:30:27 -0600340size_t bc_program_index(uint8_t *code, size_t *start) {
Gavin Howard84095072018-02-27 15:36:58 -0700341
Gavin Howardf51b1892018-09-04 10:38:51 -0600342 uint8_t bytes = code[(*start)++], i = 0;
343 size_t res = 0;
Gavin Howard84095072018-02-27 15:36:58 -0700344
Gavin Howardf51b1892018-09-04 10:38:51 -0600345 for (; i < bytes; ++i) res |= (((size_t) code[(*start)++]) << (i * CHAR_BIT));
Gavin Howard84095072018-02-27 15:36:58 -0700346
Gavin Howardf51b1892018-09-04 10:38:51 -0600347 return res;
Gavin Howard84095072018-02-27 15:36:58 -0700348}
349
Gavin Howard3f68df72018-03-22 20:30:27 -0600350char* bc_program_name(uint8_t *code, size_t *start) {
Gavin Howard84095072018-02-27 15:36:58 -0700351
Gavin Howardb09d9b12018-03-23 09:57:07 -0600352 size_t len, i;
Gavin Howardf51b1892018-09-04 10:38:51 -0600353 char byte, *s, *string = (char*) (code + *start), *ptr = strchr(string, ':');
Gavin Howard84095072018-02-27 15:36:58 -0700354
355 if (ptr) len = ((unsigned long) ptr) - ((unsigned long) string);
356 else len = strlen(string);
357
Gavin Howardb09d9b12018-03-23 09:57:07 -0600358 if (!(s = malloc(len + 1))) return NULL;
Gavin Howard84095072018-02-27 15:36:58 -0700359
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600360 for (byte = (char) code[(*start)++], i = 0; byte && byte != ':'; ++i) {
Gavin Howard45ff3572018-05-15 16:33:58 -0600361 s[i] = byte;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600362 byte = (char) code[(*start)++];
Gavin Howard84095072018-02-27 15:36:58 -0700363 }
364
365 s[i] = '\0';
366
367 return s;
368}
369
Gavin Howard3f68df72018-03-22 20:30:27 -0600370BcStatus bc_program_printString(const char *str, size_t *nchars) {
Gavin Howard84095072018-02-27 15:36:58 -0700371
Gavin Howardf51b1892018-09-04 10:38:51 -0600372 size_t i, len = strlen(str);
Gavin Howard84095072018-02-27 15:36:58 -0700373
Gavin Howardbc7cae82018-03-14 13:43:04 -0600374 for (i = 0; i < len; ++i, ++(*nchars)) {
Gavin Howard84095072018-02-27 15:36:58 -0700375
Gavin Howardf51b1892018-09-04 10:38:51 -0600376 int err;
377 char c, c2;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600378
Gavin Howard45ff3572018-05-15 16:33:58 -0600379 if ((c = str[i]) != '\\') err = putchar(c);
Gavin Howard84095072018-02-27 15:36:58 -0700380 else {
381
382 ++i;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600383 assert(i < len);
Gavin Howard84095072018-02-27 15:36:58 -0700384 c2 = str[i];
385
386 switch (c2) {
387
388 case 'a':
389 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600390 err = putchar('\a');
Gavin Howard84095072018-02-27 15:36:58 -0700391 break;
392 }
393
394 case 'b':
395 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600396 err = putchar('\b');
Gavin Howard84095072018-02-27 15:36:58 -0700397 break;
398 }
399
400 case 'e':
401 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600402 err = putchar('\\');
Gavin Howard84095072018-02-27 15:36:58 -0700403 break;
404 }
405
406 case 'f':
407 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600408 err = putchar('\f');
Gavin Howard84095072018-02-27 15:36:58 -0700409 break;
410 }
411
412 case 'n':
413 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600414 err = putchar('\n');
Gavin Howardbc7cae82018-03-14 13:43:04 -0600415 *nchars = SIZE_MAX;
Gavin Howard84095072018-02-27 15:36:58 -0700416 break;
417 }
418
419 case 'r':
420 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600421 err = putchar('\r');
Gavin Howard84095072018-02-27 15:36:58 -0700422 break;
423 }
424
425 case 'q':
426 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600427 err = putchar('"');
Gavin Howard84095072018-02-27 15:36:58 -0700428 break;
429 }
430
431 case 't':
432 {
Gavin Howarda141a0f2018-03-23 13:16:10 -0600433 err = putchar('\t');
Gavin Howard84095072018-02-27 15:36:58 -0700434 break;
435 }
436
437 default:
438 {
439 // Do nothing.
440 err = 0;
441 break;
442 }
443 }
444 }
445
Gavin Howard16bba752018-03-15 11:28:29 -0600446 if (err == EOF) return BC_STATUS_IO_ERR;
Gavin Howard84095072018-02-27 15:36:58 -0700447 }
448
449 return BC_STATUS_SUCCESS;
450}
451
Gavin Howard375263e2018-09-04 15:10:18 -0600452BcStatus bc_program_push(BcProgram *p, uint8_t *code, size_t *start,
453 uint8_t inst)
454{
Gavin Howard843fa792018-02-27 16:42:52 -0700455 BcStatus status;
456 BcResult result;
Gavin Howard843fa792018-02-27 16:42:52 -0700457
Gavin Howardb09d9b12018-03-23 09:57:07 -0600458 result.data.id.name = bc_program_name(code, start);
Gavin Howard843fa792018-02-27 16:42:52 -0700459
Gavin Howardb09d9b12018-03-23 09:57:07 -0600460 assert(result.data.id.name);
Gavin Howard843fa792018-02-27 16:42:52 -0700461
Gavin Howard375263e2018-09-04 15:10:18 -0600462 if (inst == BC_INST_PUSH_VAR || inst == BC_INST_PUSH_ARRAY) {
463 result.type = inst == BC_INST_PUSH_VAR ? BC_RESULT_VAR : BC_RESULT_ARRAY;
Gavin Howarda628aa22018-09-12 13:52:45 -0600464 status = bc_vec_push(&p->results, 1, &result);
Gavin Howard843fa792018-02-27 16:42:52 -0700465 }
466 else {
467
468 BcResult *operand;
469 BcNum *num;
470 unsigned long temp;
471
Gavin Howard38bea812018-09-20 12:01:25 -0600472 if ((status = bc_program_unaryOpPrep(p, &operand, &num, false))) goto err;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600473 if ((status = bc_num_ulong(num, &temp))) goto err;
Gavin Howard843fa792018-02-27 16:42:52 -0700474
Gavin Howard0dd566d2018-03-28 19:29:34 -0600475 if (temp > (unsigned long) BC_MAX_DIM) {
Gavin Howardbdb2f922018-03-15 09:25:18 -0600476 status = BC_STATUS_EXEC_ARRAY_LEN;
477 goto err;
478 }
479
Gavin Howard843fa792018-02-27 16:42:52 -0700480 result.data.id.idx = (size_t) temp;
Gavin Howard375263e2018-09-04 15:10:18 -0600481 status = bc_program_unaryOpRetire(p, &result, BC_RESULT_ARRAY_ELEM);
Gavin Howard843fa792018-02-27 16:42:52 -0700482 }
483
Gavin Howard843fa792018-02-27 16:42:52 -0700484err:
Gavin Howard39b64082018-09-02 00:10:18 -0600485 if (status) free(result.data.id.name);
Gavin Howard843fa792018-02-27 16:42:52 -0700486 return status;
487}
488
Gavin Howard3f68df72018-03-22 20:30:27 -0600489BcStatus bc_program_negate(BcProgram *p) {
Gavin Howard3a947362018-03-02 11:25:48 -0700490
491 BcStatus status;
Gavin Howard45ff3572018-05-15 16:33:58 -0600492 BcResult result, *ptr;
Gavin Howard3a947362018-03-02 11:25:48 -0700493 BcNum *num;
494
Gavin Howard38bea812018-09-20 12:01:25 -0600495 if ((status = bc_program_unaryOpPrep(p, &ptr, &num, false))) return status;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600496 if ((status = bc_num_init(&result.data.num, num->len))) return status;
497 if ((status = bc_num_copy(&result.data.num, num))) goto err;
Gavin Howard3a947362018-03-02 11:25:48 -0700498
Gavin Howarda654f112018-03-03 09:47:34 -0700499 result.data.num.neg = !result.data.num.neg;
500
Gavin Howard45ff3572018-05-15 16:33:58 -0600501 if ((status = bc_program_unaryOpRetire(p, &result, BC_RESULT_TEMP))) goto err;
Gavin Howard3a947362018-03-02 11:25:48 -0700502
503 return status;
504
505err:
Gavin Howard3a947362018-03-02 11:25:48 -0700506 bc_num_free(&result.data.num);
Gavin Howard3a947362018-03-02 11:25:48 -0700507 return status;
508}
509
Gavin Howard3f68df72018-03-22 20:30:27 -0600510BcStatus bc_program_logical(BcProgram *p, uint8_t inst) {
Gavin Howard843fa792018-02-27 16:42:52 -0700511
512 BcStatus status;
Gavin Howard584db5b2018-09-21 10:28:32 -0600513 BcResult *opd1, *opd2, res;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600514 BcNum *num1, *num2;
Gavin Howard843fa792018-02-27 16:42:52 -0700515 bool cond;
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600516 ssize_t cmp;
Gavin Howard843fa792018-02-27 16:42:52 -0700517
Gavin Howard584db5b2018-09-21 10:28:32 -0600518 status = bc_program_binaryOpPrep(p, &opd1, &num1, &opd2, &num2, false);
Gavin Howard843fa792018-02-27 16:42:52 -0700519 if (status) return status;
520
Gavin Howard45ff3572018-05-15 16:33:58 -0600521 if ((status = bc_num_init(&res.data.num, BC_NUM_DEF_SIZE))) return status;
Gavin Howard843fa792018-02-27 16:42:52 -0700522
Gavin Howard101b5e62018-03-26 07:46:27 -0600523 if (inst == BC_INST_BOOL_AND)
Gavin Howard6a804cf2018-05-17 16:54:12 -0600524 cond = bc_num_cmp(num1, &p->zero) && bc_num_cmp(num2, &p->zero);
Gavin Howard101b5e62018-03-26 07:46:27 -0600525 else if (inst == BC_INST_BOOL_OR)
Gavin Howard6a804cf2018-05-17 16:54:12 -0600526 cond = bc_num_cmp(num1, &p->zero) || bc_num_cmp(num2, &p->zero);
Gavin Howard843fa792018-02-27 16:42:52 -0700527 else {
528
Gavin Howard6a804cf2018-05-17 16:54:12 -0600529 cmp = bc_num_cmp(num1, num2);
Gavin Howard843fa792018-02-27 16:42:52 -0700530
531 switch (inst) {
Gavin Howard101b5e62018-03-26 07:46:27 -0600532 case BC_INST_REL_EQ:
Gavin Howard843fa792018-02-27 16:42:52 -0700533 {
534 cond = cmp == 0;
535 break;
536 }
537
Gavin Howard101b5e62018-03-26 07:46:27 -0600538 case BC_INST_REL_LE:
Gavin Howard843fa792018-02-27 16:42:52 -0700539 {
540 cond = cmp <= 0;
541 break;
542 }
543
Gavin Howard101b5e62018-03-26 07:46:27 -0600544 case BC_INST_REL_GE:
Gavin Howard843fa792018-02-27 16:42:52 -0700545 {
546 cond = cmp >= 0;
547 break;
548 }
549
Gavin Howard101b5e62018-03-26 07:46:27 -0600550 case BC_INST_REL_NE:
Gavin Howard843fa792018-02-27 16:42:52 -0700551 {
552 cond = cmp != 0;
553 break;
554 }
555
Gavin Howard101b5e62018-03-26 07:46:27 -0600556 case BC_INST_REL_LT:
Gavin Howard843fa792018-02-27 16:42:52 -0700557 {
558 cond = cmp < 0;
559 break;
560 }
561
Gavin Howard101b5e62018-03-26 07:46:27 -0600562 case BC_INST_REL_GT:
Gavin Howard843fa792018-02-27 16:42:52 -0700563 {
564 cond = cmp > 0;
565 break;
566 }
567
568 default:
569 {
Gavin Howardb09d9b12018-03-23 09:57:07 -0600570 // This is here to silence a compiler warning in release mode.
Gavin Howardf955c232018-03-21 08:56:58 -0600571 cond = 0;
Gavin Howard2168fb82018-03-24 10:40:37 -0600572 assert(cond);
Gavin Howard27fdfb92018-03-21 07:56:59 -0600573 break;
Gavin Howard843fa792018-02-27 16:42:52 -0700574 }
575 }
Gavin Howard843fa792018-02-27 16:42:52 -0700576 }
577
Gavin Howard45ff3572018-05-15 16:33:58 -0600578 (cond ? bc_num_one : bc_num_zero)(&res.data.num);
Gavin Howard843fa792018-02-27 16:42:52 -0700579
Gavin Howard45ff3572018-05-15 16:33:58 -0600580 if ((status = bc_program_binaryOpRetire(p, &res, BC_RESULT_TEMP))) goto err;
Gavin Howard843fa792018-02-27 16:42:52 -0700581
582 return status;
583
584err:
Gavin Howard45ff3572018-05-15 16:33:58 -0600585 bc_num_free(&res.data.num);
Gavin Howard843fa792018-02-27 16:42:52 -0700586 return status;
587}
588
Gavin Howard3f68df72018-03-22 20:30:27 -0600589BcStatus bc_program_assign(BcProgram *p, uint8_t inst) {
Gavin Howard84095072018-02-27 15:36:58 -0700590
591 BcStatus status;
Gavin Howard45ff3572018-05-15 16:33:58 -0600592 BcResult *left, *right, res;
593 BcNum *l, *r;
Gavin Howard45ff3572018-05-15 16:33:58 -0600594 unsigned long val, max;
Gavin Howard584db5b2018-09-21 10:28:32 -0600595 bool assign = inst == BC_INST_ASSIGN;
Gavin Howard84095072018-02-27 15:36:58 -0700596
Gavin Howard584db5b2018-09-21 10:28:32 -0600597 status = bc_program_binaryOpPrep(p, &left, &l, &right, &r, assign);
Gavin Howard84095072018-02-27 15:36:58 -0700598 if (status) return status;
599
Gavin Howard45ff3572018-05-15 16:33:58 -0600600 if (left->type == BC_RESULT_CONSTANT || left->type == BC_RESULT_TEMP)
Gavin Howardbdb2f922018-03-15 09:25:18 -0600601 return BC_STATUS_PARSE_BAD_ASSIGN;
Gavin Howard84095072018-02-27 15:36:58 -0700602
Gavin Howard6a804cf2018-05-17 16:54:12 -0600603 if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &p->zero))
Gavin Howard84095072018-02-27 15:36:58 -0700604 return BC_STATUS_MATH_DIVIDE_BY_ZERO;
605
Gavin Howard584db5b2018-09-21 10:28:32 -0600606 if (assign) status = bc_num_copy(l, r);
Gavin Howardf51b1892018-09-04 10:38:51 -0600607 else status = bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, p->scale);
Gavin Howardd5a51e62018-03-26 12:34:27 -0600608
609 if (status) return status;
610
611 if (left->type == BC_RESULT_IBASE || left->type == BC_RESULT_OBASE) {
612
Gavin Howard1dc4f092018-09-21 10:46:52 -0600613 size_t * ptr = left->type == BC_RESULT_IBASE ? &p->ibase_t : &p->obase_t;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600614
Gavin Howard45ff3572018-05-15 16:33:58 -0600615 if ((status = bc_num_ulong(l, &val))) return status;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600616
Gavin Howard691cfd52018-09-21 10:47:58 -0600617 max = left->type == BC_RESULT_IBASE ? BC_NUM_MAX_IBASE : BC_MAX_OBASE;
618
Gavin Howard45ff3572018-05-15 16:33:58 -0600619 if (val < BC_NUM_MIN_BASE || val > max)
Gavin Howard8d482d92018-09-21 10:30:20 -0600620 return left->type == BC_RESULT_IBASE ? BC_STATUS_EXEC_BAD_IBASE :
621 BC_STATUS_EXEC_BAD_OBASE;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600622
Gavin Howard45ff3572018-05-15 16:33:58 -0600623 *ptr = (size_t) val;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600624 }
625 else if (left->type == BC_RESULT_SCALE) {
626
Gavin Howard45ff3572018-05-15 16:33:58 -0600627 if ((status = bc_num_ulong(l, &val))) return status;
628 if (val > (unsigned long) BC_MAX_SCALE) return BC_STATUS_EXEC_BAD_SCALE;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600629
Gavin Howard45ff3572018-05-15 16:33:58 -0600630 p->scale = (size_t) val;
Gavin Howardd5a51e62018-03-26 12:34:27 -0600631 }
Gavin Howard84095072018-02-27 15:36:58 -0700632
Gavin Howard45ff3572018-05-15 16:33:58 -0600633 if ((status = bc_num_init(&res.data.num, l->len))) return status;
634 if ((status = bc_num_copy(&res.data.num, l))) goto err;
Gavin Howardb8181162018-02-28 12:58:09 -0700635
Gavin Howard45ff3572018-05-15 16:33:58 -0600636 if ((status = bc_program_binaryOpRetire(p, &res, BC_RESULT_TEMP))) goto err;
Gavin Howardb8181162018-02-28 12:58:09 -0700637
638 return status;
639
640err:
Gavin Howard45ff3572018-05-15 16:33:58 -0600641 bc_num_free(&res.data.num);
Gavin Howardb8181162018-02-28 12:58:09 -0700642 return status;
Gavin Howard84095072018-02-27 15:36:58 -0700643}
644
Gavin Howard3f68df72018-03-22 20:30:27 -0600645BcStatus bc_program_call(BcProgram *p, uint8_t *code, size_t *idx) {
Gavin Howard84095072018-02-27 15:36:58 -0700646
Gavin Howardf51b1892018-09-04 10:38:51 -0600647 BcStatus status = BC_STATUS_SUCCESS;
Gavin Howard84095072018-02-27 15:36:58 -0700648 BcInstPtr ip;
Gavin Howardf51b1892018-09-04 10:38:51 -0600649 size_t i, nparams = bc_program_index(code, idx);
Gavin Howard84095072018-02-27 15:36:58 -0700650 BcFunc *func;
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700651 BcAuto *auto_ptr;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600652 BcResult param, *arg;
Gavin Howard84095072018-02-27 15:36:58 -0700653
Gavin Howard84095072018-02-27 15:36:58 -0700654 ip.idx = 0;
Gavin Howard0c0d1922018-03-21 17:00:21 -0600655 ip.len = p->results.len;
Gavin Howard84095072018-02-27 15:36:58 -0700656 ip.func = bc_program_index(code, idx);
657
658 func = bc_vec_item(&p->funcs, ip.func);
659
Gavin Howardea9a9bd2018-03-21 08:04:07 -0600660 if (!func->code.len) return BC_STATUS_EXEC_UNDEFINED_FUNC;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600661 if (nparams != func->nparams) return BC_STATUS_EXEC_MISMATCHED_PARAMS;
Gavin Howard84095072018-02-27 15:36:58 -0700662
Gavin Howard3d3f73e2018-03-19 17:51:14 -0600663 for (i = 0; i < nparams; ++i) {
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700664
Gavin Howard3d3f73e2018-03-19 17:51:14 -0600665 auto_ptr = bc_vec_item(&func->autos, i);
Gavin Howard0c0d1922018-03-21 17:00:21 -0600666 arg = bc_vec_item_rev(&p->results, nparams - 1);
Gavin Howard90f13ad2018-03-17 13:32:16 -0600667 param.type = auto_ptr->var + BC_RESULT_ARRAY_AUTO;
Gavin Howard57323432018-03-14 20:06:35 -0600668
Gavin Howard1ce83252018-09-04 15:21:47 -0600669 if (!auto_ptr->var != (arg->type == BC_RESULT_ARRAY))
670 return BC_STATUS_EXEC_BAD_TYPE;
671
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700672 if (auto_ptr->var) {
673
Gavin Howardb09d9b12018-03-23 09:57:07 -0600674 BcNum *n;
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700675
Gavin Howardb09d9b12018-03-23 09:57:07 -0600676 if ((status = bc_program_num(p, arg, &n, false))) return status;
677 if ((status = bc_num_init(&param.data.num, n->len))) return status;
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700678
Gavin Howardb09d9b12018-03-23 09:57:07 -0600679 status = bc_num_copy(&param.data.num, n);
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700680 }
681 else {
682
Gavin Howardb09d9b12018-03-23 09:57:07 -0600683 BcVec *a;
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700684
Gavin Howard71815ed2018-09-04 16:33:13 -0600685 if ((status = bc_program_search(p, arg, (BcNum**) &a, 0))) return status;
Gavin Howard90f13ad2018-03-17 13:32:16 -0600686 status = bc_vec_init(&param.data.array, sizeof(BcNum), bc_num_free);
Gavin Howard90f13ad2018-03-17 13:32:16 -0600687 if (status) return status;
688
Gavin Howardb09d9b12018-03-23 09:57:07 -0600689 status = bc_array_copy(&param.data.array, a);
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700690 }
691
Gavin Howarda628aa22018-09-12 13:52:45 -0600692 if (status || (status = bc_vec_push(&p->results, 1, &param))) goto err;
Gavin Howard90f13ad2018-03-17 13:32:16 -0600693 }
694
Gavin Howard45ff3572018-05-15 16:33:58 -0600695 for (; !status && i < func->autos.len; ++i) {
Gavin Howard90f13ad2018-03-17 13:32:16 -0600696
697 auto_ptr = bc_vec_item_rev(&func->autos, i);
Gavin Howard90f13ad2018-03-17 13:32:16 -0600698 param.type = auto_ptr->var + BC_RESULT_ARRAY_AUTO;
699
700 if (auto_ptr->var) status = bc_num_init(&param.data.num, BC_NUM_DEF_SIZE);
701 else status = bc_vec_init(&param.data.array, sizeof(BcNum), bc_num_free);
702
703 if (status) return status;
Gavin Howarda628aa22018-09-12 13:52:45 -0600704 status = bc_vec_push(&p->results, 1, &param);
Gavin Howardd6e3b7b2018-02-28 12:31:58 -0700705 }
Gavin Howard84095072018-02-27 15:36:58 -0700706
Gavin Howardb09d9b12018-03-23 09:57:07 -0600707 if (status) goto err;
708
Gavin Howarda628aa22018-09-12 13:52:45 -0600709 return bc_vec_push(&p->stack, 1, &ip);
Gavin Howard57323432018-03-14 20:06:35 -0600710
711err:
Gavin Howard90f13ad2018-03-17 13:32:16 -0600712 bc_result_free(&param);
Gavin Howard57323432018-03-14 20:06:35 -0600713 return status;
Gavin Howard84095072018-02-27 15:36:58 -0700714}
715
Gavin Howard3f68df72018-03-22 20:30:27 -0600716BcStatus bc_program_return(BcProgram *p, uint8_t inst) {
Gavin Howard84095072018-02-27 15:36:58 -0700717
718 BcStatus status;
Gavin Howardf51b1892018-09-04 10:38:51 -0600719 BcResult result;
Gavin Howardc410b0f2018-02-27 16:57:05 -0700720 BcFunc *func;
Gavin Howardf51b1892018-09-04 10:38:51 -0600721 BcInstPtr *ip = bc_vec_top(&p->stack);
Gavin Howard84095072018-02-27 15:36:58 -0700722
Gavin Howard27fdfb92018-03-21 07:56:59 -0600723 assert(BC_PROGRAM_CHECK_STACK(p));
Gavin Howard0c0d1922018-03-21 17:00:21 -0600724 assert(BC_PROGRAM_CHECK_RESULTS(p, ip->len + inst == BC_INST_RETURN));
Gavin Howardc410b0f2018-02-27 16:57:05 -0700725
Gavin Howardf51b1892018-09-04 10:38:51 -0600726 func = bc_vec_item(&p->funcs, ip->func);
Gavin Howard45ff3572018-05-15 16:33:58 -0600727 result.type = BC_RESULT_TEMP;
Gavin Howard84095072018-02-27 15:36:58 -0700728
729 if (inst == BC_INST_RETURN) {
730
731 BcNum *num;
Gavin Howardf51b1892018-09-04 10:38:51 -0600732 BcResult *operand = bc_vec_top(&p->results);
Gavin Howard84095072018-02-27 15:36:58 -0700733
Gavin Howardb09d9b12018-03-23 09:57:07 -0600734 if ((status = bc_program_num(p, operand, &num, false))) return status;
735 if ((status = bc_num_init(&result.data.num, num->len))) return status;
736 if ((status = bc_num_copy(&result.data.num, num))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700737 }
738 else {
Gavin Howard84095072018-02-27 15:36:58 -0700739 status = bc_num_init(&result.data.num, BC_NUM_DEF_SIZE);
Gavin Howard84095072018-02-27 15:36:58 -0700740 if (status) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700741 bc_num_zero(&result.data.num);
742 }
743
Gavin Howardc410b0f2018-02-27 16:57:05 -0700744 // We need to pop arguments as well, so this takes that into account.
Gavin Howard0eb2a372018-03-24 09:52:55 -0600745 bc_vec_npop(&p->results, p->results.len - (ip->len - func->nparams));
Gavin Howardc410b0f2018-02-27 16:57:05 -0700746
Gavin Howarda628aa22018-09-12 13:52:45 -0600747 if ((status = bc_vec_push(&p->results, 1, &result))) goto err;
Gavin Howard0eb2a372018-03-24 09:52:55 -0600748 bc_vec_pop(&p->stack);
749
750 return status;
Gavin Howard84095072018-02-27 15:36:58 -0700751
752err:
Gavin Howard84095072018-02-27 15:36:58 -0700753 bc_num_free(&result.data.num);
Gavin Howard84095072018-02-27 15:36:58 -0700754 return status;
755}
756
Gavin Howard3f68df72018-03-22 20:30:27 -0600757unsigned long bc_program_scale(BcNum *n) {
Gavin Howard84095072018-02-27 15:36:58 -0700758 return (unsigned long) n->rdx;
759}
760
Gavin Howard77d489d2018-09-04 15:14:06 -0600761unsigned long bc_program_len(BcNum *n) {
Gavin Howard6e2fd062018-03-20 19:34:12 -0600762
Gavin Howard740989f2018-03-20 19:36:06 -0600763 unsigned long len = n->len;
Gavin Howard6e2fd062018-03-20 19:34:12 -0600764
765 if (n->rdx == n->len) {
Gavin Howardf6e3fb32018-08-09 13:48:59 -0600766 size_t i;
Gavin Howard6e2fd062018-03-20 19:34:12 -0600767 for (i = n->len - 1; i < n->len && !n->num[i]; --len, --i);
768 }
769
770 return len;
Gavin Howard84095072018-02-27 15:36:58 -0700771}
772
Gavin Howard3f68df72018-03-22 20:30:27 -0600773BcStatus bc_program_builtin(BcProgram *p, uint8_t inst) {
Gavin Howard84095072018-02-27 15:36:58 -0700774
775 BcStatus status;
Gavin Howard3aa58d12018-03-05 11:21:18 -0700776 BcResult *operand;
Gavin Howard84095072018-02-27 15:36:58 -0700777 BcNum *num1;
778 BcResult result;
779
Gavin Howard38bea812018-09-20 12:01:25 -0600780 status = bc_program_unaryOpPrep(p, &operand, &num1, inst == BC_INST_LENGTH);
781 if (status) return status;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600782 if ((status = bc_num_init(&result.data.num, BC_NUM_DEF_SIZE))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700783
Gavin Howard45ff3572018-05-15 16:33:58 -0600784 if (inst == BC_INST_SQRT)
Gavin Howard84095072018-02-27 15:36:58 -0700785 status = bc_num_sqrt(num1, &result.data.num, p->scale);
Gavin Howard71815ed2018-09-04 16:33:13 -0600786 else if (inst == BC_INST_LENGTH && operand->type == BC_RESULT_ARRAY) {
Gavin Howard77d489d2018-09-04 15:14:06 -0600787 BcVec *vec = (BcVec*) num1;
788 status = bc_num_ulong2num(&result.data.num, (unsigned long) vec->len);
789 }
Gavin Howard84095072018-02-27 15:36:58 -0700790 else {
Gavin Howard70610172018-09-05 20:30:08 -0600791 assert(operand->type != BC_RESULT_ARRAY);
Gavin Howard77d489d2018-09-04 15:14:06 -0600792 BcProgramBuiltIn f = inst == BC_INST_LENGTH ? bc_program_len :
793 bc_program_scale;
794 status = bc_num_ulong2num(&result.data.num, f(num1));
Gavin Howard84095072018-02-27 15:36:58 -0700795 }
796
Gavin Howard21427f52018-09-05 10:40:20 -0600797 if (status || (status = bc_program_unaryOpRetire(p, &result, BC_RESULT_TEMP)))
798 goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700799
800err:
Gavin Howard39b64082018-09-02 00:10:18 -0600801 if (status) bc_num_free(&result.data.num);
Gavin Howard84095072018-02-27 15:36:58 -0700802 return status;
803}
804
Gavin Howard3f68df72018-03-22 20:30:27 -0600805BcStatus bc_program_pushScale(BcProgram *p) {
Gavin Howard84095072018-02-27 15:36:58 -0700806
807 BcStatus status;
808 BcResult result;
809
810 result.type = BC_RESULT_SCALE;
Gavin Howard84095072018-02-27 15:36:58 -0700811
Gavin Howardb09d9b12018-03-23 09:57:07 -0600812 if ((status = bc_num_init(&result.data.num, BC_NUM_DEF_SIZE))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700813 status = bc_num_ulong2num(&result.data.num, (unsigned long) p->scale);
Gavin Howarda628aa22018-09-12 13:52:45 -0600814 if (status || (status = bc_vec_push(&p->results, 1, &result))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700815
816 return status;
817
818err:
Gavin Howard84095072018-02-27 15:36:58 -0700819 bc_num_free(&result.data.num);
Gavin Howard84095072018-02-27 15:36:58 -0700820 return status;
821}
822
Gavin Howard3f68df72018-03-22 20:30:27 -0600823BcStatus bc_program_incdec(BcProgram *p, uint8_t inst) {
Gavin Howard84095072018-02-27 15:36:58 -0700824
825 BcStatus status;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600826 BcResult *ptr, result, copy;
Gavin Howard84095072018-02-27 15:36:58 -0700827 BcNum *num;
Gavin Howard70610172018-09-05 20:30:08 -0600828 uint8_t inst2 = inst;
Gavin Howard84095072018-02-27 15:36:58 -0700829
Gavin Howard38bea812018-09-20 12:01:25 -0600830 if ((status = bc_program_unaryOpPrep(p, &ptr, &num, false))) return status;
Gavin Howard84095072018-02-27 15:36:58 -0700831
Gavin Howard101b5e62018-03-26 07:46:27 -0600832 if (inst == BC_INST_INC_POST || inst == BC_INST_DEC_POST) {
Gavin Howard45ff3572018-05-15 16:33:58 -0600833 copy.type = BC_RESULT_TEMP;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600834 if ((status = bc_num_init(&copy.data.num, num->len))) return status;
Gavin Howarde8bbdcc2018-09-06 13:06:02 -0600835 if ((status = bc_num_copy(&copy.data.num, num))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700836 }
837
838 result.type = BC_RESULT_ONE;
Gavin Howard45ff3572018-05-15 16:33:58 -0600839 inst = inst == BC_INST_INC_PRE || inst == BC_INST_INC_POST ?
840 BC_INST_ASSIGN_PLUS : BC_INST_ASSIGN_MINUS;
Gavin Howard84095072018-02-27 15:36:58 -0700841
Gavin Howarda628aa22018-09-12 13:52:45 -0600842 if ((status = bc_vec_push(&p->results, 1, &result))) goto err;
Gavin Howard45ff3572018-05-15 16:33:58 -0600843 if ((status = bc_program_assign(p, inst))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700844
Gavin Howard70610172018-09-05 20:30:08 -0600845 if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST) {
Gavin Howard0eb2a372018-03-24 09:52:55 -0600846 bc_vec_pop(&p->results);
Gavin Howarda628aa22018-09-12 13:52:45 -0600847 if ((status = bc_vec_push(&p->results, 1, &copy))) goto err;
Gavin Howard84095072018-02-27 15:36:58 -0700848 }
849
850 return status;
851
852err:
853
Gavin Howard70610172018-09-05 20:30:08 -0600854 if (inst2 == BC_INST_INC_POST || inst2 == BC_INST_DEC_POST)
Gavin Howard84095072018-02-27 15:36:58 -0700855 bc_num_free(&copy.data.num);
856
857 return status;
858}
Gavin Howarde723a152018-02-27 15:24:00 -0700859
Gavin Howard0011a3a2018-03-29 23:11:32 -0600860BcStatus bc_program_init(BcProgram *p, size_t line_len) {
Gavin Howard8a596d42018-01-15 15:46:01 -0700861
Gavin Howardd96bcae2018-02-07 19:16:19 -0700862 BcStatus s;
Gavin Howard91072cf2018-02-08 21:07:51 -0700863 size_t idx;
Gavin Howardf51b1892018-09-04 10:38:51 -0600864 char *main_name = NULL, *read_name = NULL;
Gavin Howard2534a812018-02-10 15:34:15 -0700865 BcInstPtr ip;
Gavin Howard8a596d42018-01-15 15:46:01 -0700866
Gavin Howard27fdfb92018-03-21 07:56:59 -0600867 assert(p);
Gavin Howard8a596d42018-01-15 15:46:01 -0700868
Gavin Howardf3cf0282018-09-21 10:54:58 -0600869 assert((unsigned long) sysconf(_SC_BC_BASE_MAX) <= BC_MAX_OBASE);
Gavin Howard84d19a62018-09-13 13:51:32 -0600870 assert((unsigned long) sysconf(_SC_BC_DIM_MAX) <= BC_MAX_DIM);
871 assert((unsigned long) sysconf(_SC_BC_SCALE_MAX) <= BC_MAX_SCALE);
872 assert((unsigned long) sysconf(_SC_BC_STRING_MAX) <= BC_MAX_STRING);
Gavin Howard57c843e2018-03-28 19:28:08 -0600873
Gavin Howard0011a3a2018-03-29 23:11:32 -0600874 p->nchars = p->scale = 0;
875 p->line_len = line_len;
Gavin Howard5d74e962018-02-26 13:44:13 -0700876
Gavin Howardb09d9b12018-03-23 09:57:07 -0600877 if ((s = bc_num_init(&p->ibase, BC_NUM_DEF_SIZE))) return s;
Gavin Howard5d74e962018-02-26 13:44:13 -0700878 bc_num_ten(&p->ibase);
879 p->ibase_t = 10;
880
Gavin Howardb09d9b12018-03-23 09:57:07 -0600881 if ((s = bc_num_init(&p->obase, BC_NUM_DEF_SIZE))) goto obase_err;
Gavin Howard5d74e962018-02-26 13:44:13 -0700882 bc_num_ten(&p->obase);
883 p->obase_t = 10;
884
Gavin Howardb09d9b12018-03-23 09:57:07 -0600885 if ((s = bc_num_init(&p->last, BC_NUM_DEF_SIZE))) goto last_err;
Gavin Howard43a027f2018-02-26 13:27:10 -0700886 bc_num_zero(&p->last);
Gavin Howardb5c77212018-02-14 17:12:34 -0700887
Gavin Howardb09d9b12018-03-23 09:57:07 -0600888 if ((s = bc_num_init(&p->zero, BC_NUM_DEF_SIZE))) goto zero_err;
Gavin Howard43a027f2018-02-26 13:27:10 -0700889 bc_num_zero(&p->zero);
Gavin Howard5a8949e2018-02-05 16:46:06 -0700890
Gavin Howardb09d9b12018-03-23 09:57:07 -0600891 if ((s = bc_num_init(&p->one, BC_NUM_DEF_SIZE))) goto one_err;
Gavin Howard43a027f2018-02-26 13:27:10 -0700892 bc_num_one(&p->one);
Gavin Howard645200e2018-02-15 15:33:35 -0700893
Gavin Howardb09d9b12018-03-23 09:57:07 -0600894 if ((s = bc_vec_init(&p->funcs, sizeof(BcFunc), bc_func_free))) goto func_err;
Gavin Howard8a596d42018-01-15 15:46:01 -0700895
Gavin Howardd96bcae2018-02-07 19:16:19 -0700896 s = bc_veco_init(&p->func_map, sizeof(BcEntry), bc_entry_free, bc_entry_cmp);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700897 if (s) goto func_map_err;
Gavin Howardd96bcae2018-02-07 19:16:19 -0700898
Gavin Howardff66e292018-03-28 12:47:58 -0600899 if (!(main_name = malloc(sizeof(bc_lang_func_main)))) {
Gavin Howard91072cf2018-02-08 21:07:51 -0700900 s = BC_STATUS_MALLOC_FAIL;
Gavin Howardd50539a2018-02-08 20:52:57 -0700901 goto name_err;
902 }
903
Gavin Howardb09d9b12018-03-23 09:57:07 -0600904 strcpy(main_name, bc_lang_func_main);
905 s = bc_program_addFunc(p, main_name, &idx);
Gavin Howardff526122018-09-06 19:58:52 -0600906 if (s || idx != BC_PROGRAM_MAIN) goto name_err;
Gavin Howard70610172018-09-05 20:30:08 -0600907 main_name = NULL;
Gavin Howardb42810f2018-03-09 10:07:58 -0700908
Gavin Howardff66e292018-03-28 12:47:58 -0600909 if (!(read_name = malloc(sizeof(bc_lang_func_read)))) {
Gavin Howarded392aa2018-02-27 13:09:26 -0700910 s = BC_STATUS_MALLOC_FAIL;
Gavin Howardff526122018-09-06 19:58:52 -0600911 goto name_err;
Gavin Howarded392aa2018-02-27 13:09:26 -0700912 }
913
Gavin Howardf456d372018-03-10 20:11:41 -0700914 strcpy(read_name, bc_lang_func_read);
Gavin Howard859e2902018-03-17 14:23:35 -0600915 s = bc_program_addFunc(p, read_name, &idx);
Gavin Howardff526122018-09-06 19:58:52 -0600916 if (s || idx != BC_PROGRAM_READ) goto name_err;
Gavin Howard70610172018-09-05 20:30:08 -0600917 read_name = NULL;
Gavin Howardb42810f2018-03-09 10:07:58 -0700918
Gavin Howardff526122018-09-06 19:58:52 -0600919 if ((s = bc_vec_init(&p->vars, sizeof(BcNum), bc_num_free))) goto name_err;
Gavin Howardd96bcae2018-02-07 19:16:19 -0700920 s = bc_veco_init(&p->var_map, sizeof(BcEntry), bc_entry_free, bc_entry_cmp);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700921 if (s) goto var_map_err;
Gavin Howardd96bcae2018-02-07 19:16:19 -0700922
Gavin Howardb09d9b12018-03-23 09:57:07 -0600923 if ((s = bc_vec_init(&p->arrays, sizeof(BcVec), bc_vec_free))) goto array_err;
Gavin Howardd96bcae2018-02-07 19:16:19 -0700924 s = bc_veco_init(&p->array_map, sizeof(BcEntry), bc_entry_free, bc_entry_cmp);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700925 if (s) goto array_map_err;
Gavin Howardd96bcae2018-02-07 19:16:19 -0700926
927 s = bc_vec_init(&p->strings, sizeof(char*), bc_string_free);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700928 if (s) goto string_err;
Gavin Howard5a8949e2018-02-05 16:46:06 -0700929
Gavin Howard4dbb65b2018-03-21 19:47:01 -0600930 s = bc_vec_init(&p->constants, sizeof(char*), bc_string_free);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700931 if (s) goto const_err;
Gavin Howarde46c6822018-02-08 20:05:39 -0700932
Gavin Howard0c0d1922018-03-21 17:00:21 -0600933 s = bc_vec_init(&p->results, sizeof(BcResult), bc_result_free);
Gavin Howard1ff37d12018-02-26 09:20:51 -0700934 if (s) goto expr_err;
Gavin Howard2534a812018-02-10 15:34:15 -0700935
Gavin Howardb09d9b12018-03-23 09:57:07 -0600936 if ((s = bc_vec_init(&p->stack, sizeof(BcInstPtr), NULL))) goto stack_err;
Gavin Howard2534a812018-02-10 15:34:15 -0700937
Gavin Howard0f32a852018-03-28 12:33:17 -0600938 memset(&ip, 0, sizeof(BcInstPtr));
Gavin Howard2534a812018-02-10 15:34:15 -0700939
Gavin Howarda628aa22018-09-12 13:52:45 -0600940 if ((s = bc_vec_push(&p->stack, 1, &ip))) goto push_err;
Gavin Howardd28e80a2018-01-23 18:24:18 -0700941
Gavin Howardd96bcae2018-02-07 19:16:19 -0700942 return s;
Gavin Howard8a596d42018-01-15 15:46:01 -0700943
Gavin Howarded392aa2018-02-27 13:09:26 -0700944push_err:
Gavin Howard602178d2018-02-09 12:03:28 -0700945 bc_vec_free(&p->stack);
Gavin Howard2534a812018-02-10 15:34:15 -0700946stack_err:
Gavin Howard0c0d1922018-03-21 17:00:21 -0600947 bc_vec_free(&p->results);
Gavin Howard2534a812018-02-10 15:34:15 -0700948expr_err:
Gavin Howarde46c6822018-02-08 20:05:39 -0700949 bc_vec_free(&p->constants);
Gavin Howarde46c6822018-02-08 20:05:39 -0700950const_err:
Gavin Howard5a8949e2018-02-05 16:46:06 -0700951 bc_vec_free(&p->strings);
Gavin Howard5a8949e2018-02-05 16:46:06 -0700952string_err:
Gavin Howardd96bcae2018-02-07 19:16:19 -0700953 bc_veco_free(&p->array_map);
Gavin Howardd96bcae2018-02-07 19:16:19 -0700954array_map_err:
Gavin Howard23fa3f32018-02-05 14:45:39 -0700955 bc_vec_free(&p->arrays);
Gavin Howard8a596d42018-01-15 15:46:01 -0700956array_err:
Gavin Howardd96bcae2018-02-07 19:16:19 -0700957 bc_veco_free(&p->var_map);
Gavin Howardd96bcae2018-02-07 19:16:19 -0700958var_map_err:
Gavin Howard23fa3f32018-02-05 14:45:39 -0700959 bc_vec_free(&p->vars);
Gavin Howardd50539a2018-02-08 20:52:57 -0700960name_err:
Gavin Howardd96bcae2018-02-07 19:16:19 -0700961 bc_veco_free(&p->func_map);
Gavin Howardd96bcae2018-02-07 19:16:19 -0700962func_map_err:
Gavin Howard23fa3f32018-02-05 14:45:39 -0700963 bc_vec_free(&p->funcs);
Gavin Howard8a596d42018-01-15 15:46:01 -0700964func_err:
Gavin Howard8389bb22018-02-15 17:40:34 -0700965 bc_num_free(&p->one);
Gavin Howardb5c77212018-02-14 17:12:34 -0700966one_err:
Gavin Howard8389bb22018-02-15 17:40:34 -0700967 bc_num_free(&p->zero);
Gavin Howardb5c77212018-02-14 17:12:34 -0700968zero_err:
Gavin Howard8389bb22018-02-15 17:40:34 -0700969 bc_num_free(&p->last);
Gavin Howard5d74e962018-02-26 13:44:13 -0700970last_err:
Gavin Howard5d74e962018-02-26 13:44:13 -0700971 bc_num_free(&p->obase);
Gavin Howard5d74e962018-02-26 13:44:13 -0700972obase_err:
Gavin Howard5d74e962018-02-26 13:44:13 -0700973 bc_num_free(&p->ibase);
Gavin Howardd96bcae2018-02-07 19:16:19 -0700974 return s;
Gavin Howard8a596d42018-01-15 15:46:01 -0700975}
976
Gavin Howard859e2902018-03-17 14:23:35 -0600977BcStatus bc_program_addFunc(BcProgram *p, char *name, size_t *idx) {
Gavin Howard3b90f9f2018-01-17 13:01:33 -0700978
Gavin Howardab67e7c2018-02-09 10:35:44 -0700979 BcStatus status;
Gavin Howardb09d9b12018-03-23 09:57:07 -0600980 BcEntry entry, *entry_ptr;
Gavin Howardab67e7c2018-02-09 10:35:44 -0700981 BcFunc f;
982
Gavin Howard27fdfb92018-03-21 07:56:59 -0600983 assert(p && name && idx);
Gavin Howard3b90f9f2018-01-17 13:01:33 -0700984
Gavin Howardab67e7c2018-02-09 10:35:44 -0700985 entry.name = name;
Gavin Howardded20a22018-03-07 15:15:19 -0700986 entry.idx = p->funcs.len;
Gavin Howardab67e7c2018-02-09 10:35:44 -0700987
Gavin Howardff526122018-09-06 19:58:52 -0600988 status = bc_veco_insert(&p->func_map, &entry, idx);
989
990 if (status) {
991 free(name);
992 if (status != BC_STATUS_VEC_ITEM_EXISTS) return status;
Gavin Howardb42810f2018-03-09 10:07:58 -0700993 }
Gavin Howardded20a22018-03-07 15:15:19 -0700994
995 entry_ptr = bc_veco_item(&p->func_map, *idx);
Gavin Howardded20a22018-03-07 15:15:19 -0700996 *idx = entry_ptr->idx;
Gavin Howardab67e7c2018-02-09 10:35:44 -0700997
Gavin Howardbdb2f922018-03-15 09:25:18 -0600998 if (status == BC_STATUS_VEC_ITEM_EXISTS) {
Gavin Howardab67e7c2018-02-09 10:35:44 -0700999
Gavin Howard0ed12322018-03-17 14:21:15 -06001000 BcFunc *func = bc_vec_item(&p->funcs, entry_ptr->idx);
Gavin Howarded392aa2018-02-27 13:09:26 -07001001 status = BC_STATUS_SUCCESS;
Gavin Howardab67e7c2018-02-09 10:35:44 -07001002
Gavin Howard90f13ad2018-03-17 13:32:16 -06001003 // We need to reset these, so the function can be repopulated.
Gavin Howardb09d9b12018-03-23 09:57:07 -06001004 func->nparams = 0;
Gavin Howard0eb2a372018-03-24 09:52:55 -06001005 bc_vec_npop(&func->autos, func->autos.len);
1006 bc_vec_npop(&func->code, func->code.len);
1007 bc_vec_npop(&func->labels, func->labels.len);
Gavin Howardab67e7c2018-02-09 10:35:44 -07001008 }
Gavin Howardded20a22018-03-07 15:15:19 -07001009 else {
Gavin Howardb09d9b12018-03-23 09:57:07 -06001010 if ((status = bc_func_init(&f))) return status;
Gavin Howarda628aa22018-09-12 13:52:45 -06001011 if ((status = bc_vec_push(&p->funcs, 1, &f))) bc_func_free(&f);
Gavin Howardded20a22018-03-07 15:15:19 -07001012 }
1013
1014 return status;
Gavin Howard8a596d42018-01-15 15:46:01 -07001015}
1016
Gavin Howard0dfe2922018-05-22 13:57:02 -06001017BcStatus bc_program_reset(BcProgram *p, BcStatus status) {
Gavin Howardf4167dc2018-03-26 14:19:28 -06001018
1019 BcFunc *func;
1020 BcInstPtr *ip;
1021
Gavin Howardabdc2d32018-03-27 08:01:08 -06001022 bc_vec_npop(&p->stack, p->stack.len - 1);
1023 bc_vec_npop(&p->results, p->results.len);
1024
Gavin Howardf4167dc2018-03-26 14:19:28 -06001025 func = bc_vec_item(&p->funcs, 0);
Gavin Howardf4167dc2018-03-26 14:19:28 -06001026 ip = bc_vec_top(&p->stack);
Gavin Howardf4167dc2018-03-26 14:19:28 -06001027 ip->idx = func->code.len;
1028
Gavin Howard3e8e6d12018-06-21 15:36:34 -06001029 if (!status && bcg.signe && !bcg.tty) return BC_STATUS_QUIT;
Gavin Howardabdc2d32018-03-27 08:01:08 -06001030
Gavin Howard0dfe2922018-05-22 13:57:02 -06001031 bcg.sigc += bcg.signe;
1032 bcg.signe = bcg.sig != bcg.sigc;
Gavin Howardabdc2d32018-03-27 08:01:08 -06001033
Gavin Howarda0492142018-08-29 15:28:35 -06001034 if (!status || status == BC_STATUS_EXEC_SIGNAL) {
1035 if (bcg.tty) {
1036 fprintf(stderr, "%s", bc_program_ready_prompt);
1037 fflush(stderr);
1038 status = BC_STATUS_SUCCESS;
1039 }
1040 else status = BC_STATUS_QUIT;
Gavin Howard46f0e422018-03-27 08:12:00 -06001041 }
1042
Gavin Howardabdc2d32018-03-27 08:01:08 -06001043 return status;
Gavin Howardf4167dc2018-03-26 14:19:28 -06001044}
1045
Gavin Howard8d1f1db2018-02-23 11:29:41 -07001046BcStatus bc_program_exec(BcProgram *p) {
Gavin Howard5b5dc572018-01-24 11:57:38 -07001047
Gavin Howardf51b1892018-09-04 10:38:51 -06001048 BcStatus status = BC_STATUS_SUCCESS;
1049 const char **string, *s;
Gavin Howard432b8b92018-08-09 10:53:03 -06001050 size_t idx, len, *addr;
Gavin Howard195706a2018-02-26 17:45:58 -07001051 BcResult result;
Gavin Howard432b8b92018-08-09 10:53:03 -06001052 BcResult *ptr;
1053 BcNum *num;
Gavin Howardf51b1892018-09-04 10:38:51 -06001054 bool cond = false;
1055 BcInstPtr *ip = bc_vec_top(&p->stack);
1056 BcFunc *func = bc_vec_item(&p->funcs, ip->func);
1057 uint8_t *code = func->code.array;
Gavin Howardb09d9b12018-03-23 09:57:07 -06001058
Gavin Howard2bb7bfc2018-03-28 15:19:45 -06001059 while (!status && !bcg.sig_other && ip->idx < func->code.len) {
Gavin Howard368459a2018-03-28 13:23:51 -06001060
Gavin Howardb09d9b12018-03-23 09:57:07 -06001061 uint8_t inst = code[(ip->idx)++];
Gavin Howard7c8dc772018-02-21 12:12:11 -07001062
Gavin Howard7c8dc772018-02-21 12:12:11 -07001063 switch (inst) {
1064
Gavin Howard195706a2018-02-26 17:45:58 -07001065 case BC_INST_CALL:
1066 {
1067 status = bc_program_call(p, code, &ip->idx);
1068 break;
1069 }
1070
1071 case BC_INST_RETURN:
1072 case BC_INST_RETURN_ZERO:
1073 {
1074 status = bc_program_return(p, inst);
1075 break;
1076 }
1077
Gavin Howard7c8dc772018-02-21 12:12:11 -07001078 case BC_INST_READ:
1079 {
1080 status = bc_program_read(p);
1081 break;
1082 }
1083
Gavin Howard195706a2018-02-26 17:45:58 -07001084 case BC_INST_JUMP_ZERO:
Gavin Howard170fcb02018-02-27 15:27:51 -07001085 {
Gavin Howard38bea812018-09-20 12:01:25 -06001086 if ((status = bc_program_unaryOpPrep(p, &ptr, &num, false)))
1087 return status;
Gavin Howard6a804cf2018-05-17 16:54:12 -06001088 cond = !bc_num_cmp(num, &p->zero);
Gavin Howard0eb2a372018-03-24 09:52:55 -06001089 bc_vec_pop(&p->results);
Gavin Howard170fcb02018-02-27 15:27:51 -07001090 }
Gavin Howard98d071b2018-03-19 19:36:23 -06001091 // Fallthrough.
Gavin Howard195706a2018-02-26 17:45:58 -07001092 case BC_INST_JUMP:
1093 {
Gavin Howard170fcb02018-02-27 15:27:51 -07001094 idx = bc_program_index(code, &ip->idx);
1095 addr = bc_vec_item(&func->labels, idx);
Gavin Howardb09d9b12018-03-23 09:57:07 -06001096 if (inst == BC_INST_JUMP || cond) ip->idx = *addr;
Gavin Howard195706a2018-02-26 17:45:58 -07001097 break;
1098 }
1099
1100 case BC_INST_PUSH_VAR:
Gavin Howard101b5e62018-03-26 07:46:27 -06001101 case BC_INST_PUSH_ARRAY_ELEM:
Gavin Howard375263e2018-09-04 15:10:18 -06001102 case BC_INST_PUSH_ARRAY:
Gavin Howard195706a2018-02-26 17:45:58 -07001103 {
Gavin Howard375263e2018-09-04 15:10:18 -06001104 status = bc_program_push(p, code, &ip->idx, inst);
Gavin Howard195706a2018-02-26 17:45:58 -07001105 break;
1106 }
1107
Gavin Howarda20bbd02018-09-12 11:52:52 -06001108 case BC_INST_PUSH_IBASE:
Gavin Howard195706a2018-02-26 17:45:58 -07001109 case BC_INST_PUSH_LAST:
Gavin Howarda20bbd02018-09-12 11:52:52 -06001110 case BC_INST_PUSH_OBASE:
Gavin Howard195706a2018-02-26 17:45:58 -07001111 {
Gavin Howarda20bbd02018-09-12 11:52:52 -06001112 result.type = inst - BC_INST_PUSH_IBASE + BC_RESULT_IBASE;
Gavin Howarda628aa22018-09-12 13:52:45 -06001113 status = bc_vec_push(&p->results, 1, &result);
Gavin Howard195706a2018-02-26 17:45:58 -07001114 break;
1115 }
1116
1117 case BC_INST_PUSH_SCALE:
1118 {
Gavin Howarda4a72892018-02-27 09:16:10 -07001119 status = bc_program_pushScale(p);
Gavin Howard195706a2018-02-26 17:45:58 -07001120 break;
1121 }
1122
Gavin Howard195706a2018-02-26 17:45:58 -07001123 case BC_INST_SCALE_FUNC:
1124 case BC_INST_LENGTH:
1125 case BC_INST_SQRT:
1126 {
1127 status = bc_program_builtin(p, inst);
1128 break;
1129 }
1130
1131 case BC_INST_PUSH_NUM:
1132 {
Gavin Howard195706a2018-02-26 17:45:58 -07001133 result.type = BC_RESULT_CONSTANT;
1134 result.data.id.idx = bc_program_index(code, &ip->idx);
Gavin Howarda628aa22018-09-12 13:52:45 -06001135 status = bc_vec_push(&p->results, 1, &result);
Gavin Howard195706a2018-02-26 17:45:58 -07001136 break;
1137 }
1138
Gavin Howard5c222e32018-02-28 13:06:41 -07001139 case BC_INST_POP:
1140 {
Gavin Howard0eb2a372018-03-24 09:52:55 -06001141 bc_vec_pop(&p->results);
Gavin Howard5c222e32018-02-28 13:06:41 -07001142 break;
1143 }
1144
Gavin Howard101b5e62018-03-26 07:46:27 -06001145 case BC_INST_INC_PRE:
1146 case BC_INST_DEC_PRE:
Gavin Howarde8bbdcc2018-09-06 13:06:02 -06001147 case BC_INST_INC_POST:
1148 case BC_INST_DEC_POST:
Gavin Howard195706a2018-02-26 17:45:58 -07001149 {
Gavin Howarde723a152018-02-27 15:24:00 -07001150 status = bc_program_incdec(p, inst);
Gavin Howard195706a2018-02-26 17:45:58 -07001151 break;
1152 }
1153
1154 case BC_INST_HALT:
1155 {
Gavin Howard01d6ec32018-03-17 19:42:02 -06001156 status = BC_STATUS_QUIT;
Gavin Howard195706a2018-02-26 17:45:58 -07001157 break;
1158 }
1159
Gavin Howard7c8dc772018-02-21 12:12:11 -07001160 case BC_INST_PRINT:
Gavin Howard152f3e82018-03-07 12:33:15 -07001161 case BC_INST_PRINT_EXPR:
Gavin Howard7c8dc772018-02-21 12:12:11 -07001162 {
Gavin Howard38bea812018-09-20 12:01:25 -06001163 if ((status = bc_program_unaryOpPrep(p, &ptr, &num, false)))
1164 return status;
Gavin Howarda883ad92018-02-22 13:34:41 -07001165
Gavin Howard45ff3572018-05-15 16:33:58 -06001166 status = bc_num_print(num, &p->obase, p->obase_t, inst == BC_INST_PRINT,
Gavin Howard0011a3a2018-03-29 23:11:32 -06001167 &p->nchars, p->line_len);
Gavin Howard534a9802018-02-28 13:06:09 -07001168 if (status) return status;
Gavin Howardb09d9b12018-03-23 09:57:07 -06001169 if ((status = bc_num_copy(&p->last, num))) return status;
Gavin Howarde351e9d2018-02-27 09:16:42 -07001170
Gavin Howard0eb2a372018-03-24 09:52:55 -06001171 bc_vec_pop(&p->results);
Gavin Howard7c8dc772018-02-21 12:12:11 -07001172
1173 break;
1174 }
1175
1176 case BC_INST_STR:
1177 {
Gavin Howard7c8dc772018-02-21 12:12:11 -07001178 idx = bc_program_index(code, &ip->idx);
Gavin Howardb09d9b12018-03-23 09:57:07 -06001179 assert(idx < p->strings.len);
Gavin Howard7c8dc772018-02-21 12:12:11 -07001180 string = bc_vec_item(&p->strings, idx);
Gavin Howard0271bfc2018-03-14 10:52:48 -06001181
Gavin Howardbc7cae82018-03-14 13:43:04 -06001182 s = *string;
1183 len = strlen(s);
1184
1185 for (idx = 0; idx < len; ++idx) {
1186 char c = s[idx];
Gavin Howarda141a0f2018-03-23 13:16:10 -06001187 if (putchar(c) == EOF) return BC_STATUS_IO_ERR;
Gavin Howardbc7cae82018-03-14 13:43:04 -06001188 if (c == '\n') p->nchars = SIZE_MAX;
1189 ++p->nchars;
1190 }
Gavin Howard7c8dc772018-02-21 12:12:11 -07001191
1192 break;
1193 }
1194
1195 case BC_INST_PRINT_STR:
1196 {
Gavin Howard7c8dc772018-02-21 12:12:11 -07001197 idx = bc_program_index(code, &ip->idx);
Gavin Howardb09d9b12018-03-23 09:57:07 -06001198 assert(idx < p->strings.len);
Gavin Howard7c8dc772018-02-21 12:12:11 -07001199 string = bc_vec_item(&p->strings, idx);
Gavin Howardbc7cae82018-03-14 13:43:04 -06001200 status = bc_program_printString(*string, &p->nchars);
Gavin Howard7c8dc772018-02-21 12:12:11 -07001201 break;
1202 }
1203
Gavin Howard101b5e62018-03-26 07:46:27 -06001204 case BC_INST_POWER:
1205 case BC_INST_MULTIPLY:
1206 case BC_INST_DIVIDE:
1207 case BC_INST_MODULUS:
1208 case BC_INST_PLUS:
1209 case BC_INST_MINUS:
Gavin Howard7c8dc772018-02-21 12:12:11 -07001210 {
1211 status = bc_program_op(p, inst);
1212 break;
1213 }
1214
Gavin Howard101b5e62018-03-26 07:46:27 -06001215 case BC_INST_REL_EQ:
1216 case BC_INST_REL_LE:
1217 case BC_INST_REL_GE:
1218 case BC_INST_REL_NE:
1219 case BC_INST_REL_LT:
1220 case BC_INST_REL_GT:
Gavin Howard195706a2018-02-26 17:45:58 -07001221 {
Gavin Howard2c154002018-02-27 16:43:26 -07001222 status = bc_program_logical(p, inst);
Gavin Howard195706a2018-02-26 17:45:58 -07001223 break;
1224 }
1225
Gavin Howard101b5e62018-03-26 07:46:27 -06001226 case BC_INST_BOOL_NOT:
Gavin Howard195706a2018-02-26 17:45:58 -07001227 {
Gavin Howard38bea812018-09-20 12:01:25 -06001228 if ((status = bc_program_unaryOpPrep(p, &ptr, &num, false)))
1229 return status;
Gavin Howarde8095352018-02-27 09:17:20 -07001230 status = bc_num_init(&result.data.num, BC_NUM_DEF_SIZE);
Gavin Howarde8095352018-02-27 09:17:20 -07001231 if (status) return status;
1232
Gavin Howard2444f272018-09-11 12:25:53 -06001233 if (!bc_num_cmp(num, &p->zero)) bc_num_one(&result.data.num);
Gavin Howarde8095352018-02-27 09:17:20 -07001234 else bc_num_zero(&result.data.num);
1235
Gavin Howard45ff3572018-05-15 16:33:58 -06001236 status = bc_program_unaryOpRetire(p, &result, BC_RESULT_TEMP);
Gavin Howarde8095352018-02-27 09:17:20 -07001237 if (status) bc_num_free(&result.data.num);
1238
Gavin Howard195706a2018-02-26 17:45:58 -07001239 break;
1240 }
1241
Gavin Howard101b5e62018-03-26 07:46:27 -06001242 case BC_INST_BOOL_OR:
1243 case BC_INST_BOOL_AND:
Gavin Howard195706a2018-02-26 17:45:58 -07001244 {
Gavin Howard2c154002018-02-27 16:43:26 -07001245 status = bc_program_logical(p, inst);
Gavin Howard195706a2018-02-26 17:45:58 -07001246 break;
1247 }
1248
Gavin Howard101b5e62018-03-26 07:46:27 -06001249 case BC_INST_NEG:
Gavin Howard7c8dc772018-02-21 12:12:11 -07001250 {
Gavin Howard3a947362018-03-02 11:25:48 -07001251 status = bc_program_negate(p);
Gavin Howard7c8dc772018-02-21 12:12:11 -07001252 break;
1253 }
1254
Gavin Howard101b5e62018-03-26 07:46:27 -06001255 case BC_INST_ASSIGN_POWER:
1256 case BC_INST_ASSIGN_MULTIPLY:
1257 case BC_INST_ASSIGN_DIVIDE:
1258 case BC_INST_ASSIGN_MODULUS:
1259 case BC_INST_ASSIGN_PLUS:
1260 case BC_INST_ASSIGN_MINUS:
1261 case BC_INST_ASSIGN:
Gavin Howard6d89b5d2018-02-26 13:21:34 -07001262 {
1263 status = bc_program_assign(p, inst);
1264 break;
1265 }
1266
Gavin Howard7c8dc772018-02-21 12:12:11 -07001267 default:
1268 {
Gavin Howard27fdfb92018-03-21 07:56:59 -06001269 assert(false);
Gavin Howardd11c2622018-02-21 21:16:46 -07001270 break;
Gavin Howard7c8dc772018-02-21 12:12:11 -07001271 }
1272 }
Gavin Howard195706a2018-02-26 17:45:58 -07001273
Gavin Howard0dfe2922018-05-22 13:57:02 -06001274 if ((status && status != BC_STATUS_QUIT) || bcg.signe)
1275 status = bc_program_reset(p, status);
Gavin Howard0a2a6742018-02-27 14:11:26 -07001276
Gavin Howard432b8b92018-08-09 10:53:03 -06001277 // We need to update because if the stack changes, pointers may be invalid.
Gavin Howard195706a2018-02-26 17:45:58 -07001278 ip = bc_vec_top(&p->stack);
Gavin Howard0a2a6742018-02-27 14:11:26 -07001279 func = bc_vec_item(&p->funcs, ip->func);
Gavin Howarda1ff02f2018-03-07 12:32:25 -07001280 code = func->code.array;
Gavin Howard7c8dc772018-02-21 12:12:11 -07001281 }
1282
1283 return status;
1284}
1285
Gavin Howard84590c82018-03-28 15:38:44 -06001286void bc_program_free(BcProgram *p) {
1287
Gavin Howardbc20f3d2018-09-11 10:26:04 -06001288 assert(p);
Gavin Howard84590c82018-03-28 15:38:44 -06001289
1290 bc_num_free(&p->ibase);
1291 bc_num_free(&p->obase);
1292
1293 bc_vec_free(&p->funcs);
1294 bc_veco_free(&p->func_map);
1295
1296 bc_vec_free(&p->vars);
1297 bc_veco_free(&p->var_map);
1298
1299 bc_vec_free(&p->arrays);
1300 bc_veco_free(&p->array_map);
1301
1302 bc_vec_free(&p->strings);
1303 bc_vec_free(&p->constants);
1304
1305 bc_vec_free(&p->results);
1306 bc_vec_free(&p->stack);
1307
1308 bc_num_free(&p->last);
1309 bc_num_free(&p->zero);
1310 bc_num_free(&p->one);
Gavin Howard84590c82018-03-28 15:38:44 -06001311}
1312
1313#ifndef NDEBUG
1314BcStatus bc_program_printIndex(uint8_t *code, size_t *start) {
1315
Gavin Howardf51b1892018-09-04 10:38:51 -06001316 uint8_t byte, i, bytes = code[(*start)++];
Gavin Howard4b62f4a2018-08-30 15:46:56 -06001317 unsigned long val = 0;
Gavin Howard24c675f2018-06-22 16:00:02 -06001318
Gavin Howard45ff3572018-05-15 16:33:58 -06001319 for (byte = 1, i = 0; byte && i < bytes; ++i) {
Gavin Howard84590c82018-03-28 15:38:44 -06001320 byte = code[(*start)++];
Gavin Howard4b62f4a2018-08-30 15:46:56 -06001321 if (byte) val |= ((unsigned long) byte) << (CHAR_BIT * sizeof(uint8_t) * i);
Gavin Howard84590c82018-03-28 15:38:44 -06001322 }
1323
Gavin Howard70610172018-09-05 20:30:08 -06001324 return printf(" (%lu) ", val) < 0 ? BC_STATUS_IO_ERR : BC_STATUS_SUCCESS;
Gavin Howard84590c82018-03-28 15:38:44 -06001325}
1326
1327BcStatus bc_program_printName(uint8_t *code, size_t *start) {
1328
Gavin Howard45ff3572018-05-15 16:33:58 -06001329 BcStatus status = BC_STATUS_SUCCESS;
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001330 char byte = (char) code[(*start)++];
Gavin Howard84590c82018-03-28 15:38:44 -06001331
Gavin Howard9d760632018-08-30 13:03:51 -06001332 if (printf(" (\"") < 0) status = BC_STATUS_IO_ERR;
1333
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001334 for (; byte && byte != ':'; byte = (char) code[(*start)++]) {
Gavin Howard84590c82018-03-28 15:38:44 -06001335 if (putchar(byte) == EOF) return BC_STATUS_IO_ERR;
Gavin Howard84590c82018-03-28 15:38:44 -06001336 }
1337
1338 assert(byte);
1339
Gavin Howard9d760632018-08-30 13:03:51 -06001340 if (printf("\") ") < 0) status = BC_STATUS_IO_ERR;
Gavin Howard84590c82018-03-28 15:38:44 -06001341
1342 return status;
1343}
1344
Gavin Howard67c38522018-02-27 16:00:49 -07001345BcStatus bc_program_print(BcProgram *p) {
Gavin Howard0a2a6742018-02-27 14:11:26 -07001346
Gavin Howardf51b1892018-09-04 10:38:51 -06001347 BcStatus status = BC_STATUS_SUCCESS;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001348 BcFunc *func;
1349 uint8_t *code;
Gavin Howardc0d9f312018-03-07 15:15:00 -07001350 BcInstPtr ip;
1351 size_t i;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001352
Gavin Howard8b3cb5b2018-03-28 15:20:15 -06001353 for (i = 0; !status && !bcg.sig_other && i < p->funcs.len; ++i) {
Gavin Howard0a2a6742018-02-27 14:11:26 -07001354
Gavin Howardf6e3fb32018-08-09 13:48:59 -06001355 bool sig;
1356
Gavin Howardb09d9b12018-03-23 09:57:07 -06001357 ip.idx = ip.len = 0;
Gavin Howardc0d9f312018-03-07 15:15:00 -07001358 ip.func = i;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001359
Gavin Howardc0d9f312018-03-07 15:15:00 -07001360 func = bc_vec_item(&p->funcs, ip.func);
Gavin Howardc0d9f312018-03-07 15:15:00 -07001361 code = func->code.array;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001362
Gavin Howard9d760632018-08-30 13:03:51 -06001363 if (printf("func[%zu]:\n", ip.func) < 0) return BC_STATUS_IO_ERR;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001364
Gavin Howardc0d9f312018-03-07 15:15:00 -07001365 while (ip.idx < func->code.len) {
Gavin Howard0a2a6742018-02-27 14:11:26 -07001366
Gavin Howardb09d9b12018-03-23 09:57:07 -06001367 uint8_t inst = code[ip.idx++];
Gavin Howard0a2a6742018-02-27 14:11:26 -07001368
Gavin Howardfd797192018-03-26 12:27:01 -06001369 if (putchar(bc_lang_inst_chars[inst]) == EOF) return BC_STATUS_IO_ERR;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001370
Gavin Howard375263e2018-09-04 15:10:18 -06001371 if (inst == BC_INST_PUSH_VAR || inst == BC_INST_PUSH_ARRAY_ELEM ||
1372 inst == BC_INST_PUSH_ARRAY)
1373 {
Gavin Howard642050e2018-03-26 12:25:45 -06001374 if ((status = bc_program_printName(code, &ip.idx))) return status;
1375 }
1376 else if (inst == BC_INST_PUSH_NUM || inst == BC_INST_CALL ||
1377 (inst >= BC_INST_STR && inst <= BC_INST_JUMP_ZERO))
1378 {
1379 if ((status = bc_program_printIndex(code, &ip.idx))) return status;
1380 if (inst == BC_INST_CALL && (status = bc_program_printIndex(code, &ip.idx)))
1381 return status;
Gavin Howard0a2a6742018-02-27 14:11:26 -07001382 }
1383 }
Gavin Howardc0d9f312018-03-07 15:15:00 -07001384
Gavin Howard9d760632018-08-30 13:03:51 -06001385 if (printf("\n\n") < 0) status = BC_STATUS_IO_ERR;
Gavin Howard368459a2018-03-28 13:23:51 -06001386
Gavin Howard0dfe2922018-05-22 13:57:02 -06001387 sig = bcg.sig != bcg.sigc;
1388 if (status || sig) status = bc_program_reset(p, status);
Gavin Howard0a2a6742018-02-27 14:11:26 -07001389 }
1390
Gavin Howard0a2a6742018-02-27 14:11:26 -07001391 return status;
1392}
Gavin Howard84590c82018-03-28 15:38:44 -06001393#endif // NDEBUG