Programavimas

Kurkite savo kalbas naudodami „JavaCC“

Ar kada susimąstote, kaip veikia „Java“ kompiliatorius? Ar reikia rašyti analizatorius žymėjimo dokumentams, kurie neprenumeruoja standartinių formatų, tokių kaip HTML ar XML? Arba jūs norite įdiegti savo mažą programavimo kalbą tik dėl to? „JavaCC“ leidžia jums tai padaryti „Java“. Taigi, nesvarbu, ar tiesiog norite sužinoti daugiau apie tai, kaip dirba kompiliatoriai ir vertėjai, ar turite konkrečių ambicijų sukurti „Java“ programavimo kalbos įpėdinį, prisijunkite prie manęs šio mėnesio ieškojime „JavaCC“, paryškintas patogus mažas komandinės eilutės skaičiuoklė.

Kompiliatorių statybos pagrindai

Programavimo kalbos dažnai skirstomos šiek tiek dirbtinai į kompiliuojamas ir interpretuojamas kalbas, nors ribos tapo neryškios. Dėl to nesijaudinkite. Čia aptartos sąvokos vienodai gerai tinka tiek kompiliuojamoms, tiek interpretuojamoms kalboms. Panaudosime žodį sudarytojas žemiau, tačiau šio straipsnio taikymo sritis apima ir vertėjas.

Kompiliatoriai, atlikdami programos tekstą (šaltinio kodą), turi atlikti tris pagrindines užduotis:

  1. Leksinė analizė
  2. Sintaktinė analizė
  3. Kodo generavimas arba vykdymas

Didžioji kompiliatoriaus darbo dalis yra apie 1 ir 2 žingsnius, kurie apima programos šaltinio kodo supratimą ir sintaksinio teisingumo užtikrinimą. Mes vadiname tą procesą analizuojant, kuris yra analizatoriuss atsakomybė.

Leksinė analizė (leksika)

Leksinė analizė paviršutiniškai žvelgia į programos šaltinio kodą ir padalija jį į tinkamą žetonai. Žetonas yra reikšminga programos šaltinio kodo dalis. Žetonų pavyzdžiai apima raktinius žodžius, skyrybos ženklus, pažodžius, tokius kaip skaičiai, ir eilutes. Nontokenuose yra tarpas, kuris dažnai nepaisomas, tačiau naudojamas atskiriant žetonus, ir komentarai.

Sintaktinė analizė (analizuojant)

Sintaksinės analizės metu analizatorius ištraukia prasmę iš programos šaltinio kodo, užtikrindamas programos sintaksinį teisingumą ir sukurdamas vidinį programos vaizdą.

Kompiuterių kalbos teorija kalba programos,gramatika, ir kalbomis. Šia prasme programa yra žetonų seka. Pažodinis yra pagrindinis kompiuterinės kalbos elementas, kurio negalima dar labiau sumažinti. Gramatika apibrėžia sintaksiškai teisingų programų kūrimo taisykles. Teisingos yra tik tos programos, kurios žaidžia pagal gramatikoje apibrėžtas taisykles. Kalba yra tiesiog visų programų, kurios atitinka visas jūsų gramatikos taisykles, rinkinys.

Sintaksinės analizės metu kompiliatorius nagrinėja programos šaltinio kodą, atsižvelgdamas į kalbos gramatikoje apibrėžtas taisykles. Jei pažeidžiama kokia nors gramatikos taisyklė, kompiliatorius rodo klaidos pranešimą. Kartu, nagrinėdamas programą, kompiliatorius sukuria lengvai apdorojamą vidinį kompiuterio programos vaizdą.

Kompiuterio kalbos gramatikos taisykles galima vienareikšmiškai ir visiškai nurodyti EBNF (Extended Backus-Naur-Form) žymėjimu (daugiau informacijos apie EBNF žr. Ištekliai). EBNF apibrėžia gramatikas pagal gamybos taisykles. Gamybos taisyklė nurodo, kad gramatikos elementas - literalai arba sudaryti elementai - gali būti sudarytas iš kitų gramatikos elementų. Nerašomi literalai yra raktiniai žodžiai arba statinio programos teksto fragmentai, pavyzdžiui, skyrybos simboliai. Sudaryti elementai gaunami taikant gamybos taisykles. Gamybos taisyklės yra tokio bendro formato:

GRAMMAR_ELEMENT: = gramatikos elementų sąrašas | pakaitinis gramatikos elementų sąrašas 

Pažvelkime į mažos kalbos gramatikos taisykles, apibūdinančias pagrindines aritmetines išraiškas:

expr: = skaičius | expr '+' expr | expr '-' expr | expr '*' expr | expr '/' expr | '(' expr ')' | - expr skaičius: = skaitmuo + ('.' skaitmuo +)? skaitmuo: = „0“ | „1“ | „2“ | „3“ | „4“ | „5“ | „6“ | „7“ | „8“ | „9“ 

Trys gamybos taisyklės apibrėžia gramatikos elementus:

  • išr
  • numeris
  • skaitmenų

Ta gramatika apibrėžta kalba leidžia mums nurodyti aritmetines išraiškas. An išr yra skaičius arba vienas iš keturių infiksų operatorių, pritaikytų dviem išrs, an išr skliaustuose arba neigiamas išr. A numeris yra slankiojo kablelio skaičius su pasirinktine dešimtainio trupmena. Apibrėžiame a skaitmenų būti vienu iš žinomų dešimtainių skaitmenų.

Kodo generavimas arba vykdymas

Kai analizatorius sėkmingai analizuoja programą be klaidų, ji yra vidiniame vaizde, kurį kompiliatorius lengvai apdoroja. Dabar palyginti lengva generuoti mašininį kodą (arba „Java“ baitkodą šiuo klausimu) iš vidinio atvaizdavimo arba tiesiogiai atlikti vidinį atvaizdavimą. Jei darome pirmąjį, rengiame; pastaruoju atveju kalbame apie vertimą žodžiu.

„JavaCC“

„JavaCC“, galima nemokamai, yra analizatoriaus generatorius. Tai suteikia „Java“ kalbos plėtinį programavimo kalbos gramatikai nurodyti. „JavaCC“ iš pradžių sukūrė „Sun Microsystems“, tačiau dabar ją prižiūri „MetaMata“. Kaip ir bet kuris tinkamas programavimo įrankis, „JavaCC“ iš tikrųjų buvo naudojamas apibrėžiant gramatiką „JavaCC“ įvesties formatas.

Be to, „JavaCC“ leidžia mums apibrėžti gramatikas panašiai kaip EBNF, kad būtų lengva versti EBNF gramatikas į „JavaCC“ formatas. Be to, „JavaCC“ yra populiariausias „Java“ analizatorių generatorius su daugybe iš anksto nustatytų „JavaCC“ gramatikos, kurias galima naudoti kaip atspirties tašką.

Sukurkite paprastą skaičiuoklę

Dabar dar kartą peržiūrime savo mažąją aritmetinę kalbą, kad sukurtume paprastą komandų eilutės skaičiuoklę „Java“ sistemoje „JavaCC“. Pirmiausia turime išversti į EBNF gramatiką į „JavaCC“ formatuoti ir išsaugoti faile Aritmetika.jj:

parinktys {LOOKAHEAD = 2; } PARSER_BEGIN (aritmetinė) viešosios klasės aritmetika {} PARSER_END (aritmetika) PASKIRTIS: "\ t" TOKEN: double expr (): {} term () ("+" expr () dvigubas terminas (): {} "/" terminas ()) * double unary (): {} "-" elementas () dvigubas elementas (): {} "(" expr () ")" 

Aukščiau pateiktas kodas turėtų suteikti jums idėją, kaip nurodyti gramatiką „JavaCC“. galimybės skyriuje viršuje nurodomas tos gramatikos parinkčių rinkinys. Nurodome 2 išvaizdą. Papildomų parinkčių valdymas „JavaCC“derinimo funkcijos ir dar daugiau. Šias parinktis taip pat galima nurodyti „JavaCC“ komandinė eilutė.

PARSER_BEGIN sąlygoje nurodoma, kad laikomasi analizatoriaus klasės apibrėžimo. „JavaCC“ sugeneruoja po vieną Java klasę kiekvienam analizatoriui. Mes vadiname analizavimo klase Aritmetika. Kol kas mums reikalingas tik tuščias klasės apibrėžimas; „JavaCC“ vėliau pridės su analizavimu susijusias deklaracijas. Baigiame klasės apibrėžimą PARSER_END sąlyga.

PRALEISTI skyriuje nurodomi simboliai, kuriuos norime praleisti. Mūsų atveju tai yra baltosios erdvės simboliai. Toliau mes apibrėžsime savo kalbos žetonus TOKEN skyrius. Skaičius ir skaitmenis apibrėžiame kaip žetonus. Prisimink tai „JavaCC“ skiria žetonų ir kitų gamybos taisyklių apibrėžimus, kurie skiriasi nuo EBNF. PRALEISTI ir TOKEN skyriuose nurodoma šios gramatikos leksinė analizė.

Toliau apibrėžiame gamybos taisyklę išr, aukščiausio lygio gramatikos elementas. Atkreipkite dėmesį, kuo šis apibrėžimas labai skiriasi nuo išr EBNF. Kas vyksta? Na, pasirodo, kad aukščiau pateiktas EBNF apibrėžimas yra dviprasmiškas, nes leidžia kelis tos pačios programos vaizdus. Pavyzdžiui, panagrinėkime išraišką 1+2*3. Mes galime prilygti 1+2 į išr pasiduodantis 3 *, kaip parodyta 1 paveiksle.

Arba, kaip alternatyva, pirmiausia galėtume rungtyniauti 2*3 į išr dėl ko 1 + išraiška, kaip parodyta 2 paveiksle.

Su „JavaCC“, turime vienareikšmiškai nurodyti gramatikos taisykles. Dėl to mes išskaidome apibrėžimą išr į tris gamybos taisykles, apibrėžiančias gramatikos elementus išr, terminas, unariškasir elementas. Dabar išraiška 1+2*3 yra analizuojamas, kaip parodyta 3 paveiksle.

Iš komandinės eilutės galime paleisti „JavaCC“ patikrinti mūsų gramatiką:

javacc Arithmetic.jj „Java Compiler Compiler 1.1“ versija („Parser Generator“) Autorinės teisės (c) 1996–1999 „Sun Microsystems, Inc.“ Autorių teisės (c) 1997–1999 „Metamata, Inc.“ (įveskite „javacc“ be argumentų dėl pagalbos) Skaitymas iš failo Aritmetika.jj. . . Įspėjimas: žvilgsnio vadovo tinkamumo tikrinimas neatliekamas, nes parinktis LOOKAHEAD yra daugiau nei 1. Nustatykite FORCE_LA_CHECK parinktį į true for force check. Analizatorius sugeneruotas su 0 klaidų ir 1 įspėjimu. 

Tai patikrina, ar mūsų gramatikos apibrėžime nėra problemų, ir sukuria „Java“ šaltinių failų rinkinį:

TokenMgrError.java ParseException.java Token.java ASCII_CharStream.java Arithmetic.java ArithmeticConstants.java ArithmeticTokenManager.java 

Šie failai kartu įgyvendina analizatorių „Java“. Galite iškviesti šį analizatorių, iškviesdami Aritmetika klasė:

public class Aritmetika įgyvendina ArithmeticConstants {public Arithmetic (java.io.InputStream stream) {...} public Arithmetic (java.io.Reader stream) {...} public Arithmetic (ArithmeticTokenManager tm) {...} static final public double expr () throws ParseException {...} static final public double term () throws ParseException {...} static final public double unary () throws ParseException {...} static final public double element () throws ParseException {. ..} static public void ReInit (java.io.InputStream stream) {...} static public void ReInit (java.io.Reader stream) {...} public void ReInit (ArithmeticTokenManager tm) {...} static final public token getNextToken () {...} static final public Token getToken (int index) {...} static final public ParseException generParseException () {...} static final public void enable_tracing () {...} static final public void disable_tracing () {...}} 

Jei norėjote naudoti šį analizatorių, turite sukurti egzempliorių naudodami vieną iš konstruktorių. Konstruktoriai leidžia jums praleisti bet kurį „InputStream“, a Skaitytojasarba an „ArithmeticTokenManager“ kaip programos šaltinio kodo šaltinis. Tada nurodykite pagrindinį savo kalbos gramatikos elementą, pavyzdžiui:

Aritmetinis analizatorius = nauja aritmetika (System.in); parser.expr (); 

Tačiau kol kas nieko nevyksta, nes Aritmetika.jj mes apibrėžėme tik gramatikos taisykles. Mes dar nepridėjome kodo, reikalingo skaičiavimams atlikti. Norėdami tai padaryti, prie gramatikos taisyklių pridedame atitinkamus veiksmus. Calcualtor.jj yra visas skaičiuoklė, įskaitant veiksmus:

parinktys {LOOKAHEAD = 2; } PARSER_BEGIN (skaičiuoklė) public class Calculator {public static void main (String args []) meta ParseException {Calculator parser = new Calculator (System.in); while (tiesa) {parser.parseOneLine (); }}} PARSER_END (skaičiuoklė) PALEISTI: "\ t" TOKEN: void parseOneLine (): {dvigubas a; } {a = expr () {System.out.println (a); } | | {System.exit (-1); }} double expr (): {dvigubas a; dvigubas b; } {a = terminas () ("+" b = expr () {a + = b;} | "-" b = expr () {a - = b;}) * {grąžinti a; }} dvigubas terminas (): {dvigubas a; dvigubas b; } {a = vienarūšis () ("*" b = terminas () {a * = b;} | "/" b = terminas () {a / = b;}) * {grąžinti a; }} dvigubas vienarūšis (): {dvigubas a; } {"-" a = elementas () {return -a; } | a = elementas () {grąžinti a; }} dvigubas elementas (): {Žetonas t; dvigubas a; } {t = {grąžinti Double.parseDouble (t.toString ()); } | "(" a = expr () ")" {grąžinti a; }} 

Pagrindinis metodas pirmiausia sukuria analizatoriaus objektą, kuris nuskaito iš standartinio įvesties ir paskui iškviečia parseOneLine () nesibaigiančioje kilpoje. Metodas parseOneLine () pati apibrėžiama papildoma gramatikos taisykle. Ta taisyklė tiesiog apibrėžia, kad mes tikimės kiekvienos eilutės išraiškos savaime, kad tuščias eilutes įvesti yra gerai ir kad mes nutraukiame programą, jei pasieksime failo pabaigą.

Pakeitėme pradinių gramatikos elementų grąžinimo tipą dvigubai. Atitinkamus skaičiavimus atliekame ten, kur juos išanalizuojame, ir perkeliame skaičiavimo rezultatus į iškvietimo medį. Mes taip pat pertvarkėme gramatikos elementų apibrėžimus, kad jų rezultatai būtų saugomi vietiniuose kintamuosiuose. Pavyzdžiui, a = elementas () analizuoja an elementas ir saugo rezultatą kintamajame a. Tai leidžia mums analizuotų elementų rezultatus naudoti veiksmų dešinėje pusėje. Veiksmai yra „Java“ kodo blokai, kurie vykdomi, kai susieta gramatikos taisyklė rado atitikmenį įvesties sraute.

Atkreipkite dėmesį, kiek mažai „Java“ kodo pridėjome, kad skaičiuoklė veiktų visiškai. Be to, lengva pridėti papildomų funkcijų, tokių kaip įmontuotos funkcijos ar net kintamieji.

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