interpreter, Linux, Mac, Raspberry Pi

Zbudujmy interpreter (6) – stałe

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");
%%
Standard

Dodaj komentarz