W projekcie tym implementujemy interpreter prostego jezyka
Transkrypt
W projekcie tym implementujemy interpreter prostego jezyka
W projekcie tym implementujemy interpreter prostego jezyka programowania. Zaczniemy od przykladu programu obliczajacego silnie, napisanego w tym jezyku: { Prosty program obliczajacy silnie } read x; { Wczytaj x } if x > 0 then { nie obliczaj gdy x <= 0 } fact := 1; repeat fact := fact * x; x := x - 1; until x = 0; write fact; { wypisz obliczona silnie } end Mamy nastepujaca gramatyke: program -> stmt-sequence stmt-sequence -> stmt-sequence ; statement | statement statement -> if-stmt | repeat-stmt | assign-stmt | read-stmt | write-stmt if-stmt -> if exp then stmt-sequence end | if exp then stmt-sequence else stmt-sequence end repeat-stmt -> repeat stmt-sequence until exp assign-stmt -> identifier := exp read-stmt -> read identifier write-stmt -> write exp exp -> simple-exp comparison-op simple-exp | simple-exp comparison-op -> < | = | > | <= | >= | != simple-exp -> term | ( simple-exp ) | simple-exp simple-op simple-exp simple-op -> + | - | * | / term -> number | identifier oraz oczywiscie naturalne priorytety dla + - / i * prosze number przyjac cyfra+, identifier [_a-zA-Z][_a-zA-Z0-9]* nasz interpreter wykonuje dwa kroki: 1) zamiana postaci programu w kodzie zrodlowym na drzewo analizy skladniowej 2) przejscie drzewa w celu wykonania programu Oznacza to, ze yyparse bedzie budowac pewne drzewo, nazwijmy je t, zas traverse bedzie je przechodzic w celu interpretacji. Caly program bedzie wygladal, jak nizej: int main() { Wezel* t = NULL; yyparse(); traverse(t); return 0; } Szkieletem dla procedury yyparse moze byc ponizszy kalkulator: %token NUMBER %token IF %token ELSE %left '-' '+' %left '*' '/' %% /* The grammar follows. */ input: exp '\n' {t = $1; } ; ifexp: IF '(' exp ')' exp { struct Wezel * tmp = (struct Wezel*) malloc(sizeof(struct Wezel)); tmp -> typ = IF_ST; tmp -> expr = $3; tmp -> seq1 = $5; $$ =tmp; } | IF '(' exp ')' exp ELSE exp { struct Wezel * tmp = (struct Wezel*) malloc(sizeof(struct Wezel)); tmp -> typ = IF_ST; tmp -> expr = $3; tmp -> seq1 = $5; tmp -> seq2 = $7; $$ =tmp; } ; exp: NUMBER | exp '+' exp malloc(sizeof(struct Wezel)); tmp seq1 = $1; tmp -> seq2 = $3; $$ = | exp '-' exp malloc(sizeof(struct Wezel)); tmp seq1 = $1; tmp -> seq2 = $3; $$ = { $$ = $1; { struct Wezel * -> op = ADD; tmp tmp; } { struct Wezel * -> op = SUB; tmp tmp; } } tmp = (struct Wezel*) -> typ = EXPR; tmp -> | exp '*' exp malloc(sizeof(struct Wezel)); tmp seq1 = $1; tmp -> seq2 = $3; $$ = | exp '/' exp malloc(sizeof(struct Wezel)); tmp seq1 = $1; tmp -> seq2 = $3; $$ = | '(' exp ')' | ifexp ; { struct Wezel * -> op = MUL; tmp tmp; } { struct Wezel * -> op = DIV; tmp tmp; } { $$ = $2; { $$ = $1; tmp = (struct Wezel*) -> typ = EXPR; tmp -> tmp = (struct Wezel*) -> typ = EXPR; tmp -> tmp = (struct Wezel*) -> typ = EXPR; tmp -> } } Zas szkieletem dla traverse() moze byc ponizszy fragment: #include <stdio.h> #include "travers.h" void traverseExpr(struct Wezel* w) { if (w->typ != EXPR) { printf("internal error in expr\n"); return; } if (w -> seq1 != NULL) traverseExpr(w -> seq1); switch(w -> op) { case ADD: printf(" + "); case SUB: printf(" - "); case DIV: printf(" / "); case MUL: printf(" * "); case NUM: printf("%d", w case ID: break; break; break; break; -> num); break; printf("%s", w -> ident); break; default: printf("internal error\n"); break; } if (w -> seq2 != NULL) traverseExpr(w -> seq2); } void traverse(struct Wezel* w) { struct Wezel * cur; switch(w -> typ) { case STMT_SEQ: cur = w-> head; traverse(cur); while (cur != w->tail) { cur = cur->next; traverse(cur); } break; case IF_ST: printf("if statement seen\n"); break; case REP_ST: printf("repeat statment seen\n"); break; case EXPR: printf("entering expression\n"); traverseExpr(w); break; default: printf("internal error\n"); break; } } Strukture drzewa mozna zbudowac w oparciu o ponizsze deklaracje (w powyzszym przykladzie bylby to plik "travers.h"): enum rodzajOperacji {ADD, SUB, MUL, DIV, NUM, ID}; enum TypWezla {STMT_SEQ, IF_ST, REP_ST, EXPR}; struct Wezel { enum TypWezla typ; struct Wezel *head; struct Wezel *tail; struct Wezel *next; struct Wezel *expr; struct Wezel *seq1; struct Wezel *seq2; // dla czesci then // dla czesci else enum rodzajOperacji op; int num; char* ident; }; void traverseExpr(struct Wezel*); void traverse(struct Wezel*); Wiecej na ten temat w dokumencie: http://math.uni.lodz.pl/~pustelj/kompilatory/codegen.html