autor: Vladimír Vérosta,
ROZPRACOVÁNO - !!!
Jak to bylo s Cobolem
Nevím jak je tomu dnes, ale v nedávné době byl programovací jazyk Cobol nejrozšířenějším jazykem používaným na symbolický zápis potřebných algoritmů, v dnešní době mu jistě šlape na paty Java, pokud ho už nepředhonila.
V roce 1972, jsem se jako softwarový elév s roční praxí, octl v podniku Kancelářské strojena pozici vývojáře softwaru. Rozbíhal se vývoj a konstrukce počítačové řady JSEP a měl jsem to štěstí, že jsem se octl v týmu, který dostal za úkol pro tuto řadu vytvořit překladač z jazyka Cobol.
Měli jsem mlhavou představu, co asi má dělat ten "překladač" nebo taky "kompilátor", ale nevěděli jsme co je to ten Cobol, a o tom jak by měl překladač pracovat ani páru. Od našeho patrona, pražského VÚMSu se nám sice dostalo jakési technické ale spíš jenom morální podpory ale stejně jsme byli v pozici stavitele "na zelené louce", který navíc nemá žádnou projektovou dokumentaci, neměli jsme takříkajíc ani čárku.
Takže nastudovat jazyk Cobol, jeho syntaxi a sémantiku, pochopit strukturu jednotlivých oddílů a vztahů mezi nimi, něco si přečíst o možné konstrukci samotného kompilátoru, k tomu.
Technologické prostředí bylo dáno, počítač EC 1021 vybavený vnitřní pamětí 64 kB, ano, pouhých 65536 bajtů, z nichž navíc část zabíral operační systém, pro aplikace tedy zbývalo nějakých 48 kB. Diskové paměti tehdy disponovaly vyměnitelnýmí médii o kapacitě 7,25 Mb, sedm a čtvrt milionu bajtů. Dneska by se tam nevlezla pomalu ani jedna fotka z digitálního fotoaparátu!
Dále, v té době ještě nebyl vynalezen jazyk C natož Java, jako vývojový "jazyk" jsme měli za řeholi používat Assembler, který jakž takž umožňoval pracovat se symbolickým zápisem, operační kódy jednotlivých instrukcí byly mnemonické, pevně dané výrobcem. Jednoduchý příklad, přesun části obsahu paměti zmísta symbolicky označeného jako ARGUMENT do místa označeného jako TEXT se dal provést např. symbolickou instrukcí:
MVC TEXT(32),ARGUMENT
Délka přesunovaného textu byla rovna 32 bajtům. O assmbleru ibm/360 a jeho následovnících se ovšem dají na webu najít spousty příruček, příkladů, návodů, ostatně moderní počítače IBM třídy mainframe disponují Assemblerem, či strojovým kódem, chcete-li, který vychází z onoho prapůvodního pradědečka ibm/360.
Tedy, nástroj pro programování byl jen těsně nad úrovní strojového kódu a velmi, velmi daleko od symbolické úrovně překladače, ve které jsme se byli nuceni pohybovat. Co teď?
Z pražského VÚMSu nás podpořili dodáním několika manuálů týkajících se jazyka Cobol na počítačích Siemens, tedy počítačích zhruba stejné váhové kategorie jako ibm/360 či budované řady JSEP. A paradoxně nejlepším vodítkem pro začátek byl nepříliš tlustý sešitek obsahující seznam chyb hlášených při překladu. S pověstnou německou důkladností byly chybové hlášky seřazeny v logickém pořadí, ze které bylo možné pochopit proces syntaktické kontroly prováděné při vstupu programy napsaného v Cobolu. Nevěříte? Nevím to už úplně přesně, ale začínalo to asi takhle:
0001 První slovo programu není klíčové slovo IDENTIFICATION.
0002 Za slovem IDENTIFICATION není uvedeno DIVISION.
0003 Za záhlavím IDENTTIFICATION DIVISION není tečka.
Jako jeden z prvních se tedy psal assemblerovský modul na čtení vstupního textu a jeho rozčlenění do jednotlivých slov, uměl určit typ čteného slova, počet znaků textu, který jej tvořil a rovněž si všímal, jakým znakem, tzv. oddělovačem, je slovo ukončeno, odděleno od textu následujícího. Např podstatné je, zda je slovo ukončeno mezerou nebo některým z významných oddělovačů, jakými jsou třeba tečka nebo čárka.
Záhlaví každého cobolského programu v té době začínalo takhle:
IDENTIFICATION DIVISION.
PROGRAM-ID. PRIKLAD.
.....
V podobném stylu se pak analogicky nesla syntaktická kontrola ostatních částí cobolského zdrojového programu. Protože každý ze čtyř oddílů cobolského programu, IDENTIFICATION, ENVIRONMENT, DATA a PROCEDURE má svoji specifickou funkci a typickou strukturu, byla každá z nich řešena samostatnými programovými jednotkami. V podstatě vše, co nějakým způsobem přinášelo důležité informace, deklarace s významem pro chod programu se v průběhu prvního průchodu zdrojovým textem zapamatovalo, uložilo ve formě tabulek, seznamů ve vnitřní paměti. Specifický je poslední oddíl PROCEDURE, kvůli kterému se všechny deklarace předem podnikaly, oddíl PROCEDURE vcházel do hry s kompletní připravenou sadou deklarací potřebných souborů a struktur dat.
Oddíl PROCEDURE rovněž procházel stádiem prvotní syntaktické analýzy, avšak syntaktické konstrukce, které se poznaly jako správné se průběžně převáděly do tzv. mezijazyka, který se musel zapisovat do pracovní oblasti na disk, protože paměti bylo málo a oddíly PROCEDURE bývaly velmi rozsáhlé. V mezijazyku byl kódován typ příkazu, slovesa, jeho operandy se převzaly z deklarací předchozích oddílů programu uložených už v paměti a postupně zapisovaly na disk.
Jakmile se takhle v tzv. prvním chodu překladače zpracovaly všechny příkazy oddílu PROCEDURE, tak se zkoumalo, zda náhodou nedošlo k chybě 3. úrovně. Ta by znamenala (nepovedený) konec překladu už v tuto chvíli. Pokud byl program v pořádku, následoval tzv. druhý chod překladače, který se ovšem týkal jenom oddílu PROCEDURE. Takřka veškeré rutiny používané v prvním chodu se zahodily a na jejich místo se do paměti uložily rutiny pro druhý průchod.
Druhý průchod překladače začal znovu číst mezijazyk uložený na disku v prvním průchodu, každý z vyskytujících se typů příkazů byl překládán vlastní rutinou, tyhle rutiny vyvolával dispečer druhého průchodu. Jakmile byla rutina s překladem svého materiálu hotova vrátila řízení opět onomu dispečerovi. A ten vyvolal další, atd.
Co to znamená, přeložit v druhém průchodu nějaký příkaz? Někdy to bylo docela jednoduché, syntaxe cobolského slovesa byla jednoduchá a přímočará, na provedení požadované funkčnosti stačila jedna kostra skupiny (a teď pozor) strojových instrukcí počítače, do které se doplnily většinou jenom adresy proměnných vstupujících do hry.
Ovšem onu kostru strojových instrukcí bylo nutno předem odpovědně vymyslet, zasadit do rámce celkové koncepce určující, jak budou přeložené programy vypadat, probrat případně s týmem a vyzkoušet. Tyhle tzv. kostry se zapisovaly jako běžné části programu v assembleru, tam kde obvykle deklarujeme konstanty a pracovní pole, do kostry se na příslušná místa do strojových instrukcí nejprve doplnily adresy operandů a takto pro konkrétní případ připravený kousek kódu se zapsal na výstup. Při tomhle generování kódu byli vývojáři vystavení schizofrenii, díky které občas v mysli omylem zaměnili posloupnosti instrukcí prováděné v době překladu s instrukcemi určenými pro spuštění později, až v době běhu přeloženého programu. Jednalo se totiž o typově i formátově shodné instrukce určené pro tentýž výpočetní stroj a ve chvílích přepracování, únavy, nepozornosti se stalo, že vývojář řešil nějaký problém zásahem do opačného balíčku instrukcí.
Přeložený program byl vlastně systémově definovaný soubor typu cílový modul (objekt) a jeho struktura byla shodná bez ohledu na to, z jakého zdrojového jazyka překlad probíhal. Tím se třeba umožnilo sestavit výsledný spustitelný program, tzv. fázi z cílových modulů, pocházejících z překladačů různých typů. Nicméně si myslím, že tahle možnost v praxi nikdy nedoznala nějakého významného uplatnění.
Tak dobře, máme cílový modul vzniklý překladem a co dál? Tohle ještě není forma, která se dá na počítači provozovat. Z více důvodů. Za prvé je potřeba upravit formát tak, aby vyhovoval pořadavkům kladeným na spustitelný tvar a za druhé, prakticky každý přeložený program obsahuje odkazy do předem připravených knihoven nejčastěji systémových, které pro programy zajišťují provedení systémových funkcí.
V případě cobolského programu se jednalo o odkazy do runtime knihovny, kterou jsme připravili speciálně pro potřeby běhu cobolských programů, ukázalo se jako jednodušší a nakonec i vůbec možné, určité cobolské příkazy (a že jich nebylo málo!) překládat tak, že ona kostra na překlad daného příkazu ve vhodném místě obsahovala volání příslušného cobolského podprogramu. Jména podprogramů vždy začínala znaky ICB. Tímto způsobem se elegantně vyřešily implementační potíže, které by bez použití knihovny bylo sotva možné překonat!
Proces, kdy se k přeloženému modulu připojují odkazované knihovny se říká spojování, linkování (z angl. link), externí odkazy se za běhu spojovacího programu postupně vyřizují až nakonec vznikne spustitelný program. Mohlo se stát, že některý odkaz se spojovacímu programu nepodařilo vyřešit, informace o tom pak byla vidět v protokolu. Buď bylo jméno odkazu chybné nebo bylo potřeba chybějící materiál ke spojování nějak doplnit.
Spustitelný program už "neví", že jeho funkčnost pochází z nějakého symbolického zápisu v Cobolu, obsahuje už pouze binární strojový kód, kterému na druhé straně rozumí CPU stroje, na kterém poběží. A přesně tohle je potřeba! :-)
Tento první z našich kompilátorů Cobol byl určen pro počítač EC1021 vybavený operačním systémem MOS, jehož vývoj měl na starosti pražský Výzkumný ústav matematických strojů (VÚMS), kompilátor Cobolu se používal jak v tehdejším Československu, tak ve státech bývalého RVHP.
Po takto nabytých zkušenostech jsme dostali zakázky i na další kompilátory Cobolu třeba pro počítač EC1027. Ale to už se na obzoru začínala objevovat první pécéčka, a s nimi i jazyk C, dále i operační systém Unix, takže poslední zadání kompilátoru Cobol bylo současně pro operační systémy MS-DOS a Unix. Překládací paradigma se změnilo, jako vývojový jazyk jsme používali jazyk C (jaký to pokrok vůči assembleru!) a výsledný produkt byl zdrojový text rovněž v jazyce C. Počítalo se oprávněně s tím, že na cílových platformách bude k dispozici kompilátor jazyka C, pomocí kterého se dosáhne až do spustitelného tvaru programu. Tímhle se časově dostáváme ke konci 80. let, revoluční změny ve společnosti tuhle naši cobolskou epochu ukončují, státní programy nejsou, VÚMS se mění, tým cobolistů se poohlíží po lepším živobytí, ale jsme rádi za to, co jsme mohli prožít a vyzkoušet si a dokázat udělat!! :-)