小白說編譯原理-9-最簡單minus-c語言編譯器
簡介
繼上節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);
同理,其他語法規則的執行動作也可以進行相應的修改,這樣當一個表示式語法分析完畢後,對應的表示式語法樹也就構建完成了。
總體概覽
- lex和yacc進行相應的詞法,語法分析,並構建對應的語法樹
- tree.h和tree.cpp用來支撐語法樹的構建過程,提供相應的建立函式,被yacc使用
- symtable.h和symtable.cpp用來支撐符號表的構建過程,提供符號表的建立,訪問,修改等操作,用於支援變數以及可能的函式擴充套件。
- 只支援最基本的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,
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);
// 建立減法結點,孩子結點為9和5
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
相關文章
- 小白說編譯原理-8-簡單minus-c語言編譯樹(支撐類)編譯原理C語言
- 源語言、目標語言、翻譯器、編譯器、直譯器編譯
- 小白說編譯原理-7-算術表示式編譯樹(支撐類)編譯原理
- 說說 方舟編譯器編譯
- C語言編譯過程簡介C語言編譯
- 現代編譯原理C語言描述pdf編譯原理C語言
- Go編譯器簡介【譯】Go編譯
- C語言編譯器手機版C語言編譯
- javascript編寫一個簡單的編譯器JavaScript編譯
- C語言編譯工具C語言編譯
- java編譯、編碼、語言設定Java編譯
- Android-NDK-11-C語言編譯原理AndroidC語言編譯原理
- 程式語言和編譯器書單(1)編譯
- Go 語言編譯期斷言Go編譯
- 【編譯原理】手工打造語法分析器編譯原理語法分析
- 編譯原理編譯原理
- 【編譯原理】語法分析(三)編譯原理語法分析
- C語言 - 條件編譯C語言編譯
- javascript編寫一個簡單的編譯器(理解抽象語法樹AST)JavaScript編譯抽象語法樹AST
- 淺談,C語言編譯原理的個人見解C語言編譯原理
- 寫給小白的開源編譯器編譯
- gcc 編譯器與 clang 編譯器GC編譯
- Go語言內幕(2):深入 Go 編譯器Go編譯
- gcc 從語言編譯全過程 預處理---->編譯---->彙編----->連結GC編譯
- C語言編譯和連結過程簡介C語言編譯
- 編譯型語言與解釋型語言編譯
- Flutter 編譯原理Flutter編譯原理
- 編譯原理概述編譯原理
- go語言編譯過程概述Go編譯
- Go語言交叉編譯工具goxGo編譯
- c語言多檔案編譯C語言編譯
- C語言編譯全過程C語言編譯
- 第一個C語言編譯器是怎樣編寫的?C語言編譯
- 第一個 C 語言編譯器是怎樣編寫的?編譯
- 【譯】說服Kotlin編譯器程式碼安全Kotlin編譯
- Java 實現《編譯原理》簡單詞法分析功能Java編譯原理詞法分析
- 深入淺出編譯原理-1-C語言的文法編譯原理C語言
- [譯]iOS編譯器iOS編譯