/* This creates a new class entity. This entity is used for, well, more than it should be. The entity is going to be either an enum, a variant, or a user-defined class. The class is assumed to be refcounted, because it usually is. The new class is automatically linked up to the current module. No default type is created, in case the newly-made class ends up needing generics. */ lily_class *lily_new_class(lily_symtab *symtab, const char *name) { lily_class *new_class = lily_malloc(sizeof(lily_class)); char *name_copy = lily_malloc(strlen(name) + 1); strcpy(name_copy, name); new_class->item_kind = 0; new_class->flags = 0; new_class->type = NULL; new_class->parent = NULL; new_class->shorthash = shorthash_for_name(name); new_class->name = name_copy; new_class->generic_count = 0; new_class->prop_count = 0; new_class->variant_members = NULL; new_class->members = NULL; new_class->module = symtab->active_module; new_class->all_subtypes = NULL; new_class->move_flags = VAL_IS_INSTANCE; new_class->dyna_start = 0; new_class->id = symtab->next_class_id; symtab->next_class_id++; new_class->next = symtab->active_module->class_chain; symtab->active_module->class_chain = new_class; return new_class; }
/* Create a new property and add it into the class. As a convenience, the newly-made property is also returned. */ lily_prop_entry *lily_add_class_property(lily_symtab *symtab, lily_class *cls, lily_type *type, const char *name, int flags) { lily_prop_entry *entry = lily_malloc(sizeof(lily_prop_entry)); char *entry_name = lily_malloc(strlen(name) + 1); strcpy(entry_name, name); entry->item_kind = ITEM_TYPE_PROPERTY; entry->flags = flags; entry->name = entry_name; entry->type = type; entry->name_shorthash = shorthash_for_name(entry_name); entry->next = NULL; entry->id = cls->prop_count; entry->cls = cls; cls->prop_count++; /* It's REALLY important that properties be linked this way, because it allows the vm to walk from a derived class up through the superclasses when setting property types in instance creation. It goes like this: Animal > Bird > Falcon [3 => 2 => 1] => [6 => 5 => 4] => [9 => 8 => 7] */ entry->next = cls->properties; cls->properties = entry; return entry; }
/* Try to find a class. If 'module' is NULL, then search through both the current module AND the builtin module. In all other cases, search just the module given. */ lily_class *lily_find_class(lily_symtab *symtab, lily_module_entry *module, const char *name) { uint64_t shorthash = shorthash_for_name(name); lily_class *result; if (module == NULL) { if (name[1] != '\0') { result = find_class(symtab->builtin_module->class_chain, name, shorthash); if (result == NULL) result = find_class(symtab->active_module->class_chain, name, shorthash); } else { lily_type *generic_type = lookup_generic(symtab, name); if (generic_type) { /* It's rather silly to make a different class for each generic type. Instead, write out whatever generic type was found as the default type. The generic class is written to have no subtypes, so this is okay. ts and other modules always special case the generic class, and won't be bothered by this little trick. */ result = symtab->generic_class; result->type = generic_type; } else result = NULL; } } else result = find_class(module->class_chain, name, shorthash); return result; }
/* lily_keyword_by_name Attempt to lookup a keyword based on 64-bit short hash, then on a name if necessary. Keywords are in a static list, (via lily_seed_symtab.h), so this doesn't require a symtab. */ int lily_keyword_by_name(char *name) { int i; uint64_t shorthash = shorthash_for_name(name); for (i = 0;i <= KEY_LAST_ID;i++) { if (keywords[i].shorthash == shorthash && strcmp(keywords[i].name, name) == 0) return i; } return -1; }
/* lily_class_by_name This function returns a class for a given name, or NULL. This doesn't take a name, because all class names are <= 8 bytes in name length. */ lily_class *lily_class_by_name(lily_symtab *symtab, char *name) { int i; lily_class **classes = symtab->classes; uint64_t shorthash = shorthash_for_name(name); for (i = 0;i <= symtab->class_pos;i++) { if (classes[i]->shorthash == shorthash && strcmp(classes[i]->name, name) == 0) return classes[i]; } return NULL; }
/* Try to find a method within the class given. The given class is search first, then any parents of the class. */ lily_var *lily_find_method(lily_class *cls, const char *name) { lily_var *iter; uint64_t shorthash = shorthash_for_name(name); for (iter = cls->call_chain;iter != NULL;iter = iter->next) { if (iter->shorthash == shorthash && strcmp(iter->name, name) == 0) break; } if (iter == NULL && cls->parent) iter = lily_find_method(cls->parent, name); return iter; }
/* lily_var_by_name Search the symtab for a var with a name of 'name'. This will return the var or NULL. */ lily_var *lily_var_by_name(lily_symtab *symtab, char *name) { lily_var *var = symtab->var_start; uint64_t shorthash = shorthash_for_name(name); while (var != NULL) { if (var->shorthash == shorthash && ((var->flags & SYM_OUT_OF_SCOPE) == 0) && strcmp(var->name, name) == 0) return var; var = var->next; } return NULL; }
/* lily_find_class_callable This function will see if a given clas has a function with the given name. NULL is returned on failure. */ lily_var *lily_find_class_callable(lily_symtab *symtab, lily_class *cls, char *name) { lily_var *iter; uint64_t shorthash = shorthash_for_name(name); for (iter = cls->call_start;iter != NULL;iter = iter->next) { if (iter->shorthash == shorthash && strcmp(iter->name, name) == 0) break; } /* Maybe it's something that hasn't been loaded in the symtab yet. */ if (iter == NULL && cls->seed_table != NULL) { const lily_func_seed *seed = cls->seed_table; while (seed) { if (strcmp(seed->name, name) == 0) { lily_var *save_top = symtab->var_top; iter = init_func_seed(symtab, cls, seed); if (iter == NULL) lily_raise_nomem(symtab->raiser); else { /* The new var is added to symtab's vars. Take it off of there since this var shouldn't be globally reachable. __main__ is the first var, so this shouldn't have to check for var_top == var_start. */ if (cls->call_start == NULL) cls->call_start = iter; if (cls->call_top != NULL) cls->call_top->next = iter; cls->call_top = iter; /* This is a builtin, so fix the line number to 0. */ iter->line_num = 0; symtab->var_top = save_top; symtab->var_top->next = NULL; } break; } seed = seed->next; } } return iter; }
/* Scoped variants are stored within the enum they're part of. This will try to find a variant stored within 'enum_cls'. */ lily_variant_class *lily_find_scoped_variant(lily_class *enum_cls, const char *name) { int i; uint64_t shorthash = shorthash_for_name(name); lily_variant_class *ret = NULL; for (i = 0;i < enum_cls->variant_size;i++) { lily_variant_class *cls = enum_cls->variant_members[i]; if (cls->shorthash == shorthash && strcmp(cls->name, name) == 0) { ret = cls; } } return ret; }
/* Try to find a var. If the given module is NULL, then search through both the current and builtin modules. For everything else, just search through the module given. */ lily_var *lily_find_var(lily_symtab *symtab, lily_module_entry *module, const char *name) { uint64_t shorthash = shorthash_for_name(name); lily_var *result; if (module == NULL) { result = find_var(symtab->builtin_module->var_chain, name, shorthash); if (result == NULL) result = find_var(symtab->active_module->var_chain, name, shorthash); } else result = find_var(module->var_chain, name, shorthash); return result; }
/* Try to find a var. If the given import is NULL, then search through both the current and builtin imports. For everything else, just search through the import given. */ lily_var *lily_find_var(lily_symtab *symtab, lily_import_entry *import, const char *name) { uint64_t shorthash = shorthash_for_name(name); lily_var *result; if (import == NULL) { result = find_var(symtab->builtin_import->var_chain, name, shorthash); if (result == NULL) result = find_var(symtab->active_import->var_chain, name, shorthash); } else result = find_var(import->var_chain, name, shorthash); return result; }
/* Create a new var, but leave it to the caller to link it somewhere. */ lily_var *lily_new_raw_unlinked_var(lily_symtab *symtab, lily_type *type, const char *name) { lily_var *var = lily_malloc(sizeof(lily_var)); var->name = lily_malloc(strlen(name) + 1); var->item_kind = ITEM_TYPE_VAR; var->flags = 0; strcpy(var->name, name); var->line_num = *symtab->lex_linenum; var->shorthash = shorthash_for_name(name); var->type = type; var->next = NULL; var->parent = NULL; return var; }
/* lily_try_new_var This creates a new var using the signature given, and copying the name. It is okay to pass a sig without list element/call info, since lily_try_sig_for_class ensures that important parts are set to NULL. This function will add the var to the symtab on success. Note: 'try' means this call returns NULL on failure. */ lily_var *lily_try_new_var(lily_symtab *symtab, lily_sig *sig, char *name, int flags) { lily_var *var = lily_malloc(sizeof(lily_var)); if (var == NULL) return NULL; var->name = lily_malloc(strlen(name) + 1); if (var->name == NULL) { lily_free(var); return NULL; } var->flags = VAL_IS_NIL | SYM_TYPE_VAR | flags; strcpy(var->name, name); var->line_num = *symtab->lex_linenum; var->shorthash = shorthash_for_name(name); var->sig = sig; var->next = NULL; var->parent = NULL; if ((flags & VAR_IS_READONLY) == 0) { var->reg_spot = symtab->next_register_spot; symtab->next_register_spot++; var->function_depth = symtab->function_depth; } else { /* Vars that are never intended to be assigned to (like functions) are not placed in a register. */ var->reg_spot = -1; var->function_depth = -1; } if (symtab->var_start == NULL) symtab->var_start = var; else symtab->var_top->next = var; symtab->var_top = var; return var; }
/* This creates a new variant called 'name' and installs it into 'enum_cls'. */ lily_variant_class *lily_new_variant(lily_symtab *symtab, lily_class *enum_cls, const char *name, int variant_id) { lily_variant_class *variant = lily_malloc(sizeof(lily_variant_class)); variant->item_kind = ITEM_TYPE_VARIANT; variant->flags = CLS_IS_VARIANT | CLS_EMPTY_VARIANT; variant->variant_id = variant_id; variant->parent = enum_cls; variant->build_type = NULL; variant->shorthash = shorthash_for_name(name); variant->name = lily_malloc(strlen(name) + 1); strcpy(variant->name, name); variant->next = symtab->active_module->class_chain; symtab->active_module->class_chain = (lily_class *)variant; /* Variant classes do not need a unique class id because they are not compared in ts. In vm, they're always accessed through their enum. */ return variant; }
/* Create a new property and add it into the class. As a convenience, the newly-made property is also returned. */ lily_prop_entry *lily_add_class_property(lily_symtab *symtab, lily_class *cls, lily_type *type, const char *name, int flags) { lily_prop_entry *entry = lily_malloc(sizeof(lily_prop_entry)); char *entry_name = lily_malloc(strlen(name) + 1); strcpy(entry_name, name); entry->item_kind = ITEM_TYPE_PROPERTY; entry->flags = flags; entry->name = entry_name; entry->type = type; entry->name_shorthash = shorthash_for_name(entry_name); entry->next = NULL; entry->id = cls->prop_count; entry->cls = cls; cls->prop_count++; entry->next = (lily_prop_entry *)cls->members; cls->members = (lily_named_sym *)entry; return entry; }
/* Does 'name' exist within 'cls' as either a var or a name? If so, return it. If not, then return NULL. */ lily_named_sym *lily_find_member(lily_class *cls, const char *name) { lily_named_sym *ret = NULL; if (cls->members != NULL) { uint64_t shorthash = shorthash_for_name(name); lily_named_sym *sym_iter = cls->members; while (sym_iter) { if (sym_iter->name_shorthash == shorthash && strcmp(sym_iter->name, name) == 0) { ret = (lily_named_sym *)sym_iter; break; } sym_iter = sym_iter->next; } } if (ret == NULL && cls->parent != NULL) ret = lily_find_member(cls->parent, name); return ret; }
/* Try to find a property with a name in a class. The parent class(es), if any, are tries as a fallback if unable to find it in the given class. */ lily_prop_entry *lily_find_property(lily_class *cls, const char *name) { lily_prop_entry *ret = NULL; if (cls->properties != NULL) { uint64_t shorthash = shorthash_for_name(name); lily_prop_entry *prop_iter = cls->properties; while (prop_iter) { if (prop_iter->name_shorthash == shorthash && strcmp(prop_iter->name, name) == 0) { ret = prop_iter; break; } prop_iter = prop_iter->next; } } if (ret == NULL && cls->parent != NULL) ret = lily_find_property(cls->parent, name); return ret; }
/* init_classes Symtab init, stage 2 This function initializes the classes of a symtab, as well as their signatures. All classes are given a signature so that signatures which don't require extra call/internal element info (integer and number, for example), can be shared. All a symbol needs to do is sym->sig to get the common signature. */ static int init_classes(lily_symtab *symtab) { int i, class_count, ret; lily_class **classes; classes = lily_malloc(sizeof(lily_class *) * INITIAL_CLASS_SIZE); if (classes == NULL) return 0; symtab->classes = classes; class_count = sizeof(class_seeds) / sizeof(class_seeds[0]); ret = 1; for (i = 0;i < class_count;i++) { lily_class *new_class = lily_malloc(sizeof(lily_class)); if (new_class != NULL) { lily_sig *sig; /* If a class doesn't take templates (or isn't template), then it can have a default sig that lily_try_sig_for_class can yield. This saves memory, and is necessary now that sig comparison is by pointer. */ if (class_seeds[i].template_count != 0 || i == SYM_CLASS_TEMPLATE) { sig = NULL; } else { /* A basic class? Make a quick default sig for it. */ sig = lily_malloc(sizeof(lily_sig)); if (sig != NULL) { sig->cls = new_class; /* Make sure this is null so any attempt to free it won't cause a problem. */ sig->siglist = NULL; sig->siglist_size = 0; sig->flags = 0; /* Non-template signatures use this to mean that this sig does not have templates inside. */ sig->template_pos = 0; if (i == SYM_CLASS_ANY) sig->flags |= SIG_MAYBE_CIRCULAR; sig->next = symtab->root_sig; symtab->root_sig = sig; } else ret = 0; } new_class->name = class_seeds[i].name; new_class->call_start = NULL; new_class->call_top = NULL; new_class->sig = sig; new_class->id = i; new_class->template_count = class_seeds[i].template_count; new_class->shorthash = shorthash_for_name(new_class->name); new_class->gc_marker = class_seeds[i].gc_marker; new_class->flags = class_seeds[i].flags; new_class->is_refcounted = class_seeds[i].is_refcounted; new_class->seed_table = NULL; new_class->setup_func = class_seeds[i].setup_func; new_class->eq_func = class_seeds[i].eq_func; } else ret = 0; classes[i] = new_class; } /* Packages are a bit too complicated for the parser now, so make them where the user can't declare them. */ if (ret == 1) classes[SYM_CLASS_PACKAGE]->shorthash = 0; /* This is so symtab cleanup catches all of the builtin classes, regardless of what parts were initialized. */ symtab->class_pos = SYM_LAST_CLASS; return ret; }