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.
|
|
|
|
|
2019-03-19 01:36:20 +02:00
|
|
|
@ At this point in the narrative of a typical run of Inform, we have read in the
|
2019-02-05 02:44:07 +02:00
|
|
|
source text supplied by the user. The lexer automatically prefaced this with
|
|
|
|
"Include Standard Rules by Graham Nelson", and the sentence-breaker
|
|
|
|
converted all such sentences to nodes of type |INCLUDE_NT| which are
|
|
|
|
children of the parse tree root. (The eldest child, therefore, is the
|
|
|
|
Standard Rules inclusion.)
|
|
|
|
|
|
|
|
We now look through the parse tree in sentence order -- something we shall
|
|
|
|
do many times, and which we call a "traverse" -- and look for INCLUDE
|
|
|
|
nodes. Each is replaced with a mass of further nodes for the material in
|
|
|
|
whatever new extensions were required. This process is repeated until there
|
|
|
|
are no "Include" sentences 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.
|
|
|
|
|
|
|
|
At the end of this routine, provided no Problems have been issued, there are
|
|
|
|
guaranteed to be no INCLUDE nodes remaining in the parse tree.
|
|
|
|
|
|
|
|
=
|
2020-03-08 13:33:57 +02:00
|
|
|
inbuild_copy *inclusions_errors_to = NULL;
|
|
|
|
void Inclusions::traverse(inbuild_copy *C, parse_node_tree *T) {
|
|
|
|
inclusions_errors_to = C;
|
2019-02-05 02:44:07 +02:00
|
|
|
int includes_cleared;
|
|
|
|
do {
|
|
|
|
includes_cleared = TRUE;
|
2020-03-08 13:33:57 +02:00
|
|
|
if (problem_count > 0) break;
|
2020-03-09 14:44:59 +02:00
|
|
|
ParseTree::traverse_ppni(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) {
|
|
|
|
inform_extension *ext = ParseTree::get_inclusion_of_extension(H0);
|
|
|
|
if (ext) return ext->as_copy->vertex;
|
|
|
|
}
|
|
|
|
if (inclusions_errors_to == NULL) internal_error("no H0 ext or inclusion");
|
|
|
|
return inclusions_errors_to->vertex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Inclusions::visit(parse_node_tree *T, parse_node *pn, parse_node *last_H0, int *includes_cleared) {
|
2019-02-05 02:44:07 +02:00
|
|
|
if (ParseTree::get_type(pn) == INCLUDE_NT) {
|
|
|
|
@<Replace INCLUDE node with sentence nodes for any extensions required@>;
|
|
|
|
*includes_cleared = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ 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-03-09 02:15:27 +02:00
|
|
|
if (!(<structural-sentence>(ParseTree::get_text(pn))))
|
|
|
|
internal_error("malformed INCLUDE");
|
|
|
|
wording title = GET_RW(<structural-sentence>, 1);
|
|
|
|
wording author = GET_RW(<structural-sentence>, 2);
|
2020-03-09 14:44:59 +02:00
|
|
|
ParseTree::set_type(pn, INCLUSION_NT); pn->down = NULL;
|
|
|
|
int l = ParseTree::push_attachment_point(T, pn);
|
|
|
|
inform_extension *E = Inclusions::fulfill_request_to_include_extension(last_H0, title, author);
|
|
|
|
ParseTree::pop_attachment_point(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)
|
|
|
|
if (ParseTree::get_type(c) == HEADING_NT)
|
|
|
|
ParseTree::set_inclusion_of_extension(c, E);
|
|
|
|
if ((last_H0) && (ParseTree::int_annotation(last_H0, implied_heading_ANNOT) != TRUE)) {
|
|
|
|
build_vertex *V = Inclusions::spawned_from_vertex(last_H0);
|
|
|
|
build_vertex *EV = E->as_copy->vertex;
|
2020-03-26 21:22:26 +02:00
|
|
|
if (V->buildable_if_copy->edition->work->genre == extension_genre)
|
|
|
|
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
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
@
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
@e IncludeExtQuoted_SYNERROR
|
|
|
|
@e BogusExtension_SYNERROR
|
|
|
|
@e ExtVersionTooLow_SYNERROR
|
|
|
|
@e ExtVersionMalformed_SYNERROR
|
|
|
|
@e ExtInadequateVM_SYNERROR
|
|
|
|
@e ExtMisidentifiedEnds_SYNERROR
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-03-08 13:33:57 +02:00
|
|
|
@ Here we parse requests to include extensions.
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
=
|
|
|
|
<extension-title-and-version> ::=
|
|
|
|
version <extension-version> of <definite-article> <extension-unversioned> | ==> R[1]
|
|
|
|
version <extension-version> of <extension-unversioned> | ==> R[1]
|
|
|
|
<definite-article> <extension-unversioned> | ==> -1
|
|
|
|
<extension-unversioned> ==> -1
|
|
|
|
|
|
|
|
<extension-unversioned> ::=
|
2020-03-08 13:33:57 +02:00
|
|
|
<extension-unversioned-inner> ( ... ) | ==> 0
|
|
|
|
<extension-unversioned-inner> ==> 0
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
<extension-unversioned-inner> ::=
|
|
|
|
<quoted-text> *** | ==> @<Issue PM_IncludeExtQuoted problem@>
|
|
|
|
... ==> 0; <<t1>> = Wordings::first_wn(W); <<t2>> = Wordings::last_wn(W)
|
|
|
|
|
|
|
|
@ Quite a popular mistake, this:
|
|
|
|
|
|
|
|
@<Issue PM_IncludeExtQuoted problem@> =
|
|
|
|
<<t1>> = -1; <<t2>> = -1;
|
2020-03-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = IncludeExtQuoted_SYNERROR;
|
|
|
|
CE->details_node = current_sentence;
|
|
|
|
Copies::attach(inclusions_errors_to, CE);
|
2019-02-05 02:44:07 +02:00
|
|
|
|
|
|
|
@ This internal parses version text such as "12/110410".
|
|
|
|
|
|
|
|
=
|
|
|
|
<extension-version> internal 1 {
|
|
|
|
*X = Wordings::first_wn(W); /* actually, defer parsing by returning a word number here */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
@ =
|
2020-03-09 14:44:59 +02:00
|
|
|
inform_extension *Inclusions::fulfill_request_to_include_extension(parse_node *last_H0, wording TW, wording AW) {
|
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@> =
|
|
|
|
if (version_word >= 0)
|
2020-03-08 13:33:57 +02:00
|
|
|
Inclusions::parse_version(version_word); /* this checks the formatting of the version number */
|
2019-02-05 02:44:07 +02:00
|
|
|
|
2020-02-17 00:01:50 +02:00
|
|
|
TEMPORARY_TEXT(exft);
|
|
|
|
TEMPORARY_TEXT(exfa);
|
|
|
|
WRITE_TO(exft, "%+W", W);
|
|
|
|
WRITE_TO(exfa, "%+W", AW);
|
|
|
|
inbuild_work *work = Works::new(extension_genre, exft, exfa);
|
|
|
|
Works::add_to_database(work, LOADED_WDBC);
|
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-03-02 14:55:33 +02:00
|
|
|
semver_range *R = NULL;
|
2020-03-23 00:45:46 +02:00
|
|
|
if (VersionNumbers::is_null(V)) R = VersionNumberRanges::any_range();
|
|
|
|
else R = VersionNumberRanges::compatibility_range(V);
|
2020-03-02 14:55:33 +02:00
|
|
|
inbuild_requirement *req = Requirements::new(work, R);
|
2020-02-17 00:01:50 +02:00
|
|
|
DISCARD_TEXT(exft);
|
|
|
|
DISCARD_TEXT(exfa);
|
|
|
|
|
2020-03-09 14:44:59 +02:00
|
|
|
E = Inclusions::load(last_H0, current_sentence, req);
|
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
|
|
|
=
|
|
|
|
inform_extension *Inclusions::load(parse_node *last_H0, parse_node *at, inbuild_requirement *req) {
|
|
|
|
inform_extension *E = NULL;
|
2020-02-17 02:16:38 +02:00
|
|
|
LOOP_OVER(E, inform_extension)
|
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@>;
|
|
|
|
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-03-03 13:02:46 +02:00
|
|
|
inbuild_search_result *search_result = Nests::first_found(req, Inbuild::nest_list());
|
|
|
|
if (search_result) {
|
2020-02-17 00:01:50 +02:00
|
|
|
E = ExtensionManager::from_copy(search_result->copy);
|
2020-03-08 13:33:57 +02:00
|
|
|
Extensions::set_inclusion_sentence(E, at);
|
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;
|
|
|
|
if (Compatibility::with(C, Inbuild::current_vm()) == FALSE)
|
|
|
|
@<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) {
|
2020-03-03 13:02:46 +02:00
|
|
|
Copies::read_source_text_for(search_result->copy);
|
2020-02-17 00:01:50 +02:00
|
|
|
}
|
2020-03-08 13:33:57 +02:00
|
|
|
#ifndef CORE_MODULE
|
|
|
|
Copies::list_problems_arising(STDERR, search_result->copy);
|
|
|
|
#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@> =
|
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = ExtInadequateVM_SYNERROR;
|
|
|
|
CE->details_node = Extensions::get_inclusion_sentence(E);
|
|
|
|
CE->details = C->parsed_from;
|
|
|
|
Copies::attach(inclusions_errors_to, CE);
|
|
|
|
|
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);
|
|
|
|
Nests::search_for(req2, Inbuild::nest_list(), L);
|
|
|
|
if (LinkedLists::len(L) == 0) {
|
2020-03-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = BogusExtension_SYNERROR;
|
|
|
|
CE->details_node = current_sentence;
|
|
|
|
Copies::attach(inclusions_errors_to, CE);
|
2020-02-17 00:01:50 +02:00
|
|
|
} 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 ");
|
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-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = ExtVersionTooLow_SYNERROR;
|
|
|
|
CE->details_node = at;
|
|
|
|
CE->details = Str::duplicate(versions);
|
|
|
|
Copies::attach(inclusions_errors_to, CE);
|
2020-02-17 00:01:50 +02:00
|
|
|
DISCARD_TEXT(versions);
|
|
|
|
}
|
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-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = ExtVersionMalformed_SYNERROR;
|
|
|
|
CE->details_node = current_sentence;
|
|
|
|
Copies::attach(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.
|
|
|
|
|
|
|
|
It is sufficient to try parsing the version number in order to check it:
|
|
|
|
we throw away the answer, as we can't use it yet, but this will provoke
|
|
|
|
problem messages if it is malformed.
|
|
|
|
|
|
|
|
@ 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-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = ExtNoBeginsHere_SYNERROR;
|
|
|
|
CE->details_node = current_sentence;
|
|
|
|
Copies::attach(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;
|
2019-02-05 02:44:07 +02:00
|
|
|
<begins-here-sentence-subject>(ParseTree::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
|
|
|
|
the "begins here". We only check this if the problem count is still 0,
|
|
|
|
since we don't want to keep on nagging somebody who has already been told
|
|
|
|
that the extension isn't the one he thinks it is.
|
|
|
|
|
|
|
|
=
|
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;
|
|
|
|
wording W = ParseTree::get_text(PN);
|
|
|
|
if (<the-prefix-for-extensions>(W)) W = GET_RW(<the-prefix-for-extensions>, 1);
|
2020-02-17 02:16:38 +02:00
|
|
|
wording T = Feeds::feed_stream(E->as_copy->edition->work->title);
|
2020-02-17 00:01:50 +02:00
|
|
|
if ((problem_count == 0) && (Wordings::match(T, W) == FALSE)) {
|
2020-03-08 13:33:57 +02:00
|
|
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
|
|
|
CE->error_subcategory = ExtMisidentifiedEnds_SYNERROR;
|
|
|
|
CE->details_node = PN;
|
|
|
|
CE->details_W = ParseTree::get_text(PN);
|
|
|
|
Copies::attach(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
|
|
|
}
|