Professional Documents
Culture Documents
Semantika obrada je proces u kojem kompilator ispituje i odreuje semantike znaajke programa. Najznaajniji zadaci semantike obrade su: kontrola tipova pretvorba tipova priprema za generiranje koda
Najvanija podatkovna struktura koja se koriste u semantikoj obradi je tablica simbola. Ona sadrava podatke o svim imenovanim simbolima. Imenovani simboli su: varijable, tipovi, procedure i imenovane konstante. Za sve simbole karakteristina su tri podatka: ime deklaracija (tipa, funkcije) nain spremanja u izvrnom kodu i zauzee memorije Ime se dobija ve tijekom leksike analize. Elementi deklaracije se utvruju tijekom parsiranja. Oni se kod jednoprolaznih kompilatora odmah biljee u tablici simbola, a kod vieprolaznih kompilatora se obino najprije registriraju u apstraktnom sintaktikom stablu AST). Zatim se vri kontrola tipova varijabli koje se koriste u izrazima i kontrola tipova argumenata funkcija i procedura. Rezultat semantike obrade se obino ponovo biljei u sintaktikom stablu (primjerice stablo se proiruje s elementima koji su potrebni za pretvorbu tipova). Takoer se dodaju razni podaci koji su potrebni za generiranje koda ( primjerice, za varijable se oznaava potrebno zauzee memorije). Ovako proireno sintaktiko stablo se naziva dekorirano apstraktno sintaktiko stablo. Najprije e biti pokazano kako se realizira tablica simbola, a zatim e primjerom biti pokazan postupak semantike obrade.
Semantika obrada
/* int, char, pointer, func, proc, array, reference*/ /* reference counting to get less allocation */ /* sub type of array or pointer, return of func *
union { struct _a { int len; } array; struct _f { int flag; Symbol *params; } func;
/* array (or string) length */ /* NotDefined, DefForward, BodyDef*/ /* list of function parameters symbols*/
}; char *name; Type *next; /* name of named types - not implemented*/ /* for link in type table - not implemented */
struct _s { Symbol *members; /* list of structure memebers */ }struc; /* struct type - not implemented */
};
Tri posljednja lana nisu implementirana. Oni , pri proirenju jezika, mogu posluiti za definiranje imenovanih strukturalnih tipova.
/* kind of type */ #define #define #define #define #define #define #define UNKNOWN VOID_TYPE CHAR_TYPE INT_TYPE POINTER_TYPE FUNCTION ARRAY_TYPE 0 1 2 4 8 16 32
Semantika obrada
#define #define REFERENCE_TYPE STRUCT_TYPE 64 128
/* /* /* /* /* /*
lexem of symbol*/ type of symbol */ Global = 0, Param = 1, Local=2,... */ stack address(offset) for locals and arguments*/ symbol init value */ pointer to keep list of symbols in symbol table*/
Pored imena (name) biljei se tip (type) , doseg (global, parametar funkcije ili lokalna varijabla), adresa lokalne varijable i argumenta funkcije na izvrnom stogu, i poetna vrijednost varijable, ako je definirana. Poetna vrijednost se biljei u strukturi:
struct _initval { int flag; union { int i; char *str; IntList *ilist; Node *expr; }; }; /* InitInt, InitIntArray, InitString, */ /* /* /* /* integer or char const string list of integers expression (for local vars) */ */ */ */
Na koji nain se simboli biljee u tablici simbola, bit e opisano u sljedeem poglavlju. Konano, definira se struktura pomou koje se biljei apstraktno sintaktiko stablo:
struct _treenode { int op; Type *expr_type; int i; int reg; int line; Symbol * sp; Node *left, *right, *third; };
/* /* /* /* /* /* /*
kind of node operator*/ type of rvalue used in expressions*/ constant value */ destination register */ line of source file*/ symbol - var, array, ref*/ the content of tree node */
Prvi lan ove strukture (op) opisuje vrstu AST vora. U Cmm koriste se sljedei tipovi vorova
enum _treeOp {OpNone,
/*int ili char konstante*/ OpIntConst, OpCharConst, /* identifikatori var, ptr, elementa niza, referenca niza, arg fun */ OpIdentifier,OpArrayElem, OpPtrIndirection, OpReferenceArg, OpArrayReference,
Semantika obrada
/* zapis naredbi */ OpAssign, OpIf, OpWhile, OpBreak, OpContinue, OpExprStmt, OpFor,OpStatList, /*poziv i povrat iz funkcije s listom argumenata */ OpFuncCall, OpReturn, OpArgList, /* ugraene funkcije */ OpPrint,OpPrintList, OpRead, /* binarni i unarni izrazi*/ OpVarAddress, OpCast, OpUnaryMinus, OpLogNot, OpPreIncr, OpPreDecr,OpPostIncr, OpPostDecr, OpPlus, OpMinus, OpMul, OpDiv, OpMod, OpLogOr, OpLogAnd, OpEq, OpNotEq, OpGr, OpGrEq, OpLess, OpLessEq, };
Ostali lanovi ine potpuni opis vora: expr_type - ako vor opisuje izraz, biljei se tip izraza. i - vrijednost cjelobrojne ili char konstante reg - oznaka registra koji sadri vrijednost izraza sp - pokaziva na simbol, ako vor sadri varijablu ili funkciju left, right i third pokazivai - kod zapisa izraza, left i right pokazuju dva operanda, a kod kontrolnih struktura pokazuju na izraze ili naredbe. Funkcije za formiranja stabla bit e opisane kasnije.
vraa pokaziva na simbol ako ime name postoji u tablici , a ako ne postoji vraa NULL.
Symbol *insert(Symbol * sym);
dodaje novi simbol u tablicu. Simbol je prethodno formiran dinamikom alokacijom, a sadraj mu se formira iz imena sym->name i ostalih elemenata.
/* umetni u tablicu */
Semantika obrada
Symbol *sp = lookup(sym->name);
if (sp = lookup(name) != 0)
sysError("Symbol %s already defined\n", sym->name); sym->next = symtable; /* umetni na poetak liste */ symtable = sym; /* novi poetak liste */ return sym; } Symbol *lookup(char* name) /* find s in symbol table */ { Symbol *sp; for (sp = symtable; sp != NULL; sp = sp->next) if (strcmp(sp->name, name)== 0) return sp; /* found */ return NULL; /* not found */ }
Temeljna ideja hash tablice je u tome da se neki simbol imena name postavlja u listu table[i], gdje se indeks i odreuje pomou hash funkcije
indeks = hash(name);
Ukoliko ova funkcija daje indeks s jednolikom vjerojatnou za sve vrijednosti iz intervala [0, HASHSIZE-1], tada moemo oekivati da e sve liste imati jednak broj elemenata, a to znai da se veliina liste u kojoj se vri traenja smanjuje za faktor koji jednak HASHSIZE. Ne postoji idealna hash funkcija. Mi emo koristiti funkciju 5
Semantika obrada
unsigned hash(char * s) { unsigned hashval=0; while(*s != '\0') { hashval = *s + 31 * hashval; s++; } return hashval % HASHSIZE; }
koja daje zadovoljavajuu raspodjelu imena u tablici. Poetno se svi elementi tablice postavljaju na vrijednost 0.
#define HASHSIZE 155 /* size of hash table */ /* symbol hash table */
Symbol *table[HASHSIZE];
void inittable () /* initialize the symbol table */ { int i; for (i=0; i < HASHSIZE; ++i) table[i] = NULL; }
U tom sluaju kompilator interno daje literalnom stringu ime primjerice _str_xx. i generira kod za sljedei zapis 6
Semantika obrada
U C-- kompilatoru stringovi se registriraju u hash tablici, kojoj temeljni element ima oblik
typedef struct string_data { char * str; char * name; struct string_data *next; } StringData; /* string */ /* interno ime za string*/
Za jezike koji podravaju ovakovu blok strukturu, potrebno je tablicu simbola realizirati tako da se registrira doseg varijable, na nain da u tablici moe biti vie razliitih simbola istoga imena, ukoliko su oni iz razliitog dosega. To se moe postii tablicom u kojoj se uz simbole sprema i oznaka dosega, ili da se generira odvojena tablice za svaki doseg. Analizirat emo oba rjeenja. 1) Stvaranje odvojene tablice za svaki doseg Ideja: Kada "uemo u neki blok" treba stvoriti novu tablicu i zapamtiti tablicu iz prethodnog dosega. Kada "napustimo taj blok" tablica za taj doseg treba prestati biti aktivna. Oito je da se tablice slau po principu stoga. Definirajmo strukturu Scope, pomou koje emo pamtiti stog tablica:
struct scope { Scope *previous; Symbol *hash_table[HASHSIZE]; }; typedef struct scope Scope; /* globalne varijable za globalni i trenutni doseg */ Scope *current_scope, *global_scope;
Za stvaranje tablice pri ulasku u novi blok koristit emo funkciju enterScope();
Scope *enterScope(void)
Semantika obrada
{ unsigned i; Scope *scope = malloc(sizeof(Scope)); scope->previous = current_scope; current_scope = scope; for (i = 0; i < HASHSIZE; i ++) scope->hash_table [i] = 0; return scope; }
Nakon izlaska iz bloka, ponovo treba postati aktivan prethodni doseg. To vri funkcija
leaveScope()
void leaveScope(void) { current_scope = current_scope->previous; }
void delete_scope(Scope *scope) { if (scope != 0) { unsigned i; Symbol *sym, *next_sym; for (i = 0; i < HASHSIZE; i ++) for (sym=scope->hash_table[i]; sym != 0; sym = next_sym) { next_sym = sym->next; delete_symbol(sym); } free(scope); } }
Napomena: funkcija delete_scope() brie kompletnu tablicu za neki doseg. Ona se poziva kada vie nisu potrebni simboli iz tog dosega. Kod jedno-prolaznih kompilatora tablica nekog dosega se brie im se napusti blok, dok se kod vie-prolaznih kompilatora tablice briu tek nakon to je zavreno generiranje koda. Funkcije za traenje simbola sada kao argument primaju i oznaku dosega (pokaziva na trenutni Scope)
void insert(Symbol *sym); Symbol *lookup(const Scope *, const char *name); Symbol *lookup_in_this_scope(const Scope *, const char *name);
Funkcija lookup_in this scope() trai simbol u trenutno aktivnom bloku. Funkcija lookup() trai simbol u trenutno aktivnom bloku. Ako ga ne nae tada trai u tablicama koje su na stogu, zakljuno s tablicom koja predstavlja globalni doseg. Implementacija prethodnih funkcija se moe napraviti na sljedei nain:
static Symbol *search(Scope *scope, const char *name, unsigned index) { Symbol *sym; for (sym = scope->hash_table[index]; sym != 0; sym=sym->next) if (strcmp(sym->name, name) == 0) return sym; return 0; } void insert(Symbol *sym)
Semantika obrada
{ unsigned index = hash(name); if (search(current_scope, name, index) != NULL) error(0, "%s already declared in this scope", name); else { sym->next = scope->hash_table[index]; scope->hash_table [index] = sym; } Symbol *lookup(const Scope *scope, const char *name) { unsigned index = hash(name); Symbol *sym; do { if ((sym = search(scope, name, index)) != 0) return sym; } while ((scope = scope->previous) != NULL); return 0; } Symbol *lookup_in_this_scope(const Scope *scope, const char *name) { return search(scope, name, hash(name)); }
Primijetimo da se pri ovakovom nainu formiranja tablice simbola, u sintaktikom stablu mora uz svaki identifikator biljeiti i njegov doseg (scope). 2) Jedna hash tablica s dodatnim oznaavanjem dosega simbola Prethodno opisani nain formiranja i koritenja tablice je najopenitiji i moe se prilagoditi zahtjevima svakog programskog jeziku. Neto jednostavniji pristup, koji zahtijeva manje memorijskih resursa, je odabran za implementaciju kod C-- jezika. Lokalni simboli postoje samo unutar blokova, a blokovi su unutar neke funkcije. Nakon izlaska iz bloka, lokalni simboli se mogu odstraniti iz tablice. Ideja je da se koristi jedna hash tablica za sve dosege, a da se doseg numerira i pridjeli svakom simbolu.
struct _symbol { char *name; Type *type; int scope;
...........
*/
Na taj nain se mogu razlikovati identifikatori koji imaju isto ime, a nalaze se u razliitim dosezima. Nakon parsiranja neke funkcije moe se odmah provesti daljnu semantiku analizu i generiranje koda. Nakon toga se mogu izbrisati svi elementi tablice s lokalnim dosegom.
Symbol * g_currFunction; int g_currScope = 0; static Symbol * SymTable[HASHSIZE]; /* pokaziva na simbol funkcije */ /* oznaka trenutnog dosega */ /* tablica simbola */
dosegu scope */
Semantika obrada
void enterScope() { g_currScope++; } void leaveScope() { removeScopeSym(g_currScope); /* remove all g_currScope syms from table*/ g_currScope--; } static Symbol *findInScope(int scope, char * name, unsigned hash) {/* returns symbol, if found in current scope or function parameter scope else return NULL */ Symbol * sp = NULL; if (g_currFunction && scope != Global) { /* local or parameters*/ for (sp = SymTable[hash]; sp; sp = sp->next) if ( (sp->scope == scope || sp->scope == Param) && strcmp(sp->name, name) == 0) return sp; /*already there*/ } else if(Global == scope) { /*global*/ for (sp = SymTable[hash]; sp; sp = sp->next) if (strcmp(sp->name, name) == 0) return sp; } return sp; } static void removeScopeSym(int scope) { Symbol * sp; int i; for (i = 0; i < HASHSIZE; i++) { sp = SymTable[i]; while(sp && sp->scope == scope) { SymTable[i] = sp->next; sp = SymTable[i]; } } } Symbol *insertSymTable(Symbol * sym) { unsigned hash; Symbol *sp; if(sym == NULL) sysError("Symbol not defined\n"); hash = sym_hash(sym->name); if(sp = findInScope(sym->scope, sym->name, hash)) { sysError("Symbol %s already defined\n", sym->name); } /* Add symbol to table */ sym->next = SymTable[hash]; SymTable[hash] = sym; } return sym;
10
Semantika obrada
Symbol *lookupSymTable(char * name) { Symbol * sp = NULL; unsigned hash = sym_hash(name); /* first look at local table, from head - to access deepest scope*/ for (sp = SymTable[hash]; sp; sp = sp->next) if (strcmp(sp->name, name) == 0) return sp; return sp; } /* Enter new function scope. */ Symbol * enterFunctionScope(Symbol * sp, Type *ret_type) { Symbol *sym; if (g_currFunction) yyerror("Cannot nest functions"); sym = lookupSymTable(sp->name); if(sym == NULL) { if(isPointerType(sp->type)) /*if return pointer*/ setArrOrPtrBaseType(ret_type, sp->type); else sp->type = ret_type; /*now convert sp type to function type*/ sp->type = mkFuncType(sp->type, NULL); sp->type->func.params=NULL; sp->addr = 0; sp->type->func.flag = ParamsNotDefined; sym = sp; insertSymTable(sym); } else if(sym->type->func.flag == ParamsAndBodyDef) sysError("Line:%d: function %s already defined", g_linenum, sp->name); else /*forward declaration*/ { /*now, check return type*/ /*formal arguments will be entered and checked later*/ if(!equalTypes(sym->type->subtype, ret_type)) sysError("Line:%d: function %s declared with different return type", g_linenum, sp->name); } /*now switch to local scope*/ g_currOffset = 0; g_currFunction = sym; g_currScope++; return sym; } /* Delete scope of local syms */ void exitFunctionScope() { int i; Symbol * sp; /*remove local symbols, keep globals*/ for (i = 0; i < HASHSIZE; i++) { sp = SymTable[i];
11
Semantika obrada
while(sp && sp->scope > Global) { SymTable[i] = sp->next; deleteType(sp->type); free(sp); sp = SymTable[i]; }
Primjetite da se nakon generiranja kda za neku Cmm funkciju, mogu izbrisati svi simboli iz lokalne tablice simbola.
12
Semantika obrada
np = mkNode(); np->expr_type = copyType(sp->type); /* propagate type */ np->sp = sp; if(sp->type->kind == REFERENCE_TYPE) np->op = OpReferenceArg; else if(sp->type->kind == ARRAY_TYPE) np->op = OpArrayReference; else np->op = OpIdentifier; return np;
Specijalni sluaj je vor za literalni string, kojeg pretvaramo u interno imenovanu referencu niza:
Node *addLitStringNode(char * name) { Node * np; Symbol * sp = lookupSymTable(name); if(sp == NULL) /*this will never happen*/ sysError("Line:%d: Dummy string not declared", g_linenum); np = mkNode(); np->op = OpArrayReference; np->expr_type = copyType(sp->type); np->sp = sp; return np; }
U sluaju kada je potrebno izvriti pretvorbu tipova, formira se vor (OpCast) pomou funkcije insertCastNode(),
Node *insertCastNode(Node * np, Type * expr_type) { Node * newp = mkNode(); newp->op = OpCast; newp->expr_type = expr_type; newp->left = np; return newp; }
Sintaktiko stablo izraza se zapisuje koristei left i right pokazivae vora, koji se inicijaliziraju tako da pokazuju na operande izraza:
Node *addBinNode(int op, Node *left, Node *right) { Node *np = mkNode(); np->op = op; np->expr_type = &UnknownType; np->left = left; np->right = right; np->third = NULL; return np; }
Vrijednost tipa rezultirajueg izraza (expr_type), bit e odreena kasnije, u semantikoj analizi izraza.
13
Semantika obrada Sintaktiko stablo veine naredbi se formira se pomou funkcije addBinNode(), ali za for i if-else naredbe koristi se funkcija addTriNode():
Node *addTriNode(int op, Node *left, Node *right, Node *third) { Node *np = mkNode(); np->op = op; np->expr_type = &UnknownType; np->left = left; np->right = right; np->third = third; return np; }
dobiju se dvije datoteke: y.tab.c i y.tab.h. Datoteka y.tab.h. slui za vezu s leksikim analizatorom ( u njoj je definicija tokena). Datoteka y.tab.c sadri funkciju yyparse() koja sadri parser. Rezultat poziva ove funkcije je izgradnja sintaktikog tabla, izrada tablice simbola. Nakon parsiranja svake funkcije vri se semantika analiza te funkcije. Ako je semantika analiza uspjena vri se generiranje asemblerskog koda. Yacc datoteka zapoima s potrebnim deklaracijama:
%{ #include #include #include #include "cmm.h" "cmm_sym.h" "cmm_tree.h" "cmm_comp.h"
/*Cmm - Simple language with C constructs but with limited types */ extern int yylex(); extern int g_linenum; extern int g_parse_error; %}
14
Semantika obrada
Type *tp; InitVal *initval; IntList *ilist; VarList *vlist; }
/* Pomou deklaracija iz ove unije, vri se deklariranje semantikih atributa terminalnih i neterminalnih simbola. */
%token WHILE_TOK BREAK_TOK CONTINUE_TOK IF_TOK ELSE_TOK FOR_TOK %token VOID_TOK RETURN_TOK PRINT_TOK READ_TOK INT_TOK CHAR_TOK %token <ivalue> INT_LITERAL CHAR_LITERAL %token <str> ID_TOK STRING_LITERAL %token <ivalue> '!' '-' '+' '*' '/' '=' '(' ',' '[' ']' %token <ivalue> GE_TOK LE_TOK EQ_TOK '>' '<' NE_TOK OR_TOK AND_TOK INCR_TOK DECR_TOK %type %type %type %type %type %type %type %type <np> <np> <sp> <tp> <vlist> <initval> <ivalue> <ilist> expression expropt lvalexpr arglist printlist statement statementlist simplestatement blok decl_statement variable dir_variable param_list funcsym type variablelist local_varlist init_value int_const intlist
/* Startni simbol je "program" koji se definira kao niz deklaracija globalnih varijabli i funkcija: */
program: /* null */ | program function | program global_decl ;
15
Semantika obrada
| ; variablelist ',' variable '=' init_value { $$ = addVarList($3, $1, $5);} {$$ = $1;} {$$ = addPointerDecl($2);} { $$ = makeVarDecl($1); } { $$ = addArrayDecl( $1, 0); } { $$ = addArrayDecl($1, $3); }
variable : dir_variable | '*' variable ; dir_variable: ID_TOK | dir_variable '[' ']' | dir_variable '[' int_const ']' ; init_value: int_const | STRING_LITERAL | '{' intlist '}' ;
{ $$ = makeInitVal( InitInt, (void *)$1);} { $$ = makeInitVal( InitString, $1); } { $$ = makeInitVal( InitIntArray, $2); }
/* Funkcije se deklariraju: 1) kao prototip ili 2) s tijelom funkcije koji nazivamo blok. Ukoliko je definiran blok funkcije, tada se pomou stabla, koje opisuje blok, vri semantika analiza i generiranje koda za svaku pojedinu funkciju. */
function: function_header blok { typeCheck($2); generateCode($2); exitFunctionScope();} | function_header ';' { declareFuncPrototype(g_currFunction); exitFunctionScope();} ; function_header: type funcsym '(' param_list ')' {enterFunctionScope($2, $1); insertFuncParams($4, g_currFunction); } ; funcsym: ID_TOK { $$ = makeVarDecl($1); } | '*' funcsym { $$ = addPointerDecl($2); } ; param_list: /* null */ { $$ = NULL;} | type '&' ID_TOK { $$ = addParamList($1, REFERENCE_TYPE, mkSym($3), NULL); } | type variable { $$ = addParamList($1, $1->kind, $2, NULL); } | param_list ',' type '&' ID_TOK { $$ = addParamList($3, REFERENCE_TYPE, mkSym($5), $1); } | param_list ',' type variable { $$ = addParamList($3, $3->kind, $4, $1); } ; blok: '{' {enterScope();} statementlist {leaveScope();} | '{' '}' {$$ = NULL;} ; '}' {$$ = $3;}
/* Deklaracija lokalnih varijabli se vri slino kao i globalne varijable, razlika je u inicijalizaciji. Varijable se inicijaliziraju pomou izraza, a ne pomou konstanti. */
decl_statement: type local_varlist ';' { $$ = declareLocalVarList($1, $2); } ; local_varlist: variable { $$ = appendLocVarList($1, NULL, NULL); } | variable '=' expression { $$ = appendLocVarList($1, NULL, $3); } | local_varlist ',' variable { $$ = appendLocVarList($3, $1, NULL); } | local_varlist ',' variable '=' expression {$$ = appendLocVarList($3, $1, $5); } ;
16
Semantika obrada
/* Naredbe se definiraju na isti nain kao i u C jeziku (sve osim naredbi print i read) */
statement: blok {$$ = $1;} | simplestatement {$$ = $1;} | WHILE_TOK '(' expression ')' statement { $$ = addBinNode(OpWhile, $3, $5); } | FOR_TOK '(' expropt ';' expropt ';' expropt ')' statement { $$ = addTriNode(OpFor, $3, $5, addBinNode(OpNone, $7, $9));} | IF_TOK '(' expression ')' statement ELSE_TOK statement { $$ = addTriNode(OpIf,$3,$5,$7); } | IF_TOK '(' expression ')' statement %prec ELSE_TOK { $$ = addTriNode(OpIf,$3,$5,NULL); } | error ';' { $$ = NULL; yyerrok; } ; simplestatement: ';' { $$ = NULL; } | expression ';' { $$ = addBinNode(OpExprStmt, $1, NULL);} | BREAK_TOK ';' { $$ = addBinNode(OpBreak, NULL, NULL); } | CONTINUE_TOK ';' { $$ = addBinNode(OpContinue, NULL, NULL); } | RETURN_TOK expression ';' { if (g_currFunction == NULL) yyerror("return not in function"); else if(isVoidFunctionType(g_currFunction->type)) yyerror("Cant return value in procedure"); else $$ = addBinNode(OpReturn, $2, NULL); } | RETURN_TOK ';' { if (g_currFunction == NULL) yyerror("return not in function"); else if(! isVoidFunctionType(g_currFunction->type)) yyerror("Must return a value in function"); else $$ = addBinNode(OpReturn, NULL, NULL); } | PRINT_TOK printlist ';' {$$ = addBinNode(OpPrint, $2, NULL); } | READ_TOK lvalexpr ';' {$$ = addBinNode(OpRead, $2, NULL); } ; printlist: expression | printlist ',' expression ; {$$ = appendNodeList(OpPrintList, $1, NULL);} {$$ = appendNodeList(OpPrintList, $3, $1);}
/* Izrazi se definiraju jednostavnije nego u C jeziku, jer je ogranien oblik lijeve strane izraza pridjele vrijednosti */
lvalexpr : ID_TOK | lvalexpr '[' expression ']' | '*' lvalexpr ; expression: INT_LITERAL { | CHAR_LITERAL { | STRING_LITERAL { | ID_TOK '(' arglist ')' { | lvalexpr | '(' expression ')' | lvalexpr '=' expression | expression '+' expression | expression '-' expression | expression '*' expression | expression '%' expression | expression '/' expression | expression '<' expression | expression '>' expression $$ $$ $$ $$ { $$ = addVarNode($1); } { $$ = appArrayNode($1, $3); } { $$ = addBinNode(OpPtrIndirection, $2, NULL);}
= addConstNode(OpIntConst, INT_TYPE, $1); } = addConstNode(OpCharConst, INT_TYPE, $1); } = addLitStringNode(makeStrVar($1));} = addBinNode(OpFuncCall, addVarNode($1), $3); } { $$ = $1;} { $$ = $2; } { $$ = addBinNode(OpAssign, $1, $3); } { $$ = addBinNode(OpPlus, $1, $3); } { $$ = addBinNode(OpMinus, $1, $3); } { $$ = addBinNode(OpMul, $1, $3); } { $$ = addBinNode(OpMod, $1, $3); } { $$ = addBinNode(OpDiv, $1, $3); } { $$ = addBinNode(OpLess, $1, $3); } { $$ = addBinNode(OpGr, $1, $3); }
17
Semantika obrada
| expression GE_TOK expression { $$ = | expression LE_TOK expression { $$ = | expression NE_TOK expression { $$ = | expression EQ_TOK expression { $$ = | expression OR_TOK expression { $$ = | expression AND_TOK expression { $$ = | '-' expression %prec UMINUS { $$ = | '!' expression %prec UMINUS { $$ = | INCR_TOK lvalexpr %prec UMINUS { $$ = | DECR_TOK lvalexpr %prec UMINUS { $$ = | '&' lvalexpr %prec UMINUS { $$ = | lvalexpr INCR_TOK { $$ = | lvalexpr DECR_TOK { $$ = ; expropt: /*null*/ {$$ = NULL;} | expression {$$ = $1;} ; addBinNode(OpGrEq, $1, $3); } addBinNode(OpLessEq, $1, $3); } addBinNode(OpNotEq, $1, $3); } addBinNode(OpEq, $1, $3); } addBinNode(OpLogOr, $1, $3); } addBinNode(OpLogAnd, $1, $3); } addBinNode(OpUnaryMinus, $2, NULL); } addBinNode(OpLogNot, $2, NULL); } addBinNode(OpPreIncr, $2, NULL); } addBinNode(OpPreDecr, $2, NULL); } addBinNode(OpVarAddress, $2, NULL);} addBinNode(OpPostIncr, $1, NULL); } addBinNode(OpPostDecr, $1, NULL); }
/* Cmm poznaje samo 3 prosta tipa: int, char i void . Korisniki tipovi nisu implementirani*/
type: | | ; INT_TOK CHAR_TOK VOID_TOK {$$ = mkIntType();} {$$ = mkCharType();} {$$ = mkVoidType();}
/* Funkcija dojave greke yyerror() ispisuje broj linije izvornog koda i poruku o greki te biljei broj greaka u varijabli g_parse_error */
void yyerror(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stdout,"Line %d:", g_linenum); vfprintf(stdout, fmt, args); fprintf(stdout,"\n"); va_end(args); g_parse_error++; } /*Kraj datoteke cmm.y*/
18
Semantika obrada Unutar postupka parsiranja (funkcija yyparse()) izgrauje se sintaktiko stablo za svaku funkciju, i zatim pozivaju funkcije typeCheck(np); i generateCode(np), gdje np predstavlja korijen sintaktikog stabla funkcije.
function: function_header blok { typeCheck($2); generateCode($2); deleteScope();}
provjera tipova izraza i odreivanje tipa kojim rezultiraju izrazi (expr_type), ako je potrebno, dodaju se vorovi za pretvorbu tipa (OpCast), provjera tipa Lvrijednosti kod izraza pridjele vrijednosti te inkrement i dekrement operatora, provjera tipova argumenata i parametara funkcije, za argumente funkcije, koji se prenose po referenci, provjerava se da li se argumentu moe odrediti potrebna adresa i dodaje se vor OpVarAddress
Konani rezultat semantike analize je da se dobije "dekorirano" sintaktiko stablo s ispravno odreenim expr_type i dodanim granama za OpCast i OpVarAdress.
bit e dopisano
19