mirror of
https://github.com/ganelson/inform.git
synced 2024-07-01 06:24:58 +03:00
Inbuild now reads bibliographic sentences
This commit is contained in:
parent
39a77b4bae
commit
c65c3ac931
|
@ -36,7 +36,6 @@ int main(int argc, char **argv) {
|
||||||
targets = NEW_LINKED_LIST(inbuild_copy);
|
targets = NEW_LINKED_LIST(inbuild_copy);
|
||||||
@<Read the command line@>;
|
@<Read the command line@>;
|
||||||
|
|
||||||
path_to_inbuild = Pathnames::installation_path("INBUILD_PATH", I"inbuild");
|
|
||||||
if (Str::len(unit_test) > 0) dry_run_mode = TRUE;
|
if (Str::len(unit_test) > 0) dry_run_mode = TRUE;
|
||||||
int use = SHELL_METHODOLOGY;
|
int use = SHELL_METHODOLOGY;
|
||||||
if (dry_run_mode) use = DRY_RUN_METHODOLOGY;
|
if (dry_run_mode) use = DRY_RUN_METHODOLOGY;
|
||||||
|
@ -132,6 +131,12 @@ int main(int argc, char **argv) {
|
||||||
Inbuild::add_nest(
|
Inbuild::add_nest(
|
||||||
Pathnames::from_text(I"inform7/Internal"), INTERNAL_NEST_TAG);
|
Pathnames::from_text(I"inform7/Internal"), INTERNAL_NEST_TAG);
|
||||||
|
|
||||||
|
path_to_inbuild = Pathnames::installation_path("INBUILD_PATH", I"inbuild");
|
||||||
|
pathname *P = Pathnames::subfolder(path_to_inbuild, I"Tangled");
|
||||||
|
filename *S = Filenames::in_folder(P, I"Syntax.preform");
|
||||||
|
wording W = Preform::load_from_file(S);
|
||||||
|
Preform::parse_preform(W, FALSE);
|
||||||
|
|
||||||
CommandLine::play_back_log();
|
CommandLine::play_back_log();
|
||||||
inbuild_copy *proj = NULL, *C;
|
inbuild_copy *proj = NULL, *C;
|
||||||
LOOP_OVER_LINKED_LIST(C, inbuild_copy, targets)
|
LOOP_OVER_LINKED_LIST(C, inbuild_copy, targets)
|
||||||
|
@ -225,6 +230,7 @@ vocabulary_meaning Main::ignore(vocabulary_entry *ve) {
|
||||||
@d PREFORM_LANGUAGE_TYPE void
|
@d PREFORM_LANGUAGE_TYPE void
|
||||||
@d PARSE_TREE_TRAVERSE_TYPE void
|
@d PARSE_TREE_TRAVERSE_TYPE void
|
||||||
@d SENTENCE_NODE Main::sentence_level
|
@d SENTENCE_NODE Main::sentence_level
|
||||||
|
@d PARSE_TREE_METADATA_SETUP SourceText::node_metadata
|
||||||
|
|
||||||
=
|
=
|
||||||
int Main::sentence_level(node_type_t t) {
|
int Main::sentence_level(node_type_t t) {
|
||||||
|
|
|
@ -206,7 +206,7 @@ inbuild_copy *Inbuild::optioneering_complete(inbuild_copy *C, int compile_only)
|
||||||
inbuild_phase = TINKERING_INBUILD_PHASE;
|
inbuild_phase = TINKERING_INBUILD_PHASE;
|
||||||
Inbuild::sort_nest_list();
|
Inbuild::sort_nest_list();
|
||||||
inbuild_phase = NESTED_INBUILD_PHASE;
|
inbuild_phase = NESTED_INBUILD_PHASE;
|
||||||
if (project) Projects::set_to_English(project);
|
if (project) Projects::read_source_text_for(project);
|
||||||
Inbuild::pass_kit_requests();
|
Inbuild::pass_kit_requests();
|
||||||
inbuild_phase = PROJECTED_INBUILD_PHASE;
|
inbuild_phase = PROJECTED_INBUILD_PHASE;
|
||||||
|
|
||||||
|
@ -486,7 +486,7 @@ inform_project *Inbuild::create_shared_project(inbuild_copy *C) {
|
||||||
|
|
||||||
=
|
=
|
||||||
inform_project *Inbuild::project(void) {
|
inform_project *Inbuild::project(void) {
|
||||||
RUN_ONLY_FROM_PHASE(PROJECTED_INBUILD_PHASE)
|
RUN_ONLY_FROM_PHASE(TINKERING_INBUILD_PHASE)
|
||||||
return shared_project;
|
return shared_project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ Setting up the use of this module.
|
||||||
@e extension_dictionary_entry_MT
|
@e extension_dictionary_entry_MT
|
||||||
@e known_extension_clash_MT
|
@e known_extension_clash_MT
|
||||||
@e build_skill_MT
|
@e build_skill_MT
|
||||||
|
@e control_structure_phrase_MT
|
||||||
|
|
||||||
=
|
=
|
||||||
ALLOCATE_INDIVIDUALLY(inform_kit)
|
ALLOCATE_INDIVIDUALLY(inform_kit)
|
||||||
|
@ -61,6 +62,7 @@ ALLOCATE_INDIVIDUALLY(copy_error)
|
||||||
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
|
ALLOCATE_INDIVIDUALLY(extension_dictionary_entry)
|
||||||
ALLOCATE_INDIVIDUALLY(known_extension_clash)
|
ALLOCATE_INDIVIDUALLY(known_extension_clash)
|
||||||
ALLOCATE_INDIVIDUALLY(build_skill)
|
ALLOCATE_INDIVIDUALLY(build_skill)
|
||||||
|
ALLOCATE_INDIVIDUALLY(control_structure_phrase)
|
||||||
|
|
||||||
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)
|
ALLOCATE_IN_ARRAYS(inbuild_work_database_entry, 100)
|
||||||
|
|
||||||
|
@ -83,6 +85,7 @@ void InbuildModule::start(void) {
|
||||||
Inform7Skill::create();
|
Inform7Skill::create();
|
||||||
Inform6Skill::create();
|
Inform6Skill::create();
|
||||||
InblorbSkill::create();
|
InblorbSkill::create();
|
||||||
|
ControlStructures::create_standard();
|
||||||
}
|
}
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
|
@ -157,6 +157,7 @@ typedef struct copy_error {
|
||||||
struct text_stream *details;
|
struct text_stream *details;
|
||||||
int details_N;
|
int details_N;
|
||||||
struct wording details_W;
|
struct wording details_W;
|
||||||
|
struct parse_node *details_node;
|
||||||
wchar_t *word;
|
wchar_t *word;
|
||||||
MEMORY_MANAGEMENT
|
MEMORY_MANAGEMENT
|
||||||
} copy_error;
|
} copy_error;
|
||||||
|
@ -170,6 +171,7 @@ copy_error *Copies::new_error(int cat, text_stream *NB) {
|
||||||
CE->details = NULL;
|
CE->details = NULL;
|
||||||
CE->details_N = -1;
|
CE->details_N = -1;
|
||||||
CE->details_W = EMPTY_WORDING;
|
CE->details_W = EMPTY_WORDING;
|
||||||
|
CE->details_node = NULL;
|
||||||
CE->pos = TextFiles::nowhere();
|
CE->pos = TextFiles::nowhere();
|
||||||
CE->copy = NULL;
|
CE->copy = NULL;
|
||||||
CE->word = NULL;
|
CE->word = NULL;
|
||||||
|
@ -245,6 +247,8 @@ void Copies::write_problem(OUTPUT_STREAM, copy_error *CE) {
|
||||||
WRITE("extension has an 'ends here' but no 'begins here'"); break;
|
WRITE("extension has an 'ends here' but no 'begins here'"); break;
|
||||||
case ExtMultipleEndsHere_SYNERROR:
|
case ExtMultipleEndsHere_SYNERROR:
|
||||||
WRITE("extension has multiple 'ends here' sentences"); break;
|
WRITE("extension has multiple 'ends here' sentences"); break;
|
||||||
|
case BadTitleSentence_SYNERROR:
|
||||||
|
WRITE("bibliographic sentence at the start is malformed"); break;
|
||||||
default:
|
default:
|
||||||
WRITE("syntax error"); break;
|
WRITE("syntax error"); break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,6 @@ calls.
|
||||||
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
|
Feeds::feed_text(L"This sentence provides a firebreak, no more. ");
|
||||||
E = Extensions::Documentation::load(work);
|
E = Extensions::Documentation::load(work);
|
||||||
if (E == NULL) return 0; /* shouldn't happen: it was there only moments ago */
|
if (E == NULL) return 0; /* shouldn't happen: it was there only moments ago */
|
||||||
WRITE_TO(STDOUT, "Wel well %X\n", work);
|
|
||||||
Copies::read_source_text_for(E->as_copy);
|
Copies::read_source_text_for(E->as_copy);
|
||||||
Extensions::Documentation::write_extension_documentation(NULL, E, census_mode);
|
Extensions::Documentation::write_extension_documentation(NULL, E, census_mode);
|
||||||
|
|
||||||
|
|
|
@ -309,7 +309,7 @@ void Extensions::read_source_text_for(inform_extension *E) {
|
||||||
E->read_into_file->your_ref = STORE_POINTER_inbuild_copy(E->as_copy);
|
E->read_into_file->your_ref = STORE_POINTER_inbuild_copy(E->as_copy);
|
||||||
wording EXW = E->read_into_file->text_read;
|
wording EXW = E->read_into_file->text_read;
|
||||||
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
|
if (Wordings::nonempty(EXW)) @<Break the extension's text into body and documentation@>;
|
||||||
Sentences::break(E->body_text, TRUE, E->as_copy);
|
Sentences::break(E->body_text, TRUE, E->as_copy, -1);
|
||||||
E->body_text_unbroken = FALSE;
|
E->body_text_unbroken = FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,12 +195,6 @@ void Kits::early_source_text(OUTPUT_STREAM, inform_kit *K) {
|
||||||
if (K->early_source) WRITE("%S\n\n", K->early_source);
|
if (K->early_source) WRITE("%S\n\n", K->early_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Kits::number_of_early_fed_sentences(inform_kit *K) {
|
|
||||||
int N = LinkedLists::len(K->extensions);
|
|
||||||
if (K->early_source) N++;
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
|
|
||||||
linked_list *Kits::inter_paths(void) {
|
linked_list *Kits::inter_paths(void) {
|
||||||
linked_list *inter_paths = NEW_LINKED_LIST(pathname);
|
linked_list *inter_paths = NEW_LINKED_LIST(pathname);
|
||||||
inbuild_nest *N;
|
inbuild_nest *N;
|
||||||
|
|
|
@ -234,14 +234,6 @@ void Projects::early_source_text(OUTPUT_STREAM, inform_project *project) {
|
||||||
Kits::early_source_text(OUT, K);
|
Kits::early_source_text(OUT, K);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Projects::number_of_early_fed_sentences(inform_project *project) {
|
|
||||||
int N = 0;
|
|
||||||
inform_kit *K;
|
|
||||||
LOOP_OVER_LINKED_LIST(K, inform_kit, project->kits_to_include)
|
|
||||||
N += Kits::number_of_early_fed_sentences(K);
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CODEGEN_MODULE
|
#ifdef CODEGEN_MODULE
|
||||||
linked_list *Projects::list_of_inter_libraries(inform_project *project) {
|
linked_list *Projects::list_of_inter_libraries(inform_project *project) {
|
||||||
linked_list *requirements_list = NEW_LINKED_LIST(link_instruction);
|
linked_list *requirements_list = NEW_LINKED_LIST(link_instruction);
|
||||||
|
@ -335,9 +327,11 @@ void Projects::construct_graph(inform_project *project) {
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
|
@e BadTitleSentence_SYNERROR
|
||||||
|
|
||||||
=
|
=
|
||||||
void Projects::read_source_text_for(inform_project *project) {
|
void Projects::read_source_text_for(inform_project *project) {
|
||||||
int wc = lexer_wordcount;
|
int wc = lexer_wordcount, bwc = -1;
|
||||||
TEMPORARY_TEXT(early);
|
TEMPORARY_TEXT(early);
|
||||||
Projects::early_source_text(early, project);
|
Projects::early_source_text(early, project);
|
||||||
if (Str::len(early) > 0) Feeds::feed_stream(early);
|
if (Str::len(early) > 0) Feeds::feed_stream(early);
|
||||||
|
@ -350,19 +344,99 @@ void Projects::read_source_text_for(inform_project *project) {
|
||||||
build_vertex *N;
|
build_vertex *N;
|
||||||
LOOP_OVER_LINKED_LIST(N, build_vertex, L) {
|
LOOP_OVER_LINKED_LIST(N, build_vertex, L) {
|
||||||
filename *F = N->buildable_if_internal_file;
|
filename *F = N->buildable_if_internal_file;
|
||||||
|
bwc = lexer_wordcount;
|
||||||
N->read_as = SourceText::read_file(project->as_copy, F, N->annotation,
|
N->read_as = SourceText::read_file(project->as_copy, F, N->annotation,
|
||||||
FALSE, TRUE);
|
FALSE, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParseTree::plant_parse_tree();
|
ParseTree::plant_parse_tree();
|
||||||
int l = ParseTree::push_attachment_point(tree_root);
|
int l = ParseTree::push_attachment_point(tree_root);
|
||||||
Sentences::break(Wordings::new(wc, lexer_wordcount-1), FALSE, project->as_copy);
|
Sentences::break(Wordings::new(wc, lexer_wordcount-1), FALSE, project->as_copy, bwc);
|
||||||
ParseTree::pop_attachment_point(l);
|
ParseTree::pop_attachment_point(l);
|
||||||
#ifdef CORE_MODULE
|
#ifdef CORE_MODULE
|
||||||
StructuralSentences::add_inventions_heading();
|
StructuralSentences::add_inventions_heading();
|
||||||
#endif
|
#endif
|
||||||
|
if (project->language_of_play == NULL) Projects::set_to_English(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ It might seem sensible to parse the opening sentence of the source text,
|
||||||
|
the bibliographic sentence giving title and author, by looking at the result
|
||||||
|
of sentence-breaking above. But this isn't fast enough, because the sentence
|
||||||
|
also specifies the language used, and we need to know of any non-Engkish
|
||||||
|
choice immediately. So a special hook in the |syntax| module calls the
|
||||||
|
following routine as soon as |BIBLIOGRAPHIC_NT| sentence is found; thus,
|
||||||
|
it happens during the call to |Sentences::break| above.
|
||||||
|
|
||||||
|
@ =
|
||||||
|
void Projects::notify_of_bibliographic_sentence(inform_project *project, parse_node *PN) {
|
||||||
|
wording W = ParseTree::get_text(PN);
|
||||||
|
if (<titling-line>(W)) {
|
||||||
|
text_stream *T = project->as_copy->edition->work->title;
|
||||||
|
if (project->as_copy->edition->work->author_name == NULL)
|
||||||
|
project->as_copy->edition->work->author_name = Str::new();
|
||||||
|
text_stream *A = project->as_copy->edition->work->author_name;
|
||||||
|
inform_language *L = <<rp>>;
|
||||||
|
if (L) {
|
||||||
|
Projects::set_language_of_play(project, L);
|
||||||
|
LOG("Language of play: %S\n", L->as_copy->edition->work->title);
|
||||||
|
}
|
||||||
|
@<Extract title and author name wording@>;
|
||||||
|
@<Dequote the title and, perhaps, author name@>;
|
||||||
|
} else {
|
||||||
|
copy_error *CE = Copies::new_error(SYNTAX_CE, NULL);
|
||||||
|
CE->error_subcategory = BadTitleSentence_SYNERROR;
|
||||||
|
CE->details_node = PN;
|
||||||
|
Copies::attach(project->as_copy, CE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ This is what the top line of the main source text should look like, if it's
|
||||||
|
to declare the title and author.
|
||||||
|
|
||||||
|
=
|
||||||
|
<titling-line> ::=
|
||||||
|
<plain-titling-line> ( in <natural-language> ) | ==> R[1]; *XP = RP[2];
|
||||||
|
<plain-titling-line> ==> R[1]; *XP = NULL;
|
||||||
|
|
||||||
|
<plain-titling-line> ::=
|
||||||
|
{<quoted-text-without-subs>} by ... | ==> TRUE
|
||||||
|
{<quoted-text-without-subs>} ==> FALSE
|
||||||
|
|
||||||
|
@<Extract title and author name wording@> =
|
||||||
|
wording TW = GET_RW(<plain-titling-line>, 1);
|
||||||
|
wording AW = EMPTY_WORDING;
|
||||||
|
if (<<r>>) AW = GET_RW(<plain-titling-line>, 2);
|
||||||
|
Str::clear(T);
|
||||||
|
WRITE_TO(T, "%+W", TW);
|
||||||
|
if (Wordings::nonempty(AW)) {
|
||||||
|
Str::clear(A);
|
||||||
|
WRITE_TO(A, "%+W", AW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ The author is sometimes given outside of quotation marks:
|
||||||
|
|
||||||
|
>> "The Large Scale Structure of Space-Time" by Lindsay Lohan
|
||||||
|
|
||||||
|
But not always:
|
||||||
|
|
||||||
|
>> "Greek Rural Postmen and Their Cancellation Numbers" by "will.i.am"
|
||||||
|
|
||||||
|
@<Dequote the title and, perhaps, author name@> =
|
||||||
|
Str::trim_white_space(T);
|
||||||
|
if ((Str::get_first_char(T) == '\"') && (Str::get_last_char(T) == '\"')) {
|
||||||
|
Str::delete_first_character(T);
|
||||||
|
Str::delete_last_character(T);
|
||||||
|
Str::trim_white_space(T);
|
||||||
|
}
|
||||||
|
LOG("Title: %S\n", T);
|
||||||
|
Str::trim_white_space(A);
|
||||||
|
if ((Str::get_first_char(A) == '\"') && (Str::get_last_char(A) == '\"')) {
|
||||||
|
Str::delete_first_character(A);
|
||||||
|
Str::delete_last_character(A);
|
||||||
|
Str::trim_white_space(A);
|
||||||
|
}
|
||||||
|
if (Str::len(A) > 0) LOG("Author: %S\n", A);
|
||||||
|
|
||||||
@ When Inform reads the (optional!) Options file, very early in its run, it
|
@ When Inform reads the (optional!) Options file, very early in its run, it
|
||||||
tries to obey any use options in the file right away -- earlier even than
|
tries to obey any use options in the file right away -- earlier even than
|
||||||
<structural-sentence>. It spots these, very crudely, as sentences which
|
<structural-sentence>. It spots these, very crudely, as sentences which
|
||||||
|
@ -373,6 +447,8 @@ stop -- it's needed before sentence-breaking has even taken place.
|
||||||
<use-option-sentence-shape> ::=
|
<use-option-sentence-shape> ::=
|
||||||
use ... .
|
use ... .
|
||||||
|
|
||||||
|
wording options_file_wording = EMPTY_WORDING_INIT;
|
||||||
|
|
||||||
void Projects::read_further_mandatory_text(filename *F) {
|
void Projects::read_further_mandatory_text(filename *F) {
|
||||||
feed_t id = Feeds::begin();
|
feed_t id = Feeds::begin();
|
||||||
TextFiles::read(F, TRUE,
|
TextFiles::read(F, TRUE,
|
||||||
|
|
224
inbuild/inbuild-module/Chapter 6/Control Structures.w
Normal file
224
inbuild/inbuild-module/Chapter 6/Control Structures.w
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
[ControlStructures::] Control Structures.
|
||||||
|
|
||||||
|
To specify the syntax of control structures such as repeat, if and otherwise.
|
||||||
|
|
||||||
|
@ Certain phrases are "structural": otherwise, if, repeat, while and so
|
||||||
|
on. These have different expectations in terms of the layout of surrounding
|
||||||
|
phrases in rule or phrase definitions, and the following structure defines
|
||||||
|
the relevant behaviour. (The contents are static.)
|
||||||
|
|
||||||
|
=
|
||||||
|
typedef struct control_structure_phrase {
|
||||||
|
struct control_structure_phrase *subordinate_to;
|
||||||
|
int indent_subblocks;
|
||||||
|
int body_empty_except_for_subordinates;
|
||||||
|
int used_at_stage;
|
||||||
|
int is_a_loop;
|
||||||
|
int requires_new_syntax;
|
||||||
|
int allow_run_on;
|
||||||
|
wchar_t *keyword;
|
||||||
|
MEMORY_MANAGEMENT
|
||||||
|
} control_structure_phrase;
|
||||||
|
|
||||||
|
@ The following set is built in to the Inform language; Basic Inform and such
|
||||||
|
extensions cannot extend it.
|
||||||
|
|
||||||
|
=
|
||||||
|
control_structure_phrase
|
||||||
|
*switch_CSP = NULL,
|
||||||
|
*if_CSP = NULL,
|
||||||
|
*repeat_CSP = NULL,
|
||||||
|
*while_CSP = NULL,
|
||||||
|
*otherwise_CSP = NULL,
|
||||||
|
*abbreviated_otherwise_CSP = NULL,
|
||||||
|
*otherwise_if_CSP = NULL,
|
||||||
|
*default_case_CSP = NULL,
|
||||||
|
*case_CSP = NULL,
|
||||||
|
*say_CSP = NULL,
|
||||||
|
*now_CSP = NULL,
|
||||||
|
*instead_CSP = NULL;
|
||||||
|
|
||||||
|
@ The following functions attempt to contain information about the
|
||||||
|
basic structural phrases in one place, so that if future loop constructs
|
||||||
|
are added, they can fairly simply be put here.
|
||||||
|
|
||||||
|
=
|
||||||
|
control_structure_phrase *ControlStructures::new(void) {
|
||||||
|
control_structure_phrase *csp = CREATE(control_structure_phrase);
|
||||||
|
csp->subordinate_to = NULL;
|
||||||
|
csp->indent_subblocks = FALSE;
|
||||||
|
csp->body_empty_except_for_subordinates = FALSE;
|
||||||
|
csp->used_at_stage = -1;
|
||||||
|
csp->requires_new_syntax = FALSE;
|
||||||
|
csp->allow_run_on = FALSE;
|
||||||
|
csp->keyword = L"<none>";
|
||||||
|
csp->is_a_loop = FALSE;
|
||||||
|
return csp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlStructures::create_standard(void) {
|
||||||
|
switch_CSP = ControlStructures::new();
|
||||||
|
switch_CSP->body_empty_except_for_subordinates = TRUE;
|
||||||
|
switch_CSP->indent_subblocks = TRUE;
|
||||||
|
switch_CSP->requires_new_syntax = TRUE;
|
||||||
|
switch_CSP->keyword = L"if";
|
||||||
|
|
||||||
|
if_CSP = ControlStructures::new();
|
||||||
|
if_CSP->keyword = L"if";
|
||||||
|
|
||||||
|
repeat_CSP = ControlStructures::new();
|
||||||
|
repeat_CSP->keyword = L"repeat";
|
||||||
|
repeat_CSP->is_a_loop = TRUE;
|
||||||
|
|
||||||
|
while_CSP = ControlStructures::new();
|
||||||
|
while_CSP->keyword = L"while";
|
||||||
|
while_CSP->is_a_loop = TRUE;
|
||||||
|
|
||||||
|
otherwise_CSP = ControlStructures::new();
|
||||||
|
otherwise_CSP->subordinate_to = if_CSP;
|
||||||
|
otherwise_CSP->used_at_stage = 1;
|
||||||
|
|
||||||
|
abbreviated_otherwise_CSP = ControlStructures::new();
|
||||||
|
abbreviated_otherwise_CSP->subordinate_to = if_CSP;
|
||||||
|
abbreviated_otherwise_CSP->used_at_stage = 1;
|
||||||
|
|
||||||
|
otherwise_if_CSP = ControlStructures::new();
|
||||||
|
otherwise_if_CSP->subordinate_to = if_CSP;
|
||||||
|
otherwise_if_CSP->used_at_stage = 0;
|
||||||
|
|
||||||
|
case_CSP = ControlStructures::new();
|
||||||
|
case_CSP->subordinate_to = switch_CSP;
|
||||||
|
case_CSP->used_at_stage = 1;
|
||||||
|
case_CSP->requires_new_syntax = TRUE;
|
||||||
|
case_CSP->allow_run_on = TRUE;
|
||||||
|
|
||||||
|
default_case_CSP = ControlStructures::new();
|
||||||
|
default_case_CSP->subordinate_to = switch_CSP;
|
||||||
|
default_case_CSP->used_at_stage = 2;
|
||||||
|
default_case_CSP->requires_new_syntax = TRUE;
|
||||||
|
default_case_CSP->allow_run_on = TRUE;
|
||||||
|
|
||||||
|
say_CSP = ControlStructures::new();
|
||||||
|
|
||||||
|
now_CSP = ControlStructures::new();
|
||||||
|
|
||||||
|
instead_CSP = ControlStructures::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlStructures::log(control_structure_phrase *csp) {
|
||||||
|
if (csp == if_CSP) LOG("IF");
|
||||||
|
if (csp == repeat_CSP) LOG("RPT");
|
||||||
|
if (csp == while_CSP) LOG("WHI");
|
||||||
|
if (csp == switch_CSP) LOG("SWI");
|
||||||
|
if (csp == otherwise_CSP) LOG("O");
|
||||||
|
if (csp == abbreviated_otherwise_CSP) LOG("AO");
|
||||||
|
if (csp == otherwise_if_CSP) LOG("OIF");
|
||||||
|
if (csp == case_CSP) LOG("CAS");
|
||||||
|
if (csp == default_case_CSP) LOG("DEF");
|
||||||
|
if (csp == say_CSP) LOG("SAY");
|
||||||
|
if (csp == now_CSP) LOG("NOW");
|
||||||
|
if (csp == instead_CSP) LOG("INS");
|
||||||
|
if (csp == NULL) LOG("---");
|
||||||
|
}
|
||||||
|
|
||||||
|
int ControlStructures::comma_possible(control_structure_phrase *csp) {
|
||||||
|
if ((csp == if_CSP) || (csp == switch_CSP) || (csp == otherwise_if_CSP))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ControlStructures::is_a_loop(control_structure_phrase *csp) {
|
||||||
|
if (csp) return csp->is_a_loop;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ControlStructures::opens_block(control_structure_phrase *csp) {
|
||||||
|
if ((csp) && (csp->subordinate_to == NULL) &&
|
||||||
|
(csp != say_CSP) && (csp != now_CSP) && (csp != instead_CSP)) return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ControlStructures::permits_break(control_structure_phrase *csp) {
|
||||||
|
if ((csp == repeat_CSP) || (csp == while_CSP)) return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *ControlStructures::incipit(control_structure_phrase *csp) {
|
||||||
|
if (csp) return csp->keyword;
|
||||||
|
return L"<none>";
|
||||||
|
}
|
||||||
|
|
||||||
|
control_structure_phrase *ControlStructures::detect(wording W) {
|
||||||
|
if (<control-structure-phrase>(W)) {
|
||||||
|
if (<<rp>> == abbreviated_otherwise_CSP) return NULL;
|
||||||
|
return <<rp>>;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ControlStructures::abbreviated_otherwise(wording W) {
|
||||||
|
if (<control-structure-phrase>(W)) {
|
||||||
|
if (<<rp>> == abbreviated_otherwise_CSP) return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
control_structure_phrase *ControlStructures::detect_end(wording W) {
|
||||||
|
if (<end-control-structure-phrase>(W)) return <<rp>>;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ Control structures such as "if" act as a sort of super-punctuation inside
|
||||||
|
rule and phrase definitions, and in particular they affect the actual
|
||||||
|
punctuation of the sentences there (consider the rules about colons versus
|
||||||
|
semicolons). So, though it's still early in Inform's run, we need to seek
|
||||||
|
them out.
|
||||||
|
|
||||||
|
Here we parse the text of a command phrase which, if any, of the control
|
||||||
|
structures it might be. Note that <s-command> has a grammar partially
|
||||||
|
overlapping with this, and they need to match.
|
||||||
|
|
||||||
|
@d NO_SIGF 0
|
||||||
|
@d SAY_SIGF 1
|
||||||
|
@d NOW_SIGF 2
|
||||||
|
|
||||||
|
=
|
||||||
|
<control-structure-phrase> ::=
|
||||||
|
if ... is begin | ==> 0; *XP = switch_CSP
|
||||||
|
if ... is | ==> 0; *XP = switch_CSP
|
||||||
|
if/unless ... | ==> 0; *XP = if_CSP
|
||||||
|
repeat ... | ==> 0; *XP = repeat_CSP
|
||||||
|
while ... | ==> 0; *XP = while_CSP
|
||||||
|
else/otherwise | ==> 0; *XP = otherwise_CSP
|
||||||
|
else/otherwise if/unless ... | ==> 0; *XP = otherwise_if_CSP
|
||||||
|
else/otherwise ... | ==> 0; *XP = abbreviated_otherwise_CSP
|
||||||
|
-- otherwise | ==> 0; *XP = default_case_CSP
|
||||||
|
-- ... ==> 0; *XP = case_CSP
|
||||||
|
|
||||||
|
<end-control-structure-phrase> ::=
|
||||||
|
end if/unless | ==> 0; *XP = if_CSP
|
||||||
|
end while | ==> 0; *XP = while_CSP
|
||||||
|
end repeat ==> 0; *XP = repeat_CSP
|
||||||
|
|
||||||
|
<other-significant-phrase> ::=
|
||||||
|
say ... | ==> SAY_SIGF
|
||||||
|
now ... ==> NOW_SIGF
|
||||||
|
|
||||||
|
@ This is used to see if an "if" is being used with the comma notation:
|
||||||
|
|
||||||
|
=
|
||||||
|
<phrase-with-comma-notation> ::=
|
||||||
|
...... , ......
|
||||||
|
|
||||||
|
@ This is used to see if an "if" is being used with the comma notation:
|
||||||
|
|
||||||
|
=
|
||||||
|
<instead-keyword> ::=
|
||||||
|
instead ... |
|
||||||
|
... instead
|
||||||
|
|
||||||
|
@ Finally, this is used to see if a control structure opens a block:
|
||||||
|
|
||||||
|
=
|
||||||
|
<phrase-beginning-block> ::=
|
||||||
|
... begin
|
|
@ -26,7 +26,9 @@ source_file *SourceText::read_file(inbuild_copy *C, filename *F, text_stream *sy
|
||||||
Copies::attach(C, Copies::new_error_on_file(OPEN_FAILED_CE, F));
|
Copies::attach(C, Copies::new_error_on_file(OPEN_FAILED_CE, F));
|
||||||
} else {
|
} else {
|
||||||
fclose(handle);
|
fclose(handle);
|
||||||
|
#ifdef CORE_MODULE
|
||||||
if (documentation_only == FALSE) @<Tell console output about the file@>;
|
if (documentation_only == FALSE) @<Tell console output about the file@>;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currently_lexing_into = NULL;
|
currently_lexing_into = NULL;
|
||||||
|
@ -55,17 +57,6 @@ application.
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
@d SENTENCE_COUNT_MONITOR SourceText::increase_sentence_count
|
|
||||||
|
|
||||||
=
|
|
||||||
wording options_file_wording = EMPTY_WORDING_INIT;
|
|
||||||
int SourceText::increase_sentence_count(wording W) {
|
|
||||||
if (Wordings::within(W, options_file_wording) == FALSE) return TRUE;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@
|
|
||||||
|
|
||||||
@d LEXER_PROBLEM_HANDLER SourceText::lexer_problem_handler
|
@d LEXER_PROBLEM_HANDLER SourceText::lexer_problem_handler
|
||||||
|
|
||||||
=
|
=
|
||||||
|
@ -200,6 +191,25 @@ known to most Inform users: it increases output to the debugging log.)
|
||||||
@e TABLE_NT /* "Table 1 - Counties of England" */
|
@e TABLE_NT /* "Table 1 - Counties of England" */
|
||||||
@e EQUATION_NT /* "Equation 2 - Newton's Second Law" */
|
@e EQUATION_NT /* "Equation 2 - Newton's Second Law" */
|
||||||
@e TRACE_NT /* A sentence consisting of an asterisk and optional quoted text */
|
@e TRACE_NT /* A sentence consisting of an asterisk and optional quoted text */
|
||||||
|
@e INVOCATION_LIST_NT /* Single invocation of a (possibly compound) phrase */
|
||||||
|
|
||||||
|
@d list_node_type ROUTINE_NT
|
||||||
|
@d list_entry_node_type INVOCATION_LIST_NT
|
||||||
|
|
||||||
|
@ =
|
||||||
|
void SourceText::node_metadata(void) {
|
||||||
|
ParseTree::md((parse_tree_node_type) { BIBLIOGRAPHIC_NT, "BIBLIOGRAPHIC_NT", 0, 0, L2_NCAT, 0 });
|
||||||
|
ParseTree::md((parse_tree_node_type) { ROUTINE_NT, "ROUTINE_NT", 0, INFTY, L2_NCAT, 0 });
|
||||||
|
ParseTree::md((parse_tree_node_type) { INFORM6CODE_NT, "INFORM6CODE_NT", 0, 0, L2_NCAT, 0 });
|
||||||
|
ParseTree::md((parse_tree_node_type) { TABLE_NT, "TABLE_NT", 0, 0, L2_NCAT, TABBED_CONTENT_NFLAG });
|
||||||
|
ParseTree::md((parse_tree_node_type) { EQUATION_NT, "EQUATION_NT", 0, 0, L2_NCAT, 0 });
|
||||||
|
ParseTree::md((parse_tree_node_type) { TRACE_NT, "TRACE_NT", 0, 0, L2_NCAT, 0 });
|
||||||
|
#ifndef CORE_MODULE
|
||||||
|
ParseTree::md((parse_tree_node_type) { INVOCATION_LIST_NT, "INVOCATION_LIST_NT", 0, INFTY, L2_NCAT, 0 });
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@
|
||||||
|
|
||||||
=
|
=
|
||||||
<structural-sentence> ::=
|
<structural-sentence> ::=
|
|
@ -36,8 +36,6 @@ Chapter 4: Managing Genres of Work
|
||||||
Pipeline Manager
|
Pipeline Manager
|
||||||
|
|
||||||
Chapter 5: Services for the Inform Compiler
|
Chapter 5: Services for the Inform Compiler
|
||||||
Source Text
|
|
||||||
Virtual Machine Grammar
|
|
||||||
Kit Services
|
Kit Services
|
||||||
Extension Services
|
Extension Services
|
||||||
Extension Dictionary
|
Extension Dictionary
|
||||||
|
@ -47,3 +45,8 @@ Chapter 5: Services for the Inform Compiler
|
||||||
Project Services
|
Project Services
|
||||||
Language Services
|
Language Services
|
||||||
Pipeline Services
|
Pipeline Services
|
||||||
|
|
||||||
|
Chapter 6: Handling Inform Source Text
|
||||||
|
Source Text
|
||||||
|
Control Structures
|
||||||
|
Virtual Machine Grammar
|
||||||
|
|
|
@ -53,7 +53,6 @@ We need to itemise the structures we'll want to allocate:
|
||||||
@e ph_stack_frame_box_MT
|
@e ph_stack_frame_box_MT
|
||||||
@e i6_inclusion_matter_MT
|
@e i6_inclusion_matter_MT
|
||||||
@e literal_list_MT
|
@e literal_list_MT
|
||||||
@e control_structure_phrase_MT
|
|
||||||
@e adjective_meaning_MT
|
@e adjective_meaning_MT
|
||||||
@e adjective_meaning_block_MT
|
@e adjective_meaning_block_MT
|
||||||
@e measurement_definition_MT
|
@e measurement_definition_MT
|
||||||
|
@ -109,7 +108,6 @@ ALLOCATE_INDIVIDUALLY(binary_predicate)
|
||||||
ALLOCATE_INDIVIDUALLY(booking)
|
ALLOCATE_INDIVIDUALLY(booking)
|
||||||
ALLOCATE_INDIVIDUALLY(constant_phrase)
|
ALLOCATE_INDIVIDUALLY(constant_phrase)
|
||||||
ALLOCATE_INDIVIDUALLY(contents_entry)
|
ALLOCATE_INDIVIDUALLY(contents_entry)
|
||||||
ALLOCATE_INDIVIDUALLY(control_structure_phrase)
|
|
||||||
ALLOCATE_INDIVIDUALLY(counting_data)
|
ALLOCATE_INDIVIDUALLY(counting_data)
|
||||||
ALLOCATE_INDIVIDUALLY(definition)
|
ALLOCATE_INDIVIDUALLY(definition)
|
||||||
ALLOCATE_INDIVIDUALLY(dval_written)
|
ALLOCATE_INDIVIDUALLY(dval_written)
|
||||||
|
|
|
@ -13,9 +13,8 @@ in order of when they work:
|
||||||
|
|
||||||
@e STARTED_CSEQ from 0
|
@e STARTED_CSEQ from 0
|
||||||
@e LEXICAL_CSEQ
|
@e LEXICAL_CSEQ
|
||||||
@e SEMANTIC_IA_CSEQ
|
|
||||||
@e SEMANTIC_LANGUAGE_CSEQ
|
@e SEMANTIC_LANGUAGE_CSEQ
|
||||||
@e SEMANTIC_IB_CSEQ
|
@e SEMANTIC_I_CSEQ
|
||||||
@e SEMANTIC_II_CSEQ
|
@e SEMANTIC_II_CSEQ
|
||||||
@e SEMANTIC_III_CSEQ
|
@e SEMANTIC_III_CSEQ
|
||||||
@e ASSERTIONS_PASS_1_CSEQ
|
@e ASSERTIONS_PASS_1_CSEQ
|
||||||
|
@ -47,8 +46,7 @@ int Sequence::carry_out(int debugging) {
|
||||||
@<Boot up the compiler@>;
|
@<Boot up the compiler@>;
|
||||||
compiler_booted_up = TRUE;
|
compiler_booted_up = TRUE;
|
||||||
}
|
}
|
||||||
@<Perform lexical analysis@>;
|
@<Perform textual analysis@>;
|
||||||
@<Perform semantic analysis@>;
|
|
||||||
@<Read the assertions in two passes@>;
|
@<Read the assertions in two passes@>;
|
||||||
@<Make the model world@>;
|
@<Make the model world@>;
|
||||||
@<Tables and grammar@>;
|
@<Tables and grammar@>;
|
||||||
|
@ -107,13 +105,9 @@ most of these worker functions are in the |core| module, some are not.
|
||||||
BENCH(InferenceSubjects::begin);
|
BENCH(InferenceSubjects::begin);
|
||||||
BENCH(Index::DocReferences::read_xrefs);
|
BENCH(Index::DocReferences::read_xrefs);
|
||||||
|
|
||||||
@<Perform lexical analysis@> =
|
@<Perform textual analysis@> =
|
||||||
Task::advance_stage_to(LEXICAL_CSEQ, I"Lexical analysis", 0);
|
Task::advance_stage_to(LEXICAL_CSEQ, I"Textual analysis", 0);
|
||||||
BENCH(Sentences::RuleSubtrees::create_standard_csps)
|
|
||||||
BENCH(Task::read_source_text)
|
BENCH(Task::read_source_text)
|
||||||
|
|
||||||
@<Perform semantic analysis@> =
|
|
||||||
Task::advance_stage_to(SEMANTIC_IA_CSEQ, I"Semantic analysis Ia", 1);
|
|
||||||
BENCH(Task::activate_language_elements)
|
BENCH(Task::activate_language_elements)
|
||||||
BENCH(Extensions::Inclusion::traverse)
|
BENCH(Extensions::Inclusion::traverse)
|
||||||
BENCH(Sentences::Headings::satisfy_dependencies)
|
BENCH(Sentences::Headings::satisfy_dependencies)
|
||||||
|
@ -124,7 +118,7 @@ most of these worker functions are in the |core| module, some are not.
|
||||||
BENCH(BinaryPredicates::make_built_in)
|
BENCH(BinaryPredicates::make_built_in)
|
||||||
BENCH(NewVerbs::add_inequalities)
|
BENCH(NewVerbs::add_inequalities)
|
||||||
|
|
||||||
Task::advance_stage_to(SEMANTIC_IB_CSEQ, I"Semantic analysis Ib", -1);
|
Task::advance_stage_to(SEMANTIC_I_CSEQ, I"Semantic analysis Ib", -1);
|
||||||
BENCH(Sentences::VPs::traverse)
|
BENCH(Sentences::VPs::traverse)
|
||||||
BENCH(Sentences::Rearrangement::tidy_up_ofs_and_froms)
|
BENCH(Sentences::Rearrangement::tidy_up_ofs_and_froms)
|
||||||
BENCH(Sentences::RuleSubtrees::register_recently_lexed_phrases)
|
BENCH(Sentences::RuleSubtrees::register_recently_lexed_phrases)
|
||||||
|
|
|
@ -113,6 +113,11 @@ target_vm *Task::vm(void) {
|
||||||
return inform7_task->task->for_vm;
|
return inform7_task->task->for_vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inbuild_edition *Task::edition(void) {
|
||||||
|
if (inform7_task == NULL) internal_error("there is no current task");
|
||||||
|
return inform7_task->project->as_copy->edition;
|
||||||
|
}
|
||||||
|
|
||||||
@ Resources in a Blorb file have unique ID numbers which are positive integers,
|
@ Resources in a Blorb file have unique ID numbers which are positive integers,
|
||||||
but these are not required to start from 1, nor to be contiguous. For Inform,
|
but these are not required to start from 1, nor to be contiguous. For Inform,
|
||||||
ID number 1 is reserved for the cover image (whether or not any cover image
|
ID number 1 is reserved for the cover image (whether or not any cover image
|
||||||
|
|
|
@ -260,6 +260,12 @@ void SourceProblems::issue_problems_arising(inbuild_copy *C) {
|
||||||
ExtensionManager::from_copy(C),
|
ExtensionManager::from_copy(C),
|
||||||
"has an 'ends here' with nothing having begun");
|
"has an 'ends here' with nothing having begun");
|
||||||
break;
|
break;
|
||||||
|
case BadTitleSentence_SYNERROR:
|
||||||
|
current_sentence = CE->details_node;
|
||||||
|
Problems::Issue::sentence_problem(_p_(PM_BadTitleSentence),
|
||||||
|
"the initial bibliographic sentence can only be a title in double-quotes",
|
||||||
|
"possibly followed with 'by' and the name of the author.");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
internal_error("unknown syntax error");
|
internal_error("unknown syntax error");
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,7 +396,7 @@ void Phrases::TypeData::Textual::parse(ph_type_data *phtd, wording XW, wording *
|
||||||
|
|
||||||
@<Does this comma presage phrase options?@> =
|
@<Does this comma presage phrase options?@> =
|
||||||
if ((<control-structure-phrase>(XW)) &&
|
if ((<control-structure-phrase>(XW)) &&
|
||||||
(Sentences::RuleSubtrees::comma_possible(<<rp>>)))
|
(ControlStructures::comma_possible(<<rp>>)))
|
||||||
comma_presages_options = FALSE;
|
comma_presages_options = FALSE;
|
||||||
|
|
||||||
@ If you find the explanation in this message unconvincing, you're not alone.
|
@ If you find the explanation in this message unconvincing, you're not alone.
|
||||||
|
|
|
@ -205,7 +205,7 @@ Are we in the body of a loop, perhaps indirectly?
|
||||||
int Frames::Blocks::inside_a_loop_body(void) {
|
int Frames::Blocks::inside_a_loop_body(void) {
|
||||||
int i;
|
int i;
|
||||||
for (i = current_block_stack.pb_sp-1; i >= 0; i--)
|
for (i = current_block_stack.pb_sp-1; i >= 0; i--)
|
||||||
if (Sentences::RuleSubtrees::is_a_loop(current_block_stack.pb_stack[i].from_structure))
|
if (ControlStructures::is_a_loop(current_block_stack.pb_stack[i].from_structure))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ int Frames::Blocks::current_block_level(void) {
|
||||||
|
|
||||||
wchar_t *Frames::Blocks::name_of_current_block(void) {
|
wchar_t *Frames::Blocks::name_of_current_block(void) {
|
||||||
if (block_being_compiled == NULL) return NULL;
|
if (block_being_compiled == NULL) return NULL;
|
||||||
return Sentences::RuleSubtrees::incipit(block_being_compiled->from_structure);
|
return ControlStructures::incipit(block_being_compiled->from_structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_node *Frames::Blocks::start_of_current_block(void) {
|
parse_node *Frames::Blocks::start_of_current_block(void) {
|
||||||
|
@ -248,7 +248,7 @@ So we do this by hand, jumping to a label placed just after the loop ends.
|
||||||
int unique_breakage_count = 0;
|
int unique_breakage_count = 0;
|
||||||
void Frames::Blocks::emit_break(void) {
|
void Frames::Blocks::emit_break(void) {
|
||||||
for (int i = current_block_stack.pb_sp-1; i >= 0; i--)
|
for (int i = current_block_stack.pb_sp-1; i >= 0; i--)
|
||||||
if (Sentences::RuleSubtrees::permits_break(current_block_stack.pb_stack[i].from_structure)) {
|
if (ControlStructures::permits_break(current_block_stack.pb_stack[i].from_structure)) {
|
||||||
if (current_block_stack.pb_stack[i].label_following == -1)
|
if (current_block_stack.pb_stack[i].label_following == -1)
|
||||||
current_block_stack.pb_stack[i].label_following =
|
current_block_stack.pb_stack[i].label_following =
|
||||||
unique_breakage_count++;
|
unique_breakage_count++;
|
||||||
|
|
|
@ -150,7 +150,7 @@ int Routines::Compile::code_block(int statement_count, parse_node *pn, int top_l
|
||||||
int Routines::Compile::code_line(int statement_count, parse_node *p) {
|
int Routines::Compile::code_line(int statement_count, parse_node *p) {
|
||||||
control_structure_phrase *csp = ParseTree::get_control_structure_used(p);
|
control_structure_phrase *csp = ParseTree::get_control_structure_used(p);
|
||||||
parse_node *to_compile = p;
|
parse_node *to_compile = p;
|
||||||
if (Sentences::RuleSubtrees::opens_block(csp)) {
|
if (ControlStructures::opens_block(csp)) {
|
||||||
Frames::Blocks::beginning_block_phrase(csp);
|
Frames::Blocks::beginning_block_phrase(csp);
|
||||||
to_compile = p->down;
|
to_compile = p->down;
|
||||||
}
|
}
|
||||||
|
@ -509,7 +509,7 @@ henceforth to be true, so we simply compile empty code in that case.
|
||||||
else if (csp == switch_CSP) @<Compile a switch tail@>
|
else if (csp == switch_CSP) @<Compile a switch tail@>
|
||||||
else if (csp == say_CSP) @<Compile a say tail@>
|
else if (csp == say_CSP) @<Compile a say tail@>
|
||||||
else if (csp == instead_CSP) @<Compile an instead tail@>
|
else if (csp == instead_CSP) @<Compile an instead tail@>
|
||||||
else if (Sentences::RuleSubtrees::opens_block(csp)) @<Compile a loop tail@>;
|
else if (ControlStructures::opens_block(csp)) @<Compile a loop tail@>;
|
||||||
|
|
||||||
@<Compile an if tail@> =
|
@<Compile an if tail@> =
|
||||||
;
|
;
|
||||||
|
|
|
@ -146,7 +146,6 @@ void ParseTreeUsage::copy_annotations(parse_node_annotation *to, parse_node_anno
|
||||||
@e TOKEN_NT /* Used for tokens in grammar */
|
@e TOKEN_NT /* Used for tokens in grammar */
|
||||||
|
|
||||||
@e CODE_BLOCK_NT /* Holds a block of source material */
|
@e CODE_BLOCK_NT /* Holds a block of source material */
|
||||||
@e INVOCATION_LIST_NT /* Single invocation of a (possibly compound) phrase */
|
|
||||||
@e INVOCATION_LIST_SAY_NT /* Single thing to be said */
|
@e INVOCATION_LIST_SAY_NT /* Single thing to be said */
|
||||||
@e INVOCATION_NT /* Usage of a phrase */
|
@e INVOCATION_NT /* Usage of a phrase */
|
||||||
@e VOID_CONTEXT_NT /* When a void phrase is required */
|
@e VOID_CONTEXT_NT /* When a void phrase is required */
|
||||||
|
@ -331,12 +330,7 @@ goes. The annotations used are identified by nonzero ID numbers, as follows:
|
||||||
=
|
=
|
||||||
void ParseTreeUsage::md(void) {
|
void ParseTreeUsage::md(void) {
|
||||||
/* first, the structural nodes: */
|
/* first, the structural nodes: */
|
||||||
ParseTree::md((parse_tree_node_type) { BIBLIOGRAPHIC_NT, "BIBLIOGRAPHIC_NT", 0, 0, L2_NCAT, 0 });
|
SourceText::node_metadata();
|
||||||
ParseTree::md((parse_tree_node_type) { ROUTINE_NT, "ROUTINE_NT", 0, INFTY, L2_NCAT, 0 });
|
|
||||||
ParseTree::md((parse_tree_node_type) { INFORM6CODE_NT, "INFORM6CODE_NT", 0, 0, L2_NCAT, 0 });
|
|
||||||
ParseTree::md((parse_tree_node_type) { TABLE_NT, "TABLE_NT", 0, 0, L2_NCAT, TABBED_CONTENT_NFLAG });
|
|
||||||
ParseTree::md((parse_tree_node_type) { EQUATION_NT, "EQUATION_NT", 0, 0, L2_NCAT, 0 });
|
|
||||||
ParseTree::md((parse_tree_node_type) { TRACE_NT, "TRACE_NT", 0, 0, L2_NCAT, 0 });
|
|
||||||
|
|
||||||
ParseTree::md((parse_tree_node_type) { ALLOWED_NT, "ALLOWED_NT", 1, 1, L3_NCAT, ASSERT_NFLAG });
|
ParseTree::md((parse_tree_node_type) { ALLOWED_NT, "ALLOWED_NT", 1, 1, L3_NCAT, ASSERT_NFLAG });
|
||||||
ParseTree::md((parse_tree_node_type) { EVERY_NT, "EVERY_NT", 0, INFTY, L3_NCAT, ASSERT_NFLAG });
|
ParseTree::md((parse_tree_node_type) { EVERY_NT, "EVERY_NT", 0, INFTY, L3_NCAT, ASSERT_NFLAG });
|
||||||
|
@ -697,7 +691,7 @@ void ParseTreeUsage::log_node(OUTPUT_STREAM, parse_node *pn) {
|
||||||
case INVOCATION_LIST_NT:
|
case INVOCATION_LIST_NT:
|
||||||
case CODE_BLOCK_NT: {
|
case CODE_BLOCK_NT: {
|
||||||
control_structure_phrase *csp = ParseTree::get_control_structure_used(pn);
|
control_structure_phrase *csp = ParseTree::get_control_structure_used(pn);
|
||||||
WRITE(" "); Sentences::RuleSubtrees::log_control_structure(csp); WRITE(" ");
|
WRITE(" "); ControlStructures::log(csp); WRITE(" ");
|
||||||
if (pn->node_type == INVOCATION_LIST_NT)
|
if (pn->node_type == INVOCATION_LIST_NT)
|
||||||
WRITE("%d", ParseTree::int_annotation(pn, indentation_level_ANNOT));
|
WRITE("%d", ParseTree::int_annotation(pn, indentation_level_ANNOT));
|
||||||
else WRITE(" ");
|
else WRITE(" ");
|
||||||
|
|
|
@ -11,41 +11,8 @@ simply listed after it in the parse tree, but we want them to become its
|
||||||
children: this is the only thing the $A$-grammar does with rules, which
|
children: this is the only thing the $A$-grammar does with rules, which
|
||||||
otherwise wait until later to be dealt with.
|
otherwise wait until later to be dealt with.
|
||||||
|
|
||||||
The single routine in this section accomplishes the regrouping: after it
|
The code in this section accomplishes the regrouping: after it runs, every
|
||||||
runs, every |INVOCATION_LIST_NT| is a child of the |ROUTINE_NT| header to which it
|
|INVOCATION_LIST_NT| is a child of the |ROUTINE_NT| header to which it belongs.
|
||||||
belongs.
|
|
||||||
|
|
||||||
@ Certain phrases are "structural": otherwise, if, repeat, while and so
|
|
||||||
on. These have different expectations in terms of the layout of surrounding
|
|
||||||
phrases in rule or phrase definitions, and the following structure defines
|
|
||||||
the relevant behaviour. (The contents are static.)
|
|
||||||
|
|
||||||
=
|
|
||||||
typedef struct control_structure_phrase {
|
|
||||||
struct control_structure_phrase *subordinate_to;
|
|
||||||
int indent_subblocks;
|
|
||||||
int body_empty_except_for_subordinates;
|
|
||||||
int used_at_stage;
|
|
||||||
int is_a_loop;
|
|
||||||
int requires_new_syntax;
|
|
||||||
int allow_run_on;
|
|
||||||
wchar_t *keyword;
|
|
||||||
MEMORY_MANAGEMENT
|
|
||||||
} control_structure_phrase;
|
|
||||||
|
|
||||||
control_structure_phrase
|
|
||||||
*switch_CSP = NULL,
|
|
||||||
*if_CSP = NULL,
|
|
||||||
*repeat_CSP = NULL,
|
|
||||||
*while_CSP = NULL,
|
|
||||||
*otherwise_CSP = NULL,
|
|
||||||
*abbreviated_otherwise_CSP = NULL,
|
|
||||||
*otherwise_if_CSP = NULL,
|
|
||||||
*default_case_CSP = NULL,
|
|
||||||
*case_CSP = NULL,
|
|
||||||
*say_CSP = NULL,
|
|
||||||
*now_CSP = NULL,
|
|
||||||
*instead_CSP = NULL;
|
|
||||||
|
|
||||||
@ This routine is used whenever new material is added. Whenever it finds a
|
@ This routine is used whenever new material is added. Whenever it finds a
|
||||||
childless |ROUTINE_NT| followed by a sequence of |INVOCATION_LIST_NT| nodes, it
|
childless |ROUTINE_NT| followed by a sequence of |INVOCATION_LIST_NT| nodes, it
|
||||||
|
@ -148,7 +115,7 @@ void Sentences::RuleSubtrees::parse_routine_structure(parse_node *routine_node)
|
||||||
parse_node *p;
|
parse_node *p;
|
||||||
for (p = routine_node->down; p; p = p->next) {
|
for (p = routine_node->down; p; p = p->next) {
|
||||||
control_structure_phrase *csp =
|
control_structure_phrase *csp =
|
||||||
Sentences::RuleSubtrees::detect_control_structure(ParseTree::get_text(p));
|
ControlStructures::detect(ParseTree::get_text(p));
|
||||||
if (csp) {
|
if (csp) {
|
||||||
int syntax_used = ParseTree::int_annotation(p, colon_block_command_ANNOT);
|
int syntax_used = ParseTree::int_annotation(p, colon_block_command_ANNOT);
|
||||||
if (syntax_used == FALSE) { /* i.e., doesn't end with a colon */
|
if (syntax_used == FALSE) { /* i.e., doesn't end with a colon */
|
||||||
|
@ -167,7 +134,7 @@ void Sentences::RuleSubtrees::parse_routine_structure(parse_node *routine_node)
|
||||||
if ((csp->requires_new_syntax) && (requires_colon_syntax == NULL))
|
if ((csp->requires_new_syntax) && (requires_colon_syntax == NULL))
|
||||||
requires_colon_syntax = p;
|
requires_colon_syntax = p;
|
||||||
}
|
}
|
||||||
if (Sentences::RuleSubtrees::detect_end_control_structure(ParseTree::get_text(p))) {
|
if (ControlStructures::detect_end(ParseTree::get_text(p))) {
|
||||||
if (uses_begin_end_syntax == NULL)
|
if (uses_begin_end_syntax == NULL)
|
||||||
uses_begin_end_syntax = p;
|
uses_begin_end_syntax = p;
|
||||||
}
|
}
|
||||||
|
@ -258,7 +225,7 @@ more certainly, and similarly for "end X" phrases.
|
||||||
@<(b.2) Annotate the parse tree with control structure usage@> =
|
@<(b.2) Annotate the parse tree with control structure usage@> =
|
||||||
for (parse_node *p = routine_node->down; p; p = p->next) {
|
for (parse_node *p = routine_node->down; p; p = p->next) {
|
||||||
control_structure_phrase *csp;
|
control_structure_phrase *csp;
|
||||||
csp = Sentences::RuleSubtrees::detect_control_structure(ParseTree::get_text(p));
|
csp = ControlStructures::detect(ParseTree::get_text(p));
|
||||||
if (csp) {
|
if (csp) {
|
||||||
if ((ParseTree::int_annotation(p, colon_block_command_ANNOT)) ||
|
if ((ParseTree::int_annotation(p, colon_block_command_ANNOT)) ||
|
||||||
(<phrase-beginning-block>(ParseTree::get_text(p))) ||
|
(<phrase-beginning-block>(ParseTree::get_text(p))) ||
|
||||||
|
@ -267,7 +234,7 @@ more certainly, and similarly for "end X" phrases.
|
||||||
if (csp == case_CSP) @<Trim a switch case to just the case value@>;
|
if (csp == case_CSP) @<Trim a switch case to just the case value@>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
csp = Sentences::RuleSubtrees::detect_end_control_structure(ParseTree::get_text(p));
|
csp = ControlStructures::detect_end(ParseTree::get_text(p));
|
||||||
if (csp) ParseTree::set_end_control_structure_used(p, csp);
|
if (csp) ParseTree::set_end_control_structure_used(p, csp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +256,7 @@ to break this up.
|
||||||
for (parse_node *p = routine_node->down; p; p = p->next)
|
for (parse_node *p = routine_node->down; p; p = p->next)
|
||||||
if (ParseTree::get_control_structure_used(p) == NULL) {
|
if (ParseTree::get_control_structure_used(p) == NULL) {
|
||||||
control_structure_phrase *csp;
|
control_structure_phrase *csp;
|
||||||
csp = Sentences::RuleSubtrees::detect_control_structure(ParseTree::get_text(p));
|
csp = ControlStructures::detect(ParseTree::get_text(p));
|
||||||
if ((csp == if_CSP) && (<phrase-with-comma-notation>(ParseTree::get_text(p))))
|
if ((csp == if_CSP) && (<phrase-with-comma-notation>(ParseTree::get_text(p))))
|
||||||
@<Effect a comma expansion@>;
|
@<Effect a comma expansion@>;
|
||||||
}
|
}
|
||||||
|
@ -331,7 +298,7 @@ to break this up.
|
||||||
ParseTree::int_annotation(rest_of_routine, indentation_level_ANNOT))) {
|
ParseTree::int_annotation(rest_of_routine, indentation_level_ANNOT))) {
|
||||||
if (ParseTree::get_control_structure_used(rest_of_routine) == otherwise_CSP)
|
if (ParseTree::get_control_structure_used(rest_of_routine) == otherwise_CSP)
|
||||||
@<Deal with an immediately following otherwise@>
|
@<Deal with an immediately following otherwise@>
|
||||||
else if (Sentences::RuleSubtrees::abbreviated_otherwise(ParseTree::get_text(rest_of_routine)))
|
else if (ControlStructures::abbreviated_otherwise(ParseTree::get_text(rest_of_routine)))
|
||||||
@<Deal with an abbreviated otherwise node@>;
|
@<Deal with an abbreviated otherwise node@>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,7 +711,7 @@ whichever syntax is used. We finally make a meaningful tree out of it.
|
||||||
csp = ParseTree::get_control_structure_used(pn);
|
csp = ParseTree::get_control_structure_used(pn);
|
||||||
if (csp) {
|
if (csp) {
|
||||||
go_down = TRUE;
|
go_down = TRUE;
|
||||||
if (Sentences::RuleSubtrees::opens_block(csp) == FALSE) {
|
if (ControlStructures::opens_block(csp) == FALSE) {
|
||||||
go_up = TRUE;
|
go_up = TRUE;
|
||||||
ParseTree::set_type(pn, CODE_BLOCK_NT);
|
ParseTree::set_type(pn, CODE_BLOCK_NT);
|
||||||
}
|
}
|
||||||
|
@ -818,7 +785,7 @@ void Sentences::RuleSubtrees::police_code_block(parse_node *block, control_struc
|
||||||
|
|
||||||
csp = ParseTree::get_control_structure_used(p);
|
csp = ParseTree::get_control_structure_used(p);
|
||||||
if (csp) {
|
if (csp) {
|
||||||
if (Sentences::RuleSubtrees::opens_block(csp)) {
|
if (ControlStructures::opens_block(csp)) {
|
||||||
if ((p->next == NULL) ||
|
if ((p->next == NULL) ||
|
||||||
(ParseTree::get_end_control_structure_used(p->next) == NULL))
|
(ParseTree::get_end_control_structure_used(p->next) == NULL))
|
||||||
@<Issue problem for begin without end@>;
|
@<Issue problem for begin without end@>;
|
||||||
|
@ -1033,7 +1000,7 @@ annotations to them.
|
||||||
@ =
|
@ =
|
||||||
void Sentences::RuleSubtrees::insert_cb_nodes(parse_node *block) {
|
void Sentences::RuleSubtrees::insert_cb_nodes(parse_node *block) {
|
||||||
for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) {
|
for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) {
|
||||||
if (Sentences::RuleSubtrees::opens_block(ParseTree::get_control_structure_used(p))) {
|
if (ControlStructures::opens_block(ParseTree::get_control_structure_used(p))) {
|
||||||
parse_node *blank_cb_node = ParseTree::new(CODE_BLOCK_NT);
|
parse_node *blank_cb_node = ParseTree::new(CODE_BLOCK_NT);
|
||||||
ParseTree::set_control_structure_used(blank_cb_node,
|
ParseTree::set_control_structure_used(blank_cb_node,
|
||||||
ParseTree::get_control_structure_used(p));
|
ParseTree::get_control_structure_used(p));
|
||||||
|
@ -1261,188 +1228,3 @@ parse_node *Sentences::RuleSubtrees::end_node(parse_node *opening) {
|
||||||
ParseTree::int_annotation(opening, indentation_level_ANNOT));
|
ParseTree::int_annotation(opening, indentation_level_ANNOT));
|
||||||
return implicit_end;
|
return implicit_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
@h Basic Structural Syntax.
|
|
||||||
The following routine is an attempt to contain information about the
|
|
||||||
basic structural phrases in one place, so that if future loop constructs
|
|
||||||
are added, they can fairly simply be put here.
|
|
||||||
|
|
||||||
=
|
|
||||||
control_structure_phrase *Sentences::RuleSubtrees::csp_new(void) {
|
|
||||||
control_structure_phrase *csp = CREATE(control_structure_phrase);
|
|
||||||
csp->subordinate_to = NULL;
|
|
||||||
csp->indent_subblocks = FALSE;
|
|
||||||
csp->body_empty_except_for_subordinates = FALSE;
|
|
||||||
csp->used_at_stage = -1;
|
|
||||||
csp->requires_new_syntax = FALSE;
|
|
||||||
csp->allow_run_on = FALSE;
|
|
||||||
csp->keyword = L"<none>";
|
|
||||||
csp->is_a_loop = FALSE;
|
|
||||||
return csp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sentences::RuleSubtrees::create_standard_csps(void) {
|
|
||||||
switch_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
switch_CSP->body_empty_except_for_subordinates = TRUE;
|
|
||||||
switch_CSP->indent_subblocks = TRUE;
|
|
||||||
switch_CSP->requires_new_syntax = TRUE;
|
|
||||||
switch_CSP->keyword = L"if";
|
|
||||||
|
|
||||||
if_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
if_CSP->keyword = L"if";
|
|
||||||
|
|
||||||
repeat_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
repeat_CSP->keyword = L"repeat";
|
|
||||||
repeat_CSP->is_a_loop = TRUE;
|
|
||||||
|
|
||||||
while_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
while_CSP->keyword = L"while";
|
|
||||||
while_CSP->is_a_loop = TRUE;
|
|
||||||
|
|
||||||
otherwise_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
otherwise_CSP->subordinate_to = if_CSP;
|
|
||||||
otherwise_CSP->used_at_stage = 1;
|
|
||||||
|
|
||||||
abbreviated_otherwise_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
abbreviated_otherwise_CSP->subordinate_to = if_CSP;
|
|
||||||
abbreviated_otherwise_CSP->used_at_stage = 1;
|
|
||||||
|
|
||||||
otherwise_if_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
otherwise_if_CSP->subordinate_to = if_CSP;
|
|
||||||
otherwise_if_CSP->used_at_stage = 0;
|
|
||||||
|
|
||||||
case_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
case_CSP->subordinate_to = switch_CSP;
|
|
||||||
case_CSP->used_at_stage = 1;
|
|
||||||
case_CSP->requires_new_syntax = TRUE;
|
|
||||||
case_CSP->allow_run_on = TRUE;
|
|
||||||
|
|
||||||
default_case_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
default_case_CSP->subordinate_to = switch_CSP;
|
|
||||||
default_case_CSP->used_at_stage = 2;
|
|
||||||
default_case_CSP->requires_new_syntax = TRUE;
|
|
||||||
default_case_CSP->allow_run_on = TRUE;
|
|
||||||
|
|
||||||
say_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
|
|
||||||
now_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
|
|
||||||
instead_CSP = Sentences::RuleSubtrees::csp_new();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sentences::RuleSubtrees::log_control_structure(control_structure_phrase *csp) {
|
|
||||||
if (csp == if_CSP) LOG("IF");
|
|
||||||
if (csp == repeat_CSP) LOG("RPT");
|
|
||||||
if (csp == while_CSP) LOG("WHI");
|
|
||||||
if (csp == switch_CSP) LOG("SWI");
|
|
||||||
if (csp == otherwise_CSP) LOG("O");
|
|
||||||
if (csp == abbreviated_otherwise_CSP) LOG("AO");
|
|
||||||
if (csp == otherwise_if_CSP) LOG("OIF");
|
|
||||||
if (csp == case_CSP) LOG("CAS");
|
|
||||||
if (csp == default_case_CSP) LOG("DEF");
|
|
||||||
if (csp == say_CSP) LOG("SAY");
|
|
||||||
if (csp == now_CSP) LOG("NOW");
|
|
||||||
if (csp == instead_CSP) LOG("INS");
|
|
||||||
if (csp == NULL) LOG("---");
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sentences::RuleSubtrees::comma_possible(control_structure_phrase *csp) {
|
|
||||||
if ((csp == if_CSP) || (csp == switch_CSP) || (csp == otherwise_if_CSP))
|
|
||||||
return TRUE;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sentences::RuleSubtrees::is_a_loop(control_structure_phrase *csp) {
|
|
||||||
if (csp) return csp->is_a_loop;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sentences::RuleSubtrees::opens_block(control_structure_phrase *csp) {
|
|
||||||
if ((csp) && (csp->subordinate_to == NULL) && (csp != say_CSP) && (csp != now_CSP) && (csp != instead_CSP)) return TRUE;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sentences::RuleSubtrees::permits_break(control_structure_phrase *csp) {
|
|
||||||
if ((csp == repeat_CSP) || (csp == while_CSP)) return TRUE;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t *Sentences::RuleSubtrees::incipit(control_structure_phrase *csp) {
|
|
||||||
if (csp) return csp->keyword;
|
|
||||||
return L"<none>";
|
|
||||||
}
|
|
||||||
|
|
||||||
control_structure_phrase *Sentences::RuleSubtrees::detect_control_structure(wording W) {
|
|
||||||
if (<control-structure-phrase>(W)) {
|
|
||||||
if (<<rp>> == abbreviated_otherwise_CSP) return NULL;
|
|
||||||
return <<rp>>;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Sentences::RuleSubtrees::abbreviated_otherwise(wording W) {
|
|
||||||
if (<control-structure-phrase>(W)) {
|
|
||||||
if (<<rp>> == abbreviated_otherwise_CSP) return TRUE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
control_structure_phrase *Sentences::RuleSubtrees::detect_end_control_structure(wording W) {
|
|
||||||
if (<end-control-structure-phrase>(W)) return <<rp>>;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ Control structures such as "if" act as a sort of super-punctuation inside
|
|
||||||
rule and phrase definitions, and in particular they affect the actual
|
|
||||||
punctuation of the sentences there (consider the rules about colons versus
|
|
||||||
semicolons). So, though it's still early in Inform's run, we need to seek
|
|
||||||
them out.
|
|
||||||
|
|
||||||
Here we parse the text of a command phrase which, if any, of the control
|
|
||||||
structures it might be. Note that <s-command> has a grammar partially
|
|
||||||
overlapping with this, and they need to match.
|
|
||||||
|
|
||||||
@d NO_SIGF 0
|
|
||||||
@d SAY_SIGF 1
|
|
||||||
@d NOW_SIGF 2
|
|
||||||
|
|
||||||
=
|
|
||||||
<control-structure-phrase> ::=
|
|
||||||
if ... is begin | ==> 0; *XP = switch_CSP
|
|
||||||
if ... is | ==> 0; *XP = switch_CSP
|
|
||||||
if/unless ... | ==> 0; *XP = if_CSP
|
|
||||||
repeat ... | ==> 0; *XP = repeat_CSP
|
|
||||||
while ... | ==> 0; *XP = while_CSP
|
|
||||||
else/otherwise | ==> 0; *XP = otherwise_CSP
|
|
||||||
else/otherwise if/unless ... | ==> 0; *XP = otherwise_if_CSP
|
|
||||||
else/otherwise ... | ==> 0; *XP = abbreviated_otherwise_CSP
|
|
||||||
-- otherwise | ==> 0; *XP = default_case_CSP
|
|
||||||
-- ... ==> 0; *XP = case_CSP
|
|
||||||
|
|
||||||
<end-control-structure-phrase> ::=
|
|
||||||
end if/unless | ==> 0; *XP = if_CSP
|
|
||||||
end while | ==> 0; *XP = while_CSP
|
|
||||||
end repeat ==> 0; *XP = repeat_CSP
|
|
||||||
|
|
||||||
<other-significant-phrase> ::=
|
|
||||||
say ... | ==> SAY_SIGF
|
|
||||||
now ... ==> NOW_SIGF
|
|
||||||
|
|
||||||
@ This is used to see if an "if" is being used with the comma notation:
|
|
||||||
|
|
||||||
=
|
|
||||||
<phrase-with-comma-notation> ::=
|
|
||||||
...... , ......
|
|
||||||
|
|
||||||
@ This is used to see if an "if" is being used with the comma notation:
|
|
||||||
|
|
||||||
=
|
|
||||||
<instead-keyword> ::=
|
|
||||||
instead ... |
|
|
||||||
... instead
|
|
||||||
|
|
||||||
@ Finally, this is used to see if a control structure opens a block:
|
|
||||||
|
|
||||||
=
|
|
||||||
<phrase-beginning-block> ::=
|
|
||||||
... begin
|
|
||||||
|
|
|
@ -4,8 +4,6 @@ To parse structurally important sentences.
|
||||||
|
|
||||||
@
|
@
|
||||||
|
|
||||||
@d list_node_type ROUTINE_NT
|
|
||||||
@d list_entry_node_type INVOCATION_LIST_NT
|
|
||||||
|
|
||||||
@h Sentence division.
|
@h Sentence division.
|
||||||
Sentence division can happen either early in Inform's run, when the vast bulk
|
Sentence division can happen either early in Inform's run, when the vast bulk
|
||||||
|
|
|
@ -78,83 +78,35 @@ int PL::Bibliographic::bibliographic_new_variable_notify(nonlocal_variable *q) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ This is what the top line of the main source text should look like, if it's
|
@ The following is called in response to the bibliographic sentence. That in
|
||||||
to declare the title and author.
|
fact has already been parsed by Inbuild, so we simply extract the results,
|
||||||
|
and set the "story title" and "story author" variables accordingly.
|
||||||
=
|
|
||||||
<titling-line> ::=
|
|
||||||
<plain-titling-line> ( in <natural-language> ) | ==> R[1]; *XP = RP[2];
|
|
||||||
<plain-titling-line> ==> R[1]; *XP = NULL;
|
|
||||||
|
|
||||||
<plain-titling-line> ::=
|
|
||||||
{<quoted-text-without-subs>} by ... | ==> TRUE
|
|
||||||
{<quoted-text-without-subs>} ==> FALSE
|
|
||||||
|
|
||||||
@ That grammar is used at two points: first, to spot the natural language
|
|
||||||
being used in the source text, something we have to look ahead to since
|
|
||||||
it affects the grammar needed to understand the rest of the file --
|
|
||||||
|
|
||||||
=
|
|
||||||
inform_language *PL::Bibliographic::scan_language(parse_node *PN) {
|
|
||||||
if (<titling-line>(ParseTree::get_text(PN))) return <<rp>>;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -- and secondly, to parse the titling-line sentence in the regular way,
|
|
||||||
setting bibliographic variables as needed. The following is called on the
|
|
||||||
first sentence in the source text if and only if it begins with text in
|
|
||||||
double quotes:
|
|
||||||
|
|
||||||
=
|
=
|
||||||
void PL::Bibliographic::bibliographic_data(parse_node *PN) {
|
void PL::Bibliographic::bibliographic_data(parse_node *PN) {
|
||||||
if (<titling-line>(ParseTree::get_text(PN))) {
|
inbuild_edition *edn = Task::edition();
|
||||||
wording TW = GET_RW(<plain-titling-line>, 1);
|
TEMPORARY_TEXT(T);
|
||||||
wording AW = EMPTY_WORDING;
|
TEMPORARY_TEXT(A);
|
||||||
if (<<r>>) AW = GET_RW(<plain-titling-line>, 2);
|
WRITE_TO(T, "\"x%S\" ", edn->work->title);
|
||||||
|
WRITE_TO(A, "\"x%S\" ", edn->work->author_name);
|
||||||
|
wording TW = Feeds::feed_stream(T);
|
||||||
|
wording AW = Feeds::feed_stream(A);
|
||||||
|
DISCARD_TEXT(T);
|
||||||
|
DISCARD_TEXT(A);
|
||||||
|
|
||||||
if ((story_title_VAR) && (story_author_VAR)) {
|
if ((story_title_VAR) && (story_author_VAR)) {
|
||||||
@<Set the story title from the titling line@>;
|
|
||||||
if (Wordings::nonempty(AW)) @<Set the author from the titling line@>;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Problems::Issue::sentence_problem(_p_(PM_BadTitleSentence),
|
|
||||||
"the initial bibliographic sentence can only be a title in double-quotes",
|
|
||||||
"possibly followed with 'by' and the name of the author.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ We must not of course simply write to the variables; we call the assertion
|
|
||||||
machinery to generate an inference about their values, because that ensures
|
|
||||||
that contradictions, and so forth, are properly complained about.
|
|
||||||
|
|
||||||
@<Set the story title from the titling line@> =
|
|
||||||
parse_node *the_title;
|
parse_node *the_title;
|
||||||
if (<s-value>(TW)) the_title = <<rp>>;
|
if (<s-value>(TW)) the_title = <<rp>>;
|
||||||
else the_title = Specifications::new_UNKNOWN(TW);
|
else the_title = Specifications::new_UNKNOWN(TW);
|
||||||
Assertions::PropertyKnowledge::initialise_global_variable(story_title_VAR, the_title);
|
Assertions::PropertyKnowledge::initialise_global_variable(story_title_VAR, the_title);
|
||||||
Strings::TextLiterals::suppress_quote_expansion(ParseTree::get_text(the_title));
|
Strings::TextLiterals::suppress_quote_expansion(ParseTree::get_text(the_title));
|
||||||
|
|
||||||
@ The author is often given outside of quotation marks:
|
|
||||||
|
|
||||||
>> "The Large Scale Structure of Space-Time" by Lindsay Lohan
|
|
||||||
|
|
||||||
and in such cases we transcribe the name-words into quotes so that we can
|
|
||||||
treat them as a text literal ("Lindsay Lohan").
|
|
||||||
|
|
||||||
@<Set the author from the titling line@> =
|
|
||||||
TEMPORARY_TEXT(author_buffer);
|
|
||||||
if (<quoted-text>(AW) == FALSE) {
|
|
||||||
WRITE_TO(author_buffer, "\"%+W", AW);
|
|
||||||
for (int i=1, L=Str::len(author_buffer); i<L; i++)
|
|
||||||
if (Str::get_at(author_buffer, i) == '\"')
|
|
||||||
Str::put_at(author_buffer, i, '\'');
|
|
||||||
WRITE_TO(author_buffer, "\" ");
|
|
||||||
AW = Feeds::feed_stream(author_buffer);
|
|
||||||
}
|
|
||||||
DISCARD_TEXT(author_buffer);
|
|
||||||
parse_node *the_author;
|
parse_node *the_author;
|
||||||
if (<s-value>(AW)) the_author = <<rp>>;
|
if (<s-value>(AW)) the_author = <<rp>>;
|
||||||
else the_author = Specifications::new_UNKNOWN(AW);
|
else the_author = Specifications::new_UNKNOWN(AW);
|
||||||
Assertions::PropertyKnowledge::initialise_global_variable(story_author_VAR, the_author);
|
Assertions::PropertyKnowledge::initialise_global_variable(story_author_VAR, the_author);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ This unattractive routine performs a string comparison of the author's name
|
@ This unattractive routine performs a string comparison of the author's name
|
||||||
against one that's supplied, case sensitively, and is used when deciding
|
against one that's supplied, case sensitively, and is used when deciding
|
||||||
|
|
|
@ -67,7 +67,7 @@ void Unit::test_kinds(text_stream *arg) {
|
||||||
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
||||||
ParseTree::plant_parse_tree();
|
ParseTree::plant_parse_tree();
|
||||||
PRINT("Read %d words\n", Wordings::length(W));
|
PRINT("Read %d words\n", Wordings::length(W));
|
||||||
Sentences::break(W, FALSE, NULL);
|
Sentences::break(W, FALSE, NULL, -1);
|
||||||
ParseTree::traverse(Unit::diagram);
|
ParseTree::traverse(Unit::diagram);
|
||||||
|
|
||||||
text_stream *save_DL = DL;
|
text_stream *save_DL = DL;
|
||||||
|
|
|
@ -70,7 +70,7 @@ void Unit::test_diagrams(text_stream *arg) {
|
||||||
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
||||||
ParseTree::plant_parse_tree();
|
ParseTree::plant_parse_tree();
|
||||||
PRINT("Read %d words\n", Wordings::length(W));
|
PRINT("Read %d words\n", Wordings::length(W));
|
||||||
Sentences::break(W, FALSE, NULL);
|
Sentences::break(W, FALSE, NULL, -1);
|
||||||
|
|
||||||
text_stream *save_DL = DL;
|
text_stream *save_DL = DL;
|
||||||
DL = STDOUT;
|
DL = STDOUT;
|
||||||
|
|
|
@ -490,7 +490,7 @@ void Problems::Issue::diagnose_further(void) {
|
||||||
"be in double quotes, \"like this\" and not 'like this'.)");
|
"be in double quotes, \"like this\" and not 'like this'.)");
|
||||||
|
|
||||||
control_structure_phrase *csp =
|
control_structure_phrase *csp =
|
||||||
Sentences::RuleSubtrees::detect_control_structure(ParseTree::get_text(current_sentence));
|
ControlStructures::detect(ParseTree::get_text(current_sentence));
|
||||||
if (csp)
|
if (csp)
|
||||||
Problems::issue_problem_segment(
|
Problems::issue_problem_segment(
|
||||||
" %P(The way this sentence starts makes me think it might have been "
|
" %P(The way this sentence starts makes me think it might have been "
|
||||||
|
|
|
@ -37,7 +37,7 @@ void Unit::test_problems(text_stream *arg) {
|
||||||
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
||||||
ParseTree::plant_parse_tree();
|
ParseTree::plant_parse_tree();
|
||||||
PRINT("Read %d words\n", Wordings::length(W));
|
PRINT("Read %d words\n", Wordings::length(W));
|
||||||
Sentences::break(W, FALSE, NULL);
|
Sentences::break(W, FALSE, NULL, -1);
|
||||||
|
|
||||||
ParseTree::traverse(Unit::scan_tree);
|
ParseTree::traverse(Unit::scan_tree);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ Structural node types are enumerated first:
|
||||||
|
|
||||||
@d BASE_OF_ENUMERATED_NTS 0x80000000
|
@d BASE_OF_ENUMERATED_NTS 0x80000000
|
||||||
|
|
||||||
@e INVALID_NT from 0x80000000 /* No node with this node should ever exist */
|
@e INVALID_NT from 0x80000000 /* No node with this node type should ever exist */
|
||||||
|
|
||||||
@e ROOT_NT /* Only one such node exists: the tree root */
|
@e ROOT_NT /* Only one such node exists: the tree root */
|
||||||
@e INCLUSION_NT /* Holds a block of source material */
|
@e INCLUSION_NT /* Holds a block of source material */
|
||||||
|
|
|
@ -9,7 +9,6 @@ English sentences, and join each to the parse tree.
|
||||||
position.
|
position.
|
||||||
|
|
||||||
=
|
=
|
||||||
int no_sentences_read = 0;
|
|
||||||
int sfsm_extension_position = 0; /* 0: not an extension; 1: before "begins here"; 2: before "ends here"; 3: after */
|
int sfsm_extension_position = 0; /* 0: not an extension; 1: before "begins here"; 2: before "ends here"; 3: after */
|
||||||
node_type_t ssnt = 0;
|
node_type_t ssnt = 0;
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ source_file *sfsm_source_file = NULL;
|
||||||
int sfsm_inside_rule_mode = FALSE;
|
int sfsm_inside_rule_mode = FALSE;
|
||||||
int sfsm_skipping_material_at_level = -1;
|
int sfsm_skipping_material_at_level = -1;
|
||||||
int sfsm_in_tabbed_mode = FALSE;
|
int sfsm_in_tabbed_mode = FALSE;
|
||||||
|
int sfsm_main_source_start_wn = -1;
|
||||||
EXTENSION_FILE_TYPE *sfsm_extension = NULL;
|
EXTENSION_FILE_TYPE *sfsm_extension = NULL;
|
||||||
|
|
||||||
@ Now for the routine itself. We break into bite-sized chunks, each of which is
|
@ Now for the routine itself. We break into bite-sized chunks, each of which is
|
||||||
|
@ -65,7 +65,12 @@ which was used to end it. Each call to this routine represents one cycle of our
|
||||||
finite state machine.
|
finite state machine.
|
||||||
|
|
||||||
=
|
=
|
||||||
void Sentences::break(wording W, int is_extension, EXTENSION_FILE_TYPE *from_extension) {
|
void Sentences::break(wording W, int is_extension,
|
||||||
|
EXTENSION_FILE_TYPE *from_extension, int bwc) {
|
||||||
|
while (((Wordings::nonempty(W))) && (compare_word(Wordings::first_wn(W), PARBREAK_V)))
|
||||||
|
W = Wordings::trim_first_word(W);
|
||||||
|
if (Wordings::empty(W)) return;
|
||||||
|
|
||||||
int sentence_start = Wordings::first_wn(W);
|
int sentence_start = Wordings::first_wn(W);
|
||||||
ParseTree::enable_last_sentence_cacheing();
|
ParseTree::enable_last_sentence_cacheing();
|
||||||
|
|
||||||
|
@ -112,6 +117,7 @@ that is why these are global variables rather than locals in |Sentences::break|.
|
||||||
sfsm_extension = from_extension;
|
sfsm_extension = from_extension;
|
||||||
if (is_extension) sfsm_extension_position = 1;
|
if (is_extension) sfsm_extension_position = 1;
|
||||||
else sfsm_extension_position = 0;
|
else sfsm_extension_position = 0;
|
||||||
|
sfsm_main_source_start_wn = bwc;
|
||||||
|
|
||||||
@ A table is any sentence beginning with the word "Table". (Bad news for
|
@ A table is any sentence beginning with the word "Table". (Bad news for
|
||||||
anyone writing "Table Mountain is a room.", of course, but there are other
|
anyone writing "Table Mountain is a room.", of course, but there are other
|
||||||
|
@ -289,12 +295,6 @@ void Sentences::make_node(wording W, int stop_character) {
|
||||||
|
|
||||||
if (Wordings::empty(W)) internal_error("empty sentence generated");
|
if (Wordings::empty(W)) internal_error("empty sentence generated");
|
||||||
|
|
||||||
#ifdef SENTENCE_COUNT_MONITOR
|
|
||||||
if (SENTENCE_COUNT_MONITOR(W)) no_sentences_read++;
|
|
||||||
#else
|
|
||||||
no_sentences_read++;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Vocabulary::identify_word_range(W); /* a precaution to catch any late unidentified text */
|
Vocabulary::identify_word_range(W); /* a precaution to catch any late unidentified text */
|
||||||
|
|
||||||
@<Detect a change of source file, and declare it as an implicit heading@>;
|
@<Detect a change of source file, and declare it as an implicit heading@>;
|
||||||
|
@ -446,20 +446,17 @@ substitutions. For instance,
|
||||||
>> "A Dream of Fair to Middling Women" by Samuel Beckett
|
>> "A Dream of Fair to Middling Women" by Samuel Beckett
|
||||||
|
|
||||||
This sentence is at the position matched by <if-start-of-source-text>.
|
This sentence is at the position matched by <if-start-of-source-text>.
|
||||||
(Strictly speaking it's the second sentence read, not the first, because all
|
(It may not be the first sentence read, because implied extension inclusion
|
||||||
source texts implicitly begin with an inclusion of the Standard Rules.)
|
sentences and options-file sentences may have been read already.)
|
||||||
|
|
||||||
=
|
=
|
||||||
<if-start-of-source-text> internal 0 {
|
<if-start-of-source-text> internal 0 {
|
||||||
int w1 = Wordings::first_wn(W);
|
int w1 = Wordings::first_wn(W);
|
||||||
#ifdef CORE_MODULE
|
while (w1 >= 0) {
|
||||||
int N = 1 + Projects::number_of_early_fed_sentences(Inbuild::project());
|
if (w1 == sfsm_main_source_start_wn) return TRUE;
|
||||||
#endif
|
if (compare_word(w1-1, PARBREAK_V) == FALSE) return FALSE;
|
||||||
#ifndef CORE_MODULE
|
w1--;
|
||||||
int N = 3;
|
}
|
||||||
#endif
|
|
||||||
if ((no_sentences_read == N) &&
|
|
||||||
((w1 == 0) || (compare_word(w1-1, PARBREAK_V)))) return TRUE;
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,10 +486,9 @@ source texts implicitly begin with an inclusion of the Standard Rules.)
|
||||||
else if (language_extension_inclusion_point == NULL) language_extension_inclusion_point = new;
|
else if (language_extension_inclusion_point == NULL) language_extension_inclusion_point = new;
|
||||||
}
|
}
|
||||||
ParseTree::set_type(new, ssnt);
|
ParseTree::set_type(new, ssnt);
|
||||||
#ifdef IF_MODULE
|
#ifdef INBUILD_MODULE
|
||||||
if (ssnt == BIBLIOGRAPHIC_NT)
|
if (ssnt == BIBLIOGRAPHIC_NT)
|
||||||
Projects::set_language_of_play(Inbuild::project(),
|
Projects::notify_of_bibliographic_sentence(Inbuild::project(), new);
|
||||||
PL::Bibliographic::scan_language(new));
|
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -614,9 +610,11 @@ instead of a semicolon. We may lament this, but it is so.)
|
||||||
@<Convert a rule preamble to a ROUTINE node and enter rule mode@> =
|
@<Convert a rule preamble to a ROUTINE node and enter rule mode@> =
|
||||||
#ifdef list_node_type
|
#ifdef list_node_type
|
||||||
if (stop_character == ':') {
|
if (stop_character == ':') {
|
||||||
if ((sfsm_inside_rule_mode) && (Sentences::RuleSubtrees::detect_control_structure(W))) {
|
if ((sfsm_inside_rule_mode) && (ControlStructures::detect(W))) {
|
||||||
ParseTree::set_type(new, list_entry_node_type);
|
ParseTree::set_type(new, list_entry_node_type);
|
||||||
|
#ifdef CORE_MODULE
|
||||||
ParseTree::annotate_int(new, colon_block_command_ANNOT, TRUE);
|
ParseTree::annotate_int(new, colon_block_command_ANNOT, TRUE);
|
||||||
|
#endif
|
||||||
sfsm_inside_rule_mode = TRUE;
|
sfsm_inside_rule_mode = TRUE;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,7 +26,7 @@ void Unit::test_tree(text_stream *arg) {
|
||||||
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
if (sf == NULL) { PRINT("File has failed to open\n"); return; }
|
||||||
ParseTree::plant_parse_tree();
|
ParseTree::plant_parse_tree();
|
||||||
PRINT("Read %d words\n", Wordings::length(W));
|
PRINT("Read %d words\n", Wordings::length(W));
|
||||||
Sentences::break(W, FALSE, NULL);
|
Sentences::break(W, FALSE, NULL, -1);
|
||||||
|
|
||||||
text_stream *save_DL = DL;
|
text_stream *save_DL = DL;
|
||||||
DL = STDOUT;
|
DL = STDOUT;
|
||||||
|
|
Loading…
Reference in a new issue