Dodajmy do naszego interpretera obsługę stałych (np. PI) z których będziemy mogli korzystać podczas wyliczania wyrażeń. W tym celu zmodyfikujmy wprowadzoną ostatnio strukturę:
struct { char *name; symbolEnum type; double val; struct Symbol *next; } Symbol;
Wprowadźmy też nowy typ, który będzie nam służył do wskazywania typu symbolu (zmienna, stała oraz symbol o niezdefiniowanej wartości).
typedef enum { symbolVar, symbolConst, symbolUndef } symbolEnum;
W związku z wprowadzonymi z zmianami musimy również zmodyfikować naszą funkcję add w pliku symbol.c (oraz jej definicję w pliku symbol.h)
Symbol *add(char *s, symbolEnum type, double d) { Symbol *sp; if ((sp = (Symbol *) malloc(sizeof(Symbol))) == NULL ) yyerror("out of memory"); if ((sp->name = (char *) malloc(strlen(s)+1)) == NULL ) yyerror("out of memory"); strcpy(sp->name,s); sp->val = d; sp->type = type; sp->next = symlist; symlist = sp; return sp; }
Parser
Wprowadźmy również zmianę w pliku parse.y (należy zwrócić uwagę na zmianę definicji unii – teraz zamiast wskaźnika do nazwy zmiennej będziemy zapamiętywać wskaźnik do zmiennej typu Symbol).
%{ #include <stdio.h>; #include "symbol.h" int yylex(void); void yyerror(char *s ); %} %union { double val; Symbol *sym; } %start expression_list %token <val> NUM %token <sym> VARIABLE CONST UNDEF %type <val> statement expression %left '-' '+' %left '*' '/' %nonassoc UMINUS %% expression_list: expression_list statement '\n' { printf(" = %f\n", $2); } | expression_list '\n' { /* allow blank lines */ } | { /* empty string */ } ; statement: expression | VARIABLE '=' expression { $$=$1->val = $3; $1->type = symbolVar; } expression: NUM | VARIABLE { if ($1->type == symbolUndef) yyerror("Undefined variable"); $$=$1->val; } | CONST { $$=$1->val;} | '-' expression %prec UMINUS { $$ = -$2; } | '(' expression ')' { $$=$2; } | expression '+' expression { $$ = $1 + $3; } | expression '-' expression { $$ = $1 - $3; } | expression '*' expression { $$ = $1 * $3; } | expression '/' expression { $$ = $1 / $3; } ; %% void yyerror(char *s ) { fprintf (stderr, "%s\n", s); } int yywrap() { return 1; } int main (int argc, char **argv) { add("PI", symbolConst, 3.14159265359); return yyparse(); }
Warto zwrócić uwagę na sposób w jaki definiowana jest stała PI powyżej. W taki sam sposób możemy dodać kolejne stałe.
Lekser
Wprowadźmy zmiany do naszego leksera (plik scan.l)
%{ #include <stdio.h> #include "parse.h" void yyerror(char *s); %} %% [a-zA-Z_"][a-zA-Z_0-9]* { Symbol *s; if ( (s = find(yytext)) == NULL ) s = add( yytext, symbolUndef, 0); yylval.sym = s; switch(s->type){ case symbolUndef: return VARIABLE; case symbolConst: return CONST; default: return VARIABLE; } } [0-9]+ { yylval = atof (yytext); return NUM; } (([0-9]+(\.[0-9]*)?)|([0-9]*\.[0-9]+)) { yylval = atof (yytext); return NUM; } [-+()=/*\n] return *yytext; [ \t] ; /* skip whitespace */ . yyerror("invalid character"); %%