interpreter, Linux, Mac, Raspberry Pi

Zbudujmy interpreter (10) – instrukcje sterujące

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;
}
Standard

Dodaj komentarz