To mark symbols up with metadata.
- §1. The possible annotations
- §3. Canonical annotations
- §6. API for making annotations
- §7. API for reading annotations
- §8. Internal representation
§1. The possible annotations. Each annotation marks a symbol with a choice of inter_annotation_form and an associated value. That value can be of three possible data types: boolean (in effect, the annotation is either made or not), integer (the value is an associated number), or textual (it's a text).
Each annotation has a keyword used in textual Inter code: these by convention always begin with __.
enum BOOLEAN_IATYPE from 1 enum INTEGER_IATYPE enum TEXTUAL_IATYPE
typedef struct inter_annotation_form { inter_ti annotation_ID; int iatype; one of the *_IATYPE constants above struct text_stream *annotation_keyword; CLASS_DEFINITION } inter_annotation_form;
- The structure inter_annotation_form is accessed in 3/iibf, 3/iitf and here.
§2. We need rapid conversion between the annotation ID numbers, which range from 0 upwards, to their corresponding //inter_annotation_form//s.
Note that SymbolAnnotation::declare returns FALSE if two contradictory definitions are made for the same ID. This is impossible when creating the canonical set (see below), but is used to detect errors when binary Inter is read in which had used an inconsistent set of annotations.
define MAX_IAFS 64
inter_annotation_form *invalid_IAF = NULL; inter_annotation_form *iafs_registered[MAX_IAFS]; int iafs_registered_initialised = FALSE; int SymbolAnnotation::declare(inter_ti ID, text_stream *keyword, int iatype) { if (iafs_registered_initialised == FALSE) { for (int i=0; i<MAX_IAFS; i++) iafs_registered[i] = NULL; iafs_registered_initialised = TRUE; } if (ID >= MAX_IAFS) internal_error("ID out of range"); if (iafs_registered[ID]) { if (Str::eq(keyword, iafs_registered[ID]->annotation_keyword)) return TRUE; return FALSE; } iafs_registered[ID] = CREATE(inter_annotation_form); iafs_registered[ID]->annotation_ID = ID; iafs_registered[ID]->annotation_keyword = Str::duplicate(keyword); iafs_registered[ID]->iatype = iatype; if (ID == INVALID_IANN) invalid_IAF = iafs_registered[ID]; return TRUE; }
§3. Canonical annotations. The set of annotations used by the Inform tool suite is as follows.
enum INVALID_IANN from 0 enum ASSIMILATED_IANN enum FAKE_ACTION_IANN enum OBJECT_IANN enum PRIVATE_IANN enum KEEPING_IANN enum C_ARRAY_ADDRESS_IANN enum I6_GLOBAL_OFFSET_IANN enum OBJECT_KIND_COUNTER_IANN enum APPEND_IANN enum INNER_PROPERTY_NAME_IANN enum TRANSLATION_IANN enum REPLACING_IANN enum NAMESPACE_IANN
§4. The special annotation __invalid, with ID INVALID_IANN, is never given to any symbol: it's used to mean "do not make an annotation".
void SymbolAnnotation::declare_canonical_annotations(void) { SymbolAnnotation::declare(INVALID_IANN, I"__invalid", INTEGER_IATYPE); SymbolAnnotation::declare(ASSIMILATED_IANN, I"__assimilated", BOOLEAN_IATYPE); SymbolAnnotation::declare(FAKE_ACTION_IANN, I"__fake_action", BOOLEAN_IATYPE); SymbolAnnotation::declare(OBJECT_IANN, I"__object", BOOLEAN_IATYPE); SymbolAnnotation::declare(PRIVATE_IANN, I"__private", BOOLEAN_IATYPE); SymbolAnnotation::declare(KEEPING_IANN, I"__keeping", BOOLEAN_IATYPE); SymbolAnnotation::declare(APPEND_IANN, I"__append", TEXTUAL_IATYPE); SymbolAnnotation::declare(INNER_PROPERTY_NAME_IANN, I"__inner_property_name", TEXTUAL_IATYPE); SymbolAnnotation::declare(TRANSLATION_IANN, I"__translation", TEXTUAL_IATYPE); SymbolAnnotation::declare(REPLACING_IANN, I"__replacing", TEXTUAL_IATYPE); SymbolAnnotation::declare(NAMESPACE_IANN, I"__my", TEXTUAL_IATYPE); SymbolAnnotation::declare(C_ARRAY_ADDRESS_IANN, I"__array_address", INTEGER_IATYPE); SymbolAnnotation::declare(I6_GLOBAL_OFFSET_IANN, I"__global_offset", INTEGER_IATYPE); SymbolAnnotation::declare(OBJECT_KIND_COUNTER_IANN, I"__object_kind_counter", INTEGER_IATYPE); }
§5. This is printed when inter is run with the -annotations switch.
void SymbolAnnotation::show_annotations(OUTPUT_STREAM) { WRITE(" Code Annotation Type of value\n"); for (int ID=0; ID<MAX_IAFS; ID++) { inter_annotation_form *IC = iafs_registered[ID]; if ((IC) && (ID != INVALID_IANN)) { WRITE(" %4x %S", ID, IC->annotation_keyword); for (int j = Str::len(IC->annotation_keyword); j<24; j++) PUT(' '); switch (IC->iatype) { case BOOLEAN_IATYPE: WRITE("none (boolean)\n"); break; case INTEGER_IATYPE: WRITE("integer\n"); break; case TEXTUAL_IATYPE: WRITE("text\n"); break; default: WRITE("unknown type\n"); break; } } } }
§6. API for making annotations.
void SymbolAnnotation::set_b(inter_symbol *S, inter_ti annot_ID, inter_ti n) { if ((n != FALSE) && (n != TRUE)) internal_error("non-boolean annotation value"); inter_annotation IA = SymbolAnnotation::from_pair(annot_ID, n); SymbolAnnotation::set(BOOLEAN_IATYPE, S, IA); } void SymbolAnnotation::set_i(inter_symbol *S, inter_ti annot_ID, inter_ti n) { inter_annotation IA = SymbolAnnotation::from_pair(annot_ID, n); SymbolAnnotation::set(INTEGER_IATYPE, S, IA); } void SymbolAnnotation::set_t(inter_tree *I, inter_package *owner, inter_symbol *S, inter_ti annot_ID, text_stream *text) { inter_ti n = InterWarehouse::create_text(InterTree::warehouse(I), owner); Str::copy(InterWarehouse::get_text(InterTree::warehouse(I), n), text); inter_annotation IA = SymbolAnnotation::from_pair(annot_ID, n); SymbolAnnotation::set(TEXTUAL_IATYPE, S, IA); } void SymbolAnnotation::set(int iatype, inter_symbol *S, inter_annotation IA) { if (S == NULL) internal_error("annotated null symbol"); if (iatype == -1) iatype = IA.annot->iatype; SymbolAnnotation::write_to_set(iatype, &(S->annotations), IA); }
§7. API for reading annotations. An important convention here is that the default value for a boolean annotation is FALSE, whereas for an integer it is -1.
int SymbolAnnotation::get_b(const inter_symbol *S, inter_ti ID) { int found = FALSE; inter_ti val = SymbolAnnotation::read_from_set(BOOLEAN_IATYPE, &(S->annotations), ID, &found); if (found) return (int) val; return FALSE; } int SymbolAnnotation::get_i(const inter_symbol *S, inter_ti ID) { int found = FALSE; inter_ti val = SymbolAnnotation::read_from_set(INTEGER_IATYPE, &(S->annotations), ID, &found); if (found) return (int) val; return -1; } text_stream *SymbolAnnotation::get_t(inter_symbol *S, inter_tree *I, inter_ti ID) { int found = FALSE; inter_ti val = SymbolAnnotation::read_from_set(TEXTUAL_IATYPE, &(S->annotations), ID, &found); if (found) return InterWarehouse::get_text(InterTree::warehouse(I), val); return NULL; }
§8. Internal representation. We can express an annotation either with an inter_annotation structure or with a pair of inter_ti values:
typedef struct inter_annotation { struct inter_annotation_form *annot; inter_ti annot_value; } inter_annotation; inter_annotation SymbolAnnotation::value_annotation(inter_annotation_form *IAF, inter_ti V) { inter_annotation IA; IA.annot = IAF; IA.annot_value = V; return IA; } inter_annotation SymbolAnnotation::invalid_annotation(void) { inter_annotation IA; IA.annot = invalid_IAF; IA.annot_value = 0; return IA; } int SymbolAnnotation::is_invalid(inter_annotation IA) { if ((IA.annot == NULL) || (IA.annot->annotation_ID == INVALID_IANN)) return TRUE; return FALSE; }
- The structure inter_annotation is accessed in 3/iibf, 3/iitf and here.
inter_annotation SymbolAnnotation::from_pair(inter_ti c1, inter_ti c2) { if ((iafs_registered_initialised) && (c1 < MAX_IAFS) && (iafs_registered[c1])) { inter_annotation IA; IA.annot = iafs_registered[c1]; IA.annot_value = c2; return IA; } return SymbolAnnotation::invalid_annotation(); } void SymbolAnnotation::to_pair(inter_annotation IA, inter_ti *c1, inter_ti *c2) { *c1 = IA.annot->annotation_ID; *c2 = IA.annot_value; }
§10. An "annotation set" is what it sounds like: a set (with no meaningful sequence) containing annotations. Each annotation ID can occur at most once during the set. A set is initially empty. Each inter_symbol has an annotation set, but they are also used when parsing textual Inter, which is why we don't assume that we are working on a symbol here.
For efficiency's sake, we store boolean annotation values in a bitmap, and any others in a linked list whose ordering has no meaning.
typedef struct inter_annotation_set { int boolean_annotations; struct linked_list *other_annotations; of inter_annotation } inter_annotation_set; inter_annotation_set SymbolAnnotation::new_annotation_set(void) { inter_annotation_set set; set.boolean_annotations = 0; set.other_annotations = NULL; return set; } int SymbolAnnotation::nonempty(inter_annotation_set *set) { if ((set) && ((set->boolean_annotations != 0) || (LinkedLists::len(set->other_annotations)))) return TRUE; return FALSE; }
- The structure inter_annotation_set is accessed in 3/iibf and here.
§11. To write an annotation to a set. Note that it is legal to write an annotation which the set already has: in that case, the new value replaces the old one. So writing __arrow_count=12 and then __arrow_count=7 results in a value of 7.
void SymbolAnnotation::write_to_set(int iatype, inter_annotation_set *set, inter_annotation A) { if (A.annot->annotation_ID == INVALID_IANN) internal_error("added invalid annotation"); if (iatype != A.annot->iatype) { WRITE_TO(STDERR, "Annotation %S (%d) should have type %d but used %d\n", A.annot->annotation_keyword, A.annot->annotation_ID, A.annot->iatype, iatype); internal_error("added annotation with wrong type"); } if (iatype == BOOLEAN_IATYPE) { if (A.annot_value) set->boolean_annotations |= (1 << A.annot->annotation_ID) ; else set->boolean_annotations &= (~(1 << A.annot->annotation_ID)); } else { inter_annotation *NA; if (set->other_annotations) { LOOP_OVER_LINKED_LIST(NA, inter_annotation, set->other_annotations) if (NA->annot == A.annot) { NA->annot_value = A.annot_value; return; } } else { set->other_annotations = NEW_LINKED_LIST(inter_annotation); } NA = CREATE(inter_annotation); NA->annot = A.annot; NA->annot_value = A.annot_value; ADD_TO_LINKED_LIST(NA, inter_annotation, set->other_annotations); } }
§12. To read an annotation. found is set to TRUE if the annotation exists for the set, and FALSE otherwise. Note that every set contains all of the boolean annotations, since they are stored in a bitmap which is always present.
inter_ti SymbolAnnotation::read_from_set(int iatype, const inter_annotation_set *set, inter_ti ID, int *found) { if ((iafs_registered_initialised == FALSE) || (ID >= MAX_IAFS)) { *found = FALSE; return 0; } if (iatype != iafs_registered[ID]->iatype) { WRITE_TO(STDERR, "Annotation %S (%d) should have type %d but sought %d\n", iafs_registered[ID]->annotation_keyword, iafs_registered[ID]->annotation_ID, iafs_registered[ID]->iatype, iatype); internal_error("sought IAF of wrong type"); } if (set) { if (iatype == BOOLEAN_IATYPE) { if (set->boolean_annotations & (1 << ID)) { *found = TRUE; return TRUE; } else { *found = TRUE; return FALSE; } } else { if (set->other_annotations) { inter_annotation *A; LOOP_OVER_LINKED_LIST(A, inter_annotation, set->other_annotations) if (A->annot->annotation_ID == ID) { *found = TRUE; return A->annot_value; } } } } *found = FALSE; return 0; }
§13. After parsing some annotations in textual Inter, and forming a set of those, we then want to impose these on some new symbol:
void SymbolAnnotation::copy_set_to_symbol(inter_annotation_set *set, inter_symbol *S) { if (set) { S->annotations.boolean_annotations |= set->boolean_annotations; if (set->other_annotations) { inter_annotation *A; LOOP_OVER_LINKED_LIST(A, inter_annotation, set->other_annotations) SymbolAnnotation::set(A->annot->iatype, S, *A); } } }
§14. Annotation sets can be written out as text. Note that booleans are only written if true, because if false then they are indistinguishable from not being there at all.
void SymbolAnnotation::write_annotations(OUTPUT_STREAM, inter_tree_node *F, inter_symbol *S) { if (S) SymbolAnnotation::write_set(OUT, &(S->annotations), F); } void SymbolAnnotation::write_set(OUTPUT_STREAM, inter_annotation_set *set, inter_tree_node *F) { if (set) { for (int b=1, c=0; c<30; b *= 2, c++) if (set->boolean_annotations & b) SymbolAnnotation::write_annotation(OUT, F, SymbolAnnotation::from_pair((inter_ti) c, TRUE)); if (set->other_annotations) { inter_annotation *A; LOOP_OVER_LINKED_LIST(A, inter_annotation, set->other_annotations) SymbolAnnotation::write_annotation(OUT, F, *A); } } } void SymbolAnnotation::write_annotation(OUTPUT_STREAM, inter_tree_node *F, inter_annotation IA) { WRITE(" %S", IA.annot->annotation_keyword); switch (IA.annot->iatype) { case TEXTUAL_IATYPE: WRITE("="); TextualInter::write_text(OUT, Inode::ID_to_text(F, IA.annot_value)); break; case INTEGER_IATYPE: WRITE("=%d", IA.annot_value); break; case BOOLEAN_IATYPE: break; } }
§15. And annotations can also be parsed, with the same syntax:
inter_annotation SymbolAnnotation::read_annotation(inter_tree *I, text_stream *text, inter_error_location *eloc, inter_error_message **E) { inter_ti val = TRUE; int iatype = BOOLEAN_IATYPE; *E = NULL; LOOP_THROUGH_TEXT(P, text) if (Str::get(P) == '=') { if (Str::get(Str::forward(P)) == '"') { TEMPORARY_TEXT(parsed_text) inter_error_message *EP = TextualInter::parse_literal_text(parsed_text, text, P.index+2, Str::len(text)-2, NULL); inter_warehouse *warehouse = InterTree::warehouse(I); val = InterWarehouse::create_text(warehouse, InterTree::root_package(I)); Str::copy(InterWarehouse::get_text(warehouse, val), parsed_text); DISCARD_TEXT(parsed_text) if (EP) *E = EP; iatype = TEXTUAL_IATYPE; } else { val = (inter_ti) Str::atoi(text, P.index + 1); iatype = INTEGER_IATYPE; } Str::truncate(text, P.index); } inter_annotation_form *IAF; LOOP_OVER(IAF, inter_annotation_form) if (Str::eq(text, IAF->annotation_keyword)) { if (IAF->iatype != iatype) *E = InterErrors::quoted(I"wrong sort of value for annotation", IAF->annotation_keyword, eloc); inter_annotation IA; IA.annot = IAF; IA.annot_value = val; return IA; } *E = InterErrors::quoted(I"unrecognised annotation", text, eloc); return SymbolAnnotation::invalid_annotation(); }