JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
parse_index.hpp
Go to the documentation of this file.
1/// \file jsonv/parse_index.hpp
2/// Parsed index of a JSON document.
3///
4/// Copyright (c) 2020 by Travis Gockel. All rights reserved.
5///
6/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8/// version.
9///
10/// \author Travis Gockel (travis@gockelhut.com)
11#pragma once
12
13#include <jsonv/config.hpp>
14#include <jsonv/ast.hpp>
15
16#include <optional>
17
18namespace jsonv
19{
20
21class deserialize_options;
22class documents;
23class parse_options;
24
25/// \addtogroup Value
26/// \{
27
28/// Represents the index of a parsed AST. When combined with the original text, can be used to create a \c value. See
29/// \c parse_index::parse to construct instances from JSON source text.
31{
32public:
33 class iterator final
34 {
35 public:
36 using value_type = ast_node;
37
38 public:
39 iterator() = default;
40
41 iterator& operator++();
42 iterator operator++(int)
43 {
44 iterator temp(*this);
45 ++*this;
46 return temp;
47 }
48
49 /// Move to the node after the structure this iterator is on, however much is inside it.
50 ///
51 /// This is a constant-time operation. The index records where each structure ends as it parses the matching
52 /// close token, so stepping over a subtree costs the same whether it holds one element or a million. Use it to
53 /// ignore a value without paying to walk it.
54 ///
55 /// \code
56 /// // `iter` is on the `[` of `"a": [ 1, 2, 3 ]`
57 /// iter.skip_subtree(); // now on the key which follows the array
58 /// \endcode
59 ///
60 /// If the structure was never closed -- only possible when \ref parse_index::success is \c false -- the
61 /// error which truncated the document stands in as its end, so this moves to the end of the index.
62 ///
63 /// \throws std::invalid_argument if this iterator is not on a \c ast_node_type::document_start,
64 /// \c ast_node_type::object_begin or \c ast_node_type::array_begin.
66
68 value_type operator*() const;
69
70 // Note the lack of comparison between `_prefix` -- valid `iterator`s will always have the same `_prefix`
72 bool operator==(const iterator& other) const { return _iter == other._iter; }
74 bool operator!=(const iterator& other) const { return _iter != other._iter; }
76 bool operator< (const iterator& other) const { return _iter < other._iter; }
78 bool operator<=(const iterator& other) const { return _iter <= other._iter; }
80 bool operator> (const iterator& other) const { return _iter > other._iter; }
82 bool operator>=(const iterator& other) const { return _iter >= other._iter; }
83
84 private:
85 explicit iterator(const std::uintptr_t prefix, const std::uint64_t* iter) :
86 _prefix(prefix),
87 _iter(iter)
88 { }
89
90 friend class parse_index;
91
92 private:
93 std::uintptr_t _prefix;
94 const std::uint64_t* _iter;
95 };
96
97 using const_iterator = iterator;
98
99public:
100 /// Creates an empty not-an-AST instance.
101 parse_index() noexcept = default;
102
103 parse_index(parse_index&& src) noexcept :
104 _impl(std::exchange(src._impl, nullptr))
105 { }
106
107 parse_index& operator=(parse_index&& src) noexcept
108 {
109 if (this == &src)
110 return *this;
111
112 using std::swap;
113 swap(_impl, src._impl);
114 src.reset();
115
116 return *this;
117 }
118
119 ~parse_index() noexcept;
120
121 /// \{
122
123 /// Create a \c parse_index from the given \a src JSON.
124 ///
125 /// Parsing is controlled by \a options where they are given, and by \c parse_options::create_default() where they
126 /// are not. The underlying buffer starts with an \a initial_buffer_capacity where one is given; without one, or
127 /// when it is \c nullopt, the buffer is sized according to the length of the \a src string.
128 ///
129 /// \throws std::length_error if a given \a initial_buffer_capacity is too large for the buffer's size in bytes to
130 /// be represented in a \c std::size_t.
132 static parse_index parse(std::string_view src,
133 const parse_options& options,
134 std::optional<std::size_t> initial_buffer_capacity
135 );
137 static parse_index parse(std::string_view src, std::optional<std::size_t> initial_buffer_capacity);
139 static parse_index parse(std::string_view src, const parse_options& options);
141 static parse_index parse(std::string_view src);
142 /// \}
143
144 /// Clear the contents of this instance.
145 void reset();
146
147 /// Check if this instance represents a valid AST. This will be \c false if the source JSON was not valid JSON text.
148 /// This will also be \c false if this instance was default-constructed or moved-from.
149 ///
150 /// \note
151 /// Even if this returns true, it is possible that conversion to a \c jsonv::value will throw an exception. For
152 /// example, if the value of a number exceeds the range of an \c int64_t. This is because JSON does not specify an
153 /// acceptable range for numbers, but the storage of \c jsonv::value does.
155 bool success() const noexcept;
156
157 /// See \ref success.
159 explicit operator bool() const noexcept;
160
161 /// Validate that the parse was a \c success.
162 ///
163 /// \throws parse_error if the parse was not successful. This will contain additional details about why the parse
164 /// failed.
165 /// \throws std::invalid_argument if this instance was default-constructed or moved-from.
166 void validate() const;
167
169 iterator begin() const;
171 iterator cbegin() const { return begin(); }
172
174 iterator end() const;
175
177 iterator cend() const { return end(); }
178
179 /// \{
180
181 /// Build a \c value from the document this index holds. Extraction is controlled by \a options where they are
182 /// given, and by \c deserialize_options::create_default() where they are not.
183 ///
184 /// \throws parse_error if the document holds no value, which is the case for source text that is empty, only
185 /// whitespace or -- with \c parse_options::comments -- only comments. Such an index is a valid one (\c validate
186 /// does not throw for it), but there is nothing to build a \c value from. The error is the input ending
187 /// unexpectedly, at the end of the source text.
192 /// \}
193
194 /// \{
195
196 /// Get a string representation of the AST.
197 ///
198 /// +--------------------+--------+
199 /// | `ast_node_type` | Output |
200 /// +--------------------+--------+
201 /// | `document_start` | `^` |
202 /// | `document_end` | `$` |
203 /// | `object_begin` | `{` |
204 /// | `object_end` | `}` |
205 /// | `array_begin` | `[` |
206 /// | `array_end` | `]` |
207 /// | `string_canonical` | `s` |
208 /// | `string_escaped` | `S` |
209 /// | `key_canonical` | `k` |
210 /// | `key_escaped` | `K` |
211 /// | `literal_true` | `t` |
212 /// | `literal_false` | `f` |
213 /// | `literal_null` | `n` |
214 /// | `integer` | `i` |
215 /// | `decimal` | `d` |
216 /// | `error` | `!` |
217 /// +--------------------+--------+
218 ///
219 /// This exists primarily for debugging purposes.
220 friend std::ostream& operator<<(std::ostream&, const parse_index&);
221 friend std::string to_string(const parse_index&);
222 /// \}
223
224private:
225 struct impl;
226
227 explicit parse_index(impl*) noexcept;
228
229 /// Reads one document after another out of the same input onto a single tape.
230 friend class documents;
231
232 /// Replace what this index holds with the first document in \a src at or after \a from, parsed with \a options.
233 /// The tape is reused rather than freed, and grows only when a document needs more of it than any before.
234 ///
235 /// The document ends with its value, so what follows the value is left for the next call rather than refused.
236 /// Positions count from the start of \a src, so an error in a document says where it is in the whole input.
237 ///
238 /// \returns Where the next document may begin: one past the value; or the end of \a src if the document failed to
239 /// parse, since nothing says where it would have ended. Nothing if \a src holds only whitespace (and comments,
240 /// where \a options allow them) from \a from on, which leaves this index holding an empty document.
241 /// \throws Whatever \c parse throws, leaving this index empty.
242 std::optional<std::size_t> parse_next(std::string_view src, std::size_t from, const parse_options& options);
243
244private:
245 impl* _impl = nullptr;
246};
247
248/// \}
249
250}
Utilities for directly dealing with a JSON AST.
Represents an entry in a JSON AST.
Definition ast.hpp:172
Configuration for various deserialization options. This becomes part of the deserialization_context.
The JSON documents in one input, read one after another, each through a reader of its own.
Definition reader.hpp:509
iterator & skip_subtree()
Move to the node after the structure this iterator is on, however much is inside it.
Represents the index of a parsed AST.
friend std::string to_string(const parse_index &)
Get a string representation of the AST.
value extract_tree() const
Build a value from the document this index holds.
parse_index() noexcept=default
Creates an empty not-an-AST instance.
friend std::ostream & operator<<(std::ostream &, const parse_index &)
Get a string representation of the AST.
value extract_tree(const deserialize_options &options) const
Build a value from the document this index holds.
Configuration for various parsing options.
Definition parse.hpp:58
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:165
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:143
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:124
JSONV_PUBLIC void swap(value &a, value &b) noexcept
Swap the values a and b.
STL namespace.
typename detail::optional_type< T >::type optional
Represents a value that may or may not be present.
Definition optional.hpp:239