INTERCAL (C-INTERCAL), 15 codes, 313 + 2 = 315 bytes
PLEASE WRITE IN .1
(8) PLEASE CREATE .1 A
PLEASE A
PLEASE COME FROM #2$!1/#1'
DO X
(123) DO (123) NEXT
DO COME FROM (222)
(222) DO STASH .2
(240) DO ,1 <- #0
(241) DO ,1 SUB #0 <- #1
(19) DO .2 <- #256 $ #0
(21) DO .1 <- #2
(148) DO GO BACK
(180) DO RETRIEVE .2
DO COME FROM (50)
(50) DO WRITE IN .2
(109) DO RESUME #0
(120) DO RESUME #9
MAYBE COME FROM (223)
(223) DO COME FROM (223)
(121) PLEASE NOT X
Try it online!
All whitespace here is irrelevant. (The original program contained tabs, but I converted them to spaces so that it'd line up correctly on SE; it's conventional to use a tab width of 8 for INTERCAL. I've tested a version of the program with all tabs, spaces, and newlines deleted, though, and it works fine.)
Compile with -abm
(2 byte penalty, because -b
is required for the compiler to be deterministic).
As usual for INTERCAL, this takes numeric input in the format, e.g., ONE TWO THREE
for 123
.
Explanation
When a C-INTERCAL program errors out, the exit status is the error code modulo 256. As a result, we can aim to write a program that's capable of producing as many runtime errors as possible. This program only omits two runtime errors that don't indicate internal compiler issues: ICL200I, because reproducing it requires the use of external libraries that are only compatible with a single-threaded program (and multithreaded programs have more errors available); and ICL533I, because 533 has the same value modulo 256 as 277 does, and the program's capable of producing ICL277I.
The program always starts the same way. First, we input (WRITE IN
) a value for the variable .1
. Then, we use a computed CREATE
statement to create new syntax (here, A
); but because it's computed, the definition of the syntax varies based on the value of .1
. Finally, in most cases we run our new A
statement, which has been defined to produce an error; the table of possible definitions we have contains a definition for each possible runtime error (other than the exceptions listed above).
First, there are two exceptions to this general scheme. (0)
is not a valid line number, so if the user inputs ZERO
, we jump from the second line (numbered (8)
) to the fourth line by means of a computed COME FROM
statement. This then falls through into a syntax error DO X
, which produces error ICL000I
. (In INTERCAL, syntax errors happen at runtime, due to the tendency of commands to be disabled, syntax to be redefined under you, etc.). The COME FROM
statement also has a side effect, even if no actual COME FROM
happens, creating an operand overload from .1
to #1
whenever a line with a line number is executed; this is used later on when producing output 21. (Random global side effects are fairly idiomatic in INTERCAL.)
The other exception is with input ONE TWO NINE
. There's no line number (129)
in the program, so we get an error for a missing line number, which is ICL129I
. So I didn't have to write any code to cover that case at all.
Here are the other errors, and what causes them:
- 123 is a
NEXT
stack overflow (DO (123) NEXT
). The NEXT
statement needs other modifiers (FORGET
or RESUME
) in order to retroactively determine what sort of control statement it was. Not having those causes error ICL123I once there are 80 unresolved `NEXT statements.
- 222 is a stash overflow (
DO STASH .2
in a COME FROM
loop). The stashes are limited only by available memory, but that will run out eventually, causing error ICL222I.
- 240 is dimensions an array to size zero. That's exactly what
DO ,1 <- #0
means, and it causes error ICL240I.
- 241 is caused by assigning outside the bounds of an array. In this case,
,1
hasn't been allocated (,
is used for array-type variables in INTERCAL), so indexing it causes error ICL241I.
- 19 assigns 65536 (
#256 $ #0
) to a 16-bit variable .2
. It doesn't fit, causing error ICL275I.
- 21 assigns
#2
to .1
. That might look like a simple enough assignment, but we overloaded .1
to mean #1
earlier, and attempting to change the value of 1 with no -v
option on the command line causes error ICL277I.
- 148 attempts to return to the top entry of the choicepoint stack (
GO BACK
), which doesn't exist at this point in the program (we haven't run any commands to manipulate the choicepoint stack, so it's still empty). That causes error ICL404I.
- 180 attempts to
RETRIEVE .2
from a nonexistent stash (because we didn't stash anything there in this branch of the program), causing error ICL436I.
- 50 requests input (
WRITE IN
) forever in a COME FROM
loop. Eventually we'll end up reading past EOF, causing error ICL562I.
- 109 runs the statement
DO RESUME #0
, which is meaningless and specifically documented as causing an error (ICL621I).
- 120 runs the statement
DO RESUME #9
. We haven't run that many NEXT
statements yet, and thus we get error ICL120I. (Intriguingly, this particular error is defined in the INTERCAL documentation as exiting the program normally and then causing the error, rather than exiting the program with an error. I don't believe these two cases are observably different, though.)
- 223 is basically a complex tangle of multithreading primitives that all point back to line 223, causing an infinite loop that blows up memory. Eventually, there's memory exhaustion in the multithreading subsystem, leading to error ICL991I.
- 121 is actually a valid statement (it's a comment), but it appears at the end of the program. As such, execution falls off the end of the program immediately after it executes, causing error ICL633I.
Verification
Some of the errors involve intentionally running the program out of memory, so I suggest setting fairly small memory limits. Here's the shell command I used to test the program (with newlines added for readability; delete them if you run it yourself):
for x in "ZERO" "ONE NINE" "TWO ONE" "FIVE ZERO" "ONE ZERO NINE"
"ONE TWO ZERO" "ONE TWO ONE" "ONE TWO THREE" "ONE TWO NINE"
"ONE FOUR EIGHT" "ONE EIGHT ZERO" "TWO TWO TWO"
"TWO TWO THREE" "TWO FOUR ZERO" "TWO FOUR ONE";
do echo;
echo $x;
echo $x | (ulimit -Sd 40000; ulimit -Sv 40000; ulimit -Ss 40000;
./errors; echo $?);
done
And here's the output (with the line numbers and "PLEASE CORRECT SOURCE" messages deleted to save space), which I added partly to demonstrate the program working but mostly to show off INTERCAL's silly error messages:
ZERO
ICL000I PLEASEWRITEIN.1(8)PLEASECREATE.1APLEASEAPLEASECOMEFROM#2$!1/#1'DOX(123)DO(123)NEXTDOCOMEFROM(222)(222)DOSTASH.2(240)DO,1<-#0(241)DO,1SUB#0<-#1(19)DO.2<-#256$#0(21)DO.1<-#2(148)DOGOBACK(180)DORETRIEVE.2DOCOMEFROM(50)(50)DOWRITEIN.2(109)DORESUME#0(120)DORESUME#9MAYBECOMEFROM(223)(223)DOCOMEFROM(223)(121)PLEASENOTX
0
ONE NINE
ICL275I DON'T BYTE OFF MORE THAN YOU CAN CHEW
19
TWO ONE
ICL277I YOU CAN ONLY DISTORT THE LAWS OF MATHEMATICS SO FAR
21
FIVE ZERO
ICL562I I DO NOT COMPUTE
50
ONE ZERO NINE
ICL621I ERROR TYPE 621 ENCOUNTERED
109
ONE TWO ZERO
ICL632I THE NEXT STACK RUPTURES. ALL DIE. OH, THE EMBARRASSMENT!
120
ONE TWO ONE
ICL633I PROGRAM FELL OFF THE EDGE
121
ONE TWO THREE
ICL123I PROGRAM HAS DISAPPEARED INTO THE BLACK LAGOON
123
ONE TWO NINE
ICL129I PROGRAM HAS GOTTEN LOST
129
ONE FOUR EIGHT
ICL404I I'M ALL OUT OF CHOICES!
148
ONE EIGHT ZERO
ICL436I THROW STICK BEFORE RETRIEVING!
180
TWO TWO TWO
ICL222I BUMMER, DUDE!
222
TWO TWO THREE
ICL991I YOU HAVE TOO MUCH ROPE TO HANG YOURSELF
223
TWO FOUR ZERO
ICL240I ERROR HANDLER PRINTED SNIDE REMARK
240
TWO FOUR ONE
ICL241I VARIABLES MAY NOT BE STORED IN WEST HYPERSPACE
241
throw new Exception()
style), that is illegal. If it is the byproduct of misuse of an existing function, then that's fine. \$\endgroup\$one zero zero
for 100) acceptable? I have an idea for this challenge, but the language has some fairly unusual ideas about I/O, and this is the most natural input format. \$\endgroup\$