lucy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mar...@apache.org
Subject [lucy-commits] svn commit: r1337367 - in /lucy/trunk/clownfish: perl/lib/Clownfish/CFC.pm perl/lib/Clownfish/CFC.xs perl/t/403-parcel.t src/CFCParcel.c src/CFCParcel.h
Date Fri, 11 May 2012 20:44:44 GMT
Author: marvin
Date: Fri May 11 20:44:44 2012
New Revision: 1337367

URL: http://svn.apache.org/viewvc?rev=1337367&view=rev
Log:
Add CFCParcel_new_from_json().

Modified:
    lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm
    lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs
    lucy/trunk/clownfish/perl/t/403-parcel.t
    lucy/trunk/clownfish/src/CFCParcel.c
    lucy/trunk/clownfish/src/CFCParcel.h

Modified: lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm
URL: http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm (original)
+++ lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm Fri May 11 20:44:44 2012
@@ -334,6 +334,29 @@ BEGIN { XSLoader::load( 'Clownfish::CFC'
         return _singleton( @args{qw( name cnick )} );
     }
 
+    our %new_PARAMS = (
+        name  => undef,
+        cnick => undef,
+    );
+
+    sub new {
+        my ( $either, %args ) = @_;
+        verify_args( \%new_PARAMS, %args ) or confess $@;
+        confess "no subclassing allowed" unless $either eq __PACKAGE__;
+        return _new( @args{qw( name cnick )} );
+    }
+
+    our %new_from_json_PARAMS = (
+        json => undef,
+    );
+
+    sub new_from_json {
+        my ( $either, %args ) = @_;
+        verify_args( \%new_from_json_PARAMS, %args ) or confess $@;
+        confess "no subclassing allowed" unless $either eq __PACKAGE__;
+        return _new_from_json( $args{json} );
+    }
+
 #    $parcel = Clownfish::CFC::Model::Parcel->aquire($parcel_name_or_parcel_object);
 #
 # Aquire a parcel one way or another.  If the supplied argument is a

Modified: lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs
URL: http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs (original)
+++ lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs Fri May 11 20:44:44 2012
@@ -1061,6 +1061,27 @@ PPCODE:
 MODULE = Clownfish::CFC   PACKAGE = Clownfish::CFC::Model::Parcel
 
 SV*
+_new(name_sv, cnick_sv)
+    SV *name_sv;
+    SV *cnick_sv;
+CODE:
+    const char *name  = SvOK(name_sv)  ? SvPV_nolen(name_sv)  : NULL;
+    const char *cnick = SvOK(cnick_sv) ? SvPV_nolen(cnick_sv) : NULL;
+    CFCParcel *self = CFCParcel_new(name, cnick);
+    RETVAL = S_cfcbase_to_perlref(self);
+    CFCBase_decref((CFCBase*)self);
+OUTPUT: RETVAL
+
+SV*
+_new_from_json(json)
+    const char *json;
+CODE:
+    CFCParcel *self = CFCParcel_new_from_json(json);
+    RETVAL = S_cfcbase_to_perlref(self);
+    CFCBase_decref((CFCBase*)self);
+OUTPUT: RETVAL
+
+SV*
 _singleton(name_sv, cnick_sv)
     SV *name_sv;
     SV *cnick_sv;

Modified: lucy/trunk/clownfish/perl/t/403-parcel.t
URL: http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/t/403-parcel.t?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/t/403-parcel.t (original)
+++ lucy/trunk/clownfish/perl/t/403-parcel.t Fri May 11 20:44:44 2012
@@ -16,10 +16,24 @@
 use strict;
 use warnings;
 
-use Test::More tests => 7;
+use Test::More tests => 9;
 
 BEGIN { use_ok('Clownfish::CFC::Model::Parcel') }
 
+isa_ok(
+    Clownfish::CFC::Model::Parcel->new( name => "Foo" ),
+    "Clownfish::CFC::Model::Parcel",
+    "new"
+);
+
+isa_ok(
+    Clownfish::CFC::Model::Parcel->new_from_json(
+        json => ' { "name": "Crustacean", "nickname": "Crust" } ',
+    ),
+    "Clownfish::CFC::Model::Parcel",
+    "new_from_json"
+);
+
 # Register singleton.
 Clownfish::CFC::Model::Parcel->singleton(
     name  => 'Crustacean',

Modified: lucy/trunk/clownfish/src/CFCParcel.c
URL: http://svn.apache.org/viewvc/lucy/trunk/clownfish/src/CFCParcel.c?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/src/CFCParcel.c (original)
+++ lucy/trunk/clownfish/src/CFCParcel.c Fri May 11 20:44:44 2012
@@ -16,6 +16,7 @@
 
 #include <ctype.h>
 #include <string.h>
+#include <stdlib.h>
 
 #ifndef true
   #define true 1
@@ -36,6 +37,31 @@ struct CFCParcel {
     char *PREFIX;
 };
 
+#define JSON_STRING 1
+#define JSON_HASH   2
+
+typedef struct JSONNode {
+    int type;
+    char *string;
+    struct JSONNode **kids;
+    size_t num_kids;
+} JSONNode;
+
+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 void
+S_skip_whitespace(const char **json);
+
+static void
+S_destroy_json(JSONNode *node);
+
 #define MAX_PARCELS 100
 static CFCParcel *registry[MAX_PARCELS + 1];
 static int first_time = true;
@@ -155,6 +181,56 @@ CFCParcel_init(CFCParcel *self, const ch
     return self;
 }
 
+CFCParcel*
+CFCParcel_new_from_json(const char *json) {
+    JSONNode *parsed = S_parse_json_for_parcel(json);
+    if (!parsed) {
+        CFCUtil_die("Invalid JSON parcel definition");
+    }
+    const char *name     = NULL;
+    const char *nickname = 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");
+        }
+        if (strcmp(key->string, "name") == 0) {
+            if (value->type != JSON_STRING) {
+                CFCUtil_die("'name' must be a string");
+            }
+            name = value->string;
+        }
+        else if (strcmp(key->string, "nickname") == 0) {
+            if (value->type != JSON_STRING) {
+                CFCUtil_die("'name' must be a string");
+            }
+            nickname = value->string;
+        }
+    }
+    if (!name) {
+        CFCUtil_die("Missing required key 'name'");
+    }
+    CFCParcel *self = CFCParcel_new(name, nickname);
+
+    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 (strcmp(key->string, "name") == 0
+            || strcmp(key->string, "nickname") == 0
+           ) {
+            ;
+        }
+        else {
+            CFCUtil_die("Unrecognized key in parcel definition: '%s'",
+                        key->string);
+        }
+    }
+
+    S_destroy_json(parsed);
+    return self;
+}
+
 void
 CFCParcel_destroy(CFCParcel *self) {
     FREEMEM(self->name);
@@ -213,3 +289,149 @@ CFCParcel_get_PREFIX(CFCParcel *self) {
     return self->PREFIX;
 }
 
+/*****************************************************************************
+ * 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);
+        }
+        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, text - start);
+
+    // Move pointer.
+    text++;
+    *json = text;
+
+    return node;
+}
+
+static void
+S_skip_whitespace(const char **json) {
+    while (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);
+}
+

Modified: lucy/trunk/clownfish/src/CFCParcel.h
URL: http://svn.apache.org/viewvc/lucy/trunk/clownfish/src/CFCParcel.h?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/src/CFCParcel.h (original)
+++ lucy/trunk/clownfish/src/CFCParcel.h Fri May 11 20:44:44 2012
@@ -58,6 +58,9 @@ CFCParcel*
 CFCParcel_new(const char *name, const char *cnick);
 
 CFCParcel*
+CFCParcel_new_from_json(const char *json);
+
+CFCParcel*
 CFCParcel_init(CFCParcel *self, const char *name, const char *cnick);
 
 void



Mime
View raw message