+

To create and subsequently parse against the list of phrase options with which the user can choose to invoke a To phrase.

-
+
-

§1. A "phrase option" is a sort of modifier tacked on to the instruction to -do something, changing how it works but not enough to merit an entirely new -phrase. It's like an argument passed to a routine which specifies optional -behaviour, and indeed that will be how it is compiled. +

§1. Introduction. A "phrase option" is a sort of modifier tacked on to an invocation of a +phrase; the only modifiers allowed are those declared in that phrase's +preamble. Phrase pptions are an early feature of Inform 7 going back to a +time when its priority was to enable the paraphrasing of Inform 6 library +features (such as the bitmap passed as a parameter to the list-printer).

-

Like the token names, phrase option names have local scope (which is why -they are here and not in the excerpts database). Unlike them, they are not -valid as values, since a condition is not also a value in Inform 7. -

- -

The packet of these associated with a phrase is stored in the PHOD structure. -

- -
define MAX_OPTIONS_PER_PHRASE 16  because held in a 16-bit Z-machine bitmap
-
-
-typedef struct id_options_data {
-    struct phrase_option *options_permitted[MAX_OPTIONS_PER_PHRASE];  see below
-    int no_options_permitted;
-    struct wording options_declaration;  the text declaring the whole set of options
-    int multiple_options_permitted;  can be combined, or mutually exclusive?
-} id_options_data;
-
-
  • The structure id_options_data is private to this section.
-

§2. There's nothing to a phrase option, really: -

- -
-typedef struct phrase_option {
-    struct wording name;  text of name
-} phrase_option;
-
-
  • The structure phrase_option is accessed in 2/rls, 2/fao, 2/act, 4/sv, 6/cii, 6/cste and here.
-

§3. Creation. By default, a phrase has no options. -

- -
-id_options_data Phrases::Options::new(wording W) {
-    id_options_data phod;
-    phod.no_options_permitted = 0;
-    phod.multiple_options_permitted = FALSE;
-    phod.options_declaration = W;
-    return phod;
-}
-
-int Phrases::Options::allows_options(id_options_data *phod) {
-    if (phod->no_options_permitted > 0) return TRUE;
-    return FALSE;
-}
-
-

§4. Parsing. This isn't very efficient, but doesn't need to be, since phrase options -are parsed only in a condition context, not in a value context, and -these are relatively rare in Inform source text. -

- -
-int Phrases::Options::parse(id_options_data *phod, wording W) {
-    for (int i = 0; i < phod->no_options_permitted; i++)
-        if (Wordings::match(W, phod->options_permitted[i]->name))
-            return (1 << i);
-    return -1;
-}
-
-

§5. Parsing phrase options in a declaration.

- -
-id_options_data *phod_being_parsed = NULL;
-id_body *idb_being_parsed = NULL;
-
-void Phrases::Options::parse_declared_options(id_options_data *phod, wording W) {
-    if (Wordings::nonempty(W)) {
-        phod->options_declaration = W;
-        phod_being_parsed = phod;
-        <phrase-option-declaration-list>(W);
-        if (<<r>>) phod->multiple_options_permitted = TRUE;
-    }
-}
-
-

§6. I have to say that I regret the syntax for phrase options, which makes -us write commas like the one here: +

I now sligjtly regret the existence of phrase options, but above all the +comma-based syntax used for them, as here. Brackets would have been better; +it makes phrase options impossible to use for text substitutions.

@@ -168,74 +97,153 @@ But it's mostly the comma which annoys me (making text substitutions unable to support phrase options); I should have gone for brackets.

-

The syntax for declaring phrase options is uncontroversial — it's just -a list of names — but there are wrinkles: if the list is divided with "or" -then the options are mutually exclusive, but with "and/or" they're not. -For example, in: +

The id_options_data for an imperative definition, which is part of its +type data, says what options it allows:

-
-

To decide which object is best route from (R1 - object) to (R2 - object), using doors or using even locked doors: ...

-
- -

the following parses this list: -

- -
-

using doors or using even locked doors

-
- -

and creates two options with <phrase-option-declaration-setting-entry>. -

- -
-<phrase-option-declaration-list> ::=
-    ... |    ==> { lookahead }
-    <phrase-option-declaration-setting-entry> <phrase-option-declaration-tail> |    ==> { pass 2 }
-    <phrase-option-declaration-setting-entry>       ==> { FALSE, - }
-
-<phrase-option-declaration-tail> ::=
-    , _or <phrase-option-declaration-list> |    ==> { pass 1 }
-    , \and/or <phrase-option-declaration-list> |    ==> { TRUE, - }
-    _,/or <phrase-option-declaration-list> |    ==> { pass 1 }
-    \and/or <phrase-option-declaration-list>        ==> { TRUE, - }
-
-<phrase-option-declaration-setting-entry> ::=
-    ... |    ==> { lookahead }
-    ...      ==> Add a phrase option6.1;
+
define MAX_OPTIONS_PER_PHRASE 16  because held in a 16-bit Z-machine bitmap
 
- -

§6.1. Add a phrase option6.1 = +

+typedef struct id_options_data {
+    struct phrase_option *options_permitted[MAX_OPTIONS_PER_PHRASE];
+    int no_options_permitted;
+    struct wording options_declaration;  the text declaring the whole set of options
+    int multiple_options_permitted;  can be combined, or mutually exclusive?
+} id_options_data;
+
+typedef struct phrase_option {
+    struct wording name;  text of name
+} phrase_option;
+
+
  • The structure id_options_data is private to this section.
  • The structure phrase_option is accessed in 3/nuor, 3/dbtr, 3/rpr, 4/ass, 5/tpf, 6/tc, 6/tbl, 6/eqt and here.
+

§2. Creation. By default, a phrase has no options.

-    Phrases::Options::phod_add_phrase_option(phod_being_parsed, W);
-    ==> { FALSE, - };
+id_options_data PhraseOptions::new(wording W) {
+    id_options_data phod;
+    phod.no_options_permitted = 0;
+    phod.multiple_options_permitted = FALSE;
+    phod.options_declaration = W;
+    return phod;
+}
+
+int PhraseOptions::allows_options(id_body *idb) {
+    id_options_data *phod = &(idb->type_data.options_data);
+    if (phod->no_options_permitted > 0) return TRUE;
+    return FALSE;
+}
 
-
  • This code is used in §6.
-

§7.

+

§3.

-int too_many_POs_error = FALSE;
-void Phrases::Options::phod_add_phrase_option(id_options_data *phod, wording W) {
+int PM_TooManyPhraseOptions_issued = FALSE;
+void PhraseOptions::phod_add_phrase_option(id_options_data *phod, wording W) {
     LOGIF(PHRASE_CREATIONS, "Adding phrase option <%W>\n", W);
     if (phod->no_options_permitted >= MAX_OPTIONS_PER_PHRASE) {
-        if (too_many_POs_error == FALSE)
-            StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TooManyPhraseOptions),
+        if (PM_TooManyPhraseOptions_issued == FALSE)
+            StandardProblems::sentence_problem(Task::syntax_tree(),
+                _p_(PM_TooManyPhraseOptions),
                 "a phrase is only allowed to have 16 different options",
-                "so either some of these will need to go, or you may want to "
-                "consider breaking up the phrase into simpler ones whose usage "
-                "is easier to describe.");
-        too_many_POs_error = TRUE;
+                "so either some of these will need to go, or you may want to consider "
+                "breaking up the phrase into simpler ones whose usage is easier to describe.");
+        PM_TooManyPhraseOptions_issued = TRUE;
         return;
     }
-    too_many_POs_error = FALSE;  so that the problem can recur on later phrases
+    PM_TooManyPhraseOptions_issued = FALSE;  so the problem can recur on later phrases
 
     phrase_option *po = CREATE(phrase_option);
     po->name = W;
     phod->options_permitted[phod->no_options_permitted++] = po;
 }
 
+

§4. Parsing. This isn't very efficient, but doesn't need to be, since phrase options are parsed +only in a condition context, not in a value context, and these are relatively +rare in Inform source text. +

+ +
+id_options_data *phod_being_parsed = NULL;
+id_body *idb_being_parsed = NULL;
+
+int PhraseOptions::parse_phod(id_options_data *phod, wording W) {
+    for (int i = 0; i < phod->no_options_permitted; i++)
+        if (Wordings::match(W, phod->options_permitted[i]->name))
+            return (1 << i);
+    return -1;
+}
+int PhraseOptions::parse(id_body *idb, wording W) {
+    return PhraseOptions::parse_phod(&(idb->type_data.options_data), W);
+}
+
+

§5. Which we wrap up thus: +

+ +
+<phrase-option> internal {
+    int bitmap = PhraseOptions::parse_phod(phod_being_parsed, W);
+    if (bitmap == -1) { ==> { fail nonterminal }; }
+    ==> { bitmap, - };
+    return TRUE;
+}
+
+ +

§6. Parsing phrase options in a declaration. The following is called with W set to just the part of a phrase prototype +containing its phrase options. In this example: +

+ +
+To decide which object is best route from (R1 - object) to (R2 - object),
+    using doors or using even locked doors:
+
+

W would be "using doors or using even locked doors". +

+ +

The syntax is just a list of names, but with the wrinkle that if the list is divided +with "or" then the options are mutually exclusive, but with "and/or" they're not. +

+ +
+void PhraseOptions::parse_declared_options(id_options_data *phod, wording W) {
+    if (Wordings::nonempty(W)) {
+        phod->options_declaration = W;
+        phod_being_parsed = phod;
+        <phrase-option-decl-list>(W);
+        if (<<r>>) phod->multiple_options_permitted = TRUE;
+    }
+}
+
+

§7. Note the following Preform grammar passes the return value TRUE up from +the final element of the list when the connective used for it was "and/or". +Note also the rare use of the Preform literal marker in \and/or to show +that the slash between "and" and "or" is part of the word. +

+ +
+<phrase-option-decl-list> ::=
+    ... |                                                           ==> { lookahead }
+    <phrase-option-decl-setting-entry> <phrase-option-decl-tail> |  ==> { pass 2 }
+    <phrase-option-decl-setting-entry>                              ==> { FALSE, - }
+
+<phrase-option-decl-tail> ::=
+    , _or <phrase-option-decl-list> |                               ==> { pass 1 }
+    , \and/or <phrase-option-decl-list> |                           ==> { TRUE, - }
+    _,/or <phrase-option-decl-list> |                               ==> { pass 1 }
+    \and/or <phrase-option-decl-list>                               ==> { TRUE, - }
+
+<phrase-option-decl-setting-entry> ::=
+    ... |                                                           ==> { lookahead }
+    ...                                                             ==> Add a phrase option7.1;
+
+ +

§7.1. Add a phrase option7.1 = +

+ +
+    PhraseOptions::phod_add_phrase_option(phod_being_parsed, W);
+    ==> { FALSE, - };
+
+
  • This code is used in §7.

§8. Parsing phrase options in an invocation. At this point, we're looking at the text after the first comma in something like:

@@ -264,18 +272,18 @@ produce.
 int phod_being_parsed_silently = FALSE;  context for the grammar below
 
-int Phrases::Options::parse_invoked_options(parse_node *inv, int silently) {
-    id_body *idb = Node::get_phrase_invoked(inv);
-    wording W = Invocations::get_phrase_options(inv);
+int PhraseOptions::parse_invoked_options(parse_node *inv, int silently) {
+    id_body *idb = Node::get_phrase_invoked(inv);
+    wording W = Invocations::get_phrase_options(inv);
 
     idb_being_parsed = idb;
-    phod_being_parsed = &(idb_being_parsed->options_data);
+    phod_being_parsed = &(idb_being_parsed->type_data.options_data);
 
     int bitmap = 0;
     int pc = problem_count;
-    Parse the supplied list of options into a bitmap8.1;
+    Parse the supplied list of options into a bitmap8.1;
 
-    Invocations::set_phrase_options_bitmap(inv, bitmap);
+    Invocations::set_phrase_options_bitmap(inv, bitmap);
     if (problem_count > pc) return FALSE;
     return TRUE;
 }
@@ -291,11 +299,11 @@ produce.
 
     if ((problem_count == pc) &&
         (phod_being_parsed->multiple_options_permitted == FALSE))
-        Reject this if multiple options are set8.1.1;
+        Reject this if multiple options are set8.1.1;
 
-
  • This code is used in §8.
+
  • This code is used in §8.

§8.1.1. Ah, bit-twiddling: fun for all the family. There's no point computing the -Hamming distance of the bitmap, that is, the number of bits set: we only need +population count of the bitmap, that is, the number of bits set: we only need to know if it's a power of 2 or not. Note that subtracting 1, in binary, clears the least significant set bit, leaves the higher bits as they are, and changes the lower bits (which were previously all 0s) to 1s. So taking @@ -314,7 +322,8 @@ this residue is zero. Problems::quote_wording(2, W); Problems::quote_phrase(3, idb); Problems::quote_wording(4, phod_being_parsed->options_declaration); - StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_PhraseOptionsExclusive)); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_PhraseOptionsExclusive)); Problems::issue_problem_segment( "You wrote %1, supplying the options '%2' to the phrase '%3', but " "the options listed for this phrase ('%4') are mutually exclusive."); @@ -323,24 +332,24 @@ this residue is zero. return FALSE; }

-
  • This code is used in §8.1.
+
  • This code is used in §8.1.

§9. When setting options, in an actual use of a phrase, the list is divided by "and":

 <phrase-option-list> ::=
-    ... |    ==> { lookahead }
+    ... |                                                ==> { lookahead }
     <phrase-option-setting-entry> <phrase-option-tail> | ==> { R[1] | R[2], - }
-    <phrase-option-setting-entry>                       ==> { pass 1 }
+    <phrase-option-setting-entry>                        ==> { pass 1 }
 
 <phrase-option-tail> ::=
-    , _and <phrase-option-list> |    ==> { pass 1 }
-    _,/and <phrase-option-list>                         ==> { pass 1 }
+    , _and <phrase-option-list> |                        ==> { pass 1 }
+    _,/and <phrase-option-list>                          ==> { pass 1 }
 
 <phrase-option-setting-entry> ::=
-    <phrase-option> |    ==> { pass 1 }
-    ...                 ==> Issue PM_NotAPhraseOption or C22NotTheOnlyPhraseOption problem9.1
+    <phrase-option> |                                    ==> { pass 1 }
+    ...  ==> Issue PM_NotAPhraseOption or C22NotTheOnlyPhraseOption problem9.1
 

§9.1. Issue PM_NotAPhraseOption or C22NotTheOnlyPhraseOption problem9.1 = @@ -353,13 +362,15 @@ by "and": Problems::quote_phrase(3, idb_being_parsed); Problems::quote_wording(4, phod_being_parsed->options_declaration); if (phod_being_parsed->no_options_permitted > 1) { - StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_NotAPhraseOption)); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_NotAPhraseOption)); Problems::issue_problem_segment( "You wrote %1, but '%2' is not one of the options allowed on " "the end of the phrase '%3'. (The options allowed are: '%4'.)"); Problems::issue_problem_end(); } else { - StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_NotTheOnlyPhraseOption)); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_NotTheOnlyPhraseOption)); Problems::issue_problem_segment( "You wrote %1, but the only option allowed on the end of the " "phrase '%3' is '%4', so '%2' is not something I know how to " @@ -368,21 +379,9 @@ by "and": } } -

  • This code is used in §9.
-

§10. The following matches any single phrase option for the phrase being used. -

- -
-<phrase-option> internal {
-    int bitmap = Phrases::Options::parse(phod_being_parsed, W);
-    if (bitmap == -1) { ==> { fail nonterminal }; }
-    ==> { bitmap, - };
-    return TRUE;
-}
-
- +
  • This code is used in §9.