Add CFCParcel_class functions

Search classes in a parcel by name or struct symbol.

In most cases, it's important to only search classes in a parcel or
its direct prerequisites. Otherwise, classes from random other parcels
could be picked up.


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

Branch: refs/heads/master
Commit: c502223ab7e3c70529352afb42973c9d77e88805
Parents: 8971263
Author: Nick Wellnhofer <wellnho...@aevum.de>
Authored: Mon Feb 27 18:53:58 2017 +0100
Committer: Nick Wellnhofer <wellnho...@aevum.de>
Committed: Thu Mar 2 20:08:03 2017 +0100

----------------------------------------------------------------------
 compiler/perl/lib/Clownfish/CFC.xs | 25 ++++++++--
 compiler/perl/t/401-class.t        |  6 +--
 compiler/perl/t/403-parcel.t       | 14 +++---
 compiler/src/CFCGoClass.c          |  6 +--
 compiler/src/CFCParcel.c           | 88 ++++++++++++++++++++++++++-------
 compiler/src/CFCParcel.h           | 25 +++++++---
 compiler/src/CFCPerlClass.c        | 33 +++++++------
 compiler/src/CFCTestClass.c        |  4 +-
 compiler/src/CFCTestParcel.c       | 14 +++---
 compiler/src/CFCType.c             | 12 ++---
 10 files changed, 156 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/perl/lib/Clownfish/CFC.xs
----------------------------------------------------------------------
diff --git a/compiler/perl/lib/Clownfish/CFC.xs 
b/compiler/perl/lib/Clownfish/CFC.xs
index 1153bfd..8abe174 100644
--- a/compiler/perl/lib/Clownfish/CFC.xs
+++ b/compiler/perl/lib/Clownfish/CFC.xs
@@ -1150,12 +1150,27 @@ PPCODE:
     CFCParcel_add_class(self, klass);
 
 SV*
-lookup_struct_sym(self, struct_sym)
-    CFCParcel  *self;
-    const char *struct_sym;
+_fetch_class(self, string)
+    CFCParcel *self;
+    const char *string;
+ALIAS:
+    class              = 1
+    class_by_short_sym = 2
+    class_by_full_sym  = 3
 CODE:
-    CFCParcel *parcel = CFCParcel_lookup_struct_sym(self, struct_sym);
-    RETVAL = S_cfcbase_to_perlref(parcel);
+    CFCClass *klass = NULL;
+    switch (ix) {
+        case 1:
+            klass = CFCParcel_class(self, string);
+            break;
+        case 2:
+            klass = CFCParcel_class_by_short_sym(self, string);
+            break;
+        case 3:
+            klass = CFCParcel_class_by_full_sym(self, string);
+            break;
+    }
+    RETVAL = S_cfcbase_to_perlref(klass);
 OUTPUT: RETVAL
 
 void

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/perl/t/401-class.t
----------------------------------------------------------------------
diff --git a/compiler/perl/t/401-class.t b/compiler/perl/t/401-class.t
index c51cd33..cafb70c 100644
--- a/compiler/perl/t/401-class.t
+++ b/compiler/perl/t/401-class.t
@@ -21,7 +21,7 @@ use Clownfish::CFC::Model::Class;
 use Clownfish::CFC::Parser;
 
 my $parser = Clownfish::CFC::Parser->new;
-$parser->parse('parcel Neato;');
+my $neato = $parser->parse('parcel Neato;');
 
 my $thing = Clownfish::CFC::Model::Variable->new(
     type => $parser->parse('Thing*'),
@@ -45,8 +45,8 @@ my $foo = 
Clownfish::CFC::Model::Class->create(%foo_create_args);
 $foo->add_function($tread_water);
 $foo->add_member_var($thing);
 $foo->add_inert_var($widget);
-my $should_be_foo = Clownfish::CFC::Model::Class->fetch_singleton('Foo');
-is( $$foo, $$should_be_foo, "fetch_singleton" );
+my $should_be_foo = $neato->class('Foo');
+is( $$foo, $$should_be_foo, "Fetch class from parcel" );
 
 eval { Clownfish::CFC::Model::Class->create(%foo_create_args) };
 like( $@, qr/two classes with name/i,

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/perl/t/403-parcel.t
----------------------------------------------------------------------
diff --git a/compiler/perl/t/403-parcel.t b/compiler/perl/t/403-parcel.t
index efd86e6..47023de 100644
--- a/compiler/perl/t/403-parcel.t
+++ b/compiler/perl/t/403-parcel.t
@@ -206,13 +206,13 @@ Clownfish::CFC::Model::Parcel->reap_singletons();
         file_spec  => $foo_file_spec,
         class_name => 'Foo::Bar',
     );
-    my $found;
-    $found = $crust->lookup_struct_sym('Swim');
-    is( $found->get_name, 'Clownfish', 'lookup_struct_sym prereq' );
-    $found = $crust->lookup_struct_sym('Pinch');
-    is( $found->get_name, 'Crustacean', 'lookup_struct_sym self' );
-    $found = $crust->lookup_struct_sym('Bar');
-    ok( !$found, 'lookup_struct_sym other' );
+    my $class;
+    $class = $crust->class_by_short_sym('Swim');
+    is( $class->get_name, 'Clownfish::Swim', 'class_by_short_sym prereq' );
+    $class = $crust->class_by_short_sym('Pinch');
+    is( $class->get_name, 'Crustacean::Pinch', 'class_by_short_sym self' );
+    $class = $crust->class_by_short_sym('Bar');
+    ok( !$class, 'class_by_short_sym other' );
 
     Clownfish::CFC::Model::Parcel->reap_singletons();
 }

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCGoClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCGoClass.c b/compiler/src/CFCGoClass.c
index cf1b74c..78b9e72 100644
--- a/compiler/src/CFCGoClass.c
+++ b/compiler/src/CFCGoClass.c
@@ -72,8 +72,8 @@ CFCGoClass_new(CFCParcel *parcel, const char *class_name) {
     CFCGoClass *self = (CFCGoClass*)CFCBase_allocate(&CFCGOCLASS_META);
     self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
     self->class_name = CFCUtil_strdup(class_name);
-    // Client may be NULL, since fetch_singleton() does not always succeed.
-    CFCClass *client = CFCClass_fetch_singleton(class_name);
+    // Client may be NULL, since class() does not always succeed.
+    CFCClass *client = CFCParcel_class(parcel, class_name);
     self->client = (CFCClass*)CFCBase_incref((CFCBase*)client);
     return self;
 }
@@ -133,7 +133,7 @@ CFCGoClass_singleton(const char *class_name) {
 CFCClass*
 CFCGoClass_get_client(CFCGoClass *self) {
     if (!self->client) {
-        CFCClass *client = CFCClass_fetch_singleton(self->class_name);
+        CFCClass *client = CFCParcel_class(self->parcel, self->class_name);
         self->client = (CFCClass*)CFCBase_incref((CFCBase*)client);
     }
     return self->client;

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCParcel.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCParcel.c b/compiler/src/CFCParcel.c
index 46ce377..ff8e0ed 100644
--- a/compiler/src/CFCParcel.c
+++ b/compiler/src/CFCParcel.c
@@ -571,7 +571,7 @@ CFCParcel_read_host_data_json(CFCParcel *self, const char 
*host_lang) {
         CFCJson **children = CFCJson_get_children(class_hash);
         for (int i = 0; children[i]; i += 2) {
             const char *class_name = CFCJson_get_string(children[i]);
-            CFCClass *klass = CFCClass_fetch_singleton(class_name);
+            CFCClass *klass = CFCParcel_class(self, class_name);
             if (!klass) {
                 CFCUtil_die("Class '%s' in '%s' not found", class_name, path);
             }
@@ -681,37 +681,91 @@ CFCParcel_sort_classes(CFCParcel *self) {
     self->classes = sorted;
 }
 
-static CFCParcel*
-S_lookup_struct_sym(CFCParcel *self, const char *struct_sym) {
+CFCClass*
+CFCParcel_class(CFCParcel *self, const char *class_name) {
     for (size_t i = 0; self->classes[i]; ++i) {
-        const char *other_sym = CFCClass_get_struct_sym(self->classes[i]);
-        if (strcmp(other_sym, struct_sym) == 0) {
-            return self;
+        CFCClass *klass = self->classes[i];
+        if (strcmp(CFCClass_get_name(klass), class_name) == 0) {
+            return klass;
         }
     }
 
     return NULL;
 }
 
-CFCParcel*
-CFCParcel_lookup_struct_sym(CFCParcel *self, const char *struct_sym) {
-    CFCParcel *parcel = S_lookup_struct_sym(self, struct_sym);
+static CFCClass*
+S_class_by_struct_sym(CFCParcel *self, const char *struct_sym,
+                      size_t prefix_len) {
+    // If prefix_len is 0, struct_sym is a short symbol without prefix.
+    // Search for a class and check for ambiguity.
+    //
+    // If prefix_len is greater than 0, struct_sym is a full symbol with 
prefix.
+    // Return a matching class as soon as it's found.
+
+    if (prefix_len != 0
+        && strncmp(self->prefix, struct_sym, prefix_len) != 0
+       ) {
+        return NULL;
+    }
+
+    const char *short_struct_sym = struct_sym + prefix_len;
+    for (size_t i = 0; self->classes[i]; ++i) {
+        CFCClass *klass = self->classes[i];
+        if (strcmp(CFCClass_get_struct_sym(klass), short_struct_sym) == 0) {
+            return klass;
+        }
+    }
+
+    return NULL;
+}
+
+static CFCClass*
+S_class_by_struct_sym_prereq(CFCParcel *self, const char *struct_sym,
+                             size_t prefix_len) {
+    CFCClass *klass = S_class_by_struct_sym(self, struct_sym, prefix_len);
+    if (klass && prefix_len != 0) { return klass; }
 
     for (size_t i = 0; self->prereqs[i]; ++i) {
         const char *prereq_name   = CFCPrereq_get_name(self->prereqs[i]);
         CFCParcel  *prereq_parcel = CFCParcel_fetch(prereq_name);
-        CFCParcel *maybe_parcel
-            = S_lookup_struct_sym(prereq_parcel, struct_sym);
-
-        if (maybe_parcel) {
-            if (parcel) {
-                CFCUtil_die("Type '%s' is ambigious", struct_sym);
+        CFCClass *candidate
+            = S_class_by_struct_sym(prereq_parcel, struct_sym, prefix_len);
+
+        if (candidate) {
+            if (prefix_len != 0) { return candidate; }
+            if (klass) {
+                CFCUtil_warn("Type '%s' is ambiguous. Do you mean %s or %s?",
+                             struct_sym, CFCClass_full_struct_sym(klass),
+                             CFCClass_full_struct_sym(candidate));
+                return NULL;
             }
-            parcel = maybe_parcel;
+            klass = candidate;
         }
     }
 
-    return parcel;
+    return klass;
+}
+
+CFCClass*
+CFCParcel_class_by_short_sym(CFCParcel *self, const char *struct_sym) {
+    return S_class_by_struct_sym_prereq(self, struct_sym, 0);
+}
+
+CFCClass*
+CFCParcel_class_by_full_sym(CFCParcel *self, const char *full_struct_sym) {
+    size_t prefix_len = 0;
+    size_t sym_len    = strlen(full_struct_sym);
+    while (prefix_len < sym_len
+           && !CFCUtil_isupper(full_struct_sym[prefix_len])
+          ) {
+        prefix_len += 1;
+    }
+    if (!prefix_len) {
+        CFCUtil_die("Full struct symbol '%s' has invalid prefix",
+                    full_struct_sym);
+    }
+
+    return S_class_by_struct_sym_prereq(self, full_struct_sym, prefix_len);
 }
 
 int

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCParcel.h
----------------------------------------------------------------------
diff --git a/compiler/src/CFCParcel.h b/compiler/src/CFCParcel.h
index 0c02a48..125364e 100644
--- a/compiler/src/CFCParcel.h
+++ b/compiler/src/CFCParcel.h
@@ -184,17 +184,30 @@ CFCParcel_add_class(CFCParcel *self, struct CFCClass 
*klass);
 void
 CFCParcel_sort_classes(CFCParcel *self);
 
+/** Search for a class by class name. Doesn't search prereqs. Returns
+ * NULL if no class was found.
+ */
+struct CFCClass*
+CFCParcel_class(CFCParcel *self, const char *class_name);
+
+/** Search for a class by short struct symbol. Searches direct prereqs.
+ * Returns NULL if no class was found. Throws an exception if the
+ * struct symbol doesn't match unambiguously.
+ */
+struct CFCClass*
+CFCParcel_class_by_short_sym(CFCParcel *self, const char *struct_sym);
+
+/** Search for a class by full struct symbol. Searches direct prereqs.
+ * Returns NULL if no class was found.
+ */
+struct CFCClass*
+CFCParcel_class_by_full_sym(CFCParcel *self, const char *full_struct_sym);
+
 /** Return the ordered list of classes in the parcel.
  */
 struct CFCClass**
 CFCParcel_get_classes(CFCParcel *self);
 
-/** Search the parcel and all direct prerequisites for a class with
- * struct_sym. Return the parcel in which the class was found or NULL.
- */
-CFCParcel*
-CFCParcel_lookup_struct_sym(CFCParcel *self, const char *struct_sym);
-
 /** Indicate whether the parcel is "clownfish", the main Clownfish runtime.
  */
 int

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCPerlClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCPerlClass.c b/compiler/src/CFCPerlClass.c
index fe40119..170e842 100644
--- a/compiler/src/CFCPerlClass.c
+++ b/compiler/src/CFCPerlClass.c
@@ -74,24 +74,29 @@ CFCPerlClass_init(CFCPerlClass *self, CFCParcel *parcel,
                   const char *class_name) {
     CFCUTIL_NULL_CHECK(class_name);
 
-    // Client may be NULL, since fetch_singleton() does not always succeed.
-    CFCClass *client = CFCClass_fetch_singleton(class_name);
-    if (client == NULL) {
-        if (parcel == NULL) {
-            CFCUtil_die("Missing parcel for class %s", class_name);
+    CFCClass *client;
+    if (!parcel) {
+        // Search all source parcels.
+        CFCParcel **parcels = CFCParcel_all_parcels();
+        for (size_t i = 0; parcels[i]; i++) {
+            CFCParcel *candidate = parcels[i];
+            if (CFCParcel_included(candidate)) { continue; }
+            client = CFCParcel_class(candidate, class_name);
+            if (client) {
+                parcel = candidate;
+                break;
+            }
         }
-    }
-    else {
-        CFCParcel *client_parcel = CFCClass_get_parcel(client);
 
-        if (parcel == NULL) {
-            parcel = client_parcel;
-        }
-        else if (client_parcel != parcel) {
-            CFCUtil_die("Wrong parcel %s for class %s",
-                        CFCParcel_get_name(parcel), class_name);
+        if (!parcel) {
+            CFCUtil_die("Class '%s' not found and no parcel specified.",
+                        class_name);
         }
     }
+    else {
+        // Client may be NULL, since class() does not always succeed.
+        client = CFCParcel_class(parcel, class_name);
+    }
 
     self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
     self->class_name = CFCUtil_strdup(class_name);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCTestClass.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCTestClass.c b/compiler/src/CFCTestClass.c
index 499e0cb..266a692 100644
--- a/compiler/src/CFCTestClass.c
+++ b/compiler/src/CFCTestClass.c
@@ -105,8 +105,8 @@ S_run_tests(CFCTest *test) {
     CFCClass_add_inert_var(foo, widget);
 
     {
-        CFCClass *should_be_foo = CFCClass_fetch_singleton("Foo");
-        OK(test, should_be_foo == foo, "fetch_singleton");
+        CFCClass *should_be_foo = CFCParcel_class(neato, "Foo");
+        OK(test, should_be_foo == foo, "Fetch class from parcel");
     }
 
     {

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCTestParcel.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCTestParcel.c b/compiler/src/CFCTestParcel.c
index 6e6cacc..d1c89f3 100644
--- a/compiler/src/CFCTestParcel.c
+++ b/compiler/src/CFCTestParcel.c
@@ -289,13 +289,13 @@ S_run_extended_tests(CFCTest *test) {
         CFCClass *bar
             = CFCClass_create(foo, NULL, "Foo::Bar", NULL, NULL, foo_file_spec,
                               "Clownfish::Obj", false, false, false);
-        CFCParcel *found;
-        found = CFCParcel_lookup_struct_sym(crust, "Swim");
-        OK(test, found == cfish, "lookup_struct_sym prereq");
-        found = CFCParcel_lookup_struct_sym(crust, "Pinch");
-        OK(test, found == crust, "lookup_struct_sym self");
-        found = CFCParcel_lookup_struct_sym(crust, "Bar");
-        OK(test, found == NULL, "lookup_struct_sym other");
+        CFCClass *klass;
+        klass = CFCParcel_class_by_short_sym(crust, "Swim");
+        OK(test, klass == swim, "class_by_short_sym prereq");
+        klass = CFCParcel_class_by_short_sym(crust, "Pinch");
+        OK(test, klass == pinch, "class_by_short_sym self");
+        klass = CFCParcel_class_by_short_sym(crust, "Bar");
+        OK(test, klass == NULL, "class_by_short_sym other");
 
         FREEMEM(prereq_parcels);
         CFCBase_decref((CFCBase*)bar);

http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/c502223a/compiler/src/CFCType.c
----------------------------------------------------------------------
diff --git a/compiler/src/CFCType.c b/compiler/src/CFCType.c
index 5df5705..9489c76 100644
--- a/compiler/src/CFCType.c
+++ b/compiler/src/CFCType.c
@@ -295,16 +295,14 @@ CFCType_resolve(CFCType *self) {
 
     char *specifier = self->specifier;
     if (CFCUtil_isupper(specifier[0])) {
-        CFCParcel *src_parcel = CFCType_get_parcel(self);
-        CFCParcel *parcel
-            = CFCParcel_lookup_struct_sym(src_parcel, specifier);
-        if (!parcel) {
+        CFCParcel *parcel = CFCType_get_parcel(self);
+        CFCClass  *klass  = CFCParcel_class_by_short_sym(parcel, specifier);
+        if (!klass) {
             CFCUtil_die("No class found for type '%s'", specifier);
         }
 
-        // Create actual specifier with prefix.
-        const char *prefix = CFCParcel_get_prefix(parcel);
-        self->specifier = CFCUtil_sprintf("%s%s", prefix, specifier);
+        // Upgrade specifier to full struct sym.
+        self->specifier = CFCUtil_strdup(CFCClass_full_struct_sym(klass));
         FREEMEM(specifier);
     }
 }

Reply via email to