Wprowadźmy do naszego interpretera instrukcję warunkową IF .. THEN .. ELSE. Instrukcja IF sprawdza prawdziwość warunku znajdującego się po niej i w zależności od wyniku wykonuje instrukcje. Jak można zauważyć nasz parser nie posiada jeszcze możliwośći ewaluacji warunków. Zacznijmy w związku z tym od wprowadzenia operatorów <, <=, >, >=, ==, NOT, AND, OR oraz token IF do leksera (plik scaner.l).
%{ #include <stdio.h> #include "symbol.h" #include "ast.h" #include "parse.h" void yyerror(char *s); %} %% [0-9]+\.?|[0-9]*\.[0-9]+ { yylval.val = atof (yytext); return NUM; } ">=" return GE; "<=" return LE; "==" return EQ; "!=" return NE; [pP][rR][iI][nN][tT] return PRINT; [iI][fF] return IF; [tT][hH][eE][nN] return THEN; [eE][lL][sS][eE] return ELSE; [aA][nN][dD] return AND; [oO][rR] return OR; [nN][oO][tT] return NOT; [a-zA-Z_][a-zA-Z_0-9]* { Symbol *s; if (( s = find(yytext)) == NULL ) s = add(yytext, symbolUndef, 0.0); yylval.sym = s; switch(s->type){ case symbolUndef: return VARIABLE; case symbolConst: return CONST; case symbolBltin: return BLTIN; default: return VARIABLE; } } [-+()<>=/*;{}.\n] return *yytext; [ \t] ; /* skip whitespace */ . yyerror("invalid character"); %%
Odpowiednie zmiany musimy również wprowadzić do parsera (plik parser.y). Warto zwrócić uwagę, że w przypadku konstrukcji IF … THEN … ELSE parser może mieć problemy z interpretacją czy po wyrażeniu znajdującym się po słowie kluczowym THEN ma spodziewać się jeszcze ELSE czy nie (i jak je obsłużyć). Aby rozwiązać tą dwuznaczność dodamy definicję pierwszeństwa symboli IFX i ELSE.
%nonassoc IFX %nonassoc ELSE
%{ #include <stdio.h> #include <stdlib.h> #include <math.h> #include "symbol.h" #include "ast.h" int yylex(void); void yyerror(char *s); %} %union { double val; Symbol *sym; nodeType *nPtr; } %start expression_list %token <val> NUM %token <sym> VARIABLE CONST BLTIN UNDEF %token PRINT IF THEN %type <nPtr> expression_list expression statement %left GE LE EQ NE '>' '<' OR AND NOT %left '-' '+' %left '*' '/' %nonassoc UMINUS %nonassoc IFX %nonassoc ELSE %% expression_list: expression_list statement '\n' { execute($2); freeNode($2); } | expression_list '\n' { /* allow blank lines */ } | { /* empty string */ } ; statement: expression | VARIABLE '=' expression { $$ = add_operator('=', 2, add_identifier($1),$3); } | PRINT expression { $$ = add_operator(PRINT,1, $2); } | IF expression THEN statement %prec IFX { $$ = add_operator(IF,2, $2, $4); } | IF expression THEN statement ELSE statement { $$ = add_operator(IF,3,$2, $4, $6); } ; expression: NUM { $$ = add_constant($1); } | VARIABLE { $$ = add_identifier($1); } | CONST { $$=add_identifier($1); } | BLTIN '(' expression ')' { $$=add_operator('@', 2, add_identifier($1), $3); } | '-' expression %prec UMINUS { $$ =add_operator(UMINUS,1,$2); } | '(' expression ')' { $$=$2; } | expression '+' expression { $$ = add_operator('+',2,$1,$3); } | expression '-' expression { $$ = add_operator('-',2,$1,$3); } | expression '*' expression { $$ = add_operator('*',2,$1,$3); } | expression '/' expression { $$ = add_operator('/',2,$1,$3); } | expression '<' expression { $$ = add_operator('<',2,$1,$3); } | expression '>' expression { $$ = add_operator('>',2,$1,$3); } | expression GE expression { $$ = add_operator(GE,2,$1,$3); } | expression LE expression { $$ = add_operator(LE,2,$1,$3); } | expression NE expression { $$ = add_operator(NE,2,$1,$3); } | expression EQ expression { $$ = add_operator(EQ,2,$1,$3); } | expression AND expression { $$ = add_operator(AND,2,$1,$3); } | expression OR expression { $$ = add_operator(OR,2,$1,$3); } | NOT expression { $$ = add_operator(NOT,1,$2); } ; %% void yyerror(char *s ) { fprintf (stderr, "%s\n", s); } int yywrap() { return 1; } double sinx(double x) { return sin(x); } double cosx(double x) { return cos(x); } int main (int argc, char **argv) { Symbol *s; add("PI", symbolConst, 3.14159265359); s = add("sin", symbolBltin, 0); s->u.ptr = sin; s = add("cos", symbolBltin, 0); s->u.ptr = cos; return yyparse(); }
Odpowiednio musimy również zmodyfikować funkcję execute() w pliku ast.c.
double execute(nodeType *p) { if (!p) return 0; switch (p->type) { case typeCon: return p->con.value; case typeId: return p->id.symbol->u.val; case typeOpr: switch(p->opr.oper) { case PRINT: printf("%f\n", execute(p->opr.op[0]) ); return 0; case IF: if ( execute(p->op[0]) ) execute(p->op[1]); else if(p->opr.nops > 2) execute(p->op[2]); return 0; case '=': p->opr.op[0]->id.symbol->type = symbolVar; return p->opr.op[0]->id.symbol->u.val = execute(p->opr.op[1]); case UMINUS: return -execute(p->opr.op[0]); case '+': return execute(p->opr.op[0]) + execute(p->opr.op[1]); case '-': return execute(p->opr.op[0]) - execute(p->opr.op[1]); case '*': return execute(p->opr.op[0]) * execute(p->opr.op[1]); case '/': return execute(p->opr.op[0]) / execute(p->opr.op[1]); case '<': return execute(p->opr.op[0]) < execute(p->opr.op[1]); case '>': return execute(p->opr.op[0]) > execute(p->opr.op[1]); case GE: return execute(p->opr.op[0]) >= execute(p->opr.op[1]); case LE: return execute(p->opr.op[0]) <= execute(p->opr.op[1]); case NE: return execute(p->opr.op[0]) != execute(p->opr.op[1]); case EQ: return execute(p->opr.op[0]) == execute(p->opr.op[1]); case OR: return execute(p->opr.op[0]) || execute(p->opr.op[1]); case AND: return execute(p->opr.op[0]) && execute(p->opr.op[1]); case NOT: return !(execute(p->opr.op[0])); case '@': return (*(p->opr.op[0]->id.symbol->u.ptr))(execute(p->opr.op[1])); } } return 0; }