Browse Source

Unstable, incomplete, uncommented in-place JSON parser

Daniele Bartolini 12 years ago
parent
commit
cfab84105d
2 changed files with 298 additions and 77 deletions
  1. 249 67
      engine/JSON.cpp
  2. 49 10
      engine/JSON.h

+ 249 - 67
engine/JSON.cpp

@@ -1,13 +1,44 @@
+/*
+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 "Assert.h"
 #include "JSON.h"
 #include "Types.h"
+#include "List.h"
+#include "Allocator.h"
+#include <cstdio>
 
 namespace crown
 {
 
+static const char* g_current_char = NULL;
+
 /// This function is EPIC. Love it. Now.
 //-----------------------------------------------------------------------------
-CE_EXPORT void JSON::next(const char* str, const char c, bool force_reset)
+char JSON::next(const char* str, const char c, bool force_reset)
 {
 	static char* current_str = (char*)str;
 	static size_t index = 0;
@@ -20,6 +51,8 @@ CE_EXPORT void JSON::next(const char* str, const char c, bool force_reset)
 		current = str[index];
 	}
 
+	g_current_char = &str[index];
+
 	if (c && c != current)
 	{
 		CE_ASSERT(false, "Expected '%c' got '%c'", c, current);
@@ -27,30 +60,155 @@ CE_EXPORT void JSON::next(const char* str, const char c, bool force_reset)
 
 	index++;
 	current = str[index];
+
+	return current;
+}
+
+//-----------------------------------------------------------------------------
+bool JSON::is_escapee(char c)
+{
+	return c == '"' || c == '\\' || c == '/' || c == '\b' || c == '\f' || c == '\n' ||
+			c == '\r' || c == '\t';
+}
+
+//-----------------------------------------------------------------------------
+JSONType JSON::type(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	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 : JT_BOOL;
+	}
+}
+
+
+//-----------------------------------------------------------------------------
+void JSON::parse_string(const char* s, List<char>& str)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	char cur = '\0';
+
+	if (s[0] == '"')
+	{
+		while ((cur = next(s)) != 0)
+		{
+			if (cur == '"')
+			{
+				next(s);
+				str.push_back('\0');
+				return;
+			}
+			else if (cur == '\\')
+			{
+				cur = next(s);
+
+				if (cur == 'u')
+				{
+					CE_ASSERT(false, "Not supported at the moment");
+				}
+				else if (is_escapee(cur))
+				{
+					str.push_back('\\');
+					str.push_back(cur);
+				}
+				else
+				{
+					break; // to invalid string
+				}
+			}
+			else
+			{
+				str.push_back(cur);
+			}
+		}
+	}
+
+	CE_ASSERT(false, "Not a valid string");
+}
+
+//-----------------------------------------------------------------------------
+double JSON::parse_number(const char* s)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+ 	List<char> str(default_allocator());
+
+ 	char cur = s[0];
+
+	if (cur == '-')
+	{
+		str.push_back('-');
+		cur = next(s, '-');
+	}
+	while (cur >= '0' && cur <= '9')
+	{
+		str.push_back(cur);
+		cur = next(s);
+	}
+
+	if (cur == '.')
+	{
+		str.push_back('.');
+		while ((cur = next(s)) != 0 && cur >= '0' && cur <= '9')
+		{
+			str.push_back(cur);
+		}
+	}
+
+	if (cur == 'e' || cur == 'E')
+	{
+		str.push_back(cur);
+		cur = next(s);
+		if (cur == '-' || cur == '+')
+		{
+			str.push_back(cur);
+			cur = next(s);
+		}
+		while (cur >= '0' && cur <= '9')
+		{
+			str.push_back(cur);
+			cur = next(s);
+		}
+	}
+
+	// Ensure null terminated
+	str.push_back('\0');
+
+	float number = 0.0f;
+
+	sscanf(str.begin(), "%f", &number);
+	printf("\n\nParsed number: %f\n\n", number);
+	return number;
 }
 
 //-----------------------------------------------------------------------------
-CE_EXPORT bool JSON::parse_bool(const char* token)
+bool JSON::parse_bool(const char* s)
 {
-	CE_ASSERT_NOT_NULL(token);
+	CE_ASSERT_NOT_NULL(s);
 
-	switch(token[0])
+	switch(s[0])
 	{
 		case 't':
 		{
-			next(token, 't');
-			next(token, 'r');
-			next(token, 'u');
-			next(token, 'e');
+			next(s, 't');
+			next(s, 'r');
+			next(s, 'u');
+			next(s, 'e');
 			return true;
 		}
 		case 'f':
 		{
-			next(token, 'f');
-			next(token, 'a');
-			next(token, 'l');
-			next(token, 's');			
-			next(token, 'e');
+			next(s, 'f');
+			next(s, 'a');
+			next(s, 'l');
+			next(s, 's');			
+			next(s, 'e');
 			return false;
 		}
 		default:
@@ -61,72 +219,96 @@ CE_EXPORT bool JSON::parse_bool(const char* token)
 }
 
 //-----------------------------------------------------------------------------
-CE_EXPORT int32_t	JSON::parse_int(const char* token)
+int32_t JSON::parse_int(const char* s)
 {
-	// uint32_t index = 0;
-
-	// uint32_t start = index;
-
-	// uint32_t end = -1;
-
-	// while(token[index] != '\0' || end == -1)
-	// {
-	// 	switch(token[index])
-	// 	{
-	//         case '-':
-	//         {
-	        	
-	//         }
- //            case '0':
- //            case '1':
- //            case '2':
- //            case '3':
- //            case '4':
- //            case '5':
- //            case '6':
- //            case '7':
- //            case '8':
- //            case '9':
- //            {
- //            	index++;
- //            }
-	// 		case '\t': 
-	// 		case '\r': 
-	// 		case '\n': 
-	// 		case ' ':
-	// 		case ',': 
-	// 		case '}':
-	// 		case ']':
-	// 		{
-	// 			end = index - 1;
-	// 		}
-	// 	}
-	// }
+	CE_ASSERT_NOT_NULL(s);
+
+	return (int32_t) parse_number(s);
 }
 
 //-----------------------------------------------------------------------------
-CE_EXPORT float JSON::parse_float(const char* token)
+float JSON::parse_float(const char* s)
 {
+	CE_ASSERT_NOT_NULL(s);
 
+	return (float) parse_number(s);
 }
 
-// //-----------------------------------------------------------------------------
-// void JSON::parse_string(const char* token, List<char>& str)
-// {
+//-----------------------------------------------------------------------------
+void JSON::parse_array(const char* s, List<const char*>& array)
+{
+	CE_ASSERT_NOT_NULL(s);
+
+	char cur = s[0];
+
+	if (cur == '[')
+	{
+		cur = next(s, '[');
+
+		// Skip whitespaces
+		while (cur && cur <= ' ')
+		{
+			cur = next(s);
+		}
 
-// }
+		if (cur == ']')
+		{
+			cur = next(s, ']');
+			return;
+		}
 
-// //-----------------------------------------------------------------------------
-// void JSON::parse_array(const char* token, List<const char*>& array)
-// {
+		while ((cur = next(s)) != 0)
+		{
+			array.push_back(g_current_char);
+			JSONType t = type(g_current_char);
 
-// }
+			while (cur != 0)
+			{
+				cur = next(s);
 
-// //-----------------------------------------------------------------------------
-// void JSON::parse_object(const char* token, Dictionary<const char* key, const char* val>& dict)
-// {
+				if (t == JT_NUMBER)
+				{
+					if (cur == ',')
+					{
+						cur = next(s, ',');
+						// Skip whitespace
+						while (cur && cur <= ' ')
+						{
+							cur = next(s);
+						}
+						break;
+					}
+				}
+				else if (t == JT_ARRAY)
+				{
+					while (cur && cur != ']')
+					{
+						cur = next(s);
+					}
+					while (cur && cur <= ' ')
+					{
+						cur = next(s);
+					}
+					if (cur == ',')
+					{
+						cur = next(s, ' ');
+						while (cur && cur <= ' ')
+						{
+							cur = next(s);
+						}
+					}
+				}
 
-// }
+				if (cur == ']')
+				{
+					cur = next(s, ']');
+					return;
+				}
+			}
+		}
+	}
 
+	CE_ASSERT(false, "Not an array");
+}
 
-} // namespace crown
+} // namespace crown

+ 49 - 10
engine/JSON.h

@@ -1,15 +1,45 @@
+/*
+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 "Types.h"
+#include "List.h"
 
 namespace crown
 {
 
-enum JSONValue
+enum JSONType
 {
-	JSON_NULL,
-	JSON_BOOL,
-	JSON_NUMBER
+	JT_NIL,
+	JT_OBJECT,
+	JT_ARRAY,
+	JT_STRING,
+	JT_NUMBER,
+	JT_BOOL
 };
 
 class JSON
@@ -19,13 +49,22 @@ public:
 
 	void 				skip_whitespace();
 
-	static void			next(const char* str, const char c = 0, bool force_reset = false);
+	static char			next(const char* s, const char c = 0, bool force_reset = false);
+
+	static bool			is_escapee(char c);
+
+	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 bool			parse_bool(const char* token);
-	static int32_t 		parse_int(const char* token);
-	static float		parse_float(const char* token);
-	// static void			parse_string(const char* token, List<char>& str);
-	
 	// static void			parse_array(const char* token, List<const char*>& array);
 	// static void			parse_object(const char* token, Dictionary<const char* key, const char* val>& dict);