小白說編譯原理-9-最簡單minus-c語言編譯器

lipeng08發表於2016-05-07

簡介

繼上節8說到利用手動構建的語法樹解析下面的c語言程式碼:

    a = 1
    sum = 0
    input(x)
    while(a <= x){
       sum = sum + a
       a = a+1;
    }
    print(sum)

而一個編譯器不應該依賴使用者去手動構建對應語言的語法樹,我們需要的是一種支援自動構建語法樹的策略。本節將要說明的就是如何利用前面1-6節學到的lex,yacc以及符號表,7-8節學到的語法樹來支援給定c語言程式碼自動構建過程。

動機

在第5節變數支援的計算器中,對於expr的語法解析有如下的yacc程式碼:

 lines   :   lines expr EOL  { printf("%g\n", $2); }
            |   lines EOL
            |   lines COMMENT
            |   
            ;

    expr    :   expr PLUS expr  { $$ = $1 + $3; }
			|	expr MINUS expr	{ $$ = $1 - $3; }
			|	expr TIMES expr	{ $$ = $1 * $3; }
			|	expr OVER expr	{ $$ = $1 / $3; }
			|	LP expr RP	{ $$ = $2; }
			|	'-' expr %prec UMINUS	{ $$ = -$2; }
			|	NUMBER {$$=$1;} //$$=$1 can be ignored
            |   ID {$$ = sym_table.getValue($1);}//get value from sym_table
			|   ID ASSIGN expr {sym_table.setValue($1, $3); $$=$3; }//modify the value

已經知道的是上面每一行都是對應的語法匹配規則(例如expr PLUS expr)以及當規則匹配後要執行的動作(位於{}中,例如$$ = $1 + $3;)。如果將上面要執行的動作修改為建立對應的語法表示式節點,不就可以實現自動構建語法樹了嗎? 針對上面的expr PLUS expr,其對應的動作可以修改為

$$ = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(PLUS), Integer, $1, $3);

同理,其他語法規則的執行動作也可以進行相應的修改,這樣當一個表示式語法分析完畢後,對應的表示式語法樹也就構建完成了。

總體概覽

  1. lex和yacc進行相應的詞法,語法分析,並構建對應的語法樹
  2. tree.h和tree.cpp用來支撐語法樹的構建過程,提供相應的建立函式,被yacc使用
  3. symtable.h和symtable.cpp用來支撐符號表的構建過程,提供符號表的建立,訪問,修改等操作,用於支援變數以及可能的函式擴充套件。
  4. 只支援最基本的c語言,也就是第8節已經進行測試過的。總述如下:變數,變數賦值,算術邏輯等運算,if語句,while語句,輸入輸出語句,表示式語句,複合語句(不支援變數宣告,變數名出現的第一次開始將其加入到符號表,預設值為0,使用賦值運算子可對變數值進行修改)。

可支援如下的程式碼:
其為迭代法解一元二次方程組,方程的三個引數為a,b,c。

main() 
{      
    //求解X1,在曲線對稱軸處選擇初始點     
    //x^2+3x+2 = 0
    a = 1;
    b = 3;
    c = 2;
    accuracy = 0.00001;
    index=(-1.0*b)/(2*a);
    if(b!=0)//b不等於0時進行迭代     
    {          
        temp=index;          
        index=-1.0*(a*temp*temp+c)/b;    
        while((fabs(index-temp))>accuracy)         
        {              
            temp=index;              
            index=-1.0*(a*temp*temp+c)/b;         
        }          
        x1=index;          
        x2=(-1.0*b)/a-x1; 
    }      
    else//b=0時ax^2+c=0直接求解     
    {          
        x1=sqrt(-1.0*c/a);         
        x2=-x1;     
    } 

    output(x1);
    output(x2);
}

編譯器輸出結果
這裡寫圖片描述

lex檔案原始碼以及解析

前面所述,lex檔案完成的詞法解析的工作。 它將輸入的字串進行拆分成一個個的token,然後返回相應的token型別,將其傳入到yacc中。例如:

letter [a-zA-Z]
digit [0-9]
id    {letter}({letter}|{digit})*

上述三行lex程式碼定義了三種token,字母,數字以及標示符。

lex在解析c語言程式碼時候,如果遇到相應的token,會向執行token中預定義的動作,例如:

{id}        {
                int p = sym_table.lookup(yytext);
                if(p == -1){//not find
                    p = sym_table.insert(yytext);//insert the default value 0.0
                }
                yylval = &dummy;
                yylval->attr.symtbl_seq = p;//return the position
                return ID;
            }
{number}    {
                yylval = &dummy;
                yylval->attr.vali = atof(yytext);
                return NUMBER;
            }

上述程式碼顯得有些複雜了。id是標示符的解析,yytext是已經切分的token,首先向符號表sym_table中查詢yytext,如果沒有找到,那麼將其插入到符號表;如果找到,直接返回此yytext對應的符號表的索引位置p。 隨後對yylval賦值,並設定它的符號表的序號為p,最後返回ID,表明它是一個標示符。 注意p要被yacc所用,例如當碰到a=2,這個賦值動作是在yacc中進行分析的,在yacc構建語法樹的時候,必須能夠得到a這個標示符,然而lex僅僅返回了ID標示符,但lex通過yylval這個變數間接告訴了yacc詞法解析器,此ID確切的內容,即是a。 如果id能夠完全理解,那麼number就更簡單了,直接使用atof函式將字串轉換為double值,並賦值給yylval。

你可能有疑問yacc在哪裡使用的yylval呢? 下面給出一小段yacc的程式碼,作為引子:

var : ID { $$ = createId(parser_tree, (int)($1->attr.symtbl_seq));} //$1 stores yylval, returned by lex.l

上面的是yacc語法分析動作,當碰到ID標示符的時候,呼叫createId建立一個Node節點,並傳入$1->attr.symtbl_seq。 $1就是lex中構造的yylval,這裡直接使用它的符號表的序號。

下面是完整的lex程式碼:

%{
//this code will be added into the header of generated .cpp file
#include <iostream>
#include "sym_table.h"
#include "yacc.h"
#include "tree.h"
using namespace std;

int lineno = 1;
Node dummy;

//already defined in yacc.y, use %token...
//enum{LT,  EQ, GT, IF, ELSE, ID, NUMBER, PLUS, MINUS, TIMES, OVER, INT, DOUBLE,CHAR, LP,RP};

const char* tokenStr[] = {"LT",  "EQ", "GT", "IF", "ELSE", "ID", "NUMBER", "PLUS", "MINUS", "TIMES", "OVER", "INT", "DOUBLE","CHAR"};
static void print_token(int token, char* lex);

%}

%name lexer

delim [ \t]
ws    {delim}+
letter [a-zA-Z]
digit [0-9]
id    {letter}({letter}|{digit})*
/* can support 12.34 */
number -?{digit}+(\.{digit}+)?
//(-?[1-9]+[0-9]*)|(-?[1-9])|0

%%
%{
//this code will be added into yyaction function
    YYSTYPE YYFAR& yylval = *(YYSTYPE YYFAR*)yyparserptr->yylvalptr;
%}

{ws} {/* do nothing */}
"int"  {print_token(INT, yytext); return INT;}
"double"  {print_token(DOUBLE, yytext);}
"char"  {print_token(CHAR, yytext);}

";"         {return SEMICOLON;}
","         {return COMMA;}

"+"         {print_token(PLUS, yytext); return PLUS;}
"-"         {print_token(MINUS, yytext); return MINUS;}
"*"         {print_token(TIMES, yytext); return TIMES;}
"/"         {print_token(OVER, yytext); return OVER;}
"("         {return LP;}
")"         {return RP;}

"<"         {return LT;}
">"         {return GT;}
"<="        {return LE;}
">="        {return GE;}
"=="        {return EQ;}
"!="        {return NE;}
"\n"        {lineno++;}
"="         {return ASSIGN;}
"if"        {return IF;}
"else"      {return ELSE;}
"while"     {return WHILE;}
"input"     {return INPUT;}
"output"    {return OUTPUT;}
"main"      {return MAIN;}
"$"            {return END;}

"{"         {print_token(LBRACE, yytext); return LBRACE;}
"}"         {print_token(RBRACE, yytext); return RBRACE;}
"||"        {print_token(OR, yytext); return OR;}
"&&"        {print_token(AND, yytext); return AND;}
"!"         {print_token(NOT, yytext); return NOT;}
"sqrt"      {return SQRT;}
"fabs"      {return FABS;} 

{id}        {
                int p = sym_table.lookup(yytext);
                if(p == -1){//not find
                    p = sym_table.insert(yytext);//insert the default value 0.0
                }
                yylval = &dummy;
                yylval->attr.symtbl_seq = p;//return the position
                return ID;
            }
{number}    {
                yylval = &dummy;
                yylval->attr.vali = atof(yytext);
                return NUMBER;
            }
"//"       {
                char c;
                do
                {
                    c = input();
                }while(c != '\n');
                lineno++;
            }
"\""        {
                char c = '0';
                int i = 0;
                char* buf = (char*)malloc(1024);
                while(c != '\"')
                {
                    c = input();
                    buf[i++] = c;
                };
                buf[i] = '\0';
                yylval->sibling = (Node*)buf; 
                printf("buf=%s\n", buf);
                //return STRING;
            }
"."         {printf("Mystery character %s\n", yytext); }
%%



static void print_token(int token, char* lex)
{
#ifdef LEX_DEUB
    cout<<"token:" << token<<" "<<"lex:"<<lex<<endl;
#endif
}

符號表程式碼及分析

符號表顧名思義就是一個表格,能夠根據符號的名字找到它的詳細資訊。為了簡單,這裡使用陣列實現,能夠根據符號名得到它隸屬於的表格的序號,以及它的節點Node指標(這個是構建語法樹使用的節點)。

比較簡單,不再詳細解析
sym_talbe.h:

#include <iostream>
#include <map>
#include <vector>
#include "yacc.h"
#include "lex.h"
using namespace std;

struct Node;
struct TableNode
{
    string name;
    double value;
    Node* p_node;//also store the corresponding node pointer of name
};
class SymTable
{
public:
    SymTable(){

    }
public:
    int lookup(const string& name){
        for (int i = 0; i < idValueTable.size(); ++i)
        {
            if(idValueTable[i].name.compare(name) == 0){
                return i;
            }
        }
        return -1;//not find
    }

    int insert(const string& name){//when parser x=2 (current we get x)
        TableNode node;
        node.name = name;
        node.value = 0.0;
        node.p_node = NULL;
        idValueTable.push_back(node);
        return idValueTable.size()-1;
    }
    void setValue(int pos, double value){//when parser x=2 (current we get x)
        idValueTable[pos].value = value;
    }
    double getValue(int pos){
        return idValueTable[pos].value;
    }
    Node* getNode(int pos){
        return idValueTable[pos].p_node;
    }
    void setNode(int pos, Node* p){
        idValueTable[pos].p_node = p;
    }
    const string& getName(int pos){
        return idValueTable[pos].name;
    }
private:
    vector<TableNode> idValueTable;
};
extern SymTable sym_table;

sym_table.cpp

#include "sym_table.h"

SymTable sym_table;

yacc程式碼以及分析

yacc語句描述的就是構建語法樹的過程,例如:

program : MAIN LP RP compond_stmt { $$=$4; parser_tree.setRoot($$); executeTree(parser_tree); exit(0);} //for test
            ;
compond_stmt : LBRACE stmt_list RBRACE
            {
                $$ = createCompStmt(parser_tree, $2);    

            }
            ;

首先program是由後面的MAIN LP RP compond_stmt構建的,分別對應於$1-$4。也就是說它需要碰到main()這樣的格式才算語法正確。執行的動作首先$$=$4,表明首先計算compond_stmt,這就會遞迴到compond_stmt節點進行構建,並將返回的結果賦值給當前的$$, 隨後將其設定為根root, 並執行此語法樹。 這就與第8節的有所不同,這裡利用yacc的規則動作自動構建的語法樹,第8節是手動一個節點一個節點的組合構造。

再舉例if語句:

if_stmt  : IF LP expression RP stmt ELSE stmt {$$ = createIfStmt(parser_tree, $3, $5, $7);}
			| IF LP expression RP stmt {$$ = createIfStmt(parser_tree, $3, $5);}

這定義了if的規則需要滿足if( expr ) stmt else stmt 或者 if (expr) stmt,執行的動作為建立一個if語句Node,並將expr, stmt1, stm2放入到此Node節點中。

其他所有的規則與這個都非常相似,簡單分析即可明白其原因。

下面是完整yacc程式碼:

#include <fstream>
#include "lex.h"
#include "sym_table.h"
#include "tree.h"
%}

%name parser
// class definition
{
    // place any extra class members here
}
// constructor
{
    // place any extra initialisation code here
}

// destructor
{
    // place any extra cleanup code here
}

// place any declarations here
%include {
#ifndef YYSTYPE
#define YYSTYPE Node *
#endif
}

%token NUMBER ID
%token PLUS MINUS TIMES OVER
%token LP RP EOL COMMENT LBRACE RBRACE
%TOKEN INT DOUBLE CHAR
%token ASSIGN LT GT GE NE LE EQ AND OR NOT SQRT FABS
%token IF ELSE WHILE INPUT OUTPUT
%token SEMICOLON COMMA MAIN END

%right UMINUS

%%

program : MAIN LP RP compond_stmt { $$=$4; parser_tree.setRoot($$); executeTree(parser_tree); exit(0);} //for test
            ;
compond_stmt : LBRACE stmt_list RBRACE
            {
                $$ = createCompStmt(parser_tree, $2);    

            }
            ;

stmt_list : stmt_list stmt
            {
                YYSTYPE t = $1;
                if($1 != NULL){
                    while(t->sibling != NULL)
                        t = t->sibling;
                    t->sibling = $2;
                    $$ = $1;
				}else {
					$$ = $2;
                }

            }
            | stmt {$$ = $1;}
			;
stmt	:	expr_stmt{$$=$1;}
			| compond_stmt {$$=$1;}
			| if_stmt {$$=$1;}
			| while_stmt {$$=$1;}
			| input_stmt {$$=$1;}
			| output_stmt {$$=$1;}
            ;

if_stmt  : IF LP expression RP stmt ELSE stmt {$$ = createIfStmt(parser_tree, $3, $5, $7);}
			| IF LP expression RP stmt {$$ = createIfStmt(parser_tree, $3, $5);}
			;
while_stmt : WHILE LP expression RP stmt {$$ = createWhileStmt(parser_tree, $3, $5);}
			;
input_stmt : INPUT LP var RP {$$ = createInputStmt(parser_tree, $3);}
			;
output_stmt : OUTPUT LP var RP {$$ = createOutStmt(parser_tree, $3);}
			;
expr_stmt : expression SEMICOLON {$$=$1;}
			| SEMICOLON {$$=NULL;} //not to create any Node
			;
expression: var ASSIGN expression {$$ = createOpExpr(parser_tree, $1, $3, ASSIGN);}
			| or_expression {$$=$1;}
			| MINUS expression %prec UMINUS	{ $$ = createOpExpr(parser_tree, $2, NULL, UMINUS_EXPR); }
            ;

or_expression : or_expression OR and_expression {$$ = createOpExpr(parser_tree, $1, $3, OR);}
			| and_expression {$$ = $1;}
			;
and_expression : and_expression AND additive_expression {$$ = createOpExpr(parser_tree, $1, $3, AND);}
			| additive_expression {$$=$1;}
			;
additive_expression : additive_expression EQ rel_expression {$$ = createOpExpr(parser_tree, $1, $3, EQ);}
					| additive_expression LT rel_expression	{$$ = createOpExpr(parser_tree, $1, $3, LT);}
					| additive_expression GT rel_expression	{$$ = createOpExpr(parser_tree, $1, $3, GT);}
					| additive_expression LE rel_expression	{$$ = createOpExpr(parser_tree, $1, $3, LE);}
					| additive_expression GE rel_expression	{$$ = createOpExpr(parser_tree, $1, $3, GE);}
					| additive_expression NE rel_expression	{$$ = createOpExpr(parser_tree, $1, $3, NE);}
					| rel_expression {$$ = $1;}
					;
rel_expression : rel_expression PLUS term {$$ = createOpExpr(parser_tree, $1, $3, PLUS);}
                    | rel_expression MINUS term {$$ = createOpExpr(parser_tree, $1, $3, MINUS);}
                    | term { $$ = $1; }
                    ;
term                : term TIMES factor {$$ = createOpExpr(parser_tree, $1, $3, TIMES);}
                    | term OVER factor {$$ = createOpExpr(parser_tree, $1, $3, OVER);}
                    | factor { $$ = $1; }
                    ;
factor              : LP expression RP { $$ = $2; }
                    | var { $$ = $1; }
                    | NUMBER {$$ = createConst(parser_tree, $1->attr.vali);}
                    //| CONSTCHAR {$$ = createOpExpr(parser_tree, $1, $3, OVER);}
                    | NOT factor {$$ = createOpExpr(parser_tree, $2, NULL, NOT_EXPR);}
					| SQRT factor {$$ = createOpExpr(parser_tree, $3, NULL, SQRT);}
					| FABS factor  {$$ = createOpExpr(parser_tree, $3, NULL, FABS);}
                    ;
var               : ID { $$ = createId(parser_tree, (int)($1->attr.symtbl_seq));} //$1 stores sym_seq, returned by lex.l
                    ;
%%



int main(int argc, char *argv[])
{
    printf("This is a simple compiler.\n");
    printf("You can copy the main functions in input.txt and paste it here\n");
    printf("There are two main functions and they will work both.\n");

    int n = 1;
    lexer lexer;
    parser parser;
    if (parser.yycreate(&lexer)) {
        if (lexer.yycreate(&parser)) {
            //lexer.yyin = new ifstream("input.txt");
            //lexer.yyin = new ifstream(argv[1]);
            //lexer.yyout = new ofstream(argv[2]);
            n = parser.yyparse();
            executeTree(parser_tree);
            //parse_tree.get_label();
            //parser_tree.gen_code(*lexer.yyout);
        }
    }

    getchar();
    return n;
}

語法樹提供的簡單函式以及遍歷的程式碼

tree.h:

#ifndef __TREE__H__
#define __TREE__H__

#include <iostream>
#include <malloc.h>
using namespace std;

#define  MAX_CHILDREN 4


enum                    // 結點型別——kind
{
    STMT_NODE = 0,
    EXPR_NODE,
    DECL_NODE
};

enum                    // 語句結點子型別——kindkind
{
    IF_STMT = 0,
    WHILE_STMT,
    INPUT_STMT,
    PRINT_STMT,
    COMP_STMT,
    EXPR_STMT,  //只是一個包裝而已
};

enum                    // 表示式結點子型別——kindkind
{
    TYPE_EXPR = 0,
    OP_EXPR,
    NOT_EXPR,
    UMINUS_EXPR,
    ARRAY_EXPR,
    CONST_EXPR,
    ID_EXPR,
};

enum                    // 宣告結點子型別——kindkind
{
    VAR_DECL = 0,
    ARRAY_DECL
};
/*
enum                    // 運算——op
{
    //加減乘除,算術運算子
    PLUS = 0,
    MINUS,
    TIMES,   
    OVER,
    //與或,邏輯運算子
    AND,
    OR,
    //比較運算子
    EQ,
    LT,
    GT,
    GE,
    LE,
    NE,
    ASSIGN,
};
*/
enum
{
    Integer = 0,
};

union NodeAttr {
    int op;             // 表示式結點,子型別是運算型別時,用op儲存具體運算
    double vali;                // 表示式結點,常量表示式時,用vali儲存整型常量值
    char valc;          // 字元值
    int symtbl_seq;     //符號表位置

    NodeAttr(void) { op = 0; }      // 幾種建構函式
    NodeAttr(int i) { op = i; }
    NodeAttr(double i)  { vali = i; }
    NodeAttr(char c) { valc = c; }
};


struct Node
{
    struct Node *children[MAX_CHILDREN];    // 孩子結點
    struct Node * sibling;
    int kind;                   // 結點型別
    int kind_kind;              // 子型別
    NodeAttr attr;              // 結點屬性
    int addr;                   // 分配的記憶體空間(陣列下標)
};



class tree  // 語法樹類
{
private:
    Node *root;         // 根結點

private:
    void recursive_get_addr(Node *t);   // 為臨時變數(如表示式)分配儲存空間
    void recursive_execute(Node *t);    // 遍歷樹,執行源程式

public:
    void setRoot(Node* p){root = p;}
    Node *NewRoot(int kind, int kind_kind, NodeAttr attr, int type,
        Node *child1 = NULL, Node *child2 = NULL, Node *child3 = NULL, Node *child4 = NULL);                    // 建立一個結點,設定其屬性,連線孩子結點
    void get_addr(void);        // 分配空間和執行程式碼的介面
    void execute(void);
};

extern tree parser_tree;
Node* createOpExpr(tree& expr, Node* p, Node* q, int type);
Node* createId(tree& expr);
Node* createId(tree& expr, int seq); //建立變數節點,其屬性儲存其符號表的序號
Node* createConst(tree& expr, double value);
Node* createSTMT(tree& expr, int type, Node* p1, Node* p2=NULL, Node* p3 = NULL, Node* p4=NULL);
Node* createIfStmt(tree& expr,Node* p1, Node* p2, Node* p3 = NULL );
Node* createWhileStmt(tree& expr, Node* p1, Node* p2);
Node* createInputStmt(tree& expr, Node* p);
Node* createOutStmt(tree& expr, Node* p);
Node* createExprStmt(tree& expr, Node* p);
Node* createAssignStmt(tree& expr, Node* variable, int value);
Node* createAssignStmt(tree& expr, Node* variable, Node* q);
Node* createCompStmt(tree& expr, Node* p1, Node* p2=NULL, Node* p3 = NULL, Node* p4=NULL);
void executeTree(tree& expr, Node* root);
void executeTree(tree& expr);

#endif//__TREE__H__

基本利用了先前的框架,加入很多功能輔助函式,例如createXXX的語句,這樣yacc中進行規則執行的動作的時候可以很方便的呼叫。例如剛才的createIfStmt(parser_tree, 3,

3,
5, $7)就是對應的這個裡面的createIfStmt。

tree.cpp就是相應的功能實現。絕大部分時間我都在複製,貼上,修改,除錯中,沒太大的技術函式。程式碼量雖然不小,但是語句重複率很高,唯一需要特別注意的就是recursive_execute函式。當發現賦值語句,輸入語句的時候,需要修改符號表中的value值,當碰到ID標示符的時候,需要從符號表把相應的值取出來。

tree.cpp:

#include "tree.h"
#include "sym_table.h"
#include <math.h>

tree parser_tree;

double my_mem[10000];           // “記憶體”
int offset;


Node * tree::NewRoot(int kind, int kind_kind, NodeAttr attr, int type,
    Node *child1, Node *child2, Node *child3 , Node *child4)
{
    Node* node = new Node();
    node->sibling = NULL;
    node->kind = kind;
    node->kind_kind = kind_kind;
    node->attr = attr;
    node->children[0] = child1;
    node->children[1] = child2;
    node->children[2] = child3;
    node->children[3] = child4;

    return node;
}

void tree::get_addr(void)
{
    cout << "allocate memory..." << endl;
    offset = 0;
    recursive_get_addr(root);       // 介面函式直接呼叫實際分配空間的遞迴函式
}



void tree::recursive_get_addr(Node *t)
{
    if (t) {        // 空指標什麼也不做
        if (t->kind == EXPR_NODE) { // 為表示式結點分配儲存空間
            t->addr = offset++;
            //cout << t->addr << " | "<<t->kind<<" " <<t->kind_kind<<endl;
        }
        for (int i = 0; i < MAX_CHILDREN; i++)  // 遞迴處理所有子樹——先序遍歷
            recursive_get_addr(t->children[i]);
        //TODO: 專門的sibing到children的轉換函式。
        recursive_get_addr(t->sibling);
    }
}

void tree::execute(void)
{
    cout << "execute..." << endl;
    recursive_execute(root);                // 介面函式呼叫遞迴函式
    //cout << my_mem[root->addr] << endl;   // 從記憶體取出執行結果,輸出
}
/*
功能逐步新增摘要:
if條件判斷功能:
根據if的condition來決定是否執行statement程式碼,因此不能首先對其所有的孩子進行後續遍歷,需要根據第一個孩子的執行結果來決定是否執行第二個孩子。
while迴圈功能:
此功能框架程式碼與if功能相似,只是多了一個while迴圈而已
變數賦值:
讀取使用者輸入,並賦值到變數,支援input(a),它將使用者的輸入賦值到變數a,a為其第一個孩子。
賦值語句:
支援a=2,包括兩個孩子,左邊為變數,右邊為值,當前值為左邊的值。
輸入功能:
能夠接收使用者輸入,並賦值到變數中,前提需要增加賦值功能
輸出功能:
構建輸出語句,它只有一個孩子,即要輸出的變數。
*/
void tree::recursive_execute(Node *t)
{
    if (t) {
//      printf("deal with recursive_execute:%d %d\n", t->kind, t->kind_kind);
        if(t->kind == STMT_NODE){
            if(t->kind_kind == IF_STMT){
                //if條件判斷結果,第二個孩子儲存if成功的程式碼,第三個孩子儲存else成功的程式碼
                recursive_execute(t->children[0]);
                if (my_mem[t->children[0]->addr] )
                    recursive_execute(t->children[1]);
                else
                    recursive_execute(t->children[2]);
            }else if(t->kind_kind == WHILE_STMT){
                //第一個孩子儲存條件判斷結果,第二個孩子儲存while成功的程式碼
                recursive_execute(t->children[0]);
                while (my_mem[t->children[0]->addr])
                {
                    recursive_execute(t->children[1]);
                    recursive_execute(t->children[0]);
                }
            }else if(t->kind_kind == INPUT_STMT){//input statement has one expr child
                cout<<"please input data:";
                cin>>my_mem[t->children[0]->addr];
                int seq = t->children[0]->attr.symtbl_seq;
                sym_table.setValue(seq, my_mem[t->children[0]->addr]);//修改符號表
            }else if(t->kind_kind == PRINT_STMT){//print statement has one expr child to print.
                recursive_execute(t->children[0]);
                int seq = t->children[0]->attr.symtbl_seq;
                const string& name = sym_table.getName(seq);
                cout<<name.c_str()<<" = "<<my_mem[t->children[0]->addr]<<endl;
            }else if(t->kind_kind == COMP_STMT){//組合語句,逐個語句執行即可。
                Node* p = t->children[0];
                while (p)
                {
                    recursive_execute(p);
                    p = p->sibling;
                }
                //for (int i = 0; i < MAX_CHILDREN; ++i)
                //  recursive_execute(t->children[i]);                     
            }else if(t->kind_kind == EXPR_STMT){
                recursive_execute(t->children[0]);
            }
        }
        if (t->kind == EXPR_NODE){          // 表示式結點
            recursive_execute(t->children[0]);
            recursive_execute(t->children[1]);

            if (t->kind_kind == OP_EXPR) {      // 運算型別表示式
                if (t->attr.op == PLUS)         // 加法表示式
                    // 從記憶體(my_mem)中取出兩個孩子的值,進行加法,結果寫回記憶體
                    my_mem[t->addr] = my_mem[t->children[0]->addr] + my_mem[t->children[1]->addr]; 
                else if (t->attr.op == MINUS)   // 減法的處理類似加法
                    my_mem[t->addr] = my_mem[t->children[0]->addr] - my_mem[t->children[1]->addr];
                else if (t->attr.op == TIMES)
                    my_mem[t->addr] = my_mem[t->children[0]->addr] * my_mem[t->children[1]->addr];
                else if (t->attr.op == OVER){
                    if(my_mem[t->children[1]->addr] == 0){
                        cout<<"error: divide by zero"<<endl;
                        my_mem[t->addr] = 0;
                    }else{
                        my_mem[t->addr] = my_mem[t->children[0]->addr] / my_mem[t->children[1]->addr];
                    }
                }
                else if (t->attr.op == AND)
                    my_mem[t->addr] = my_mem[t->children[0]->addr] && my_mem[t->children[1]->addr];
                else if (t->attr.op == OR)
                    my_mem[t->addr] = my_mem[t->children[0]->addr] || my_mem[t->children[1]->addr];
                else if (t->attr.op == EQ)
                    my_mem[t->addr] = (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
                else if (t->attr.op == GT)
                    my_mem[t->addr] = my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr];
                else if (t->attr.op == LT)
                    my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]);
                else if (t->attr.op == NE)
                    my_mem[t->addr] = !(my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
                else if (t->attr.op == GE)
                    my_mem[t->addr] = (my_mem[t->children[0]->addr] > my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
                else if (t->attr.op == LE)
                    my_mem[t->addr] = (my_mem[t->children[0]->addr] < my_mem[t->children[1]->addr]) || (my_mem[t->children[0]->addr] == my_mem[t->children[1]->addr]);
                else if(t->attr.op == ASSIGN){
                    int seq = t->children[0]->attr.symtbl_seq;
                    sym_table.setValue(seq, my_mem[t->children[1]->addr]);//修改符號表

                    my_mem[t->addr] = my_mem[t->children[0]->addr] = my_mem[t->children[1]->addr];//得到表示式的值
                }else if(t->attr.op == SQRT){
                    my_mem[t->addr] = sqrt(my_mem[t->children[0]->addr]);
                }else if(t->attr.op == FABS){
                    my_mem[t->addr] = fabs(my_mem[t->children[0]->addr]);
                }else if(t->attr.op == UMINUS_EXPR){
                    my_mem[t->addr] = -my_mem[t->children[0]->addr];
                }
            }
            else if (t->kind_kind == CONST_EXPR)    // 常量表示式,將值(在vali中)儲存至分配的記憶體中
                my_mem[t->addr] = t->attr.vali;
            else if(t->kind_kind == ID_EXPR){//變數的值從符號表中取得,不儲存在my_mem記憶體中
                my_mem[t->addr] = sym_table.getValue(t->attr.symtbl_seq);
            }else if(t->kind_kind == NOT_EXPR){
                my_mem[t->addr] = ! my_mem[t->children[0]->addr];
            }

        }//EXPR_NODE
    }
}


Node* createOpExpr(tree& expr, Node* p, Node* q, int type)
{
    Node* p1;
    p1 = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(type), Integer, p,q);
    return p1;
}
Node* createId(tree& expr)
{
    Node* p;
    p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    return p;
}
//the Node object is only created once.
Node* createId(tree& expr, int seq) //建立變數節點,其屬性儲存其符號表的序號
{
    /*
    Node* p;
    p = sym_table.getNode(seq);
    if(p == NULL){
        p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(seq), Integer);
        sym_table.setNode(seq, p);
    }
    */
    Node* p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(seq), Integer);
    return p;

}
Node* createConst(tree& expr, double value)
{
    Node* q2;
    q2 = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(value), Integer);
    return q2;
}
Node* createSTMT(tree& expr, int type, Node* p1, Node* p2, Node* p3 , Node* p4)
{
    Node* r;
    r = expr.NewRoot(STMT_NODE, type, NodeAttr(), Integer, p1,p2,p3,p4);

    return r;
}
Node* createIfStmt(tree& expr,Node* p1, Node* p2, Node* p3  )
{
    return createSTMT(expr, IF_STMT,  p1, p2, p3);
}
Node* createWhileStmt(tree& expr, Node* p1, Node* p2)
{
    return createSTMT(expr, WHILE_STMT, p1, p2);
}
Node* createInputStmt(tree& expr, Node* p)
{
    return createSTMT(expr, INPUT_STMT, p);
}
Node* createOutStmt(tree& expr, Node* p)
{
    return createSTMT(expr, PRINT_STMT, p);
}
Node* createExprStmt(tree& expr, Node* p)
{
    return expr.NewRoot(STMT_NODE, EXPR_STMT, NodeAttr(0), Integer, p);//xxxx; xxxx為表示式,組合成語句
}
Node* createAssignStmt(tree& expr, Node* variable, int value)
{
    Node* p = variable;
    Node *q, *r, *s;
    q = createConst(expr, value);
    r = createOpExpr(expr, p,q, ASSIGN);

    return createExprStmt(expr, r);
}
Node* createAssignStmt(tree& expr, Node* variable, Node* q)
{
    Node* p = variable;
    Node *r, *s;
    r = createOpExpr(expr, p,q, ASSIGN);

    return createExprStmt(expr, r);
}
Node* createCompStmt(tree& expr, Node* p1, Node* p2, Node* p3, Node* p4)
{
    return createSTMT(expr, COMP_STMT, p1,p2,p3,p4);
}
void executeTree(tree& expr, Node* root)
{
    expr.setRoot(root);
    expr.get_addr();    // 為(子)表示式(們)分配儲存空間
    expr.execute(); // 執行程式碼
    cout<<endl;
}
void executeTree(tree& expr)
{
    expr.get_addr();    // 為(子)表示式(們)分配儲存空間
    expr.execute(); // 執行程式碼
    cout<<endl;
}

/*
if:  st_if -> (condition, action)
while: st_while->(condition, action)
*/
void basicTest()
{
    tree expr;
    Node *p, *q, *r;

    // 建立結點a
    p = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(9), Integer);
    // 建立結點5
    q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(5), Integer);
    // 建立減法結點,孩子結點為95
    r = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(MINUS), Integer, p, q);
    // q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(2), Integer);
    // p = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(PLUS), Integer, r, q);
    expr.setRoot(r);
    expr.get_addr();    // 為(子)表示式(們)分配儲存空間
    expr.execute(); // 執行程式碼
}
void assignTest()
{
    /*
    a = 1
    output(a)

    兩個語句
    */

    tree expr;
    Node *p, *q, *r, *s, *o, *u, *t;

    //構建賦值語句
    // 建立結點a
    p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    // 建立結點1
    q = expr.NewRoot(EXPR_NODE, CONST_EXPR, NodeAttr(5), Integer);
    //賦值
    r = expr.NewRoot(EXPR_NODE, OP_EXPR, NodeAttr(ASSIGN), Integer, p, q);
    s = expr.NewRoot(STMT_NODE, EXPR_STMT, NodeAttr(0), Integer, r);//賦值語句:孩子賦值表示式


    //構建輸出語句
    t = expr.NewRoot(STMT_NODE, PRINT_STMT, NodeAttr(), Integer, p);

    //構建組合語句
    o = expr.NewRoot(STMT_NODE, COMP_STMT, NodeAttr(), Integer, s, t);

    expr.setRoot(o);
    expr.get_addr();    // 為(子)表示式(們)分配儲存空間
    expr.execute(); // 執行程式碼
    cout<<endl;
}

void inputOutTest()
{
    /*
    input(a)
    output(a)
    */
    tree expr;
    Node *p, *q, *r, *s, *o, *u, *t;

    //輸入語句
    // 建立結點a
    p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    s = createInputStmt(expr, p);
    q = createOutStmt(expr, p);
    o = createCompStmt(expr, s, q);

    executeTree(expr, o);
}
void ifTest()
{
    /*
    a = 1
    if(a>10){
      a = 11
    }else{
      a = 7
    }
    print(a)
    */
    tree expr;
    Node *p, *q, *r, *s, *o, *u, *t;

    //輸入語句
    // 建立結點a
    p = expr.NewRoot(EXPR_NODE, ID_EXPR, NodeAttr(0), Integer);
    s = createAssignStmt(expr, p, 1);//a=1


    //if語句
    q = createOpExpr(expr, p, createConst(expr, 10), GT);//a>10
    r = createIfStmt(expr, q, createAssignStmt(expr, p, 11), createAssignStmt(expr, p, 7));//if(a>10) a=11 else a=7

    //構建輸出語句
    t = createOutStmt(expr, p);//print(a)

    //構建組合語句
    o = createCompStmt(expr,s ,r, t );

    executeTree(expr , o);
}
void whileTest()
{
    /*
    a =1
    sum = 0
    while(a <= 10){
       sum = sum + a
       a = a+1;
    }
    print(sum)
    */

    tree expr;
    Node *p, *q, *r, *s, *o, *u, *t,*p1,*q1;
    p = createId(expr);//a
    q = createId(expr);//sum
    p1 = createAssignStmt(expr,p, 1);//a=1
    q1 = createAssignStmt(expr, q, 0);//sum=0

    Node *a, *b, *c, *d, *e, *f, *g;
    a = createOpExpr(expr, p, createConst(expr, 10), LE);//a<=10
    b = createOpExpr(expr, p, q, PLUS);//sum+a
    c = createAssignStmt(expr, q, b);//sum = sum+a

    d = createOpExpr(expr, p, createConst(expr, 1), PLUS);
    e = createAssignStmt(expr, p, d);   //a=a+1
    f = createCompStmt(expr, c, e); //{sum = sum+a, a= a+1}
    r = createWhileStmt(expr, a,  f);//while

    t = createOutStmt(expr, q);//print sum

    o = createCompStmt(expr, p1, q1, r, t);

    executeTree(expr, o);
}

void whileInputTest()
{
    /*
    a = 1
    sum = 0
    input(x)
    while(a <= x){
       sum = sum + a
       a = a+1;
    }
    print(sum)
    */

    tree expr;
    Node *p, *q, *r, *s, *o, *u, *t,*p1,*q1;
    p = createId(expr);//a
    q = createId(expr);//sum
    p1 = createAssignStmt(expr, p, 1);//a=1
    q1 = createAssignStmt(expr, q, 0);//sum=0

    Node *a, *b, *c, *d, *e, *f, *g;
    g = createId(expr);//x
    f = createInputStmt(expr,g);
    u = createCompStmt(expr, p1, q1, f);//first 3 sentences. because Comp not support more than 4 children.

    a = createOpExpr(expr, p, g, LE);//a<=x, x from input
    b = createOpExpr(expr, p, q, PLUS);//sum+a
    c = createAssignStmt(expr, q, b);//sum = sum+a

    d = createOpExpr(expr, p, createConst(expr, 1), PLUS);
    e = createAssignStmt(expr, p, d);   //a=a+1
    f = createCompStmt(expr, c, e); //{sum = sum+a, a= a+1}
    r = createWhileStmt(expr, a,  f);//while

    t = createOutStmt(expr, q);//print sum

    o = createCompStmt(expr, u, r, t);

    executeTree(expr, o);
}

int xxx(int argc, char *argv[])
{
    //assignTest();
    //inputOutTest();
    //ifTest();

    //whileTest();
    whileInputTest();
    return 0;
}

main函式

main其實位於yacc檔案中,我們不需要額外定義main函式,直接在yacc.y中寫好,當編譯yacc.y的時候,就會將相應的main函式放置到yacc.cpp中。

它的實現很簡單,建立lex和yacc,並建立連線。呼叫yyparse進行語法解析構建語法樹,然後執行語法樹:

int main(int argc, char *argv[])
{
    printf("This is a simple compiler.\n");
    printf("You can copy the main functions in input.txt and paste it here\n");
    printf("There are two main functions and they will work both.\n");

    int n = 1;
    lexer lexer;
    parser parser;
    if (parser.yycreate(&lexer)) {
        if (lexer.yycreate(&parser)) {
            //lexer.yyin = new ifstream("input.txt");
            //lexer.yyin = new ifstream(argv[1]);
            //lexer.yyout = new ofstream(argv[2]);
            n = parser.yyparse();
            executeTree(parser_tree);
            //parse_tree.get_label();
            //parser_tree.gen_code(*lexer.yyout);
        }
    }

    getchar();
    return n;
}

輸入測試用例

我的測試兩個例子如下所示:

1.簡單的if,while測試

main()
{
    a = 1;
    if(a>10){
      a = 11;
    }else{
      a = 7;
    }
    output(a);

    sum = 0;
    a = 1;
    while(a<100){
        sum = sum + a;
        a = a + 1;
    }
    output(sum);

    a = 1;
    output(a);
    while(a != 100){
        input(a);
        if(a<10){
            output(a);
        }else{
            output(sum);
        }
    }
}

2.求解方程的根

main() 
{      
    //求解X1,在曲線對稱軸處選擇初始點     
    //x^2+3x+2 = 0
    a = 1;
    b = 3;
    c = 2;
    accuracy = 0.00001;
    index=(-1.0*b)/(2*a);
    if(b!=0)//b不等於0時進行迭代     
    {          
        temp=index;          
        index=-1.0*(a*temp*temp+c)/b;    
        while((fabs(index-temp))>accuracy)         
        {              
            temp=index;              
            index=-1.0*(a*temp*temp+c)/b;         
        }          
        x1=index;          
        x2=(-1.0*b)/a-x1; 
    }      
    else//b=0時ax^2+c=0直接求解     
    {          
        x1=sqrt(-1.0*c/a);         
        x2=-x1;     
    } 

    output(x1);
    output(x2);
}

如何執行

首先將上述的tree.h, tree.cpp, sym_table.h, sym_table.cpp包含到vs工程中,還需要加入lex.l和yacc.y編譯後的檔案,lex.h, lex.cpp, yacc.h, yacc.cpp。 這些程式碼檔案包含完畢後,還需要配置vs環境的包含目錄pargen的目錄,我的目錄是C:\Program Files (x86)\Parser Generator 2\Cpp\Include, 還需要包含它的lib庫目錄,庫目錄為./,輸入中的附加依賴項的庫檔案為ylmtri.lib。 我使用的動態連結庫,需要將庫檔案ylmtri.lib, ylmtri.dll放入到工程目錄下。
如圖:
這裡寫圖片描述

程式碼

萬事都是這樣,說起來簡單,做起來難。 好多一個毫不起眼的bug就可能耗費一整天的功夫,尤其是對於這種依賴外部的pargen進行編譯執行的庫。只配置好環境可能就需要大半天,因此我將我弄好的vs2013工程程式碼上傳到github上了,你如果想測試程式碼可以直接拿來用。
github地址:https://github.com/lpstudy/compile

相關文章