無意中在github上發現乙個很有意思的專案,比如這裡它利用flex實現了字串的識別,利用bison實現了ast語法樹的構建,最後直接利用ast進行計算和識別。ast節點遍歷的時候,作者應該是根左右遍歷的,其實左右根遍歷或許更好一些。注意,語法解析的時候肯定是樹的結構,但是不一定是二叉樹。
比如,它的lex檔案是這麼寫的,
/***********************************/
/* file: cmm.l version 2.0 */
/* flex version for cmm */
/* cmm interpreter construction */
/***********************************/
%option noyywrap
%digit [0-9]
int_num +
real_num +"."*
letter [a-za-z]
identifier +(||_)*(+|+)|+
whitespace [ \t]*
%%"if"
"else"
"while"
"int"
"real"
"bool"
"read"
"write"
"main"
"+"
"-"
"*"
"/"
"<"
">"
"<="
">="
"=="
"<>"
"&&"
"||"
"="
"("
")"
";"
""}"
"["
"]"
","
"//"
lineno++;
}"/*"
} while (flag);
}\n
. %%
/* 用於語法分析時初始化詞法分析介面 */
void inilexer(void)
}/* 詞法分析器專用
tokentype gettoken(void)
currenttoken = yylex();
strncpy(tokenstring,yytext,maxtokenlen);
if (tracescan)
return currenttoken;
}*/
它的yacc檔案是這麼寫的,
/***********************************/
/* file: cmm.y */
/* bison grammar file about cmm */
/* cmm interpreter construction */
/***********************************/
/*預計有1個移進/歸約衝突*/
%expect 1
%%union
%token int_value
%token real_value
%token id
%token int real bool
/* 優先順序宣告 */
%right assign
%left plus sub
%left mul div
%nonassoc rel_op
%nonassoc uminus
/* 宣告文法中用到的tokens */
%token if else while read write main
%token lparen rparen semi lbrace rbrace lbracket rbracket comma
%token assign
%token newline error
%type stmt_list stmt
%type if_stmt decl_stmt compound_stmt while_stmt assign_stmt read_stmt write_stmt
%type exp factor bin_exp
%type type_spec
%start program
%% /* cmm文法 */
program : stmt_list
;stmt_list :
| stmt_list stmt
t->sibling = $2;
$$ = $1;
}else $$ = $2;};
stmt : if_stmt
| decl_stmt semi
| compound_stmt
| while_stmt
| assign_stmt semi
| read_stmt semi
| write_stmt semi
| error
;compound_stmt
: lbrace stmt_list rbrace
;decl_stmt : type_spec id
| type_spec id lbracket int_value rbracket
else if($$->type == real)
$$->lineno = lineno;};
type_spec : int
| real
| bool
;if_stmt : if lparen bin_exp rparen stmt
| if lparen bin_exp rparen stmt else stmt
;while_stmt : while lparen bin_exp rparen stmt
;assign_stmt : id assign exp
| id lbracket exp rbracket assign exp
;read_stmt : read lparen id rparen
| read lparen id lbracket exp rbracket rparen
;write_stmt : write lparen exp rparen
;exp : factor
| bin_exp
;factor : int_value
| real_value
| lparen exp rparen
| id
| id lbracket exp rbracket
| error
;bin_exp : /* 關係運算子 */
exp rel_op exp
/* 算數運算子 */
| exp plus exp
| exp sub exp
| exp mul exp
| exp div exp
| sub exp %prec uminus
;%%int yyerror(char * message)
/* 與主函式互動的語法分析函式 */
treenode * parse(void)
編譯也非常簡單,
#the makefile for interpreter based on flex and bison
cc = gcc
flag = -w
interpreter: main.o util.o cmm.tab.o cmm.lex.o symtab.o analyze.o
$(cc) $(flag) -o ../bin\&test/interpreter main.o util.o cmm.tab.o cmm.lex.o symtab.o analyze.o
main.o: main.c parse.h analyze.h
$(cc) $(flag) -c main.c
cmm.tab.o:cmm.tab.c parse.h
$(cc) $(flag) -c cmm.tab.c
cmm.tab.c:cmm.y
bison -d cmm.y
cmm.lex.o:cmm.lex.c cmm.tab.h
$(cc) $(flag) -c cmm.lex.c
cmm.lex.c:cmm.l
flex -o cmm.lex.c cmm.l
symtab.o:symtab.c symtab.h globals.h
$(cc) $(flag) -c symtab.c
analyze.o:analyze.c globals.h symtab.h analyze.h
$(cc) $(flag) -c analyze.c
util.o:util.c
$(cc) $(flag) -c util.c
clean:
-rm *.o
最後測試的時候,就是直接利用./interpreter [file_name]就可以了。 隨想錄(lcc編譯器)
lcc編譯器是一款開源編譯器,和我們之前談過的ucc差不多。一開始的時候,這款編譯器是用來進行教學使用的,但是後來越來越多的人開始了解它 使用它,並且將這款編譯器用到實際專案當中。當前一般的用法就是利用lcc將c檔案轉變成asm彙編檔案,這種使用方法是最常見的。如果我們自己開發的程式是微控制器軟體 ...
隨想錄(編譯器是怎麼工作的)
其實,現在的編譯器早已經突破了原來的概念。比如說,編譯器最終的 不一定在實際機器上執行,可能是虛擬機器 編譯器編譯語言時不一定需要生成可執行檔案,能解釋就行 編譯器最好並行編譯 編譯器不一定很大,可能十幾個檔案就可以,比如說lua等等。不過,我們今天說的編譯器還是比較傳統的c編譯器,有興趣的同學可以...
隨想錄(編譯器是怎麼工作的)
其實,現在的編譯器早已經突破了原來的概念。比如說,編譯器最終的 不一定在實際機器上執行,可能是虛擬機器 編譯器編譯語言時不一定需要生成可執行檔案,能解釋就行 編譯器最好並行編譯 編譯器不一定很大,可能十幾個檔案就可以,比如說lua等等。不過,我們今天說的編譯器還是比較傳統的c編譯器,有興趣的同學可以...