hakeの日記

Windows環境でプログラミングの勉強をしています。

bison & flexメモ その6

C言語でbisonとflexを使う方法のメモ
式の構文木を作ってみる。


mainからyyparse()を呼んで入力した式の構文木を作成してそのポインタを返す。そのポインタからeval_tree()で式を評価、free_tree()で構文木の為に確保したメモリの解放を行う。構文木はNode型の構造体を定義して、それを積み重ねていくことで作成している。
よくわからないのが、構文木のトップのポインタをmain側へ渡す方法。yyparse()で渡せないので、とりあえずnode.c内にstaticなポインタを作成してbison側でset_tree()を呼び、main側でget_tree()を呼ぶことで、これを介して渡しているけれども正しい方法なのか不明。

expr.y

%{
#include <stdio.h>
#include "node.h"

/* デバッグ用 */
/*#define YYDEBUG 1*/

/* エラー内容を詳細に報告 */
#define YYERROR_VERBOSE 1

%}

/* ここで宣言した変数は共用体yylvalのメンバとしてアクセス可能 */
%union { 
   Node   *node;
   double val;
}

%token NUMBER
%token PLUS MINUS MULT DIV
%token LP RP EOL ERR

%left  MINUS PLUS
%left  MULT DIV
%left  NEG

/* < >の中は%union{ }に書いたメンバ名                  */
%type  <node> exp
%type  <val>  NUMBER


/* 以下、%%〜%%間は構文規則 */
%%
line    :                        { set_tree(NULL);} 
        | exp                    { set_tree($1);  }
;

exp     : NUMBER                 { $$ = node_num_new($1); }

        | exp PLUS  exp          { $$ = node_new(OP_ADD, $1, $3);}
        | exp MINUS exp          { $$ = node_new(OP_SUB, $1, $3);}
        | exp MULT  exp          { $$ = node_new(OP_MUL, $1, $3);}
        | exp DIV   exp          { $$ = node_new(OP_DIV, $1, $3);}
        | MINUS  exp %prec NEG   { $$ = node_new(OP_NEG, $2, NULL);}
        | LP exp RP              { $$ = $2; }
;

%%

void yyerror(char *s)
{
  printf("%s\n",s);
}

expr.l

%{
#include <stdio.h>
#include "node.h"        /* こちらを先にincludeする */
#include "expr.tab.h"

/* 読み込みファイルが複数ある場合に使用、今は1を返す様にしておく */
int yywrap(void){ return 1;}

%}

%%

[0-9]+            { /* ここで読んだ値がbison側の構文規則 NUMBERの$1の値になる */
                    yylval.val = atof(yytext);
                    return(NUMBER);
                  }
[0-9]+\.[0-9]+    { /* ここで読んだ値がbison側の構文規則 NUMBERの$1の値になる */
                    yylval.val = atof(yytext);
                    return(NUMBER);
                  }
"+"        return(PLUS);
"-"        return(MINUS);
"*"        return(MULT);
"/"        return(DIV);
"("        return(LP);
")"        return(RP);
" "        {}
"\n"       {}
.          { yyerror("Illegal character"); 
             return(ERR);
           }
%%

main.c

#include <stdio.h>
#include "node.h"

extern int yyparse(void);
extern FILE *yyin;
extern int yydebug; /* デバッグ用 */


int main(int argc, char ** argv){
    Node *p;
    int ret;

    /* デバッグ用 */
    /*yydebug = 1;*/

    /* yyinをファイル入力に切り替え */
    if((yyin = fopen( argv[1], "r")) == NULL){
        fprintf(stderr,"Can't open a input file!\n");
            return 1;
    }

    ret = yyparse();   /* 入力した式の構文解析実施 */
    printf("yyparse return : %d\n", ret);
    if(ret == 0){
        p = get_tree(); /* 構文木の取得 */
        if(p == NULL)
            printf("tree is NULL\n");
        else{
            printf("tree value = %g\n", eval_tree(p)); /* 構文木の評価と表示 */
            free_tree(p); /* 構文木で使用したメモリの解放 */
        }
    }
    return ret;
}

node.h

#ifndef NODE_H
#define NODE_H

#include <stdlib.h>

typedef enum{ OP_ADD = 1, OP_SUB, OP_MUL, OP_DIV, OP_NEG, OP_NUM
    } NodeType;

typedef struct Node_def Node;

struct Node_def{
    NodeType type;
    double   val;
    Node     *left;
    Node     *right;
};

void set_tree(Node *);
Node *get_tree(void);
Node *node_alloc(void);
Node *node_new(NodeType, Node *, Node *);
Node *node_num_new(double);
void free_tree(Node *);
double eval_tree(Node *);

#endif /*NODE_H*/

node.c

#include "node.h"

static Node *tree_root; /* mainとbison間の構文木受け渡し用ポインタ */

/* bison(yyparse)で作成した構文木のルートポインタを設定 */
void set_tree(Node *p){
    tree_root = p;
}

/* 設定された構文木のルートポインタを取得(mainから呼ばれる) */
Node *get_tree(void){
    return tree_root;
}

/* 構文木のノードに必要なメモリを取得 */
Node *node_alloc(void){
    Node *p;
    p = (Node *)malloc(sizeof(Node));
    return p;
}

/* 構文木のノードを新規作成 */
Node *node_new(NodeType t, Node *left, Node *right){
    Node *p;
    p = node_alloc();
    p->type = t;
    p->left  = left;
    p->right = right;
    return p;
}

/* 数値用ノードの作成 */
Node *node_num_new(double v){
    Node *p;
    p = node_alloc();
    p->type = OP_NUM;
    p->val  = v;
    p->left  = NULL;
    p->right = NULL;
    return p;
}

/* 構文木のメモリの解放 */
void free_tree(Node *p){

    if(p == NULL) return;

    Node *left;
    Node *right;
    left  = p->left;
    right = p->right;

    free(p);
    if(left != NULL)
    	free_tree(left);
    if(right != NULL)
        free_tree(right);
}

/* 構文木の評価 */
double eval_tree(Node *p){
    NodeType t;
    double ret;

    t = p->type;
    switch(t){
        case OP_NUM:
            ret =  p->val;
            break;
        case OP_ADD:
            ret = eval_tree(p->left) + eval_tree(p->right);
            break;
        case OP_SUB:
            ret = eval_tree(p->left) - eval_tree(p->right);
            break;
        case OP_MUL:
            ret = eval_tree(p->left) * eval_tree(p->right);
            break;
        case OP_DIV:
            ret = eval_tree(p->left) / eval_tree(p->right);
            break;
        case OP_NEG:
            ret = eval_tree(p->left) * -1;
            break;
    }
    return ret;
}

Makefile

TARGET = expr
BISON_SRC = $(TARGET).y
BISON_H   = $(TARGET).tab.h
BISON_C   = $(TARGET).tab.c
BISON_O   = $(TARGET).tab.o
BISON_OUT = $(BISON_C) $(BISON_H) $(TARGET).output
FLEX_SRC  = $(TARGET).l
FLEX_C    = lex.yy.c
FLEX_O    = lex.yy.o
C_FILES   = $(BISON_C) $(FLEX_C) node.c main.c
OBJ       = $(FLEX_O) $(BISON_O) node.o main.o

all : $(TARGET)

$(TARGET) : $(OBJ)
	gcc -o $@ $(OBJ)

$(FLEX_C) : $(FLEX_SRC) $(BISON_H)
	flex $(FLEX_SRC)

$(BISON_OUT) : $(BISON_SRC)
	bison -dv $(BISON_SRC)

.c.o :
	gcc -c $<


clean :
	rm -f *.exe
	rm -f lex.yy.c expr.tab.c
	rm -f lex.yy.h expr.tab.c
	rm -f *.output
	rm -f *.o

lex.yy.o: lex.yy.c expr.tab.h
expr.tab.o: expr.tab.c expr.tab.h node.h
node.o: node.c node.h
main.o: main.c node.h

sample.txt

(1+2+3 + 4+5+6+7+8+9+10)
*3.14
*-1
/2

実行結果

~$ ./expr.exe sample.txt
yyparse return : 0
tree value = -86.35