#define BOOST_SPIRIT_X3_NO_FILESYSTEM // TODO #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace json { struct value; enum class value_kind { object, array, number, string, boolean, null }; struct null_t {}; namespace detail { template struct is_object; template struct is_array; template struct is_string; struct value_impl_base { virtual ~value_impl_base() = 0; virtual std::unique_ptr copy_impl() const = 0; virtual value_kind kind() const noexcept = 0; virtual std::ostream & to_json(std::ostream & os) const noexcept = 0; }; inline value_impl_base::~value_impl_base() {} template struct value_impl; // TODO: Use this, and put objects (unordered_maps) on the heap, in // the implementation of jason_value_impl below. constexpr int value_buffer_size() { return (std::max)(sizeof(std::vector), sizeof(std::string)); } } template T const & get(value const & v) noexcept; template T & get(value & v) noexcept; using array = std::vector; using object = std::unordered_map; // TODO: operator==(). struct value { value(); value(value const & other) { if (other.ptr_) ptr_ = other.ptr_->copy_impl(); } value & operator=(value const & other) { ptr_.reset(); if (other.ptr_) ptr_ = other.ptr_->copy_impl(); return *this; } template value( JSONObject const & o, typename std::enable_if::value>:: type ** enable = nullptr); template value( JSONArray const & a, typename std::enable_if::value>::type ** enable = nullptr); value(double d); template value( String const & str, typename std::enable_if::value>::type ** enable = nullptr); value(std::string && str); value(char const * c_str); value(bool b); value(null_t); template typename std::enable_if::value, value &>:: type operator=(JSONObject const & o); template typename std::enable_if::value, value &>:: type operator=(JSONArray const & a); value & operator=(double d); template typename std::enable_if::value, value &>::type operator=(String const & str); value & operator=(std::string && str); value & operator=(char const * c_str); value & operator=(bool b); value & operator=(null_t); value_kind kind() const noexcept { return ptr_->kind(); } bool is_object() const noexcept { return ptr_->kind() == value_kind::object; } bool is_array() const noexcept { return ptr_->kind() == value_kind::array; } bool is_number() const noexcept { return ptr_->kind() == value_kind::number; } bool is_string() const noexcept { return ptr_->kind() == value_kind::string; } bool is_boolean() const noexcept { return ptr_->kind() == value_kind::boolean; } bool is_null() const noexcept { return ptr_->kind() == value_kind::null; } template friend T const & get(value const & v) noexcept; template friend T & get(value & v) noexcept; friend std::ostream & operator<<(std::ostream & os, value const & value) { return value.ptr_->to_json(os); } private: std::unique_ptr ptr_; }; namespace detail { template struct void_ { using type = void; }; template using void_t = typename void_::type; template using remove_cv_ref_t = typename std::remove_cv< typename std::remove_reference::type>::type; struct nonesuch {}; template< typename Default, typename AlwaysVoid, template class Template, typename... Args> struct detector { using value_t = std::false_type; using type = Default; }; template< typename Default, template class Template, typename... Args> struct detector>, Template, Args...> { using value_t = std::true_type; using type = Template; }; template class Template, typename... Args> using is_detected = typename detector::value_t; template class Template, typename... Args> using detected_t = typename detector::type; template< typename Default, template class Template, typename... Args> using detected_or = typename detector::type; template using has_begin = decltype(*std::begin(std::declval())); template using has_end = decltype(*std::end(std::declval())); template using is_range_of = std::integral_constant< bool, ((std::is_same>, U>:: value || std::is_same>, V>:: value) && is_detected::value)>; template struct is_object : is_range_of< T, std::pair, std::pair> { }; template struct is_array : is_range_of { }; template using is_range_of_char = std::integral_constant< bool, (std::is_convertible::value && sizeof(BeginValueType) == 1 && is_detected::value)>; template struct is_string : is_range_of_char>> { }; template<> struct value_impl : value_impl_base { template value_impl(Iter first, Iter last) : value_(first, last) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::object; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { // TODO: Indentation. os << '{'; bool first = true; for (auto const & pair : value_) { if (!first) os << ", "; os << '"' << pair.first << "\": " << pair.second; first = false; } os << '}'; return os; } object value_; }; template<> struct value_impl : value_impl_base { template value_impl(Iter first, Iter last) : value_(first, last) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::array; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { // TODO: Indentation. os << '['; bool first = true; for (auto const & value : value_) { if (!first) os << ", "; os << value; first = false; } os << ']'; return os; } array value_; }; template<> struct value_impl : value_impl_base { value_impl(double d) : value_(d) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::number; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { return os << value_; } double value_; }; template<> struct value_impl : value_impl_base { value_impl(std::string && s) : value_(std::move(s)) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::string; } template static std::ostream & write_escaped_cps(std::ostream & os, CPSubRange subr) { os << std::hex << std::setfill('0'); for (auto cp : subr) { switch (cp) { case 0x0022: os << "\\\""; break; case 0x005c: os << "\\\\"; break; case 0x002f: os << "\\/"; break; case 0x0008: os << "\\b"; break; case 0x000c: os << "\\f"; break; case 0x000a: os << "\\n"; break; case 0x000d: os << "\\r"; break; case 0x0009: os << "\\t"; break; default: if (cp < 0x20) { os << "\\u" << std::setw(4) << cp; } else { uint16_t const high_surrogate_base = 0xd7c0; uint16_t const low_surrogate_base = 0xdc00; auto const hi = static_cast(cp >> 10) + high_surrogate_base; auto const lo = static_cast(cp & 0x3ff) + low_surrogate_base; os << "\\u" << std::setw(4) << hi; os << "\\u" << std::setw(4) << lo; } break; } } os << std::dec; return os; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { os << '"'; auto const r = boost::text::make_to_utf32_range(value_); char const * last_written_it = &*value_.begin(); boost::text::foreach_subrange_if( r.begin(), r.end(), [](uint32_t cp) { return cp < 0x0020 || 0xffff < cp; }, [&](auto subr) { os.write( last_written_it, &*subr.begin().base() - last_written_it); write_escaped_cps(os, subr); last_written_it = &*subr.end().base(); }); os.write(last_written_it, &*value_.end() - last_written_it); os << '"'; return os; } std::string value_; }; template<> struct value_impl : value_impl_base { value_impl(bool b) : value_(b) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::boolean; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { return os << (value_ ? "true" : "false"); } bool value_; }; template<> struct value_impl : value_impl_base { value_impl() {} value_impl(null_t) {} virtual std::unique_ptr copy_impl() const override { return std::unique_ptr( new value_impl(*this)); } virtual value_kind kind() const noexcept override { return value_kind::null; } virtual std::ostream & to_json(std::ostream & os) const noexcept override { return os << "null"; } null_t value_; }; } inline value::value() { *this = null_t{}; } template value::value( JSONObject const & o, typename std::enable_if::value>::type ** enable) : ptr_(new detail::value_impl(o.begin(), o.end())) {} template value::value( JSONArray const & a, typename std::enable_if::value>::type ** enable) : ptr_(new detail::value_impl(a.begin(), a.end())) {} inline value::value(double d) : ptr_(new detail::value_impl(d)) {} inline value::value(std::string && s) : ptr_(new detail::value_impl{std::move(s)}) {} inline value::value(char const * c_str) : value(std::string(c_str)) {} template value::value( String const & str, typename std::enable_if::value>::type ** enable) : value(std::string(std::begin(str), std::end(str))) {} inline value::value(bool b) : ptr_(new detail::value_impl(b)) {} inline value::value(null_t) : ptr_(new detail::value_impl) {} template typename std::enable_if::value, value &>::type value::operator=(JSONObject const & object) { return *this = value(object); } template typename std::enable_if::value, value &>::type value::operator=(JSONArray const & object) { return *this = value(object); } inline value & value::operator=(double d) { return *this = value(d); } inline value & value::operator=(std::string && s) { return *this = value(std::move(s)); } inline value & value::operator=(char const * c_str) { return *this = value(std::string(c_str)); } template typename std::enable_if::value, value &>::type value::operator=(String const & str) { return *this = value(std::string(std::begin(str), std::end(str))); } inline value & value::operator=(bool b) { return *this = value(b); } inline value & value::operator=(null_t) { return *this = value(null_t{}); } template<> inline object const & get(value const & v) noexcept { assert(v.is_object()); return static_cast *>(v.ptr_.get())->value_; } template<> inline array const & get(value const & v) noexcept { assert(v.is_array()); return static_cast *>(v.ptr_.get())->value_; } template<> inline double const & get(value const & v) noexcept { assert(v.is_number()); return static_cast *>(v.ptr_.get())->value_; } template<> inline std::string const & get(value const & v) noexcept { assert(v.is_string()); return static_cast *>(v.ptr_.get()) ->value_; } template<> inline bool const & get(value const & v) noexcept { assert(v.is_boolean()); return static_cast *>(v.ptr_.get())->value_; } template<> inline null_t const & get(value const & v) noexcept { assert(v.is_null()); return static_cast *>(v.ptr_.get())->value_; } template<> inline object & get(value & v) noexcept { assert(v.is_object()); return static_cast *>(v.ptr_.get())->value_; } template<> inline array & get(value & v) noexcept { assert(v.is_array()); return static_cast *>(v.ptr_.get())->value_; } template<> inline double & get(value & v) noexcept { assert(v.is_number()); return static_cast *>(v.ptr_.get())->value_; } template<> inline std::string & get(value & v) noexcept { assert(v.is_string()); return static_cast *>(v.ptr_.get()) ->value_; } template<> inline bool & get(value & v) noexcept { assert(v.is_boolean()); return static_cast *>(v.ptr_.get())->value_; } template<> inline null_t & get(value & v) noexcept { assert(v.is_null()); return static_cast *>(v.ptr_.get())->value_; } // parsing namespace x3 = boost::spirit::x3; namespace ascii = boost::spirit::x3::ascii; x3::rule const ws = "whitespace"; auto const ws_def = x3::lit('\x09') | '\x0a' | '\x0d' | '\x20'; BOOST_SPIRIT_DEFINE(ws); auto append_utf8 = [](auto & ctx) { std::array const cps = {{_attr(ctx)}}; auto const r = boost::text::make_from_utf32_range(cps); std::string & str = _val(ctx); str.insert(str.end(), r.begin(), r.end()); }; auto object_init = [](auto & ctx) { _val(ctx) = object(); }; auto object_insert = [](auto & ctx) { value & v = _val(ctx); get(v).insert(std::make_pair( std::move(boost::fusion::at_c<0>(_attr(ctx))), std::move(boost::fusion::at_c<1>(_attr(ctx))))); }; auto array_init = [](auto & ctx) { _val(ctx) = array(); }; auto array_append = [](auto & ctx) { value & v = _val(ctx); get(v).push_back(std::move(_attr(ctx))); }; struct first_surrogate_tag {}; auto first_hex_escape = [](auto & ctx) { uint32_t const cu = _attr(ctx); if (!boost::text::utf8::high_surrogate(cu)) _pass(ctx) = false; else x3::get(ctx).get() = cu; }; auto second_hex_escape = [](auto & ctx) { uint32_t const cu = _attr(ctx); if (!boost::text::utf8::low_surrogate(cu)) { _pass(ctx) = false; } else { uint32_t const high_surrogate_min = 0xd800; uint32_t const low_surrogate_min = 0xdc00; uint32_t const surrogate_offset = 0x10000 - (high_surrogate_min << 10) - low_surrogate_min; uint32_t const first_cu = x3::get(ctx).get(); _val(ctx) = (first_cu << 10) + cu + surrogate_offset; } }; x3::rule const string_char = "code point (code points <= U+001F must be escaped)"; x3::rule const hex_digit = "hexidecimal digit"; x3::rule const four_hex_digits = "four hexidecimal digits"; x3::rule const escape_seq = "\\uXXXX hexidecimal escape sequence"; x3::rule const escape_double_seq = "\\uXXXX hexidecimal escape sequence"; x3::rule const single_escaped_char = "\", \\, /, b, f, n, r, or t"; auto const hex_digit_def = x3::ascii::xdigit; BOOST_SPIRIT_DEFINE(hex_digit); auto const four_hex_digits_def = &x3::repeat(4)[hex_digit]; BOOST_SPIRIT_DEFINE(four_hex_digits); auto const escape_seq_def = (x3::lit("\\u") > four_hex_digits) >> x3::hex; BOOST_SPIRIT_DEFINE(escape_seq); auto const escape_double_seq_def = escape_seq[first_hex_escape] >> escape_seq[second_hex_escape]; BOOST_SPIRIT_DEFINE(escape_double_seq); auto const single_escaped_char_def = x3::lit('"') >> x3::attr(0x0022u) | x3::lit('\\') >> x3::attr(0x005cu) | x3::lit('/') >> x3::attr(0x002fu) | x3::lit('b') >> x3::attr(0x0008u) | x3::lit('f') >> x3::attr(0x000cu) | x3::lit('n') >> x3::attr(0x000au) | x3::lit('r') >> x3::attr(0x000du) | x3::lit('t') >> x3::attr(0x0009u); BOOST_SPIRIT_DEFINE(single_escaped_char); auto const string_char_def = escape_double_seq | escape_seq | x3::lit('\\') > single_escaped_char | (x3::unicode::char_ - x3::unicode::char_(0x0000u, 0x001fu)); BOOST_SPIRIT_DEFINE(string_char); x3::rule const null = "null"; x3::rule const string = "string"; x3::rule> const object_element = "object_element"; x3::rule const object_p = "object"; x3::rule const array_p = "array"; struct value_parser_struct; x3::rule const value_p = "value"; auto const null_def = x3::lit("null") >> x3::attr(value()); BOOST_SPIRIT_DEFINE(null); auto const string_def = x3::lit('"') >> x3::lexeme[*(string_char[append_utf8] - '"')] > '"'; BOOST_SPIRIT_DEFINE(string); auto const object_element_def = string > ':' > value_p; BOOST_SPIRIT_DEFINE(object_element); auto const object_p_def = x3::lit('{')[object_init] >> -(object_element[object_insert] % ',') > '}'; BOOST_SPIRIT_DEFINE(object_p); auto const array_p_def = x3::lit('[')[array_init] >> -(value_p[array_append] % ',') > ']'; BOOST_SPIRIT_DEFINE(array_p); auto const value_p_def = x3::double_ | x3::bool_ | null | string | array_p | object_p; BOOST_SPIRIT_DEFINE(value_p); struct error_handler_base { template x3::error_handler_result on_error( Iterator & first, Iterator const & last, Exception const & e, Context const & ctx) { auto & error_handler = x3::get(ctx).get(); std::string message = "Error! Expecting: " + e.which() + " here:"; error_handler(e.where(), message); return x3::error_handler_result::fail; } }; struct value_parser_struct : error_handler_base {}; template boost::optional parse(CPRange const & range) { using iter_t = decltype(range.begin()); auto first = range.begin(); auto const last = range.end(); // TODO: Emit the error via a callback instead of jsut dumping it to // the console. x3::error_handler error_handler{first, last, std::cout}; uint32_t first_surrogate = 0; auto parser = x3::with( std::ref(error_handler))[x3::with( std::ref(first_surrogate))[value_p]]; value v; bool const result = x3::phrase_parse(first, last, parser, ws, v); if (!result || first != last) return {}; return boost::optional(std::move(v)); } }} int main() { std::string str; while (getline(std::cin, str)) { if (str.empty()) break; auto const range = boost::text::make_to_utf32_range(str); boost::optional value; if (value = boost::json::parse(range)) { std::cout << "Parsed value:\n"; std::cout << *value << std::endl; } } return 0; }