The mechanism by which Inform records the characteristics of different kinds.
+ +- §6. Creation
- §7. The noun
- §8. Names in the I6 template
- §10. Transformations
- §13. For construction purposes
- §14. Questions about constructors
- §15. Cast and instance lists
- §17. Compatibility
+ +
§1. Constructors are divided into four: +
+ +define KIND_VARIABLE_GRP 1 just CON_KIND_VARIABLE on its own +define KIND_OF_KIND_GRP 2 an indefinite base constructor such as "arithmetic value" +define BASE_CONSTRUCTOR_GRP 3 a definite one such as "number" +define PROPER_CONSTRUCTOR_GRP 4 with positive arity, such as "list of K" ++
§2. 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. +
+ +define LOWEST_INDEX_PRIORITY 100 +define MAX_KIND_CONSTRUCTION_ARITY 2 ++
+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 structure kind_constructor is accessed in 2/tlok, 2/dk, 2/uk, 4/kc and here.
§3. 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. +
+ +define NO_TUPLING 0 a single kind +define ALLOW_NOTHING_TUPLING 1 a single kind, or "nothing" +define ARBITRARY_TUPLING 10000 a list of kinds of any length ++
§4. Constant compilation modes are: +
+ +define NONE_CCM 1 constant values of this kind cannot exist +define LITERAL_CCM 2 a numerical annotation decides the value +define NAMED_CONSTANT_CCM 3 an instance annotation decides the value +define SPECIAL_CCM 4 special code specific to the kind of value is needed ++
§5. We keep track of the newest-created base kind of value (which isn't a kind +of object) here: +
+ ++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; ++
§6. 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 = FamiliarKinds::known_con(source_name); + if (pC) *pC = con; + + if (super == Kinds::get_construct(K_value)) Fill in a new constructor6.1 + else Copy the new constructor from its superconstructor6.2; + + 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 = FamiliarKinds::known_kind(source_name); + if (pK) *pK = Kinds::base_construction(con); + return con; +} ++
§6.1. 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 constructor6.1 = +
+ ++ 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; + + KindCommands::play_back_kind_macro(T, + KindCommands::parse_kind_macro_name(I"#DEFAULTS"), con); + if (Str::len(initialisation_macro) > 0) + KindCommands::play_back_kind_macro(T, + KindCommands::parse_kind_macro_name(initialisation_macro), con); ++
- This code is used in §6.
§6.2. 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 superconstructor6.2 = +
+ ++ 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 ++
- This code is used in §6.
§7. 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; +} ++
§8. 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 +void Kinds::Constructors::compile_I6_constants(void) { + 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) { + return CON_UNKNOWN->con_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) { + 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; +} ++
§9. 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; +} ++
§10. 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) { + KindCommands::play_back_kind_macro(T, + KindCommands::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) { + KindCommands::play_back_kind_macro(T, + KindCommands::parse_kind_macro_name(I"#ENUMERATION"), con); + if (con->linguistic) + KindCommands::play_back_kind_macro(T, + KindCommands::parse_kind_macro_name(I"#LINGUISTIC"), con); + return TRUE; + } + if (Kinds::Constructors::is_enumeration(con)) return TRUE; i.e., if it succeeded + return FALSE; +} ++ + +
+void Kinds::Constructors::convert_to_real(parse_node_tree *T, kind_constructor *con) { + KindCommands::play_back_kind_macro(T, + KindCommands::parse_kind_macro_name(I"#REAL"), con); +} ++
§12. 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; +} ++
§13. 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]; +} ++
§14. 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; +} ++
§15. 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; +} ++
§16. 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; +} ++
§17. 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; +} ++ + +
+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; +} ++ + +