|
@@ -1,9 +1,6 @@
|
|
|
#ifndef JIM_H_
|
|
|
#define JIM_H_
|
|
|
|
|
|
-#include <assert.h>
|
|
|
-#include <string.h>
|
|
|
-
|
|
|
#ifndef JIM_STACK_CAPACITY
|
|
|
#define JIM_STACK_CAPACITY 1024
|
|
|
#endif // JIM_STACK_CAPACITY
|
|
@@ -15,50 +12,62 @@ typedef enum {
|
|
|
JIM_OK = 0,
|
|
|
JIM_WRITE_ERROR,
|
|
|
JIM_STACK_OVERFLOW,
|
|
|
- JIM_STACK_UNDERFLOW
|
|
|
+ JIM_STACK_UNDERFLOW,
|
|
|
+ JIM_OUT_OF_SCOPE_KEY,
|
|
|
+ JIM_DOUBLE_KEY
|
|
|
} Jim_Error;
|
|
|
|
|
|
const char *jim_error_string(Jim_Error error);
|
|
|
|
|
|
+typedef struct {
|
|
|
+ int tail;
|
|
|
+ int key;
|
|
|
+} Jim_Scope;
|
|
|
+
|
|
|
typedef struct {
|
|
|
Jim_Sink sink;
|
|
|
Jim_Write write;
|
|
|
Jim_Error error;
|
|
|
- size_t stack[JIM_STACK_CAPACITY];
|
|
|
+ Jim_Scope stack[JIM_STACK_CAPACITY];
|
|
|
size_t stack_size;
|
|
|
} Jim;
|
|
|
|
|
|
-void jim_begin(Jim *jim);
|
|
|
-void jim_end(Jim *jim);
|
|
|
-
|
|
|
void jim_null(Jim *jim);
|
|
|
void jim_bool(Jim *jim, int boolean);
|
|
|
void jim_integer(Jim *jim, long long int x);
|
|
|
-void jim_float(Jim *jim, double x);
|
|
|
+void jim_float(Jim *jim, double x, int precision);
|
|
|
void jim_string(Jim *jim, const char *str, const unsigned int *size);
|
|
|
|
|
|
-void jim_array_begin(Jim *jim);
|
|
|
void jim_element_begin(Jim *jim);
|
|
|
void jim_element_end(Jim *jim);
|
|
|
+
|
|
|
+void jim_array_begin(Jim *jim);
|
|
|
void jim_array_end(Jim *jim);
|
|
|
|
|
|
void jim_object_begin(Jim *jim);
|
|
|
-void jim_member_begin(Jim *jim);
|
|
|
void jim_member_key(Jim *jim, const char *str, const unsigned int *size);
|
|
|
-void jim_member_value_begin(Jim *jim);
|
|
|
-void jim_member_value_end(Jim *jim);
|
|
|
-void jim_member_end(Jim *jim);
|
|
|
void jim_object_end(Jim *jim);
|
|
|
|
|
|
#endif // JIM_H_
|
|
|
|
|
|
#ifdef JIM_IMPLEMENTATION
|
|
|
|
|
|
+static size_t jim_strlen(const char *s)
|
|
|
+{
|
|
|
+ size_t count = 0;
|
|
|
+ while (*(s + count)) {
|
|
|
+ count += 1;
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
static void jim_stack_push(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
if (jim->stack_size < JIM_STACK_CAPACITY) {
|
|
|
- jim->stack[jim->stack_size++] = 0;
|
|
|
+ jim->stack[jim->stack_size].tail = 0;
|
|
|
+ jim->stack[jim->stack_size].key = 0;
|
|
|
+ jim->stack_size += 1;
|
|
|
} else {
|
|
|
jim->error = JIM_STACK_OVERFLOW;
|
|
|
}
|
|
@@ -76,13 +85,11 @@ static void jim_stack_pop(Jim *jim)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static size_t *jim_stack_top(Jim *jim)
|
|
|
+static Jim_Scope *jim_stack_top(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
if (jim->stack_size > 0) {
|
|
|
return &jim->stack[jim->stack_size - 1];
|
|
|
- } else {
|
|
|
- jim->error = JIM_STACK_UNDERFLOW;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -101,7 +108,7 @@ static void jim_write(Jim *jim, const char *buffer, size_t size)
|
|
|
static void jim_write_cstr(Jim *jim, const char *cstr)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
- jim_write(jim, cstr, strlen(cstr));
|
|
|
+ jim_write(jim, cstr, jim_strlen(cstr));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -118,8 +125,30 @@ static int jim_get_utf8_char_len(unsigned char ch)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void jim_element_begin(Jim *jim)
|
|
|
+{
|
|
|
+ if (jim->error == JIM_OK) {
|
|
|
+ Jim_Scope *scope = jim_stack_top(jim);
|
|
|
+ if (scope && scope->tail && !scope->key) {
|
|
|
+ jim_write_cstr(jim, ",");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void jim_element_end(Jim *jim)
|
|
|
+{
|
|
|
+ if (jim->error == JIM_OK) {
|
|
|
+ Jim_Scope *scope = jim_stack_top(jim);
|
|
|
+ if (scope) {
|
|
|
+ scope->tail = 1;
|
|
|
+ scope->key = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const char *jim_error_string(Jim_Error error)
|
|
|
{
|
|
|
+ // TODO: error strings are not particularly useful
|
|
|
switch (error) {
|
|
|
case JIM_OK:
|
|
|
return "There is no error. The developer of this software just had a case of \"Task failed successfully\" https://i.imgur.com/Bdb3rkq.jpg - Please contact the developer and tell them that they are very lazy for not checking errors properly.";
|
|
@@ -129,56 +158,107 @@ const char *jim_error_string(Jim_Error error)
|
|
|
return "Stack Overflow";
|
|
|
case JIM_STACK_UNDERFLOW:
|
|
|
return "Stack Underflow";
|
|
|
+ case JIM_OUT_OF_SCOPE_KEY:
|
|
|
+ return "Out of Scope key";
|
|
|
+ case JIM_DOUBLE_KEY:
|
|
|
+ return "Tried to set the member key twice";
|
|
|
default:
|
|
|
return NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void jim_begin(Jim *jim)
|
|
|
-{
|
|
|
- (void) jim;
|
|
|
-}
|
|
|
-
|
|
|
-void jim_end(Jim *jim)
|
|
|
-{
|
|
|
- (void) jim;
|
|
|
-}
|
|
|
-
|
|
|
void jim_null(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
+ jim_element_begin(jim);
|
|
|
jim_write_cstr(jim, "null");
|
|
|
+ jim_element_end(jim);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void jim_bool(Jim *jim, int boolean)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
+ jim_element_begin(jim);
|
|
|
if (boolean) {
|
|
|
jim_write_cstr(jim, "true");
|
|
|
} else {
|
|
|
jim_write_cstr(jim, "false");
|
|
|
}
|
|
|
+ jim_element_end(jim);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void jim_integer_no_element(Jim *jim, long long int x)
|
|
|
+{
|
|
|
+ if (jim->error == JIM_OK) {
|
|
|
+ if (x < 0) {
|
|
|
+ jim_write_cstr(jim, "-");
|
|
|
+ x = -x;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (x == 0) {
|
|
|
+ jim_write_cstr(jim, "0");
|
|
|
+ } else {
|
|
|
+ char buffer[64];
|
|
|
+ size_t count = 0;
|
|
|
+
|
|
|
+ while (x > 0) {
|
|
|
+ buffer[count++] = (x % 10) + '0';
|
|
|
+ x /= 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (size_t i = 0; i < count / 2; ++i) {
|
|
|
+ char t = buffer[i];
|
|
|
+ buffer[i] = buffer[count - i - 1];
|
|
|
+ buffer[count - i - 1] = t;
|
|
|
+ }
|
|
|
+
|
|
|
+ jim_write(jim, buffer, count);
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void jim_integer(Jim *jim, long long int x)
|
|
|
{
|
|
|
- assert(0 && "TODO: jim_integer is not implemented yet");
|
|
|
+ if (jim->error == JIM_OK) {
|
|
|
+ jim_element_begin(jim);
|
|
|
+ jim_integer_no_element(jim, x);
|
|
|
+ jim_element_end(jim);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-void jim_float(Jim *jim, double x)
|
|
|
+void jim_float(Jim *jim, double x, int precision)
|
|
|
{
|
|
|
- assert(0 && "TODO: jim_float is not implemented yet");
|
|
|
+ // TODO: jim_float does not support NaN and Inf-s
|
|
|
+ if (jim->error == JIM_OK) {
|
|
|
+ jim_element_begin(jim);
|
|
|
+
|
|
|
+ jim_integer_no_element(jim, (long long int) x);
|
|
|
+ x -= (double) (long long int) x;
|
|
|
+ while (precision-- > 0) {
|
|
|
+ x *= 10.0;
|
|
|
+ }
|
|
|
+ jim_write_cstr(jim, ".");
|
|
|
+
|
|
|
+ long long int y = (long long int) x;
|
|
|
+ if (y < 0) {
|
|
|
+ y = -y;
|
|
|
+ }
|
|
|
+ jim_integer_no_element(jim, y);
|
|
|
+
|
|
|
+ jim_element_end(jim);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-void jim_string(Jim *jim, const char *str, const unsigned int *size)
|
|
|
+static void jim_string_no_element(Jim *jim, const char *str, const unsigned int *size)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
const char *hex_digits = "0123456789abcdef";
|
|
|
const char *specials = "btnvfr";
|
|
|
const char *p = str;
|
|
|
- size_t len = size ? *size : strlen(str);
|
|
|
+ size_t len = size ? *size : jim_strlen(str);
|
|
|
|
|
|
jim_write_cstr(jim, "\"");
|
|
|
size_t cl;
|
|
@@ -206,74 +286,59 @@ void jim_string(Jim *jim, const char *str, const unsigned int *size)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void jim_array_begin(Jim *jim)
|
|
|
+void jim_string(Jim *jim, const char *str, const unsigned int *size)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
- jim_write_cstr(jim, "[");
|
|
|
- jim_stack_push(jim);
|
|
|
+ jim_element_begin(jim);
|
|
|
+ jim_string_no_element(jim, str, size);
|
|
|
+ jim_element_end(jim);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void jim_element_begin(Jim *jim)
|
|
|
+void jim_array_begin(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
- size_t *top = jim_stack_top(jim);
|
|
|
- if (top && *top > 0) {
|
|
|
- jim_write_cstr(jim, ",");
|
|
|
- }
|
|
|
+ jim_element_begin(jim);
|
|
|
+ jim_write_cstr(jim, "[");
|
|
|
+ jim_stack_push(jim);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void jim_element_end(Jim *jim)
|
|
|
-{
|
|
|
- if (jim->error == JIM_OK) {
|
|
|
- size_t *top = jim_stack_top(jim);
|
|
|
- if (top) {
|
|
|
- *top += 1;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
void jim_array_end(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
jim_write_cstr(jim, "]");
|
|
|
jim_stack_pop(jim);
|
|
|
+ jim_element_end(jim);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void jim_object_begin(Jim *jim)
|
|
|
{
|
|
|
if (jim->error == JIM_OK) {
|
|
|
+ jim_element_begin(jim);
|
|
|
jim_write_cstr(jim, "{");
|
|
|
jim_stack_push(jim);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void jim_member_begin(Jim *jim)
|
|
|
-{
|
|
|
- if (jim->error == JIM_OK) {
|
|
|
- size_t *top = jim_stack_top(jim);
|
|
|
- if (top && *top > 0) {
|
|
|
- jim_write_cstr(jim, ",");
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
void jim_member_key(Jim *jim, const char *str, const unsigned int *size)
|
|
|
{
|
|
|
+ // TODO: jim_member_key does not throw an error when used inside of array scope instead of object scope
|
|
|
if (jim->error == JIM_OK) {
|
|
|
- jim_string(jim, str, size);
|
|
|
- jim_write_cstr(jim, ":");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void jim_member_end(Jim *jim)
|
|
|
-{
|
|
|
- if (jim->error == JIM_OK) {
|
|
|
- size_t *top = jim_stack_top(jim);
|
|
|
- if (top) {
|
|
|
- *top += 1;
|
|
|
+ jim_element_begin(jim);
|
|
|
+ Jim_Scope *scope = jim_stack_top(jim);
|
|
|
+ if (scope) {
|
|
|
+ if (!scope->key) {
|
|
|
+ jim_string_no_element(jim, str, size);
|
|
|
+ jim_write_cstr(jim, ":");
|
|
|
+ scope->key = 1;
|
|
|
+ } else {
|
|
|
+ jim->error = JIM_DOUBLE_KEY;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ jim->error = JIM_OUT_OF_SCOPE_KEY;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -283,6 +348,7 @@ void jim_object_end(Jim *jim)
|
|
|
if (jim->error == JIM_OK) {
|
|
|
jim_write_cstr(jim, "}");
|
|
|
jim_stack_pop(jim);
|
|
|
+ jim_element_end(jim);
|
|
|
}
|
|
|
}
|
|
|
|