Next: External Calls and auto, Previous: ick_get/setone/twospot, Up: External Calls to C [Index]
The ick_create
function (prototype: void
ick_create(char*, unsigned long)
) allows the external calls
system to be used to create new INTERCAL syntax; to
do this, you give a ‘signature’ representing the syntax you
want to define and a line number to the function (which are its two
arguments, respectively). The signature defines the syntax that you are
defining; whenever that syntax is encountered within the
INTERCAL program, it will NEXT
to the
line number you specify, which can do various clever things and then
RESUME
back to the INTERCAL program (or
if you’re defining a flow-control operation, you might want to
leave the NEXT
stack entry there and do other things).
However, note that the overloading of :1601
, etc., will
still take place as in the INTERCAL version of
CREATE
if the -a option is used (see -a), so care is needed when writing flow
control statements that they work both with and without the option and
don’t cause STASH
leaks (which means no
FORGET
ting the relevant NEXT
stack entry, and
no looking at 1600-range variables). This allows the external calls
system to define whole new INTERCAL commands, with
the same power as any other programming language.
There are various restrictions on what syntax you can
CREATE
with this method, which are best explained by an
explanation of the relevant C-INTERCAL compiler internals.
When an INTERCAL program is compiled by
C-INTERCAL, any unrecognised statements it comes across
are compiled by a ‘just-in-case’ compiler that attempts to
compile them anyway with no knowledge of their syntax, just in case the
syntax becomes defined later. (E000 (see E000) will be thrown when such statements are
encountered at runtime, unless the syntax has been CREATE
d
since to give a statement a meaning.) For the just-in-case compiler to
run, the resulting statement must be completely unrecognised; this
means that it may contain no keywords (not even a sequence of letters
that forms a keyword, such as FROM
or DO
), it
must consist only of variable names, expressions, and capital letters
other than ‘V’ (because
‘V’ is a unary operator, so otherwise there
would be ambiguity), and in which any two variable names or expressions
are separated by at least one capital letter. The compiler will produce
a ‘signature’ for the unknown command that can be defined.
A signature consists of a sequence of characters (and is represented as
a null-terminated string; the runtime makes a shallow copy of the
string and keeps it until the end of the program, so arrangements must
be made to ensure that the storage in which the string is allocated
stays around that long, but this opens up interesting possibilities in
which the signature that was actually CREATE
d can be
modified retroactively); whitespace is not allowed in a signature.
Capital letters can be used (apart from ‘V’),
and match the same capital letters literally in the
INTERCAL syntax being created; also available are
the special characters ‘.,;~’, which match
respectively a scalar variable (a onespot or twospot variable such as
:1
), an array variable (such as ;2
), an array
element (such as ,3 SUB #4 #5
), and an expression that
isn’t a variable name and isn’t an array element (such as
.4$.5
). If you want to be able to match other things (say,
to be able to match all expressions), you will need to submit multiple
signatures using multiple calls to ick_create
; maybe you
could write a library to do that automatically.
CREATE
d operators also have signatures, but of quite a
different form. The signature for a single-character operator is a
lowercase u, followed by its character code in hexadecimal (no leading
zeros, and in lowercase); the signature for an overstrike is a
lowercase o, followed by the lower relevant character code in
hexadecimal, followed by a lowercase x, followed by the higher relevant
character code in hexadecimal.
The routine that is NEXT
ed to will presumably want to be
able to see what in the INTERCAL program was matched
by the signature, so a range of function-like macros is provided to
access that. They must be run from within the invocation of the
function which was NEXT
ed into by the created syntax (see
External Calls and
auto for when a function invocation ends, which could be sooner
than you think when the C-INTERCAL external calls system
is used), and are undefined behaviour when that invocation did not gain
control from a CREATE
d statement. Here are their effective
prototypes:
int ick_c_width(int); int ick_c_isarray(int); unsigned short ick_c_varnumber(int); uint32_t ick_c_value(int); /* These require -a to work */ uint32_t ick_c_getvalue(int); void ick_c_setvalue(int, uint32_t);
The first argument to all these macros is the position of the match in the signature (0 for the first non-capital-letter match in the signature, 1 for the second, and so on until no more items are left in the signature to match); specifying a position that isn’t in the signature is undefined behaviour.
ick_c_width
returns the data type, as a width in bits, of
the expression (or the width in bits of an element of the passed in
array), and ick_c_isarray
returns 1 if the argument was an
array variable or 0 if it was an expression (array elements and scalar
variables are expressions). ick_c_varnumber
returns the
variable’s number (for instance 123 for .123
), or 0
if the corresponding argument was not a variable; in the cases where
the argument was a variable, these three functions together provide
enough information to figure out which variable (which is useful if
you’re writing an extension which takes a variable name as an
argument).
ick_c_value
returns the value of the corresponding
expression at the time the CREATE
d command was called;
ick_c_getvalue
is almost equivalent, but only works if the
-a option (see -a)
was used during compilation, and returns the value of the corresponding
expression now. (The uint32_t return type is large enough to hold
either a onespot or twospot value, and will be zero-extended if the
corresponding expression had onespot type.) ick_c_setvalue
also depends on -a, and will assign to the corresponding
expression (be careful not to provide a value that is too large for
it!). In the case that the corresponding expression is not a variable,
this will attempt to perform a reverse assignment to the expression,
and can produce ordinary INTERCAL errors if it
fails. It is not possible to redimension an array this way, as this is
assignment, not a calculate operation.
Next: External Calls and auto, Previous: ick_get/setone/twospot, Up: External Calls to C [Index]