Post on 04-Mar-2018
Pravljenje sintaksnog analizatora
Sintaksni analizator Ručno: analizator sa rekurzivnim spuštanjem Uz pomoć alata: sličnost sa interpreterom
1
Sintaksni analizator sa rekurzivnim spuštanjem (recursive-descent)
Ima po jednu (međurekurzivnu) funkciju za svaki neterminal (sintaksnu klasu) Unutar svake funkcije switch po vrsti ulaznog simbola
sa po jednom case klauzulom za svaku produkciju
Sinonim: analiza sa predviđanjem (predictive)Ograničenje: Radi dobro samo ako prvi terminalni simbol u svakom
pod izrazu jednoznačno određuje produkciju Ako to nije slučaj, postoji konflikt
2
Primer1 (1/3)Specifikacija gramatike
Napraviti sintaksni analizator sa rekurzivnim spuštanjem za zadatu gramatiku Neterminalni simboli: S, E, L
Defišu dozvoljene iskaze i izraze u jeziku
Terminali: let, in, begin, print, end, “;”, “=” Ključne reči, punktoatori i operatori u jeziku
3
S let E in S L endS begin S L L ; S LS print E E num = num
Primer1 (2/3)Program
4
S let E in S L endS begin S L L ; S LS print E E num = num
enum token {LET, IN, BEGIN, END, PRINT, SEMI, NUM, EQ};extern enum token getToken();
enum token tok;void advance() {tok=getToken();}void eat(enum token t) {if(tok==t) advance(); else error();}
void S(void) {switch(tok) {case LET: eat(LET); E(); eat(IN); S(); break;case BEGIN: eat(BEGIN); S(); L(); break;case PRINT: eat(PRINT); E(); break;default: error();
}}
Primer1 (3/3)Program
5
S let E in S L endS begin S L L ; S LS print E E num = num
void L(void) {switch(tok) {case END: eat(END); break;case SEMI: eat(SEMI); S(); L(); break;default: error();
}}void E(void) {eat(NUM); eat(EQ); eat(NUM);
}
Primer sa konfliktima
Gramatika sa konfliktima Tada je potrebno odrediti skupove FIRST i FOLLOW Za niz terminala i neterminala , skup FIRST čine svi
terminali kojima započinju simboli razvijeni iz Npr. za = {T * F}, FIRST() = {id, num, (}
Simbol X je poništiv (nullable) ako se iz njega može razviti prazan niz
Skup FOLLOW(X) je skup terminala koji u rečenicama jezika mogu pratiti X 6
S E $E E + T T T * F F idE E - T T T / F F numE T T F F ( E )
Algoritam za određivanjeFIRST, FOLLOW i NULLABLE
7
Initialize FIRST and FOLLOW to empty sets, and nullable to all false.for each terminal symbol ZFIRST[Z] = {Z}
repeatfor each production X Y1 Y2 ... Ykfor each i from 1 to k, each j from i+1 to kif all the Yi are nullablethen nullable[X] ← true
if Y1 ... Yi-1 are all nullablethen FIRST[X] ← FIRST[X] U FIRST[Yi]
if Yi+1 ... Yk are all nullablethen FOLLOW[Yi] ← FOLLOW[Yi] U FOLLOW[X]
if Yi+1 ... Yj-1 are all nullablethen FOLLOW[Yi] ← FOLLOW[Yi] U FOLLOW[Yj]
until FIRST, FOLLOW, and nullable did not change in this iteration.
Eliminisanje leve rekurzije
8
Opšte pravilo za prepisivanje regularnih izraza sa levom rekurzijom u izraze sa desnom rekurzijom Primenjeno na posmatrani primer
Početni skup produkcija
Rezultat transformacije
X X 1X X 2X 1X 2
X 1 X’X 2 X’X’ 1 X’X’ 2 X’X’
S E $E T E’ T F T’E’ + T E’ T’ * F T’ F idE’ - T E’ T’ / F T’ F numE’ T’ F ( E )
Jezik nakon transformacije:
Sadržaji skupova
9
Simbol nullable FIRST FOLLOWS false ( id numE false ( id num ) $E' true + - ) $T false ( id num ) + - $T' true * / ) + - $F false ( id num ) * / + - $
Pored transformacije jezika, potrebno je odrediti sadržaje skupova Za svaki neterminal: nullable, FIRST i FOLLOW
Interpreter sa rekurzivnimspuštanjem (1/2)
10
enum token {EOF, ID, NUM, PLUS, MINUS, ...};union tokenval {string id; int num; ...};enum token tok;union tokenval tokval;int lookup(string id) {...}int F_follow[] = {RPAREN, TIMES, DIVIDE, PLUS, MINUS, EOF, -1};int F(void) {switch(tok) {case ID: {int i=lookup(tokval.id); advance(); return i;}case NUM: {int i=tokval.num; advance(); return i;}case LPAREN: eat(LPAREN); { int i = E();
eatOrSkipTo(RPAREN, F_follow);return i; }
case EOF:default: printf(“Expected ID, NUM, or (“);
skipto(F_follow); return 0;}
}
F idF numF ( E )
Interpreter sa rekurzivnimspuštanjem (2/2)
11
int T_follow[] = {RPAREN, PLUS, MINUS, EOF, -1};int T(void) {switch(tok) {case ID: case NUM: case LPAREN: return Tprime(F());default: printf(“Expected ID, NUM, or (“);
skipto(T_follow); return 0;}
}int Tprime(int a) {switch(tok) {case TIMES: eat(TIMES); return(a*F());case RPAREN: case PLUS: case MINUS: case EOF: return a;default: ...
}}void eatOrSkipTo(int expected, int *stop) {if(tok==expected) eat(expected);else {printf(...); skipto(stop);}
}
T F T’T’ * F T’T’ / F T’T’
Originalne produkcije:T T * FT T / FT FT se prenosi kroz a
Napomene uz prethodni C kod Semantika
Simboli ID i NUM nose vrednosti tipa sting i int Podrazumeva se postojanje tabele
Koja preslikava ID-ove na NUM-ove (cele brojeve)
Tip pridružen neterminalima E, T, F, itd. je int Semantičke akcije se lako realizuju
Problem: Semantička akcija za veštački simbol T’ Npr. u produkciji T’*FT’ nedostaje levi operand za * Jedan način je da se levi operand prosledi kao argument
Funkcija T ga uzima sa F() i prosleđuje funkciji Tprime
Razvoj simbola u prazan string se prepoznaje pojavom nekog od simbola iz skupa FOLLOW
12
Odnos interpretera i sintaksnog analizatora
Postoji velika sličnost između njihAko se koristi Yacc, razlikuju se samo akcije Interpreter: akcije dovode do direktnog izvršenja
Npr. Iskaz dodele vrednosti zaista upisuje vrednost u promenljivu
Iskaz štampanja (print) sadržaja promenljive ga zaista prikazuje na monitoru, itd.
Sintaksni analizator: akcije izgrađuju stablo apstraktne sintakse dodavanjem novih čvorova To stablo će kasnije u nizu transformacija biti prevedeno u
mašinski kod Tek pri njegovom izvršenje dobijaju se rezultati
13
Gramatika pravolinijskih programaSLP interpreter i sintaksni analizator za SLP Gramatika SLP zadata donjim nizom produkcija
Preuzimanjem struktura iz ručno pisanog SLP interpretera, dolazi se do Yacc specifikacija U slučaju sintaksnog analizatora te strukture
podataka predstavljaju apstraktnu sintaksu
14
S S ; S E id L ES id := E E num L L ES print L E E B E
E S , E B + | - | * | /
Primer:Specifikacija SLP interpretera (1/2)
15
%{#include "table.h"extern table tab;%}
%union {int ival; string sval;}%token <sval> ID%token <ival> INT%token ASSIGN PRINT LPAREN RPAREN%type <ival> exp
%right SEMICOLON%left MINUS PLUS%left TIMES DIVIDE%start prog
%%prog: stm
Primer:Specifikacija SLP interpretera (2/2)
16
stm: stm SEMICOLON stmstm: ID ASSIGN exp {update(&tab, $1, $3);}stm: PRINT LPAREN exps RPAREN {printf("\n");}
exps: exp {printf("%d ", $1);}exps: exps COMMA exp {printf("%d ", $3);}
exp: INT {$$ = $1;}exp: ID {$$ = lookup(tab, $1);}exp: exp PLUS exp {$$ = $1 + $3;}exp: exp MINUS exp {$$ = $1 - $3;}exp: exp TIMES exp {$$ = $1 * $3;}exp: exp DIVIDE exp {$$ = $1 / $3;}exp: stm COMMA exp {$$ = $3;}exp: LPAREN exp RPAREN {$$ = $2;}
// $1, $2, itd. se odnose na definicione članove produkcije
Primer: Specifikacija SLP sinatksnog analizatora (1/2)
17
%{extern A_stm rootprg;%}
%union {int num; string id; A_stm stm; A_exp exp; A_expList expList;}%token <num> INT%token <id> ID%token ASSIGN PRINT LPAREN RPAREN%type <stm> stm prog%type <exp> exp%type <expList> exps%left SEMICOLON%left MINUS PLUS%left TIMES DIVIDE%start prog
%%prog: stm {$$=$1; rootprg=$1;}
Primer: Specifikacija SLP sinatksnog analizatora (2/2)
18
stm: stm SEMICOLON stm {$$=A_CompoundStm($1,$3);}stm: ID ASSIGN exp {$$=A_AssignStm($1,$3);}stm: PRINT LPAREN exps RPAREN {$$=A_PrintStm($3);}
exps: exp {$$=A_LastExpList($1);}exps: exp COMMA exps {$$=A_PairExpList($1,$3);}
exp: INT {$$=A_NumExp($1);}exp: ID {$$=A_IdExp($1);}exp: exp PLUS exp {$$=A_OpExp($1,A_plus,$3);}exp: exp MINUS exp {$$=A_OpExp($1,A_minus,$3);}exp: exp TIMES exp {$$=$$=A_OpExp($1,A_times,$3);}exp: exp DIVIDE exp {$$=$$=A_OpExp($1,A_div,$3);}exp: stm COMMA exp {$$=A_EseqExp($1,$3);}exp: LPAREN exp RPAREN {$$=$2;}
Apstraktna sintaksa za tiger (1/2)
19
/* Function Prototypes */A_var A_SimpleVar(A_pos pos, S_symbol sym);A_var A_FieldVar(A_pos pos, A_var var, S_symbol sym);A_var A_SubscriptVar(A_pos pos, A_var var, A_exp exp);A_exp A_VarExp(A_pos pos, A_var var);A_exp A_NilExp(A_pos pos);A_exp A_IntExp(A_pos pos, int i);A_exp A_StringExp(A_pos pos, string s);A_exp A_CallExp(A_pos pos, S_symbol func, A_expList args);A_exp A_OpExp(A_pos pos, A_oper oper, A_exp left, A_exp right);A_exp A_RecordExp(A_pos pos, S_symbol typ, A_efieldList fields);A_exp A_SeqExp(A_pos pos, A_expList seq);A_exp A_AssignExp(A_pos pos, A_var var, A_exp exp);A_exp A_IfExp(A_pos pos, A_exp test, A_exp then, A_exp elsee);A_exp A_WhileExp(A_pos pos, A_exp test, A_exp body);A_exp A_ForExp(A_pos pos, S_symbol var, A_exp lo, A_exp hi, A_exp body);A_exp A_BreakExp(A_pos pos);A_exp A_LetExp(A_pos pos, A_decList decs, A_exp body);A_exp A_ArrayExp(A_pos pos, S_symbol typ, A_exp size, A_exp init);
Apstraktna sintaksa za tiger (2/2)
20
A_dec A_FunctionDec(A_pos pos, A_fundecList function);A_dec A_VarDec(A_pos pos, S_symbol var, S_symbol typ, A_exp init);A_dec A_TypeDec(A_pos pos, A_nametyList type);A_ty A_NameTy(A_pos pos, S_symbol name);A_ty A_RecordTy(A_pos pos, A_fieldList record);A_ty A_ArrayTy(A_pos pos, S_symbol array);A_field A_Field(A_pos pos, S_symbol name, S_symbol typ);A_fieldList A_FieldList(A_field head, A_fieldList tail);A_expList A_ExpList(A_exp head, A_expList tail);A_fundec A_Fundec(A_pos pos, S_symbol name, A_fieldList params,
S_symbol result,A_exp body);A_fundecList A_FundecList(A_fundec head, A_fundecList tail);A_decList A_DecList(A_dec head, A_decList tail);A_namety A_Namety(S_symbol name, A_ty ty);A_nametyList A_NametyList(A_namety head, A_nametyList tail);A_efield A_Efield(S_symbol name, A_exp exp);A_efieldList A_EfieldList(A_efield head, A_efieldList tail);
Yacc specifikacija za tiger
21
exp: lvalue {$$=A_VarExp(EM_tokPos,$1);}| NIL {$$=A_NilExp(EM_tokPos);}| INT {$$=A_IntExp(EM_tokPos,$1);}| STRING {$$=A_StringExp(EM_tokPos,$1);}| NEG INT
{$$=A_OpExp(EM_tokPos,A_minusOp,A_IntExp(EM_tokPos,1),A_IntExp(EM_tokPos,$2));}
| functioncall {$$=$1;}| arithmetic {$$=$1;}| comparison {$$=$1;}| booleanexp {$$=$1;}| recordcreation {$$=$1;}| arraycreation {$$=$1;}
;lvalue: ID {$$=A_SimpleVar(EM_tokPos,S_Symbol($1));}
| lvalue DOT ID {$$=A_FieldVar(EM_tokPos,$1,S_Symbol($3));}| ID LBRACK exp RBRACK {$$=A_SubscriptVar(EM_tokPos,
A_SimpleVar(EM_tokPos, S_Symbol($1)),$3);};...
Primer stabla sintaksne analizeProgram queens
22
...\tigerc.exe
..\testcases\queens_book.tigParsing successful!
letExp(decList(functionDec(fundecList(fundec(try,fieldList(field(c,int,TRUE),
fieldList()),iffExp(opExp(EQUAL,varExp(simpleVar(c)),
varExp(simpleVar(N))),
callExp(printboard,expList()),
forExp(r,intExp(0),opExp(MINUS,varExp(simpleVar(N)),
intExp(1)),iffExp(iffExp(iffExp(opExp(EQUAL,varExp(subscriptVar(simpleVar(row),varExp(simpleVar(r)))),
intExp(0)),