1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-26 04:00:43 +03:00

Moved census mode out of core Inform

This commit is contained in:
Graham Nelson 2020-02-23 12:36:39 +00:00
parent 7cc9c598f3
commit 9206d88dbb
32 changed files with 754 additions and 649 deletions

View file

@ -22,7 +22,7 @@
set: $I6OPTIONS = -kE2~S~Dwv8
set: $BLORBEXT = zblorb
endif
set: $I7OPTIONS = -fixtime -release -format=$FORMAT -noprogress -rng -sigils -clock -log nothing -external inblorb/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7OPTIONS = -fixtime -release -format=$FORMAT -noprogress -rng -sigils -log nothing -external inblorb/Tests -transient $WORK/Transient -internal $INTERNAL
set: $CBLORBOPTIONS = -fixtime
mkdir: $WORK/Transient

View file

@ -28,7 +28,7 @@ linked_list *inbuild_nest_list = NULL;
int main(int argc, char **argv) {
Foundation::start();
WordsModule::start();
HTMLModule::start();
ArchModule::start();
InbuildModule::start();
targets = NEW_LINKED_LIST(inbuild_copy);
@ -74,6 +74,7 @@ int main(int argc, char **argv) {
WordsModule::end();
ArchModule::end();
InbuildModule::end();
HTMLModule::end();
Foundation::end();
return 0;
}

View file

@ -8,6 +8,7 @@ Version Name: Avignon
Import: foundation
Import: inform7/words
Import: html
Import: arch
Import: inbuild

View file

@ -0,0 +1,41 @@
[HTMLModule::] Index Module.
Setting up the use of this module.
@h Introduction.
@d HTML_MODULE TRUE
@ To begin with, this module needs to allocate memory:
=
@h The beginning.
(The client doesn't need to call the start and end routines, because the
foundation module does that automatically.)
=
void HTMLModule::start(void) {
@<Register this module's stream writers@>;
@<Register this module's debugging log aspects@>;
@<Register this module's debugging log writers@>;
@<Register this module's command line switches@>;
}
@<Register this module's stream writers@> =
;
@<Register this module's debugging log aspects@> =
;
@<Register this module's debugging log writers@> =
;
@<Register this module's command line switches@> =
;
@h The end.
=
void HTMLModule::end(void) {
}

View file

@ -235,6 +235,12 @@ far as the user is concerned it opens the example and goes there.
HTML_CLOSE("a");
HTML_TAG("br");
@
=
<table-sentence> ::=
<if-start-of-paragraph> table ...
@h Setting the body text.
@d EDOC_ALL_EXAMPLES_CLOSED -1 /* do not change this without also changing Extensions */
@ -406,7 +412,7 @@ need to achieve with an HTML |<table>|.
HTML_OPEN("blockquote");
HTML::begin_colour(OUT, I"000080");
mid_displayed_source_text = TRUE;
if ((<structural-sentence>(Wordings::from(W, i))) && (ssnt == TABLE_NT))
if (<table-sentence>(Wordings::from(W, i)))
start_table_next_line = TRUE;
}
indentation--;

View file

@ -150,14 +150,20 @@ int outcome_image_style = SIDE_OUTCOME_IMAGE_STYLE;
void HTMLFiles::html_outcome_image(OUTPUT_STREAM, char *image, char *verdict) {
char *vn = "";
if (internal_error_thrown == FALSE) {
int be_festive = TRUE;
#ifdef CORE_MODULE
if (internal_error_thrown == FALSE) be_festive = FALSE;
#endif
if (be_festive) {
switch (Time::feast()) {
case CHRISTMAS_FEAST: vn = "_2"; break;
case EASTER_FEAST: vn = "_3"; break;
}
if (vn[0]) outcome_image_style = CENTRED_OUTCOME_IMAGE_STYLE;
}
#ifdef PROBLEMS_MODULE
Problems::Issue::issue_problems_banner(OUT, verdict);
#endif
switch (outcome_image_style) {
case CENTRED_OUTCOME_IMAGE_STYLE:
HTML_OPEN("p");
@ -197,11 +203,16 @@ void HTMLFiles::outcome_image_tail(OUTPUT_STREAM) {
void HTMLFiles::html_header(OUTPUT_STREAM, text_stream *title) {
HTML::declare_as_HTML(OUT, FALSE);
HTML::begin_head(OUT, NULL);
HTML::incorporate_CSS(OUT,
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(pathname_of_HTML_models, I"main.js"));
pathname *models = Extensions::Census::doc_models();
if (models) {
HTML::incorporate_CSS(OUT,
Filenames::in_folder(models, I"main.css"));
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(models, I"main.js"));
}
#ifdef INDEX_MODULE
Index::scripting(OUT);
#endif
HTML::end_head(OUT);
HTML::begin_body(OUT, NULL);
HTML::comment(OUT, I"CONTENT BEGINS");
@ -250,7 +261,12 @@ int source_ref_field = -1; /* which field we are buffering */
void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
if (source_ref_field >= 0) {
if (source_ref_fields[source_ref_field] == NULL) source_ref_fields[source_ref_field] = Str::new();
#ifdef PROBLEMS_MODULE
if (charcode != SOURCE_REF_CHAR) { PUT_TO(source_ref_fields[source_ref_field], charcode); return; }
#endif
#ifndef PROBLEMS_MODULE
PUT_TO(source_ref_fields[source_ref_field], charcode); return;
#endif
}
switch(charcode) {
case '"': WRITE("&quot;"); return;
@ -258,6 +274,7 @@ void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
case '>': WRITE("&gt;"); return;
case '&': WRITE("&amp;"); break;
case NEWLINE_IN_STRING: HTML_TAG("br"); return;
#ifdef PROBLEMS_MODULE
case FORCE_NEW_PARA_CHAR: HTML_CLOSE("p"); HTML_OPEN_WITH("p", "class=\"in2\"");
HTMLFiles::html_icon_with_tooltip(OUT, "ornament_flower.png", NULL, NULL);
WRITE("&nbsp;"); return;
@ -271,6 +288,7 @@ void HTMLFiles::char_out(OUTPUT_STREAM, int charcode) {
HTMLFiles::html_source_link(OUT, sl, TRUE);
} else Str::clear(source_ref_fields[source_ref_field]);
return;
#endif
default:
PUT(charcode);
return;
@ -291,189 +309,3 @@ void HTMLFiles::write_xml_safe_text(OUTPUT_STREAM, text_stream *txt) {
}
}
}
@h Bibliographic text.
"Bibliographic text" is text used in bibliographic data about the work
of IF compiled: for instance, in the iFiction record, or in the Library
Card section of the HTML index. Note that the exact output format depends
on global variables, which allow the bibliographic text writing code to
configure NI for its current purposes. On non-empty strings this routine
therefore splits into one of three independent methods.
=
void HTMLFiles::compile_bibliographic_text(OUTPUT_STREAM, wchar_t *p) {
if (p == NULL) return;
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_XML_CMODE))
@<Compile bibliographic text as XML respecting Treaty of Babel rules@>;
if (TEST_COMPILATION_MODE(TRUNCATE_TEXT_CMODE))
@<Compile bibliographic text as a truncated filename@>;
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_I6_CMODE))
@<Compile bibliographic text as an I6 string@>
@<Compile bibliographic text as HTML@>;
}
@ This looks like a standard routine for converting ISO Latin-1 to UTF-8
with XML escapes, but there are a few conventions on whitespace, too, in order
to comply with a strict reading of the Treaty of Babel. (This is intended
for fields in iFiction records.)
@<Compile bibliographic text as XML respecting Treaty of Babel rules@> =
int i = 0, i2 = Wide::len(p)-1, snl, wsc;
if ((p[0] == '"') && (p[i2] == '"')) { i++; i2--; } /* omit surrounding double-quotes */
while (Characters::is_babel_whitespace(p[i])) i++; /* omit leading whitespace */
while ((i2>=0) && (Characters::is_babel_whitespace(p[i2]))) i2--; /* omit trailing whitespace */
for (snl = FALSE, wsc = 0; i<=i2; i++) {
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t':
snl = FALSE;
wsc++;
int k = i;
while ((p[k] == ' ') || (p[k] == '\x0a') || (p[k] == '\x0d') || (p[k] == '\t')) k++;
if ((wsc == 1) && (p[k] != NEWLINE_IN_STRING)) WRITE(" ");
break;
case NEWLINE_IN_STRING:
if (snl) break;
WRITE("<br/>");
snl = TRUE; wsc = 1; break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
WRITE("'"); break;
}
int n = CompiledText::expand_unisub(OUT, p, i);
if (n >= 0) { i = n; break; }
/* and otherwise fall through to the default case */
default:
snl = FALSE;
wsc = 0;
switch(p[i]) {
case '&': WRITE("&amp;"); break;
case '<': WRITE("&lt;"); break;
case '>': WRITE("&gt;"); break;
default: PUT(p[i]); break;
}
break;
}
}
return;
@ In the HTML version, we want to respect the forcing of newlines, and
also the |[']| escape to obtain a literal single quotation mark.
@<Compile bibliographic text as HTML@> =
int i, whitespace_count=0;
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t':
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case NEWLINE_IN_STRING:
while (p[i+1] == NEWLINE_IN_STRING) i++;
PUT('<');
PUT('p');
PUT('>');
whitespace_count = 1;
break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
PUT('\''); break;
}
int n = CompiledText::expand_unisub(OUT, p, i);
if (n >= 0) { i = n; break; }
/* and otherwise fall through to the default case */
default:
whitespace_count = 0;
PUT(p[i]);
break;
}
}
return;
@ In the Inform 6 string version, we suppress the forcing of newlines, but
otherwise it's much the same.
@<Compile bibliographic text as an I6 string@> =
int i, whitespace_count=0;
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
PUT('\''); break;
} /* and otherwise fall through to the default case */
default:
whitespace_count = 0;
PUT(p[i]);
break;
}
}
return;
@ This code is used to work out a good filename for something given a name
inside NI. For instance, if a project is called
>> "St. Bartholemew's Fair: \'Etude for a Push-Me/Pull-You Machine"
then what would be a good filename for its released story file?
In the filename version we must forcibly truncate the text to ensure
that it does not exceed a certain length, and must also make it filename-safe,
omitting characters used as folder separators on various platforms and
(for good measure) removing accents from accented letters, so that we can
arrive at a sequence of ASCII characters. Each run of whitespace is also
converted to a single space. If this would result in an empty text or only
a single space, we return the text "story" instead.
Our example (if not truncated) then emerges as:
|St- Bartholemew's Fair- Etude for a Push-Me-Pull-You Machine|
Note that we do not write any filename extension (e.g., |.z5|) here.
We change possible filename separators or extension indicators to hyphens,
and remove accents from each possible ISO Latin-1 accented letter. This does
still mean that the OE and AE digraphs will simply be omitted, while the
German eszet will be barbarously shortened to a single "s", but life is
just too short to care overmuch about this.
@<Compile bibliographic text as a truncated filename@> =
int i, pos = STREAM_EXTENT(OUT), whitespace_count=0, black_chars_written = 0;
int N = 100;
#ifdef IF_MODULE
N = BIBLIOGRAPHIC_TEXT_TRUNCATION;
#endif
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if (STREAM_EXTENT(OUT) - pos >= N) break;
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case '?': case '*':
if ((p[i+1]) && (p[i+1] != '\"')) PUT('-');
break;
default: {
int charcode = p[i];
charcode = Characters::make_filename_safe(charcode);
whitespace_count = 0;
if (charcode < 128) {
PUT(charcode); black_chars_written++;
}
break;
}
}
}
if (black_chars_written == 0) WRITE("story");
return;

View file

@ -0,0 +1,14 @@
Title: html
Author: Graham Nelson
Purpose: HTML and Javascript generation tools.
Language: InC
Licence: Artistic License 2.0
Chapter 1: Starting Up
HTML Module
Chapter 2: HTML
"HTML and JavaScript generation."
HTML Files
Javascript Pastes
HTML Documentation

View file

@ -46,6 +46,7 @@ to add and process command line switches handled by inbuild:
@e DEBUG_CLSW
@e FORMAT_CLSW
@e RELEASE_CLSW
@e CENSUS_CLSW
=
void Inbuild::declare_options(void) {
@ -71,6 +72,8 @@ void Inbuild::declare_options(void) {
L"compile I6 code suitable for the virtual machine X");
CommandLine::declare_switch(SOURCE_CLSW, L"source", 2,
L"use file X as the Inform source text");
CommandLine::declare_boolean_switch(CENSUS_CLSW, L"census", 1,
L"perform an extensions census");
CommandLine::end_group();
}
@ -78,6 +81,7 @@ pathname *shared_transient_resources = NULL;
int this_is_a_debug_compile = FALSE; /* Destined to be compiled with debug features */
int this_is_a_release_compile = FALSE; /* Omit sections of source text marked not for release */
text_stream *story_filename_extension = NULL; /* What story file we will eventually have */
int census_mode = FALSE; /* Running only to update extension documentation */
void Inbuild::option(int id, int val, text_stream *arg, void *state) {
RUN_ONLY_IN_PHASE(CONFIGURATION_INBUILD_PHASE)
@ -98,6 +102,7 @@ void Inbuild::option(int id, int val, text_stream *arg, void *state) {
if (Inbuild::set_I7_source(arg) == FALSE)
Errors::fatal_with_text("can't specify the source file twice: '%S'", arg);
break;
case CENSUS_CLSW: census_mode = val; break;
}
}
@ -165,6 +170,7 @@ void Inbuild::go_operational(void) {
LOOP_OVER(C, inbuild_copy)
Copies::go_operational(C);
inbuild_phase = OPERATIONAL_INBUILD_PHASE;
if (census_mode) Extensions::Census::handle_census_mode();
}
@h The nest list.

View file

@ -31,6 +31,8 @@ Setting up the use of this module.
@e inform_language_MT
@e inform_pipeline_MT
@e copy_error_MT
@e extension_dictionary_entry_MT
@e known_extension_clash_MT
=
ALLOCATE_INDIVIDUALLY(inform_kit)
@ -55,6 +57,8 @@ ALLOCATE_INDIVIDUALLY(inform_project)
ALLOCATE_INDIVIDUALLY(inform_language)
ALLOCATE_INDIVIDUALLY(inform_pipeline)
ALLOCATE_INDIVIDUALLY(copy_error)
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
ALLOCATE_INDIVIDUALLY(known_extension_clash)
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)

View file

@ -728,3 +728,148 @@ void Extensions::Census::write_icons(OUTPUT_STREAM, compatibility_specification
if (Compatibility::with(C, VM))
Extensions::Census::plot_icon(OUT, VM);
}
@h Updating the documentation.
This is done in the course of taking an extension census, which is called
for in one of two circumstances: when Inform is being run in "census mode" to
notify it that extensions have been installed or uninstalled; or when Inform
has completed the successful compilation of a source text. In the latter
case, it knows quite a lot about the extensions actually used in that
compilation, and so can write detailed versions of their documentation:
since it is updating extension documentation anyway, it conducts a census
as well. (In both cases the extension dictionary is also worked upon.) The
two alternatives are expressed here:
=
void Extensions::Census::handle_census_mode(void) {
extension_census *C = Extensions::Census::new();
Extensions::Dictionary::load();
Extensions::Census::perform(C);
Extensions::Census::write_top_level_of_extensions_documentation(C);
Extensions::Census::write_sketchy_documentation_for_extensions_found(TRUE);
}
void Extensions::Census::update_census(void) {
Extensions::Dictionary::load();
extension_census *C = Extensions::Census::new();
Extensions::Census::perform(C);
Extensions::Census::write_top_level_of_extensions_documentation(C);
#ifdef CORE_MODULE
inform_extension *E;
LOOP_OVER(E, inform_extension) Extensions::Documentation::write_detailed(E);
#endif
Extensions::Census::write_sketchy_documentation_for_extensions_found(FALSE);
Extensions::Dictionary::write_back();
if (Log::aspect_switched_on(EXTENSIONS_CENSUS_DA)) Works::log_work_hash_table();
}
@ Documenting extensions seen but not used: we run through the census
results in no particular order and create a sketchy page of documentation,
if there's no better one already.
=
void Extensions::Census::write_sketchy_documentation_for_extensions_found(int census_mode) {
extension_census_datum *ecd;
LOOP_OVER(ecd, extension_census_datum)
Extensions::Documentation::write_sketchy(ecd, census_mode);
}
@h Writing the extensions home pages.
Extensions documentation forms a mini-website within the Inform
documentation. There is a top level consisting of two home pages: a
directory of all installed extensions, and an index to the terms defined in
those extensions. A cross-link switches between them. Each of these links
down to the bottom level, where there is a page for every installed
extension (wherever it is installed). The picture is therefore something
like this:
= (not code)
(Main documentation contents page)
|
Extensions.html--ExtIndex.html
| \/ |
| /\ |
Nigel Toad/Eggs Barnabas Dundritch/Neopolitan Iced Cream ...
@ These pages are stored at the relative pathnames
|Extensions/Documentation/Extensions.html|
|Extensions/Documentation/ExtIndex.html|
They are made by inserting content in place of the material between the
HTML anchors |on| and |off| in a template version of the page built in
to the application, with a leafname which varies from platform to
platform, for reasons as always to do with the vagaries of Internet
Explorer 7 for Windows.
=
void Extensions::Census::write_top_level_of_extensions_documentation(extension_census *C) {
Extensions::Census::write_top_level_extensions_page(I"Extensions.html", 1, C);
Extensions::Census::write_top_level_extensions_page(I"ExtIndex.html", 2, NULL);
}
@ =
pathname *Extensions::Census::doc_pathname(void) {
pathname *P = Inbuild::transient();
if (P == NULL) return NULL;
if (Pathnames::create_in_file_system(P) == 0) return NULL;
P = Pathnames::subfolder(P, I"Documentation");
if (Pathnames::create_in_file_system(P) == 0) return NULL;
return P;
}
pathname *Extensions::Census::doc_models(void) {
inbuild_nest *I = Inbuild::internal(); if (I == NULL) return NULL;
return Pathnames::subfolder(I->location, I"HTML");
}
void Extensions::Census::write_top_level_extensions_page(text_stream *leaf, int content, extension_census *C) {
pathname *P = Extensions::Census::doc_pathname();
if (P == NULL) return;
filename *F = Filenames::in_folder(P, leaf);
pathname *models = Extensions::Census::doc_models();
if (models == NULL) return;
text_stream HOMEPAGE_struct;
text_stream *OUT = &HOMEPAGE_struct;
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE) {
#ifdef CORE_MODULE
Problems::Fatal::filename_related(
"Unable to open extensions documentation index for writing", F);
#endif
#ifndef CORE_MODULE
Errors::fatal_with_file("extensions documentation index for writing: %f", F);
#endif
}
HTML::declare_as_HTML(OUT, FALSE);
HTML::begin_head(OUT, NULL);
HTML::title(OUT, I"Extensions");
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(models, I"extensions.js"));
HTML::incorporate_CSS(OUT,
Filenames::in_folder(models, I"main.css"));
HTML::end_head(OUT);
HTML::begin_body(OUT, NULL);
HTML::begin_html_table(OUT, NULL, TRUE, 0, 4, 0, 0, 0);
HTML::first_html_column(OUT, 0);
HTML_TAG_WITH("img", "src='inform:/doc_images/extensions@2x.png' border=0 width=150 height=150");
HTML::next_html_column(OUT, 0);
HTML_OPEN_WITH("div", "class=\"headingboxDark\"");
HTML_OPEN_WITH("div", "class=\"headingtextWhite\"");
WRITE("Installed Extensions");
HTML_CLOSE("div");
HTML_OPEN_WITH("div", "class=\"headingrubricWhite\"");
WRITE("Bundles of extra rules or phrases to extend what Inform can do");
HTML_CLOSE("div");
HTML_CLOSE("div");
switch (content) {
case 1: Extensions::Census::write_results(OUT, C); break;
case 2: Extensions::Dictionary::write_to_HTML(OUT); break;
}
HTML::end_body(OUT);
}

View file

@ -215,11 +215,20 @@ Not a surprising routine: open, convert one line at a time to dictionary
entries, close.
=
filename *Extensions::Dictionary::filename(void) {
pathname *P = Inbuild::transient();
if (P == NULL) return NULL;
return Filenames::in_folder(P, I"Dictionary.txt");
}
void Extensions::Dictionary::load(void) {
filename *F = Extensions::Dictionary::filename();
if (F == NULL) return;
@<Ensure the serialised extensions dictionary file exists@>;
LOGIF(EXTENSIONS_CENSUS, "Reading dictionary file\n");
TextFiles::read(filename_of_extensions_dictionary, FALSE,
TextFiles::read(F, FALSE,
NULL, FALSE, Extensions::Dictionary::load_helper, NULL, NULL);
LOGIF(EXTENSIONS_CENSUS, "Finished reading dictionary file\n");
}
@ -233,10 +242,10 @@ file, we are then unable to open it again: that must mean a file I/O error of
some kind, which is bad enough news to bother the user with.
@<Ensure the serialised extensions dictionary file exists@> =
FILE *DICTF = Filenames::fopen(filename_of_extensions_dictionary, "r");
FILE *DICTF = Filenames::fopen(F, "r");
if (DICTF == NULL) {
LOGIF(EXTENSIONS_CENSUS, "Creating new empty dictionary file\n");
FILE *EMPTY_DICTF = Filenames::fopen(filename_of_extensions_dictionary, "w");
FILE *EMPTY_DICTF = Filenames::fopen(F, "w");
if (EMPTY_DICTF == NULL) return;
fclose(EMPTY_DICTF);
}
@ -305,7 +314,9 @@ void Extensions::Dictionary::write_back(void) {
text_stream DICTF_struct;
text_stream *DICTF = &DICTF_struct;
if (STREAM_OPEN_TO_FILE(DICTF, filename_of_extensions_dictionary, UTF8_ENC) == FALSE) return;
filename *F = Extensions::Dictionary::filename();
if (F == NULL) return;
if (STREAM_OPEN_TO_FILE(DICTF, F, UTF8_ENC) == FALSE) return;
LOGIF(EXTENSIONS_CENSUS, "Writing dictionary file\n");

View file

@ -10,10 +10,10 @@ understand what it entails). The following routine writes both kinds of page.
=
void Extensions::Documentation::write_detailed(inform_extension *E) {
Extensions::Documentation::write_extension_documentation(NULL, E);
Extensions::Documentation::write_extension_documentation(NULL, E, FALSE);
}
void Extensions::Documentation::write_sketchy(extension_census_datum *ecd) {
Extensions::Documentation::write_extension_documentation(ecd, NULL);
void Extensions::Documentation::write_sketchy(extension_census_datum *ecd, int census_mode) {
Extensions::Documentation::write_extension_documentation(ecd, NULL, census_mode);
}
@ Thus we pass two arguments, |ecd| and |ef|, to |Extensions::Documentation::write_extension_documentation|:
@ -35,11 +35,11 @@ in sequence,
where these are pathnames relative to the external resources area.
=
void Extensions::Documentation::write_extension_documentation(extension_census_datum *ecd, inform_extension *E) {
void Extensions::Documentation::write_extension_documentation(extension_census_datum *ecd, inform_extension *E, int census_mode) {
int c, eg_count;
eg_count = Extensions::Documentation::write_extension_documentation_page(ecd, E, -1);
eg_count = Extensions::Documentation::write_extension_documentation_page(ecd, E, -1, census_mode);
for (c=1; c<=eg_count; c++)
Extensions::Documentation::write_extension_documentation_page(ecd, E, c);
Extensions::Documentation::write_extension_documentation_page(ecd, E, c, census_mode);
}
@ Here then is the nub of it. An ECD is not really enough information to go on.
@ -57,7 +57,7 @@ is any, as well as the correct identifying headings and requirements.
=
int Extensions::Documentation::write_extension_documentation_page(extension_census_datum *ecd, inform_extension *E,
int eg_number) {
int eg_number, int census_mode) {
inbuild_work *work = NULL;
text_stream DOCF_struct;
text_stream *DOCF = &DOCF_struct;
@ -71,7 +71,7 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
TEMPORARY_TEXT(leaf);
Str::copy(leaf, work->title);
if (eg_number > 0) WRITE_TO(leaf, "-eg%d", eg_number);
filename *name = Locations::of_extension_documentation(leaf, work->author_name);
filename *name = Extensions::Documentation::location(leaf, work->author_name);
page_exists_already = FALSE;
TEST_DOCF = Filenames::fopen(name, "r");
@ -86,8 +86,9 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
}
if (E == NULL) internal_error("null E in extension documentation writer");
if (Pathnames::create_in_file_system(
Pathnames::subfolder(pathname_of_extension_docs_inner, work->author_name)) == 0)
pathname *P = Extensions::Documentation::path();
if (P == NULL) return 0;
if (Pathnames::create_in_file_system(Pathnames::subfolder(P, work->author_name)) == 0)
return 0;
if (STREAM_OPEN_TO_FILE(DOCF, name, UTF8_ENC) == FALSE)
@ -99,6 +100,27 @@ int Extensions::Documentation::write_extension_documentation_page(extension_cens
return no_egs;
}
pathname *Extensions::Documentation::path(void) {
pathname *P = Inbuild::transient();
if ((P == NULL) || (Pathnames::create_in_file_system(P) == 0)) return NULL;
P = Pathnames::subfolder(P, I"Extensions");
if (Pathnames::create_in_file_system(P) == 0) return NULL;
P = Pathnames::subfolder(P, I"Documentation");
if (Pathnames::create_in_file_system(P) == 0) return NULL;
return P;
}
filename *Extensions::Documentation::location(text_stream *title, text_stream *author) {
pathname *P = Extensions::Documentation::path();
if (P == NULL) return NULL;
TEMPORARY_TEXT(leaf);
WRITE_TO(leaf, "%S.html", title);
filename *F = Filenames::in_folder(Pathnames::subfolder(P, author), leaf);
DISCARD_TEXT(leaf);
return F;
}
@ The reader may wonder why we perform the conversion in this slightly recursive
way, by calling our parent routine again. Wouldn't it be simpler just to set
|ecd| to null and let events take their course? The answer is that this would
@ -110,45 +132,36 @@ our EF, and return 0 in response to the ECD call to prevent any further ECD
calls.
@<Convert ECD to a text-only EF@> =
feed_t id = Feeds::begin();
Feeds::feed_stream(work->raw_author_name);
Feeds::feed_text(L" ");
wording AW = Feeds::end(id);
id = Feeds::begin();
Feeds::feed_stream(work->raw_title);
Feeds::feed_text(L" ");
wording TW = Feeds::end(id);
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
if (<unsuitable-name>(AW)) return 0;
if (<unsuitable-name>(TW)) return 0;
inbuild_requirement *req = Requirements::any_version_of(work);
E = Extensions::Inclusion::load(req);
E = Extensions::Documentation::load(work);
if (E == NULL) return 0; /* shouldn't happen: it was there only moments ago */
Extensions::Documentation::write_extension_documentation(NULL, E);
Copies::read_source_text_for(E->as_copy);
Extensions::Documentation::write_extension_documentation(NULL, E, census_mode);
@ We now make much the same "paste into the gap in the template" copying
exercise as when generating the home pages for extensions, though with a
different template:
@<Write the actual extension documentation page to DOCF@> =
text_stream *OUT = DOCF;
HTML::declare_as_HTML(OUT, FALSE);
pathname *models = Extensions::Census::doc_models();
if (models) {
text_stream *OUT = DOCF;
HTML::declare_as_HTML(OUT, FALSE);
HTML::begin_head(OUT, NULL);
HTML::title(OUT, I"Extension");
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(pathname_of_HTML_models, I"extensionfile.js"));
HTML::incorporate_CSS(OUT,
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
HTML::end_head(OUT);
HTML::begin_head(OUT, NULL);
HTML::title(OUT, I"Extension");
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(models, I"extensionfile.js"));
HTML::incorporate_CSS(OUT,
Filenames::in_folder(models, I"main.css"));
HTML::end_head(OUT);
HTML::begin_body(OUT, NULL);
HTML::incorporate_HTML(OUT,
Filenames::in_folder(pathname_of_HTML_models, I"extensionfile.html"));
@<Write documentation for a specific extension into the page@>;
HTML::end_body(OUT);
HTML::begin_body(OUT, NULL);
HTML::incorporate_HTML(OUT,
Filenames::in_folder(models, I"extensionfile.html"));
@<Write documentation for a specific extension into the page@>;
HTML::end_body(OUT);
}
@ And this is the body:
@ -168,7 +181,9 @@ different template:
HTML_CLOSE("p");
@<Write up the rubric, if any@>;
@<Write up the table of contents for the extension author's supplied documentation, if any@>;
@<Document and dictionary the definitions made in extension file E@>;
#ifdef CORE_MODULE
Extensions::Files::document_in_detail(OUT, E);
#endif
HTML_TAG("hr");
@<Write up the extension author's supplied documentation, if any@>;
@ -233,175 +248,34 @@ easily be scrolled down off screen when the user first visits the page.
}
}
@ Nothing can prevent a certain repetitiousness intruding here, but there is
just enough local knowledge required to make it foolhardy to try to automate
this from a dump of the excerpt meanings table (say). The ordering of
paragraphs, as in Roget's Thesaurus, tries to proceed from solid things
through to diffuse linguistic ones. But the reader of the resulting
documentation page could be forgiven for thinking it a miscellany.
@<Document and dictionary the definitions made in extension file E@> =
Extensions::Dictionary::erase_entries(E);
if (E) Extensions::Dictionary::time_stamp(E);
@<Document and dictionary the kinds made in extension@>;
@<Document and dictionary the objects made in extension@>;
@<Document and dictionary the global variables made in extension@>;
@<Document and dictionary the enumerated constant values made in extension@>;
@<Document and dictionary the kinds of action made in extension@>;
@<Document and dictionary the actions made in extension@>;
@<Document and dictionary the verbs made in extension@>;
@<Document and dictionary the adjectival phrases made in extension@>;
@<Document and dictionary the property names made in extension@>;
@<Document and dictionary the use options made in extension@>;
@ Off we go, then. Kinds of object:
@<Document and dictionary the kinds made in extension@> =
kind *K;
int kc = 0;
LOOP_OVER_BASE_KINDS(K) {
parse_node *S = Kinds::Behaviour::get_creating_sentence(K);
if (S) {
if (Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(S))) == E->read_into_file) {
wording W = Kinds::Behaviour::get_name(K, FALSE);
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Kinds", I"kind", W);
kind *S = Kinds::Compare::super(K);
if (S) {
W = Kinds::Behaviour::get_name(S, FALSE);
if (Wordings::nonempty(W)) WRITE(" (a kind of %+W)", W);
}
}
}
}
if (kc != 0) HTML_CLOSE("p");
@ Actual objects:
@<Document and dictionary the objects made in extension@> =
instance *I;
int kc = 0;
LOOP_OVER_OBJECT_INSTANCES(I) {
wording OW = Instances::get_name(I, FALSE);
if ((Instances::get_creating_sentence(I)) && (Wordings::nonempty(OW))) {
if (Lexer::file_of_origin(
Wordings::first_wn(ParseTree::get_text(Instances::get_creating_sentence(I))))
== E->read_into_file) {
TEMPORARY_TEXT(name_of_its_kind);
kind *k = Instances::to_kind(I);
wording W = Kinds::Behaviour::get_name(k, FALSE);
WRITE_TO(name_of_its_kind, "%+W", W);
kc = Extensions::Documentation::document_headword(OUT, kc, E,
"Physical creations", name_of_its_kind, OW);
WRITE(" (a %S)", name_of_its_kind);
DISCARD_TEXT(name_of_its_kind);
}
}
}
if (kc != 0) HTML_CLOSE("p");
@ Global variables:
@<Document and dictionary the global variables made in extension@> =
nonlocal_variable *q;
int kc = 0;
LOOP_OVER(q, nonlocal_variable)
if ((Wordings::first_wn(q->name) >= 0) &&
(NonlocalVariables::is_global(q)) &&
(Lexer::file_of_origin(Wordings::first_wn(q->name)) == E->read_into_file) &&
(Sentences::Headings::indexed(Sentences::Headings::of_wording(q->name)))) {
if (<value-understood-variable-name>(q->name) == FALSE)
kc = Extensions::Documentation::document_headword(OUT,
kc, E, "Values that vary", I"value", q->name);
}
if (kc != 0) HTML_CLOSE("p");
@ Constants:
@<Document and dictionary the enumerated constant values made in extension@> =
instance *q;
int kc = 0;
LOOP_OVER_ENUMERATION_INSTANCES(q) {
wording NW = Instances::get_name(q, FALSE);
if ((Wordings::nonempty(NW)) && (Lexer::file_of_origin(Wordings::first_wn(NW)) == E->read_into_file))
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Values", I"value", NW);
}
if (kc != 0) HTML_CLOSE("p");
@ Kinds of action:
@<Document and dictionary the kinds of action made in extension@> =
#ifdef IF_MODULE
PL::Actions::Patterns::Named::index_for_extension(OUT, E->read_into_file, E);
#endif
@ Actions:
@<Document and dictionary the actions made in extension@> =
#ifdef IF_MODULE
PL::Actions::Index::index_for_extension(OUT, E->read_into_file, E);
#endif
@ Verbs (this one we delegate):
@<Document and dictionary the verbs made in extension@> =
Index::Lexicon::list_verbs_in_file(OUT, E->read_into_file, E);
@ Adjectival phrases:
@<Document and dictionary the adjectival phrases made in extension@> =
adjectival_phrase *adj;
int kc = 0;
LOOP_OVER(adj, adjectival_phrase) {
wording W = Adjectives::get_text(adj, FALSE);
if ((Wordings::nonempty(W)) &&
(Lexer::file_of_origin(Wordings::first_wn(W)) == E->read_into_file))
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Adjectives", I"adjective", W);
}
if (kc != 0) HTML_CLOSE("p");
@ Other adjectives:
@<Document and dictionary the property names made in extension@> =
property *prn;
int kc = 0;
LOOP_OVER(prn, property)
if ((Wordings::nonempty(prn->name)) &&
(Properties::is_shown_in_index(prn)) &&
(Lexer::file_of_origin(Wordings::first_wn(prn->name)) == E->read_into_file))
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Properties", I"property",
prn->name);
if (kc != 0) HTML_CLOSE("p");
@ Use options:
@<Document and dictionary the use options made in extension@> =
use_option *uo;
int kc = 0;
LOOP_OVER(uo, use_option)
if ((Wordings::first_wn(uo->name) >= 0) &&
(Lexer::file_of_origin(Wordings::first_wn(uo->name)) == E->read_into_file))
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Use options", I"use option",
uo->name);
if (kc != 0) HTML_CLOSE("p");
@ Finally, the utility routine which keeps count (hence |kc|) and displays
suitable lists, while entering each entry in turn into the extension
dictionary.
@
=
int Extensions::Documentation::document_headword(OUTPUT_STREAM, int kc, inform_extension *E, char *par_heading,
text_stream *category, wording W) {
if (kc++ == 0) { HTML_OPEN("p"); WRITE("%s: ", par_heading); }
else WRITE(", ");
WRITE("<b>%+W</b>", W);
Extensions::Dictionary::new_entry(category, E, W);
return kc;
}
inform_extension *Extensions::Documentation::load(inbuild_work *work) {
inbuild_requirement *req = Requirements::any_version_of(work);
req->allow_malformed = TRUE;
@ And that at last brings us to a milestone: the end of the Land of Extensions.
We can return to Inform's more usual concerns.
inform_extension *E;
LOOP_OVER(E, inform_extension)
if (Requirements::meets(E->as_copy->edition, req)) {
Extensions::must_satisfy(E, req);
return E;
}
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
Nests::search_for(req, Inbuild::nest_list(), L);
inbuild_search_result *search_result;
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
E = ExtensionManager::from_copy(search_result->copy);
int origin = Nests::get_tag(search_result->nest);
switch (origin) {
case MATERIALS_NEST_TAG:
case EXTERNAL_NEST_TAG:
E->loaded_from_built_in_area = FALSE; break;
case INTERNAL_NEST_TAG:
E->loaded_from_built_in_area = TRUE; break;
}
return E;
}
return NULL;
}

View file

@ -19,8 +19,8 @@ typedef struct inform_extension {
struct text_stream *extra_credit_as_lexed;
struct source_file *read_into_file; /* Which source file loaded this */
struct inbuild_requirement *must_satisfy;
#ifdef CORE_MODULE
int loaded_from_built_in_area; /* Located within Inform application */
#ifdef CORE_MODULE
struct parse_node *inclusion_sentence; /* Where the source called for this */
#endif
MEMORY_MANAGEMENT
@ -40,8 +40,8 @@ void Extensions::scan(inbuild_genre *G, inbuild_copy *C) {
E->rubric_as_lexed = Str::new();
E->extra_credit_as_lexed = NULL;
E->must_satisfy = NULL;
#ifdef CORE_MODULE
E->loaded_from_built_in_area = FALSE;
#ifdef CORE_MODULE
E->inclusion_sentence = NULL;
#endif
@ -282,17 +282,18 @@ void Extensions::make_standard(inform_extension *E) {
E->standard = TRUE;
}
#ifdef CORE_MODULE
void Extensions::must_satisfy(inform_extension *E, inbuild_requirement *req) {
if (E->must_satisfy == NULL) E->must_satisfy = req;
else {
semantic_version_number V = req->min_version;
if (VersionNumbers::is_null(V) == FALSE)
if (Requirements::ratchet_minimum(V, E->must_satisfy))
if (Requirements::ratchet_minimum(V, E->must_satisfy)) {
#ifdef CORE_MODULE
Extensions::set_inclusion_sentence(E, current_sentence);
#endif
}
}
}
#endif
int Extensions::satisfies(inform_extension *E) {
if (E == NULL) return FALSE;
@ -305,9 +306,7 @@ int Extensions::satisfies(inform_extension *E) {
void Extensions::read_source_text_for(inform_extension *E) {
filename *F = E->as_copy->location_if_file;
int doc_only = FALSE;
#ifdef CORE_MODULE
if (CoreMain::census_mode()) doc_only = TRUE;
#endif
if (census_mode) doc_only = TRUE;
TEMPORARY_TEXT(synopsis);
@<Concoct a synopsis for the extension to be read@>;
E->read_into_file = SourceText::read_file(E->as_copy, F, synopsis, doc_only, FALSE);

View file

@ -36,6 +36,8 @@ Chapter 5: Services for the Inform Compiler
Virtual Machine Grammar
Kit Services
Extension Services
Extension Dictionary
Extension Documentation
Extension Census
Template Services
Project Services

View file

@ -54,6 +54,7 @@ int Main::core_inform_main(int argc, char *argv[]) {
CoreModule::start();
IFModule::start();
MultimediaModule::start();
HTMLModule::start();
IndexModule::start();
ArchModule::start();
InterModule::start();
@ -73,6 +74,7 @@ int Main::core_inform_main(int argc, char *argv[]) {
CoreModule::end();
IFModule::end();
IndexModule::end();
HTMLModule::end();
InterModule::end();
ArchModule::end();
BuildingModule::end();

View file

@ -16,6 +16,7 @@ Import: syntax
Import: problems
Import: linguistics
Import: kinds
Import: inbuild/html
Import: inbuild/arch
Import: inter/inter
Import: inbuild/inbuild

View file

@ -71,9 +71,9 @@
mkdir: $PATH/_Results_Ideal
if: $CASE PM_Map%c*
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
else
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
set: $I7OPTIONS = -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
endif
if: $VM G
@ -191,9 +191,9 @@
mkdir: $PATH/_Results_Ideal
if: $CASE PM_Map%c*
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
else
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
set: $I7OPTIONS = -kit BasicInformKit -format=$FORMAT -noprogress -fixtime -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL
endif
if: $VM G
@ -273,7 +273,7 @@
set: $VM = G
extract: $WORK/Example.inform/Source/story.ni $VM
set: $I7OPTIONS = -fixtime -format=ulx -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7OPTIONS = -fixtime -format=ulx -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7CONSOLE = $WORK/Example.inform/Build/i7_output.txt
mkdir: $WORK/Transient
@ -370,7 +370,7 @@
mkdir: $PATH/_Maps_Actual
mkdir: $PATH/_Maps_Ideal
set: $I7OPTIONS = -format=ulx -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7OPTIONS = -format=ulx -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -internal $INTERNAL
set: $I7CONSOLE = $WORK/Example.inform/Build/i7_output.txt
step: $I7 `$I7OPTIONS -project $WORK/Example.inform >$I7CONSOLE 2>&1
@ -422,7 +422,7 @@
copy: $WORKSPACE/Samples/Powermac.aiff $WORK/Example.materials/Sounds/Powermac.aiff
endif
set: $I7OPTIONS = -fixtime -format=$FORMAT -noprogress -rng -sigils -clock -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL -release
set: $I7OPTIONS = -fixtime -format=$FORMAT -noprogress -rng -sigils -log nothing -external inform7/Tests -transient $WORK/Transient -noindex -internal $INTERNAL -release
mkdir: $WORK/Transient
step: find $WORK/Transient -mindepth 1 -delete

View file

@ -36,8 +36,6 @@ We need to itemise the structures we'll want to allocate:
@e pcalc_prop_deferral_MT
@e literal_pattern_MT
@e generalisation_MT
@e extension_dictionary_entry_MT
@e known_extension_clash_MT
@e i6_schema_array_MT
@e list_together_routine_MT
@e past_tense_condition_record_MT
@ -113,7 +111,6 @@ ALLOCATE_INDIVIDUALLY(dval_written)
ALLOCATE_INDIVIDUALLY(equation_node)
ALLOCATE_INDIVIDUALLY(equation_symbol)
ALLOCATE_INDIVIDUALLY(equation)
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
ALLOCATE_INDIVIDUALLY(generalisation)
ALLOCATE_INDIVIDUALLY(heading)
ALLOCATE_INDIVIDUALLY(i6_inclusion_matter)
@ -125,7 +122,6 @@ ALLOCATE_INDIVIDUALLY(instance)
ALLOCATE_INDIVIDUALLY(internal_test_case)
ALLOCATE_INDIVIDUALLY(inv_token_problem_token)
ALLOCATE_INDIVIDUALLY(kind_interaction)
ALLOCATE_INDIVIDUALLY(known_extension_clash)
ALLOCATE_INDIVIDUALLY(list_together_routine)
ALLOCATE_INDIVIDUALLY(literal_list)
ALLOCATE_INDIVIDUALLY(literal_pattern_name)

View file

@ -10,7 +10,6 @@ options with inbuild: see that module for more.
=
int existing_story_file = FALSE; /* Ignore source text to blorb existing story file? */
int rng_seed_at_start_of_play = 0; /* The seed value, or 0 if not seeded */
int census_mode = FALSE; /* Inform running only to update extension documentation */
int show_progress_indicator = TRUE; /* Produce percentage of progress messages */
int scoring_option_set = NOT_APPLICABLE; /* Whether in this case a score is kept at run time */
int disable_import = FALSE;
@ -40,7 +39,6 @@ int model_world_constructed = FALSE; /* World model is now constructed */
int indexing_stage = FALSE; /* Everything is done except indexing */
@ =
int report_clock_time = FALSE;
time_t right_now;
text_stream *inter_processing_file = NULL;
text_stream *inter_processing_pipeline = NULL;
@ -48,7 +46,6 @@ dictionary *pipeline_vars = NULL;
pathname *path_to_inform7 = NULL;
int CoreMain::main(int argc, char *argv[]) {
clock_t start = clock();
@<Banner and startup@>;
@<Register command-line arguments@>;
int proceed = CommandLine::read(argc, argv, NULL, &CoreMain::switch, &CoreMain::bareword);
@ -57,23 +54,10 @@ int CoreMain::main(int argc, char *argv[]) {
@<With that done, configure all other settings@>;
@<Open the debugging log and the problems report@>;
@<Boot up the compiler@>;
if (census_mode)
Extensions::Files::handle_census_mode();
else {
@<Work out our kit requirements@>;
@<Perform lexical analysis@>;
@<Perform semantic analysis@>;
@<Read the assertions in two passes@>;
@<Make the model world@>;
@<Tables and grammar@>;
@<Phrases and rules@>;
@<Generate inter@>;
@<Convert inter to Inform 6@>;
@<Generate metadata@>;
@<Post mortem logging@>;
}
Inbuild::go_operational();
inform_project *project = Inbuild::project();
if (project) CoreMain::task(project);
}
clock_t end = clock();
@<Shutdown and rennab@>;
if (problem_count > 0) Problems::Fatal::exit(1);
return 0;
@ -94,8 +78,6 @@ arguments in order to set certain pathnames or filenames, so the following
list is not exhaustive.
@e CASE_CLSW
@e CENSUS_CLSW
@e CLOCK_CLSW
@e CRASHALL_CLSW
@e NOINDEX_CLSW
@e NOPROGRESS_CLSW
@ -111,10 +93,6 @@ list is not exhaustive.
L"inform7: a compiler from source text to Inform 6 code\n\n"
L"Usage: inform7 [OPTIONS] [SOURCETEXT]\n");
CommandLine::declare_boolean_switch(CENSUS_CLSW, L"census", 1,
L"perform an extensions census (rather than compile)");
CommandLine::declare_boolean_switch(CLOCK_CLSW, L"clock", 1,
L"time how long inform7 takes to run");
CommandLine::declare_boolean_switch(CRASHALL_CLSW, L"crash-all", 1,
L"crash intentionally on Problem messages (for debugger backtraces)");
CommandLine::declare_boolean_switch(NOINDEX_CLSW, L"noindex", 1,
@ -142,7 +120,7 @@ list is not exhaustive.
@<With that done, configure all other settings@> =
Inbuild::optioneering_complete(NULL);
if (Locations::set_defaults(census_mode) == FALSE)
if (Locations::set_defaults() == FALSE)
Problems::Fatal::issue("Unable to create folders in local file system");
Log::set_debug_log_filename(filename_of_debugging_log);
NaturalLanguages::default_to_English();
@ -155,6 +133,24 @@ list is not exhaustive.
CommandLine::play_back_log();
Problems::Issue::start_problems_report();
@ =
void CoreMain::task(inform_project *project) {
clock_t start = clock();
@<Perform lexical analysis@>;
@<Perform semantic analysis@>;
@<Read the assertions in two passes@>;
@<Make the model world@>;
@<Tables and grammar@>;
@<Phrases and rules@>;
@<Generate inter@>;
@<Convert inter to Inform 6@>;
@<Generate metadata@>;
@<Post mortem logging@>;
clock_t end = clock();
int cpu_time_used = ((int) (end - start)) / (CLOCKS_PER_SEC/100);
LOG("CPU time: %d centiseconds\n", cpu_time_used);
}
@
@d COMPILATION_STEP(routine, mark) {
@ -183,9 +179,6 @@ list is not exhaustive.
COMPILATION_STEP(Index::DocReferences::read_xrefs, I"Index::DocReferences::read_xrefs")
doc_references_top = lexer_wordcount - 1;
@<Work out our kit requirements@> =
Inbuild::go_operational();
@<Perform lexical analysis@> =
ProgressBar::update_progress_bar(0, 0);
if (problem_count == 0) CoreMain::go_to_log_phase(I"Lexical analysis");
@ -448,14 +441,9 @@ with "Output.i6t".
Problems::write_reports(FALSE);
LOG("Total of %d files written as streams.\n", total_file_writes);
int cpu_time_used = ((int) (end - start)) / (CLOCKS_PER_SEC/100);
LOG("CPU time: %d centiseconds\n", cpu_time_used);
Writers::log_escape_usage();
WRITE_TO(STDOUT, "%s has finished", HUMAN_READABLE_INTOOL_NAME);
if (report_clock_time)
WRITE_TO(STDOUT, ": %d centiseconds used", cpu_time_used);
WRITE_TO(STDOUT, ".\n");
WRITE_TO(STDOUT, "%s has finished.\n", HUMAN_READABLE_INTOOL_NAME);
}
@ -473,8 +461,6 @@ void CoreMain::go_to_log_phase(text_stream *argument) {
void CoreMain::switch(int id, int val, text_stream *arg, void *state) {
switch (id) {
/* Miscellaneous boolean settings */
case CENSUS_CLSW: census_mode = val; break;
case CLOCK_CLSW: report_clock_time = val; break;
case CRASHALL_CLSW: debugger_mode = val; crash_on_all_errors = val; break;
case NOINDEX_CLSW: do_not_generate_index = val; break;
case NOPROGRESS_CLSW: show_progress_indicator = val?FALSE:TRUE; break;
@ -508,10 +494,6 @@ void CoreMain::switch(int id, int val, text_stream *arg, void *state) {
Inbuild::option(id, val, arg, state);
}
int CoreMain::census_mode(void) {
return census_mode;
}
void CoreMain::bareword(int id, text_stream *opt, void *state) {
if (Inbuild::set_I7_source(opt) == FALSE)
Errors::fatal_with_text("unknown command line argument: %S (see -help)", opt);

View file

@ -11,8 +11,6 @@ these are stored in the following globals. Explanations are given below,
not here.
= (early code)
pathname *pathname_of_extension_docs = NULL;
pathname *pathname_of_extension_docs_inner = NULL;
pathname *pathname_of_HTML_models = NULL;
pathname *pathname_of_materials_figures = NULL;
pathname *pathname_of_materials_release = NULL;
@ -22,7 +20,6 @@ pathname *pathname_of_project_index_folder = NULL;
pathname *pathname_of_released_figures = NULL;
pathname *pathname_of_released_interpreter = NULL;
pathname *pathname_of_released_sounds = NULL;
pathname *pathname_of_transient_census_data = NULL;
@ And secondly, the files:
@ -35,7 +32,6 @@ filename *filename_of_debugging_log = NULL;
filename *filename_of_documentation_snippets = NULL;
filename *filename_of_epsfile = NULL;
filename *filename_of_existing_story_file = NULL;
filename *filename_of_extensions_dictionary = NULL;
filename *filename_of_headings = NULL;
filename *filename_of_ifiction_record = NULL;
filename *filename_of_intro_booklet = NULL;
@ -66,16 +62,11 @@ Inform can run in two modes: regular mode, when it's compiling source text,
and census mode, when it's scanning the file system for extensions.
=
int Locations::set_defaults(int census_mode) {
int Locations::set_defaults(void) {
@<Internal resources@>;
@<External resources@>;
@<Project resources@>;
@<Materials resources@>;
inform_project *project = Inbuild::project();
if ((census_mode == FALSE) && (project == NULL))
Problems::Fatal::issue("Except in census mode, source text must be supplied");
if ((census_mode) && (project))
Problems::Fatal::issue("In census mode, no source text may be supplied");
return TRUE;
}
@ -152,30 +143,8 @@ not distinguish between permanent and transient external resources.
pathname *pathname_of_transient_external_resources = Inbuild::transient();
if (Pathnames::create_in_file_system(pathname_of_transient_external_resources) == 0) return FALSE;
@<Transient documentation@>;
@<Transient telemetry@>;
@ The documentation folder is in effect a little website of its own, generated
automatically by Inform. There'll be some files at the top level, and then
there are files on each extension, in suitable subfolders. The census data
subfolder is not browsable or linked to, but holds working files needed when
assembling all this.
@<Transient documentation@> =
pathname_of_extension_docs =
Pathnames::subfolder(pathname_of_transient_external_resources, I"Documentation");
if (Pathnames::create_in_file_system(pathname_of_extension_docs) == 0) return FALSE;
pathname_of_transient_census_data =
Pathnames::subfolder(pathname_of_extension_docs, I"Census");
if (Pathnames::create_in_file_system(pathname_of_transient_census_data) == 0) return FALSE;
filename_of_extensions_dictionary =
Filenames::in_folder(pathname_of_transient_census_data, I"Dictionary.txt");
pathname_of_extension_docs_inner =
Pathnames::subfolder(pathname_of_extension_docs, I"Extensions");
if (Pathnames::create_in_file_system(pathname_of_extension_docs_inner) == 0) return FALSE;
@ Telemetry is not as sinister as it sounds: the app isn't sending data out
on the Internet, only (if requested) logging what it's doing to a local file.
This was provided for classroom use, so that teachers can see what their
@ -225,7 +194,7 @@ during the compilation process. The opening part here may be a surprise:
In extension census mode, Inform is running not to compile something but to
extract details of all the extensions installed. But it still needs somewhere
to write its temporary and debugging files, and there is no project bundle
to write into. To get round this, we use the census data area as if it
to write into. To get round this, we use the transient data area as if it
were indeed a project bundle.
Briefly: we aim to compile the source text to an Inform 6 program; we issue
@ -236,12 +205,8 @@ called; and similarly for the report which the releasing tool Inblorb
will produce if this is a Release run.
@<The Build folder within the project@> =
pathname *build_folder = pathname_of_transient_census_data;
if (census_mode == FALSE) {
build_folder = Pathnames::subfolder(proj, I"Build");
if (Pathnames::create_in_file_system(build_folder) == 0) return FALSE;
}
pathname *build_folder = Pathnames::subfolder(proj, I"Build");
if (Pathnames::create_in_file_system(build_folder) == 0) return FALSE;
filename_of_report = Filenames::in_folder(build_folder, I"Problems.html");
filename_of_debugging_log = Filenames::in_folder(build_folder, I"Debug log.txt");
@ -271,10 +236,9 @@ the index as seen by the user.
pathname_of_project_index_details_folder =
Pathnames::subfolder(pathname_of_project_index_folder, I"Details");
if (census_mode == FALSE)
if ((Pathnames::create_in_file_system(pathname_of_project_index_folder) == 0) ||
(Pathnames::create_in_file_system(pathname_of_project_index_details_folder) == 0))
return FALSE;
if ((Pathnames::create_in_file_system(pathname_of_project_index_folder) == 0) ||
(Pathnames::create_in_file_system(pathname_of_project_index_details_folder) == 0))
return FALSE;
filename_of_headings =
Filenames::in_folder(pathname_of_project_index_folder, I"Headings.xml");
@ -360,18 +324,6 @@ filename *Locations::of_extension(pathname *E, text_stream *title, text_stream *
return F;
}
@ Documentation is similarly arranged:
=
filename *Locations::of_extension_documentation(text_stream *title, text_stream *author) {
TEMPORARY_TEXT(leaf);
WRITE_TO(leaf, "%S.html", title);
filename *F = Filenames::in_folder(
Pathnames::subfolder(pathname_of_extension_docs_inner, author), leaf);
DISCARD_TEXT(leaf);
return F;
}
@h Location of index files.
Filenames within the |Index| subfolder. Filenames in |Details| have the form
|N_S| where |N| is the integer supplied and |S| the leafname; for instance,

View file

@ -361,122 +361,177 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, inform_extension *f
}
}
@h Updating the documentation.
This is done in the course of taking an extension census, which is called
for in one of two circumstances: when Inform is being run in "census mode" to
notify it that extensions have been installed or uninstalled; or when Inform
has completed the successful compilation of a source text. In the latter
case, it knows quite a lot about the extensions actually used in that
compilation, and so can write detailed versions of their documentation:
since it is updating extension documentation anyway, it conducts a census
as well. (In both cases the extension dictionary is also worked upon.) The
two alternatives are expressed here:
@ Nothing can prevent a certain repetitiousness intruding here, but there is
just enough local knowledge required to make it foolhardy to try to automate
this from a dump of the excerpt meanings table (say). The ordering of
paragraphs, as in Roget's Thesaurus, tries to proceed from solid things
through to diffuse linguistic ones. But the reader of the resulting
documentation page could be forgiven for thinking it a miscellany.
=
void Extensions::Files::handle_census_mode(void) {
if (census_mode) {
extension_census *C = Extensions::Census::new();
Extensions::Dictionary::load();
Extensions::Census::perform(C);
Extensions::Files::write_top_level_of_extensions_documentation(C);
Extensions::Files::write_sketchy_documentation_for_extensions_found();
void Extensions::Files::document_in_detail(OUTPUT_STREAM, inform_extension *E) {
Extensions::Dictionary::erase_entries(E);
if (E) Extensions::Dictionary::time_stamp(E);
@<Document and dictionary the kinds made in extension@>;
@<Document and dictionary the objects made in extension@>;
@<Document and dictionary the global variables made in extension@>;
@<Document and dictionary the enumerated constant values made in extension@>;
@<Document and dictionary the kinds of action made in extension@>;
@<Document and dictionary the actions made in extension@>;
@<Document and dictionary the verbs made in extension@>;
@<Document and dictionary the adjectival phrases made in extension@>;
@<Document and dictionary the property names made in extension@>;
@<Document and dictionary the use options made in extension@>;
}
@ Off we go, then. Kinds of object:
@<Document and dictionary the kinds made in extension@> =
kind *K;
int kc = 0;
LOOP_OVER_BASE_KINDS(K) {
parse_node *S = Kinds::Behaviour::get_creating_sentence(K);
if (S) {
if (Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(S))) == E->read_into_file) {
wording W = Kinds::Behaviour::get_name(K, FALSE);
kc = Extensions::Files::document_headword(OUT, kc, E, "Kinds", I"kind", W);
kind *S = Kinds::Compare::super(K);
if (S) {
W = Kinds::Behaviour::get_name(S, FALSE);
if (Wordings::nonempty(W)) WRITE(" (a kind of %+W)", W);
}
}
}
}
}
if (kc != 0) HTML_CLOSE("p");
void Extensions::Files::update_census(void) {
Extensions::Dictionary::load();
extension_census *C = Extensions::Census::new();
Extensions::Census::perform(C);
Extensions::Files::write_top_level_of_extensions_documentation(C);
inform_extension *E;
LOOP_OVER(E, inform_extension) Extensions::Documentation::write_detailed(E);
Extensions::Files::write_sketchy_documentation_for_extensions_found();
Extensions::Dictionary::write_back();
if (Log::aspect_switched_on(EXTENSIONS_CENSUS_DA)) Works::log_work_hash_table();
}
@ Actual objects:
@ Documenting extensions seen but not used: we run through the census
results in no particular order and create a sketchy page of documentation,
if there's no better one already.
=
void Extensions::Files::write_sketchy_documentation_for_extensions_found(void) {
extension_census_datum *ecd;
LOOP_OVER(ecd, extension_census_datum)
Extensions::Documentation::write_sketchy(ecd);
}
@h Writing the extensions home pages.
Extensions documentation forms a mini-website within the Inform
documentation. There is a top level consisting of two home pages: a
directory of all installed extensions, and an index to the terms defined in
those extensions. A cross-link switches between them. Each of these links
down to the bottom level, where there is a page for every installed
extension (wherever it is installed). The picture is therefore something
like this:
= (not code)
(Main documentation contents page)
|
Extensions.html--ExtIndex.html
| \/ |
| /\ |
Nigel Toad/Eggs Barnabas Dundritch/Neopolitan Iced Cream ...
@ These pages are stored at the relative pathnames
|Extensions/Documentation/Extensions.html|
|Extensions/Documentation/ExtIndex.html|
They are made by inserting content in place of the material between the
HTML anchors |on| and |off| in a template version of the page built in
to the application, with a leafname which varies from platform to
platform, for reasons as always to do with the vagaries of Internet
Explorer 7 for Windows.
=
void Extensions::Files::write_top_level_of_extensions_documentation(extension_census *C) {
Extensions::Files::write_top_level_extensions_page(I"Extensions.html", 1, C);
Extensions::Files::write_top_level_extensions_page(I"ExtIndex.html", 2, NULL);
}
@ =
void Extensions::Files::write_top_level_extensions_page(text_stream *leaf, int content, extension_census *C) {
text_stream HOMEPAGE_struct;
text_stream *OUT = &HOMEPAGE_struct;
filename *F = Filenames::in_folder(pathname_of_extension_docs, leaf);
if (STREAM_OPEN_TO_FILE(OUT, F, UTF8_ENC) == FALSE)
Problems::Fatal::filename_related(
"Unable to open extensions documentation index for writing", F);
HTML::declare_as_HTML(OUT, FALSE);
HTML::begin_head(OUT, NULL);
HTML::title(OUT, I"Extensions");
HTML::incorporate_javascript(OUT, TRUE,
Filenames::in_folder(pathname_of_HTML_models, I"extensions.js"));
HTML::incorporate_CSS(OUT,
Filenames::in_folder(pathname_of_HTML_models, I"main.css"));
HTML::end_head(OUT);
HTML::begin_body(OUT, NULL);
HTML::begin_html_table(OUT, NULL, TRUE, 0, 4, 0, 0, 0);
HTML::first_html_column(OUT, 0);
HTML_TAG_WITH("img", "src='inform:/doc_images/extensions@2x.png' border=0 width=150 height=150");
HTML::next_html_column(OUT, 0);
HTML_OPEN_WITH("div", "class=\"headingboxDark\"");
HTML_OPEN_WITH("div", "class=\"headingtextWhite\"");
WRITE("Installed Extensions");
HTML_CLOSE("div");
HTML_OPEN_WITH("div", "class=\"headingrubricWhite\"");
WRITE("Bundles of extra rules or phrases to extend what Inform can do");
HTML_CLOSE("div");
HTML_CLOSE("div");
switch (content) {
case 1: Extensions::Census::write_results(OUT, C); break;
case 2: Extensions::Dictionary::write_to_HTML(OUT); break;
@<Document and dictionary the objects made in extension@> =
instance *I;
int kc = 0;
LOOP_OVER_OBJECT_INSTANCES(I) {
wording OW = Instances::get_name(I, FALSE);
if ((Instances::get_creating_sentence(I)) && (Wordings::nonempty(OW))) {
if (Lexer::file_of_origin(
Wordings::first_wn(ParseTree::get_text(Instances::get_creating_sentence(I))))
== E->read_into_file) {
TEMPORARY_TEXT(name_of_its_kind);
kind *k = Instances::to_kind(I);
wording W = Kinds::Behaviour::get_name(k, FALSE);
WRITE_TO(name_of_its_kind, "%+W", W);
kc = Extensions::Files::document_headword(OUT, kc, E,
"Physical creations", name_of_its_kind, OW);
WRITE(" (a %S)", name_of_its_kind);
DISCARD_TEXT(name_of_its_kind);
}
}
}
if (kc != 0) HTML_CLOSE("p");
HTML::end_body(OUT);
@ Global variables:
@<Document and dictionary the global variables made in extension@> =
nonlocal_variable *q;
int kc = 0;
LOOP_OVER(q, nonlocal_variable)
if ((Wordings::first_wn(q->name) >= 0) &&
(NonlocalVariables::is_global(q)) &&
(Lexer::file_of_origin(Wordings::first_wn(q->name)) == E->read_into_file) &&
(Sentences::Headings::indexed(Sentences::Headings::of_wording(q->name)))) {
if (<value-understood-variable-name>(q->name) == FALSE)
kc = Extensions::Files::document_headword(OUT,
kc, E, "Values that vary", I"value", q->name);
}
if (kc != 0) HTML_CLOSE("p");
@ Constants:
@<Document and dictionary the enumerated constant values made in extension@> =
instance *q;
int kc = 0;
LOOP_OVER_ENUMERATION_INSTANCES(q) {
wording NW = Instances::get_name(q, FALSE);
if ((Wordings::nonempty(NW)) && (Lexer::file_of_origin(Wordings::first_wn(NW)) == E->read_into_file))
kc = Extensions::Files::document_headword(OUT, kc, E, "Values", I"value", NW);
}
if (kc != 0) HTML_CLOSE("p");
@ Kinds of action:
@<Document and dictionary the kinds of action made in extension@> =
#ifdef IF_MODULE
PL::Actions::Patterns::Named::index_for_extension(OUT, E->read_into_file, E);
#endif
@ Actions:
@<Document and dictionary the actions made in extension@> =
#ifdef IF_MODULE
PL::Actions::Index::index_for_extension(OUT, E->read_into_file, E);
#endif
@ Verbs (this one we delegate):
@<Document and dictionary the verbs made in extension@> =
Index::Lexicon::list_verbs_in_file(OUT, E->read_into_file, E);
@ Adjectival phrases:
@<Document and dictionary the adjectival phrases made in extension@> =
adjectival_phrase *adj;
int kc = 0;
LOOP_OVER(adj, adjectival_phrase) {
wording W = Adjectives::get_text(adj, FALSE);
if ((Wordings::nonempty(W)) &&
(Lexer::file_of_origin(Wordings::first_wn(W)) == E->read_into_file))
kc = Extensions::Files::document_headword(OUT, kc, E, "Adjectives", I"adjective", W);
}
if (kc != 0) HTML_CLOSE("p");
@ Other adjectives:
@<Document and dictionary the property names made in extension@> =
property *prn;
int kc = 0;
LOOP_OVER(prn, property)
if ((Wordings::nonempty(prn->name)) &&
(Properties::is_shown_in_index(prn)) &&
(Lexer::file_of_origin(Wordings::first_wn(prn->name)) == E->read_into_file))
kc = Extensions::Files::document_headword(OUT, kc, E, "Properties", I"property",
prn->name);
if (kc != 0) HTML_CLOSE("p");
@ Use options:
@<Document and dictionary the use options made in extension@> =
use_option *uo;
int kc = 0;
LOOP_OVER(uo, use_option)
if ((Wordings::first_wn(uo->name) >= 0) &&
(Lexer::file_of_origin(Wordings::first_wn(uo->name)) == E->read_into_file))
kc = Extensions::Files::document_headword(OUT, kc, E, "Use options", I"use option",
uo->name);
if (kc != 0) HTML_CLOSE("p");
@ Finally, the utility routine which keeps count (hence |kc|) and displays
suitable lists, while entering each entry in turn into the extension
dictionary.
=
int Extensions::Files::document_headword(OUTPUT_STREAM, int kc, inform_extension *E, char *par_heading,
text_stream *category, wording W) {
if (kc++ == 0) { HTML_OPEN("p"); WRITE("%s: ", par_heading); }
else WRITE(", ");
WRITE("<b>%+W</b>", W);
Extensions::Dictionary::new_entry(category, E, W);
return kc;
}
@ And that at last brings us to a milestone: the end of the Land of Extensions.
We can return to Inform's more usual concerns.

View file

@ -69,8 +69,6 @@ which, we also handle extension installation, uninstallation and
documentation here."
Extension Files
Including Extensions
Extension Dictionary
Extension Documentation
Chapter 9: The A-Parser
"We work through the assertion sentences in the parse tree one by one, and

View file

@ -171,7 +171,7 @@ int PL::Bibliographic::story_author_is(text_stream *p) {
TEMPORARY_TEXT(TEMP);
wording W = ParseTree::get_text(spec);
int w1 = Wordings::first_wn(W);
HTMLFiles::compile_bibliographic_text(TEMP, Lexer::word_text(w1));
PL::Bibliographic::compile_bibliographic_text(TEMP, Lexer::word_text(w1));
if (Str::eq(TEMP, p)) result = TRUE;
DISCARD_TEXT(TEMP);
return result;
@ -400,9 +400,195 @@ void PL::Bibliographic::index_bibliographic_variable(OUTPUT_STREAM, nonlocal_var
COMPILATION_MODE_ENTER(COMPILE_TEXT_TO_XML_CMODE);
if ((nlv) && (NonlocalVariables::has_initial_value_set(nlv))) {
wording W = NonlocalVariables::treat_as_plain_text_word(nlv);
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(Wordings::first_wn(W)));
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(Wordings::first_wn(W)));
} else {
WRITE("%S", t);
}
END_COMPILATION_MODE;
}
@h Bibliographic text.
"Bibliographic text" is text used in bibliographic data about the work
of IF compiled: for instance, in the iFiction record, or in the Library
Card section of the HTML index. Note that the exact output format depends
on global variables, which allow the bibliographic text writing code to
configure NI for its current purposes. On non-empty strings this routine
therefore splits into one of three independent methods.
=
void PL::Bibliographic::compile_bibliographic_text(OUTPUT_STREAM, wchar_t *p) {
if (p == NULL) return;
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_XML_CMODE))
@<Compile bibliographic text as XML respecting Treaty of Babel rules@>;
if (TEST_COMPILATION_MODE(TRUNCATE_TEXT_CMODE))
@<Compile bibliographic text as a truncated filename@>;
if (TEST_COMPILATION_MODE(COMPILE_TEXT_TO_I6_CMODE))
@<Compile bibliographic text as an I6 string@>
@<Compile bibliographic text as HTML@>;
}
@ This looks like a standard routine for converting ISO Latin-1 to UTF-8
with XML escapes, but there are a few conventions on whitespace, too, in order
to comply with a strict reading of the Treaty of Babel. (This is intended
for fields in iFiction records.)
@<Compile bibliographic text as XML respecting Treaty of Babel rules@> =
int i = 0, i2 = Wide::len(p)-1, snl, wsc;
if ((p[0] == '"') && (p[i2] == '"')) { i++; i2--; } /* omit surrounding double-quotes */
while (Characters::is_babel_whitespace(p[i])) i++; /* omit leading whitespace */
while ((i2>=0) && (Characters::is_babel_whitespace(p[i2]))) i2--; /* omit trailing whitespace */
for (snl = FALSE, wsc = 0; i<=i2; i++) {
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t':
snl = FALSE;
wsc++;
int k = i;
while ((p[k] == ' ') || (p[k] == '\x0a') || (p[k] == '\x0d') || (p[k] == '\t')) k++;
if ((wsc == 1) && (p[k] != NEWLINE_IN_STRING)) WRITE(" ");
break;
case NEWLINE_IN_STRING:
if (snl) break;
WRITE("<br/>");
snl = TRUE; wsc = 1; break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
WRITE("'"); break;
}
int n = CompiledText::expand_unisub(OUT, p, i);
if (n >= 0) { i = n; break; }
/* and otherwise fall through to the default case */
default:
snl = FALSE;
wsc = 0;
switch(p[i]) {
case '&': WRITE("&amp;"); break;
case '<': WRITE("&lt;"); break;
case '>': WRITE("&gt;"); break;
default: PUT(p[i]); break;
}
break;
}
}
return;
@ In the HTML version, we want to respect the forcing of newlines, and
also the |[']| escape to obtain a literal single quotation mark.
@<Compile bibliographic text as HTML@> =
int i, whitespace_count=0;
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t':
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case NEWLINE_IN_STRING:
while (p[i+1] == NEWLINE_IN_STRING) i++;
PUT('<');
PUT('p');
PUT('>');
whitespace_count = 1;
break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
PUT('\''); break;
}
int n = CompiledText::expand_unisub(OUT, p, i);
if (n >= 0) { i = n; break; }
/* and otherwise fall through to the default case */
default:
whitespace_count = 0;
PUT(p[i]);
break;
}
}
return;
@ In the Inform 6 string version, we suppress the forcing of newlines, but
otherwise it's much the same.
@<Compile bibliographic text as an I6 string@> =
int i, whitespace_count=0;
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case '[':
if ((p[i+1] == '\'') && (p[i+2] == ']')) {
i += 2;
PUT('\''); break;
} /* and otherwise fall through to the default case */
default:
whitespace_count = 0;
PUT(p[i]);
break;
}
}
return;
@ This code is used to work out a good filename for something given a name
inside NI. For instance, if a project is called
>> "St. Bartholemew's Fair: \'Etude for a Push-Me/Pull-You Machine"
then what would be a good filename for its released story file?
In the filename version we must forcibly truncate the text to ensure
that it does not exceed a certain length, and must also make it filename-safe,
omitting characters used as folder separators on various platforms and
(for good measure) removing accents from accented letters, so that we can
arrive at a sequence of ASCII characters. Each run of whitespace is also
converted to a single space. If this would result in an empty text or only
a single space, we return the text "story" instead.
Our example (if not truncated) then emerges as:
|St- Bartholemew's Fair- Etude for a Push-Me-Pull-You Machine|
Note that we do not write any filename extension (e.g., |.z5|) here.
We change possible filename separators or extension indicators to hyphens,
and remove accents from each possible ISO Latin-1 accented letter. This does
still mean that the OE and AE digraphs will simply be omitted, while the
German eszet will be barbarously shortened to a single "s", but life is
just too short to care overmuch about this.
@<Compile bibliographic text as a truncated filename@> =
int i, pos = STREAM_EXTENT(OUT), whitespace_count=0, black_chars_written = 0;
int N = 100;
#ifdef IF_MODULE
N = BIBLIOGRAPHIC_TEXT_TRUNCATION;
#endif
if (p[0] == '"') p++;
for (i=0; p[i]; i++) {
if (STREAM_EXTENT(OUT) - pos >= N) break;
if ((p[i] == '"') && (p[i+1] == 0)) break;
switch(p[i]) {
case ' ': case '\x0a': case '\x0d': case '\t': case NEWLINE_IN_STRING:
whitespace_count++;
if (whitespace_count == 1) PUT(' ');
break;
case '?': case '*':
if ((p[i+1]) && (p[i+1] != '\"')) PUT('-');
break;
default: {
int charcode = p[i];
charcode = Characters::make_filename_safe(charcode);
whitespace_count = 0;
if (charcode < 128) {
PUT(charcode); black_chars_written++;
}
break;
}
}
}
if (black_chars_written == 0) WRITE("story");
return;

View file

@ -757,7 +757,7 @@ int PL::Bibliographic::Release::write_var_to_XML(OUTPUT_STREAM, nonlocal_variabl
} else {
wording W = ParseTree::get_text(val);
int w1 = Wordings::first_wn(W);
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(w1));
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(w1));
}
}
return TRUE;
@ -785,7 +785,7 @@ int PL::Bibliographic::Release::write_var_to_text(OUTPUT_STREAM, nonlocal_variab
} else {
wording W = ParseTree::get_text(val);
int w1 = Wordings::first_wn(W);
HTMLFiles::compile_bibliographic_text(OUT, Lexer::word_text(w1));
PL::Bibliographic::compile_bibliographic_text(OUT, Lexer::word_text(w1));
}
}
return TRUE;

View file

@ -274,7 +274,7 @@ void PL::Actions::Index::index_for_extension(OUTPUT_STREAM, source_file *sf, inf
int kc = 0;
LOOP_OVER(acn, action_name)
if (Lexer::file_of_origin(Wordings::first_wn(acn->present_name)) == E->read_into_file)
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Actions", I"action",
kc = Extensions::Files::document_headword(OUT, kc, E, "Actions", I"action",
acn->present_name);
if (kc != 0) HTML_CLOSE("p");
}

View file

@ -117,7 +117,7 @@ void PL::Actions::Patterns::Named::index_for_extension(OUTPUT_STREAM, source_fil
int kc = 0;
LOOP_OVER(nap, named_action_pattern)
if (Lexer::file_of_origin(Wordings::first_wn(nap->text_of_declaration)) == E->read_into_file)
kc = Extensions::Documentation::document_headword(OUT, kc, E, "Kinds of action", I"kind of action",
kc = Extensions::Files::document_headword(OUT, kc, E, "Kinds of action", I"kind of action",
nap->text_of_declaration);
if (kc != 0) HTML_CLOSE("p");
}

View file

@ -570,7 +570,7 @@ void Index::index_actual_element(OUTPUT_STREAM, text_stream *elt) {
if (Str::eq_wide_string(elt, L"C")) {
Sentences::Headings::index(OUT);
Extensions::Files::index(OUT);
Extensions::Files::update_census();
Extensions::Census::update_census();
return;
}
if (Str::eq_wide_string(elt, L"Vl")) {

View file

@ -7,13 +7,7 @@ Licence: Artistic License 2.0
Chapter 1: Starting Up
Index Module
Chapter 2: HTML
"HTML and JavaScript generation."
HTML Files
Javascript Pastes
HTML Documentation
Chapter 3: Indexing
Chapter 2: Indexing
"Utility routines for generating index pages."
Index File Services
Documentation References

View file

@ -61,6 +61,7 @@ INBUILDX = inbuild/Tangled/inbuild
{module} INTER inter inter/inter-module
{module} BUILDING building inter/building-module
{module} CODEGEN codegen inter/codegen-module
{module} HTML html inbuild/html-module
{module} ARCH arch inbuild/arch-module
{module} INBUILD inbuild inbuild/inbuild-module
@ -88,6 +89,7 @@ INBUILDX = inbuild/Tangled/inbuild
{dep} INFORM7 on IF
{dep} INFORM7 on MULTIMEDIA
{dep} INFORM7 on INDEX
{dep} INFORM7 on HTML
{dep} INFORM7 on ARCH
{dep} INFORM7 on INTER
{dep} INFORM7 on BUILDING
@ -103,6 +105,7 @@ INBUILDX = inbuild/Tangled/inbuild
{tool} INBUILDTOOL inbuild inbuild
{dep} INBUILDTOOL on FOUNDATION
{dep} INBUILDTOOL on WORDS
{dep} INBUILDTOOL on HTML
{dep} INBUILDTOOL on ARCH
{dep} INBUILDTOOL on INBUILD