Programavimas

Ieškote „Java“ lex ir yacc? Tu nepažįsti Džeko

„Sun“ išleido naują „Java“ rašytą įrankį „Jack“, kuris automatiškai sugeneruoja analizatorius, sudarydamas aukšto lygio gramatikos specifikaciją, saugomą tekstiniame faile. Šis straipsnis bus įvadas į šį naują įrankį. Pirmoje straipsnio dalyje pateikiamas trumpas automatinio analizatorių generavimo įvadas ir pirmoji mano patirtis su jais. Tada straipsnis bus sutelktas į Džeką ir į tai, kaip jį panaudosite kurdami analizatorius ir programas, sukurtas kartu su tais parsidavėjais, remiantis jūsų aukšto lygio gramatika.

Automatinis kompiliatoriaus analizatoriaus generavimas

Analizatorius yra vienas iš labiausiai paplitusių kompiuterinės programos komponentų. Tai paverčia tekstą, kurį žmonės gali perskaityti, į duomenų struktūras, žinomas kaip parsės medžiai, kurias supranta kompiuteris. Aš aiškiai prisimenu savo įvadą į automatinį analizatorių generavimą: Kolegijoje baigiau kompiliatorių konstravimo klasę. Padedama žmonos, buvau parašiusi paprastą kompiliatorių, kuris paverstų programas, parašytas klasei sukurta kalba, į vykdomąsias programas. Pamenu, tuo metu jaučiausi labai nuveikta.

Pirmajame „tikrame“ darbe po studijų gavau užduotį sukurti naują grafikos apdorojimo kalbą, kurią būtų galima surinkti į grafinio koprocesoriaus komandas. Aš pradėjau nuo ką tik sukomponuotos gramatikos ir pasiruošiau pradėti kelių savaičių kompiliatoriaus sudarymo projektą. Tada draugas man parodė „Unix“ komunalines paslaugas lex ir yacc. Leksas pastatė leksinius analizatorius iš taisyklingų posakių ir yacc sumažino gramatikos specifikaciją į lentele valdomą kompiliatorių, galintį sukurti kodą, kai jis sėkmingai išanalizavo tos gramatikos produkciją. aš naudojau lex ir yaccir po mažiau nei savaitės mano kompiliatorius buvo parengtas ir veikia! Vėliau Laisvos programinės įrangos fondo GNU projektas sukūrė „patobulintas“ versijas lex ir yacc -- pavadintas lankstytis ir bizonai - skirtas naudoti platformose, kuriose nebuvo įdiegta „Unix“ operacinės sistemos išvestinė priemonė.

Automatinio analizatorių generavimo pasaulis vėl pasistūmėjo į priekį, kai tuo metu Purdue universiteto studentė Terrence Parr sukūrė „Purdue Compiler Construction Tool Set“ arba PCCTS. Du PCCTS komponentai - DFA ir ANTLR - teikia tas pačias funkcijas kaip lex ir yacc; tačiau gramatikos kad ANTLR priima LL (k) gramatikas, priešingai nei naudojamos LALR gramatikos yacc. Be to, PCCTS sukurtas kodas yra daug lengviau skaitomas nei kodas, kurį sukūrė yacc. Generuodamas lengviau skaitomą kodą, PCCTS padeda žmogui, skaitančiam kodą, lengviau suprasti, ką daro įvairūs kūriniai. Šis supratimas gali būti būtinas bandant diagnozuoti gramatikos specifikacijos klaidas. PCCTS greitai sukūrė daugybę žmonių, kuriems buvo lengviau naudotis failais nei yacc.

Automatinio analizatorių generavimo galia yra ta, kad jis leidžia vartotojams susikoncentruoti ties gramatika ir nesijaudinti dėl diegimo teisingumo. Tai gali nepaprastai sutaupyti laiko tiek paprastuose, tiek sudėtinguose projektuose.

Džekas žengia prie lėkštės

Įrankius vertinu pagal jų sprendžiamos problemos bendrumą. Kadangi reikalavimas išanalizuoti teksto įvestį iškyla vėl ir vėl, automatinis analizatorių generavimas mano įrankių rinkinyje yra gana aukštas. Kartu su greitu „Java“ kūrimo ciklu, automatinis analizatorių generavimas suteikia kompiliatorių projektavimo įrankį, kurį sunku įveikti.

Džekas (rimuojasi su „yacc“) yra analizatorių generatorius, atsižvelgiant į PCCTS dvasią, kurį „Sun“ nemokamai išleido „Java“ programavimo bendruomenei. Džekas yra išskirtinai paprastas įrankis apibūdinti: Paprasčiau tariant, jūs suteikiate jam kombinuotų gramatinių ir leksikos taisyklių rinkinį .jack failo pavidalu ir paleisite įrankį, ir jis grąžins jums Java klasę, kuri analizuos tą gramatiką. Kas gali būti lengviau?

Taip pat gana lengva užsikrėsti Džeku. Pirmiausia atsisiųskite kopiją iš „Jack“ pagrindinio puslapio. Tai gaunama kaip savaime išpakuojanti „Java“ klasė, vadinama diegti. Norėdami įdiegti Džeką, turite tai pakviesti diegti klasė, kuri „Windows 95“ kompiuteryje atliekama naudojant komandą: C:> Java įdiegti.

Aukščiau parodyta komanda daro prielaidą, kad java komanda yra jūsų komandos kelyje ir kad klasės kelias buvo tinkamai nustatytas. Jei pirmiau nurodyta komanda neveikė arba nesate tikri, ar viskas tinkamai nustatyta, ar ne, atidarykite MS-DOS langą, pereidami meniu Pradėti-> Programos-> MS-DOS raginimas. Jei turite „Sun JDK“ įdiegtą, galite įvesti šias komandas:

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

Jei įdiegta „Symantec Cafe“ 1.2 ar naujesnė versija, galite įvesti šias komandas:

C:> kelias C: \ cafe \ java \ bin;% path% 

Klasės kelias jau turėtų būti nustatytas faile, vadinamame sc.ini kavinės šiukšliadėžės kataloge.

Tada įveskite java diegti komandą iš viršaus. Įdiegimo programa paklaus jūsų, į kokį katalogą norite įdiegti, o po juo bus sukurtas „Jack“ pakatalogis.

Naudodamasis Džeku

„Jack“ yra visiškai parašytas „Java“ kalba, todėl turint „Jack“ klases reiškia, kad šis įrankis yra iškart pasiekiamas kiekvienoje platformoje, palaikančioje „Java“ virtualią mašiną. Tačiau tai taip pat reiškia, kad „Windows“ dėžutėse turite paleisti „Jack“ iš komandinės eilutės. Tarkime, kad katalogo pavadinimą „JavaTools“ pasirinkote įdiegę „Jack“ į savo sistemą. Norėdami naudoti Džeką, prie savo kelio turėsite pridėti Džeko klases. Tai galite padaryti savo autoexec.bat faile arba jūsų .cshrc failą, jei esate „Unix“ vartotojas. Kritinė komanda yra kažkas panašaus į žemiau pateiktą eilutę:

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

Atminkite, kad „Symantec Cafe“ vartotojai gali redaguoti sc.ini failą ir įtraukite „Jack“ klases ten, arba jie gali nustatyti CLASSPATH kaip parodyta aukščiau.

Nustačius aplinkos kintamąjį, kaip parodyta aukščiau, „Jack“ klasės priskiriamos CLASSPATH tarp „“. (dabartinis katalogas) ir „Java“ pagrindinės sistemos klases. Pagrindinė Jacko klasė yra COM.sun.labs.jack.Main. Didžiosios raidės yra svarbios! Komandoje yra tiksliai keturios didžiosios raidės („C“, „O“, „M“ ir dar viena „M“). Norėdami paleisti „Jack“ rankiniu būdu, įveskite komandą:

C:> java COM.sun.labs.jack.Main analizatorius-input.jack

Jei klasės kelyje neturite „Jack“ failų, galite naudoti šią komandą:

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

Kaip matote, tai šiek tiek užsitęsia. Norėdami sumažinti spausdinimą, aš įtraukiau iškvietimą į a .šikšnosparnis failas pavadintas Džekas.šikšnosparnis. Kažkuriuo metu ateityje bus galima naudotis paprasta „C wrapper“ programa, galbūt net jums tai skaitant. Norėdami sužinoti apie šios ir kitų programų prieinamumą, apsilankykite pagrindiniame „Jack“ puslapyje.

Paleidus „Jack“, jis dabartiniame kataloge sukuria kelis failus, kuriuos vėliau sukompiliuosite į savo analizatorių. Dauguma jų yra su priešais su jūsų analizatoriaus vardu arba yra bendri visiems analizatoriams. Tačiau vienas iš jų ASCII_CharStream.java, gali susidurti su kitais analizatoriais, todėl tikriausiai verta pradėti kataloge, kuriame yra tik .Domkratas failas, kurį ketinate naudoti analizatoriui generuoti.

Kai paleisite Džeką, jei karta praėjo sklandžiai, turėsite krūvą .java failai dabartiniame kataloge su įvairiais įdomiais pavadinimais. Tai jūsų parsers. Aš raginu jus atidaryti juos su redaktoriumi ir peržiūrėti juos. Kai būsite pasirengę, galėsite juos surinkti naudodami komandą

C:> javac -d. ParserName.java

kur ParserName yra vardas, kurį įvedimo faile nurodėte analizatoriui. Daugiau apie tai šiek tiek. Jei visi jūsų analizatoriaus failai nėra kompiliuojami, galite naudoti grubios jėgos rinkimo metodą:

C:> javac * .java 

Tai sukomponuos viską, kas yra kataloge. Šiuo metu jūsų naujas analizatorius yra paruoštas naudoti.

Džeko analizatoriaus aprašymai

„Jack“ analizatoriaus aprašymo failai turi plėtinį .Domkratas ir yra suskirstyti į tris pagrindines dalis: pasirinkimo ir pagrindinės klasės; leksiniai žetonai; ir ne terminalų. Pažvelkime į paprastą analizatoriaus aprašymą (jis yra įtrauktas į pavyzdžių su Jacku).

parinktys {LOOKAHEAD = 1; } PARSER_BEGIN (Paprasta1) visuomenės klasė Paprasta1 {public static void main (String args []) meta ParseError { Paprasta1 analizatorius = naujas Paprasta1(„System.in“); analizatorius. Įvestis (); }} PARSER_END (Paprasta1) 

Pirmose pirmose eilutėse aprašytos analizatoriaus parinktys; tokiu atveju ŽIŪRĖTI Į ATEITĮ yra nustatytas kaip 1. Čia taip pat galima nustatyti kitas parinktis, pvz., diagnostiką, „Java Unicode“ tvarkymą ir pan. Vadovaujantis parinktimis ateina pagrindinė analizatoriaus klasė. Dvi žymos PARSER_BEGIN ir PARSER_END pažymėkite klasę, kuri tampa pagrindiniu gauto analizatoriaus „Java“ kodu. Atminkite, kad klasės pavadinimas, naudojamas analizatoriaus specifikacijoje turi būti tas pats šio skyriaus pradžioje, viduryje ir pabaigoje. Anksčiau pateiktame pavyzdyje klasės pavadinimą paryškinau, kad tai būtų aišku. Kaip matote aukščiau pateiktame kode, ši klasė apibrėžia statiką pagrindinis metodas, kad klasę galėtų iškviesti „Java“ vertėjas komandinėje eilutėje. pagrindinis metodas tiesiog sukuria naują analizatorių su įvesties srautu (šiuo atveju System.in) ir tada iškviečia Įvestis metodas. Įvestis metodas yra neterminuotas mūsų gramatikoje ir yra apibrėžtas EBNF elemento pavidalu. EBNF reiškia „Extended Backus-Naur Form“. „Backus-Naur“ forma yra metodas, nurodantis be konteksto esančias gramatikas. Specifikaciją sudaro a terminalas kairėje pusėje - gamybos simbolis, kuris paprastai yra „:: =“, ir vienas ar daugiau pastatymai dešinėje pusėje. Naudojamas žymėjimas paprastai yra maždaug toks:

 Raktažodis :: = "jei" | "tada" | "Kitas" 

Tai būtų skaitoma taip: Raktažodis terminalas yra vienas iš eilutės tiesioginių žodžių „jei“, „tada“ ar „dar kitaip“. „Džeke ši forma yra išplėsta, kad kairiajai daliai būtų pavaizduotas metodas, o pakaitinius išplėtimus gali žymėti reguliarūs posakiai ar kiti neterminalai. Tęsiant paprastą pavyzdį, faile yra šie apibrėžimai:

negaliojantis įvestis (): {} {atitikusios braselės () "\ n"} negaliojančios atitikusios breketės (): {} {"{" [suderintos breketės ()] "}"} 

Šis paprastas analizatorius analizuoja žemiau pateiktą gramatiką:

Įvestis::=„MatchedBraces“ "\ n"
„MatchedBraces“::="{" [ „MatchedBraces“ ] "}"

Aš naudoju kursyvą, norėdamas parodyti ne terminalus dešinėje spektaklių pusėje, o paryškintu šriftu - literalus. Kaip matote, gramatika paprasčiausiai išanalizuoja suderintus „{“ ir „}“ simbolių rinkinius. Džeko byloje yra du kūriniai, apibūdinantys šią gramatiką. Pirmasis terminalas, Įvestis, šis apibrėžimas apibrėžiamas kaip trys elementai iš eilės: a „MatchedBraces“ terminalas, naujos eilutės simbolis ir failo pabaigos žetonas. prieigos raktą nustato Jackas, kad nereikėtų jo nurodyti savo platformai.

Kai sukuriama ši gramatika, kairės pusės kūriniai yra paverčiami metodais Paprasta1 klasė; sudarant, Paprasta1 klasė skaito simbolius iš Sistema.į ir patikrina, ar juose yra atitinkamas petnešų rinkinys. Tai pasiekiama pasinaudojus sugeneruotu metodu Įvestis, kuris generavimo proceso metu transformuojamas į metodą, analizuojantį Įvestis neterminuotas. Jei analizuoti nepavyksta, metodas išmeta išimtį „ParseError“, kurią pagrindinė rutina gali pagauti ir paskui pasiskųsti, jei taip pasirenka.

Žinoma, yra dar daugiau. Bloke, kurį apibūdina „{“ ir „}“ po terminalo pavadinimo, kuris šiame pavyzdyje yra tuščias, gali būti savavališkas „Java“ kodas, įterptas sugeneruoto metodo priekyje. Tada po kiekvieno išplėtimo yra dar vienas neprivalomas blokas, kuriame gali būti savavališkas „Java“ kodas, kuris bus vykdomas, kai analizatorius sėkmingai atitiks tą išplėtimą.

Sudėtingesnis pavyzdys

Taigi, kaip su pavyzdžiu, kuris yra šiek tiek sudėtingesnis? Apsvarstykite šią gramatiką, vėl suskaidytą į dalis. Ši gramatika skirta matematinėms lygtims interpretuoti naudojant keturis pagrindinius operatorius - sudėjimą, dauginimą, atimimą ir padalijimą. Šaltinį galite rasti čia:

parinktys {LOOKAHEAD = 1; } PARSER_BEGIN (Calc1) viešoji klasė Calc1 {public static void main (String args []) meta ParseError {Calc1 parser = nauja Calc1 (System.in); while (true) {System.out.print ("Įveskite išraišką:"); System.out.flush (); pabandykite {switch (parser.one_line ()) {atvejis -1: System.exit (0); numatytasis: pertrauka; }} gaudyti (ParseError x) {System.out.println ("Exiting."); mesti x; }}}} PARSER_END (apskaičiuoti 1) 

Pirmoji dalis yra beveik tokia pati kaip Paprasta1, išskyrus tai, kad pagrindinė rutina dabar skambina į terminalą viena linija pakartotinai, kol nepavyksta išanalizuoti. Toliau pateikiamas šis kodas:

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

Šie apibrėžimai apima pagrindinius terminalus, kuriuose nurodoma gramatika. Pirmasis, pavadintas IGNORE_IN_BNF, yra specialus ženklas. Visi analizatoriaus perskaityti žetonai, atitinkantys simbolyje apibrėžtus simbolius IGNORE_IN_BNF žetonai tyliai išmetami. Kaip matote mūsų pavyzdyje, dėl to analizatorius ignoruoja tarpo simbolius, skirtukus ir grąžinimo simbolius įvestyje.

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