[InterpretIndex::] Index Interpreter. An index is generated by interpreting an index structure file. @ The index is generated by a bare-bones structure file. These are identified by name: in particular |Basic| is the barer version, for Basic Inform projects, while |Standard| is the index as generated to suit typical IF uses of the Standard Rules -- so, in other words, that's the index which most Inform users know. These names have to correspond to |.indext| structure files stored in the Inform distribution. The structure file really just shows which index elements to include on which pages. For example: = (text) page Kinds e9cf08 element Ch element Ar element Vl = declares a new index page with leafname |Kinds.html|, and with the highlight colour (in CSS hexadecimal style) |e9cf08|; and then declzres that it contains the three elements |Ch|, |Ar| and |Vl|, in that order. Note that this file does not contain any user-facing text: all of that comes from //Localisation//, with |%%Kinds|, |%%Ch|, |%%Ar| and |%%Vl| being localisation contexts. The file also doesn't contain instructions for what goes into those elements. All of that is hardwired into this module's code: for example, |Ar| is generated by the function //ArithmeticElement::render// in //Arithmetic Element//. The method here is basically to read all the declarations, generating a set of //index_page// and //index_element// objects to represent them, and only to begin writing some HTML when this scanning is done, i.e., when the whole structure file has been read. = void InterpretIndex::generate(inter_tree *I, text_stream *structure, localisation_dictionary *D) { InterpretIndex::set_tree(I); filename *index_structure = InstalledFiles::index_structure_file(structure); index_generation_state igs; @; @; @; InterpretIndex::set_tree(NULL); } @ This little structure is just some state being carried by the reader-function which goes through the |.indext| file line by line. = typedef struct index_generation_state { struct localisation_dictionary *dict; struct linked_list *pages; /* of |index_page| */ } index_generation_state; @ = igs.pages = NEW_LINKED_LIST(index_page); igs.dict = D; @ = TextFiles::read(index_structure, FALSE, "unable to read index structure file", TRUE, &InterpretIndex::read_structure, NULL, (void *) &igs); @ |contents| is currently implemented identically to |pages|, but it is supposed to represent the home page of the index. The declaration for it should come last in the structure file, like so: = (text) contents Welcome 111111 = It has no elements, because its only content will be a menu of links to the other pages. = void InterpretIndex::read_structure(text_stream *text, text_file_position *tfp, void *state) { index_generation_state *igs = (index_generation_state *) state; localisation_dictionary *D = igs->dict; match_results mr = Regexp::create_mr(); if (Regexp::match(&mr, text, L"page (%C+) (%C+)")) { @; } else if (Regexp::match(&mr, text, L"contents (%C+) (%C+)")) { @; } else if (Regexp::match(&mr, text, L"element (%C+)")) { @; } Regexp::dispose_of(&mr); } @ Note that the heading written on |Kinds.html|, say, is drawn from the localisation file under the key |%Title| in the context |%%Kinds|. In English, that will be "Kinds", but if the index is being generated with a Dutch localisation dictionary then perhaps it would be "Soorten". Under the hood, though, the filename will be |Kinds.html| either way. @ = text_stream *col = mr.exp[1]; text_stream *key = mr.exp[0]; text_stream *heading = Localisation::read(D, key, I"Title"); text_stream *explanation = Localisation::read(D, key, I"Caption"); index_page *new_page = InterpretIndex::register_page(col, heading, explanation, key); ADD_TO_LINKED_LIST(new_page, index_page, igs->pages); @ = if (LinkedLists::len(igs->pages) == 0) internal_error("element without page"); index_page *latest = LAST_IN_LINKED_LIST(index_page, igs->pages); text_stream *elt = mr.exp[0]; InterpretIndex::register_element(elt, latest, Localisation::read(D, elt, I"Title"), Localisation::read(D, elt, I"Heading")); @ = index_page *page; LOOP_OVER_LINKED_LIST(page, index_page, igs.pages) { TEMPORARY_TEXT(leafname) WRITE_TO(leafname, "%S.html", page->page_leafname); text_stream *OUT = InterpretIndex::open_file(page, leafname, Localisation::read(D, page->page_leafname, I"Title"), -1, D); Elements::periodic_table(OUT, page, leafname, D); InterpretIndex::close_index_file(OUT); DISCARD_TEXT(leafname) } GroupedElement::detail_pages(D); @ This is a one-off function for generating the content of an index element (without its heading, or any HTML surround): it's used for unit-testing those elements. = void InterpretIndex::generate_one_element(OUTPUT_STREAM, inter_tree *I, wording elt, localisation_dictionary *D) { InterpretIndex::set_tree(I); Elements::test_card(OUT, elt, D); InterpretIndex::set_tree(NULL); } @h Registering. So, then, here are the objects representing the index pages and their elements of content: = typedef struct index_page { int no_elements; struct text_stream *key_colour; struct text_stream *page_title; struct text_stream *page_explanation; struct text_stream *page_leafname; CLASS_DEFINITION } index_page; index_page *InterpretIndex::register_page(text_stream *col, text_stream *title, text_stream *exp, text_stream *leaf) { index_page *new_page = CREATE(index_page); new_page->no_elements = 0; new_page->key_colour = Str::duplicate(col); new_page->page_title = Str::duplicate(title); new_page->page_explanation = Str::duplicate(exp); new_page->page_leafname = Str::duplicate(leaf); return new_page; } @ = typedef struct index_element { int atomic_number; /* 1, 2, 3, ..., within its page */ struct text_stream *chemical_symbol; struct text_stream *element_name; struct text_stream *explanatory_note; struct index_page *owning_page; CLASS_DEFINITION } index_element; index_element *InterpretIndex::register_element(text_stream *abb, index_page *owner, text_stream *title, text_stream *explanation) { if (owner == NULL) internal_error("template creates index elements improperly"); if (Str::len(abb) > 2) internal_error("abbreviation for index element too long"); index_element *ie = CREATE(index_element); ie->owning_page = owner; ie->atomic_number = ++(owner->no_elements); ie->chemical_symbol = Str::duplicate(abb); ie->element_name = Str::duplicate(title); ie->explanatory_note = Str::duplicate(explanation); return ie; } @h Current. A clumsy convenience: keeping track of the page currently being written, and of the Inter tree currently being indexed. = index_page *current_index_page = NULL; void InterpretIndex::set_page(index_page *page) { current_index_page = page; } index_page *InterpretIndex::get_page(void) { return current_index_page; } inter_tree *indexing_tree = NULL; void InterpretIndex::set_tree(inter_tree *I) { indexing_tree = I; } inter_tree *InterpretIndex::get_tree(void) { if (indexing_tree == NULL) internal_error("no indexing tree"); return indexing_tree; } @h Opening and closing HTML files. = text_stream index_file_struct; /* The current index file being written */ text_stream *InterpretIndex::open_file(index_page *page, text_stream *index_leaf, text_stream *title, int sub, localisation_dictionary *D) { filename *F = IndexLocations::filename(index_leaf, sub); if (STREAM_OPEN_TO_FILE(&index_file_struct, F, UTF8_ENC) == FALSE) { #ifdef CORE_MODULE Problems::fatal_on_file("Can't open index file", F); #endif #ifndef CORE_MODULE Errors::fatal_with_file("can't open index file", F); #endif } text_stream *OUT = &index_file_struct; InterpretIndex::set_page(page); HTML::header(OUT, title, InstalledFiles::filename(CSS_FOR_STANDARD_PAGES_IRES), InstalledFiles::filename(JAVASCRIPT_FOR_STANDARD_PAGES_IRES)); return OUT; } @ = void InterpretIndex::close_index_file(text_stream *ifl) { HTML::footer(ifl); STREAM_CLOSE(ifl); InterpretIndex::set_page(NULL); }