Next: , Previous: , Up: External Calls to C   [Index]


14.1.10 ick_create

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 FORGETting 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 CREATEd 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 CREATEd 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.

CREATEd 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 NEXTed 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 NEXTed 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 CREATEd 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 CREATEd 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: , Previous: , Up: External Calls to C   [Index]