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