Název tohoto článku odráží otázku, kterou slýchám stále dokola na fórech nebo v e-mailech, které dostávám.
Myslím, že všichni zvídaví vývojáři si ji alespoň jednou položili. Je normální být fascinován tím, jak programovací jazyky fungují. Bohužel většina odpovědí, které čteme, je velmi akademická nebo teoretická. Některé další obsahují příliš mnoho implementačních detailů. Po jejich přečtení nás stále zajímá, jak věci fungují v praxi.
Odpovíme si tedy na ni. Ano, podíváme se, jaký je postup pro vytvoření vlastního plnohodnotného jazyka s kompilátorem pro něj a co všechno.
Přehled
Většina osob, které se chtějí naučit, jak „vytvořit programovací jazyk“, ve skutečnosti hledá informace o tom, jak vytvořit kompilátor. Chtějí porozumět mechanice, která umožňuje provádět nový programovací jazyk.
Kompilátor je základním dílem skládačky, ale vytvoření nového programovacího jazyka vyžaduje více než to:
1) Jazyk musí být navržen: Tvůrce jazyka musí učinit několik zásadních rozhodnutí o paradigmatech, která se mají použít, a o syntaxi jazyka
2) Musí být vytvořen překladač
3) Musí být implementována standardní knihovna
4) Musí být poskytnuty podpůrné nástroje, jako jsou editory a sestavovací systémy
Podívejme se podrobněji, co každý z těchto bodů obnáší.
Návrh programovacího jazyka
Pokud chcete pouze napsat vlastní překladač, abyste se naučili, jak tyto věci fungují, můžete tuto fázi přeskočit. Můžete prostě vzít podmnožinu existujícího jazyka nebo vymyslet jeho jednoduchou variantu a začít. Pokud však máte v plánu vytvořit vlastní programovací jazyk, budete se nad tím muset zamyslet.
Podle mého názoru se návrh programovacího jazyka dělí na dvě fáze:
- Fáze velké představy
- Fáze zpřesňování
V první fázi si zodpovíme základní otázky týkající se našeho jazyka.
- Jaké paradigma provádění chceme použít? Bude imperativní nebo funkcionální? Nebo snad založený na stavových strojích či obchodních pravidlech?
- Chceme statické nebo dynamické typování?
- Jaký druh programů bude tento jazyk nejlépe zvládat? Bude se používat pro malé skripty nebo velké systémy?
- Na čem nám záleží nejvíce: na výkonu? Čitelnost?
- Chceme, aby byl podobný nějakému existujícímu programovacímu jazyku? Bude zaměřen na vývojáře v jazyce C, nebo se ho snadno naučí ten, kdo přichází z Pythonu?
- Chceme, aby fungoval na konkrétní platformě (JVM, CLR)?
- Jaké možnosti metaprogramování chceme podporovat, pokud vůbec nějaké? Makra? Šablony? Reflexe?
V druhé fázi budeme jazyk vyvíjet podle toho, jak ho budeme používat. Budeme narážet na problémy, na věci, které je velmi obtížné nebo nemožné vyjádřit v našem jazyce, a nakonec ho budeme vyvíjet. Druhá fáze možná není tak okouzlující jako ta první, ale je to fáze, ve které náš jazyk neustále ladíme, aby byl použitelný v praxi, takže bychom ji neměli podceňovat.
Stavba překladače
Stavba překladače je nejzajímavějším krokem při vytváření programovacího jazyka. Jakmile máme kompilátor, můžeme náš jazyk skutečně přivést k životu. Kompilátor nám umožňuje začít si s jazykem hrát, používat ho a zjistit, co nám v původním návrhu chybí. Umožňuje vidět první výsledky. Je těžké překonat radost ze spuštění prvního programu napsaného v našem zbrusu novém programovacím jazyce, bez ohledu na to, jak jednoduchý tento program může být.
Ale jak sestavíme překladač?
Jako všechno složité to děláme v několika krocích:
- Sestavíme parser: Parser je část našeho překladače, která vezme text našich programů a pochopí, jaké příkazy vyjadřují. Rozpoznává výrazy, příkazy, třídy a vytváří vnitřní datové struktury, které je reprezentují. Zbytek parseru bude pracovat s těmito datovými strukturami, nikoli s původním textem
- (nepovinné) Parsovací strom převedeme na abstraktní syntaktický strom. Datové struktury vytvořené parserem jsou obvykle poněkud nízkoúrovňové, protože obsahují mnoho detailů, které nejsou pro náš překladač rozhodující. Z tohoto důvodu chceme často datové struktury přeuspořádat do něčeho o něco vyšší úrovně
- Překládáme symboly. V kódu píšeme věci jako
a + 1
. Náš překladač musí zjistit, čeho sea
týká. Je to pole? Je to proměnná? Je to parametr metody? Zkoumáme kód, abychom na to odpověděli - Ověřujeme strom. Potřebujeme zkontrolovat, zda se programátor nedopustil chyby. Snaží se sečíst boolean a int? Nebo přistupuje k neexistujícímu poli? Potřebujeme vytvořit příslušné chybové hlášení
- Vygenerujeme strojový kód. V tomto okamžiku převedeme kód do něčeho, co stroj dokáže vykonat. Může to být správný strojový kód nebo bajtový kód pro nějaký virtuální stroj
- (nepovinné) Provedeme propojení. V některých případech musíme spojit strojový kód vytvořený pro naše programy s kódem statických knihoven, které chceme zahrnout, abychom vytvořili jediný spustitelný soubor
Potřebujeme vždy překladač? Ne. Můžeme ho nahradit jinými prostředky k provádění kódu:
- Můžeme napsat překladač: překladač je v podstatě program, který provede kroky 1-4 překladače a pak přímo provede to, co je určeno abstraktním syntaktickým stromem
- Můžeme napsat transpiler: transpiler provede to, co je specifikováno v krocích 1-4, a pak vypíše kód v nějakém jazyce, pro který již máme kompilátor (například C++ nebo Java)
Tyto dvě alternativy jsou zcela platné a často má smysl zvolit jednu z nich, protože potřebné úsilí je obvykle menší.
Napsali jsme článek, který vysvětluje, jak napsat transpiler. Podívejte se na něj, pokud chcete vidět praktický příklad i s kódem.
V tomto článku podrobněji vysvětlíme rozdíl mezi překladačem a interpretem.
Standardní knihovna pro váš programovací jazyk
Každý programovací jazyk musí umět několik věcí:
- Tisk na obrazovku
- Přístup k souborovému systému
- Využívání síťových připojení
- Vytváření grafických uživatelských rozhraní
To jsou základní funkce pro interakci se zbytkem systému. Bez nich je jazyk v podstatě k ničemu. Jak tyto funkcionality zajistíme? Vytvořením standardní knihovny. Bude to sada funkcí nebo tříd, které lze volat v programech napsaných v našem programovacím jazyce, ale které budou napsány v nějakém jiném jazyce. Mnoho jazyků má například standardní knihovny napsané alespoň částečně v jazyce C.
Standardní knihovna pak může obsahovat mnohem více. Například třídy pro reprezentaci hlavních kolekcí, jako jsou seznamy a mapy, nebo pro zpracování běžných formátů, jako je JSON nebo XML. Často bude obsahovat i pokročilé funkce pro zpracování řetězců a regulárních výrazů.
Jinými slovy, napsat standardní knihovnu je spousta práce. Není to nijak okouzlující, není to koncepčně tak zajímavé jako psaní překladače, ale přesto je to základní součást, aby byl programovací jazyk životaschopný.
Existují způsoby, jak se tomuto požadavku vyhnout. Jedním z nich je zajistit, aby jazyk běžel na nějaké platformě, a umožnit opětovné použití standardní knihovny jiného jazyka. Například všechny jazyky běžící na JVM mohou jednoduše znovu použít standardní knihovnu Javy.
Podpůrné nástroje pro nový programovací jazyk
Aby byl jazyk použitelný v praxi, často potřebujeme napsat několik podpůrných nástrojů.
Nejzřejmější je editor. Specializovaný editor se zvýrazňováním syntaxe, inline kontrolou chyb a automatickým doplňováním je dnes nutností, aby byl každý vývojář produktivní.
Ale dnes jsou vývojáři zhýčkaní a budou očekávat nejrůznější další podpůrné nástroje. Například debugger může být opravdu užitečný při řešení nepříjemné chyby. Nebo systém sestavování podobný Mavenu nebo Gradle by mohl být něčím, co budou uživatelé později požadovat.
Na úplném začátku by mohl stačit editor, ale jak bude uživatelská základna růst, poroste i složitost projektů a bude potřeba více podpůrných nástrojů. Doufejme, že v té době bude existovat komunita ochotná pomoci s jejich vytvářením.
Shrnutí
Vytváření programovacího jazyka je proces, který se mnoha vývojářům zdá záhadný. V tomto článku jsme se pokusili ukázat, že je to jen proces. Je fascinující a není snadný, ale dá se zvládnout.
Možná chcete vytvořit programovací jazyk z různých důvodů. Jedním z dobrých důvodů je zábava, dalším důvodem je naučit se, jak fungují překladače. Váš jazyk může být nakonec velmi užitečný nebo ne, záleží na mnoha faktorech. Pokud se však při jeho vytváření budete bavit a/nebo učit, pak se vyplatí investovat do něj nějaký čas.
A samozřejmě se budete moci pochlubit svým kolegům vývojářům.
Pokud se chcete o vytváření jazyka dozvědět více, podívejte se na další zdroje, které jsme vytvořili: Naučte se vytvářet jazyky.
Mohou vás také zajímat některé naše články: