1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-05 16:44:21 +03:00
inform7/inter/index-module/Chapter 2/Index Interpreter.w
2021-07-07 23:43:23 +01:00

236 lines
8.2 KiB
OpenEdge ABL

[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;
@<Initialise IGS@>;
@<Read the structure file line by line@>;
@<Actually generate the index files@>;
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;
@<Initialise IGS@> =
igs.pages = NEW_LINKED_LIST(index_page);
igs.dict = D;
@<Read the structure file line by line@> =
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+)")) {
@<Register a new index page@>;
} else if (Regexp::match(&mr, text, L"contents (%C+) (%C+)")) {
@<Register a new index page@>;
} else if (Regexp::match(&mr, text, L"element (%C+)")) {
@<Register a new index element@>;
}
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.
@<Register a new index page@> =
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);
@<Register a new index element@> =
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"));
@<Actually generate the index files@> =
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);
}