Move JSON parser to separate file

Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/c851ebb3
Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/c851ebb3
Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/c851ebb3

Branch: refs/heads/master
Commit: c851ebb38fbaae7e5039929751d098ac44ed0bfa
Parents: 73832f1
Author: Nick Wellnhofer <[email protected]>
Authored: Sat Mar 12 18:22:01 2016 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Fri Jul 15 22:09:57 2016 +0200

----------------------------------------------------------------------
 compiler/include/CFC.h   |   1 +
 compiler/src/CFCJson.c   | 237 +++++++++++++++++++++++++++++++++++++
 compiler/src/CFCJson.h   |  53 +++++++++
 compiler/src/CFCParcel.c | 268 ++++++------------------------------------
 4 files changed, 326 insertions(+), 233 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c851ebb3/compiler/include/CFC.h
----------------------------------------------------------------------
diff --git a/compiler/include/CFC.h b/compiler/include/CFC.h
index fdd34c8..9dadd7c 100644
--- a/compiler/include/CFC.h
+++ b/compiler/include/CFC.h
@@ -24,6 +24,7 @@
 #include "CFCFileSpec.h"
 #include "CFCFunction.h"
 #include "CFCHierarchy.h"
+#include "CFCJson.h"
 #include "CFCMethod.h"
 #include "CFCMemPool.h"
 #include "CFCParamList.h"

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c851ebb3/compiler/src/CFCJson.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCJson.c b/compiler/src/CFCJson.c
new file mode 100644
index 0000000..b186492
--- /dev/null
+++ b/compiler/src/CFCJson.c
@@ -0,0 +1,237 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "CFCJson.h"
+#include "CFCUtil.h"
+
+struct CFCJson {
+    int type;
+    char *string;
+    struct CFCJson **kids;
+    size_t num_kids;
+};
+
+static CFCJson*
+S_parse_json_hash(const char **json);
+
+static CFCJson*
+S_parse_json_string(const char **json);
+
+static CFCJson*
+S_parse_json_null(const char **json);
+
+static void
+S_skip_whitespace(const char **json);
+
+/*****************************************************************************
+ * The hack JSON parser coded up below is only meant to parse Clownfish parcel
+ * file content.  It is limited in its capabilities because so little is legal
+ * in .cfp and .cfx files.
+ */
+
+CFCJson*
+CFCJson_parse(const char *json) {
+    if (!json) {
+        return NULL;
+    }
+    S_skip_whitespace(&json);
+    if (*json != '{') {
+        return NULL;
+    }
+    CFCJson *parsed = S_parse_json_hash(&json);
+    S_skip_whitespace(&json);
+    if (*json != '\0') {
+        CFCJson_destroy(parsed);
+        parsed = NULL;
+    }
+    return parsed;
+}
+
+static void
+S_append_kid(CFCJson *self, CFCJson *child) {
+    size_t size = (self->num_kids + 2) * sizeof(CFCJson*);
+    self->kids = (CFCJson**)REALLOCATE(self->kids, size);
+    self->kids[self->num_kids++] = child;
+    self->kids[self->num_kids]   = NULL;
+}
+
+static CFCJson*
+S_parse_json_hash(const char **json) {
+    const char *text = *json;
+    S_skip_whitespace(&text);
+    if (*text != '{') {
+        return NULL;
+    }
+    text++;
+    CFCJson *node = (CFCJson*)CALLOCATE(1, sizeof(CFCJson));
+    node->type = CFCJSON_HASH;
+    while (1) {
+        // Parse key.
+        S_skip_whitespace(&text);
+        if (*text == '}') {
+            text++;
+            break;
+        }
+        else if (*text == '"') {
+            CFCJson *key = S_parse_json_string(&text);
+            S_skip_whitespace(&text);
+            if (!key || *text != ':') {
+                CFCJson_destroy(node);
+                return NULL;
+            }
+            text++;
+            S_append_kid(node, key);
+        }
+        else {
+            CFCJson_destroy(node);
+            return NULL;
+        }
+
+        // Parse value.
+        S_skip_whitespace(&text);
+        CFCJson *value = NULL;
+        if (*text == '"') {
+            value = S_parse_json_string(&text);
+        }
+        else if (*text == '{') {
+            value = S_parse_json_hash(&text);
+        }
+        else if (*text == 'n') {
+            value = S_parse_json_null(&text);
+        }
+        if (!value) {
+            CFCJson_destroy(node);
+            return NULL;
+        }
+        S_append_kid(node, value);
+
+        // Parse comma.
+        S_skip_whitespace(&text);
+        if (*text == ',') {
+            text++;
+        }
+        else if (*text == '}') {
+            text++;
+            break;
+        }
+        else {
+            CFCJson_destroy(node);
+            return NULL;
+        }
+    }
+
+    // Move pointer.
+    *json = text;
+
+    return node;
+}
+
+// Parse a double quoted string.  Don't allow escapes.
+static CFCJson*
+S_parse_json_string(const char **json) {
+    const char *text = *json;
+    if (*text != '\"') {
+        return NULL;
+    }
+    text++;
+    const char *start = text;
+    while (*text != '"') {
+        if (*text == '\\' || *text == '\0') {
+            return NULL;
+        }
+        text++;
+    }
+    CFCJson *node = (CFCJson*)CALLOCATE(1, sizeof(CFCJson));
+    node->type = CFCJSON_STRING;
+    node->string = CFCUtil_strndup(start, (size_t)(text - start));
+
+    // Move pointer.
+    text++;
+    *json = text;
+
+    return node;
+}
+
+// Parse a JSON null value.
+static CFCJson*
+S_parse_json_null(const char **json) {
+    static const char null_str[] = "null";
+
+    if (strncmp(*json, null_str, sizeof(null_str) - 1) != 0) {
+        return NULL;
+    }
+
+    CFCJson *node = (CFCJson*)CALLOCATE(1, sizeof(CFCJson));
+    node->type = CFCJSON_NULL;
+
+    // Move pointer.
+    *json += sizeof(null_str) - 1;
+
+    return node;
+}
+
+static void
+S_skip_whitespace(const char **json) {
+    while (CFCUtil_isspace(json[0][0])) { *json = *json + 1; }
+}
+
+void
+CFCJson_destroy(CFCJson *self) {
+    if (!self) {
+        return;
+    }
+    if (self->kids) {
+        for (size_t i = 0; self->kids[i] != NULL; i++) {
+            CFCJson_destroy(self->kids[i]);
+        }
+    }
+    FREEMEM(self->string);
+    FREEMEM(self->kids);
+    FREEMEM(self);
+}
+
+int
+CFCJson_get_type(CFCJson *self) {
+    return self->type;
+}
+
+const char*
+CFCJson_get_string(CFCJson *self) {
+    if (self->type != CFCJSON_STRING) {
+        CFCUtil_die("Not a JSON string");
+    }
+    return self->string;
+}
+
+size_t
+CFCJson_get_num_children(CFCJson *self) {
+    if (self->type != CFCJSON_HASH) {
+        CFCUtil_die("Not a JSON hash");
+    }
+    return self->num_kids;
+}
+
+CFCJson**
+CFCJson_get_children(CFCJson *self) {
+    if (self->type != CFCJSON_HASH) {
+        CFCUtil_die("Not a JSON hash");
+    }
+    return self->kids;
+}
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c851ebb3/compiler/src/CFCJson.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCJson.h b/compiler/src/CFCJson.h
new file mode 100644
index 0000000..8d75a90
--- /dev/null
+++ b/compiler/src/CFCJson.h
@@ -0,0 +1,53 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_CFCJSON
+#define H_CFCJSON
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CFCJSON_STRING 1
+#define CFCJSON_HASH   2
+#define CFCJSON_NULL   3
+
+typedef struct CFCJson CFCJson;
+
+CFCJson*
+CFCJson_parse(const char *json);
+
+void
+CFCJson_destroy(CFCJson *self);
+
+int
+CFCJson_get_type(CFCJson *self);
+
+const char*
+CFCJson_get_string(CFCJson *self);
+
+size_t
+CFCJson_get_num_children(CFCJson *self);
+
+CFCJson**
+CFCJson_get_children(CFCJson *self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* H_CFCJSON */
+

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c851ebb3/compiler/src/CFCParcel.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCParcel.c b/compiler/src/CFCParcel.c
index 5379d63..95d093e 100644
--- a/compiler/src/CFCParcel.c
+++ b/compiler/src/CFCParcel.c
@@ -28,6 +28,7 @@
 #include "CFCFileSpec.h"
 #include "CFCVersion.h"
 #include "CFCUtil.h"
+#include "CFCJson.h"
 
 struct CFCParcel {
     CFCBase base;
@@ -48,37 +49,8 @@ struct CFCParcel {
     size_t num_prereqs;
 };
 
-#define JSON_STRING 1
-#define JSON_HASH   2
-#define JSON_NULL   3
-
-typedef struct JSONNode {
-    int type;
-    char *string;
-    struct JSONNode **kids;
-    size_t num_kids;
-} JSONNode;
-
-static void
-S_set_prereqs(CFCParcel *self, JSONNode *node, const char *path);
-
-static JSONNode*
-S_parse_json_for_parcel(const char *json);
-
-static JSONNode*
-S_parse_json_hash(const char **json);
-
-static JSONNode*
-S_parse_json_string(const char **json);
-
-static JSONNode*
-S_parse_json_null(const char **json);
-
 static void
-S_skip_whitespace(const char **json);
-
-static void
-S_destroy_json(JSONNode *node);
+S_set_prereqs(CFCParcel *self, CFCJson *node, const char *path);
 
 static CFCParcel **registry = NULL;
 static size_t num_registered = 0;
@@ -240,45 +212,44 @@ CFCParcel_init(CFCParcel *self, const char *name, const 
char *nickname,
 
 static CFCParcel*
 S_new_from_json(const char *json, const char *path, CFCFileSpec *file_spec) {
-    JSONNode *parsed = S_parse_json_for_parcel(json);
+    CFCJson *parsed = CFCJson_parse(json);
     if (!parsed) {
         CFCUtil_die("Invalid JSON parcel definition in '%s'", path);
     }
-    if (parsed->type != JSON_HASH) {
+    if (CFCJson_get_type(parsed) != CFCJSON_HASH) {
         CFCUtil_die("Parcel definition must be a hash in '%s'", path);
     }
-    const char *name     = NULL;
-    const char *nickname = NULL;
-    CFCVersion *version  = NULL;
-    JSONNode   *prereqs  = NULL;
-    for (size_t i = 0, max = parsed->num_kids; i < max; i += 2) {
-        JSONNode *key   = parsed->kids[i];
-        JSONNode *value = parsed->kids[i + 1];
-        if (key->type != JSON_STRING) {
-            CFCUtil_die("JSON parsing error (filepath '%s')", path);
-        }
-        if (strcmp(key->string, "name") == 0) {
-            if (value->type != JSON_STRING) {
+    const char  *name     = NULL;
+    const char  *nickname = NULL;
+    CFCVersion  *version  = NULL;
+    CFCJson     *prereqs  = NULL;
+    CFCJson    **children = CFCJson_get_children(parsed);
+    for (size_t i = 0; children[i]; i += 2) {
+        const char *key = CFCJson_get_string(children[i]);
+        CFCJson *value = children[i + 1];
+        int value_type = CFCJson_get_type(value);
+        if (strcmp(key, "name") == 0) {
+            if (value_type != CFCJSON_STRING) {
                 CFCUtil_die("'name' must be a string (filepath %s)", path);
             }
-            name = value->string;
+            name = CFCJson_get_string(value);
         }
-        else if (strcmp(key->string, "nickname") == 0) {
-            if (value->type != JSON_STRING) {
+        else if (strcmp(key, "nickname") == 0) {
+            if (value_type != CFCJSON_STRING) {
                 CFCUtil_die("'nickname' must be a string (filepath %s)",
                             path);
             }
-            nickname = value->string;
+            nickname = CFCJson_get_string(value);
         }
-        else if (strcmp(key->string, "version") == 0) {
-            if (value->type != JSON_STRING) {
+        else if (strcmp(key, "version") == 0) {
+            if (value_type != CFCJSON_STRING) {
                 CFCUtil_die("'version' must be a string (filepath %s)",
                             path);
             }
-            version = CFCVersion_new(value->string);
+            version = CFCVersion_new(CFCJson_get_string(value));
         }
-        else if (strcmp(key->string, "prerequisites") == 0) {
-            if (value->type != JSON_HASH) {
+        else if (strcmp(key, "prerequisites") == 0) {
+            if (value_type != CFCJSON_HASH) {
                 CFCUtil_die("'prerequisites' must be a hash (filepath %s)",
                             path);
             }
@@ -286,7 +257,7 @@ S_new_from_json(const char *json, const char *path, 
CFCFileSpec *file_spec) {
         }
         else {
             CFCUtil_die("Unrecognized key: '%s' (filepath '%s')",
-                        key->string, path);
+                        key, path);
         }
     }
     if (!name) {
@@ -301,29 +272,27 @@ S_new_from_json(const char *json, const char *path, 
CFCFileSpec *file_spec) {
     }
     CFCBase_decref((CFCBase*)version);
 
-    S_destroy_json(parsed);
+    CFCJson_destroy(parsed);
     return self;
 }
 
 static void
-S_set_prereqs(CFCParcel *self, JSONNode *node, const char *path) {
-    size_t num_prereqs = node->num_kids / 2;
+S_set_prereqs(CFCParcel *self, CFCJson *node, const char *path) {
+    size_t num_prereqs = CFCJson_get_num_children(node) / 2;
+    CFCJson **children = CFCJson_get_children(node);
     CFCPrereq **prereqs
         = (CFCPrereq**)MALLOCATE((num_prereqs + 1) * sizeof(CFCPrereq*));
 
     for (size_t i = 0; i < num_prereqs; ++i) {
-        JSONNode *key = node->kids[2*i];
-        if (key->type != JSON_STRING) {
-            CFCUtil_die("Prereq key must be a string (filepath '%s')", path);
-        }
-        const char *name = key->string;
+        const char *name = CFCJson_get_string(children[2*i]);
 
-        JSONNode *value = node->kids[2*i+1];
+        CFCJson *value = children[2*i+1];
+        int value_type = CFCJson_get_type(value);
         CFCVersion *version = NULL;
-        if (value->type == JSON_STRING) {
-            version = CFCVersion_new(value->string);
+        if (value_type == CFCJSON_STRING) {
+            version = CFCVersion_new(CFCJson_get_string(value));
         }
-        else if (value->type != JSON_NULL) {
+        else if (value_type != CFCJSON_NULL) {
             CFCUtil_die("Invalid prereq value (filepath '%s')", path);
         }
 
@@ -655,170 +624,3 @@ CFCPrereq_get_version(CFCPrereq *self) {
     return self->version;
 }
 
-/*****************************************************************************
- * The hack JSON parser coded up below is only meant to parse Clownfish parcel
- * file content.  It is limited in its capabilities because so little is legal
- * in a .cfp file.
- */
-
-static JSONNode*
-S_parse_json_for_parcel(const char *json) {
-    if (!json) {
-        return NULL;
-    }
-    S_skip_whitespace(&json);
-    if (*json != '{') {
-        return NULL;
-    }
-    JSONNode *parsed = S_parse_json_hash(&json);
-    S_skip_whitespace(&json);
-    if (*json != '\0') {
-        S_destroy_json(parsed);
-        parsed = NULL;
-    }
-    return parsed;
-}
-
-static void
-S_append_kid(JSONNode *node, JSONNode *child) {
-    size_t size = (node->num_kids + 2) * sizeof(JSONNode*);
-    node->kids = (JSONNode**)realloc(node->kids, size);
-    node->kids[node->num_kids++] = child;
-    node->kids[node->num_kids]   = NULL;
-}
-
-static JSONNode*
-S_parse_json_hash(const char **json) {
-    const char *text = *json;
-    S_skip_whitespace(&text);
-    if (*text != '{') {
-        return NULL;
-    }
-    text++;
-    JSONNode *node = (JSONNode*)calloc(1, sizeof(JSONNode));
-    node->type = JSON_HASH;
-    while (1) {
-        // Parse key.
-        S_skip_whitespace(&text);
-        if (*text == '}') {
-            text++;
-            break;
-        }
-        else if (*text == '"') {
-            JSONNode *key = S_parse_json_string(&text);
-            S_skip_whitespace(&text);
-            if (!key || *text != ':') {
-                S_destroy_json(node);
-                return NULL;
-            }
-            text++;
-            S_append_kid(node, key);
-        }
-        else {
-            S_destroy_json(node);
-            return NULL;
-        }
-
-        // Parse value.
-        S_skip_whitespace(&text);
-        JSONNode *value = NULL;
-        if (*text == '"') {
-            value = S_parse_json_string(&text);
-        }
-        else if (*text == '{') {
-            value = S_parse_json_hash(&text);
-        }
-        else if (*text == 'n') {
-            value = S_parse_json_null(&text);
-        }
-        if (!value) {
-            S_destroy_json(node);
-            return NULL;
-        }
-        S_append_kid(node, value);
-
-        // Parse comma.
-        S_skip_whitespace(&text);
-        if (*text == ',') {
-            text++;
-        }
-        else if (*text == '}') {
-            text++;
-            break;
-        }
-        else {
-            S_destroy_json(node);
-            return NULL;
-        }
-    }
-
-    // Move pointer.
-    *json = text;
-
-    return node;
-}
-
-// Parse a double quoted string.  Don't allow escapes.
-static JSONNode*
-S_parse_json_string(const char **json) {
-    const char *text = *json; 
-    if (*text != '\"') {
-        return NULL;
-    }
-    text++;
-    const char *start = text;
-    while (*text != '"') {
-        if (*text == '\\' || *text == '\0') {
-            return NULL;
-        }
-        text++;
-    }
-    JSONNode *node = (JSONNode*)calloc(1, sizeof(JSONNode));
-    node->type = JSON_STRING;
-    node->string = CFCUtil_strndup(start, (size_t)(text - start));
-
-    // Move pointer.
-    text++;
-    *json = text;
-
-    return node;
-}
-
-// Parse a JSON null value.
-static JSONNode*
-S_parse_json_null(const char **json) {
-    static const char null_str[] = "null";
-
-    if (strncmp(*json, null_str, sizeof(null_str) - 1) != 0) {
-        return NULL;
-    }
-
-    JSONNode *node = (JSONNode*)calloc(1, sizeof(JSONNode));
-    node->type = JSON_NULL;
-
-    // Move pointer.
-    *json += sizeof(null_str) - 1;
-
-    return node;
-}
-
-static void
-S_skip_whitespace(const char **json) {
-    while (CFCUtil_isspace(json[0][0])) { *json = *json + 1; }
-}
-
-static void
-S_destroy_json(JSONNode *node) {
-    if (!node) {
-        return;
-    }
-    if (node->kids) {
-        for (size_t i = 0; node->kids[i] != NULL; i++) {
-            S_destroy_json(node->kids[i]);
-        }
-    }
-    free(node->string);
-    free(node->kids);
-    free(node);
-}
-

Reply via email to