1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-08 01:54:21 +03:00
inform7/inter/final-module/Chapter 4/Final Inform 6.w
2021-10-13 23:11:42 +01:00

228 lines
6.9 KiB
OpenEdge ABL

[I6Target::] Generating Inform 6.
To generate I6 code from intermediate code.
@h Target.
This generator produces Inform 6 source code, using the Vanilla algorithm.
=
code_generator *inform6_target = NULL;
void I6Target::create_generator(void) {
code_generator *cgt = Generators::new(I"inform6");
I6TargetCode::create_generator(cgt);
I6TargetObjects::create_generator(cgt);
I6TargetConstants::create_generator(cgt);
I6TargetVariables::create_generator(cgt);
METHOD_ADD(cgt, BEGIN_GENERATION_MTID, I6Target::begin_generation);
METHOD_ADD(cgt, MANGLE_IDENTIFIER_MTID, I6Target::mangle);
METHOD_ADD(cgt, OFFER_PRAGMA_MTID, I6Target::offer_pragma)
METHOD_ADD(cgt, END_GENERATION_MTID, I6Target::end_generation);
inform6_target = cgt;
}
@ We will write a single output file of I6 source code, but segmented as follows:
@e ICL_directives_I7CGS
@e compiler_versioning_matter_I7CGS
@e attributes_I7CGS
@e properties_I7CGS
@e global_variables_I7CGS
@e global_variables_array_I7CGS
@e constants_I7CGS
@e fake_actions_I7CGS
@e arrays_I7CGS
@e classes_I7CGS
@e objects_I7CGS
@e property_stubs_I7CGS
@e functions_I7CGS
@e command_grammar_I7CGS
=
int I6_target_segments[] = {
ICL_directives_I7CGS,
compiler_versioning_matter_I7CGS,
attributes_I7CGS,
properties_I7CGS,
global_variables_I7CGS,
global_variables_array_I7CGS,
constants_I7CGS,
fake_actions_I7CGS,
arrays_I7CGS,
classes_I7CGS,
objects_I7CGS,
property_stubs_I7CGS,
functions_I7CGS,
command_grammar_I7CGS,
-1
};
@ This generator uses the following state data while it works:
@d I6_GEN_DATA(x) ((I6_generation_data *) (gen->generator_private_data))->x
=
typedef struct I6_generation_data {
int attribute_slots_used;
int value_ranges_needed;
int value_property_holders_needed;
int DebugAttribute_seen;
int subterfuge_count;
CLASS_DEFINITION
} I6_generation_data;
I6_generation_data *I6Target::new_data(void) {
I6_generation_data *data = CREATE(I6_generation_data);
data->attribute_slots_used = 0;
data->value_ranges_needed = FALSE;
data->value_property_holders_needed = FALSE;
data->DebugAttribute_seen = FALSE;
data->subterfuge_count = 0;
return data;
}
@ We return |FALSE| here to signal that we want the Vanilla algorithm to
manage the process.
=
int I6Target::begin_generation(code_generator *cgt, code_generation *gen) {
CodeGen::create_segments(gen, I6Target::new_data(), I6_target_segments);
@<Compile some I6 oddities@>;
@<Compile some veneer replacement code@>;
return FALSE;
}
@ Defining a constant called |Grammar__Version| tells Inform 6 which storage
layout to use for command parser grammar. 2 is the shiny, modern one -- 1995
not 1993.
The I6 compiler adds a thin layer of hidden code to every program it compiles,
called the "veneer". This layer of code requires a global variable called |debug_flag|
to exist, and since that doesn't exist in the Inter tree, we must make it by hand.
The |or_tmp_var| variable is not significant to I6, and is just a temporary location
we will need for the code we are compiling. But this seems a good time to make it.
See the Inform 6 Technical Manual for more on these oddities.
@<Compile some I6 oddities@> =
segmentation_pos saved = CodeGen::select(gen, compiler_versioning_matter_I7CGS);
text_stream *OUT = CodeGen::current(gen);
WRITE("Constant Grammar__Version 2;\n");
WRITE("Global debug_flag;\n");
WRITE("Global or_tmp_var;\n");
CodeGen::deselect(gen, saved);
@ As noted above, I6 will add a veneer of code to what we compile. That veneer
will contain a function called |OC__Cl| which implements "ofclass", the I6
condition determining whether an object belongs to a given class. The I6
compiler's stock copy of |OC__Cl| doesn't work right with I7 code, though,
so we replace it here with a better one. (The I6 compiler uses our definition
in preference to its own.)
We need do this only when compiling to the Z-machine; our replacement function
is implemented in pure Z-machine assembly language. See the Z-Machine Standards
Document for a specification.
@<Compile some veneer replacement code@> =
segmentation_pos saved = CodeGen::select(gen, functions_I7CGS);
text_stream *OUT = CodeGen::current(gen);
WRITE("#Ifdef TARGET_ZCODE;\n");
WRITE("Global max_z_object;\n");
WRITE("[ OC__Cl obj cla j a n objflag;\n"); INDENT;
WRITE("@jl obj 1 ?NotObj;\n");
WRITE("@jg obj max_z_object ?NotObj;\n");
WRITE("@inc objflag;\n");
WRITE("#Ifdef K1_room;\n");
WRITE("@je cla K1_room ?~NotRoom;\n");
WRITE("@test_attr obj mark_as_room ?rtrue;\n");
WRITE("@rfalse;\n");
WRITE(".NotRoom;\n");
WRITE("#Endif;\n");
WRITE("#Ifdef K2_thing;\n");
WRITE("@je cla K2_thing ?~NotObj;\n");
WRITE("@test_attr obj mark_as_thing ?rtrue;\n");
WRITE("@rfalse;\n");
WRITE("#Endif;\n");
WRITE(".NotObj;\n");
WRITE("\n");
WRITE("@je cla Object Class ?ObjOrClass;\n");
WRITE("@je cla Routine String ?RoutOrStr;\n");
WRITE("\n");
WRITE("@jin cla 1 ?~Mistake;\n");
WRITE("\n");
WRITE("@jz objflag ?rfalse;\n");
WRITE("@get_prop_addr obj 2 -> a;\n");
WRITE("@jz a ?rfalse;\n");
WRITE("@get_prop_len a -> n;\n");
WRITE("\n");
WRITE("@div n 2 -> n;\n");
WRITE(".Loop;\n");
WRITE("@loadw a j -> sp;\n");
WRITE("@je sp cla ?rtrue;\n");
WRITE("@inc j;\n");
WRITE("@jl j n ?Loop;\n");
WRITE("@rfalse;\n");
WRITE("\n");
WRITE(".ObjOrClass;\n");
WRITE("@jz objflag ?rfalse;\n");
WRITE("@je cla Object ?JustObj;\n");
WRITE("\n");
WRITE("! So now cla is Class\n");
WRITE("@jg obj String ?~rtrue;\n");
WRITE("@jin obj Class ?rtrue;\n");
WRITE("@rfalse;\n");
WRITE("\n");
WRITE(".JustObj;\n");
WRITE("! So now cla is Object\n");
WRITE("@jg obj String ?~rfalse;\n");
WRITE("@jin obj Class ?rfalse;\n");
WRITE("@rtrue;\n");
WRITE("\n");
WRITE(".RoutOrStr;\n");
WRITE("@jz objflag ?~rfalse;\n");
WRITE("@call_2s Z__Region obj -> sp;\n");
WRITE("@inc sp;\n");
WRITE("@je sp cla ?rtrue;\n");
WRITE("@rfalse;\n");
WRITE("\n");
WRITE(".Mistake;\n");
WRITE("RT__Err(\"apply 'ofclass' for\", cla, -1);\n");
WRITE("rfalse;\n");
OUTDENT; WRITE("];\n");
WRITE("#Endif;\n");
CodeGen::deselect(gen, saved);
@ Pragmas are interpreted as ICL directives -- ICL being the Inform Configuration
Language part of Inform 6, which basically allows maxima in the compiler to be
tweaked. These go at the top of the source code and typically look like this:
= (text as Inform 6)
!% $MAX_NUM_STATIC_STRINGS=500000
=
=
void I6Target::offer_pragma(code_generator *cgt, code_generation *gen,
inter_tree_node *P, text_stream *tag, text_stream *content) {
if (Str::eq(tag, I"Inform6")) {
segmentation_pos saved = CodeGen::select(gen, ICL_directives_I7CGS);
text_stream *OUT = CodeGen::current(gen);
WRITE("!%% %S\n", content);
CodeGen::deselect(gen, saved);
}
}
@ Names are not mangled: all Inter identifiers are used as-is.
=
void I6Target::mangle(code_generator *cgt, OUTPUT_STREAM, text_stream *identifier) {
WRITE("%S", identifier);
}
@ The end:
=
int I6Target::end_generation(code_generator *cgt, code_generation *gen) {
I6TargetObjects::end_generation(cgt, gen);
I6TargetCode::end_generation(cgt, gen);
return FALSE;
}