# Tips for golfing in INTERCAL

What general tips do you have for golfing in INTERCAL? I'm looking for ideas which can be applied to code golf challenges and are also at least somewhat specific to INTERCAL (i.e. "remove comments" is not a useful answer).

I know exotic languages can be really useful to win golf contests, but I don't see much INTERCAL code around here. Do you have any advice that can help people get competitive code sizes with INTERCAL? Could this language ever be competitive?

INTERCAL is so underutilized that it doesn't even have a tag. So sad...

• Most languages don't have or need their own tags here, as language-specific challenges are generally discouraged. – Alex A. Aug 10 '15 at 4:29
• A hint that this might not be the best golfing language, from its wikipedia page: Despite the language's intentionally obtuse and wordy syntax, – isaacg Aug 10 '15 at 5:01

# Whitespace / "noise" removal can go further than you might expect

INTERCAL is a whitespace-insensitive language. Unlike most whitespace-insensitive languages, though, the insensitivity goes much further than you might expect.

For example, DO NOT is two tokens, but can be written DONOT without the parser complaining (in pretty much any widely used implementation). (Of course, you could also write DON'T, but it isn't any terser. It might be easier to read, though. PLEASEN'T is probably harder to read than PLEASE NOT, though.) Actually, there's some debate as to whether whitespace does anything at all; at least one INTERCAL parser allows it even inside numerical constants (not that that's very useful when golfing). One thing to bear in mind is that removing whitespace from DO READ OUT gives DOREADOUT which can confuse some older INTERCAL parsers due to the embedded DO (although their authors generally consider this a bug, and thus nowadays it typically works in a valid program, it's not advisable to put code like this in the vicinity of a syntax error, as it can be much harder to disambiguate then).

Also remember that you can overpunch characters to save space. In ASCII, you can only really pull this off with '.!, but that's a highly useful trick in its own right. (When you aren't using arrays, there's no possibility of a sparkears ambiguity even when all your grouping characters are the same, so for golfing entries, it's recommended to stick to just ' unless an array subscript genuinely requires a ".) A bookworm can be represented in one byte by using the ? abbreviation (C-INTERCAL) or Latin-1 for ¥ (CLC-INTERCAL), rather than the three that INTERCAL-72 needs.

# Focus on doing as much work as possible in one statement

INTERCAL's statement identifiers are rather verbose; DO is two noise characters on every statement, the statement's name itself also tends to be quite long, and you have to throw in a PLEASE every now and then to keep the parser happy. (The best you can do is a ratio of four DO to one PLEASE, meaning you're using 14 characters in identifiers for every 5 commands.) On the other hand, the expression syntax is fairly terse (ridiculous, but terse). This means that it's often worthwhile fitting part of your program into a single expression even when using multiple statements would be a more "natural" way to do things.

For example, if you want to assign #1 to .1 and #2 to .2, instead of doing it in the obvious INTERCAL-72 way:

DO.1<-#1DO.2<-#2


it's strongly worth considering overloading a random variable to allow you to assign both at once:

DO:1<-#1$#2  (with :1/!1$.2' thrown in somewhere earlier in the program; note that this notation postdates INTERCAL-72 by quite a way, so you'll need to use a modern INTERCAL for this to work). This is only slightly longer even if you take the setup into account, and becomes shorter if you ever need to, or can arrange to, simultaneously assign to .1 and .2 more than once.

It's not just calculate commands where this trick works. If you need to stash a variable twice, don't do it like this:

DOSTASH.1DOSTASH.1


but like this:

DOSTASH.1+.1


(The + notation works for most commands where it could conceptually make sense.)

# Use a single RESUME for all INTERCAL-72-style if consructs

If you need to write the equivalent of an "if" statement, the normal method using INTERCAL-72 code is to NEXT twice and then do a computed RESUME. (In modern code, often a computed COME FROM will be better, but this tip assumes your code prefers NEXT.) You almost certainly have to pay the bytes for the first NEXT, as it jumps from one branch of the "if" to the other. Sharing the second NEXT is also nontrivial, unless you have a lot of "if" statements that go to the same place upon seeing a #1. However, the RESUME can be anywhere in the program (because control is going to leave it instantly anywhere).

There are two ways to handle this. If you have a lot of "if" statements, then the RESUME probably warrants a single-digit line number, so that your second NEXT statement can be as short as possible. If possible, try to make it a computed RESUME that would naturally occur in your code (admittedly, this is difficult, as it's rare for those to appear in the "normal flow" of code rather than being NEXTed to); then, the only cost is the line number. You'll have to use a single boolean variable for all these NEXTs; the universal consensus here is to use .5, mostly because it's the variable that the standard library uses for boolean return values.

Alternatively, it's possible to make use of an undocumented (technically underdocumented, because I slipped a hint into the INTERCAL documentation when I noticed) feature of the standard library. Because a central location for a RESUME is so useful, the standard library uses one internally. Line numbers in INTERCAL are global (with namespacing conventions, but which can be broken if you know what you're doing), so you can NEXT right into the standard library internals if you want to, and in particular, can NEXT to its central RESUME location. This is sufficiently popular in existing INTERCAL code that standard library replacements tend to have to implement it to avoid breaking existing programs.

The line in question is (either literally or effectively, depending on the implementation):

(1001) DO RESUME .5


The main reason not to use this is its long line number; if you need to do a lot of INTERCAL-72-style if constructs, it'll be better to use your own to give it a shorter number.

Of course, you can combine the techniques, writing something like

(9)DO(1001)NEXT


which is only marginally longer than

(9)DORESUME.5


and has the benefit that the booleans become #2 and #3 (which is harder to read, but normally easier to generate). Actually, it might even be worth putting in the extra code to handle #0 and #1 if you're going to be iffing a lot (but computed COME FROM will probably work better in this case unless your requirements are very weird).

# INTERCAL doesn't specify precedence, but it also doesn't error on ambiguous precedence

An expression like

#1$#2~#3  is ambiguous, and could mean '#1$#2'~#3


or

#1\$'#2~#3'


The INTERCAL spec leaves it intentionally unclear which is meant, and in general there's no standard (although C-INTERCAL and CLC-INTERCAL make an effort to match each other in the simpler cases). That said, the original isn't incorrect; it's ambiguous and I wouldn't advise using it in production code (but then, I wouldn't advise using INTERCAL itself in production code), but it will have some meaning in the majority of compilers.

In other words, it may be worth just removing grouping characters and hoping your program still works. Most interpreters will parse any given ambiguous expression consistently, so for each pair of grouping characters, there's a 1 in 2 chance it's unnecessary; that can add up to quite some savings. (Unfortunately, INTERCAL parsers tend to be sufficiently confusing that nobody's entirely sure what the rules actually are, but it can normally be determined by experiment. In the simplest cases, operators tend to all have equal precedence and to have a consistent associativity.)

# In C-INTERCAL, consider abbreviating code using CREATE

The CREATE statement allows you to create new syntax. This is particularly useful in golfing because it allows you to give statements shorter names. You can also use it to effectively "define a function" via creating a new operator (which has the huge advantage that it lets you call the function in the middle of an expression).

The setup cost here is fairly high, but if there's a construct that you use a lot, inventing shorter syntax for it is probably going to be a good idea.