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

Podobne dokumenty