INTERCAL (programozási nyelv)

Az INTERCAL egy ezoterikus programozási nyelv, amit Don Woods és James M. Lyon, a Princetoni Egyetem hallgatói alkottak 1972-ben. A korabeli programnyelvek, nyelvtervek különböző jellemzőit,[1] és az 1960-as évek jelöléseit parodizálja.

Jimbo Lyon, az INTERCAL egyik szerzője

Jelenleg két támogatott verziója létezik: a C-INTERCAL, amit korábban Eric S. Raymond gondozott,[2] és a CLC-INTERCAL Claudio Calvelli gondozásában.[3]

Története

A szerzők eredeti leírása szerint:[4]

A fordító teljes neve „Compiler Language With No Pronounceable Acronym” lett volna, amit nyilvánvaló okok miatt INTERCAL-ra lett lerövidítve.

Az eredeti implementáció EBCDIC karakterkészletet és lyukkártyákat használt. Az ASCII-t használó számítógépeken való futáshoz két karaktert kicseréltek: a ¢-t $-ra, ami a szoftver emelkedő költségét szimbolizálja a hardverhez viszonyítva, és a ∀-t ?-re, ami az átlagember reakcióját fejezi ki, amikor először találkozik a kizáró vaggyal.[4] A C-INTERCAL újabb verzióiban a régebbi operátorok is elérhetők szinonimaként; az INTERCAL programok lehetnek ASCII, Latin-1, vagy UTF-8 kódolásúak is.[5]

Részletek

A nyelvet úgy tervezték, hogy különbözzön más programozási nyelvektől. A legtöbb nyelvben használt operátorok szintaxisa rejtélyes és redundáns. A manuál szerint:

Ismert és jól bemutatható tény, hogy akinek a munkája érthetetlen, azt nagyra becsülik. Például a 65536 eltárolása egy 32 bites változóban a következő módon tehető meg a legegyszerűbben:

DO :1 <- #0¢#256

amire minden értelmes programozó azt mondaná, hogy abszurd.[4]

A nyelv több más esztétikai eszközt is tartalmaz: utasításokat, mint a "READ OUT" (olvasd ki), "IGNORE" (ignoráld), "FORGET" (feledd el), és módosítókat, mint a "PLEASE" (kérem). Ha ez utóbbiból túl kevés van, akkor a fordító hibát jelez, mert a program nem elég udvarias. Ha túl sok van, akkor is hibát kapunk, mert túl udvarias. Habár az első fordító is így működött, akkor még dokumentálatlan maradt.[6]

A nyelv akaratlagos szószátyársága ellenére Turing-teljes: elegendő memóriával bármit meg lehet benne oldani, amire az univerzális Turing-gép képes. Azonban a legtöbb implementáció nagyon lassú. Egy 65536-ig minden prímet kiszámoló Eratoszthenész szitáját egy Sun SPARCstation 1-en tesztelték. C-ben kevesebb, mint fél másodpercig tartott; ugyanez a program INTERCAL-ban implementálva több, mint tizenhét órán át futott.[7]

Az INTERCAL kézikönyv szerint a tervezésben fontos volt, hogy ne hasonlítson semmilyen más nyelvre. Itt gondolhattak akár a vezérlési szerkezetekre, akár az adatok feldolgozására. Ebben a tervezők elég sikeresnek bizonyultak. A BESM-6 szovjet számítógéptípus (megjelent 1967-ben) gépi kódja tartalmaz egy SELECT-nek megfelelő utasítást.[8]

Dokumentáció

A "select" operátor működését szemléltető "circuitous diagram" az INTERCAL Reference Manualból

A kézikönyv sok paradox, nonszensz, vagy más humoros megjegyzést tartalmaz:

Vigyázat! Semmilyen körülmények között ne tévessze össze a mesh és az interleave operátort, kivéve azokat a körülményeket, ahol össze lehet őket téveszteni!

A manuál tartalmaz egy mandulát is; ahogy a lábjegyzetben kifejtik: Mivel a kézikönyvekben szokott lenni függelék (appendix, ami vakbelet is jelent), ezért úgy döntöttünk, hogy ez a manuál egy másik eltávolítható szervet tartalmazzon.

A kézikönyv minden nem alfanumerikus karakternek új, szokatlan nevet ad; az egyszerű idézőjel (') neve sparks, a dupláé (") rabbit ears (nyúlfül). Az egyetlen kivétel az ampersand (&); ahogy a Jargon File írja: Mi lehet ennél hülyébb? A legtöbb nyelvben használt = jelet (half mesh, fél háló) az INTERCAL-ban balra mutató nyíl (<-) helyettesíti, ami egy angle-ből (szög) és egy wormból (kukac) épül fel.

Szintaxis

Az input (a WRITE IN utasítás) és az output (a READ OUT utasítás) nem használják a megszokott formákat; az INTERCAL-72-ben a WRITE IN az angolul kiírt számjegyeket támogatja, mint SIX FIVE FIVE THREE FIVE (65535) és a READ OUT római számokat ír ki.[4] Az újabb verzióknak saját I/O rendszerük van.[3][6]

A kommenteket a megfordított utasításnevek jelzik a NOT vagy a N'T egyikével bővítve. Az ezek után írottakat alapból nem veszi figyelembe (ABSTAIN). Ha egy sor szintaktikailag hibás, akkor a fordító szintén figyelmen kívül hagyja, és a hiba futás közben jelenik meg.[4]

Adattípusok

Az INTERCAL-72 4 adattípust ismer: a 16 bites egészet (reprezentációja . 'spot' pötty), a 32 bites egészet (jele :, 'twospot' kétpötty), a 16 bites egészek tömbje (, 'tail' farok), és a 32 bites egészek tömbje (;, 'hybrid', hibrid). Mindegyik típus számára 65535 változó áll rendelkezésre, például a 16 bites egészeket a .1-től a .65535-ig terjedő változók tárolhatják. Mindezek a változók veremként működnek; a betevés neve INTERCAL-ban STASH, a kivevésé RETRIEVE, ami támogatja a bonyolultabb adatszerkezeteket is.[4] Mindezek az adatszerkezetek az újabb implementációkban is megtalálhatók apróbb módosításokkal. A TriINTERCAL például hármas számrendszerben tárolja az adatokat, így a 16 bites számok helyett 10 trites számokat használ.[6] A CLC-INTERCAL más adatszerkezetekkel is bír, mint 'classes and lectures' (osztályok és órák), amelyek az újabb típusok létrehozása helyett azt teszik lehetővé, hogy az eddigi szerkezetek még több információt tárolhassanak.[3] A tömbök dimenzióját skalárszerű értékadással lehet megadni. A konstansok jele a # ('mesh', háló), amit maga a konstans követ, tízes számrendszerben; csak a 0 és a 65535 közötti értékeket vehetik fel.[4]

Operátorok

Az INTERCAL-72 öt operátort tartalmaz; az egyes implementációk ezeket másként jelölik. Egyes implementációkban több jel is használható:

INTERCAL operators[3][4][6]
OperátorINTERCAL-72 karakterekAtari karakterekC-INTERCAL karakterekCLC-INTERCAL karakterek
INTERLEAVE / MINGLEc backspace /$¢, $, c backspace /¢
SELECT~~~~
AND&&&&
ORVVVV
XORV backspace -?V backspace -, ?, V backspace -, ¥

Szemben a legtöbb nyelvvel az AND, az OR és a XOR unáris operátorok, amelyek a következő módon működnek:

  • az eredmény legszignifikánsabb bitje az argumentum legszignifikánsabb és a legkevésbé szignifikáns jegyéből keletkezik
  • az eredmény második legszignifikánsabb bitjét a legszignifikánsabb és az azt követő bitjéből számítják ki
  • az eredmény harmadik legszignifikánsabb bitje az argumentum második legszignifikánsabb és az azt követő bitjéből adódik
  • és így tovább

Az operátor helye a változót jelölő pont vagy kettőspont után, a számjelölés előtt van, vagy a csoportosító jelek közül az elsőt követi; azaz egy karakterrel hátrébb áll, mint például a C-ben.

A SELECT és az INTERLEAVE, ami MINGLE néven is ismert, infix bináris operátorok. A SELECT veszi az első argumentumának azokat a bitjeit, amelyek 1-ek, és a második argumentumából kitörli az ezeken a helyeken álló nullákat. Az így kapott számot eltolja a kevésbé szignifikáns irányba, és nullákkal tölti ki. Például az 51 (110011) SELECT 21 (10101) eredménye 5 (101). A MINGLE felváltva veszi a biteket az első és a második operandusból úgy, hogy az eredmény utolsó jegye a második argumentum utolsó jegye legyen.

Az operátorok között nincs precedencia; szükség esetén csoportosító jelek használhatók. Ezek a ' és a ", amelyek a megfelelőjükkel állnak párba. A programozó felelőssége, hogy a jelölés egyértelmű legyen.

Vezérlési szerkezetek

Az INTERCAL programok a DO, a PLEASE vagy a PLEASE DO szavakkal kezdődnek. Ezek mind ugyanazt jelentik, de túl gyakori alkalmazásuk miatt a fordító hibát jelez, amit az INTERCAL-72-ben nem dokumentáltak, de a C-INTERCAL kézikönyve leírja.[6] Jöhetnek ezek a szavak fordítva is, a NOT vagy a N'T hozzáfűzésével.[4] A Backtracking INTERCAL ezek mellett megengedi a MAYBE szó használatát, ami egy választási pontot vezet be.[9] Ez kombinálható a fenti lehetőségekkel. Az azonosító elé írható zárójelben egy sorszám; utána megadható, hogy a sor milyen százalékos eséllyel hajtódjon végre: ennek formája %50. Az alapértelmezett a 100%.[4]

Az INTERCAL-72-ben a fő vezérlési szerkezetek a NEXT, RESUME, és a FORGET. A DO (sor) NEXT leágazik a megadott sorra azzal, hogy megjegyzi azt a sort, ami végrehajtódott volna, ha nem lenne NEXT a hívási vermen. Itt a DO helyett más azonosítók is használhatók. A DO FORGET kifejezés eltávolítja a kifejezést a hívási veremből, aminek a kapacitása 80. Hasonlóan cselekszik a DO RESUME is, de utána visszaugrik az utoljára megjegyzett sorra.[4]

A C-INTERCAL tartalmazza a COME FROM utasítást is, aminek formája DO COME FROM (sor); a CLC-INTERCAL és az újabb C-INTERCAL számított COME FROMot (DO COME FROM kifejezés) is tartalmaz, meg a NEXT FROMot, ami ugyanaz, mint a COME FROM, de elmenti a visszatérési címet a NEXT STACKre.[3]

A COME FROM a GOTOhoz hasonlóan, de azzal ellentétes irányban működik. Amikor a vezérlés eléri a megadott címet, az adott sor végrehajtása után ide ugrik. Ha több COME FROM hivatkozza ugyanazt a sort, akkor az eredmény lehet érvénytelen, viselkedhet nem determinisztikusan, végrehajtódhat egymás után prioritás szerint, vagy végrehajtódhat párhuzamosan, mint a Threaded Intercalban.[10]

További működést befolyásoló tényezők az INTERCAL-72-ben az IGNORE és a REMEMBER változók kiiktatására és visszakapcsolására szolgálnak. Emiatt kimaradnak azok a sorok, ahol a változó szerepel. A sorok megjegyzésbe tételére és a megjegyzés végének jelzésére való az ABSTAIN és a REINSTATE, amelyek utasítástípusokra is vonatkozhatnak.[4]

Példaprogramok

Hello, world!

A hagyományos "Hello, world!" program C-ben:

#include <stdio.h>int main(){    printf("Hello, world!\n");    return 0;}

Ugyanez INTERCALban:

DO ,1 <- #13PLEASE DO ,1 SUB #1 <- #238DO ,1 SUB #2 <- #108DO ,1 SUB #3 <- #112DO ,1 SUB #4 <- #0DO ,1 SUB #5 <- #64DO ,1 SUB #6 <- #194DO ,1 SUB #7 <- #48PLEASE DO ,1 SUB #8 <- #22DO ,1 SUB #9 <- #248DO ,1 SUB #10 <- #168DO ,1 SUB #11 <- #24DO ,1 SUB #12 <- #16DO ,1 SUB #13 <- #162PLEASE READ OUT ,1PLEASE GIVE UP

ROT13-program

   (10) PLEASE DON'T GIVE UP   (1) DO .2 <- '?.1$#64'~'#0$#65535'   DO .2 <- '&"'.1~.2'~'"?'?.2~.2'$#32768"~"#0$#65535“'"$".2~.2“'~#1   DO .3 <- '?#91$.1'~'#0$#65535'   DO .3 <- '&"'#91~.3'~'"?'?.3~.3'$#32768"~"#0$#65535“'"$".3~.3“'~#1   DO (11) NEXT   DO (2) NEXT   DO (12) NEXT   (11) DO (13) NEXT   PLEASE FORGET #1   DO (12) NEXT   (13) DO (14) NEXT   PLEASE FORGET #2   DO (12) NEXT   (14) DO STASH .1   DO .1 <- .3   DO (1000) NEXT   DO .1 <- .3   DO .2 <- #1   PLEASE DO (1000) NEXT   DO RETRIEVE .1   PLEASE RESUME .3   (12) PLEASE FORGET #1   DO .2 <- '?.1$#96'~'#0$#65535'   DO .2 <- '&"'.1~.2'~'"?'?.2~.2'$#32768"~"#0$#65535“'"$".2~.2“'~#1   DO .3 <- '?#123$.1'~'#0$#65535'   DO .3 <- '&"'#123~.3'~'"?'?.3~.3'$#32768"~"#0$#65535“'"$".3~.3“'~#1   PLEASE DO (15) NEXT   PLEASE DO (3) NEXT   DO (16) NEXT   (15) DO (17) NEXT   PLEASE FORGET #1   DO (16) NEXT   (17) DO (18) NEXT   PLEASE FORGET #2   DO (16) NEXT   (18) PLEASE STASH .1   DO .1 <- .3   DO (1000) NEXT   DO .1 <- .3   DO .2 <- #1   DO (1000) NEXT   PLEASE RETRIEVE .1   PLEASE RESUME .3   (16) PLEASE FORGET #1   DO RESUME #1   (2) DO .2 <- #65   DO (1010) NEXT   PLEASE .1 <- .3   PLEASE .2 <- #13   DO (1000) NEXT   DO STASH .3   DO .1 <- .3   DO .2 <- #26   DO (1040) NEXT   DO .1 <- .3   DO (1030) NEXT   DO .2 <- .3   DO RETRIEVE .3   DO .1 <- .3   DO (1010) NEXT   DO .1 <- .3   DO .2 <- #65   DO (1000) NEXT   DO .1 <- .3   DO RESUME #1   (3) DO .2 <- #97   DO (1010) NEXT   DO .1 <- .3   DO .2 <- #13   DO (1000) NEXT   DO STASH .3   DO .1 <- .3   DO .2 <- #26   DO (1040) NEXT   DO .1 <- .3   DO (1030) NEXT   DO .2 <- .3   DO RETRIEVE .3   DO .1 <- .3   DO (1010) NEXT   DO .1 <- .3   DO .2 <- #97   DO (1000) NEXT   DO .1 <- .3   DO RESUME #1   DO COME FROM (10)   DO .4 <- #0   DO .5 <- #0   DO STASH .4 + .5   DO ,1 <- #1   DO COME FROM (33)   DO WRITE IN ,1   DO .1 <- ,1 SUB #1   DO (31) NEXT   PLEASE DO .6 <- #1   PLEASE DO (34) NEXT   (32) PLEASE RESUME '?.1$#256'~'#256$#256'   (31) DO (32) NEXT   DO FORGET #1   DO .6 <- #0   PLEASE DO (34) NEXT   (33) DON'T GIVE UP   (34) DO .6 <- "?!6'$#1"~#3   DO (40) NEXT   DO GIVE UP   (40) DO (41) NEXT   PLEASE FORGET #1   DO (42) NEXT   (41) DO RESUME .6   (42) DO FORGET #1   DO RETRIEVE .4   DO .2 <- .4   DO (1000) NEXT   DO .4 <- .3~#255   DO .3 <- .4   DO STASH .4   DO .1 <- .3   DO (1) NEXT   DO .3 <- !1~#15'$!1~#240'   DO .3 <- !3~#15'$!3~#240'   DO .2 <- !3~#15'$!3~#240'   DO .1 <- .5   DO (1010) NEXT   DO .5 <- .2   DO ,1 SUB #1 <- .3   PLEASE READ OUT ,1   PLEASE RESUME #1

Nyelvjárások

Az eredeti Woods–Lyon INTERCALban nagyon korlátozottak voltak a be- és a kiviteli eszközök: beolvasni csak angol számjegyeket tudott, kiírni csak kiterjesztett római számokat. Az Atari implementációról a manuál is megemlékezik, mint ami az eredetitől leginkább a kódolásban különbözik.[4]

Az interneten elérhető C-INTERCAL implementáció népszerűvé tette a nyelvet az ezoterikus programnyelvek iránt érdeklődők körében.[6] A C-INTERCAL bevezetett néhány új eszközt, mint a COME FROM utasítást, és a Turing-szövegmodellen alapuló be- és kivitelt.[6]

A C-INTERCAL implementálói alkották a TriINTERCALt, ami a hármas számrendszeren alapul, és általánosítja az operátorokat.[6]

Egy újabb variáns a Threaded Intercal, ami kiterjeszti a COME FROM funkcionalitását a többszálúság támogatására.[11]

Vélemények

Az "A Box, Darkly: Obfuscation, Weird Languages, and Code Aesthetics" cikk az INTERCALt az "Abandon all sanity, ye who enter here: INTERCAL" (INTERCAL: ki itt belépsz, hagyj fel minden épeszűséggel) kezdetű alcímmel vezeti be. A furcsa viselkedések között a nem kódszerű sorok kezeléséről is ír, ami kommentek készítésére is jó, csak arra kell vigyázni, nehogy kód kerüljön bele. Azonban megjegyzi azt is, hogy ezzel a módszerrel nehéz felderíteni a hibákat.[12]

Jegyzetek

Források

  • Oliver Lau, Hexenwerk - Ein Plädoyer für esoterische Programmiersprachen, c’t 22/07, S. 192-199 sowie c’t extra 02/09, S. 40-45.

Külső linkek

A Wikimédia Commons tartalmaz INTERCAL (programozási nyelv) témájú médiaállományokat.