1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-06-17 07:40:47 +03:00

Rudimentary syntax work for dialogue

This commit is contained in:
Graham Nelson 2022-09-01 13:04:08 +01:00
parent 03592cb26f
commit 27c5570586
20 changed files with 210 additions and 34 deletions

View file

@ -219,6 +219,10 @@ void CopyErrors::write(OUTPUT_STREAM, copy_error *CE) {
WRITE("heading is in place of another heading which doesn't exist"); break;
case UnavailableLOS_SYNERROR:
WRITE("this language bundle does not provide Preform syntax"); break;
case DialogueOnSectionsOnly_SYNERROR:
WRITE("only 'Section' headings can be marked as '(dialogue)'"); break;
case UnexpectedDialogue_SYNERROR:
WRITE("something other than a cue or a line under a dialogue heading"); break;
default:
WRITE("syntax error"); break;
}

View file

@ -995,6 +995,7 @@ it comes.
int l = SyntaxTree::push_bud(proj->syntax_tree, proj->syntax_tree->root_node);
Sentences::break_into_project_copy(
proj->syntax_tree, Wordings::new(wc, lexer_wordcount-1), proj->as_copy, proj);
LOG("Heya!\n$T\n", proj->syntax_tree->root_node);
SyntaxTree::pop_bud(proj->syntax_tree, l);
@ Inventions are when the //inform7// compiler makes up extra sentences, not

View file

@ -146,6 +146,7 @@ typedef struct heading {
int for_release; /* include this material in a release version? */
int omit_material; /* if set, simply ignore all of this */
int use_with_or_without; /* if TRUE, use with the extension; if FALSE, without */
int holds_dialogue; /* if TRUE, contains dialogue in a different format */
struct inbuild_work *for_use_with; /* e.g. "for use with ... by ..." */
struct wording in_place_of_text; /* e.g. "in place of ... in ... by ..." */
struct wording heading_text; /* once provisos have been stripped away */
@ -175,6 +176,7 @@ heading *Headings::new(parse_node_tree *T, parse_node *pn, int level, source_loc
h->in_place_of_text = EMPTY_WORDING;
h->for_use_with = NULL;
h->sentence_declaring = pn;
h->holds_dialogue = FALSE;
h->start_location = sl;
h->level = level;
h->heading_text = EMPTY_WORDING;
@ -262,6 +264,7 @@ heading *Headings::attach(parse_node_tree *T, parse_node *pn) {
@d USE_WITH_HQ 5
@d USE_WITHOUT_HQ 6
@d IN_PLACE_OF_HQ 7
@d DIALOGUE_HQ 8
@<Parse heading text for release or other stipulations@> =
current_sentence = pn;
@ -275,6 +278,14 @@ heading *Headings::attach(parse_node_tree *T, parse_node *pn) {
case UNINDEXED_HQ: h->index_definitions_made_under_this = FALSE; break;
case USE_WITH_HQ: h->use_with_or_without = TRUE; break;
case USE_WITHOUT_HQ: h->use_with_or_without = FALSE; break;
case DIALOGUE_HQ:
if (h->level != 5) {
copy_error *CE = CopyErrors::new(SYNTAX_CE, DialogueOnSectionsOnly_SYNERROR);
CopyErrors::supply_node(CE, current_sentence);
Copies::attach_error(sfsm->ref, CE);
}
h->holds_dialogue = TRUE;
break;
case IN_PLACE_OF_HQ:
h->use_with_or_without = TRUE;
h->in_place_of_text = GET_RW(<extension-qualifier>, 1);
@ -296,6 +307,11 @@ would match twice, first registering the VM requirement, then the unindexedness.
It's an unfortunate historical quirk that the unbracketed qualifiers are
allowed; they should probably be withdrawn.
Properly speaking the annotation "(dialogue)" should be recognised only if
the dialogue feature is active, but there are timing reasons not to do this:
headings are parsed before the set of active compiler features for a project
is determined.
=
<heading-qualifier> ::=
... ( <bracketed-heading-qualifier> ) | ==> { pass 1 }
@ -307,6 +323,8 @@ allowed; they should probably be withdrawn.
not for release | ==> { NOT_FOR_RELEASE_HQ, - }
for release only | ==> { FOR_RELEASE_ONLY_HQ, - }
unindexed | ==> { UNINDEXED_HQ, - }
dialogue | ==> { DIALOGUE_HQ, - }
dialog | ==> { DIALOGUE_HQ, - }
<platform-qualifier> | ==> { pass 1 }
<extension-qualifier> ==> { pass 1 }

View file

@ -149,6 +149,7 @@ add to those generated in //syntax//.
@e ExtInadequateVM_SYNERROR
@e ExtMisidentifiedEnds_SYNERROR
@e UnavailableLOS_SYNERROR
@e DialogueOnSectionsOnly_SYNERROR
@ The next tweak to //syntax// is to give it some node metadata. //syntax//
itself places nodes of a small number of basic types into the syntax tree;
@ -210,6 +211,8 @@ and here goes:
book ... | ==> { 2, - }
part ... | ==> { 3, - }
chapter ... | ==> { 4, - }
section ... ( dialog ) | ==> { 6, - }
section ... ( dialogue ) | ==> { 6, - }
section ... ==> { 5, - }
<extension-end-marker-sentence> ::=

View file

@ -0,0 +1,5 @@
Lab is a room.
Book 1 - Meeting (dialogue)
Section 1 - Actual Meeting (dialogue)

View file

@ -0,0 +1,13 @@
Lab is a room.
Section 1 - Meeting (dialogue)
The claw is here.
(About the paranormal.)
Marcellus: "What, has this thing appear'd again to-night?"
Bernardo: "I have seen naught but [list of things in the Battlements]."
Marcellus: "Horatio says 'tis but our fantasy."

View file

@ -0,0 +1,10 @@
Inform 7 v10.2.0 has started.
I've now read your source text, which is 15 words long.
I've also read Basic Inform by Graham Nelson, which is 7691 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32187 words long.
Problem__ PM_DialogueOnSectionsOnly
>--> In the heading 'Book 1 - Meeting (dialogue)' (source text, line 3),
you've marked for '(dialogue)', but only Sections can be so marked - not
Chapters, Books, and so on.
Inform 7 has finished.

View file

@ -0,0 +1,11 @@
Inform 7 v10.2.0 has started.
I've now read your source text, which is 43 words long.
I've also read Basic Inform by Graham Nelson, which is 7691 words long.
I've also read English Language by Graham Nelson, which is 2328 words long.
I've also read Standard Rules by Graham Nelson, which is 32187 words long.
Problem__ PM_UnexpectedDialogue
>--> The text 'The claw is here .' (source text, line 5) appears under a
section heading marked as dialogue, so it needs to be either a cue in
brackets '(like this.)', or else a line of dialogue 'Speaker: "Something to
say!"'. It doesn't seem to be either of those.
Inform 7 has finished.

View file

@ -132,6 +132,9 @@ organisation, and are not directly functional in themselves.
#endif
break;
case DIALOGUE_CUE_NT: break; /* not yet implemented */
case DIALOGUE_LINE_NT: break; /* not yet implemented */
case INVOCATION_LIST_NT: break; /* for error recovery; shouldn't be here otherwise */
case UNKNOWN_NT: break; /* for error recovery; shouldn't be here otherwise */

View file

@ -20,16 +20,13 @@ to be part of a released version.
@e INFORM_INTER_DA
=
compiler_feature *core_feature, *naming_feature, *counting_feature, *experimental_feature;
compiler_feature *naming_feature, *counting_feature;
void CoreModule::start(void) {
core_feature = Features::new(NULL, I"core", NULL);
Features::make_permanently_active(core_feature);
Features::activate_core();
naming_feature = Features::new(&Naming::start, I"naming", core_feature);
counting_feature = Features::new(&InstanceCounting::start, I"instance counting", core_feature);
experimental_feature = Features::new(NULL, I"experimental features", NULL);
Log::declare_aspect(TASK_QUEUE_DA, L"task queue", FALSE, FALSE);
Log::declare_aspect(INTER_DA, L"inter", FALSE, FALSE);
Log::declare_aspect(INFORM_INTER_DA, L"inform inter", FALSE, FALSE);

View file

@ -427,6 +427,25 @@ void SourceProblems::issue_problems_arising(inbuild_copy *C) {
"for that language does not provide a file of Preform definitions.");
Problems::issue_problem_end();
break;
case DialogueOnSectionsOnly_SYNERROR:
current_sentence = CE->details_node;
Problems::quote_source(1, current_sentence);
StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_DialogueOnSectionsOnly));
Problems::issue_problem_segment(
"In the heading %1, you've marked for '(dialogue)', but only "
"Sections can be so marked - not Chapters, Books, and so on.");
Problems::issue_problem_end();
break;
case UnexpectedDialogue_SYNERROR:
Problems::quote_source(1, Diagrams::new_UNPARSED_NOUN(CE->details_W));
StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_UnexpectedDialogue));
Problems::issue_problem_segment(
"The text %1 appears under a section heading marked as dialogue, "
"so it needs to be either a cue in brackets '(like this.)', or "
"else a line of dialogue 'Speaker: \"Something to say!\"'. It "
"doesn't seem to be either of those.");
Problems::issue_problem_end();
break;
default:
internal_error("unknown syntax error");
}

View file

@ -163,3 +163,16 @@ void Features::run_activation_function(compiler_feature *F) {
if (start) (*start)();
}
}
@ Basic features which are present in all three Inform compiler tools:
=
compiler_feature *core_feature = NULL, *experimental_feature = NULL, *dialogue_feature = NULL;
void Features::activate_core(void) {
core_feature = Features::new(NULL, I"core", NULL);
Features::make_permanently_active(core_feature);
experimental_feature = Features::new(NULL, I"experimental features", NULL);
dialogue_feature = Features::new(NULL, I"dialogue", experimental_feature);
}

View file

@ -1,6 +1,6 @@
Seek verb in: there is a ming vase on the table called the table of having
viability map of 'there is a ming vase on the table called the table of having':
-- is[1] -- -- -- -- -- -- -- -- -- of[1] --
-- is[1] -- -- -- -- -- -- called[1] -- -- -- --
Found usage, pass 1 tier 2: (there) be(0) (a ming vase on the table called the table of having)
Seek verb in: a ming vase on the table | called the table of having
viability map of 'a ming vase on the table called the table of having':

View file

@ -212,20 +212,24 @@ do this. All a bit clumsy, but it works.
@e SENTENCE_NT /* "The Garden is a room" */
@e AMBIGUITY_NT /* Marks an ambiguous set of readings in the tree */
@e UNKNOWN_NT /* "arfle barfle gloop" */
@e DIALOGUE_CUE_NT /* A dialogue cue under a dialogue Section heading */
@e DIALOGUE_LINE_NT /* A line of dialogue under a dialogue Section heading */
=
void NodeType::metadata_setup(void) {
NodeType::new(INVALID_NT, I"(INVALID_NT)", 0, INFTY, INVALID_NCAT, 0);
NodeType::new(INVALID_NT, I"(INVALID_NT)", 0, INFTY, INVALID_NCAT, 0);
NodeType::new(ROOT_NT, I"ROOT_NT", 0, INFTY, L1_NCAT, DONT_VISIT_NFLAG);
NodeType::new(INCLUSION_NT, I"INCLUSION_NT", 0, INFTY, L1_NCAT, DONT_VISIT_NFLAG);
NodeType::new(HEADING_NT, I"HEADING_NT", 0, INFTY, L1_NCAT, 0);
NodeType::new(INCLUDE_NT, I"INCLUDE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(BEGINHERE_NT, I"BEGINHERE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(ENDHERE_NT, I"ENDHERE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(SENTENCE_NT, I"SENTENCE_NT", 0, INFTY, L2_NCAT, 0);
NodeType::new(AMBIGUITY_NT, I"AMBIGUITY_NT", 0, INFTY, L1_NCAT, 0);
NodeType::new(UNKNOWN_NT, I"UNKNOWN_NT", 0, INFTY, UNKNOWN_NCAT, 0);
NodeType::new(ROOT_NT, I"ROOT_NT", 0, INFTY, L1_NCAT, DONT_VISIT_NFLAG);
NodeType::new(INCLUSION_NT, I"INCLUSION_NT", 0, INFTY, L1_NCAT, DONT_VISIT_NFLAG);
NodeType::new(HEADING_NT, I"HEADING_NT", 0, INFTY, L1_NCAT, 0);
NodeType::new(INCLUDE_NT, I"INCLUDE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(BEGINHERE_NT, I"BEGINHERE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(ENDHERE_NT, I"ENDHERE_NT", 0, 0, L2_NCAT, 0);
NodeType::new(SENTENCE_NT, I"SENTENCE_NT", 0, INFTY, L2_NCAT, 0);
NodeType::new(AMBIGUITY_NT, I"AMBIGUITY_NT", 0, INFTY, L1_NCAT, 0);
NodeType::new(UNKNOWN_NT, I"UNKNOWN_NT", 0, INFTY, UNKNOWN_NCAT, 0);
NodeType::new(DIALOGUE_CUE_NT, I"DIALOGUE_CUE_NT", 0, INFTY, L2_NCAT, 0);
NodeType::new(DIALOGUE_LINE_NT, I"DIALOGUE_LINE_NT", 0, INFTY, L2_NCAT, 0);
#ifdef NODE_METADATA_SETUP_SYNTAX_CALLBACK
NODE_METADATA_SETUP_SYNTAX_CALLBACK();

View file

@ -48,6 +48,7 @@ typedef struct syntax_fsm_state {
node_type_t nt;
int inside_rule_mode;
int inside_table_mode;
int inside_dialogue_mode;
PROBLEM_REF_SYNTAX_TYPE *ref;
PROJECT_REF_SYNTAX_TYPE *project_ref;
} syntax_fsm_state;
@ -69,6 +70,8 @@ void Sentences::reset(syntax_fsm_state *sfsm, int is_extension,
PROBLEM_REF_SYNTAX_TYPE *ref, PROJECT_REF_SYNTAX_TYPE *project_ref) {
sfsm->sf = NULL;
sfsm->inside_rule_mode = FALSE;
sfsm->inside_table_mode = FALSE;
sfsm->inside_dialogue_mode = FALSE;
sfsm->skipping_material_at_level = -1;
sfsm->ref = ref;
sfsm->project_ref = project_ref;
@ -89,6 +92,7 @@ void Sentences::reset(syntax_fsm_state *sfsm, int is_extension,
@e ExtNoEndsHere_SYNERROR
@e HeadingOverLine_SYNERROR
@e HeadingStopsBeforeEndOfLine_SYNERROR
@e UnexpectedDialogue_SYNERROR
@ Now for the function itself. We break into bite-sized chunks, each of which is
despatched to the |Sentences::make_node| function with a note of the punctuation
@ -208,19 +212,22 @@ sentence divisions. The other cases are more complicated: see below.
if (stop_character == ':') @<Issue problem for colon at end of paragraph@>;
stop_character = '|'; stopped = TRUE;
}
if (Lexer::word(at) == FULLSTOP_V) {
if (stop_character == ':') @<Issue problem for colon at end of sentence@>;
if (stop_character == ';') @<Issue problem for semicolon at end of sentence@>;
stop_character = '.'; stopped = TRUE;
}
if (Lexer::word(at) == SEMICOLON_V) {
if (stop_character == ':') @<Issue problem for semicolon after colon@>;
if (stop_character == '.') @<Issue problem for semicolon after full stop@>;
stop_character = ';'; stopped = TRUE;
}
if (sfsm->inside_dialogue_mode == FALSE) {
if (Lexer::word(at) == FULLSTOP_V) {
if (stop_character == ':') @<Issue problem for colon at end of sentence@>;
if (stop_character == ';') @<Issue problem for semicolon at end of sentence@>;
stop_character = '.'; stopped = TRUE;
}
if (Lexer::word(at) == SEMICOLON_V) {
if (stop_character == ':') @<Issue problem for semicolon after colon@>;
if (stop_character == '.') @<Issue problem for semicolon after full stop@>;
stop_character = ';'; stopped = TRUE;
}
@<Consider if a colon divides a sentence@>;
@<Consider if punctuation within a preceding quoted text divides a sentence, making an X break@>;
@<Consider if a colon divides a sentence@>;
@<Consider if punctuation within a preceding quoted text divides a sentence, making an X break@>;
}
if (stopped == FALSE) break;
no_stop_words++; at++;
@ -325,7 +332,7 @@ or until reaching the end of the text: whichever comes first.
=
void Sentences::make_node(parse_node_tree *T, wording W, int stop_character) {
int heading_level = 0;
int heading_level = 0, entering_dialogue = FALSE;
int begins_or_ends = 0; /* 1 for "begins here", -1 for "ends here" */
parse_node *new;
@ -343,6 +350,7 @@ void Sentences::make_node(parse_node_tree *T, wording W, int stop_character) {
if (sfsm->skipping_material_at_level >= 0) return;
if (heading_level > 0) {
sfsm->inside_dialogue_mode = entering_dialogue;
@<Issue a problem message if the heading incorporates a line break@>;
@<Issue a problem message if the heading does not end with a line break@>;
@<Make a new HEADING node, possibly beginning to skip material@>;
@ -386,7 +394,15 @@ important), or |-1| to mean that the sentence begins an extension, or
switch (<<r>>) {
case -1: if (sfsm->ext_pos != NO_EXTENSION_POS) begins_or_ends = 1; break;
case -2: if (sfsm->ext_pos != NO_EXTENSION_POS) begins_or_ends = -1; break;
default: heading_level = <<r>>; break;
default:
if (<<r>> == 6) {
heading_level = 5;
entering_dialogue = TRUE;
} else {
heading_level = <<r>>;
entering_dialogue = FALSE;
}
break;
}
}
@ -492,6 +508,7 @@ sentences and options-file sentences may have been read already.)
}
@<Accept the new sentence as one or more nodes in the parse tree@> =
if (sfsm->inside_dialogue_mode) @<Make a DIALOGUE node@>;
@<Convert comma-divided rule into two sentences, if this is allowed@>;
@<Otherwise, make a SENTENCE node@>;
@ -523,6 +540,35 @@ sentences and options-file sentences may have been read already.)
NEW_NONSTRUCTURAL_SENTENCE_SYNTAX_CALLBACK(new);
#endif
@
=
<dialogue-piece> ::=
( ... ) | ==> { 1, - }
...... : ...... ==> { 2, - }
@<Make a DIALOGUE node@> =
if (<dialogue-piece>(W)) {
switch (<<r>>) {
case 1:
new = Node::new(DIALOGUE_CUE_NT);
Node::set_text(new, W);
SyntaxTree::graft_sentence(T, new);
return;
case 2:
new = Node::new(DIALOGUE_LINE_NT);
Node::set_text(new, W);
SyntaxTree::graft_sentence(T, new);
return;
}
} else {
Sentences::syntax_problem(UnexpectedDialogue_SYNERROR, W, sfsm->ref, 0);
new = Node::new(UNKNOWN_NT);
Node::set_text(new, W);
SyntaxTree::graft_sentence(T, new);
}
return;
@ We make an exception to the exception for the serial comma used in a list of
alternatives: thus the comma in "Aeschylus, Sophocles, or Euripides" does
not trigger this rule. We need this exception because such lists of

View file

@ -7,6 +7,7 @@ What shall we test?
@d PROGRAM_NAME "syntax-test"
@e TEST_TREE_CLSW
@e TEST_DIALOGUE_CLSW
=
int main(int argc, char **argv) {
@ -18,6 +19,8 @@ int main(int argc, char **argv) {
CommandLine::declare_switch(TEST_TREE_CLSW, L"test-tree", 2,
L"test the syntax tree (from text in X)");
CommandLine::declare_switch(TEST_DIALOGUE_CLSW, L"test-dialogue", 2,
L"test a syntax tree with dialogue (from text in X)");
CommandLine::read(argc, argv, NULL, &Main::respond, &Main::ignore);
@ -30,6 +33,7 @@ int main(int argc, char **argv) {
void Main::respond(int id, int val, text_stream *arg, void *state) {
switch (id) {
case TEST_TREE_CLSW: Main::load(I"Syntax.preform"); Unit::test_tree(arg); break;
case TEST_DIALOGUE_CLSW: Main::load(I"Syntax.preform"); Unit::test_tree(arg); break;
}
}

View file

@ -8,8 +8,13 @@ any text but then fail.
=
<dividing-sentence> ::=
chapter ... | ==> { 1, - }
section ... ==> { 2, - }
volume ... | ==> { 1, - }
book ... | ==> { 2, - }
part ... | ==> { 3, - }
chapter ... | ==> { 4, - }
section ... ( dialog ) | ==> { 6, - }
section ... ( dialogue ) | ==> { 6, - }
section ... ==> { 5, - }
<structural-sentence> ::=
... ==> { fail }

View file

@ -0,0 +1,9 @@
Read 38 words
ROOT_NT
HEADING_NT'elsinore is a room' {heading 0}
SENTENCE_NT'elsinore is a room'
HEADING_NT'section 1 - on the battlements ( dialogue )' {heading 5}
DIALOGUE_CUE_NT'( about the paranormal . )'
DIALOGUE_LINE_NT'marcellus : "What, has this thing appear'd again to-night?"'
DIALOGUE_LINE_NT'bernardo : "I have seen naught but [list of things in the Ba'
DIALOGUE_LINE_NT'marcellus : "Horatio says 'tis but our fantasy."'

View file

@ -1,10 +1,10 @@
Read 106 words
ROOT_NT
HEADING_NT'chapter 1' {heading 0}
HEADING_NT'chapter 1' {heading 1}
HEADING_NT'chapter 1' {heading 4}
SENTENCE_NT'i burn to paint a certain woman who has appeared to me so ra'
SENTENCE_NT'it is already long since i saw her'
SENTENCE_NT'she is beautiful , and more than beautiful , she is overpowe'
HEADING_NT'chapter 2' {heading 1}
HEADING_NT'chapter 2' {heading 4}
SENTENCE_NT'i would compare her to a black sun if one could conceive of '
SENTENCE_NT'but it is the moon that she makes one dream of most readily'

View file

@ -0,0 +1,11 @@
Elsinore is a room.
Section 1 - On the Battlements (dialogue)
(About the paranormal.)
Marcellus: "What, has this thing appear'd again to-night?"
Bernardo: "I have seen naught but [list of things in the Battlements]."
Marcellus: "Horatio says 'tis but our fantasy."