To tidy up invocation nodes into a list of children under the relevant rule node, and so turn each rule definition into a single subtree.
+- Home
- Inform7
- assertions
- Chapter 2: Declarations
- Imperative Subtrees
To tidy up blocks of rule and phrase definition in the syntax tree.
-- -
§1. Initially, the invocations (parsed as just UNKNOWN_NT) defining a -rule (RULE_NT) are simply listed after it in the parse tree, but we -want them to become its children, and we give them the node type -INVOCATION_LIST_NT. -
- -This function is used whenever new material is added. Whenever it finds a -childless RULE_NT followed by a sequence of UNKNOWN_NT nodes, it -joins these in sequence as children of the RULE_NT. Since it always -acts so as to leave a non-zero number of children, and since it acts only -on childless nodes, it cannot ever act on the same node twice. +
§1. Blocks of imperative code in Inform 7 source text enter the syntax tree +at IMPERATIVE_NT nodes: some define phrases, some define rules. Those nodes +are initially followed by a run of UNKNOWN_NT nodes for the actual code. +The process of "acceptance" turns such definitions into a subtree, as +follows:
-void RuleSubtrees::register_recently_lexed_phrases(void) { +IMPERATIVE_NT 'every turn' IMPERATIVE_NT 'every turn +UNKNOWN_NT 'say "Hello!"' --> INVOCATION_LIST_NT 'say "Hello!"' +UNKNOWN_NT 'now the guard is alert' INVOCATION_LIST_NT 'now the guard is alert' ++
ImperativeSubtrees::accept needs to be called on every IMPERATIVE_NT node in order +for this to work; note that it does nothing further, but also causes no harm, +if called multiple times on the same node. ImperativeSubtrees::accept_all can +therefore safely be used to sweep up any IMPERATIVE_NT nodes not already processed. +
+ ++void ImperativeSubtrees::accept_all(void) { if (problem_count > 0) return; for then the tree is perhaps broken anyway - SyntaxTree::traverse(Task::syntax_tree(), RuleSubtrees::demote_command_nodes); + SyntaxTree::traverse(Task::syntax_tree(), ImperativeSubtrees::accept); } -void RuleSubtrees::demote_command_nodes(parse_node *p) { - if ((Node::get_type(p) == RULE_NT) && (p->down == NULL)) { +void ImperativeSubtrees::accept(parse_node *p) { + if ((Node::get_type(p) == IMPERATIVE_NT) && (p->down == NULL)) { parse_node *end_def = p; while ((end_def->next) && (Node::get_type(end_def->next) == UNKNOWN_NT)) end_def = end_def->next; - if (p == end_def) return; RULE_NT not followed by any UNKNOWN_NTs + if (p == end_def) return; IMPERATIVE_NT not followed by any UNKNOWN_NTs splice so that p->next to end_def become the children of p: p->down = p->next; p->next = end_def->next; end_def->next = NULL; for (parse_node *inv_p = p->down; inv_p; inv_p = inv_p->next) Node::set_type(inv_p, INVOCATION_LIST_NT); - RuleSubtrees::parse_routine_structure(p); + Parse the structure of the code block1.1; } }-
§2. Parsing Routine Structure. There are now two possible syntaxes to express the structural makeup of a -routine. Traditional I7 syntax for blocks is to place them within begin/end -markers: the "begin" occurring at the end of the conditional or loop header, -and the "end if", "end while", etc., as a phrase of its own at the end of -the block. Newer I7 syntax (March 2008) is to use Python-style colons and -indentation. Both are allowed, but not in the same routine. +
§1.1. After acceptance, and therefore exactly once, the structure of the code in +the definition is parsed and checked for sanity.
-This routine opens with the routine's parse tree consisting of a simple -linked list of code-point nodes, one for each phrase. We must work out -which syntax is used, decipher it, and turn the list into a proper tree -structure in a single unified format. +
Though it is now a historical relic, Inform has two different syntaxes for +blocks of code: "colon syntax", introduced in March 2008, which uses Python-like +colons and indentation to show structural subdivision; and "begin/end syntax", +which uses explicit marker phrases like "end if" and "end while". The compiler +continues to support both though they cannot be mixed in a single IMPERATIVE_NT +subtree.
-How much simpler this would all be if we could abolish the old format, but -it's kept for the benefit of partially sighted users, who find tabbed -indentation difficult to manage with screen-readers. +
The old syntax is retained not for compatibility with old code — very little +remains from the pre-2008 era which has not been modernised — but because +some partially sighted users find tabbed indentation difficult to manage +with screen-readers. +
+ +Here, then, we must work out which syntax is used, decipher it, and turn the +list into a proper tree structure in a single unified format. We will also +try to find and report as many problems as we can which are due to code blocks +being improperly opened or closed, because punctuation errors in rules are +one of the biggest sources of beginners' difficulties with Inform, and we want +to catch and report these problems early. +
+ +This means looking out for control structures such as "if" and "while": see +Control Structures (in supervisor) for where these are defined. +
+ +Parse the structure of the code block1.1 =
-void RuleSubtrees::parse_routine_structure(parse_node *routine_node) { int initial_problem_count = problem_count; + parse_node *imperative_node = p; + parse_node *uses_colon_syntax = NULL; parse_node *uses_begin_end_syntax = NULL; parse_node *mispunctuates_begin_end_syntax = NULL; parse_node *requires_colon_syntax = NULL; - (a.1) See which block syntax is used by conditionals and loops2.1; - (a.2) Report problems if the two syntaxes are mixed up with each other2.2; + (a.1) See which block syntax is used by conditionals and loops1.1.1; + (a.2) Report problems if the two syntaxes are mixed up with each other1.1.2; if (problem_count > initial_problem_count) return; - if (uses_colon_syntax) (b.1) Annotate the parse tree with indentation levels2.3; - (b.2) Annotate the parse tree with control structure usage2.4; + if (uses_colon_syntax) (b.1) Annotate the parse tree with indentation levels1.1.3; + (b.2) Annotate the parse tree with control structure usage1.1.4; - (c) Expand comma notation for blocks2.5; + (c) Expand comma notation for blocks1.1.5; if (problem_count > initial_problem_count) return; - if (uses_colon_syntax) (d) Insert end nodes and check the indentation2.6; + if (uses_colon_syntax) (d) Insert end nodes and check the indentation1.1.6; if (problem_count > initial_problem_count) return; - (e) Structure the parse tree to match the use of control structures2.7; + (e) Structure the parse tree to match the use of control structures1.1.7; if (problem_count > initial_problem_count) return; - (f) Police the structure of the parse tree2.8; + (f) Police the structure of the parse tree1.1.8; if (problem_count > initial_problem_count) return; - (g) Optimise out the otherwise if nodes2.9; + (g) Optimise out the otherwise if nodes1.1.9; if (problem_count > initial_problem_count) return; - (h) Remove any end markers as no longer necessary2.11; + (h) Remove any end markers as no longer necessary1.1.11; if (problem_count > initial_problem_count) return; if (uses_colon_syntax == FALSE) - (i) Remove any begin markers as no longer necessary2.13; + (i) Remove any begin markers as no longer necessary1.1.13; - (j) Insert code block nodes so that nodes needing to be parsed are childless2.15; - (k) Insert instead marker nodes2.17; - (l) Break up say phrases2.19; -} + (j) Insert code block nodes so that nodes needing to be parsed are childless1.1.15; + (k) Insert instead marker nodes1.1.17; + (l) Break up say phrases1.1.19;-
§2.1. (a.1) See which block syntax is used by conditionals and loops2.1 = +
- This code is used in §1.
§1.1.1. (a.1) See which block syntax is used by conditionals and loops1.1.1 =
parse_node *p; - for (p = routine_node->down; p; p = p->next) { + for (p = imperative_node->down; p; p = p->next) { control_structure_phrase *csp = ControlStructures::detect(Node::get_text(p)); if (csp) { @@ -191,7 +209,7 @@ indentation difficult to manage with screen-readers. if (syntax_used) { if (uses_colon_syntax == NULL) uses_colon_syntax = p; } else { - Note what looks like a begin-end piece of syntax2.1.1; + Note what looks like a begin-end piece of syntax1.1.1.1; } } if ((csp->requires_new_syntax) && (requires_colon_syntax == NULL)) @@ -203,11 +221,11 @@ indentation difficult to manage with screen-readers. } }-
- This code is used in §2.
§2.1.1. It's possible in oddball cases to mis-punctuate such as to fool us, so: +
- This code is used in §1.1.
§1.1.1.1. It's possible in oddball cases to mis-punctuate such as to fool us, so:
-Note what looks like a begin-end piece of syntax2.1.1 = +
Note what looks like a begin-end piece of syntax1.1.1.1 =
@@ -218,94 +236,90 @@ indentation difficult to manage with screen-readers. mispunctuates_begin_end_syntax = p; }-
- This code is used in §2.1.
§2.2. (a.2) Report problems if the two syntaxes are mixed up with each other2.2 = +
- This code is used in §1.1.1.
§1.1.2. (a.2) Report problems if the two syntaxes are mixed up with each other1.1.2 =
if ((uses_colon_syntax) && (mispunctuates_begin_end_syntax)) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source(1, current_sentence); Problems::quote_source(2, mispunctuates_begin_end_syntax); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_BadOldSyntax)); Problems::issue_problem_segment( - "The rule or phrase definition %1 seems to use indentation and " - "colons to group phrases together into 'if', 'repeat' or 'while' " - "blocks. That's fine, but then this phrase seems to be missing " - "some punctuation - %2. Perhaps a colon is missing?"); + "The rule or phrase definition %1 seems to use indentation and colons to group " + "phrases together into 'if', 'repeat' or 'while' blocks. That's fine, but then " + "this phrase seems to be missing some punctuation - %2. Perhaps a colon is missing?"); Problems::issue_problem_end(); return; } if ((uses_colon_syntax) && (uses_begin_end_syntax)) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source(1, current_sentence); Problems::quote_source(2, uses_colon_syntax); Problems::quote_source(3, uses_begin_end_syntax); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_BothBlockSyntaxes)); Problems::issue_problem_segment( - "The rule or phrase definition %1 seems to use both ways of grouping " - "phrases together into 'if', 'repeat' and 'while' blocks at once. " - "Inform allows two alternative forms, but they cannot be mixed in " - "the same definition. %POne way is to end the 'if', 'repeat' or " - "'while' phrases with a 'begin', and then to match that with an " - "'end if' or similar. ('Otherwise' or 'otherwise if' clauses are " - "phrases like any other, and end with semicolons in this case.) " + "The rule or phrase definition %1 seems to use both ways of grouping phrases " + "together into 'if', 'repeat' and 'while' blocks at once. Inform allows two " + "alternative forms, but they cannot be mixed in the same definition. %P" + "One way is to end the 'if', 'repeat' or 'while' phrases with a 'begin', and " + "then to match that with an 'end if' or similar. ('Otherwise' or 'otherwise if' " + "clauses are phrases like any other, and end with semicolons in this case.) " "You use this begin/end form here, for instance - %3. %P" - "The other way is to end with a colon ':' and then indent the " - "subsequent phrases underneath, using tabs. (Note that any " - "'otherwise' or 'otherwise if' clauses also have to end with " - "colons in this case.) You use this indented form here - %2."); + "The other way is to end with a colon ':' and then indent the subsequent phrases " + "underneath, using tabs. (Note that any 'otherwise' or 'otherwise if' clauses " + "also have to end with colons in this case.) You use this indented form here - %2."); Problems::issue_problem_end(); return; } if ((requires_colon_syntax) && (uses_begin_end_syntax)) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source(1, current_sentence); Problems::quote_source(2, requires_colon_syntax); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_NotInOldSyntax)); Problems::issue_problem_segment( - "The construction %2, in the rule or phrase definition %1, " - "is only allowed if the rule is written in the 'new' format, " - "that is, with the phrases written one to a line with " - "indentation showing how they are grouped together, and " - "with colons indicating the start of such a group."); + "The construction %2, in the rule or phrase definition %1, is only allowed if the " + "rule is written in the 'new' format, that is, with the phrases written one to a " + "line with indentation showing how they are grouped together, and with colons " + "indicating the start of such a group."); Problems::issue_problem_end(); return; }-
- This code is used in §2.
§2.3. If we're using Pythonesque notation, then the number of tab stops of +
- This code is used in §1.1.
§1.1.3. If we're using Pythonesque notation, then the number of tab stops of indentation of a phrase tells us where it belongs in the structure, so we mark up the tree with that information.
-(b.1) Annotate the parse tree with indentation levels2.3 = +
(b.1) Annotate the parse tree with indentation levels1.1.3 =
- Annotations::write_int(routine_node, indentation_level_ANNOT, - Lexer::indentation_level(Wordings::first_wn(Node::get_text(routine_node)))); + Annotations::write_int(imperative_node, indentation_level_ANNOT, + Lexer::indentation_level(Wordings::first_wn(Node::get_text(imperative_node)))); parse_node *p; - for (p = routine_node->down; p; p = p->next) { + for (p = imperative_node->down; p; p = p->next) { int I = Lexer::indentation_level(Wordings::first_wn(Node::get_text(p))); Annotations::write_int(p, indentation_level_ANNOT, I); }-
- This code is used in §2.
§2.4. Note that we are a little cautious about recognising phrases which will +
- This code is used in §1.1.
§1.1.4. Note that we are a little cautious about recognising phrases which will open blocks, such as "repeat...", because of the dangers of false positives; so we look for the "begin" keyword, or the colon. We're less cautious with subordinate phrases (such as "otherwise") because we know their wonding more certainly, and similarly for "end X" phrases.
-(b.2) Annotate the parse tree with control structure usage2.4 = +
(b.2) Annotate the parse tree with control structure usage1.1.4 =
- for (parse_node *p = routine_node->down; p; p = p->next) { + for (parse_node *p = imperative_node->down; p; p = p->next) { control_structure_phrase *csp; csp = ControlStructures::detect(Node::get_text(p)); if (csp) { @@ -313,26 +327,26 @@ more certainly, and similarly for "end X" phrases. (<phrase-beginning-block>(Node::get_text(p))) || (csp->subordinate_to)) { Node::set_control_structure_used(p, csp); - if (csp == case_CSP) Trim a switch case to just the case value2.4.1; + if (csp == case_CSP) Trim a switch case to just the case value1.1.4.1; } } csp = ControlStructures::detect_end(Node::get_text(p)); if (csp) Node::set_end_control_structure_used(p, csp); }-
- This code is used in §2.
§2.4.1. At this point anything at all can be a case value: it won't be parsed +
- This code is used in §1.1.
§1.1.4.1. At this point anything at all can be a case value: it won't be parsed or type-checked until compilation.
-Trim a switch case to just the case value2.4.1 = +
Trim a switch case to just the case value1.1.4.1 =
Node::set_text(p, GET_RW(<control-structure-phrase>, 1));-
- This code is used in §2.4.
§2.5. "Comma notation" is when a comma is used in an "if" statement to divide +
- This code is used in §1.1.4.
§1.1.5. "Comma notation" is when a comma is used in an "if" statement to divide off only a single consequential phrase, as in
@@ -344,20 +358,20 @@ off only a single consequential phrase, as in to break this up. -(c) Expand comma notation for blocks2.5 = +
(c) Expand comma notation for blocks1.1.5 =
- for (parse_node *p = routine_node->down; p; p = p->next) + for (parse_node *p = imperative_node->down; p; p = p->next) if (Node::get_control_structure_used(p) == NULL) { control_structure_phrase *csp; csp = ControlStructures::detect(Node::get_text(p)); if ((csp == if_CSP) && (<phrase-with-comma-notation>(Node::get_text(p)))) - Effect a comma expansion2.5.1; + Effect a comma expansion1.1.5.1; }-
- This code is used in §2.
§2.5.1. Effect a comma expansion2.5.1 = +
- This code is used in §1.1.
§1.1.5.1. Effect a comma expansion1.1.5.1 =
@@ -376,49 +390,49 @@ to break this up. Annotations::read_int(p, indentation_level_ANNOT) + 1); Node::set_text(then_node, ACW); - parse_node *last_node_of_if_construction = then_node, *rest_of_routine = p->next; + parse_node *last_node_of_if_construction = then_node, *rest_of_defn = p->next; Attach the "then" node after the "if" node: p->next = then_node; - Deal with an immediately following otherwise node, if there is one2.5.1.1; + Deal with an immediately following otherwise node, if there is one1.1.5.1.1; if (uses_colon_syntax == FALSE) { - last_node_of_if_construction->next = RuleSubtrees::end_node(p); - last_node_of_if_construction->next->next = rest_of_routine; + last_node_of_if_construction->next = ImperativeSubtrees::end_node(p); + last_node_of_if_construction->next->next = rest_of_defn; } else { - last_node_of_if_construction->next = rest_of_routine; + last_node_of_if_construction->next = rest_of_defn; }-
- This code is used in §2.5.
§2.5.1.1. Deal with an immediately following otherwise node, if there is one2.5.1.1 = +
- This code is used in §1.1.5.
§1.1.5.1.1. Deal with an immediately following otherwise node, if there is one1.1.5.1.1 =
- if (rest_of_routine) + if (rest_of_defn) if ((uses_colon_syntax == FALSE) || (Annotations::read_int(p, indentation_level_ANNOT) == - Annotations::read_int(rest_of_routine, indentation_level_ANNOT))) { - if (Node::get_control_structure_used(rest_of_routine) == otherwise_CSP) - Deal with an immediately following otherwise2.5.1.1.1 - else if (ControlStructures::abbreviated_otherwise(Node::get_text(rest_of_routine))) - Deal with an abbreviated otherwise node2.5.1.1.2; + Annotations::read_int(rest_of_defn, indentation_level_ANNOT))) { + if (Node::get_control_structure_used(rest_of_defn) == otherwise_CSP) + Deal with an immediately following otherwise1.1.5.1.1.1 + else if (ControlStructures::abbreviated_otherwise(Node::get_text(rest_of_defn))) + Deal with an abbreviated otherwise node1.1.5.1.1.2; }-
- This code is used in §2.5.1.
§2.5.1.1.1. We string a plain "otherwise" node onto the "if" construction. +
- This code is used in §1.1.5.1.
§1.1.5.1.1.1. We string a plain "otherwise" node onto the "if" construction.
-Deal with an immediately following otherwise2.5.1.1.1 = +
Deal with an immediately following otherwise1.1.5.1.1.1 =
- then_node->next = rest_of_routine; + then_node->next = rest_of_defn; last_node_of_if_construction = last_node_of_if_construction->next; - rest_of_routine = rest_of_routine->next; + rest_of_defn = rest_of_defn->next;-
- This code is used in §2.5.1.1.
§2.5.1.1.2. An abbreviated otherwise clause looks like this: +
- This code is used in §1.1.5.1.1.
§1.1.5.1.1.2. An abbreviated otherwise clause looks like this:
@@ -428,7 +442,7 @@ to break this up.and we want to split this, too, into distinct nodes.
-Deal with an abbreviated otherwise node2.5.1.1.2 = +
Deal with an abbreviated otherwise node1.1.5.1.1.2 =
@@ -437,23 +451,23 @@ to break this up. Annotations::write_int(otherwise_node, indentation_level_ANNOT, Annotations::read_int(p, indentation_level_ANNOT)); Node::set_text(otherwise_node, - Wordings::one_word(Wordings::first_wn(Node::get_text(rest_of_routine)))); extract just the word "otherwise" + Wordings::one_word(Wordings::first_wn(Node::get_text(rest_of_defn)))); extract just the word "otherwise" Node::set_control_structure_used(otherwise_node, otherwise_CSP); then_node->next = otherwise_node; - otherwise_node->next = rest_of_routine; + otherwise_node->next = rest_of_defn; - Node::set_text(rest_of_routine, - Wordings::trim_first_word(Node::get_text(rest_of_routine))); to remove the "otherwise" + Node::set_text(rest_of_defn, + Wordings::trim_first_word(Node::get_text(rest_of_defn))); to remove the "otherwise" - Annotations::write_int(rest_of_routine, indentation_level_ANNOT, - Annotations::read_int(rest_of_routine, indentation_level_ANNOT) + 1); + Annotations::write_int(rest_of_defn, indentation_level_ANNOT, + Annotations::read_int(rest_of_defn, indentation_level_ANNOT) + 1); - last_node_of_if_construction = rest_of_routine; - rest_of_routine = rest_of_routine->next; + last_node_of_if_construction = rest_of_defn; + rest_of_defn = rest_of_defn->next;--
- This code is used in §2.5.1.1.
§2.6. If the old-style syntax is used, there are explicit "end if", "end repeat" +
+
- This code is used in §1.1.5.1.1.
§1.1.6. If the old-style syntax is used, there are explicit "end if", "end repeat" and "end while" nodes in the list already. But if the Pythonesque syntax is used then we need to create these nodes and insert them into the list; we do these by reading off the structure from the pattern of indentation. It's @@ -461,7 +475,7 @@ quite a long task, since this pattern may contain errors, which we have to report more or less helpfully.
-(d) Insert end nodes and check the indentation2.6 = +
(d) Insert end nodes and check the indentation1.1.6 =
@@ -476,32 +490,32 @@ report more or less helpfully. int blstack_stage[GROSS_AMOUNT_OF_INDENTATION+1]; int blo_sp = 0, suppress_further_problems = FALSE; - if (Annotations::read_int(routine_node, indentation_level_ANNOT) != 0) - Issue problem message for failing to start flush on the left margin2.6.1; + if (Annotations::read_int(imperative_node, indentation_level_ANNOT) != 0) + Issue problem message for failing to start flush on the left margin1.1.6.1; - for (prev = NULL, p = routine_node->down, k=1; p; prev = p, p = p->next, k++) { + for (prev = NULL, p = imperative_node->down, k=1; p; prev = p, p = p->next, k++) { control_structure_phrase *csp = Node::get_control_structure_used(p); - Determine actual indentation of this phrase2.6.2; - Compare actual indentation to what we expect from structure so far2.6.3; - Insert begin marker and increase expected indentation if a block begins here2.6.4; + Determine actual indentation of this phrase1.1.6.2; + Compare actual indentation to what we expect from structure so far1.1.6.3; + Insert begin marker and increase expected indentation if a block begins here1.1.6.4; } indent = 1; - Try closing blocks to bring expected indentation down to match2.6.5; + Try closing blocks to bring expected indentation down to match1.1.6.5; - if (indent_overmuch) Issue problem message for an excess of indentation2.6.7 - else if (run_on_at) Issue problem message for run-ons within phrase definition2.6.8 - else if (indent_misalign) Issue problem message for misaligned indentation2.6.6; + if (indent_overmuch) Issue problem message for an excess of indentation1.1.6.7 + else if (run_on_at) Issue problem message for run-ons within phrase definition1.1.6.8 + else if (indent_misalign) Issue problem message for misaligned indentation1.1.6.6;--
- This code is used in §2.
+ -
- This code is used in §1.1.
Issue problem message for failing to start flush on the left margin2.6.1 = +
Issue problem message for failing to start flush on the left margin1.1.6.1 =
- current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_NonflushRule)); Problems::issue_problem_segment( @@ -511,8 +525,8 @@ report more or less helpfully. Problems::issue_problem_end(); suppress_further_problems = TRUE;--
- This code is used in §2.6.
§2.6.2. Here we set indent to the number of tab-stops in from the margin, or to +
+
- This code is used in §1.1.6.
§1.1.6.2. Here we set indent to the number of tab-stops in from the margin, or to expected_indent if the text does not appear to be at the start of its own line in the source (because it runs on from a previous phrase, in which case we set the run_on_at flag: except for following on from cases @@ -520,7 +534,7 @@ in switches with a non-control-structure, which is allowed, because otherwise the lines often look silly and short).
-Determine actual indentation of this phrase2.6.2 = +
Determine actual indentation of this phrase1.1.6.2 =
@@ -541,10 +555,10 @@ the lines often look silly and short). break; } } - if (indent >= GROSS_AMOUNT_OF_INDENTATION) Record an excess of indentation2.6.2.1; + if (indent >= GROSS_AMOUNT_OF_INDENTATION) Record an excess of indentation1.1.6.2.1;--
- This code is used in §2.6.
§2.6.3. We now know the indent level of the line as read, and also the +
+
- This code is used in §1.1.6.
§1.1.6.3. We now know the indent level of the line as read, and also the expected_indent given the definition so far. If they agree, fine. If they don't agree, it isn't necessarily bad news — if each line's indentation were a function of the last, there would be no information in it, after all. @@ -556,29 +570,29 @@ current block(s) has or have been closed, because blocks are indeed closed implicitly just by moving the indentation back in.
-Compare actual indentation to what we expect from structure so far2.6.3 = +
Compare actual indentation to what we expect from structure so far1.1.6.3 =
if (indent == 0) { - Record a misalignment of indentation2.6.3.3; - Record a phrase within current block2.6.3.2; + Record a misalignment of indentation1.1.6.3.3; + Record a phrase within current block1.1.6.3.2; } else { if ((csp) && (csp->subordinate_to)) { - Compare actual indentation to what we expect for an intermediate phrase2.6.3.1; + Compare actual indentation to what we expect for an intermediate phrase1.1.6.3.1; just_opened_block = TRUE; } else { - if (expected_indent < indent) Record a misalignment of indentation2.6.3.3; + if (expected_indent < indent) Record a misalignment of indentation1.1.6.3.3; if (expected_indent > indent) - Try closing blocks to bring expected indentation down to match2.6.5; + Try closing blocks to bring expected indentation down to match1.1.6.5; expected_indent = indent; - Record a phrase within current block2.6.3.2; + Record a phrase within current block1.1.6.3.2; } } if (expected_indent < 1) expected_indent = 1;--
- This code is used in §2.6.
§2.6.3.1. This is a small variation used for an intermediate phrase like "otherwise". +
+
- This code is used in §1.1.6.
§1.1.6.3.1. This is a small variation used for an intermediate phrase like "otherwise". These are required to be at the same indentation as the line which opened the block, rather than being one tab step in from there: in other words they are not deemed part of the block itself. They can also occur in "stages", which @@ -587,28 +601,28 @@ one — for instance, "otherwise if..." is not allowed after an "otherwise" within an "if".
-Compare actual indentation to what we expect for an intermediate phrase2.6.3.1 = +
Compare actual indentation to what we expect for an intermediate phrase1.1.6.3.1 =
expected_indent--; if (expected_indent < indent) { - Issue problem for an intermediate phrase not matching2.6.3.1.1; + Issue problem for an intermediate phrase not matching1.1.6.3.1.1; } else { - Try closing blocks to bring expected indentation down to match2.6.5; + Try closing blocks to bring expected indentation down to match1.1.6.5; if ((blo_sp == 0) || (csp->subordinate_to != blstack_construct[blo_sp-1])) { - Issue problem for an intermediate phrase not matching2.6.3.1.1; + Issue problem for an intermediate phrase not matching1.1.6.3.1.1; } else { if (blstack_stage[blo_sp-1] > csp->used_at_stage) - Issue problem for an intermediate phrase out of sequence2.6.3.1.2; + Issue problem for an intermediate phrase out of sequence1.1.6.3.1.2; blstack_stage[blo_sp-1] = csp->used_at_stage; } } expected_indent++;--
- This code is used in §2.6.3.
§2.6.4. In colon syntax, blocks are explicitly opened; they are only implicitly +
+
- This code is used in §1.1.6.3.
§1.1.6.4. In colon syntax, blocks are explicitly opened; they are only implicitly closed. Here is the opening:
@@ -617,11 +631,12 @@ colon syntax, then it is followed by a word which is the colon: thus if -Insert begin marker and increase expected indentation if a block begins here2.6.4 = +
Insert begin marker and increase expected indentation if a block begins here1.1.6.4 =
- if ((csp) && (csp->subordinate_to == NULL) && (Annotations::read_int(p, colon_block_command_ANNOT))) { + if ((csp) && (csp->subordinate_to == NULL) && + (Annotations::read_int(p, colon_block_command_ANNOT))) { expected_indent++; if (csp->indent_subblocks) expected_indent++; blstack_construct[blo_sp] = csp; @@ -630,25 +645,25 @@ reads "if x is 2" then the word following the "2" will be ":". just_opened_block = TRUE; }--
- This code is used in §2.6.
§2.6.5. Now for the closing of colon-syntax blocks. We know that blocks must be +
+
- This code is used in §1.1.6.
§1.1.6.5. Now for the closing of colon-syntax blocks. We know that blocks must be being closed if the indentation has jumped backwards: but it may be that many blocks are being closed at once. (It may also be that the indentation has gone awry.)
-Try closing blocks to bring expected indentation down to match2.6.5 = +
Try closing blocks to bring expected indentation down to match1.1.6.5 =
if ((just_opened_block) && (blo_sp > 0) && (!(blstack_construct[blo_sp-1]->body_empty_except_for_subordinates)) && (p)) - Issue problem for an empty block2.6.5.2; + Issue problem for an empty block1.1.6.5.2; while (indent < expected_indent) { parse_node *opening; if (blo_sp == 0) { - Record a misalignment of indentation2.6.3.3; + Record a misalignment of indentation1.1.6.3.3; indent = expected_indent; break; } @@ -660,199 +675,194 @@ gone awry.) expected_indent--; if (blstack_construct[blo_sp-1]->indent_subblocks) expected_indent--; opening = blstack_opening_phrase[--blo_sp]; - Insert end marker to match the opening of the block phrase2.6.5.1; + Insert end marker to match the opening of the block phrase1.1.6.5.1; }- -§2.6.3.2. Record a phrase within current block2.6.3.2 = +
+
- This code is used in §1.1.6, §1.1.6.3, §1.1.6.3.1.
§1.1.6.3.2. Record a phrase within current block1.1.6.3.2 =
if ((blo_sp > 0) && (blstack_stage[blo_sp-1] == 0) && (blstack_construct[blo_sp-1]->body_empty_except_for_subordinates)) { - Issue problem for non-case in a switch2.6.3.2.1; + Issue problem for non-case in a switch1.1.6.3.2.1; } just_opened_block = FALSE;--
- This code is used in §2.6.3 (twice).
§2.6.5.1. An end marker is a phrase like "end if" which matches the "if... begin" +
+
- This code is used in §1.1.6.3 (twice).
§1.1.6.5.1. An end marker is a phrase like "end if" which matches the "if... begin" above it: here we insert such a marker at a place where the source text indentation implicitly requires it.
-Insert end marker to match the opening of the block phrase2.6.5.1 = +
Insert end marker to match the opening of the block phrase1.1.6.5.1 =
- parse_node *implicit_end = RuleSubtrees::end_node(opening); + parse_node *implicit_end = ImperativeSubtrees::end_node(opening); implicit_end->next = prev->next; prev->next = implicit_end; prev = implicit_end;--
- This code is used in §2.6.5.
§2.6.3.3. Here we throw what amounts to an exception... +
+
- This code is used in §1.1.6.5.
§1.1.6.3.3. Here we throw what amounts to an exception...
-Record a misalignment of indentation2.6.3.3 = +
Record a misalignment of indentation1.1.6.3.3 =
indent_misalign = TRUE; if (first_misaligned_phrase == NULL) first_misaligned_phrase = p;- -§2.6.6. ...and catch it with something of a catch-all message: +
+§1.1.6.6. ...and catch it with something of a catch-all message:
-Issue problem message for misaligned indentation2.6.6 = +
Issue problem message for misaligned indentation1.1.6.6 =
if (suppress_further_problems == FALSE) { - LOG("$T\n", routine_node); - current_sentence = routine_node; + LOG("$T\n", imperative_node); + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); Problems::quote_source_eliding_begin(2, first_misaligned_phrase); - StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_MisalignedIndentation)); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_MisalignedIndentation)); Problems::issue_problem_segment( - "The phrase or rule definition %1 is written using the 'colon " - "and indentation' syntax for its 'if's, 'repeat's and 'while's, " - "where blocks of phrases grouped together are indented one " - "tab step inward from the 'if ...:' or similar phrase to which " - "they belong. But the tabs here seem to be misaligned, and I can't " - "determine the structure. The first phrase going awry in the " - "definition seems to be %2, in case that helps. %PThis sometimes " - "happens even when the code looks about right, to the eye, if rows " + "The phrase or rule definition %1 is written using the 'colon and indentation' " + "syntax for its 'if's, 'repeat's and 'while's, where blocks of phrases grouped " + "together are indented one tab step inward from the 'if ...:' or similar phrase " + "to which they belong. But the tabs here seem to be misaligned, and I can't " + "determine the structure. The first phrase going awry in the definition seems " + "to be %2, in case that helps. %P" + "This sometimes happens even when the code looks about right, to the eye, if rows " "of spaces have been used to indent phrases instead of tabs."); Problems::Using::diagnose_further(); Problems::issue_problem_end(); }--
- This code is used in §2.6.
+ -
- This code is used in §1.1.6.
Record an excess of indentation2.6.2.1 = +
Record an excess of indentation1.1.6.2.1 =
indent_overmuch = TRUE; if (first_overindented_phrase == NULL) first_overindented_phrase = p;--
- This code is used in §2.6.2.
+ -
- This code is used in §1.1.6.2.
Issue problem message for an excess of indentation2.6.7 = +
Issue problem message for an excess of indentation1.1.6.7 =
if (suppress_further_problems == FALSE) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); Problems::quote_source_eliding_begin(2, first_overindented_phrase); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_TooMuchIndentation)); Problems::issue_problem_segment( - "The phrase or rule definition %1 is written using tab indentations " - "to show how its phrases are to be grouped together. But the level " - "of indentation goes far too deep, reaching more than 25 tab stops " - "from the left margin."); + "The phrase or rule definition %1 is written using tab indentations to show how " + "its phrases are to be grouped together. But the level of indentation goes far " + "too deep, reaching more than 25 tab stops from the left margin."); Problems::issue_problem_end(); }--
- This code is used in §2.6.
§2.6.8. Issue problem message for run-ons within phrase definition2.6.8 = +
+
- This code is used in §1.1.6.
§1.1.6.8. Issue problem message for run-ons within phrase definition1.1.6.8 =
if (suppress_further_problems == FALSE) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); Problems::quote_source_eliding_begin(2, run_on_at); - StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_RunOnsInTabbedRoutine)); + StandardProblems::handmade_problem(Task::syntax_tree(), + _p_(PM_RunOnsInTabbedRoutine)); Problems::issue_problem_segment( - "The phrase or rule definition %1 is written using the 'colon " - "and indentation' syntax for its 'if's, 'repeat's and 'while's, " - "but that's only allowed if each phrase in the definition " - "occurs on its own line. So phrases like %2, which follow " + "The phrase or rule definition %1 is written using the 'colon and indentation' " + "syntax for its 'if's, 'repeat's and 'while's, but that's only allowed if each " + "phrase in the definition occurs on its own line. So phrases like %2, which follow " "directly on from the previous phrase, aren't allowed."); Problems::issue_problem_end(); }--
- This code is used in §2.6.
§2.6.5.2. It's a moot point whether the following should be incorrect syntax, but it +
+
- This code is used in §1.1.6.
§1.1.6.5.2. It's a moot point whether the following should be incorrect syntax, but it far more often happens as an accident than anything else, and it's hard to think of a sensible use.
-Issue problem for an empty block2.6.5.2 = +
Issue problem for an empty block1.1.6.5.2 =
if (suppress_further_problems == FALSE) { - LOG("$T\n", routine_node); - current_sentence = routine_node; + LOG("$T\n", imperative_node); + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); Problems::quote_source_eliding_begin(2, prev); Problems::quote_source_eliding_begin(3, p); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_EmptyIndentedBlock)); Problems::issue_problem_segment( - "The phrase or rule definition %1 is written using the 'colon " - "and indentation' syntax for its 'if's, 'repeat's and 'while's, " - "where blocks of phrases grouped together are indented one " - "tab step inward from the 'if ...:' or similar phrase to which " - "they belong. But the phrase %2, which ought to begin a block, " - "is immediately followed by %3 at the same or a lower indentation, " - "so the block seems to be empty - this must mean there has been " - "a mistake in indenting the phrases."); + "The phrase or rule definition %1 is written using the 'colon and indentation' " + "syntax for its 'if's, 'repeat's and 'while's, where blocks of phrases grouped " + "together are indented one tab step inward from the 'if ...:' or similar phrase " + "to which they belong. But the phrase %2, which ought to begin a block, is " + "immediately followed by %3 at the same or a lower indentation, so the block " + "seems to be empty - this must mean there has been a mistake in indenting the " + "phrases."); Problems::issue_problem_end(); }--
- This code is used in §2.6.5.
§2.6.3.2.1. Issue problem for non-case in a switch2.6.3.2.1 = +
+
- This code is used in §1.1.6.5.
§1.1.6.3.2.1. Issue problem for non-case in a switch1.1.6.3.2.1 =
if (suppress_further_problems == FALSE) { - current_sentence = routine_node; + current_sentence = imperative_node; Problems::quote_source_eliding_begin(1, current_sentence); Problems::quote_source_eliding_begin(2, p); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_NonCaseInIf)); Problems::issue_problem_segment( - "In the phrase or rule definition %1, the phrase %2 came as a " - "surprise since it was not a case in an 'if X is...' but was " - "instead some other miscellaneous instruction."); + "In the phrase or rule definition %1, the phrase %2 came as a surprise since " + "it was not a case in an 'if X is...' but was instead some other miscellaneous " + "instruction."); Problems::issue_problem_end(); }--
- This code is used in §2.6.3.2.
§2.6.3.1.1. Issue problem for an intermediate phrase not matching2.6.3.1.1 = +
+
- This code is used in §1.1.6.3.2.
§1.1.6.3.1.1. Issue problem for an intermediate phrase not matching1.1.6.3.1.1 =
if ((indent_misalign == FALSE) && (suppress_further_problems == FALSE)) { current_sentence = p; if (csp->subordinate_to == if_CSP) { - LOG("$T\n", routine_node); + LOG("$T\n", imperative_node); StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_MisalignedOtherwise), "this doesn't match a corresponding 'if'", - "as it must. An 'otherwise' must be vertically underneath the " - "'if' to which it corresponds, at the same indentation, and " - "if the 'otherwise' uses a colon to begin a block then the " - "'if' must do the same."); + "as it must. An 'otherwise' must be vertically underneath the 'if' to which " + "it corresponds, at the same indentation, and if the 'otherwise' uses a colon " + "to begin a block then the 'if' must do the same."); } if (csp->subordinate_to == switch_CSP) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_MisalignedCase), - "this seems to be misplaced since it is not a case within an " - "'if X is...'", - "as it must be. Each case must be placed one tab stop in from " - "the 'if X is...' to which it belongs, and the instructions " - "for what to do in that case should be one tab stop further in " - "still."); + "this seems to be misplaced since it is not a case within an 'if X is...'", + "as it must be. Each case must be placed one tab stop in from the 'if X " + "is...' to which it belongs, and the instructions for what to do in that " + "case should be one tab stop further in still."); }--
- This code is used in §2.6.3.1 (twice).
§2.6.3.1.2. Issue problem for an intermediate phrase out of sequence2.6.3.1.2 = +
+
- This code is used in §1.1.6.3.1 (twice).
§1.1.6.3.1.2. Issue problem for an intermediate phrase out of sequence1.1.6.3.1.2 =
@@ -861,35 +871,33 @@ think of a sensible use. if ((csp == default_case_CSP) || (csp == case_CSP)) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_DefaultCaseNotLast), "'otherwise' must be the last clause if an 'if ... is:'", - "and in particular it has to come after all the '-- V:' " - "case values supplied."); + "and in particular it has to come after all the '-- V:' case values supplied."); else StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_MisarrangedOtherwise), "this seems to be misplaced since it is out of sequence within its 'if'", - "with an 'otherwise if...' coming after the more general 'otherwise' " - "rather than before. (Note that an 'otherwise' or 'otherwise if' must " - "be vertically underneath the 'if' to which it corresponds, at the " - "same indentation."); + "with an 'otherwise if...' coming after the more general 'otherwise' rather " + "than before. (Note that an 'otherwise' or 'otherwise if' must be vertically " + "underneath the 'if' to which it corresponds, at the same indentation."); }--
- This code is used in §2.6.3.1.
§2.7. And after all that work, the routine's parse tree still consists only of a +
+
- This code is used in §1.1.6.3.1.
§1.1.7. And after all that work, the routine's parse tree still consists only of a linked list of nodes; but at least it now contains the same pattern of nodes whichever syntax is used. We finally make a meaningful tree out of it.
-(e) Structure the parse tree to match the use of control structures2.7 = +
(e) Structure the parse tree to match the use of control structures1.1.7 =
- parse_node *routine_list = routine_node->down; + parse_node *routine_list = imperative_node->down; parse_node *top_level = Node::new(CODE_BLOCK_NT); - routine_node->down = top_level; + imperative_node->down = top_level; - parse_node *attach_owners[MAX_BLOCK_NESTING+1]; - parse_node *attach_points[MAX_BLOCK_NESTING+1]; - control_structure_phrase *attach_csps[MAX_BLOCK_NESTING+1]; + parse_node *attach_owners[MAX_BLOCK_NESTING+1]; + parse_node *attach_points[MAX_BLOCK_NESTING+1]; + control_structure_phrase *attach_csps[MAX_BLOCK_NESTING+1]; int attach_point_sp = 0; push the top level code block onto the stack @@ -901,7 +909,7 @@ whichever syntax is used. We finally make a meaningful tree out of it. for (parse_node *pn = routine_list, *pn_prev = NULL; pn; pn_prev = pn, pn = pn->next) { unstring this node from the old list if (pn_prev) pn_prev->next = NULL; - Attach the node to the routine's growing parse tree2.7.1; + Attach the node to the routine's growing parse tree1.1.7.1; } if (overflow_point) { current_sentence = overflow_point; @@ -910,8 +918,8 @@ whichever syntax is used. We finally make a meaningful tree out of it. "perhaps because many have begun but not been properly ended?"); }--
- This code is used in §2.
§2.7.1. Attach the node to the routine's growing parse tree2.7.1 = +
+
- This code is used in §1.1.
§1.1.7.1. Attach the node to the routine's growing parse tree1.1.7.1 =
@@ -928,21 +936,21 @@ whichever syntax is used. We finally make a meaningful tree out of it. } } } - if (go_up) Move the attachment point up in the tree2.7.1.1; - Attach this latest node2.7.1.2; - if (go_down) Move the attachment point down in the tree2.7.1.3; + if (go_up) Move the attachment point up in the tree1.1.7.1.1; + Attach this latest node1.1.7.1.2; + if (go_down) Move the attachment point down in the tree1.1.7.1.3;--
- This code is used in §2.7.
§2.7.1.1. Move the attachment point up in the tree2.7.1.1 = +
+
- This code is used in §1.1.7.
§1.1.7.1.1. Move the attachment point up in the tree1.1.7.1.1 =
control_structure_phrase *superior_csp = attach_csps[attach_point_sp-1]; - if ((superior_csp) && (superior_csp->subordinate_to)) Pop the CSP stack2.7.1.1.1; - if (go_down == FALSE) Pop the CSP stack2.7.1.1.1; + if ((superior_csp) && (superior_csp->subordinate_to)) Pop the CSP stack1.1.7.1.1.1; + if (go_down == FALSE) Pop the CSP stack1.1.7.1.1.1;--
- This code is used in §2.7.1.
§2.7.1.2. Attach this latest node2.7.1.2 = +
+
- This code is used in §1.1.7.1.
§1.1.7.1.2. Attach this latest node1.1.7.1.2 =
@@ -951,8 +959,8 @@ whichever syntax is used. We finally make a meaningful tree out of it. to = attach_owners[attach_point_sp-1]; SyntaxTree::graft(Task::syntax_tree(), pn, to);--
- This code is used in §2.7.1.
§2.7.1.3. Move the attachment point down in the tree2.7.1.3 = +
+
- This code is used in §1.1.7.1.
§1.1.7.1.3. Move the attachment point down in the tree1.1.7.1.3 =
@@ -961,27 +969,27 @@ whichever syntax is used. We finally make a meaningful tree out of it. pn->down = Node::new(CODE_BLOCK_NT); next_attach_point = pn->down; } - Push the CSP stack2.7.1.3.1; + Push the CSP stack1.1.7.1.3.1;--
- This code is used in §2.7.1.
§2.7.1.1.1. It's an error to let this underflow, but we'll catch that problem later. +
+
- This code is used in §1.1.7.1.
§1.1.7.1.1.1. It's an error to let this underflow, but we'll catch that problem later.
-Pop the CSP stack2.7.1.1.1 = +
Pop the CSP stack1.1.7.1.1.1 =
if (attach_point_sp != 1) attach_point_sp--;--
- This code is used in §2.7.1.1 (twice).
§2.7.1.3.1. An overflow, however, we must catch right here. +
+
- This code is used in §1.1.7.1.1 (twice).
§1.1.7.1.3.1. An overflow, however, we must catch right here.
-Push the CSP stack2.7.1.3.1 = +
Push the CSP stack1.1.7.1.3.1 =
- if (attach_point_sp <= MAX_BLOCK_NESTING) { + if (attach_point_sp <= MAX_BLOCK_NESTING) { attach_owners[attach_point_sp] = pn; attach_csps[attach_point_sp] = csp; attach_points[attach_point_sp++] = next_attach_point; @@ -989,8 +997,8 @@ whichever syntax is used. We finally make a meaningful tree out of it. if (overflow_point == NULL) overflow_point = pn; }--
- This code is used in §2.7.1.3.
§2.8. We now have a neatly structured tree, so from here on anything we do will +
+
- This code is used in §1.1.7.1.3.
§1.1.8. We now have a neatly structured tree, so from here on anything we do will need a recursive procedure.
@@ -999,20 +1007,20 @@ of nonsense: "if" blocks with multiple "otherwise"s, for example. This is where we look for such mistakes. -(f) Police the structure of the parse tree2.8 = +
(f) Police the structure of the parse tree1.1.8 =
int n = problem_count; - RuleSubtrees::police_code_block(routine_node->down, NULL); - if (problem_count > n) LOG("Local parse tree: $T\n", routine_node); + ImperativeSubtrees::police_code_block(imperative_node->down, NULL); + if (problem_count > n) LOG("Local parse tree: $T\n", imperative_node);--
- This code is used in §2.
§3. Which recursively uses the following: +
+
- This code is used in §1.1.
§2. Which recursively uses the following:
-void RuleSubtrees::police_code_block(parse_node *block, control_structure_phrase *context) { +void ImperativeSubtrees::police_code_block(parse_node *block, control_structure_phrase *context) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { current_sentence = p; @@ -1020,8 +1028,8 @@ where we look for such mistakes. (prev_p)?Node::get_control_structure_used(prev_p):NULL; control_structure_phrase *csp = Node::get_end_control_structure_used(p); if ((csp) && (csp != prior)) { - if (prior == NULL) Issue problem for end without begin3.1 - else Issue problem for wrong sort of end3.2; + if (prior == NULL) Issue problem for end without begin2.1 + else Issue problem for wrong sort of end2.2; } csp = Node::get_control_structure_used(p); @@ -1029,45 +1037,44 @@ where we look for such mistakes. if (ControlStructures::opens_block(csp)) { if ((p->next == NULL) || (Node::get_end_control_structure_used(p->next) == NULL)) - Issue problem for begin without end3.3; + Issue problem for begin without end2.3; } else { if (context == NULL) - Choose a problem for a loose clause3.4 + Choose a problem for a loose clause2.4 else if (context != csp->subordinate_to) - Choose a problem for the wrong clause3.5 + Choose a problem for the wrong clause2.5 else if ((csp == otherwise_CSP) && (p->next)) - Choose a problem for otherwise not occurring last3.6 + Choose a problem for otherwise not occurring last2.6 else if ((csp == default_case_CSP) && (p->next)) - Issue a problem for the default case not occurring last3.7; + Issue a problem for the default case not occurring last2.7; } } - if (p->down) RuleSubtrees::police_code_block(p, csp); + if (p->down) ImperativeSubtrees::police_code_block(p, csp); } }-§3.1. These used to be much-seen problem messages, until Inform moved to Pythonesque +
§2.1. These used to be much-seen problem messages, until Inform moved to Pythonesque structure-by-indentation. Nowadays "end if", "end while" and such are automatically inserted into the stream of commands, always in the right place, and always passing these checks. But the problem messages are kept for the sake of old-format source text, and for refuseniks.
-Issue problem for end without begin3.1 = +
Issue problem for end without begin2.1 =
StandardProblems::sentence_problem_with_note(Task::syntax_tree(), _p_(PM_EndWithoutBegin), "this is an 'end' with no matching 'begin'", - "which should not happen: every phrase like 'if ... begin;' " - "should eventually be followed by its bookend 'end if'. " - "It makes no sense to have an 'end ...' on its own.", - "Perhaps the problem is actually that you opened several " - "such begin... end 'blocks' and accidentally closed them " - "once too many? This is very easily done."); + "which should not happen: every phrase like 'if ... begin;' should eventually be " + "followed by its bookend 'end if'. It makes no sense to have an 'end ...' on its " + "own.", + "Perhaps the problem is actually that you opened several such begin... end " + "'blocks' and accidentally closed them once too many? This is very easily done.");--
- This code is used in §3.
§3.2. Issue problem for wrong sort of end3.2 = +
+
- This code is used in §2.
§2.2. Issue problem for wrong sort of end2.2 =
@@ -1080,20 +1087,19 @@ of old-format source text, and for refuseniks. "finishing the block you began with %3."); Problems::issue_problem_end();--
- This code is used in §3.
§3.3. Issue problem for begin without end3.3 = +
+
- This code is used in §2.
§2.3. Issue problem for begin without end2.3 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BeginWithoutEnd), - "the definition of the phrase ended with no matching 'end' for " - "this 'begin'", - "bearing in mind that every begin must have a matching end, and " - "that the one most recently begun must be the one first to end. For " - "instance, 'if ... begin' must have a matching 'end if'."); + "the definition of the phrase ended with no matching 'end' for this 'begin'", + "bearing in mind that every begin must have a matching end, and that the one " + "most recently begun must be the one first to end. For instance, 'if ... begin' " + "must have a matching 'end if'.");--
- This code is used in §3.
§3.4. Choose a problem for a loose clause3.4 = +
+
- This code is used in §2.
§2.4. Choose a problem for a loose clause2.4 =
@@ -1103,19 +1109,18 @@ of old-format source text, and for refuseniks. "which must be wrong."); else if (csp == otherwise_if_CSP) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_OtherwiseIfMisplaced), - "the 'otherwise if' clause here seems not to be occurring inside " - "a large 'if'", - "and seems to be freestanding instead. (Though 'otherwise ...' can " - "usually be used after simple one-line 'if's to provide an alternative " - "course of action, 'otherwise if...' is a different matter, and is " - "used to divide up larger-scale instructions.)"); + "the 'otherwise if' clause here seems not to be occurring inside a large 'if'", + "and seems to be freestanding instead. (Though 'otherwise ...' can usually " + "be used after simple one-line 'if's to provide an alternative course of action, " + "'otherwise if...' is a different matter, and is used to divide up larger-scale " + "instructions.)"); else StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible), "this clause can't occur outside of a control phrase", "which suggests that the structure of this routine is wrong.");--
- This code is used in §3.
§3.5. Choose a problem for the wrong clause3.5 = +
+
- This code is used in §2.
§2.5. Choose a problem for the wrong clause2.5 =
@@ -1124,16 +1129,16 @@ of old-format source text, and for refuseniks. Problems::quote_wide_text(2, context->keyword); StandardProblems::handmade_problem(Task::syntax_tree(), _p_(PM_OtherwiseInNonIf)); Problems::issue_problem_segment( - "The %1 here did not make sense inside a " - "'%2' structure: it's provided for 'if' (or 'unless')."); + "The %1 here did not make sense inside a '%2' structure: it's provided for 'if' " + "(or 'unless')."); Problems::issue_problem_end(); } else StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible), "this clause is wrong for the phrase containing it", "which suggests that the structure of this routine is wrong.");--
- This code is used in §3.
§3.6. Choose a problem for otherwise not occurring last3.6 = +
+
- This code is used in §2.
§2.6. Choose a problem for otherwise not occurring last2.6 =
@@ -1148,27 +1153,26 @@ of old-format source text, and for refuseniks. } if (doubled) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_DoubleOtherwise), - "that makes two unconditional 'otherwise' or 'else' clauses " - "for this 'if'", - "which is forbidden since 'otherwise' is meant to be a single " - "(optional) catch-all clause at the end."); + "that makes two unconditional 'otherwise' or 'else' clauses for this 'if'", + "which is forbidden since 'otherwise' is meant to be a single (optional) " + "catch-all clause at the end."); else if (oi) StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_OtherwiseIfAfterOtherwise), "this seems to be misplaced since it is out of sequence within its 'if'", - "with an 'otherwise if...' coming after the more general 'otherwise' " - "rather than before. (If there's an 'otherwise' clause, it has to be " - "the last clause of the 'if'.)"); + "with an 'otherwise if...' coming after the more general 'otherwise' rather " + "than before. (If there's an 'otherwise' clause, it has to be the last clause " + "of the 'if'.)"); else StandardProblems::sentence_problem(Task::syntax_tree(), _p_(BelievedImpossible), "'otherwise' must be the last clause", "but it seems not to be.");--
- This code is used in §3.
§3.7. This shouldn't happen because the switch construct requires Python syntax +
+
- This code is used in §2.
§2.7. This shouldn't happen because the switch construct requires Python syntax and the structure of that was checked at indentation time, but just in case.
-Issue a problem for the default case not occurring last3.7 = +
Issue a problem for the default case not occurring last2.7 =
@@ -1176,30 +1180,30 @@ and the structure of that was checked at indentation time, but just in case. "'otherwise' must be the last clause", "which must be wrong.");--
- This code is used in §3.
§2.9. The tree is now known to be correctly structured, and there are no possible +
+
- This code is used in §2.
§1.1.9. The tree is now known to be correctly structured, and there are no possible problem messages left to issue. It's therefore safe to begin rearranging it. We'll first eliminate one whole construction: "otherwise if whatever: ..." can now become "otherwise: if whatever: ...".
-(g) Optimise out the otherwise if nodes2.9 = +
(g) Optimise out the otherwise if nodes1.1.9 =
int n = problem_count; - RuleSubtrees::purge_otherwise_if(routine_node->down); - if (problem_count > n) LOG("Local parse tree: $T\n", routine_node); + ImperativeSubtrees::purge_otherwise_if(imperative_node->down); + if (problem_count > n) LOG("Local parse tree: $T\n", imperative_node);--
- This code is used in §2.
§2.10. We made a similar manoeuvre above, but for one-line "otherwise do something" +
+
- This code is used in §1.1.
§1.1.10. We made a similar manoeuvre above, but for one-line "otherwise do something" phrases following one-line "if", not for the wider case of "otherwise if". We didn't handle this back then because to do so would have made it impossible to issue good problem messages for failures to use "otherwise if" correctly.
-void RuleSubtrees::purge_otherwise_if(parse_node *block) { +void ImperativeSubtrees::purge_otherwise_if(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { if (Node::get_control_structure_used(p) == otherwise_if_CSP) { parse_node *former_contents = p->down; @@ -1226,59 +1230,59 @@ to issue good problem messages for failures to use "otherwise if" correctly. any further "otherwise if" or "otherwise" nodes after p follow p->down->next = former_successors; } - if (p->down) RuleSubtrees::purge_otherwise_if(p); + if (p->down) ImperativeSubtrees::purge_otherwise_if(p); } }-§2.11. End nodes are now redundant: maybe they got here as explicit "end if" phrases +
§1.1.11. End nodes are now redundant: maybe they got here as explicit "end if" phrases in the source text, or maybe they were auto-inserted by the indentation reader, but now that the structure is known to be correct they serve no further purpose. We remove them.
-(h) Remove any end markers as no longer necessary2.11 = +
(h) Remove any end markers as no longer necessary1.1.11 =
- RuleSubtrees::purge_end_markers(routine_node->down); + ImperativeSubtrees::purge_end_markers(imperative_node->down);-- +
- This code is used in §2.
+
- This code is used in §1.1.
-void RuleSubtrees::purge_end_markers(parse_node *block) { +void ImperativeSubtrees::purge_end_markers(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { if (Node::get_end_control_structure_used(p)) { if (prev_p) prev_p->next = p->next; else block->down = p->next; } - if (p->down) RuleSubtrees::purge_end_markers(p); + if (p->down) ImperativeSubtrees::purge_end_markers(p); } }-§2.13. The "begin" keyword at the end of control constructs in the old-style syntax +
§1.1.13. The "begin" keyword at the end of control constructs in the old-style syntax can now be removed, too.
-(i) Remove any begin markers as no longer necessary2.13 = +
(i) Remove any begin markers as no longer necessary1.1.13 =
- RuleSubtrees::purge_begin_markers(routine_node->down); + ImperativeSubtrees::purge_begin_markers(imperative_node->down);-- +
- This code is used in §2.
+
- This code is used in §1.1.
-void RuleSubtrees::purge_begin_markers(parse_node *block) { +void ImperativeSubtrees::purge_begin_markers(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { if (Node::get_control_structure_used(p)) if (<phrase-beginning-block>(Node::get_text(p))) Node::set_text(p, GET_RW(<phrase-beginning-block>, 1)); - if (p->down) RuleSubtrees::purge_begin_markers(p); + if (p->down) ImperativeSubtrees::purge_begin_markers(p); } }-§2.15. This all makes a nice tree, but it has the defect that the statements heading +
§1.1.15. This all makes a nice tree, but it has the defect that the statements heading block-opening phrases (the ifs, whiles, repeats) have child nodes (the blocks of code consequent on them). We want them to be leaves for now, so that we can append statement-parsing data underneath them later. So we insert blank @@ -1286,17 +1290,17 @@ code block nodes to mark these phrases, and transfer the control structure annotations to them.
-(j) Insert code block nodes so that nodes needing to be parsed are childless2.15 = +
(j) Insert code block nodes so that nodes needing to be parsed are childless1.1.15 =
- RuleSubtrees::insert_cb_nodes(routine_node->down); + ImperativeSubtrees::insert_cb_nodes(imperative_node->down);-- +
- This code is used in §2.
+
- This code is used in §1.1.
-void RuleSubtrees::insert_cb_nodes(parse_node *block) { +void ImperativeSubtrees::insert_cb_nodes(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { if (ControlStructures::opens_block(Node::get_control_structure_used(p))) { parse_node *blank_cb_node = Node::new(CODE_BLOCK_NT); @@ -1310,24 +1314,24 @@ annotations to them. if (prev_p) prev_p->next = blank_cb_node; else block->down = blank_cb_node; p = blank_cb_node; } - if (p->down) RuleSubtrees::insert_cb_nodes(p); + if (p->down) ImperativeSubtrees::insert_cb_nodes(p); } }- -(k) Insert instead marker nodes2.17 = +
(k) Insert instead marker nodes1.1.17 =
- RuleSubtrees::read_instead_markers(routine_node->down); + ImperativeSubtrees::read_instead_markers(imperative_node->down);-- +
- This code is used in §2.
+
- This code is used in §1.1.
-void RuleSubtrees::read_instead_markers(parse_node *block) { +void ImperativeSubtrees::read_instead_markers(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { if (<instead-keyword>(Node::get_text(p))) { Node::set_text(p, GET_RW(<instead-keyword>, 1)); @@ -1336,24 +1340,24 @@ annotations to them. instead_node->next = p->next; p->next = instead_node; } - if (p->down) RuleSubtrees::read_instead_markers(p); + if (p->down) ImperativeSubtrees::read_instead_markers(p); } }- -(l) Break up say phrases2.19 = +
(l) Break up say phrases1.1.19 =
- RuleSubtrees::break_up_says(routine_node->down); + ImperativeSubtrees::break_up_says(imperative_node->down);-- +
- This code is used in §2.
+
- This code is used in §1.1.
-void RuleSubtrees::break_up_says(parse_node *block) { +void ImperativeSubtrees::break_up_says(parse_node *block) { for (parse_node *p = block->down, *prev_p = NULL; p; prev_p = p, p = p->next) { int sf = NO_SIGF; wording W = Node::get_text(p); @@ -1371,7 +1375,7 @@ annotations to them. if (prev_p) prev_p->next = blank_cb_node; else block->down = blank_cb_node; current_sentence = p; - RuleSubtrees::unroll_says(blank_cb_node, W, 0); + ImperativeSubtrees::unroll_says(blank_cb_node, W, 0); p = blank_cb_node; break; } @@ -1383,32 +1387,34 @@ annotations to them. break; } } - if (p->down) RuleSubtrees::break_up_says(p); + if (p->down) ImperativeSubtrees::break_up_says(p); } } -void RuleSubtrees::unroll_says(parse_node *cb_node, wording W, int depth) { +void ImperativeSubtrees::unroll_says(parse_node *cb_node, wording W, int depth) { while (<phrase-with-comma-notation>(W)) { wording AW = GET_RW(<phrase-with-comma-notation>, 1); wording BW = GET_RW(<phrase-with-comma-notation>, 2); W = AW; - Bite off a say term4.1; + Bite off a say term3.1; W = BW; } - Bite off a say term4.1; + Bite off a say term3.1; }-§4.1. Bite off a say term4.1 = +
§3.1. Bite off a say term3.1 =
- if ((Wordings::length(W) > 1) || (Wide::cmp(Lexer::word_text(Wordings::first_wn(W)), L"\"\"") != 0)) { - if ((Wordings::length(W) == 1) && (Vocabulary::test_flags(Wordings::first_wn(W), TEXTWITHSUBS_MC)) && (depth == 0)) { + if ((Wordings::length(W) > 1) || + (Wide::cmp(Lexer::word_text(Wordings::first_wn(W)), L"\"\"") != 0)) { + if ((Wordings::length(W) == 1) && + (Vocabulary::test_flags(Wordings::first_wn(W), TEXTWITHSUBS_MC)) && (depth == 0)) { wchar_t *p = Lexer::word_raw_text(Wordings::first_wn(W)); - Check that substitution does not contain suspicious punctuation4.1.1; + Check that substitution does not contain suspicious punctuation3.1.1; wording A = Feeds::feed_C_string_expanding_strings(p); if (<verify-expanded-text-substitution>(A)) - RuleSubtrees::unroll_says(cb_node, A, depth+1); + ImperativeSubtrees::unroll_says(cb_node, A, depth+1); } else { parse_node *say_term_node = Node::new(INVOCATION_LIST_SAY_NT); Node::set_text(say_term_node, W); @@ -1416,42 +1422,41 @@ annotations to them. } }--
- This code is used in §4 (twice).
§4.1.1. Check that substitution does not contain suspicious punctuation4.1.1 = +
+
- This code is used in §3 (twice).
§3.1.1. Check that substitution does not contain suspicious punctuation3.1.1 =
int k, sqb = 0; for (k=0; p[k]; k++) { switch (p[k]) { - case '[': sqb++; if (sqb > 1) Issue problem message for nested substitution4.1.1.2; break; - case ']': sqb--; if (sqb < 0) Issue problem message for unopened substitution4.1.1.4; break; + case '[': sqb++; if (sqb > 1) Issue problem message for nested substitution3.1.1.2; break; + case ']': sqb--; if (sqb < 0) Issue problem message for unopened substitution3.1.1.4; break; case ':': if ((k>0) && (Characters::isdigit(p[k-1])) && (Characters::isdigit(p[k+1]))) break; case ';': - if (sqb > 0) Issue PM_TSWithPunctuation problem4.1.1.6; + if (sqb > 0) Issue PM_TSWithPunctuation problem3.1.1.6; break; case ',': - if (sqb > 0) Issue problem message for comma in a substitution4.1.1.1; + if (sqb > 0) Issue problem message for comma in a substitution3.1.1.1; break; } } - if (sqb != 0) Issue problem message for unclosed substitution4.1.1.3; + if (sqb != 0) Issue problem message for unclosed substitution3.1.1.3;--
- This code is used in §4.1.
§4.1.1.1. And the more specialised: +
+
- This code is used in §3.1.
§3.1.1.1. And the more specialised:
-Issue problem message for comma in a substitution4.1.1.1 = +
Issue problem message for comma in a substitution3.1.1.1 =
Strings::TextSubstitutions::it_is_not_worth_adding(); StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TSWithComma), "a substitution contains a comma ','", - "which is against the rules, because 'say' is a special phrase in " - "which the comma divides items in a list of things to say, and so it " - "loses its ordinary meanings. Because of this, no text substitution " - "can contain a comma. " + "which is against the rules, because 'say' is a special phrase in which the comma " + "divides items in a list of things to say, and so it loses its ordinary meanings. " + "Because of this, no text substitution can contain a comma. " "(If you're trying to use a value produced by a phrase with a phrase " "option - say 'the best route from A to B, using even locked doors' - " "you'll need to put this in a 'let' variable first and then say that, " @@ -1459,8 +1464,8 @@ annotations to them. Strings::TextSubstitutions::it_is_worth_adding(); return;--
- This code is used in §4.1.1.
§4.1.1.2. Issue problem message for nested substitution4.1.1.2 = +
+
- This code is used in §3.1.1.
§3.1.1.2. Issue problem message for nested substitution3.1.1.2 =
@@ -1469,22 +1474,21 @@ annotations to them. (p[k+5] == 'o') && (p[k+6] == 'd') && (p[k+7] == 'e') && (p[k+8] == ' ')) { StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_NestedUSubstitution), "the text here contains one substitution '[...]' inside another", - "which is not allowed. Actually, it looks as if you might have got " - "into this by typing an exotic character as part of the name of a " - "text substitution - those get rewritten automatically as '[unicode N]' " - "for the appropriate Unicode character code number N. Either way - " - "this isn't allowed."); + "which is not allowed. Actually, it looks as if you might have got into this " + "by typing an exotic character as part of the name of a text substitution - " + "those get rewritten automatically as '[unicode N]' for the appropriate Unicode " + "character code number N. Either way - this isn't allowed."); } else { StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_NestedSubstitution), "the text here contains one substitution '[...]' inside another", - "which is not allowed. (If you just wanted a literal open and closed " - "square bracket, use '[bracket]' and '[close bracket]'.)"); + "which is not allowed. (If you just wanted a literal open and closed square " + "bracket, use '[bracket]' and '[close bracket]'.)"); } Strings::TextSubstitutions::it_is_worth_adding(); return;--
- This code is used in §4.1.1.
§4.1.1.3. Issue problem message for unclosed substitution4.1.1.3 = +
+
- This code is used in §3.1.1.
§3.1.1.3. Issue problem message for unclosed substitution3.1.1.3 =
@@ -1492,61 +1496,60 @@ annotations to them. StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_UnclosedSubstitution), "the text here uses an open square bracket '[', which opens a substitution " "in the text, but doesn't close it again", - "so that the result is malformed. (If you just wanted a literal open " - "square bracket, use '[bracket]'.)"); + "so that the result is malformed. (If you just wanted a literal open square " + "bracket, use '[bracket]'.)"); Strings::TextSubstitutions::it_is_worth_adding(); return;--
- This code is used in §4.1.1.
§4.1.1.4. Issue problem message for unopened substitution4.1.1.4 = +
+
- This code is used in §3.1.1.
§3.1.1.4. Issue problem message for unopened substitution3.1.1.4 =
Strings::TextSubstitutions::it_is_not_worth_adding(); StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_UnopenedSubstitution), - "the text here uses a close square bracket ']', which closes a substitution " - "in the text, but never actually opened it", - "with a matching '['. (If you just wanted a literal close square bracket, " - "use '[close bracket]'.)"); + "the text here uses a close square bracket ']', which closes a substitution in the " + "text, but never actually opened it", + "with a matching '['. (If you just wanted a literal close square bracket, use " + "'[close bracket]'.)"); Strings::TextSubstitutions::it_is_worth_adding(); return;--
- This code is used in §4.1.1.
§4.1.1.5. Something devious happens when production (b) of <s-say-phrase> is matched. -Double-quoted text is literal if it contains no square brackets, but is -expanded if it includes text substitutions in squares. When (b) matches, -Inform expands a text such as +
+
- This code is used in §3.1.1.
§3.1.1.5. Something devious happens when text following a "say" is found. Double-quoted text +is literal if it contains no square brackets, but is expanded if it includes text +substitutions in squares. Thus:
-"Look, [the noun] said."
into: +
becomes:
-"Look, ", the noun, " said."
and then re-parses the result with the following nonterminal; note that we -make sure commas are used correctly before handing back to <s-say-phrase> -to parse the list. +
This is then re-parsed with the following nonterminal; note that we report any +problem with misuse of commas — really, of square brackets — before handing back +to <s-say-phrase> to parse the list.
<verify-expanded-text-substitution> ::= - *** . *** | ==> Issue PM_TSWithPunctuation problem4.1.1.6; ==> { fail } - , *** | ==> Issue PM_EmptySubstitution problem4.1.1.5.1; ==> { fail } - *** , | ==> Issue PM_EmptySubstitution problem4.1.1.5.1; ==> { fail } - *** , , *** | ==> Issue PM_EmptySubstitution problem4.1.1.5.1; ==> { fail } - ... + *** . *** | ==> Issue PM_TSWithPunctuation problem3.1.1.6; ==> { fail }; + , *** | ==> Issue PM_EmptySubstitution problem3.1.1.5.1; ==> { fail }; + *** , | ==> Issue PM_EmptySubstitution problem3.1.1.5.1; ==> { fail }; + *** , , *** | ==> Issue PM_EmptySubstitution problem3.1.1.5.1; ==> { fail }; + ... ==> { -, - }-
- This is Preform grammar, not regular C code.
§4.1.1.6. So now just the problem messages: +
§3.1.1.6. So now just the problem messages:
-Issue PM_TSWithPunctuation problem4.1.1.6 = +
Issue PM_TSWithPunctuation problem3.1.1.6 =
@@ -1556,11 +1559,11 @@ to parse the list. "which suggests that a close square bracket ']' may have gone astray."); Strings::TextSubstitutions::it_is_worth_adding();- - + -Issue PM_EmptySubstitution problem4.1.1.5.1 = +
Issue PM_EmptySubstitution problem3.1.1.5.1 =
@@ -1570,13 +1573,12 @@ to parse the list. "which is not allowed. To say nothing - well, say nothing."); Strings::TextSubstitutions::it_is_worth_adding();--
- This code is used in §4.1.1.5 (three times).
§5. That just leaves one utility routine, for manufacturing end nodes which -match a given begin node. +
+
- This code is used in §3.1.1.5 (three times).
§4. The following manufactures end nodes to match a given begin node.
-parse_node *RuleSubtrees::end_node(parse_node *opening) { +parse_node *ImperativeSubtrees::end_node(parse_node *opening) { parse_node *implicit_end = Node::new(INVOCATION_LIST_NT); Node::set_end_control_structure_used(implicit_end, Node::get_control_structure_used(opening)); @@ -1586,7 +1588,7 @@ match a given begin node. }