Translating kinds to and from textual descriptions.
§1. The K-grammar. This is a Preform grammar for textual descriptions of kinds. In effect it's a mini-language of its own, with a specification closer to traditional computer-science norms than Inform's free-wheeling approach — this is not an accident. It allows for awkward functional-programming needs in a way which vaguer natural language syntax would not.
All K-grammar nonterminals begin with the "k-" prefix, and their pointer results are to kind structures.
§2. For speed, we parse some kind names as single words, and others as common nouns, which is slower:
define KIND_SLOW_MC 0x00000008 e.g., weight define KIND_FAST_MC 0x01000000 number, text, relation, rule, ...
§3. The K-grammar actually has two modes: normal, and phrase-token-mode. Normal mode is aptly named: it's almost always the one we're using. Phrase token mode is used only when parsing definitions of phrases, like so:
To repeat with (LV - nonexisting K variable) running from (V1 - arithmetic value of kind K) to (V2 - K)
Here the tokens "nonexisting K variable" and so on are parsed as specifications, but in such a way that any kinds mentioned are parsed in phrase-token-mode. The difference is that this enables them to refer to kind variables such as K which are still being defined; in normal mode, that would only be allowed if K already existed.
define NORMAL_KIND_PARSING 1 define PHRASE_TOKEN_KIND_PARSING 2
int kind_parsing_mode = NORMAL_KIND_PARSING;
§4. This tests which mode we are in, consuming no words:
<if-parsing-phrase-tokens> internal 0 { if (kind_parsing_mode != NORMAL_KIND_PARSING) return TRUE; ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§5. And the following internal is in fact only <k-kind> but inside phrase token parsing mode; it's used when parsing the kind to be decided by a phrase (which, like phrase tokens, can involve the variables).
<k-kind-prototype> internal { int s = kind_parsing_mode; kind_parsing_mode = PHRASE_TOKEN_KIND_PARSING; int t = <k-kind>(W); kind_parsing_mode = s; if (t) { ==> { -, <<rp>> }; return TRUE; } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§6. And here is that "name of kind..." construction, which is valid only in phrase tokens.
<k-kind-as-name-token> ::= ( <k-kind-as-name-token> ) | ==> { pass 1 } name of kind of <k-kind-abbreviating> | ==> { pass 1 } name of kind <k-kind-abbreviating> | ==> { pass 1 } name of kind of ... | ==> { -, NULL } name of kind ... ==> { -, NULL } <k-kind-abbreviating> ::= ( <k-kind-abbreviating> ) | ==> { pass 1 } <k-kind-of-kind> <k-formal-variable> | ==> { -, Kinds::var_construction(R[2], RP[1]) } <k-kind> ==> { pass 1 }
- This is Preform grammar, not regular C code.
§7. So now we can begin properly. Every valid kind matches <k-kind>:
<k-kind> ::= ( <k-kind> ) | ==> { pass 1 } ^<if-parsing-phrase-tokens> <k-kind-variable> | ==> { pass 2 } <if-parsing-phrase-tokens> <k-variable-definition> | ==> { pass 2 } <k-base-kind> | ==> { pass 1 } <k-irregular-kind-construction> | ==> { pass 1 } <k-kind-construction> ==> { pass 1 }
- This is Preform grammar, not regular C code.
§8. And, as a convenient shorthand:
<k-kind-articled> ::= <indefinite-article> <k-kind> | ==> { pass 2 } <k-kind> ==> { pass 1 }
- This is Preform grammar, not regular C code.
§9. In phrase-token mode, kind variables are treated as formal symbols, not as the kinds which are their current values:
<k-variable-definition> ::= <k-formal-variable> | ==> { pass 1 } <k-kind-of-kind> of kind <k-formal-variable> ==> { -, Kinds::var_construction(R[2], RP[1]) }
- This is Preform grammar, not regular C code.
§10. Some base kinds with one-word names have that word flagged with a direct pointer to the kind, for speed of parsing. Names of base kinds, such as number or vehicle, can be registered in two different ways (according to whether they come from the source text or from template files), so we then make two further checks:
<k-base-kind> internal { kind *K = NULL; if (Wordings::empty(W)) { ==> { fail nonterminal }; } if (Wordings::length(W) == 1) K = Kinds::read_kind_marking_from_vocabulary(Lexer::word(Wordings::first_wn(W))); if (K == NULL) { if (<definite-article>(Wordings::first_word(W))) { ==> { fail nonterminal }; } parse_node *p = Lexicon::retrieve(KIND_SLOW_MC, W); if (p) { excerpt_meaning *em = Node::get_meaning(p); general_pointer m = Lexicon::get_data(em); if (m.run_time_type_code == noun_usage_CLASS) { noun_usage *nu = RETRIEVE_POINTER_noun_usage(m); m = nu->noun_used->meaning; } K = Kinds::base_construction(RETRIEVE_POINTER_kind_constructor(m)); } else { p = Lexicon::retrieve(NOUN_MC, W); if (p) { noun_usage *nu = Nouns::disambiguate(p, TRUE); noun *nt = (nu)?(nu->noun_used):NULL; if (nt) K = Kinds::base_construction( RETRIEVE_POINTER_kind_constructor(Nouns::meaning(nt))); } } } if (K) { ==> { -, K }; return TRUE; } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§11. "Object based rulebook" has been on a voyage of unhyphenation: in the early public beta of Inform 7, it was "object-based-rulebook" (at that time, built-in kinds had to have one-word names); then it became "object-based rulebook", when one-word adjectives were allowed to modify the names of built-in kinds; and now it is preferably "object based rulebook". But the previous syntax is permitted as an alias to keep old source text working. And similarly for the others here, except "either-or property", which is a 2010 addition.
<k-irregular-kind-construction> ::= indexed text | ==> { -, K_text } indexed texts | ==> { -, K_text } stored action | ==> Stored action11.1 stored actions | ==> Stored action11.1 object-based rulebook producing <indefinite-article> <k-kind> | ==> Rulebook obj on 211.2 object-based rulebook producing <k-kind> | ==> Rulebook obj on 111.3 object-based rulebook | ==> Rulebook obj on void11.4 action-based rulebook | ==> Action rulebook11.5 object-based rule producing <indefinite-article> <k-kind> | ==> Rule obj on 211.6 object-based rule producing <k-kind> | ==> Rule obj on 111.7 object-based rule | ==> Rule obj on void11.8 action-based rule | ==> Action rule11.9 either-or property ==> Property on truth state11.10
- This is Preform grammar, not regular C code.
#ifdef IF_MODULE if (K_stored_action == NULL) { ==> { fail production }; } ==> { -, K_stored_action }; #endif #ifndef IF_MODULE ==> { fail production }; #endif
- This code is used in §11 (twice).
§11.2. Rulebook obj on 211.2 =
==> { -, Kinds::binary_con(CON_rulebook, K_object, RP[2]) }
- This code is used in §11.
§11.3. Rulebook obj on 111.3 =
==> { -, Kinds::binary_con(CON_rulebook, K_object, RP[1]) }
- This code is used in §11.
§11.4. Rulebook obj on void11.4 =
==> { -, Kinds::binary_con(CON_rulebook, K_object, K_void) }
- This code is used in §11.
#ifdef IF_MODULE if (K_action_name == NULL) { ==> { fail production }; } ==> { -, Kinds::binary_con(CON_rulebook, K_action_name, K_void) }; #endif #ifndef IF_MODULE ==> { fail production }; #endif
- This code is used in §11.
==> { -, Kinds::binary_con(CON_rule, K_object, RP[2]) }
- This code is used in §11.
==> { -, Kinds::binary_con(CON_rule, K_object, RP[1]) }
- This code is used in §11.
==> { -, Kinds::binary_con(CON_rule, K_object, K_void) }
- This code is used in §11.
#ifdef IF_MODULE if (K_action_name == NULL) { ==> { fail production }; } ==> { -, Kinds::binary_con(CON_rule, K_action_name, K_void) }; #endif #ifndef IF_MODULE ==> { fail production }; #endif
- This code is used in §11.
§11.10. Property on truth state11.10 =
==> { -, Kinds::unary_con(CON_property, K_truth_state) }
- This code is used in §11.
§12. This loop looks a little slow, but there are only about 10 proper constructors.
<k-kind-construction> internal { kind_constructor *con; LOOP_OVER(con, kind_constructor) if (KindConstructors::arity(con) > 0) { wording X = W; wording Y = EMPTY_WORDING; if (Kinds::Textual::parse_constructor_name(con, &X, &Y)) See if this partial kind-constructor match works out12.1; } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§12.1. So at this point we have a match of the fixed words in the constructor. For example, we might have "list of ...", where the X excerpt represents the "..." and the Y excerpt is empty; or "... based rule producing ...", where both X and Y are non-empty; or even something like "relation", where both X and Y are empty.
We try to match X and Y against kinds, filling in defaults if either is unspecified because a short form of the constructor is used (e.g., "relation" instead of "relation of ..." or "relation of ... to ...").
See if this partial kind-constructor match works out12.1 =
kind *KX = K_value, *KY = K_value; if (con->variance[0] == CONTRAVARIANT) KX = K_nil; if ((KindConstructors::arity(con) == 2) && (con->variance[1] == CONTRAVARIANT)) KY = K_nil; The rule and rulebook constructors default to actions for X12.1.1; if (Wordings::nonempty(X)) { int tupling = KindConstructors::tupling(con, 0); if ((tupling == 0) && (<k-single-term>(X))) KX = <<rp>>; else if ((tupling == 1) && (<k-optional-term>(X))) KX = <<rp>>; else if ((tupling >= 2) && (<k-tupled-term>(X))) KX = <<rp>>; else KX = NULL; } if (Wordings::nonempty(Y)) { int tupling = KindConstructors::tupling(con, 1); if ((tupling == 0) && (<k-single-term>(Y))) KY = <<rp>>; else if ((tupling == 1) && (<k-optional-term>(Y))) KY = <<rp>>; else if ((tupling >= 2) && (<k-tupled-term>(Y))) KY = <<rp>>; else KY = NULL; } The relation constructor defaults to Y matching X, if X is specified12.1.2; if ((KindConstructors::arity(con) == 1) && (KX)) { ==> { -, Kinds::unary_con(con, KX) }; return TRUE; } if ((KindConstructors::arity(con) == 2) && (KX) && (KY)) { ==> { -, Kinds::binary_con(con, KX, KY) }; return TRUE; }
- This code is used in §12.
§12.1.1. Ordinarily missing X or Y are filled in as "value", but...
The rule and rulebook constructors default to actions for X12.1.1 =
if ((con == CON_rule) || (con == CON_rulebook)) { #ifdef IF_MODULE if (K_action_name) KX = K_action_name; else KX = K_void; #endif #ifndef IF_MODULE KX = K_void; #endif KY = K_void; }
- This code is used in §12.1.
The relation constructor defaults to Y matching X, if X is specified12.1.2 =
if ((con == CON_relation) && (Wordings::empty(Y))) KY = KX;
- This code is used in §12.1.
§13. Where the materials used in construction are not quite kinds, but can be more varied.
<k-single-term> ::= ( <k-single-term> ) | ==> { pass 1 } <article> <k-single-term> | ==> { pass 2 } <k-kind> ==> { pass 1 } <k-optional-term> ::= ( <k-optional-term> ) | ==> { pass 1 } <article> <k-optional-term> | ==> { pass 2 } nothing | ==> { -, K_nil } action | ==> The action term13.1 <k-kind> ==> { pass 1 } <k-tupled-term> ::= ( <k-tuple-list> ) | ==> { pass 1 } nothing | ==> { -, K_void } <k-single-term> ==> { -, Kinds::binary_con(CON_TUPLE_ENTRY, RP[1], K_void) } <k-tuple-list> ::= <k-single-term> , <k-tuple-list> | ==> { -, Kinds::binary_con(CON_TUPLE_ENTRY, RP[1], RP[2]) } <k-single-term> ==> { -, Kinds::binary_con(CON_TUPLE_ENTRY, RP[1], K_void) }
- This is Preform grammar, not regular C code.
#ifdef IF_MODULE ==> { -, K_action_name }; #endif #ifndef IF_MODULE ==> { fail production }; #endif
- This code is used in §13.
§14. The following looks at a word range and tries to find text making a kind construction: if it does, it adjusts the word ranges to the kind(s) being constructed on, and returns TRUE; if it fails, it returns FALSE. For instance, given "list of marbles", it adjusts the word range to "marbles" and returns TRUE.
int Kinds::Textual::parse_constructor_name(kind_constructor *con, wording *KW, wording *LW) { wording W = *KW; for (int p=1; p<=2; p++) { wording NW = KindConstructors::get_name(con, (p==1)?FALSE:TRUE); if (Wordings::nonempty(NW)) { int full_length = Wordings::length(NW); int k1 = Wordings::first_wn(NW); *KW = EMPTY_WORDING; *LW = EMPTY_WORDING; Try this as a constructor name14.1; } } *KW = EMPTY_WORDING; *LW = EMPTY_WORDING; return FALSE; }
§14.1. Note that the name text for a constructor is likely to have a form like so:
relation STROKE relation of k to l STROKE relation of k
with multiple possibilities divided by strokes; each possibility must be checked for.
Try this as a constructor name14.1 =
int base = 0, length = full_length; int k; for (k=0; k<full_length; k++) if (Lexer::word(k1+k) == STROKE_V) { length = k - base; if (length > 0) Try one option among the constructor's names14.1.1; base = k+1; } length = full_length - base; if (length > 0) Try one option among the constructor's names14.1.1;
- This code is used in §14.
§14.1.1. Try one option among the constructor's names14.1.1 =
if (Wordings::length(W) >= length) { int i, p, failed = FALSE; for (i=0, p=Wordings::first_wn(W); i<length; i++) { vocabulary_entry *ve = Lexer::word(k1+base+i); if ((ve == CAPITAL_K_V) || (ve == CAPITAL_L_V)) { int from = p; if (i == length-1) { p = Wordings::last_wn(W)+1; } else { int bl = 0; while (p <= Wordings::last_wn(W)) { vocabulary_entry *nw = Lexer::word(p); if (nw == OPENBRACKET_V) bl++; else if (nw == CLOSEBRACKET_V) bl--; else if (bl == 0) { if (nw == Lexer::word(k1+base+i+1)) break; } p++; } if (p > Wordings::last_wn(W)) { failed = TRUE; break; } } if (ve == CAPITAL_K_V) { *KW = Wordings::new(from, p-1); } else { *LW = Wordings::new(from, p-1); } } else { if (ve != Lexer::word(p++)) { failed = TRUE; break; } } } if (p != Wordings::last_wn(W)+1) failed = TRUE; if (failed == FALSE) return TRUE; }
- This code is used in §14.1 (twice).
§15. Kinds of kind. This is actually just <k-kind> in disguise, but only lets the result through if it's a kind of kind, like "arithmetic value"; something like "number" or "list of texts" will fail.
<k-kind-of-kind> ::= <k-kind> ==> { pass 1 }; if (Kinds::Behaviour::is_kind_of_kind(RP[1]) == FALSE) return FALSE;
- This is Preform grammar, not regular C code.
§16. Parsing kind variables. As a small detour, here's how we deal with the pleasingly simple names A to Z for kind variables, converting them to and from the numbers 1 to 26:
int Kinds::Textual::parse_variable(vocabulary_entry *ve) { if (ve == NULL) return 0; return Kinds::Textual::parse_kind_variable_name( Vocabulary::get_exemplar(ve, TRUE), TRUE); } int Kinds::Textual::parse_kind_variable_name(inchar32_t *p, int allow_lower) { if (p == NULL) return 0; if ((p[1] == 0) || ((p[1] == 's') && (p[2] == 0))) { if ((p[0] >= 'A') && (p[0] <= 'Z')) return (int) (p[0] - 'A' + 1); if ((allow_lower) && (p[0] >= 'a') && (p[0] <= 'z')) return (int) (p[0] - 'a' + 1); } return 0; } int Kinds::Textual::parse_kind_variable_name_singular(inchar32_t *p) { if ((p) && (p[1] == 0) && (p[0] >= 'A') && (p[0] <= 'Z')) return (int) (p[0] - 'A' + 1); return 0; }
§17. Kind variables are written with the letters A to Z. That provides for only 26 of them, but it's very, very rare to need more than 2, in practice.
The following nonterminal matches only those kind variables whose values are actually set, and it returns those values. This is how kind variables are parsed almost all of the time.
<k-kind-variable> internal 1 { int k = Kinds::Textual::parse_kind_variable_name( Lexer::word_raw_text(Wordings::first_wn(W)), FALSE); if (k != 0) { kind *K = Kinds::variable_from_context(k); if (K) { ==> { k, K }; return TRUE; } } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§18. But we can also formally parse A to Z as their own abstract identities; now they always parse, regardless of what might be stored in them, and they aren't replaced with their values (which they may not even have).
<k-formal-variable> internal 1 { int k = Kinds::Textual::parse_kind_variable_name( Lexer::word_raw_text(Wordings::first_wn(W)), FALSE); if (k != 0) { ==> { k, Kinds::var_construction(k, NULL) }; return TRUE; } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§19. And it's also convenient to have:
<k-formal-variable-singular> internal 1 { int k = Kinds::Textual::parse_kind_variable_name_singular( Lexer::word_raw_text(Wordings::first_wn(W))); if (k != 0) { ==> { k, Kinds::var_construction(k, NULL) }; return TRUE; } ==> { fail nonterminal }; }
- This is Preform grammar, not regular C code.
§20. For efficiency's sake, we don't actually parse directly using this nonterminal, but it's needed all the same because of Preform's optimisations.
<k-kind-variable-texts> ::= a/as | b/bs | c/cs | d/ds | e/es | f/fs | g/gs | h/hs | i/is | j/js | k/ks | l/ls | m/ms | n/ns | o/os | p/ps | q/qs | r/rs | s/ss | t/ts | u/us | v/vs | w/ws | x/xs | y/ys | z/zs
- This is Preform grammar, not regular C code.
§21. Textual descriptions. The following pretty-printer is inverse to the code which parses text and turns it into a kind structure, or very nearly so. We use common code to handle all of the reasons why we might want to spell out a kind in words — the log, the index, problem messages, comments in code, and so on. For example:
void Kinds::Textual::log(kind *K) { Kinds::Textual::write(DL, K); } void Kinds::Textual::logger(OUTPUT_STREAM, void *vK) { kind *K = (kind *) vK; Kinds::Textual::write(OUT, K); } void Kinds::Textual::writer(OUTPUT_STREAM, char *format_string, void *vK) { kind *K = (kind *) vK; Kinds::Textual::write(OUT, K); }
§22. Thus we have a basic pretty-printer...
void Kinds::Textual::write(OUTPUT_STREAM, kind *K) { Kinds::Textual::write_inner(OUT, K, FALSE, FALSE); } void Kinds::Textual::write_plural(OUTPUT_STREAM, kind *K) { Kinds::Textual::write_inner(OUT, K, TRUE, FALSE); }
§23. ...and also one which prefaces the kind name with an article.
void Kinds::Textual::write_articled(OUTPUT_STREAM, kind *K) { TEMPORARY_TEXT(TEMP) Kinds::Textual::write_inner(TEMP, K, FALSE, TRUE); ArticleInflection::preface_by_article(OUT, TEMP, DefaultLanguage::get(NULL)); DISCARD_TEXT(TEMP) }
§24. In all cases we make use of the following recursive method:
void Kinds::Textual::write_inner(OUTPUT_STREAM, kind *K, int plural_form, int substituting) { if (K == NULL) { WRITE("nothing"); return; } if (K == K_nil) { WRITE("nothing"); return; } if (K == K_void) { WRITE("nothing"); return; } kind_constructor *con = NULL; if (Kinds::is_proper_constructor(K)) con = Kinds::get_construct(K); Write punctuation kinds out to the stream24.1; if (con) Write constructor kinds out to the stream24.3 else Write base kinds out to the stream24.2; }
§24.1. Note that we do cheat in one case, in our resolve to have only a single written form for kinds in all contexts — we give a low-level description of an intermediate value in the debugging log. But such a thing can't be exposed higher up in Inform, so it's no loss to the index, problem messages, etc., to miss out on this detail.
Write punctuation kinds out to the stream24.1 =
kind_constructor *con = Kinds::get_construct(K); if (con == CON_VOID) { WRITE("void"); return; } if (con == CON_NIL) { WRITE("nil"); return; } if (con == CON_TUPLE_ENTRY) { Describe a continuing tuple24.1.1; return; } if (con == CON_KIND_VARIABLE) { Describe a kind variable, either by name or by value24.1.2; return; } if (con == CON_INTERMEDIATE) { if (OUT == DL) LOG("$Q", Kinds::Behaviour::get_dimensional_form(K)); else Kinds::Dimensions::index_unit_sequence(OUT, Kinds::Behaviour::get_dimensional_form(K), FALSE); return; }
- This code is used in §24.
§24.1.1. Describe a continuing tuple24.1.1 =
kind *head = NULL, *tail = NULL; Kinds::binary_construction_material(K, &head, &tail); Kinds::Textual::write_inner(OUT, head, FALSE, substituting); if (Kinds::get_construct(tail) != CON_VOID) { WRITE(", "); Kinds::Textual::write_inner(OUT, tail, FALSE, substituting); }
- This code is used in §24.1.
§24.1.2. Describe a kind variable, either by name or by value24.1.2 =
int vn = Kinds::get_variable_number(K); if ((substituting) && (vn > 0)) { kind *subst = Kinds::variable_from_context(vn); if (subst) { Kinds::Textual::write_inner(OUT, subst, plural_form, TRUE); return; } } WRITE("%c", 'A' + vn - 1); if (plural_form) WRITE("s"); kind *S = Kinds::get_variable_stipulation(K); if ((S) && (Kinds::eq(S, K_value) == FALSE)) { WRITE(" ["); Kinds::Textual::write(OUT, S); WRITE("]"); }
- This code is used in §24.1.
§24.2. Write base kinds out to the stream24.2 =
if (Kinds::eq(K, K_pointer_value)) WRITE("pointer value"); else if (Kinds::eq(K, K_stored_value)) WRITE("stored value"); else WRITE("%W", Kinds::Behaviour::get_name(K, plural_form));
- This code is used in §24.
§24.3. Write constructor kinds out to the stream24.3 =
kind *first_base = NULL, *second_base = NULL; if (KindConstructors::arity(con) == 1) first_base = Kinds::unary_construction_material(K); else Kinds::binary_construction_material(K, &first_base, &second_base); Make a special case for either/or properties24.3.1; wording KW = Kinds::Behaviour::get_name(K, plural_form); int k1 = Wordings::first_wn(KW); int full_length = Wordings::length(KW); int from, to; Choose which form of the constructor to use when writing this out24.3.2; Actually write out the chosen form of the constructor24.3.3;
- This code is used in §24.
§24.3.1. Since Inform reads "either/or property" as syntactic sugar for "truth state valued property", we'll also write it: it's a much more familiar usage.
Make a special case for either/or properties24.3.1 =
if ((con == CON_property) && (Kinds::eq(first_base, K_truth_state))) { WRITE("either/or property"); return; }
- This code is used in §24.3.
§24.3.2. Choose which form of the constructor to use when writing this out24.3.2 =
int k_present = 0, l_present = 0; these initialisations have no effect but avoid warnings int choice_from[2][2], choice_to[2][2]; Determine the possible forms for writing this constructor24.3.2.1; k_present = 1; l_present = 1; if ((con == CON_rule) || (con == CON_rulebook)) { #ifdef IF_MODULE if (Kinds::eq(first_base, K_action_name)) k_present = 0; #endif } else { if (Kinds::eq(first_base, K_nil)) k_present = 0; if (Kinds::eq(first_base, K_void)) k_present = 0; } if ((con == CON_property) && (Kinds::eq(first_base, K_value))) k_present = 0; if ((con == CON_table_column) && (Kinds::eq(first_base, K_value))) k_present = 0; if ((con == CON_relation) && (Kinds::eq(first_base, second_base))) l_present = 0; if (KindConstructors::arity(con) == 1) l_present = 0; else if (Kinds::eq(second_base, K_nil)) l_present = 0; else if (Kinds::eq(second_base, K_void)) l_present = 0; if (choice_from[k_present][l_present] == -1) { if ((k_present == 0) && (choice_from[1][l_present] >= 0)) k_present++; else if ((l_present == 0) && (choice_from[k_present][1] >= 0)) l_present++; else if ((k_present == 0) && (l_present == 0) && (choice_from[1][1] >= 0)) { k_present++; l_present++; } } from = choice_from[k_present][l_present]; to = choice_to[k_present][l_present]; if ((from < 0) || (from >= full_length) || (to < 0) || (to >= full_length) || (to<from)) { LOG("%W: %d, %d, %d\n", KW, from, to, full_length); internal_error("constructor form choice failed"); }
- This code is used in §24.3.
§24.3.2.1. Determine the possible forms for writing this constructor24.3.2.1 =
int from, i; choice_from[0][0] = -1; choice_from[0][1] = -1; choice_from[1][0] = -1; choice_from[1][1] = -1; for (i=0, from = -1; i<full_length; i++) { if (from == -1) { from = i; k_present = 0; l_present = 0; } if (Lexer::word(k1+i) == CAPITAL_K_V) k_present = 1; if (Lexer::word(k1+i) == CAPITAL_L_V) l_present = 1; if (Lexer::word(k1+i) == STROKE_V) End this constructor possibility24.3.2.1.1; } End this constructor possibility24.3.2.1.1;
- This code is used in §24.3.2.
§24.3.2.1.1. End this constructor possibility24.3.2.1.1 =
choice_from[k_present][l_present] = from; choice_to[k_present][l_present] = i-1; from = -1;
- This code is used in §24.3.2.1 (twice).
§24.3.3. Actually write out the chosen form of the constructor24.3.3 =
int i; for (i=from; i<=to; i++) { if (i > from) WRITE(" "); if (Lexer::word(k1+i) == CAPITAL_K_V) Kinds::Textual::desc_base(OUT, con, 0, first_base, substituting); else if (Lexer::word(k1+i) == CAPITAL_L_V) Kinds::Textual::desc_base(OUT, con, 1, second_base, substituting); else WRITE("%V", Lexer::word(k1+i)); }
- This code is used in §24.3.
void Kinds::Textual::desc_base(OUTPUT_STREAM, kind_constructor *con, int b, kind *K, int substituting) { if (K == NULL) { WRITE("nothing"); return; } if (K == K_nil) { WRITE("nothing"); return; } if (K == K_void) { WRITE("nothing"); return; } int pluralised = TRUE; int tupled = KindConstructors::tupling(con, b); int bracketed = FALSE; if ((tupled > 1) && (Kinds::get_construct(K) == CON_TUPLE_ENTRY)) { kind *first_base = NULL, *second_base = NULL; Kinds::binary_construction_material(K, &first_base, &second_base); if ((second_base) && (Kinds::get_construct(second_base) == CON_TUPLE_ENTRY)) bracketed = TRUE; } if ((b == 1) && (con == CON_phrase) && (Kinds::get_construct(K) == CON_phrase)) bracketed = TRUE; if (bracketed) WRITE("("); if ((tupled > 1) || (con == CON_phrase)) pluralised = FALSE; Kinds::Textual::write_inner(OUT, K, pluralised, substituting); if (bracketed) WRITE(")"); }
§26. HTML. Amd this is used in indexing, for example.
void Kinds::Textual::write_as_HTML(OUTPUT_STREAM, kind *K) { if (K == NULL) return; wording W = Kinds::Behaviour::get_name(K, FALSE); if (Wordings::nonempty(W)) { if (Kinds::is_proper_constructor(K)) { Index the constructor text26.1; } else { WRITE("%W", W); } } }
§26.1. Index the constructor text26.1 =
int length = Wordings::length(W), w1 = Wordings::first_wn(W), tinted = TRUE; int i, first_stroke = -1, last_stroke = -1; for (i=0; i<length; i++) { if (Lexer::word(w1+i) == STROKE_V) { if (first_stroke == -1) first_stroke = i; last_stroke = i; } } int from = 0, to = length-1; if (last_stroke >= 0) from = last_stroke+1; else tinted = FALSE; if (tinted) HTML::begin_span(OUT, I"indexgrey"); for (i=from; i<=to; i++) { int j, untinted = FALSE; for (j=0; j<first_stroke; j++) if (Lexer::word(w1+j) == Lexer::word(w1+i)) untinted = TRUE; if (untinted) HTML::end_span(OUT); if (i>from) WRITE(" "); if (Lexer::word(w1+i) == CAPITAL_K_V) WRITE("K"); else if (Lexer::word(w1+i) == CAPITAL_L_V) WRITE("L"); else WRITE("%V", Lexer::word(w1+i)); if (untinted) HTML::begin_span(OUT, I"indexgrey"); } if (tinted) HTML::end_span(OUT);
- This code is used in §26.