mirror of
https://github.com/ganelson/inform.git
synced 2024-07-05 08:34:22 +03:00
147 lines
5.1 KiB
OpenEdge ABL
147 lines
5.1 KiB
OpenEdge ABL
[CNamespace::] C Namespace.
|
|
|
|
How identifiers are used in the C code we generate.
|
|
|
|
@ =
|
|
void CNamespace::initialise(code_generator *gtr) {
|
|
METHOD_ADD(gtr, MANGLE_IDENTIFIER_MTID, CNamespace::mangle);
|
|
METHOD_ADD(gtr, DECLARE_CONSTANT_MTID, CNamespace::declare_constant);
|
|
}
|
|
|
|
@ A fundamental decision we have to make here is what namespace of identifiers
|
|
we will use in the code we generate. If an Inter function is called |example|,
|
|
we are going to need to compile that to some C function: what should this be
|
|
called?
|
|
|
|
It seems unwise to call it just |example|, that is, to compile Inter identifiers
|
|
directly into C ones. What if the Inter code used the identifier |printf| for
|
|
something, for example, or indeed the identifiers |struct| or |unsigned|? Moreover,
|
|
unlike the Inform 6 generator, this one is expecting the compile code which will
|
|
only be part of a larger program. We want to avoid hogging the entire namespace;
|
|
in fact, we want our code to use only names beginning |i7_| or |glulx_|, with
|
|
the single exception of |main|, and even that only when |main| is compiled.
|
|
|
|
@ To that end, we "mangle" identifier names, and this is how. Examples:
|
|
= (text)
|
|
example i7_mgl_example
|
|
##Attack i7_ss_Attack
|
|
#g$self i7_ss_gself
|
|
=
|
|
This is not quite a faithful scheme because, say, |#a#a| and |#aa| both mangle
|
|
to the same result, |i7_ss_aa|. But |#| and |$| characters are used extremely
|
|
sparingly in Inter, and these can never arise.
|
|
|
|
=
|
|
void CNamespace::mangle(code_generator *gtr, OUTPUT_STREAM, text_stream *identifier) {
|
|
if (Str::get_first_char(identifier) == '#') {
|
|
WRITE("i7_ss_");
|
|
LOOP_THROUGH_TEXT(pos, identifier)
|
|
if ((Str::get(pos) != '#') && (Str::get(pos) != '$'))
|
|
PUT(Str::get(pos));
|
|
} else WRITE("i7_mgl_%S", identifier);
|
|
}
|
|
|
|
void CNamespace::mangle_with(code_generator *gtr, OUTPUT_STREAM, text_stream *identifier,
|
|
text_stream *modifier) {
|
|
WRITE("i7_%S_%S", modifier, identifier);
|
|
}
|
|
|
|
@ Opcode names are also mangled. Each assembly language opcode will use a
|
|
corresponding C function, whose name is mangled from that of the opcode. For
|
|
example:
|
|
= (text)
|
|
@jz i7_opcode_jz
|
|
@streamnum i7_opcode_streamnum
|
|
=
|
|
|
|
=
|
|
void CNamespace::mangle_opcode(OUTPUT_STREAM, text_stream *opcode) {
|
|
WRITE("i7_opcode_");
|
|
LOOP_THROUGH_TEXT(pos, opcode)
|
|
if (Str::get(pos) != '@')
|
|
PUT(Str::get(pos));
|
|
}
|
|
|
|
@ Global variable names are similarly mangled:
|
|
= (text)
|
|
howmayyou i7_var_howmayyou
|
|
=
|
|
|
|
=
|
|
void CNamespace::mangle_variable(OUTPUT_STREAM, text_stream *var) {
|
|
WRITE("i7_var_%S", var);
|
|
}
|
|
|
|
@ Local variable names have to be handled slightly differently. This is because
|
|
Inter frequently makes use of local variables whose identifiers are also used
|
|
for some global construct. Of course, C also allows for this: for example --
|
|
= (text as C)
|
|
int xerxes = 1;
|
|
void govern_Sophene(void) {
|
|
int xerxes = 2;
|
|
printf("%d\n", xerxes);
|
|
}
|
|
=
|
|
...is legal, and prints 2 when the function is called. So at first sight, there's
|
|
no problem giving a local variable the same name as some global construct.
|
|
|
|
But that does not work if the global definition is made by the C preprocessor
|
|
rather than its syntax analyser. Consider:
|
|
= (text as C)
|
|
#define xerxes 1
|
|
void govern_Sophene(void) {
|
|
int xerxes = 2;
|
|
printf("%d\n", xerxes);
|
|
}
|
|
=
|
|
This throws an error message: |int xerxes = 2;| then reads as |int 1 = 2;|. And
|
|
since some of our Inter constructs will indeed result in |#define|d values
|
|
rather than named C variables, we cannot allow a local variable name to coincide
|
|
with the name of anything else (after mangling).
|
|
|
|
We avoid this by changing the pre-mangled identifiers for all local variables
|
|
to begin with |local_|:
|
|
= (text)
|
|
original translation mangled translation
|
|
"xerxes" "local_xerxes" "i7_mgl_local_xerxes"
|
|
=
|
|
This is not an elegant trick, but it works nicely enough.
|
|
|
|
=
|
|
void CNamespace::fix_locals(code_generation *gen) {
|
|
InterTree::traverse(gen->from, CNamespace::sweep_for_locals, gen, NULL, LOCAL_IST);
|
|
}
|
|
|
|
void CNamespace::sweep_for_locals(inter_tree *I, inter_tree_node *P, void *state) {
|
|
inter_symbol *var_name = LocalInstruction::variable(P);
|
|
TEMPORARY_TEXT(T)
|
|
WRITE_TO(T, "local_%S", InterSymbol::identifier(var_name));
|
|
InterSymbol::set_translate(var_name, T);
|
|
DISCARD_TEXT(T)
|
|
}
|
|
|
|
@ Constants in Inter are indeed directly converted to |#define|d constants in C,
|
|
but with their names of course mangled.
|
|
|
|
For the reason why |Serial| and |Release| are placed higher-up in the file, see
|
|
//C Memory Model//.
|
|
|
|
=
|
|
void CNamespace::declare_constant(code_generator *gtr, code_generation *gen,
|
|
inter_symbol *const_s, int form, text_stream *val) {
|
|
text_stream *name = InterSymbol::trans(const_s);
|
|
int seg = c_constants_I7CGS;
|
|
if (Str::eq(name, I"Serial")) seg = c_ids_and_maxima_I7CGS;
|
|
if (Str::eq(name, I"Release")) seg = c_ids_and_maxima_I7CGS;
|
|
if (Str::eq(name, I"BASICINFORMKIT")) seg = c_ids_and_maxima_I7CGS;
|
|
segmentation_pos saved = CodeGen::select_layered(gen, seg,
|
|
ConstantInstruction::constant_depth(const_s));
|
|
text_stream *OUT = CodeGen::current(gen);
|
|
WRITE("#define ");
|
|
CNamespace::mangle(gtr, OUT, name);
|
|
WRITE(" ");
|
|
VanillaConstants::definition_value(gen, form, const_s, val);
|
|
WRITE("\n");
|
|
CodeGen::deselect(gen, saved);
|
|
}
|