llvm.org GIT mirror llvm / 61c3d30
Goodbye, JSONParser... git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@154930 91177308-0d34-0410-b5e6-96231b3b80d8 Manuel Klimek 8 years ago
10 changed file(s) with 1 addition(s) and 1056 deletion(s). Raw diff Collapse all Expand all
395395 add_subdirectory(utils/count)
396396 add_subdirectory(utils/not)
397397 add_subdirectory(utils/llvm-lit)
398 add_subdirectory(utils/json-bench)
399398 add_subdirectory(utils/yaml-bench)
400399
401400 add_subdirectory(projects)
+0
-448
include/llvm/Support/JSONParser.h less more
None //===--- JSONParser.h - Simple JSON parser ----------------------*- C++ -*-===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements a JSON parser.
10 //
11 // See http://www.json.org/ for an overview.
12 // See http://www.ietf.org/rfc/rfc4627.txt for the full standard.
13 //
14 // FIXME: Currently this supports a subset of JSON. Specifically, support
15 // for numbers, booleans and null for values is missing.
16 //
17 //===----------------------------------------------------------------------===//
18
19 #ifndef LLVM_SUPPORT_JSON_PARSER_H
20 #define LLVM_SUPPORT_JSON_PARSER_H
21
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/Support/Allocator.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/Support/ErrorHandling.h"
26 #include "llvm/Support/SourceMgr.h"
27
28 namespace llvm {
29
30 class JSONContainer;
31 class JSONString;
32 class JSONValue;
33 class JSONKeyValuePair;
34
35 /// \brief Base class for a parsable JSON atom.
36 ///
37 /// This class has no semantics other than being a unit of JSON data which can
38 /// be parsed out of a JSON document.
39 class JSONAtom {
40 public:
41 /// \brief Possible types of JSON objects.
42 enum Kind { JK_KeyValuePair, JK_Array, JK_Object, JK_String };
43
44 /// \brief Returns the type of this value.
45 Kind getKind() const { return MyKind; }
46
47 static bool classof(const JSONAtom *Atom) { return true; }
48
49 protected:
50 JSONAtom(Kind MyKind) : MyKind(MyKind) {}
51
52 private:
53 Kind MyKind;
54 };
55
56 /// \brief A parser for JSON text.
57 ///
58 /// Use an object of JSONParser to iterate over the values of a JSON text.
59 /// All objects are parsed during the iteration, so you can only iterate once
60 /// over the JSON text, but the cost of partial iteration is minimized.
61 /// Create a new JSONParser if you want to iterate multiple times.
62 class JSONParser {
63 public:
64 /// \brief Create a JSONParser for the given input.
65 ///
66 /// Parsing is started via parseRoot(). Access to the object returned from
67 /// parseRoot() will parse the input lazily.
68 JSONParser(StringRef Input, SourceMgr *SM);
69
70 /// \brief Returns the outermost JSON value (either an array or an object).
71 ///
72 /// Can return NULL if the input does not start with an array or an object.
73 /// The object is not parsed yet - the caller must iterate over the
74 /// returned object to trigger parsing.
75 ///
76 /// A JSONValue can be either a JSONString, JSONObject or JSONArray.
77 JSONValue *parseRoot();
78
79 /// \brief Parses the JSON text and returns whether it is valid JSON.
80 ///
81 /// In case validate() return false, failed() will return true and
82 /// getErrorMessage() will return the parsing error.
83 bool validate();
84
85 /// \brief Returns true if an error occurs during parsing.
86 ///
87 /// If there was an error while parsing an object that was created by
88 /// iterating over the result of 'parseRoot', 'failed' will return true.
89 bool failed() const;
90
91 private:
92 /// \brief These methods manage the implementation details of parsing new JSON
93 /// atoms.
94 /// @{
95 JSONString *parseString();
96 JSONValue *parseValue();
97 JSONKeyValuePair *parseKeyValuePair();
98 /// @}
99
100 /// \brief Helpers to parse the elements out of both forms of containers.
101 /// @{
102 const JSONAtom *parseElement(JSONAtom::Kind ContainerKind);
103 StringRef::iterator parseFirstElement(JSONAtom::Kind ContainerKind,
104 char StartChar, char EndChar,
105 const JSONAtom *&Element);
106 StringRef::iterator parseNextElement(JSONAtom::Kind ContainerKind,
107 char EndChar,
108 const JSONAtom *&Element);
109 /// @}
110
111 /// \brief Whitespace parsing.
112 /// @{
113 void nextNonWhitespace();
114 bool isWhitespace();
115 /// @}
116
117 /// \brief These methods are used for error handling.
118 /// {
119 void setExpectedError(StringRef Expected, StringRef Found);
120 void setExpectedError(StringRef Expected, char Found);
121 bool errorIfAtEndOfFile(StringRef Message);
122 bool errorIfNotAt(char C, StringRef Message);
123 /// }
124
125 /// \brief Skips all elements in the given container.
126 bool skipContainer(const JSONContainer &Container);
127
128 /// \brief Skips to the next position behind the given JSON atom.
129 bool skip(const JSONAtom &Atom);
130
131 /// All nodes are allocated by the parser and will be deallocated when the
132 /// parser is destroyed.
133 BumpPtrAllocator ValueAllocator;
134
135 /// \brief The original input to the parser.
136 MemoryBuffer *InputBuffer;
137
138 /// \brief The source manager used for diagnostics and buffer management.
139 SourceMgr *SM;
140
141 /// \brief The current position in the parse stream.
142 StringRef::iterator Position;
143
144 /// \brief The end position for fast EOF checks without introducing
145 /// unnecessary dereferences.
146 StringRef::iterator End;
147
148 /// \brief If true, an error has occurred.
149 bool Failed;
150
151 friend class JSONContainer;
152 };
153
154
155 /// \brief Base class for JSON value objects.
156 ///
157 /// This object represents an abstract JSON value. It is the root node behind
158 /// the group of JSON entities that can represent top-level values in a JSON
159 /// document. It has no API, and is just a placeholder in the type hierarchy of
160 /// nodes.
161 class JSONValue : public JSONAtom {
162 protected:
163 JSONValue(Kind MyKind) : JSONAtom(MyKind) {}
164
165 public:
166 /// \brief dyn_cast helpers
167 ///@{
168 static bool classof(const JSONAtom *Atom) {
169 switch (Atom->getKind()) {
170 case JK_Array:
171 case JK_Object:
172 case JK_String:
173 return true;
174 case JK_KeyValuePair:
175 return false;
176 }
177 llvm_unreachable("Invalid JSONAtom kind");
178 }
179 static bool classof(const JSONValue *Value) { return true; }
180 ///@}
181 };
182
183 /// \brief Gives access to the text of a JSON string.
184 ///
185 /// FIXME: Implement a method to return the unescaped text.
186 class JSONString : public JSONValue {
187 public:
188 /// \brief Returns the underlying parsed text of the string.
189 ///
190 /// This is the unescaped content of the JSON text.
191 /// See http://www.ietf.org/rfc/rfc4627.txt for details.
192 StringRef getRawText() const { return RawText; }
193
194 private:
195 JSONString(StringRef RawText) : JSONValue(JK_String), RawText(RawText) {}
196
197 StringRef RawText;
198
199 friend class JSONParser;
200
201 public:
202 /// \brief dyn_cast helpers
203 ///@{
204 static bool classof(const JSONAtom *Atom) {
205 return Atom->getKind() == JK_String;
206 }
207 static bool classof(const JSONString *String) { return true; }
208 ///@}
209 };
210
211 /// \brief A (key, value) tuple of type (JSONString *, JSONValue *).
212 ///
213 /// Note that JSONKeyValuePair is not a JSONValue, it is a bare JSONAtom.
214 /// JSONKeyValuePairs can be elements of a JSONObject, but not of a JSONArray.
215 /// They are not viable as top-level values either.
216 class JSONKeyValuePair : public JSONAtom {
217 public:
218 const JSONString * const Key;
219 const JSONValue * const Value;
220
221 private:
222 JSONKeyValuePair(const JSONString *Key, const JSONValue *Value)
223 : JSONAtom(JK_KeyValuePair), Key(Key), Value(Value) {}
224
225 friend class JSONParser;
226
227 public:
228 /// \brief dyn_cast helpers
229 ///@{
230 static bool classof(const JSONAtom *Atom) {
231 return Atom->getKind() == JK_KeyValuePair;
232 }
233 static bool classof(const JSONKeyValuePair *KeyValuePair) { return true; }
234 ///@}
235 };
236
237 /// \brief Implementation of JSON containers (arrays and objects).
238 ///
239 /// JSONContainers drive the lazy parsing of JSON arrays and objects via
240 /// forward iterators.
241 class JSONContainer : public JSONValue {
242 private:
243 /// \brief An iterator that parses the underlying container during iteration.
244 ///
245 /// Iterators on the same collection use shared state, so when multiple copies
246 /// of an iterator exist, only one is allowed to be used for iteration;
247 /// iterating multiple copies of an iterator of the same collection will lead
248 /// to undefined behavior.
249 class AtomIterator {
250 public:
251 AtomIterator(const AtomIterator &I) : Container(I.Container) {}
252
253 /// \brief Iterator interface.
254 ///@{
255 bool operator==(const AtomIterator &I) const {
256 if (isEnd() || I.isEnd())
257 return isEnd() == I.isEnd();
258 return Container->Position == I.Container->Position;
259 }
260 bool operator!=(const AtomIterator &I) const {
261 return !(*this == I);
262 }
263 AtomIterator &operator++() {
264 Container->parseNextElement();
265 return *this;
266 }
267 const JSONAtom *operator*() {
268 return Container->Current;
269 }
270 ///@}
271
272 private:
273 /// \brief Create an iterator for which 'isEnd' returns true.
274 AtomIterator() : Container(0) {}
275
276 /// \brief Create an iterator for the given container.
277 AtomIterator(const JSONContainer *Container) : Container(Container) {}
278
279 bool isEnd() const {
280 return Container == 0 || Container->Position == StringRef::iterator();
281 }
282
283 const JSONContainer * const Container;
284
285 friend class JSONContainer;
286 };
287
288 protected:
289 /// \brief An iterator for the specified AtomT.
290 ///
291 /// Used for the implementation of iterators for JSONArray and JSONObject.
292 template
293 class IteratorTemplate : public std::iterator
294 const AtomT*> {
295 public:
296 explicit IteratorTemplate(const AtomIterator& AtomI)
297 : AtomI(AtomI) {}
298
299 bool operator==(const IteratorTemplate &I) const {
300 return AtomI == I.AtomI;
301 }
302 bool operator!=(const IteratorTemplate &I) const { return !(*this == I); }
303
304 IteratorTemplate &operator++() {
305 ++AtomI;
306 return *this;
307 }
308
309 const AtomT *operator*() { return dyn_cast(*AtomI); }
310
311 private:
312 AtomIterator AtomI;
313 };
314
315 JSONContainer(JSONParser *Parser, char StartChar, char EndChar,
316 JSONAtom::Kind ContainerKind)
317 : JSONValue(ContainerKind), Parser(Parser),
318 Position(), Current(0), Started(false),
319 StartChar(StartChar), EndChar(EndChar) {}
320
321 /// \brief Returns a lazy parsing iterator over the container.
322 ///
323 /// As the iterator drives the parse stream, begin() must only be called
324 /// once per container.
325 AtomIterator atom_begin() const {
326 if (Started)
327 report_fatal_error("Cannot parse container twice.");
328 Started = true;
329 // Set up the position and current element when we begin iterating over the
330 // container.
331 Position = Parser->parseFirstElement(getKind(), StartChar, EndChar, Current);
332 return AtomIterator(this);
333 }
334 AtomIterator atom_end() const {
335 return AtomIterator();
336 }
337
338 private:
339 AtomIterator atom_current() const {
340 if (!Started)
341 return atom_begin();
342
343 return AtomIterator(this);
344 }
345
346 /// \brief Parse the next element in the container into the Current element.
347 ///
348 /// This routine is called as an iterator into this container walks through
349 /// its elements. It mutates the container's internal current node to point to
350 /// the next atom of the container.
351 void parseNextElement() const {
352 Parser->skip(*Current);
353 Position = Parser->parseNextElement(getKind(), EndChar, Current);
354 }
355
356 // For parsing, JSONContainers call back into the JSONParser.
357 JSONParser * const Parser;
358
359 // 'Position', 'Current' and 'Started' store the state of the parse stream
360 // for iterators on the container, they don't change the container's elements
361 // and are thus marked as mutable.
362 mutable StringRef::iterator Position;
363 mutable const JSONAtom *Current;
364 mutable bool Started;
365
366 const char StartChar;
367 const char EndChar;
368
369 friend class JSONParser;
370
371 public:
372 /// \brief dyn_cast helpers
373 ///@{
374 static bool classof(const JSONAtom *Atom) {
375 switch (Atom->getKind()) {
376 case JK_Array:
377 case JK_Object:
378 return true;
379 case JK_KeyValuePair:
380 case JK_String:
381 return false;
382 }
383 llvm_unreachable("Invalid JSONAtom kind");
384 }
385 static bool classof(const JSONContainer *Container) { return true; }
386 ///@}
387 };
388
389 /// \brief A simple JSON array.
390 class JSONArray : public JSONContainer {
391 public:
392 typedef IteratorTemplate const_iterator;
393
394 /// \brief Returns a lazy parsing iterator over the container.
395 ///
396 /// As the iterator drives the parse stream, begin() must only be called
397 /// once per container.
398 const_iterator begin() const { return const_iterator(atom_begin()); }
399 const_iterator end() const { return const_iterator(atom_end()); }
400
401 private:
402 JSONArray(JSONParser *Parser)
403 : JSONContainer(Parser, '[', ']', JSONAtom::JK_Array) {}
404
405 public:
406 /// \brief dyn_cast helpers
407 ///@{
408 static bool classof(const JSONAtom *Atom) {
409 return Atom->getKind() == JSONAtom::JK_Array;
410 }
411 static bool classof(const JSONArray *Array) { return true; }
412 ///@}
413
414 friend class JSONParser;
415 };
416
417 /// \brief A JSON object: an iterable list of JSON key-value pairs.
418 class JSONObject : public JSONContainer {
419 public:
420 typedef IteratorTemplate const_iterator;
421
422 /// \brief Returns a lazy parsing iterator over the container.
423 ///
424 /// As the iterator drives the parse stream, begin() must only be called
425 /// once per container.
426 const_iterator begin() const { return const_iterator(atom_begin()); }
427 const_iterator end() const { return const_iterator(atom_end()); }
428
429 private:
430 JSONObject(JSONParser *Parser)
431 : JSONContainer(Parser, '{', '}', JSONAtom::JK_Object) {}
432
433 public:
434 /// \brief dyn_cast helpers
435 ///@{
436 static bool classof(const JSONAtom *Atom) {
437 return Atom->getKind() == JSONAtom::JK_Object;
438 }
439 static bool classof(const JSONObject *Object) { return true; }
440 ///@}
441
442 friend class JSONParser;
443 };
444
445 } // end namespace llvm
446
447 #endif // LLVM_SUPPORT_JSON_PARSER_H
3131 IntrusiveRefCntPtr.cpp
3232 IsInf.cpp
3333 IsNAN.cpp
34 JSONParser.cpp
3534 LockFileManager.cpp
3635 ManagedStatic.cpp
3736 MemoryBuffer.cpp
+0
-302
lib/Support/JSONParser.cpp less more
None //===--- JSONParser.cpp - Simple JSON parser ------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements a JSON parser.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "llvm/Support/JSONParser.h"
14
15 #include "llvm/ADT/Twine.h"
16 #include "llvm/Support/Casting.h"
17 #include "llvm/Support/MemoryBuffer.h"
18
19 using namespace llvm;
20
21 JSONParser::JSONParser(StringRef Input, SourceMgr *SM)
22 : SM(SM), Failed(false) {
23 InputBuffer = MemoryBuffer::getMemBuffer(Input, "JSON");
24 SM->AddNewSourceBuffer(InputBuffer, SMLoc());
25 End = InputBuffer->getBuffer().end();
26 Position = InputBuffer->getBuffer().begin();
27 }
28
29 JSONValue *JSONParser::parseRoot() {
30 if (Position != InputBuffer->getBuffer().begin())
31 report_fatal_error("Cannot reuse JSONParser.");
32 if (isWhitespace())
33 nextNonWhitespace();
34 if (errorIfAtEndOfFile("'[' or '{' at start of JSON text"))
35 return 0;
36 switch (*Position) {
37 case '[':
38 return new (ValueAllocator.Allocate(1)) JSONArray(this);
39 case '{':
40 return new (ValueAllocator.Allocate(1)) JSONObject(this);
41 default:
42 setExpectedError("'[' or '{' at start of JSON text", *Position);
43 return 0;
44 }
45 }
46
47 bool JSONParser::validate() {
48 JSONValue *Root = parseRoot();
49 if (Root == NULL) {
50 return false;
51 }
52 return skip(*Root);
53 }
54
55 bool JSONParser::skip(const JSONAtom &Atom) {
56 switch(Atom.getKind()) {
57 case JSONAtom::JK_Array:
58 case JSONAtom::JK_Object:
59 return skipContainer(*cast(&Atom));
60 case JSONAtom::JK_String:
61 return true;
62 case JSONAtom::JK_KeyValuePair:
63 return skip(*cast(&Atom)->Value);
64 }
65 llvm_unreachable("Impossible enum value.");
66 }
67
68 // Sets the current error to:
69 // "expected , but found ".
70 void JSONParser::setExpectedError(StringRef Expected, StringRef Found) {
71 SM->PrintMessage(SMLoc::getFromPointer(Position), SourceMgr::DK_Error,
72 "expected " + Expected + ", but found " + Found + ".", ArrayRef());
73 Failed = true;
74 }
75
76 // Sets the current error to:
77 // "expected , but found ".
78 void JSONParser::setExpectedError(StringRef Expected, char Found) {
79 setExpectedError(Expected, ("'" + StringRef(&Found, 1) + "'").str());
80 }
81
82 // If there is no character available, returns true and sets the current error
83 // to: "expected , but found EOF.".
84 bool JSONParser::errorIfAtEndOfFile(StringRef Expected) {
85 if (Position == End) {
86 setExpectedError(Expected, "EOF");
87 return true;
88 }
89 return false;
90 }
91
92 // Sets the current error if the current character is not C to:
93 // "expected 'C', but got ".
94 bool JSONParser::errorIfNotAt(char C, StringRef Message) {
95 if (*Position != C) {
96 std::string Expected =
97 ("'" + StringRef(&C, 1) + "' " + Message).str();
98 if (Position == End)
99 setExpectedError(Expected, "EOF");
100 else
101 setExpectedError(Expected, *Position);
102 return true;
103 }
104 return false;
105 }
106
107 // Forbidding inlining improves performance by roughly 20%.
108 // FIXME: Remove once llvm optimizes this to the faster version without hints.
109 LLVM_ATTRIBUTE_NOINLINE static bool
110 wasEscaped(StringRef::iterator First, StringRef::iterator Position);
111
112 // Returns whether a character at 'Position' was escaped with a leading '\'.
113 // 'First' specifies the position of the first character in the string.
114 static bool wasEscaped(StringRef::iterator First,
115 StringRef::iterator Position) {
116 assert(Position - 1 >= First);
117 StringRef::iterator I = Position - 1;
118 // We calulate the number of consecutive '\'s before the current position
119 // by iterating backwards through our string.
120 while (I >= First && *I == '\\') --I;
121 // (Position - 1 - I) now contains the number of '\'s before the current
122 // position. If it is odd, the character at 'Positon' was escaped.
123 return (Position - 1 - I) % 2 == 1;
124 }
125
126 // Parses a JSONString, assuming that the current position is on a quote.
127 JSONString *JSONParser::parseString() {
128 assert(Position != End);
129 assert(!isWhitespace());
130 if (errorIfNotAt('"', "at start of string"))
131 return 0;
132 StringRef::iterator First = Position + 1;
133
134 // Benchmarking shows that this loop is the hot path of the application with
135 // about 2/3rd of the runtime cycles. Since escaped quotes are not the common
136 // case, and multiple escaped backslashes before escaped quotes are very rare,
137 // we pessimize this case to achieve a smaller inner loop in the common case.
138 // We're doing that by having a quick inner loop that just scans for the next
139 // quote. Once we find the quote we check the last character to see whether
140 // the quote might have been escaped. If the last character is not a '\', we
141 // know the quote was not escaped and have thus found the end of the string.
142 // If the immediately preceding character was a '\', we have to scan backwards
143 // to see whether the previous character was actually an escaped backslash, or
144 // an escape character for the quote. If we find that the current quote was
145 // escaped, we continue parsing for the next quote and repeat.
146 // This optimization brings around 30% performance improvements.
147 do {
148 // Step over the current quote.
149 ++Position;
150 // Find the next quote.
151 while (Position != End && *Position != '"')
152 ++Position;
153 if (errorIfAtEndOfFile("'\"' at end of string"))
154 return 0;
155 // Repeat until the previous character was not a '\' or was an escaped
156 // backslash.
157 } while (*(Position - 1) == '\\' && wasEscaped(First, Position));
158
159 return new (ValueAllocator.Allocate())
160 JSONString(StringRef(First, Position - First));
161 }
162
163
164 // Advances the position to the next non-whitespace position.
165 void JSONParser::nextNonWhitespace() {
166 do {
167 ++Position;
168 } while (isWhitespace());
169 }
170
171 // Checks if there is a whitespace character at the current position.
172 bool JSONParser::isWhitespace() {
173 return *Position == ' ' || *Position == '\t' ||
174 *Position == '\n' || *Position == '\r';
175 }
176
177 bool JSONParser::failed() const {
178 return Failed;
179 }
180
181 // Parses a JSONValue, assuming that the current position is at the first
182 // character of the value.
183 JSONValue *JSONParser::parseValue() {
184 assert(Position != End);
185 assert(!isWhitespace());
186 switch (*Position) {
187 case '[':
188 return new (ValueAllocator.Allocate(1)) JSONArray(this);
189 case '{':
190 return new (ValueAllocator.Allocate(1)) JSONObject(this);
191 case '"':
192 return parseString();
193 default:
194 setExpectedError("'[', '{' or '\"' at start of value", *Position);
195 return 0;
196 }
197 }
198
199 // Parses a JSONKeyValuePair, assuming that the current position is at the first
200 // character of the key, value pair.
201 JSONKeyValuePair *JSONParser::parseKeyValuePair() {
202 assert(Position != End);
203 assert(!isWhitespace());
204
205 JSONString *Key = parseString();
206 if (Key == 0)
207 return 0;
208
209 nextNonWhitespace();
210 if (errorIfNotAt(':', "between key and value"))
211 return 0;
212
213 nextNonWhitespace();
214 const JSONValue *Value = parseValue();
215 if (Value == 0)
216 return 0;
217
218 return new (ValueAllocator.Allocate(1))
219 JSONKeyValuePair(Key, Value);
220 }
221
222 /// \brief Parses the first element of a JSON array or object, or closes the
223 /// array.
224 ///
225 /// The method assumes that the current position is before the first character
226 /// of the element, with possible white space in between. When successful, it
227 /// returns the new position after parsing the element. Otherwise, if there is
228 /// no next value, it returns a default constructed StringRef::iterator.
229 StringRef::iterator JSONParser::parseFirstElement(JSONAtom::Kind ContainerKind,
230 char StartChar, char EndChar,
231 const JSONAtom *&Element) {
232 assert(*Position == StartChar);
233 Element = 0;
234 nextNonWhitespace();
235 if (errorIfAtEndOfFile("value or end of container at start of container"))
236 return StringRef::iterator();
237
238 if (*Position == EndChar)
239 return StringRef::iterator();
240
241 Element = parseElement(ContainerKind);
242 if (Element == 0)
243 return StringRef::iterator();
244
245 return Position;
246 }
247
248 /// \brief Parses the next element of a JSON array or object, or closes the
249 /// array.
250 ///
251 /// The method assumes that the current position is before the ',' which
252 /// separates the next element from the current element. When successful, it
253 /// returns the new position after parsing the element. Otherwise, if there is
254 /// no next value, it returns a default constructed StringRef::iterator.
255 StringRef::iterator JSONParser::parseNextElement(JSONAtom::Kind ContainerKind,
256 char EndChar,
257 const JSONAtom *&Element) {
258 Element = 0;
259 nextNonWhitespace();
260 if (errorIfAtEndOfFile("',' or end of container for next element"))
261 return 0;
262
263 if (*Position == ',') {
264 nextNonWhitespace();
265 if (errorIfAtEndOfFile("element in container"))
266 return StringRef::iterator();
267
268 Element = parseElement(ContainerKind);
269 if (Element == 0)
270 return StringRef::iterator();
271
272 return Position;
273 } else if (*Position == EndChar) {
274 return StringRef::iterator();
275 } else {
276 setExpectedError("',' or end of container for next element", *Position);
277 return StringRef::iterator();
278 }
279 }
280
281 const JSONAtom *JSONParser::parseElement(JSONAtom::Kind ContainerKind) {
282 switch (ContainerKind) {
283 case JSONAtom::JK_Array:
284 return parseValue();
285 case JSONAtom::JK_Object:
286 return parseKeyValuePair();
287 default:
288 llvm_unreachable("Impossible code path");
289 }
290 }
291
292 bool JSONParser::skipContainer(const JSONContainer &Container) {
293 for (JSONContainer::AtomIterator I = Container.atom_current(),
294 E = Container.atom_end();
295 I != E; ++I) {
296 assert(*I != 0);
297 if (!skip(**I))
298 return false;
299 }
300 return !failed();
301 }
164164 Support/CommandLineTest.cpp
165165 Support/ConstantRangeTest.cpp
166166 Support/EndianTest.cpp
167 Support/JSONParserTest.cpp
168167 Support/LeakDetectorTest.cpp
169168 Support/MathExtrasTest.cpp
170169 Support/Path.cpp
+0
-191
unittests/Support/JSONParserTest.cpp less more
None //===- unittest/Tooling/JSONParserTest ------------------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/Support/Casting.h"
10 #include "llvm/Support/JSONParser.h"
11 #include "llvm/ADT/Twine.h"
12 #include "gtest/gtest.h"
13
14 namespace llvm {
15
16 // Checks that the given input gives a parse error. Makes sure that an error
17 // text is available and the parse fails.
18 static void ExpectParseError(StringRef Message, StringRef Input) {
19 SourceMgr SM;
20 JSONParser Parser(Input, &SM);
21 EXPECT_FALSE(Parser.validate()) << Message << ": " << Input;
22 EXPECT_TRUE(Parser.failed()) << Message << ": " << Input;
23 }
24
25 // Checks that the given input can be parsed without error.
26 static void ExpectParseSuccess(StringRef Message, StringRef Input) {
27 SourceMgr SM;
28 JSONParser Parser(Input, &SM);
29 EXPECT_TRUE(Parser.validate()) << Message << ": " << Input;
30 }
31
32 TEST(JSONParser, FailsOnEmptyString) {
33 ExpectParseError("Empty JSON text", "");
34 }
35
36 TEST(JSONParser, FailsIfStartsWithString) {
37 ExpectParseError("Top-level string", "\"x\"");
38 }
39
40 TEST(JSONParser, ParsesEmptyArray) {
41 ExpectParseSuccess("Empty array", "[]");
42 }
43
44 TEST(JSONParser, FailsIfNotClosingArray) {
45 ExpectParseError("Not closing array", "[");
46 ExpectParseError("Not closing array", " [ ");
47 ExpectParseError("Not closing array", " [x");
48 }
49
50 TEST(JSONParser, ParsesEmptyArrayWithWhitespace) {
51 ExpectParseSuccess("Array with spaces", " [ ] ");
52 ExpectParseSuccess("All whitespaces", "\t\r\n[\t\n \t\r ]\t\r \n\n");
53 }
54
55 TEST(JSONParser, ParsesEmptyObject) {
56 ExpectParseSuccess("Empty object", "[{}]");
57 }
58
59 TEST(JSONParser, ParsesObject) {
60 ExpectParseSuccess("Object with an entry", "[{\"a\":\"/b\"}]");
61 }
62
63 TEST(JSONParser, ParsesMultipleKeyValuePairsInObject) {
64 ExpectParseSuccess("Multiple key, value pairs",
65 "[{\"a\":\"/b\",\"c\":\"d\",\"e\":\"f\"}]");
66 }
67
68 TEST(JSONParser, FailsIfNotClosingObject) {
69 ExpectParseError("Missing close on empty", "[{]");
70 ExpectParseError("Missing close after pair", "[{\"a\":\"b\"]");
71 }
72
73 TEST(JSONParser, FailsIfMissingColon) {
74 ExpectParseError("Missing colon between key and value", "[{\"a\"\"/b\"}]");
75 ExpectParseError("Missing colon between key and value", "[{\"a\" \"b\"}]");
76 }
77
78 TEST(JSONParser, FailsOnMissingQuote) {
79 ExpectParseError("Missing open quote", "[{a\":\"b\"}]");
80 ExpectParseError("Missing closing quote", "[{\"a\":\"b}]");
81 }
82
83 TEST(JSONParser, ParsesEscapedQuotes) {
84 ExpectParseSuccess("Parses escaped string in key and value",
85 "[{\"a\":\"\\\"b\\\" \\\" \\\"\"}]");
86 }
87
88 TEST(JSONParser, ParsesEmptyString) {
89 ExpectParseSuccess("Parses empty string in value", "[{\"a\":\"\"}]");
90 }
91
92 TEST(JSONParser, FailsOnMissingString) {
93 ExpectParseError("Missing value", "[{\"a\":}]");
94 ExpectParseError("Missing key", "[{:\"b\"}]");
95 }
96
97 TEST(JSONParser, ParsesMultipleObjects) {
98 ExpectParseSuccess(
99 "Multiple objects in array",
100 "["
101 " { \"a\" : \"b\" },"
102 " { \"a\" : \"b\" },"
103 " { \"a\" : \"b\" }"
104 "]");
105 }
106
107 TEST(JSONParser, FailsOnMissingComma) {
108 ExpectParseError(
109 "Missing comma",
110 "["
111 " { \"a\" : \"b\" }"
112 " { \"a\" : \"b\" }"
113 "]");
114 }
115
116 TEST(JSONParser, FailsOnSuperfluousComma) {
117 ExpectParseError("Superfluous comma in array", "[ { \"a\" : \"b\" }, ]");
118 ExpectParseError("Superfluous comma in object", "{ \"a\" : \"b\", }");
119 }
120
121 TEST(JSONParser, ParsesSpacesInBetweenTokens) {
122 ExpectParseSuccess(
123 "Various whitespace between tokens",
124 " \t \n\n \r [ \t \n\n \r"
125 " \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"
126 " \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r,\t \n\n \r"
127 " \t \n\n \r { \t \n\n \r\"a\"\t \n\n \r :"
128 " \t \n\n \r \"b\"\t \n\n \r } \t \n\n \r]\t \n\n \r");
129 }
130
131 TEST(JSONParser, ParsesArrayOfArrays) {
132 ExpectParseSuccess("Array of arrays", "[[]]");
133 }
134
135 TEST(JSONParser, HandlesEndOfFileGracefully) {
136 ExpectParseError("In string starting with EOF", "[\"");
137 ExpectParseError("In string hitting EOF", "[\" ");
138 ExpectParseError("In string escaping EOF", "[\" \\");
139 ExpectParseError("In array starting with EOF", "[");
140 ExpectParseError("In array element starting with EOF", "[[], ");
141 ExpectParseError("In array hitting EOF", "[[] ");
142 ExpectParseError("In array hitting EOF", "[[]");
143 ExpectParseError("In object hitting EOF", "{\"\"");
144 }
145
146 // Checks that the given string can be parsed into an identical string inside
147 // of an array.
148 static void ExpectCanParseString(StringRef String) {
149 std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str();
150 SourceMgr SM;
151 JSONParser Parser(StringInArray, &SM);
152 const JSONArray *ParsedArray = dyn_cast(Parser.parseRoot());
153 StringRef ParsedString =
154 dyn_cast(*ParsedArray->begin())->getRawText();
155 EXPECT_EQ(String, ParsedString.str());
156 }
157
158 // Checks that parsing the given string inside an array fails.
159 static void ExpectCannotParseString(StringRef String) {
160 std::string StringInArray = (llvm::Twine("[\"") + String + "\"]").str();
161 ExpectParseError((Twine("When parsing string \"") + String + "\"").str(),
162 StringInArray);
163 }
164
165 TEST(JSONParser, ParsesStrings) {
166 ExpectCanParseString("");
167 ExpectCannotParseString("\\");
168 ExpectCannotParseString("\"");
169 ExpectCanParseString(" ");
170 ExpectCanParseString("\\ ");
171 ExpectCanParseString("\\\"");
172 ExpectCannotParseString("\"\\");
173 ExpectCannotParseString(" \\");
174 ExpectCanParseString("\\\\");
175 ExpectCannotParseString("\\\\\\");
176 ExpectCanParseString("\\\\\\\\");
177 ExpectCanParseString("\\\" ");
178 ExpectCannotParseString("\\\\\" ");
179 ExpectCanParseString("\\\\\\\" ");
180 ExpectCanParseString(" \\\\ \\\" \\\\\\\" ");
181 }
182
183 TEST(JSONParser, WorksWithIteratorAlgorithms) {
184 SourceMgr SM;
185 JSONParser Parser("[\"1\", \"2\", \"3\", \"4\", \"5\", \"6\"]", &SM);
186 const JSONArray *Array = dyn_cast(Parser.parseRoot());
187 EXPECT_EQ(6, std::distance(Array->begin(), Array->end()));
188 }
189
190 } // end namespace llvm
88
99 LEVEL = ..
1010 PARALLEL_DIRS := FileCheck FileUpdate TableGen PerfectShuffle \
11 count fpcmp llvm-lit not unittest json-bench
11 count fpcmp llvm-lit not unittest
1212
1313 EXTRA_DIST := check-each-file codegen-diff countloc.sh \
1414 DSAclean.py DSAextract.py emacs findsym.pl GenLibDeps.pl \
+0
-5
utils/json-bench/CMakeLists.txt less more
None add_llvm_utility(json-bench
1 JSONBench.cpp
2 )
3
4 target_link_libraries(json-bench LLVMSupport)
+0
-85
utils/json-bench/JSONBench.cpp less more
None //===- JSONBench - Benchmark the JSONParser implementation ----------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This program executes the JSONParser on differntly sized JSON texts and
10 // outputs the run time.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/ADT/Twine.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/JSONParser.h"
17 #include "llvm/Support/Timer.h"
18 #include "llvm/Support/raw_ostream.h"
19
20 static llvm::cl::opt
21 Verify("verify", llvm::cl::desc(
22 "Run a quick verification useful for regression testing"),
23 llvm::cl::init(false));
24
25 static llvm::cl::opt
26 MemoryLimitMB("memory-limit", llvm::cl::desc(
27 "Do not use more megabytes of memory"),
28 llvm::cl::init(1000));
29
30 void benchmark(llvm::TimerGroup &Group, llvm::StringRef Name,
31 llvm::StringRef JSONText) {
32 llvm::Timer BaseLine((Name + ": Loop").str(), Group);
33 BaseLine.startTimer();
34 char C = 0;
35 for (llvm::StringRef::iterator I = JSONText.begin(),
36 E = JSONText.end();
37 I != E; ++I) { C += *I; }
38 BaseLine.stopTimer();
39 volatile char DontOptimizeOut = C; (void)DontOptimizeOut;
40
41 llvm::Timer Parsing((Name + ": Parsing").str(), Group);
42 Parsing.startTimer();
43 llvm::SourceMgr SM;
44 llvm::JSONParser Parser(JSONText, &SM);
45 if (!Parser.validate()) {
46 llvm::errs() << "Parsing error in JSON parser benchmark.\n";
47 exit(1);
48 }
49 Parsing.stopTimer();
50 }
51
52 std::string createJSONText(size_t MemoryMB, unsigned ValueSize) {
53 std::string JSONText;
54 llvm::raw_string_ostream Stream(JSONText);
55 Stream << "[\n";
56 size_t MemoryBytes = MemoryMB * 1024 * 1024;
57 while (JSONText.size() < MemoryBytes) {
58 Stream << " {\n"
59 << " \"key1\": \"" << std::string(ValueSize, '*') << "\",\n"
60 << " \"key2\": \"" << std::string(ValueSize, '*') << "\",\n"
61 << " \"key3\": \"" << std::string(ValueSize, '*') << "\"\n"
62 << " }";
63 Stream.flush();
64 if (JSONText.size() < MemoryBytes) Stream << ",";
65 Stream << "\n";
66 }
67 Stream << "]\n";
68 Stream.flush();
69 return JSONText;
70 }
71
72 int main(int argc, char **argv) {
73 llvm::cl::ParseCommandLineOptions(argc, argv);
74 llvm::TimerGroup Group("JSON parser benchmark");
75 if (Verify) {
76 benchmark(Group, "Fast", createJSONText(10, 500));
77 } else {
78 benchmark(Group, "Small Values", createJSONText(MemoryLimitMB, 5));
79 benchmark(Group, "Medium Values", createJSONText(MemoryLimitMB, 500));
80 benchmark(Group, "Large Values", createJSONText(MemoryLimitMB, 50000));
81 }
82 return 0;
83 }
84
+0
-21
utils/json-bench/Makefile less more
None ##===- utils/FileCheck/Makefile ----------------------------*- Makefile -*-===##
1 #
2 # The LLVM Compiler Infrastructure
3 #
4 # This file is distributed under the University of Illinois Open Source
5 # License. See LICENSE.TXT for details.
6 #
7 ##===----------------------------------------------------------------------===##
8
9 LEVEL = ../..
10 TOOLNAME = json-bench
11 USEDLIBS = LLVMSupport.a
12
13 # This tool has no plugins, optimize startup time.
14 TOOL_NO_EXPORTS = 1
15
16 # Don't install this utility
17 NO_INSTALL = 1
18
19 include $(LEVEL)/Makefile.common
20