mirror of
https://github.com/ganelson/inform.git
synced 2024-07-09 02:24:21 +03:00
1024 lines
42 KiB
OpenEdge ABL
1024 lines
42 KiB
OpenEdge ABL
[CodeGen::IP::] Instances and Properties.
|
|
|
|
To generate the initial state of storage for instances and their
|
|
properties, and all associated metadata.
|
|
|
|
@
|
|
|
|
=
|
|
int properties_written = FALSE;
|
|
int FBNA_found = FALSE, properties_found = FALSE, attribute_slots_used = 0;
|
|
|
|
int no_property_frames = 0, no_instance_frames = 0, no_kind_frames = 0;
|
|
inter_tree_node **property_frames = NULL;
|
|
inter_tree_node **instance_frames = NULL;
|
|
inter_tree_node **kind_frames = NULL;
|
|
|
|
void CodeGen::IP::prepare(code_generation *gen) {
|
|
properties_written = FALSE;
|
|
FBNA_found = FALSE;
|
|
properties_found = FALSE;
|
|
attribute_slots_used = 0;
|
|
no_property_frames = 0; no_instance_frames = 0; no_kind_frames = 0;
|
|
InterTree::traverse(gen->from, CodeGen::IP::count, NULL, NULL, 0);
|
|
if (no_property_frames > 0)
|
|
property_frames = (inter_tree_node **)
|
|
(Memory::calloc(no_property_frames, sizeof(inter_tree_node *), CODE_GENERATION_MREASON));
|
|
if (no_instance_frames > 0)
|
|
instance_frames = (inter_tree_node **)
|
|
(Memory::calloc(no_instance_frames, sizeof(inter_tree_node *), CODE_GENERATION_MREASON));
|
|
if (no_kind_frames > 0)
|
|
kind_frames = (inter_tree_node **)
|
|
(Memory::calloc(no_kind_frames, sizeof(inter_tree_node *), CODE_GENERATION_MREASON));
|
|
no_property_frames = 0; no_instance_frames = 0; no_kind_frames = 0;
|
|
InterTree::traverse(gen->from, CodeGen::IP::store, NULL, NULL, 0);
|
|
}
|
|
|
|
void CodeGen::IP::count(inter_tree *I, inter_tree_node *P, void *state) {
|
|
if (P->W.data[ID_IFLD] == PROPERTY_IST) no_property_frames++;
|
|
if (P->W.data[ID_IFLD] == INSTANCE_IST) no_instance_frames++;
|
|
if (P->W.data[ID_IFLD] == KIND_IST) no_kind_frames++;
|
|
}
|
|
|
|
void CodeGen::IP::store(inter_tree *I, inter_tree_node *P, void *state) {
|
|
if (P->W.data[ID_IFLD] == PROPERTY_IST) property_frames[no_property_frames++] = P;
|
|
if (P->W.data[ID_IFLD] == INSTANCE_IST) instance_frames[no_instance_frames++] = P;
|
|
if (P->W.data[ID_IFLD] == KIND_IST) kind_frames[no_kind_frames++] = P;
|
|
}
|
|
|
|
void CodeGen::IP::write_properties(code_generation *gen) {
|
|
if (properties_written == FALSE) {
|
|
generated_segment *saved = CodeGen::select(gen, CodeGen::Targets::default_segment(gen));
|
|
text_stream *TO = CodeGen::current(gen);
|
|
if (CodeGen::CL::quartet_present()) {
|
|
WRITE_TO(TO, "Object Compass \"compass\" has concealed;\n");
|
|
WRITE_TO(TO, "Object thedark \"(darkness object)\";\n");
|
|
WRITE_TO(TO, "Object InformParser \"(Inform Parser)\" has proper;\n");
|
|
WRITE_TO(TO, "Object InformLibrary \"(Inform Library)\" has proper;\n");
|
|
}
|
|
CodeGen::IP::knowledge(gen);
|
|
CodeGen::deselect(gen, saved);
|
|
properties_written = TRUE;
|
|
}
|
|
}
|
|
|
|
@h Representing instances in I6.
|
|
Partly for historical reasons, partly to squeeze performance out of the
|
|
virtual machines used in traditional parser IF, the I6 run-time
|
|
implementation of instances and their properties is complicated.
|
|
|
|
The main complication is that there are two sorts of instance: objects,
|
|
such as doors and people, and everything else, such as scenes. The two
|
|
sorts are handled equally in Inter, but have completely different run-time
|
|
representations in I6:
|
|
|
|
(a) "Object instances" are instances of a kind which is a subkind, perhaps
|
|
indirectly, of "object". These are stored as I6 objects, and the I6 classes
|
|
of these objects correspond exactly to their I7 kinds (except that the kind
|
|
"object" itself is mapped to the I6 class "Class").
|
|
|
|
(b) "Value instances" are instances of kinds which are not objects but can
|
|
nevertheless have properties. The scene "entire game" is a value instance; the
|
|
number 27 is not. Value instances are stored as enumerated constants. For
|
|
example, if there are scenes called "entire game", "Overture" and "Grand
|
|
Finale", then these are stored as the constants 1, 2, 3; and I6 constants are
|
|
defined to represent them.
|
|
|
|
@h Representing properties in I6.
|
|
Both sorts of instance can have properties; for example:
|
|
|
|
>> A supporter has a number called carrying capacity.
|
|
|
|
>> A scene has a number called completion score.
|
|
|
|
allows a property to be held by object instances (supporters) and value
|
|
instances (scenes). To the writer of I7 source text, no distinction between
|
|
these cases is visible, and the same is true of Inter code.
|
|
|
|
How to store these at run-time is not so straightforward. Speed and
|
|
compactness are unusually important here, and constraints imposed by the
|
|
virtual machine targeted by I6 add further complications.
|
|
|
|
(a) Properties of object instances are stored as either I6 properties or I6
|
|
attributes of their I6 objects. As far as possible, this is a direct mapping
|
|
from I7 instances and kinds onto I6 objects and classes. It is a little
|
|
bulkier in memory than using flat arrays, but the Glulx and Z-machine virtual
|
|
machines offer a very rapid lookup operation. Thus "the carrying capacity of
|
|
the player" can be compiled to the I6 expression |player.capacity|, which
|
|
compiles to a single short call to the I6 veneer -- and one which many
|
|
interpreters today have optimised out as a basic operation, taking only a
|
|
single VM clock cycle.
|
|
|
|
(b) The properties of value instances are stored in flat arrays called
|
|
"sticks", with each property having its own stick. For example, the property
|
|
"recurring" for a scene would have a stick holding three values, one each for
|
|
the three scenes. Sticks have the same run-time format as table columns and
|
|
this is not a coincidence, because some kinds of value instances are created
|
|
by table in I7, and this lets us use the table as a ready-made set of sticks.
|
|
But now we don't have run-time lookup mechanisms already provided for us, so
|
|
we will need to set up some metadata structures to make it possible to seek
|
|
property values for value instances quickly.
|
|
|
|
In practice, property access is slightly faster for object instances, and
|
|
property storage is slightly more compact for value instances, which is
|
|
probably the right bargain.
|
|
|
|
@h Properties.
|
|
Properties in I7 are of two sorts: either-or, which behave adjectivally,
|
|
such as "open"; and value, which behave as nouns, such as "carrying capacity".
|
|
We can distinguish these because the I7 compiler annotates the property name
|
|
symbols with the |EITHER_OR_IANN| flag. It also always gives either-or
|
|
properties the kind |K_truth_state|, but note that a few value properties
|
|
also have this kind, so the annotation is the only way to be sure.
|
|
|
|
Some either-or properties of object instances can be stored as I6
|
|
"attributes". This is memory-efficient and fast at run-time: but only a
|
|
limited number can be stored this way. Here we choose which.
|
|
|
|
=
|
|
void CodeGen::IP::property(inter_tree *I, inter_symbol *prop_name, code_generation *gen) {
|
|
if (prop_name == NULL) internal_error("bad property");
|
|
if (Inter::Symbols::read_annotation(prop_name, EITHER_OR_IANN) >= 0) {
|
|
int translated = FALSE;
|
|
if (Inter::Symbols::read_annotation(prop_name, EXPLICIT_ATTRIBUTE_IANN) >= 0) translated = TRUE;
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) >= 0) translated = TRUE;
|
|
|
|
int make_attribute = NOT_APPLICABLE;
|
|
@<Any either/or property which can belong to a value instance is ineligible@>;
|
|
@<An either/or property translated to an attribute declared in the I6 template must be chosen@>;
|
|
@<Otherwise give away attribute slots on a first-come-first-served basis@>;
|
|
if (make_attribute == TRUE) Inter::Symbols::set_flag(prop_name, ATTRIBUTE_MARK_BIT);
|
|
@<Check against the I7 compiler's beliefs@>;
|
|
|
|
if (make_attribute) {
|
|
@<Declare as an I6 attribute@>;
|
|
} else {
|
|
@<Worry about the FBNA@>;
|
|
}
|
|
}
|
|
}
|
|
|
|
@ The dodge of using an attribute to store an either-or property won't work
|
|
for properties of value instances, because then the value-property-holder
|
|
object couldn't store the necessary table address (see next section). So we
|
|
must rule out any property which might belong to any value.
|
|
|
|
@<Any either/or property which can belong to a value instance is ineligible@> =
|
|
inter_node_list *PL =
|
|
Inter::Warehouse::get_frame_list(
|
|
InterTree::warehouse(I),
|
|
Inter::Property::permissions_list(prop_name));
|
|
if (PL == NULL) internal_error("no permissions list");
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, PL) {
|
|
inter_symbol *owner_name =
|
|
InterSymbolsTables::symbol_from_id(Inter::Packages::scope_of(X), X->W.data[OWNER_PERM_IFLD]);
|
|
if (owner_name == NULL) internal_error("bad owner");
|
|
inter_symbol *owner_kind = NULL;
|
|
inter_tree_node *D = Inter::Symbols::definition(owner_name);
|
|
if ((D) && (D->W.data[ID_IFLD] == INSTANCE_IST)) {
|
|
owner_kind = Inter::Instance::kind_of(owner_name);
|
|
} else {
|
|
owner_kind = owner_name;
|
|
}
|
|
if (CodeGen::IP::is_kind_of_object(owner_kind) == FALSE) make_attribute = FALSE;
|
|
}
|
|
|
|
@ An either/or property which has been deliberately equated to an I6
|
|
template attribute with a sentence like...
|
|
|
|
>> The fixed in place property translates into I6 as "static".
|
|
|
|
...is (we must assume) already declared as an |Attribute|, so we need to
|
|
remember that it's implemented as an attribute when compiling references
|
|
to it.
|
|
|
|
@<An either/or property translated to an attribute declared in the I6 template must be chosen@> =
|
|
if (translated) make_attribute = TRUE;
|
|
|
|
@ We have in theory 48 Attribute slots to use up, that being the number
|
|
available in versions 5 and higher of the Z-machine, but the I6 template
|
|
layer consumes so many that only a few slots remain for the user's own
|
|
creations. Giving these away to the first-created properties is the
|
|
simplest way to allocate them, and in fact it works pretty well, because
|
|
the first such either/or properties tend to be created in extensions and
|
|
to be frequently used.
|
|
|
|
@d ATTRIBUTE_SLOTS_TO_GIVE_AWAY 11
|
|
|
|
@<Otherwise give away attribute slots on a first-come-first-served basis@> =
|
|
if (make_attribute == NOT_APPLICABLE) {
|
|
if (attribute_slots_used++ < ATTRIBUTE_SLOTS_TO_GIVE_AWAY)
|
|
make_attribute = TRUE;
|
|
else
|
|
make_attribute = FALSE;
|
|
}
|
|
|
|
@ At present the I7 compiler makes a decision matching this one for its
|
|
own internal needs. We want to make sure its decision matches ours, so we
|
|
check that here. (It tells us by marking the property name with the
|
|
|ATTRIBUTE_IANN| annotation.) But this code will eventually go.
|
|
|
|
@<Check against the I7 compiler's beliefs@> =
|
|
int made_attribute = FALSE;
|
|
if (Inter::Symbols::read_annotation(prop_name, ATTRIBUTE_IANN) >= 0)
|
|
made_attribute = TRUE;
|
|
if (made_attribute != make_attribute) {
|
|
LOG("Disagree on %S: %d vs %d\n", prop_name->symbol_name, made_attribute, make_attribute);
|
|
internal_error("attribute allocation dispute");
|
|
}
|
|
|
|
@ A curiosity of I6 is that attributes must be declared before use, whereas
|
|
properties need not be. We generate suitable |Attribute| statements here.
|
|
Note that if the property has been translated onto an existing I6 name, then
|
|
we assume that's the name of an attribute already declared (for example
|
|
in the I6 template, or some extension), and we therefore do nothing.
|
|
|
|
@<Declare as an I6 attribute@> =
|
|
generated_segment *saved = CodeGen::select(gen, CodeGen::Targets::basic_constant_segment(gen, 1));
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) >= 0) {
|
|
text_stream *A = Inter::Symbols::get_translate(prop_name);
|
|
if (A == NULL) A = CodeGen::CL::name(prop_name);
|
|
WRITE_TO(CodeGen::current(gen), "Attribute %S;\n", A);
|
|
} else {
|
|
if (translated == FALSE)
|
|
WRITE_TO(CodeGen::current(gen), "Attribute %S;\n", CodeGen::CL::name(prop_name));
|
|
}
|
|
CodeGen::deselect(gen, saved);
|
|
|
|
@ The weak point in our scheme for making some either/or properties into
|
|
Attributes is that run-time code is going to need a fast way to determine
|
|
which, since they have to be accessed differently. We rely on the facts that
|
|
|
|
(a) at run-time, attribute numbers are all numerically lower than property
|
|
numbers, and
|
|
|
|
(b) property numbers increase in order of their first appearances in the
|
|
I6 source code.
|
|
|
|
Thus an either/or property |P| must be an I6 attribute if |P < F| and must be
|
|
an I6 property if |P >= F|, where |F| is the earliest-defined either/or
|
|
property which isn't stored as an attribute.
|
|
|
|
This cutoff value |F| is customarily called FBNA, the "first boolean not
|
|
an attribute". (Perhaps she ought to be called FEONA.) The following
|
|
compiles an I6 constant for this value.
|
|
|
|
@<Worry about the FBNA@> =
|
|
if (FBNA_found == FALSE) {
|
|
FBNA_found = TRUE;
|
|
generated_segment *saved = CodeGen::select(gen, CodeGen::Targets::constant_segment(gen));
|
|
CodeGen::Targets::begin_constant(gen, I"FBNA_PROP_NUMBER", TRUE);
|
|
WRITE_TO(CodeGen::current(gen), "%S", CodeGen::CL::name(prop_name));
|
|
CodeGen::Targets::end_constant(gen, I"FBNA_PROP_NUMBER");
|
|
CodeGen::deselect(gen, saved);
|
|
}
|
|
|
|
@ It's unlikely, but just possible, that no FBNAs ever exist, so after the
|
|
above has been tried on all properties:
|
|
|
|
=
|
|
void CodeGen::IP::knowledge(code_generation *gen) {
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
inter_tree *I = gen->from;
|
|
if ((FBNA_found == FALSE) && (properties_found)) {
|
|
generated_segment *saved = CodeGen::select(gen, CodeGen::Targets::constant_segment(gen));
|
|
CodeGen::Targets::begin_constant(gen, I"FBNA_PROP_NUMBER", TRUE);
|
|
WRITE_TO(CodeGen::current(gen), "MAX_POSITIVE_NUMBER");
|
|
CodeGen::Targets::end_constant(gen, I"FBNA_PROP_NUMBER");
|
|
CodeGen::deselect(gen, saved);
|
|
}
|
|
inter_symbol **all_props_in_source_order = NULL;
|
|
inter_symbol **props_in_source_order = NULL;
|
|
int no_properties = 0, total_no_properties = 0;
|
|
@<Make a list of properties in source order@>;
|
|
@<Compile the property numberspace forcer@>;
|
|
|
|
inter_symbol **kinds_in_source_order = NULL;
|
|
inter_symbol **kinds_in_declaration_order = NULL;
|
|
@<Make a list of kinds in source order@>;
|
|
|
|
inter_symbol **instances_in_declaration_order = NULL;
|
|
@<Make a list of instances in declaration order@>;
|
|
|
|
if (properties_found) @<Write Value Property Holder objects for each kind of value instance@>;
|
|
@<Make a list of kinds in declaration order@>;
|
|
@<Annotate kinds of object with a sequence counter@>;
|
|
@<Write the KindHierarchy array@>;
|
|
@<Write an I6 Class definition for each kind of object@>;
|
|
@<Write an I6 Object definition for each object instance@>;
|
|
@<Write the property metadata array@>;
|
|
|
|
@<Stub the properties@>;
|
|
}
|
|
|
|
@<Make a list of properties in source order@> =
|
|
for (int i=0; i<no_property_frames; i++) {
|
|
inter_tree_node *P = property_frames[i];
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_PROP_IFLD);
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) != 1)
|
|
total_no_properties++;
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) != 1)
|
|
no_properties++;
|
|
}
|
|
if (no_properties > 0) properties_found = TRUE;
|
|
|
|
if (total_no_properties > 0) {
|
|
all_props_in_source_order = (inter_symbol **)
|
|
(Memory::calloc(total_no_properties, sizeof(inter_symbol *), CODE_GENERATION_MREASON));
|
|
int c = 0;
|
|
for (int i=0; i<no_property_frames; i++) {
|
|
inter_tree_node *P = property_frames[i];
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_PROP_IFLD);
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) != 1)
|
|
all_props_in_source_order[c++] = prop_name;
|
|
else
|
|
CodeGen::IP::property(I, prop_name, gen);
|
|
}
|
|
qsort(all_props_in_source_order, (size_t) total_no_properties, sizeof(inter_symbol *),
|
|
CodeGen::IP::compare_kind_symbols);
|
|
for (int p=0; p<total_no_properties; p++) {
|
|
inter_symbol *prop_name = all_props_in_source_order[p];
|
|
CodeGen::IP::property(I, prop_name, gen);
|
|
}
|
|
}
|
|
|
|
if (properties_found) {
|
|
props_in_source_order = (inter_symbol **)
|
|
(Memory::calloc(no_properties, sizeof(inter_symbol *), CODE_GENERATION_MREASON));
|
|
int c = 0;
|
|
for (int i=0; i<no_property_frames; i++) {
|
|
inter_tree_node *P = property_frames[i];
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_PROP_IFLD);
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) != 1)
|
|
props_in_source_order[c++] = prop_name;
|
|
}
|
|
|
|
for (int i=0; i<no_property_frames; i++) {
|
|
inter_tree_node *P = property_frames[i];
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_PROP_IFLD);
|
|
if ((Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) == 1) &&
|
|
(Inter::Symbols::read_annotation(prop_name, ATTRIBUTE_IANN) != 1)) {
|
|
CodeGen::Targets::declare_property(gen, prop_name, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
@<Make a list of kinds in source order@> =
|
|
if (no_kind_frames == 0) return;
|
|
|
|
kinds_in_source_order = (inter_symbol **)
|
|
(Memory::calloc(no_kind_frames, sizeof(inter_symbol *), CODE_GENERATION_MREASON));
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_tree_node *P = kind_frames[i];
|
|
inter_symbol *kind_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_KIND_IFLD);
|
|
kinds_in_source_order[i] = kind_name;
|
|
}
|
|
qsort(kinds_in_source_order, (size_t) no_kind_frames, sizeof(inter_symbol *),
|
|
CodeGen::IP::compare_kind_symbols);
|
|
|
|
@<Make a list of kinds in declaration order@> =
|
|
kinds_in_declaration_order = (inter_symbol **)
|
|
(Memory::calloc(no_kind_frames, sizeof(inter_symbol *), CODE_GENERATION_MREASON));
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_tree_node *P = kind_frames[i];
|
|
inter_symbol *kind_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_KIND_IFLD);
|
|
kinds_in_declaration_order[i] = kind_name;
|
|
}
|
|
qsort(kinds_in_declaration_order, (size_t) no_kind_frames, sizeof(inter_symbol *),
|
|
CodeGen::IP::compare_kind_symbols_decl);
|
|
|
|
@<Make a list of instances in declaration order@> =
|
|
if (no_instance_frames > 0) {
|
|
instances_in_declaration_order = (inter_symbol **)
|
|
(Memory::calloc(no_instance_frames, sizeof(inter_symbol *), CODE_GENERATION_MREASON));
|
|
for (int i=0; i<no_instance_frames; i++) {
|
|
inter_tree_node *P = instance_frames[i];
|
|
inter_symbol *inst_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_INST_IFLD);
|
|
instances_in_declaration_order[i] = inst_name;
|
|
}
|
|
qsort(instances_in_declaration_order, (size_t) no_instance_frames, sizeof(inter_symbol *),
|
|
CodeGen::IP::compare_kind_symbols_decl);
|
|
}
|
|
|
|
@ But there's a snag. The above assumes that property values will have the
|
|
same ordering at run-time as their definition order here, but that isn't
|
|
necessarily true. The run-time ordering depends on how early in the I6
|
|
source code they appear, and that in turn depends on which objects have
|
|
which properties, and so on -- nothing we can rely on.
|
|
|
|
We finesse this by creating the following spurious object before the
|
|
class hierarchy and object tree are created: its properties are therefore
|
|
all new creations, and since we declare them in I7 creation order, they
|
|
are now allocated I6 property numbers in a sequence matching this. (We
|
|
don't care about the numbering of non-either/or properties, so we don't
|
|
bother to force them.)
|
|
|
|
@<Compile the property numberspace forcer@> =
|
|
if (properties_found) {
|
|
WRITE("Object property_numberspace_forcer\n"); INDENT;
|
|
for (int p=0; p<no_properties; p++) {
|
|
inter_symbol *prop_name = props_in_source_order[p];
|
|
if (Inter::Symbols::get_flag(prop_name, ATTRIBUTE_MARK_BIT) == FALSE) {
|
|
inter_symbol *kind_name = Inter::Property::kind_of(prop_name);
|
|
if (kind_name == truth_state_kind_symbol) {
|
|
WRITE(" with %S false\n", CodeGen::CL::name(prop_name));
|
|
}
|
|
}
|
|
}
|
|
OUTDENT; WRITE(";\n");
|
|
}
|
|
|
|
@<Annotate kinds of object with a sequence counter@> =
|
|
inter_ti c = 1;
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::is_kind_of_object(kind_name))
|
|
Inter::Symbols::annotate_i(kind_name, OBJECT_KIND_COUNTER_IANN, c++);
|
|
}
|
|
|
|
@h The kind inheritance tree.
|
|
We begin with an array providing metadata on the kinds of object: there
|
|
are just two words per kind -- the Inform 6 class corresponding to the kind,
|
|
then the instance count for its own kind. For instance, "door" is usually
|
|
kind number 4, so it occupies record 4 in this array -- words 8 and 9. Word
|
|
8 will be |K4_door|, the Inform 6 class for doors, and word 9 will be the
|
|
number 2, meaning kind number 2, "thing". This tells us that a door is
|
|
a kind of thing. In this way, we store the hierarchy of |N| kinds in |2N|
|
|
words of memory; it's needed at run-time for checking dynamically that
|
|
property usage is legal.
|
|
|
|
@<Write the KindHierarchy array@> =
|
|
int no_kos = 0;
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::is_kind_of_object(kind_name)) no_kos++;
|
|
}
|
|
|
|
if (no_kos > 0) {
|
|
WRITE("Array KindHierarchy --> K0_kind (0)");
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::is_kind_of_object(kind_name)) {
|
|
inter_symbol *super_name = Inter::Kind::super(kind_name);
|
|
if ((super_name) && (super_name != object_kind_symbol)) {
|
|
WRITE(" %S (%d)", CodeGen::CL::name(kind_name),
|
|
CodeGen::IP::kind_of_object_count(super_name));
|
|
} else {
|
|
WRITE(" %S (0)", CodeGen::CL::name(kind_name));
|
|
}
|
|
}
|
|
}
|
|
WRITE(";\n");
|
|
} else {
|
|
WRITE("Array KindHierarchy --> (0) (0);\n");
|
|
}
|
|
|
|
@h Lookup mechanism for properties of value instances.
|
|
As noted above, if |K| is a kind which can have properties but is not a subkind
|
|
of object, then a property for instances of |K| is stored in an array called
|
|
a "stick". At run-time, given the property number and |K|, we will need to find
|
|
where in memory the correct stick is, and this needs to be quick.
|
|
|
|
This is essentially a dictionary lookup problem and we solve it by compiling
|
|
a faux object |V| for each |K|, called a "value property holder" or VPH.
|
|
Given |K| we find |V| by looking it up in the array |value_property_holders|.
|
|
|
|
Once we know |V|, we then look up |V.P| to get the address of the stick for
|
|
property |P|, something which the virtual machine can do quickly.
|
|
|
|
This comes at the cost of several hundred bytes of overhead, which we don't
|
|
take lightly in the Z-machine. But speed and flexibility are worth more.
|
|
|
|
@<Write Value Property Holder objects for each kind of value instance@> =
|
|
@<Define the I6 VPH class@>;
|
|
inter_symbol *max_weak_id = InterSymbolsTables::url_name_to_symbol(I, NULL,
|
|
I"/main/synoptic/kinds/BASE_KIND_HWM");
|
|
if (max_weak_id) {
|
|
int M = Inter::Symbols::evaluate_to_int(max_weak_id);
|
|
if (M != 0) {
|
|
@<Decide who gets a VPH@>;
|
|
@<Write the VPH lookup array@>;
|
|
for (int w=1; w<M; w++) {
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::weak_id(kind_name) == w) {
|
|
if (Inter::Symbols::get_flag(kind_name, VPH_MARK_BIT)) {
|
|
TEMPORARY_TEXT(sticks)
|
|
WRITE("VPH_Class VPH_%d\n with value_range %d\n",
|
|
w, Inter::Kind::instance_count(kind_name));
|
|
for (int p=0; p<no_properties; p++) {
|
|
inter_symbol *prop_name = props_in_source_order[p];
|
|
CodeGen::unmark(prop_name);
|
|
}
|
|
inter_node_list *FL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Kind::permissions_list(kind_name));
|
|
@<Work through this frame list of permissions@>;
|
|
for (int in=0; in<no_instance_frames; in++) {
|
|
inter_symbol *inst_name = instances_in_declaration_order[in];
|
|
if (Inter::Kind::is_a(Inter::Instance::kind_of(inst_name), kind_name)) {
|
|
inter_node_list *FL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Instance::permissions_list(inst_name));
|
|
@<Work through this frame list of permissions@>;
|
|
}
|
|
}
|
|
WRITE(";\n%S\n", sticks);
|
|
DISCARD_TEXT(sticks)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ It's convenient to be able to distinguish, at run-time, which objects are
|
|
the VPH objects used only for kind-property indexing; we can test if |O| is
|
|
such an object with the I6 condition |(O ofclass VPH_Class)|.
|
|
|
|
The property |value_range| for a VPH object is the number |N| such that the
|
|
legal values at run-time for this kind are |1, 2, 3, ..., N|: or in other
|
|
words, the number of instances of this kind.
|
|
|
|
@<Define the I6 VPH class@> =
|
|
WRITE("Class VPH_Class;\n");
|
|
|
|
@<Decide who gets a VPH@> =
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::is_kind_of_object(kind_name)) continue;
|
|
if (kind_name == object_kind_symbol) continue;
|
|
if (kind_name == unchecked_kind_symbol) continue;
|
|
int vph_me = FALSE;
|
|
inter_node_list *FL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Kind::permissions_list(kind_name));
|
|
if (FL->first_in_inl) vph_me = TRUE;
|
|
else for (int in=0; in<no_instance_frames; in++) {
|
|
inter_symbol *inst_name = instances_in_declaration_order[in];
|
|
if (Inter::Kind::is_a(Inter::Instance::kind_of(inst_name), kind_name)) {
|
|
inter_node_list *FL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Instance::permissions_list(inst_name));
|
|
if (FL->first_in_inl) vph_me = TRUE;
|
|
}
|
|
}
|
|
if (vph_me) Inter::Symbols::set_flag(kind_name, VPH_MARK_BIT);
|
|
}
|
|
|
|
@<Look through this frame list of permissions@> =
|
|
|
|
@ This array is indexed by the weak kind ID of |K|. The entry is 0 if |K|
|
|
doesn't have a VPH, or the object number of its VPH if it has.
|
|
|
|
@<Write the VPH lookup array@> =
|
|
WRITE("Array value_property_holders --> 0");
|
|
int vph = 0;
|
|
for (int w=1; w<M; w++) {
|
|
int written = FALSE;
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[i];
|
|
if (CodeGen::IP::weak_id(kind_name) == w) {
|
|
if (Inter::Symbols::get_flag(kind_name, VPH_MARK_BIT)) {
|
|
written = TRUE;
|
|
WRITE(" VPH_%d", w);
|
|
}
|
|
}
|
|
}
|
|
if (written) vph++; else WRITE(" 0");
|
|
}
|
|
WRITE(";\n");
|
|
@<Stub a faux VPH if none have otherwise been created@>;
|
|
|
|
@ In the event that no value instances have properties, there'll be no
|
|
instances of the |VPH_Class|, and no I6 object will be compiled with a
|
|
|value_range| property; that means I6 code referring to this will fail with an
|
|
I6 error. We don't want that, so if necessary we compile a useless VPH object
|
|
just to force the property into being.
|
|
|
|
@<Stub a faux VPH if none have otherwise been created@> =
|
|
if (vph == 0) WRITE("VPH_Class UnusedVPH with value_range 0;\n");
|
|
|
|
@<Work through this frame list of permissions@> =
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, FL) {
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(X, PROP_PERM_IFLD);
|
|
if (prop_name == NULL) internal_error("no property");
|
|
if (CodeGen::marked(prop_name) == FALSE) {
|
|
CodeGen::mark(prop_name);
|
|
text_stream *call_it = CodeGen::CL::name(prop_name);
|
|
WRITE(" with %S ", call_it);
|
|
if (X->W.data[STORAGE_PERM_IFLD]) {
|
|
inter_symbol *store = InterSymbolsTables::symbol_from_frame_data(X, STORAGE_PERM_IFLD);
|
|
if (store == NULL) internal_error("bad PP in inter");
|
|
WRITE("%S", CodeGen::CL::name(store));
|
|
} else {
|
|
@<Compile a stick of property values and put its address here@>;
|
|
}
|
|
WRITE("\n");
|
|
}
|
|
}
|
|
|
|
@ These little arrays are sticks of property values, and they are laid out
|
|
as if they were column arrays in a Table data structure. This means they must
|
|
be |table| arrays (which wastes one word of memory) and must have blanked-out
|
|
table column header words at the front (which wastes a further |COL_HSIZE|
|
|
words). But the cost is a simple overhead, not rising with the number of
|
|
instances, and it's a small price for the gain in simplicity and speed.
|
|
|
|
The entries here are bracketed to avoid the Inform 6 syntax ambiguity between
|
|
|4 -5| (two entries, four followed by minus five) and |4-5| (one entry, just
|
|
minus one). Inform 6 always uses the second interpretation, so just in case
|
|
there are negative literal integers in these array entries, we use
|
|
brackets: thus |(4) (-5)|. This cannot be confused with function calling
|
|
because I6 doesn't allow function calls in a constant context.
|
|
|
|
@<Compile a stick of property values and put its address here@> =
|
|
TEMPORARY_TEXT(ident)
|
|
WRITE_TO(ident, "KOVP_%d_P%d", w, CodeGen::IP::pnum(prop_name));
|
|
WRITE("%S", ident);
|
|
WRITE_TO(sticks, "Array %S table 0 0", ident);
|
|
for (int j=0; j<no_instance_frames; j++) {
|
|
inter_symbol *inst_name = instances_in_declaration_order[j];
|
|
if (Inter::Kind::is_a(Inter::Instance::kind_of(inst_name), kind_name)) {
|
|
int found = 0;
|
|
inter_node_list *PVL =
|
|
Inode::ID_to_frame_list(X,
|
|
Inter::Instance::properties_list(inst_name));
|
|
@<Work through this frame list of values@>;
|
|
PVL = Inode::ID_to_frame_list(X,
|
|
Inter::Kind::properties_list(kind_name));
|
|
@<Work through this frame list of values@>;
|
|
if (found == 0) WRITE_TO(sticks, " (0)");
|
|
}
|
|
}
|
|
WRITE_TO(sticks, ";\n");
|
|
|
|
@<Work through this frame list of values@> =
|
|
inter_tree_node *Y;
|
|
LOOP_THROUGH_INTER_NODE_LIST(Y, PVL) {
|
|
inter_symbol *p_name = InterSymbolsTables::symbol_from_id(Inter::Packages::scope_of(Y), Y->W.data[PROP_PVAL_IFLD]);
|
|
if ((p_name == prop_name) && (found == 0)) {
|
|
found = 1;
|
|
inter_ti v1 = Y->W.data[DVAL1_PVAL_IFLD];
|
|
inter_ti v2 = Y->W.data[DVAL2_PVAL_IFLD];
|
|
WRITE_TO(sticks, " (");
|
|
CodeGen::select_temporary(gen, sticks);
|
|
CodeGen::CL::literal(gen, NULL, Inter::Packages::scope_of(Y), v1, v2, FALSE);
|
|
CodeGen::deselect_temporary(gen);
|
|
WRITE_TO(sticks, ")");
|
|
}
|
|
}
|
|
|
|
@<Write an I6 Class definition for each kind of object@> =
|
|
for (int i=0; i<no_kind_frames; i++) {
|
|
inter_symbol *kind_name = kinds_in_declaration_order[i];
|
|
if ((kind_name == object_kind_symbol) ||
|
|
(CodeGen::IP::is_kind_of_object(kind_name))) {
|
|
WRITE("Class %S\n", CodeGen::CL::name(kind_name));
|
|
inter_symbol *super_name = Inter::Kind::super(kind_name);
|
|
if (super_name) WRITE(" class %S\n", CodeGen::CL::name(super_name));
|
|
CodeGen::IP::append(gen, kind_name);
|
|
inter_node_list *FL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Kind::properties_list(kind_name));
|
|
CodeGen::IP::plist(gen, FL);
|
|
WRITE(";\n\n");
|
|
}
|
|
}
|
|
|
|
@<Write an I6 Object definition for each object instance@> =
|
|
for (int i=0; i<no_instance_frames; i++) {
|
|
inter_symbol *inst_name = instances_in_declaration_order[i];
|
|
inter_tree_node *D = Inter::Symbols::definition(inst_name);
|
|
CodeGen::IP::object_instance(gen, D);
|
|
}
|
|
|
|
@ The following lets the run-time environment know what properties are
|
|
called, and which kinds of object are allowed to have them. This might look
|
|
a little odd: why does the run-time code need to know any of that?
|
|
|
|
The answer is that the Inform compiler will prevent grossly type-unsafe
|
|
property accesses at compile time -- for example, asking if a number is
|
|
"recurring" (an either/or property of scenes), which can be ruled out
|
|
because numbers and scenes are wholly disjoint as values. But it will allow
|
|
any object property of any object to be accessed, because it's not usually
|
|
possible for the typechecker to know if an object value |O| is a vehicle, a
|
|
direction, and so on. So the finer access controls for properties of
|
|
objects are left until run-time (whereas no such regime is needed for
|
|
properties of values). To make this possible, we need to tell the run-time
|
|
code what is and is not allowed.
|
|
|
|
The |property_metadata| array is organised as a sequence of variable-sized
|
|
records. Because of that, we also need arrays telling us where to find
|
|
the start of the record for a given I6 property (or attribute): we have two
|
|
of these, called |attributed_property_offsets| and |valued_property_offsets|.
|
|
The dummy value |-1| means that the relevant property has no metadata record,
|
|
though this won't happen for any property created by I7 source text.
|
|
|
|
@<Write the property metadata array@> =
|
|
if (properties_found) {
|
|
TEMPORARY_TEXT(pm_writer)
|
|
WRITE_TO(pm_writer, "[ CreatePropertyOffsets i;\n"); STREAM_INDENT(pm_writer);
|
|
WRITE_TO(pm_writer, "for (i=0: i<attributed_property_offsets_SIZE: i++)"); STREAM_INDENT(pm_writer);
|
|
WRITE_TO(pm_writer, "attributed_property_offsets-->i = -1;\n"); STREAM_OUTDENT(pm_writer);
|
|
WRITE_TO(pm_writer, "for (i=0: i<valued_property_offsets_SIZE: i++)"); STREAM_INDENT(pm_writer);
|
|
WRITE_TO(pm_writer, "valued_property_offsets-->i = -1;\n"); STREAM_OUTDENT(pm_writer);
|
|
|
|
WRITE("Array property_metadata -->\n"); INDENT;
|
|
int pos = 0;
|
|
for (int p=0; p<no_properties; p++) {
|
|
inter_symbol *prop_name = props_in_source_order[p];
|
|
WRITE("! offset %d: property %S\n", pos, CodeGen::CL::name(prop_name));
|
|
if (Inter::Symbols::get_flag(prop_name, ATTRIBUTE_MARK_BIT))
|
|
WRITE_TO(pm_writer, "attributed_property_offsets");
|
|
else
|
|
WRITE_TO(pm_writer, "valued_property_offsets");
|
|
WRITE_TO(pm_writer, "-->%S = %d;\n", CodeGen::CL::name(prop_name), pos);
|
|
|
|
@<Write the property name in double quotes@>;
|
|
@<Write a list of kinds or objects which are permitted to have this property@>;
|
|
WRITE("NULL\n"); pos++;
|
|
}
|
|
OUTDENT; WRITE(";\n");
|
|
STREAM_OUTDENT(pm_writer);
|
|
WRITE_TO(pm_writer, "];\n");
|
|
WRITE("%S", pm_writer);
|
|
DISCARD_TEXT(pm_writer)
|
|
}
|
|
|
|
@<Write the property name in double quotes@> =
|
|
WRITE("\"");
|
|
int N = Inter::Symbols::read_annotation(prop_name, PROPERTY_NAME_IANN);
|
|
if (N <= 0) WRITE("<nameless>");
|
|
else WRITE("%S", Inter::Warehouse::get_text(InterTree::warehouse(I), (inter_ti) N));
|
|
WRITE("\" ");
|
|
pos++;
|
|
|
|
@ A complete list here would be wasteful both of space and run-time
|
|
checking time, but we only need a list $O_1, O_2, ..., O_k$ such that for
|
|
each $W$ allowed to have the property, either $W = O_i$ for some $i$, or
|
|
$W$ is of kind $O_i$ for some $i$ (perhaps indirectly).
|
|
|
|
In a tricksy complication, we need to allow for the possibility that two
|
|
or more different I7 properties are actually equal at run-time. This wouldn't
|
|
happen by itself, but does happen if two different properties are translated
|
|
to the same I6 property, and in fact the template does this: "lighted" and
|
|
"lit" both translate to I6 |light|. The only way to reconcile this is to
|
|
make the list a union of the lists of both. This does mean the routine
|
|
runs in $O(P^2N)$ time, where $P$ is the number of properties and $N$ the
|
|
number of objects, but we can live with that. $P$ does not in practice rise
|
|
linearly with the size of the source text, even though $N$ does.
|
|
|
|
@<Write a list of kinds or objects which are permitted to have this property@> =
|
|
for (int e=0; e<no_properties; e++) {
|
|
inter_symbol *eprop_name = props_in_source_order[e];
|
|
if (Str::eq(CodeGen::CL::name(eprop_name), CodeGen::CL::name(prop_name))) {
|
|
inter_node_list *EVL =
|
|
Inter::Warehouse::get_frame_list(InterTree::warehouse(I), Inter::Property::permissions_list(eprop_name));
|
|
|
|
@<List any O with an explicit permission@>;
|
|
@<List all top-level kinds if "object" itself has an explicit permission@>;
|
|
}
|
|
}
|
|
|
|
@<List any O with an explicit permission@> =
|
|
for (int k=0; k<no_kind_frames; k++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[k];
|
|
if (CodeGen::IP::is_kind_of_object(kind_name)) {
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, EVL) {
|
|
inter_symbol *owner_name = InterSymbolsTables::symbol_from_frame_data(X, OWNER_PERM_IFLD);
|
|
if (owner_name == kind_name) {
|
|
WRITE("%S ", CodeGen::CL::name(kind_name));
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (int in=0; in<no_instance_frames; in++) {
|
|
inter_symbol *inst_name = instances_in_declaration_order[in];
|
|
if (CodeGen::IP::is_kind_of_object(Inter::Instance::kind_of(inst_name))) {
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, EVL) {
|
|
inter_symbol *owner_name = InterSymbolsTables::symbol_from_frame_data(X, OWNER_PERM_IFLD);
|
|
if (owner_name == inst_name) {
|
|
WRITE("%S ", CodeGen::CL::name(inst_name));
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@<List all top-level kinds if "object" itself has an explicit permission@> =
|
|
if (Inter::Symbols::read_annotation(eprop_name, RTO_IANN) < 0) {
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, EVL) {
|
|
inter_symbol *owner_name = InterSymbolsTables::symbol_from_frame_data(X, OWNER_PERM_IFLD);
|
|
if (owner_name == object_kind_symbol) {
|
|
WRITE("K0_kind "); pos++;
|
|
for (int k=0; k<no_kind_frames; k++) {
|
|
inter_symbol *kind_name = kinds_in_source_order[k];
|
|
if (Inter::Kind::super(kind_name) == object_kind_symbol) {
|
|
WRITE("%S ", CodeGen::CL::name(kind_name));
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@
|
|
|
|
@<Stub the properties@> =
|
|
for (int p=0; p<no_properties; p++) {
|
|
inter_symbol *prop_name = props_in_source_order[p];
|
|
if (Inter::Symbols::read_annotation(prop_name, ASSIMILATED_IANN) != 1) {
|
|
CodeGen::Targets::declare_property(gen, prop_name, FALSE);
|
|
}
|
|
}
|
|
|
|
@h Instances.
|
|
|
|
=
|
|
void CodeGen::IP::instance(code_generation *gen, inter_tree_node *P) {
|
|
inter_symbol *inst_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_INST_IFLD);
|
|
inter_symbol *inst_kind = InterSymbolsTables::symbol_from_frame_data(P, KIND_INST_IFLD);
|
|
|
|
if (Inter::Kind::is_a(inst_kind, object_kind_symbol) == FALSE) {
|
|
inter_ti val1 = P->W.data[VAL1_INST_IFLD];
|
|
inter_ti val2 = P->W.data[VAL2_INST_IFLD];
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
int defined = TRUE;
|
|
if (val1 == UNDEF_IVAL) defined = FALSE;
|
|
CodeGen::Targets::begin_constant(gen, CodeGen::CL::name(inst_name), defined);
|
|
if (defined) {
|
|
int hex = FALSE;
|
|
if (Inter::Annotations::find(&(inst_name->ann_set), HEX_IANN)) hex = TRUE;
|
|
if (hex) WRITE("$%x", val2); else WRITE("%d", val2);
|
|
}
|
|
CodeGen::Targets::end_constant(gen, CodeGen::CL::name(inst_name));
|
|
}
|
|
}
|
|
|
|
@ =
|
|
int CodeGen::IP::pnum(inter_symbol *prop_name) {
|
|
int N = Inter::Symbols::read_annotation(prop_name, SOURCE_ORDER_IANN);
|
|
if (N >= 0) return N;
|
|
return 0;
|
|
}
|
|
|
|
int CodeGen::IP::compare_kind_symbols(const void *elem1, const void *elem2) {
|
|
const inter_symbol **e1 = (const inter_symbol **) elem1;
|
|
const inter_symbol **e2 = (const inter_symbol **) elem2;
|
|
if ((*e1 == NULL) || (*e2 == NULL))
|
|
internal_error("Disaster while sorting kinds");
|
|
int s1 = CodeGen::IP::kind_sequence_number(*e1);
|
|
int s2 = CodeGen::IP::kind_sequence_number(*e2);
|
|
if (s1 != s2) return s1-s2;
|
|
return Inter::Symbols::sort_number(*e1) - Inter::Symbols::sort_number(*e2);
|
|
}
|
|
|
|
int CodeGen::IP::compare_kind_symbols_decl(const void *elem1, const void *elem2) {
|
|
const inter_symbol **e1 = (const inter_symbol **) elem1;
|
|
const inter_symbol **e2 = (const inter_symbol **) elem2;
|
|
if ((*e1 == NULL) || (*e2 == NULL))
|
|
internal_error("Disaster while sorting kinds");
|
|
int s1 = CodeGen::IP::kind_sequence_number_decl(*e1);
|
|
int s2 = CodeGen::IP::kind_sequence_number_decl(*e2);
|
|
if (s1 != s2) return s1-s2;
|
|
return Inter::Symbols::sort_number(*e1) - Inter::Symbols::sort_number(*e2);
|
|
}
|
|
|
|
int CodeGen::IP::kind_sequence_number(const inter_symbol *kind_name) {
|
|
int N = Inter::Symbols::read_annotation(kind_name, SOURCE_ORDER_IANN);
|
|
if (N >= 0) return N;
|
|
return 100000000;
|
|
}
|
|
|
|
int CodeGen::IP::kind_sequence_number_decl(const inter_symbol *kind_name) {
|
|
int N = Inter::Symbols::read_annotation(kind_name, DECLARATION_ORDER_IANN);
|
|
if (N >= 0) return N;
|
|
return 100000000;
|
|
}
|
|
|
|
int CodeGen::IP::weak_id(inter_symbol *kind_name) {
|
|
inter_package *pack = Inter::Packages::container(kind_name->definition);
|
|
inter_symbol *weak_s = Metadata::read_optional_symbol(pack, I"^weak_id");
|
|
int alt_N = -1;
|
|
if (weak_s) alt_N = Inter::Symbols::evaluate_to_int(weak_s);
|
|
if (alt_N >= 0) return alt_N;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
@ For the I6 header syntax, see the DM4. Note that the "hardwired" short
|
|
name is intentionally made blank: we always use I6's |short_name| property
|
|
instead. I7's spatial plugin, if loaded (as it usually is), will have
|
|
annotated the Inter symbol for the object with an arrow count, that is,
|
|
a measure of its spatial depth. This we translate into I6 arrow notation.
|
|
If the spatial plugin wasn't loaded then we have no notion of containment,
|
|
all arrow counts are 0, and we define a flat sequence of free-standing objects.
|
|
|
|
One last oddball thing is that direction objects have to be compiled in I6
|
|
as if they were spatially inside a special object called |Compass|. This doesn't
|
|
really make much conceptual sense, and I7 dropped the idea -- it has no
|
|
"compass".
|
|
|
|
=
|
|
void CodeGen::IP::object_instance(code_generation *gen, inter_tree_node *P) {
|
|
inter_symbol *inst_name = InterSymbolsTables::symbol_from_frame_data(P, DEFN_INST_IFLD);
|
|
inter_symbol *inst_kind = InterSymbolsTables::symbol_from_frame_data(P, KIND_INST_IFLD);
|
|
|
|
if (Inter::Kind::is_a(inst_kind, object_kind_symbol)) {
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
WRITE("Object ");
|
|
int c = Inter::Symbols::read_annotation(inst_name, ARROW_COUNT_IANN);
|
|
for (int i=0; i<c; i++) WRITE("-> ");
|
|
WRITE("%S \"\"", CodeGen::CL::name(inst_name));
|
|
if (Inter::Kind::is_a(inst_kind, direction_kind_symbol)) { WRITE(" Compass"); }
|
|
WRITE("\n class %S\n", CodeGen::CL::name(inst_kind));
|
|
CodeGen::IP::append(gen, inst_name);
|
|
inter_node_list *FL =
|
|
Inode::ID_to_frame_list(P,
|
|
Inter::Instance::properties_list(inst_name));
|
|
CodeGen::IP::plist(gen, FL);
|
|
WRITE(";\n\n");
|
|
}
|
|
}
|
|
|
|
void CodeGen::IP::plist(code_generation *gen, inter_node_list *FL) {
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
if (FL == NULL) internal_error("no properties list");
|
|
inter_tree_node *X;
|
|
LOOP_THROUGH_INTER_NODE_LIST(X, FL) {
|
|
inter_symbol *prop_name = InterSymbolsTables::symbol_from_frame_data(X, PROP_PVAL_IFLD);
|
|
if (prop_name == NULL) internal_error("no property");
|
|
text_stream *call_it = CodeGen::CL::name(prop_name);
|
|
if (Inter::Symbols::get_flag(prop_name, ATTRIBUTE_MARK_BIT)) {
|
|
char *maybe = "";
|
|
if ((X->W.data[DVAL1_PVAL_IFLD] == LITERAL_IVAL) &&
|
|
(X->W.data[DVAL2_PVAL_IFLD] == 0)) maybe = "~";
|
|
WRITE(" has %s%S\n", maybe, call_it);
|
|
} else {
|
|
WRITE(" with %S ", call_it);
|
|
int done = FALSE;
|
|
if (Inter::Symbols::is_stored_in_data(X->W.data[DVAL1_PVAL_IFLD], X->W.data[DVAL2_PVAL_IFLD])) {
|
|
inter_symbol *S = InterSymbolsTables::symbol_from_data_pair_and_frame(X->W.data[DVAL1_PVAL_IFLD], X->W.data[DVAL2_PVAL_IFLD], X);
|
|
if ((S) && (Inter::Symbols::read_annotation(S, INLINE_ARRAY_IANN) == 1)) {
|
|
inter_tree_node *P = Inter::Symbols::definition(S);
|
|
for (int i=DATA_CONST_IFLD; i<P->W.extent; i=i+2) {
|
|
if (i>DATA_CONST_IFLD) WRITE(" ");
|
|
CodeGen::CL::literal(gen, NULL, Inter::Packages::scope_of(P), P->W.data[i], P->W.data[i+1], FALSE);
|
|
}
|
|
done = TRUE;
|
|
}
|
|
}
|
|
if (done == FALSE)
|
|
CodeGen::CL::literal(gen, NULL, Inter::Packages::scope_of(X),
|
|
X->W.data[DVAL1_PVAL_IFLD], X->W.data[DVAL2_PVAL_IFLD], FALSE);
|
|
WRITE("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CodeGen::IP::append(code_generation *gen, inter_symbol *symb) {
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
inter_tree *I = gen->from;
|
|
text_stream *S = Inter::Symbols::read_annotation_t(symb, I, APPEND_IANN);
|
|
if (Str::len(S) == 0) return;
|
|
WRITE(" ");
|
|
int L = Str::len(S);
|
|
for (int i=0; i<L; i++) {
|
|
wchar_t c = Str::get_at(S, i);
|
|
if (c == URL_SYMBOL_CHAR) {
|
|
TEMPORARY_TEXT(T)
|
|
for (i++; i<L; i++) {
|
|
wchar_t c = Str::get_at(S, i);
|
|
if (c == URL_SYMBOL_CHAR) break;
|
|
PUT_TO(T, c);
|
|
}
|
|
inter_symbol *symb = InterSymbolsTables::url_name_to_symbol(I, NULL, T);
|
|
WRITE("%S", CodeGen::CL::name(symb));
|
|
DISCARD_TEXT(T)
|
|
} else PUT(c);
|
|
if ((c == '\n') && (i != Str::len(S)-1)) WRITE(" ");
|
|
}
|
|
}
|
|
|
|
@ =
|
|
int CodeGen::IP::is_kind_of_object(inter_symbol *kind_name) {
|
|
if (kind_name == object_kind_symbol) return FALSE;
|
|
inter_data_type *idt = Inter::Kind::data_type(kind_name);
|
|
if (idt == unchecked_idt) return FALSE;
|
|
if (Inter::Kind::is_a(kind_name, object_kind_symbol)) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
@ Counting kinds of object, not very quickly:
|
|
|
|
=
|
|
inter_ti CodeGen::IP::kind_of_object_count(inter_symbol *kind_name) {
|
|
if ((kind_name == NULL) || (kind_name == object_kind_symbol)) return 0;
|
|
int N = Inter::Symbols::read_annotation(kind_name, OBJECT_KIND_COUNTER_IANN);
|
|
if (N >= 0) return (inter_ti) N;
|
|
return 0;
|
|
}
|