소스 검색

Finish the library implementation

rexim 4 년 전
부모
커밋
5229e51304
4개의 변경된 파일262개의 추가작업 그리고 111개의 파일을 삭제
  1. 1 1
      Makefile
  2. 91 0
      README.md
  3. 32 38
      example.c
  4. 138 72
      jim.h

+ 1 - 1
Makefile

@@ -1,2 +1,2 @@
 example: example.c jim.h
-	cc -Wall -Wextra -Wno-unused-parameter -std=c99 -pedantic -o example example.c 
+	cc -Wall -Wextra -Wswitch-enum -std=c99 -pedantic -o example example.c 

+ 91 - 0
README.md

@@ -1,3 +1,94 @@
 # JIM
 
 Immediate Mode JSON Serialization Library in C
+
+## Example
+
+[`example.c`](./example.c):
+
+```c
+#include <stdio.h>
+
+#define JIM_IMPLEMENTATION
+#include "./jim.h"
+
+int main()
+{
+    Jim jim = {
+        .sink = stdout,
+        .write = (Jim_Write) fwrite,
+    };
+
+    jim_object_begin(&jim);
+        jim_member_key(&jim, "null", NULL);
+        jim_null(&jim);
+
+        jim_member_key(&jim, "bool", NULL);
+        jim_array_begin(&jim);
+            jim_bool(&jim, 0);
+            jim_bool(&jim, 1);
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "integers", NULL);
+        jim_array_begin(&jim);
+            for (int i = -3; i <= 3; ++i) {
+                jim_integer(&jim, i);
+            }
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "floats", NULL);
+        jim_array_begin(&jim);
+            jim_float(&jim, 3.1415, 4);
+            jim_float(&jim, 2.71828, 5);
+            jim_float(&jim, 1.6180, 4);
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "string", NULL);
+        jim_array_begin(&jim);
+            jim_string(&jim, "Hello\tWorld\n", NULL);
+            unsigned int size = 4;
+            jim_string(&jim, "\0\0\0\0", &size);
+        jim_array_end(&jim);
+    jim_object_end(&jim);
+
+    if (jim.error != JIM_OK) {
+        fprintf(stderr, "ERROR: could not serialize json properly: %s\n",
+                jim_error_string(jim.error));
+        return -1;
+    }
+
+    return 0;
+}
+```
+
+### Output
+
+```console
+$ cc -o example example.c
+$ ./example | jq .
+{
+  "null": null,
+  "bool": [
+    false,
+    true
+  ],
+  "integers": [
+    -3,
+    -2,
+    -1,
+    0,
+    1,
+    2,
+    3
+  ],
+  "floats": [
+    3.1415,
+    2.71828,
+    1.618
+  ],
+  "string": [
+    "Hello\tWorld\n",
+    "\u0000\u0000\u0000\u0000"
+  ]
+}
+```

+ 32 - 38
example.c

@@ -7,50 +7,44 @@ int main()
 {
     Jim jim = {
         .sink = stdout,
-        .write = fwrite,
+        .write = (Jim_Write) fwrite,
     };
 
-    jim_begin(&jim);
+    jim_object_begin(&jim);
+        jim_member_key(&jim, "null", NULL);
+        jim_null(&jim);
+
+        jim_member_key(&jim, "bool", NULL);
+        jim_array_begin(&jim);
+            jim_bool(&jim, 0);
+            jim_bool(&jim, 1);
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "integers", NULL);
+        jim_array_begin(&jim);
+            for (int i = -3; i <= 3; ++i) {
+                jim_integer(&jim, i);
+            }
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "floats", NULL);
+        jim_array_begin(&jim);
+            jim_float(&jim, 3.1415, 4);
+            jim_float(&jim, 2.71828, 5);
+            jim_float(&jim, 1.6180, 4);
+        jim_array_end(&jim);
+
+        jim_member_key(&jim, "string", NULL);
         jim_array_begin(&jim);
-            jim_element_begin(&jim);
-                jim_array_begin(&jim);
-                    jim_element_begin(&jim);
-                    jim_null(&jim);
-                    jim_element_end(&jim);
-                    for (int i = 0; i < 5; ++i) {
-                        jim_element_begin(&jim);
-                        jim_bool(&jim, i);
-                        jim_element_end(&jim);
-                    }
-                jim_array_end(&jim);
-            jim_element_end(&jim);
-
-            jim_element_begin(&jim);
-                jim_bool(&jim, 0);
-            jim_element_end(&jim);
-
-            jim_element_begin(&jim);
-                jim_bool(&jim, 1);
-            jim_element_end(&jim);
-
-            jim_element_begin(&jim);
-                size_t size = 5;
-                jim_string(&jim, "\0\0\0\0\0", &size);
-            jim_element_end(&jim);
-            
-            jim_element_begin(&jim);
-                jim_object_begin(&jim);
-                    jim_member_begin(&jim);
-                        jim_member_key(&jim, "hello", NULL);
-                        jim_bool(&jim, 0);
-                    jim_member_end(&jim);
-                jim_object_end(&jim);
-            jim_element_end(&jim);
+            jim_string(&jim, "Hello\tWorld\n", NULL);
+            unsigned int size = 4;
+            jim_string(&jim, "\0\0\0\0", &size);
         jim_array_end(&jim);
-    jim_end(&jim);
+    jim_object_end(&jim);
 
     if (jim.error != JIM_OK) {
-        fprintf(stderr, "ERROR: could not serialize json properly: %s\n", jim_error_string(jim.error));
+        fprintf(stderr, "ERROR: could not serialize json properly: %s\n",
+                jim_error_string(jim.error));
         return -1;
     }
 

+ 138 - 72
jim.h

@@ -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);
     }
 }