INTERCAL (C-INTERCAL), 15 кодів, 313 + 2 = 315 байт
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
Спробуйте в Інтернеті!
Весь пробіл тут не має значення. (Оригінальна програма містила вкладки, але я перетворив їх на пробіли, щоб вона правильно вибудовувалася в SE; звичайно використовувати ширину вкладок 8 для INTERCAL. Я протестував версію програми з усіма вкладками, пробілами , але нові рядки видалені, але це працює чудово.)
Компілюйте з -abm
(2 байтовим штрафом, тому -b
що компілятор повинен бути детермінованим).
Як зазвичай для INTERCAL, для цього потрібно ввести числовий формат, наприклад, ONE TWO THREE
для 123
.
Пояснення
Коли програма C-INTERCAL помиляється, статусом виходу є код помилки за модулем 256. В результаті ми можемо поставити за мету написати програму, яка здатна створювати якомога більше помилок виконання. Ця програма упускає лише дві помилки виконання, які не вказують на проблеми внутрішнього компілятора: ICL200I, оскільки для її відтворення потрібне використання зовнішніх бібліотек, сумісних лише з однопотоковою програмою (а багатопотокові програми мають більше помилок); і ICL533I, оскільки 533 має таке ж значення по модулю 256, як і 277, і програма здатна виробляти ICL277I.
Програма завжди починається однаково. Спочатку ми вводимо ( WRITE IN
) значення для змінної .1
. Потім ми використовуємо обчислений CREATE
оператор для створення нового синтаксису (тут, A
); але оскільки він обчислюється, визначення синтаксису змінюється залежно від значення .1
. Нарешті, у більшості випадків ми запускаємо наше нове A
твердження, яке було визначено для помилки; таблиця можливих визначень, яку ми маємо, містить визначення для кожної можливої помилки виконання (крім винятків, перелічених вище).
По-перше, є дві винятки із цієї загальної схеми. (0)
не є дійсним номером рядка, тому, якщо користувач вводить ZERO
, ми переходимо з другого рядка (пронумерованого (8)
) до четвертого рядка за допомогою обчисленого COME FROM
оператора. Потім це потрапляє в синтаксичну помилку DO X
, яка призводить до помилки ICL000I
. (У системі INTERCAL помилки синтаксису трапляються під час виконання, через тенденцію відключення команд, переопределення синтаксису під вами тощо). COME FROM
Затвердження також має побічний ефект, навіть якщо фактична COME FROM
не відбувається, створюючи перевантаження операнда з .1
до #1
всякий раз , коли рядок з номером рядка виконується; це використовується пізніше, коли виробляється вихід 21. (Випадкові глобальні побічні ефекти є досить ідіоматичними в INTERCAL.)
Інший виняток - із введенням даних ONE TWO NINE
. У програмі немає номера рядка (129)
, тому ми отримуємо помилку щодо відсутнього номера рядка, який є ICL129I
. Тож мені взагалі не довелося писати жодного коду, щоб висвітлити цю справу.
Ось інші помилки, і що їх викликає:
- 123 -
NEXT
переповнення стека ( DO (123) NEXT
). Оператору NEXT
потрібні інші модифікатори ( FORGET
або RESUME
) для того, щоб заднім числом визначити, що це за оператор управління. Якщо не виникає цих причин помилка ICL123I, як тільки є 80 невирішених `NEXT-операцій.
- 222 - прихований перелив (
DO STASH .2
у COME FROM
циклі). Сховища обмежені лише наявною пам’яттю, але ця кількість закінчиться, що призведе до помилки ICL222I.
- 240 - розміри масиву до нуля. Саме це
DO ,1 <- #0
означає, і це викликає помилку ICL240I.
- 241 викликається призначенням поза межами масиву. У цьому випадку
,1
не було виділено ( ,
використовується для змінних типу масиву в INTERCAL), тому індексація викликає помилку ICL241I.
- 19 призначає 65536 (
#256 $ #0
) 16-бітній змінній .2
. Він не підходить, викликаючи помилку ICL275I.
- 21 призначає
#2
на .1
. Це може виглядати як досить просте завдання, але ми перевантажені, .1
щоб мати на увазі #1
раніше, і спроба змінити значення 1 без жодної -v
опції в командному рядку викликає помилку ICL277I.
- 148 спроб повернутися до верхнього запису стеку selectpoint (
GO BACK
), який не існує на даний момент у програмі (ми не виконали жодних команд для маніпулювання стеком selectpoint, тому він все ще порожній). Це викликає помилку ICL404I.
- 180 спроб
RETRIEVE .2
з неіснуючого сховища (тому що ми нічого не зберігали там у цій гілці програми), викликаючи помилку ICL436I.
- 50 запитів введення (
WRITE IN
) назавжди у COME FROM
цикл. Врешті-решт ми закінчимо читання минулого EOF, викликаючи помилку ICL562I.
- 109 запускає твердження
DO RESUME #0
, яке безглуздо і спеціально задокументовано як спричинення помилки (ICL621I).
- 120 запускає твердження
DO RESUME #9
. Ми ще не виконали так багато NEXT
тверджень, і таким чином ми отримуємо помилку ICL120I. (Цікаво, що ця конкретна помилка визначена в документації INTERCAL як нормально виходить із програми, а потім викликає помилку, а не виходить із програми з помилкою. Я не вважаю, що ці два випадки помітно відрізняються.)
- 223 - це в основному складний клубок багатопотокових примітивів, які всі вказують на лінію 223, викликаючи нескінченну петлю, яка підірває пам'ять. Зрештою, в багатопотоковій підсистемі відбувається виснаження пам'яті, що призводить до помилки ICL991I.
- 121 насправді є дійсним твердженням (це коментар), але воно з’являється в кінці програми. Таким чином, виконання відпадає від кінця програми відразу після її виконання, викликаючи помилку ICL633I.
Перевірка
Деякі помилки пов'язані з навмисним запуском програми з пам’яті, тому я пропоную встановити досить невеликі межі пам’яті. Ось команда оболонки, яку я використав для тестування програми (додано нові рядки для читабельності; видаліть їх, якщо запустити її самостійно):
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
І ось результат (з номерами рядків та повідомленнями "BLEASE CORRECT SOURCE" видалено, щоб заощадити місце), який я частково додав, щоб продемонструвати роботу програми, але, здебільшого, щоб показати нерозумні повідомлення про помилки INTERCAL:
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