interpreter, Linux, Mac, Raspberry Pi

Zbudujmy interpreter (7) – funkcje wbudowane

Skoro dodaliśmy obsługę stałych do naszego interpretera to nic nie stoi na przeszkodzie aby dodać również możliwość korzystania z funkcji wbudowanych (np. trygonometrycznych).
Zacznijmy od modyfikacji definicji struktury Symbol oraz definicji typu wyliczeniowego symbolEnum w pliku symbol.h.

typedef enum { symbolVar, symbolConst, symbolBltin, symbolUndef } symbolEnum;
struct {
        char *name;
        symbolEnum type;
        union {
           double val;
           double (*ptr)();
        } u;
        struct Symbol *next;
} Symbol;

Parser

W pliku parse.y dodajmy obsługę funkcji.

%{
#include <stdio.h>
#include <math.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 BLTIN
%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->u.val = $3; $1->type = symbolVar; }
         
expression:
        NUM 
        | VARIABLE { if ($1->type == symbolUndef) yyerror("Undefined variable"); $$=$1->u.val; }
        | CONST { $$=$1->u.val;}
        | BLTIN '(' expression ')' { $$ = (*($1->u.ptr))($3); }
        | '-' 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; }

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 = sinx;
        s=add("cos", symbolBltin, 0);
        s->u.ptr = cosx;

        return yyparse();
}

Warto zwrócić uwagę na sposób definiowania funkcji wbudowanych (sin() i cos()) powyżej.

Lekser

Ostatnią rzeczą jaką musimy zrobić to zmiana pliku scan.l w celu identyfikacji tokenu BLTIN (w taki sposób lekser identyfikuje nasze funkcje wbudowane).

%{
#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;
                              case symbolBltin: return BLTIN;
                              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");
%%
Standard

Dodaj komentarz