Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
spec_test_generator.cpp
Go to the documentation of this file.
1
2#include <fstream>
3#include <iostream>
4#include <map>
5#include <set>
6#include <sstream>
7#include <string>
8#include <vector>
9
10/*
11 * Copyright 2009-2010 Cybozu Labs, Inc.
12 * Copyright 2011-2014 Kazuho Oku
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright notice,
19 * this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright notice,
22 * this list of conditions and the following disclaimer in the documentation
23 * and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37#ifndef picojson_h
38# define picojson_h
39
40# include <algorithm>
41# include <cstddef>
42# include <cstdio>
43# include <cstdlib>
44# include <cstring>
45# include <iostream>
46# include <iterator>
47# include <limits>
48# include <map>
49# include <stdexcept>
50# include <string>
51# include <utility>
52# include <vector>
53
54// for isnan/isinf
55# if __cplusplus >= 201103L
56# include <cmath>
57# else
58extern "C" {
59# ifdef _MSC_VER
60# include <float.h>
61# elif defined(__INTEL_COMPILER)
62# include <mathimf.h>
63# else
64# include <math.h>
65# endif
66}
67# endif
68
69# ifndef PICOJSON_USE_RVALUE_REFERENCE
70# if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || \
71 (defined(_MSC_VER) && _MSC_VER >= 1600)
72# define PICOJSON_USE_RVALUE_REFERENCE 1
73# else
74# define PICOJSON_USE_RVALUE_REFERENCE 0
75# endif
76# endif // PICOJSON_USE_RVALUE_REFERENCE
77
78# ifndef PICOJSON_NOEXCEPT
79# if PICOJSON_USE_RVALUE_REFERENCE
80# define PICOJSON_NOEXCEPT noexcept
81# else
82# define PICOJSON_NOEXCEPT throw()
83# endif
84# endif
85
86// experimental support for int64_t (see README.mkdn for detail)
87# ifdef PICOJSON_USE_INT64
88# define __STDC_FORMAT_MACROS
89# include <errno.h>
90# include <inttypes.h>
91# endif
92
93// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
94# ifndef PICOJSON_USE_LOCALE
95# define PICOJSON_USE_LOCALE 1
96# endif
97# if PICOJSON_USE_LOCALE
98extern "C" {
99# include <locale.h>
100}
101# endif
102
103# ifndef PICOJSON_ASSERT
104# define PICOJSON_ASSERT(e) \
105 do { \
106 if (!(e)) \
107 throw std::runtime_error(#e); \
108 } while (0)
109# endif
110
111# ifdef _MSC_VER
112# define SNPRINTF _snprintf_s
113# pragma warning(push)
114# pragma warning(disable : 4244) // conversion from int to char
115# pragma warning(disable : 4127) // conditional expression is constant
116# pragma warning(disable : 4702) // unreachable code
117# else
118# define SNPRINTF snprintf
119# endif
120
121namespace picojson {
122
123enum {
130# ifdef PICOJSON_USE_INT64
131 ,
132 int64_type
133# endif
135
136enum { INDENT_WIDTH = 2 };
137
138struct null {};
139
140class value {
141 public:
142 typedef std::vector<value> array;
143 typedef std::map<std::string, value> object;
144 union _storage {
146 double number_;
147# ifdef PICOJSON_USE_INT64
148 int64_t int64_;
149# endif
150 std::string* string_;
152 object* object_;
153 };
154
155 protected:
156 int type_;
158
159 public:
160 value();
161 value(int type, bool);
162 explicit value(bool b);
163# ifdef PICOJSON_USE_INT64
164 explicit value(int64_t i);
165# endif
166 explicit value(double n);
167 explicit value(const std::string& s);
168 explicit value(const array& a);
169 explicit value(const object& o);
170# if PICOJSON_USE_RVALUE_REFERENCE
171 explicit value(std::string&& s);
172 explicit value(array&& a);
173 explicit value(object&& o);
174# endif
175 explicit value(const char* s);
176 value(const char* s, size_t len);
177 ~value();
178 value(const value& x);
179 value& operator=(const value& x);
180# if PICOJSON_USE_RVALUE_REFERENCE
183# endif
185 template <typename T>
186 bool is() const;
187 template <typename T>
188 const T& get() const;
189 template <typename T>
190 T& get();
191 template <typename T>
192 void set(const T&);
193# if PICOJSON_USE_RVALUE_REFERENCE
194 template <typename T>
195 void set(T&&);
196# endif
197 bool evaluate_as_boolean() const;
198 const value& get(const size_t idx) const;
199 const value& get(const std::string& key) const;
200 value& get(const size_t idx);
201 value& get(const std::string& key);
202
203 bool contains(const size_t idx) const;
204 bool contains(const std::string& key) const;
205 std::string to_str() const;
206 template <typename Iter>
207 void serialize(Iter os, bool prettify = false) const;
208 std::string serialize(bool prettify = false) const;
209
210 private:
211 template <typename T>
212 value(const T*); // intentionally defined to block implicit conversion of pointer to bool
213 template <typename Iter>
214 static void _indent(Iter os, int indent);
215 template <typename Iter>
216 void _serialize(Iter os, int indent) const;
217 std::string _serialize(int indent) const;
218 void clear();
219};
220
223
224inline value::value() : type_(null_type), u_() {}
225
226inline value::value(int type, bool) : type_(type), u_() {
227 switch (type) {
228# define INIT(p, v) \
229 case p##type: u_.p = v; break
230 INIT(boolean_, false);
231 INIT(number_, 0.0);
232# ifdef PICOJSON_USE_INT64
233 INIT(int64_, 0);
234# endif
235 INIT(string_, new std::string());
236 INIT(array_, new array());
237 INIT(object_, new object());
238# undef INIT
239 default: break;
240 }
241}
242
243inline value::value(bool b) : type_(boolean_type), u_() { u_.boolean_ = b; }
244
245# ifdef PICOJSON_USE_INT64
246inline value::value(int64_t i) : type_(int64_type), u_() { u_.int64_ = i; }
247# endif
248
249inline value::value(double n) : type_(number_type), u_() {
250 if (
251# ifdef _MSC_VER
252 !_finite(n)
253# elif __cplusplus >= 201103L
254 std::isnan(n) || std::isinf(n)
255# else
256 isnan(n) || isinf(n)
257# endif
258 ) {
259 throw std::overflow_error("");
260 }
261 u_.number_ = n;
262}
263
264inline value::value(const std::string& s) : type_(string_type), u_() { u_.string_ = new std::string(s); }
265
266inline value::value(const array& a) : type_(array_type), u_() { u_.array_ = new array(a); }
267
268inline value::value(const object& o) : type_(object_type), u_() { u_.object_ = new object(o); }
269
270# if PICOJSON_USE_RVALUE_REFERENCE
271inline value::value(std::string&& s) : type_(string_type), u_() { u_.string_ = new std::string(std::move(s)); }
272
273inline value::value(array&& a) : type_(array_type), u_() { u_.array_ = new array(std::move(a)); }
274
275inline value::value(object&& o) : type_(object_type), u_() { u_.object_ = new object(std::move(o)); }
276# endif
277
278inline value::value(const char* s) : type_(string_type), u_() { u_.string_ = new std::string(s); }
279
280inline value::value(const char* s, size_t len) : type_(string_type), u_() { u_.string_ = new std::string(s, len); }
281
282inline void value::clear() {
283 switch (type_) {
284# define DEINIT(p) \
285 case p##type: delete u_.p; break
286 DEINIT(string_);
287 DEINIT(array_);
288 DEINIT(object_);
289# undef DEINIT
290 default: break;
291 }
292}
293
294inline value::~value() { clear(); }
295
296inline value::value(const value& x) : type_(x.type_), u_() {
297 switch (type_) {
298# define INIT(p, v) \
299 case p##type: u_.p = v; break
300 INIT(string_, new std::string(*x.u_.string_));
301 INIT(array_, new array(*x.u_.array_));
302 INIT(object_, new object(*x.u_.object_));
303# undef INIT
304 default: u_ = x.u_; break;
305 }
306}
307
308inline value& value::operator=(const value& x) {
309 if (this != &x) {
310 value t(x);
311 swap(t);
312 }
313 return *this;
314}
315
316# if PICOJSON_USE_RVALUE_REFERENCE
317inline value::value(value&& x) PICOJSON_NOEXCEPT : type_(null_type), u_() { swap(x); }
319 swap(x);
320 return *this;
321}
322# endif
324 std::swap(type_, x.type_);
325 std::swap(u_, x.u_);
326}
327
328# define IS(ctype, jtype) \
329 template <> \
330 inline bool value::is<ctype>() const { \
331 return type_ == jtype##_type; \
332 }
333IS(null, null)
334IS(bool, boolean)
335# ifdef PICOJSON_USE_INT64
336IS(int64_t, int64)
337# endif
338IS(std::string, string)
339IS(array, array)
340IS(object, object)
341# undef IS
342template <>
343inline bool value::is<double>() const {
344 return type_ == number_type
345# ifdef PICOJSON_USE_INT64
346 || type_ == int64_type
347# endif
348 ;
349}
350
351# define GET(ctype, var) \
352 template <> \
353 inline const ctype& value::get<ctype>() const { \
354 PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \
355 return var; \
356 } \
357 template <> \
358 inline ctype& value::get<ctype>() { \
359 PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \
360 return var; \
361 }
362GET(bool, u_.boolean_)
363GET(std::string, *u_.string_)
364GET(array, *u_.array_)
365GET(object, *u_.object_)
366# ifdef PICOJSON_USE_INT64
367GET(double, (type_ == int64_type &&
368 (const_cast<value*>(this)->type_ = number_type, const_cast<value*>(this)->u_.number_ = u_.int64_),
369 u_.number_))
370GET(int64_t, u_.int64_)
371# else
372GET(double, u_.number_)
373# endif
374# undef GET
375
376# define SET(ctype, jtype, setter) \
377 template <> \
378 inline void value::set<ctype>(const ctype& _val) { \
379 clear(); \
380 type_ = jtype##_type; \
381 setter \
382 }
383SET(bool, boolean, u_.boolean_ = _val;)
384SET(std::string, string, u_.string_ = new std::string(_val);)
385SET(array, array, u_.array_ = new array(_val);)
386SET(object, object, u_.object_ = new object(_val);)
387SET(double, number, u_.number_ = _val;)
388# ifdef PICOJSON_USE_INT64
389SET(int64_t, int64, u_.int64_ = _val;)
390# endif
391# undef SET
392
393# if PICOJSON_USE_RVALUE_REFERENCE
394# define MOVESET(ctype, jtype, setter) \
395 template <> \
396 inline void value::set<ctype>(ctype && _val) { \
397 clear(); \
398 type_ = jtype##_type; \
399 setter \
400 }
401MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
402MOVESET(array, array, u_.array_ = new array(std::move(_val));)
403MOVESET(object, object, u_.object_ = new object(std::move(_val));)
404# undef MOVESET
405# endif
406
407inline bool value::evaluate_as_boolean() const {
408 switch (type_) {
409 case null_type: return false;
410 case boolean_type: return u_.boolean_;
411 case number_type: return u_.number_ != 0;
412# ifdef PICOJSON_USE_INT64
413 case int64_type: return u_.int64_ != 0;
414# endif
415 case string_type: return !u_.string_->empty();
416 default: return true;
417 }
418}
419
420inline const value& value::get(const size_t idx) const {
421 static value s_null;
422 PICOJSON_ASSERT(is<array>());
423 return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
424}
425
426inline value& value::get(const size_t idx) {
427 static value s_null;
428 PICOJSON_ASSERT(is<array>());
429 return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
430}
431
432inline const value& value::get(const std::string& key) const {
433 static value s_null;
434 PICOJSON_ASSERT(is<object>());
435 object::const_iterator i = u_.object_->find(key);
436 return i != u_.object_->end() ? i->second : s_null;
437}
438
439inline value& value::get(const std::string& key) {
440 static value s_null;
441 PICOJSON_ASSERT(is<object>());
442 object::iterator i = u_.object_->find(key);
443 return i != u_.object_->end() ? i->second : s_null;
444}
445
446inline bool value::contains(const size_t idx) const {
447 PICOJSON_ASSERT(is<array>());
448 return idx < u_.array_->size();
449}
450
451inline bool value::contains(const std::string& key) const {
452 PICOJSON_ASSERT(is<object>());
453 object::const_iterator i = u_.object_->find(key);
454 return i != u_.object_->end();
455}
456
457inline std::string value::to_str() const {
458 switch (type_) {
459 case null_type: return "null";
460 case boolean_type: return u_.boolean_ ? "true" : "false";
461# ifdef PICOJSON_USE_INT64
462 case int64_type: {
463 char buf[sizeof("-9223372036854775808")];
464 SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
465 return buf;
466 }
467# endif
468 case number_type: {
469 char buf[256];
470 double tmp;
471 SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g",
472 u_.number_);
473# if PICOJSON_USE_LOCALE
474 char* decimal_point = localeconv()->decimal_point;
475 if (strcmp(decimal_point, ".") != 0) {
476 size_t decimal_point_len = strlen(decimal_point);
477 for (char* p = buf; *p != '\0'; ++p) {
478 if (strncmp(p, decimal_point, decimal_point_len) == 0) {
479 return std::string(buf, p) + "." + (p + decimal_point_len);
480 }
481 }
482 }
483# endif
484 return buf;
485 }
486 case string_type: return *u_.string_;
487 case array_type: return "array";
488 case object_type: return "object";
489 default: PICOJSON_ASSERT(0);
490# ifdef _MSC_VER
491 __assume(0);
492# endif
493 }
494 return std::string();
495}
496
497template <typename Iter>
498void copy(const std::string& s, Iter oi) {
499 std::copy(s.begin(), s.end(), oi);
500}
501
502template <typename Iter>
504 Iter oi;
505 void operator()(char c) {
506 switch (c) {
507# define MAP(val, sym) \
508 case val: copy(sym, oi); break
509 MAP('"', "\\\"");
510 MAP('\\', "\\\\");
511 MAP('/', "\\/");
512 MAP('\b', "\\b");
513 MAP('\f', "\\f");
514 MAP('\n', "\\n");
515 MAP('\r', "\\r");
516 MAP('\t', "\\t");
517# undef MAP
518 default:
519 if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
520 char buf[7];
521 SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff);
522 copy(buf, buf + 6, oi);
523 } else {
524 *oi++ = c;
525 }
526 break;
527 }
528 }
529};
530
531template <typename Iter>
532void serialize_str(const std::string& s, Iter oi) {
533 *oi++ = '"';
534 serialize_str_char<Iter> process_char = { oi };
535 std::for_each(s.begin(), s.end(), process_char);
536 *oi++ = '"';
537}
538
539template <typename Iter>
540void value::serialize(Iter oi, bool prettify) const {
541 return _serialize(oi, prettify ? 0 : -1);
542}
543
544inline std::string value::serialize(bool prettify) const { return _serialize(prettify ? 0 : -1); }
545
546template <typename Iter>
547void value::_indent(Iter oi, int indent) {
548 *oi++ = '\n';
549 for (int i = 0; i < indent * INDENT_WIDTH; ++i) { *oi++ = ' '; }
550}
551
552template <typename Iter>
553void value::_serialize(Iter oi, int indent) const {
554 switch (type_) {
555 case string_type: serialize_str(*u_.string_, oi); break;
556 case array_type: {
557 *oi++ = '[';
558 if (indent != -1) {
559 ++indent;
560 }
561 for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) {
562 if (i != u_.array_->begin()) {
563 *oi++ = ',';
564 }
565 if (indent != -1) {
566 _indent(oi, indent);
567 }
568 i->_serialize(oi, indent);
569 }
570 if (indent != -1) {
571 --indent;
572 if (!u_.array_->empty()) {
573 _indent(oi, indent);
574 }
575 }
576 *oi++ = ']';
577 break;
578 }
579 case object_type: {
580 *oi++ = '{';
581 if (indent != -1) {
582 ++indent;
583 }
584 for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) {
585 if (i != u_.object_->begin()) {
586 *oi++ = ',';
587 }
588 if (indent != -1) {
589 _indent(oi, indent);
590 }
591 serialize_str(i->first, oi);
592 *oi++ = ':';
593 if (indent != -1) {
594 *oi++ = ' ';
595 }
596 i->second._serialize(oi, indent);
597 }
598 if (indent != -1) {
599 --indent;
600 if (!u_.object_->empty()) {
601 _indent(oi, indent);
602 }
603 }
604 *oi++ = '}';
605 break;
606 }
607 default: copy(to_str(), oi); break;
608 }
609 if (indent == 0) {
610 *oi++ = '\n';
611 }
612}
613
614inline std::string value::_serialize(int indent) const {
615 std::string s;
616 _serialize(std::back_inserter(s), indent);
617 return s;
618}
619
620template <typename Iter>
621class input {
622 protected:
623 Iter cur_, end_;
625 int line_;
626
627 public:
628 input(const Iter& first, const Iter& last) : cur_(first), end_(last), consumed_(false), line_(1) {}
629 int getc() {
630 if (consumed_) {
631 if (*cur_ == '\n') {
632 ++line_;
633 }
634 ++cur_;
635 }
636 if (cur_ == end_) {
637 consumed_ = false;
638 return -1;
639 }
640 consumed_ = true;
641 return *cur_ & 0xff;
642 }
643 void ungetc() { consumed_ = false; }
644 Iter cur() const {
645 if (consumed_) {
646 input<Iter>* self = const_cast<input<Iter>*>(this);
647 self->consumed_ = false;
648 ++self->cur_;
649 }
650 return cur_;
651 }
652 int line() const { return line_; }
653 void skip_ws() {
654 while (1) {
655 int ch = getc();
656 if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
657 ungetc();
658 break;
659 }
660 }
661 }
662 bool expect(const int expected) {
663 skip_ws();
664 if (getc() != expected) {
665 ungetc();
666 return false;
667 }
668 return true;
669 }
670 bool match(const std::string& pattern) {
671 for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {
672 if (getc() != *pi) {
673 ungetc();
674 return false;
675 }
676 }
677 return true;
678 }
679};
680
681template <typename Iter>
683 int uni_ch = 0, hex;
684 for (int i = 0; i < 4; i++) {
685 if ((hex = in.getc()) == -1) {
686 return -1;
687 }
688 if ('0' <= hex && hex <= '9') {
689 hex -= '0';
690 } else if ('A' <= hex && hex <= 'F') {
691 hex -= 'A' - 0xa;
692 } else if ('a' <= hex && hex <= 'f') {
693 hex -= 'a' - 0xa;
694 } else {
695 in.ungetc();
696 return -1;
697 }
698 uni_ch = uni_ch * 16 + hex;
699 }
700 return uni_ch;
701}
702
703template <typename String, typename Iter>
704inline bool _parse_codepoint(String& out, input<Iter>& in) {
705 int uni_ch;
706 if ((uni_ch = _parse_quadhex(in)) == -1) {
707 return false;
708 }
709 if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
710 if (0xdc00 <= uni_ch) {
711 // a second 16-bit of a surrogate pair appeared
712 return false;
713 }
714 // first 16-bit of surrogate pair, get the next one
715 if (in.getc() != '\\' || in.getc() != 'u') {
716 in.ungetc();
717 return false;
718 }
719 int second = _parse_quadhex(in);
720 if (!(0xdc00 <= second && second <= 0xdfff)) {
721 return false;
722 }
723 uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
724 uni_ch += 0x10000;
725 }
726 if (uni_ch < 0x80) {
727 out.push_back(static_cast<char>(uni_ch));
728 } else {
729 if (uni_ch < 0x800) {
730 out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6)));
731 } else {
732 if (uni_ch < 0x10000) {
733 out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12)));
734 } else {
735 out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18)));
736 out.push_back(static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f)));
737 }
738 out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f)));
739 }
740 out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f)));
741 }
742 return true;
743}
744
745template <typename String, typename Iter>
746inline bool _parse_string(String& out, input<Iter>& in) {
747 while (1) {
748 int ch = in.getc();
749 if (ch < ' ') {
750 in.ungetc();
751 return false;
752 } else if (ch == '"') {
753 return true;
754 } else if (ch == '\\') {
755 if ((ch = in.getc()) == -1) {
756 return false;
757 }
758 switch (ch) {
759# define MAP(sym, val) \
760 case sym: out.push_back(val); break
761 MAP('"', '\"');
762 MAP('\\', '\\');
763 MAP('/', '/');
764 MAP('b', '\b');
765 MAP('f', '\f');
766 MAP('n', '\n');
767 MAP('r', '\r');
768 MAP('t', '\t');
769# undef MAP
770 case 'u':
771 if (!_parse_codepoint(out, in)) {
772 return false;
773 }
774 break;
775 default: return false;
776 }
777 } else {
778 out.push_back(static_cast<char>(ch));
779 }
780 }
781 return false;
782}
783
784template <typename Context, typename Iter>
785inline bool _parse_array(Context& ctx, input<Iter>& in) {
786 if (!ctx.parse_array_start()) {
787 return false;
788 }
789 size_t idx = 0;
790 if (in.expect(']')) {
791 return ctx.parse_array_stop(idx);
792 }
793 do {
794 if (!ctx.parse_array_item(in, idx)) {
795 return false;
796 }
797 idx++;
798 } while (in.expect(','));
799 return in.expect(']') && ctx.parse_array_stop(idx);
800}
801
802template <typename Context, typename Iter>
803inline bool _parse_object(Context& ctx, input<Iter>& in) {
804 if (!ctx.parse_object_start()) {
805 return false;
806 }
807 if (in.expect('}')) {
808 return true;
809 }
810 do {
811 std::string key;
812 if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
813 return false;
814 }
815 if (!ctx.parse_object_item(in, key)) {
816 return false;
817 }
818 } while (in.expect(','));
819 return in.expect('}');
820}
821
822template <typename Iter>
823inline std::string _parse_number(input<Iter>& in) {
824 std::string num_str;
825 while (1) {
826 int ch = in.getc();
827 if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {
828 num_str.push_back(static_cast<char>(ch));
829 } else if (ch == '.') {
830# if PICOJSON_USE_LOCALE
831 num_str += localeconv()->decimal_point;
832# else
833 num_str.push_back('.');
834# endif
835 } else {
836 in.ungetc();
837 break;
838 }
839 }
840 return num_str;
841}
842
843template <typename Context, typename Iter>
844inline bool _parse(Context& ctx, input<Iter>& in) {
845 in.skip_ws();
846 int ch = in.getc();
847 switch (ch) {
848# define IS(ch, text, op) \
849 case ch: \
850 if (in.match(text) && op) { \
851 return true; \
852 } else { \
853 return false; \
854 }
855 IS('n', "ull", ctx.set_null());
856 IS('f', "alse", ctx.set_bool(false));
857 IS('t', "rue", ctx.set_bool(true));
858# undef IS
859 case '"': return ctx.parse_string(in);
860 case '[': return _parse_array(ctx, in);
861 case '{': return _parse_object(ctx, in);
862 default:
863 if (('0' <= ch && ch <= '9') || ch == '-') {
864 double f;
865 char* endp;
866 in.ungetc();
867 std::string num_str(_parse_number(in));
868 if (num_str.empty()) {
869 return false;
870 }
871# ifdef PICOJSON_USE_INT64
872 {
873 errno = 0;
874 intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
875 if (errno == 0 && std::numeric_limits<int64_t>::min() <= ival &&
876 ival <= std::numeric_limits<int64_t>::max() && endp == num_str.c_str() + num_str.size()) {
877 ctx.set_int64(ival);
878 return true;
879 }
880 }
881# endif
882 f = strtod(num_str.c_str(), &endp);
883 if (endp == num_str.c_str() + num_str.size()) {
884 ctx.set_number(f);
885 return true;
886 }
887 return false;
888 }
889 break;
890 }
891 in.ungetc();
892 return false;
893}
894
896 public:
897 bool set_null() { return false; }
898 bool set_bool(bool) { return false; }
899# ifdef PICOJSON_USE_INT64
900 bool set_int64(int64_t) { return false; }
901# endif
902 bool set_number(double) { return false; }
903 template <typename Iter>
905 return false;
906 }
907 bool parse_array_start() { return false; }
908 template <typename Iter>
910 return false;
911 }
912 bool parse_array_stop(size_t) { return false; }
913 bool parse_object_start() { return false; }
914 template <typename Iter>
915 bool parse_object_item(input<Iter>&, const std::string&) {
916 return false;
917 }
918};
919
921 protected:
923
924 public:
926 bool set_null() {
927 *out_ = value();
928 return true;
929 }
930 bool set_bool(bool b) {
931 *out_ = value(b);
932 return true;
933 }
934# ifdef PICOJSON_USE_INT64
935 bool set_int64(int64_t i) {
936 *out_ = value(i);
937 return true;
938 }
939# endif
940 bool set_number(double f) {
941 *out_ = value(f);
942 return true;
943 }
944 template <typename Iter>
946 *out_ = value(string_type, false);
947 return _parse_string(out_->get<std::string>(), in);
948 }
950 *out_ = value(array_type, false);
951 return true;
952 }
953 template <typename Iter>
954 bool parse_array_item(input<Iter>& in, size_t) {
955 array& a = out_->get<array>();
956 a.push_back(value());
957 default_parse_context ctx(&a.back());
958 return _parse(ctx, in);
959 }
960 bool parse_array_stop(size_t) { return true; }
962 *out_ = value(object_type, false);
963 return true;
964 }
965 template <typename Iter>
966 bool parse_object_item(input<Iter>& in, const std::string& key) {
967 object& o = out_->get<object>();
968 default_parse_context ctx(&o[key]);
969 return _parse(ctx, in);
970 }
971
972 private:
975};
976
978 public:
979 struct dummy_str {
980 void push_back(int) {}
981 };
982
983 public:
985 bool set_null() { return true; }
986 bool set_bool(bool) { return true; }
987# ifdef PICOJSON_USE_INT64
988 bool set_int64(int64_t) { return true; }
989# endif
990 bool set_number(double) { return true; }
991 template <typename Iter>
993 dummy_str s;
994 return _parse_string(s, in);
995 }
996 bool parse_array_start() { return true; }
997 template <typename Iter>
998 bool parse_array_item(input<Iter>& in, size_t) {
999 return _parse(*this, in);
1000 }
1001 bool parse_array_stop(size_t) { return true; }
1002 bool parse_object_start() { return true; }
1003 template <typename Iter>
1004 bool parse_object_item(input<Iter>& in, const std::string&) {
1005 return _parse(*this, in);
1006 }
1007
1008 private:
1010 null_parse_context& operator=(const null_parse_context&);
1011};
1012
1013// obsolete, use the version below
1014template <typename Iter>
1015inline std::string parse(value& out, Iter& pos, const Iter& last) {
1016 std::string err;
1017 pos = parse(out, pos, last, &err);
1018 return err;
1019}
1020
1021template <typename Context, typename Iter>
1022inline Iter _parse(Context& ctx, const Iter& first, const Iter& last, std::string* err) {
1023 input<Iter> in(first, last);
1024 if (!_parse(ctx, in) && err != NULL) {
1025 char buf[64];
1026 SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
1027 *err = buf;
1028 while (1) {
1029 int ch = in.getc();
1030 if (ch == -1 || ch == '\n') {
1031 break;
1032 } else if (ch >= ' ') {
1033 err->push_back(static_cast<char>(ch));
1034 }
1035 }
1036 }
1037 return in.cur();
1038}
1039
1040template <typename Iter>
1041inline Iter parse(value& out, const Iter& first, const Iter& last, std::string* err) {
1042 default_parse_context ctx(&out);
1043 return _parse(ctx, first, last, err);
1044}
1045
1046inline std::string parse(value& out, const std::string& s) {
1047 std::string err;
1048 parse(out, s.begin(), s.end(), &err);
1049 return err;
1050}
1051
1052inline std::string parse(value& out, std::istream& is) {
1053 std::string err;
1054 parse(out, std::istreambuf_iterator<char>(is.rdbuf()), std::istreambuf_iterator<char>(), &err);
1055 return err;
1056}
1057
1058template <typename T>
1060 static std::string s;
1061};
1062template <typename T>
1063std::string last_error_t<T>::s;
1064
1065inline void set_last_error(const std::string& s) { last_error_t<bool>::s = s; }
1066
1067inline const std::string& get_last_error() { return last_error_t<bool>::s; }
1068
1069inline bool operator==(const value& x, const value& y) {
1070 if (x.is<null>())
1071 return y.is<null>();
1072# define PICOJSON_CMP(type) \
1073 if (x.is<type>()) \
1074 return y.is<type>() && x.get<type>() == y.get<type>()
1075 PICOJSON_CMP(bool);
1076 PICOJSON_CMP(double);
1077 PICOJSON_CMP(std::string);
1079 PICOJSON_CMP(object);
1080# undef PICOJSON_CMP
1081 PICOJSON_ASSERT(0);
1082# ifdef _MSC_VER
1083 __assume(0);
1084# endif
1085 return false;
1086}
1087
1088inline bool operator!=(const value& x, const value& y) { return !(x == y); }
1089} // namespace picojson
1090
1091# if !PICOJSON_USE_RVALUE_REFERENCE
1092namespace std {
1093template <>
1095 x.swap(y);
1096}
1097} // namespace std
1098# endif
1099
1100inline std::istream& operator>>(std::istream& is, picojson::value& x) {
1101 picojson::set_last_error(std::string());
1102 const std::string err(picojson::parse(x, is));
1103 if (!err.empty()) {
1105 is.setstate(std::ios::failbit);
1106 }
1107 return is;
1108}
1109
1110inline std::ostream& operator<<(std::ostream& os, const picojson::value& x) {
1111 x.serialize(std::ostream_iterator<char>(os));
1112 return os;
1113}
1114# ifdef _MSC_VER
1115# pragma warning(pop)
1116# endif
1117
1118#endif
1119
1120using namespace std;
1121
1122const string test_includes = "// Generated by spec_test_generator. DO NOT MODIFY THIS FILE.\n\n"
1123 "#include <algorithm>\n#include <vector>\n#include <iostream>\n#include "
1124 "<iterator>\n#include <cmath>\n#include <cstdlib>\n#include <catch2/catch.hpp>\n"
1125 "#include <utils.hpp>\n#include <wasm_config.hpp>\n#include "
1126 "<sysio/vm/backend.hpp>\n\nusing namespace sysio;\nusing namespace sysio::vm;\n"
1127 "extern wasm_allocator wa;\n\n";
1128const string test_preamble_0 = "using backend_t = backend<standalone_function_t, TestType>;\n auto code = read_wasm( ";
1129const string test_preamble_1 = "backend_t bkend( code, &wa );";
1130
1131std::string cpp_string(const picojson::value& x) {
1132 std::string result = "\"";
1133 std::string original = x.to_str();
1134 for(unsigned char ch : original) {
1135 if ((unsigned char)ch < 0x20 || (unsigned char)ch > 0x7F) {
1136 // Use a three digit octal escape, because that won't
1137 // accidentally consume the following characters.
1138 result += '\\';
1139 result += (((unsigned char)ch >> 6) & 0x07) + '0';
1140 result += (((unsigned char)ch >> 3) & 0x07) + '0';
1141 result += (((unsigned char)ch >> 0) & 0x07) + '0';
1142 } else if(ch == '\\' || ch == '\"') {
1143 result += '\\';
1144 result += ch;
1145 } else {
1146 result += ch;
1147 }
1148 }
1149 result += "\"";
1150 // If the string contains a null byte, make sure that we don't truncate the literal
1151 if(original.find('\0') == std::string::npos)
1152 return result;
1153 else
1154 return "std::string(" + result + ", " + std::to_string(original.size()) + ")";
1155}
1156
1157string generate_test_call(picojson::object obj, string expected_t, string expected_v) {
1158 stringstream ss;
1159
1160 if (expected_t == "i32") {
1161 ss << "bkend.call_with_return(\"env\", ";
1162 } else if (expected_t == "i64") {
1163 ss << "bkend.call_with_return(\"env\", ";
1164 } else if (expected_t == "f32") {
1165 ss << "bit_cast<uint32_t>(bkend.call_with_return(\"env\", ";
1166 } else if (expected_t == "f64") {
1167 ss << "bit_cast<uint64_t>(bkend.call_with_return(\"env\", ";
1168 } else {
1169 ss << "!bkend.call_with_return(\"env\", ";
1170 }
1171
1172 ss << cpp_string(obj["field"]);
1173
1174 for (picojson::value argv : obj["args"].get<picojson::array>()) {
1175 ss << ", ";
1177 if (arg["type"].to_str() == "i32")
1178 ss << "UINT32_C(" << arg["value"].to_str() << ")";
1179 else if (arg["type"].to_str() == "i64")
1180 ss << "UINT64_C(" << arg["value"].to_str() << ")";
1181 else if (arg["type"].to_str() == "f32")
1182 ss << "bit_cast<float>(UINT32_C(" << arg["value"].to_str() << "))";
1183 else
1184 ss << "bit_cast<double>(UINT64_C(" << arg["value"].to_str() << "))";
1185 }
1186 if (expected_t == "i32") {
1187 ss << ")->to_ui32() == ";
1188 ss << "UINT32_C(" << expected_v << ")";
1189 } else if (expected_t == "i64") {
1190 ss << ")->to_ui64() == ";
1191 ss << "UINT32_C(" << expected_v << ")";
1192 } else if (expected_t == "f32") {
1193 ss << ")->to_f32()) == ";
1194 ss << "UINT32_C(" << expected_v << ")";
1195 } else if (expected_t == "f64") {
1196 ss << ")->to_f64()) == ";
1197 ss << "UINT64_C(" << expected_v << ")";
1198 } else {
1199 ss << ")";
1200 }
1201 return ss.str();
1202}
1203
1204
1206 stringstream ss;
1207
1208 ss << "check_nan(bkend.call_with_return(\"env\", ";
1209
1210 ss << cpp_string(obj["field"]);
1211
1212 for (picojson::value argv : obj["args"].get<picojson::array>()) {
1213 ss << ", ";
1215 if (arg["type"].to_str() == "i32")
1216 ss << "UINT32_C(" << arg["value"].to_str() << ")";
1217 else if (arg["type"].to_str() == "i64")
1218 ss << "UINT64_C(" << arg["value"].to_str() << ")";
1219 else if (arg["type"].to_str() == "f32")
1220 ss << "bit_cast<float>(UINT32_C(" << arg["value"].to_str() << "))";
1221 else
1222 ss << "bit_cast<double>(UINT64_C(" << arg["value"].to_str() << "))";
1223 }
1224 ss << "))";
1225 return ss.str();
1226}
1227
1229 stringstream ss;
1230
1231 ss << "bkend(\"env\", ";
1232 ss << cpp_string(obj["field"]);
1233
1234 for (picojson::value argv : obj["args"].get<picojson::array>()) {
1235 ss << ", ";
1237 if (arg["type"].to_str() == "i32")
1238 ss << "UINT32_C(" << arg["value"].to_str() << ")";
1239 else if (arg["type"].to_str() == "i64")
1240 ss << "UINT64_C(" << arg["value"].to_str() << ")";
1241 else if (arg["type"].to_str() == "f32")
1242 ss << "bit_cast<float>(UINT32_C(" << arg["value"].to_str() << "))";
1243 else
1244 ss << "bit_cast<double>(UINT64_C(" << arg["value"].to_str() << "))";
1245 }
1246 ss << "), std::exception";
1247 return ss.str();
1248}
1249
1251 stringstream ss;
1252
1253 ss << "bkend(\"env\", ";
1254 ss << cpp_string(obj["field"]);
1255
1256 for (picojson::value argv : obj["args"].get<picojson::array>()) {
1257 ss << ", ";
1259 if (arg["type"].to_str() == "i32")
1260 ss << "UINT32_C(" << arg["value"].to_str() << ")";
1261 else if (arg["type"].to_str() == "i64")
1262 ss << "UINT64_C(" << arg["value"].to_str() << ")";
1263 else if (arg["type"].to_str() == "f32")
1264 ss << "bit_cast<float>(UINT32_C(" << arg["value"].to_str() << "))";
1265 else
1266 ss << "bit_cast<double>(UINT64_C(" << arg["value"].to_str() << "))";
1267 }
1268 ss << ")";
1269 return ss.str();
1270}
1271
1272// Tests that should be skipped because they use unsupported imports.
1273const std::set<std::string> blacklist = {
1274 "data.2.wasm",
1275 "data.4.wasm", "data.5.wasm", "data.6.wasm", "data.7.wasm", "data.8.wasm", "data.10.wasm",
1276 "data.13.wasm", "data.17.wasm", "data.19.wasm", "data.20.wasm", "data.21.wasm",
1277 "data.22.wasm", "data.23.wasm", "data.24.wasm", "data.30.wasm", "data.32.wasm", "data.36.wasm", "data.38.wasm",
1278 "elem.2.wasm", "elem.4.wasm", "elem.5.wasm", "elem.6.wasm", "elem.9.wasm",
1279 "elem.11.wasm", "elem.14.wasm", "elem.15.wasm", "elem.16.wasm", "elem.17.wasm", "elem.23.wasm",
1280 "elem.25.wasm", "elem.27.wasm", "elem.29.wasm", "elem.37.wasm", "elem.39.wasm", "elem.40.wasm",
1281 "func_ptrs.0.wasm", "globals.14.wasm", "names.3.wasm",
1282 "start.5.wasm", "start.6.wasm", "start.7.wasm"
1283};
1284
1285void generate_tests(const map<string, vector<picojson::object>>& mappings) {
1286 stringstream unit_tests;
1287 string exp_t, exp_v;
1288 unit_tests << test_includes;
1289 auto grab_expected = [&](auto obj) {
1290 exp_t = obj["type"].to_str();
1291 exp_v = obj["value"].to_str();
1292 };
1293
1294 for (const auto& [tsn_file, cmds] : mappings) {
1295 if(tsn_file.empty() || blacklist.count(tsn_file)) continue;
1296 auto tsn = tsn_file;
1297 std::replace(tsn.begin(), tsn.end(), '.', '_');
1298 unit_tests << "BACKEND_TEST_CASE( \"Testing wasm <" << tsn << ">\", \"[" << tsn << "_tests]\" ) {\n";
1299 unit_tests << " " << test_preamble_0 << "std::string(wasm_directory) + \"" << tsn_file << "\");\n";
1300
1301 if(!cmds.empty() && [](picojson::object cmd) {
1302 return (cmd["type"].to_str() == "assert_invalid" ||
1303 cmd["type"].to_str() == "assert_malformed" ||
1304 cmd["type"].to_str() == "assert_unlinkable") &&
1305 cmd["module_type"].to_str() == "binary"; }(cmds.front())) {
1306 if (picojson::object(cmds.front())["type"].to_str() == "assert_unlinkable") {
1307 // Unfortunately it's no longer possible to distingish between link errors and parse errors.
1308 unit_tests << " CHECK_THROWS_AS(backend_t(code, &wa), std::exception);\n";
1309 } else {
1310 unit_tests << " CHECK_THROWS_AS(backend_t(code, nullptr), std::exception);\n";
1311 }
1312 } else {
1313 unit_tests << " " << test_preamble_1 << "\n\n";
1314
1315 for (picojson::object cmd : cmds) {
1316 if (cmd["type"].to_str() == "assert_return") {
1317 unit_tests << " CHECK(";
1318 exp_t = "";
1319 exp_v = "";
1320 if (cmd["expected"].get<picojson::array>().size() > 0) {
1321 grab_expected(cmd["expected"].get<picojson::array>()[0].get<picojson::object>());
1322 unit_tests << generate_test_call(cmd["action"].get<picojson::object>(), exp_t, exp_v) << ");\n";
1323 } else {
1324 unit_tests << generate_test_call(cmd["action"].get<picojson::object>(), exp_t, exp_v) << ");\n";
1325 }
1326 } else if (cmd["type"].to_str() == "assert_trap") {
1327 unit_tests << " CHECK_THROWS_AS(";
1328 unit_tests << generate_trap_call(cmd["action"].get<picojson::object>()) << ");\n";
1329 } else if (cmd["type"].to_str() == "action") {
1330 unit_tests << generate_call(cmd["action"].get<picojson::object>()) << ";\n";
1331 } else if (cmd["type"].to_str() == "assert_return_canonical_nan" ||
1332 cmd["type"].to_str() == "assert_return_arithmetic_nan") {
1333 unit_tests << " CHECK(";
1334 unit_tests << generate_test_call_nan(cmd["action"].get<picojson::object>()) << ");\n";
1335 }
1336 }
1337 }
1338 unit_tests << "}\n\n";
1339 cout << unit_tests.str();
1340 unit_tests.str("");
1341 }
1342}
1343
1344void usage(const char* name) {
1345 std::cerr << "Usage:\n"
1346 << " " << name << " [json file created by wast2json]\n";
1347 std::exit(2);
1348}
1349
1350int main(int argc, char** argv) {
1351 ifstream ifs;
1352 stringstream ss;
1353 if(argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
1354 usage(argc?argv[0]:"spec_test_generator");
1355 }
1356 ifs.open(argv[1]);
1357 if(!ifs) {
1358 std::cerr << "Cannot open file: " << argv[1] << std::endl;
1359 return EXIT_FAILURE;
1360 }
1361 string s;
1362 while (getline(ifs, s)) ss << s;
1363 ifs.close();
1364
1366 picojson::parse(v, ss.str());
1367 string test_suite_name;
1368
1369 map<string, vector<picojson::object>> test_mappings;
1371 for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); i++)
1372 if (i->first == "commands") {
1373 for (const auto& o : i->second.get<picojson::array>()) {
1374 picojson::object obj = o.get<picojson::object>();
1375 if (obj["type"].to_str() == "module" || obj["type"].to_str() == "assert_invalid" || (obj["type"].to_str() == "assert_malformed" && obj["module_type"].to_str() == "binary") || obj["type"].to_str() == "assert_unlinkable" ) {
1376 test_suite_name = obj["filename"].to_str();
1377 test_mappings[test_suite_name] = {};
1378 }
1379 test_mappings[test_suite_name].push_back(obj);
1380 }
1381 }
1382
1383 generate_tests(test_mappings);
1384}
#define strtod
const mie::Vuint & p
Definition bn.cpp:27
std::string name
bool parse_array_item(input< Iter > &in, size_t)
bool parse_object_item(input< Iter > &in, const std::string &key)
bool parse_object_item(input< Iter > &, const std::string &)
bool parse_array_item(input< Iter > &, size_t)
input(const Iter &first, const Iter &last)
bool match(const std::string &pattern)
bool expect(const int expected)
bool parse_array_item(input< Iter > &in, size_t)
bool parse_object_item(input< Iter > &in, const std::string &)
bool parse_string(input< Iter > &in)
std::string to_str() const
const T & get() const
void serialize(Iter os, bool prettify=false) const
std::vector< value > array
bool evaluate_as_boolean() const
void set(const T &)
void swap(value &x) PICOJSON_NOEXCEPT
bool contains(const size_t idx) const
value & operator=(const value &x)
std::map< std::string, value > object
bool is() const
os_t os
char ** argv
#define strtoimax
Definition inttypes.h:307
#define PRId64
Definition inttypes.h:88
#define SET(a, b, c, d, k, s, Ti)
bool _parse_codepoint(String &out, input< Iter > &in)
void serialize_str(const std::string &s, Iter oi)
std::string parse(value &out, Iter &pos, const Iter &last)
value::object object
bool _parse_object(Context &ctx, input< Iter > &in)
bool operator==(const value &x, const value &y)
void set_last_error(const std::string &s)
const std::string & get_last_error()
std::string _parse_number(input< Iter > &in)
int _parse_quadhex(input< Iter > &in)
bool _parse_string(String &out, input< Iter > &in)
void copy(const std::string &s, Iter oi)
value::array array
bool _parse_array(Context &ctx, input< Iter > &in)
bool operator!=(const value &x, const value &y)
bool _parse(Context &ctx, input< Iter > &in)
Definition name.hpp:106
std::ostream & operator<<(std::ostream &st, const std::variant< fc::alt_bn128_error, bytes > &err)
void swap(picojson::value &x, picojson::value &y)
#define value
Definition pkcs11.h:157
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1181
#define T(meth, val, expected)
#define PICOJSON_ASSERT(e)
#define INIT(p, v)
const string test_includes
#define MAP(val, sym)
#define IS(ctype, jtype)
void generate_tests(const map< string, vector< picojson::object > > &mappings)
std::string cpp_string(const picojson::value &x)
string generate_call(picojson::object obj)
#define GET(ctype, var)
string generate_test_call_nan(picojson::object obj)
#define SNPRINTF
string generate_test_call(picojson::object obj, string expected_t, string expected_v)
void usage(const char *name)
const string test_preamble_0
std::istream & operator>>(std::istream &is, picojson::value &x)
string generate_trap_call(picojson::object obj)
const string test_preamble_1
#define DEINIT(p)
#define PICOJSON_CMP(type)
#define PICOJSON_NOEXCEPT
int64_t intmax_t
Definition stdint.h:169
signed __int64 int64_t
Definition stdint.h:135
void fabs()
uint8_t key[16]
Definition yubico_otp.c:41
char * s
size_t len
uint8_t buf[2048]
bool set