1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/inter/building-module/Chapter 3/Value Holsters.w
2022-02-24 23:53:40 +00:00

135 lines
4.6 KiB
OpenEdge ABL

[Holsters::] Value Holsters.
Representing compilation contexts, and holding compiled values.
@ Value holsters were created as a shim at a time when almost the entire Inform
code-base had to be turned around. Pre-2017, Inform generated code by writing
Inform 6 syntax out by hand: but post-2019, Inter bytecode was generated instead.
Hundreds of subsystems had to be rewritten, a process taking over a year, and
during this time the compiler had to work in a piebald sort of way -- some
systems generating bytecode, others still pouring our raw I6 as text.
The //value_holster// was invented as a way to manage this. It allows the caller
of a compilation function to ask for code to be made in a particular way: this
is the |vhmode_wanted| field. These ways are:
(*) |INTER_VAL_VHMODE| -- generate Inter bytecode inside a function, producing a value
(*) |INTER_VOID_VHMODE| -- generate Inter bytecode inside a function but in
void context, i.e., not producing a value
(*) |INTER_DATA_VHMODE| -- encode a constant value as a pair of Inter values,
for use in, say, an array entry.
So much for what the caller wants. The compilation function, or subsystem, then
does whatever it does, and sets |vhmode_provided| to the mode it actually compiled
in; the caller can then deal with the situation arising if it wasn't what was
wanted. During 2017 this often meant that the function would write out some
raw I6 syntax, and reply |INTER_TEXT_VHMODE| to signal this; the caller would
then turn this into Inter by wrapping it up as either a "splat" or a "glob".
With the transition now complete, |INTER_TEXT_VHMODE| no longer exists. But
value holsters continue to be a useful device.
@e INTER_DATA_VHMODE from 1
@e INTER_VAL_VHMODE
@e INTER_VOID_VHMODE
@e NO_VHMODE
=
typedef struct value_holster {
int vhmode_wanted;
int vhmode_provided;
inter_pair val;
} value_holster;
@ =
value_holster Holsters::new(int vhm) {
value_holster vh;
vh.val = InterValuePairs::undef();
vh.vhmode_wanted = vhm;
vh.vhmode_provided = NO_VHMODE; /* the compilation function has not yet set this */
if (vhm == NO_VHMODE) internal_error("can't want NO_VHMODE");
return vh;
}
@ A compilation function can produce, as its output, a value in either
|INTER_DATA_VHMODE| (where this is exactly what is wanted) or in |INTER_VAL_VHMODE|
(where it can easily be adapted).
=
int Holsters::value_pair_allowed(value_holster *vh) {
if (vh == NULL) internal_error("no VH");
if ((vh->vhmode_wanted == INTER_DATA_VHMODE) ||
(vh->vhmode_wanted == INTER_VAL_VHMODE)) return TRUE;
return FALSE;
}
@ This is how a compilation function "holsters" a value pair:
=
void Holsters::holster_pair(value_holster *vh, inter_pair val) {
if (vh == NULL) internal_error("no VH");
vh->val = val;
vh->vhmode_provided = INTER_DATA_VHMODE;
}
@ And this is how the caller "unholsters" that value, after the function has
returned. If we find |NO_VHMODE|, we convert that to |INTER_DATA_VHMODE| with
the literal number value 0.
On exit, the provided mode is always |INTER_DATA_VHMODE|.
A second or subsequent call on the same holster does nothing, except to return
the same value pair, which is still stored in it. (In that sense, these aren't
really like a gunslinger's holster, where a revolver once drawn is no longer
in the holster.)
=
inter_pair Holsters::unholster_to_pair(value_holster *vh) {
if (vh == NULL) internal_error("no VH");
switch (vh->vhmode_provided) {
case INTER_DATA_VHMODE:
break;
case INTER_VAL_VHMODE:
internal_error("impossible to unholster pair for compiled val code");
break;
case INTER_VOID_VHMODE:
internal_error("impossible to unholster pair for compiled void code");
break;
case NO_VHMODE:
vh->vhmode_provided = INTER_DATA_VHMODE;
vh->val = InterValuePairs::number(0);
break;
}
return vh->val;
}
@ If, on the other hand, the caller was asking for |INTER_VAL_VHMODE|, it
should make use of the following. If we find |NO_VHMODE|, we compile a |val|
producing the literal value 0; if we find |INTER_DATA_VHMODE|, we compile a
|val| producing whatever value was holstered.
On exit, the provided mode is always |INTER_VAL_VHMODE|.
A second or subsequent call on the same holster does nothing.
=
void Holsters::unholster_to_code_val(inter_tree *I, value_holster *vh) {
if (vh == NULL) internal_error("no VH");
switch (vh->vhmode_provided) {
case INTER_DATA_VHMODE:
case NO_VHMODE: {
inter_pair val = Holsters::unholster_to_pair(vh);
Produce::val(I, K_value, val);
vh->vhmode_provided = INTER_VAL_VHMODE;
break;
}
case INTER_VAL_VHMODE:
break;
case INTER_VOID_VHMODE:
internal_error("impossible to use void Inter code in val context");
break;
}
}