mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 18:14:21 +03:00
627 lines
23 KiB
OpenEdge ABL
627 lines
23 KiB
OpenEdge ABL
[Kinds::Constructors::] Kind Constructors.
|
|
|
|
The mechanism by which Inform records the characteristics of different
|
|
kinds.
|
|
|
|
@h Definitions.
|
|
|
|
@ Constructors are divided into four:
|
|
|
|
@d KIND_VARIABLE_GRP 1 /* just |CON_KIND_VARIABLE| on its own */
|
|
@d KIND_OF_KIND_GRP 2 /* an indefinite base constructor such as "arithmetic value" */
|
|
@d BASE_CONSTRUCTOR_GRP 3 /* a definite one such as "number" */
|
|
@d PROPER_CONSTRUCTOR_GRP 4 /* with positive arity, such as "list of K" */
|
|
|
|
@ Inform provides much more extensive facilities for kinds than most
|
|
programming languages do for their types. As far as possible, we want to
|
|
write this code in a generalised way, rather than writing hacky routines
|
|
which each apply to a fixed, named kind. This won't always be possible, it's
|
|
true, but we can try. The only way to achieve this is to store an enormous
|
|
rag-bag of properties for every kind constructor, showing exactly how it
|
|
behaves.
|
|
|
|
All of which is by way of apology for the enormous size of the |kind_constructor|
|
|
structure. It looks impossibly large to fill out, and this is why we have the
|
|
kind interpreter (see next section) to give the I6 template the ability to
|
|
forge new kind constructors.
|
|
|
|
@d LOWEST_INDEX_PRIORITY 100
|
|
|
|
=
|
|
typedef struct kind_constructor {
|
|
struct noun *dt_tag; /* text of name */
|
|
int group; /* one of the four values above */
|
|
|
|
/* A: how this came into being */
|
|
int defined_in_source_text; /* rather than by I6 template files, i.e., by being built-in */
|
|
int is_incompletely_defined; /* newly defined and ambiguous as yet */
|
|
struct parse_node *where_defined_in_source_text; /* if so */
|
|
struct kind *stored_as; /* currently unused: if this is a typedef for some construction */
|
|
|
|
/* B: constructing kinds */
|
|
int constructor_arity; /* 0 for base, 1 for unary, 2 for binary */
|
|
int variance[MAX_KIND_CONSTRUCTION_ARITY]; /* must be |COVARIANT| or |CONTRAVARIANT| */
|
|
int tupling[MAX_KIND_CONSTRUCTION_ARITY]; /* extent to which tupling is permitted */
|
|
struct kind *cached_kind; /* cached result of |Kinds::base_construction| */
|
|
|
|
/* C: compatibility with other kinds */
|
|
struct parse_node *superkind_set_at; /* where it says, e.g., "A rabbit is a kind of animal" */
|
|
struct kind_constructor_casting_rule *first_casting_rule; /* list of these */
|
|
struct kind_constructor_instance *first_instance_rule; /* list of these */
|
|
|
|
/* D: how constant values of this kind are expressed */
|
|
struct literal_pattern *ways_to_write_literals; /* list of ways to write this */
|
|
int named_values_created_with_assertions; /* such as "Train Arrival is a scene." */
|
|
struct table *named_values_created_with_table; /* alternatively... */
|
|
int next_free_value; /* to make distinguishable instances of this kind */
|
|
int constant_compilation_method; /* one of the |*_CCM| values */
|
|
|
|
/* E: knowledge about values of this kind */
|
|
struct inference_subject *dt_knowledge; /* inferences about properties */
|
|
struct text_stream *default_value; /* used for built-in types only */
|
|
|
|
/* F: behaviour as a property as well */
|
|
int can_coincide_with_property; /* allowed to coincide in name with a property */
|
|
struct property *coinciding_property; /* property of the same name, if any */
|
|
|
|
/* G: performing arithmetic */
|
|
struct text_stream *comparison_routine; /* for instance, when sorting table or list entries */
|
|
struct dimensional_rules dim_rules; /* how arithmetic operations work here */
|
|
struct unit_sequence dimensional_form; /* dimensions of this kind */
|
|
int dimensional_form_fixed; /* whether they are derived */
|
|
|
|
/* H: representing this kind at run-time */
|
|
int weak_kind_ID; /* as used at run-time by the I6 template */
|
|
struct text_stream *name_in_template_code; /* an I6 identifier */
|
|
int class_number; /* for classes of object */
|
|
#ifdef CORE_MODULE
|
|
struct inter_name *con_iname;
|
|
struct package_request *kc_package;
|
|
#endif
|
|
int small_block_size; /* if stored as a block value, size in words of the SB */
|
|
|
|
/* I: storing values at run-time */
|
|
int multiple_block; /* TRUE for flexible-size values stored on the heap */
|
|
int heap_size_estimate; /* typical number of bytes used */
|
|
int can_exchange; /* with external files and therefore other story files */
|
|
struct text_stream *distinguisher; /* I6 routine to see if values distinguishable */
|
|
struct kind_constructor_comparison_schema *first_comparison_schema; /* list of these */
|
|
struct text_stream *loop_domain_schema; /* how to compile an I6 loop over the instances */
|
|
|
|
/* J: printing and parsing values at run-time */
|
|
#ifdef BYTECODE_MODULE
|
|
struct inter_name *kind_GPR_iname;
|
|
struct inter_name *instance_GPR_iname;
|
|
struct inter_name *first_instance_iname;
|
|
struct inter_name *next_instance_iname;
|
|
struct inter_name *pr_iname;
|
|
struct inter_name *inc_iname;
|
|
struct inter_name *dec_iname;
|
|
struct inter_name *ranger_iname;
|
|
struct inter_name *trace_iname;
|
|
#endif
|
|
struct text_stream *dt_I6_identifier; /* an I6 identifier used for compiling printing rules */
|
|
struct text_stream *name_of_printing_rule_ACTIONS; /* ditto but for ACTIONS testing command */
|
|
struct grammar_verb *understand_as_values; /* used when parsing such values */
|
|
int has_i6_GPR; /* a general parsing routine exists in the I6 code for this */
|
|
int I6_GPR_needed; /* and is actually required */
|
|
struct text_stream *explicit_i6_GPR; /* routine name, when not compiled automatically */
|
|
struct text_stream *recognition_only_GPR; /* for recognising an explicit value as preposition */
|
|
|
|
/* K: indexing and documentation */
|
|
struct text_stream *specification_text; /* text for parse_node */
|
|
struct text_stream *constructor_description; /* text used in index pages */
|
|
struct text_stream *index_default_value; /* and its description in the Kinds index */
|
|
struct text_stream *index_maximum_value; /* ditto */
|
|
struct text_stream *index_minimum_value; /* ditto */
|
|
int index_priority; /* from 1 (highest) to |LOWEST_INDEX_PRIORITY| (lowest) */
|
|
int linguistic; /* divide off as having linguistics content */
|
|
int indexed_grey_if_empty; /* shaded grey in the Kinds index */
|
|
struct text_stream *documentation_reference; /* documentation symbol, if any */
|
|
|
|
CLASS_DEFINITION
|
|
} kind_constructor;
|
|
|
|
@ The "tupling" of an argument is the extent to which an argument can be
|
|
allowed to hold a variable-length list of kinds, rather than a single one.
|
|
There aren't actually many possibilities.
|
|
|
|
@d NO_TUPLING 0 /* a single kind */
|
|
@d ALLOW_NOTHING_TUPLING 1 /* a single kind, or "nothing" */
|
|
@d ARBITRARY_TUPLING 10000 /* a list of kinds of any length */
|
|
|
|
@ Constant compilation modes are:
|
|
|
|
@d NONE_CCM 1 /* constant values of this kind cannot exist */
|
|
@d LITERAL_CCM 2 /* a numerical annotation decides the value */
|
|
@d NAMED_CONSTANT_CCM 3 /* an |instance| annotation decides the value */
|
|
@d SPECIAL_CCM 4 /* special code specific to the kind of value is needed */
|
|
|
|
@ We keep track of the newest-created base kind of value (which isn't a kind
|
|
of object) here:
|
|
|
|
= (early code)
|
|
int next_free_data_type_ID = 2; /* i.e., leaving room for |UNKNOWN_TY| to be 1 at run-time */
|
|
kind *latest_base_kind_of_value = NULL;
|
|
|
|
@h Creation.
|
|
Constructors come from two sources. Built-in ones like "number" or
|
|
"list of K" mainly come from the commands given in the "Load-Core.i6t"
|
|
template file, which consists almost entirely of commands for the kind
|
|
interpreter, which sets up most of the above. (Similar files for other
|
|
language plugins add the remainder.) Thus a great deal can be changed about
|
|
the interplay of kinds without altering the compiler itself.
|
|
|
|
New kinds created by the source text, by sentences like "Air pressure is a
|
|
kind of value", are always base constructors (i.e., they have arity 0); at
|
|
present there's no way to create new kinds of kinds, or new constructors, in
|
|
Inform source text. (So an extension wanting to make new constructors, say
|
|
to add new "collection classes" to Inform, will have to get its hands
|
|
dirty with Inform 6 insertions and use of the kind interpreter.)
|
|
|
|
Here |super| will be the super-constructor, the one which this will construct
|
|
subkinds of. In practice this will be |NULL| when |CON_VALUE| is created, and
|
|
then |CON_VALUE| for kinds like "number" or this one:
|
|
|
|
>> Weight is a kind of value.
|
|
|
|
but will be the constructor for "door" for kinds like this one:
|
|
|
|
>> Portal is a kind of door.
|
|
|
|
=
|
|
kind_constructor *Kinds::Constructors::new(parse_node_tree *T, kind_constructor *super, text_stream *source_name,
|
|
text_stream *initialisation_macro) {
|
|
kind_constructor *con = CREATE(kind_constructor);
|
|
kind_constructor **pC = Kinds::known_constructor_name(source_name);
|
|
if (pC) *pC = con;
|
|
|
|
if (super == Kinds::get_construct(K_value)) @<Fill in a new constructor@>
|
|
else @<Copy the new constructor from its superconstructor@>;
|
|
|
|
con->name_in_template_code = Str::new();
|
|
#ifdef CORE_MODULE
|
|
con->con_iname = NULL;
|
|
con->kc_package = NULL;
|
|
#endif
|
|
if (Str::len(source_name) > 0) WRITE_TO(con->name_in_template_code, "%S", source_name);
|
|
#ifdef CORE_MODULE
|
|
if ((con != CON_KIND_VARIABLE) && (con != CON_INTERMEDIATE))
|
|
con->dt_knowledge = Kinds::Knowledge::create_for_constructor(con);
|
|
#endif
|
|
con->where_defined_in_source_text = current_sentence;
|
|
|
|
kind **pK = Kinds::known_kind_name(source_name);
|
|
if (pK) *pK = Kinds::base_construction(con);
|
|
return con;
|
|
}
|
|
|
|
@ If our new constructor is wholly new, and isn't a subkind of something else,
|
|
we need to initialise the entire data structure; but note that, having done so,
|
|
we ask the kind interpreter to load it up with any defaults set in the
|
|
I6 template files.
|
|
|
|
@<Fill in a new constructor@> =
|
|
con->dt_tag = NULL;
|
|
con->group = 0; /* which is invalid, so the interpreter needs to set it */
|
|
|
|
/* A: how this came into being */
|
|
con->defined_in_source_text = FALSE;
|
|
con->is_incompletely_defined = FALSE;
|
|
con->where_defined_in_source_text = NULL; /* but will be filled in imminently */
|
|
con->stored_as = NULL;
|
|
|
|
/* B: constructing kinds */
|
|
con->constructor_arity = 0; /* by default a base constructor */
|
|
int i;
|
|
for (i=0; i<MAX_KIND_CONSTRUCTION_ARITY; i++) {
|
|
con->variance[i] = COVARIANT;
|
|
con->tupling[i] = NO_TUPLING;
|
|
}
|
|
con->cached_kind = NULL;
|
|
|
|
/* C: compatibility with other kinds */
|
|
con->superkind_set_at = NULL;
|
|
con->first_casting_rule = NULL;
|
|
con->first_instance_rule = NULL;
|
|
|
|
/* D: how constant values of this kind are expressed */
|
|
con->ways_to_write_literals = NULL;
|
|
con->named_values_created_with_assertions = FALSE;
|
|
con->named_values_created_with_table = NULL;
|
|
con->next_free_value = 1;
|
|
con->constant_compilation_method = NONE_CCM;
|
|
|
|
/* E: knowledge about values of this kind */
|
|
con->dt_knowledge = NULL; /* but will be filled in imminently, in almost all cases */
|
|
con->default_value = Str::new();
|
|
|
|
/* F: behaviour as a property as well */
|
|
con->can_coincide_with_property = FALSE;
|
|
con->coinciding_property = NULL;
|
|
|
|
/* G: performing arithmetic */
|
|
con->comparison_routine = Str::new();
|
|
WRITE_TO(con->comparison_routine, "UnsignedCompare");
|
|
if ((con == CON_KIND_VARIABLE) || (con == CON_INTERMEDIATE) ||
|
|
((Str::eq_wide_string(source_name, L"NUMBER_TY")) ||
|
|
(Str::eq_wide_string(source_name, L"REAL_NUMBER_TY"))))
|
|
con->dimensional_form =
|
|
Kinds::Dimensions::fundamental_unit_sequence(NULL);
|
|
else
|
|
con->dimensional_form =
|
|
Kinds::Dimensions::fundamental_unit_sequence(Kinds::base_construction(con));
|
|
con->dimensional_form_fixed = FALSE;
|
|
Kinds::Dimensions::dim_initialise(&(con->dim_rules));
|
|
|
|
/* H: representing this kind at run-time */
|
|
con->weak_kind_ID = next_free_data_type_ID++;
|
|
con->name_in_template_code = Str::new();
|
|
con->class_number = 0;
|
|
|
|
/* I: storing values at run-time */
|
|
con->multiple_block = FALSE;
|
|
con->small_block_size = 1;
|
|
con->heap_size_estimate = 0;
|
|
con->can_exchange = FALSE;
|
|
con->first_comparison_schema = NULL;
|
|
con->distinguisher = NULL;
|
|
con->loop_domain_schema = NULL;
|
|
|
|
/* J: printing and parsing values at run-time */
|
|
con->dt_I6_identifier = Str::new();
|
|
con->name_of_printing_rule_ACTIONS = Str::new();
|
|
#ifdef BYTECODE_MODULE
|
|
con->kind_GPR_iname = NULL;
|
|
con->instance_GPR_iname = NULL;
|
|
con->first_instance_iname = NULL;
|
|
con->next_instance_iname = NULL;
|
|
con->pr_iname = NULL;
|
|
con->inc_iname = NULL;
|
|
con->dec_iname = NULL;
|
|
con->ranger_iname = NULL;
|
|
con->trace_iname = NULL;
|
|
if (Str::len(source_name) == 0) {
|
|
package_request *R = Kinds::Constructors::package(con);
|
|
con->pr_iname = Hierarchy::make_iname_in(PRINT_DASH_FN_HL, R);
|
|
con->trace_iname = con->pr_iname;
|
|
}
|
|
#endif
|
|
|
|
con->understand_as_values = NULL;
|
|
con->has_i6_GPR = FALSE;
|
|
con->I6_GPR_needed = FALSE;
|
|
con->explicit_i6_GPR = NULL;
|
|
con->recognition_only_GPR = NULL;
|
|
|
|
/* K: indexing and documentation */
|
|
con->specification_text = NULL;
|
|
con->constructor_description = NULL;
|
|
con->index_default_value = I"--";
|
|
con->index_maximum_value = I"--";
|
|
con->index_minimum_value = I"--";
|
|
con->index_priority = LOWEST_INDEX_PRIORITY;
|
|
con->linguistic = FALSE;
|
|
con->indexed_grey_if_empty = FALSE;
|
|
con->documentation_reference = NULL;
|
|
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(I"#DEFAULTS"), con);
|
|
if (Str::len(initialisation_macro) > 0)
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(initialisation_macro), con);
|
|
|
|
@ However, if we create our constructor as a subkind, like so:
|
|
|
|
>> A turtle is a kind of animal.
|
|
|
|
then we copy the entire "animal" constructor to initialise the "turtle" one.
|
|
|
|
Note that the weak ID number is one of the things copied; this is deliberate.
|
|
It means that all kinds of object share the same weak ID as "object".
|
|
|
|
@<Copy the new constructor from its superconstructor@> =
|
|
int I = con->allocation_id;
|
|
void *N = con->next_structure;
|
|
void *P = con->prev_structure;
|
|
*con = *super;
|
|
con->allocation_id = I;
|
|
con->next_structure = N;
|
|
con->prev_structure = P;
|
|
con->cached_kind = NULL; /* otherwise the superkind's cache is used by mistake */
|
|
con->name_in_template_code = Str::new(); /* otherwise this will be called |OBJECT_TY| by mistake */
|
|
|
|
@h The noun.
|
|
It's a requirement that the following be called soon after the creation
|
|
of the constructor:
|
|
|
|
=
|
|
void Kinds::Constructors::attach_noun(kind_constructor *con, noun *nt) {
|
|
if ((con == NULL) || (nt == NULL)) internal_error("bad noun attachment");
|
|
con->dt_tag = nt;
|
|
}
|
|
|
|
wording Kinds::Constructors::get_name(kind_constructor *con, int plural_form) {
|
|
if (con->dt_tag) {
|
|
noun *nt = con->dt_tag;
|
|
if (nt) return Nouns::nominative(nt, plural_form);
|
|
}
|
|
return EMPTY_WORDING;
|
|
}
|
|
|
|
wording Kinds::Constructors::get_name_in_play(kind_constructor *con, int plural_form,
|
|
NATURAL_LANGUAGE_WORDS_TYPE *nl) {
|
|
if (con->dt_tag) {
|
|
noun *nt = con->dt_tag;
|
|
if (nt) return Nouns::nominative_in_language(nt, plural_form, nl);
|
|
}
|
|
return EMPTY_WORDING;
|
|
}
|
|
|
|
noun *Kinds::Constructors::get_noun(kind_constructor *con) {
|
|
if (con == NULL) return NULL;
|
|
return con->dt_tag;
|
|
}
|
|
|
|
@h Names in the I6 template.
|
|
An identifier like |WHATEVER_TY|, then, begins life in a definition inside an
|
|
I6 template file; becomes attached to a constructor here; and finally winds up
|
|
back in I6 code, because we define it as the constant for the weak kind ID
|
|
of the kind which the constructor makes:
|
|
|
|
=
|
|
#ifdef CORE_MODULE
|
|
inter_name *UNKNOWN_TY_iname = NULL;
|
|
void Kinds::Constructors::compile_I6_constants(void) {
|
|
UNKNOWN_TY_iname = Hierarchy::find(UNKNOWN_TY_HL);
|
|
Emit::named_numeric_constant(UNKNOWN_TY_iname, (inter_ti) UNKNOWN_NT);
|
|
Hierarchy::make_available(Emit::tree(), UNKNOWN_TY_iname);
|
|
|
|
kind_constructor *con;
|
|
LOOP_OVER(con, kind_constructor) {
|
|
text_stream *tn = Kinds::Constructors::name_in_template_code(con);
|
|
if (Str::len(tn) > 0) {
|
|
con->con_iname = Hierarchy::make_iname_with_specific_name(WEAK_ID_HL, tn, Kinds::Constructors::package(con));
|
|
Hierarchy::make_available(Emit::tree(), con->con_iname);
|
|
Emit::named_numeric_constant(con->con_iname, (inter_ti) con->weak_kind_ID);
|
|
}
|
|
}
|
|
|
|
inter_name *hwm = Hierarchy::find(BASE_KIND_HWM_HL);
|
|
Emit::named_numeric_constant(hwm, (inter_ti) next_free_data_type_ID);
|
|
Hierarchy::make_available(Emit::tree(), hwm);
|
|
}
|
|
inter_name *Kinds::Constructors::UNKNOWN_iname(void) {
|
|
if (UNKNOWN_TY_iname == NULL) internal_error("no unknown yet");
|
|
return UNKNOWN_TY_iname;
|
|
}
|
|
package_request *Kinds::Constructors::package(kind_constructor *con) {
|
|
if (con->kc_package == NULL) {
|
|
if (con->defined_in_source_text) {
|
|
compilation_unit *C = CompilationUnits::find(con->where_defined_in_source_text);
|
|
con->kc_package = Hierarchy::package(C, KIND_HAP);
|
|
} else if (con->superkind_set_at) {
|
|
compilation_unit *C = CompilationUnits::find(con->superkind_set_at);
|
|
con->kc_package = Hierarchy::package(C, KIND_HAP);
|
|
} else {
|
|
con->kc_package = Hierarchy::synoptic_package(KIND_HAP);
|
|
}
|
|
wording W = Kinds::Constructors::get_name(con, FALSE);
|
|
if (Wordings::nonempty(W))
|
|
Hierarchy::markup_wording(con->kc_package, KIND_NAME_HMD, W);
|
|
else if (Str::len(con->name_in_template_code) > 0)
|
|
Hierarchy::markup(con->kc_package, KIND_NAME_HMD, con->name_in_template_code);
|
|
else
|
|
Hierarchy::markup(con->kc_package, KIND_NAME_HMD, I"(anonymous kind)");
|
|
}
|
|
return con->kc_package;
|
|
}
|
|
inter_name *Kinds::Constructors::iname(kind_constructor *con) {
|
|
if (UNKNOWN_TY_iname == NULL) internal_error("no con symbols yet");
|
|
return con->con_iname;
|
|
}
|
|
inter_name *Kinds::Constructors::first_instance_iname(kind_constructor *con) {
|
|
return con->first_instance_iname;
|
|
}
|
|
void Kinds::Constructors::set_first_instance_iname(kind_constructor *con, inter_name *iname) {
|
|
con->first_instance_iname = iname;
|
|
}
|
|
inter_name *Kinds::Constructors::next_instance_iname(kind_constructor *con) {
|
|
return con->next_instance_iname;
|
|
}
|
|
void Kinds::Constructors::set_next_instance_iname(kind_constructor *con, inter_name *iname) {
|
|
con->next_instance_iname = iname;
|
|
}
|
|
#endif
|
|
|
|
text_stream *Kinds::Constructors::name_in_template_code(kind_constructor *con) {
|
|
return con->name_in_template_code;
|
|
}
|
|
|
|
@ We also need to parse this, occasionally (if we needed this more than a
|
|
small and bounded number of times we'd want a faster method, but we don't):
|
|
|
|
=
|
|
kind_constructor *Kinds::Constructors::parse(text_stream *sn) {
|
|
if (sn == NULL) return NULL;
|
|
kind_constructor *con;
|
|
LOOP_OVER(con, kind_constructor)
|
|
if (Str::eq(sn, con->name_in_template_code))
|
|
return con;
|
|
return NULL;
|
|
}
|
|
|
|
@h Transformations.
|
|
Conversions of an existing constructor to make it a unit or enumeration also
|
|
require running macros in the kind interpreter:
|
|
|
|
=
|
|
int Kinds::Constructors::convert_to_unit(parse_node_tree *T, kind_constructor *con) {
|
|
if (con->is_incompletely_defined == TRUE) {
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(I"#UNIT"), con);
|
|
return TRUE;
|
|
}
|
|
if (Kinds::Constructors::is_arithmetic(con)) return TRUE; /* i.e., if it succeeded */
|
|
return FALSE;
|
|
}
|
|
|
|
int Kinds::Constructors::convert_to_enumeration(parse_node_tree *T, kind_constructor *con) {
|
|
if (con->is_incompletely_defined == TRUE) {
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(I"#ENUMERATION"), con);
|
|
if (con->linguistic)
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(I"#LINGUISTIC"), con);
|
|
return TRUE;
|
|
}
|
|
if (Kinds::Constructors::is_enumeration(con)) return TRUE; /* i.e., if it succeeded */
|
|
return FALSE;
|
|
}
|
|
|
|
@ And similarly:
|
|
|
|
=
|
|
void Kinds::Constructors::convert_to_real(parse_node_tree *T, kind_constructor *con) {
|
|
Kinds::Interpreter::play_back_kind_macro(T,
|
|
Kinds::Interpreter::parse_kind_macro_name(I"#REAL"), con);
|
|
}
|
|
|
|
@ A few base kinds are marked as "linguistic", which simply enables us to fence
|
|
them tidily off in the index.
|
|
|
|
=
|
|
void Kinds::Constructors::mark_as_linguistic(kind_constructor *con) {
|
|
con->linguistic = TRUE;
|
|
}
|
|
|
|
@h For construction purposes.
|
|
|
|
=
|
|
kind **Kinds::Constructors::cache_location(kind_constructor *con) {
|
|
if (con) return &(con->cached_kind);
|
|
return NULL;
|
|
}
|
|
|
|
int Kinds::Constructors::arity(kind_constructor *con) {
|
|
if (con == NULL) return 0;
|
|
if (con->group == PROPER_CONSTRUCTOR_GRP) return con->constructor_arity;
|
|
return 0;
|
|
}
|
|
|
|
int Kinds::Constructors::tupling(kind_constructor *con, int b) {
|
|
return con->tupling[b];
|
|
}
|
|
|
|
int Kinds::Constructors::variance(kind_constructor *con, int b) {
|
|
return con->variance[b];
|
|
}
|
|
|
|
@h Questions about constructors.
|
|
The rest of Inform is not encouraged to poke at constructors directly; it
|
|
ought to ask questions about kinds instead (see "Using Kinds"). However:
|
|
|
|
=
|
|
int Kinds::Constructors::is_definite(kind_constructor *con) {
|
|
if ((con->group == BASE_CONSTRUCTOR_GRP) ||
|
|
(con->group == PROPER_CONSTRUCTOR_GRP))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int Kinds::Constructors::get_weak_ID(kind_constructor *con) {
|
|
if (con == NULL) return 0;
|
|
return con->weak_kind_ID;
|
|
}
|
|
|
|
int Kinds::Constructors::is_arithmetic(kind_constructor *con) {
|
|
if (con == NULL) return FALSE;
|
|
if ((Kinds::Constructors::is_definite(con)) &&
|
|
(Kinds::Constructors::compatible(con,
|
|
Kinds::get_construct(K_arithmetic_value), FALSE))) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int Kinds::Constructors::is_arithmetic_and_real(kind_constructor *con) {
|
|
if (con == NULL) return FALSE;
|
|
if ((Kinds::Constructors::is_definite(con)) &&
|
|
(Kinds::Constructors::compatible(con,
|
|
Kinds::get_construct(K_real_arithmetic_value), FALSE))) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int Kinds::Constructors::is_enumeration(kind_constructor *con) {
|
|
if (con == NULL) return FALSE;
|
|
if ((Kinds::Constructors::is_definite(con)) &&
|
|
(Kinds::Constructors::compatible(con,
|
|
Kinds::get_construct(K_enumerated_value), FALSE))) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
@h Cast and instance lists.
|
|
Each constructor has a list of other constructors (all of the |KIND_OF_KIND_GRP|
|
|
group) which it's an instance of: value, word value, arithmetic value, and so on.
|
|
|
|
=
|
|
int Kinds::Constructors::find_cast(kind_constructor *from, kind_constructor *to) {
|
|
if (to) {
|
|
kind_constructor_casting_rule *dtcr;
|
|
for (dtcr = to->first_casting_rule; dtcr; dtcr = dtcr->next_casting_rule) {
|
|
if (Str::len(dtcr->cast_from_kind_unparsed) > 0) {
|
|
dtcr->cast_from_kind =
|
|
Kinds::Constructors::parse(dtcr->cast_from_kind_unparsed);
|
|
Str::clear(dtcr->cast_from_kind_unparsed);
|
|
}
|
|
if (from == dtcr->cast_from_kind)
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
@ Each constructor has a list of other constructors (all of the |BASE_CONSTRUCTOR_GRP|
|
|
group or |PROPER_CONSTRUCTOR_GRP|) which it can cast to.
|
|
|
|
=
|
|
int Kinds::Constructors::find_instance(kind_constructor *from, kind_constructor *to) {
|
|
kind_constructor_instance *dti;
|
|
for (dti = from->first_instance_rule; dti; dti = dti->next_instance_rule) {
|
|
if (Str::len(dti->instance_of_this_unparsed) > 0) {
|
|
dti->instance_of_this =
|
|
Kinds::Constructors::parse(dti->instance_of_this_unparsed);
|
|
Str::clear(dti->instance_of_this_unparsed);
|
|
}
|
|
if (dti->instance_of_this == to) return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
@h Compatibility.
|
|
The following tests if |from| is compatible with |to|.
|
|
|
|
=
|
|
int Kinds::Constructors::compatible(kind_constructor *from, kind_constructor *to, int allow_casts) {
|
|
if (to == from) return TRUE;
|
|
if ((to == NULL) || (from == NULL)) return FALSE;
|
|
if ((allow_casts) && (Kinds::Constructors::find_cast(from, to))) return TRUE;
|
|
if (Kinds::Constructors::find_instance(from, to)) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
@ And more elaborately:
|
|
|
|
=
|
|
int Kinds::Constructors::uses_pointer_values(kind_constructor *con) {
|
|
if (con == NULL) return FALSE;
|
|
if ((Kinds::Constructors::is_definite(con)) &&
|
|
(Kinds::Constructors::compatible(con, Kinds::get_construct(K_pointer_value), FALSE))) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
int Kinds::Constructors::allow_word_as_pointer(kind_constructor *left, kind_constructor *right) {
|
|
if (Kinds::Constructors::uses_pointer_values(left) == FALSE) return FALSE;
|
|
if (Kinds::Constructors::uses_pointer_values(right) == TRUE) return FALSE;
|
|
if (Kinds::Constructors::compatible(right, left, TRUE)) return TRUE;
|
|
return FALSE;
|
|
}
|