blob: 2bb5e08a7677debeedb788b2184005f02ea0245a [file] [log] [blame]
/* 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 <string.h>
#include <ctype.h>
#ifndef true
#define true 1
#define false 0
#endif
#define CFC_NEED_SYMBOL_STRUCT_DEF
#include "CFCSymbol.h"
#include "CFCParcel.h"
#include "CFCUtil.h"
const static CFCMeta CFCSYMBOL_META = {
"Clownfish::CFC::Symbol",
sizeof(CFCSymbol),
(CFCBase_destroy_t)CFCSymbol_destroy
};
CFCSymbol*
CFCSymbol_new(struct CFCParcel *parcel, const char *exposure,
const char *class_name, const char *class_cnick,
const char *micro_sym) {
CFCSymbol *self = (CFCSymbol*)CFCBase_allocate(&CFCSYMBOL_META);
return CFCSymbol_init(self, parcel, exposure, class_name, class_cnick,
micro_sym);
}
static int
S_validate_exposure(const char *exposure) {
if (!exposure) { return false; }
if (strcmp(exposure, "public")
&& strcmp(exposure, "parcel")
&& strcmp(exposure, "private")
&& strcmp(exposure, "local")
) {
return false;
}
return true;
}
static int
S_validate_class_name(const char *class_name) {
const char *ptr;
// Must be UpperCamelCase, separated by "::".
for (ptr = class_name; *ptr != 0;) {
if (!isupper(*ptr)) { return false; }
// Each component must contain lowercase letters.
const char *substring;
for (substring = ptr; ; substring++) {
if (*substring == 0) { return false; }
else if (*substring == ':') { return false; }
else if (islower(*substring)) { break; }
}
while (*ptr != 0) {
if (*ptr == 0) { break; }
else if (*ptr == ':') {
ptr++;
if (*ptr != ':') { return false; }
ptr++;
if (*ptr == 0) { return false; }
break;
}
else if (!isalnum(*ptr)) { return false; }
ptr++;
}
}
return true;
}
int
CFCSymbol_validate_class_name_component(const char *name) {
if (!S_validate_class_name(name)) { return false; }
if (strchr(name, ':') != NULL) { return false; }
return true;
}
static int
S_validate_class_cnick(const char *class_cnick) {
// Allow all caps.
const char *ptr;
for (ptr = class_cnick; ; ptr++) {
if (*ptr == 0) {
if (strlen(class_cnick)) { return true; }
else { break; }
}
else if (!isupper(*ptr)) { break; }
}
// Same as one component of a class name.
return CFCSymbol_validate_class_name_component(class_cnick);
}
static int
S_validate_identifier(const char *identifier) {
const char *ptr = identifier;
if (!isalpha(*ptr) && *ptr != '_') { return false; }
for (; *ptr != 0; ptr++) {
if (!isalnum(*ptr) && *ptr != '_') { return false; }
}
return true;
}
CFCSymbol*
CFCSymbol_init(CFCSymbol *self, struct CFCParcel *parcel,
const char *exposure, const char *class_name,
const char *class_cnick, const char *micro_sym) {
// Validate.
CFCUTIL_NULL_CHECK(parcel);
if (!S_validate_exposure(exposure)) {
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Invalid exposure: '%s'", exposure ? exposure : "[NULL]");
}
if (class_name && !S_validate_class_name(class_name)) {
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Invalid class_name: '%s'", class_name);
}
if (!micro_sym || !S_validate_identifier(micro_sym)) {
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Invalid micro_sym: '%s'", micro_sym ? micro_sym : "[NULL]");
}
// Derive class_cnick if necessary, then validate.
const char *real_cnick = NULL;
if (class_name) {
if (class_cnick) {
real_cnick = class_cnick;
}
else {
const char *last_colon = strrchr(class_name, ':');
real_cnick = last_colon ? last_colon + 1 : class_name;
}
}
else if (class_cnick) {
// Sanity check class_cnick without class_name.
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Can't supply class_cnick without class_name");
}
else {
real_cnick = NULL;
}
if (real_cnick && !S_validate_class_cnick(real_cnick)) {
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Invalid class_cnick: '%s'", real_cnick);
}
// Assign.
self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel);
self->exposure = CFCUtil_strdup(exposure);
self->class_name = CFCUtil_strdup(class_name);
self->class_cnick = CFCUtil_strdup(real_cnick);
self->micro_sym = CFCUtil_strdup(micro_sym);
// Derive short_sym.
size_t class_cnick_len = self->class_cnick
? strlen(self->class_cnick)
: 0;
size_t short_sym_len = class_cnick_len
+ strlen("_")
+ strlen(self->micro_sym);
self->short_sym = (char*)MALLOCATE(short_sym_len + 1);
if (self->class_cnick) {
memcpy((void*)self->short_sym, self->class_cnick, class_cnick_len);
}
self->short_sym[class_cnick_len] = '_';
memcpy(&self->short_sym[class_cnick_len + 1],
self->micro_sym, strlen(micro_sym));
self->short_sym[short_sym_len] = '\0';
// Derive full_sym;
const char *prefix = CFCParcel_get_prefix(self->parcel);
size_t prefix_len = strlen(prefix);
size_t full_sym_len = prefix_len + short_sym_len;
self->full_sym = (char*)MALLOCATE(full_sym_len + 1);
memcpy(self->full_sym, prefix, prefix_len);
memcpy(&self->full_sym[prefix_len], self->short_sym, short_sym_len);
self->full_sym[full_sym_len] = '\0';
return self;
}
void
CFCSymbol_destroy(CFCSymbol *self) {
CFCBase_decref((CFCBase*)self->parcel);
FREEMEM(self->exposure);
FREEMEM(self->class_name);
FREEMEM(self->class_cnick);
FREEMEM(self->micro_sym);
FREEMEM(self->short_sym);
FREEMEM(self->full_sym);
CFCBase_destroy((CFCBase*)self);
}
int
CFCSymbol_equals(CFCSymbol *self, CFCSymbol *other) {
if (strcmp(self->micro_sym, other->micro_sym) != 0) { return false; }
if (!CFCParcel_equals(self->parcel, other->parcel)) { return false; }
if (strcmp(self->exposure, other->exposure) != 0) { return false; }
if (self->class_name) {
if (!other->class_name) { return false; }
if (strcmp(self->class_name, other->class_name) != 0) {
return false;
}
}
else if (other->class_name) {
return false;
}
return true;
}
int
CFCSymbol_public(CFCSymbol *self) {
return !strcmp(self->exposure, "public");
}
int
CFCSymbol_parcel(CFCSymbol *self) {
return !strcmp(self->exposure, "parcel");
}
int
CFCSymbol_private(CFCSymbol *self) {
return !strcmp(self->exposure, "private");
}
int
CFCSymbol_local(CFCSymbol *self) {
return !strcmp(self->exposure, "local");
}
const char*
CFCSymbol_full_sym(CFCSymbol *self) {
return self->full_sym;
}
const char*
CFCSymbol_short_sym(CFCSymbol *self) {
return self->short_sym;
}
struct CFCParcel*
CFCSymbol_get_parcel(CFCSymbol *self) {
return self->parcel;
}
const char*
CFCSymbol_get_class_name(CFCSymbol *self) {
return self->class_name;
}
const char*
CFCSymbol_get_class_cnick(CFCSymbol *self) {
return self->class_cnick;
}
const char*
CFCSymbol_get_exposure(CFCSymbol *self) {
return self->exposure;
}
const char*
CFCSymbol_micro_sym(CFCSymbol *self) {
return self->micro_sym;
}
const char*
CFCSymbol_get_prefix(CFCSymbol *self) {
return CFCParcel_get_prefix(self->parcel);
}
const char*
CFCSymbol_get_Prefix(CFCSymbol *self) {
return CFCParcel_get_Prefix(self->parcel);
}
const char*
CFCSymbol_get_PREFIX(CFCSymbol *self) {
return CFCParcel_get_PREFIX(self->parcel);
}