1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-04-28 23:29:35 +03:00
inform7/inform7/assertions-module/Chapter 3/Translation Requests.w
2022-10-29 12:11:58 +01:00

496 lines
17 KiB
OpenEdge ABL

[Translations::] Translation Requests.
Three unrelated senses of "X translates into Y as Z" sentences.
@h Translation into natural languages.
The sentence "X translates into Y as Z" has this sense provided Y matches:
=
<translation-target-language> ::=
<natural-language> ==> { pass 1 }
@ =
int Translations::translates_into_language_as_SMF(int task, parse_node *V, wording *NPs) {
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
wording O2W = (NPs)?(NPs[2]):EMPTY_WORDING;
switch (task) { /* "Thing translates into French as chose" */
case ACCEPT_SMFT:
if (<translation-target-language>(O2W)) {
inform_language *nl = (inform_language *) (<<rp>>);
<np-articled>(SW);
V->next = <<rp>>;
<np-articled>(OW);
V->next->next = <<rp>>;
Node::set_defn_language(V->next->next, nl);
return TRUE;
}
break;
case PASS_1_SMFT:
@<Parse subject phrase and send the translation to the linguistics module@>;
break;
}
return FALSE;
}
@ The subject phrase can only be parsed on traverse 1, since it only makes
sense once kinds and instances exist.
@d TRANS_KIND 1
@d TRANS_INSTANCE 2
=
<translates-into-language-sentence-subject> ::=
<k-kind> | ==> { TRANS_KIND, RP[1] }
<instance> ==> { TRANS_INSTANCE, RP[1] }
@<Parse subject phrase and send the translation to the linguistics module@> =
wording SP = Node::get_text(V->next);
wording OP = Node::get_text(V->next->next);
inform_language *L = Node::get_defn_language(V->next->next);
int g = Annotations::read_int(V->next->next, explicit_gender_marker_ANNOT);
if (L == NULL) internal_error("No such NL");
if (L == DefaultLanguage::get(NULL)) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_CantTranslateIntoEnglish),
"you can't translate from a language into itself",
"only from the current language to a different one.");
return FALSE;
}
if ((<translates-into-language-sentence-subject>(SP)) == FALSE) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_CantTranslateValue),
"this isn't something which can be translated",
"that is, it isn't a kind or instance.");
return FALSE;
}
switch (<<r>>) {
case TRANS_INSTANCE: {
instance *I = <<rp>>;
noun *t = Instances::get_noun(I);
if (t == NULL) internal_error("stuck on instance name");
Nouns::supply_text(t, OP, L, g, SINGULAR_NUMBER, ADD_TO_LEXICON_NTOPT);
break;
}
case TRANS_KIND: {
kind *K = <<rp>>;
kind_constructor *KC = Kinds::get_construct(K);
if (KC == NULL) internal_error("stuck on kind name");
noun *t = KindConstructors::get_noun(KC);
if (t == NULL) internal_error("further stuck on kind name");
Nouns::supply_text(t, OP, L, g, SINGULAR_NUMBER,
ADD_TO_LEXICON_NTOPT + WITH_PLURAL_FORMS_NTOPT);
break;
}
default: internal_error("bad translation category");
}
@h Translation into Unicode.
The following sentence form is now deprecated:
>> leftwards harpoon with barb upwards translates into Unicode as 8636.
Until Inform 10.1, this equated a Unicode name to its code point value; see
IE-0005 and //values: Unicode Literals// for what now happens instead.
The sentence "X translates into Y as Z" has this sense provided Y matches:
=
<translation-target-unicode> ::=
unicode
@ =
int PM_UnicodeDeprecated_thrown = FALSE;
int Translations::translates_into_unicode_as_SMF(int task, parse_node *V, wording *NPs) {
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
wording O2W = (NPs)?(NPs[2]):EMPTY_WORDING;
switch (task) { /* "Black king chess piece translates into Unicode as 9818" */
case ACCEPT_SMFT:
if (<translation-target-unicode>(O2W)) {
<np-articled>(SW);
V->next = <<rp>>;
<np-articled>(OW);
V->next->next = <<rp>>;
return TRUE;
}
break;
case PASS_2_SMFT:
if (PM_UnicodeDeprecated_thrown == FALSE) {
PM_UnicodeDeprecated_thrown = TRUE;
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_UnicodeDeprecated),
"the sentence 'X translates into Unicode as Y' has been removed "
"from the Inform language",
"because it is now redundant. Inform already knows all the names "
"in the Unicode standard. If you're getting this problem message "
"because you included the extension 'Unicode Full Character Names' "
"or 'Unicode Character Names', all you need do is to not include it.");
}
break;
}
return FALSE;
}
@h Translation into Inter.
There are three sentences here, but the first is now deprecated: it has split
off into two different meanings, each with its own wording for clarity.
@d TRANSLATION_DEPRECATED_FORM 1
@d TRANSLATION_DEFINED_BY_FORM 2
@d TRANSLATION_ACCESSIBLE_TO_FORM 3
@ The sentence "X translates into Y as Z" has this sense provided Y matches the
following. Before the coming of Inter code, the only conceivable compilation
target was Inform 6, but these now set Inter identifiers, so really the first
wording is to be preferred.
=
<translation-target-i6> ::=
inter |
i6 |
inform 6
@
@e INTER_NAMING_SMFT
=
void Translations::traverse_for_late_namings(void) {
SyntaxTree::traverse(Task::syntax_tree(), Translations::visit_to_name);
}
void Translations::visit_to_name(parse_node *p) {
if ((Node::get_type(p) == SENTENCE_NT) && (p->down))
MajorNodes::try_special_meaning(INTER_NAMING_SMFT, p->down);
}
@ =
int Translations::translates_into_Inter_as_SMF(int task, parse_node *V, wording *NPs) {
int translates_into_verb = TRANSLATION_DEPRECATED_FORM;
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
wording O2W = (NPs)?(NPs[2]):EMPTY_WORDING;
switch (task) { /* "The taking inventory action translates into Inter as "Inv"" */
case ACCEPT_SMFT:
if (<translation-target-i6>(O2W)) {
<np-articled>(SW);
V->next = <<rp>>;
<np-articled>(OW);
V->next->next = <<rp>>;
return TRUE;
}
break;
case PASS_1_SMFT:
case PASS_2_SMFT:
@<Act on the Inter translation@>;
break;
case INTER_NAMING_SMFT:
@<Act on late naming@>;
break;
}
return FALSE;
}
@
=
<defined-by-inter-sentence-object> ::=
defined by inter as ...
@
=
int Translations::defined_by_Inter_as_SMF(int task, parse_node *V, wording *NPs) {
int translates_into_verb = TRANSLATION_DEFINED_BY_FORM;
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
switch (task) { /* The seed random number generator rule is defined by Inter as "SEED_RANDOM_NUMBER_GENERATOR_R". */
case ACCEPT_SMFT:
if (<defined-by-inter-sentence-object>(OW)) {
OW = GET_RW(<defined-by-inter-sentence-object>, 1);
<np-articled>(SW);
V->next = <<rp>>;
<np-articled>(OW);
V->next->next = <<rp>>;
return TRUE;
}
break;
case PASS_1_SMFT:
case PASS_2_SMFT:
@<Act on the Inter translation@>;
break;
case INTER_NAMING_SMFT:
@<Act on late naming@>;
break;
}
return FALSE;
}
@
=
<accessible-to-inter-sentence-object> ::=
accessible to inter as ...
@
=
int Translations::accessible_to_Inter_as_SMF(int task, parse_node *V, wording *NPs) {
int translates_into_verb = TRANSLATION_ACCESSIBLE_TO_FORM;
wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
switch (task) { /* The time advancing rule is accessible to Inter as "TIME_ADV_RULE". */
case ACCEPT_SMFT:
if (<accessible-to-inter-sentence-object>(OW)) {
OW = GET_RW(<accessible-to-inter-sentence-object>, 1);
<np-articled>(SW);
V->next = <<rp>>;
<np-articled>(OW);
V->next->next = <<rp>>;
return TRUE;
}
break;
case PASS_1_SMFT:
case PASS_2_SMFT:
@<Act on the Inter translation@>;
break;
case INTER_NAMING_SMFT:
@<Act on late naming@>;
break;
}
return FALSE;
}
@ Translations can be made in a number of contexts:
@d INVALID_I6TR -1
@d PROPERTY_I6TR 0 /* "The open property translates into I6 as "open"." */
@d NOUN_I6TR 1 /* "The north object translates into I6 as "n_obj"." */
@d RULE_I6TR 2 /* "The baffling rule translates into I6 as "BAFFLING_R"." */
@d RULEBOOK_I6TR 3 /* "The high security rules translates into I6 as "SECURITY_RULEBOOK". */
@d ACTIVITY_I6TR 4 /* "The draining something activity translates into I6 as "DRAINING_ACTIVITY". */
@d VARIABLE_I6TR 5 /* "The sludge count variable translates into I6 as "sldgc". */
@d ACTION_I6TR 6 /* "The taking action translates into I6 as "Take". */
@d GRAMMAR_TOKEN_I6TR 7 /* "The grammar token "[whatever]" translates into I6 as "WHATEVER". */
=
<translates-into-i6-sentence-subject> ::=
... property | ==> { PROPERTY_I6TR, - }
... object/kind | ==> { NOUN_I6TR, - }
{... rule} | ==> { RULE_I6TR, - }
{... rules/rulebook} | ==> { RULEBOOK_I6TR, - }
{... activity} | ==> { ACTIVITY_I6TR, - }
... variable | ==> { VARIABLE_I6TR, - }
... action | ==> { ACTION_I6TR, - }
understand token ... | ==> { GRAMMAR_TOKEN_I6TR, - }
... ==> @<Issue PM_TranslatedUnknownCategory problem@>
@<Issue PM_TranslatedUnknownCategory problem@> =
StandardProblems::sentence_problem(Task::syntax_tree(), _p_(PM_TranslatedUnknownCategory),
"that isn't one of the things which can be translated to I6",
"and should be '... variable', '... property', '... object', "
"'... kind', '... rule', or '... action'. For instance, 'The yourself "
"object translates into I6 as \"selfobj\".'");
==> { INVALID_I6TR, - };
@ The object noun phrase is usually just an I6 identifier in quotation marks,
but it's also possible to list literal texts (for the benefit of rules).
Following the optional "with" is an articled list, each entry of which
will be required to pass |<extra-response>|.
=
<translates-into-i6-sentence-object> ::=
<quoted-text> with <np-articled-list> | ==> { R[1], RP[2] }
<quoted-text> ==> { R[1], NULL }
@<Act on the Inter translation@> =
parse_node *p1 = V->next;
parse_node *p2 = V->next->next;
parse_node *responses_list = NULL;
int category = INVALID_I6TR;
<translates-into-i6-sentence-subject>(Node::get_text(p1));
category = <<r>>;
if (category != INVALID_I6TR) {
wording W = GET_RW(<translates-into-i6-sentence-subject>, 1);
if (global_pass_state.pass == 1) {
Annotations::write_int(V, category_of_I6_translation_ANNOT, INVALID_I6TR);
@<Ensure that we are translating to a quoted I6 identifier@>;
Annotations::write_int(V, category_of_I6_translation_ANNOT, category);
if (responses_list) SyntaxTree::graft(Task::syntax_tree(), responses_list, p2);
} else category = Annotations::read_int(V, category_of_I6_translation_ANNOT);
@<Take action in pass 1 or 2 where possible@>;
}
@<Ensure that we are translating to a quoted I6 identifier@> =
int valid = TRUE;
if (<translates-into-i6-sentence-object>(Node::get_text(p2)) == FALSE) valid = FALSE;
else responses_list = <<rp>>;
if (valid) @<Dequote it and see if it's valid@>;
if (valid == FALSE) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_TranslatedToNonIdentifier),
"Inform 7 constructions can only translate into quoted I6 identifiers",
"which must be strings of 1 to 31 characters drawn from 1, 2, ..., 9, "
"a or A, b or B, ..., z or Z, or underscore '_', except that the "
"first character is not allowed to be a digit.");
return FALSE;
}
@ If it turns out not to be, we simply set |valid| to false.
@<Dequote it and see if it's valid@> =
int wn = Wordings::first_wn(Node::get_text(p2));
Node::set_text(p2, Wordings::one_word(wn));
Word::dequote(wn);
if (valid) valid = Identifiers::valid(Lexer::word_text(wn));
@ In some cases, we act on pass 1, but in others pass 2, and in the case of
|NOUN_I6TR|, later even than that. There are messy timing issues here.
@<Take action in pass 1 or 2 where possible@> =
switch(category) {
case PROPERTY_I6TR:
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_DEFINED_BY_FORM;
@<Do not use the translates-into form@>;
if (translates_into_verb == TRANSLATION_ACCESSIBLE_TO_FORM) {
if (global_pass_state.pass == 2) Properties::accessible_as(W, p2);
} else {
Properties::translates(W, p2);
Annotations::write_int(V, category_of_I6_translation_ANNOT, INVALID_I6TR);
}
break;
case NOUN_I6TR: break;
case RULE_I6TR:
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_DEFINED_BY_FORM;
@<Require the defined-by form@>;
if (global_pass_state.pass == 1)
Rules::declare_Inter_rule(W, Node::get_text(p2));
if ((global_pass_state.pass == 2) && (p2->down) && (<rule-name>(W)))
Translations::plus_responses(p2->down, <<rp>>);
break;
case RULEBOOK_I6TR:
@<Require the accessible-to form@>;
if (global_pass_state.pass == 2) Rulebooks::translates(W, p2);
break;
case ACTIVITY_I6TR:
@<Require the accessible-to form@>;
if (global_pass_state.pass == 2) Activities::translates(W, p2);
break;
case VARIABLE_I6TR:
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_DEFINED_BY_FORM;
@<Require the defined-by form@>;
if (global_pass_state.pass == 2) NonlocalVariables::translates(W, p2);
break;
case ACTION_I6TR:
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_ACCESSIBLE_TO_FORM;
@<Require the accessible-to form@>;
if (global_pass_state.pass == 2) Actions::translates(W, p2);
break;
case GRAMMAR_TOKEN_I6TR:
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_DEFINED_BY_FORM;
@<Require the defined-by form@>;
if (global_pass_state.pass == 2) CommandGrammars::new_translated_token(W, p2);
break;
}
@<Require the accessible-to form@> =
if (translates_into_verb != TRANSLATION_ACCESSIBLE_TO_FORM) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(...),
"this is a language construct which cannot be defined in Inter code",
"so although you can say 'X is accessible to Inter as Y' to give it "
"an Inter identifier, you cannot say 'X translates into Inter as Y' or "
"'X is defined by Inter as Y'.");
}
@<Require the defined-by form@> =
if (translates_into_verb != TRANSLATION_DEFINED_BY_FORM) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(...),
"this is a language construct which cannot be given an Inter name "
"except by defining it from Inter",
"so although you can say 'X is defined by Inter as Y' to make "
"this available to source text, you cannot say 'X translates into "
"Inter as Y' or 'X is accessible to Inter as Y'.");
}
@<Do not use the translates-into form@> =
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM) {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(...),
"the verb 'X translates into Inter as Y' (or '... into I6...') "
"has been removed from Inform",
"and should either be 'X is defined by Inter as Y' if Y is something "
"whose definition is given in Inter - for example, for a rule defined in "
"a kit - or else 'X is accessible to Inter as Y', if you just want a "
"a name you can use from Inter code to refer to an X created by Inform.");
}
@ Extra responses look just as they would in running code.
=
<extra-response> ::=
<quoted-text> ( <response-letter> ) ==> { pass 2 }
@ =
void Translations::plus_responses(parse_node *p, rule *R) {
if (Node::get_type(p) == AND_NT) {
Translations::plus_responses(p->down, R);
Translations::plus_responses(p->down->next, R);
} else {
if (<extra-response>(Node::get_text(p))) {
int marker = <<r>>;
Responses::set_via_translation(R, marker, Node::get_text(p));
} else {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_I6ResponsesAwry),
"additional information about I6 translation of a rule can "
"only take the form of a list of responses",
"each quoted and followed by a bracketed letter.");
}
}
}
@ As noted above, |NOUN_I6TR| renamings happen much later on.
@<Act on late naming@> =
wording SP = Node::get_text(V->next);
wording OP = Node::get_text(V->next->next);
int category = Annotations::read_int(V, category_of_I6_translation_ANNOT);
switch(category) {
case NOUN_I6TR: {
wording W = Wordings::trim_last_word(SP);
parse_node *res = Lexicon::retrieve(NOUN_MC, W);
if (res) {
if (translates_into_verb == TRANSLATION_DEPRECATED_FORM)
translates_into_verb = TRANSLATION_ACCESSIBLE_TO_FORM;
@<Require the accessible-to form@>;
noun_usage *nu = Nouns::disambiguate(res, FALSE);
noun *nt = (nu)?(nu->noun_used):NULL;
if (nt) {
TEMPORARY_TEXT(i6r)
WRITE_TO(i6r, "%N", Wordings::first_wn(OP));
NounIdentifiers::noun_set_translation(nt, i6r);
DISCARD_TEXT(i6r)
}
} else {
StandardProblems::sentence_problem(Task::syntax_tree(),
_p_(PM_BadObjectTranslation),
"there is no such object or kind of object",
"so its name will never be translated into an Inter "
"identifier in any event.");
}
break;
}
}