隨想錄(c編譯器的實現)

2021-08-10 01:54:44 字數 4633 閱讀 1628

無意中在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編譯器,有興趣的同學可以...