1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 18:14:21 +03:00
inform7/services/kinds-module/Chapter 2/Kinds.w

880 lines
32 KiB
OpenEdge ABL
Raw Normal View History

2019-02-05 02:44:07 +02:00
[Kinds::] Kinds.
To build tree structures which represent Inform's universe of kinds.
@h Definitions.
@ Inform has a rich universe of kinds: "number", "list of texts",
"relation of texts to lists of times", and so on. We can regard
each valid kind as the outcome of a series of constructions performed on
existing kinds. Here, for example, we get to out destination with four
constructions in a row:
2020-04-08 01:02:44 +03:00
= (text)
(nothing) --> text
(nothing) --> time
time --> list of times
text, list of times --> relation of texts to lists of times
=
2019-02-05 02:44:07 +02:00
At each step there is only a finite choice of possible "kind constructions"
which can be made, but since there can in principle be an unlimited number
of steps, the set of all possible kinds is infinite. At each step we make
use of 0, 1 or 2 existing kinds to make a new one: this number (0, 1 or 2)
is the "arity" of the construction. These four steps have arities 0, 0, 1, 2.
@d MAX_KIND_CONSTRUCTION_ARITY 2
@ Inform stores the possible constructions in |kind_constructor| structures;
about 40 of these are used to provide the built-in range of kinds, and come
in a mixture of arities. (The four constructions above all use built-in
constructors.) Further constructors are added each time the source text
creates a new kind. For example,
>> A weight is a kind of value. A mammal is a kind of animal.
creates two new constructors:
2020-04-08 01:02:44 +03:00
= (text)
(nothing) --> weight
(nothing) --> animal
=
2019-02-05 02:44:07 +02:00
At present these additional constructors all have arity 0. High-level Inform 7
source is not currently able to define new constructors of higher arity;
I6 template code can do this (and that's how the built-in set is defined),
and it may be that future developments of Inform will bring this ability up
to source text level.
@ A given kind is represented in Inform by a pointer to a |kind| tree.
A |NULL| pointer is a valid kind, and means "unknown".
Each node in the tree has a pointer (|->construct|) to the kind constructor
2019-03-17 14:40:57 +02:00
used to make it; this is never null. In the case of two special
2019-02-05 02:44:07 +02:00
constructors, there are further annotations (see below). The number of
downward branches at the node is equal to the arity of the constructor
being used; so the kind "number" is represented by a single leaf node:
2020-04-08 01:02:44 +03:00
= (text)
number
=
2019-02-05 02:44:07 +02:00
whereas "relation of texts to lists of times" is represented by a tree of
four nodes like so:
2020-04-08 01:02:44 +03:00
= (text)
relation of K to L
text
list of K
time
=
2019-02-05 02:44:07 +02:00
@ We will often use the word "base" to refer to arity-0 constructors
(or to the kinds which use them): thus, "text" and "time" are bases,
but "list of K" is not. We call constructors of higher arity "proper".
It would be neat if there were exactly one |kind| structure somewhere in
memory for each different kind -- if that were true then we could compare
two kinds for equality simply by comparing pointers, and by definition it
would use the least possible memory. But in practice we don't do this,
because (i) it's too slow and tricky to arrange, (ii) we want to abstract
the testing process with the |Kinds::Compare::eq| function in case of later changes,
and (iii) careful use of caches where access is fast enable us to reduce
memory waste, mostly through intermediates but sometimes constructors, to
only a very small percentage in typical Inform usage -- say about 2K on a
2019-03-17 14:40:57 +02:00
medium-sized source text like "Bronze", which is not worth economising.
2019-02-05 02:44:07 +02:00
@ In principle we could imagine constructors needing arbitrarily large
arity, or needing different arity in different usages, so the scheme of
having fixed arities in the range 0 to 2 looks limited. In practice we get
around that by using "punctuation nodes" in the tree. For example,
2020-04-08 01:02:44 +03:00
= (text)
function K -> L
CON_TUPLE_ENTRY
text
CON_TUPLE_ENTRY
text
CON_NIL
number
=
2019-02-05 02:44:07 +02:00
represents |function (text, text) -> number|. Note two special constructors
used here: |CON_TUPLE_ENTRY| and |CON_NIL|. These are called "punctuation";
they cannot appear in isolation -- see below.
@ In the Inform source code, we're clearly going to need to refer to some
of these kinds. The compiler provides support for, say, parsing times of
day, or for indexing scenes, which go beyond the generic facilities it
provides for kinds created in source text. We adopt two naming conventions:
(i) Kinds are written as |K_source_text_name|, that is, |K_| followed by
the name of the kind in I7 source text, with spaces made into underscores.
For instance, |K_number|. These are all |kind *| global variables
which are initially |NULL|, but which, once set, are never changed.
(ii) Constructors are likewise written as |CON_source_text_name| if they can
be created in source text; or by |CON_TEMPLATE_NAME|, that is, |CON_|
followed by the constructor's identifier as given in the I6 template file
which created it (but with the |_TY| suffix removed) if not. For instance,
|CON_list_of| means the constructor able to make, e.g., "list of texts";
|CON_TUPLE_ENTRY| refers to the constructor created by the |TUPLE_ENTRY_TY|
block in the |Load-Core.i6t| template file. These are all |kind_constructor
*| global variables which are initially |NULL|, but which, once set, are
never changed.
We will now define all of the |K_...| and |CON_...| used by the core of
Inform. (Others are created and used within specific plugins.)
@ We begin with some base kinds which are "kinds of kinds" useful in
generic programming.
|K_value| is a superhero, or perhaps a supervillain: it matches values of
every kind. Not being a kind in its own right, it can't be the kind of a
variable -- which is just as well, since no use of such a variable could
ever be safe.
The finer distinctions |K_word_value| and |K_pointer_value| are used to
divide all run-time data into two very different storage implementations:
(a) those where instances are stored as word-value data, where a single I6 value
holds the whole thing, like "number";
(b) those where instances are stored as pointers to larger blocks of data on the
heap, like "stored action".
= (early code)
kind *K_value = NULL;
kind *K_word_value = NULL;
kind *K_pointer_value = NULL;
kind *K_sayable_value = NULL;
@ The following refer to values subject to arithmetic operations (drawn with a
little calculator icon in the Kinds index), and those which are implemented as
enumerations of named constants. (This includes, e.g., scenes and figure names
but not objects, whose run-time storage is not a simple numerical enumeration,
or truth states, which are stored as 0 and 1 not 1 and 2. In particular, it
2019-03-17 14:40:57 +02:00
isn't the same thing as having a finite range in the Kinds index.)
2019-02-05 02:44:07 +02:00
= (early code)
kind *K_arithmetic_value = NULL;
kind *K_real_arithmetic_value = NULL; /* those using real, not integer, arithmetic */
kind *K_enumerated_value = NULL;
@h Next, the two constructors used to punctuate tuples, that is, collections
$(K_1, K_2, ..., K_n)$ of kinds of value. |CON_NIL| represents the empty
tuple, where $n=0$; while |CON_TUPLE_ENTRY| behaves like a kind constructor
with arity 2, its two bases being the first item and the rest, respectively.
Thus we store $(A, B, C)$ as
2020-04-08 01:02:44 +03:00
= (text)
CON_TUPLE_ENTRY(A, CON_TUPLE_ENTRY(B, CON_TUPLE_ENTRY(C, CON_NIL)))
=
2019-02-05 02:44:07 +02:00
This traditional LISP-like device enables us to store tuples of arbitrary
size without need for any constructor of arity greater than 2.
(a) Inform has no "nil" or "void" kind visible to the writer of source
text, though it does occasionally use a kind it calls |K_nil| internally
to represent this idea -- for instance for a rulebook producing nothing;
|K_nil| is the kind constructed by |CON_NIL|.
(b) Inform does allow combinations, but they're identified by trees headed
by the constructor |CON_combination|, which then uses punctuation in its own
subtree. You might guess that an ordered pair of a text and a time would be
represented by the |CON_TUPLE_ENTRY| constructor on its own, but it isn't.
= (early code)
kind *K_nil = NULL;
kind_constructor *CON_NIL = NULL;
kind_constructor *CON_TUPLE_ENTRY = NULL;
@ It was mentioned above that two special constructors carry additional
annotations with them. The first of these is |CON_INTERMEDIATE|, used to
represent kinds which are brought into being through uncompleted arithmetic
operations: see "Dimensions.w" for a full discussion. Such a node in a
kind tree might represent "area divided by time squared", say, and it must
be annotated to show exactly which intermediate kind is meant.
=
kind_constructor *CON_INTERMEDIATE = NULL;
@ While that doesn't significantly change the kinds system, the second special
constructor certainly does. This is |CON_KIND_VARIABLE|, annotated to show
which of the 26 kind variables it represents in any given situation. These
variables are, in effect, wildcards; each is marked with a "kind of kind"
as its range of possible values. (Thus a typical use of this constructor
might result in a kind node labelled as L, which can be any kind matching
"arithmetic value".)
= (early code)
kind_constructor *CON_KIND_VARIABLE = NULL;
@ So much for the exotica: back onto familiar ground for anyone who uses
Inform. Some standard kinds:
= (early code)
kind *K_action_name = NULL;
kind *K_equation = NULL;
kind *K_grammatical_gender = NULL;
2020-07-29 20:43:44 +03:00
kind *K_natural_language = NULL;
2019-02-05 02:44:07 +02:00
kind *K_number = NULL;
kind *K_object = NULL;
kind *K_real_number = NULL;
kind *K_response = NULL;
kind *K_snippet = NULL;
kind *K_stored_action = NULL;
kind *K_table = NULL;
kind *K_text = NULL;
kind *K_truth_state = NULL;
kind *K_unicode_character = NULL;
kind *K_use_option = NULL;
kind *K_verb = NULL;
@ And here are two more standard kinds, but which most Inform uses don't
realise are there, because they are omitted from the Kinds index:
(a) |K_rulebook_outcome|. Rulebooks end in success, failure, no outcome, or
possibly one of a range of named alternative outcomes. These all share a
single namespace, and the names in question share a single kind of value.
It's not a very elegant system, and we really don't want people storing
these in variables; we want them to be used only as part of the process of
receiving the outcome back. So although there's no technical reason why this
kind shouldn't be used for storage, it's hidden from the user.
(b) |K_understanding| is used to hold the result of a grammar token. An actual
constant value specification of this kind stores a |grammar_verb *| pointer.
It's an untidy internal device which may well be removed later.
= (early code)
kind *K_rulebook_outcome = NULL;
kind *K_understanding = NULL;
@ Finally, the constructors used by Inform authors:
= (early code)
kind_constructor *CON_list_of = NULL;
kind_constructor *CON_description = NULL;
kind_constructor *CON_relation = NULL;
kind_constructor *CON_rule = NULL;
kind_constructor *CON_rulebook = NULL;
kind_constructor *CON_activity = NULL;
kind_constructor *CON_phrase = NULL;
kind_constructor *CON_property = NULL;
kind_constructor *CON_table_column = NULL;
kind_constructor *CON_combination = NULL;
kind_constructor *CON_variable = NULL;
@ Finally, then, it's time to define what a kind node looks like:
=
typedef struct kind {
struct kind_constructor *construct; /* which can never be |NULL| */
int kind_variable_number; /* only used if construct is |CON_KIND_VARIABLE| */
struct unit_sequence *intermediate_result; /* only used if construct is |CON_INTERMEDIATE| */
struct kind *kc_args[MAX_KIND_CONSTRUCTION_ARITY]; /* used if arity positive, or for |CON_KIND_VARIABLE| */
} kind;
@ We keep some statistics for tracking memory usage:
=
int no_base_kinds_created = 0;
int no_intermediate_kinds_created = 0;
int no_constructed_kinds_created = 0;
@h Constructing kinds.
All kind structures are obtained by one of the following. First, a base
construction, one with arity 0. This makes a kind tree with a single leaf
node, of course, and that's something we need very often. So we create it
only on the first request, and cache the pointer to it with the constructor;
we can then use that same pointer on all subsequent requests.
=
kind *Kinds::base_construction(kind_constructor *con) {
if (con == NULL) internal_error("impossible construction");
if ((con == CON_KIND_VARIABLE) || (con == CON_INTERMEDIATE))
internal_error("forbidden construction");
switch (Kinds::Constructors::arity(con)) {
case 1:
if (con == CON_list_of) return Kinds::unary_construction(con, NULL);
return Kinds::unary_construction(con, K_value);
case 2: return Kinds::binary_construction(con, K_value, K_value);
}
kind **cache = Kinds::Constructors::cache_location(con);
if (cache) { if (*cache) return *cache; }
kind *K;
@<Create a raw kind structure@>;
K->construct = con;
if (cache) *cache = K;
no_base_kinds_created++;
return K;
}
@ As noted above, |CON_INTERMEDIATE| is used to store intermediate results
of calculations that are never accessible to outside source text, and have
kinds which couldn't be represented there. For example, if we evaluate
$$ E = mc^2 $$
then we may have perfectly good kinds of value to store energy, mass and
velocity, but have no kind of value for $c^2$, a velocity squared. Such
evanescent kinds are given the special constructor |CON_INTERMEDIATE|.
These are needed relatively seldom and are not cached.
=
kind *Kinds::intermediate_construction(unit_sequence *ik) {
if (ik == NULL) internal_error("made unknown as Kinds::intermediate_construction");
kind *K;
@<Create a raw kind structure@>;
K->construct = CON_INTERMEDIATE;
K->intermediate_result = CREATE(unit_sequence);
*(K->intermediate_result) = *ik;
no_intermediate_kinds_created++;
return K;
}
@ Kind variables A to Z (where |N| is 1 to 26 below) can usually stand for
any kind, but can also be marked with a "declaration", usually
constraining what kind of value they are allowed to hold. For example, K
might be marked as being an arithmetical kind of value. See "Kind
Checking.w".
=
kind *Kinds::variable_construction(int N, kind *declaration) {
if ((N == 0) || (N > MAX_KIND_VARIABLES)) internal_error("bad kind variable");
kind *K;
@<Create a raw kind structure@>;
K->construct = CON_KIND_VARIABLE;
K->kind_variable_number = N;
K->kc_args[0] = declaration;
return K;
}
@ That completes the possible base constructions. Proper constructions are made
using the following. For example,
2020-04-08 01:02:44 +03:00
= (text)
Kinds::unary_construction(CON_list_of, K_number)
=
2019-02-05 02:44:07 +02:00
produces a kind structure meaning "list of numbers". This is not cached
anywhere, so a second request for the same thing will produce a different copy
in memory of the same structure. Profiling shows that little memory is in
practice wasted.
=
kind *Kinds::unary_construction(kind_constructor *con, kind *X) {
kind *K;
if (Kinds::Constructors::arity(con) != 1) internal_error("bad unary construction");
@<Create a raw kind structure@>;
K->construct = con; K->kc_args[0] = X;
no_constructed_kinds_created++;
return K;
}
kind *Kinds::binary_construction(kind_constructor *con, kind *X, kind *Y) {
kind *K;
if (Kinds::Constructors::arity(con) != 2) internal_error("bad binary construction");
@<Create a raw kind structure@>;
K->construct = con; K->kc_args[0] = X; K->kc_args[1] = Y;
no_constructed_kinds_created++;
if (con == CON_phrase) {
if ((X == NULL) || (Y == NULL)) internal_error("bad function kind");
if ((X->construct == CON_TUPLE_ENTRY) && (X->kc_args[0] == K_nil))
internal_error("nil nil");
if (Y->construct == CON_TUPLE_ENTRY) internal_error("bizarre");
}
return K;
}
@ We've now seen the only ways to create a kind structure, and they share the
following initialisation:
@<Create a raw kind structure@> =
K = CREATE(kind);
K->construct = NULL;
K->intermediate_result = NULL;
K->kind_variable_number = 0;
int i;
for (i=0; i<MAX_KIND_CONSTRUCTION_ARITY; i++) K->kc_args[i] = NULL;
@h Constructing kinds for functions.
The following uses the above methods to put together the kind of a function,
making use of the punctuation nodes |CON_TUPLE_ENTRY| and |CON_NIL|. Note
that we use |K_nil| to represent the absence of a return kind (the "nothing"
in a function to nothing). Note also that a function from X to Y, with just
one argument, comes out as:
2020-04-08 01:02:44 +03:00
= (text)
CON_phrase
CON_TUPLE_ENTRY
X
CON_NIL
Y
=
2019-02-05 02:44:07 +02:00
rather than as:
2020-04-08 01:02:44 +03:00
= (text)
CON_phrase
X
Y
=
2019-02-05 02:44:07 +02:00
(It's more convenient to have a predictable form than to save on kind nodes.)
=
kind *Kinds::function_kind(int no_args, kind **args, kind *return_K) {
kind *arguments_K = K_nil;
int i;
for (i=no_args-1; i>=0; i--)
arguments_K = Kinds::binary_construction(CON_TUPLE_ENTRY, args[i], arguments_K);
if (return_K == NULL) return_K = K_nil;
return Kinds::binary_construction(CON_phrase, arguments_K, return_K);
}
@h Constructing kinds for pairs.
Similarly, but more simply, here is the kind for an ordered pair of values:
=
kind *Kinds::pair_kind(kind *X, kind *Y) {
return Kinds::binary_construction(CON_combination, X, Y);
}
@h Iterating through kinds.
It's clearly not literally possible to iterate through kinds (there are
infinitely many) or even through base kinds (since intermediate and variable
constructions confuse the picture), but it does turn out to be convenient
to iterate through all possible constructions, wrapped up into base kind
format. Thus:
@d LOOP_OVER_BASE_KINDS(K)
for (K=Kinds::first_base_k(); K; K = Kinds::next_base_k(K))
@ This requires the following iterator routines. Note that these will
produce base constructions using constructors of higher arity than that
(for example, it will make "list of K" as a base kind, with no arguments);
this would be unsuitable as the kind of any data, but is convenient for
drawing up the index, and so on.
=
kind *Kinds::first_base_k(void) {
kind_constructor *con;
LOOP_OVER(con, kind_constructor)
if ((con != CON_KIND_VARIABLE) && (con != CON_INTERMEDIATE))
return Kinds::base_construction(con);
return NULL;
}
kind *Kinds::next_base_k(kind *K) {
if (K == NULL) return NULL;
kind_constructor *con = K->construct;
do {
con = NEXT_OBJECT(con, kind_constructor);
} while ((con == CON_KIND_VARIABLE) || (con == CON_INTERMEDIATE));
if (con == NULL) return NULL;
return Kinds::base_construction(con);
}
@h Annotations of kinds.
Most of the time, the only annotation of a kind node is the constructor used:
=
kind_constructor *Kinds::get_construct(kind *K) {
if (K) return K->construct;
return NULL;
}
@ But for the benefit of intermediate and variable kind nodes, we also need:
=
int Kinds::is_intermediate(kind *K) {
if ((K) && (K->construct == CON_INTERMEDIATE)) return TRUE;
return FALSE;
}
int Kinds::get_variable_number(kind *K) {
if ((K) && (K->construct == CON_KIND_VARIABLE)) return K->kind_variable_number;
return -1;
}
kind *Kinds::get_variable_stipulation(kind *K) {
if ((K) && (K->construct == CON_KIND_VARIABLE)) return K->kc_args[0];
return NULL;
}
@ Two convenient wrappers for talking about the constructor used:
=
int Kinds::is_proper_constructor(kind *K) {
if (Kinds::arity_of_constructor(K) > 0) return TRUE;
return FALSE;
}
int Kinds::arity_of_constructor(kind *K) {
if (K) return Kinds::Constructors::arity(K->construct);
return 0;
}
@ Given, say, "list of numbers", the following returns "number":
=
kind *Kinds::unary_construction_material(kind *K) {
if (Kinds::arity_of_constructor(K) != 1) return NULL;
return K->kc_args[0];
}
@ More awkwardly:
=
void Kinds::binary_construction_material(kind *K, kind **X, kind **Y) {
if (Kinds::arity_of_constructor(K) != 2) {
if (X) *X = NULL;
if (Y) *Y = NULL;
} else {
if (X) *X = K->kc_args[0];
if (Y) *Y = K->kc_args[1];
}
}
@h Traversing the tree.
Here we look through a kind tree in search of a given constructor at any node.
=
int Kinds::contains(kind *K, kind_constructor *con) {
if (K == NULL) return FALSE;
if (K->construct == con) return TRUE;
int i;
for (i=0; i<MAX_KIND_CONSTRUCTION_ARITY; i++)
if (Kinds::contains(K->kc_args[i], con))
return TRUE;
return FALSE;
}
@h Kind variable substitution.
Once we have determined what the kind variables stand for, we sometimes want
to perform substitution to convert (say) "relation of K to list of K" to
(say) "relation of numbers to list of numbers".
However, in order to ensure that caches are never invalidated, we are careful
never to alter a |kind| structure once it has been created; instead,
we return a different structure imitating the shape of the original.
We set the flag indicated by |changed| to |TRUE| if we make any change,
assuming that it was originally |FALSE| before the first use of this function.
=
kind *Kinds::substitute(kind *K, kind **meanings, int *changed) {
if (meanings == NULL) meanings = values_of_kind_variables;
int N = Kinds::get_variable_number(K);
if (N > 0) {
*changed = TRUE;
return meanings[N];
}
if (Kinds::is_proper_constructor(K)) {
kind *X = NULL, *X_after = NULL, *Y = NULL, *Y_after = NULL;
int tx = FALSE, ty = FALSE;
int a = Kinds::arity_of_constructor(K);
if (a == 1) {
X = Kinds::unary_construction_material(K);
X_after = Kinds::substitute(X, meanings, &tx);
if (tx) {
*changed = TRUE;
return Kinds::unary_construction(K->construct, X_after);
}
} else {
Kinds::binary_construction_material(K, &X, &Y);
X_after = Kinds::substitute(X, meanings, &tx);
Y_after = Kinds::substitute(Y, meanings, &ty);
if ((tx) || (ty)) {
*changed = TRUE;
return Kinds::binary_construction(K->construct, X_after, Y_after);
}
}
}
return K;
}
@h Weakening.
This operation corresponds to rounding kinds up to "object": that is, any
subkind of "object" is replaced by "object".
=
kind *Kinds::weaken(kind *K) {
if (Kinds::is_proper_constructor(K)) {
kind *X = NULL, *Y = NULL;
int a = Kinds::arity_of_constructor(K);
if (a == 1) {
X = Kinds::unary_construction_material(K);
return Kinds::unary_construction(K->construct, Kinds::weaken(X));
} else {
Kinds::binary_construction_material(K, &X, &Y);
return Kinds::binary_construction(K->construct, Kinds::weaken(X), Kinds::weaken(Y));
}
} else {
if ((K) && (Kinds::Compare::lt(K, K_object))) return K_object;
}
return K;
}
@h Property dereferencing.
Properties are sometimes nouns referring to themselves, and sometimes nouns
referring to their values, and these have different kinds. So:
=
kind *Kinds::dereference_properties(kind *K) {
if ((K) && (K->construct == CON_property))
return Kinds::unary_construction_material(K);
if (Kinds::is_proper_constructor(K)) {
kind *X = NULL, *Y = NULL;
int a = Kinds::arity_of_constructor(K);
if (a == 1) {
X = Kinds::unary_construction_material(K);
return Kinds::unary_construction(K->construct,
Kinds::dereference_properties(X));
} else {
Kinds::binary_construction_material(K, &X, &Y);
return Kinds::binary_construction(K->construct,
Kinds::dereference_properties(X), Kinds::dereference_properties(Y));
}
}
return K;
}
@ Inform builds "natural language" in so that it can create an instance for
each natural language whose bundle it can find. "Grammatical gender" is used
as a kind whose name coincides with a property.
=
<notable-linguistic-kinds> ::=
natural language |
grammatical gender |
grammatical tense |
narrative viewpoint |
grammatical case
@h Creating new base kind constructors.
Inform's whole stock of constructors comes from two routes: this one, from the
source text, and another we shall see later, from the Kind Interpreter. The
following is called in response to sentences like:
>> Texture is a kind of value. A musical instrument is a kind of thing.
The word range is the name ("texture", "musical instrument"), and |super|
is the super-kind ("value", "thing").
=
int no_kinds_of_object = 1;
kind *Kinds::new_base(parse_node_tree *T, wording W, kind *super) {
2019-02-05 02:44:07 +02:00
#ifdef PROTECTED_MODEL_PROCEDURE
PROTECTED_MODEL_PROCEDURE;
#endif
kind *K = Kinds::base_construction(
Kinds::Constructors::new(T, Kinds::get_construct(super), NULL, I"#NEW"));
2019-02-05 02:44:07 +02:00
@<Renew the subject if necessary to cope with an early subject creation@>;
#ifdef CORE_MODULE
if (Kinds::Compare::le(super, K_object))
InferenceSubjects::falls_within(Kinds::Knowledge::as_subject(K), Kinds::Knowledge::as_subject(super));
#endif
@<Use the source-text name to attach a noun to the constructor@>;
if (<notable-linguistic-kinds>(W)) {
Kinds::Constructors::mark_as_linguistic(K->construct);
switch (<<r>>) {
2020-07-29 20:43:44 +03:00
case 0: K_natural_language = K;
#ifdef NOTIFY_NATURAL_LANGUAGE_KINDS_CALLBACK
NOTIFY_NATURAL_LANGUAGE_KINDS_CALLBACK(K);
2019-02-05 02:44:07 +02:00
#endif
break;
case 1: K_grammatical_gender = K; break;
}
}
#ifdef CORE_MODULE
if (<property-name>(W)) {
property *P = <<rp>>;
Properties::Valued::set_kind(P, K);
Instances::make_kind_coincident(K, P);
if (Kinds::Compare::eq(K, K_grammatical_gender)) P_grammatical_gender = P;
}
#endif
#ifdef NEW_BASE_KIND_NOTIFY
Plugins::Call::new_base_kind_notify(K, Kinds::Behaviour::get_name_in_template_code(K), W);
#endif
latest_base_kind_of_value = K;
LOGIF(KIND_CREATIONS, "Created base kind $u\n", K);
return K;
}
@ This is used to overcome a timing problem. A few inference subjects need to
be defined early in Inform's run to set up relations -- "thing", for example.
So when we do finally create "thing" as a kind of object, it needs to be
matched up with the inference subject already existing.
@<Renew the subject if necessary to cope with an early subject creation@> =
#ifdef CORE_MODULE
inference_subject *revised = NULL;
if (Wordings::nonempty(W)) Plugins::Call::name_to_early_infs(W, &revised);
if (revised) {
InferenceSubjects::renew(revised,
Kinds::Knowledge::as_subject(super), KIND_SUB, STORE_POINTER_kind_constructor(K->construct), LIKELY_CE);
Kinds::Knowledge::set_subject(K, revised);
}
#endif
@<Use the source-text name to attach a noun to the constructor@> =
unsigned int mc = KIND_SLOW_MC;
if (Kinds::Compare::le(super, K_object)) mc = NOUN_MC;
2020-07-03 01:09:26 +03:00
NATURAL_LANGUAGE_WORDS_TYPE *L = NULL;
#ifdef CORE_MODULE
L = Task::language_of_syntax();
#endif
2019-02-05 02:44:07 +02:00
noun *nt = Nouns::new_common_noun(W, NEUTER_GENDER,
ADD_TO_LEXICON_NTOPT + WITH_PLURAL_FORMS_NTOPT,
2020-07-03 01:09:26 +03:00
KIND_SLOW_MC, STORE_POINTER_kind_constructor(K->construct), L);
#ifdef CORE_MODULE
Sentences::Headings::initialise_noun_resolution(nt);
#endif
2019-02-05 02:44:07 +02:00
Kinds::Constructors::attach_noun(K->construct, nt);
if (Kinds::Compare::le(super, K_object))
Kinds::Behaviour::set_range_number(K, no_kinds_of_object++);
2019-02-05 02:44:07 +02:00
@h Kind names in the I6 template.
We defined some "constant" kinds and constructors above, to provide
values like |K_number| for use in this C source code. We will also want to
refer to these kinds in the Inform 6 source code for the template, where
they will have identifiers such as |NUMBER_TY|. (If anything it's the other
way round, since the template creates these kinds at run-time, using the
kind interpreter -- of which, more later.)
So we need a way of pairing up names in these two source codes, and here
it is. There is no need for speed here.
@d IDENTIFIERS_CORRESPOND(text_of_I6_name, pointer_to_I7_structure)
if ((sn) && (Str::eq_narrow_string(sn, text_of_I6_name))) return pointer_to_I7_structure;
=
kind_constructor **Kinds::known_constructor_name(text_stream *sn) {
IDENTIFIERS_CORRESPOND("ACTIVITY_TY", &CON_activity);
IDENTIFIERS_CORRESPOND("COMBINATION_TY", &CON_combination);
IDENTIFIERS_CORRESPOND("DESCRIPTION_OF_TY", &CON_description);
IDENTIFIERS_CORRESPOND("INTERMEDIATE_TY", &CON_INTERMEDIATE);
IDENTIFIERS_CORRESPOND("KIND_VARIABLE_TY", &CON_KIND_VARIABLE);
IDENTIFIERS_CORRESPOND("LIST_OF_TY", &CON_list_of);
IDENTIFIERS_CORRESPOND("PHRASE_TY", &CON_phrase);
IDENTIFIERS_CORRESPOND("NIL_TY", &CON_NIL);
IDENTIFIERS_CORRESPOND("PROPERTY_TY", &CON_property);
IDENTIFIERS_CORRESPOND("RELATION_TY", &CON_relation);
IDENTIFIERS_CORRESPOND("RULE_TY", &CON_rule);
IDENTIFIERS_CORRESPOND("RULEBOOK_TY", &CON_rulebook);
IDENTIFIERS_CORRESPOND("TABLE_COLUMN_TY", &CON_table_column);
IDENTIFIERS_CORRESPOND("TUPLE_ENTRY_TY", &CON_TUPLE_ENTRY);
IDENTIFIERS_CORRESPOND("VARIABLE_TY", &CON_variable);
return NULL;
}
kind **Kinds::known_kind_name(text_stream *sn) {
IDENTIFIERS_CORRESPOND("ARITHMETIC_VALUE_TY", &K_arithmetic_value);
IDENTIFIERS_CORRESPOND("ENUMERATED_VALUE_TY", &K_enumerated_value);
IDENTIFIERS_CORRESPOND("EQUATION_TY", &K_equation);
IDENTIFIERS_CORRESPOND("TEXT_TY", &K_text);
IDENTIFIERS_CORRESPOND("NUMBER_TY", &K_number);
IDENTIFIERS_CORRESPOND("OBJECT_TY", &K_object);
IDENTIFIERS_CORRESPOND("POINTER_VALUE_TY", &K_pointer_value);
IDENTIFIERS_CORRESPOND("REAL_ARITHMETIC_VALUE_TY", &K_real_arithmetic_value);
IDENTIFIERS_CORRESPOND("REAL_NUMBER_TY", &K_real_number);
IDENTIFIERS_CORRESPOND("RESPONSE_TY", &K_response);
IDENTIFIERS_CORRESPOND("RULEBOOK_OUTCOME_TY", &K_rulebook_outcome);
IDENTIFIERS_CORRESPOND("SAYABLE_VALUE_TY", &K_sayable_value);
IDENTIFIERS_CORRESPOND("SNIPPET_TY", &K_snippet);
IDENTIFIERS_CORRESPOND("TABLE_TY", &K_table);
IDENTIFIERS_CORRESPOND("TRUTH_STATE_TY", &K_truth_state);
IDENTIFIERS_CORRESPOND("UNDERSTANDING_TY", &K_understanding);
IDENTIFIERS_CORRESPOND("UNICODE_CHARACTER_TY", &K_unicode_character);
IDENTIFIERS_CORRESPOND("USE_OPTION_TY", &K_use_option);
IDENTIFIERS_CORRESPOND("VALUE_TY", &K_value);
IDENTIFIERS_CORRESPOND("VERB_TY", &K_verb);
IDENTIFIERS_CORRESPOND("WORD_VALUE_TY", &K_word_value);
IDENTIFIERS_CORRESPOND("NIL_TY", &K_nil);
return NULL;
}
int Kinds::known_name(text_stream *sn) {
if (Kinds::known_constructor_name(sn)) return TRUE;
if (Kinds::known_kind_name(sn)) return TRUE;
return FALSE;
}
@h Annotating vocabulary.
=
2020-05-27 01:02:00 +03:00
#ifdef KINDS_MODULE
2019-02-05 02:44:07 +02:00
kind *Kinds::read_kind_marking_from_vocabulary(vocabulary_entry *ve) {
return ve->means.one_word_kind;
}
void Kinds::mark_vocabulary_as_kind(vocabulary_entry *ve, kind *K) {
ve->means.one_word_kind = K;
Vocabulary::set_flags(ve, KIND_FAST_MC);
2020-05-19 13:46:13 +03:00
NTI::mark_vocabulary(ve, <k-kind>);
2019-02-05 02:44:07 +02:00
}
2020-05-27 01:02:00 +03:00
#endif
2019-02-05 02:44:07 +02:00
@h From context.
Sometimes we need to kmow the current values of the 26 kind variables, A
to Z: that depemds on a much wider context than the |kinds| module can see,
so we need the client to help us. |v| is in the range 1 to 26. Returning
|NULL| means there is no current meaning; so if the client provides no
function to tell us, then all variables are permanently unset.
=
kind *Kinds::variable_from_context(int v) {
#ifdef KIND_VARIABLE_FROM_CONTEXT
return KIND_VARIABLE_FROM_CONTEXT(v);
#endif
#ifndef KIND_VARIABLE_FROM_CONTEXT
return NULL;
#endif
}
2019-02-05 02:44:07 +02:00
@h Errors.
@e DimensionRedundant_KINDERROR from 1
@e DimensionNotBaseKOV_KINDERROR
@e NonDimensional_KINDERROR
@e UnitSequenceOverflow_KINDERROR
@e DimensionsInconsistent_KINDERROR
@e KindUnalterable_KINDERROR
@e KindsCircular_KINDERROR
@e LPCantScaleYet_KINDERROR
@e LPCantScaleTwice_KINDERROR
@ Some tools using this module will want to push simple error messages out to
the command line; others will want to translate them into elaborate problem
texts in HTML. So the client is allowed to define |KINDS_PROBLEM_HANDLER|
to some routine of her own, gazumping this one.
=
void Kinds::problem_handler(int err_no, parse_node *pn, kind *K1, kind *K2) {
#ifdef KINDS_PROBLEM_HANDLER
KINDS_PROBLEM_HANDLER(err_no, pn, K1, K2);
#endif
#ifndef KINDS_PROBLEM_HANDLER
2020-06-28 01:18:54 +03:00
TEMPORARY_TEXT(text)
2020-05-11 17:21:29 +03:00
WRITE_TO(text, "%+W", Node::get_text(pn));
switch (err_no) {
case DimensionRedundant_KINDERROR:
Errors::with_text("multiplication rule given twice: %S", text);
break;
case DimensionNotBaseKOV_KINDERROR:
Errors::with_text("multiplication rule too complex: %S", text);
break;
case NonDimensional_KINDERROR:
Errors::with_text("multiplication rule quotes non-numerical kinds: %S", text);
break;
case UnitSequenceOverflow_KINDERROR:
Errors::with_text("multiplication rule far too complex: %S", text);
break;
case DimensionsInconsistent_KINDERROR:
Errors::with_text("multiplication rule creates inconsistency: %S", text);
break;
case KindUnalterable_KINDERROR:
Errors::with_text("making this subkind would lead to a contradiction: %S", text);
break;
case KindsCircular_KINDERROR:
Errors::with_text("making this subkind would lead to a circularity: %S", text);
break;
case LPCantScaleYet_KINDERROR:
Errors::with_text("tries to scale a value with no point of reference: %S", text);
break;
case LPCantScaleTwice_KINDERROR:
Errors::with_text("tries to scale a value which has already been scaled: %S", text);
break;
default: internal_error("unimplemented problem message");
}
2020-06-28 01:18:54 +03:00
DISCARD_TEXT(text)
#endif
}
2019-02-05 02:44:07 +02:00