Programovanie

Zostavte tlmočníka v jazyku Java - implementujte vykonávací modul

Predchádzajúca 1 2 3 Strana 2 Ďalšia Strana 2 z 3

Ostatné aspekty: Struny a polia

Tlmočník COCOA implementuje dve ďalšie časti jazyka BASIC: reťazce a polia. Najprv sa pozrime na implementáciu reťazcov.

Ak chcete implementovať reťazce ako premenné, Vyjadrenie trieda bola upravená tak, aby obsahovala pojem výrazov „reťazec“. Táto úprava mala formu dvoch dodatkov: isString a hodnota reťazca. Zdroj týchto dvoch nových metód je uvedený nižšie.

 String stringValue (Program pgm) hodí BASICRuntimeError {hodiť nové BASICRuntimeError ("Pre toto nie je reprezentácia reťazca."); } boolean isString () {return false; } 

Je zrejmé, že pre program BASIC nie je príliš užitočné získať hodnotu reťazca základného výrazu (ktorý je vždy buď číselný alebo boolovský výraz). Z nedostatku užitočnosti by ste mohli usúdiť, že do týchto metód potom nepatrili Vyjadrenie a patrili do podtriedy Vyjadrenie namiesto toho. Umiestnením týchto dvoch metód do základnej triedy však všetky Vyjadrenie objekty môžu byť testované a zisťuje sa, či v skutočnosti sú to reťazce.

Ďalším dizajnovým prístupom je vrátenie číselných hodnôt ako reťazcov pomocou a StringBuffer objekt na vygenerovanie hodnoty. Napríklad by sa dal prepísať rovnaký kód ako:

 String stringValue (Program pgm) hodí BASICRuntimeError {StringBuffer sb = nový StringBuffer (); sb.append (this.value (pgm)); vrátiť sb.toString (); } 

A ak sa použije vyššie uvedený kód, môžete jeho použitie vylúčiť isString pretože každý výraz môže vrátiť hodnotu reťazca. Ďalej môžete upraviť hodnotu metóda, ktorá sa pokúsi vrátiť číslo, ak sa výraz vyhodnotí ako reťazec, a to tak, že ho prevedie cez hodnota metóda java.lang.Double. V mnohých jazykoch, ako sú Perl, TCL a REXX, sa tento druh amorfného písania používa s veľkou výhodou. Oba prístupy sú platné a mali by ste sa rozhodnúť na základe návrhu tlmočníka. V jazyku BASIC musí tlmočník vrátiť chybu, keď je reťazec priradený k číselnej premennej, takže som zvolil prvý prístup (vrátenie chyby).

Pokiaľ ide o polia, existuje niekoľko spôsobov, ako môžete navrhnúť svoj jazyk tak, aby ich interpretoval. C používa hranaté zátvorky okolo prvkov poľa na rozlíšenie referencií indexu poľa od odkazov na funkcie, ktoré majú okolo argumentov zátvorky. Avšak jazykoví návrhári jazyka BASIC sa rozhodli použiť zátvorky pre funkcie aj polia, takže pri texte NÁZOV (V1, V2) je videný parserom, môže to byť buď volanie funkcie, alebo odkaz na pole.

Lexikálny analyzátor rozlišuje medzi tokenmi, za ktorými nasledujú zátvorky, a to najskôr za predpokladu, že sú funkciami a testujú ich. Potom sa ďalej zisťuje, či ide o kľúčové slová alebo premenné. Práve toto rozhodnutie zabráni vášmu programu v definovaní premennej s názvom „SIN“. Akákoľvek premenná, ktorej názov sa zhodoval s názvom funkcie, by namiesto toho vrátil lexikálny analyzátor ako token funkcie. Druhým trikom, ktorý lexikálny analyzátor používa, je skontrolovať, či za názvom premennej bezprostredne nasleduje `('. Ak je, analyzátor predpokladá, že ide o referenciu poľa. Jeho analýzou v lexikálnom analyzátore odstránime reťazec`MYARRAY (2)'z interpretácie ako platného poľa (všimnite si medzeru medzi názvom premennej a otvorenou zátvorkou).

Posledný trik s implementáciou polí je v Variabilné trieda. Táto trieda sa používa pre inštanciu premennej a ako som už hovoril v stĺpci z minulého mesiaca, ide o podtriedu Token. Má však aj nejaké mechanizmy na podporu polí, a to ukážem nižšie:

trieda Premenná rozširuje token {// Legálne premenné podtypy final static int ČÍSLO = 0; konečný statický int STRING = 1; konečný statický int NUMBER_ARRAY = 2; konečný statický int STRING_ARRAY = 4; Názov reťazca; int subType; / * * Ak je premenná v tabuľke symbolov, tieto hodnoty sú * inicializované. * / int ndx []; // indexy polí. int mult []; // multiplikátory polí dvojnásobné nArrayValues ​​[]; Reťazec sArrayValues ​​[]; 

Vyššie uvedený kód zobrazuje inštančné premenné spojené s premennou, ako v Konštantný výraz trieda. Je potrebné zvoliť si počet tried, ktoré sa majú použiť, a zložitosť triedy. Jednou z dizajnérskych možností môže byť stavba a Variabilné trieda, ktorá obsahuje iba skalárne premenné a potom pridá znak ArrayVariable podtrieda na riešenie zložitosti polí. Rozhodol som sa ich skombinovať a premeniť skalárne premenné v podstate na polia dĺžky 1.

Ak si prečítate vyššie uvedený kód, uvidíte indexy polí a multiplikátory. Sú tu preto, lebo multidimenzionálne polia v BASICe sú implementované pomocou jediného lineárneho poľa Java. Lineárny index do poľa Java sa počíta ručne pomocou prvkov poľa multiplikátora. Platnosť indexov použitých v programe BASIC sa kontroluje porovnaním s maximálnym legálnym indexom v indexoch. ndx pole.

Napríklad pole BASIC s tromi rozmermi 10, 10 a 8 bude mať hodnoty 10, 10 a 8 uložené v ndx. Toto umožňuje hodnotiacemu výrazu testovať stav „index mimo hranice“ porovnaním čísla použitého v programe BASIC s maximálnym legálnym číslom, ktoré je teraz uložené v ndx. Pole multiplikátora v našom príklade obsahuje hodnoty 1, 10 a 100. Tieto konštanty predstavujú čísla, ktoré sa používajú na mapovanie zo špecifikácie indexu viacrozmerného poľa na špecifikáciu indexu lineárneho poľa. Aktuálna rovnica je:

Index Java = Index1 + Index2 * Maximálna veľkosť Index1 + Index3 * (MaxSize of Index1 * MaxSizeIndex 2)

Ďalšie pole Java v Variabilné trieda je uvedená nižšie.

 Vyjadrenie výrazov []; 

The exp pole sa používa na prácu s poľami, ktoré sú zapísané ako „A (10 * B, i). "V takom prípade sú indexy v skutočnosti skôr výrazmi ako konštantami, takže odkaz musí obsahovať odkazy na tie výrazy, ktoré sa vyhodnocujú za behu. Nakoniec existuje tento dosť škaredo vyzerajúci kúsok kódu, ktorý vypočítava index podľa toho, čo bol odovzdaný v programe. Táto súkromná metóda je uvedená nižšie.

 private int computeIndex (int ii []) hodí BASICRuntimeError {int offset = 0; if ((ndx == null) || (ii.length! = ndx.length)) throw new BASICRuntimeError ("Chybný počet indexov."); for (int i = 0; i <ndx.length; i ++) {if ((ii [i] ndx [i])) throw new BASICRuntimeError ("Index out of range."); offset = offset + (ii [i] -1) * mult [i]; } offset posunu; } 

Pri pohľade na vyššie uvedený kód si všimnete, že kód najskôr skontroluje, či sa pri odkazovaní na pole použil správny počet indexov, a potom aby každý index spadal do zákonného rozsahu pre daný index. Ak sa zistí chyba, tlmočník vyvolá výnimku. Metódy numValue a hodnota reťazca vráti hodnotu z premennej ako číslo alebo reťazec. Tieto dve metódy sú uvedené nižšie.

 double numValue (int ii []) hodí BASICRuntimeError {return nArrayValues ​​[computeIndex (ii)]; } String stringValue (int ii []) vyvolá BASICRuntimeError {if (subType == NUMBER_ARRAY) return "" + nArrayValues ​​[computeIndex (ii)]; vrátiť sArrayValues ​​[computeIndex (ii)]; } 

Existujú ďalšie spôsoby nastavenia hodnoty premennej, ktoré tu nie sú zobrazené.

Skrytím veľkej časti zložitosti spôsobu implementácie jednotlivých častí, keď konečne príde čas na vykonanie programu BASIC, je kód Java celkom jednoduchý.

Spustenie kódu

Kód na interpretáciu príkazov BASIC a ich vykonávanie je obsiahnutý v

bežať

metóda

Program

trieda. Kód tejto metódy je uvedený nižšie a ja si ho podrobím, aby som poukázal na zaujímavé časti.

 1 verejné vyprázdnenie (vstup InputStream, výstup OutStream) vyhodí BASICRuntimeError {2 PrintStream pout; 3 Výpočet e = stmts.elements (); 4 stmtStack = nový zásobník (); // nepredpokladajme žiadne zložené príkazy ... 5 dataStore = new Vector (); // ... a žiadne dáta na čítanie. 6 dataPtr = 0; 7 výrok s; 8 9 vars = nový RedBlackTree (); 10 11 // ak program ešte nie je platný. 12 if (! E.hasMoreElements ()) 13 návrat; 14 15 if (out instanceof PrintStream) {16 pout = (PrintStream) out; 17} else {18 pout = nový PrintStream (out); 19} 

Vyššie uvedený kód ukazuje, že: bežať metóda vyžaduje InputStream a an OutputStream na použitie ako „konzola“ vykonávacieho programu. V riadku 3 objekt výpočtu e je nastavená na množinu výpisov z pomenovanej kolekcie známky. Pre túto kolekciu som použil variáciu na binárny vyhľadávací strom nazývanú „červeno-čierny“ strom. (Ďalšie informácie o binárnych vyhľadávacích stromoch nájdete v mojom predchádzajúcom stĺpci o vytváraní všeobecných zbierok.) Potom sa vytvoria dve ďalšie zbierky - jedna s použitím Stoh a jeden používajúci a Vektor. Zásobník sa používa ako zásobník v ľubovoľnom počítači, ale vektor sa používa výslovne pre príkazy DATA v programe BASIC. Výsledná kolekcia je ďalší červeno-čierny strom, ktorý obsahuje referencie na premenné definované programom BASIC. Tento strom je tabuľka symbolov, ktorú program používa počas vykonávania.

Po inicializácii sa nastavia vstupné a výstupné toky a potom, ak e nie je null, začneme zhromažďovaním údajov, ktoré boli deklarované. Vykonáva sa to, ako je uvedené v nasledujúcom kóde.

 / * Najskôr načítame všetky údajové príkazy * / while (e.hasMoreElements ()) {s = (Príkaz) e.nextElement (); if (s.keyword == Statement.DATA) {s.execute (this, in, pout); }} 

Vyššie uvedená slučka jednoducho sleduje všetky príkazy a potom sa vykonajú všetky nájdené príkazy DATA. Vykonaním každého príkazu DATA sa vložia hodnoty deklarované týmto príkazom do súboru Uloženie údajov vektor. Ďalej vykonáme správny program, ktorý sa vykonáva pomocou tejto ďalšej časti kódu:

 e = stmts.elements (); s = (Výpis) e.nextElement (); do {int rrr; / * Počas behu preskakujeme dátové príkazy. * / skúsiť {yyy = in.available (); } chytit (IOException ez) {rrr = 0; } if (rrr! = 0) {pout.println ("Zastavené o:" + s); push (y); prestávka; } if (s.keyword! = Statement.DATA) {if (traceState) {s.trace (this, (traceFile! = null)? traceFile: pout); } s = s.execute (this, in, pout); } else s = nextStatement (s); } while (s! = null); } 

Ako vidíte v kóde vyššie, prvým krokom je reinicializácia e. Ďalším krokom je načítanie prvého príkazu do premennej s a potom vstúpiť do vykonávacej slučky. Existuje nejaký kód na kontrolu čakajúcich vstupov do vstupného toku, ktorý umožňuje prerušenie postupu programu zadaním textu do programu. Potom slučka skontroluje, či by príkaz na vykonanie bol príkazom DATA. Ak je, cyklus preskočí príkaz, pretože už bol vykonaný. Vyžaduje sa pomerne komplikovaná technika vykonávania všetkých dátových príkazov ako prvých, pretože program BASIC umožňuje, aby sa kdekoľvek v zdrojovom kóde zobrazovali príkazy DATA, ktoré vyhovujú príkazu READ. Nakoniec, ak je sledovanie povolené, záznam trasovania sa vytlačí a veľmi neinpresívny výpis s = s.execute (this, in, pout); je vyvolaná. Krása je v tom, že všetko úsilie spojené so zapuzdrením základných konceptov do ľahko pochopiteľných tried robí výsledný kód triviálnym. Ak to nie je triviálne, možno máte predstavu, že môže existovať iný spôsob, ako rozdeliť svoj dizajn.

Balenie a ďalšie myšlienky

Tlmočník bol navrhnutý tak, aby mohol bežať ako vlákno, takže vo vašom programovom priestore môže byť súčasne spustených niekoľko vlákien tlmočníka COCOA. Ďalej s využitím rozšírenia funkcií môžeme poskytnúť prostriedky, prostredníctvom ktorých môžu tieto vlákna vzájomne interagovať. Existoval program pre Apple II a neskôr pre PC a Unix s názvom C-robots, čo bol systém interagujúcich „robotických“ entít, ktoré boli programované pomocou jednoduchého derivačného jazyka BASIC. Táto hra mne a ostatným poskytla veľa hodín zábavy, ale bola tiež vynikajúcim spôsobom, ako predstaviť základné princípy výpočtu mladším študentom (ktorí sa mylne domnievali, že iba hrajú a neučia sa). Subsystémy tlmočníkov založené na prostredí Java sú oveľa výkonnejšie ako ich predjavské náprotivky, pretože sú okamžite dostupné na ľubovoľnej platforme Java. COCOA bežal na systémoch Unix a Macintoshes v ten istý deň, keď som začal pracovať na počítači so systémom Windows 95. Aj keď je Java porazená nekompatibilitou v implementáciách sady nástrojov pre vlákna alebo okná, často sa prehliada toto: Mnoho kódu „jednoducho funguje“.

$config[zx-auto] not found$config[zx-overlay] not found