Chromium Code Reviews| Index: runtime/vm/json_parser.h |
| diff --git a/runtime/vm/json_parser.h b/runtime/vm/json_parser.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a4253c34598f6ba4f5a128fa3f6d62214020acd8 |
| --- /dev/null |
| +++ b/runtime/vm/json_parser.h |
| @@ -0,0 +1,326 @@ |
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +#ifndef RUNTIME_VM_JSON_PARSER_H_ |
| +#define RUNTIME_VM_JSON_PARSER_H_ |
| + |
| +#include "vm/allocation.h" |
| +#include "vm/zone.h" |
| +#include "vm/growable_array.h" |
| + |
| +namespace dart { |
| + |
| +class ParsedJSONArray; |
| + |
| +class ParsedJSONValue : public ZoneAllocated { |
| + public: |
| + ParsedJSONValue() {} |
|
Florian Schneider
2016/12/15 19:09:35
Empty constructor not needed.
rmacnak
2016/12/15 21:27:19
Done.
|
| + virtual ~ParsedJSONValue() {} |
| + |
| + virtual bool IsObject() const { return false; } |
| + virtual bool IsArray() const { return false; } |
| + virtual bool IsString() const { return false; } |
| + virtual bool IsNumber() const { return false; } |
| + virtual bool IsBoolean() const { return false; } |
| + virtual bool IsError() const { return false; } |
| +}; |
| + |
| +class ParsedJSONString : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONString(const char* value) : value_(value) {} |
| + bool Equals(const char* other) { return strcmp(value_, other) == 0; } |
| + const char* value() { return value_; } |
| + virtual bool IsString() const { return true; } |
| + |
| + private: |
| + const char* value_; |
| +}; |
| + |
| +class ParsedJSONNumber : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONNumber(int64_t value) : value_(value) {} |
| + |
| + int64_t value() { return value_; } |
| + virtual bool IsNumber() const { return true; } |
| + |
| + private: |
| + int64_t value_; |
| +}; |
| + |
| +class ParsedJSONBoolean : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONBoolean(bool value) : value_(value) {} |
| + |
| + bool value() { return value_; } |
| + virtual bool IsBoolean() const { return true; } |
| + |
| + private: |
| + bool value_; |
| +}; |
| + |
| +class ParsedJSONNull : public ParsedJSONValue { |
| + public: |
| + ParsedJSONNull() {} |
|
Florian Schneider
2016/12/15 19:09:35
Empty constructor not needed.
rmacnak
2016/12/15 21:27:19
Done.
|
| + |
| + virtual bool IsNull() const { return true; } |
| +}; |
| + |
| +class ParsedJSONObject : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONObject(intptr_t length, ParsedJSONValue** keys_and_values) |
|
Florian Schneider
2016/12/15 19:09:35
explicit not needed.
rmacnak
2016/12/15 21:27:19
Done.
|
| + : length_(length), keys_and_values_(keys_and_values) {} |
| + |
| + ParsedJSONValue* At(const char* key) const { |
| + for (intptr_t i = 0; i < length_; i += 2) { |
| + ASSERT(keys_and_values_[i]->IsString()); |
| + ParsedJSONString* jskey = |
| + static_cast<ParsedJSONString*>(keys_and_values_[i]); |
| + if (jskey->Equals(key)) { |
| + return keys_and_values_[i + 1]; |
| + } |
| + } |
| + return NULL; |
| + } |
| + |
| + virtual bool IsObject() const { return true; } |
| + |
| + ParsedJSONNumber* NumberAt(const char* key) { |
| + ParsedJSONValue* member = At(key); |
| + if ((member == NULL) || !member->IsNumber()) { |
| + return NULL; |
| + } |
| + return static_cast<ParsedJSONNumber*>(member); |
| + } |
| + |
| + ParsedJSONString* StringAt(const char* key) { |
| + ParsedJSONValue* member = At(key); |
| + if ((member == NULL) || !member->IsString()) { |
| + return NULL; |
| + } |
| + return static_cast<ParsedJSONString*>(member); |
| + } |
| + |
| + ParsedJSONBoolean* BooleanAt(const char* key) { |
| + ParsedJSONValue* member = At(key); |
| + if ((member == NULL) || !member->IsBoolean()) { |
| + return NULL; |
| + } |
| + return static_cast<ParsedJSONBoolean*>(member); |
| + } |
| + |
| + ParsedJSONArray* ArrayAt(const char* key); |
| + |
| + private: |
| + intptr_t length_; |
| + ParsedJSONValue** keys_and_values_; |
| +}; |
| + |
| +class ParsedJSONArray : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONArray(intptr_t length, ParsedJSONValue** elements) |
|
Florian Schneider
2016/12/15 19:09:35
explicit not needed.
rmacnak
2016/12/15 21:27:19
Done.
|
| + : length_(length), elements_(elements) {} |
| + |
| + ParsedJSONValue* At(intptr_t index) const { |
| + ASSERT(index < length_); |
| + return elements_[index]; |
| + } |
| + |
| + intptr_t Length() const { return length_; } |
| + |
| + virtual bool IsArray() const { return true; } |
| + |
| + ParsedJSONObject* ObjectAt(intptr_t index) { |
| + ParsedJSONValue* element = At(index); |
| + if ((element == NULL) || !element->IsObject()) { |
| + return NULL; |
| + } |
| + return static_cast<ParsedJSONObject*>(element); |
| + } |
| + |
| + ParsedJSONNumber* NumberAt(intptr_t index) { |
| + ParsedJSONValue* element = At(index); |
| + if ((element == NULL) || !element->IsNumber()) { |
| + return NULL; |
| + } |
| + return static_cast<ParsedJSONNumber*>(element); |
| + } |
| + |
| + private: |
| + intptr_t length_; |
| + ParsedJSONValue** elements_; |
| +}; |
| + |
| +class ParsedJSONError : public ParsedJSONValue { |
| + public: |
| + explicit ParsedJSONError(const char* message, intptr_t position) |
| + : message_(message), position_(position) {} |
| + |
| + virtual bool IsError() const { return true; } |
| + |
| + const char* message() const { return message_; } |
| + intptr_t position() const { return position_; } |
| + |
| + private: |
| + const char* message_; |
| + intptr_t position_; |
| +}; |
| + |
| +class JSONParser { |
| + public: |
| + JSONParser(const char* buffer, intptr_t length, Zone* zone) |
| + : buffer_(buffer), position_(0), length_(length), zone_(zone) {} |
| + |
| + ParsedJSONValue* ParseValue() { |
| + ConsumeWhitespace(); |
| + if (Peek() == '\"') return ParseString(); |
| + if (IsDigitOrMinus(Peek())) return ParseNumber(); |
| + if (Peek() == '{') return ParseObject(); |
| + if (Peek() == '[') return ParseArray(); |
| + if (PeekAndConsume("true")) return new (zone_) ParsedJSONBoolean(true); |
| + if (PeekAndConsume("false")) return new (zone_) ParsedJSONBoolean(false); |
| + if (PeekAndConsume("null")) return new (zone_) ParsedJSONNull(); |
| + return Error("value expected"); |
| + } |
| + |
| + private: |
| + intptr_t Available() const { return length_ - position_; } |
| + char Peek() const { |
| + if (position_ < length_) return buffer_[position_]; |
| + return 0; |
| + } |
| + char Consume() { |
| + ASSERT(position_ < length_); |
| + return buffer_[position_++]; |
| + } |
| + bool PeekAndConsume(const char* expected) { |
| + intptr_t n = strlen(expected); |
| + if (Available() < n) return false; |
| + if (strncmp(&buffer_[position_], expected, n) != 0) return false; |
| + position_ += n; |
| + return true; |
| + } |
| + void ConsumeWhitespace() { |
| + while ((Available() > 0) && (buffer_[position_] < ' ')) |
| + position_++; |
| + } |
| + bool IsDigit(char c) { return c >= '0' && c <= '9'; } |
| + bool IsDigitOrMinus(char c) { return (c == '-') || (c >= '0' && c <= '9'); } |
| + |
| + ParsedJSONValue* ParseString() { |
| + ConsumeWhitespace(); |
| + if (Peek() != '\"') return Error("string expected"); |
| + Consume(); |
| + intptr_t start = position_; |
| + for (;;) { |
| + if (Available() == 0) return Error("unterminated string"); |
| + if (Consume() == '\"') break; |
| + } |
| + intptr_t end = position_ - 1; |
| + |
| + char* cstr = zone_->Alloc<char>(end - start + 1); |
| + intptr_t dst_pos = 0; |
| + for (intptr_t src_pos = start; src_pos < end; src_pos++) { |
| + if (buffer_[src_pos] == '\\') { |
| + src_pos++; |
| + } |
| + cstr[dst_pos++] = buffer_[src_pos]; |
| + } |
| + cstr[dst_pos] = '\0'; |
| + |
| + return new (zone_) ParsedJSONString(cstr); |
| + } |
| + |
| + ParsedJSONValue* ParseNumber() { |
| + ConsumeWhitespace(); |
| + bool negate = false; |
| + if (Peek() == '-') { |
| + Consume(); |
| + negate = true; |
| + } |
| + if (!IsDigit(Peek())) return Error("number expected"); |
| + int64_t value = 0; |
| + for (;;) { |
| + if (!IsDigit(Peek())) break; |
| + char c = Consume(); |
| + value *= 10; |
| + value += (c - '0'); |
| + } |
| + if (negate) { |
| + value = -value; |
| + } |
| + return new (zone_) ParsedJSONNumber(value); |
| + } |
| + |
| + ParsedJSONValue* ParseObject() { |
| + ConsumeWhitespace(); |
| + if (Peek() != '{') return Error("object expected"); |
| + Consume(); |
| + ConsumeWhitespace(); |
| + if (Peek() == '}') return new (zone_) ParsedJSONObject(0, NULL); |
| + ZoneGrowableArray<ParsedJSONValue*>* keys_and_values = |
| + new (zone_) ZoneGrowableArray<ParsedJSONValue*>(zone_, 6); |
| + for (;;) { |
| + ParsedJSONValue* key = ParseString(); |
| + if (key->IsError()) return key; |
| + ConsumeWhitespace(); |
| + if (Consume() != ':') return Error(": expected"); |
| + ConsumeWhitespace(); |
| + ParsedJSONValue* value = ParseValue(); |
| + if (value->IsError()) return value; |
| + ConsumeWhitespace(); |
| + |
| + keys_and_values->Add(key); |
| + keys_and_values->Add(value); |
| + |
| + char c = Consume(); |
| + if (c == '}') break; |
| + if (c != ',') return Error(", expected (object)"); |
| + ConsumeWhitespace(); |
| + } |
| + |
| + return new (zone_) |
| + ParsedJSONObject(keys_and_values->length(), keys_and_values->data()); |
| + } |
| + |
| + ParsedJSONValue* ParseArray() { |
| + ConsumeWhitespace(); |
| + if (Peek() != '[') return Error("array expected"); |
| + Consume(); |
| + ConsumeWhitespace(); |
| + if (Peek() == ']') { |
| + Consume(); |
| + return new (zone_) ParsedJSONArray(0, NULL); |
| + } |
| + ZoneGrowableArray<ParsedJSONValue*>* elements = |
| + new (zone_) ZoneGrowableArray<ParsedJSONValue*>(zone_, 6); |
| + for (;;) { |
| + ParsedJSONValue* element = ParseValue(); |
| + if (element->IsError()) return element; |
| + ConsumeWhitespace(); |
| + |
| + elements->Add(element); |
| + |
| + char c = Consume(); |
| + if (c == ']') break; |
| + if (c != ',') return Error(", expected (array)"); |
| + ConsumeWhitespace(); |
| + } |
| + |
| + return new (zone_) ParsedJSONArray(elements->length(), elements->data()); |
| + } |
| + |
| + private: |
| + ParsedJSONError* Error(const char* message) { |
| + return new (zone_) ParsedJSONError(message, position_); |
| + } |
| + |
| + const char* const buffer_; |
| + intptr_t position_; |
| + intptr_t length_; |
| + Zone* zone_; |
| +}; |
| + |
| +} // namespace dart |
| + |
| +#endif // RUNTIME_VM_JSON_PARSER_H_ |