Programmēšana

Vai meklējat Java lex un yacc? Jūs nepazīstat Džeku

Sun ir izlaidis jaunu Java valodā rakstītu rīku Jack, kas automātiski ģenerē parsētājus, apkopojot teksta failā saglabātu augsta līmeņa gramatikas specifikāciju. Šis raksts kalpos kā ievads šim jaunajam rīkam. Raksta pirmajā daļā ir īss ievads parsēšanas automātiskai ģenerēšanai un mana pirmā pieredze ar tiem. Tad rakstā galvenā uzmanība tiks pievērsta Džekam un tam, kā jūs to varat izmantot, lai ģenerētu parsētājus un lietojumprogrammas, kas izveidotas ar šiem parsētājiem, balstoties uz jūsu augsta līmeņa gramatiku.

Automātiska kompilatora parsētāja ģenerēšana

Parsētājs ir viena no visizplatītākajām datora lietojumprogrammas sastāvdaļām. Tas pārveido tekstu, kuru cilvēki var nolasīt, par datu struktūrām, kas pazīstamas kā parsēšanas koki un kuras saprot dators. Es skaidri atceros savu ievadu automātiskajā parsētāja paaudzē: koledžā biju pabeidzis kursu par kompilatoru konstruēšanu. Ar manas sievas palīdzību es biju uzrakstījis vienkāršu kompilatoru, kas varētu pārvērst programmas, kas izveidotas klasē veidotajā valodā, par izpildāmām programmām. Es atceros, ka tajā brīdī jutos ļoti paveicies.

Pirmajā "īstajā" darbā pēc koledžas es saņēmu uzdevumu izveidot jaunu grafikas apstrādes valodu, lai apkopotu komandās grafikas kopprocesoram. Es sāku ar svaigi sacerētu gramatiku un gatavojos sākt vairāku nedēļu ilgo kompilatora salikšanas projektu. Tad draugs man parādīja Unix utilītas lex un yacc. Leks uzbūvēti leksiskie analizatori no regulārām izteiksmēm un yacc samazināja gramatikas specifikāciju par tabulu vadītu kompilatoru, kas varētu radīt kodu, kad tas bija veiksmīgi parsējis produkciju no šīs gramatikas. ES izmantoju lex un yacc, un mazāk nekā pēc nedēļas mans sastādītājs bija izveidots un darbojas! Vēlāk Brīvās programmatūras fonda GNU projekts radīja "uzlabotas" versijas lex un yacc - nosauca elastība un bizons - lietošanai platformās, kurās nedarbojās Unix operētājsistēmas atvasinājums.

Automātiskā parsētāja paaudzes pasaule atkal attīstījās, kad Terence Parra, toreizējā Purdue Universitātes studente, izveidoja Purdue Compiler Construction Tool Set jeb PCCTS. Divas PCCTS sastāvdaļas - DFA un ANTLR - nodrošina tādas pašas funkcijas kā lex un yacc; tomēr gramatikas, ka ANTLR pieņem LL (k) gramatikas, nevis LALR gramatikas, kuras lieto yacc. Turklāt PCCTS ģenerētais kods ir daudz lasāmāks nekā yacc. Ģenerējot vieglāk lasāmu kodu, PCCTS atvieglo koda lasīšanas procesu cilvēkiem, lai saprastu, ko dara dažādi gabali. Šī izpratne var būt būtiska, mēģinot diagnosticēt kļūdas gramatikas specifikācijās. PCCTS ātri izstrādāja sekojošus ļaudis, kuriem faili bija vieglāk lietojami nekā yacc.

Automātiskās parsētāja ģenerēšanas spēks ir tāds, ka tas ļauj lietotājiem koncentrēties uz gramatiku un neuztraukties par ieviešanas pareizību. Gan vienkāršos, gan sarežģītos projektos tas var ievērojami ietaupīt laiku.

Džeks uzkāpj uz šķīvja

Es instrumentus vērtēju pēc to problēmu vispārības, kuras viņi risina. Tā kā prasība parsēt teksta ievadi parādās atkal un atkal, automātiskā parsētāja ģenerēšana manā rīkjoslā ir diezgan augsta. Kopā ar Java ātrās attīstības ciklu automātiskā parsētāja ģenerēšana nodrošina rīku kompilatora noformēšanai, kuru ir grūti pārspēt.

Džeks (atskaņa ar yacc) ir parsēšanas ģenerators PCCTS garā, ko Sun ir bez maksas izlaidis Java programmēšanas kopienai. Džeks ir ārkārtīgi viegli aprakstāms rīks: Vienkārši sakot, jūs piešķirat tam kombinētu gramatisko un leksikas likumu kopu .jack faila formā un palaižat rīku, un tas dod jums atpakaļ Java klasi, kas parsēs šo gramatiku. Kas var būt vieglāk?

Arī iegūt Džeku ir diezgan viegli. Vispirms lejupielādējiet kopiju no Džeka mājas lapas. Tas nāk pie jums pašizkravātās Java klases veidā, ko sauc uzstādīt. Lai instalētu Jack, jums tas jāizsauc uzstādīt klase, kas Windows 95 datorā tiek veikta, izmantojot komandu: C:> java instalēt.

Iepriekš parādītajā komandā tiek pieņemts, ka java komanda atrodas jūsu komandas ceļā un ka klases ceļš ir iestatīts atbilstoši. Ja iepriekš minētā komanda nedarbojās vai neesat pārliecināts, vai lietas ir pareizi iestatītas, atveriet logu MS-DOS, pārvietojoties pa izvēlnes Sākt-> Programmas-> MS-DOS uzvedne vienumiem. Ja jums ir instalēts Sun JDK, varat ierakstīt šīs komandas:

C:> ceļš C: \ java \ bin;% path% C:> set CLASSPATH =; c: \ java \ lib \ class.zip 

Ja ir instalēta Symantec Cafe versija 1.2 vai jaunāka, varat ierakstīt šīs komandas:

C:> ceļš C: \ cafe \ java \ bin;% path% 

Klases ceļš jau ir jāiestata failā ar nosaukumu sc.ini kafejnīcas atkritumu direktorijā.

Pēc tam ierakstiet java instalēt komandu no augšas. Instalēšanas programma jums jautās, kurā direktorijā vēlaties instalēt, un zem tā tiks izveidots Jack apakšdirektorijs.

Džeka izmantošana

Džeks ir pilnībā rakstīts Java valodā, tāpēc Džeka klases nozīmē, ka šis rīks ir uzreiz pieejams visās platformās, kas atbalsta Java virtuālo mašīnu. Tomēr tas nozīmē arī to, ka Windows lodziņos ir jāpalaiž Džeks no komandrindas. Pieņemsim, ka jūs izvēlējāties direktorijas nosaukumu JavaTools, kad sistēmā instalējāt Jack. Lai izmantotu Džeku, jums būs jāpievieno Džeka klases savam klases ceļam. To var izdarīt savā autoexec.bat failā vai jūsu .cshrc failu, ja esat Unix lietotājs. Kritiskā komanda ir kaut kas līdzīgs zemāk redzamajai līnijai:

C:> set CLASSPATH =; C: \ JavaTools \ Jack \ java; C: \ java \ lib \ class.zip 

Ņemiet vērā, ka Symantec Cafe lietotāji var rediģēt sc.ini failu un iekļauj tur džeka klases, vai arī viņi var iestatīt CLASSPATH skaidri parādīts iepriekš.

Iestatot vides mainīgo, kā parādīts iepriekš, Džeka klases tiek ievietotas CLASSPATH starp "." (pašreizējais direktorijs) un Java bāzes sistēmas klases. Džekam galvenā klase ir COM.sun.labs.jack.Main. Lielie burti ir svarīgi! Komandā ir tieši četri lielie burti (“C”, “O”, “M” un vēl viens “M”). Lai manuāli palaistu Džeku, ierakstiet komandu:

C:> java COM.sun.labs.jack.Main parsētājs-input.jack

Ja klases ceļā nav Jack failu, varat izmantot šo komandu:

C:> java -classpath.; C: \ JavaTools \ Jack \ java; c: \ java \ lib \ class.zip COM.sun.labs.jack.Main parser-input.jack 

Kā redzat, tas kļūst mazliet garš. Lai samazinātu rakstīšanu, es izsaukumu ievietoju a .sikspārnis faila nosaukums Džeks.sikspārnis. Kādā brīdī nākotnē būs pieejama vienkārša C iesaiņojuma programma, iespējams, pat tad, kad jūs to izlasīsit. Pārbaudiet Džeka mājas lapu par šīs un citu programmu pieejamību.

Kad Džeks tiek palaists, tas pašreizējā direktorijā izveido vairākus failus, kurus vēlāk apkoposiet savā parsatorā. Lielākajai daļai ir vai nu prefikss ar jūsu parsētāja vārdu, vai arī tie ir kopīgi visiem parsētājiem. Tomēr viens no šiem ASCII_CharStream.javavar sadurties ar citiem parsētājiem, tāpēc, iespējams, ir ieteicams sākt direktorijā, kurā ir tikai .jack fails, kuru izmantosit, lai ģenerētu parsētāju.

Kad jūs palaidīsit Džeku, ja paaudze būs gājusi gludi, jums būs ķekars .java faili pašreizējā direktorijā ar dažādiem interesantiem nosaukumiem. Tie ir jūsu parsētāji. Es iesaku jums tos atvērt kopā ar redaktoru un apskatīt tos. Kad esat gatavs, varat tos apkopot ar komandu

C:> javac -d. ParserName.java

kur ParserName ir nosaukums, kuru ievadījāt parsētājam ievades failā. Vairāk par to mazliet. Ja visi jūsu parsētāja faili netiek apkopoti, varat izmantot rakstīšanas metodi Brute Force:

C:> javac * .java 

Tas apkopos visu direktorijā. Šajā brīdī jūsu jaunais parsētājs ir gatavs lietošanai.

Džeka parsētāja apraksti

Džeka parsētāja apraksta failiem ir paplašinājums .jack un ir sadalīti trīs pamatdaļās: opcijas un bāzes klase; leksikas žetoni; un ne-termināli. Apskatīsim vienkāršu parsētāja aprakstu (tas ir iekļauts piemēri direktoriju, kas nāk ar Džeku).

opcijas {LOOKAHEAD = 1; } PARSER_BEGIN (Vienkāršs) sabiedrības klase Vienkāršs {public static void main (String args []) met ParseError { Vienkāršs parsētājs = jauns Vienkāršs(System.in); parsētājs.Ievade (); }} PARSER_END (Vienkāršs) 

Pirmās iepriekšējās rindas apraksta parsētāja opcijas; šajā gadījumā SKATIES UZ PRIEKŠU ir iestatīts uz 1. Ir arī citas iespējas, piemēram, diagnostika, Java Unicode apstrāde utt., kuras arī šeit var iestatīt. Pēc opcijām nāk parsētāja bāzes klase. Abi tagi PARSER_BEGIN un PARSER_END iekavās klase, kas kļūst par iegūtā parsētāja Java pamata kodu. Ņemiet vērā, ka parsētāja specifikācijā izmantotais klases nosaukums jābūt jābūt vienādam šīs sadaļas sākumā, vidū un beigās. Iepriekš minētajā piemērā klases nosaukumu esmu treknrakstā ievietojis, lai tas būtu skaidrs. Kā redzat augstāk esošajā kodā, šī klase definē statisku galvenais metodi, lai Java tulks komandrindā varētu izsaukt klasi. The galvenais metode vienkārši ieceļ jaunu parsētāju ar ievades straumi (šajā gadījumā System.in) un pēc tam izsauc Ievade metodi. The Ievade metode mūsu gramatikā nav termināla, un tā ir definēta EBNF elementa formā. EBNF nozīmē paplašinātu Backus-Naur formu. Veidlapa Backus-Naur ir metode, lai norādītu bez konteksta gramatikas. Specifikācija sastāv no: terminālis kreisajā pusē ražošanas simbols, kas parasti ir ":: =", un viens vai vairāki iestudējumi labajā pusē. Izmantotais apzīmējums parasti ir kaut kas līdzīgs šim:

 Atslēgvārds :: = "ja" | "pēc tam" | "cits" 

Tas būtu lasāms šādi: Atslēgvārds termināls ir viens no virknes literāliem “ja”, “tad” vai “cits”. ”Džekā šī forma tiek paplašināta, lai kreisās puses daļu varētu attēlot ar metodi, un alternatīvos paplašinājumus var attēlot ar regulāras izteiksmes vai citus terminālus. Turpinot mūsu vienkāršo piemēru, failā ir šādas definīcijas:

void Input (): {} {MatchedBraces () "\ n"} void MatchedBraces (): {} {"{" [MatchedBraces ()] "}"}} 

Šis vienkāršais parsētājs parsē zemāk redzamo gramatiku:

Ievade::=MatchedBraces "\ n"
MatchedBraces::="{" [ MatchedBraces ] "}"

Es izmantoju slīprakstu, lai parādītu produkcijas labajā pusē esošos bez terminālus, un treknrakstā, lai parādītu literāļus. Kā redzat, gramatika vienkārši parsē atbilstošās iekavu "{" un "}" rakstzīmju kopas. Džeka failā ir divi iestudējumi, lai aprakstītu šo gramatiku. Pirmais terminālis, Ievadear šo definīciju definē kā trīs secīgus vienumus: a MatchedBraces termināls, jaunas rindas raksturs un faila beigu marķieris. The marķieri ir definējis Džeks, lai jums tas nebūtu jānorāda savai platformai.

Kad šī gramatika tiek ģenerēta, iestudējuma kreisās puses tiek pārveidotas par metodēm Vienkāršs klase; sastādot, Vienkāršs klase lasa rakstzīmes no Sistēma.iekšā un pārbauda, ​​vai tajos ir atbilstošs lencīšu komplekts. Tas tiek panākts, izmantojot ģenerēto metodi Ievade, kuru ģenerēšanas procesā pārveido par metodi, kas parsē an Ievade bez termināla. Ja parsēšana neizdodas, metode izslēdz izņēmumu ParseError, kuru galvenā kārtība var noķert un pēc tam sūdzēties, ja tā izvēlas.

Protams, tur ir vairāk. Bloks, kas atdalīts ar "{" un "}" pēc termināla nosaukuma, kurš šajā piemērā ir tukšs, var saturēt patvaļīgu Java kodu, kas tiek ievietots ģenerētās metodes priekšā. Pēc katras paplašināšanas ir vēl viens izvēles bloks, kas var saturēt patvaļīgu Java kodu, kas jāizpilda, kad parsētājs veiksmīgi saskaņo šo paplašinājumu.

Sarežģītāks piemērs

Tātad, kā par piemēru, kas ir nedaudz sarežģītāks? Apsveriet šādu gramatiku, kas atkal sadalīta gabalos. Šī gramatika ir paredzēta matemātisko vienādojumu interpretēšanai, izmantojot četrus galvenos operatorus - saskaitīšanu, reizināšanu, atņemšanu un dalīšanu. Avots atrodams šeit:

opcijas {LOOKAHEAD = 1; } PARSER_BEGIN (Calc1) public class Calc1 {public static void main (String args []) throws ParseError {Calc1 parser = new Calc1 (System.in); while (true) {System.out.print ("Ievadiet izteiksmi:"); System.out.flush (); mēģiniet {switch (parser.one_line ()) {gadījums -1: System.exit (0); noklusējums: pārtraukums; }} catch (ParseError x) {System.out.println ("Iziet."); mest x; }}}} PARSER_END (aprēķins1) 

Pirmā daļa ir gandrīz tāda pati kā Vienkāršs, izņemot to, ka galvenā kārtība tagad izsauc termināli one_line atkārtoti, līdz neizdodas parsēt. Nākamais nāk šāds kods:

IGNORE_IN_BNF: {} "" TOKEN: {} {} TOKEN: / * OPERATORS * / {} TOKEN: {} 

Šīs definīcijas aptver pamatterminālus, kuros norādīta gramatika. Pirmais, nosaukts IGNORE_IN_BNF, ir īpašs marķieris. Visi parsētāja nolasītie marķieri, kas atbilst rakstā definētajām rakstzīmēm IGNORE_IN_BNF žetons tiek klusi izmests. Kā redzat mūsu piemērā, tas parsatoram liek ievadā ignorēt atstarpes rakstzīmes, cilnes un atdošanas rakstzīmes.

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