From 104a1782b5b1467ffcebc03dc9f11bf95f04b2ad Mon Sep 17 00:00:00 2001 From: Graham Nelson Date: Sun, 16 Feb 2020 22:01:50 +0000 Subject: [PATCH] Wrangly refactor of extension and source lexing --- inblorb/Tests/inblorb.intest | 1 + .../inbuild-module/Chapter 1/Inbuild Module.w | 2 + .../inbuild-module/Chapter 2/Build Graphs.w | 2 + .../Chapter 2/Conceptual Model.w | 18 + inbuild/inbuild-module/Chapter 2/Nests.w | 4 +- .../Chapter 2/Version Numbers.w | 37 ++- inbuild/inbuild-module/Chapter 2/Works.w | 2 + .../Chapter 3/Extension Manager.w | 53 ++- .../inbuild-module/Chapter 3/Kit Manager.w | 2 +- .../Chapter 3/Language Manager.w | 2 +- .../Chapter 3/Pipeline Manager.w | 2 +- .../Chapter 3/Project Bundle Manager.w | 6 + .../Chapter 3/Project File Manager.w | 8 + .../Chapter 3/Template Manager.w | 2 +- .../Chapter 4/Extension Services.w | 90 ++++- .../Chapter 4/Project Services.w | 32 ++ .../inbuild-module/Chapter 4/Source Text.w | 81 +++++ inbuild/inbuild-module/Contents.w | 1 + .../Tests/Test Problems/PM_ExtNoVersion.txt | 3 - .../Test Problems/PM_ExtVersionMalformed.txt | 11 - .../Test Problems/PM_ExtVersionTooLow.txt | 2 + .../_Results_Ideal/PM_BogusExtension.txt | 7 +- .../_Results_Ideal/PM_ExtMalformedVM.txt | 13 +- .../PM_ExtMiswordedBeginsHere.txt | 4 +- .../_Results_Ideal/PM_ExtNoVersion.txt | 11 - .../_Results_Ideal/PM_ExtVersionMalformed.txt | 44 +-- .../_Results_Ideal/PM_ExtVersionTooLow.txt | 26 +- .../_Results_Ideal/PM_IncludesTooLong.txt | 30 +- inform7/core-module/Chapter 1/Main Routine.w | 4 + inform7/core-module/Chapter 19/Tables.w | 2 +- .../Chapter 2/Supplementary Issues.w | 10 - .../Chapter 2/Supplementary Quotes.w | 12 + inform7/core-module/Chapter 21/Rulebooks.w | 2 +- .../core-module/Chapter 22/Phrasebook Index.w | 4 +- .../core-module/Chapter 25/Compile Phrases.w | 6 - inform7/core-module/Chapter 26/Use Options.w | 4 +- .../Chapter 27/Compilation Modules.w | 9 +- .../core-module/Chapter 3/Read Source Text.w | 175 ++-------- inform7/core-module/Chapter 5/Instances.w | 2 +- inform7/core-module/Chapter 6/New Verbs.w | 2 +- .../Chapter 8/Extension Documentation.w | 9 +- .../core-module/Chapter 8/Extension Files.w | 163 +++------ .../Chapter 8/Including Extensions.w | 311 ++++++++---------- .../if-module/Chapter 2/Bibliographic Data.w | 4 +- inform7/if-module/Chapter 3/The Player.w | 2 +- inform7/if-module/Chapter 4/Actions.w | 4 +- .../Chapter 3/Documentation References.w | 2 +- .../Chapter 3/Index File Services.w | 2 +- .../Documentation/Writing with Inform.txt | 2 +- 49 files changed, 663 insertions(+), 564 deletions(-) create mode 100644 inbuild/inbuild-module/Chapter 4/Source Text.w delete mode 100644 inform7/Tests/Test Problems/PM_ExtNoVersion.txt delete mode 100644 inform7/Tests/Test Problems/_Results_Ideal/PM_ExtNoVersion.txt diff --git a/inblorb/Tests/inblorb.intest b/inblorb/Tests/inblorb.intest index 13104ab9f..d6b6fcef0 100644 --- a/inblorb/Tests/inblorb.intest +++ b/inblorb/Tests/inblorb.intest @@ -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 diff --git a/inbuild/inbuild-module/Chapter 1/Inbuild Module.w b/inbuild/inbuild-module/Chapter 1/Inbuild Module.w index 887da94bb..fb5a52de8 100644 --- a/inbuild/inbuild-module/Chapter 1/Inbuild Module.w +++ b/inbuild/inbuild-module/Chapter 1/Inbuild Module.w @@ -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) diff --git a/inbuild/inbuild-module/Chapter 2/Build Graphs.w b/inbuild/inbuild-module/Chapter 2/Build Graphs.w index f152da28b..199423b11 100644 --- a/inbuild/inbuild-module/Chapter 2/Build Graphs.w +++ b/inbuild/inbuild-module/Chapter 2/Build Graphs.w @@ -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; } diff --git a/inbuild/inbuild-module/Chapter 2/Conceptual Model.w b/inbuild/inbuild-module/Chapter 2/Conceptual Model.w index 76a25bf59..889ddf052 100644 --- a/inbuild/inbuild-module/Chapter 2/Conceptual Model.w +++ b/inbuild/inbuild-module/Chapter 2/Conceptual Model.w @@ -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; +} diff --git a/inbuild/inbuild-module/Chapter 2/Nests.w b/inbuild/inbuild-module/Chapter 2/Nests.w index b92f7ad93..864dcd0d0 100644 --- a/inbuild/inbuild-module/Chapter 2/Nests.w +++ b/inbuild/inbuild-module/Chapter 2/Nests.w @@ -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); } diff --git a/inbuild/inbuild-module/Chapter 2/Version Numbers.w b/inbuild/inbuild-module/Chapter 2/Version Numbers.w index c6ecda3b3..6f6c642c2 100644 --- a/inbuild/inbuild-module/Chapter 2/Version Numbers.w +++ b/inbuild/inbuild-module/Chapter 2/Version Numbers.w @@ -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; ias_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, @; @; 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); +} diff --git a/inbuild/inbuild-module/Chapter 3/Kit Manager.w b/inbuild/inbuild-module/Chapter 3/Kit Manager.w index c89930539..c6c3eed5a 100644 --- a/inbuild/inbuild-module/Chapter 3/Kit Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Kit Manager.w @@ -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); } } } diff --git a/inbuild/inbuild-module/Chapter 3/Language Manager.w b/inbuild/inbuild-module/Chapter 3/Language Manager.w index be1cb2bce..c1a6f37d8 100644 --- a/inbuild/inbuild-module/Chapter 3/Language Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Language Manager.w @@ -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); } } } diff --git a/inbuild/inbuild-module/Chapter 3/Pipeline Manager.w b/inbuild/inbuild-module/Chapter 3/Pipeline Manager.w index 707fb2825..6ec2134d6 100644 --- a/inbuild/inbuild-module/Chapter 3/Pipeline Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Pipeline Manager.w @@ -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); } } } diff --git a/inbuild/inbuild-module/Chapter 3/Project Bundle Manager.w b/inbuild/inbuild-module/Chapter 3/Project Bundle Manager.w index 9f0968ab5..95f6c9a2b 100644 --- a/inbuild/inbuild-module/Chapter 3/Project Bundle Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Project Bundle Manager.w @@ -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); +} diff --git a/inbuild/inbuild-module/Chapter 3/Project File Manager.w b/inbuild/inbuild-module/Chapter 3/Project File Manager.w index 159e644d1..94880e0f5 100644 --- a/inbuild/inbuild-module/Chapter 3/Project File Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Project File Manager.w @@ -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); +} diff --git a/inbuild/inbuild-module/Chapter 3/Template Manager.w b/inbuild/inbuild-module/Chapter 3/Template Manager.w index ab5e68293..1cbefa4ce 100644 --- a/inbuild/inbuild-module/Chapter 3/Template Manager.w +++ b/inbuild/inbuild-module/Chapter 3/Template Manager.w @@ -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); } } } diff --git a/inbuild/inbuild-module/Chapter 4/Extension Services.w b/inbuild/inbuild-module/Chapter 4/Extension Services.w index 992483461..7487f900b 100644 --- a/inbuild/inbuild-module/Chapter 4/Extension Services.w +++ b/inbuild/inbuild-module/Chapter 4/Extension Services.w @@ -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); + @; + 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)) @; + } +} + +@ 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. + +@ = + 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. + += + ::= + *** ---- documentation ---- ... | ==> TRUE + ... ==> FALSE + +@ = + (EXW); + E->body_text = GET_RW(, 1); + if (<>) E->documentation_text = GET_RW(, 2); + E->body_text_unbroken = TRUE; /* mark this to be sentence-broken */ diff --git a/inbuild/inbuild-module/Chapter 4/Project Services.w b/inbuild/inbuild-module/Chapter 4/Project Services.w index 1769220c5..d424081df 100644 --- a/inbuild/inbuild-module/Chapter 4/Project Services.w +++ b/inbuild/inbuild-module/Chapter 4/Project Services.w @@ -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; +} diff --git a/inbuild/inbuild-module/Chapter 4/Source Text.w b/inbuild/inbuild-module/Chapter 4/Source Text.w new file mode 100644 index 000000000..3d5746b47 --- /dev/null +++ b/inbuild/inbuild-module/Chapter 4/Source Text.w @@ -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) @; + } + 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. + +@ = + 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); diff --git a/inbuild/inbuild-module/Contents.w b/inbuild/inbuild-module/Contents.w index beaaec6ae..f5cf2e097 100644 --- a/inbuild/inbuild-module/Contents.w +++ b/inbuild/inbuild-module/Contents.w @@ -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 diff --git a/inform7/Tests/Test Problems/PM_ExtNoVersion.txt b/inform7/Tests/Test Problems/PM_ExtNoVersion.txt deleted file mode 100644 index 7f125cad5..000000000 --- a/inform7/Tests/Test Problems/PM_ExtNoVersion.txt +++ /dev/null @@ -1,3 +0,0 @@ -Include version 3/070628 of ExtNoVersion Extension by Araminta Intest. - -Beachy Head is a room. diff --git a/inform7/Tests/Test Problems/PM_ExtVersionMalformed.txt b/inform7/Tests/Test Problems/PM_ExtVersionMalformed.txt index a5529cb73..50e976783 100644 --- a/inform7/Tests/Test Problems/PM_ExtVersionMalformed.txt +++ b/inform7/Tests/Test Problems/PM_ExtVersionMalformed.txt @@ -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.] diff --git a/inform7/Tests/Test Problems/PM_ExtVersionTooLow.txt b/inform7/Tests/Test Problems/PM_ExtVersionTooLow.txt index 7c9a29be3..f1206211d 100644 --- a/inform7/Tests/Test Problems/PM_ExtVersionTooLow.txt +++ b/inform7/Tests/Test Problems/PM_ExtVersionTooLow.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_BogusExtension.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_BogusExtension.txt index 6fd75c4a9..342a39ef5 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_BogusExtension.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_BogusExtension.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMalformedVM.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMalformedVM.txt index 335932362..dbb7ed1d7 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMalformedVM.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMalformedVM.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMiswordedBeginsHere.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMiswordedBeginsHere.txt index 7b9594c42..2dc2d8416 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMiswordedBeginsHere.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtMiswordedBeginsHere.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtNoVersion.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtNoVersion.txt deleted file mode 100644 index 4930455eb..000000000 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtNoVersion.txt +++ /dev/null @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionMalformed.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionMalformed.txt index ec805a3f8..28464a6db 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionMalformed.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionMalformed.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionTooLow.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionTooLow.txt index 77f719a7a..f595d8226 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionTooLow.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_ExtVersionTooLow.txt @@ -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. diff --git a/inform7/Tests/Test Problems/_Results_Ideal/PM_IncludesTooLong.txt b/inform7/Tests/Test Problems/_Results_Ideal/PM_IncludesTooLong.txt index a59d6b496..ff7d22f22 100644 --- a/inform7/Tests/Test Problems/_Results_Ideal/PM_IncludesTooLong.txt +++ b/inform7/Tests/Test Problems/_Results_Ideal/PM_IncludesTooLong.txt @@ -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. diff --git a/inform7/core-module/Chapter 1/Main Routine.w b/inform7/core-module/Chapter 1/Main Routine.w index 4df129429..e05bc48ba 100644 --- a/inform7/core-module/Chapter 1/Main Routine.w +++ b/inform7/core-module/Chapter 1/Main Routine.w @@ -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); diff --git a/inform7/core-module/Chapter 19/Tables.w b/inform7/core-module/Chapter 19/Tables.w index e70047036..620339c72 100644 --- a/inform7/core-module/Chapter 19/Tables.w +++ b/inform7/core-module/Chapter 19/Tables.w @@ -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("%+W", ef->title_text); + WRITE("%S", ef->found->edition->work->title); HTML_CLOSE("p"); } HTML::begin_plain_html_table(OUT); diff --git a/inform7/core-module/Chapter 2/Supplementary Issues.w b/inform7/core-module/Chapter 2/Supplementary Issues.w index 4f4c0207a..3544755aa 100644 --- a/inform7/core-module/Chapter 2/Supplementary Issues.w +++ b/inform7/core-module/Chapter 2/Supplementary Issues.w @@ -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 diff --git a/inform7/core-module/Chapter 2/Supplementary Quotes.w b/inform7/core-module/Chapter 2/Supplementary Quotes.w index f69a3179f..5eac321c6 100644 --- a/inform7/core-module/Chapter 2/Supplementary Quotes.w +++ b/inform7/core-module/Chapter 2/Supplementary Quotes.w @@ -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); } diff --git a/inform7/core-module/Chapter 21/Rulebooks.w b/inform7/core-module/Chapter 21/Rulebooks.w index ba8e69142..c6bc6bcdf 100644 --- a/inform7/core-module/Chapter 21/Rulebooks.w +++ b/inform7/core-module/Chapter 21/Rulebooks.w @@ -964,7 +964,7 @@ void Rulebooks::index_page(OUTPUT_STREAM, int n) { @; 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) @; } diff --git a/inform7/core-module/Chapter 22/Phrasebook Index.w b/inform7/core-module/Chapter 22/Phrasebook Index.w index d26715600..16879ef7a 100644 --- a/inform7/core-module/Chapter 22/Phrasebook Index.w +++ b/inform7/core-module/Chapter 22/Phrasebook Index.w @@ -64,7 +64,7 @@ the extension's name as a major subheading in our index. HTML_OPEN_WITH("p", "class=\"in1\""); WRITE("Defined in the source"); 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("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) @; - if (this_extension == standard_rules_extension) + if (Extensions::Files::is_SR(this_extension)) @; } diff --git a/inform7/core-module/Chapter 25/Compile Phrases.w b/inform7/core-module/Chapter 25/Compile Phrases.w index 7da8a3d3c..a993176a5 100644 --- a/inform7/core-module/Chapter 25/Compile Phrases.w +++ b/inform7/core-module/Chapter 25/Compile Phrases.w @@ -53,12 +53,6 @@ void Routines::Compile::routine(phrase *ph, } @ = - 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)); diff --git a/inform7/core-module/Chapter 26/Use Options.w b/inform7/core-module/Chapter 26/Use Options.w index 4ac3bfb06..19b5dce48 100644 --- a/inform7/core-module/Chapter 26/Use Options.w +++ b/inform7/core-module/Chapter 26/Use Options.w @@ -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(":"); diff --git a/inform7/core-module/Chapter 27/Compilation Modules.w b/inform7/core-module/Chapter 27/Compilation Modules.w index 554c535b9..9f97a75aa 100644 --- a/inform7/core-module/Chapter 27/Compilation Modules.w +++ b/inform7/core-module/Chapter 27/Compilation Modules.w @@ -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. @ = - 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)); diff --git a/inform7/core-module/Chapter 3/Read Source Text.w b/inform7/core-module/Chapter 3/Read Source Text.w index 6249123bb..f532a78cb 100644 --- a/inform7/core-module/Chapter 3/Read Source Text.w +++ b/inform7/core-module/Chapter 3/Read Source Text.w @@ -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) @; - } - 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. - -@ = - 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; - @; - 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. - -@ = - 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. diff --git a/inform7/core-module/Chapter 5/Instances.w b/inform7/core-module/Chapter 5/Instances.w index b4f9e68f7..a95ac372b 100644 --- a/inform7/core-module/Chapter 5/Instances.w +++ b/inform7/core-module/Chapter 5/Instances.w @@ -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"); diff --git a/inform7/core-module/Chapter 6/New Verbs.w b/inform7/core-module/Chapter 6/New Verbs.w index 706f969e8..6ab8210ea 100644 --- a/inform7/core-module/Chapter 6/New Verbs.w +++ b/inform7/core-module/Chapter 6/New Verbs.w @@ -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", diff --git a/inform7/core-module/Chapter 8/Extension Documentation.w b/inform7/core-module/Chapter 8/Extension Documentation.w index ab74bd099..d9a5d58de 100644 --- a/inform7/core-module/Chapter 8/Extension Documentation.w +++ b/inform7/core-module/Chapter 8/Extension Documentation.w @@ -123,7 +123,8 @@ calls. Feeds::feed_text(L"This sentence provides a firebreak, no more. "); if ((AW)) return 0; if ((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(" "); @ = - 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(" "); - VirtualMachines::write_icons(OUT, ef->VM_restriction_text); + VirtualMachines::write_icons(OUT, Extensions::Files::VM_text(ef)); } @ = diff --git a/inform7/core-module/Chapter 8/Extension Files.w b/inform7/core-module/Chapter 8/Extension Files.w index 6faea509d..4bc9d8780 100644 --- a/inform7/core-module/Chapter 8/Extension Files.w +++ b/inform7/core-module/Chapter 8/Extension Files.w @@ -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; - @; - 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; +} -@ = - 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(""); 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"); diff --git a/inform7/core-module/Chapter 8/Including Extensions.w b/inform7/core-module/Chapter 8/Including Extensions.w index f71538d7c..20dd1196f 100644 --- a/inform7/core-module/Chapter 8/Including Extensions.w +++ b/inform7/core-module/Chapter 8/Including Extensions.w @@ -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 */ + @; - extension_file *ef; - LOOP_OVER(ef, extension_file) - if ((Wordings::match(ef->author_text, A)) && (Wordings::match(ef->title_text, T))) - @; + inform_extension *E = NULL; + @; - ef = Extensions::Files::new(A, T, VMW, version_word); - if (problem_count == 0) - @; + 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.) -@ = - 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; +@ = + 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. @ = - TEMPORARY_TEXT(synopsis); - @; - 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)) @; + 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. - -@ = - 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. - -= - ::= - *** ---- documentation ---- ... | ==> TRUE - ... ==> FALSE - -@ = - (EXW); - E->body_text = GET_RW(, 1); - if (<>) E->documentation_text = GET_RW(, 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) @ + 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 */ +@ = + 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)) @; return V; - - Malformed: @; } -@ 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. @ = 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)); (ParseTree::get_text(PN)); - wording W = Wordings::new(<>, <>); - wording AW = Wordings::new(<>, <>); - if (Wordings::empty(AW)) return; - if (<> < 0) Extensions::Files::set_version(ef, VersionNumbers::null()); - else Extensions::Files::set_version(ef, Extensions::Inclusion::parse_version(<>)); - ef->VM_restriction_text = Wordings::new(<>, <>); + Extensions::Files::set_VM_text(ef, Wordings::new(<>, <>)); - if (Wordings::nonempty(ef->VM_restriction_text)) + if (Wordings::nonempty(Extensions::Files::VM_text(ef))) @; - - if ((Wordings::match(ef->title_text, W) == FALSE) || - (Wordings::match(ef->author_text, AW) == FALSE)) - @; } @ 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. @ = - if ((ef->VM_restriction_text)) { + if ((Extensions::Files::VM_text(ef))) { if (<> == PLATFORM_UNMET_HQ) @; } else { @; } -@ 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.) - -@ = - 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. @ = 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. @ = - 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)); diff --git a/inform7/if-module/Chapter 2/Bibliographic Data.w b/inform7/if-module/Chapter 2/Bibliographic Data.w index eea92393b..d8096f4e9 100644 --- a/inform7/if-module/Chapter 2/Bibliographic Data.w +++ b/inform7/if-module/Chapter 2/Bibliographic Data.w @@ -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()); diff --git a/inform7/if-module/Chapter 3/The Player.w b/inform7/if-module/Chapter 3/The Player.w index d4ff9d6ce..500f2c544 100644 --- a/inform7/if-module/Chapter 3/The Player.w +++ b/inform7/if-module/Chapter 3/The Player.w @@ -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)) diff --git a/inform7/if-module/Chapter 4/Actions.w b/inform7/if-module/Chapter 4/Actions.w index f4cfcf73a..0624443fb 100644 --- a/inform7/if-module/Chapter 4/Actions.w +++ b/inform7/if-module/Chapter 4/Actions.w @@ -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("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); diff --git a/inform7/index-module/Chapter 3/Documentation References.w b/inform7/index-module/Chapter 3/Documentation References.w index 8e242b029..bfeb227ea 100644 --- a/inform7/index-module/Chapter 3/Documentation References.w +++ b/inform7/index-module/Chapter 3/Documentation References.w @@ -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; diff --git a/inform7/index-module/Chapter 3/Index File Services.w b/inform7/index-module/Chapter 3/Index File Services.w index bfc74c627..5cc54797c 100644 --- a/inform7/index-module/Chapter 3/Index File Services.w +++ b/inform7/index-module/Chapter 3/Index File Services.w @@ -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(" "); 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"); diff --git a/resources/Documentation/Writing with Inform.txt b/resources/Documentation/Writing with Inform.txt index b3de97470..81441c4f5 100644 --- a/resources/Documentation/Writing with Inform.txt +++ b/resources/Documentation/Writing with Inform.txt @@ -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}