code / print

Lines202 C188 Markdown10 make4
(208 lines)
1 #pragma once
3 // This file defines some functions to make it easy to do formatted text
4 // without using printf style specifiers:
5 //
6 // print(...) - print text
7 // fprint(file, ...) - print text to file
8 // String(...) - return an allocated string
9 //
10 // If you put `#define PRINT_COLOR 1` before the import, text will be printed
11 // with terminal colors.
13 #include <assert.h>
14 #include <ctype.h>
15 #include <gc.h>
16 #include <stdbool.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/param.h>
23 #ifndef PRINT_COLOR
24 #define PRINT_COLOR 0
25 #endif
27 #define EVAL0(...) __VA_ARGS__
28 #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
29 #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
30 #define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
31 #define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
32 #define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
34 #define MAP_END(...)
35 #define MAP_OUT
36 #define MAP_COMMA ,
38 #define MAP_GET_END2() 0, MAP_END
39 #define MAP_GET_END1(...) MAP_GET_END2
40 #define MAP_GET_END(...) MAP_GET_END1
41 #define MAP_NEXT0(test, next, ...) next MAP_OUT
43 #define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
44 #define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
46 #define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
47 #define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
49 #define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
51 // GCC lets you define macro-like functions which are always inlined and never
52 // compiled using this combination of flags. See: https://gcc.gnu.org/onlinedocs/gcc/Inline.html
53 #ifndef PRINT_FN
54 #define PRINT_FN extern inline __attribute__((gnu_inline, always_inline)) int
55 #endif
57 typedef struct {
58 uint64_t n;
59 bool no_prefix;
60 bool uppercase;
61 int digits;
62 } hex_format_t;
63 #define hex(x, ...) ((hex_format_t){.n=x, __VA_ARGS__})
65 typedef struct {
66 uint64_t n;
67 bool no_prefix;
68 bool uppercase;
69 int digits;
70 } oct_format_t;
71 #define oct(x, ...) ((oct_format_t){.n=x, __VA_ARGS__})
73 typedef struct {
74 double n;
75 int precision;
76 } num_format_t;
77 #define num_format(x, ...) ((num_format_t){.n=x, __VA_ARGS__})
79 typedef struct {
80 const char *str;
81 } quoted_t;
82 #define quoted(s) ((quoted_t){s})
84 #if PRINT_COLOR
85 #define hl(s) "\033[35m" s "\033[m"
86 #else
87 #define hl(s) s
88 #endif
89 PRINT_FN _print_int(FILE *f, int64_t x) { return fprintf(f, hl("%ld"), x); }
90 PRINT_FN _print_uint(FILE *f, uint64_t x) { return fprintf(f, hl("%lu"), x); }
91 PRINT_FN _print_double(FILE *f, double x) { return fprintf(f, hl("%g"), x); }
92 PRINT_FN _print_pointer(FILE *f, void *p) { return fprintf(f, hl("%p"), p); }
93 PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? hl("yes") : hl("no"), f); }
94 PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s, f); }
95 PRINT_FN _print_char(FILE *f, char c) {
96 #if PRINT_COLOR
97 #define ESC(e) "\033[35m'\033[34;1m\\" e "\033[0;35m'\033[m"
98 #else
99 #define ESC(e) "'\\" e "'"
100 #endif
101 const char *named[256] = {['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"),
102 ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")};
103 const char *name = named[(uint8_t)c];
104 if (name != NULL)
105 return fputs(name, f);
106 else if (isprint(c))
107 return fprintf(f, hl("'%c'"), c);
108 else
109 return fprintf(f, ESC("x%02X"), (uint8_t)c);
110 #undef ESC
112 PRINT_FN _print_quoted(FILE *f, quoted_t quoted) {
113 #if PRINT_COLOR
114 #define ESC(e) "\033[34;1m\\" e "\033[0;35m"
115 #else
116 #define ESC(e) "\\" e
117 #endif
118 const char *named[256] = {['\n']=ESC("n"), ['\t']=ESC("t"), ['\r']=ESC("r"),
119 ['\033']=ESC("e"), ['\v']=ESC("v"), ['\a']=ESC("a"), ['\b']=ESC("b")};
120 int printed = fputs("\033[35m\"", f);
121 for (const char *p = quoted.str; *p; p++) {
122 const char *name = named[(uint8_t)*p];
123 if (name != NULL) {
124 printed += fputs(name, f);
125 } else if (isprint(*p) || (uint8_t)*p > 0x7F) {
126 printed += fputc(*p, f);
127 } else {
128 printed += fprintf(f, ESC("x%02X"), (uint8_t)*p);
131 printed += fputs("\"\033[m", f);
132 #undef ESC
133 return printed;
135 PRINT_FN _print_hex(FILE *f, hex_format_t hex) {
136 return fprintf(f, hex.no_prefix ? (hex.uppercase ? hl("%0*lX") : hl("%0*lx")) : (hex.uppercase ? hl("0x%0*lX") : hl("%#0*lx")), hex.digits, hex.n);
138 PRINT_FN _print_oct(FILE *f, oct_format_t oct) {
139 return fprintf(f, oct.no_prefix ? (oct.uppercase ? hl("%0*lO") : hl("%0*lo")) : (oct.uppercase ? hl("%#0*lO") : hl("%#0*lo")), oct.digits, oct.n);
141 PRINT_FN _print_num_format(FILE *f, num_format_t num) {
142 return fprintf(f, hl("%.*lf"), num.precision, num.n);
144 #undef hl
146 #define _fprint1(f, x) _Generic((x), \
147 char*: _print_str, \
148 const char*: _print_str, \
149 char: _print_char, \
150 bool: _print_bool, \
151 int64_t: _print_int, \
152 int32_t: _print_int, \
153 int16_t: _print_int, \
154 int8_t: _print_int, \
155 uint64_t: _print_uint, \
156 uint32_t: _print_uint, \
157 uint16_t: _print_uint, \
158 uint8_t: _print_uint, \
159 float: _print_double, \
160 double: _print_double, \
161 hex_format_t: _print_hex, \
162 oct_format_t: _print_oct, \
163 num_format_t: _print_num_format, \
164 quoted_t: _print_quoted, \
165 void*: _print_pointer)(f, x)
167 typedef struct {
168 char **buffer;
169 size_t *size;
170 size_t position;
171 } gc_stream_t;
173 static ssize_t _gc_stream_write(void *cookie, const char *buf, size_t size) {
174 gc_stream_t *stream = (gc_stream_t *)cookie;
175 if (stream->position + size + 1 > *stream->size)
176 *stream->buffer = GC_REALLOC(*stream->buffer, (*stream->size += MAX(MAX(16, *stream->size/2), size + 1)));
177 memcpy(&(*stream->buffer)[stream->position], buf, size);
178 stream->position += size;
179 (*stream->buffer)[stream->position] = '\0';
180 return size;
183 static FILE *gc_memory_stream(char **buf, size_t *size) {
184 gc_stream_t *stream = GC_MALLOC(sizeof(gc_stream_t));
185 stream->size = size;
186 stream->buffer = buf;
187 *stream->size = 16;
188 *stream->buffer = GC_MALLOC_ATOMIC(*stream->size);
189 (*stream->buffer)[0] = '\0';
190 stream->position = 0;
191 cookie_io_functions_t functions = {.write = _gc_stream_write};
192 return fopencookie(stream, "w", functions);
195 #define _print(x) _n += _fprint1(_printing, x)
196 #define _fprint(f, ...) ({ FILE *_printing = f; int _n = 0; MAP_LIST(_print, __VA_ARGS__); _n; })
197 #define fprint(f, ...) _fprint(f, __VA_ARGS__, "\n")
198 #define print(...) fprint(stdout, __VA_ARGS__)
199 #define fprint_inline(f, ...) _fprint(f, __VA_ARGS__)
200 #define print_inline(...) fprint_inline(stdout, __VA_ARGS__)
201 #define String(...) ({ \
202 char *_buf = NULL; \
203 size_t _size = 0; \
204 FILE *_stream = gc_memory_stream(&_buf, &_size); \
205 assert(_stream); \
206 _fprint(_stream, __VA_ARGS__); \
207 fflush(_stream); \
208 _buf; })