Phasor 3.3.0
Stack VM based Programming Language
Loading...
Searching...
No Matches
vformat.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <iostream>
4#include <sstream>
5#include <string>
6#include <cstdarg>
7#include <cstdio>
8#include <cstring>
9#include <cerrno>
10#include <cstdint>
11#include <climits>
12#include <algorithm>
13#include <vector>
14#include <Value.hpp>
15
16#ifdef _MSC_VER
17#pragma warning(push)
18#pragma warning(disable: 4996)
19#endif
20
21namespace vformat {
22
23namespace detail {
24
25enum class Length { None, hh, h, l, ll, L, z, t, j };
26
27struct Spec {
28 bool minus = false;
29 bool plus = false;
30 bool space = false;
31 bool hash = false;
32 bool zero = false;
33 bool quote = false;
34
35 int width = 0;
36 int prec = -1;
37
39 char conv = '\0';
40};
41
42static const char *parse(const char *f, Spec &s, va_list &ap)
43{
44 for (bool more = true; more;) {
45 switch (*f) {
46 case '-':
47 s.minus = true;
48 ++f;
49 break;
50 case '+':
51 s.plus = true;
52 ++f;
53 break;
54 case ' ':
55 s.space = true;
56 ++f;
57 break;
58 case '#':
59 s.hash = true;
60 ++f;
61 break;
62 case '0':
63 s.zero = true;
64 ++f;
65 break;
66 case '\'':
67 s.quote = true;
68 ++f;
69 break;
70 default:
71 more = false;
72 break;
73 }
74 }
75
76 if (*f == '*') {
77 s.width = va_arg(ap, int);
78 if (s.width < 0) { s.minus = true; s.width = -s.width; }
79 ++f;
80 } else {
81 while (*f >= '0' && *f <= '9') { s.width = s.width * 10 + (*f++ - '0'); }
82 }
83
84 if (*f == '.') {
85 ++f;
86 s.prec = 0;
87 if (*f == '*') {
88 s.prec = va_arg(ap, int);
89 if (s.prec < 0) s.prec = -1;
90 ++f;
91 } else {
92 while (*f >= '0' && *f <= '9') { s.prec = s.prec * 10 + (*f++ - '0'); }
93 }
94 }
95
96 switch (*f) {
97 case 'h':
98 ++f;
99 if (*f == 'h') { s.length = Length::hh; ++f; }
100 else s.length = Length::h;
101 break;
102 case 'l':
103 ++f;
104 if (*f == 'l') { s.length = Length::ll; ++f; }
105 else s.length = Length::l;
106 break;
107 case 'L':
108 s.length = Length::L;
109 ++f;
110 break;
111 case 'z':
112 case 'Z':
113 s.length = Length::z;
114 ++f;
115 break;
116 case 't':
117 s.length = Length::t;
118 ++f;
119 break;
120 case 'j':
121 s.length = Length::j;
122 ++f;
123 break;
124 case 'q':
125 s.length = Length::ll;
126 ++f;
127 break;
128 default:
129 break;
130 }
131
132 s.conv = *f;
133 if (*f) ++f;
134 return f;
135}
136
137static std::string build_fmt(const Spec &s, const char *len_override, char conv)
138{
139 std::string f = "%";
140 if (s.minus) f += '-';
141 if (s.plus) f += '+';
142 if (s.space) f += ' ';
143 if (s.hash) f += '#';
144 if (s.zero) f += '0';
145 if (s.quote) f += '\'';
146 if (s.width > 0) f += std::to_string(s.width);
147 if (s.prec >= 0) { f += '.'; f += std::to_string(s.prec); }
148 f += len_override;
149 f += conv;
150 return f;
151}
152
153template<typename Fill>
154static std::string snprintf_into(int hint, Fill fill)
155{
156 int sz = std::max(hint, 64);
157 std::vector<char> buf(sz);
158 int needed = fill(buf.data(), sz);
159 if (needed < 0) return {};
160 if (needed >= sz) {
161 buf.resize(needed + 1);
162 fill(buf.data(), needed + 1);
163 }
164 return std::string(buf.data());
165}
166
167static int hint(const Spec &s) {
168 return std::max({ s.width, s.prec, 0 }) + 80;
169}
170
171static std::string fmt_signed(const Spec &s, va_list &ap)
172{
173 long long val;
174 switch (s.length) {
175 case Length::hh:
176 val = (signed char)va_arg(ap, int);
177 break;
178 case Length::h:
179 val = (short)va_arg(ap, int);
180 break;
181 case Length::l:
182 val = va_arg(ap, long);
183 break;
184 case Length::ll:
185 val = va_arg(ap, long long);
186 break;
187 case Length::z:
188 val = (long long)va_arg(ap, std::ptrdiff_t);
189 break;
190 case Length::t:
191 val = va_arg(ap, std::ptrdiff_t);
192 break;
193 case Length::j:
194 val = (long long)va_arg(ap, std::intmax_t);
195 break;
196 default:
197 val = va_arg(ap, int);
198 break;
199 }
200 std::string f = build_fmt(s, "ll", s.conv);
201 return snprintf_into(hint(s), [&](char *b, int n){
202 return std::snprintf(b, n, f.c_str(), val);
203 });
204}
205
206static std::string fmt_unsigned(const Spec &s, va_list &ap)
207{
208 unsigned long long val;
209 switch (s.length) {
210 case Length::hh:
211 val = (unsigned char)va_arg(ap, unsigned int);
212 break;
213 case Length::h:
214 val = (unsigned short)va_arg(ap, unsigned int);
215 break;
216 case Length::l:
217 val = va_arg(ap, unsigned long);
218 break;
219 case Length::ll:
220 val = va_arg(ap, unsigned long long);
221 break;
222 case Length::z:
223 val = (unsigned long long) va_arg(ap, std::size_t);
224 break;
225 case Length::t:
226 val = (unsigned long long)(std::ptrdiff_t)va_arg(ap, std::ptrdiff_t);
227 break;
228 case Length::j:
229 val = (unsigned long long) va_arg(ap, std::uintmax_t);
230 break;
231 default:
232 val = va_arg(ap, unsigned int);
233 break;
234 }
235 std::string f = build_fmt(s, "ll", s.conv);
236 return snprintf_into(hint(s), [&](char *b, int n){
237 return std::snprintf(b, n, f.c_str(), val);
238 });
239}
240
241static std::string fmt_float(const Spec &s, va_list &ap)
242{
243 if (s.length == Length::L) {
244 long double val = va_arg(ap, long double);
245 std::string f = build_fmt(s, "L", s.conv);
246 return snprintf_into(hint(s), [&](char *b, int n){
247 return std::snprintf(b, n, f.c_str(), val);
248 });
249 } else {
250 double val = va_arg(ap, double);
251 std::string f = build_fmt(s, "", s.conv);
252 return snprintf_into(hint(s), [&](char *b, int n){
253 return std::snprintf(b, n, f.c_str(), val);
254 });
255 }
256}
257
258static std::string fmt_char(const Spec &s, va_list &ap)
259{
260 char c = (char)va_arg(ap, int);
261 int pad = s.width - 1;
262 std::string out;
263 if (!s.minus && pad > 0) out.append(pad, ' ');
264 out += c;
265 if ( s.minus && pad > 0) out.append(pad, ' ');
266 return out;
267}
268
269static std::string fmt_string(const Spec &s, va_list &ap)
270{
271 const char *str = va_arg(ap, const char *);
272 if (!str) str = "(null)";
273
274 std::size_t len = (s.prec >= 0)
275 ? ::strnlen(str, (std::size_t)s.prec)
276 : ::strlen(str);
277
278 int pad = s.width - (int)len;
279 std::string out;
280 out.reserve(len + std::max(pad, 0));
281 if (!s.minus && pad > 0) out.append(pad, ' ');
282 out.append(str, len);
283 if ( s.minus && pad > 0) out.append(pad, ' ');
284 return out;
285}
286
287static std::string fmt_pointer(const Spec &s, va_list &ap)
288{
289 void *val = va_arg(ap, void *);
290 std::string f = "%";
291 if (s.minus) f += '-';
292 if (s.width > 0) f += std::to_string(s.width);
293 f += 'p';
294 return snprintf_into(hint(s), [&](char *b, int n){
295 return std::snprintf(b, n, f.c_str(), val);
296 });
297}
298
299static std::string do_vformat(const char *fmt, va_list ap_in)
300{
301 va_list ap;
302 va_copy(ap, ap_in);
303
304 std::string result;
305 result.reserve(128);
306
307 const char *f = fmt;
308 while (*f) {
309 if (*f != '%') {
310 result += *f++;
311 continue;
312 }
313
314 ++f;
315
316 if (*f == '%') {
317 result += '%';
318 ++f;
319 continue;
320 }
321
322 Spec s;
323 f = parse(f, s, ap);
324
325 switch (s.conv) {
326 case 'd':
327 case 'i':
328 result += fmt_signed(s, ap);
329 break;
330
331 case 'u':
332 case 'o':
333 case 'x':
334 case 'X':
335 result += fmt_unsigned(s, ap);
336 break;
337
338 case 'f': case 'F':
339 case 'e': case 'E':
340 case 'g': case 'G':
341 case 'a': case 'A':
342 result += fmt_float(s, ap);
343 break;
344
345 case 'c':
346 result += fmt_char(s, ap);
347 break;
348
349 case 's':
350 result += fmt_string(s, ap);
351 break;
352
353 case 'p':
354 result += fmt_pointer(s, ap);
355 break;
356
357 case 'm':
358 result += ::strerror(errno);
359 break;
360
361 case 'n': {
362 (void)va_arg(ap, int *);
363 break;
364 }
365
366 default:
367 result += '%';
368 if (s.conv) result += s.conv;
369 break;
370 }
371 }
372
373 va_end(ap);
374 return result;
375}
376
377} // namespace detail
378
379inline std::string vformat(const char *fmt, va_list ap)
380{
381 return detail::do_vformat(fmt, ap);
382}
383
384inline std::string format(const char *fmt, ...)
385{
386 va_list ap;
387 va_start(ap, fmt);
388 std::string result = detail::do_vformat(fmt, ap);
389 va_end(ap);
390 return result;
391}
392
393inline int vfprintf(std::ostream &os, const char *fmt, va_list ap)
394{
395 std::string s = detail::do_vformat(fmt, ap);
396 os.write(s.data(), (std::streamsize)s.size());
397 if (!os) return -1;
398 return (int)s.size();
399}
400
401inline int fprintf(std::ostream &os, const char *fmt, ...)
402{
403 va_list ap;
404 va_start(ap, fmt);
405 int r = vfprintf(os, fmt, ap);
406 va_end(ap);
407 return r;
408}
409
410inline int printf(const char *fmt, ...)
411{
412 va_list ap;
413 va_start(ap, fmt);
414 int r = vfprintf(std::cout, fmt, ap);
415 va_end(ap);
416 return r;
417}
418
419inline std::string str_format_v(const char *fmt, const std::vector<Phasor::Value> &args)
420{
421 std::string result;
422 result.reserve(128);
423
424 const char *f = fmt;
425 size_t argIndex = 0;
426
427 while (*f) {
428 if (*f != '%') {
429 result += *f++;
430 continue;
431 }
432
433 ++f;
434
435 if (*f == '%') {
436 result += '%';
437 ++f;
438 continue;
439 }
440
442
443 for (bool more = true; more;) {
444 switch (*f) {
445 case '-':
446 s.minus = true;
447 ++f;
448 break;
449 case '+':
450 s.plus = true;
451 ++f;
452 break;
453 case ' ':
454 s.space = true;
455 ++f;
456 break;
457 case '#':
458 s.hash = true;
459 ++f;
460 break;
461 case '0':
462 s.zero = true;
463 ++f;
464 break;
465 case '\'':
466 s.quote = true;
467 ++f;
468 break;
469 default:
470 more = false;
471 break;
472 }
473 }
474
475 while (*f >= '0' && *f <= '9') s.width = s.width * 10 + (*f++ - '0');
476
477 if (*f == '.') {
478 ++f;
479 s.prec = 0;
480 while (*f >= '0' && *f <= '9') s.prec = s.prec * 10 + (*f++ - '0');
481 }
482
483 switch (*f) {
484 case 'h':
485 ++f;
486 if (*f == 'h') { s.length = detail::Length::hh; ++f; }
487 else s.length = detail::Length::h;
488 break;
489 case 'l':
490 ++f;
491 if (*f == 'l') { s.length = detail::Length::ll; ++f; }
492 else s.length = detail::Length::l;
493 break;
494 case 'L':
495 s.length = detail::Length::L;
496 ++f;
497 break;
498 case 'z': case 'Z':
499 s.length = detail::Length::z;
500 ++f;
501 break;
502 case 't':
503 s.length = detail::Length::t;
504 ++f;
505 break;
506 case 'j':
507 s.length = detail::Length::j;
508 ++f;
509 break;
510 default:
511 break;
512 }
513
514 s.conv = *f;
515 if (*f) ++f;
516
517 if (s.conv == 'm') { result += ::strerror(errno); continue; }
518 if (s.conv == 'n') { continue; }
519
520 if (argIndex >= args.size()) continue;
521
522 const Phasor::Value &val = args[argIndex++];
523 const Phasor::ValueType type = val.getType();
524
525 switch (s.conv) {
526 case 'd': case 'i':
527 case 'u': case 'o': case 'x': case 'X': {
528 long long ival = 0;
529 switch (type) {
531 ival = val.asInt();
532 break;
534 ival = val.asBool() ? 1LL : 0LL;
535 break;
537 ival = (long long)val.asFloat();
538 break;
539 default:
540 break;
541 }
542 std::string fmtStr = detail::build_fmt(s, "ll", s.conv);
543 if (s.conv == 'd' || s.conv == 'i') {
544 result += detail::snprintf_into(detail::hint(s), [&](char *b, int n) {
545 return std::snprintf(b, n, fmtStr.c_str(), ival);
546 });
547 } else {
548 unsigned long long uval = (unsigned long long)ival;
549 result += detail::snprintf_into(detail::hint(s), [&](char *b, int n) {
550 return std::snprintf(b, n, fmtStr.c_str(), uval);
551 });
552 }
553 break;
554 }
555
556 case 'f': case 'F': case 'e': case 'E':
557 case 'g': case 'G': case 'a': case 'A': {
558 double dval = 0.0;
559 switch (type) {
561 dval = val.asFloat();
562 break;
564 dval = (double)val.asInt();
565 break;
567 dval = val.asBool() ? 1.0 : 0.0;
568 break;
569 default:
570 break;
571 }
572 std::string fmtStr = detail::build_fmt(s, "", s.conv);
573 result += detail::snprintf_into(detail::hint(s), [&](char *b, int n) {
574 return std::snprintf(b, n, fmtStr.c_str(), dval);
575 });
576 break;
577 }
578
579 case 's': {
580 if (type == Phasor::ValueType::Struct || type == Phasor::ValueType::Array) break;
581
582 std::string str;
583 switch (type) {
585 str = val.asString();
586 break;
588 str = val.asBool() ? "true" : "false";
589 break;
591 str = "(null)";
592 break;
593 default:
594 str = val.toString();
595 break;
596 }
597
598 std::size_t len = (s.prec >= 0)
599 ? std::min((std::size_t)s.prec, str.size())
600 : str.size();
601 int pad = s.width - (int)len;
602 if (!s.minus && pad > 0) result.append(pad, ' ');
603 result.append(str.data(), len);
604 if ( s.minus && pad > 0) result.append(pad, ' ');
605 break;
606 }
607
608 case 'c': {
609 char c = '\0';
610 switch (type) {
612 c = (char)val.asInt();
613 break;
615 c = val.asBool() ? '\x01' : '\x00';
616 break;
617 default:
618 break;
619 }
620 int pad = s.width - 1;
621 if (!s.minus && pad > 0) result.append(pad, ' ');
622 result += c;
623 if ( s.minus && pad > 0) result.append(pad, ' ');
624 break;
625 }
626
627 case 'p': {
628 const void *ptr = static_cast<const void *>(&val);
629 std::string fmtStr = "%";
630 if (s.minus) fmtStr += '-';
631 if (s.width > 0) fmtStr += std::to_string(s.width);
632 fmtStr += 'p';
633 result += detail::snprintf_into(detail::hint(s), [&](char *b, int n) {
634 return std::snprintf(b, n, fmtStr.c_str(), ptr);
635 });
636 break;
637 }
638
639 default:
640 result += '%';
641 if (s.conv) result += s.conv;
642 break;
643 }
644 }
645
646 return result;
647}
648
649} // namespace vformat
650
651#ifdef _MSC_VER
652#pragma warning(pop)
653#endif
A value in the Phasor VM.
Definition Value.hpp:59
PhsString asString() const noexcept
Get the value as a Small String.
Definition Value.hpp:199
ValueType getType() const noexcept
Get the type of the value.
Definition Value.hpp:118
f64 asFloat() const noexcept
Get the value as a f64.
Definition Value.hpp:177
i64 asInt() const noexcept
Get the value as an integer.
Definition Value.hpp:164
bool asBool() const noexcept
Get the value as a boolean.
Definition Value.hpp:159
std::string toString() const noexcept
Convert to string for printing.
Definition Value.hpp:478
static Phasor::u64 s[2]
Definition random.cpp:7
ValueType
Runtime value types for the VM.
Definition Value.hpp:43
static std::string snprintf_into(int hint, Fill fill)
Definition vformat.hpp:154
static std::string build_fmt(const Spec &s, const char *len_override, char conv)
Definition vformat.hpp:137
static std::string fmt_string(const Spec &s, va_list &ap)
Definition vformat.hpp:269
static std::string fmt_float(const Spec &s, va_list &ap)
Definition vformat.hpp:241
static std::string fmt_signed(const Spec &s, va_list &ap)
Definition vformat.hpp:171
static std::string do_vformat(const char *fmt, va_list ap_in)
Definition vformat.hpp:299
static const char * parse(const char *f, Spec &s, va_list &ap)
Definition vformat.hpp:42
static std::string fmt_char(const Spec &s, va_list &ap)
Definition vformat.hpp:258
static std::string fmt_unsigned(const Spec &s, va_list &ap)
Definition vformat.hpp:206
static int hint(const Spec &s)
Definition vformat.hpp:167
static std::string fmt_pointer(const Spec &s, va_list &ap)
Definition vformat.hpp:287
std::string format(const char *fmt,...)
Definition vformat.hpp:384
int vfprintf(std::ostream &os, const char *fmt, va_list ap)
Definition vformat.hpp:393
int printf(const char *fmt,...)
Definition vformat.hpp:410
std::string str_format_v(const char *fmt, const std::vector< Phasor::Value > &args)
Definition vformat.hpp:419
int fprintf(std::ostream &os, const char *fmt,...)
Definition vformat.hpp:401