Просмотр исходного кода

Move JSON in-place parsing API into JSONParser and add interface for easily accessing JSON documents

Daniele Bartolini 12 лет назад
Родитель
Сommit
e030fe14eb
2 измененных файлов с 612 добавлено и 147 удалено
  1. 539 123
      engine/core/json/JSONParser.cpp
  2. 73 24
      engine/core/json/JSONParser.h

+ 539 - 123
engine/core/json/JSONParser.cpp

@@ -1,225 +1,641 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <cstdio>
+
 #include "JSONParser.h"
-#include "DiskFile.h"
-#include "OS.h"
+#include "TempAllocator.h"
 #include "StringUtils.h"
-#include "Assert.h"
-#include "Log.h"
 
 namespace crown
 {
 
-//--------------------------------------------------------------------------
-JSONParser::JSONParser(Allocator& allocator, const char* s) :
-	m_buffer(s),
-	m_nodes(allocator)
+//-----------------------------------------------------------------------------
+static const char* next(const char* str, const char c = 0)
 {
+	CE_ASSERT_NOT_NULL(str);
+
+	if (c && c != (*str))
+	{
+		CE_ASSERT(false, "Expected '%c' got '%c'", c, (*str));
+	}
+
+	return str + 1;
 }
 
-//--------------------------------------------------------------------------
-JSONParser&	JSONParser::root()
+//-----------------------------------------------------------------------------
+static const char* skip_whites(const char* s)
 {
-	m_nodes.clear();
+	CE_ASSERT_NOT_NULL(s);
 
-	List<JSONPair> tmp(default_allocator());
+	const char* ch = s;
 
-	JSON::parse_object(m_buffer, tmp);
+	while ((*ch) && (*ch) <= ' ') ch = next(ch);
 
-	JSONNode node;
+	return ch;
+}
+
+//-----------------------------------------------------------------------------
+static const char* skip_string(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
 
-	for (uint32_t i = 0; i < tmp.size(); i++)
+	bool escaped = false;
+
+	if ((*ch) == '"')
 	{
-		node.type = JSON::type(tmp[i].val);
-		node.key = tmp[i].key;
-		node.val = tmp[i].val;
+		while ((*(ch = next(ch))) != 0)
+		{
+			if ((*ch) == '"' && !escaped)
+			{
+				ch = next(ch);
+				return ch;
+			}
+			else if ((*ch) == '\\') escaped = true;
+			else escaped = false;
+		}
+	}
+
+	return ch;
+}
+
+//-----------------------------------------------------------------------------
+static const char* skip_number(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
 
-		m_nodes.push_back(node);
+	const char* ch = s;
+
+	while ((*ch) && (((*ch) >= '0' && (*ch) <= '9') ||
+			(*ch) == '-' || (*ch) == '.' || (*ch) == '+' ||
+			(*ch) == 'e' || (*ch) == 'E'))
+	{
+		ch = next(ch);
 	}
 
-	return *this;
+	return ch;
 }
 
-//--------------------------------------------------------------------------
-JSONParser&	JSONParser::object(const char* key)
+//-----------------------------------------------------------------------------
+static const char* skip_object(const char* s)
 {
-	bool found = false;
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	uint32_t brackets = 1;
 
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
+	if ((*ch) == '{')
 	{
-		if (m_nodes[i].type == JT_OBJECT)
+		brackets++;
+		ch = next(ch, '{');
+
+		while ((*ch) && brackets != 1)
 		{
-			List<char> str(default_allocator());
-			JSON::parse_string(m_nodes[i].key, str);
+			if ((*ch) == '}') brackets--;
+			else if ((*ch) == '{') brackets++;
+			ch = next(ch);
+		}
+	}
 
-			if (string::strcmp(key, str.begin()) == 0)
-			{
-				List<JSONPair> obj(default_allocator());
-				JSON::parse_object(m_nodes[i].val, obj);
+	return ch;
+}
 
-				m_nodes.clear();
+//-----------------------------------------------------------------------------
+static const char* skip_array(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
 
-				JSONNode node;
+	const char* ch = s;
 
-				for (uint32_t j = 0; j < obj.size(); j++)
-				{
-					node.type = JSON::type(obj[j].val);
-					node.key = obj[j].key;
-					node.val = obj[j].val;
+	uint32_t brackets = 1;
 
-					m_nodes.push_back(node);
-				}
+	if ((*ch) == '[')
+	{
+		brackets++;
+		ch = next(ch, '[');
 
-				found = true;
+		while ((*ch) && brackets != 1)
+		{
+			if ((*ch) == ']') brackets--;
+			else if ((*ch) == '[') brackets++;
+			ch = next(ch);
+		}
+	}
 
-				break;
-			}
+	return ch;
+}
+
+//-----------------------------------------------------------------------------
+static const char* skip_bool(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	switch ((*ch))
+	{
+		case 't':
+		{
+			ch = next(ch, 't');
+			ch = next(ch, 'r');
+			ch = next(ch, 'u');
+			ch = next(ch, 'e');
+			break;
+		}
+		case 'f':
+		{
+			ch = next(ch, 'f');
+			ch = next(ch, 'a');
+			ch = next(ch, 'l');
+			ch = next(ch, 's');
+			ch = next(ch, 'e');
+			break;
+		}
+		default:
+		{
+			break;
 		}
 	}
 
-	CE_ASSERT(found, "Object called %s not found", key);
+	return ch;
+}
+
+//-----------------------------------------------------------------------------
+static bool is_escapee(char c)
+{
+	return c == '"' || c == '\\' || c == '/' || c == '\b' || c == '\f' || c == '\n' ||
+			c == '\r' || c == '\t';
+}
+
+//--------------------------------------------------------------------------
+JSONParser::JSONParser(const char* s) :
+	m_document(s),
+	m_at(s)
+{
+	CE_ASSERT_NOT_NULL(s);
+}
+
+//--------------------------------------------------------------------------
+JSONParser&	JSONParser::root()
+{
+	m_at = skip_whites(m_document);
 
 	return *this;
 }
 
 //--------------------------------------------------------------------------
-JSONParser&	JSONParser::array(const char* key, uint32_t index)
+JSONParser& JSONParser::operator[](uint32_t i)
 {
-	bool found = false;
+	TempAllocator1024 alloc;
+	List<const char*> array(alloc);
 
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
-	{
-		if (m_nodes[i].type == JT_ARRAY)
-		{
-			List<char> str(default_allocator());
-			JSON::parse_string(m_nodes[i].key, str);
+	JSONParser::parse_array(m_at, array);
 
-			if (string::strcmp(key, str.begin()) == 0)
-			{
-				List<const char*> arr(default_allocator());
-				JSON::parse_array(m_nodes[i].val, arr);
+	CE_ASSERT(i < array.size(), "Index out of bounds");
 
-				m_nodes.clear();
+	m_at = array[i];
 
-				JSONNode node;
+	return *this;
+}
 
-				node.type = JSON::type(arr[index]);
-				node.key = NULL;
-				node.val = arr[index];
+//--------------------------------------------------------------------------
+JSONParser& JSONParser::key(const char* k)
+{
+	TempAllocator1024 alloc;
+	List<JSONPair> object(alloc);
 
-				m_nodes.push_back(node);
+	JSONParser::parse_object(m_at, object);
 
-				found = true;
+	bool found = false;
 
-				break;
-			}
+	for (uint32_t i = 0; i < object.size(); i++)
+	{
+		TempAllocator256 key_alloc;
+		List<char> key(key_alloc);
+
+		JSONParser::parse_string(object[i].key, key);
+
+		if (string::strcmp(k, key.begin()) == 0)
+		{
+			m_at = object[i].val;
+			found = true;
 		}
 	}
 
-	CE_ASSERT(found, "Array called %s not found", key);
+	CE_ASSERT(found, "Key not found");
 
 	return *this;
 }
 
 //--------------------------------------------------------------------------
-const char* JSONParser::string(const char* key, List<char>& str)
+bool JSONParser::bool_value() const
 {
-	bool found = false;
+	return JSONParser::parse_bool(m_at);
+}
 
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
+//--------------------------------------------------------------------------
+int32_t JSONParser::int_value() const
+{
+	return JSONParser::parse_int(m_at);
+}
+
+//--------------------------------------------------------------------------
+float JSONParser::float_value() const
+{
+	return JSONParser::parse_float(m_at);
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_nil() const
+{
+	return JSONParser::type(m_at) == JT_NIL;
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_bool() const
+{
+	return JSONParser::type(m_at) == JT_BOOL;
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_number() const
+{
+	return JSONParser::type(m_at) == JT_NUMBER;
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_string() const
+{
+	return JSONParser::type(m_at) == JT_STRING;
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_array() const
+{
+	return JSONParser::type(m_at) == JT_ARRAY;
+}
+
+//--------------------------------------------------------------------------
+bool JSONParser::is_object() const
+{
+	return JSONParser::type(m_at) == JT_OBJECT;
+}
+
+//--------------------------------------------------------------------------
+uint32_t JSONParser::size()
+{
+	switch(JSONParser::type(m_at))
 	{
-		if (JSON::type(m_nodes[i].val) == JT_STRING)
+		case JT_NIL:
+		{
+			return 1;
+		}
+		case JT_OBJECT:
 		{
-			List<char> tmp(default_allocator());
-			JSON::parse_string(m_nodes[i].key, tmp);
+			TempAllocator1024 alloc;
+			List<JSONPair> object(alloc);
+			JSONParser::parse_object(m_at, object);
 
-			if (string::strcmp(key, tmp.begin()) == 0)
-			{
-				JSON::parse_string(m_nodes[i].val, str);	
+			return object.size();
+		}
+		case JT_ARRAY:
+		{
+			TempAllocator1024 alloc;
+			List<const char*> array(alloc);
+			JSONParser::parse_array(m_at, array);
 
-				found = true;
+			return array.size();
+		}
+		case JT_STRING:
+		{
+			TempAllocator1024 alloc;
+			List<char> string(alloc);
+			JSONParser::parse_string(m_at, string);
 
-				break;
-			}
+			return string.size();
+		}
+		case JT_NUMBER:
+		{
+			return 1;
+		}
+		case JT_BOOL:
+		{
+			return 1;
+		}
+		default:
+		{
+			return 0;
 		}
 	}
+}
 
-	CE_ASSERT(found, "String not found");
+//-----------------------------------------------------------------------------
+JSONType JSONParser::type(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
 
-	// FIXME FIXME FIXME
-	return "FIXME";
+	switch (s[0])
+	{
+		case '{': return JT_OBJECT;
+		case '[': return JT_ARRAY;
+		case '"': return JT_STRING;
+		case '-': return JT_NUMBER;
+		default: return s[0] >= '0' && s[0] <= '9' ? JT_NUMBER : (s[0] == 'n' ? JT_NIL : JT_BOOL);
+	}
 }
 
-//--------------------------------------------------------------------------
-double JSONParser::number(const char* key)
+//-----------------------------------------------------------------------------
+void JSONParser::parse_string(const char* s, List<char>& str)
 {
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	if ((*ch) == '"')
 	{
-		if (JSON::type(m_nodes[i].val) == JT_NUMBER)
+		while ((*(ch = next(ch))))
 		{
-			if (m_nodes[i].key == NULL)
+			// Empty string
+			if ((*ch) == '"')
 			{
-				return JSON::parse_number(m_nodes[i].val);	
+				ch = next(ch);
+				str.push_back('\0');
+				return;
 			}
+			else if ((*ch) == '\\')
+			{
+				ch = next(ch);
 
-			List<char> tmp(default_allocator());
-			JSON::parse_string(m_nodes[i].key, tmp);
+				if ((*ch) == 'u')
+				{
+					CE_ASSERT(false, "Not supported at the moment");
+				}
+				else if (is_escapee(*ch))
+				{
+					str.push_back('\\');
+					str.push_back(*ch);
+				}
+				else
+				{
+					// Go to invalid string
+					break;
+				}
+			}
+			else
+			{
+				str.push_back(*ch);
+			}
+		}
+	}
 
+	CE_ASSERT(false, "Not a valid string");
+}
 
-			if (string::strcmp(key, tmp.begin()) == 0)
-			{			
-				return JSON::parse_number(m_nodes[i].val);	
-			}
+//-----------------------------------------------------------------------------
+double JSONParser::parse_number(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	TempAllocator1024 allocator;
+ 	List<char> str(allocator);
+
+	if ((*ch) == '-')
+	{
+		str.push_back('-');
+		ch = next(ch, '-');
+	}
+	while ((*ch) >= '0' && (*ch) <= '9')
+	{
+		str.push_back((*ch));
+		ch = next(ch);
+	}
+
+	if ((*ch) == '.')
+	{
+		str.push_back('.');
+		while ((*(ch = next(ch))) && (*ch) >= '0' && (*ch) <= '9')
+		{
+			str.push_back(*ch);
+		}
+	}
+
+	if ((*ch) == 'e' || (*ch) == 'E')
+	{
+		str.push_back(*ch);
+		ch = next(ch);
+
+		if ((*ch) == '-' || (*ch) == '+')
+		{
+			str.push_back(*ch);
+			ch = next(ch);
+		}
+		while ((*ch) >= '0' && (*ch) <= '9')
+		{
+			str.push_back(*ch);
+			ch = next(ch);
 		}
 	}
 
-	// CE_ASSERT(found, "Number not found");
+	// Ensure null terminated
+	str.push_back('\0');
 
-	// FIXME FIXME FIXME
-	return 0.0;
-}		
+	float number = 0.0f;
 
-//--------------------------------------------------------------------------
-bool JSONParser::boolean(const char* key)
+	// Fixme
+	sscanf(str.begin(), "%f", &number);
+
+	return number;
+}
+
+//-----------------------------------------------------------------------------
+bool JSONParser::parse_bool(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	switch(*ch)
+	{
+		case 't':
+		{
+			ch = next(ch, 't');
+			ch = next(ch, 'r');
+			ch = next(ch, 'u');
+			ch = next(ch, 'e');
+			return true;
+		}
+		case 'f':
+		{
+			ch = next(ch, 'f');
+			ch = next(ch, 'a');
+			ch = next(ch, 'l');
+			ch = next(ch, 's');			
+			ch = next(ch, 'e');
+			return false;
+		}
+		default: break;
+	}
+
+	CE_ASSERT(false, "Not a boolean");
+}
+
+//-----------------------------------------------------------------------------
+int32_t JSONParser::parse_int(const char* s)
 {
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
+	CE_ASSERT_NOT_NULL(s);
+
+	return (int32_t) parse_number(s);
+}
+
+//-----------------------------------------------------------------------------
+float JSONParser::parse_float(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	return (float) parse_number(s);
+}
+
+//-----------------------------------------------------------------------------
+void JSONParser::parse_array(const char* s, List<const char*>& array)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	if ((*ch) == '[')
 	{
-		if (JSON::type(m_nodes[i].val) == JT_BOOL)
+		ch = next(ch, '[');
+
+		// Skip whitespaces
+		while ((*ch) && (*ch) <= ' ')
+		{
+			ch = next(ch);
+		}
+
+		if ((*ch) == ']')
 		{
-			if (m_nodes[i].key == NULL)
+			ch = next(ch, ']');
+			return;
+		}
+
+		while (*ch)
+		{
+			array.push_back(ch);
+
+			ch = skip_array(ch);
+			ch = skip_object(ch);
+			ch = skip_number(ch);
+			ch = skip_string(ch);
+			ch = skip_bool(ch);
+
+			ch = skip_whites(ch);
+
+			// Closing bracket (top-most array)
+			if ((*ch) == ']')
 			{
-				return JSON::parse_bool(m_nodes[i].val);	
+				ch = next(ch, ']');
+				return;
 			}
 
-			List<char> tmp(default_allocator());
-			JSON::parse_string(m_nodes[i].key, tmp);
+			// Skip until next ','
+			ch = next(ch, ',');
 
-			if (string::strcmp(key, tmp.begin()) == 0)
-			{			
-				return JSON::parse_bool(m_nodes[i].val);	
-			}
+			// Skip whites, eventually
+			ch = skip_whites(ch);
 		}
 	}
 
-	// CE_ASSERT(found, "Boolean not found");
-
-	// FIXME FIXME FIXME
-	return false;
+	CE_ASSERT(false, "Not an array");
 }
 
-//--------------------------------------------------------------------------
-void JSONParser::print_nodes()
+//-----------------------------------------------------------------------------
+void JSONParser::parse_object(const char* s, List<JSONPair>& map)
 {
-	for (uint32_t i = 0; i < m_nodes.size(); i++)
+	CE_ASSERT_NOT_NULL(s);
+
+	const char* ch = s;
+
+	if ((*ch) == '{')
 	{
-		Log::i("Index: %d", i);	
-		Log::i("Type : %d", m_nodes[i].type);
-		Log::i("Key  : %s", m_nodes[i].key);
-		Log::i("Val  : %s", m_nodes[i].val);
-	}
-}
+		ch = next(ch, '{');
+
+		ch = skip_whites(ch);
 
+		if ((*ch) == '}')
+		{
+			next(ch, '}');
+			return;
+		}
+
+		while (*ch)
+		{
+			JSONPair pair;
+
+			pair.key = ch;
+
+			// Skip any value
+			ch = skip_array(ch);
+			ch = skip_object(ch);
+			ch = skip_number(ch);
+			ch = skip_string(ch);
+			ch = skip_bool(ch);
+
+			ch = skip_whites(ch);
+			ch = next(ch, ':');
+			ch = skip_whites(ch);
 
+			pair.val = ch;
+			map.push_back(pair);
 
+			// Skip any value
+			ch = skip_array(ch);
+			ch = skip_object(ch);
+			ch = skip_number(ch);
+			ch = skip_string(ch);
+			ch = skip_bool(ch);
 
+			ch = skip_whites(ch);
 
+			if ((*ch) == '}')
+			{
+				next(ch, '}');
+				return;
+			}
+
+			ch = next(ch, ',');
+			ch = skip_whites(ch);
+		}
+	}
+
+	CE_ASSERT(false, "Not an object");
+}
 
-} //namespace crown
+} //namespace crown

+ 73 - 24
engine/core/json/JSONParser.h

@@ -1,17 +1,49 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
 #pragma once
 
-#include <cstdarg>
 #include "Types.h"
 #include "List.h"
-#include "HeapAllocator.h"
-#include "JSON.h"
 
 namespace crown
 {
 
-struct JSONNode
+enum JSONType
+{
+	JT_NIL,
+	JT_OBJECT,
+	JT_ARRAY,
+	JT_STRING,
+	JT_NUMBER,
+	JT_BOOL
+};
+
+struct JSONPair
 {
-	JSONType	type;
 	const char* key;
 	const char* val;
 };
@@ -19,28 +51,45 @@ struct JSONNode
 class JSONParser
 {
 public:
-	/// Constructor
-							JSONParser(Allocator& allocator, const char* s);
-	/// Get root element
-	JSONParser&				root();
-	/// Get object @a key
-	JSONParser&				object(const char* key);
-	/// Get array @a key and element @a index  
-	JSONParser&				array(const char* key, uint32_t index);
-	/// Get string
-	const char* 			string(const char* key, List<char>& str);
-	/// Get number
-	double					number(const char* key = NULL);	
-	/// Get boolean
-	bool 					boolean(const char* key = NULL);
-
-	void					print_nodes();
 
-private:
+						JSONParser(const char* s);
+
+	JSONParser&			root();
+
+	JSONParser&			operator[](uint32_t i);
+	JSONParser&			key(const char* k);
 
-	const char*				m_buffer;
+	bool				is_nil() const;
+	bool				is_bool() const;
+	bool				is_number() const;
+	bool				is_string() const;
+	bool				is_array() const;
+	bool				is_object() const;
+
+	uint32_t			size();
+
+	bool				bool_value() const;
+	int32_t				int_value() const;
+	float				float_value() const;
+
+public:
+
+	static JSONType		type(const char* s);
+
+	static void			parse_string(const char* s, List<char>& str);
+	static double		parse_number(const char* s);
+
+	static bool			parse_bool(const char* s);
+	static int32_t 		parse_int(const char* s);
+	static float		parse_float(const char* s);
+
+	static void			parse_array(const char* s, List<const char*>& array);
+	static void			parse_object(const char* s, List<JSONPair>& map);
+
+private:
 
-	List<JSONNode>			m_nodes;
+	const char* const	m_document;
+	const char*			m_at;
 };
 
 } // namespace crown