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..83b9d6b171d68c96087b7b8d59aa5502daa73e3e |
--- /dev/null |
+++ b/runtime/vm/json_parser.h |
@@ -0,0 +1,331 @@ |
+// 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: |
+ 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: |
+ virtual bool IsNull() const { return true; } |
+}; |
+ |
+class ParsedJSONObject : public ParsedJSONValue { |
+ public: |
+ ParsedJSONObject(intptr_t length, ParsedJSONValue** keys_and_values) |
+ : 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); |
+ } |
+ |
+ inline ParsedJSONArray* ArrayAt(const char* key); |
+ |
+ private: |
+ intptr_t length_; |
+ ParsedJSONValue** keys_and_values_; |
+}; |
+ |
+class ParsedJSONArray : public ParsedJSONValue { |
+ public: |
+ ParsedJSONArray(intptr_t length, ParsedJSONValue** elements) |
+ : 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_; |
+}; |
+ |
+ParsedJSONArray* ParsedJSONObject::ArrayAt(const char* key) { |
+ ParsedJSONValue* member = At(key); |
+ if ((member == NULL) || !member->IsArray()) { |
+ return NULL; |
+ } |
+ return static_cast<ParsedJSONArray*>(member); |
+} |
+ |
+} // namespace dart |
+ |
+#endif // RUNTIME_VM_JSON_PARSER_H_ |