package prekladac; import derivacnistrom.*; import java.util.ArrayList; /** * Implementace Syntaktického analyzátoru -> vytvoření derivačního stromu * Při načítání kontroluje i správnost výrazů, použité typy proměnných a jejich deklarací * např v porovnání musí být oba výrazy stejného datového typu * stejně tak ve výrazu musí být všechny členy stejného typu * * TJ. Sémantická analýza se provádí rovnou za běhu - ušetříme */ public class Parser { Lexer lex; //reference na lexikální analyzátor LexSymbol lexSymb; //aktuálně zpracovávaný lexikální symbol int pocetVarovani;//nedeklarované proměnné,... TabulkaPromennych tabprom=new TabulkaPromennych(); /*******************************************************************************************/ /** * konstruktor beroucí si lexikální analyzátor */ public Parser(Lexer l) throws Exception { lex=l; nactiDalsiLexikalniSymbol(); if(lexSymb==lexSymb.EOF) { String zprava="zadany soubor je prazdny"; napisChybu(zprava,new Pozice(0,0)); } } /*******************************************************************************************/ /** * Tato funkce vrátí vytvořený derivační strom daného vstupu */ public DerivacniStrom vytvorDerivacniStrom()throws Exception { DerivacniStrom ds=null; DerivacniStrom.nastavitTabulkuPromennych(tabprom); //zde je blok try - abychom nevypisovali chybu z vytváření derivačního stromu dvakrát try { ds=blokprikazu();//kořenem je blokprikazu }catch(Exception ex){ throw new Exception("Chyba při vytváření derivačního stromu"); } //Nastavíme statickou referenci na použitou tabulku proměnných - zapisují do ní potomci této třídy //úspěch, či uživatel musí ještě ladit if(pocetVarovani>0)throw new Exception("Pri prekladu bylo zjisteno celkem "+pocetVarovani+" varovani."); //úspěch return ds; } /*******************************************************************************************/ /** * blok příkazů: přiřazení; cykly: for, do, while; podmínka; deklarace; výpis do konzole; příkazy ukonční; */ private DerivacniStrom blokprikazu() throws Exception { BlokPrikazuStrom d=new BlokPrikazuStrom(); //d protože jde o derivační strom - historický důvod //podle FIRST, resp. FOLLOW provedeme rozdělení switch(lexSymb) { //FIRST pravidla case FOR: d.nastavitPotomka( forcykl() ); //vytvoříme jako potomka return d; case DOUBLE: case INTEGER: case STRING: d.nastavitPotomka( deklarace() ); return d; case BREAK: case END: d.nastavitPotomka( ukonceni() ); return d; case IDENT: d.nastavitPotomka( prirazeni() ); return d; case WHILE: d.nastavitPotomka( whilecykl() ); return d; case PRINT: case PRINTLN: d.nastavitPotomka( konzole() ); return d; case DO: d.nastavitPotomka( docykl() ); return d; case IF: d.nastavitPotomka( ifpodm() ); return d; case SELECT: d.nastavitPotomka( sswitch() ); return d; //FOLLOW pravidla - přepíšeme na epsilon case NEXT: case ENDSELECT: case DEFAULT: case WEND: case DOWHILE: case ELSE: case ENDIF: case EOF: case CASE: return new PrazdnyStrom(); } //chyba neplatnySymbol( lexSymb.toString() ); // return d; } /*******************************************************************************************/ /** * SELECT CASE ( ident) case integer : blok DEFAULT: blok ENDSELECT * SELECT CASE (ident) potomek2 (case) potomek3(caselse) ENDSELECT potomek */ private DerivacniStrom sswitch() throws Exception { //FIRST: SELECT, pak CASE ( ident typu INTEGER ) porovnejSOcekavanymSymbolem(lexSymb.SELECT); porovnejSOcekavanymSymbolem(lexSymb.CASE); porovnejSOcekavanymSymbolem(lexSymb.LPAR); //typ identifikátoru porovnejSOcekavanymSymbolem(lexSymb.IDENT,false); int id=otestovatPromennou( lex.vratIdentifikator(),"INTEGER"); nactiDalsiLexikalniSymbol(); // porovnejSOcekavanymSymbolem(lexSymb.RPAR); SwitchStrom ss=new SwitchStrom(id); //nyní následuje blok CASE ss.nastavitPotomka2( ccase() ); //následuje default - caseelse ss.nastavitPotomka3( caseelse() ); //následuje ENDSELECT porovnejSOcekavanymSymbolem(lexSymb.ENDSELECT); //nakonec přilepíme blok příkazů ss.nastavitPotomka( blokprikazu() ); //hloubka tabprom.zvysitHloubku(1); //nahrání proměnné int na zásobník tabprom.zvysitHloubku(-1); //odebrání pro porovnání // return ss; } /*******************************************************************************************/ /** * CASE - součást celku příkazu SWITCH - zde SELECT CASE */ private DerivacniStrom ccase() throws Exception { switch(lexSymb) { //FIRST pravidla case CASE: //následuje znaménko a celé číslo nactiDalsiLexikalniSymbol(); LexSymbol ls=znamenko(); porovnejSOcekavanymSymbolem(ls.INTEGER_CONST,false); int cislo=lex.vratHodnotu().intValue(); nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(ls.COLON); // if(ls==ls.MINUS){cislo=-cislo;} SwitchStrom ss=new SwitchStrom( new Integer(cislo) ); //nyní následuje blok příkazů ss.nastavitPotomka2( blokprikazu() ); //nyní přilepéíme nakonec další case ss.nastavitPotomka( ccase() ); // return ss; //FOLLOW pravidla case ENDSELECT:case DEFAULT: return new PrazdnyStrom(); } //CHYBA neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * CASEELSE - součát příkazu switch - SELECT CASE * DEFAULT: blok příkazů */ private DerivacniStrom caseelse() throws Exception { switch(lexSymb) { //FIRST pravidla case DEFAULT: nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.COLON); SwitchStrom ss=new SwitchStrom(true); ss.nastavitPotomka2( blokprikazu() ); // return ss; //FOLLOW pravidla case ENDSELECT: return new PrazdnyStrom(); } //CHYBA neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * Podmínka IF * IF (podmínka) THEN blokpříkazů ELSE blokpříkazů ENDIF * IF ( potomek3 ) THEN potomek2 ELSE potomek4 potomek */ private DerivacniStrom ifpodm() throws Exception { //FIRST: IF. následuej závorka porovnejSOcekavanymSymbolem(lexSymb.IF); porovnejSOcekavanymSymbolem(lexSymb.LPAR); IFstrom is=new IFstrom(); //následuje podmínka is.nastavitPotomka3( podminka() ); //hloubka is.referenceNaPotomka3().vratitPotrebnouHloubku(); // is.nastavitTyp( ((PodminkaStrom)is.referenceNaPotomka3()).vratitTyp() ); //následuje pravá závorka a THEN porovnejSOcekavanymSymbolem(lexSymb.RPAR); porovnejSOcekavanymSymbolem(lexSymb.THEN); //následuje blok příkazů - vnitřek co se má provádět is.nastavitPotomka2( blokprikazu() ); //následuje ELSE část is.nastavitPotomka4( ifelse() ); //následuje ENDIF porovnejSOcekavanymSymbolem(lexSymb.ENDIF); //nakonec přilepíme blokpříkazů is.nastavitPotomka( blokprikazu() ); // return is; } /*******************************************************************************************/ /** * Nepovinná část podmínky ELSE - vrací blok s alternativním prováděním podmínky */ private DerivacniStrom ifelse() throws Exception { switch(lexSymb) { //FIST pravidla case ELSE: nactiDalsiLexikalniSymbol(); return blokprikazu(); //FOLLOW pravidla case ENDIF: return null; } //CHYBA neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * Do cykl * DO blokpříkazů DOWHILE(podmínka) */ private DerivacniStrom docykl() throws Exception { //FIRST - do porovnejSOcekavanymSymbolem(lexSymb.DO); DOstrom ds=new DOstrom(); //vnitřní blok příkazů ds.nastavitPotomka2( blokprikazu() ); //následuje DOWHILE a závorka porovnejSOcekavanymSymbolem(lexSymb.DOWHILE); porovnejSOcekavanymSymbolem(lexSymb.LPAR); //v závorce je podmínka ds.nastavitPotomka3( podminka() ); //hloubka ds.referenceNaPotomka3().vratitPotrebnouHloubku(); // ds.nastavitTyp( ((PodminkaStrom)ds.referenceNaPotomka3()).vratitTyp() ); //následuje pravá závorka porovnejSOcekavanymSymbolem(lexSymb.RPAR); //nakonec přilepíme blok příkazů ds.nastavitPotomka( blokprikazu() ); // return ds; } /*******************************************************************************************/ /** * Výpis do konzole: PRINT(vyraz) PRINTLN(vyraz) * vypis ( potomek2 ) potomek */ private DerivacniStrom konzole() throws Exception { //první je výpis LexSymbol ls=vypis(); //následuje závorka porovnejSOcekavanymSymbolem(ls.LPAR); //následuje výraz VyrazStrom vs=(VyrazStrom)vyraz(); String typ=zkontrolovatTypVyrazu(vs,null); //hloubka tabprom.zvysitHloubku(1); //getstatic.... vs.vratitPotrebnouHloubku(); tabprom.zvysitHloubku( -tabprom.vratitMinulyPrispevek() ); //vyzvendneme hodnotu ze zásobníku tabprom.zvysitHloubku(-1); //get static se vyjme // KonzoleStrom ks=new KonzoleStrom(ls,typ); ks.nastavitPotomka2( vs ); //následuje pravá závorka a středník porovnejSOcekavanymSymbolem(ls.RPAR); porovnejSOcekavanymSymbolem(ls.SEMICOLON); //nakonec přilepíme blok příkazů ks.nastavitPotomka( blokprikazu() ); // return ks; } /*******************************************************************************************/ /** * Výpis: pomocná funkce vracící typ výpisu - se zalomením, bez zalomení */ private LexSymbol vypis() throws Exception { //FIRST pravidla switch(lexSymb) { case PRINT:case PRINTLN: LexSymbol ls=lexSymb; nactiDalsiLexikalniSymbol(); return ls; } //chyba napisChybu("byl ocekavan symbol nebo <"+lexSymb.toString()+">",lex.vratPolohuPocatkuSymbolu()); return lexSymb; } /*******************************************************************************************/ /** * Podmínka: výraz porovnání výrazu */ private DerivacniStrom podminka() throws Exception { PodminkaStrom ps=new PodminkaStrom(); //na levé straně je výraz ps.nastavitPotomka2( vyraz() ); String typVyrazu=zkontrolovatTypVyrazu( (VyrazStrom)ps.referenceNaPotomka2(),null ); //následuje operátor ps.nastavitOperator( porovnani() ); //následuje váraz na pravé straně ps.nastavitPotomka( vyraz() ); String typVyrazu2=zkontrolovatTypVyrazu( (VyrazStrom)ps.referenceNaPotomka(),null ); //výrazy musí být stejného typu if( !typVyrazu.equalsIgnoreCase(typVyrazu2) ) { //chyba varovani("datovy typ vyrazu na leve strane se neshoduje s typem na prave strane",lex.vratCisloRadku()); }else if( typVyrazu.equalsIgnoreCase("STRING") ) { //chyba varovani("nelze porovnat řetězce, pouze číselné výrazy",lex.vratCisloRadku()); } ps.nastavitTyp(typVyrazu); // return ps; } /*******************************************************************************************/ /** * Porovnání: == != < > <= >= */ private LexSymbol porovnani() throws Exception { //FIRST pravidla switch(lexSymb) { case EQ:case NE:case GT:case LT:case GE:case LE: LexSymbol ls=lexSymb; nactiDalsiLexikalniSymbol(); return ls; } //chyba napisChybu("byl ocekavan symbol relace porovnani, ale byl nalezen symbol <"+lexSymb.toString()+">",lex.vratPolohuPocatkuSymbolu()); return lexSymb; } /*******************************************************************************************/ /** * While cyklus: WHILE( podmínka ) blokpříkazů WEND */ private DerivacniStrom whilecykl() throws Exception { //FIRST pravidlo - WHILE porovnejSOcekavanymSymbolem(lexSymb.WHILE); porovnejSOcekavanymSymbolem(lexSymb.LPAR); // WHILEstrom ws=new WHILEstrom(); //následuje podmínka ws.nastavitPotomka3( podminka() ); //hloubka ws.referenceNaPotomka3().vratitPotrebnouHloubku(); // ws.nastavitTyp( ((PodminkaStrom)ws.referenceNaPotomka3()).vratitTyp() ); //po té ukončení závorky porovnejSOcekavanymSymbolem(lexSymb.RPAR); //následuje vnitřní blok příkazů ws.nastavitPotomka2( blokprikazu() ); //vše je ukončeno WEND porovnejSOcekavanymSymbolem(lexSymb.WEND); //nakonec přilepíme blok příkazů ws.nastavitPotomka( blokprikazu() ); // return ws; } /*******************************************************************************************/ /** * Výraz: (a) 5+a 5+(a*6)%6/8+1-8 * VYRAZ: ndmd vyrazc */ private DerivacniStrom vyraz() throws Exception { //FIRST pravidla switch(lexSymb) { case UVOZOVKY: //následuje STROBSAH nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.STROBSAH,false); String obsah=lex.vratIdentifikator(); //následují uvozovky nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.UVOZOVKY); // return new VyrazStrom(obsah); case PLUS: case MINUS: case LPAR: case IDENT:case INTEGER_CONST:case DOUBLE_CONST: //Výraz: ndmd vyrazc VyrazStrom vs=new VyrazStrom(); vs.nastavitPotomka2( ndmd() ); vs.nastavitPotomka( vyrazc() ); return vs; } neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * Výraz s čárkou * VYRAZC: Operátor ndmd vyrazc * epsilon */ private DerivacniStrom vyrazc() throws Exception { switch(lexSymb) { //FOLLOW pravidla - nahradíme epsilon case EQ:case NE:case GT:case LT:case GE:case LE: case RPAR:case SEMICOLON: return new PrazdnyStrom(); //FIRST pravidla //VýrazC: PLUS ndmd vyrazc // MINUS ndmd vyrazc case PLUS:case MINUS: OperaceStrom os=new OperaceStrom(lexSymb); nactiDalsiLexikalniSymbol(); os.nastavitPotomka2( ndmd() ); os.nastavitPotomka( vyrazc() ); // return os; } neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * NDMD: zavrk ndmdc */ private DerivacniStrom ndmd() throws Exception { //FIRST pravidla switch(lexSymb) { case PLUS: case MINUS: case LPAR: case IDENT:case INTEGER_CONST:case DOUBLE_CONST: //NDMD: zavrk ndmdc VyrazStrom vs=new VyrazStrom(); vs.nastavitPotomka2( zavrk() ); vs.nastavitPotomka( ndmdc() ); return vs; } neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * NDMDC - násobením dělení, modulo, celočíselné dělení - s čárkou * NDMDC: Operátor zavrk ndmdc * epsilon */ private DerivacniStrom ndmdc() throws Exception { switch(lexSymb) { //FOLLOW pravidla - nahradíme epsilon case EQ:case NE:case GT:case LT:case GE:case LE: case RPAR:case SEMICOLON:case PLUS:case MINUS: return new PrazdnyStrom(); //FIRST pravidla //NDMDC: TIMES zavrk ndmdc // ostatní obdobně case TIMES:case DIVIDED:case MODULO:case INTDIV: OperaceStrom os=new OperaceStrom(lexSymb); nactiDalsiLexikalniSymbol(); os.nastavitPotomka2( zavrk() ); os.nastavitPotomka( ndmdc() ); // return os; } neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * Závorka: ( výraz ) * ident * znaménko hodnota */ private DerivacniStrom zavrk() throws Exception { //FIRST pravidla switch(lexSymb) { //Závorka: LPAR vyraz RPAR // ident // znamenko hodnota case IDENT: //IDENT - musí být deklarovaný porovnejSOcekavanymSymbolem(lexSymb.IDENT,false); int id=otestovatPromennou( lex.vratIdentifikator() ); VyrazStrom vs=new VyrazStrom(id); // nactiDalsiLexikalniSymbol(); // return vs; case LPAR: //( výraz ) VyrazStrom vs2=new VyrazStrom(true); nactiDalsiLexikalniSymbol(); //nesmí být vnořený string if(lexSymb==lexSymb.UVOZOVKY)napisChybu( "neni povolovano vnorovani promennych typu STRING",lex.vratPolohuPocatkuSymbolu() ); // vs2.nastavitPotomka2( vyraz() ); porovnejSOcekavanymSymbolem(lexSymb.RPAR); // return vs2; case PLUS:case MINUS: case INTEGER_CONST:case DOUBLE_CONST: //znamenko hodnota VyrazStrom vs3; boolean minus=false; LexSymbol l=znamenko(); if(l==LexSymbol.MINUS)minus=true; Number hod=hodnota(); //rozlišení hodnoty vs3=new VyrazStrom(hod,minus); // return vs3; } neplatnySymbol( lexSymb.toString() ); return new PrazdnyStrom(); } /*******************************************************************************************/ /** * Tato funkce vrací obejkt typu Number s požadovaných číselným obsahem * HODNOTA: integer_const || double_const */ private Number hodnota() throws Exception { //FIRST pravidla - musí jít o znak PLUS nebo MINUS if(lexSymb!=lexSymb.INTEGER_CONST && lexSymb!=lexSymb.DOUBLE_CONST) { //chyba Pozice p=lex.vratPolohuPocatkuSymbolu(); String zprava="chyba v syntaxi - byl ocekavan nebo , ale byl nalezen <"+lexSymb+">"; napisChybu(zprava,p); } // Number n=lex.vratHodnotu(); nactiDalsiLexikalniSymbol(); // return n; } /*******************************************************************************************/ /** * přiřazení: a= .. výraz .. */ private DerivacniStrom prirazeni() throws Exception { //akdutální symbol je identifikátor, vyhledáme ho v tabulce symbolů int id=otestovatPromennou( lex.vratIdentifikator() ); //musí následovat rovnítko nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.ASSIGN); //vytvoříme nový uzel stromu PrirazeniStrom ps=new PrirazeniStrom(id); //vyhodnocení výrazu - výraz bude potomkem přiřazení ps.nastavitPotomka2( vyraz() ); //je proměnná stejného typu jako výraz? zkontrolovatTypVyrazu( (VyrazStrom)ps.referenceNaPotomka2(),tabprom.vratitTypPromenne(id) ); //hloubka ps.referenceNaPotomka2().vratitPotrebnouHloubku(); tabprom.zvysitHloubku(-tabprom.vratitMinulyPrispevek()); //STORE //nakonci je středník porovnejSOcekavanymSymbolem(lexSymb.SEMICOLON); //nakonec přilepíme blok příkazů ps.nastavitPotomka( blokprikazu() ); return ps; } /*******************************************************************************************/ /** * ukončení provádění - break: vyskočení z cyklu; end: ukončení programu */ private DerivacniStrom ukonceni() throws Exception { //aktuální symbol je příkaz na ukončení - END nebo BREAK UkonceniStrom us=new UkonceniStrom( lexSymb.toString() ); //následuje středník nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.SEMICOLON); //nakonec zřetězíme blok příkazů us.nastavitPotomka( blokprikazu() ); return us; } /*******************************************************************************************/ /** * deklarace - integer a; double c LISTINDENT; */ private DerivacniStrom deklarace() throws Exception { //FIRST: TYPY: skočíme do přechodu typy - zjistíme typ proměnné String typ; typ=typy(); // ArrayList seznam=new ArrayList(); //kolekce ID proměnných String nazev; //název proměnné int id; //id nově vytvořené proměnné; -1 při neúspěchu //IDENT porovnejSOcekavanymSymbolem(lexSymb.IDENT,false);//bez načítání dalšího symbolu nazev=lex.vratIdentifikator(); id=tabprom.vlozitPromennou(nazev,typ); if(id==-1) { varovani( promennaJizDeklarovana(nazev),lex.vratPolohuPocatkuSymbolu() );//proměnná už je deklarovaná } seznam.add(id); //přidáme proměnnou do tabulky proměnných tohoto uzlu nactiDalsiLexikalniSymbol(); //LISTIDENT - následuje - zavoláme ho seznam=listident(seznam,typ); //vše je ukončeno středníkem porovnejSOcekavanymSymbolem(lexSymb.SEMICOLON); //vytvoříme uzel stromu - Deklarace, předáme mu typ a seznam proměnných DeklaraceStrom ds=new DeklaraceStrom(typ,seznam); //nakonec napojíme další příkaz ds.nastavitPotomka( blokprikazu() ); // return ds; } /*******************************************************************************************/ /** * tato metoda přidává deklarace - proměnné do tabulky proměnných - nepovinná část deklarace * , a, c60 */ private ArrayList listident(ArrayList promenne,String typ) throws Exception { //FOLLOW pravidlo - vrátíme pouze vstup - tj. přepíšeme na epsilon if(lexSymb==lexSymb.SEMICOLON)return promenne; //FIRST: COMMA porovnejSOcekavanymSymbolem(lexSymb.COMMA); //následuje IDENT porovnejSOcekavanymSymbolem(lexSymb.IDENT,false);//bez načítání dalšího symbolu //provedeme kontrolu a vložení do tabulky String nazev; int id; nazev=lex.vratIdentifikator(); id=tabprom.vlozitPromennou(nazev,typ); if(id==-1) { varovani( promennaJizDeklarovana(nazev),lex.vratPolohuPocatkuSymbolu() );//proměnná už je deklarovaná } promenne.add(id);//přidáme proměnnou do tabulky proměnných uzlu deklarace nactiDalsiLexikalniSymbol(); //zavoláme LISTIDENT return listident(promenne,typ); } /*******************************************************************************************/ /** * tato funkce vrátí typ proměnné */ private String typy() throws Exception { String s=lexSymb.toString(); nactiDalsiLexikalniSymbol(); return s; } /*******************************************************************************************/ /** * for cyklus: for(i=znamenko a to znamenko b {step znamenko c}) potomek2 next potomek1 * Znaménko se vyhodnocuje pomocí přechodu ZNAMENKO */ private DerivacniStrom forcykl() throws Exception { porovnejSOcekavanymSymbolem(lexSymb.FOR); porovnejSOcekavanymSymbolem(lexSymb.LPAR); porovnejSOcekavanymSymbolem(lexSymb.IDENT,false); //proměnná musí být deklarována a musí být správného typu int id=otestovatPromennou(lex.vratIdentifikator(),"INTEGER"); // = (ASSIGN) nactiDalsiLexikalniSymbol(); porovnejSOcekavanymSymbolem(lexSymb.ASSIGN); //nyní může být znaménko boolean minus=false; LexSymbol l=znamenko(); if(l==LexSymbol.MINUS)minus=true; //číslo porovnejSOcekavanymSymbolem(lexSymb.INTEGER_CONST); int a=lex.vratHodnotu().intValue(); if(minus)a=-a; //TO porovnejSOcekavanymSymbolem(lexSymb.TO); //číslo se znaménkem minus=false; l=znamenko(); if(l==LexSymbol.MINUS)minus=true; porovnejSOcekavanymSymbolem(lexSymb.INTEGER_CONST); int b=lex.vratHodnotu().intValue(); if(minus)b=-b; //následuje přechod FORSTEP int c; c=forstep(); //následuje pravá závorka porovnejSOcekavanymSymbolem(lexSymb.RPAR); //přivěsíme vnitřní blok příkazů cyklu FORstrom ds=new FORstrom(id,a,b,c); ds.nastavitPotomka2( blokprikazu() ); //NEXT porovnejSOcekavanymSymbolem(lexSymb.NEXT); //po next následuje blok příkazů ds.nastavitPotomka( blokprikazu() ); //hloubka tabprom.zvysitHloubku(2); //LDC from (1), ldc from, ldc TO tabprom.zvysitHloubku(-2);//store from (1), compare FROM TO return ds; } /*******************************************************************************************/ /** * tato funkce vrací nepovinný parametr STEP do forcyklu */ public int forstep()throws Exception { //FOLLOW pravidlo - přepíšeme na epsilon - vrátíme 1 pokud je na vstupu pravá závorka if(lexSymb==LexSymbol.RPAR)return 1; //FIRST pravidlo - STEP porovnejSOcekavanymSymbolem(lexSymb.STEP); int c;//krok cyklu boolean minus=false; //následuje znaménko LexSymbol l=znamenko(); if(l==LexSymbol.MINUS)minus=true; //následuje celé číslo porovnejSOcekavanymSymbolem(lexSymb.INTEGER_CONST); c=lex.vratHodnotu().intValue(); if(minus)c=-c; // return c; } /*******************************************************************************************/ /** * Tato metoda načte ze vstupu znaménko */ public LexSymbol znamenko() throws Exception { //FOLLOW PRAVIDLA - double const, integer const - přepíšeme na epsilon if(lexSymb==lexSymb.DOUBLE_CONST || lexSymb==lexSymb.INTEGER_CONST)return LexSymbol.EPSILON; //FIRST pravidla - musí jít o znak PLUS nebo MINUS if(lexSymb!=lexSymb.PLUS && lexSymb!=lexSymb.MINUS) { //chyba Pozice p=lex.vratPolohuPocatkuSymbolu(); String zprava="chyba v syntaxi - byl ocekavan nebo , ale byl nalezen <"+lexSymb+">"; napisChybu(zprava,p); } // LexSymbol l=lexSymb; nactiDalsiLexikalniSymbol(); // return l; } /*******************************************************************************************/ /** * Tato metoda zjistí, je li celý podstrom daného typu */ public String zkontrolovatTypVyrazu(VyrazStrom vs,String typ)throws Exception { boolean chyba=false; //TEST typu string if(vs.vratString()!=null) { if(typ==null)typ="STRING"; else if( !typ.equalsIgnoreCase("STRING") )chyba=true; //Test ostatních výrazů }else{ String t=zkontrolovatTypVyrazuRekurze(vs,typ); if(typ==null)typ=t; //kontrola chyby se provádí automaticky uvnitř funkce } //výpis chyby pokud nastala - pouze pro proměnné typu STRING if(chyba) { varovani("zadany vyraz neni typu <"+typ+">",lex.vratPolohuPoslednihoSymbolu()); } // return typ; } /*******************************************************************************************/ /** * Tato funkce rekurzivně projde celý výraz, kontroluje jednotnost datových typů */ //používáme globální proměnnou - aby jsme ověřily platnost komplexného výrazu String typGlobal=null; public String zkontrolovatTypVyrazuRekurze(DerivacniStrom ds,String typ)throws Exception { typGlobal=typ; String kontrolni=zkontrolovatTypVyrazuRekurze(ds); //kontrola jednontnosti if( !kontrolni.equalsIgnoreCase(typ) && typ!=null ) { varovani("vyraz neni pozadovaneho typu <"+typ+">",lex.vratCisloRadku() ); } // return kontrolni; } /*******************************************************************************************/ /** * Funkce volaná při validaci */ private String zkontrolovatTypVyrazuRekurze(DerivacniStrom ds)throws Exception { //Jsme v listu? String t=null; /*****************************/ if( ds instanceof VyrazStrom ) { VyrazStrom vs=(VyrazStrom)ds; //IDENT if( vs.vratIdent()!=null ) { t=tabprom.vratitTypPromenne( vs.vratIdent().intValue() ); // if(typGlobal==null)typGlobal=t; else{ //otestovatPromennou(vs.vratIdent().intValue(),null);//deklarace if( !t.equalsIgnoreCase(typGlobal) ) { varovani( "promenna <"+tabprom.najitPromennou(vs.vratIdent().intValue())+"> neni typu "+typGlobal,lex.vratCisloRadku() ); } } // return typGlobal; } //CISLO if( vs.vratHodnotu()!=null ) { Number n=vs.vratHodnotu(); t="INTEGER"; //jedná se o číslo typu INTEGER if(n instanceof Double)t="DOUBLE";//jedná se o číslo typu DOUBLE if(typGlobal==null)typGlobal=t; else if( !t.equalsIgnoreCase(typGlobal) ) varovani("cislo <"+n.toString()+"> neni typu <"+typGlobal+">",lex.vratCisloRadku()); // return typGlobal; } /*****************************/ //Některé operandy vyžadují specifický typ proměnných // MODULO : integer // INTDIV : double }else if( ds instanceof OperaceStrom ) { OperaceStrom os=(OperaceStrom)ds; LexSymbol ls=os.vratitOperaci(); // % if(ls==ls.MODULO) { //natvrdo požadujeme typ integer typGlobal="INTEGER"; } // \ else if(ls==ls.INTDIV) { //natvrdo požadujeme typ double typGlobal="DOUBLE"; } // } /*****************************/ //nejsme v listu, zavoláme potomky - nejdřív levého if(ds.referenceNaPotomka2()!=null) { t=zkontrolovatTypVyrazuRekurze(ds.referenceNaPotomka2()); if(typGlobal==null)typGlobal=t; //test typu if( !t.equalsIgnoreCase(typGlobal) ) varovani("vyraz "+ds.referenceNaPotomka2().toString()+" neni typu <"+typGlobal+">",lex.vratCisloRadku() ); } //poté pravého if(ds.referenceNaPotomka()!=null) { //System.out.println( ds.referenceNaPotomka().toString() ); t=zkontrolovatTypVyrazuRekurze(ds.referenceNaPotomka()); if(typGlobal==null)typGlobal=t; //test typu if( !t.equalsIgnoreCase(typGlobal) ) varovani("vyraz < "+ds.referenceNaPotomka().toString()+" neni typu <"+typGlobal+">",lex.vratCisloRadku() ); } // return typGlobal; } /*******************************************************************************************/ /** * Tato metoda otestuje, jestli je daná proměnná deklarována a poté jestli je správného typu * Vrací ID dané proměnné, pokud není proměnná definovánam vrací -1 */ public int otestovatPromennou(String nazev) throws Exception { return otestovatPromennou(nazev,null); } public int otestovatPromennou(String nazev,String typ) throws Exception { int id=tabprom.najitPromennou(nazev); if(id==-1) { varovani( promennaNedeklarovana(nazev),lex.vratPolohuPocatkuSymbolu() ); return id; } return otestovatPromennou(id,typ); } public int otestovatPromennou(int id,String typ) throws Exception { if(id==-1) { varovani( promennaNedeklarovana( tabprom.najitPromennou(id) ),lex.vratPolohuPocatkuSymbolu()); }else if(!tabprom.otestovatTypPromenne(id,typ) && typ!=null){ varovani( prommenaChybnehoTypu( tabprom.najitPromennou(id),typ),lex.vratPolohuPocatkuSymbolu()); } return id; } /*******************************************************************************************/ /** * Tato metoda načte z Lexeru další lexiální symbol */ public void nactiDalsiLexikalniSymbol() throws Exception { lexSymb=lex.vratDalsiLexikalniSymbol(); } /*******************************************************************************************/ /** * Tato metoda porovná aktuálně zpracovávaný lexikální symbol se symbolem, který by tu měl teoretický být, pokud není uvedeno jinak., načte další lexikální symbol */ public void porovnejSOcekavanymSymbolem(LexSymbol ls) throws Exception { porovnejSOcekavanymSymbolem(ls,true); } public void porovnejSOcekavanymSymbolem(LexSymbol ls,boolean nacistDalsiSymbol) throws Exception { if(ls==lexSymb) { if(nacistDalsiSymbol)nactiDalsiLexikalniSymbol(); }else{ //chyba v syntaxi vypíšeme do konzole Pozice p=lex.vratPolohuPocatkuSymbolu(); String zprava="chyba v syntaxi - byl ocekavan <"+ls+">, ale byl nalezen <"+lexSymb+">"; napisChybu(zprava,p); } } /*******************************************************************************************/ /** * Tato procedura vypíše chybovou hlášku do konzole a vyhodí vyjímku, pokud to není zakázáno */ private void napisChybu(String x,Pozice p, boolean vyhoditVyjimku) throws Exception { x="radek "+p.vratY()+", sloupec "+p.vratX()+": "+x; System.err.println(x); if(vyhoditVyjimku)throw new Exception(x); } private void napisChybu(String x,Pozice p) throws Exception { napisChybu(x,p,true); } /*******************************************************************************************/ /** * Tato procedura zavolá vypsání chyby do konzole, ale nevyhodí vyjímku - jedná se o ,,varování'' */ private void varovani(String x,Pozice p) throws Exception { pocetVarovani++; napisChybu(x,p,false); } /*******************************************************************************************/ /** * Blok funkcí pro pomoc při vypisování chybových hlášek */ private String promennaNedeklarovana(String nazev) { return "varovani: promenna <"+nazev+"> nebyla deklarovana"; } private String promennaJizDeklarovana(String nazev) { return "varovani: promenna <"+nazev+"> je jiz deklarovana"; } private String prommenaChybnehoTypu(String nazev,String maByt) { return "varovani: promnenna <"+nazev+"> neni typu "+maByt; } public void neplatnySymbol(String nazev) throws Exception { napisChybu("byl nalezen neplatný symbol <"+nazev+">", lex.vratPolohuPocatkuSymbolu()); } }