Úvod |  |
Vítám všechny programátorské nadšence v kurzu jenž si bere za cíl sestavit funkční jednoduchý skriptovací jazyk. Obsah: Čeho se bude kurz týkat? Skriptovací jazyk Kompiler Virtální stroj Závěr ## Čeho se bude kurz týkat, co se naučíme? Vzali jsme si za úkol docela velké sousto. Vytvořit kompiler a virtální stroj pro skriptovací jazyk a všechno to optimalizovat a odladit není zrovna nic jednoduchého. Přesněji řečeno, pokud vás zajímá jestli je tvorba kompileru náročná na pochopení, tak možná budete rádi, že relativně těžké jsou jen první dvě nebo tři části. Tvorba kompileru je náročná spíše na čas který u toho strávíte. V tomto kurzu se budeme zabývat jen prvními dvěma problémy. To znamená, že optimalizaci a celkové odladění nechám na každém z vás. Konec konců optimalizace a ladění není cílem kurzu. Cílem kurzu je napsat kompiler a virtuální stroj. Možná by teď nebylo mimo mísu ujasnit si co to vlastně kompiler a virtuální stroj je. Pokud teď čekáte nějakou učebnicovou definici, tak vás zklamu. Jednak si zrovna žádnou nevybavuji a za druhé nemám rád tyhle definice kterým rozumí jen zasvěcení lidé. Kompiler (minimálně v našem případě) je prográmek, který má za úkol vzít zdrojový kód nějakého jazyka, zkontrolovat kód jestli neobsahuje chyby a převést ho do nějaké formy které bude počítač lépe rozumět. Většinou do podoby nějakých jednoduchých bytových instrukcí, které jsou pro dnešní počítače, které znají akorát byty a jejich násobky a dvojková čísla, srozumitelnější než nějaký text. Virtální stroj je zase takový počítač uvnitř počítače. U skriptovacích jazyků není výsledkem kompilace takový kód, který bychom mohli přímo spustit na počítači jako nějaký spustitelný soubor nebo program. Výstupem je a bude to tak i u nás nějaká reprezentace skriptu v podobě posloupnosti jednoduchých instrukcí, které ale nejsou stejné jako ty kterým rozumí počítač. Takže potřebujeme něco co postavíme mezi skript a počítač, něco co bude schopno vykonávat náš skript. To je právě virtuální stroj - Virtual Machine. V současné době se skriptovací jazyky a virtální stroje používají čím dál tím více. Virtuální stroje zmenšují sémantickou mezeru mezi hardwarem a softwarem a rychlost dnešního železa je již taková, že použití virtuálního stroje není mnohokrát už ani citelným zpomalenim aplikace. Samozřejmě že instrukce skriptovacího jazyka jsou zase emulovány nějakým kódem, většinou napsaným v nějkém vyšším programovacím jazyce a konstrukce tohoto vyššího programovacího jazyka jsou zase rozebrány na několik strojových instrukcí. Je to jako když si koupíte kuře. Taky nepřemýšlíte o tom že ho 40 dnů krmili, zabili, zabalili, přivezli do obchodu. Ale podvědomně víte že se to děje. Pod lupou se na kompiler i virtální stroj podíváme dále během kurzu. Obsah ## Skriptovací jazyk Než vůbec začneme vytvářet skriptovací jazyk, měli bychom si říct co od něj chceme. Jakou má mít syntaxi, jaké budeme chtít používat operátory, jaké datové typy? Skriptovací jazyky se většinou dělají na míru projektu pro který jsou použity. Hlavní oblastí kde jsem chtěl použít svůj skriptovací byl herní engine. V enginu bych ale potřeboval volat funkce kterými engine disponuje a pracovat s proměnnými enginu. Jak zjistíte v dalších částech, budeme muset definovat každý symbol, který se může ve skriptu vyskytnout. Kdybychom takto museli definovat každou funkci z enginu, museli bychom pro každou hru nebo projekt kde chceme jazyk použít kompiler znova přepracovat. To ale nezní moc lákavě ani optimálně. Bylo by lepší zavést nějaké klíčové slovo po definování odkazů na externí funkce a ty pak volat. Pak bude jazyk více univerzální. To je jedna z možností Universal Scripting Language, který v tomto kurzu vytvoříme. Abychom se nemuseli zbytečně učit nějaká nová syntaktická pravidla, řekněme, že jazyk bude mít stejnou syntaxi jako jazyk C. Dále budeme podporovat větvení, cykly, komentáře. Ve skriptu budeme moci definovat funkce a volat je, přičemž definice funkce může být až za místem kde ji voláme. Také by se asi mohly hodit standartní aritmetické a logické operace a porovnávání. Zde tedy přehled možností jazyka: Skript se sestává z funkcí Funkce rozdělíme na lokální a externí. Lokální funkce budou napsány ve skriptu. Externí funkce se musí nacházet v dynamické knihovně .dll. Podporujeme také přetěžování funkcí a rekurzi. Tři základní datové typy. Integer (celé číslo), Double (Číslo s dvojitou přesností) a String (Řetězec). Boolean bude automaticky převeden na Integer. Typ proměnné nebude zadáván. Do proměnné můžeme uložit jakákoliv data která jsou jednoho ze tří jmenovaným typů. Syntaxe stejná jako u jazyka C Podpora větvení if, if-else Podpora cyklů for, while Aritmetické operátory + - / * % (sčítání, odčítání, dělení, násobení, zbytek po celočíselném dělení) Bitové operátory & | << >> (and, or, bitový posun vlevo, bitový posun vpravo) Logické operátory && || (logický součin, logický součet) Operátory přiřazení = += -= *= /= %= <<= >>= &= |= Porovnávací operátory == != <= >= To jsou snad všechny možnosti vytvářeného skriptovacího jazyka. Je asi na čase ukázat si příklad skriptu. A protože pokaždé když začínáme nový jazyk, je tradicí říct "Ahoj světe". Skript udělá právě to. `/*****************************Ahoj svete****************************************/ //Definice externi funkce. MessageBox slouzi pro zobrazeni dialogoveho okna s textem a nachazi se v knihovne user32.dll. int rika ze funkce vrati hodnotu typu int. Vice o tomto problemu v dalsich castech kurzu. Cislo 4 v zavorce udava ze funkce ma 4 parametry. extern <"user32.dll"> int MessageBoxA(4) main() { /*To ze pro kazdy parametr definuji promennou neznamena, ze by nebylo podporovano prime zadani parametru do funkce. Chci jen ukazat definici promennych u nichz se nezadava datovy typ*/ Nula = 0; //Promenna je typ jakykoliv a je v ni ulozen integer. V tomto miste se symbol Nula objevuje poprve. Jedna se tedy o deklaraci promenne a jeji inicializaci na 0. Zprava = "Ahoj světe";//Promenna je typ jakykoliv a je v ni ulozen retezec. Titulek = "Universal Script Language"; Nula = 0; //Symbol Nula se uz vyskytuje nahore, kde je deklarovan. Zde tedy nejde o deklaraci promenne ale o prirazeni hodnoty. MessageBoxA(Nula,Zprava,Titulek,Nula); }` Obsah ## Kompiler V úvodu už jsem zhruba řekl co to vlastně kompiler je, nebo co má dělat a bude dělat i v našem případě. No jo, ono ale to zkontrolování napsaného skriptu a jeho převedení do nějaké počítači srozumitelnější formy není zase tak jednoduché. Takový skript nebo program který vypadá například jako příklad Ahoj světe výše, ale počítači asi moc neřekne, že? Pro něj to je jen nějaká posloupnost znaků, a co jako s tím? Navíc je navyklý jen na násobky bajtů a takovýhle skript se mu vůbec nebude líbit. První věcí kterou tedy musíme udělat, abychom mohli skript strojivě zpracovat, je to, že ho převedeme na čísla. Přesněji řečeno do reprezentace pomocí datových struktur, které už jsou pro zpracování přijatelnější. Tyto struktury budou uspořádány do jakéhosi stromu. Přeci jenom kdybychom měli struktury nepospojované, nevěděli bychom co kam patří. V dalších částech kompileru, říkejme těm částem moduly, bude nutné odchytit například do očí bijící pokusy o dělení nulou, zjistit jestli funkce vrací hodnotu. Také budeme muset kontrolovat zda volané funkce jsou deklarované, kontrolovat deklarace proměnných. I když kontrola deklarací proměnných v našem případě bude spíš znamenat automaticky nadeklarovat proměnnou pokud deklarovaná není. Více o tom později. Kdybychom vytvářeli jazyk kde se musí zadat datový typ proměnné, museli bychom také napsat modul který by kontroloval správnost výrazů. Třeba jestli se nepokoušíte do proměnné typu integer přiřadit řetězec znaků nebo jiné chyby. Tento modul by také vkládal implicitní datové konverze tam kde je to možné. Protože ale náš jazyk nepotřebuje znát datový typ proměnné, do našich proměnných si můžete uložit co chcete (z toho co jsem jmenoval ve výpisu :)), tento modul budeme mít mnohem jednodušší. Budeme v něm jen kontrolovat zda se v nějakém výrazu nenachází volání funkce která nevrací hodnotu.. Tím vlastně skončíme front-end část kompileru, která kontroluje vstupní soubor. Druhou částí kompileru, která se bude skládat také z více modulů, je back-end část, která se stará o vygenerování výstupního souboru. Bude tam nějaký modul, který převede skript do podoby sekvence instrukcí v assembleru který si pro jazyk nadefinujeme a tuto sekvenci pak další modul převede na binární soubor. Kompiler pro tento kurz Obsah ## Virtální stroj Virtuální stroj je tedy jakýsi virtuální počítač, který provádí kód skriptů skriptovacího jazyka. Bez virtuálního je stroje soubor skriptu jen nějakou posloupností bytů, které nám nic neřeknou. Virtální stroj je vcelku jednoduchá mechanicky pracující záležitost. Jde jen o to načíst skript, kódový segment a pak postupně dekódovat jednotlivé instrukce a provádět operace které jim přísluší. Virtální stroj pro tento kurz Obsah ## Závěr Výhodou skriptovacích jazyků je že se s nimi dá jednak rozšířit funkčnost a prodloužit životnost a použitelnost programů a her a jednak že jsou univerzální. Tím myslím, že je můžete spustit na jakémkoliv systému pokud máte virtální stroj pro daný systém. Stačí abych měl kompiler svého jazyka pod Windows a zkompiloval v něm nějaké skripty. Pak pokud mám virtální stroj pro Windows, můžu skripty spouštět pod Windows. Pokud mám také virtuální stroj pro Linux, mohu ten samý skript co jsem zkompiloval pod Windows spustit i pod Linuxem a stejně tak v dalších systémech, pokud pro ně mám virtuální stroj. Nemusím skript nijak kompilovat pro každý systém zvlášť nebo ho nějak přizpůsobovat pro daný systém. Nevávýhodou je zpomalení které vzniká vlivem emulace instrukcí skriptovacího jazyka většinou víceřádkovým kódem v nějakém vyšším programovacím jazyce. Toto zpomalení však bývá dnes už zanedbatelné a většinou ho ani nepostřehnete. Ve skriptovacím jazyce nebo jazyce, který běží na visrtálním stroji, samozřejmě také nejde programovat vše. Jedno z plusů je také to, že takovéto jazyky se většinou dělají lépe srozumitelnější pro laiky, lépe se učí a je v nich větší volnost než třeba v C nebo v C++. To je třeba zmiňovaná absence zadávaání datových typů u proměnných kterou budeme implementovat také. To dává větší volnost programátorům nebo lidem kteří budou v našem jazyce psát skripty. Obsah