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

174 lines
7.7 KiB
OpenEdge ABL

Textual Inter.
A specification of the inter language, as written out in text file form.
@h A manual of a sort.
In its textual form, Inter could conceivably be used as a programming
language, albeit a low-level and verbose one, and this section is a brief
manual for that language. But the real purpose of Inter is to assist testing
and debugging of the Inform tool-chain. We can write test cases in textual
Inter to see if inter works; or we can write out memory inter to the
debugging log to see if inform7 works.
A textual inter file is a UTF-8 encoded text file which conventionally takes
the file extension ".intert". Here is an example, the test case |Hello|:
= (text as Inter)
package main _plain
package Main _code
code
inv !enableprinting
inv !print
val "Hello, world.\n"
=
Some basic characteristics of textual Inter can be seen at a glance. It's line-based:
each line makes a single use of a "construct" like |package| or |code|. Lines
can be of arbitrary length. A line beginning with a |#| (in column 1) is
a comment, and blank lines are ignored.
As in Python, indentation from the left margin is highly significant, and
must be in the form of tab characters.
Most identifiers consist of a string of one or more English upper or lower
case letters, underscores, or digits, except that it must not begin with
a digit. Identifier lengths are unlimited. A few identifiers of special sorts
begin with a special character to mark them out as special: |!enableprinting|,
for example, is a "primitive", marked with a |!|.
A convention followed in Inter code generated by Inform is that many identifiers
have the form |P_Name|, where |P| is some prefix letter showing what sort of
thing is referred to: for example, variables begin with |V|, instances with |I|,
properties with |P|, and so on. This is all done only for clarity, and is not
part of the Inter specification.
@ The main organising idea of Inter is the "package". Each program is a nested
hierarchy of packages, and each package has both a name and a "package type".
In the |Hello| example, an outer package called |main|, of type |_plain|,
holds only a inner package called |Main|, of type |_code|.
In fact, every program must contain just one outermost package, called |main|,
which has to have type |_plain|, so that is unsurprising.
Packages of type |_code| are functions. A program is not required to contain any
functions at all, and even if it does, it is not required to contain one called
|Main|. For example, the Inter program into which BasicInformKit is compiled
does not provide a |Main|, and we could imagine kits which provided, say, only
some variables and arrays. But if we want a program to compile and run in a
stand-alone way, then it needs a |Main| function as a place to begin running.
@h Outside even main.
Though this does not happen in the |Hello| example, a handful of instructions
can appear at the top of the program, outside even |main|. These really just
define conventions used in the rest of the program, rather than describing
what the program does, and that is why they are on the outside.
@ |version NUMBER| indicates that the file was written in that version of
the Inter language. |NUMBER| is a semantic version number, and it forces
//inter// to reject the file if the version of the Inter specification
understood by //inter// is not compatible with the one in the file. (Note
that this about the version of the specification, not the version of //inter//
as a program.)
See //bytecode: The Inter Version// for more on the significance of this.
@ |pragma TARGET "WHATEVER"| does not change the meaning of the inter file;
it simply provides pragmatic advice to the eventual compiler of code
generated from this file. |TARGET| indicates the context for which this
is intended.
At present Inform 7 uses this to pass on ICL (Inform Command Language)
commands to Inform 6, such as memory settings or command-line switches.
For example,
= (text as Inter)
pragma Inform6 "$MAX_LABELS=200000"
=
When a program containing this instruction is generated to C, say, then this
is ignored completely. And in fact, now that Inform has adopted Inform 6.36 as
its preferred copy of I6, memory settings have become unnecessary and are
ignored by I6 anyway, so even this use of |pragma| may yet disappear.
@ |packagetype _NAME| declares that |_NAME| can be used as a package type.
It's actually unnecessary to write this explicitly, which is why the |Hello|
example did not begin with:
= (text as Inter)
packagetype _plain
packagetype _code
=
If the program creates, say, |package MOMA _museum|, then the necessary
package type instruction |packagetype _museum| will be inferred automatically.
Nevertheless, it's valid to spell this out.
All package types have names beginning with an underscore |_|. Authors of
Inter programs are free to choose any types they like, and Inform tends to
use semantically meaningful type names like |_adjective|. Only two package
types are special: |_code|, which as we have seen marks that a package is a
function, and |_linkage|, which is used only for a special package called
|connectors|. Stand-alone programs never contain that package: see
//bytecode// for an explanation of what it is for.
@ |primitive !PRIMITIVE SIGNATURE| declares that a primitive invocation of
this name and signature can be used in the program. Primitives occur only
inside of functions, where they are "invoked", and they are operations
sufficiently basic that they cannot themselves be written as other Inter
functions. For example, in |Hello| we saw:
= (text as Inter)
inv !print
val "Hello, world.\n"
=
This is an invocation of the primitive |!print|, which prints text. The
"signature" of this is |val -> void|, meaning that it expects to have one
argument, a value, and produces no result.
The declarations for the two primitives in |Hello| would look like this:
= (text as Inter)
primitive !enableprinting void -> void
primitive !print val -> void
=
But once again they do not need to be given, because they are part of the
standard set of Inter primitives supported by //inter//. So these definitions
are implicitly made as needed.
In principle, though, the program could create entirely novel primitives:
= (text as Inter)
primitive !flytoarabia val val val -> void
=
And this will work perfectly well... except that //inter// will not be able
to code-generate from the result without modification, because it won't know
how to fly to Arabia. So in practice, unless you are experimenting with
modified versions of Inter, do not use the |primitive| construct at all.
@ The declaration line for a package begins at the level of indentation of
the package's owner. For |main|, it should be unindented, and this is the
only package allowed to appear at the top level: all other packages should
be inside |main| in some way.
The contents of the package are then one tab stop in from the declaration. Thus:
= (text as Inter)
package main _plain
...
package m1_RBLK1 _code
...
package m1_RBLK2 _code
...
=
Here, |main| contains two sub-packages, |m1_RBLK1| and |m1_RBLK2|, and
indentation is used to show which package a statement belongs to.
Packages of any type can freely be created inside packages of any other type,
except that |_code| packages cannot have subpackages. So this hypothetical bit of
Inter is illegal:
= (text as Inter)
package my_function _code
package inner_part _code
...
...
=
Here, |inner_part| cannot be made a subpackage of |my_function| because |_code|
packages have no subpackages.
@ The remaining statements to cover fall into two categories.
(*) Those used outside functions, i.e., packages of type |_code|, are covered in
//Data Packages in Textual Inter//.
(*) Those used in functions, i.e., packages of type |_code|, are covered in
//Code Packages in Textual Inter//.