1// This file is part of SmallBASIC
2//
3// bc module. Bytecode manipulation routines (bytecode segments API)
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2001 Nicholas Christopoulos
9
10#include "common/bc.h"
11#include "common/smbas.h"
12#include "common/str.h"
13
14/*
15 * string escape codes
16 */
17const char escapes[][2] = {
18 {'a', 0x07},
19 {'b', 0x08},
20 {'e', 0x1b},
21 {'f', 0x0c},
22 {'n', 0x0a},
23 {'r', 0x0d},
24 {'t', 0x09},
25 {'v', 0x0b},
26};
27
28/*
29 * whether the character is an escape
30 */
31int bc_is_escape(char c, char *value) {
32 int len = sizeof(escapes) / sizeof(escapes[0]);
33 int result = 0;
34 for (int i = 0; i < len && !result; i++) {
35 if (escapes[i][0] == c) {
36 *value = escapes[i][1];
37 result = 1;
38 }
39 }
40 return result;
41}
42
43/*
44 * whether the character is octal escape
45 */
46int bc_is_octal(char *str, char *output) {
47 char *next = str + 1;
48 int result = 0;
49 int digits = 0;
50 int value = 0;
51
52 while (isdigit(*next) && digits < 4) {
53 value = (value << 3) + (*next - '0');
54 digits++;
55 next++;
56 }
57 if (digits == 3 && value < 256) {
58 *output = value;
59 result = 1;
60 }
61 return result;
62}
63
64/*
65 * Create a bytecode segment
66 */
67void bc_create(bc_t *bc) {
68 bc->ptr = malloc(BC_ALLOC_INCR);
69 bc->size = BC_ALLOC_INCR;
70 bc->count = 0;
71 bc->cp = 0;
72 bc->eoc_position = 0;
73 bc->line_position = 0;
74}
75
76/*
77 * Destroy a bytecode segment
78 */
79void bc_destroy(bc_t *bc) {
80 free(bc->ptr);
81 bc->ptr = NULL;
82 bc->size = 0;
83 bc->count = 0;
84 bc->cp = 0;
85 bc->eoc_position = 0;
86 bc->line_position = 0;
87}
88
89/*
90 * Resize a bytecode segment
91 */
92void bc_resize(bc_t *bc, uint32_t new_size) {
93 if (new_size != bc->size) {
94 bc->ptr = realloc(bc->ptr, new_size);
95 bc->size = new_size;
96 if (new_size == 1) {
97 bc->count = 0;
98 }
99 }
100}
101
102/*
103 * add one command
104 */
105void bc_add1(bc_t *bc, char code) {
106 if (bc->count + sizeof(byte) >= bc->size) {
107 bc_resize(bc, bc->size + BC_ALLOC_INCR);
108 }
109 bc->ptr[bc->count] = code;
110 bc->count++;
111}
112
113/*
114 * add one uint32_t
115 */
116void bc_add_dword(bc_t *bc, uint32_t p1) {
117 if (bc->count + sizeof(uint32_t) >= bc->size) {
118 bc_resize(bc, bc->size + BC_ALLOC_INCR);
119 }
120 memcpy(bc->ptr + bc->count, &p1, sizeof(uint32_t));
121 bc->count += sizeof(uint32_t);
122}
123
124/*
125 * add one command and one long int (32 bits)
126 */
127void bc_add2l(bc_t *bc, byte code, bid_t p1) {
128 if (bc->count + sizeof(bid_t) >= bc->size) {
129 bc_resize(bc, bc->size + BC_ALLOC_INCR);
130 }
131 bc->ptr[bc->count] = code;
132 bc->count++;
133 memcpy(bc->ptr + bc->count, &p1, sizeof(bid_t));
134 bc->count += sizeof(bid_t);
135}
136
137/*
138 * add buildin function call
139 */
140void bc_add_fcode(bc_t *bc, bid_t idx) {
141 bc_add2l(bc, kwTYPE_CALLF, idx);
142}
143
144/*
145 * add buildin procedure call
146 */
147void bc_add_pcode(bc_t *bc, bid_t idx) {
148 bc_add2l(bc, kwTYPE_CALLP, idx);
149}
150
151/*
152 * add an external function-call
153 */
154void bc_add_extfcode(bc_t *bc, uint32_t lib, uint32_t idx) {
155 bc_add_code(bc, kwTYPE_CALLEXTF);
156 bc_add_dword(bc, lib);
157 bc_add_dword(bc, idx);
158}
159
160/*
161 * add an external procedure-call
162 */
163void bc_add_extpcode(bc_t *bc, uint32_t lib, uint32_t idx) {
164 bc_add_code(bc, kwTYPE_CALLEXTP);
165 bc_add_dword(bc, lib);
166 bc_add_dword(bc, idx);
167}
168
169/*
170 * add an address
171 */
172void bc_add_addr(bc_t *bc, bcip_t idx) {
173 if (bc->count + sizeof(bcip_t) >= bc->size) {
174 bc_resize(bc, bc->size + BC_ALLOC_INCR);
175 }
176 memcpy(bc->ptr + bc->count, &idx, sizeof(bcip_t));
177 bc->count += sizeof(bcip_t);
178}
179
180/*
181 * add a control code
182 *
183 * Control codes are followed by 2 bcip_t elements (jump on true, jump on false)
184 */
185void bc_add_ctrl(bc_t *bc, code_t code, bcip_t true_ip, bcip_t false_ip) {
186 bc_add1(bc, code);
187 bc_add_addr(bc, true_ip);
188 bc_add_addr(bc, false_ip);
189}
190
191/*
192 * add an integer
193 */
194void bc_add_cint(bc_t *bc, var_int_t v) {
195 if (bc->count + sizeof(var_int_t) >= bc->size) {
196 bc_resize(bc, bc->size + BC_ALLOC_INCR);
197 }
198 bc->ptr[bc->count] = kwTYPE_INT;
199 bc->count++;
200 memcpy(bc->ptr + bc->count, &v, sizeof(var_int_t));
201 bc->count += sizeof(var_int_t);
202}
203
204/*
205 * add an real
206 */
207void bc_add_creal(bc_t *bc, var_num_t v) {
208 if (bc->count + sizeof(var_num_t) >= bc->size) {
209 bc_resize(bc, bc->size + BC_ALLOC_INCR);
210 }
211 bc->ptr[bc->count] = kwTYPE_NUM;
212 bc->count++;
213 memcpy(bc->ptr + bc->count, &v, sizeof(var_num_t));
214 bc->count += sizeof(var_num_t);
215}
216
217/*
218 * add one command and one string (see: bc_store_string)
219 */
220void bc_add_strn(bc_t *bc, const char *str, int len) {
221 bc_add_code(bc, kwTYPE_STR);
222 bc_add_dword(bc, len + 1);
223 if (bc->count + len >= bc->size) {
224 bc_resize(bc, bc->size + BC_ALLOC_INCR + len);
225 }
226 memcpy(bc->ptr + bc->count, str, len);
227 bc->count += len;
228 bc_add1(bc, 0);
229}
230
231/*
232 * adds a string.
233 * returns a pointer of src to the next "element"
234 */
235char *bc_store_string(bc_t *bc, char *src) {
236 // skip past opening quotes
237 char *p = src + 1;
238 char *base = src + 1;
239 char escape = 0;
240 cstr cs;
241 cstr_init(&cs, 5);
242
243 while (*p) {
244 if ((*p == '\\' && ((*(p + 1) == '\"') || *(p + 1) == '\\'))
245 || *p == V_JOIN_LINE) {
246 // escaped quote " or escaped escape
247 cstr_append_i(&cs, base, p - base);
248
249 if (*p == V_JOIN_LINE) {
250 // skip null newline
251 comp_line++;
252 if (*(p+1) == '\"') {
253 base = ++p;
254 continue;
255 }
256 }
257
258 // include " (or \ ) in next segment
259 base = ++p;
260 } else if (*p == '\\' && bc_is_escape(*(p + 1), &escape)) {
261 char code[] = {escape, '\0'};
262 cstr_append_i(&cs, base, p - base);
263 cstr_append_i(&cs, code, 1);
264 // skip single escape character
265 base = (++p) + 1;
266 } else if (*p == '\\' && bc_is_octal(p, &escape)) {
267 char code[] = {escape, '\0'};
268 cstr_append_i(&cs, base, p - base);
269 cstr_append_i(&cs, code, 1);
270 // skip octal digits
271 p += 3;
272 base = p + 1;
273 } else if (*p == V_QUOTE) {
274 // revert hidden quote
275 *p = '\"';
276 } else if (*p == V_LINE) {
277 // revert hidden newline
278 comp_line++;
279 *p = '\n';
280 } else if (*p == '\"') {
281 // end of string detected
282 cstr_append_i(&cs, base, p - base);
283 bc_add_strn(bc, cs.buf, cs.length);
284 p++;
285 break;
286 }
287 p++;
288 }
289 free(cs.buf);
290 return p;
291}
292
293/*
294 * adds an EOC mark at the current position
295 */
296void bc_eoc(bc_t *bc) {
297 if (bc && bc->count &&
298 (bc->eoc_position == 0 || bc->eoc_position != bc->count - 1)) {
299 // avoid appending multiple kwTYPE_EOCs (or kwTYPE_LINE)
300 bc->eoc_position = bc->count;
301 bc_add1(bc, kwTYPE_EOC);
302 }
303}
304
305/*
306 * pops any EOC mark at the current position
307 */
308int bc_pop_eoc(bc_t *bc) {
309 int result;
310 if (bc->eoc_position > 0 && bc->eoc_position == bc->count - 1) {
311 bc->eoc_position = 0;
312 result = (bc->ptr[--bc->count] == kwTYPE_EOC);
313 } else {
314 result = 0;
315 }
316 return result;
317}
318
319/*
320 * appends the src to dst
321 */
322void bc_append(bc_t *dst, bc_t *src) {
323 bc_resize(dst, dst->count + src->count + 4);
324 memcpy(dst->ptr + dst->count, src->ptr, src->count);
325 dst->count += src->count;
326}
327
328/*
329 * appends n bytes from src to dst
330 */
331void bc_add_n(bc_t *dst, byte *src, uint32_t n) {
332 if (dst->count + n >= dst->size) {
333 bc_resize(dst, dst->size + BC_ALLOC_INCR + n);
334 }
335 memcpy(dst->ptr + dst->count, src, n);
336 dst->count += n;
337}
338