GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/print.c Lines: 267 304 87.8 %
Date: 2025-04-14 11:29:35 Branches: 129 206 62.6 %

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
// is_printable_string is turning out to be a headache.
48
// What do we want from this function???
49
//
50
// Value                   | Print as                | Condition
51
// [0]                     | [0]                     |
52
// [1]                     | [1]                     |
53
// ""                      | [0]                     | (array->size <= 1) => false
54
// "hej"                   | "hej"                   | printable characters followed by a 0
55
// [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
56
// [0 65 66 0]             | [0 65 66 0]             | position of first 0 after printable characters = array->size-1
57
5432
bool lbm_value_is_printable_string(lbm_value v, char **str) {
58
5432
  bool is_a_string = false;
59
5432
  lbm_array_header_t *array = lbm_dec_array_r(v);
60
5432
  if (array) {
61
4424
    char *c_data = (char *)array->data;
62
4424
    if (array->size > 1) { // nonzero length
63
3920
      unsigned int i = 0;
64
3920
      is_a_string = true;
65
7140
      for (i = 0; i < array->size; i ++) {
66
7140
	if (c_data[i] == 0) break;
67

4676
	if (!isprint((unsigned char)c_data[i]) && ((c_data[i] < 8) || c_data[i] > 13)) {
68
1456
	  is_a_string = false;
69
1456
	  break;
70
	}
71
      }
72

3920
      if (i > 0 && i != array->size-1 && c_data[i-1] != 0) is_a_string = false;
73
3920
      if (array->size-1 > i) is_a_string = false;
74
3920
      if (is_a_string) {
75
784
        *str = (char*)array->data;
76
      }
77
    }
78
  }
79
5432
  return is_a_string;
80
}
81
82
563908
static int push_n(lbm_stack_t *s, lbm_uint *values, lbm_uint n) {
83
563908
  if (s->sp + n < s->size) {
84
1701248
    for (lbm_uint i = 0; i < n; i ++) {
85
1137340
      s->data[s->sp+i] = values[i];
86
    }
87
563908
    s->sp+=n;
88
563908
    return 1;
89
  }
90
  return 0;
91
}
92
93
21924
int lbm_print_init(lbm_uint print_stack_size) {
94
95
21924
  if (print_stack_size == 0)
96
    return 0;
97
98
21924
  lbm_uint *print_stack_storage = (lbm_uint*)lbm_malloc(print_stack_size * sizeof(lbm_uint));
99
21924
  if (!print_stack_storage) return 0;
100
101
21924
  if (lbm_stack_create(&print_stack, print_stack_storage, print_stack_size)) {
102
21924
    print_has_stack = true;
103
21924
    return 1;
104
  }
105
  return 0;
106
}
107
108
#define EMIT_BUFFER_SIZE 30
109
110
#define EMIT_FAILED -1
111
#define EMIT_OK      0
112
113
697964
static int print_emit_string(lbm_char_channel_t *chan, char* str) {
114
697964
  if (str == NULL) return EMIT_FAILED;
115
2413492
  while (*str != 0) {
116
1969506
    int r = lbm_channel_write(chan, *str);
117
1969506
    str++;
118
1969506
    if (r != CHANNEL_SUCCESS) return EMIT_FAILED;
119
  }
120
443986
  return EMIT_OK;
121
}
122
123
219704
static int print_emit_char(lbm_char_channel_t *chan, char c) {
124
125
219704
  int r = lbm_channel_write(chan, c);
126
219704
  if (r != CHANNEL_SUCCESS) return EMIT_FAILED;
127
219554
  return EMIT_OK;
128
}
129
130
131
1596
static int emit_escape(lbm_char_channel_t *chan, char c) {
132

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

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

1283324
  while (!lbm_stack_is_empty(&print_stack) && !chan_full) {
295
620542
    lbm_pop(&print_stack, &instr);
296


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

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

350
        res = res && push_n(&print_stack, continuation, 5);
337
      } else {
338

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

61084
      if (lbm_type_of(cdr_val) == LBM_TYPE_CONS ||
357
8558
          lbm_type_of(cdr_val) == (LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT)) {
358
43968
        lbm_value cont[2] = {cdr_val, CONTINUE_LIST};
359

43968
        res = res && push_n(&print_stack, cont, 2);
360

8558
      } else if (lbm_type_of(cdr_val) == LBM_TYPE_SYMBOL &&
361
                 cdr_val == ENC_SYM_NIL) {
362

4582
        res = res && lbm_push(&print_stack, END_LIST);
363
      } else {
364
3976
        lbm_value cont[4] = {END_LIST, cdr_val, PRINT, PRINT_DOT};
365

3976
        res = res && push_n(&print_stack, cont, 4);
366
      }
367
52526
      lbm_value cont[2] = {car_val, PRINT};
368

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

104956
      if (lbm_type_of(cdr_val) == LBM_TYPE_CONS ||
391
43726
          lbm_type_of(cdr_val) == (LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT)) {
392
17504
        lbm_value cont[2] = {cdr_val, CONTINUE_LIST};
393

17504
        res = res && push_n(&print_stack, cont, 2);
394

43726
      } else if (lbm_type_of(cdr_val) == LBM_TYPE_SYMBOL &&
395
                  cdr_val == ENC_SYM_NIL) {
396

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

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