rts-stowed.c
1 //! @file rts-stowed.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! Interpreter routines for STOWED values.
25
26 #include "a68g.h"
27 #include "a68g-genie.h"
28 #include "a68g-prelude.h"
29
30 // Routines for handling stowed objects.
31 //
32 // An A68G row is a reference to a descriptor in the heap:
33 //
34 // ...
35 // A68_REF row -> A68_ARRAY ----+ ARRAY: Description of row, ref to elements.
36 // A68_TUPLE 1 | TUPLE: Bounds, one for every dimension.
37 // ... |
38 // A68_TUPLE dim |
39 // ... |
40 // ... |
41 // Element 1 <---+ Sequential row elements in the heap.
42 // ...
43 // Element n
44
45 //! @brief Size of a row.
46
47 int get_row_size (A68_TUPLE * tup, int dim)
48 {
49 int span = 1;
50 for (int k = 0; k < dim; k++) {
51 int stride = ROW_SIZE (&tup[k]);
52 ABEND ((stride > 0 && span > A68_MAX_INT / stride), ERROR_INVALID_SIZE, __func__);
53 span *= stride;
54 }
55 return span;
56 }
57
58 //! @brief Initialise index for FORALL constructs.
59
60 void initialise_internal_index (A68_TUPLE * tup, int dim)
61 {
62 for (int k = 0; k < dim; k++) {
63 A68_TUPLE *ref = &tup[k];
64 K (ref) = LWB (ref);
65 }
66 }
67
68 //! @brief Calculate index.
69
70 ADDR_T calculate_internal_index (A68_TUPLE * tup, int dim)
71 {
72 ADDR_T idx = 0;
73 for (int k = 0; k < dim; k++) {
74 A68_TUPLE *ref = &tup[k];
75 // Only consider non-empty rows.
76 if (ROW_SIZE (ref) > 0) {
77 idx += (SPAN (ref) * K (ref) - SHIFT (ref));
78 }
79 }
80 return idx;
81 }
82
83 //! @brief Increment index for FORALL constructs.
84
85 BOOL_T increment_internal_index (A68_TUPLE * tup, int dim)
86 {
87 BOOL_T carry = A68_TRUE;
88 for (int k = dim - 1; k >= 0 && carry; k--) {
89 A68_TUPLE *ref = &tup[k];
90 if (K (ref) < UPB (ref)) {
91 (K (ref))++;
92 carry = A68_FALSE;
93 } else {
94 K (ref) = LWB (ref);
95 }
96 }
97 return carry;
98 }
99
100 //! @brief Print index.
101
102 void print_internal_index (FILE_T f, A68_TUPLE * tup, int dim)
103 {
104 for (int k = 0; k < dim; k++) {
105 A68_TUPLE *ref = &tup[k];
106 BUFFER buf;
107 BUFCLR (buf);
108 ASSERT (a68_bufprt (buf, SNPRINTF_SIZE, A68_LD, K (ref)) >= 0);
109 WRITE (f, buf);
110 if (k < dim - 1) {
111 WRITE (f, ", ");
112 }
113 }
114 }
115
116 //! @brief Convert C string to A68 [] CHAR.
117
118 A68_REF c_string_to_row_char (NODE_T * p, char *str, int width)
119 {
120 A68_REF z, row; A68_ARRAY arr; A68_TUPLE tup;
121 NEW_ROW_1D (z, row, arr, tup, M_ROW_CHAR, M_CHAR, width);
122 BYTE_T *base = ADDRESS (&row);
123 int len = strlen (str);
124 for (int k = 0; k < width; k++) {
125 A68_CHAR *ch = (A68_CHAR *) & (base[k * SIZE_ALIGNED (A68_CHAR)]);
126 STATUS (ch) = INIT_MASK;
127 VALUE (ch) = (k < len ? TO_UCHAR (str[k]) : NULL_CHAR);
128 }
129 return z;
130 }
131
132 //! @brief Convert C string to A68 string.
133
134 A68_REF c_to_a_string (NODE_T * p, char *str, int width)
135 {
136 if (str == NO_TEXT) {
137 return empty_string (p);
138 } else {
139 if (width == DEFAULT_WIDTH) {
140 return c_string_to_row_char (p, str, (int) strlen (str));
141 } else {
142 return c_string_to_row_char (p, str, (int) width);
143 }
144 }
145 }
146
147 //! @brief Size of a string.
148
149 int a68_string_size (NODE_T * p, A68_REF row)
150 {
151 (void) p;
152 if (INITIALISED (&row)) {
153 A68_ARRAY *arr; A68_TUPLE *tup;
154 GET_DESCRIPTOR (arr, tup, &row);
155 return ROW_SIZE (tup);
156 } else {
157 return 0;
158 }
159 }
160
161 //! @brief Convert A68 string to C string.
162
163 char *a_to_c_string (NODE_T * p, char *str, A68_REF row)
164 {
165 // Assume "str" to be long enough - caller's responsibility!.
166 (void) p;
167 if (INITIALISED (&row)) {
168 A68_ARRAY *arr; A68_TUPLE *tup;
169 GET_DESCRIPTOR (arr, tup, &row);
170 int size = ROW_SIZE (tup), n = 0;
171 if (size > 0) {
172 BYTE_T *base_address = ADDRESS (&ARRAY (arr));
173 for (int k = LWB (tup); k <= UPB (tup); k++) {
174 int addr = INDEX_1_DIM (arr, tup, k);
175 A68_CHAR *ch = (A68_CHAR *) & (base_address[addr]);
176 CHECK_INIT (p, INITIALISED (ch), M_CHAR);
177 str[n++] = (char) VALUE (ch);
178 }
179 }
180 str[n] = NULL_CHAR;
181 return str;
182 } else {
183 return NO_TEXT;
184 }
185 }
186
187 //! @brief Return an empty row.
188
189 A68_REF empty_row (NODE_T * p, MOID_T * m_row)
190 {
191 if (IS_FLEX (m_row)) {
192 m_row = SUB (m_row);
193 }
194 MOID_T *m_elem = SUB (m_row);
195 int dim = DIM (m_row);
196 A68_REF dsc; A68_ARRAY *arr; A68_TUPLE *tup;
197 dsc = heap_generator (p, m_row, DESCRIPTOR_SIZE (dim));
198 GET_DESCRIPTOR (arr, tup, &dsc);
199 DIM (arr) = dim;
200 MOID (arr) = SLICE (m_row);
201 ELEM_SIZE (arr) = moid_size (SLICE (m_row));
202 SLICE_OFFSET (arr) = 0;
203 FIELD_OFFSET (arr) = 0;
204 if (IS_ROW (m_elem) || IS_FLEX (m_elem)) {
205 // [] AMODE or FLEX [] AMODE
206 ARRAY (arr) = heap_generator (p, m_elem, A68_REF_SIZE);
207 *DEREF (A68_REF, &ARRAY (arr)) = empty_row (p, m_elem);
208 } else {
209 ARRAY (arr) = nil_ref;
210 }
211 STATUS (&ARRAY (arr)) = (STATUS_MASK_T) (INIT_MASK | IN_HEAP_MASK);
212 for (int k = 0; k < dim; k++) {
213 LWB (&tup[k]) = 1;
214 UPB (&tup[k]) = 0;
215 SPAN (&tup[k]) = 1;
216 SHIFT (&tup[k]) = LWB (tup);
217 }
218 return dsc;
219 }
220
221 //! @brief An empty string, FLEX [1 : 0] CHAR.
222
223 A68_REF empty_string (NODE_T * p)
224 {
225 return empty_row (p, M_STRING);
226 }
227
228 //! @brief Make [,, ..] MODE from [, ..] MODE.
229
230 A68_REF genie_make_rowrow (NODE_T *p, MOID_T * m_row, int len, ADDR_T pop_sp)
231 {
232 MOID_T *m_deflex = IS_FLEX (m_row) ? SUB (m_row) : m_row;
233 int old_dim = DIM (m_deflex) - 1;
234 // Make the new descriptor.
235 A68_ARRAY *new_arr; A68_TUPLE *new_tup;
236 A68_REF new_row = heap_generator (p, m_row, DESCRIPTOR_SIZE (DIM (m_deflex)));
237 GET_DESCRIPTOR (new_arr, new_tup, &new_row);
238 DIM (new_arr) = DIM (m_deflex);
239 MOID_T *m_elem = SUB (m_deflex);
240 MOID (new_arr) = m_elem;
241 ELEM_SIZE (new_arr) = SIZE (m_elem);
242 SLICE_OFFSET (new_arr) = 0;
243 FIELD_OFFSET (new_arr) = 0;
244 if (len == 0) {
245 // There is a vacuum on the stack.
246 for (int k = 0; k < old_dim; k++) {
247 LWB (&new_tup[k + 1]) = 1;
248 UPB (&new_tup[k + 1]) = 0;
249 SPAN (&new_tup[k + 1]) = 1;
250 SHIFT (&new_tup[k + 1]) = LWB (&new_tup[k + 1]);
251 }
252 LWB (new_tup) = 1;
253 UPB (new_tup) = 0;
254 SPAN (new_tup) = 0;
255 SHIFT (new_tup) = 0;
256 ARRAY (new_arr) = nil_ref;
257 return new_row;
258 } else if (len > 0) {
259 A68_ARRAY *tmp = NO_ARRAY;
260 // Arrays in the stack must have equal bounds.
261 A68_REF row_0 = *(A68_REF *) STACK_ADDRESS (pop_sp);
262 A68_TUPLE *tup_0;
263 GET_DESCRIPTOR (tmp, tup_0, &row_0);
264 for (int j = 1; j < len; j++) {
265 A68_REF row_j = *(A68_REF *) STACK_ADDRESS (pop_sp + j * A68_REF_SIZE);
266 A68_TUPLE *tup_j;
267 GET_DESCRIPTOR (tmp, tup_j, &row_j);
268 for (int k = 0; k < old_dim; k++) {
269 if ((UPB (&tup_0[k]) != UPB (&tup_j[k])) || (LWB (&tup_0[k]) != LWB (&tup_j[k]))) {
270 diagnostic (A68_RUNTIME_ERROR, p, ERROR_DIFFERENT_BOUNDS);
271 exit_genie (p, A68_RUNTIME_ERROR);
272 }
273 }
274 }
275 // Fill descriptor of new row with info from (arbitrary) first one.
276 A68_ARRAY *old_arr; A68_TUPLE *old_tup;
277 A68_REF old_row = *(A68_REF *) STACK_ADDRESS (pop_sp);
278 GET_DESCRIPTOR (tmp, old_tup, &old_row);
279 int span = 1;
280 for (int k = 0; k < old_dim; k++) {
281 A68_TUPLE *tup = &new_tup[k + 1];
282 LWB (tup) = LWB (&old_tup[k]);
283 UPB (tup) = UPB (&old_tup[k]);
284 SPAN (tup) = span;
285 SHIFT (tup) = LWB (tup) * SPAN (tup);
286 span *= ROW_SIZE (tup);
287 }
288 LWB (new_tup) = 1;
289 UPB (new_tup) = len;
290 SPAN (new_tup) = span;
291 SHIFT (new_tup) = LWB (new_tup) * SPAN (new_tup);
292 ARRAY (new_arr) = heap_generator_2 (p, m_row, len, span * ELEM_SIZE (new_arr));
293 for (int j = 0; j < len; j++) {
294 // Copy new[j,, ] := old[, ].
295 GET_DESCRIPTOR (old_arr, old_tup, (A68_REF *) STACK_ADDRESS (pop_sp + j * A68_REF_SIZE));
296 if (LWB (old_tup) > UPB (old_tup)) {
297 A68_REF dst = ARRAY (new_arr);
298 ADDR_T new_k = j * SPAN (new_tup) + calculate_internal_index (&new_tup[1], old_dim);
299 OFFSET (&dst) += ROW_ELEMENT (new_arr, new_k);
300 A68_REF clone = empty_row (p, SLICE (m_row));
301 MOVE (ADDRESS (&dst), ADDRESS (&clone), SIZE (m_elem));
302 } else {
303 initialise_internal_index (old_tup, old_dim);
304 initialise_internal_index (&new_tup[1], old_dim);
305 BOOL_T done = A68_FALSE;
306 while (!done) {
307 A68_REF src = ARRAY (old_arr), dst = ARRAY (new_arr);
308 ADDR_T old_k = calculate_internal_index (old_tup, old_dim);
309 ADDR_T new_k = j * SPAN (new_tup) + calculate_internal_index (&new_tup[1], old_dim);
310 OFFSET (&src) += ROW_ELEMENT (old_arr, old_k);
311 OFFSET (&dst) += ROW_ELEMENT (new_arr, new_k);
312 if (HAS_ROWS (m_elem)) {
313 A68_REF clone = genie_clone (p, m_elem, (A68_REF *) & nil_ref, &src);
314 MOVE (ADDRESS (&dst), ADDRESS (&clone), SIZE (m_elem));
315 } else {
316 MOVE (ADDRESS (&dst), ADDRESS (&src), SIZE (m_elem));
317 }
318 done = increment_internal_index (old_tup, old_dim) | increment_internal_index (&new_tup[1], old_dim);
319 }
320 }
321 }
322 }
323 return new_row;
324 }
325
326 //! @brief Make a row of 'len' objects that are in the stack.
327
328 A68_REF genie_make_row (NODE_T * p, MOID_T * m_elem, int len, ADDR_T pop_sp)
329 {
330 A68_REF new_row, new_arr; A68_ARRAY arr; A68_TUPLE tup;
331 NEW_ROW_1D (new_row, new_arr, arr, tup, MOID (p), m_elem, len);
332 for (int k = 0; k < len * ELEM_SIZE (&arr); k += ELEM_SIZE (&arr)) {
333 A68_REF dst = new_arr, src;
334 OFFSET (&dst) += k;
335 STATUS (&src) = (STATUS_MASK_T) (INIT_MASK | IN_STACK_MASK);
336 OFFSET (&src) = pop_sp + k;
337 REF_HANDLE (&src) = (A68_HANDLE *) & nil_handle;
338 if (HAS_ROWS (m_elem)) {
339 A68_REF clone = genie_clone (p, m_elem, (A68_REF *) & nil_ref, &src);
340 MOVE (ADDRESS (&dst), ADDRESS (&clone), SIZE (m_elem));
341 } else {
342 MOVE (ADDRESS (&dst), ADDRESS (&src), SIZE (m_elem));
343 }
344 }
345 return new_row;
346 }
347
348 //! @brief Make REF [1 : 1] [] MODE from REF [] MODE.
349
350 A68_REF genie_make_ref_row_of_row (NODE_T * p, MOID_T * m_dst, MOID_T * m_src, ADDR_T pop_sp)
351 {
352 m_dst = DEFLEX (m_dst);
353 m_src = DEFLEX (m_src);
354 A68_REF array = *(A68_REF *) STACK_ADDRESS (pop_sp);
355 // ROWING NIL yields NIL.
356 if (IS_NIL (array)) {
357 return nil_ref;
358 } else {
359 A68_REF new_row = heap_generator (p, SUB (m_dst), DESCRIPTOR_SIZE (1));
360 A68_REF name = heap_generator (p, m_dst, A68_REF_SIZE);
361 A68_ARRAY *arr; A68_TUPLE *tup;
362 GET_DESCRIPTOR (arr, tup, &new_row);
363 DIM (arr) = 1;
364 MOID (arr) = m_src;
365 ELEM_SIZE (arr) = SIZE (m_src);
366 SLICE_OFFSET (arr) = 0;
367 FIELD_OFFSET (arr) = 0;
368 ARRAY (arr) = array;
369 LWB (tup) = 1;
370 UPB (tup) = 1;
371 SPAN (tup) = 1;
372 SHIFT (tup) = LWB (tup);
373 *DEREF (A68_REF, &name) = new_row;
374 return name;
375 }
376 }
377
378 //! @brief Make REF [1 : 1, ..] MODE from REF [..] MODE.
379
380 A68_REF genie_make_ref_row_row (NODE_T * p, MOID_T * m_dst, MOID_T * m_src, ADDR_T pop_sp)
381 {
382 m_dst = DEFLEX (m_dst);
383 m_src = DEFLEX (m_src);
384 A68_REF name = *(A68_REF *) STACK_ADDRESS (pop_sp);
385 // ROWING NIL yields NIL.
386 if (IS_NIL (name)) {
387 return nil_ref;
388 }
389 A68_REF old_row = *DEREF (A68_REF, &name); A68_TUPLE *new_tup, *old_tup;
390 A68_ARRAY *old_arr;
391 GET_DESCRIPTOR (old_arr, old_tup, &old_row);
392 // Make new descriptor.
393 A68_REF new_row = heap_generator (p, m_dst, DESCRIPTOR_SIZE (DIM (SUB (m_dst))));
394 A68_ARRAY *new_arr;
395 name = heap_generator (p, m_dst, A68_REF_SIZE);
396 GET_DESCRIPTOR (new_arr, new_tup, &new_row);
397 DIM (new_arr) = DIM (SUB (m_dst));
398 MOID (new_arr) = MOID (old_arr);
399 ELEM_SIZE (new_arr) = ELEM_SIZE (old_arr);
400 SLICE_OFFSET (new_arr) = 0;
401 FIELD_OFFSET (new_arr) = 0;
402 ARRAY (new_arr) = ARRAY (old_arr);
403 // Fill out the descriptor.
404 LWB (&(new_tup[0])) = 1;
405 UPB (&(new_tup[0])) = 1;
406 SPAN (&(new_tup[0])) = 1;
407 SHIFT (&(new_tup[0])) = LWB (&(new_tup[0]));
408 for (int k = 0; k < DIM (SUB (m_src)); k++) {
409 new_tup[k + 1] = old_tup[k];
410 }
411 // Yield the new name.
412 *DEREF (A68_REF, &name) = new_row;
413 return name;
414 }
415
416 //! @brief Coercion to [1 : 1, ] MODE.
417
418 PROP_T genie_rowing_row_row (NODE_T * p)
419 {
420 ADDR_T pop_sp = A68_SP;
421 GENIE_UNIT_NO_GC (SUB (p));
422 STACK_DNS (p, MOID (SUB (p)), A68_FP);
423 A68_REF row = genie_make_rowrow (p, MOID (p), 1, pop_sp);
424 A68_SP = pop_sp;
425 PUSH_REF (p, row);
426 return GPROP (p);
427 }
428
429 //! @brief Coercion to [1 : 1] [] MODE.
430
431 PROP_T genie_rowing_row_of_row (NODE_T * p)
432 {
433 ADDR_T pop_sp = A68_SP;
434 GENIE_UNIT_NO_GC (SUB (p));
435 STACK_DNS (p, MOID (SUB (p)), A68_FP);
436 A68_REF row = genie_make_row (p, SLICE (MOID (p)), 1, pop_sp);
437 A68_SP = pop_sp;
438 PUSH_REF (p, row);
439 return GPROP (p);
440 }
441
442 //! @brief Coercion to REF [1 : 1, ..] MODE.
443
444 PROP_T genie_rowing_ref_row_row (NODE_T * p)
445 {
446 ADDR_T pop_sp = A68_SP;
447 MOID_T *dst = MOID (p), *src = MOID (SUB (p));
448 GENIE_UNIT_NO_GC (SUB (p));
449 STACK_DNS (p, MOID (SUB (p)), A68_FP);
450 A68_SP = pop_sp;
451 A68_REF name = genie_make_ref_row_row (p, dst, src, pop_sp);
452 PUSH_REF (p, name);
453 return GPROP (p);
454 }
455
456 //! @brief REF [1 : 1] [] MODE from [] MODE
457
458 PROP_T genie_rowing_ref_row_of_row (NODE_T * p)
459 {
460 ADDR_T pop_sp = A68_SP;
461 MOID_T *m_dst = MOID (p), *src = MOID (SUB (p));
462 GENIE_UNIT_NO_GC (SUB (p));
463 STACK_DNS (p, MOID (SUB (p)), A68_FP);
464 A68_SP = pop_sp;
465 A68_REF name = genie_make_ref_row_of_row (p, m_dst, src, pop_sp);
466 PUSH_REF (p, name);
467 return GPROP (p);
468 }
469
470 //! @brief Rowing coercion.
471
472 PROP_T genie_rowing (NODE_T * p)
473 {
474 PROP_T self;
475 if (IS_REF (MOID (p))) {
476 // REF ROW, decide whether we want A->[] A or [] A->[,] A.
477 MOID_T *mode = SUB_MOID (p);
478 if (DIM (DEFLEX (mode)) >= 2) {
479 (void) genie_rowing_ref_row_row (p);
480 UNIT (&self) = genie_rowing_ref_row_row;
481 SOURCE (&self) = p;
482 } else {
483 (void) genie_rowing_ref_row_of_row (p);
484 UNIT (&self) = genie_rowing_ref_row_of_row;
485 SOURCE (&self) = p;
486 }
487 } else {
488 // ROW, decide whether we want A->[] A or [] A->[,] A.
489 if (DIM (DEFLEX (MOID (p))) >= 2) {
490 (void) genie_rowing_row_row (p);
491 UNIT (&self) = genie_rowing_row_row;
492 SOURCE (&self) = p;
493 } else {
494 (void) genie_rowing_row_of_row (p);
495 UNIT (&self) = genie_rowing_row_of_row;
496 SOURCE (&self) = p;
497 }
498 }
499 return self;
500 }
501
502 //! @brief Clone a compounded value referred to by 'old'.
503
504 A68_REF genie_clone (NODE_T * p, MOID_T * m, A68_REF * tmp, A68_REF * old)
505 {
506 // This complex routine is needed as arrays are not always contiguous.
507 // The routine takes a REF to the value and returns a REF to the clone.
508 if (m == M_SOUND) {
509 // REF SOUND.
510 A68_REF new_snd = heap_generator (p, m, SIZE (m));
511 A68_SOUND *w = DEREF (A68_SOUND, &new_snd);
512 int size = A68_SOUND_DATA_SIZE (w);
513 COPY ((BYTE_T *) w, ADDRESS (old), SIZE (M_SOUND));
514 BYTE_T *owd = ADDRESS (&(DATA (w)));
515 DATA (w) = heap_generator (p, M_SOUND_DATA, size);
516 COPY (ADDRESS (&(DATA (w))), owd, size);
517 return new_snd;
518 } else if (IS_STRUCT (m)) {
519 // REF STRUCT.
520 A68_REF new_str = heap_generator (p, m, SIZE (m));
521 for (PACK_T *field = PACK (m); field != NO_PACK; FORWARD (field)) {
522 MOID_T *m_f = MOID (field);
523 A68_REF old_f = *old, new_f = new_str;
524 OFFSET (&old_f) += OFFSET (field);
525 OFFSET (&new_f) += OFFSET (field);
526 A68_REF tmp_f = *tmp;
527 if (!IS_NIL (tmp_f)) {
528 OFFSET (&tmp_f) += OFFSET (field);
529 }
530 if (HAS_ROWS (m_f)) {
531 A68_REF clone = genie_clone (p, m_f, &tmp_f, &old_f);
532 MOVE (ADDRESS (&new_f), ADDRESS (&clone), SIZE (m_f));
533 } else {
534 MOVE (ADDRESS (&new_f), ADDRESS (&old_f), SIZE (m_f));
535 }
536 }
537 return new_str;
538 } else if (IS_UNION (m)) {
539 // REF UNION.
540 A68_REF new_uni = heap_generator (p, m, SIZE (m));
541 A68_REF src = *old;
542 A68_UNION *u = DEREF (A68_UNION, &src);
543 MOID_T *m_u = (MOID_T *) VALUE (u);
544 OFFSET (&src) += UNION_OFFSET;
545 A68_REF dst = new_uni;
546 *DEREF (A68_UNION, &dst) = *u;
547 OFFSET (&dst) += UNION_OFFSET;
548 // A union has formal members, so 'tmp' is irrelevant.
549 A68_REF tmp_u = nil_ref;
550 if (m_u != NO_MOID && HAS_ROWS (m_u)) {
551 A68_REF clone = genie_clone (p, m_u, &tmp_u, &src);
552 MOVE (ADDRESS (&dst), ADDRESS (&clone), SIZE (m_u));
553 } else if (m_u != NO_MOID) {
554 MOVE (ADDRESS (&dst), ADDRESS (&src), SIZE (m_u));
555 }
556 return new_uni;
557 } else if (IS_FLEXETY_ROW (m)) {
558 // REF [FLEX] [].
559 MOID_T *em = SUB (IS_FLEX (m) ? SUB (m) : m);
560 // Make new array.
561 A68_ARRAY *old_arr; A68_TUPLE *old_tup;
562 GET_DESCRIPTOR (old_arr, old_tup, DEREF (A68_REF, old));
563 A68_ARRAY *new_arr; A68_TUPLE *new_tup;
564 A68_REF nrow = heap_generator (p, m, DESCRIPTOR_SIZE (DIM (old_arr)));
565 GET_DESCRIPTOR (new_arr, new_tup, &nrow);
566 DIM (new_arr) = DIM (old_arr);
567 MOID (new_arr) = MOID (old_arr);
568 ELEM_SIZE (new_arr) = ELEM_SIZE (old_arr);
569 SLICE_OFFSET (new_arr) = 0;
570 FIELD_OFFSET (new_arr) = 0;
571 // Get size and copy bounds; check in case of a row.
572 // This is just song and dance to comply with the RR.
573 BOOL_T check_bounds = A68_FALSE;
574 A68_REF ntmp; A68_ARRAY *tarr; A68_TUPLE *ttup = NO_TUPLE;
575 if (IS_NIL (*tmp)) {
576 ntmp = nil_ref;
577 } else {
578 A68_REF *z = DEREF (A68_REF, tmp);
579 if (!IS_NIL (*z)) {
580 GET_DESCRIPTOR (tarr, ttup, z);
581 ntmp = ARRAY (tarr);
582 check_bounds = IS_ROW (m);
583 }
584 }
585 int span = 1;
586 for (int k = 0; k < DIM (old_arr); k++) {
587 A68_TUPLE *op = &old_tup[k], *np = &new_tup[k];
588 if (check_bounds) {
589 A68_TUPLE *tp = &ttup[k];
590 if (UPB (tp) >= LWB (tp) && UPB (op) >= LWB (op)) {
591 if (UPB (tp) != UPB (op) || LWB (tp) != LWB (op)) {
592 diagnostic (A68_RUNTIME_ERROR, p, ERROR_DIFFERENT_BOUNDS);
593 exit_genie (p, A68_RUNTIME_ERROR);
594 }
595 }
596 }
597 LWB (np) = LWB (op);
598 UPB (np) = UPB (op);
599 SPAN (np) = span;
600 SHIFT (np) = LWB (np) * SPAN (np);
601 span *= ROW_SIZE (np);
602 }
603 // Make a new array with at least a ghost element.
604 if (span == 0) {
605 ARRAY (new_arr) = heap_generator (p, em, ELEM_SIZE (new_arr));
606 } else {
607 ARRAY (new_arr) = heap_generator_2 (p, em, span, ELEM_SIZE (new_arr));
608 }
609 // Copy the ghost element if there are no elements.
610 if (span == 0) {
611 if (IS_UNION (em)) {
612 // UNION has formal members.
613 } else if (HAS_ROWS (em)) {
614 A68_REF old_ref, dst_ref, clone;
615 old_ref = ARRAY (old_arr);
616 OFFSET (&old_ref) += ROW_ELEMENT (old_arr, 0);
617 dst_ref = ARRAY (new_arr);
618 OFFSET (&dst_ref) += ROW_ELEMENT (new_arr, 0);
619 clone = genie_clone (p, em, &ntmp, &old_ref);
620 MOVE (ADDRESS (&dst_ref), ADDRESS (&clone), SIZE (em));
621 }
622 } else if (span > 0) {
623 // The n-dimensional copier.
624 initialise_internal_index (old_tup, DIM (old_arr));
625 initialise_internal_index (new_tup, DIM (new_arr));
626 BOOL_T done = A68_FALSE;
627 while (!done) {
628 A68_REF old_ref = ARRAY (old_arr), dst_ref = ARRAY (new_arr);
629 ADDR_T old_k = calculate_internal_index (old_tup, DIM (old_arr));
630 ADDR_T new_k = calculate_internal_index (new_tup, DIM (new_arr));
631 OFFSET (&old_ref) += ROW_ELEMENT (old_arr, old_k);
632 OFFSET (&dst_ref) += ROW_ELEMENT (new_arr, new_k);
633 if (HAS_ROWS (em)) {
634 A68_REF clone;
635 clone = genie_clone (p, em, &ntmp, &old_ref);
636 MOVE (ADDRESS (&dst_ref), ADDRESS (&clone), SIZE (em));
637 } else {
638 MOVE (ADDRESS (&dst_ref), ADDRESS (&old_ref), SIZE (em));
639 }
640 // Increase pointers.
641 done = increment_internal_index (old_tup, DIM (old_arr)) | increment_internal_index (new_tup, DIM (new_arr));
642 }
643 }
644 A68_REF heap = heap_generator (p, m, A68_REF_SIZE);
645 *DEREF (A68_REF, &heap) = nrow;
646 return heap;
647 }
648 return nil_ref;
649 }
650
651 //! @brief Store into a row, fi. trimmed destinations.
652
653 A68_REF genie_store (NODE_T * p, MOID_T * m, A68_REF * dst, A68_REF * old)
654 {
655 // This complex routine is needed as arrays are not always contiguous.
656 // The routine takes a REF to the value and returns a REF to the clone.
657 if (IS_FLEXETY_ROW (m)) {
658 // REF [FLEX] [].
659 A68_TUPLE *old_tup, *new_tup, *old_p, *new_p;
660 MOID_T *em = SUB (IS_FLEX (m) ? SUB (m) : m);
661 BOOL_T done = A68_FALSE;
662 A68_ARRAY *old_arr, *new_arr;
663 GET_DESCRIPTOR (old_arr, old_tup, DEREF (A68_REF, old));
664 GET_DESCRIPTOR (new_arr, new_tup, DEREF (A68_REF, dst));
665 // Get size and check bounds.
666 // This is just song and dance to comply with the RR.
667 int span = 1;
668 for (int k = 0; k < DIM (old_arr); k++) {
669 old_p = &old_tup[k];
670 new_p = &new_tup[k];
671 if ((UPB (new_p) >= LWB (new_p) && UPB (old_p) >= LWB (old_p))) {
672 if ((UPB (new_p) != UPB (old_p) || LWB (new_p) != LWB (old_p))) {
673 diagnostic (A68_RUNTIME_ERROR, p, ERROR_DIFFERENT_BOUNDS);
674 exit_genie (p, A68_RUNTIME_ERROR);
675 }
676 }
677 span *= ROW_SIZE (new_p);
678 }
679 // Destination is an empty row, inspect if the source has elements.
680 if (span == 0) {
681 span = 1;
682 for (int k = 0; k < DIM (old_arr); k++) {
683 span *= ROW_SIZE (old_p);
684 }
685 if (span > 0) {
686 for (int k = 0; k < DIM (old_arr); k++) {
687 new_tup[k] = old_tup[k];
688 }
689 ARRAY (new_arr) = heap_generator_2 (p, em, span, ELEM_SIZE (new_arr));
690 }
691 }
692 if (span > 0) {
693 initialise_internal_index (old_tup, DIM (old_arr));
694 initialise_internal_index (new_tup, DIM (new_arr));
695 while (!done) {
696 A68_REF new_old = ARRAY (old_arr), new_dst = ARRAY (new_arr);
697 ADDR_T old_index = calculate_internal_index (old_tup, DIM (old_arr));
698 ADDR_T new_index = calculate_internal_index (new_tup, DIM (new_arr));
699 OFFSET (&new_old) += ROW_ELEMENT (old_arr, old_index);
700 OFFSET (&new_dst) += ROW_ELEMENT (new_arr, new_index);
701 MOVE (ADDRESS (&new_dst), ADDRESS (&new_old), SIZE (em));
702 done = increment_internal_index (old_tup, DIM (old_arr)) | increment_internal_index (new_tup, DIM (new_arr));
703 }
704 }
705 return *dst;
706 }
707 return nil_ref;
708 }
709
710 //! @brief Assignment of complex objects in the stack.
711
712 void genie_clone_stack (NODE_T * p, MOID_T * srcm, A68_REF * dst, A68_REF * tmp)
713 {
714 // STRUCT, UNION, [FLEX] [] or SOUND.
715 A68_REF stack;
716 STATUS (&stack) = (STATUS_MASK_T) (INIT_MASK | IN_STACK_MASK);
717 OFFSET (&stack) = A68_SP;
718 REF_HANDLE (&stack) = (A68_HANDLE *) & nil_handle;
719 A68_REF *src = DEREF (A68_REF, &stack);
720 if (IS_ROW (srcm) && !IS_NIL (*tmp)) {
721 if (STATUS (src) & SKIP_ROW_MASK) {
722 return;
723 }
724 A68_REF clone = genie_clone (p, srcm, tmp, &stack);
725 (void) genie_store (p, srcm, dst, &clone);
726 } else {
727 A68_REF clone = genie_clone (p, srcm, tmp, &stack);
728 MOVE (ADDRESS (dst), ADDRESS (&clone), SIZE (srcm));
729 }
730 }
731
732 //! @brief Strcmp for qsort.
733
734 int qstrcmp (const void *a, const void *b)
735 {
736 return strcmp (*(char *const *) a, *(char *const *) b);
737 }
738
739 //! @brief Sort row of string.
740
741 void genie_sort_row_string (NODE_T * p)
742 {
743 A68_REF z; A68_ARRAY *arr; A68_TUPLE *tup;
744 POP_REF (p, &z);
745 ADDR_T pop_sp = A68_SP;
746 CHECK_REF (p, z, M_ROW_STRING);
747 GET_DESCRIPTOR (arr, tup, &z);
748 int size = ROW_SIZE (tup);
749 if (size > 0) {
750 BYTE_T *base = ADDRESS (&ARRAY (arr));
751 char **ptrs = (char **) a68_alloc ((size_t) (size * (int) sizeof (char *)), __func__, __LINE__);
752 if (ptrs == NO_VAR) {
753 diagnostic (A68_RUNTIME_ERROR, p, ERROR_OUT_OF_CORE);
754 exit_genie (p, A68_RUNTIME_ERROR);
755 }
756 // Copy C-strings into the stack and sort.
757 for (int j = 0, k = LWB (tup); k <= UPB (tup); j++, k++) {
758 int addr = INDEX_1_DIM (arr, tup, k);
759 A68_REF ref = *(A68_REF *) & (base[addr]);
760 CHECK_REF (p, ref, M_STRING);
761 int len = A68_ALIGN (a68_string_size (p, ref) + 1);
762 if (A68_SP + len > A68 (expr_stack_limit)) {
763 diagnostic (A68_RUNTIME_ERROR, p, ERROR_STACK_OVERFLOW);
764 exit_genie (p, A68_RUNTIME_ERROR);
765 }
766 ptrs[j] = (char *) STACK_TOP;
767 ASSERT (a_to_c_string (p, (char *) STACK_TOP, ref) != NO_TEXT);
768 INCREMENT_STACK_POINTER (p, len);
769 }
770 qsort (ptrs, (size_t) size, sizeof (char *), qstrcmp);
771 // Construct an array of sorted strings.
772 A68_REF row; A68_ARRAY arrn; A68_TUPLE tupn;
773 NEW_ROW_1D (z, row, arrn, tupn, M_ROW_STRING, M_STRING, size);
774 A68_REF *base_ref = DEREF (A68_REF, &row);
775 for (int k = 0; k < size; k++) {
776 base_ref[k] = c_to_a_string (p, ptrs[k], DEFAULT_WIDTH);
777 }
778 a68_free (ptrs);
779 A68_SP = pop_sp;
780 PUSH_REF (p, z);
781 } else {
782 // This is how we sort an empty row of strings ...
783 A68_SP = pop_sp;
784 PUSH_REF (p, empty_row (p, M_ROW_STRING));
785 }
786 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|