-parse_node *AConditions::new_action_TEST_VALUE(action_pattern *ap, wordingW) {
+parse_node *AConditions::new_action_TEST_VALUE(action_pattern *ap, wordingW) {if (ap == NULL) internal_error("null action pattern");parse_node *spec = Node::new_with_words(TEST_VALUE_NT, W);spec->down = ARvalues::from_action_pattern(ap);
@@ -101,7 +101,21 @@ depending on whether the test is against an explicit action or something vaguer.
returnspec;}
-intAConditions::is_action_TEST_VALUE(parse_node *spec) {
+parse_node *AConditions::new_past_action_TEST_VALUE(action_pattern *ap, wordingW) {
+parse_node *spec = AConditions::new_action_TEST_VALUE(ap, W);
+if (ActionPatterns::makes_callings(ap)) {
+StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_PastActionCalled),
+"a description of an action cannot both refer to past history and also "
+"use '(called ...)'",
+"because that would require Inform in general to remember too much "
+"information about past events.");
+ } else {
+spec = Conditions::attach_tense(spec, HASBEEN_TENSE);
+ }
+returnspec;
+}
+
+intAConditions::is_action_TEST_VALUE(parse_node *spec) {if ((Node::is(spec, TEST_VALUE_NT)) && ((ARvalues::to_action_pattern(spec->down)) || (ARvalues::to_explicit_action(spec->down)))) returnTRUE;
@@ -119,7 +133,7 @@ reconstruct the original action pattern which led to this condition:
returnspec->down;}
-action_pattern *AConditions::pattern_from_action_TEST_VALUE(parse_node *spec) {
+action_pattern *AConditions::pattern_from_action_TEST_VALUE(parse_node *spec) {if (AConditions::is_action_TEST_VALUE(spec)) {parse_node *gerund = AConditions::gerund_from_TEST_VALUE(spec);action_pattern *ap = ARvalues::to_action_pattern(gerund);
@@ -136,7 +150,7 @@ reconstruct the original action pattern which led to this condition:
-intAConditions::compile_condition(value_holster *VH, parse_node *spec) {
+intAConditions::compile_condition(value_holster *VH, parse_node *spec) {if (AConditions::is_action_TEST_VALUE(spec)) {action_pattern *ap = AConditions::pattern_from_action_TEST_VALUE(spec);RTActionPatterns::compile_pattern_match(VH, ap, FALSE);
@@ -146,7 +160,7 @@ reconstruct the original action pattern which led to this condition:
}
diff --git a/docs/if-module/4-act.html b/docs/if-module/4-act.html
index 849478ae4..84ed5f7fd 100644
--- a/docs/if-module/4-act.html
+++ b/docs/if-module/4-act.html
@@ -214,7 +214,7 @@ have to correspond to names referenced in }
diff --git a/docs/if-module/4-ak.html b/docs/if-module/4-ak.html
index e1be859a4..3055a78c6 100644
--- a/docs/if-module/4-ak.html
+++ b/docs/if-module/4-ak.html
@@ -188,7 +188,7 @@ but in practice there seems little need, so for the moment we do not.
}
diff --git a/docs/if-module/4-anaa.html b/docs/if-module/4-anaa.html
index 69604db48..1b05fd4bd 100644
--- a/docs/if-module/4-anaa.html
+++ b/docs/if-module/4-anaa.html
@@ -288,7 +288,7 @@ so that it can indeed be used as a value.
}
diff --git a/docs/if-module/4-anl.html b/docs/if-module/4-anl.html
index 8352e5728..c114cc744 100644
--- a/docs/if-module/4-anl.html
+++ b/docs/if-module/4-anl.html
@@ -142,7 +142,7 @@ list can be marked accordingly:
The structure anl_item is accessed in 4/pap and here.
+
The structure anl_item is accessed in 4/pc and here.
§12. The following is one of Inform's standardised comparison routines, which
takes a pair of objects A, B and returns 1 if A makes a more specific
description than B, 0 if they seem equally specific, or \(-1\) if B makes a
@@ -352,7 +352,7 @@ used in sorting algorithms.
-intActionNameLists::compare_specificity(action_name_list *anl1, action_name_list *anl2) {
+intActionNameLists::compare_specificity(action_name_list *anl1, action_name_list *anl2) {intcount1, count2;count1 = ActionNameLists::count_actions_covered(anl1);count2 = ActionNameLists::count_actions_covered(anl2);
@@ -374,12 +374,12 @@ used in sorting algorithms.
returnk;}
-named_action_pattern *ActionNameLists::nap(anl_entry *entry) {
+named_action_pattern *ActionNameLists::nap(anl_entry *entry) {if (entry) returnentry->item.nap_listed;returnNULL;}
-action_name *ActionNameLists::action(anl_entry *entry) {
+action_name *ActionNameLists::action(anl_entry *entry) {if (entry) returnentry->item.action_listed;returnNULL;}
@@ -392,7 +392,7 @@ negation) but not "doing something other than looking".
-intActionNameLists::covers_action(action_name_list *list, action_name *an) {
+intActionNameLists::covers_action(action_name_list *list, action_name *an) {LOOP_THROUGH_ANL(entry, list) {anl_item *item = &(entry->item);intwithin = FALSE;
@@ -437,17 +437,17 @@ the text leading to a list:
entry->parsing_data.abbreviation_level = 0;}
-intActionNameLists::parc(anl_entry *entry) {
+intActionNameLists::parc(anl_entry *entry) {if (entry) returnentry->parsing_data.parc;return0;}
-wordingActionNameLists::par(anl_entry *entry, inti) {
+wordingActionNameLists::par(anl_entry *entry, inti) {if ((entry) && (entry->parsing_data.parc > i)) returnentry->parsing_data.parameter[i];returnEMPTY_WORDING;}
-wordingActionNameLists::in_clause(anl_entry *entry) {
+wordingActionNameLists::in_clause(anl_entry *entry) {if (entry) returnentry->parsing_data.in_clause;returnEMPTY_WORDING;}
@@ -466,17 +466,17 @@ the text leading to a list:
returnentry;}
-intActionNameLists::first_position(action_name_list *list) {
+intActionNameLists::first_position(action_name_list *list) {if (list) returnActionNameLists::word_position(list->entries);return -1;}
-intActionNameLists::word_position(anl_entry *entry) {
+intActionNameLists::word_position(anl_entry *entry) {if (entry) returnentry->parsing_data.word_position;return -1;}
-intActionNameLists::same_word_position(anl_entry *entry, anl_entry *Y) {
+intActionNameLists::same_word_position(anl_entry *entry, anl_entry *Y) {if ((entry) && (Y) && (entry->parsing_data.word_position == Y->parsing_data.word_position))returnTRUE;returnFALSE;
@@ -487,7 +487,7 @@ the text leading to a list:
-action_name *ActionNameLists::single_positive_action(action_name_list *list) {
+action_name *ActionNameLists::single_positive_action(action_name_list *list) {if ((ActionNameLists::length(list) == 1) && (ActionNameLists::positive(list)))returnActionNameLists::first_item(list)->action_listed;
@@ -518,7 +518,7 @@ is no best action. (For example, in "throwing or removing something".)
-action_name *ActionNameLists::get_best_action(action_name_list *list) {
+action_name *ActionNameLists::get_best_action(action_name_list *list) {intposn = -1, best_score = -1;action_name *best = NULL;if (ActionNameLists::positive(list) == FALSE) returnNULL;
@@ -629,7 +629,7 @@ something other than something — or intanl_parsing_tense = IS_TENSE;
-action_name_list *ActionNameLists::parse(wordingW, inttense, int *sense) {
+action_name_list *ActionNameLists::parse(wordingW, inttense, int *sense) {if (Wordings::mismatched_brackets(W)) returnNULL;intt = anl_parsing_tense;anl_parsing_tense = tense;
@@ -918,7 +918,7 @@ the trial entry for future trials.
diff --git a/docs/if-module/4-ann.html b/docs/if-module/4-ann.html
index f8f787bb5..de07d1361 100644
--- a/docs/if-module/4-ann.html
+++ b/docs/if-module/4-ann.html
@@ -304,7 +304,7 @@ number of words.
-action_name *ActionNameNames::longest_nounless(wordingW, inttense, int *posn) {
+action_name *ActionNameNames::longest_nounless(wordingW, inttense, int *posn) {action_name *an;LOOP_OVER(an, action_name)if (ActionSemantics::can_have_noun(an) == FALSE) {
@@ -318,7 +318,7 @@ number of words.
}
diff --git a/docs/if-module/4-ap.html b/docs/if-module/4-ap.html
index 8b54701e3..c37108e42 100644
--- a/docs/if-module/4-ap.html
+++ b/docs/if-module/4-ap.html
@@ -487,7 +487,7 @@ to nothing".
}
diff --git a/docs/if-module/4-ap2.html b/docs/if-module/4-ap2.html
index f03a32b18..15951e416 100644
--- a/docs/if-module/4-ap2.html
+++ b/docs/if-module/4-ap2.html
@@ -156,7 +156,7 @@ of the text they came from; for which, see structtime_period *duration; to refer to repetitions in the past} action_pattern;
-
§4. Unlike most data structures in the compiler, APs can churn quickly into
and out of existence on the stack during parsing, and so the following
directly returns a struct, rather than allocating a permanent object and
@@ -164,7 +164,7 @@ returning only a pointer to it.
-action_patternActionPatterns::new(wordingW) {
+action_patternActionPatterns::temporary(wordingW) {action_patternap;ap.ap_clauses = NULL;ap.text_of_pattern = W;
@@ -178,20 +178,25 @@ returning only a pointer to it.
-voidActionPatterns::log(action_pattern *ap) {
+voidActionPatterns::log(action_pattern *ap) {ActionPatterns::write(DL, ap);}
-voidActionPatterns::write(OUTPUT_STREAM, action_pattern *ap) {
+voidActionPatterns::write(OUTPUT_STREAM, action_pattern *ap) {if (ap == NULL) WRITE("<null-ap>");elseif (ap->parameter_kind) {WRITE("<parametric: ");
@@ -207,7 +212,21 @@ returning only a pointer to it.
}}
-
§7. Access to the actions mentioned:
+
§7. This simple parser converts text to a parametric AP of kind K. The parser
+for action-based APs is very much more complicated: see Parse Action Patterns.
+
The structure ap_clause is accessed in 2/ri, 3/tm, 3/scn, 3/ts, 4/ap, 4/av, 4/ap2, 4/gng, 4/pap, 4/ea, 5/tfg, 5/gl and here.
+
The structure ap_clause is accessed in 2/ri, 3/tm, 3/scn, 3/ts, 4/ap, 4/av, 4/ap2, 4/gng, 4/pc, 4/ea, 5/tfg, 5/gl and here.
§3. This loop conveniently runs through the clauses for ap:
@@ -203,7 +203,7 @@ position), depending on whether
-ap_clause *APClauses::clause(action_pattern *ap, intC) {
+ap_clause *APClauses::clause(action_pattern *ap, intC) {returnAPClauses::find_clause(ap, C, FALSE);}
@@ -217,12 +217,12 @@ almost, because there could also be options set on it.
-parse_node *APClauses::spec(action_pattern *ap, intC) {
+parse_node *APClauses::spec(action_pattern *ap, intC) {ap_clause *apoc = APClauses::clause(ap, C);return (apoc)?(apoc->clause_spec):NULL;}
-voidAPClauses::set_spec(action_pattern *ap, intC, parse_node *val) {
+voidAPClauses::set_spec(action_pattern *ap, intC, parse_node *val) {if (val == NULL) {ap_clause *apoc = APClauses::clause(ap, C);if (apoc) apoc->clause_spec = val;
@@ -237,7 +237,7 @@ makes sense in a given clause:
§18. Specificity. See ActionPatterns::compare_specificity, which calls this to look at clauses.
The code here looks innocent enough but has significant implications for rule
sorting.
diff --git a/docs/if-module/4-as.html b/docs/if-module/4-as.html
index f88d12a36..9ad41fbed 100644
--- a/docs/if-module/4-as.html
+++ b/docs/if-module/4-as.html
@@ -185,12 +185,12 @@ preferred way to do that is to use activities for selecting missing parameters.
-intActionSemantics::can_have_noun(action_name *an) {
+intActionSemantics::can_have_noun(action_name *an) {if (an->semantics.max_parameters >= 1) returnTRUE;returnFALSE;}
-intActionSemantics::can_have_second(action_name *an) {
+intActionSemantics::can_have_second(action_name *an) {if (an->semantics.max_parameters >= 2) returnTRUE;returnFALSE;}
@@ -214,7 +214,7 @@ preferred way to do that is to use activities for selecting missing parameters.
returnFALSE;}
-intActionSemantics::is_out_of_world(action_name *an) {
+intActionSemantics::is_out_of_world(action_name *an) {if (an->semantics.out_of_world) returnTRUE;returnFALSE;}
@@ -227,11 +227,11 @@ preferred way to do that is to use activities for selecting missing parameters.
returnan->semantics.second_access;}
-kind *ActionSemantics::kind_of_noun(action_name *an) {
+kind *ActionSemantics::kind_of_noun(action_name *an) {returnan->semantics.noun_kind;}
-kind *ActionSemantics::kind_of_second(action_name *an) {
+kind *ActionSemantics::kind_of_second(action_name *an) {returnan->semantics.second_kind;}
@@ -356,7 +356,7 @@ clear from the implementation in }
diff --git a/docs/if-module/4-av.html b/docs/if-module/4-av.html
index 136c4eccc..b1d83f05f 100644
--- a/docs/if-module/4-av.html
+++ b/docs/if-module/4-av.html
@@ -297,12 +297,12 @@ action patterns. For example, the Standard Rules define:
diff --git a/docs/if-module/4-ea.html b/docs/if-module/4-ea.html
index 62d4d078d..7b9e94512 100644
--- a/docs/if-module/4-ea.html
+++ b/docs/if-module/4-ea.html
@@ -118,7 +118,7 @@ action (example: "taking") or it does specify them, but too vaguely (example:
diff --git a/docs/if-module/4-gng.html b/docs/if-module/4-gng.html
index 344b34f7d..d0e1fac9e 100644
--- a/docs/if-module/4-gng.html
+++ b/docs/if-module/4-gng.html
@@ -150,7 +150,7 @@ a special ID number of our choice.
§12. Each clause can be within one of up to two kinds, or else can be "nothing"
@@ -358,7 +358,7 @@ code if ap1 has
}
diff --git a/docs/if-module/4-nap.html b/docs/if-module/4-nap.html
index 477ae66fb..e6ffd02e3 100644
--- a/docs/if-module/4-nap.html
+++ b/docs/if-module/4-nap.html
@@ -172,13 +172,13 @@ if and only if it appears in one of the patterns in the list:
named_action_pattern_entry *nape;if (nap)LOOP_OVER_LINKED_LIST(nape, named_action_pattern_entry, nap->patterns)
-if (ActionPatterns::covers_action(nape->behaviour, an))
+if (ActionPatterns::covers_action(nape->behaviour, an))returnTRUE;returnFALSE;}
diff --git a/docs/if-module/4-pap.html b/docs/if-module/4-pap.html
index 813bc2152..d5c9d8cec 100644
--- a/docs/if-module/4-pap.html
+++ b/docs/if-module/4-pap.html
@@ -20,6 +20,11 @@ function togglePopup(material_id) {
+
+
+
+
@@ -73,268 +78,298 @@ function togglePopup(material_id) {
§1. Failure reasons. Action patterns are complicated to parse and text can fail to match for many
+different reasons, so that it can be hard to give a helpful problem message if
+the author gets it wrong. (We can't simply fire off errors at the time they
+occur, because text is often parsed in several contexts at once, so just
+because it fails this one does not mean it is wrong.) To improve our chances,
+the code below sets the following global variable on each failure.
§2. When we parse action patterns, we record why they fail, in order to make
-it easier to produce helpful error messages. (We can't simply fire off
-errors at the time they occur, because text is often parsed in several
-contexts at once, so just because it fails this one does not mean it is
-wrong.) PAPF stands for "parse action pattern failure".
-
intpap_failure_reason; one of the above
-intpermit_trying_omission = FALSE; allow the keyword 'trying' to be omitted
-intpermit_nonconstant_action_parameters = TRUE;
-
§3. NB: Next time this is rewritten - (1) handle in, in the presence of, with
-STV clauses; (2) get this right:
+
§2. Global modes. The parser is not contextless, and in particular can run in several globally
+set modes:
-
The Rocky Promontory by the Waterfall is a room.
-
+
● When we PERMIT_TRYING_OMISSION, we allow "Ganatus going east" as well as the
+more cumbersome "Ganatus trying going east".
+
● When we FORBID_NONCONSTANT_ACTION_PARAMETERS, we disallow the use of local
+or global variables in action patterns.
+
● When SCANNING_ANL_ONLY, we do not perform a full parse, but only enough to
+get as far as the action name list.
+
● When we SUPPRESS_AP_PARSING, the nonterminal <action-pattern-core> is
+rigged always to fail.
+
§4. First a much easier, parametric form of parsing, used for the APs which
-form the usage conditions for rules in object-based rulebooks.
+intParseActionPatterns::exit_mode(intpm) {
+intwas = parse_action_pattern_mode;
+if (parse_action_pattern_mode & pm)
+parse_action_pattern_mode -= pm;
+returnwas;
+}
+
+voidParseActionPatterns::restore_mode(intsaved) {
+parse_action_pattern_mode = saved;
+}
+
§5. A useful utility: parsing a parameter in an action pattern.
+
§5. Extracting only the action name list. This might seem redundant, since surely we could just parse the text to an AP
+in the ordinary way and then take its action list. But going into SCANNING_ANL_ONLY
+mode enables us to ignore the text used as parameters; which means that we can
+get the list even if we are parsing early in Inform's run, when such text may
+still be incomprehensible.
-parse_node *ParseActionPatterns::parameter(wordingW) {
-if (<action-parameter>(W)) return<<rp>>;
-returnSpecifications::new_UNKNOWN(W);
-}
-
-parse_node *ParseActionPatterns::verified_action_parameter(wordingW) {
-parse_node *spec = ParseActionPatterns::parameter(W);
-if (Node::is(spec, UNKNOWN_NT)) {
-Problems::quote_source(1, current_sentence);
-Problems::quote_wording(2, W);
-StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_BadOptionalAPClause));
-Problems::issue_problem_segment(
-"In %1, I tried to read a description of an action - a complicated "
-"one involving optional clauses; but '%2' wasn't something I "
-"recognised.");
-Problems::issue_problem_end();
- }
-returnspec;
-}
-
§7. The main action pattern parser is called only by the following shell
-routine, which exists in order to change some parsing rules.
+
§6. Level One. This is where an action pattern is wrapped up as a test of a condition: see
+Action Conditions for more on this. The nonterminals here have no
+return code, and have return value set to the parse_node for the condition,
+so that they can be used in the S-parser.
-
Match "doing it" as a repetition of the previous successfully
-matched action pattern.
+
There are two forms of this: the first is for contexts where the AP might
+occur as a noun ("Taking a jewel is felonious behaviour."). This makes sense
+only in the present tense, and no "we are" or "we have" prefixes are allowed.
+
+
+
To see why this case is not like the others at Level One, imagine a story
+where there is an action "setting", something called a "meter", and also a
+value called the "meter setting".1 Clearly the text "meter trying setting"
+would be unambiguous, but if we allow "trying" to be omitted then there are
+two possible readings of "meter setting" as a noun:
+
+
+
● the obvious one to a human reader, i.e., the value of the meter setting;
+
● the action in which the meter is performing a setting.
+
+
We reject the second option only by testing the actor to make sure it is a
+person: for something inanimate like the meter, it is not.
+
+
+
1 If that's too much of a stretch for the imagination, see the documentation
+example "Witnessed 2", test case Witnessed.
+ ↩
§7. The second form is for contexts where the AP occurs as a condition: e.g.,
+in a sentence like "if we have taken a jewel". Since these can occur in
+both tenses and can be negated ("if we are not taking a jewel"), there are
+four combinations.
+
§8. Level Two. The five s-nonterminals of Level One hand decisions down to five corresponding
+nonterminals here. For each of these, the return code is one of the following
+five values, and the return pointer is the action_pattern structure made.
+Our aim here is to determine who will perform the action.
+
§9. Action patterns are textual descriptions which act as predicates on actions,
-that is, they are descriptions which are true of some actions and false of
-others. For example,
+
won't be true of taking the ball in the Beach, or of dropping the torch in the
-Cellars. Although precisely described actions are valid as APs:
-
-
-
-
taking the beach ball
-
-
-
(which is true for this one action and false for all others), APs can be both
-more general — as above — and even more specific:
-
-
-
-
taking the beach ball in the presence of a lifeguard
-
-
-
...which might not be true even if the current action is "taking the beach
-ball".
-
-
-
APs can be very flexible and have the most complicated syntax in Inform. It's
-not practical to make the Preform grammar as explicit as one might like, but
-we'll do our best. The top level establishes who the actor will be, and whether
-it is an actual action or merely a request to perform the action. There are
-two versions of this: the first is for contexts where the AP might occur as
-a noun (e.g., in a sentence like "Taking a jewel is felonious behaviour.").
-These are always present tense, and can't be negated.
-
§10. The second version is for contexts where the AP occurs as a condition: e.g.,
-in a sentence like "if we have taken a jewel". Since these can occur in
-both tenses and can be negated ("if we are not taking a jewel"), there are
-four combinations:
+
§9. Note that the three present-tense cases all allow the abbreviated form
+"Raffles taking a jewel" rather than the less likely to be ambiguous "Raffles
+trying taking a jewel". This is allowed only in PERMIT_TRYING_OMISSION mode,
+and makes use of the following voracious nonterminal to match the actor's
+name — here, just "Raffles".
§11. There is one more tweak at this top level. Inform allows an ambiguous but
-shorter and more natural syntax in which the actor's name simply appears at
-the front of the AP:
-
-
-
-
Raffles taking a jewel
-
-
-
Here there are no textual markers like "trying" to separate the actor's
-name ("Raffles") from the action itself ("taking a jewel"), and all
-we can do is search out possibilities. If it's possible to match the action
-without an initial actor name, that takes priority, to ensure that this
-actorless possibility can always be written.
-
§12. And this voracious token matches the actor's name as an initial excerpt,
-which is much faster than exhaustive searching. It tries to break just before
-any "-ing" word (i.e., participle) which is not inside parentheses; but only
-if the resulting name matches <action-parameter> as a constant,
-variable, or description; and there is no match if the text is the name of an
-instance but the "-ing" word could also be read as part of that same name.
-For example, if we read the text
+
The following tries to break just before any "-ing" word (i.e., participle)
+which is not inside parentheses; but only if the resulting name matches
+<s-ap-parameter> as a constant, variable, or description; and there is no
+match if the text is the name of an instance but the "-ing" word could also be
+read as part of that same name. For example, if we read the text
@@ -348,7 +383,7 @@ would match as an abbreviated form of the name of "angry waiting man".
<actor-description>internal?{
-if (permit_trying_omission) {
+if (parse_action_pattern_mode & PERMIT_TRYING_OMISSION) {intbl = 0;LOOP_THROUGH_WORDING(i, W)if (i > Wordings::first_wn(W)) {
@@ -358,9 +393,9 @@ would match as an abbreviated form of the name of "angry waiting man".
if (<k-kind>(Wordings::up_to(W, i-1))) continue;parse_node *try_stem = NULL;instance *I;
-intold_state = ParseActionPatterns::suppress();
-if (<action-parameter>(Wordings::up_to(W, i-1))) try_stem = <<rp>>;
-ParseActionPatterns::resume(old_state);
+intold_state = ParseActionPatterns::enter_mode(SUPPRESS_AP_PARSING);
+if (<s-ap-parameter>(Wordings::up_to(W, i-1))) try_stem = <<rp>>;
+ParseActionPatterns::restore_mode(old_state);intk = 0;LOOP_THROUGH_WORDING(j, Wordings::up_to(W, i-1))if (Vocabulary::test_flags(j, ACTION_PARTICIPLE_MC)) k++;
@@ -384,140 +419,126 @@ would match as an abbreviated form of the name of "angry waiting man".
}
§14. That completes the top level, and we can forget about actors. All of those
-productions come down now to just two nonterminals, one for the present tense,
+
§10. Level Three. We can forget about actors, and the above five cases reduce to only two, one
+for each tense we support.
-
-
taking or dropping a container
-
-
-
and one for the past,
-
-
-
-
taken or dropped a container
-
-
-
These are written as internals so that they can set a flag to change the
-current tense as appropriate, but they don't otherwise do much:
-
-
-
(a) They trim away an indication of duration using <historical-reference>, so
-that, e.g., "taking the box for the third time" has "for the third time"
-trimmed away;
-
(b) They match <action-pronominal> as the most recently parsed action pattern;
-
(c) But otherwise they hand over to <ap-common-core> to do the work.
-
§15. "Doing it" is not the happiest of syntaxes. The idea is for this to be
-a sort of pronoun for actions, allowing for anaphora, but to parse such things
-naturally in all cases is wishful thinking. It enables us to write, e.g.:
+
§11. Other than merging the tenses into one code path, all we do at this level
+is to look for any indication of duration. For example, "taking the box for the
+third time" has "for the third time" trimmed away, and "taking the box" is
+what passes down to Level Four.
-
-
Instead of Peter taking the box for the second time, try Jane doing it.
-
-
-
where "doing it" will refer to "taking the box". But I wonder if the
-possibility for confusion is too great; perhaps we should just cut this idea.
-
§17. Anyway, we are now down to level 3: all action patterns have been whittled
-down to a single use of <ap-common-core>. Our next step is to recognise
-a condition attached with "when":
+
§11.1. This saves a huge amount of time, since virtually any text gets through Levels
+One and Two down to here. "Lady Eustace's Diamonds" cannot be an action pattern
+since it contains no words which are part of an action (or named action pattern)
+name; so we needn't spend any further time.
+
+
+
There has to be a participle here somewhere11.1 =
+
§12. Level Four. This level deals only with the pronominal action "doing it", an anaphora which
+refers by implication to whatever action pattern was previously discussed before
+this one. Parsing this accurately is a fool's errand, and allowing this syntax
+in Inform was not a good idea, because the potential for confusion is too great.
+That said, it does enable, for example, the cool rule:
+
+
+
+
Instead of Raffles taking the box for the second time, try Bunny doing it.
§14. Lark's Tongues in Aspic, Part V. Down here at Level Five, everything has funnelled into a single nonterminal.
+Its task is to recognise a condition attached with "when", which goes into a
+special clause of its own.
-<ap-common-core>::=
-<ap-common-core-inner>when/while<condition-in-ap>|==> { 0, RP[1] }; action_pattern *ap = *XP; APClauses::set_spec(ap, WHEN_AP_CLAUSE, RP[2]); if (pap_failure_reason == MISC_PAPF) pap_failure_reason = WHENOKAY_PAPF;
-<ap-common-core-inner>|==> { 0, RP[1] };
-...when/while<condition-in-ap>|==> { 0, NULL }; pap_failure_reason = WHENOKAY_PAPF; return FALSE; used only to diagnose problems
-...when/while...==> { 0, NULL }; if (pap_failure_reason != WHENOKAY_PAPF) pap_failure_reason = WHEN_PAPF; return FALSE; used only to diagnose problems
+<ap-five>::=
+<ap-six>when/while<ap-five-condition>|==> { 0, RP[1] }; action_pattern *ap = *XP; APClauses::set_spec(ap, WHEN_AP_CLAUSE, RP[2]); if (pap_failure_reason == MISC_PAPF) pap_failure_reason = WHENOKAY_PAPF;
+<ap-six>|==> { 0, RP[1] };
+...when/while<ap-five-condition>|==> { 0, NULL }; pap_failure_reason = WHENOKAY_PAPF; return FALSE; used only to diagnose problems
+...when/while...==> { 0, NULL }; if (pap_failure_reason != WHENOKAY_PAPF) pap_failure_reason = WHEN_PAPF; return FALSE; used only to diagnose problems
§18. <condition-in-ap> is really just <spec-condition> in disguise — i.e.,
+
§15. <ap-five-condition> is really just <s-condition> in disguise — i.e.,
it matches a standard Inform condition — but it's implemented as an internal
to enable Inform to set up a stack frame if there isn't one already, and so on.
-<condition-in-ap>internal{
+<ap-five-condition>internal{ph_stack_frame *phsf = NULL;if (Frames::current_stack_frame() == NULL) phsf = Frames::new_nonphrasal();StackedVariables::append_owner_list(
@@ -526,11 +547,10 @@ to enable Inform to set up a stack frame if there isn't one already, and so on.
LOGIF(ACTION_PATTERN_PARSING, "A when clause <%W> is suspected.\n", W);parse_node *wts = NULL;ints = pap_failure_reason;
-intpto = permit_trying_omission;
-permit_trying_omission = FALSE;
+intsaved = ParseActionPatterns::exit_mode(PERMIT_TRYING_OMISSION);if (<s-condition>(W)) wts = <<rp>>;pap_failure_reason = s;
-permit_trying_omission = pto;
+ParseActionPatterns::restore_mode(saved);if (phsf) Frames::remove_nonphrase_stack_frame();if ((wts) && (Dash::validate_conditional_clause(wts))) {LOGIF(ACTION_PATTERN_PARSING, "When clause validated: $P.\n", wts);
@@ -541,64 +561,56 @@ to enable Inform to set up a stack frame if there isn't one already, and so on.
}
§20. Level 5 now. The initial "in" clause, e.g., "in the Pantry", requires
-special handling to prevent it from clashing with other interpretations of
-"in" elsewhere in the grammar. It's perhaps unexpected that "in the Pantry"
-is valid as an AP, but this enables many natural-looking rules to be written
-("Report rule in the Pantry: ...", say).
+
§17. Level Seven. The initial "in" clause, e.g., "in the Pantry", requires special handling to
+prevent it from clashing with other interpretations of "in" elsewhere in the
+grammar.
§21. And that's as far down as we go: to level 6. Most of the complexity is gone
-now, but what's left can't very efficiently be written in Preform. Essentially
-we apply <action-list> to the text and then parse the operands using
-<action-operand>, though it's a bit more involved because we also recognise
-optional suffixes special to individual actions, like the "from the cage" in
-"exiting from the cage", and we fail the result if it produces
-inconsistencies between alternative actions (e.g., "taking or waiting the
-box" makes no sense since only one is transitive).
+
§18. Level Eight. Much of the complexity is gone now, but much potential ambiguity remains, and
+so what's left can't very efficiently be written in Preform.
§22. The "operands" of an action pattern are the nouns to which it applies: for
-example, in "Kevin taking or dropping something", the operand is "something".
-We treat words like "something" specially to avoid them being read as
-"some thing" and thus forcing the kind of the operand to be "thing".
-
§23. Finally, then, <action-parameter>. Almost anything syntactically matches
-here — a constant, a description, a table entry, a variable, and so on.
-
§24.5. Now to fill in the gaps. At this point we have the action name
-list as a linked list of all possible lexical matches, but want to
-whittle it down to remove those which do not semantically make
-sense. For instance, "taking inventory" has two possible lexical
-matches: "taking inventory" with 0 parameters, or "taking" with
-1 parameter "inventory", and we cannot judge without parsing
-the expression "inventory". The list passes muster if at least
-one match succeeds at the first word position represented in the
-list, which is to say the last one lexically, since the list is
-reverse-ordered. (This is so that "taking or dropping something"
-requires only "dropping" to have its objects specified; "taking",
-of course, does not.) We delete all entries in the list at this
-crucial word position except for the one matched.
-
§24.5.1. For example, "taking inventory or waiting" produces two positions, words
-0 and 3, and minimum parameter count 0 in each case. ("Taking inventory"
-can be read as "taking (inventory)", par-count 1, or "taking inventory",
-par-count 0, so the minimum is 0.)
-
-
-
Find the positions of individual action names, and their minimum parameter counts24.5.1 =
-
§24.5.3. The following test is done to reject patterns like "taking ball or dropping
-bat", which have a positive minimum parameter count in more than one position;
-which means there couldn't be an action pattern which shared the same noun
-description.
-
-
-
Find how many different positions have each possible minimum count24.5.3 =
-
§24.6. Not all actions can cohabit. We require that as far as the user has
-specified the parameters, the actions in the list must all agree (i) to be
-allowed to have such a parameter, and (ii) to be allowed to have a
-parameter of the same type. Thus "waiting or taking something" fails
-(waiting takes 0 parameters, but we specified one), and so would "painting
-or taking something" if painting had to be followed by a colour, say. Note
-that the "doing anything" action is always allowed a parameter (this is
-the case when the first action name in the list is NULL).
-
§1.5. Now to fill in the gaps. At this point we have the action name
+list as a linked list of all possible lexical matches, but want to
+whittle it down to remove those which do not semantically make
+sense. For instance, "taking inventory" has two possible lexical
+matches: "taking inventory" with 0 parameters, or "taking" with
+1 parameter "inventory", and we cannot judge without parsing
+the expression "inventory". The list passes muster if at least
+one match succeeds at the first word position represented in the
+list, which is to say the last one lexically, since the list is
+reverse-ordered. (This is so that "taking or dropping something"
+requires only "dropping" to have its objects specified; "taking",
+of course, does not.) We delete all entries in the list at this
+crucial word position except for the one matched.
+
§1.5.1. For example, "taking inventory or waiting" produces two positions, words
+0 and 3, and minimum parameter count 0 in each case. ("Taking inventory"
+can be read as "taking (inventory)", par-count 1, or "taking inventory",
+par-count 0, so the minimum is 0.)
+
+
+
Find the positions of individual action names, and their minimum parameter counts1.5.1 =
+
§1.5.3. The following test is done to reject patterns like "taking ball or dropping
+bat", which have a positive minimum parameter count in more than one position;
+which means there couldn't be an action pattern which shared the same noun
+description.
+
+
+
Find how many different positions have each possible minimum count1.5.3 =
+
§1.6. Not all actions can cohabit. We require that as far as the user has
+specified the parameters, the actions in the list must all agree (i) to be
+allowed to have such a parameter, and (ii) to be allowed to have a
+parameter of the same type. Thus "waiting or taking something" fails
+(waiting takes 0 parameters, but we specified one), and so would "painting
+or taking something" if painting had to be followed by a colour, say. Note
+that the "doing anything" action is always allowed a parameter (this is
+the case when the first action name in the list is NULL).
+
§3. The "operands" of an action pattern are the nouns to which it applies: for
+example, in "Kevin taking or dropping something", the operand is "something".
+We treat words like "something" specially to avoid them being read as
+"some thing" and thus forcing the kind of the operand to be "thing".
+
+ Parse Clauses -
+ Parsing the clauses part of an AP from source text.
+
Explicit Actions -
diff --git a/docs/imperative-module/3-pu.html b/docs/imperative-module/3-pu.html
index f495a7cd6..3f9579420 100644
--- a/docs/imperative-module/3-pu.html
+++ b/docs/imperative-module/3-pu.html
@@ -888,22 +888,22 @@ seen problems in Inform. A couple of variables are needed just for that:
#ifdefIF_MODULEif (Rulebooks::focus(phud->owning_rulebook) == ACTION_FOCUS) {
-permit_trying_omission = TRUE;
+intsaved = ParseActionPatterns::enter_mode(PERMIT_TRYING_OMISSION);Frames::set_stvol(Frames::current_stack_frame(), all_action_processing_vars);if (<action-pattern>(phud->rule_parameter)) phrcd.ap = <<rp>>;Frames::remove_nonphrase_stack_frame();
-permit_trying_omission = FALSE;
+ParseActionPatterns::restore_mode(saved);if (phrcd.ap == NULL)Issue a problem message for a bad action20.1.1.1; } else {kind *pk = Rulebooks::get_parameter_kind(phud->owning_rulebook);
-phrcd.ap = ParseActionPatterns::parametric(phud->rule_parameter, pk);
+phrcd.ap = ActionPatterns::parse_parametric(phud->rule_parameter, pk);if (phrcd.ap == NULL) {if (Wordings::nonempty(phud->whenwhile)) {wordingF = Wordings::up_to(phud->rule_parameter, Wordings::last_wn(phud->whenwhile));
-phrcd.ap = ParseActionPatterns::parametric(F, pk);
+phrcd.ap = ActionPatterns::parse_parametric(F, pk);if (phrcd.ap) {phud->rule_parameter = F;phud->whenwhile = EMPTY_WORDING;
diff --git a/docs/imperative-module/6-pi.html b/docs/imperative-module/6-pi.html
index 1ffff7820..c2ebf9e34 100644
--- a/docs/imperative-module/6-pi.html
+++ b/docs/imperative-module/6-pi.html
@@ -488,18 +488,18 @@ parser needs our help in the first place.)
parse_node *as_parsed = Invocations::get_token_as_parsed(inv, i);wordingXW = Node::get_text(as_parsed); #ifdefIF_MODULE
-intpto = permit_trying_omission;
+intsaved = ParseActionPatterns::enter_mode(0);if ((Specifications::is_kind_like(to_match)) && (Kinds::eq(Specifications::to_kind(to_match), K_stored_action))) {
-permit_trying_omission = TRUE;
+ParseActionPatterns::enter_mode(PERMIT_TRYING_OMISSION);Parse the action in a try phrase5.1; } else {
-permit_trying_omission = FALSE;
+ParseActionPatterns::exit_mode(PERMIT_TRYING_OMISSION); #endifParse any other token5.2; #ifdefIF_MODULE }
-permit_trying_omission = pto;
+ParseActionPatterns::restore_mode(saved); #endifNode::set_text(as_parsed, XW);Invocations::set_token_as_parsed(inv, i, as_parsed);
@@ -514,7 +514,7 @@ parser needs our help in the first place.)
if (<action-pattern>(XW))as_parsed = AConditions::new_action_TEST_VALUE(<<rp>>, XW);else {
-permit_trying_omission = FALSE;
+ParseActionPatterns::exit_mode(PERMIT_TRYING_OMISSION);Parse any other token5.2; }
diff --git a/docs/runtime-module/4-itc.html b/docs/runtime-module/4-itc.html
index 28e64ac5c..360b4b22e 100644
--- a/docs/runtime-module/4-itc.html
+++ b/docs/runtime-module/4-itc.html
@@ -578,7 +578,7 @@ only and may change at any time without notice.
action_pattern *InternalTests::ap_of_nap(action_pattern *ap, wordingW) {named_action_pattern *nap = NamedActionPatterns::add(ap, W);
-action_pattern *new_ap = ActionPatterns::perpetuate(ActionPatterns::new(W));
+action_pattern *new_ap = ActionPatterns::new(W);anl_entry *entry = ActionNameLists::new_entry_at(W);entry->item.nap_listed = nap;new_ap->action_list = ActionNameLists::new_list(entry, ANL_POSITIVE);
@@ -639,7 +639,9 @@ only and may change at any time without notice.
for (inti=0; i<10; i++) ap_test_register[i] = NULL; }Begin reporting on the internal test case4.3; Streams::enable_I6_escapes(DL);
+intsaved = ParseActionPatterns::enter_mode(PERMIT_TRYING_OMISSION);<perform-ap-test>(itc->text_supplying_the_case);
+ParseActionPatterns::restore_mode(saved);Streams::disable_I6_escapes(DL); End reporting on the internal test case4.4;
diff --git a/docs/syntax-module/2-pn.html b/docs/syntax-module/2-pn.html
index e777750dd..aff572e4b 100644
--- a/docs/syntax-module/2-pn.html
+++ b/docs/syntax-module/2-pn.html
@@ -429,14 +429,13 @@ so that this code won't be used again on the same horizontal list of possibiliti
§18. All of those routines make use of the following, which actually performs
-the log of a parse node. Note that this always produces exactly one line of
-text in the debugging log.
+the log of a parse node.
§1. Conditions. In Inform syntax, a condition is an excerpt of text which measures the truth of
something. We will call it "pure" if it is self-sufficient, rather than
@@ -232,7 +232,7 @@ testing the existence of something.
parse_node *p = Lexicon::retrieve(COND_PHRASE_MC, W);if (p) {parse_node *spec = Node::new_with_words(PHRASE_TO_DECIDE_VALUE_NT, W);
-SPCond::add_ilist(spec, p);
+SPCond::add_ilist(spec, p);parse_node *tval = Node::new_with_words(TEST_VALUE_NT, W);tval->down = spec; ==> { -, tval };
@@ -259,60 +259,7 @@ being compiled; all others are out of scope.
}
§8.1. Convert stored past action pattern to condition node8.1 =
-
-
-
- #ifdefIF_MODULE
-action_pattern *ap = RP[1];
-if (ActionPatterns::makes_callings(ap)) {
-StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_PastActionCalled),
-"a description of an action cannot both refer to past "
-"history and also use '(called ...)'",
-"because that would require Inform in general to remember "
-"too much information about past events.");
-returnFALSE;
- }
-parse_node *C = AConditions::new_action_TEST_VALUE(ap, W);
-C = Conditions::attach_tense(C, HASBEEN_TENSE);
- ==> { -, C };
- #endif
- #ifndefIF_MODULE
-returnFALSE;
- #endif
-
§9. Command phrases. The final clutch of nonterminals in the S-grammar handles individual commands,
+
§7. Command phrases. The final clutch of nonterminals in the S-grammar handles individual commands,
written in their semicolon-divided list in the body of a rule or "To..."
definition. For instance, in the not very sensible rule:
@@ -342,8 +289,8 @@ typechecking to choose between much later on.
<adaptive-verb>verb|==> { -, SPCond::say_verb(RP[1], R[1], NULL, W) }<adaptive-adjective>adjective|==> { -, SPCond::say_adjective(RP[1], W) }<adaptive-verb>|==> { -, SPCond::say_verb(RP[1], R[1], NULL, W) }
-<modal-verb><adaptive-verb-infinitive>verb|==>Annotate the verb with a modal9.3
-<modal-verb><adaptive-verb-infinitive>|==>Annotate the verb with a modal9.3
+<modal-verb><adaptive-verb-infinitive>verb|==>Annotate the verb with a modal7.3
+<modal-verb><adaptive-verb-infinitive>|==>Annotate the verb with a modal7.3<adaptive-adjective>==> { -, SPCond::say_adjective(RP[1], W) }<adaptive-adjective>internal{
@@ -362,7 +309,7 @@ typechecking to choose between much later on.
}
§9.1. "To..." phrases are easy, or at least, easy to delegate:
+
§7.1. "To..." phrases are easy, or at least, easy to delegate:
@@ -370,7 +317,7 @@ typechecking to choose between much later on.
parse_node *p = Lexicon::retrieve(VOID_PHRASE_MC, W);if (p) {parse_node *spec = Node::new_with_words(PHRASE_TO_DECIDE_VALUE_NT, W);
-SPCond::add_ilist(spec, p);
+SPCond::add_ilist(spec, p); ==> { -, spec };returnTRUE; }
@@ -378,14 +325,14 @@ typechecking to choose between much later on.
}
§12. There are three basic kinds of phrase: those used as commands
+
§10. There are three basic kinds of phrase: those used as commands
(i.e., void procedures in C terms), those used as values (returning values
other than true/false) and those used as conditions (returning true or false).
These are stored in a way making basically the same use of a specification's
@@ -447,21 +394,21 @@ possibilities will all be invoked.
§10.2. This problem used to be experienced for long say phrases in a situation
where many kinds of value have been created, so that "say V" for a value V
was heavily ambiguous — pumping up the number of invocations generated. In
2010, the introduction of generics into Inform made it possible to define
@@ -489,7 +436,7 @@ was heavily ambiguous — pumping up the number of invocations generated. In
limit that we were unable to construct a test case for it.
-
Issue overcomplicated phrase problem message12.2 =
+
Issue overcomplicated phrase problem message10.2 =
@@ -508,7 +455,7 @@ limit that we were unable to construct a test case for it.
"original phrase.");Problems::issue_problem_end();