mirror of
https://github.com/ganelson/inform.git
synced 2024-06-29 05:24:57 +03:00
Wrangly refactor of extension and source lexing
This commit is contained in:
parent
fbe371caa5
commit
104a1782b5
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
Semantic version numbers such as 3.7.1.
|
||||
|
||||
@ For example, 4, 7.1, and 0.2.3 are all version numbers. Up to |VERSION_NUMBER_DEPTH|
|
||||
@ Traditional semantic version numbers look like dot-divided runs of
|
||||
non-negative integers: for example, 4, 7.1, and 0.2.3. Up to |VERSION_NUMBER_DEPTH|
|
||||
components can be given. The tail of the array should be padded with |-1| values;
|
||||
otherwise, components should all be non-negative integers.
|
||||
|
||||
|
@ -13,6 +14,24 @@ typedef struct inbuild_version_number {
|
|||
int version_numbers[VERSION_NUMBER_DEPTH];
|
||||
} inbuild_version_number;
|
||||
|
||||
@ However, Inform 7 extensions have for many years allowed two forms of
|
||||
version number: either just |N|, which clearly fits the scheme above, or
|
||||
|N/DDDDDD|, which does not. This is a format which was chosen for sentimental
|
||||
reasons: IF enthusiasts know it well from the banner text of the Infocom
|
||||
titles of the 1980s. This story file, for instance, was compiled at the
|
||||
time of the Reykjavik summit between Presidents Gorbachev and Reagan:
|
||||
|
||||
|Moonmist|
|
||||
|Infocom interactive fiction - a mystery story|
|
||||
|Copyright (c) 1986 by Infocom, Inc. All rights reserved.|
|
||||
|Moonmist is a trademark of Infocom, Inc.|
|
||||
|Release number 9 / Serial number 861022|
|
||||
|
||||
Story file collectors customarily abbreviate this in catalogues to |9/861022|.
|
||||
|
||||
We will therefore allow this notation, and convert it silently each way.
|
||||
|N/DDDDDD| is equivalent to |N.DDDDDD|.
|
||||
|
||||
@ All invalid strings of numbers -- i.e., breaking the above rules -- are
|
||||
called "null" versions, and can never be valid as the version of anything.
|
||||
Instead they are used to represent the absence of a version number.
|
||||
|
@ -72,20 +91,30 @@ void VersionNumbers::writer(OUTPUT_STREAM, char *format_string, void *vE) {
|
|||
|
||||
inbuild_version_number VersionNumbers::from_text(text_stream *T) {
|
||||
inbuild_version_number V;
|
||||
int component = 0, val = -1;
|
||||
int component = 0, val = -1, dots_used = 0, slashes_used = 0, count = 0;
|
||||
LOOP_THROUGH_TEXT(pos, T) {
|
||||
wchar_t c = Str::get(pos);
|
||||
if (c == '.') {
|
||||
if (c == '.') dots_used++;
|
||||
if (c == '/') slashes_used++;
|
||||
if ((c == '.') || (c == '/')) {
|
||||
if (val == -1) return VersionNumbers::null();
|
||||
if (component >= VERSION_NUMBER_DEPTH) return VersionNumbers::null();
|
||||
V.version_numbers[component] = val;
|
||||
component++; val = -1;
|
||||
component++; val = -1; count = 0;
|
||||
} else if (Characters::isdigit(c)) {
|
||||
int digit = c - '0';
|
||||
if ((val == 0) && (slashes_used == 0))
|
||||
return VersionNumbers::null();
|
||||
if (val < 0) val = digit; else val = 10*val + digit;
|
||||
count++;
|
||||
} else return VersionNumbers::null();
|
||||
}
|
||||
if (val == -1) return VersionNumbers::null();
|
||||
if ((dots_used > 0) && (slashes_used > 0)) return VersionNumbers::null();
|
||||
if (slashes_used > 0) {
|
||||
if (component > 1) return VersionNumbers::null();
|
||||
if (count != 6) return VersionNumbers::null();
|
||||
}
|
||||
if (component >= VERSION_NUMBER_DEPTH) return VersionNumbers::null();
|
||||
V.version_numbers[component] = val;
|
||||
for (int i=component+1; i<VERSION_NUMBER_DEPTH; i++) V.version_numbers[i] = -1;
|
||||
|
|
|
@ -188,6 +188,7 @@ int Works::is_standard_rules(inbuild_work *work) {
|
|||
Works::new(extension_genre, I"Standard Rules", I"Graham Nelson");
|
||||
Works::add_to_database(a_work_for_standard_rules, HYPOTHETICAL_WDBC);
|
||||
}
|
||||
if (work == NULL) return FALSE;
|
||||
return Works::match(work, a_work_for_standard_rules);
|
||||
}
|
||||
|
||||
|
@ -198,6 +199,7 @@ int Works::is_basic_inform(inbuild_work *work) {
|
|||
Works::new(extension_genre, I"Basic Inform", I"Graham Nelson");
|
||||
Works::add_to_database(a_work_for_basic_inform, HYPOTHETICAL_WDBC);
|
||||
}
|
||||
if (work == NULL) return FALSE;
|
||||
return Works::match(work, a_work_for_basic_inform);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ void ExtensionManager::start(void) {
|
|||
METHOD_ADD(extension_genre, GENRE_CLAIM_AS_COPY_MTID, ExtensionManager::claim_as_copy);
|
||||
METHOD_ADD(extension_genre, GENRE_SEARCH_NEST_FOR_MTID, ExtensionManager::search_nest_for);
|
||||
METHOD_ADD(extension_genre, GENRE_COPY_TO_NEST_MTID, ExtensionManager::copy_to_nest);
|
||||
METHOD_ADD(extension_genre, GENRE_READ_SOURCE_TEXT_FOR_MTID, ExtensionManager::read_source_text_for);
|
||||
}
|
||||
|
||||
void ExtensionManager::write_work(inbuild_genre *gen, OUTPUT_STREAM, inbuild_work *work) {
|
||||
|
@ -45,10 +46,25 @@ inform_extension *ExtensionManager::from_copy(inbuild_copy *C) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
dictionary *ext_copy_cache = NULL;
|
||||
inbuild_copy *ExtensionManager::new_copy(inbuild_edition *edition, filename *F) {
|
||||
inform_extension *E = Extensions::new_ie();
|
||||
inbuild_copy *C = Model::copy_in_file(edition, F, STORE_POINTER_inform_extension(E));
|
||||
E->as_copy = C;
|
||||
if (ext_copy_cache == NULL) ext_copy_cache = Dictionaries::new(16, FALSE);
|
||||
TEMPORARY_TEXT(key);
|
||||
WRITE_TO(key, "%f", F);
|
||||
inbuild_copy *C = NULL;
|
||||
if (Dictionaries::find(ext_copy_cache, key))
|
||||
C = Dictionaries::read_value(ext_copy_cache, key);
|
||||
if (C == NULL) {
|
||||
inform_extension *E = Extensions::new_ie();
|
||||
C = Model::copy_in_file(edition, F, STORE_POINTER_inform_extension(E));
|
||||
E->as_copy = C;
|
||||
if (Works::is_standard_rules(C->edition->work)) Extensions::make_standard(E);
|
||||
Dictionaries::create(ext_copy_cache, key);
|
||||
Dictionaries::write_value(ext_copy_cache, key, C);
|
||||
} else {
|
||||
C->edition = edition;
|
||||
}
|
||||
DISCARD_TEXT(key);
|
||||
return C;
|
||||
}
|
||||
|
||||
|
@ -69,21 +85,27 @@ void ExtensionManager::claim_as_copy(inbuild_genre *gen, inbuild_copy **C,
|
|||
if (directory_status == TRUE) return;
|
||||
if (Str::eq_insensitive(ext, I"i7x")) {
|
||||
filename *F = Filenames::from_text(arg);
|
||||
*C = ExtensionManager::claim_file_as_copy(F, NULL, FALSE);
|
||||
*C = ExtensionManager::claim_file_as_copy(F, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F, text_stream *error_text,
|
||||
inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F,
|
||||
int allow_malformed) {
|
||||
if ((allow_malformed) && (TextFiles::exists(F) == FALSE)) return NULL;
|
||||
TEMPORARY_TEXT(author);
|
||||
TEMPORARY_TEXT(title);
|
||||
TEMPORARY_TEXT(rubric_text);
|
||||
TEMPORARY_TEXT(requirement_text);
|
||||
TEMPORARY_TEXT(error_text);
|
||||
inbuild_version_number V =
|
||||
ExtensionManager::scan_file(F, title, author, rubric_text, requirement_text, error_text);
|
||||
inbuild_copy *C = ExtensionManager::new_copy(
|
||||
Model::edition(Works::new(extension_genre, title, author), V), F);
|
||||
if (Str::len(error_text) > 0) {
|
||||
source_text_error *ste = SourceText::ste_text(EXT_MISWORDED_STE, error_text);
|
||||
ste->copy = C;
|
||||
ADD_TO_LINKED_LIST(ste, source_text_error, C->errors_reading_source_text);
|
||||
}
|
||||
if ((allow_malformed) || (Str::len(error_text) == 0)) {
|
||||
Works::add_to_database(C->edition->work, CLAIMED_WDBC);
|
||||
ExtensionManager::build_vertex(C);
|
||||
|
@ -94,6 +116,7 @@ inbuild_copy *ExtensionManager::claim_file_as_copy(filename *F, text_stream *err
|
|||
DISCARD_TEXT(title);
|
||||
DISCARD_TEXT(rubric_text);
|
||||
DISCARD_TEXT(requirement_text);
|
||||
DISCARD_TEXT(error_text);
|
||||
return C;
|
||||
}
|
||||
|
||||
|
@ -117,7 +140,14 @@ inbuild_version_number ExtensionManager::scan_file(filename *F,
|
|||
@<Read the rubric text, if any is present@>;
|
||||
@<Parse the version, title, author and VM requirements from the titling line@>;
|
||||
fclose(EXTF);
|
||||
if (Str::len(version_text) > 0) V = VersionNumbers::from_text(version_text);
|
||||
if (Str::len(version_text) > 0) {
|
||||
V = VersionNumbers::from_text(version_text);
|
||||
if (VersionNumbers::is_null(V)) {
|
||||
if (error_text)
|
||||
WRITE_TO(error_text, "the version number '%S' is malformed", version_text);
|
||||
return V;
|
||||
}
|
||||
}
|
||||
DISCARD_TEXT(titling_line);
|
||||
DISCARD_TEXT(version_text);
|
||||
return V;
|
||||
|
@ -289,9 +319,9 @@ void ExtensionManager::search_nest_for_r(pathname *P, inbuild_nest *N,
|
|||
|
||||
void ExtensionManager::search_nest_for_single_file(filename *F, inbuild_nest *N,
|
||||
inbuild_requirement *req, linked_list *search_results) {
|
||||
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F, NULL, req->allow_malformed);
|
||||
inbuild_copy *C = ExtensionManager::claim_file_as_copy(F, req->allow_malformed);
|
||||
if ((C) && (Requirements::meets(C->edition, req))) {
|
||||
Nests::add_search_result(search_results, N, C);
|
||||
Nests::add_search_result(search_results, N, C, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,3 +381,10 @@ build an extension at all.
|
|||
void ExtensionManager::build_vertex(inbuild_copy *C) {
|
||||
Graphs::copy_vertex(C);
|
||||
}
|
||||
|
||||
@h Source text.
|
||||
|
||||
=
|
||||
void ExtensionManager::read_source_text_for(inbuild_genre *G, inbuild_copy *C, linked_list *errors) {
|
||||
Extensions::read_source_text_for(ExtensionManager::from_copy(C), errors);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,31 +5,115 @@ An Inform 7 extension.
|
|||
@ =
|
||||
typedef struct inform_extension {
|
||||
struct inbuild_copy *as_copy;
|
||||
#ifdef CORE_MODULE
|
||||
struct wording body_text; /* Body of source text supplied in extension, if any */
|
||||
int body_text_unbroken; /* Does this contain text waiting to be sentence-broken? */
|
||||
struct wording documentation_text; /* Documentation supplied in extension, if any */
|
||||
struct wording VM_restriction_text; /* Restricting use to certain VMs */
|
||||
int standard; /* the (or perhaps just a) Standard Rules extension */
|
||||
#ifdef CORE_MODULE
|
||||
int loaded_from_built_in_area; /* Located within Inform application */
|
||||
int authorial_modesty; /* Do not credit in the compiled game */
|
||||
struct source_file *read_into_file; /* Which source file loaded this */
|
||||
struct extension_file *ef; /* Corresponding Inform7 compiler structure */
|
||||
struct text_stream *rubric_as_lexed;
|
||||
struct text_stream *extra_credit_as_lexed;
|
||||
struct parse_node *inclusion_sentence; /* Where the source called for this */
|
||||
#endif
|
||||
struct source_file *read_into_file; /* Which source file loaded this */
|
||||
MEMORY_MANAGEMENT
|
||||
} inform_extension;
|
||||
|
||||
inform_extension *Extensions::new_ie(void) {
|
||||
inform_extension *E = CREATE(inform_extension);
|
||||
E->as_copy = NULL;
|
||||
#ifdef CORE_MODULE
|
||||
E->body_text = EMPTY_WORDING;
|
||||
E->body_text_unbroken = FALSE;
|
||||
E->documentation_text = EMPTY_WORDING;
|
||||
E->VM_restriction_text = EMPTY_WORDING;
|
||||
E->standard = FALSE;
|
||||
#ifdef CORE_MODULE
|
||||
E->loaded_from_built_in_area = FALSE;
|
||||
E->authorial_modesty = FALSE;
|
||||
E->rubric_as_lexed = NULL;
|
||||
E->extra_credit_as_lexed = NULL;
|
||||
E->read_into_file = NULL;
|
||||
E->ef = NULL;
|
||||
E->inclusion_sentence = NULL;
|
||||
#endif
|
||||
return E;
|
||||
}
|
||||
|
||||
#ifdef CORE_MODULE
|
||||
void Extensions::set_inclusion_sentence(inform_extension *E, parse_node *N) {
|
||||
E->inclusion_sentence = N;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Extensions::set_VM_text(inform_extension *E, wording W) {
|
||||
E->VM_restriction_text = W;
|
||||
}
|
||||
|
||||
int Extensions::is_standard(inform_extension *E) {
|
||||
if (E == NULL) return FALSE;
|
||||
return E->standard;
|
||||
}
|
||||
|
||||
void Extensions::make_standard(inform_extension *E) {
|
||||
E->standard = TRUE;
|
||||
}
|
||||
|
||||
@
|
||||
|
||||
@e EXT_MISWORDED_STE
|
||||
|
||||
=
|
||||
void Extensions::read_source_text_for(inform_extension *E, linked_list *errors) {
|
||||
filename *F = E->as_copy->location_if_file;
|
||||
int doc_only = FALSE;
|
||||
#ifdef CORE_MODULE
|
||||
if (CoreMain::census_mode()) doc_only = TRUE;
|
||||
#endif
|
||||
TEMPORARY_TEXT(synopsis);
|
||||
@<Concoct a synopsis for the extension to be read@>;
|
||||
E->read_into_file = SourceText::read_file(F, synopsis, doc_only, errors, FALSE);
|
||||
DISCARD_TEXT(synopsis);
|
||||
if (E->read_into_file) {
|
||||
E->read_into_file->your_ref = STORE_POINTER_inbuild_copy(E->as_copy);
|
||||
wording EXW = E->read_into_file->text_read;
|
||||
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
|
||||
}
|
||||
}
|
||||
|
||||
@ We concoct a textual synopsis in the form
|
||||
|
||||
|"Pantomime Sausages by Mr Punch"|
|
||||
|
||||
to be used by |SourceFiles::read_extension_source_text| for printing to |stdout|. Since
|
||||
we dare not assume |stdout| can manage characters outside the basic ASCII
|
||||
range, we flatten them from general ISO to plain ASCII.
|
||||
|
||||
@<Concoct a synopsis for the extension to be read@> =
|
||||
WRITE_TO(synopsis, "%S by %S", E->as_copy->edition->work->title, E->as_copy->edition->work->author_name);
|
||||
LOOP_THROUGH_TEXT(pos, synopsis)
|
||||
Str::put(pos,
|
||||
Characters::make_filename_safe(Str::get(pos)));
|
||||
|
||||
@ If an extension file contains the special text (outside literal mode) of
|
||||
|
||||
|---- Documentation ----|
|
||||
|
||||
then this is taken as the end of the Inform source, and the beginning of a
|
||||
snippet of documentation about the extension; text from that point on is
|
||||
saved until later, but not broken into sentences for the parse tree, and it
|
||||
is therefore invisible to the rest of Inform. If this division line is not
|
||||
present then the extension contains only body source and no documentation.
|
||||
|
||||
=
|
||||
<extension-body> ::=
|
||||
*** ---- documentation ---- ... | ==> TRUE
|
||||
... ==> FALSE
|
||||
|
||||
@<Break the extension's text into body and documentation@> =
|
||||
<extension-body>(EXW);
|
||||
E->body_text = GET_RW(<extension-body>, 1);
|
||||
if (<<r>>) E->documentation_text = GET_RW(<extension-body>, 2);
|
||||
E->body_text_unbroken = TRUE; /* mark this to be sentence-broken */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
81
inbuild/inbuild-module/Chapter 4/Source Text.w
Normal file
81
inbuild/inbuild-module/Chapter 4/Source Text.w
Normal file
|
@ -0,0 +1,81 @@
|
|||
[SourceText::] Source Text.
|
||||
|
||||
Code for reading Inform 7 source text, which Inbuild uses for both extensions
|
||||
and projects.
|
||||
|
||||
@ Either way, we use the following code. The |SourceText::read_file| function returns
|
||||
one of the following values to indicate the source of the source: the value
|
||||
only really tells us something we didn't know in the case of extensions,
|
||||
but in that event the Extensions.w routines do indeed want to know this.
|
||||
|
||||
@e SEARCH_FAILED_STE from 1
|
||||
@e OPEN_FAILED_STE
|
||||
|
||||
=
|
||||
typedef struct source_text_error {
|
||||
int ste_code;
|
||||
struct inbuild_copy *copy;
|
||||
struct filename *file;
|
||||
struct text_file_position pos;
|
||||
struct text_stream *notes;
|
||||
MEMORY_MANAGEMENT
|
||||
} source_text_error;
|
||||
|
||||
source_text_error *SourceText::ste(int code, filename *F) {
|
||||
source_text_error *ste = CREATE(source_text_error);
|
||||
ste->ste_code = code;
|
||||
ste->file = F;
|
||||
ste->notes = NULL;
|
||||
ste->pos = TextFiles::nowhere();
|
||||
ste->copy = NULL;
|
||||
return ste;
|
||||
}
|
||||
|
||||
source_text_error *SourceText::ste_text(int code, text_stream *NB) {
|
||||
source_text_error *ste = CREATE(source_text_error);
|
||||
ste->ste_code = code;
|
||||
ste->file = NULL;
|
||||
ste->notes = Str::duplicate(NB);
|
||||
ste->pos = TextFiles::nowhere();
|
||||
ste->copy = NULL;
|
||||
return ste;
|
||||
}
|
||||
|
||||
source_file *SourceText::read_file(filename *F, text_stream *synopsis,
|
||||
int documentation_only, linked_list *errors, int primary) {
|
||||
general_pointer ref = STORE_POINTER_inbuild_copy(NULL);
|
||||
FILE *handle = Filenames::fopen(F, "r");
|
||||
if (handle == NULL) return NULL;
|
||||
text_stream *leaf = Filenames::get_leafname(F);
|
||||
if (primary) leaf = I"main source text";
|
||||
source_file *sf = TextFromFiles::feed_open_file_into_lexer(F, handle,
|
||||
leaf, documentation_only, ref);
|
||||
if (sf == NULL) {
|
||||
source_text_error *ste = SourceText::ste(OPEN_FAILED_STE, F);
|
||||
ADD_TO_LINKED_LIST(ste, source_text_error, errors);
|
||||
} else {
|
||||
fclose(handle);
|
||||
if (documentation_only == FALSE) @<Tell console output about the file@>;
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
||||
@ This is where messages like
|
||||
|
||||
|I've also read Standard Rules by Graham Nelson, which is 27204 words long.|
|
||||
|
||||
are printed to |stdout| (not |stderr|), in something of an affectionate nod
|
||||
to \TeX's traditional console output, though occasionally I think silence is
|
||||
golden and that the messages could go. It's a moot point for almost all users,
|
||||
though, because the console output is concealed from them by the Inform
|
||||
application.
|
||||
|
||||
@<Tell console output about the file@> =
|
||||
int wc;
|
||||
char *message;
|
||||
if (primary) message = "I've now read %S, which is %d words long.\n";
|
||||
else message = "I've also read %S, which is %d words long.\n";
|
||||
wc = TextFromFiles::total_word_count(sf);
|
||||
WRITE_TO(STDOUT, message, synopsis, wc);
|
||||
STREAM_FLUSH(STDOUT);
|
||||
LOG(message, synopsis, wc);
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
Include version 3/070628 of ExtNoVersion Extension by Araminta Intest.
|
||||
|
||||
Beachy Head is a room.
|
|
@ -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.]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1332,7 +1332,7 @@ int Tables::index_tables_in(OUTPUT_STREAM, extension_file *ef, int efc) {
|
|||
if (tc > 0) {
|
||||
if (ef) {
|
||||
HTML_OPEN("p");
|
||||
WRITE("<i>%+W</i>", ef->title_text);
|
||||
WRITE("<i>%S</i>", ef->found->edition->work->title);
|
||||
HTML_CLOSE("p");
|
||||
}
|
||||
HTML::begin_plain_html_table(OUT);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -964,7 +964,7 @@ void Rulebooks::index_page(OUTPUT_STREAM, int n) {
|
|||
@<Index the segment for new rulebooks and activities@>;
|
||||
extension_file *ef;
|
||||
LOOP_OVER(ef, extension_file)
|
||||
if (ef != standard_rules_extension)
|
||||
if (Extensions::Files::is_SR(ef) == FALSE)
|
||||
if (Rulebooks::noteworthy_rulebooks(ef) > 0)
|
||||
@<Index the segment for the rulebooks in this extension@>;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ the extension's name as a major subheading in our index.
|
|||
HTML_OPEN_WITH("p", "class=\"in1\"");
|
||||
WRITE("<b>Defined in the source</b>");
|
||||
HTML_CLOSE("p");
|
||||
} else if (this_extension != standard_rules_extension) {
|
||||
} else if (Extensions::Files::is_SR(this_extension) == FALSE) {
|
||||
if (pass == 2) HTML_TAG("hr");
|
||||
HTML_OPEN_WITH("p", "class=\"in1\"");
|
||||
WRITE("<b>From the extension ");
|
||||
|
@ -83,7 +83,7 @@ each has a paragraph of its own.
|
|||
wording HW = Sentences::Headings::get_text(this_heading);
|
||||
if (Wordings::nonempty(HW)) {
|
||||
if (pass == 1) @<Strip away bracketed matter in the heading name@>;
|
||||
if (this_extension == standard_rules_extension)
|
||||
if (Extensions::Files::is_SR(this_extension))
|
||||
@<Mark a faked division due to inter-hyphen clue in SR heading@>;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,12 +53,6 @@ void Routines::Compile::routine(phrase *ph,
|
|||
}
|
||||
|
||||
@<Compile some commentary about the routine to follow@> =
|
||||
heading *definition_area =
|
||||
Sentences::Headings::of_wording(ParseTree::get_text(ph->declaration_node));
|
||||
extension_file *definition_extension =
|
||||
Sentences::Headings::get_extension_containing(definition_area);
|
||||
if (definition_extension)
|
||||
Extensions::Files::write_I6_comment_describing(definition_extension);
|
||||
Routines::ToPhrases::comment_on_request(req);
|
||||
Phrases::Usage::write_I6_comment_describing(&(ph->usage_data));
|
||||
|
||||
|
|
|
@ -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(":");
|
||||
|
|
|
@ -43,8 +43,9 @@ void Modules::look_for_cu(parse_node *p) {
|
|||
compilation_module *Modules::new(parse_node *from) {
|
||||
extension_file *owner = NULL;
|
||||
source_location sl = Wordings::location(ParseTree::get_text(from));
|
||||
if (sl.file_of_origin == NULL) owner = standard_rules_extension;
|
||||
else owner = SourceFiles::get_extension_corresponding(
|
||||
if (sl.file_of_origin == NULL) internal_error("null foo");
|
||||
// if (sl.file_of_origin == NULL) owner = standard_rules_extension; else
|
||||
owner = SourceFiles::get_extension_corresponding(
|
||||
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(from))));
|
||||
|
||||
compilation_module *C = Packaging::new_cm();
|
||||
|
@ -67,7 +68,7 @@ compilation_module *Modules::new(parse_node *from) {
|
|||
DISCARD_TEXT(V);
|
||||
}
|
||||
|
||||
if (owner == standard_rules_extension) SR_module = C;
|
||||
if (Extensions::Files::is_SR(owner)) SR_module = C;
|
||||
if (owner == NULL) source_text_module = C;
|
||||
return C;
|
||||
}
|
||||
|
@ -76,7 +77,7 @@ compilation_module *Modules::new(parse_node *from) {
|
|||
compiled from the compilation module will go into a package of that name.
|
||||
|
||||
@<Compose a name for the module package this will lead to@> =
|
||||
if (owner == standard_rules_extension) WRITE_TO(pname, "standard_rules");
|
||||
if (Extensions::Files::is_SR(owner)) WRITE_TO(pname, "standard_rules");
|
||||
else if (owner == NULL) WRITE_TO(pname, "source_text");
|
||||
else {
|
||||
WRITE_TO(pname, "%X", Extensions::Files::get_work(owner));
|
||||
|
|
|
@ -12,35 +12,19 @@ does this), and some extensions, such as Basic Inform, need to be given
|
|||
inclusion sentences -- see Kits.
|
||||
|
||||
=
|
||||
source_file *primary_source_file = NULL; /* first to be opened */
|
||||
|
||||
@ There is no real difference between the loading of the primary source text
|
||||
and the loading of an extension's text, except for the descriptions we
|
||||
supply in case of any problem messages which might need to be issued,
|
||||
and for the fact that the mandatory insertion text is loaded before the
|
||||
primary source text.
|
||||
|
||||
=
|
||||
int SourceFiles::read_extension_source_text(extension_file *EF,
|
||||
text_stream *synopsis, int documentation_only) {
|
||||
int rv = SourceFiles::read_file(NULL, synopsis, EF, documentation_only);
|
||||
if (Log::aspect_switched_on(LEXICAL_OUTPUT_DA)) Word::log_lexer_output();
|
||||
return rv;
|
||||
void SourceFiles::read_primary_source_text(void) {
|
||||
inbuild_copy *C = Inbuild::project()->as_copy;
|
||||
Model::read_source_text_for(C);
|
||||
SourceFiles::issue_problems_arising(C);
|
||||
}
|
||||
|
||||
void SourceFiles::read_primary_source_text(void) {
|
||||
TEMPORARY_TEXT(early);
|
||||
Projects::early_source_text(early, Inbuild::project());
|
||||
if (Str::len(early) > 0) Feeds::feed_stream(early);
|
||||
DISCARD_TEXT(early);
|
||||
SourceFiles::read_further_mandatory_text();
|
||||
linked_list *L = Projects::source(Inbuild::project());
|
||||
if (L) {
|
||||
build_vertex *N;
|
||||
LOOP_OVER_LINKED_LIST(N, build_vertex, L) {
|
||||
filename *F = N->buildable_if_internal_file;
|
||||
if (TextFiles::exists(F) == FALSE) {
|
||||
Problems::quote_stream(1, Filenames::get_leafname(F));
|
||||
void SourceFiles::issue_problems_arising(inbuild_copy *C) {
|
||||
if (C == NULL) return;
|
||||
source_text_error *ste;
|
||||
LOOP_OVER_LINKED_LIST(ste, source_text_error, C->errors_reading_source_text) {
|
||||
switch (ste->ste_code) {
|
||||
case OPEN_FAILED_STE:
|
||||
Problems::quote_stream(1, Filenames::get_leafname(ste->file));
|
||||
Problems::Issue::handmade_problem(_p_(Untestable));
|
||||
Problems::issue_problem_segment(
|
||||
"I can't open the file '%1' of source text. %P"
|
||||
|
@ -48,9 +32,18 @@ void SourceFiles::read_primary_source_text(void) {
|
|||
"hold your source text, maybe your 'Contents.txt' has a "
|
||||
"typo in it?");
|
||||
Problems::issue_problem_end();
|
||||
} else {
|
||||
SourceFiles::read_file(F, N->annotation, NULL, FALSE);
|
||||
}
|
||||
break;
|
||||
case EXT_MISWORDED_STE:
|
||||
Problems::quote_work(1, ste->copy->found_by->work);
|
||||
Problems::quote_stream(2, ste->notes);
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtMiswordedBeginsHere));
|
||||
Problems::issue_problem_segment(
|
||||
"The extension %1, which your source text makes use of, seems to be "
|
||||
"damaged or incorrect: its identifying opening line is wrong. "
|
||||
"Specifically, %2.");
|
||||
Problems::issue_problem_end();
|
||||
break;
|
||||
default: internal_error("an unknown error occurred");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,124 +74,14 @@ int SourceFiles::increase_sentence_count(wording W) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
@ Either way, we use the following code. The |SourceFiles::read_file| function returns
|
||||
one of the following values to indicate the source of the source: the value
|
||||
only really tells us something we didn't know in the case of extensions,
|
||||
but in that event the Extensions.w routines do indeed want to know this.
|
||||
|
||||
=
|
||||
int SourceFiles::read_file(filename *F, text_stream *synopsis, extension_file *EF,
|
||||
int documentation_only) {
|
||||
source_file *sf = NULL;
|
||||
int area = -1;
|
||||
if (EF)
|
||||
area = SourceFiles::read_file_inner(F, synopsis,
|
||||
Inbuild::nest_list(), documentation_only, &sf,
|
||||
STORE_POINTER_extension_file(EF), FALSE, EF);
|
||||
else
|
||||
area = SourceFiles::read_file_inner(F, synopsis,
|
||||
NULL, documentation_only, &sf,
|
||||
STORE_POINTER_extension_file(NULL), TRUE, NULL);
|
||||
if (area == -1) {
|
||||
if (EF) {
|
||||
LOG("Author: %W\n", EF->author_text);
|
||||
LOG("Title: %W\n", EF->title_text);
|
||||
Problems::quote_source(1, current_sentence);
|
||||
Problems::quote_stream(2, synopsis);
|
||||
Problems::Issue::handmade_problem(_p_(PM_BogusExtension));
|
||||
Problems::issue_problem_segment(
|
||||
"I can't find the extension '%2', which seems not to be installed, "
|
||||
"but was requested by: %1. %P"
|
||||
"You can get hold of extensions which people have made public at "
|
||||
"the Inform website, www.inform7.com, or by using the Public "
|
||||
"Library in the Extensions panel.");
|
||||
Problems::issue_problem_end();
|
||||
} else {
|
||||
Problems::Fatal::filename_related(
|
||||
"Error: can't open source text file", F);
|
||||
}
|
||||
} else {
|
||||
if (EF == NULL) primary_source_file = sf;
|
||||
else Extensions::Files::set_corresponding_source_file(EF, sf);
|
||||
if (documentation_only == FALSE) @<Tell console output about the file@>;
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
@ This is where messages like
|
||||
|
||||
|I've also read Standard Rules by Graham Nelson, which is 27204 words long.|
|
||||
|
||||
are printed to |stdout| (not |stderr|), in something of an affectionate nod
|
||||
to \TeX's traditional console output, though occasionally I think silence is
|
||||
golden and that the messages could go. It's a moot point for almost all users,
|
||||
though, because the console output is concealed from them by the Inform
|
||||
application.
|
||||
|
||||
@<Tell console output about the file@> =
|
||||
int wc;
|
||||
char *message;
|
||||
if (EF == NULL) message = "I've now read %S, which is %d words long.\n";
|
||||
else message = "I've also read %S, which is %d words long.\n";
|
||||
wc = TextFromFiles::total_word_count(sf);
|
||||
WRITE_TO(STDOUT, message, synopsis, wc);
|
||||
STREAM_FLUSH(STDOUT);
|
||||
LOG(message, synopsis, wc);
|
||||
|
||||
@ =
|
||||
int SourceFiles::read_file_inner(filename *F, text_stream *synopsis,
|
||||
linked_list *search_list, int documentation_only, source_file **S,
|
||||
general_pointer ref, int primary, extension_file *EF) {
|
||||
int origin_tried = 1;
|
||||
|
||||
FILE *handle = NULL; filename *eventual = F;
|
||||
@<Set pathname and filename, and open file@>;
|
||||
if (handle == NULL) return -1;
|
||||
text_stream *leaf = Filenames::get_leafname(eventual);
|
||||
if (primary) leaf = I"main source text";
|
||||
source_file *sf = TextFromFiles::feed_open_file_into_lexer(eventual, handle,
|
||||
leaf, documentation_only, ref);
|
||||
fclose(handle);
|
||||
|
||||
if (S) *S = sf;
|
||||
return origin_tried;
|
||||
}
|
||||
|
||||
@ The primary source text must be found where we expect it, or a fatal
|
||||
error is issued. An extension, however, can be in one of two places: the
|
||||
user's own repository of installed extensions, or the built-in stock. We
|
||||
must try each possibility -- in that order, so that the user can supplant
|
||||
the built-in extensions by installing hacked versions of her own -- and in
|
||||
the event of failing, we issue only a standard Inform problem message and
|
||||
continue. While meaningful compilation is unlikely to succeed now, this is
|
||||
not a fatal error, because fatality would cause the user interface
|
||||
application to communicate the problem badly.
|
||||
|
||||
@<Set pathname and filename, and open file@> =
|
||||
handle = NULL;
|
||||
if (search_list) {
|
||||
text_stream *author_name = EF->ef_req->work->author_name;
|
||||
text_stream *title = EF->ef_req->work->title;
|
||||
inbuild_work *work = Works::new(extension_genre, title, author_name);
|
||||
inbuild_requirement *req = Requirements::any_version_of(work);
|
||||
req->allow_malformed = TRUE;
|
||||
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
||||
Nests::search_for(req, search_list, L);
|
||||
inbuild_search_result *search_result;
|
||||
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
|
||||
eventual = search_result->copy->location_if_file;
|
||||
handle = Filenames::fopen_caseless(eventual, "r");
|
||||
origin_tried = Nests::get_tag(search_result->nest);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
handle = Filenames::fopen(F, "r");
|
||||
}
|
||||
|
||||
@ =
|
||||
extension_file *SourceFiles::get_extension_corresponding(source_file *sf) {
|
||||
if (sf == NULL) return NULL;
|
||||
return RETRIEVE_POINTER_extension_file(sf->your_ref);
|
||||
inbuild_copy *C = RETRIEVE_POINTER_inbuild_copy(sf->your_ref);
|
||||
if (C == NULL) return NULL;
|
||||
if (C->edition->work->genre != extension_genre) return NULL;
|
||||
inform_extension *E = ExtensionManager::from_copy(C);
|
||||
if (E == NULL) return NULL;
|
||||
return E->ef;
|
||||
}
|
||||
|
||||
@ And the following converts lexer error conditions into I7 problem messages.
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -123,7 +123,8 @@ calls.
|
|||
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
|
||||
if (<unsuitable-name>(AW)) return 0;
|
||||
if (<unsuitable-name>(TW)) return 0;
|
||||
ef = Extensions::Inclusion::load(AW, TW, -1, EMPTY_WORDING);
|
||||
inbuild_requirement *req = Requirements::any_version_of(work);
|
||||
ef = Extensions::Inclusion::load(req);
|
||||
if (ef == NULL) return 0; /* shouldn't happen: it was there only moments ago */
|
||||
Extensions::Documentation::write_extension_documentation(NULL, ef);
|
||||
|
||||
|
@ -181,10 +182,10 @@ different template:
|
|||
WRITE(" ");
|
||||
|
||||
@<Write up any restrictions on VM usage@> =
|
||||
if (Wordings::nonempty(ef->VM_restriction_text)) {
|
||||
WRITE("%+W", ef->VM_restriction_text);
|
||||
if (Wordings::nonempty(Extensions::Files::VM_text(ef))) {
|
||||
WRITE("%+W", Extensions::Files::VM_text(ef));
|
||||
WRITE(" ");
|
||||
VirtualMachines::write_icons(OUT, ef->VM_restriction_text);
|
||||
VirtualMachines::write_icons(OUT, Extensions::Files::VM_text(ef));
|
||||
}
|
||||
|
||||
@<Write up the version number, if any, and location@> =
|
||||
|
|
|
@ -137,34 +137,25 @@ its purpose.
|
|||
=
|
||||
typedef struct extension_file {
|
||||
struct inbuild_requirement *ef_req; /* Work and version needed */
|
||||
struct wording author_text; /* Author's name */
|
||||
struct wording title_text; /* Extension name */
|
||||
struct wording VM_restriction_text; /* Restricting use to certain VMs */
|
||||
struct parse_node *inclusion_sentence; /* Where the source called for this */
|
||||
struct inbuild_copy *found;
|
||||
MEMORY_MANAGEMENT
|
||||
} extension_file;
|
||||
|
||||
extension_file *standard_rules_extension; /* the Standard Rules by Graham Nelson */
|
||||
|
||||
@ We begin with some housekeeping, really: the code required to create new
|
||||
extension file structures, and to manage existing ones.
|
||||
|
||||
=
|
||||
extension_file *Extensions::Files::new(wording AW, wording NW, wording VMW, int version_word) {
|
||||
TEMPORARY_TEXT(violation);
|
||||
int Extensions::Files::is_SR(extension_file *ef) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E == NULL) return FALSE;
|
||||
if (Extensions::is_standard(E)) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
extension_file *Extensions::Files::new(inbuild_requirement *req) {
|
||||
extension_file *ef = CREATE(extension_file);
|
||||
ef->author_text = AW;
|
||||
ef->title_text = NW;
|
||||
@<Create work for new extension file@>;
|
||||
ef->inclusion_sentence = current_sentence;
|
||||
ef->VM_restriction_text = VMW;
|
||||
ef->ef_req = req;
|
||||
ef->found = NULL;
|
||||
if (Str::len(violation) > 0) {
|
||||
LOG("So %S\n", violation);
|
||||
Problems::Issue::extension_problem_S(_p_(PM_IncludesTooLong), ef, violation); /* see below */
|
||||
}
|
||||
DISCARD_TEXT(violation);
|
||||
return ef;
|
||||
}
|
||||
|
||||
|
@ -174,42 +165,29 @@ inform_extension *Extensions::Files::find(extension_file *ef) {
|
|||
return ExtensionManager::from_copy(ef->found);
|
||||
}
|
||||
|
||||
@ We protect ourselves a little against absurdly long requested author or
|
||||
title names, and then produce problem messages in the event of only longish
|
||||
ones, unless the census is going on: in which case it's better to leave the
|
||||
matter to the census errors system elsewhere.
|
||||
parse_node *Extensions::Files::where_included(extension_file *ef) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E == NULL) return NULL;
|
||||
return E->inclusion_sentence;
|
||||
}
|
||||
|
||||
@<Create work for new extension file@> =
|
||||
TEMPORARY_TEXT(exft);
|
||||
TEMPORARY_TEXT(exfa);
|
||||
WRITE_TO(exft, "%+W", ef->title_text);
|
||||
WRITE_TO(exfa, "%+W", ef->author_text);
|
||||
if (Extensions::Census::currently_recording_errors() == FALSE) {
|
||||
if (Str::len(exfa) >= MAX_EXTENSION_AUTHOR_LENGTH) {
|
||||
WRITE_TO(violation,
|
||||
"has an author's name which is too long, exceeding the maximum "
|
||||
"allowed (%d characters) by %d",
|
||||
MAX_EXTENSION_AUTHOR_LENGTH-1,
|
||||
(int) (1+Str::len(exfa)-MAX_EXTENSION_AUTHOR_LENGTH));
|
||||
Str::truncate(exfa, MAX_EXTENSION_AUTHOR_LENGTH-1);
|
||||
}
|
||||
if (Str::len(exft) >= MAX_EXTENSION_AUTHOR_LENGTH) {
|
||||
WRITE_TO(violation,
|
||||
"has a title which is too long, exceeding the maximum allowed "
|
||||
"(%d characters) by %d",
|
||||
MAX_EXTENSION_TITLE_LENGTH-1,
|
||||
(int) (1+Str::len(exft)-MAX_EXTENSION_TITLE_LENGTH));
|
||||
Str::truncate(exft, MAX_EXTENSION_AUTHOR_LENGTH-1);
|
||||
}
|
||||
}
|
||||
inbuild_work *work = Works::new(extension_genre, exft, exfa);
|
||||
Works::add_to_database(work, LOADED_WDBC);
|
||||
inbuild_version_number min = VersionNumbers::null();
|
||||
if (version_word >= 0) min = Extensions::Inclusion::parse_version(version_word);
|
||||
ef->ef_req = Requirements::new(work, min, VersionNumbers::null());
|
||||
if (Works::is_standard_rules(work)) standard_rules_extension = ef;
|
||||
DISCARD_TEXT(exft);
|
||||
DISCARD_TEXT(exfa);
|
||||
void Extensions::Files::set_where_included(extension_file *ef, parse_node *N) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E == NULL) internal_error("unfound ef");
|
||||
Extensions::set_inclusion_sentence(E, N);
|
||||
}
|
||||
|
||||
wording Extensions::Files::VM_text(extension_file *ef) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E == NULL) return EMPTY_WORDING;
|
||||
return E->VM_restriction_text;
|
||||
}
|
||||
|
||||
void Extensions::Files::set_VM_text(extension_file *ef, wording W) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E == NULL) internal_error("unfound ef");
|
||||
Extensions::set_VM_text(E, W);
|
||||
}
|
||||
|
||||
@ Three pieces of information (not available when the EF is created) will
|
||||
be set later on, by other parts of Inform calling the routines below.
|
||||
|
@ -242,26 +220,6 @@ when the EF structure is created, we don't know that yet), we need to tally
|
|||
it up with the corresponding source file structure.
|
||||
|
||||
=
|
||||
void Extensions::Files::set_corresponding_source_file(extension_file *ef, source_file *sf) {
|
||||
TEMPORARY_TEXT(error_text);
|
||||
inbuild_copy *C = ExtensionManager::claim_file_as_copy(sf->name, error_text, TRUE);
|
||||
if (Str::len(error_text) > 0) {
|
||||
Problems::quote_extension(1, ef);
|
||||
Problems::quote_stream(2, error_text);
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtMiswordedBeginsHere));
|
||||
Problems::issue_problem_segment(
|
||||
"The extension %1, which your source text makes use of, seems to be "
|
||||
"damaged or incorrect: its identifying opening line is wrong. "
|
||||
"Specifically, %2.");
|
||||
Problems::issue_problem_end();
|
||||
} else {
|
||||
ef->found = C;
|
||||
inform_extension *E = ExtensionManager::from_copy(C);
|
||||
E->read_into_file = sf;
|
||||
}
|
||||
DISCARD_TEXT(error_text);
|
||||
}
|
||||
|
||||
source_file *Extensions::Files::source(extension_file *ef) {
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E) return E->read_into_file;
|
||||
|
@ -317,11 +275,11 @@ First, printing the name to an arbitrary UTF-8 file:
|
|||
|
||||
=
|
||||
void Extensions::Files::write_name_to_file(extension_file *ef, OUTPUT_STREAM) {
|
||||
WRITE("%+W", ef->title_text);
|
||||
WRITE("%+W", ef->found->edition->work->title);
|
||||
}
|
||||
|
||||
void Extensions::Files::write_author_to_file(extension_file *ef, OUTPUT_STREAM) {
|
||||
WRITE("%+W", ef->author_text);
|
||||
WRITE("%+W", ef->found->edition->work->author_name);
|
||||
}
|
||||
|
||||
@ Next, the debugging log:
|
||||
|
@ -329,29 +287,14 @@ void Extensions::Files::write_author_to_file(extension_file *ef, OUTPUT_STREAM)
|
|||
=
|
||||
void Extensions::Files::log(extension_file *ef) {
|
||||
if (ef == NULL) { LOG("<null-extension-file>"); return; }
|
||||
LOG("%W by %W", ef->title_text, ef->author_text);
|
||||
}
|
||||
|
||||
@ Next, printing the name in the form of a comment in an (ISO Latin-1)
|
||||
Inform 6 source file:
|
||||
|
||||
=
|
||||
void Extensions::Files::write_I6_comment_describing(extension_file *ef) {
|
||||
if (ef == standard_rules_extension) {
|
||||
Produce::comment(Emit::tree(), I"From the Standard Rules");
|
||||
} else {
|
||||
TEMPORARY_TEXT(C);
|
||||
WRITE_TO(C, "From \"%~W\" by %~W", ef->title_text, ef->author_text);
|
||||
Produce::comment(Emit::tree(), C);
|
||||
DISCARD_TEXT(C);
|
||||
}
|
||||
LOG("%X", ef->found);
|
||||
}
|
||||
|
||||
@ And finally printing the name to a C string:
|
||||
|
||||
=
|
||||
void Extensions::Files::write_full_title_to_stream(OUTPUT_STREAM, extension_file *ef) {
|
||||
WRITE("%+W by %+W", ef->title_text, ef->author_text);
|
||||
WRITE("%X", ef->found->edition->work);
|
||||
}
|
||||
|
||||
@h Checking version numbers.
|
||||
|
@ -371,20 +314,20 @@ void Extensions::Files::check_versions(void) {
|
|||
if (Requirements::meets(Extensions::Files::get_edition(ef), ef->ef_req) == FALSE) {
|
||||
inbuild_version_number have = Extensions::Files::get_version(ef);
|
||||
LOG("Need %v, have %v\n", &(ef->ef_req->min_version), &have);
|
||||
current_sentence = ef->inclusion_sentence;
|
||||
current_sentence = Extensions::Files::where_included(ef);
|
||||
Problems::quote_source(1, current_sentence);
|
||||
Problems::quote_extension(2, ef);
|
||||
if (VersionNumbers::is_null(have) == FALSE) {
|
||||
TEMPORARY_TEXT(vn);
|
||||
VersionNumbers::to_text(vn, have);
|
||||
Problems::quote_stream(3, vn);
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtVersionTooLow));
|
||||
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
|
||||
Problems::issue_problem_segment(
|
||||
"You wrote %1: but my copy of %2 is only version %3.");
|
||||
Problems::issue_problem_end();
|
||||
DISCARD_TEXT(vn);
|
||||
} else {
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtNoVersion));
|
||||
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
|
||||
Problems::issue_problem_segment(
|
||||
"You wrote %1: but my copy of %2 contains no version "
|
||||
"number, and is therefore considered to be earlier than "
|
||||
|
@ -421,7 +364,7 @@ void Extensions::Files::ShowExtensionVersions_routine(void) {
|
|||
extension_file *ef;
|
||||
LOOP_OVER(ef, extension_file) {
|
||||
TEMPORARY_TEXT(the_author_name);
|
||||
WRITE_TO(the_author_name, "%+W", ef->author_text);
|
||||
WRITE_TO(the_author_name, "%S", ef->found->edition->work->author_name);
|
||||
int self_penned = FALSE;
|
||||
#ifdef IF_MODULE
|
||||
if (PL::Bibliographic::story_author_is(the_author_name)) self_penned = TRUE;
|
||||
|
@ -509,9 +452,11 @@ void Extensions::Files::index(OUTPUT_STREAM) {
|
|||
Extensions::Files::index_extensions_from(OUT, NULL);
|
||||
extension_file *from;
|
||||
LOOP_OVER(from, extension_file)
|
||||
if (from != standard_rules_extension)
|
||||
if (Extensions::Files::is_SR(from) == FALSE)
|
||||
Extensions::Files::index_extensions_from(OUT, from);
|
||||
LOOP_OVER(from, extension_file)
|
||||
if (Extensions::Files::is_SR(from))
|
||||
Extensions::Files::index_extensions_from(OUT, from);
|
||||
Extensions::Files::index_extensions_from(OUT, standard_rules_extension);
|
||||
HTML_OPEN("p"); HTML_CLOSE("p");
|
||||
}
|
||||
|
||||
|
@ -520,22 +465,22 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
|
|||
extension_file *ef;
|
||||
LOOP_OVER(ef, extension_file) {
|
||||
extension_file *owner = NULL;
|
||||
if (ef == standard_rules_extension) owner = standard_rules_extension;
|
||||
else if (Wordings::nonempty(ParseTree::get_text(ef->inclusion_sentence))) {
|
||||
source_location sl = Wordings::location(ParseTree::get_text(ef->inclusion_sentence));
|
||||
if (sl.file_of_origin == NULL) owner = standard_rules_extension;
|
||||
parse_node *N = Extensions::Files::where_included(from);
|
||||
if (Wordings::nonempty(ParseTree::get_text(N))) {
|
||||
source_location sl = Wordings::location(ParseTree::get_text(N));
|
||||
if (sl.file_of_origin == NULL) owner = NULL;
|
||||
else owner = SourceFiles::get_extension_corresponding(
|
||||
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(ef->inclusion_sentence))));
|
||||
Lexer::file_of_origin(Wordings::first_wn(ParseTree::get_text(N))));
|
||||
}
|
||||
if (owner != from) continue;
|
||||
if (show_head) {
|
||||
HTMLFiles::open_para(OUT, 2, "hanging");
|
||||
HTML::begin_colour(OUT, I"808080");
|
||||
WRITE("Included ");
|
||||
if (from == standard_rules_extension) WRITE("automatically by Inform");
|
||||
if (Extensions::Files::is_SR(from)) WRITE("automatically by Inform");
|
||||
else if (from == NULL) WRITE("from the source text");
|
||||
else {
|
||||
WRITE("by the extension %+W", from->title_text);
|
||||
WRITE("by the extension %S", from->found->edition->work->title);
|
||||
}
|
||||
show_head = FALSE;
|
||||
HTML::end_colour(OUT);
|
||||
|
@ -544,12 +489,12 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
|
|||
HTML_OPEN_WITH("ul", "class=\"leaders\"");
|
||||
HTML_OPEN_WITH("li", "class=\"leaded indent2\"");
|
||||
HTML_OPEN("span");
|
||||
WRITE("%+W ", ef->title_text);
|
||||
WRITE("%S ", ef->found->edition->work->title);
|
||||
Works::begin_extension_link(OUT, ef->ef_req->work, NULL);
|
||||
HTML_TAG_WITH("img", "border=0 src=inform:/doc_images/help.png");
|
||||
Works::end_extension_link(OUT, ef->ef_req->work);
|
||||
if (ef != standard_rules_extension) { /* give author and inclusion links, but not for SR */
|
||||
WRITE(" by %+W", ef->author_text);
|
||||
if (Extensions::Files::is_SR(ef) == FALSE) { /* give author and inclusion links, but not for SR */
|
||||
WRITE(" by %X", ef->found->edition->work->author_name);
|
||||
}
|
||||
if (VersionNumbers::is_null(Extensions::Files::get_version(ef)) == FALSE) {
|
||||
WRITE(" ");
|
||||
|
@ -568,7 +513,7 @@ void Extensions::Files::index_extensions_from(OUTPUT_STREAM, extension_file *fro
|
|||
HTML_CLOSE("span");
|
||||
HTML_OPEN("span");
|
||||
WRITE("%d words", TextFromFiles::total_word_count(Extensions::Files::source(ef)));
|
||||
if (from == NULL) Index::link(OUT, Wordings::first_wn(ParseTree::get_text(ef->inclusion_sentence)));
|
||||
if (from == NULL) Index::link(OUT, Wordings::first_wn(ParseTree::get_text(Extensions::Files::where_included(ef))));
|
||||
HTML_CLOSE("span");
|
||||
HTML_CLOSE("li");
|
||||
HTML_CLOSE("ul");
|
||||
|
|
|
@ -125,10 +125,54 @@ parse tree.
|
|||
if (version_word >= 0)
|
||||
Extensions::Inclusion::parse_version(version_word); /* this checks the formatting of the version number */
|
||||
|
||||
extension_file *requested_extension =
|
||||
Extensions::Inclusion::load(AW, W, version_word, RW);
|
||||
TEMPORARY_TEXT(violation);
|
||||
TEMPORARY_TEXT(exft);
|
||||
TEMPORARY_TEXT(exfa);
|
||||
WRITE_TO(exft, "%+W", W);
|
||||
WRITE_TO(exfa, "%+W", AW);
|
||||
if (Extensions::Census::currently_recording_errors() == FALSE) {
|
||||
if (Str::len(exfa) >= MAX_EXTENSION_AUTHOR_LENGTH) {
|
||||
WRITE_TO(violation,
|
||||
"has an author's name which is too long, exceeding the maximum "
|
||||
"allowed (%d characters) by %d",
|
||||
MAX_EXTENSION_AUTHOR_LENGTH-1,
|
||||
(int) (1+Str::len(exfa)-MAX_EXTENSION_AUTHOR_LENGTH));
|
||||
Str::truncate(exfa, MAX_EXTENSION_AUTHOR_LENGTH-1);
|
||||
}
|
||||
if (Str::len(exft) >= MAX_EXTENSION_AUTHOR_LENGTH) {
|
||||
WRITE_TO(violation,
|
||||
"has a title which is too long, exceeding the maximum allowed "
|
||||
"(%d characters) by %d",
|
||||
MAX_EXTENSION_TITLE_LENGTH-1,
|
||||
(int) (1+Str::len(exft)-MAX_EXTENSION_TITLE_LENGTH));
|
||||
Str::truncate(exft, MAX_EXTENSION_AUTHOR_LENGTH-1);
|
||||
}
|
||||
}
|
||||
inbuild_work *work = Works::new(extension_genre, exft, exfa);
|
||||
Works::add_to_database(work, LOADED_WDBC);
|
||||
inbuild_version_number min = VersionNumbers::null();
|
||||
if (version_word >= 0) min = Extensions::Inclusion::parse_version(version_word);
|
||||
inbuild_requirement *req = Requirements::new(work, min, VersionNumbers::null());
|
||||
DISCARD_TEXT(exft);
|
||||
DISCARD_TEXT(exfa);
|
||||
if (Str::len(violation) > 0) {
|
||||
Problems::quote_wording(1, W);
|
||||
Problems::quote_wording(2, AW);
|
||||
Problems::quote_stream(3, violation);
|
||||
Problems::Issue::handmade_problem(_p_(PM_IncludesTooLong));
|
||||
Problems::issue_problem_segment(
|
||||
"The extension %1 by %2, which your source text requests, %3.");
|
||||
Problems::issue_problem_end();
|
||||
}
|
||||
DISCARD_TEXT(violation);
|
||||
|
||||
extension_file *requested_extension = Extensions::Inclusion::load(req);
|
||||
|
||||
inform_extension *E = Extensions::Files::find(requested_extension);
|
||||
if (E) {
|
||||
Extensions::set_inclusion_sentence(E, current_sentence);
|
||||
Extensions::set_VM_text(E, RW);
|
||||
}
|
||||
if ((E) && (E->body_text_unbroken)) {
|
||||
Sentences::break(E->body_text, requested_extension);
|
||||
E->body_text_unbroken = FALSE;
|
||||
|
@ -138,18 +182,18 @@ parse tree.
|
|||
Extensions are loaded here.
|
||||
|
||||
=
|
||||
extension_file *Extensions::Inclusion::load(wording A, wording T,
|
||||
int version_word, wording VMW) {
|
||||
extension_file *Extensions::Inclusion::load(inbuild_requirement *req) {
|
||||
NaturalLanguages::scan(); /* to avoid wording from those interleaving with extension wording */
|
||||
@<Do not load the same extension work twice@>;
|
||||
|
||||
extension_file *ef;
|
||||
LOOP_OVER(ef, extension_file)
|
||||
if ((Wordings::match(ef->author_text, A)) && (Wordings::match(ef->title_text, T)))
|
||||
@<This is an extension already loaded, so note any version number hike and return@>;
|
||||
inform_extension *E = NULL;
|
||||
@<Read the extension file into the lexer, and break it into body and documentation@>;
|
||||
|
||||
ef = Extensions::Files::new(A, T, VMW, version_word);
|
||||
if (problem_count == 0)
|
||||
@<Read the extension file into the lexer, and break it into body and documentation@>;
|
||||
extension_file *ef = Extensions::Files::new(req);
|
||||
if (E) {
|
||||
ef->found = E->as_copy;
|
||||
E->ef = ef;
|
||||
}
|
||||
return ef;
|
||||
}
|
||||
|
||||
|
@ -165,140 +209,103 @@ then we need to note that the version requirement on PS has been raised to 3.
|
|||
(This is why version numbers are not checked at load time: in general, we
|
||||
can't know at load time what we will ultimately require.)
|
||||
|
||||
@<This is an extension already loaded, so note any version number hike and return@> =
|
||||
if (version_word >= 0) {
|
||||
inbuild_version_number V = Extensions::Inclusion::parse_version(version_word);
|
||||
if (Requirements::ratchet_minimum(V, ef->ef_req))
|
||||
ef->inclusion_sentence = current_sentence;
|
||||
}
|
||||
return ef;
|
||||
@<Do not load the same extension work twice@> =
|
||||
extension_file *ef;
|
||||
LOOP_OVER(ef, extension_file)
|
||||
if (ef->found)
|
||||
if (Requirements::meets(ef->found->edition, req)) {
|
||||
inbuild_version_number V = req->min_version;
|
||||
if (VersionNumbers::is_null(V) == FALSE)
|
||||
if (Requirements::ratchet_minimum(V, ef->ef_req))
|
||||
Extensions::Files::set_where_included(ef, current_sentence);
|
||||
return ef;
|
||||
}
|
||||
|
||||
@ We finally make our call out of the Extensions section, down through the
|
||||
trap-door into Read Source Text, to seek and open the file.
|
||||
|
||||
@<Read the extension file into the lexer, and break it into body and documentation@> =
|
||||
TEMPORARY_TEXT(synopsis);
|
||||
@<Concoct a synopsis for the extension to be read@>;
|
||||
feed_t id = Feeds::begin();
|
||||
int origin = SourceFiles::read_extension_source_text(ef, synopsis, census_mode);
|
||||
inform_extension *E = Extensions::Files::find(ef);
|
||||
if (E) {
|
||||
int found_to_be_malformed = FALSE;
|
||||
req->allow_malformed = TRUE;
|
||||
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
||||
Nests::search_for(req, Inbuild::nest_list(), L);
|
||||
inbuild_search_result *search_result;
|
||||
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
|
||||
E = ExtensionManager::from_copy(search_result->copy);
|
||||
int origin = Nests::get_tag(search_result->nest);
|
||||
switch (origin) {
|
||||
case MATERIALS_NEST_TAG:
|
||||
case EXTERNAL_NEST_TAG:
|
||||
E->loaded_from_built_in_area = FALSE; break;
|
||||
case INTERNAL_NEST_TAG:
|
||||
E->loaded_from_built_in_area = TRUE; break;
|
||||
default: /* which can happen if the extension file cannot be found */
|
||||
E->loaded_from_built_in_area = FALSE; break;
|
||||
}
|
||||
wording EXW = Feeds::end(id);
|
||||
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
|
||||
if (LinkedLists::len(search_result->copy->errors_reading_source_text) > 0) {
|
||||
SourceFiles::issue_problems_arising(search_result->copy);
|
||||
E = NULL;
|
||||
found_to_be_malformed = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
DISCARD_TEXT(synopsis);
|
||||
|
||||
@ We concoct a textual synopsis in the form
|
||||
|
||||
|"Pantomime Sausages by Mr Punch"|
|
||||
|
||||
to be used by |SourceFiles::read_extension_source_text| for printing to |stdout|. Since
|
||||
we dare not assume |stdout| can manage characters outside the basic ASCII
|
||||
range, we flatten them from general ISO to plain ASCII.
|
||||
|
||||
@<Concoct a synopsis for the extension to be read@> =
|
||||
WRITE_TO(synopsis, "%+W by %+W", T, A);
|
||||
LOOP_THROUGH_TEXT(pos, synopsis)
|
||||
Str::put(pos,
|
||||
Characters::make_filename_safe(Str::get(pos)));
|
||||
|
||||
@ If an extension file contains the special text (outside literal mode) of
|
||||
|
||||
|---- Documentation ----|
|
||||
|
||||
then this is taken as the end of the Inform source, and the beginning of a
|
||||
snippet of documentation about the extension; text from that point on is
|
||||
saved until later, but not broken into sentences for the parse tree, and it
|
||||
is therefore invisible to the rest of Inform. If this division line is not
|
||||
present then the extension contains only body source and no documentation.
|
||||
|
||||
=
|
||||
<extension-body> ::=
|
||||
*** ---- documentation ---- ... | ==> TRUE
|
||||
... ==> FALSE
|
||||
|
||||
@<Break the extension's text into body and documentation@> =
|
||||
<extension-body>(EXW);
|
||||
E->body_text = GET_RW(<extension-body>, 1);
|
||||
if (<<r>>) E->documentation_text = GET_RW(<extension-body>, 2);
|
||||
E->body_text_unbroken = TRUE; /* mark this to be sentence-broken */
|
||||
|
||||
@h Parsing extension version numbers.
|
||||
Extensions can have versions in the form N/DDDDDD, a format which was chosen
|
||||
for sentimental reasons: IF enthusiasts know it well from the banner text of
|
||||
the Infocom titles of the 1980s. This story file, for instance, was compiled
|
||||
at the time of the Reykjavik summit between Presidents Gorbachev and Reagan:
|
||||
|
||||
|Moonmist|
|
||||
|Infocom interactive fiction - a mystery story|
|
||||
|Copyright (c) 1986 by Infocom, Inc. All rights reserved.|
|
||||
|Moonmist is a trademark of Infocom, Inc.|
|
||||
|Release number 9 / Serial number 861022|
|
||||
|
||||
Story file collectors customarily abbreviate this in catalogues to |9/861022|.
|
||||
|
||||
In our scheme, DDDDDD can be omitted (in which case so must the slash be).
|
||||
Spacing is not allowed around the slash (if present), so the version number
|
||||
always occupies a single lexical word.
|
||||
|
||||
The following routine parses the version number at word |vwn| to give an
|
||||
non-negative integer -- in fact it really just construes the whole thing,
|
||||
with the slash removed, as a 7-digit number -- in such a way that an earlier
|
||||
version always has a lower integer than a later one. It is legal for |vwn|
|
||||
to be $-1$, which means "no version number quoted", and evaluates as
|
||||
0 -- corresponding to |0/000000|, lower than the lowest version number it is
|
||||
legal to quote explicitly, which is |1|. (It follows that requiring no
|
||||
version in particular is equivalent to requiring |0/000000| or better, since
|
||||
every extension passes that test.)
|
||||
|
||||
In order that the numerical form of a version number should be a signed
|
||||
32-bit integer which does not overflow, we require that the release number
|
||||
|N| be at most 999. It could in fact rise to 2146 without incident, but
|
||||
it seems cleaner to constrain the number of digits than the value.
|
||||
|
||||
=
|
||||
inbuild_version_number Extensions::Inclusion::parse_version(int vwn) {
|
||||
int i, slashes = 0, digits = 0, slash_at = 0;
|
||||
wchar_t *p, *q;
|
||||
if (vwn == -1) return VersionNumbers::from_pair(0, 0); /* an unspecified version equates to |0/000000| */
|
||||
p = Lexer::word_text(vwn); q = p;
|
||||
for (i=0; p[i] != 0; i++)
|
||||
if (p[i] == '/') {
|
||||
slashes++; if ((i == 0) || (slashes > 1)) goto Malformed;
|
||||
slash_at = i; q = p+i+1;
|
||||
} else {
|
||||
if (!(Characters::isdigit(p[i]))) goto Malformed;
|
||||
digits++;
|
||||
if (found_to_be_malformed == FALSE) {
|
||||
if (E == NULL) @<Issue a cannot-find problem@>
|
||||
else {
|
||||
inbuild_copy *C = E->as_copy;
|
||||
Model::read_source_text_for(C);
|
||||
SourceFiles::issue_problems_arising(C);
|
||||
}
|
||||
if ((p[0] == '0') || (digits == 0)) goto Malformed;
|
||||
}
|
||||
|
||||
if ((slashes == 0) && (digits <= 3)) /* so that |p| points to 1 to 3 digits, not starting with |0| */
|
||||
return VersionNumbers::from_major(Wide::atoi(p));
|
||||
p[slash_at] = 0; /* temporarily replace the slash with a null, making |p| and |q| distinct C strings */
|
||||
if (Wide::len(p) > 3) goto Malformed; /* now |p| points to 1 to 3 digits, not starting with |0| */
|
||||
if (Wide::len(q) != 6) goto Malformed;
|
||||
while (*q == '0') q++; /* now |q| points to 0 to 6 digits, not starting with |0| */
|
||||
if (q[0] == 0) q--; /* if it was 0 digits, backspace to make it a single digit |0| */
|
||||
inbuild_version_number V = VersionNumbers::from_pair(Wide::atoi(p), Wide::atoi(q));
|
||||
p[slash_at] = '/'; /* put the slash back over the null byte temporarily dividing the string */
|
||||
@<Issue a cannot-find problem@> =
|
||||
inbuild_requirement *req2 = Requirements::any_version_of(req->work);
|
||||
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
||||
Nests::search_for(req2, Inbuild::nest_list(), L);
|
||||
if (LinkedLists::len(L) == 0) {
|
||||
LOG("Author: %W\n", req->work->author_name);
|
||||
LOG("Title: %W\n", req->work->title);
|
||||
Problems::quote_source(1, current_sentence);
|
||||
Problems::Issue::handmade_problem(_p_(PM_BogusExtension));
|
||||
Problems::issue_problem_segment(
|
||||
"I can't find the extension requested by: %1. %P"
|
||||
"You can get hold of extensions which people have made public at "
|
||||
"the Inform website, www.inform7.com, or by using the Public "
|
||||
"Library in the Extensions panel.");
|
||||
Problems::issue_problem_end();
|
||||
} else {
|
||||
TEMPORARY_TEXT(versions);
|
||||
inbuild_search_result *search_result;
|
||||
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
|
||||
if (Str::len(versions) > 0) WRITE_TO(versions, " or ");
|
||||
inbuild_version_number V = search_result->copy->edition->version;
|
||||
if (VersionNumbers::is_null(V)) WRITE_TO(versions, "an unnumbered version");
|
||||
else WRITE_TO(versions, "version %v", &V);
|
||||
}
|
||||
Problems::quote_source(1, current_sentence);
|
||||
Problems::quote_stream(2, versions);
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtVersionTooLow));
|
||||
Problems::issue_problem_segment(
|
||||
"I can't find the right version of the extension requested by %1 - "
|
||||
"I can only find %2. %P"
|
||||
"You can get hold of extensions which people have made public at "
|
||||
"the Inform website, www.inform7.com, or by using the Public "
|
||||
"Library in the Extensions panel.");
|
||||
Problems::issue_problem_end();
|
||||
DISCARD_TEXT(versions);
|
||||
}
|
||||
|
||||
@ =
|
||||
inbuild_version_number Extensions::Inclusion::parse_version(int vwn) {
|
||||
TEMPORARY_TEXT(vtext);
|
||||
WRITE_TO(vtext, "%N", vwn, vwn);
|
||||
inbuild_version_number V = VersionNumbers::from_text(vtext);
|
||||
if (VersionNumbers::is_null(V)) @<Issue a problem message for a malformed version number@>;
|
||||
return V;
|
||||
|
||||
Malformed: @<Issue a problem message for a malformed version number@>;
|
||||
}
|
||||
|
||||
@ Because we tend to call |Extensions::Inclusion::parse_version| repeatedly on the same word, we
|
||||
want to recover tidily from this problem, and not report it over and over.
|
||||
We do this by altering the text to |1|, the lowest well-formed version
|
||||
number text.
|
||||
@ Because we tend to call |Extensions::Inclusion::parse_version| repeatedly on
|
||||
the same word, we want to recover tidily from this problem, and not report it
|
||||
over and over. We do this by altering the text to |1|, the lowest well-formed
|
||||
version number text.
|
||||
|
||||
@<Issue a problem message for a malformed version number@> =
|
||||
LOG("Offending word number %d <%N>\n", vwn, vwn);
|
||||
|
@ -356,19 +363,10 @@ void Extensions::Inclusion::check_begins_here(parse_node *PN, extension_file *ef
|
|||
Problems::quote_wording(2, ParseTree::get_text(PN));
|
||||
|
||||
<begins-here-sentence-subject>(ParseTree::get_text(PN));
|
||||
wording W = Wordings::new(<<t1>>, <<t2>>);
|
||||
wording AW = Wordings::new(<<auth1>>, <<auth2>>);
|
||||
if (Wordings::empty(AW)) return;
|
||||
if (<<r>> < 0) Extensions::Files::set_version(ef, VersionNumbers::null());
|
||||
else Extensions::Files::set_version(ef, Extensions::Inclusion::parse_version(<<r>>));
|
||||
ef->VM_restriction_text = Wordings::new(<<rest1>>, <<rest2>>);
|
||||
Extensions::Files::set_VM_text(ef, Wordings::new(<<rest1>>, <<rest2>>));
|
||||
|
||||
if (Wordings::nonempty(ef->VM_restriction_text))
|
||||
if (Wordings::nonempty(Extensions::Files::VM_text(ef)))
|
||||
@<Check that the extension's stipulation about the virtual machine can be met@>;
|
||||
|
||||
if ((Wordings::match(ef->title_text, W) == FALSE) ||
|
||||
(Wordings::match(ef->author_text, AW) == FALSE))
|
||||
@<Issue a problem message pointing out that name and author do not agree with filename@>;
|
||||
}
|
||||
|
||||
@ On the other hand, we do already know what virtual machine we are compiling
|
||||
|
@ -376,38 +374,19 @@ for, so we can immediately object if the loaded extension cannot be used
|
|||
with our VM de jour.
|
||||
|
||||
@<Check that the extension's stipulation about the virtual machine can be met@> =
|
||||
if (<platform-qualifier>(ef->VM_restriction_text)) {
|
||||
if (<platform-qualifier>(Extensions::Files::VM_text(ef))) {
|
||||
if (<<r>> == PLATFORM_UNMET_HQ)
|
||||
@<Issue a problem message saying that the VM does not meet requirements@>;
|
||||
} else {
|
||||
@<Issue a problem message saying that the VM requirements are malformed@>;
|
||||
}
|
||||
|
||||
@ Suppose we wanted Onion Cookery by Delia Smith. We loaded the extension
|
||||
file called Onion Cookery in the Delia Smith folder of the (probably external)
|
||||
extensions area: but suppose that file turns out instead to be French Cuisine
|
||||
by Elizabeth David, according to its "begins here" sentence? Then the
|
||||
following problem message is produced. (In fact it's now hard to get this
|
||||
problem to arise, because inbuild searches for extensions much more carefully
|
||||
than Inform 7 used to.)
|
||||
|
||||
@<Issue a problem message pointing out that name and author do not agree with filename@> =
|
||||
Problems::quote_extension(1, ef);
|
||||
Problems::quote_wording(2, ParseTree::get_text(PN));
|
||||
Problems::Issue::handmade_problem(_p_(BelievedImpossible));
|
||||
Problems::issue_problem_segment(
|
||||
"The extension %1, which your source text makes use of, seems to be "
|
||||
"misidentified: its 'begins here' sentence declares it as '%2'. "
|
||||
"(Perhaps it was wrongly installed?)");
|
||||
Problems::issue_problem_end();
|
||||
return;
|
||||
|
||||
@ See Virtual Machines for the grammar of what can be given as a VM
|
||||
requirement.
|
||||
|
||||
@<Issue a problem message saying that the VM requirements are malformed@> =
|
||||
Problems::quote_extension(1, ef);
|
||||
Problems::quote_wording(2, ef->VM_restriction_text);
|
||||
Problems::quote_wording(2, Extensions::Files::VM_text(ef));
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtMalformedVM));
|
||||
Problems::issue_problem_segment(
|
||||
"Your source text makes use of the extension %1: but my copy "
|
||||
|
@ -422,15 +401,14 @@ matter of removing the inclusion, not of altering the extension, so we
|
|||
report this problem at the inclusion line.
|
||||
|
||||
@<Issue a problem message saying that the VM does not meet requirements@> =
|
||||
current_sentence = ef->inclusion_sentence;
|
||||
current_sentence = Extensions::Files::where_included(ef);
|
||||
Problems::quote_source(1, current_sentence);
|
||||
Problems::quote_wording(2, ef->title_text);
|
||||
Problems::quote_wording(3, ef->author_text);
|
||||
Problems::quote_wording(4, ef->VM_restriction_text);
|
||||
Problems::quote_copy(2, ef->found);
|
||||
Problems::quote_wording(3, Extensions::Files::VM_text(ef));
|
||||
Problems::Issue::handmade_problem(_p_(PM_ExtInadequateVM));
|
||||
Problems::issue_problem_segment(
|
||||
"You wrote %1: but my copy of %2 by %3 stipulates that it "
|
||||
"is '%4'. That means it can only be used with certain of "
|
||||
"You wrote %1: but my copy of %2 stipulates that it "
|
||||
"is '%3'. That means it can only be used with certain of "
|
||||
"the possible compiled story file formats, and at the "
|
||||
"moment, we don't fit the requirements. (You can change "
|
||||
"the format used for this project on the Settings panel.)");
|
||||
|
@ -445,7 +423,8 @@ that the extension isn't the one he thinks it is.
|
|||
=
|
||||
void Extensions::Inclusion::check_ends_here(parse_node *PN, extension_file *ef) {
|
||||
wording W = Articles::remove_the(ParseTree::get_text(PN));
|
||||
if ((problem_count == 0) && (Wordings::match(ef->title_text, W) == FALSE)) {
|
||||
wording T = Feeds::feed_stream(ef->found->edition->work->title);
|
||||
if ((problem_count == 0) && (Wordings::match(T, W) == FALSE)) {
|
||||
current_sentence = PN;
|
||||
Problems::quote_extension(1, ef);
|
||||
Problems::quote_wording(2, ParseTree::get_text(PN));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -1381,7 +1381,7 @@ int PL::Actions::index(OUTPUT_STREAM, action_name *an, int pass,
|
|||
HTML_TAG("br");
|
||||
f = FALSE;
|
||||
*new_par = TRUE;
|
||||
} else if (*ext != standard_rules_extension) {
|
||||
} else if (Extensions::Files::is_SR(*ext) == FALSE) {
|
||||
if (f) HTML_CLOSE("p");
|
||||
HTML_OPEN("p");
|
||||
WRITE("<b>Actions defined by the extension ");
|
||||
|
@ -1394,7 +1394,7 @@ int PL::Actions::index(OUTPUT_STREAM, action_name *an, int pass,
|
|||
*new_par = TRUE;
|
||||
}
|
||||
}
|
||||
if ((definition_area != *current_area) && (*ext == standard_rules_extension)) {
|
||||
if ((definition_area != *current_area) && (Extensions::Files::is_SR(*ext))) {
|
||||
if (f) HTML_CLOSE("p");
|
||||
HTML_OPEN("p");
|
||||
wording W = Sentences::Headings::get_text(definition_area);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in a new issue