Quellcode durchsuchen

Add NJSON parser

Daniele Bartolini vor 11 Jahren
Ursprung
Commit
a121ba4d2b
4 geänderte Dateien mit 408 neuen und 23 gelöschten Zeilen
  1. 0 18
      engine/core/json/json.cpp
  2. 5 5
      engine/core/json/json.h
  3. 340 0
      engine/core/json/njson.cpp
  4. 63 0
      engine/core/json/njson.h

+ 0 - 18
engine/core/json/json.cpp

@@ -59,21 +59,6 @@ namespace json
 		return str;
 	}
 
-	static const char* skip_comments(const char* str)
-	{
-		CE_ASSERT_NOT_NULL(str);
-
-		while (*str == '/')
-		{
-			str = next(str, '/');
-			str = next(str, '/');
-			while (*str && *str != '\n') str = next(str);
-			str = skip_spaces(str);
-		}
-
-		return str;
-	}
-
 	JSONType::Enum type(const char* str)
 	{
 		CE_ASSERT_NOT_NULL(str);
@@ -268,7 +253,6 @@ namespace json
 			str = next(str, '{');
 
 			str = skip_spaces(str);
-			str = skip_comments(str);
 
 			if (*str == '}')
 			{
@@ -290,7 +274,6 @@ namespace json
 
 				str = skip_value(str);
 				str = skip_spaces(str);
-				str = skip_comments(str);
 
 				if (*str == '}')
 				{
@@ -300,7 +283,6 @@ namespace json
 
 				str = next(str, ',');
 				str = skip_spaces(str);
-				str = skip_comments(str);
 			}
 		}
 

+ 5 - 5
engine/core/json/json.h

@@ -20,12 +20,12 @@ struct JSONType
 {
 	enum Enum
 	{
-		OBJECT,
-		ARRAY,
-		STRING,
-		NUMBER,
+		NIL,
 		BOOL,
-		NIL
+		NUMBER,
+		STRING,
+		ARRAY,
+		OBJECT
 	};
 };
 

+ 340 - 0
engine/core/json/njson.cpp

@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2012-2014 Daniele Bartolini and individual contributors.
+ * License: https://github.com/taylor001/crown/blob/master/LICENSE
+ */
+
+#include "njson.h"
+#include "string_utils.h"
+#include "temp_allocator.h"
+#include "map.h"
+
+namespace crown
+{
+namespace njson
+{
+	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;
+	}
+
+	static const char* skip_string(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		bool escaped = false;
+
+		while ((*(str = next(str))) != 0)
+		{
+			if (*str == '"' && !escaped)
+			{
+				str = next(str);
+				return str;
+			}
+			else if (*str == '\\') escaped = true;
+			else escaped = false;
+		}
+
+		return str;
+	}
+
+	static const char* skip_value(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		switch (*str)
+		{
+			case '"': str = skip_string(str); break;
+			case '[': str = skip_block(str, '[', ']'); break;
+			case '{': str = skip_block(str, '{', '}'); break;
+			default: for (; *str != ',' && *str != '\n' && *str != ' ' && *str != '}' && *str != ']'; ++str) ; break;
+		}
+
+		return str;
+	}
+
+	static const char* skip_comments(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		if (*str == '/')
+		{
+			str = next(str, '/');
+			str = next(str, '/');
+			while (*str && *str != '\n') str = next(str);
+		}
+
+		return str;
+	}
+
+	static const char* skip_spaces(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		while (*str)
+		{
+			if (*str == '/') str = skip_comments(str);
+			else if (isspace(*str) || *str == ',') ++str;
+			else break;
+		}
+
+		return str;
+	}
+
+	NJSONType::Enum type(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		switch (*str)
+		{
+			case '"': return NJSONType::STRING;
+			case '{': return NJSONType::OBJECT;
+			case '[': return NJSONType::ARRAY;
+			case '-': return NJSONType::NUMBER;
+			default: return (isdigit(*str)) ? NJSONType::NUMBER : (*str == 'n' ? NJSONType::NIL : NJSONType::BOOL);
+		}
+	}
+
+	void parse_string(const char* str, DynamicString& string)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		if (*str == '"')
+		{
+			while (*(str = next(str)))
+			{
+				// Empty string
+				if (*str == '"')
+				{
+					str = next(str);
+					return;
+				}
+				else if (*str == '\\')
+				{
+					str = next(str);
+
+					switch (*str)
+					{
+						case '"': string += '"'; break;
+						case '\\': string += '\\'; break;
+						case '/': string += '/'; break;
+						case 'b': string += '\b'; break;
+						case 'f': string += '\f'; break;
+						case 'n': string += '\n'; break;
+						case 'r': string += '\r'; break;
+						case 't': string += '\t'; break;
+						default:
+						{
+							CE_FATAL("Bad escape character");
+							break;
+						}
+					}
+				}
+				else
+				{
+					string += *str;
+				}
+			}
+		}
+
+		CE_FATAL("Bad string");
+	}
+
+	static const char* parse_key(const char* str, DynamicString& key)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		if (*str == '"')
+		{
+			parse_string(str, key);
+			return skip_string(str);
+		}
+		else if (isalpha(*str))
+		{
+			while (true)
+			{
+				if (isspace(*str) || *str == '=') return str;
+
+				key += *str;
+				++str;
+			}
+		}
+
+		CE_FATAL("Bad key");
+	}
+
+	double parse_number(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		TempAllocator512 alloc;
+	 	Array<char> number(alloc);
+
+		if (*str == '-')
+		{
+			array::push_back(number, '-');
+			str = next(str, '-');
+		}
+		while (isdigit(*str))
+		{
+			array::push_back(number, *str);
+			str = next(str);
+		}
+
+		if (*str == '.')
+		{
+			array::push_back(number, '.');
+			while ((*(str = next(str))) && isdigit(*str))
+			{
+				array::push_back(number, *str);
+			}
+		}
+
+		if (*str == 'e' || *str == 'E')
+		{
+			array::push_back(number, *str);
+			str = next(str);
+
+			if (*str == '-' || *str == '+')
+			{
+				array::push_back(number, *str);
+				str = next(str);
+			}
+			while (isdigit(*str))
+			{
+				array::push_back(number, *str);
+				str = next(str);
+			}
+		}
+
+		// Ensure null terminated
+		array::push_back(number, '\0');
+		return parse_double(array::begin(number));
+	}
+
+	bool parse_bool(const char* str)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		switch (*str)
+		{
+			case 't':
+			{
+				str = next(str, 't');
+				str = next(str, 'r');
+				str = next(str, 'u');
+				str = next(str, 'e');
+				return true;
+			}
+			case 'f':
+			{
+				str = next(str, 'f');
+				str = next(str, 'a');
+				str = next(str, 'l');
+				str = next(str, 's');
+				str = next(str, 'e');
+				return false;
+			}
+			default:
+			{
+				CE_FATAL("Bad boolean");
+				return false;
+			}
+		}
+	}
+
+	int32_t parse_int(const char* str)
+	{
+		return (int32_t) parse_number(str);
+	}
+
+	float parse_float(const char* str)
+	{
+		return (float) parse_number(str);
+	}
+
+	void parse_array(const char* str, Array<const char*>& array)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		if (*str == '[')
+		{
+			str = next(str, '[');
+			str = skip_spaces(str);
+
+			if (*str == ']')
+			{
+				str = next(str, ']');
+				return;
+			}
+
+			while (*str)
+			{
+				array::push_back(array, str);
+
+				str = skip_value(str);
+				str = skip_spaces(str);
+
+				if (*str == ']')
+				{
+					str = next(str, ']');
+					return;
+				}
+
+				str = skip_spaces(str);
+			}
+		}
+
+		CE_FATAL("Bad array");
+	}
+
+	void parse_object(const char* str, Map<DynamicString, const char*>& object)
+	{
+		CE_ASSERT_NOT_NULL(str);
+
+		if (*str == '{')
+		{
+			str = next(str, '{');
+
+			str = skip_spaces(str);
+
+			if (*str == '}')
+			{
+				next(str, '}');
+				return;
+			}
+
+			while (*str)
+			{
+				DynamicString key;
+				str = parse_key(str, key);
+
+				str = skip_spaces(str);
+				str = next(str, '=');
+				str = skip_spaces(str);
+
+				map::set(object, key, str);
+
+				str = skip_value(str);
+				str = skip_spaces(str);
+
+				if (*str == '}')
+				{
+					next(str, '}');
+					return;
+				}
+
+				str = skip_spaces(str);
+			}
+		}
+
+		CE_FATAL("Bad object");
+	}
+} // namespace njson
+} // namespace crown

+ 63 - 0
engine/core/json/njson.h

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012-2014 Daniele Bartolini and individual contributors.
+ * License: https://github.com/taylor001/crown/blob/master/LICENSE
+ */
+
+#pragma once
+
+#include "dynamic_string.h"
+#include "container_types.h"
+
+namespace crown
+{
+
+/// @defgroup JSON
+
+/// Enumerates JSON value types.
+///
+/// @ingroup JSON
+struct NJSONType
+{
+	enum Enum
+	{
+		NIL,
+		BOOL,
+		NUMBER,
+		STRING,
+		ARRAY,
+		OBJECT
+	};
+};
+
+/// Functions to parse NJSON-encoded strings.
+///
+/// @ingroup JSON
+namespace njson
+{
+	/// Returns the data type of the NJSON string @a str.
+	NJSONType::Enum type(const char* str);
+
+	/// Parses the NJSON string @a str ad puts it into @a string.
+	void parse_string(const char* str, DynamicString& string);
+
+	/// Returns the NJSON number @a str as double.
+	double parse_number(const char* str);
+
+	/// Returns the NJSON number @a str as int.
+	int32_t parse_int(const char* str);
+
+	/// Returns the NJSON number @a str as float.
+	float parse_float(const char* str);
+
+	/// Returns the NJSON boolean @a str as bool.
+	bool parse_bool(const char* str);
+
+	/// Parses the NJSON array @a str and puts it into @a array as pointers to
+	/// the corresponding items into the original @a str string.
+	void parse_array(const char* str, Array<const char*>& array);
+
+	/// Parses the NJSON object @a str and puts it into @a object as map from
+	/// key to pointer to the corresponding value into the original string @a str.
+	void parse_object(const char* str, Map<DynamicString, const char*>& object);
+} // namespace njson
+} // namespace crown