Functii pentru generarea de cod
Functiile pentru generarea de cod realizeaza completarea tabelei de cod TAB_COD cu instructiuni ale limbajului virtual, date ca parametri.
La o executie, o functie de generare de cod depune in TAB_COD o instructiune virtuala. Ca parametri ai functiei trebuie sa se furnizeze codul instructiunii virtuale si eventualele argumente ale acesteia.
In cele ce urmeaza vom considera ca avem o singura functie de generare, numita GEN, care accepta numar variabil de parametri, iar explicatiile vor fi axate pe limbajul virtual din Anexa C. In acest caz, exemple de apeluri ale functiei GEN ar putea fi:
GEN (OUT)GEN (CALL, adr_salt, nr_param, nivel)
Forma concreta de implementare a functiei GEN
depinde de limbajul in care se scrie compilatorul. Cateva solutii posibile:
procedure GEN(cod, arg1, arg2, . . . , argn) isTAB_COD[ICOD] = codend GEN
TAB_COD[ICOD + 1] = arg1
TAB_COD[ICOD + 2] = arg2
. . .
TAB_COD[ICOD + n] = argn
ICOD = ICOD + n + 1
Variabila ICOD este o variabila globala
a compilatorului (v. Lucrarea nr.8), ea indicand in orice moment prima
intrare libera din TAB_COD. La initializarea variabilei ICOD
se tine cont ca primele locatii din TAB_COD urmeaza sa contina informatii
necesare executivului. In cazul masinii virtuale definite in
Lucrarea nr.10 numarul acestor locatii este 3.
Obs: a nu se confunda variabila ICOD cu registrul NI; desi ambele joaca rol de indice in TAB_COD, ICOD este utilizata doar de compilator in procesul de completare a tabelei de cod (ea nici nu este vizibila in executiv), in timp ce NI este utilizat doar de executiv (nu este vizibil in compilator) pentru a parcurge tabela gata completata . ICOD baleiaza intrarile tabelei de cod in ordine fizica, pe cand NI evolueaza conform ordinii logice a instructiunilor programului
Exemple de apeluri ale functiei GEN
Modul in care se apeleaza functia GEN va fi ilustrat cu ajutorul marcajelor inserate in productii, pentru fiecare tip de instructiune a limbajului sursa dat in Anexa B. Integrarea rutinelor corespunzatoare marcajelor cu analizorul sintactico-semantic se realizeaza dupa modelul prezentat in Anexa D.
In rutinele ce vor fi prezentate mai jos se va evidentia
doar partea de generare de cod, presupunandu-se ca verificarile semantice
(de domeniu si de tipuri) sunt deja incluse in compilator.
Instr_atrib -> Variabila :=Expresie1![]()
Variabila -> identificator1|
identificator2[Expresie2]|
identificator3. identificator4
loc1 = Cauta _TS (identificator1)
if TS[loc1].CLASA = 'nume de functie' thenGEN(LODA,VNIVEL,TS[loc1].ADREL)elseGEN(LODA, TS[loc1].NIVEL, TS[loc1].ADREL)endif
Obs: in cazul in care membrul stang al unei atribuiri este un nume de functie, adresa lui se calculeaza fata de blocul curent din stiva de lucru (v. Anexa C, § C.6).
*fie loc2 indicele intrarii din TS corespunzatoare lui identificator2Obs: in acest punct, in faza de executie a programului, tocmai a avut loc evaluarea expresiei de la indice; deci, in varful stivei de lucru se afla rezultatul acestei expresii, rezultat pe care se bazeaza calculul adresei pentru elementele de tablou.
*genereaza secventa de verificare a indicelui
*genereaza secventa de incarcare a adresei elementului de tablou
Secventa de verificare a indicelui este:
COPYSecventa de incarcare a adresei elementului de tablou este:
LODI Insert_const( 'intreg', TS[loc2].IND_MIN)
LES
FJP ICOD+4
ERR nr_mesaj_depasire_limita_inferioara_indice
COPY
LODI Insert_const( 'intreg', TS[loc2].IND_MAX )
GRT
FJP ICOD+4
ERR nr_mesaj_depasire_limita_superioara_indice
LODI Insert_const( 'intreg', TS[loc2].IND_MIN )
SUB
LODA TS[loc2].NIVEL TS[loc2].ADREL
ADD
*fie loc3 si loc4 indicii in TS unde se afla identificator3, respectiv identificator4Secventa de incarcare a adresei de camp este:
*genereaza secventa de incarcare a adresei campului de structura
LODA TS[loc3].NIVEL TS[loc3].ADREL
LODI Insert_const( 'intreg', TS[loc4].DEPLREC )
ADD
*fie tipv si tipe_1 tipurile determinate pentru Variabila, respectiv Expresie1Tinand cont de restrictiile privitoare la tipuri, formulate in Lucrarea nr.9, rezulta ca singura situatie admisa in care tipv <> tipe_1 este cea in care tipv = 'real' si tipe_1 = 'intreg'. In consecinta, secventa de conversie este:
if tipv <> tipe_1 then*genereaza secventa de conversie a valorii din varful stivei (valoarea expresiei)endif
GEN(STO)CONV 'real' 0
Conditii si expresii
Conditie ->Expr_logica |
Expr_logica -> Expr_rel |not Expr_logicaExpr_rel -> Expresie1Op_rel Expresie2Expr_logica Op_log Expr_rel|
Expresie -> Termen1 |( Conditie )Termen -> Factor1 |Expresie3Op_ad Termen2Factor -> identificator1Termen3Op_mul Factor2|
Constanta|
identificator2[Expresie1]
|
identificator3.identificator4
|
identificator5(
Lista_expresii)
|
( Expresie )
Vom utiliza urmatoarele notatii:
if tipe_1 <> tipe_2 thenif tipe_1 = 'intreg' thenendifGEN(CONV, 'real', 1)elseGEN(CONV, 'real', 0)endif
select Op_rel of> : GEN(GRT)endselect
< : GEN(LES)
>= : GEN(GEQ)
<= : GEN(LEQ)
= : GEN(EQU)
<> : GEN(NEQ)
if tipe_3 <> tipt_2 thenif tipe_3 = 'intreg' thenendifGEN(CONV, 'real', 1)elseGEN(CONV, 'real', 0)endif
ifOp_ad = op_plus thenGEN(ADD)elseGEN(SUB)endif
Ca si, dar se aplica operatiile de inmultire/impartire.
loc_1 = Cauta _TS(identificator1)
select TS[loc_1].CLASA of'nume de constanta ':endselectGEN(LODI, TS[loc_1].VAL)'nume variabila simpla ','nume parametru formal':GEN(LOD, TS[loc_1].NIVEL, TS[loc_1].ADREL) (**)'nume de functie':*se procedeaza ca lasi
Aici, atomul este o constanta, deci atributul completat de ALEX este indicele in TAB_CONST unde se afla valoarea constantei.
GEN(LODI, atom.atribut )
loc_2 = Cauta _TS(identificator2)Incarcarea valorii elementului de tablou este realizata de secventa:
*genereaza secventa de verificare a indicelui
*genereaza secventa de incarcare a valorii elementului de tablou (**)
LODI Insert_const('intreg', TS[loc_2].IND_MIN)
SUB
MVRX
LODX TS[loc_2].NIVEL TS[loc_2].ADREL
loc_3 = Cauta _TS(identificator3)
loc_4 = Cauta _TS(identificator4)
*genereaza secventa de incarcare a valorii campului (**)
Secventa de incarcare a valorii campului de structura este:LODI Insert_const('intreg', TS[loc_4].DEPLREC)
MVRX
LODX TS[loc_3].NIVEL TS[loc_3].ADREL
Obs: daca in procedura Factor se ajunge din Lista_expresii, adica este vorba despre un parametru de apel, iar parametrul respectiv este transmis prin adresa , in liniile notate cu (**) in loc de 'incarcare valoare' se va genera 'incarcare adresa'.
loc_5 = Cauta _TS(identificator5)
GEN(RBM, TS[loc_5].DIM_VAR, 2)
if TS[loc_5].ADR_START < 0 thenGEN(FCALL, loc_5, TS[loc_5].NR_PAR, TS[loc_5].NIVEL)elseGEN(CALL,TS[loc_5].ADR_START,TS[loc_5].NR_PAR, TS[loc_5].NIVEL)endif
Obs: se constata ca rutinele de generare de cod, in anumite cazuri, depind de contextul in care este tratat simbolul gramatical de care sunt atasate. Acest lucru se intampla mai ales la neterminalele care apar in secvente derivate din Expresie si Variabila. De aceea, acestor neterminale trebuie sa li se asocieze niste atribute (parametri) care sa indice contextul in care sunt folosite. De exemplu, daca Expresie apare ca element in lista de parametri a unui apel de subprogram, este nevoie sa se cunoasca numarul de ordine al expresiei in cadrul listei.
Blocuri de program
Program_sursa ->program identificator ; Bloc .
Bloc -> Sectiune_const Sectiune_var Sectiune_decl_subprog
Instr_comp
![]()
*initializare ICOD si TAB_ERR
loc = ST_BLOC[VSTB]Obs: rutina de mai sus va fi apelata dupa ce a avut loc completarea campului TS[loc].ADR_START (v. Lucrarea 8).
if VNIVEL > 1 theni = TS[loc].INCDOMelse
while i < ICOD doif TAB_COD[i] = FCALL and TAB_COD[i+1] = loc thenendwhileTAB_COD[i] = CALLendif
TAB_COD[i+1] = TS[loc].ADR_START
*incrementeaza i cu n = (nr. argumentelor instr TAB_COD[i]) + 1*initializeaza primele 3 locatii din TAB_COD cu: TS[loc].ADR_START, TS[loc].DIM_VAR si indicele primei locatii libere din TAB_ERRendif
if VNIVEL > 1 thenGEN(RET)elseGEN(STOP)endif
Instructiunea if
Instr_if -> if Conditiethen Instr
Ramura_else
Ramura_else -> epsilon |
elseInstr
et1 = ICOD
GEN(FJP, 0)
TAB_COD[et1 + 1] = ICOD
et2 = ICOD
GEN(UJP, 0)
TAB_COD[et1 + 1] = ICOD
TAB_COD[et2 +1] = ICOD
Instructiunea while
Instr_while -> whileConditie do
Instr
![]()
et1 = ICOD
et2 = ICOD
GEN(FJP, 0)
GEN(UJP, et1)
TAB_COD[et2 + 1] = ICOD
Instructiunea repeat
Se propune studentilor ca exercitiu.
Instructiunea for
Instr_for -> for Variabila := Expresie1Pentru instructiunea for generarea de cod corespunzatoare simbolului Variabila (contorul instructiunii) se face la fel ca pentru instructiunea de atribuire. In plus, insa, este necesar sa se retina indicele (indicii) din TS in care se afla simbolurile ce compun variabila.Sens Expresie2
Pas do
Instr
![]()
Sens -> to|
downtoPas -> epsilon |stepExpresie3
GEN(STO) //initializare contor cu valoarea Expresie1
et1 = ICOD
GEN(COPI)
*genereaza secventa de incarcare a valorii variabilei contor
if sens = cheie_to thenGEN(GEQ)elseGEN(LEQ)endif
et2 = ICOD
GEN(FJP, 0) //la depasirea valorii Expresie2 se paraseste bucla
et3 = ICOD
et4 = ICOD
GEN(UJP, 0)
if et3 = et4 thenICOD = et3 //daca nu exista clauza step se elimina instructiunea UJP de laelse//adresa et3TAB_COD[et3 + 1] = ICOD //completare argument instructiune UJP de laendif//adresa et3 cu adresa de inceput a corpului buclei
*genereaza secventa de incarcare a adresei variabilei contor
*genereaza secventa de incarcare a valorii variabilei contor
if et3 < et4 thenGEN(UJP, et5) //daca exista clauza step se sare la evaluarea lui Expresie3else
TAB_COD[et4 + 1] = ICODGEN(LODI, Insert_const( 'intreg', 1))endif
if sens = cheie_to then //secventaGEN(ADD) //deelse //actualizareGEN(SUB) //contorendif //cu valoarea
GEN(STO) //pasului
GEN(UJP, et1) //salt la testul contor vs. Expresie2
TAB_COD[et2 + 1] = ICOD //completarea argumentului instructiunii FJP de la
Instructiunea read
Instr_read -> read(Lista_variab)Lista_variab -> Variabila
|
Lista_variab , Variabila
Generarea de cod pentru Variabilase realizeaza
ca si la instructiunea de atribuire. Vom nota
cu tipv tipul determinat pentru
variabila .
GEN(INP, tipv)
GEN(STO)
Instructiunea print
Instr_print -> print(Lista_elem)Lista_elem -> Element | Lista_elem , Element
Element -> Expresie
|
const_sir
GEN(OUT)
sir = atom.atribut
* l = lungimea sirului
for i = 1 . . l doGEN(LODI, Insert_const( 'caracter', sir[i] ))endfor
GEN(OUT)
Instructiunea case
Instr_case -> caseIn cazul instructiunii case se impune de la inceput o limita maxima a numarului de alternative (ramuri). Acest lucru este practicat de altfel in limbajele dotate cu o asemenea instructiune, deoarece rezultatul expresiei care se testeaza poate apartine doar unui tip scalar (caracter, enumerare, intreg reprezentabil pe un octet), tip a carui cardinalitate, dupa cum se stie este limitata.Expresie ofLista_altern end
![]()
Lista_altern -> Lista_ramuri |Lista_ramuri -> Ramura |Lista_ramuri otherwise: InstrRamura ->Lista_ramuri RamuraLista_val :
Instr ;
![]()
Lista_val -> Constanta|
Lista_val , Constanta
Vom utiliza urmatoarele notatii:
Desfasurarea lucrarii
Se vor implementa rutinele de generare a codului virtual, conform celor prezentate in paragraful de mai sus, si se vor atasa la compilatorul realizat in cadrul lucrarilor anterioare. Pentru adaugarea rutinelor la compilator se vor utiliza modelele prezentate in Anexa D.