GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/print.c Lines: 266 302 88.1 %
Date: 2025-01-19 11:10:47 Branches: 128 204 62.7 %

Line Branch Exec Source
1
/*
2
    Copyright 2018, 2020 - 2024      Joel Svensson    svenssonjoel@yahoo.se
3
                           2022      Benjamin Vedder
4
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <stdio.h>
20
#include <string.h>
21
#include <ctype.h>
22
#include <inttypes.h>
23
#include <lbm_types.h>
24
#include <lbm_custom_type.h>
25
26
#include "print.h"
27
#include "heap.h"
28
#include "symrepr.h"
29
#include "stack.h"
30
#include "lbm_channel.h"
31
32
#define PRINT          1
33
#define PRINT_SPACE    2
34
#define START_LIST     3
35
#define CONTINUE_LIST  4
36
#define END_LIST       5
37
#define PRINT_DOT      6
38
#define START_ARRAY    7
39
#define CONTINUE_ARRAY 8
40
#define END_ARRAY      9
41
42
static lbm_stack_t print_stack = { NULL, 0, 0};
43
static bool print_has_stack = false;
44
45
const char *failed_str = "Error: print failed\n";
46
47
549179
static int push_n(lbm_stack_t *s, lbm_uint *values, lbm_uint n) {
48
549179
  if (s->sp + n < s->size) {
49
1655489
    for (lbm_uint i = 0; i < n; i ++) {
50
1106310
      s->data[s->sp+i] = values[i];
51
    }
52
549179
    s->sp+=n;
53
549179
    return 1;
54
  }
55
  return 0;
56
}
57
58
// is_printable_string is turning out to be a headache.
59
// What do we want from this function???
60
//
61
// Value                   | Print as                | Condition
62
// [0]                     | [0]                     |
63
// [1]                     | [1]                     |
64
// ""                      | [0]                     | (array->size <= 1) => false
65
// "hej"                   | "hej"                   | printable characters followed by a 0
66
// [65 66 67 0 65 66 67 0] | [65 66 67 0 65 66 67 0] | position of first 0 after printable characters = array->size-1
67
// [0 65 66 0]             | [0 65 66 0]             | position of first 0 after printable characters = array->size-1
68
5320
bool lbm_value_is_printable_string(lbm_value v, char **str) {
69
5320
  bool is_a_string = false;
70
5320
  lbm_array_header_t *array = lbm_dec_array_r(v);
71
5320
  if (array) {
72
4424
    char *c_data = (char *)array->data;
73
4424
    if (array->size > 1) { // nonzero length
74
3920
      unsigned int i = 0;
75
3920
      is_a_string = true;
76
7140
      for (i = 0; i < array->size; i ++) {
77
7140
	if (c_data[i] == 0) break;
78

4676
	if (!isprint((unsigned char)c_data[i]) && ((c_data[i] < 8) || c_data[i] > 13)) {
79
1456
	  is_a_string = false;
80
1456
	  break;
81
	}
82
      }
83

3920
      if (i > 0 && i != array->size-1 && c_data[i-1] != 0) is_a_string = false;
84
3920
      if (array->size-1 > i) is_a_string = false;
85
3920
      if (is_a_string) {
86
784
        *str = (char*)array->data;
87
      }
88
    }
89
  }
90
5320
  return is_a_string;
91
}
92
93
94
21588
int lbm_print_init(lbm_uint print_stack_size) {
95
96
21588
  if (print_stack_size == 0)
97
    return 0;
98
99
21588
  lbm_uint *print_stack_storage = (lbm_uint*)lbm_malloc(print_stack_size * sizeof(lbm_uint));
100
21588
  if (!print_stack_storage) return 0;
101
102
21588
  if (lbm_stack_create(&print_stack, print_stack_storage, print_stack_size)) {
103
21588
    print_has_stack = true;
104
21588
    return 1;
105
  }
106
  return 0;
107
}
108
109
#define EMIT_BUFFER_SIZE 30
110
111
#define EMIT_FAILED -1
112
#define EMIT_OK      0
113
114
683653
static int print_emit_string(lbm_char_channel_t *chan, char* str) {
115
683653
  if (str == NULL) return EMIT_FAILED;
116
2377374
  while (*str != 0) {
117
1940433
    int r = lbm_channel_write(chan, *str);
118
1940433
    str++;
119
1940433
    if (r != CHANNEL_SUCCESS) return EMIT_FAILED;
120
  }
121
436941
  return EMIT_OK;
122
}
123
124
211784
static int print_emit_char(lbm_char_channel_t *chan, char c) {
125
126
211784
  int r = lbm_channel_write(chan, c);
127
211784
  if (r != CHANNEL_SUCCESS) return EMIT_FAILED;
128
211696
  return EMIT_OK;
129
}
130
131
132
1596
static int emit_escape(lbm_char_channel_t *chan, char c) {
133

1596
  switch(c) {
134
  case '"': return print_emit_string(chan, "\\\"");
135
  case '\n': return print_emit_string(chan, "\\n");
136
  case '\r': return print_emit_string(chan, "\\r");
137
  case '\t': return print_emit_string(chan, "\\t");
138
  case '\\': return print_emit_string(chan, "\\\\");
139
1596
  default:
140
1596
    return print_emit_char(chan, c);
141
  }
142
}
143
144
532
static int print_emit_string_value(lbm_char_channel_t *chan, char* str) {
145
532
  if (str == NULL) return EMIT_FAILED;
146
2128
  while (*str != 0) {
147
1596
    int r = emit_escape(chan, *str++);
148
1596
    if (r != EMIT_OK) return r;
149
  }
150
532
  return EMIT_OK;
151
}
152
153
259761
static int print_emit_symbol(lbm_char_channel_t *chan, lbm_value sym) {
154
259761
  char *str_ptr = (char*)lbm_get_name_by_symbol(lbm_dec_sym(sym));
155
259761
  return print_emit_string(chan, str_ptr);
156
}
157
158
41246
static int print_emit_i(lbm_char_channel_t *chan, lbm_int v) {
159
  char buf[EMIT_BUFFER_SIZE];
160
41246
  snprintf(buf, EMIT_BUFFER_SIZE, "%"PRI_INT, v);
161
41246
  return print_emit_string(chan, buf);
162
}
163
164
21288
static int print_emit_u(lbm_char_channel_t *chan, lbm_uint v, bool ps) {
165
  char buf[EMIT_BUFFER_SIZE];
166
21288
  snprintf(buf, EMIT_BUFFER_SIZE, "%"PRI_UINT"%s", v, ps ? "u" : "");
167
21288
  return print_emit_string(chan, buf);
168
}
169
170
293678
static int print_emit_byte(lbm_char_channel_t *chan, uint8_t v, bool ps) {
171
  char buf[EMIT_BUFFER_SIZE];
172
293678
  snprintf(buf, EMIT_BUFFER_SIZE, "%u%s", v, ps ? "b" : "");
173
293678
  return print_emit_string(chan, buf);
174
}
175
176
420
static int print_emit_float(lbm_char_channel_t *chan, float v, bool ps) {
177
  char buf[EMIT_BUFFER_SIZE];
178
420
  snprintf(buf, EMIT_BUFFER_SIZE, "%"PRI_FLOAT"%s", (double)v, ps ? "f32" : "");
179
420
  return print_emit_string(chan, buf);
180
}
181
182
336
static int print_emit_double(lbm_char_channel_t *chan, double v, bool ps) {
183
  char buf[EMIT_BUFFER_SIZE];
184
336
  snprintf(buf, EMIT_BUFFER_SIZE, "%lf%s", v, ps ? "f64" : "");
185
336
  return print_emit_string(chan, buf);
186
}
187
188
112
static int print_emit_u32(lbm_char_channel_t *chan, uint32_t v, bool ps) {
189
  char buf[EMIT_BUFFER_SIZE];
190
112
  snprintf(buf,EMIT_BUFFER_SIZE, "%"PRIu32"%s", v, ps ? "u32" : "");
191
112
  return print_emit_string(chan, buf);
192
}
193
194
112
static int print_emit_i32(lbm_char_channel_t *chan, int32_t v, bool ps) {
195
  char buf[EMIT_BUFFER_SIZE];
196
112
  snprintf(buf,EMIT_BUFFER_SIZE, "%"PRId32"%s", v, ps ? "i32" : "");
197
112
  return print_emit_string(chan, buf);
198
}
199
200
112
static int print_emit_u64(lbm_char_channel_t *chan, uint64_t v, bool ps) {
201
  char buf[EMIT_BUFFER_SIZE];
202
112
  snprintf(buf,EMIT_BUFFER_SIZE, "%"PRIu64"%s", v, ps ? "u64" : "");
203
112
  return print_emit_string(chan, buf);
204
}
205
206
112
static int print_emit_i64(lbm_char_channel_t *chan, int64_t v, bool ps) {
207
  char buf[EMIT_BUFFER_SIZE];
208
112
  snprintf(buf,EMIT_BUFFER_SIZE, "%"PRId64"%s", v, ps ? "i64" : "");
209
112
  return print_emit_string(chan, buf);
210
}
211
212
50532
static int print_emit_continuation(lbm_char_channel_t *chan, lbm_value v) {
213
  char buf[EMIT_BUFFER_SIZE];
214
50532
  lbm_uint cont = (v & ~LBM_CONTINUATION_INTERNAL) >> LBM_ADDRESS_SHIFT;
215
50532
  snprintf(buf, EMIT_BUFFER_SIZE, "CONT[" "%"PRI_UINT"]", cont);
216
50532
  return print_emit_string(chan, buf);
217
}
218
219
static int print_emit_custom(lbm_char_channel_t *chan, lbm_value v) {
220
  lbm_uint *custom = (lbm_uint*)lbm_car(v);
221
  int r; // NULL checks works against SYM_NIL.
222
  if (custom && custom[CUSTOM_TYPE_DESCRIPTOR]) {
223
    r = print_emit_string(chan, (char*)custom[CUSTOM_TYPE_DESCRIPTOR]);
224
  } else {
225
    r = print_emit_string(chan, "INVALID_CUSTOM_TYPE");
226
  }
227
  return r;
228
}
229
230
56
static int print_emit_defrag_mem(lbm_char_channel_t *chan, lbm_value v) {
231
  (void) v;
232
56
  return print_emit_string(chan, "DM");
233
}
234
235
11940
static int print_emit_channel(lbm_char_channel_t *chan, lbm_value v) {
236
  (void) v;
237
11940
  return print_emit_string(chan, "~CHANNEL~");
238
}
239
240
3500
static int print_emit_array_data(lbm_char_channel_t *chan, lbm_array_header_t *array) {
241
242
3500
  int r = print_emit_char(chan, '[');
243
244
3500
  if (r == EMIT_OK) {
245
246
296898
    for (unsigned int i = 0; i < array->size; i ++) {
247
248
293398
      char *c_data = (char*)array->data;
249
293398
      r = print_emit_byte(chan, (uint8_t)c_data[i], false);
250
251

293398
      if (r == EMIT_OK && i != array->size - 1) {
252
43988
        r = print_emit_char(chan, ' ');
253
      }
254
    }
255
256
3500
    if (r != EMIT_OK) return r;
257
3220
    return print_emit_char(chan, ']');
258
  }
259
  return r;
260
}
261
262
4256
static int print_emit_bytearray(lbm_char_channel_t *chan, lbm_value v) {
263
4256
  int r = 0;
264
  char *str;
265
4256
  lbm_array_header_t *array = lbm_dec_array_r(v);
266
4256
  if (array) {
267
4032
    if (lbm_value_is_printable_string(v, &str)) {
268
532
      r = print_emit_char(chan, '"');
269
532
      if (r == EMIT_OK) {
270
532
        r = print_emit_string_value(chan, str);
271
532
        if (r == EMIT_OK) {
272
532
          r = print_emit_char(chan, '"');
273
        }
274
      }
275
    } else {
276
3500
      r=  print_emit_array_data(chan, array);
277
    }
278
  } else {
279
224
    r = print_emit_string(chan, "[INVALID_ARRAY]");
280
  }
281
4256
  return r;
282
}
283
284
285
328843
static int lbm_print_internal(lbm_char_channel_t *chan, lbm_value v) {
286
287
328843
  lbm_stack_clear(&print_stack);
288
328843
  lbm_value start_print[2] = {v, PRINT};
289
328843
  push_n(&print_stack, start_print, 2);
290
328843
  bool chan_full = false;
291
  lbm_value curr;
292
  lbm_uint instr;
293
328843
  int r = EMIT_FAILED;
294
295

1260609
  while (!lbm_stack_is_empty(&print_stack) && !chan_full) {
296
603039
    lbm_pop(&print_stack, &instr);
297


603039
    switch (instr) {
298
56
    case START_ARRAY: {
299
56
      lbm_pop(&print_stack, &curr);
300
56
      int res = 1;
301
56
      r = print_emit_char(chan, '[');
302
56
      lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(curr);
303
56
      lbm_uint size = arr->size / sizeof(lbm_value);
304
56
      lbm_value *arrdata = (lbm_value*)arr->data;
305
56
      if (size >= 1) {
306
56
        lbm_value continuation[5] =
307
          {1,  // next index
308
56
           (lbm_uint) arr,
309
           CONTINUE_ARRAY,
310
56
           arrdata[0], // first elt
311
           PRINT};
312

56
        res = res && push_n(&print_stack, continuation, 5);
313
      } else {
314
        res = res && lbm_push(&print_stack, END_LIST);
315
      }
316
56
      if (!res) {
317
        return EMIT_FAILED;
318
      }
319
56
      break;
320
    }
321
168
    case CONTINUE_ARRAY: {
322
      lbm_uint arr_ptr;
323
      lbm_array_header_t *arr;
324
      lbm_uint ix;
325
168
      int res = 1;
326
168
      lbm_pop_2(&print_stack, &arr_ptr, &ix);
327
168
      arr = (lbm_array_header_t *)arr_ptr;
328
168
      lbm_value *arrdata = (lbm_value*)arr->data;
329
168
      if (ix < (arr->size / sizeof(lbm_value))) {
330
112
        r = print_emit_char(chan, ' ');
331
112
        lbm_value continuation[5] =
332
112
          {ix + 1,
333
112
           (lbm_uint) arr,
334
           CONTINUE_ARRAY,
335
112
           arrdata[ix],
336
           PRINT};
337

112
        res = res && push_n(&print_stack, continuation, 5);
338
      } else {
339

56
        res = res && lbm_push(&print_stack, END_ARRAY);
340
      }
341
168
      if (!res) {
342
        return EMIT_FAILED;
343
      }
344
168
      break;
345
    }
346
56
    case END_ARRAY: {
347
56
      r = print_emit_char(chan, ']');
348
56
      break;
349
    }
350
50224
    case START_LIST: {
351
50224
      lbm_pop(&print_stack, &curr);
352
50224
      r = print_emit_char(chan, '(');
353
50224
      if (r != EMIT_OK) return r;
354
50224
      lbm_value car_val = lbm_car(curr);
355
50224
      lbm_value cdr_val = lbm_cdr(curr);
356
50224
      int res = 1;
357

58336
      if (lbm_type_of(cdr_val) == LBM_TYPE_CONS ||
358
8112
          lbm_type_of(cdr_val) == (LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT)) {
359
42112
        lbm_value cont[2] = {cdr_val, CONTINUE_LIST};
360

42112
        res = res && push_n(&print_stack, cont, 2);
361

8112
      } else if (lbm_type_of(cdr_val) == LBM_TYPE_SYMBOL &&
362
                 cdr_val == ENC_SYM_NIL) {
363

4388
        res = res && lbm_push(&print_stack, END_LIST);
364
      } else {
365
3724
        lbm_value cont[4] = {END_LIST, cdr_val, PRINT, PRINT_DOT};
366

3724
        res = res && push_n(&print_stack, cont, 4);
367
      }
368
50224
      lbm_value cont[2] = {car_val, PRINT};
369

50224
      res = res && push_n(&print_stack, cont,2);
370
50224
      if (!res) {
371
        return EMIT_FAILED;
372
      }
373
50224
      break;
374
    }
375
57916
    case CONTINUE_LIST: {
376
57916
      int res = 1;
377
57916
      lbm_pop(&print_stack, &curr);
378
379
57916
      if (lbm_type_of(curr) == LBM_TYPE_SYMBOL &&
380
          curr == ENC_SYM_NIL) {
381
57912
        break;
382
      }
383
384
57916
      lbm_value car_val = lbm_car(curr);
385
57916
      lbm_value cdr_val = lbm_cdr(curr);
386
387
57916
      r = print_emit_char(chan, ' ');
388
57916
      if (r != EMIT_OK) {
389
4
        return r;
390
      }
391

99908
      if (lbm_type_of(cdr_val) == LBM_TYPE_CONS ||
392
41996
          lbm_type_of(cdr_val) == (LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT)) {
393
15916
        lbm_value cont[2] = {cdr_val, CONTINUE_LIST};
394

15916
        res = res && push_n(&print_stack, cont, 2);
395

41996
      } else if (lbm_type_of(cdr_val) == LBM_TYPE_SYMBOL &&
396
                  cdr_val == ENC_SYM_NIL) {
397

41996
        res = res && lbm_push(&print_stack, END_LIST);
398
      } else {
399
        lbm_value cont[4] = {END_LIST, cdr_val, PRINT, PRINT_DOT};
400
        res = res && push_n(&print_stack, cont, 4);
401
      }
402
57912
      lbm_value cont[2] = {car_val, PRINT};
403

57912
      res = res && push_n(&print_stack, cont, 2);
404
57912
      if (!res) {
405
        return EMIT_FAILED;
406
      }
407
57912
      break;
408
    }
409
50052
    case END_LIST:
410
50052
      r = print_emit_char(chan, ')');
411
50052
      if (r != EMIT_OK) return r;
412
49968
      break;
413
    case PRINT_SPACE:
414
      r = print_emit_char(chan, ' ');
415
      if (r != EMIT_OK) return r;
416
      break;
417
3724
    case PRINT_DOT:
418
3724
      r = print_emit_string(chan, " . ");
419
3724
      if (r != EMIT_OK) return r;
420
3696
      break;
421
440843
    case PRINT:
422
440843
      lbm_pop(&print_stack, &curr);
423
424
440843
      lbm_type t = lbm_type_of(curr);
425
440843
      if (lbm_is_ptr(curr))
426
118268
          t = t & LBM_PTR_TO_CONSTANT_MASK; // print constants normally
427
428
      switch(t) {
429
50224
      case LBM_TYPE_CONS: {
430
50224
        lbm_value cont[2] = {curr, START_LIST};
431
50224
        int res = push_n(&print_stack, cont, 2);
432
50224
        if (!res) {
433
          print_emit_string(chan," ...");
434
          return EMIT_OK;
435
        }
436
50224
        break;
437
      }
438
259761
      case LBM_TYPE_SYMBOL:
439
259761
        r = print_emit_symbol(chan, curr);
440
259761
        break;
441
41246
      case LBM_TYPE_I:
442
41246
        r = print_emit_i(chan, lbm_dec_i(curr));
443
41246
        break;
444
21288
      case LBM_TYPE_U:
445
21288
        r = print_emit_u(chan, lbm_dec_u(curr), true);
446
21288
        break;
447
280
      case LBM_TYPE_CHAR:
448
280
        r = print_emit_byte(chan, (uint8_t)lbm_dec_char(curr), true);
449
280
        break;
450
420
      case LBM_TYPE_FLOAT:
451
420
        r = print_emit_float(chan, lbm_dec_float(curr), true);
452
420
        break;
453
336
      case LBM_TYPE_DOUBLE:
454
336
        r = print_emit_double(chan, lbm_dec_double(curr), true);
455
336
        break;
456
112
      case LBM_TYPE_U32:
457
112
        r = print_emit_u32(chan, lbm_dec_u32(curr), true);
458
112
        break;
459
112
      case LBM_TYPE_I32:
460
112
        r = print_emit_i32(chan, lbm_dec_i32(curr), true);
461
112
        break;
462
112
      case LBM_TYPE_U64:
463
112
        r = print_emit_u64(chan, lbm_dec_u64(curr), true);
464
112
        break;
465
112
      case LBM_TYPE_I64:
466
112
        r = print_emit_i64(chan, lbm_dec_i64(curr), true);
467
112
        break;
468
50532
      case LBM_CONTINUATION_INTERNAL_TYPE:
469
50532
        r = print_emit_continuation(chan, curr);
470
50532
        break;
471
      case LBM_TYPE_CUSTOM:
472
        r = print_emit_custom(chan, curr);
473
        break;
474
11940
      case LBM_TYPE_CHANNEL:
475
11940
        r = print_emit_channel(chan, curr);
476
11940
        break;
477
4256
      case LBM_TYPE_ARRAY:
478
4256
        r = print_emit_bytearray(chan, curr);
479
4256
        break;
480
56
      case LBM_TYPE_DEFRAG_MEM:
481
56
	r = print_emit_defrag_mem(chan, curr);
482
56
	break;
483
56
      case LBM_TYPE_LISPARRAY: {
484
56
        lbm_value cont[2] = {curr, START_ARRAY};
485
56
        int res = push_n(&print_stack, cont, 2);
486
56
        if (!res) {
487
          print_emit_string(chan, " ...");
488
          return EMIT_OK;
489
        }
490
56
        break;
491
      }
492
      default:
493
        return EMIT_FAILED;
494
      }
495
931766
    }
496
  }
497
328727
  return r;
498
}
499
500
328843
int lbm_print_value(char *buf, unsigned int len, lbm_value v) {
501
502
  lbm_string_channel_state_t st;
503
  lbm_char_channel_t chan;
504
505
328843
  memset(buf, 0, len);
506
328843
  lbm_create_string_char_channel_size(&st, &chan, buf, len);
507
328843
  if (lbm_print_internal(&chan,v) == EMIT_OK)
508
328433
    return 1;
509
410
  return 0;
510
}