To include Inform 6 code almost verbatim in the output, as instructed by low-level Inform 7 sentences.
§1. Inclusions are a very low-level language feature: rather like the way some C compilers, such as gcc, allow assembly-language code to be inserted at crucial points in the middle of C, so Inform 7 allows fragments of I6 code to be "included". Note that this is different from the ability to define phrases using I6: what we're talking about here is the ability to add I6 material in Class or Object definitions, or simply in between other declarations in the I6 output, but always outside of a routine.
typedef struct i6_inclusion_matter { struct parse_node *material_to_include; normally an I6 escape (- ... -) struct inference_subject *infs_to_include_with; typically an object or class definition CLASS_DEFINITION } i6_inclusion_matter;
- The structure i6_inclusion_matter is private to this section.
§2. Inclusions are primitive things, but fine control is needed over exactly where they go.
define SEGMENT_LEVEL_INC 1 before, instead of, or after a segment of I6 template define SECTION_LEVEL_INC 2 before, instead of, or after a section of I6 template define WHEN_DEFINING_INC 3 as part of an Object or Class definition define AS_PREFORM_INC 4 include it not as I6, but as Preform grammar
§3. Some variables used only in parsing inclusion instructions:
int inclusion_side, section_inclusion_wn, segment_inclusion_wn;
§4. Include sentences are a way to merge lower-level programming, from another language, into Inform source text. They are intended only as a last resort, though seasoned I6 hackers tend to reach for them a little sooner than that.
A sentence typically takes the form:
Include (- ... -) when defining a thing.
and the following grammar defines the "when defining a thing" end.
<inform6-inclusion-location> ::= <inclusion-side> {<quoted-text-without-subs>} | ==> Note segment-level inclusion4.1 <inclusion-side> {<quoted-text-without-subs>} in {<quoted-text-without-subs>} | ==> Note section-level inclusion4.2 when defining <s-type-expression> | ==> { WHEN_DEFINING_INC, -, <<parse_node:s>> = RP[1] } when defining ... | ==> Issue PM_WhenDefiningUnknown problem4.3 before the library | ==> Issue PM_BeforeTheLibrary problem4.4 in the preform grammar ==> { AS_PREFORM_INC, - } <inclusion-side> ::= before | ==> { BEFORE_LINK_STAGE, - } instead of | ==> { INSTEAD_LINK_STAGE, - } after ==> { AFTER_LINK_STAGE, - }
- This is Preform grammar, not regular C code.
§4.1. Note segment-level inclusion4.1 =
inclusion_side = R[1]; segment_inclusion_wn = R[2]; ==> { SEGMENT_LEVEL_INC, - };
- This code is used in §4.
§4.2. Note section-level inclusion4.2 =
inclusion_side = R[1]; section_inclusion_wn = R[2]; segment_inclusion_wn = R[3]; ==> { SEGMENT_LEVEL_INC, - };
- This code is used in §4.
§4.3. Issue PM_WhenDefiningUnknown problem4.3 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_WhenDefiningUnknown), "I do not understand what definition you're referring to", "so I can't make an Inform 6 inclusion there.");
- This code is used in §4.
§4.4. Issue PM_BeforeTheLibrary problem4.4 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BeforeTheLibrary), "this syntax was withdrawn in January 2008", "in favour of a more finely controlled I6 inclusion command. The effect " "you want can probably be achieved by writing 'after \"Definitions.i6t\".' " "instead of 'before the library.'");
- This code is used in §4.
§5. Note that although Preform inclusions are syntactically like I6 inclusions, and share the grammar above, they're nevertheless a different thing and aren't handled here: if we see one, we ignore it.
void Config::Inclusions::inform_6_inclusion(parse_node *PN) { current_sentence = PN; wording IW = Node::get_text(PN); skip to the instructions IW = Wordings::trim_first_word(Wordings::trim_first_word(Wordings::trim_first_word(IW))); if (Wordings::empty(IW)) There are no specific instructions about where it goes5.1; int problem = TRUE; if (<inform6-inclusion-location>(IW)) { problem = FALSE; switch (<<r>>) { case SEGMENT_LEVEL_INC: It's positioned with respect to a template segment5.2; break; case SECTION_LEVEL_INC: It's positioned with respect to a template section5.3; break; case WHEN_DEFINING_INC: It's positioned in the middle of a class or object definition5.4; break; case AS_PREFORM_INC: return; } } if (problem) Issue problem message for bad inclusion instructions5.5; }
§5.1. In the absence of any instructions, we emulate this:
Include ... before "I6 Inclusions" in "Output.i6t".
Note that the inclusion side 1 means "before": see the grammar above. (Though "after" would probably have worked just as well.)
The actual output of I6 material is going to be done by the Template code, and what we do here is simply to give it instructions to do so at the appropriate time.
There are no specific instructions about where it goes5.1 =
Config::Inclusions::new_intervention(AFTER_LINK_STAGE, I"Output.i6t", I"I6 Inclusions", Lexer::word_raw_text(Wordings::first_wn(Node::get_text(PN)) + 2), NULL); return;
- This code is used in §5.
§5.2. It's positioned with respect to a template segment5.2 =
Word::dequote(segment_inclusion_wn); TEMPORARY_TEXT(seg) WRITE_TO(seg, "%W", Wordings::one_word(segment_inclusion_wn)); Config::Inclusions::new_intervention(inclusion_side, seg, NULL, Lexer::word_raw_text(Wordings::first_wn(Node::get_text(PN)) + 2), NULL); DISCARD_TEXT(seg)
- This code is used in §5.
§5.3. It's positioned with respect to a template section5.3 =
Word::dequote(section_inclusion_wn); Word::dequote(segment_inclusion_wn); TEMPORARY_TEXT(sec) TEMPORARY_TEXT(seg) WRITE_TO(sec, "%W", Wordings::one_word(section_inclusion_wn)); WRITE_TO(seg, "%W", Wordings::one_word(segment_inclusion_wn)); Config::Inclusions::new_intervention(inclusion_side, seg, sec, Lexer::word_raw_text(Wordings::first_wn(Node::get_text(PN)) + 2), NULL); DISCARD_TEXT(sec) DISCARD_TEXT(seg)
- This code is used in §5.
§5.4. When it comes to class and object definitions, we don't give the Template code instructions; we remember what's needed ourselves:
It's positioned in the middle of a class or object definition5.4 =
parse_node *spec = <<parse_node:s>>; inference_subject *infs = InferenceSubjects::from_specification(spec); if (infs) { i6_inclusion_matter *inclm = CREATE(i6_inclusion_matter); inclm->material_to_include = PN; inclm->infs_to_include_with = infs; } else problem = TRUE;
- This code is used in §5.
§5.5. Issue problem message for bad inclusion instructions5.5 =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_BadI6Inclusion), "this is not a form of I6 code inclusion I recognise", "because the clause at the end telling me where to put the code " "excerpt is not one of the possibilities I know. The clause can " "either be blank (in which case I'll find somewhere sensible to " "put it), or 'when defining' plus the name of an object or kind " "of object, or 'before', 'instead of' or 'after' a double-quoted " "name of a template layer segment, or of a part of one. For " "instance, 'before \"Parser.i6t\".' or 'after \"Pronouns\" in " "\"Language.i6t\".'");
- This code is used in §5.
void Config::Inclusions::new_intervention(int stage, text_stream *segment, text_stream *part, wchar_t *i6, text_stream *seg) { text_stream *T = NULL; if (i6) { T = Str::new(); I6T::interpret_i6t(T, i6, -1); } Emit::intervention(stage, segment, part, T, seg); }
§7. The following is our opportunity to redeem those inclusion-in-definitions requests, which, again, we do by instructing the Template code.
void Config::Inclusions::compile_inclusions_for_subject(OUTPUT_STREAM, inference_subject *infs) { i6_inclusion_matter *inclm; LOOP_OVER (inclm, i6_inclusion_matter) if (inclm->infs_to_include_with == infs) { I6T::interpret_i6t(OUT, Lexer::word_raw_text(Wordings::first_wn(Node::get_text(inclm->material_to_include)) + 2), -1); WRITE("\n"); } }