Update path to xapp descriptor. Add schema parser library
[ric-app/mc.git] / schemaparser / json.cc
diff --git a/schemaparser/json.cc b/schemaparser/json.cc
new file mode 100644 (file)
index 0000000..5fdff19
--- /dev/null
@@ -0,0 +1,482 @@
+// Distributed under the MIT license. Copyright (c) 2010, Ivan Vashchaev
+
+
+
+
+#include <string.h>
+#include "json.h"
+
+namespace mc_json{
+
+// true if character represent a digit
+#define IS_DIGIT(c) (c >= '0' && c <= '9')
+
+// convert string to integer
+static char *atoi(char *first, char *last, int *out)
+{
+       int sign = 1;
+       if (first != last)
+       {
+               if (*first == '-')
+               {
+                       sign = -1;
+                       ++first;
+               }
+               else if (*first == '+')
+               {
+                       ++first;
+               }
+       }
+
+       int result = 0;
+       for (; first != last && IS_DIGIT(*first); ++first)
+       {
+               result = 10 * result + (*first - '0');
+       }
+       *out = result * sign;
+
+       return first;
+}
+
+// convert hexadecimal string to unsigned integer
+static char *hatoui(char *first, char *last, unsigned int *out)
+{
+       unsigned int result = 0;
+       for (; first != last; ++first)
+       {
+               int digit;
+               if (IS_DIGIT(*first))
+               {
+                       digit = *first - '0';
+               }
+               else if (*first >= 'a' && *first <= 'f')
+               {
+                       digit = *first - 'a' + 10;
+               }
+               else if (*first >= 'A' && *first <= 'F')
+               {
+                       digit = *first - 'A' + 10;
+               }
+               else
+               {
+                       break;
+               }
+               result = 16 * result + (unsigned int)digit;
+       }
+       *out = result;
+
+       return first;
+}
+
+// convert string to floating point
+static char *atof(char *first, char *last, float *out)
+{
+       // sign
+       float sign = 1;
+       if (first != last)
+       {
+               if (*first == '-')
+               {
+                       sign = -1;
+                       ++first;
+               }
+               else if (*first == '+')
+               {
+                       ++first;
+               }
+       }
+
+       // integer part
+       float result = 0;
+       for (; first != last && IS_DIGIT(*first); ++first)
+       {
+               result = 10 * result + (float)(*first - '0');
+       }
+
+       // fraction part
+       if (first != last && *first == '.')
+       {
+               ++first;
+
+               float inv_base = 0.1f;
+               for (; first != last && IS_DIGIT(*first); ++first)
+               {
+                       result += (float)(*first - '0') * inv_base;
+                       inv_base *= 0.1f;
+               }
+       }
+
+       // result w\o exponent
+       result *= sign;
+
+       // exponent
+       bool exponent_negative = false;
+       int exponent = 0;
+       if (first != last && (*first == 'e' || *first == 'E'))
+       {
+               ++first;
+
+               if (*first == '-')
+               {
+                       exponent_negative = true;
+                       ++first;
+               }
+               else if (*first == '+')
+               {
+                       ++first;
+               }
+
+               for (; first != last && IS_DIGIT(*first); ++first)
+               {
+                       exponent = 10 * exponent + (*first - '0');
+               }
+       }
+
+       if (exponent)
+       {
+               float power_of_ten = 10;
+               for (; exponent > 1; exponent--)
+               {
+                       power_of_ten *= 10;
+               }
+
+               if (exponent_negative)
+               {
+                       result /= power_of_ten;
+               }
+               else
+               {
+                       result *= power_of_ten;
+               }
+       }
+
+       *out = result;
+
+       return first;
+}
+
+static inline json_value *json_alloc(block_allocator *allocator)
+{
+       json_value *value = (json_value *)allocator->malloc(sizeof(json_value));
+       memset(value, 0, sizeof(json_value));
+       return value;
+}
+
+static inline void json_append(json_value *lhs, json_value *rhs)
+{
+       rhs->parent = lhs;
+       if (lhs->last_child)
+       {
+               lhs->last_child = lhs->last_child->next_sibling = rhs;
+       }
+       else
+       {
+               lhs->first_child = lhs->last_child = rhs;
+       }
+}
+
+#define ERROR(it, desc)\
+       *error_pos = it;\
+       *error_desc = desc;\
+       *error_line = 1 - escaped_newlines;\
+       for (char *c = it; c != source; --c)\
+               if (*c == '\n') ++*error_line;\
+       return 0
+
+#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");}
+
+json_value *json_parse(char *source, char **error_pos, const char **error_desc, int *error_line, block_allocator *allocator)
+{
+       json_value *root = 0;
+       json_value *top = 0;
+
+       char *name = 0;
+       char *it = source;
+
+       int escaped_newlines = 0;
+
+       while (*it)
+       {
+               // skip white space
+               while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA')
+               {
+                       ++it;
+               }
+
+               switch (*it)
+               {
+               case '\0':
+                       break;
+               case '{':
+               case '[':
+                       {
+                               // create new value
+                               json_value *object = json_alloc(allocator);
+
+                               // name
+                               object->name = name;
+                               name = 0;
+
+                               // type
+                               object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY;
+
+                               // skip open character
+                               ++it;
+
+                               // set top and root
+                               if (top)
+                               {
+                                       json_append(top, object);
+                               }
+                               else if (!root)
+                               {
+                                       root = object;
+                               }
+                               else
+                               {
+                                       ERROR(it, "Second root. Only one root allowed");
+                               }
+                               top = object;
+                       }
+                       break;
+
+               case '}':
+               case ']':
+                       {
+                               if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY))
+                               {
+                                       ERROR(it, "Mismatch closing brace/bracket");
+                               }
+
+                               // skip close character
+                               ++it;
+
+                               // set top
+                               top = top->parent;
+                       }
+                       break;
+
+               case ':':
+                       if (!top || top->type != JSON_OBJECT)
+                       {
+                               ERROR(it, "Unexpected character");
+                       }
+                       ++it;
+                       break;
+
+               case ',':
+                       CHECK_TOP();
+                       ++it;
+                       break;
+
+               case '"':
+                       {
+                               CHECK_TOP();
+
+                               // skip '"' character
+                               ++it;
+
+                               char *first = it;
+                               char *last = it;
+                               while (*it)
+                               {
+                                       if ((unsigned char)*it < '\x20')
+                                       {
+                                               ERROR(first, "Control characters not allowed in strings");
+                                       }
+                                       else if (*it == '\\')
+                                       {
+                                               switch (it[1])
+                                               {
+                                               case '"':
+                                                       *last = '"';
+                                                       break;
+                                               case '\\':
+                                                       *last = '\\';
+                                                       break;
+                                               case '/':
+                                                       *last = '/';
+                                                       break;
+                                               case 'b':
+                                                       *last = '\b';
+                                                       break;
+                                               case 'f':
+                                                       *last = '\f';
+                                                       break;
+                                               case 'n':
+                                                       *last = '\n';
+                                                       ++escaped_newlines;
+                                                       break;
+                                               case 'r':
+                                                       *last = '\r';
+                                                       break;
+                                               case 't':
+                                                       *last = '\t';
+                                                       break;
+                                               case 'u':
+                                                       {
+                                                               unsigned int codepoint;
+                                                               if (hatoui(it + 2, it + 6, &codepoint) != it + 6)
+                                                               {
+                                                                       ERROR(it, "Bad unicode codepoint");
+                                                               }
+
+                                                               if (codepoint <= 0x7F)
+                                                               {
+                                                                       *last = (char)codepoint;
+                                                               }
+                                                               else if (codepoint <= 0x7FF)
+                                                               {
+                                                                       *last++ = (char)(0xC0 | (codepoint >> 6));
+                                                                       *last = (char)(0x80 | (codepoint & 0x3F));
+                                                               }
+                                                               else if (codepoint <= 0xFFFF)
+                                                               {
+                                                                       *last++ = (char)(0xE0 | (codepoint >> 12));
+                                                                       *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F));
+                                                                       *last = (char)(0x80 | (codepoint & 0x3F));
+                                                               }
+                                                       }
+                                                       it += 4;
+                                                       break;
+                                               default:
+                                                       ERROR(first, "Unrecognized escape sequence");
+                                               }
+
+                                               ++last;
+                                               it += 2;
+                                       }
+                                       else if (*it == '"')
+                                       {
+                                               *last = 0;
+                                               ++it;
+                                               break;
+                                       }
+                                       else
+                                       {
+                                               *last++ = *it++;
+                                       }
+                               }
+
+                               if (!name && top->type == JSON_OBJECT)
+                               {
+                                       // field name in object
+                                       name = first;
+                               }
+                               else
+                               {
+                                       // new string value
+                                       json_value *object = json_alloc(allocator);
+
+                                       object->name = name;
+                                       name = 0;
+
+                                       object->type = JSON_STRING;
+                                       object->string_value = first;
+
+                                       json_append(top, object);
+                               }
+                       }
+                       break;
+
+               case 'n':
+               case 't':
+               case 'f':
+                       {
+                               CHECK_TOP();
+
+                               // new null/bool value
+                               json_value *object = json_alloc(allocator);
+
+                               object->name = name;
+                               name = 0;
+
+                               // null
+                               if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l')
+                               {
+                                       object->type = JSON_NULL;
+                                       it += 4;
+                               }
+                               // true
+                               else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e')
+                               {
+                                       object->type = JSON_BOOL;
+                                       object->int_value = 1;
+                                       it += 4;
+                               }
+                               // false
+                               else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e')
+                               {
+                                       object->type = JSON_BOOL;
+                                       object->int_value = 0;
+                                       it += 5;
+                               }
+                               else
+                               {
+                                       ERROR(it, "Unknown identifier");
+                               }
+
+                               json_append(top, object);
+                       }
+                       break;
+
+               case '-':
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       {
+                               CHECK_TOP();
+
+                               // new number value
+                               json_value *object = json_alloc(allocator);
+
+                               object->name = name;
+                               name = 0;
+
+                               object->type = JSON_INT;
+
+                               char *first = it;
+                               while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}')
+                               {
+                                       if (*it == '.' || *it == 'e' || *it == 'E')
+                                       {
+                                               object->type = JSON_FLOAT;
+                                       }
+                                       ++it;
+                               }
+
+                               if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it)
+                               {
+                                       ERROR(first, "Bad integer number");
+                               }
+
+                               if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it)
+                               {
+                                       ERROR(first, "Bad float number");
+                               }
+
+                               json_append(top, object);
+                       }
+                       break;
+
+               default:
+                       ERROR(it, "Unexpected character");
+               }
+       }
+
+       if (top)
+       {
+               ERROR(it, "Not all objects/arrays have been properly closed");
+       }
+
+       return root;
+}
+
+} // end namespace mc_json