1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-01 06:24:58 +03:00
inform7/inter/Manual/Using Inter.w
2022-03-24 23:35:22 +00:00

217 lines
9.6 KiB
OpenEdge ABL

Using Inter.
Using Inter at the command line.
@h What Inter does.
"Inter" is the intermediate representation of a program used in the Inform
compiler toolchain. Most compilers have one of these: for example, Microsoft
compilers mostly use "CIL" (common intermediate language), while |gcc| uses
something called GIMPLE, and so on. See //bytecode// for a longer discussion
of what motivates the design of Inter.
The practical effect is that the back end of the Inform compiler deals only
with Inter code. This back end exists as part of the executable //inform7//,
but also as a stand-alone program called //inter//, which comes with its
own command-line interface. Whereas //inform7// has a specific task to perform
and uses Inter code only as a means to an end, //inter// is designed to be
as flexible as possible.
@ Inter code can exist in memory, or in a human-readable text file, or in a
rapid-access binary file. The tool //inter// can convert between these formats:
= (text as BoxArt)
textual textual
inter ---+ +---> inter
\ /
\ /
----> memory ----
/ inter \
binary / \ binary
inter ---+ +---> inter
=
However, Inter can do much more than simply convert between these forms. It
has a "pipeline" design, meaning that it can run memory inter through any
desired series of compilation stages, one at a time:
= (text as BoxArt)
T --------> T --------> T --------> ... --------> T
step 1 step 2 step 3 ... step N
=
The Inter tree |T| starts out empty,[1] and at the end it is thrown away. So any
useful pipeline will begin by loading something in (usually as step 1), and
end by producing some useful output (usually in its last step or steps).
The advantage of this design is that it enables us to build what amount to
entirely different compilers, just by using different pipelines -- see
"assimilation" below for an example of a pipeline very different to the one
used in normal Inform 7 compilations. Pipelines also allow us to test existing
or proposed code-generation stages individually.
[1] Programs in Inter format are called "Inter trees".
@h Verify only.
If you have compiled the standard distribution of the command-line tools
for Inform then the Inter executable will be at |inter/Tangled/inter|.
Inter has three basic modes. In the first, the command line specifies only
a single file:
= (text as ConsoleText)
$ inter/Tangled/inter INTERFILE
=
Inter simply verifies this file for correctness: that is, to see if the inter
code supplied conforms to the inter specification. It returns the exit code 0
if all is well, and issues error messages and returns 1 if not.
Such files can be in either textual or binary form, and Inter automatically
detects which by looking at their contents. (Conventionally, such files
have the filename extension |.intert| or |.interb| respectively, but that's
not how Inter decides.)
@h Format conversion.
In the second mode, Inter not only loads (and verifies) the named file, but
then converts it to a different format and writes that out. For example,
= (text as ConsoleText)
$ inter/Tangled/inter my.intert -o my.interb -format=binary
=
converts |my.intert| (a textual inter file) to its binary equivalent |my.interb|,
and conversely:
= (text as ConsoleText)
$ inter/Tangled/inter my.interb -o my.intert -format=text
=
Two parameters must be specified: |-o| giving the output file, and |-format=F|
to say what format |F| this should have. Formats are in the same notation as
those used by //inbuild//, which similarly supports |-o| and |-format|.
In fact, |-format=text| is the default.
To take an elaborate example,
= (text as ConsoleText)
$ inter/Tangled/inter my.interb -o my.intert -format=C/32d/nomain
=
generates a 32-bit-word, debugging-enabled ANSI C program from the Inter tree
in |my.interb|, with no |main| function included in it.
As a special case, if |-o| is given just as |-|, then the output is printed
to the console rather than to a file.
@h Running a pipeline.
If we specify |-trace| as a command-line switch, Inter prints out every step
of the pipeline(s) it is following. This reveals that even the simple commands
above are, in fact, running pipelines, albeit short ones:
= (text as ConsoleText)
$ inter/Tangled/inter my.intert -trace
step 1/1: read <- my.intert
$ inter/Tangled/inter my.intert -o my.interb -format=binary -trace
step 1/2: read <- my.intert
step 2/2: generate binary -> my.interb
=
As this shows, a one or two-step pipeline was running:
(1) The first step used the |read| compilation stage, which reads some Inter
code into memory. Here, it comes from the file |my.intert|.
(2) The second step used the |generate| stage, which writes out Inter code
in the format of one's choice -- here "binary".
@ However, we don't have to use this default pipeline. |-pipeline-text 'PIPELINE'|
reads in a textual description of the pipeline to follow, with the steps divided
by commas. The examples above used a pipeline which in this notation would
be written as:
= (text as ConsoleText)
-pipeline-text 'read <- *in, generate -> *out'
=
|*in| and |*out| are examples of "pipeline variables". |*in| is the filename
of whatever file is to be read in, and |*out| is whatever was specified by |-o|
at the command line, or in other words, the filename to write the output to.
This is not quite the smallest possible pipeline. Consider:
= (text as ConsoleText)
$ inter/Tangled/inter -o my.intert -pipeline-text 'new, generate -> *out' -trace
step 1/2: new
step 2/2: generate text -> my.intert
=
Here we didn't specify any Inter file to read in, so |*in| does not appear.
Instead wevbegan the pipeline with the |new| compilation stage, which creates
a minimal Inter program from nothing.
Even three-step pipelines can be very useful. For example:
= (text as ConsoleText)
$ inter/Tangled/inter -o my.intert -pipeline-text 'read <- *in, eliminate-redundant-labels, generate -> *out' -trace
step 1/3: read <- my.intert
step 2/3: eliminate-redundant-labels
step 3/3: generate text -> my.intert
=
This could be used to test that the |eliminate-redundant-labels| compilation
stage is working as it should. We can feed our choice of Inter code into it,
and examine its direct output, in isolation from the working of the rest of
the compiler (and, of course, more quickly).
@ In practice, it becomes cumbersome to spell the pipeline out longhand on
the command line, so we can also put it into a text file:
= (text as ConsoleText)
$ inter/Tangled/inter -pipeline-file mypl.interpipeline
=
It's not allowed to specify both |-pipeline-file| and |-pipeline-text|.
The text file, however, specifies pipelines with one step on each line, not
using commas. So |-pipeline-text 'read <- *in, eliminate-redundant-labels, generate -> *out'|
is equivalent to |-pipeline-file| with the file:
= (text)
read <- *in
eliminate-redundant-labels
generate -> *out
=
For more on how to write and use pipeline files, see //Pipelines and Stages//.
@ In general, filenames follow the usual Unix conventions: they are taken as
relative to the current working directory, unless given as absolute filenames
beginning with |/|. But we can also set a "default directory" to take the
place of the CWD, using |-domain|:
= (text)
-domain D
@h Assimilation.
Inform makes use of what are called "kits" of pre-compiled Inter code:
for example, |CommandParserKit| contains code for the traditional interactive
fiction command parser. That pre-compilation is called "assimilation", and
is performed by the //inter// tool alone: it does not require, or use, the
bulk of the //inform7// compiler.
The source code for a kit could in principle be textual Inter, but that's too
verbose to write comfortably. In practice we use Inform 6 code as a notation,
and therefore assimilation is really compilation from I6 to Inter.
Kits are like so-called "fat binaries", in that they contain binary Inter
for each different architecture with which they are compatible. Inter can
build kits for only one architecture at a time, so a command must specify
which is wanted. For example:
= (text as ConsoleText)
$ inter/Tangled/inter -architecture 16 -build-kit inform7/Internal/Inter/BasicInformKit
$ inter/Tangled/inter -architecture 32d -build-kit inform7/Internal/Inter/BasicInformKit
=
At present there are four architectures: |16|, |16d|, |32| and |32d|.
Note that an architecture is not the same thing as a format: it specifies
only the word size (16 or 32 bit) and the presence, or not, of debugging data.
Incrementally building kits as needed could be done with something like
the Unix tool |make|, but in fact Inbuild has this ability: the command
= (text as ConsoleText)
$ inbuild/Tangled/inbuild -build K
=
looks at the kit |K|, works out which architectures need rebuilding, and
then issues commands like the above to instruct |inter| to do so. Indeed,
multiple kits can be managed with a single command:
= (text as ConsoleText)
$ inbuild/Tangled/inbuild -build -contents-of inform7/Internal/Inter
@ Under the hood, assimilation is just another use of pipeline processing. If we
run one of these |-build-kit| commands with |-trace| switched on, we see
something like this:
= (text as ConsoleText)
step 1/6: new
step 2/6: load-kit-source <- BasicInformKit
step 3/6: parse-insertions
step 4/6: resolve-conditional-compilation
step 5/6: compile-splats
step 6/6: generate binary -> inform7/Internal/Inter/BasicInformKit/arch-32.interb
=
This is in fact the result of running a pipeline file called |build-kit.interpipeline|
which is included in the standard Inter distribution.