Ohjelmointi

Rakenna tulkki Java-sovellukseen - Toteuta toteutusmoottori

Edellinen 1 2 3 Sivu 2 Seuraava Sivu 2/3

Muut näkökohdat: Merkkijonot ja taulukot

COCOA-tulkki toteuttaa kaksi muuta BASIC-kielen osaa: merkkijonot ja taulukot. Tarkastellaan ensin merkkijonojen toteutusta.

Merkkijonojen toteuttamiseksi muuttujina, Ilmaisu luokka muokattiin sisällyttämään käsite "merkkijono" lausekkeita. Tämä muutos oli kahden lisäyksen muodossa: isString ja stringValue. Näiden kahden uuden menetelmän lähde on esitetty alla.

 String stringValue (Program pgm) heittää BASICRuntimeError {heittää uuden BASICRuntimeError ("Ei merkkijonoa tälle."); } looginen isString () {return false; } 

On selvää, että BASIC-ohjelmalle ei ole liian hyödyllistä saada perusilmaisuuden merkkijonoarvo (joka on aina joko numeerinen tai looginen lauseke). Voit päätellä hyödyllisyyden puutteesta, johon nämä menetelmät eivät silloin kuuluneet Ilmaisu ja kuului ryhmän alaluokkaan Ilmaisu sen sijaan. Laittamalla nämä kaksi menetelmää perusluokkaan kaikki Ilmaisu esineitä voidaan testata, onko ne itse asiassa merkkijonoja.

Toinen suunnittelutapa on palauttaa numeeriset arvot merkkijonoina käyttämällä a StringBuffer objekti tuottaa arvon. Joten esimerkiksi sama koodi voidaan kirjoittaa uudestaan:

 String stringValue (Program pgm) heittää BASICRuntimeError {StringBuffer sb = uusi StringBuffer (); sb.append (tämä.arvo (pgm)); return sb.toString (); } 

Ja jos yllä olevaa koodia käytetään, voit estää sen käytön isString koska jokainen lauseke voi palauttaa merkkijonon arvon. Voit myös muokata arvo menetelmä yrittää palauttaa numero, jos lauseke arvioi merkkijonon suorittamalla sen jonkin arvo menetelmä java.lang.Tupla. Monilla kielillä, kuten Perl, TCL ja REXX, tällaista amorfista kirjoittamista käytetään suureksi hyödyksi. Molemmat lähestymistavat ovat päteviä, ja sinun tulee tehdä valintasi tulkin suunnittelun perusteella. BASICissa tulkin on palautettava virhe, kun merkkijono määritetään numeeriselle muuttujalle, joten valitsin ensimmäisen lähestymistavan (palauttamalla virheen).

Matriisien osalta on olemassa erilaisia ​​tapoja, joilla voit suunnitella kielesi tulkitsemaan niitä. C käyttää taulukon elementtien ympärillä olevia hakasulkeita erottaakseen taulukon hakemistoviitteet funktioviittauksista, joiden argumenttien ympärillä on sulkeet. BASICin kielisuunnittelijat päättivät kuitenkin käyttää sulkeita sekä funktioihin että matriiseihin, kun tekstiä NIMI (V1, V2) jäsennin näkee, se voi olla joko funktion kutsu tai taulukon viite.

Leksikaalinen analysaattori erottaa merkit, joita seuraa sulkeet olettaen ensin, että ne ovat toimintoja, ja testaamalla niitä. Sitten se tarkistaa, ovatko ne avainsanoja vai muuttujia. Juuri tämä päätös estää ohjelmaa määrittelemästä muuttujaa nimeltä SIN. Kaikki muuttujat, joiden nimi vastasi funktion nimeä, palautettaisiin leksikaalianalysaattorilla sen sijaan toimintotunnukseksi. Toinen temppu, jota leksikaalianalysaattori käyttää, on tarkistaa, seuraauko muuttujan nimeä välittömästi `` (''. Jos näin on, analysaattori olettaa, että se on taulukon viite. Analysoimalla tämä leksikaalisessa analysaattorissa, poistetaan merkkijonoMYARRAY (2)'tulkita kelvolliseksi taulukoksi (huomioi muuttujan nimen ja avoimen sulun välinen tila).

Viimeinen temppu taulukoiden toteuttamiseen on Vaihteleva luokassa. Tätä luokkaa käytetään muuttujan esiintymään, ja kuten keskustelin viime kuukauden sarakkeessa, se on alaluokka Tunnus. Siinä on kuitenkin myös joitain koneita, jotka tukevat taulukoita, ja minä näytän sen alla:

luokka Muuttuja laajentaa tunnusta {// Lakimuuttujan alatyypit lopullinen staattinen int NUMBER = 0; lopullinen staattinen int STRING = 1; lopullinen staattinen int NUMBER_ARRAY = 2; lopullinen staattinen int STRING_ARRAY = 4; Merkkijono nimi; int alatyyppi; / * * Jos muuttuja on symbolitaulukossa, nämä arvot * alustetaan. * / int ndx []; // taulukkoindeksit. int mult []; // matriisikertoimet tuplasti nArrayValues ​​[]; Merkkijonon sArrayValues ​​[]; 

Yllä oleva koodi näyttää muuttujaan liittyvät esiintymämuuttujat, kuten JatkuvaLauseke luokassa. On valittava käytettävien luokkien määrä verrattuna luokan monimutkaisuuteen. Yksi suunnitteluvaihtoehto voi olla a Vaihteleva luokka, joka sisältää vain skalaarimuuttujia ja lisää sitten TaulukkoVaihteleva alaluokka käsittelemään matriisien monimutkaisuutta. Päätin yhdistää ne, muuttamalla skalaarimuuttujat olennaisesti pituuksiksi 1.

Jos luet yllä olevan koodin, näet taulukkoindeksit ja kertoimet. Nämä ovat täällä, koska BASIC: n moniulotteiset taulukot toteutetaan käyttämällä yhtä lineaarista Java-taulukkoa. Java-taulukon lineaarinen hakemisto lasketaan manuaalisesti käyttämällä kertojaryhmän elementtejä. BASIC-ohjelmassa käytettyjen indeksien pätevyys tarkistetaan vertaamalla niitä indeksien suurimpaan lailliseen indeksiin ndx taulukko.

Esimerkiksi BASIC-taulukossa, jonka kolme ulottuvuutta ovat 10, 10 ja 8, arvot 10, 10 ja 8 tallennetaan ndx: ään. Tämä antaa lausekkeen arvioijalle mahdollisuuden testata "indeksin rajojen ulkopuolella" -olosuhteita vertaamalla BASIC-ohjelmassa käytettyä lukua ndx: ään tallennettavaan enimmäisarvoon. Esimerkissämme oleva kerroinryhmä sisältäisi arvot 1, 10 ja 100. Nämä vakiot edustavat numeroita, joita käytetään kartoittamaan moniulotteisesta taulukkoindeksimäärityksestä lineaariseksi taulukkoindeksimääritykseksi. Varsinainen yhtälö on:

Java-hakemisto = Hakemisto1 + Hakemisto2 * Indeksin1 + Indeksin enimmäiskoko * (Indeksin1 maksimikoko * Suurennuksen koko 2)

Seuraava Java-taulukko Vaihteleva luokka on esitetty alla.

 Lauseke odottaa []; 

laskee taulukkoa käytetään käsittelemään taulukoita, jotka on kirjoitettu "A (10 * B, i)"Tällöin indeksit ovat itse asiassa lausekkeita eikä vakioita, joten viitteessä on oltava viitteitä lausekkeisiin, jotka arvioidaan ajon aikana. Lopuksi on tämä melko ruma näköinen koodikappale, joka laskee indeksin sen mukaan, mikä hyväksyttiin ohjelmassa. Tämä yksityinen menetelmä on esitetty alla.

 private int computeIndex (int ii []) heittää BASICRuntimeError {int offset = 0; jos ((ndx == null) || (ii.pituus! = ndx.pituus)) heittää uuden BASICRuntimeError ("Väärä indeksien määrä."); for (int i = 0; i <ndx.pituus; i ++) {if (((ii [i] ndx [i])) heittää uuden BASICRuntimeError ("Indeksi alueen ulkopuolelle."); offset = offset + (ii [i] -1) * mult [i]; } paluupoikkeama; } 

Yllä olevaa koodia tarkasteltaessa huomaat, että koodi tarkistaa ensin, että taulukkoa viitattaessa käytettiin oikeaa lukumäärää indeksejä, ja että sitten kukin indeksi oli kyseisen indeksin laillisella alueella. Jos havaitaan virhe, tulkki heittää poikkeuksen. Menetelmät numValue ja stringValue palauta arvo muuttujasta numerona tai merkkijonona. Nämä kaksi menetelmää on esitetty alla.

 double numValue (int ii []) heittää BASICRuntimeError {return nArrayValues ​​[computeIndex (ii)]; } String stringValue (int ii []) heittää BASICRuntimeError {if (subType == NUMBER_ARRAY) return "" + nArrayValues ​​[computeIndex (ii)]; return sArrayValues ​​[computeIndex (ii)]; } 

Muuttujan arvon asettamiseksi on muita menetelmiä, joita ei näytetä tässä.

Piilottamalla suuren osan kunkin kappaleen toteutuksen monimutkaisuudesta, kun BASIC-ohjelman suorittaminen tulee vihdoin, Java-koodi on melko yksinkertainen.

Suoritetaan koodi

Koodi BASIC-käskyjen tulkitsemiseksi ja suorittamiseksi sisältyy

juosta

menetelmä

Ohjelmoida

luokassa. Tämän menetelmän koodi on esitetty alla, ja aion käydä läpi läpi mielenkiintoisia osia.

 Yksi julkinen tyhjiöajo (InputStream sisään, OutputStream ulos) heittää BASICRuntimeError {2 PrintStream -outu; 3 Luettelo e = stmts.elements (); 4 stmtStack = uusi pino (); // ei oleteta pinottuja lauseita ... 5 dataStore = new Vector (); // ... eikä tietoja lueta. 6 dataPtr = 0; 7 lausunto s; 8 9 vars = uusi RedBlackTree (); 10 11 // jos ohjelma ei ole vielä kelvollinen. 12 if (! E.hasMoreElements ()) 13 palaa; 14 15 if (out of PrintStream) {16 pout = (PrintStream) out; 17} else {18 pout = new PrintStream (out); 19} 

Yllä oleva koodi osoittaa, että juosta menetelmä vie InputStream ja OutputStream käytettäväksi suorittavan ohjelman "konsolina". Rivillä 3 on luettelo-objekti e on asetettu nimettyjen kokoelmien lausejoukolle stmts. Tässä kokoelmassa käytin muunnelmaa binaarihakupuussa nimeltä "punainen-musta" puu. (Lisätietoja binäärihakupuista on edellisessä sarakkeessani geneeristen kokoelmien rakentamisesta.) Sen jälkeen luodaan kaksi uutta kokoelmaa - yksi käyttäen Pino ja yksi käyttämällä a Vektori. Pinoa käytetään kuten pinoa missä tahansa tietokoneessa, mutta vektoria käytetään nimenomaisesti BASIC-ohjelman DATA-käskyille. Lopullinen kokoelma on toinen puna-musta puu, joka sisältää viitteet BASIC-ohjelman määrittelemille muuttujille. Tämä puu on symbolitaulukko, jota ohjelma käyttää suorituksen aikana.

Alustamisen jälkeen tulo- ja lähtövirrat määritetään ja sitten jos e ei ole nolla, aloitamme keräämällä kaikki ilmoitetut tiedot. Se tehdään seuraavan koodin mukaisesti.

 / * Ensin ladataan kaikki tieto lausekkeet * / while (e.hasMoreElements ()) {s = (Statement) e.nextElement (); if (s.avainsana == Lauseke.DATA) {s. suorita (this, in, pout); }} 

Yllä oleva silmukka tarkastelee yksinkertaisesti kaikkia lauseita, ja kaikki sen löytämät DATA-käskyt suoritetaan sitten. Jokaisen DATA-käskyn suorittaminen lisää kyseisen lauseen ilmoittamat arvot dataStore vektori. Seuraavaksi suoritamme varsinaisen ohjelman, joka tehdään tällä seuraavalla koodilla:

 e = elementtien elementit (); s = (Lausunto) e.sextElement (); tee {int yyy; / * Ohitamme suorituksen aikana Data-lauseet. * / kokeile {yyy = in.available (); } saalis (IOException ez) {yyy = 0; } if (yyy! = 0) {pout.println ("Pysäytetty:" + s); työntö (t); tauko; } if (s.avainsana! = Lauseke.DATA) {if (traceState) {s.trace (tämä, (traceFile! = null)? traceFile: pout); } s = s. suorita (tämä, sisään, harmaata); } else s = seuraavaLausunto (t); } while (s! = tyhjä); } 

Kuten yllä olevasta koodista näet, ensimmäinen vaihe on alustaa uudelleen e. Seuraava vaihe on hakea ensimmäinen lause muuttujaan s ja anna sitten suoritussilmukka. On jonkin verran koodia, joka tarkistaa, onko tulovirtaan odotettavissa oleva tulo, jotta ohjelman eteneminen voidaan keskeyttää kirjoittamalla ohjelmalle, ja sitten silmukka tarkistaa, olisiko suoritettava käsky DATA-käsky. Jos on, silmukka ohittaa käskyn, koska se oli jo suoritettu. Kaikkien datalausekkeiden suorittamisen edellyttämä melko sekava tekniikka vaaditaan, koska BASIC antaa READ-käskyn tyydyttävien DATA-käskyjen näkyvän missä tahansa lähdekoodissa. Lopuksi, jos jäljitys on käytössä, jäljitystietue tulostetaan ja hyvin ei-painostava lauseke s = s. suorita (tämä, sisään, harmaata); kutsutaan. Kauneus on, että kaikki pyrkimykset kapseloida peruskäsitteet helposti ymmärrettäviin luokkiin tekevät lopullisesta koodista triviaalin. Jos se ei ole triviaali, ehkä sinulla on aavistustakaan siitä, että voi olla toinen tapa jakaa muotoilu.

Kääriminen ja muita ajatuksia

Tulkki on suunniteltu siten, että se voi toimia ketjuna, joten ohjelmatilassa voi olla useita COCOA-tulkkiketjuja samanaikaisesti. Lisäksi funktiolaajennuksen avulla voimme tarjota keinon, jolla nuo ketjut voivat olla vuorovaikutuksessa toistensa kanssa. Apple II: lle ja myöhemmin PC: lle ja Unixille oli olemassa ohjelma nimeltä C-robotit, joka oli vuorovaikutuksessa olevien "robotti" -elementtien järjestelmä, joka oli ohjelmoitu yksinkertaisen BASIC-johdannaiskielen avulla. Peli tarjosi minulle ja muille monen tunnin viihdettä, mutta se oli myös erinomainen tapa esitellä laskennan perusperiaatteet nuoremmille opiskelijoille (jotka virheellisesti uskoivat pelaavansa eivätkä oppineet). Java-pohjaiset tulkkialijärjestelmät ovat paljon tehokkaampia kuin ennen Java-vastaavia, koska ne ovat heti käytettävissä kaikilla Java-alustoilla. COCOA juoksi Unix-järjestelmissä ja Macintoshesissa samana päivänä, kun työskentelin Windows 95 -tietokoneella. Vaikka Java on pahoinpidellyt langan tai ikkunan työkalupaketin toteutusten yhteensopimattomuuksien vuoksi, huomiotta jätetään usein tämä: Monet koodit "vain toimivat".