2020-03-08 13:33:57 +02:00
|
|
|
[Inclusions::] Inclusions.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
To fulfill requests to include extensions, adding their material
|
|
|
|
to the parse tree as needed, and removing INCLUDE nodes.
|
|
|
|
|
2020-05-07 18:44:07 +03:00
|
|
|
@ Our main task here is to look through the syntax tree for |INCLUDE_NT|
|
|
|
|
nodes, which are requests to include an extension, and replace them with
|
|
|
|
syntax trees for the extensions in question.
|
|
|
|
|
|
|
|
This process is repeated until there are no |INCLUDE_NT| nodes left.
|
|
|
|
In principle this could go on forever if A includes B which includes A, or
|
|
|
|
some such, but we log each extension read in to ensure that nothing is
|
|
|
|
read twice.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
2020-03-08 13:33:57 +02:00
|
|
|
inbuild_copy *inclusions_errors_to = NULL;
|
2020-05-05 23:59:02 +03:00
|
|
|
inform_project *inclusions_for_project = NULL;
|
2020-05-07 18:44:07 +03:00
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
void Inclusions::traverse(inbuild_copy *C, parse_node_tree *T) {
|
|
|
|
inclusions_errors_to = C;
|
2020-05-07 18:44:07 +03:00
|
|
|
int no_copy_errors = LinkedLists::len(C->errors_reading_source_text);
|
2022-12-08 01:28:26 +02:00
|
|
|
inform_project *project = Projects::from_copy(C);
|
2020-05-05 23:59:02 +03:00
|
|
|
inclusions_for_project = project;
|
2019-02-05 02:44:07 +02:00
|
|
|
int includes_cleared;
|
|
|
|
do {
|
|
|
|
includes_cleared = TRUE;
|
2020-05-07 18:44:07 +03:00
|
|
|
if (LinkedLists::len(C->errors_reading_source_text) > no_copy_errors) break;
|
2020-05-11 17:21:29 +03:00
|
|
|
SyntaxTree::traverse_headingwise(T, Inclusions::visit, &includes_cleared);
|
2019-02-05 02:44:07 +02:00
|
|
|
} while (includes_cleared == FALSE);
|
2020-03-08 13:33:57 +02:00
|
|
|
inclusions_errors_to = NULL;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
2020-03-09 14:44:59 +02:00
|
|
|
build_vertex *Inclusions::spawned_from_vertex(parse_node *H0) {
|
|
|
|
if (H0) {
|
2020-05-11 17:21:29 +03:00
|
|
|
inform_extension *ext = Node::get_inclusion_of_extension(H0);
|
2020-03-09 14:44:59 +02:00
|
|
|
if (ext) return ext->as_copy->vertex;
|
|
|
|
}
|
|
|
|
if (inclusions_errors_to == NULL) internal_error("no H0 ext or inclusion");
|
|
|
|
return inclusions_errors_to->vertex;
|
|
|
|
}
|
|
|
|
|
2022-11-01 00:59:25 +02:00
|
|
|
@
|
|
|
|
|
|
|
|
@e MissingSourceFile_SYNERROR
|
|
|
|
@e HeadingWithFileNonempty_SYNERROR
|
|
|
|
@e MisheadedSourceFile_SYNERROR
|
|
|
|
|
|
|
|
=
|
2020-05-07 18:44:07 +03:00
|
|
|
void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0,
|
|
|
|
int *includes_cleared) {
|
2020-05-11 17:21:29 +03:00
|
|
|
if (Node::get_type(pn) == INCLUDE_NT) {
|
2019-02-05 02:44:07 +02:00
|
|
|
@<Replace INCLUDE node with sentence nodes for any extensions required@>;
|
|
|
|
*includes_cleared = FALSE;
|
|
|
|
}
|
2022-11-01 00:59:25 +02:00
|
|
|
if (Node::get_type(pn) == HEADING_NT) {
|
|
|
|
heading *h = Node::get_embodying_heading(pn);
|
|
|
|
if ((h) && (Wordings::nonempty(h->external_file)) && (h->external_file_loaded == FALSE)) {
|
|
|
|
inform_project *proj = ProjectBundleManager::from_copy(inclusions_errors_to);
|
2022-11-02 01:56:24 +02:00
|
|
|
if ((proj) && (proj->as_copy->location_if_path))
|
|
|
|
@<Include the contents of the external file under the heading@>;
|
2022-11-01 00:59:25 +02:00
|
|
|
}
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ The INCLUDE node becomes an INCLUSION, which in turn contains the extension's code.
|
|
|
|
|
|
|
|
@<Replace INCLUDE node with sentence nodes for any extensions required@> =
|
2020-05-11 17:21:29 +03:00
|
|
|
if (!(<structural-sentence>(Node::get_text(pn))))
|
2020-03-09 02:15:27 +02:00
|
|
|
internal_error("malformed INCLUDE");
|
|
|
|
wording title = GET_RW(<structural-sentence>, 1);
|
|
|
|
wording author = GET_RW(<structural-sentence>, 2);
|
2020-05-11 17:21:29 +03:00
|
|
|
Node::set_type(pn, INCLUSION_NT); pn->down = NULL;
|
|
|
|
int l = SyntaxTree::push_bud(T, pn);
|
2020-05-05 23:59:02 +03:00
|
|
|
inform_extension *E = Inclusions::fulfill_request_to_include_extension(last_H0,
|
|
|
|
title, author, inclusions_for_project);
|
2020-05-11 17:21:29 +03:00
|
|
|
SyntaxTree::pop_bud(T, l);
|
2020-03-09 02:15:27 +02:00
|
|
|
if (E) {
|
2020-03-09 14:44:59 +02:00
|
|
|
for (parse_node *c = pn->down; c; c = c->next)
|
2020-05-11 17:21:29 +03:00
|
|
|
if (Node::get_type(c) == HEADING_NT)
|
|
|
|
Node::set_inclusion_of_extension(c, E);
|
2020-05-07 18:44:07 +03:00
|
|
|
if ((last_H0) &&
|
2020-05-11 17:21:29 +03:00
|
|
|
(Annotations::read_int(last_H0, implied_heading_ANNOT) != TRUE)) {
|
2020-03-09 14:44:59 +02:00
|
|
|
build_vertex *V = Inclusions::spawned_from_vertex(last_H0);
|
|
|
|
build_vertex *EV = E->as_copy->vertex;
|
2020-03-30 14:23:06 +03:00
|
|
|
if (V->as_copy->edition->work->genre == extension_genre)
|
2020-03-26 21:22:26 +02:00
|
|
|
Graphs::need_this_to_use(V, EV);
|
|
|
|
else
|
|
|
|
Graphs::need_this_to_build(V, EV);
|
2020-03-09 14:44:59 +02:00
|
|
|
}
|
2020-03-09 02:15:27 +02:00
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2022-11-02 01:56:24 +02:00
|
|
|
@ Here we're at a HEADING node which is annotated |(see "source-file")|, and
|
|
|
|
we need to read that file in: it will be considered as the contents of the
|
|
|
|
HEADING.
|
|
|
|
|
|
|
|
@<Include the contents of the external file under the heading@> =
|
|
|
|
pathname *P = Pathnames::down(Projects::materials_path(proj), I"Source");
|
|
|
|
text_stream *leaf = Str::new();
|
|
|
|
WRITE_TO(leaf, "%W", h->external_file);
|
|
|
|
h->external_file_loaded = TRUE;
|
|
|
|
if (pn->down) {
|
|
|
|
copy_error *CE = CopyErrors::new_T(SYNTAX_CE, HeadingWithFileNonempty_SYNERROR, leaf);
|
|
|
|
CopyErrors::supply_node(CE, pn);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
|
|
|
} else {
|
|
|
|
filename *F = Filenames::in(P, leaf);
|
|
|
|
if (TextFiles::exists(F) == FALSE) {
|
|
|
|
copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MissingSourceFile_SYNERROR, leaf);
|
|
|
|
CopyErrors::supply_node(CE, pn);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
|
|
|
} else {
|
|
|
|
int l = SyntaxTree::push_bud(T, pn);
|
2023-07-13 02:23:12 +03:00
|
|
|
source_file *S = SourceText::read_file(inclusions_errors_to, F, leaf, FALSE);
|
2022-11-02 01:56:24 +02:00
|
|
|
if (S) @<Sentence-break the contents of S under the heading node@>;
|
|
|
|
SyntaxTree::pop_bud(T, l);
|
|
|
|
*includes_cleared = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ Note that the opening words of the external file, here |TOPW|, must match
|
|
|
|
exactly the heading which called for the file, except for the annotation in
|
|
|
|
brackets. (It's to prevent that that we look for a round bracket at the
|
|
|
|
start of |RESTW| as a sign that the user has accidentally included this.)
|
|
|
|
|
|
|
|
Provided a match is made, the |TOPW| words are then thrown away. Their purpose
|
|
|
|
was just to identify the file sensibly.
|
|
|
|
|
|
|
|
@<Sentence-break the contents of S under the heading node@> =
|
|
|
|
h->external_file_read = S;
|
|
|
|
wording SW = S->text_read;
|
|
|
|
int N = Wordings::length(h->heading_text);
|
|
|
|
wording TOPW = Wordings::up_to(SW, Wordings::first_wn(SW) + N - 1);
|
|
|
|
wording RESTW = Wordings::from(SW, Wordings::first_wn(SW) + N);
|
|
|
|
if ((Wordings::match(h->heading_text, TOPW) == FALSE) ||
|
|
|
|
(compare_word(Wordings::first_wn(RESTW), OPENBRACKET_V))) {
|
|
|
|
copy_error *CE = CopyErrors::new_T(SYNTAX_CE, MisheadedSourceFile_SYNERROR, leaf);
|
|
|
|
CopyErrors::supply_node(CE, pn);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
|
|
|
} else if (Wordings::nonempty(RESTW)) {
|
|
|
|
Sentences::break_into_project_copy(T, RESTW, inclusions_errors_to, proj);
|
|
|
|
}
|
|
|
|
|
2020-05-07 18:44:07 +03:00
|
|
|
@ Here we parse the content of an Include sentence: i.e., what comes after the
|
|
|
|
word "Include", which might e.g. be "Locksmith by Emily Short".
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
<extension-title-and-version> ::=
|
2020-07-28 12:43:16 +03:00
|
|
|
version <extension-version> of <definite-article> <extension-unversioned> | ==> { pass 1 }
|
|
|
|
version <extension-version> of <extension-unversioned> | ==> { pass 1 }
|
|
|
|
<definite-article> <extension-unversioned> | ==> { -1, - }
|
|
|
|
<extension-unversioned> ==> { -1, - }
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
<extension-unversioned> ::=
|
2020-07-28 11:57:58 +03:00
|
|
|
<extension-unversioned-inner> ( ... ) | ==> { 0, - }
|
|
|
|
<extension-unversioned-inner> ==> { 0, - }
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
<extension-unversioned-inner> ::=
|
2020-05-07 18:44:07 +03:00
|
|
|
<quoted-text> *** | ==> @<Issue PM_IncludeExtQuoted problem@>
|
2020-07-28 17:49:03 +03:00
|
|
|
... ==> { 0, -, <<t1>> = Wordings::first_wn(W), <<t2>> = Wordings::last_wn(W) }
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@ Quite a popular mistake, this:
|
|
|
|
|
|
|
|
@<Issue PM_IncludeExtQuoted problem@> =
|
|
|
|
<<t1>> = -1; <<t2>> = -1;
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new(SYNTAX_CE, IncludeExtQuoted_SYNERROR);
|
|
|
|
CopyErrors::supply_node(CE, current_sentence);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-05-07 18:44:07 +03:00
|
|
|
@ This nonterminal parses text which will probably be something like "3.14" or
|
|
|
|
"12/110410", but at this stage it accepts anything: actual parsing comes later.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
<extension-version> internal 1 {
|
2020-07-28 21:19:38 +03:00
|
|
|
==> { Wordings::first_wn(W), - };
|
2019-02-05 02:44:07 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ =
|
2020-05-05 23:59:02 +03:00
|
|
|
inform_extension *Inclusions::fulfill_request_to_include_extension(parse_node *last_H0,
|
|
|
|
wording TW, wording AW, inform_project *for_project) {
|
2020-03-09 02:15:27 +02:00
|
|
|
inform_extension *E = NULL;
|
2019-02-05 02:44:07 +02:00
|
|
|
<<t1>> = -1; <<t2>> = -1;
|
2020-03-09 02:15:27 +02:00
|
|
|
<extension-title-and-version>(TW);
|
2019-02-05 02:44:07 +02:00
|
|
|
wording W = Wordings::new(<<t1>>, <<t2>>);
|
|
|
|
int version_word = <<r>>;
|
|
|
|
|
|
|
|
if (Wordings::nonempty(W)) @<Fulfill request to include a single extension@>;
|
2020-03-09 02:15:27 +02:00
|
|
|
return E;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ A request consists of author, name and version, the latter being optional.
|
|
|
|
We obtain the extension file structure corresponding to this: it may have
|
2019-03-19 01:36:20 +02:00
|
|
|
no text at all (for instance if Inform could not open the file), or it may be
|
2019-02-05 02:44:07 +02:00
|
|
|
one we have seen before, thanks to an earlier inclusion. Only when it
|
|
|
|
provided genuinely new text will its |body_text_unbroken| flag be set,
|
2020-03-05 14:42:33 +02:00
|
|
|
and then we call the sentence-breaker to graft the new material on to the
|
2019-02-05 02:44:07 +02:00
|
|
|
parse tree.
|
|
|
|
|
|
|
|
@<Fulfill request to include a single extension@> =
|
2020-06-28 01:18:54 +03:00
|
|
|
TEMPORARY_TEXT(exft)
|
|
|
|
TEMPORARY_TEXT(exfa)
|
2020-02-17 00:01:50 +02:00
|
|
|
WRITE_TO(exft, "%+W", W);
|
|
|
|
WRITE_TO(exfa, "%+W", AW);
|
|
|
|
inbuild_work *work = Works::new(extension_genre, exft, exfa);
|
2020-03-02 14:55:33 +02:00
|
|
|
semantic_version_number V = VersionNumbers::null();
|
2020-03-08 13:33:57 +02:00
|
|
|
if (version_word >= 0) V = Inclusions::parse_version(version_word);
|
2020-05-07 18:44:07 +03:00
|
|
|
semver_range *R = VersionNumberRanges::compatibility_range(V);
|
2020-03-02 14:55:33 +02:00
|
|
|
inbuild_requirement *req = Requirements::new(work, R);
|
2020-06-28 01:18:54 +03:00
|
|
|
DISCARD_TEXT(exft)
|
|
|
|
DISCARD_TEXT(exfa)
|
2020-02-17 00:01:50 +02:00
|
|
|
|
2020-05-05 23:59:02 +03:00
|
|
|
E = Inclusions::load(last_H0, current_sentence, req, for_project);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@h Extension loading.
|
2020-03-10 12:08:45 +02:00
|
|
|
Note that we ignore a request for an extension which has already been
|
2019-02-05 02:44:07 +02:00
|
|
|
loaded, except if the new request ups the ante in terms of the minimum
|
|
|
|
version permitted: in which case we need to record that the requirement has
|
|
|
|
been tightened. That is, if we previously wanted version 2 of Pantomime
|
|
|
|
Sausages by Mr Punch, and loaded it, but then read the sentence
|
|
|
|
|
|
|
|
>> Include version 3 of Pantomime Sausages by Mr Punch.
|
|
|
|
|
|
|
|
then we need to note that the version requirement on PS has been raised to 3.
|
|
|
|
|
2020-03-10 12:08:45 +02:00
|
|
|
=
|
2020-05-06 01:52:20 +03:00
|
|
|
inform_extension *Inclusions::load(parse_node *last_H0, parse_node *at,
|
|
|
|
inbuild_requirement *req, inform_project *for_project) {
|
2020-03-10 12:08:45 +02:00
|
|
|
inform_extension *E = NULL;
|
2023-07-23 17:28:37 +03:00
|
|
|
if (for_project)
|
|
|
|
LOOP_OVER_LINKED_LIST(E, inform_extension, for_project->extensions_included)
|
2020-03-10 12:08:45 +02:00
|
|
|
if ((Requirements::meets(E->as_copy->edition, req)) &&
|
|
|
|
(Copies::source_text_has_been_read(E->as_copy))) {
|
2020-02-17 02:16:38 +02:00
|
|
|
Extensions::must_satisfy(E, req);
|
|
|
|
return E;
|
|
|
|
}
|
2020-03-10 12:08:45 +02:00
|
|
|
@<Read the extension file into the lexer, and break it into body and documentation@>;
|
2022-12-11 01:50:28 +02:00
|
|
|
if ((for_project) && (E)) {
|
2020-05-07 18:44:07 +03:00
|
|
|
ADD_TO_LINKED_LIST(E, inform_extension, for_project->extensions_included);
|
2022-12-11 01:50:28 +02:00
|
|
|
}
|
2020-03-10 12:08:45 +02:00
|
|
|
return E;
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@<Read the extension file into the lexer, and break it into body and documentation@> =
|
2020-05-05 23:59:02 +03:00
|
|
|
inbuild_search_result *search_result =
|
|
|
|
Nests::search_for_best(req, Projects::nest_list(for_project));
|
2020-03-03 13:02:46 +02:00
|
|
|
if (search_result) {
|
2022-12-08 01:28:26 +02:00
|
|
|
E = Extensions::from_copy(search_result->copy);
|
2020-03-08 13:33:57 +02:00
|
|
|
Extensions::set_inclusion_sentence(E, at);
|
2020-05-05 23:59:02 +03:00
|
|
|
Extensions::set_associated_project(E, for_project);
|
2020-03-03 13:02:46 +02:00
|
|
|
if (Nests::get_tag(search_result->nest) == INTERNAL_NEST_TAG)
|
|
|
|
E->loaded_from_built_in_area = TRUE;
|
2020-03-08 13:33:57 +02:00
|
|
|
compatibility_specification *C = E->as_copy->edition->compatibility;
|
2020-05-10 01:49:59 +03:00
|
|
|
if (Compatibility::test(C, Supervisor::current_vm()) == FALSE)
|
2020-03-08 13:33:57 +02:00
|
|
|
@<Issue a problem message saying that the VM does not meet requirements@>;
|
|
|
|
|
2020-03-10 12:08:45 +02:00
|
|
|
if (LinkedLists::len(search_result->copy->errors_reading_source_text) == 0) {
|
2023-07-23 17:28:37 +03:00
|
|
|
Copies::get_source_text(search_result->copy, I"loading extension");
|
2020-02-17 00:01:50 +02:00
|
|
|
}
|
2020-03-08 13:33:57 +02:00
|
|
|
#ifndef CORE_MODULE
|
2020-03-29 19:39:17 +03:00
|
|
|
Copies::list_attached_errors(STDERR, search_result->copy);
|
2020-03-08 13:33:57 +02:00
|
|
|
#endif
|
2020-03-09 14:44:59 +02:00
|
|
|
} else {
|
|
|
|
#ifdef CORE_MODULE
|
|
|
|
@<Issue a cannot-find problem@>;
|
|
|
|
#endif
|
|
|
|
build_vertex *RV = Graphs::req_vertex(req);
|
|
|
|
build_vertex *V = Inclusions::spawned_from_vertex(last_H0);
|
|
|
|
Graphs::need_this_to_build(V, RV);
|
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
@ Here the problem is not that the extension is broken in some way: it's
|
|
|
|
just not what we can currently use. Therefore the correction should be a
|
|
|
|
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@> =
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new_T(SYNTAX_CE, ExtInadequateVM_SYNERROR, C->parsed_from);
|
|
|
|
CopyErrors::supply_node(CE, Extensions::get_inclusion_sentence(E));
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2020-03-08 13:33:57 +02:00
|
|
|
|
2020-02-17 00:01:50 +02:00
|
|
|
@<Issue a cannot-find problem@> =
|
|
|
|
inbuild_requirement *req2 = Requirements::any_version_of(req->work);
|
|
|
|
linked_list *L = NEW_LINKED_LIST(inbuild_search_result);
|
2020-05-05 23:59:02 +03:00
|
|
|
Nests::search_for(req2, Projects::nest_list(for_project), L);
|
2020-02-17 00:01:50 +02:00
|
|
|
if (LinkedLists::len(L) == 0) {
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new(SYNTAX_CE, BogusExtension_SYNERROR);
|
|
|
|
CopyErrors::supply_node(CE, current_sentence);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2020-02-17 00:01:50 +02:00
|
|
|
} else {
|
2020-06-28 01:18:54 +03:00
|
|
|
TEMPORARY_TEXT(versions)
|
2020-02-17 00:01:50 +02:00
|
|
|
inbuild_search_result *search_result;
|
|
|
|
LOOP_OVER_LINKED_LIST(search_result, inbuild_search_result, L) {
|
|
|
|
if (Str::len(versions) > 0) WRITE_TO(versions, " or ");
|
2020-02-19 22:48:30 +02:00
|
|
|
semantic_version_number V = search_result->copy->edition->version;
|
2020-02-17 00:01:50 +02:00
|
|
|
if (VersionNumbers::is_null(V)) WRITE_TO(versions, "an unnumbered version");
|
|
|
|
else WRITE_TO(versions, "version %v", &V);
|
|
|
|
}
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new_T(SYNTAX_CE, ExtVersionTooLow_SYNERROR, versions);
|
|
|
|
CopyErrors::supply_node(CE, at);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2020-06-28 01:18:54 +03:00
|
|
|
DISCARD_TEXT(versions)
|
2020-02-17 00:01:50 +02:00
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-02-17 00:01:50 +02:00
|
|
|
@ =
|
2020-02-20 02:39:36 +02:00
|
|
|
int last_PM_ExtVersionMalformed_at = -1;
|
2020-03-08 13:33:57 +02:00
|
|
|
semantic_version_number Inclusions::parse_version(int vwn) {
|
2020-02-20 02:39:36 +02:00
|
|
|
semantic_version_number V = VersionNumbers::null();
|
2020-02-19 22:48:30 +02:00
|
|
|
wording W = Wordings::one_word(vwn);
|
|
|
|
if (<version-number>(W)) {
|
|
|
|
semantic_version_number_holder *H = (semantic_version_number_holder *) <<rp>>;
|
|
|
|
V = H->version;
|
|
|
|
} else {
|
|
|
|
@<Issue a problem message for a malformed version number@>;
|
|
|
|
}
|
2020-02-02 13:25:56 +02:00
|
|
|
return V;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
@ Because we tend to call |Inclusions::parse_version| repeatedly on
|
2020-02-17 00:01:50 +02:00
|
|
|
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.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@<Issue a problem message for a malformed version number@> =
|
2020-02-20 02:39:36 +02:00
|
|
|
if (last_PM_ExtVersionMalformed_at != vwn) {
|
|
|
|
last_PM_ExtVersionMalformed_at = vwn;
|
|
|
|
LOG("Offending word number %d <%N>\n", vwn, vwn);
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new(SYNTAX_CE, ExtVersionMalformed_SYNERROR);
|
|
|
|
CopyErrors::supply_node(CE, current_sentence);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2020-02-20 02:39:36 +02:00
|
|
|
}
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@h Checking the begins here and ends here sentences.
|
|
|
|
When a newly loaded extension is being sentence-broken, problem messages
|
|
|
|
will be turned up unless it contains the matching pair of "begins here"
|
|
|
|
and "ends here" sentences. Assuming it does, the sentence breaker has no
|
|
|
|
objection, but it also calls the two routines below to verify that these
|
|
|
|
sentences have the correct format. (The point of this is to catch a malformed
|
|
|
|
extension at the earliest possible moment after loading it: it's easy to
|
|
|
|
mis-install extensions, especially if doing so by hand, and the resulting
|
|
|
|
problem messages could be quite inscrutable if one extension was wrongly
|
|
|
|
identified as another.)
|
|
|
|
|
|
|
|
First, we check the "begins here" sentence. We also identify where the
|
|
|
|
version number is given (if it is), and check that we are not trying to
|
|
|
|
use an extension which is marked as not working on the current VM.
|
|
|
|
|
|
|
|
@ This parses the subject noun-phrase in the sentence
|
|
|
|
|
|
|
|
>> Version 3 of Pantomime Sausages by Mr Punch begins here.
|
|
|
|
|
|
|
|
=
|
|
|
|
<begins-here-sentence-subject> ::=
|
2020-03-08 13:33:57 +02:00
|
|
|
<extension-title-and-version> by ... |
|
2020-02-08 12:34:58 +02:00
|
|
|
... ==> @<Issue problem@>
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-02-08 12:34:58 +02:00
|
|
|
@<Issue problem@> =
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new(SYNTAX_CE, ExtNoBeginsHere_SYNERROR);
|
|
|
|
CopyErrors::supply_node(CE, current_sentence);
|
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@ =
|
2020-03-08 13:33:57 +02:00
|
|
|
void Inclusions::check_begins_here(parse_node *PN, inform_extension *E) {
|
|
|
|
inbuild_copy *S = inclusions_errors_to;
|
|
|
|
inclusions_errors_to = E->as_copy;
|
2020-05-11 17:21:29 +03:00
|
|
|
<begins-here-sentence-subject>(Node::get_text(PN));
|
2020-03-08 13:33:57 +02:00
|
|
|
inclusions_errors_to = S;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@ Similarly, we check the "ends here" sentence. Here there are no
|
|
|
|
side-effects: we merely verify that the name matches the one quoted in
|
2020-05-07 18:44:07 +03:00
|
|
|
the "begins here".
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
2020-03-08 13:33:57 +02:00
|
|
|
<the-prefix-for-extensions> ::=
|
|
|
|
the ...
|
|
|
|
|
|
|
|
void Inclusions::check_ends_here(parse_node *PN, inform_extension *E) {
|
|
|
|
inbuild_copy *S = inclusions_errors_to;
|
|
|
|
inclusions_errors_to = E->as_copy;
|
2020-05-11 17:21:29 +03:00
|
|
|
wording W = Node::get_text(PN);
|
2020-03-08 13:33:57 +02:00
|
|
|
if (<the-prefix-for-extensions>(W)) W = GET_RW(<the-prefix-for-extensions>, 1);
|
2020-05-12 12:00:53 +03:00
|
|
|
wording T = Feeds::feed_text(E->as_copy->edition->work->title);
|
2020-05-07 18:44:07 +03:00
|
|
|
if (Wordings::match(T, W) == FALSE) {
|
2020-03-29 19:39:17 +03:00
|
|
|
copy_error *CE = CopyErrors::new(SYNTAX_CE, ExtMisidentifiedEnds_SYNERROR);
|
|
|
|
CopyErrors::supply_node(CE, PN);
|
2020-05-11 17:21:29 +03:00
|
|
|
CopyErrors::supply_wording(CE, Node::get_text(PN));
|
2020-03-29 19:39:17 +03:00
|
|
|
Copies::attach_error(inclusions_errors_to, CE);
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|
2020-03-08 13:33:57 +02:00
|
|
|
inclusions_errors_to = S;
|
2019-02-05 02:44:07 +02:00
|
|
|
}
|