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

Wrangly refactor of extension and source lexing

This commit is contained in:
Graham Nelson 2020-02-16 22:01:50 +00:00
parent fbe371caa5
commit 104a1782b5
49 changed files with 663 additions and 564 deletions

View file

@ -43,6 +43,7 @@
endif
set: $I7CONSOLE = $WORK/Example.inform/Build/i7_output.txt
debugger: lldb -f $I7 -- `$I7OPTIONS -project $WORK/Example.inform -crash-all
step: $I7 `$I7OPTIONS -project $WORK/Example.inform >$I7CONSOLE 2>&1
or: 'failed with Problem message(s)' $I7CONSOLE

View file

@ -30,6 +30,7 @@ Setting up the use of this module.
@e inform_project_MT
@e inform_language_MT
@e inform_pipeline_MT
@e source_text_error_MT
=
ALLOCATE_INDIVIDUALLY(inform_kit)
@ -53,6 +54,7 @@ ALLOCATE_INDIVIDUALLY(inform_template)
ALLOCATE_INDIVIDUALLY(inform_project)
ALLOCATE_INDIVIDUALLY(inform_language)
ALLOCATE_INDIVIDUALLY(inform_pipeline)
ALLOCATE_INDIVIDUALLY(source_text_error)
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)

View file

@ -23,6 +23,7 @@ typedef struct build_vertex {
struct filename *buildable_if_internal_file;
struct inbuild_requirement *findable;
struct text_stream *annotation;
struct source_file *read_as;
struct linked_list *build_edges; /* of |build_vertex| */
struct linked_list *use_edges; /* of |build_vertex| */
struct build_script *script;
@ -41,6 +42,7 @@ build_vertex *Graphs::file_vertex(filename *F) {
V->timestamp = (time_t) 0;
V->script = BuildSteps::new_script();
V->annotation = NULL;
V->read_as = NULL;
V->last_described = 0;
return V;
}

View file

@ -11,6 +11,7 @@ few of these.
@e GENRE_SEARCH_NEST_FOR_MTID
@e GENRE_COPY_TO_NEST_MTID
@e GENRE_GO_OPERATIONAL_MTID
@e GENRE_READ_SOURCE_TEXT_FOR_MTID
=
typedef struct inbuild_genre {
@ -24,6 +25,7 @@ VMETHOD_TYPE(GENRE_CLAIM_AS_COPY_MTID, inbuild_genre *gen, inbuild_copy **C, tex
VMETHOD_TYPE(GENRE_SEARCH_NEST_FOR_MTID, inbuild_genre *gen, inbuild_nest *N, inbuild_requirement *req, linked_list *search_results)
VMETHOD_TYPE(GENRE_COPY_TO_NEST_MTID, inbuild_genre *gen, inbuild_copy *C, inbuild_nest *N, int syncing, build_methodology *meth)
VMETHOD_TYPE(GENRE_GO_OPERATIONAL_MTID, inbuild_genre *gen, inbuild_copy *C)
VMETHOD_TYPE(GENRE_READ_SOURCE_TEXT_FOR_MTID, inbuild_genre *gen, inbuild_copy *C, linked_list *errors)
@ =
inbuild_genre *Model::genre(text_stream *name) {
@ -72,6 +74,9 @@ typedef struct inbuild_copy {
struct filename *location_if_file;
general_pointer content; /* the type of which depends on the work's genre */
struct build_vertex *vertex;
struct wording source_text;
struct linked_list *errors_reading_source_text;
struct inbuild_requirement *found_by;
MEMORY_MANAGEMENT
} inbuild_copy;
@ -82,6 +87,9 @@ inbuild_copy *Model::copy_in_file(inbuild_edition *edition, filename *F, general
copy->location_if_file = F;
copy->content = C;
copy->vertex = NULL;
copy->source_text = EMPTY_WORDING;
copy->errors_reading_source_text = NEW_LINKED_LIST(source_text_error);
copy->found_by = NULL;
return copy;
}
@ -92,6 +100,9 @@ inbuild_copy *Model::copy_in_directory(inbuild_edition *edition, pathname *P, ge
copy->location_if_file = NULL;
copy->content = C;
copy->vertex = NULL;
copy->source_text = EMPTY_WORDING;
copy->errors_reading_source_text = NEW_LINKED_LIST(source_text_error);
copy->found_by = NULL;
return copy;
}
@ -131,3 +142,10 @@ inbuild_copy *Model::claim(text_stream *arg) {
void Model::cppy_go_operational(inbuild_copy *C) {
VMETHOD_CALL(C->edition->work->genre, GENRE_GO_OPERATIONAL_MTID, C);
}
void Model::read_source_text_for(inbuild_copy *C) {
feed_t id = Feeds::begin();
VMETHOD_CALL(C->edition->work->genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, C, C->errors_reading_source_text);
wording W = Feeds::end(id);
if (Wordings::nonempty(W)) C->source_text = W;
}

View file

@ -41,10 +41,12 @@ void Nests::protect(inbuild_nest *N) {
N->read_only = TRUE;
}
void Nests::add_search_result(linked_list *results, inbuild_nest *N, inbuild_copy *C) {
void Nests::add_search_result(linked_list *results, inbuild_nest *N, inbuild_copy *C,
inbuild_requirement *req) {
inbuild_search_result *R = CREATE(inbuild_search_result);
R->nest = N;
R->copy = C;
C->found_by = req;
ADD_TO_LINKED_LIST(R, inbuild_search_result, results);
}

View file

@ -2,7 +2,8 @@
Semantic version numbers such as 3.7.1.
@ For example, 4, 7.1, and 0.2.3 are all version numbers. Up to |VERSION_NUMBER_DEPTH|
@ Traditional semantic version numbers look like dot-divided runs of
non-negative integers: for example, 4, 7.1, and 0.2.3. Up to |VERSION_NUMBER_DEPTH|
components can be given. The tail of the array should be padded with |-1| values;
otherwise, components should all be non-negative integers.
@ -13,6 +14,24 @@ typedef struct inbuild_version_number {
int version_numbers[VERSION_NUMBER_DEPTH];
} inbuild_version_number;
@ However, Inform 7 extensions have for many years allowed two forms of
version number: either just |N|, which clearly fits the scheme above, or
|N/DDDDDD|, which does not. This is a format which was chosen for sentimental
reasons: IF enthusiasts know it well from the banner text of the Infocom
titles of the 1980s. This story file, for instance, was compiled at the
time of the Reykjavik summit between Presidents Gorbachev and Reagan:
|Moonmist|
|Infocom interactive fiction - a mystery story|
|Copyright (c) 1986 by Infocom, Inc. All rights reserved.|
|Moonmist is a trademark of Infocom, Inc.|
|Release number 9 / Serial number 861022|
Story file collectors customarily abbreviate this in catalogues to |9/861022|.
We will therefore allow this notation, and convert it silently each way.
|N/DDDDDD| is equivalent to |N.DDDDDD|.
@ All invalid strings of numbers -- i.e., breaking the above rules -- are
called "null" versions, and can never be valid as the version of anything.
Instead they are used to represent the absence of a version number.
@ -72,20 +91,30 @@ void VersionNumbers::writer(OUTPUT_STREAM, char *format_string, void *vE) {
inbuild_version_number VersionNumbers::from_text(text_stream *T) {
inbuild_version_number V;
int component = 0, val = -1;
int component = 0, val = -1, dots_used = 0, slashes_used = 0, count = 0;
LOOP_THROUGH_TEXT(pos, T) {
wchar_t c = Str::get(pos);
if (c == '.') {
if (c == '.') dots_used++;
if (c == '/') slashes_used++;
if ((c == '.') || (c == '/')) {
if (val == -1) return VersionNumbers::null();
if (component >= VERSION_NUMBER_DEPTH) return VersionNumbers::null();
V.version_numbers[component] = val;
component++; val = -1;
component++; val = -1; count = 0;
} else if (Characters::isdigit(c)) {
int digit = c - '0';
if ((val == 0) && (slashes_used == 0))
return VersionNumbers::null();
if (val < 0) val = digit; else val = 10*val + digit;
count++;
} else return VersionNumbers::null();
}
if (val == -1) return VersionNumbers::null();
if ((dots_used > 0) && (slashes_used > 0)) return VersionNumbers::null();
if (slashes_used > 0) {
if (component > 1) return VersionNumbers::null();
if (count != 6) return VersionNumbers::null();
}
if (component >= VERSION_NUMBER_DEPTH) return VersionNumbers::null();
V.version_numbers[component] = val;
for (int i=component+1; i<VERSION_NUMBER_DEPTH; i++) V.version_numbers[i] = -1;

View file

@ -188,6 +188,7 @@ int Works::is_standard_rules(inbuild_work *work) {
Works::new(extension_genre, I"Standard Rules", I"Graham Nelson");
Works::add_to_database(a_work_for_standard_rules, HYPOTHETICAL_WDBC);
}
if (work == NULL) return FALSE;
return Works::match(work, a_work_for_standard_rules);
}
@ -198,6 +199,7 @@ int Works::is_basic_inform(inbuild_work *work) {
Works::new(extension_genre, I"Basic Inform", I"Graham Nelson");
Works::add_to_database(a_work_for_basic_inform, HYPOTHETICAL_WDBC);
}
if (work == NULL) return FALSE;
return Works::match(work, a_work_for_basic_inform);
}

View file

@ -20,6 +20,7 @@ void ExtensionManager::start(void) {
METHOD_ADD(extension_genre, GENRE_CLAIM_AS_COPY_MTID, ExtensionManager::claim_as_copy);
METHOD_ADD(extension_genre, GENRE_SEARCH_NEST_FOR_MTID, ExtensionManager::search_nest_for);
METHOD_ADD(extension_genre, GENRE_COPY_TO_NEST_MTID, ExtensionManager::copy_to_nest);
METHOD_ADD(extension_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionManager::read_source_text_for);
}
void ExtensionManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) {
@ -45,10 +46,25 @@ inform_extension *ExtensionManager::from_copy(inbuild_copy *C) {
return NULL;
}
dictionary *ext_copy_cache = NULL;
inbuild_copy *ExtensionManager::new_copy(inbuild_edition *edition, filename *F) {
inform_extension *E = Extensions::new_ie();
inbuild_copy *C = Model::copy_in_file(edition, F, STORE_POINTER_inform_extension(E));
E->as_copy = C;
if (ext_copy_cache == NULL) ext_copy_cache = Dictionaries::new(16, FALSE);
TEMPORARY_TEXT(key);
WRITE_TO(key, "%f", F);
inbuild_copy *C = NULL;
if (Dictionaries::find(ext_copy_cache, key))
C = Dictionaries::read_value(ext_copy_cache, key);
if (C == NULL) {
inform_extension *E = Extensions::new_ie();
C = Model::copy_in_file(edition, F, STORE_POINTER_inform_extension(E));
E->as_copy = C;
if (Works::is_standard_rules(C->edition->work)) Extensions::make_standard(E);
Dictionaries::create(ext_copy_cache, key);
Dictionaries::write_value(ext_copy_cache, key, C);
} else {
C->edition = edition;
}
DISCARD_TEXT(key);
return C;
}
@ -69,21 +85,27 @@ void ExtensionManager::claim_as_copy(inbuild_genre *gen, inbuild_copy **C,
if (directory_status == TRUE) return;
if (Str::eq_insensitive(ext, I"i7x")) {
filename *F = Filenames::from_text(arg);
*C = ExtensionManager::claim_file_as_copy(F, NULL, FALSE);
*C = ExtensionManager::claim_file_as_copy(F, FALSE);
}
}
inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F, text_stream *error_text,
inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F,
int allow_malformed) {
if ((allow_malformed) && (TextFiles::exists(F) == FALSE)) return NULL;
TEMPORARY_TEXT(author);
TEMPORARY_TEXT(title);
TEMPORARY_TEXT(rubric_text);
TEMPORARY_TEXT(requirement_text);
TEMPORARY_TEXT(error_text);
inbuild_version_number V =
ExtensionManager::scan_file(F, title, author, rubric_text, requirement_text, error_text);
inbuild_copy *C = ExtensionManager::new_copy(
Model::edition(Works::new(extension_genre, title, author), V), F);
if (Str::len(error_text) > 0) {
source_text_error *ste = SourceText::ste_text(EXT_MISWORDED_STE, error_text);
ste->copy = C;
ADD_TO_LINKED_LIST(ste, source_text_error, C->errors_reading_source_text);
}
if ((allow_malformed) || (Str::len(error_text) == 0)) {
Works::add_to_database(C->edition->work, CLAIMED_WDBC);
ExtensionManager::build_vertex(C);
@ -94,6 +116,7 @@ inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F, text_stream *err
DISCARD_TEXT(title);
DISCARD_TEXT(rubric_text);
DISCARD_TEXT(requirement_text);
DISCARD_TEXT(error_text);
return C;
}
@ -117,7 +140,14 @@ inbuild_version_number ExtensionManager::scan_file(filename *F,
@<Read the rubric text, if any is present@>;
@<Parse the version, title, author and VM requirements from the titling line@>;
fclose(EXTF);
if (Str::len(version_text) > 0) V = VersionNumbers::from_text(version_text);
if (Str::len(version_text) > 0) {
V = VersionNumbers::from_text(version_text);
if (VersionNumbers::is_null(V)) {
if (error_text)
WRITE_TO(error_text, "the version number '%S' is malformed", version_text);
return V;
}
}
DISCARD_TEXT(titling_line);
DISCARD_TEXT(version_text);
return V;
@ -289,9 +319,9 @@ void ExtensionManager::search_nest_for_r(pathname *P, inbuild_nest *N,
void ExtensionManager::search_nest_for_single_file(filename *F, inbuild_nest *N,
inbuild_requirement *req, linked_list *search_results) {
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F, NULL, req->allow_malformed);
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F, req->allow_malformed);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C);
Nests::add_search_result(search_results, N, C, req);
}
}
@ -351,3 +381,10 @@ build an extension at all.
void ExtensionManager::build_vertex(inbuild_copy *C) {
Graphs::copy_vertex(C);
}
@h Source text.
=
void ExtensionManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C, linked_list *errors) {
Extensions::read_source_text_for(ExtensionManager::from_copy(C), errors);
}

View file

@ -111,7 +111,7 @@ void KitManager::search_nest_for(inbuild_genre *gen, inbuild_nest *N,
pathname *Q = Pathnames::subfolder(P, LEAFNAME);
inbuild_copy *C = KitManager::claim_folder_as_copy(Q);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C);
Nests::add_search_result(search_results, N, C, req);
}
}
}

View file

@ -116,7 +116,7 @@ void LanguageManager::search_nest_for(inbuild_genre *gen, inbuild_nest *N,
pathname *Q = Pathnames::subfolder(P, LEAFNAME);
inbuild_copy *C = LanguageManager::claim_folder_as_copy(Q);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C);
Nests::add_search_result(search_results, N, C, req);
}
}
}

View file

@ -97,7 +97,7 @@ void PipelineManager::search_nest_for(inbuild_genre *gen, inbuild_nest *N,
inbuild_copy *C = PipelineManager::claim_file_as_copy(F, NULL,
req->allow_malformed);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C);
Nests::add_search_result(search_results, N, C, req);
}
}
}

View file

@ -13,6 +13,7 @@ void ProjectBundleManager::start(void) {
METHOD_ADD(project_bundle_genre, GENRE_SEARCH_NEST_FOR_MTID, ProjectBundleManager::search_nest_for);
METHOD_ADD(project_bundle_genre, GENRE_COPY_TO_NEST_MTID, ProjectBundleManager::copy_to_nest);
METHOD_ADD(project_bundle_genre, GENRE_GO_OPERATIONAL_MTID, ProjectBundleManager::go_operational);
METHOD_ADD(project_bundle_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ProjectBundleManager::read_source_text_for);
}
void ProjectBundleManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) {
@ -99,4 +100,9 @@ void ProjectBundleManager::go_operational(inbuild_genre *G, inbuild_copy *C) {
Projects::construct_graph(ProjectBundleManager::from_copy(C));
}
@h Source text.
=
void ProjectBundleManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C, linked_list *errors) {
Projects::read_source_text_for(ProjectBundleManager::from_copy(C), errors);
}

View file

@ -13,6 +13,7 @@ void ProjectFileManager::start(void) {
METHOD_ADD(project_file_genre, GENRE_SEARCH_NEST_FOR_MTID, ProjectFileManager::search_nest_for);
METHOD_ADD(project_file_genre, GENRE_COPY_TO_NEST_MTID, ProjectFileManager::copy_to_nest);
METHOD_ADD(project_file_genre, GENRE_GO_OPERATIONAL_MTID, ProjectFileManager::go_operational);
METHOD_ADD(project_file_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ProjectFileManager::read_source_text_for);
}
void ProjectFileManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) {
@ -101,3 +102,10 @@ void ProjectFileManager::build_vertex(inbuild_copy *C) {
void ProjectFileManager::go_operational(inbuild_genre *G, inbuild_copy *C) {
Projects::construct_graph(ProjectFileManager::from_copy(C));
}
@h Source text.
=
void ProjectFileManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C, linked_list *errors) {
Projects::read_source_text_for(ProjectBundleManager::from_copy(C), errors);
}

View file

@ -93,7 +93,7 @@ void TemplateManager::search_nest_for(inbuild_genre *gen, inbuild_nest *N,
pathname *Q = Pathnames::subfolder(P, LEAFNAME);
inbuild_copy *C = TemplateManager::claim_folder_as_copy(Q);
if ((C) && (Requirements::meets(C->edition, req))) {
Nests::add_search_result(search_results, N, C);
Nests::add_search_result(search_results, N, C, req);
}
}
}

View file

@ -5,31 +5,115 @@ An Inform 7 extension.
@ =
typedef struct inform_extension {
struct inbuild_copy *as_copy;
#ifdef CORE_MODULE
struct wording body_text; /* Body of source text supplied in extension, if any */
int body_text_unbroken; /* Does this contain text waiting to be sentence-broken? */
struct wording documentation_text; /* Documentation supplied in extension, if any */
struct wording VM_restriction_text; /* Restricting use to certain VMs */
int standard; /* the (or perhaps just a) Standard Rules extension */
#ifdef CORE_MODULE
int loaded_from_built_in_area; /* Located within Inform application */
int authorial_modesty; /* Do not credit in the compiled game */
struct source_file *read_into_file; /* Which source file loaded this */
struct extension_file *ef; /* Corresponding Inform7 compiler structure */
struct text_stream *rubric_as_lexed;
struct text_stream *extra_credit_as_lexed;
struct parse_node *inclusion_sentence; /* Where the source called for this */
#endif
struct source_file *read_into_file; /* Which source file loaded this */
MEMORY_MANAGEMENT
} inform_extension;
inform_extension *Extensions::new_ie(void) {
inform_extension *E = CREATE(inform_extension);
E->as_copy = NULL;
#ifdef CORE_MODULE
E->body_text = EMPTY_WORDING;
E->body_text_unbroken = FALSE;
E->documentation_text = EMPTY_WORDING;
E->VM_restriction_text = EMPTY_WORDING;
E->standard = FALSE;
#ifdef CORE_MODULE
E->loaded_from_built_in_area = FALSE;
E->authorial_modesty = FALSE;
E->rubric_as_lexed = NULL;
E->extra_credit_as_lexed = NULL;
E->read_into_file = NULL;
E->ef = NULL;
E->inclusion_sentence = NULL;
#endif
return E;
}
#ifdef CORE_MODULE
void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
E->inclusion_sentence = N;
}
#endif
void Extensions::set_VM_text(inform_extension *E, wording W) {
E->VM_restriction_text = W;
}
int Extensions::is_standard(inform_extension *E) {
if (E == NULL) return FALSE;
return E->standard;
}
void Extensions::make_standard(inform_extension *E) {
E->standard = TRUE;
}
@
@e EXT_MISWORDED_STE
=
void Extensions::read_source_text_for(inform_extension *E, linked_list *errors) {
filename *F = E->as_copy->location_if_file;
int doc_only = FALSE;
#ifdef CORE_MODULE
if (CoreMain::census_mode()) doc_only = TRUE;
#endif
TEMPORARY_TEXT(synopsis);
@<Concoct a synopsis for the extension to be read@>;
E->read_into_file = SourceText::read_file(F, synopsis, doc_only, errors, FALSE);
DISCARD_TEXT(synopsis);
if (E->read_into_file) {
E->read_into_file->your_ref = STORE_POINTER_inbuild_copy(E->as_copy);
wording EXW = E->read_into_file->text_read;
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
}
}
@ We concoct a textual synopsis in the form
|"Pantomime Sausages by Mr Punch"|
to be used by |SourceFiles::read_extension_source_text| for printing to |stdout|. Since
we dare not assume |stdout| can manage characters outside the basic ASCII
range, we flatten them from general ISO to plain ASCII.
@<Concoct a synopsis for the extension to be read@> =
WRITE_TO(synopsis, "%S by %S", E->as_copy->edition->work->title, E->as_copy->edition->work->author_name);
LOOP_THROUGH_TEXT(pos, synopsis)
Str::put(pos,
Characters::make_filename_safe(Str::get(pos)));
@ If an extension file contains the special text (outside literal mode) of
|---- Documentation ----|
then this is taken as the end of the Inform source, and the beginning of a
snippet of documentation about the extension; text from that point on is
saved until later, but not broken into sentences for the parse tree, and it
is therefore invisible to the rest of Inform. If this division line is not
present then the extension contains only body source and no documentation.
=
<extension-body> ::=
*** ---- documentation ---- ... | ==> TRUE
... ==> FALSE
@<Break the extension's text into body and documentation@> =
<extension-body>(EXW);
E->body_text = GET_RW(<extension-body>, 1);
if (<<r>>) E->documentation_text = GET_RW(<extension-body>, 2);
E->body_text_unbroken = TRUE; /* mark this to be sentence-broken */

View file

@ -257,3 +257,35 @@ void Projects::construct_graph(inform_project *project) {
Graphs::need_this_to_build(V, LV);
}
}
@
=
void Projects::read_source_text_for(inform_project *project, linked_list *errors) {
TEMPORARY_TEXT(early);
Projects::early_source_text(early, project);
if (Str::len(early) > 0) Feeds::feed_stream(early);
DISCARD_TEXT(early);
#ifdef CORE_MODULE
SourceFiles::read_further_mandatory_text();
#endif
linked_list *L = Projects::source(project);
if (L) {
build_vertex *N;
LOOP_OVER_LINKED_LIST(N, build_vertex, L) {
filename *F = N->buildable_if_internal_file;
N->read_as = SourceText::read_file(F, N->annotation, FALSE, errors, TRUE);
}
}
}
int Projects::draws_from_source_file(inform_project *project, source_file *sf) {
linked_list *L = Projects::source(project);
if (L) {
build_vertex *N;
LOOP_OVER_LINKED_LIST(N, build_vertex, L)
if (sf == N->read_as)
return TRUE;
}
return FALSE;
}

View file

@ -0,0 +1,81 @@
[SourceText::] Source Text.
Code for reading Inform 7 source text, which Inbuild uses for both extensions
and projects.
@ Either way, we use the following code. The |SourceText::read_file| function returns
one of the following values to indicate the source of the source: the value
only really tells us something we didn't know in the case of extensions,
but in that event the Extensions.w routines do indeed want to know this.
@e SEARCH_FAILED_STE from 1
@e OPEN_FAILED_STE
=
typedef struct source_text_error {
int ste_code;
struct inbuild_copy *copy;
struct filename *file;
struct text_file_position pos;
struct text_stream *notes;
MEMORY_MANAGEMENT
} source_text_error;
source_text_error *SourceText::ste(int code, filename *F) {
source_text_error *ste = CREATE(source_text_error);
ste->ste_code = code;
ste->file = F;
ste->notes = NULL;
ste->pos = TextFiles::nowhere();
ste->copy = NULL;
return ste;
}
source_text_error *SourceText::ste_text(int code, text_stream *NB) {
source_text_error *ste = CREATE(source_text_error);
ste->ste_code = code;
ste->file = NULL;
ste->notes = Str::duplicate(NB);
ste->pos = TextFiles::nowhere();
ste->copy = NULL;
return ste;
}
source_file *SourceText::read_file(filename *F, text_stream *synopsis,
int documentation_only, linked_list *errors, int primary) {
general_pointer ref = STORE_POINTER_inbuild_copy(NULL);
FILE *handle = Filenames::fopen(F, "r");
if (handle == NULL) return NULL;
text_stream *leaf = Filenames::get_leafname(F);
if (primary) leaf = I"main source text";
source_file *sf = TextFromFiles::feed_open_file_into_lexer(F, handle,
leaf, documentation_only, ref);
if (sf == NULL) {
source_text_error *ste = SourceText::ste(OPEN_FAILED_STE, F);
ADD_TO_LINKED_LIST(ste, source_text_error, errors);
} else {
fclose(handle);
if (documentation_only == FALSE) @<Tell console output about the file@>;
}
return sf;
}
@ This is where messages like
|I've also read Standard Rules by Graham Nelson, which is 27204 words long.|
are printed to |stdout| (not |stderr|), in something of an affectionate nod
to \TeX's traditional console output, though occasionally I think silence is
golden and that the messages could go. It's a moot point for almost all users,
though, because the console output is concealed from them by the Inform
application.
@<Tell console output about the file@> =
int wc;
char *message;
if (primary) message = "I've now read %S, which is %d words long.\n";
else message = "I've also read %S, which is %d words long.\n";
wc = TextFromFiles::total_word_count(sf);
WRITE_TO(STDOUT, message, synopsis, wc);
STREAM_FLUSH(STDOUT);
LOG(message, synopsis, wc);

View file

@ -27,6 +27,7 @@ Chapter 3: Managing Genres of Work
Pipeline Manager
Chapter 4: Services for the Inform Compiler
Source Text
Kit Services
Extension Services
Extension Census

View file

@ -1,3 +0,0 @@
Include version 3/070628 of ExtNoVersion Extension by Araminta Intest.
Beachy Head is a room.

View file

@ -1,19 +1,8 @@
Include version 1 of Standard Rules by Graham Nelson. [Legal.]
Include version / of Standard Rules by Graham Nelson.
Include version 0 of Standard Rules by Graham Nelson.
Include version 012 of Standard Rules by Graham Nelson.
Include version 1/ of Standard Rules by Graham Nelson.
Include version /000000 of Standard Rules by Graham Nelson.
Include version 1/01234 of Standard Rules by Graham Nelson.
Include version 1/0123456 of Standard Rules by Graham Nelson.
Include version 1/010101 of Standard Rules by Graham Nelson. [Legal.]
Include version 1//010101 of Standard Rules by Graham Nelson.
Include version 1/23/010101 of Standard Rules by Graham Nelson.
Include version 999/010101 of Standard Rules by Graham Nelson. [Legal.]
Include version 1000/010101 of Standard Rules by Graham Nelson.
Include version 999 of Standard Rules by Graham Nelson. [Legal.]
Include version 1000 of Standard Rules by Graham Nelson.
Include version 0.1 of Standard Rules by Graham Nelson.
Include version three of Standard Rules by Graham Nelson.
Include version 21 of Standard Rules by Graham Nelson. [Legal.]
Include version 321 of Standard Rules by Graham Nelson. [Legal.]

View file

@ -1,3 +1,5 @@
Include version 3/070628 of ExtVersionTooLow Extension by Araminta Intest.
Include version 2.7 of ExtNoVersion Extension by Araminta Intest.
Beachy Head is a room.

View file

@ -1,12 +1,11 @@
Inform 7.10.1 build 6Q21 has started.
I've now read your source text, which is 6 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
Problem__ PM_BogusExtension
>--> I can't find the extension 'Bogus Extension by Mr Nobody', which seems
not to be installed, but was requested by: 'Include Bogus Extension by Mr
>--> I can't find the extension requested by: 'Include Bogus Extension by Mr
Nobody' (source text, line 1). You can get hold of extensions which people
have made public at the Inform website, www.inform7.com, or by using the
Public Library in the Extensions panel.
Inform 7 has finished: 11 centiseconds used.
Inform 7 has finished: 12 centiseconds used.

View file

@ -1,12 +1,13 @@
Inform 7 build 6L26 has started.
Inform 7.10.1 build 6Q21 has started.
I've now read your source text, which is 10 words long.
I've also read Standard Rules by Graham Nelson, which is 42597 words long.
I've also read English Language by Graham Nelson, which is 2288 words long.
I've also read ExtMalformedVM Extension by Araminta Intest, which is 15 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
I've also read Extmalformedvm Extension by Araminta Intest, which is 15 words long.
Problem__ PM_ExtMalformedVM
>--> Your source text makes use of the extension ExtMalformedVM Extension by
>--> Your source text makes use of the extension Extmalformedvm Extension by
Araminta Intest: but my copy stipulates that it is 'for clockwork train
sets', which is a description of the required story file format which I
can't understand, and should be something like '(for Z-machine version 5 or
8 only)'.
Inform 7 has finished: 9 centiseconds used.
Inform 7 has finished: 12 centiseconds used.

View file

@ -3,10 +3,10 @@ I've now read your source text, which is 10 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
I've also read Extmiswordedbeginshere Extension by Araminta Intest, which is 12 words long.
Problem__ PM_ExtMiswordedBeginsHere
>--> The extension Extmiswordedbeginshere Extension by Araminta Intest, which
your source text makes use of, seems to be damaged or incorrect: its
identifying opening line is wrong. Specifically, the titling line does not
give both author and title.
I've also read Extmiswordedbeginshere Extension by Araminta Intest, which is 12 words long.
Inform 7 has finished: 11 centiseconds used.
Inform 7 has finished: 12 centiseconds used.

View file

@ -1,11 +0,0 @@
Inform 7 build 6L26 has started.
I've now read your source text, which is 14 words long.
I've also read Standard Rules by Graham Nelson, which is 42597 words long.
I've also read English Language by Graham Nelson, which is 2288 words long.
I've also read ExtNoVersion Extension by Araminta Intest, which is 11 words long.
Problem__ PM_ExtNoVersion
>--> You wrote 'Include version 3/070628 of ExtNoVersion Extension by
Araminta Intest' (source text, line 1): but my copy of ExtNoVersion
Extension by Araminta Intest contains no version number, and is therefore
considered to be earlier than all numbered versions.
Inform 7 has finished: 14 centiseconds used.

View file

@ -1,50 +1,34 @@
Inform 7 build 6L26 has started.
I've now read your source text, which is 171 words long.
I've also read Standard Rules by Graham Nelson, which is 42597 words long.
I've also read English Language by Graham Nelson, which is 2288 words long.
Inform 7.10.1 build 6Q21 has started.
I've now read your source text, which is 72 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version / of Standard Rules by Graham Nelson' (source
text, line 2): but a version number must have the form N/DDDDDD, as in the
text, line 1): but a version number must have the form N/DDDDDD, as in the
example '2/040426' for release 2 made on 26 April 2004. (The DDDDDD part is
optional, so '3' is a legal version number too. N must be between 1 and
999: in particular, there is no version 0.)
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 0 of Standard Rules by Graham Nelson' (source
text, line 3): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 012 of Standard Rules by Graham Nelson' (source
text, line 4): again, a version number must have the form N/DDDDDD.
text, line 2): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1/ of Standard Rules by Graham Nelson' (source
text, line 5): again, a version number must have the form N/DDDDDD.
text, line 3): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version /000000 of Standard Rules by Graham Nelson' (source
text, line 6): again, a version number must have the form N/DDDDDD.
text, line 4): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1/01234 of Standard Rules by Graham Nelson' (source
text, line 7): again, a version number must have the form N/DDDDDD.
text, line 5): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1/0123456 of Standard Rules by Graham Nelson'
(source text, line 8): again, a version number must have the form N/DDDDDD.
(source text, line 6): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1//010101 of Standard Rules by Graham Nelson'
(source text, line 10): again, a version number must have the form
N/DDDDDD.
(source text, line 7): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1/23/010101 of Standard Rules by Graham
Nelson' (source text, line 11): again, a version number must have the form
Nelson' (source text, line 8): again, a version number must have the form
N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1000/010101 of Standard Rules by Graham
Nelson' (source text, line 13): again, a version number must have the form
N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 1000 of Standard Rules by Graham Nelson' (source
text, line 15): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version 0.1 of Standard Rules by Graham Nelson' (source
text, line 16): again, a version number must have the form N/DDDDDD.
Problem__ PM_ExtVersionMalformed
>--> You wrote 'Include version three of Standard Rules by Graham Nelson' (source
text, line 17): again, a version number must have the form N/DDDDDD.
Inform 7 has finished: 10 centiseconds used.
Inform 7 has finished: 12 centiseconds used.

View file

@ -1,10 +1,18 @@
Inform 7 build 6L26 has started.
I've now read your source text, which is 14 words long.
I've also read Standard Rules by Graham Nelson, which is 42597 words long.
I've also read English Language by Graham Nelson, which is 2288 words long.
I've also read ExtVersionTooLow Extension by Araminta Intest, which is 14 words long.
Inform 7.10.1 build 6Q21 has started.
I've now read your source text, which is 23 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
Problem__ PM_ExtVersionTooLow
>--> You wrote 'Include version 3/070628 of ExtVersionTooLow Extension by
Araminta Intest' (source text, line 1): but my copy of ExtVersionTooLow
Extension by Araminta Intest is only version 2.
Inform 7 has finished: 14 centiseconds used.
>--> I can't find the right version of the extension requested by 'Include
version 3/070628 of ExtVersionTooLow Extension by Araminta Intest' (source
text, line 1) - I can only find version 2. You can get hold of extensions
which people have made public at the Inform website, www.inform7.com, or by
using the Public Library in the Extensions panel.
Problem__ PM_ExtVersionTooLow
>--> I can't find the right version of the extension requested by 'Include
version 2.7 of ExtNoVersion Extension by Araminta Intest' (source text, line 3)
- I can only find an unnumbered version. You can get hold of extensions
which people have made public at the Inform website, www.inform7.com, or by
using the Public Library in the Extensions panel.
Inform 7 has finished: 13 centiseconds used.

View file

@ -1,7 +1,8 @@
Inform 7 build 6M62 has started.
Inform 7.10.1 build 6Q21 has started.
I've now read your source text, which is 127 words long.
I've also read Standard Rules by Graham Nelson, which is 42655 words long.
I've also read English Language by Graham Nelson, which is 2297 words long.
I've also read Basic Inform by Graham Nelson, which is 7645 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32123 words long.
Problem__ PM_IncludesTooLong
>--> The extension Blah by Author aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaaaaaa aaaaaaa aaaaaaa
@ -15,9 +16,22 @@ Problem__ PM_IncludesTooLong
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaaaaaa
aaaaaaa aaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa, which your source text makes use of, has an author's name
aaaaaaa aaaaaaa, which your source text requests, has an author's name
which is too long, exceeding the maximum allowed (50 characters) by 1025.
Inform 7 has finished: 10 centiseconds used.
Problem__ PM_BogusExtension
>--> I can't find the extension requested by: 'Include Blah by Author aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaaaaaa aaaaaaaaaaaaaa aaaaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaaaaaaaaaa aaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaa aaaaaaaaaa aaaaaaa aaaaaaa
aaaaaaa aaaaaaa aaaaaaa' (source text, line 1). You can get hold of
extensions which people have made public at the Inform website,
www.inform7.com, or by using the Public Library in the Extensions panel.
Inform 7 has finished: 13 centiseconds used.

View file

@ -549,6 +549,10 @@ 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

@ -1332,7 +1332,7 @@ int Tables::index_tables_in(OUTPUT_STREAM, extension_file *ef, int efc) {
if (tc > 0) {
if (ef) {
HTML_OPEN("p");
WRITE("<i>%+W</i>", ef->title_text);
WRITE("<i>%S</i>", ef->found->edition->work->title);
HTML_CLOSE("p");
}
HTML::begin_plain_html_table(OUT);

View file

@ -274,16 +274,6 @@ void Problems::Issue::extension_problem(SIGIL_ARGUMENTS, extension_file *ef, cha
Problems::issue_problem_end();
}
void Problems::Issue::extension_problem_S(SIGIL_ARGUMENTS, extension_file *ef, text_stream *message) {
ACT_ON_SIGIL
Problems::quote_extension(1, ef);
Problems::quote_stream(2, message);
Problems::issue_problem_begin("");
Problems::issue_problem_segment(
"The extension %1, which your source text makes use of, %2.");
Problems::issue_problem_end();
}
@h Releasing problems.
These occur when the release instructions do not make sense. Sometimes it's
possible to pin down an exact place where the difficulty occurs, but

View file

@ -32,6 +32,18 @@ void Problems::quote_extension(int t, extension_file *p) {
void Problems::expand_extension(OUTPUT_STREAM, void *p) {
Extensions::Files::write_full_title_to_stream(OUT, (extension_file *) p);
}
void Problems::quote_copy(int t, inbuild_copy *p) {
Problems::problem_quote(t, (void *) p, Problems::expand_copy);
}
void Problems::expand_copy(OUTPUT_STREAM, void *p) {
Model::write_copy(OUT, (inbuild_copy *) p);
}
void Problems::quote_work(int t, inbuild_work *p) {
Problems::problem_quote(t, (void *) p, Problems::expand_work);
}
void Problems::expand_work(OUTPUT_STREAM, void *p) {
Works::write(OUT, (inbuild_work *) p);
}
void Problems::quote_object(int t, instance *p) {
Problems::problem_quote(t, (void *) p, Problems::expand_object);
}

View file

@ -964,7 +964,7 @@ void Rulebooks::index_page(OUTPUT_STREAM, int n) {
@<Index the segment for new rulebooks and activities@>;
extension_file *ef;
LOOP_OVER(ef, extension_file)
if (ef != standard_rules_extension)
if (Extensions::Files::is_SR(ef) == FALSE)
if (Rulebooks::noteworthy_rulebooks(ef) > 0)
@<Index the segment for the rulebooks in this extension@>;
}

View file

@ -64,7 +64,7 @@ the extension's name as a major subheading in our index.
HTML_OPEN_WITH("p", "class=\"in1\"");
WRITE("<b>Defined in the source</b>");
HTML_CLOSE("p");
} else if (this_extension != standard_rules_extension) {
} else if (Extensions::Files::is_SR(this_extension) == FALSE) {
if (pass == 2) HTML_TAG("hr");
HTML_OPEN_WITH("p", "class=\"in1\"");
WRITE("<b>From the extension ");
@ -83,7 +83,7 @@ each has a paragraph of its own.
wording HW = Sentences::Headings::get_text(this_heading);
if (Wordings::nonempty(HW)) {
if (pass == 1) @<Strip away bracketed matter in the heading name@>;
if (this_extension == standard_rules_extension)
if (Extensions::Files::is_SR(this_extension))
@<Mark a faked division due to inter-hyphen clue in SR heading@>;
}

View file

@ -53,12 +53,6 @@ void Routines::Compile::routine(phrase *ph,
}
@<Compile some commentary about the routine to follow@> =
heading *definition_area =
Sentences::Headings::of_wording(ParseTree::get_text(ph->declaration_node));
extension_file *definition_extension =
Sentences::Headings::get_extension_containing(definition_area);
if (definition_extension)
Extensions::Files::write_I6_comment_describing(definition_extension);
Routines::ToPhrases::comment_on_request(req);
Phrases::Usage::write_I6_comment_describing(&(ph->usage_data));

View file

@ -512,9 +512,9 @@ void UseOptions::index_options_in_force_from(OUTPUT_STREAM, int category, extens
WRITE("the Options.txt configuration file");
Index::DocReferences::link(OUT, I"OPTIONSFILE"); break;
case EXTENSION_UO_ORIGIN:
if (ef == standard_rules_extension) WRITE("the ");
if (Extensions::Files::is_SR(ef)) WRITE("the ");
else WRITE("the extension ");
WRITE("%+W", ef->title_text);
WRITE("%S", ef->found->edition->work->title);
break;
}
WRITE(":");

View file

@ -43,8 +43,9 @@ void Modules::look_for_cu(parse_node *p) {
compilation_module *Modules::new(parse_node *from) {
extension_file *owner = NULL;
source_location sl = Wordings::location(ParseTree::get_text(from));
if (sl.file_of_origin == NULL) owner = standard_rules_extension;
else owner = SourceFiles::get_extension_corresponding(
if (sl.file_of_origin == NULL) internal_error("null foo");
// if (sl.file_of_origin == NULL) owner = standard_rules_extension; else
owner = SourceFiles::get_extension_corresponding(
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(from))));
compilation_module *C = Packaging::new_cm();
@ -67,7 +68,7 @@ compilation_module *Modules::new(parse_node *from) {
DISCARD_TEXT(V);
}
if (owner == standard_rules_extension) SR_module = C;
if (Extensions::Files::is_SR(owner)) SR_module = C;
if (owner == NULL) source_text_module = C;
return C;
}
@ -76,7 +77,7 @@ compilation_module *Modules::new(parse_node *from) {
compiled from the compilation module will go into a package of that name.
@<Compose a name for the module package this will lead to@> =
if (owner == standard_rules_extension) WRITE_TO(pname, "standard_rules");
if (Extensions::Files::is_SR(owner)) WRITE_TO(pname, "standard_rules");
else if (owner == NULL) WRITE_TO(pname, "source_text");
else {
WRITE_TO(pname, "%X", Extensions::Files::get_work(owner));

View file

@ -12,35 +12,19 @@ does this), and some extensions, such as Basic Inform, need to be given
inclusion sentences -- see Kits.
=
source_file *primary_source_file = NULL; /* first to be opened */
@ There is no real difference between the loading of the primary source text
and the loading of an extension's text, except for the descriptions we
supply in case of any problem messages which might need to be issued,
and for the fact that the mandatory insertion text is loaded before the
primary source text.
=
int SourceFiles::read_extension_source_text(extension_file *EF,
text_stream *synopsis, int documentation_only) {
int rv = SourceFiles::read_file(NULL, synopsis, EF, documentation_only);
if (Log::aspect_switched_on(LEXICAL_OUTPUT_DA)) Word::log_lexer_output();
return rv;
void SourceFiles::read_primary_source_text(void) {
inbuild_copy *C = Inbuild::project()->as_copy;
Model::read_source_text_for(C);
SourceFiles::issue_problems_arising(C);
}
void SourceFiles::read_primary_source_text(void) {
TEMPORARY_TEXT(early);
Projects::early_source_text(early, Inbuild::project());
if (Str::len(early) > 0) Feeds::feed_stream(early);
DISCARD_TEXT(early);
SourceFiles::read_further_mandatory_text();
linked_list *L = Projects::source(Inbuild::project());
if (L) {
build_vertex *N;
LOOP_OVER_LINKED_LIST(N, build_vertex, L) {
filename *F = N->buildable_if_internal_file;
if (TextFiles::exists(F) == FALSE) {
Problems::quote_stream(1, Filenames::get_leafname(F));
void SourceFiles::issue_problems_arising(inbuild_copy *C) {
if (C == NULL) return;
source_text_error *ste;
LOOP_OVER_LINKED_LIST(ste, source_text_error, C->errors_reading_source_text) {
switch (ste->ste_code) {
case OPEN_FAILED_STE:
Problems::quote_stream(1, Filenames::get_leafname(ste->file));
Problems::Issue::handmade_problem(_p_(Untestable));
Problems::issue_problem_segment(
"I can't open the file '%1' of source text. %P"
@ -48,9 +32,18 @@ void SourceFiles::read_primary_source_text(void) {
"hold your source text, maybe your 'Contents.txt' has a "
"typo in it?");
Problems::issue_problem_end();
} else {
SourceFiles::read_file(F, N->annotation, NULL, FALSE);
}
break;
case EXT_MISWORDED_STE:
Problems::quote_work(1, ste->copy->found_by->work);
Problems::quote_stream(2, ste->notes);
Problems::Issue::handmade_problem(_p_(PM_ExtMiswordedBeginsHere));
Problems::issue_problem_segment(
"The extension %1, which your source text makes use of, seems to be "
"damaged or incorrect: its identifying opening line is wrong. "
"Specifically, %2.");
Problems::issue_problem_end();
break;
default: internal_error("an unknown error occurred");
}
}
}
@ -81,124 +74,14 @@ int SourceFiles::increase_sentence_count(wording W) {
return FALSE;
}
@ Either way, we use the following code. The |SourceFiles::read_file| function returns
one of the following values to indicate the source of the source: the value
only really tells us something we didn't know in the case of extensions,
but in that event the Extensions.w routines do indeed want to know this.
=
int SourceFiles::read_file(filename *F, text_stream *synopsis, extension_file *EF,
int documentation_only) {
source_file *sf = NULL;
int area = -1;
if (EF)
area = SourceFiles::read_file_inner(F, synopsis,
Inbuild::nest_list(), documentation_only, &sf,
STORE_POINTER_extension_file(EF), FALSE, EF);
else
area = SourceFiles::read_file_inner(F, synopsis,
NULL, documentation_only, &sf,
STORE_POINTER_extension_file(NULL), TRUE, NULL);
if (area == -1) {
if (EF) {
LOG("Author: %W\n", EF->author_text);
LOG("Title: %W\n", EF->title_text);
Problems::quote_source(1, current_sentence);
Problems::quote_stream(2, synopsis);
Problems::Issue::handmade_problem(_p_(PM_BogusExtension));
Problems::issue_problem_segment(
"I can't find the extension '%2', which seems not to be installed, "
"but was requested by: %1. %P"
"You can get hold of extensions which people have made public at "
"the Inform website, www.inform7.com, or by using the Public "
"Library in the Extensions panel.");
Problems::issue_problem_end();
} else {
Problems::Fatal::filename_related(
"Error: can't open source text file", F);
}
} else {
if (EF == NULL) primary_source_file = sf;
else Extensions::Files::set_corresponding_source_file(EF, sf);
if (documentation_only == FALSE) @<Tell console output about the file@>;
}
return area;
}
@ This is where messages like
|I've also read Standard Rules by Graham Nelson, which is 27204 words long.|
are printed to |stdout| (not |stderr|), in something of an affectionate nod
to \TeX's traditional console output, though occasionally I think silence is
golden and that the messages could go. It's a moot point for almost all users,
though, because the console output is concealed from them by the Inform
application.
@<Tell console output about the file@> =
int wc;
char *message;
if (EF == NULL) message = "I've now read %S, which is %d words long.\n";
else message = "I've also read %S, which is %d words long.\n";
wc = TextFromFiles::total_word_count(sf);
WRITE_TO(STDOUT, message, synopsis, wc);
STREAM_FLUSH(STDOUT);
LOG(message, synopsis, wc);
@ =
int SourceFiles::read_file_inner(filename *F, text_stream *synopsis,
linked_list *search_list, int documentation_only, source_file **S,
general_pointer ref, int primary, extension_file *EF) {
int origin_tried = 1;
FILE *handle = NULL; filename *eventual = F;
@<Set pathname and filename, and open file@>;
if (handle == NULL) return -1;
text_stream *leaf = Filenames::get_leafname(eventual);
if (primary) leaf = I"main source text";
source_file *sf = TextFromFiles::feed_open_file_into_lexer(eventual, handle,
leaf, documentation_only, ref);
fclose(handle);
if (S) *S = sf;
return origin_tried;
}
@ The primary source text must be found where we expect it, or a fatal
error is issued. An extension, however, can be in one of two places: the
user's own repository of installed extensions, or the built-in stock. We
must try each possibility -- in that order, so that the user can supplant
the built-in extensions by installing hacked versions of her own -- and in
the event of failing, we issue only a standard Inform problem message and
continue. While meaningful compilation is unlikely to succeed now, this is
not a fatal error, because fatality would cause the user interface
application to communicate the problem badly.
@<Set pathname and filename, and open file@> =
handle = NULL;
if (search_list) {
text_stream *author_name = EF->ef_req->work->author_name;
text_stream *title = EF->ef_req->work->title;
inbuild_work *work = Works::new(extension_genre, title, author_name);
inbuild_requirement *req = Requirements::any_version_of(work);
req->allow_malformed = TRUE;
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
Nests::search_for(req, search_list, L);
inbuild_search_result *search_result;
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
eventual = search_result->copy->location_if_file;
handle = Filenames::fopen_caseless(eventual, "r");
origin_tried = Nests::get_tag(search_result->nest);
break;
}
} else {
handle = Filenames::fopen(F, "r");
}
@ =
extension_file *SourceFiles::get_extension_corresponding(source_file *sf) {
if (sf == NULL) return NULL;
return RETRIEVE_POINTER_extension_file(sf->your_ref);
inbuild_copy *C = RETRIEVE_POINTER_inbuild_copy(sf->your_ref);
if (C == NULL) return NULL;
if (C->edition->work->genre != extension_genre) return NULL;
inform_extension *E = ExtensionManager::from_copy(C);
if (E == NULL) return NULL;
return E->ef;
}
@ And the following converts lexer error conditions into I7 problem messages.

View file

@ -575,7 +575,7 @@ void Instances::index_usages(OUTPUT_STREAM, instance *I) {
parse_node *at = IU->where_instance_used;
if (at) {
source_file *sf = Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(at)));
if (sf == primary_source_file) {
if (Projects::draws_from_source_file(Inbuild::project(), sf)) {
k++;
if (k == 1) {
HTMLFiles::open_para(OUT, 1, "tight");

View file

@ -342,7 +342,7 @@ void NewVerbs::parse_new(parse_node *PN, int imperative) {
#ifndef IF_MODULE
source_file *pos = Lexer::file_of_origin(Wordings::first_wn(MW));
extension_file *loc = SourceFiles::get_extension_corresponding(pos);
if (loc == standard_rules_extension) return;
if (Extensions::Files::is_SR(loc)) return;
#endif
Problems::Issue::sentence_problem(_p_(PM_NoSuchBuiltInMeaning),
"that's not one of the built-in meanings I know",

View file

@ -123,7 +123,8 @@ calls.
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
if (<unsuitable-name>(AW)) return 0;
if (<unsuitable-name>(TW)) return 0;
ef = Extensions::Inclusion::load(AW, TW, -1, EMPTY_WORDING);
inbuild_requirement *req = Requirements::any_version_of(work);
ef = Extensions::Inclusion::load(req);
if (ef == NULL) return 0; /* shouldn't happen: it was there only moments ago */
Extensions::Documentation::write_extension_documentation(NULL, ef);
@ -181,10 +182,10 @@ different template:
WRITE("&nbsp;");
@<Write up any restrictions on VM usage@> =
if (Wordings::nonempty(ef->VM_restriction_text)) {
WRITE("%+W", ef->VM_restriction_text);
if (Wordings::nonempty(Extensions::Files::VM_text(ef))) {
WRITE("%+W", Extensions::Files::VM_text(ef));
WRITE("&nbsp;");
VirtualMachines::write_icons(OUT, ef->VM_restriction_text);
VirtualMachines::write_icons(OUT, Extensions::Files::VM_text(ef));
}
@<Write up the version number, if any, and location@> =

View file

@ -137,34 +137,25 @@ its purpose.
=
typedef struct extension_file {
struct inbuild_requirement *ef_req; /* Work and version needed */
struct wording author_text; /* Author's name */
struct wording title_text; /* Extension name */
struct wording VM_restriction_text; /* Restricting use to certain VMs */
struct parse_node *inclusion_sentence; /* Where the source called for this */
struct inbuild_copy *found;
MEMORY_MANAGEMENT
} extension_file;
extension_file *standard_rules_extension; /* the Standard Rules by Graham Nelson */
@ We begin with some housekeeping, really: the code required to create new
extension file structures, and to manage existing ones.
=
extension_file *Extensions::Files::new(wording AW, wording NW, wording VMW, int version_word) {
TEMPORARY_TEXT(violation);
int Extensions::Files::is_SR(extension_file *ef) {
inform_extension *E = Extensions::Files::find(ef);
if (E == NULL) return FALSE;
if (Extensions::is_standard(E)) return TRUE;
return FALSE;
}
extension_file *Extensions::Files::new(inbuild_requirement *req) {
extension_file *ef = CREATE(extension_file);
ef->author_text = AW;
ef->title_text = NW;
@<Create work for new extension file@>;
ef->inclusion_sentence = current_sentence;
ef->VM_restriction_text = VMW;
ef->ef_req = req;
ef->found = NULL;
if (Str::len(violation) > 0) {
LOG("So %S\n", violation);
Problems::Issue::extension_problem_S(_p_(PM_IncludesTooLong), ef, violation); /* see below */
}
DISCARD_TEXT(violation);
return ef;
}
@ -174,42 +165,29 @@ inform_extension *Extensions::Files::find(extension_file *ef) {
return ExtensionManager::from_copy(ef->found);
}
@ We protect ourselves a little against absurdly long requested author or
title names, and then produce problem messages in the event of only longish
ones, unless the census is going on: in which case it's better to leave the
matter to the census errors system elsewhere.
parse_node *Extensions::Files::where_included(extension_file *ef) {
inform_extension *E = Extensions::Files::find(ef);
if (E == NULL) return NULL;
return E->inclusion_sentence;
}
@<Create work for new extension file@> =
TEMPORARY_TEXT(exft);
TEMPORARY_TEXT(exfa);
WRITE_TO(exft, "%+W", ef->title_text);
WRITE_TO(exfa, "%+W", ef->author_text);
if (Extensions::Census::currently_recording_errors() == FALSE) {
if (Str::len(exfa) >= MAX_EXTENSION_AUTHOR_LENGTH) {
WRITE_TO(violation,
"has an author's name which is too long, exceeding the maximum "
"allowed (%d characters) by %d",
MAX_EXTENSION_AUTHOR_LENGTH-1,
(int) (1+Str::len(exfa)-MAX_EXTENSION_AUTHOR_LENGTH));
Str::truncate(exfa, MAX_EXTENSION_AUTHOR_LENGTH-1);
}
if (Str::len(exft) >= MAX_EXTENSION_AUTHOR_LENGTH) {
WRITE_TO(violation,
"has a title which is too long, exceeding the maximum allowed "
"(%d characters) by %d",
MAX_EXTENSION_TITLE_LENGTH-1,
(int) (1+Str::len(exft)-MAX_EXTENSION_TITLE_LENGTH));
Str::truncate(exft, MAX_EXTENSION_AUTHOR_LENGTH-1);
}
}
inbuild_work *work = Works::new(extension_genre, exft, exfa);
Works::add_to_database(work, LOADED_WDBC);
inbuild_version_number min = VersionNumbers::null();
if (version_word >= 0) min = Extensions::Inclusion::parse_version(version_word);
ef->ef_req = Requirements::new(work, min, VersionNumbers::null());
if (Works::is_standard_rules(work)) standard_rules_extension = ef;
DISCARD_TEXT(exft);
DISCARD_TEXT(exfa);
void Extensions::Files::set_where_included(extension_file *ef, parse_node *N) {
inform_extension *E = Extensions::Files::find(ef);
if (E == NULL) internal_error("unfound ef");
Extensions::set_inclusion_sentence(E, N);
}
wording Extensions::Files::VM_text(extension_file *ef) {
inform_extension *E = Extensions::Files::find(ef);
if (E == NULL) return EMPTY_WORDING;
return E->VM_restriction_text;
}
void Extensions::Files::set_VM_text(extension_file *ef, wording W) {
inform_extension *E = Extensions::Files::find(ef);
if (E == NULL) internal_error("unfound ef");
Extensions::set_VM_text(E, W);
}
@ Three pieces of information (not available when the EF is created) will
be set later on, by other parts of Inform calling the routines below.
@ -242,26 +220,6 @@ when the EF structure is created, we don't know that yet), we need to tally
it up with the corresponding source file structure.
=
void Extensions::Files::set_corresponding_source_file(extension_file *ef, source_file *sf) {
TEMPORARY_TEXT(error_text);
inbuild_copy *C = ExtensionManager::claim_file_as_copy(sf->name, error_text, TRUE);
if (Str::len(error_text) > 0) {
Problems::quote_extension(1, ef);
Problems::quote_stream(2, error_text);
Problems::Issue::handmade_problem(_p_(PM_ExtMiswordedBeginsHere));
Problems::issue_problem_segment(
"The extension %1, which your source text makes use of, seems to be "
"damaged or incorrect: its identifying opening line is wrong. "
"Specifically, %2.");
Problems::issue_problem_end();
} else {
ef->found = C;
inform_extension *E = ExtensionManager::from_copy(C);
E->read_into_file = sf;
}
DISCARD_TEXT(error_text);
}
source_file *Extensions::Files::source(extension_file *ef) {
inform_extension *E = Extensions::Files::find(ef);
if (E) return E->read_into_file;
@ -317,11 +275,11 @@ First, printing the name to an arbitrary UTF-8 file:
=
void Extensions::Files::write_name_to_file(extension_file *ef, OUTPUT_STREAM) {
WRITE("%+W", ef->title_text);
WRITE("%+W", ef->found->edition->work->title);
}
void Extensions::Files::write_author_to_file(extension_file *ef, OUTPUT_STREAM) {
WRITE("%+W", ef->author_text);
WRITE("%+W", ef->found->edition->work->author_name);
}
@ Next, the debugging log:
@ -329,29 +287,14 @@ void Extensions::Files::write_author_to_file(extension_file *ef, OUTPUT_STREAM)
=
void Extensions::Files::log(extension_file *ef) {
if (ef == NULL) { LOG("<null-extension-file>"); return; }
LOG("%W by %W", ef->title_text, ef->author_text);
}
@ Next, printing the name in the form of a comment in an (ISO Latin-1)
Inform 6 source file:
=
void Extensions::Files::write_I6_comment_describing(extension_file *ef) {
if (ef == standard_rules_extension) {
Produce::comment(Emit::tree(), I"From the Standard Rules");
} else {
TEMPORARY_TEXT(C);
WRITE_TO(C, "From \"%~W\" by %~W", ef->title_text, ef->author_text);
Produce::comment(Emit::tree(), C);
DISCARD_TEXT(C);
}
LOG("%X", ef->found);
}
@ And finally printing the name to a C string:
=
void Extensions::Files::write_full_title_to_stream(OUTPUT_STREAM, extension_file *ef) {
WRITE("%+W by %+W", ef->title_text, ef->author_text);
WRITE("%X", ef->found->edition->work);
}
@h Checking version numbers.
@ -371,20 +314,20 @@ void Extensions::Files::check_versions(void) {
if (Requirements::meets(Extensions::Files::get_edition(ef), ef->ef_req) == FALSE) {
inbuild_version_number have = Extensions::Files::get_version(ef);
LOG("Need %v, have %v\n", &(ef->ef_req->min_version), &have);
current_sentence = ef->inclusion_sentence;
current_sentence = Extensions::Files::where_included(ef);
Problems::quote_source(1, current_sentence);
Problems::quote_extension(2, ef);
if (VersionNumbers::is_null(have) == FALSE) {
TEMPORARY_TEXT(vn);
VersionNumbers::to_text(vn, have);
Problems::quote_stream(3, vn);
Problems::Issue::handmade_problem(_p_(PM_ExtVersionTooLow));
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
Problems::issue_problem_segment(
"You wrote %1: but my copy of %2 is only version %3.");
Problems::issue_problem_end();
DISCARD_TEXT(vn);
} else {
Problems::Issue::handmade_problem(_p_(PM_ExtNoVersion));
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
Problems::issue_problem_segment(
"You wrote %1: but my copy of %2 contains no version "
"number, and is therefore considered to be earlier than "
@ -421,7 +364,7 @@ void Extensions::Files::ShowExtensionVersions_routine(void) {
extension_file *ef;
LOOP_OVER(ef, extension_file) {
TEMPORARY_TEXT(the_author_name);
WRITE_TO(the_author_name, "%+W", ef->author_text);
WRITE_TO(the_author_name, "%S", ef->found->edition->work->author_name);
int self_penned = FALSE;
#ifdef IF_MODULE
if (PL::Bibliographic::story_author_is(the_author_name)) self_penned = TRUE;
@ -509,9 +452,11 @@ void Extensions::Files::index(OUTPUT_STREAM) {
Extensions::Files::index_extensions_from(OUT, NULL);
extension_file *from;
LOOP_OVER(from, extension_file)
if (from != standard_rules_extension)
if (Extensions::Files::is_SR(from) == FALSE)
Extensions::Files::index_extensions_from(OUT, from);
LOOP_OVER(from, extension_file)
if (Extensions::Files::is_SR(from))
Extensions::Files::index_extensions_from(OUT, from);
Extensions::Files::index_extensions_from(OUT, standard_rules_extension);
HTML_OPEN("p"); HTML_CLOSE("p");
}
@ -520,22 +465,22 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
extension_file *ef;
LOOP_OVER(ef, extension_file) {
extension_file *owner = NULL;
if (ef == standard_rules_extension) owner = standard_rules_extension;
else if (Wordings::nonempty(ParseTree::get_text(ef->inclusion_sentence))) {
source_location sl = Wordings::location(ParseTree::get_text(ef->inclusion_sentence));
if (sl.file_of_origin == NULL) owner = standard_rules_extension;
parse_node *N = Extensions::Files::where_included(from);
if (Wordings::nonempty(ParseTree::get_text(N))) {
source_location sl = Wordings::location(ParseTree::get_text(N));
if (sl.file_of_origin == NULL) owner = NULL;
else owner = SourceFiles::get_extension_corresponding(
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(ef->inclusion_sentence))));
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(N))));
}
if (owner != from) continue;
if (show_head) {
HTMLFiles::open_para(OUT, 2, "hanging");
HTML::begin_colour(OUT, I"808080");
WRITE("Included ");
if (from == standard_rules_extension) WRITE("automatically by Inform");
if (Extensions::Files::is_SR(from)) WRITE("automatically by Inform");
else if (from == NULL) WRITE("from the source text");
else {
WRITE("by the extension %+W", from->title_text);
WRITE("by the extension %S", from->found->edition->work->title);
}
show_head = FALSE;
HTML::end_colour(OUT);
@ -544,12 +489,12 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
HTML_OPEN_WITH("ul", "class=\"leaders\"");
HTML_OPEN_WITH("li", "class=\"leaded indent2\"");
HTML_OPEN("span");
WRITE("%+W ", ef->title_text);
WRITE("%S ", ef->found->edition->work->title);
Works::begin_extension_link(OUT, ef->ef_req->work, NULL);
HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/help.png");
Works::end_extension_link(OUT, ef->ef_req->work);
if (ef != standard_rules_extension) { /* give author and inclusion links, but not for SR */
WRITE(" by %+W", ef->author_text);
if (Extensions::Files::is_SR(ef) == FALSE) { /* give author and inclusion links, but not for SR */
WRITE(" by %X", ef->found->edition->work->author_name);
}
if (VersionNumbers::is_null(Extensions::Files::get_version(ef)) == FALSE) {
WRITE(" ");
@ -568,7 +513,7 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
HTML_CLOSE("span");
HTML_OPEN("span");
WRITE("%d words", TextFromFiles::total_word_count(Extensions::Files::source(ef)));
if (from == NULL) Index::link(OUT, Wordings::first_wn(ParseTree::get_text(ef->inclusion_sentence)));
if (from == NULL) Index::link(OUT, Wordings::first_wn(ParseTree::get_text(Extensions::Files::where_included(ef))));
HTML_CLOSE("span");
HTML_CLOSE("li");
HTML_CLOSE("ul");

View file

@ -125,10 +125,54 @@ parse tree.
if (version_word >= 0)
Extensions::Inclusion::parse_version(version_word); /* this checks the formatting of the version number */
extension_file *requested_extension =
Extensions::Inclusion::load(AW, W, version_word, RW);
TEMPORARY_TEXT(violation);
TEMPORARY_TEXT(exft);
TEMPORARY_TEXT(exfa);
WRITE_TO(exft, "%+W", W);
WRITE_TO(exfa, "%+W", AW);
if (Extensions::Census::currently_recording_errors() == FALSE) {
if (Str::len(exfa) >= MAX_EXTENSION_AUTHOR_LENGTH) {
WRITE_TO(violation,
"has an author's name which is too long, exceeding the maximum "
"allowed (%d characters) by %d",
MAX_EXTENSION_AUTHOR_LENGTH-1,
(int) (1+Str::len(exfa)-MAX_EXTENSION_AUTHOR_LENGTH));
Str::truncate(exfa, MAX_EXTENSION_AUTHOR_LENGTH-1);
}
if (Str::len(exft) >= MAX_EXTENSION_AUTHOR_LENGTH) {
WRITE_TO(violation,
"has a title which is too long, exceeding the maximum allowed "
"(%d characters) by %d",
MAX_EXTENSION_TITLE_LENGTH-1,
(int) (1+Str::len(exft)-MAX_EXTENSION_TITLE_LENGTH));
Str::truncate(exft, MAX_EXTENSION_AUTHOR_LENGTH-1);
}
}
inbuild_work *work = Works::new(extension_genre, exft, exfa);
Works::add_to_database(work, LOADED_WDBC);
inbuild_version_number min = VersionNumbers::null();
if (version_word >= 0) min = Extensions::Inclusion::parse_version(version_word);
inbuild_requirement *req = Requirements::new(work, min, VersionNumbers::null());
DISCARD_TEXT(exft);
DISCARD_TEXT(exfa);
if (Str::len(violation) > 0) {
Problems::quote_wording(1, W);
Problems::quote_wording(2, AW);
Problems::quote_stream(3, violation);
Problems::Issue::handmade_problem(_p_(PM_IncludesTooLong));
Problems::issue_problem_segment(
"The extension %1 by %2, which your source text requests, %3.");
Problems::issue_problem_end();
}
DISCARD_TEXT(violation);
extension_file *requested_extension = Extensions::Inclusion::load(req);
inform_extension *E = Extensions::Files::find(requested_extension);
if (E) {
Extensions::set_inclusion_sentence(E, current_sentence);
Extensions::set_VM_text(E, RW);
}
if ((E) && (E->body_text_unbroken)) {
Sentences::break(E->body_text, requested_extension);
E->body_text_unbroken = FALSE;
@ -138,18 +182,18 @@ parse tree.
Extensions are loaded here.
=
extension_file *Extensions::Inclusion::load(wording A, wording T,
int version_word, wording VMW) {
extension_file *Extensions::Inclusion::load(inbuild_requirement *req) {
NaturalLanguages::scan(); /* to avoid wording from those interleaving with extension wording */
@<Do not load the same extension work twice@>;
extension_file *ef;
LOOP_OVER(ef, extension_file)
if ((Wordings::match(ef->author_text, A)) && (Wordings::match(ef->title_text, T)))
@<This is an extension already loaded, so note any version number hike and return@>;
inform_extension *E = NULL;
@<Read the extension file into the lexer, and break it into body and documentation@>;
ef = Extensions::Files::new(A, T, VMW, version_word);
if (problem_count == 0)
@<Read the extension file into the lexer, and break it into body and documentation@>;
extension_file *ef = Extensions::Files::new(req);
if (E) {
ef->found = E->as_copy;
E->ef = ef;
}
return ef;
}
@ -165,140 +209,103 @@ then we need to note that the version requirement on PS has been raised to 3.
(This is why version numbers are not checked at load time: in general, we
can't know at load time what we will ultimately require.)
@<This is an extension already loaded, so note any version number hike and return@> =
if (version_word >= 0) {
inbuild_version_number V = Extensions::Inclusion::parse_version(version_word);
if (Requirements::ratchet_minimum(V, ef->ef_req))
ef->inclusion_sentence = current_sentence;
}
return ef;
@<Do not load the same extension work twice@> =
extension_file *ef;
LOOP_OVER(ef, extension_file)
if (ef->found)
if (Requirements::meets(ef->found->edition, req)) {
inbuild_version_number V = req->min_version;
if (VersionNumbers::is_null(V) == FALSE)
if (Requirements::ratchet_minimum(V, ef->ef_req))
Extensions::Files::set_where_included(ef, current_sentence);
return ef;
}
@ We finally make our call out of the Extensions section, down through the
trap-door into Read Source Text, to seek and open the file.
@<Read the extension file into the lexer, and break it into body and documentation@> =
TEMPORARY_TEXT(synopsis);
@<Concoct a synopsis for the extension to be read@>;
feed_t id = Feeds::begin();
int origin = SourceFiles::read_extension_source_text(ef, synopsis, census_mode);
inform_extension *E = Extensions::Files::find(ef);
if (E) {
int found_to_be_malformed = FALSE;
req->allow_malformed = TRUE;
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;
default: /* which can happen if the extension file cannot be found */
E->loaded_from_built_in_area = FALSE; break;
}
wording EXW = Feeds::end(id);
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
if (LinkedLists::len(search_result->copy->errors_reading_source_text) > 0) {
SourceFiles::issue_problems_arising(search_result->copy);
E = NULL;
found_to_be_malformed = TRUE;
}
break;
}
DISCARD_TEXT(synopsis);
@ We concoct a textual synopsis in the form
|"Pantomime Sausages by Mr Punch"|
to be used by |SourceFiles::read_extension_source_text| for printing to |stdout|. Since
we dare not assume |stdout| can manage characters outside the basic ASCII
range, we flatten them from general ISO to plain ASCII.
@<Concoct a synopsis for the extension to be read@> =
WRITE_TO(synopsis, "%+W by %+W", T, A);
LOOP_THROUGH_TEXT(pos, synopsis)
Str::put(pos,
Characters::make_filename_safe(Str::get(pos)));
@ If an extension file contains the special text (outside literal mode) of
|---- Documentation ----|
then this is taken as the end of the Inform source, and the beginning of a
snippet of documentation about the extension; text from that point on is
saved until later, but not broken into sentences for the parse tree, and it
is therefore invisible to the rest of Inform. If this division line is not
present then the extension contains only body source and no documentation.
=
<extension-body> ::=
*** ---- documentation ---- ... | ==> TRUE
... ==> FALSE
@<Break the extension's text into body and documentation@> =
<extension-body>(EXW);
E->body_text = GET_RW(<extension-body>, 1);
if (<<r>>) E->documentation_text = GET_RW(<extension-body>, 2);
E->body_text_unbroken = TRUE; /* mark this to be sentence-broken */
@h Parsing extension version numbers.
Extensions can have versions in the form N/DDDDDD, a format which was chosen
for sentimental reasons: IF enthusiasts know it well from the banner text of
the Infocom titles of the 1980s. This story file, for instance, was compiled
at the time of the Reykjavik summit between Presidents Gorbachev and Reagan:
|Moonmist|
|Infocom interactive fiction - a mystery story|
|Copyright (c) 1986 by Infocom, Inc. All rights reserved.|
|Moonmist is a trademark of Infocom, Inc.|
|Release number 9 / Serial number 861022|
Story file collectors customarily abbreviate this in catalogues to |9/861022|.
In our scheme, DDDDDD can be omitted (in which case so must the slash be).
Spacing is not allowed around the slash (if present), so the version number
always occupies a single lexical word.
The following routine parses the version number at word |vwn| to give an
non-negative integer -- in fact it really just construes the whole thing,
with the slash removed, as a 7-digit number -- in such a way that an earlier
version always has a lower integer than a later one. It is legal for |vwn|
to be $-1$, which means "no version number quoted", and evaluates as
0 -- corresponding to |0/000000|, lower than the lowest version number it is
legal to quote explicitly, which is |1|. (It follows that requiring no
version in particular is equivalent to requiring |0/000000| or better, since
every extension passes that test.)
In order that the numerical form of a version number should be a signed
32-bit integer which does not overflow, we require that the release number
|N| be at most 999. It could in fact rise to 2146 without incident, but
it seems cleaner to constrain the number of digits than the value.
=
inbuild_version_number Extensions::Inclusion::parse_version(int vwn) {
int i, slashes = 0, digits = 0, slash_at = 0;
wchar_t *p, *q;
if (vwn == -1) return VersionNumbers::from_pair(0, 0); /* an unspecified version equates to |0/000000| */
p = Lexer::word_text(vwn); q = p;
for (i=0; p[i] != 0; i++)
if (p[i] == '/') {
slashes++; if ((i == 0) || (slashes > 1)) goto Malformed;
slash_at = i; q = p+i+1;
} else {
if (!(Characters::isdigit(p[i]))) goto Malformed;
digits++;
if (found_to_be_malformed == FALSE) {
if (E == NULL) @<Issue a cannot-find problem@>
else {
inbuild_copy *C = E->as_copy;
Model::read_source_text_for(C);
SourceFiles::issue_problems_arising(C);
}
if ((p[0] == '0') || (digits == 0)) goto Malformed;
}
if ((slashes == 0) && (digits <= 3)) /* so that |p| points to 1 to 3 digits, not starting with |0| */
return VersionNumbers::from_major(Wide::atoi(p));
p[slash_at] = 0; /* temporarily replace the slash with a null, making |p| and |q| distinct C strings */
if (Wide::len(p) > 3) goto Malformed; /* now |p| points to 1 to 3 digits, not starting with |0| */
if (Wide::len(q) != 6) goto Malformed;
while (*q == '0') q++; /* now |q| points to 0 to 6 digits, not starting with |0| */
if (q[0] == 0) q--; /* if it was 0 digits, backspace to make it a single digit |0| */
inbuild_version_number V = VersionNumbers::from_pair(Wide::atoi(p), Wide::atoi(q));
p[slash_at] = '/'; /* put the slash back over the null byte temporarily dividing the string */
@<Issue a cannot-find problem@> =
inbuild_requirement *req2 = Requirements::any_version_of(req->work);
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
Nests::search_for(req2, Inbuild::nest_list(), L);
if (LinkedLists::len(L) == 0) {
LOG("Author: %W\n", req->work->author_name);
LOG("Title: %W\n", req->work->title);
Problems::quote_source(1, current_sentence);
Problems::Issue::handmade_problem(_p_(PM_BogusExtension));
Problems::issue_problem_segment(
"I can't find the extension requested by: %1. %P"
"You can get hold of extensions which people have made public at "
"the Inform website, www.inform7.com, or by using the Public "
"Library in the Extensions panel.");
Problems::issue_problem_end();
} else {
TEMPORARY_TEXT(versions);
inbuild_search_result *search_result;
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
if (Str::len(versions) > 0) WRITE_TO(versions, " or ");
inbuild_version_number V = search_result->copy->edition->version;
if (VersionNumbers::is_null(V)) WRITE_TO(versions, "an unnumbered version");
else WRITE_TO(versions, "version %v", &V);
}
Problems::quote_source(1, current_sentence);
Problems::quote_stream(2, versions);
Problems::Issue::handmade_problem(_p_(PM_ExtVersionTooLow));
Problems::issue_problem_segment(
"I can't find the right version of the extension requested by %1 - "
"I can only find %2. %P"
"You can get hold of extensions which people have made public at "
"the Inform website, www.inform7.com, or by using the Public "
"Library in the Extensions panel.");
Problems::issue_problem_end();
DISCARD_TEXT(versions);
}
@ =
inbuild_version_number Extensions::Inclusion::parse_version(int vwn) {
TEMPORARY_TEXT(vtext);
WRITE_TO(vtext, "%N", vwn, vwn);
inbuild_version_number V = VersionNumbers::from_text(vtext);
if (VersionNumbers::is_null(V)) @<Issue a problem message for a malformed version number@>;
return V;
Malformed: @<Issue a problem message for a malformed version number@>;
}
@ Because we tend to call |Extensions::Inclusion::parse_version| repeatedly on the same word, we
want to recover tidily from this problem, and not report it over and over.
We do this by altering the text to |1|, the lowest well-formed version
number text.
@ Because we tend to call |Extensions::Inclusion::parse_version| repeatedly on
the same word, we want to recover tidily from this problem, and not report it
over and over. We do this by altering the text to |1|, the lowest well-formed
version number text.
@<Issue a problem message for a malformed version number@> =
LOG("Offending word number %d <%N>\n", vwn, vwn);
@ -356,19 +363,10 @@ void Extensions::Inclusion::check_begins_here(parse_node *PN, extension_file *ef
Problems::quote_wording(2, ParseTree::get_text(PN));
<begins-here-sentence-subject>(ParseTree::get_text(PN));
wording W = Wordings::new(<<t1>>, <<t2>>);
wording AW = Wordings::new(<<auth1>>, <<auth2>>);
if (Wordings::empty(AW)) return;
if (<<r>> < 0) Extensions::Files::set_version(ef, VersionNumbers::null());
else Extensions::Files::set_version(ef, Extensions::Inclusion::parse_version(<<r>>));
ef->VM_restriction_text = Wordings::new(<<rest1>>, <<rest2>>);
Extensions::Files::set_VM_text(ef, Wordings::new(<<rest1>>, <<rest2>>));
if (Wordings::nonempty(ef->VM_restriction_text))
if (Wordings::nonempty(Extensions::Files::VM_text(ef)))
@<Check that the extension's stipulation about the virtual machine can be met@>;
if ((Wordings::match(ef->title_text, W) == FALSE) ||
(Wordings::match(ef->author_text, AW) == FALSE))
@<Issue a problem message pointing out that name and author do not agree with filename@>;
}
@ On the other hand, we do already know what virtual machine we are compiling
@ -376,38 +374,19 @@ for, so we can immediately object if the loaded extension cannot be used
with our VM de jour.
@<Check that the extension's stipulation about the virtual machine can be met@> =
if (<platform-qualifier>(ef->VM_restriction_text)) {
if (<platform-qualifier>(Extensions::Files::VM_text(ef))) {
if (<<r>> == PLATFORM_UNMET_HQ)
@<Issue a problem message saying that the VM does not meet requirements@>;
} else {
@<Issue a problem message saying that the VM requirements are malformed@>;
}
@ Suppose we wanted Onion Cookery by Delia Smith. We loaded the extension
file called Onion Cookery in the Delia Smith folder of the (probably external)
extensions area: but suppose that file turns out instead to be French Cuisine
by Elizabeth David, according to its "begins here" sentence? Then the
following problem message is produced. (In fact it's now hard to get this
problem to arise, because inbuild searches for extensions much more carefully
than Inform 7 used to.)
@<Issue a problem message pointing out that name and author do not agree with filename@> =
Problems::quote_extension(1, ef);
Problems::quote_wording(2, ParseTree::get_text(PN));
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
Problems::issue_problem_segment(
"The extension %1, which your source text makes use of, seems to be "
"misidentified: its 'begins here' sentence declares it as '%2'. "
"(Perhaps it was wrongly installed?)");
Problems::issue_problem_end();
return;
@ See Virtual Machines for the grammar of what can be given as a VM
requirement.
@<Issue a problem message saying that the VM requirements are malformed@> =
Problems::quote_extension(1, ef);
Problems::quote_wording(2, ef->VM_restriction_text);
Problems::quote_wording(2, Extensions::Files::VM_text(ef));
Problems::Issue::handmade_problem(_p_(PM_ExtMalformedVM));
Problems::issue_problem_segment(
"Your source text makes use of the extension %1: but my copy "
@ -422,15 +401,14 @@ matter of removing the inclusion, not of altering the extension, so we
report this problem at the inclusion line.
@<Issue a problem message saying that the VM does not meet requirements@> =
current_sentence = ef->inclusion_sentence;
current_sentence = Extensions::Files::where_included(ef);
Problems::quote_source(1, current_sentence);
Problems::quote_wording(2, ef->title_text);
Problems::quote_wording(3, ef->author_text);
Problems::quote_wording(4, ef->VM_restriction_text);
Problems::quote_copy(2, ef->found);
Problems::quote_wording(3, Extensions::Files::VM_text(ef));
Problems::Issue::handmade_problem(_p_(PM_ExtInadequateVM));
Problems::issue_problem_segment(
"You wrote %1: but my copy of %2 by %3 stipulates that it "
"is '%4'. That means it can only be used with certain of "
"You wrote %1: but my copy of %2 stipulates that it "
"is '%3'. That means it can only be used with certain of "
"the possible compiled story file formats, and at the "
"moment, we don't fit the requirements. (You can change "
"the format used for this project on the Settings panel.)");
@ -445,7 +423,8 @@ that the extension isn't the one he thinks it is.
=
void Extensions::Inclusion::check_ends_here(parse_node *PN, extension_file *ef) {
wording W = Articles::remove_the(ParseTree::get_text(PN));
if ((problem_count == 0) && (Wordings::match(ef->title_text, W) == FALSE)) {
wording T = Feeds::feed_stream(ef->found->edition->work->title);
if ((problem_count == 0) && (Wordings::match(T, W) == FALSE)) {
current_sentence = PN;
Problems::quote_extension(1, ef);
Problems::quote_wording(2, ParseTree::get_text(PN));

View file

@ -344,7 +344,9 @@ void PL::Bibliographic::index_library_card(OUTPUT_STREAM) {
PL::Bibliographic::library_card_entry(OUT, "Release number", story_release_number_VAR, I"1");
PL::Bibliographic::library_card_entry(OUT, "Story creation year", story_creation_year_VAR, I"(This year)");
TEMPORARY_TEXT(lang);
WRITE_TO(lang, "%X", Projects::get_language_of_play(Inbuild::project())->as_copy->edition->work);
inform_language *L = Projects::get_language_of_play(Inbuild::project());
if (L == NULL) WRITE_TO(lang, "English");
else WRITE_TO(lang, "%X", L->as_copy->edition->work);
PL::Bibliographic::library_card_entry(OUT, "Language of play", NULL, lang);
DISCARD_TEXT(lang);
PL::Bibliographic::library_card_entry(OUT, "IFID number", NULL, PL::Bibliographic::IFID::read_uuid());

View file

@ -279,7 +279,7 @@ int PL::Player::player_complete_model(int stage) {
instance *I;
LOOP_OVER_OBJECT_INSTANCES(I)
if ((PL::Spatial::object_is_a_room(I)) && (start_room == NULL)
&& (Instances::get_creating_file(I) == primary_source_file))
&& (Projects::draws_from_source_file(Inbuild::project(), Instances::get_creating_file(I))))
start_room = I;
LOOP_OVER_OBJECT_INSTANCES(I)
if ((PL::Spatial::object_is_a_room(I)) && (start_room == NULL))

View file

@ -1381,7 +1381,7 @@ int PL::Actions::index(OUTPUT_STREAM, action_name *an, int pass,
HTML_TAG("br");
f = FALSE;
*new_par = TRUE;
} else if (*ext != standard_rules_extension) {
} else if (Extensions::Files::is_SR(*ext) == FALSE) {
if (f) HTML_CLOSE("p");
HTML_OPEN("p");
WRITE("<b>Actions defined by the extension ");
@ -1394,7 +1394,7 @@ int PL::Actions::index(OUTPUT_STREAM, action_name *an, int pass,
*new_par = TRUE;
}
}
if ((definition_area != *current_area) && (*ext == standard_rules_extension)) {
if ((definition_area != *current_area) && (Extensions::Files::is_SR(*ext))) {
if (f) HTML_CLOSE("p");
HTML_OPEN("p");
wording W = Sentences::Headings::get_text(definition_area);

View file

@ -168,7 +168,7 @@ void Index::DocReferences::doc_mark_used(text_stream *symb, int at_word) {
source_file *pos = Lexer::file_of_origin(at_word);
loc = SourceFiles::get_extension_corresponding(pos);
if (loc == NULL) dr->usage_count++;
else if (loc == standard_rules_extension) dr->sr_usage_count++;
else if (Extensions::Files::is_SR(loc)) dr->sr_usage_count++;
else dr->ext_usage_count++;
} else dr->sr_usage_count++;
return;

View file

@ -778,7 +778,7 @@ void Index::link_to(OUTPUT_STREAM, int wn, int nonbreaking_space) {
void Index::link_to_location(OUTPUT_STREAM, source_location sl, int nonbreaking_space) {
extension_file *ef = SourceFiles::get_extension_corresponding(sl.file_of_origin);
if (ef) {
if (ef != standard_rules_extension) {
if (Extensions::Files::is_SR(ef) == FALSE) {
if (nonbreaking_space) WRITE("&nbsp;"); else WRITE(" ");
Works::begin_extension_link(OUT, Extensions::Files::get_work(ef), NULL);
HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/Revealext.png");

View file

@ -17479,7 +17479,7 @@ A second double-quoted text can also, optionally, be added in yet a third specia
Note the typical style here: it's a phrase rather than a sentence, and neither starts with an upper-case letter nor ends with a full stop. (The additional credit is then used in documentation and also in the VERSION text of any Inform story file using the extension.)
[x] Version numbering {PM_ExtVersionTooLow} {PM_ExtNoVersion}
[x] Version numbering {PM_ExtVersionTooLow}
^^{version number (of extension)}
^^{extensions: writing: version number}