lex和yacc的使用很簡單,但環境配置卻是各種問題,本章說明lex和yacc在windows下的環境配置。
軟件需求:
系統 win7-64位(win7-32, win8, win10全部通過)
c++編譯器: vs2010(2008,2013,2015也全部通過)
lex和yacc編譯器: ParGen.exe
基本流程:
安裝Pargen.exe,采用的默認目錄安裝在C:\Program Files (x86)\Parser Generator 2\
安裝vs2010,這個各種教程,不再贅述
啟動Pargen程序,並選擇Project->ParserWizard…
選中ParserWizard,開始工程的創建,此處我創建功能,命名為Test,目錄位置可自己選擇,目標語言為C++,編譯器為vc++(32-bit)
下一步,選擇是創建lex,還是yacc,還是兩者兼有。此處我選擇是lex和yacc都有,准備實現一個不支持變量的計算器,使用lex識別token,使用yacc識別語法。
下一步,設置yacc的文件名字以及使用的解析器,此處我使用的默認選項,不進行修改,文件名默認為myparser.y
下一步,設置lex的文件名以及使用的分析器的名字,此處我使用的默認選項,文件名默認為mylexer.l
點擊完成按鈕,創建工程完畢,同時有兩個文件mylexer.l 和myparser.y
工程的管理,可使用window->project菜單,查看工程下的所有文件
當你點擊文件編輯窗口的放大按鈕,會將其他的文件編輯覆蓋,此時可以使用window->project菜單查看,也可以使用window->Tile vertically查看全部文件的平鋪
編輯mylexer.l文件,粘貼入以下內容:
%{
//this code will be added into the header of generated .cpp file #include <iostream> #include "myparser.h" using namespace std; //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 mylexer delim [ \t] ws {delim}+ letter [a-zA-Z] digit [0-9] id {letter}({letter}|{digit})* /* can support 12.34 */ number {digit}+(\.{digit}+)? %% %{ //this code will be added into yyaction function YYSTYPE YYFAR& yylval = *(YYSTYPE YYFAR*)yyparserptr->yylvalptr; //double yylval; %} {ws} {/* do nothing */} "int" {print_token(INT, yytext); return INT;} "double" {print_token(DOUBLE, yytext);} "char" {print_token(CHAR, yytext);} "+" {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;} "\n" {return EOL;} {id} { return ID;} {number} { yylval = atof(yytext);return NUMBER;} "//".* {return COMMENT;} "." {printf("Mystery character %s\n", yytext); } %% static void print_token(int token, char* lex) { #ifdef LEX_DEUB cout<<"token:" << token<<" "<<"lex:"<<lex<<endl; #endif }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
編譯myparser.y文件,粘貼入以下內容
%{
#include "mylexer.h" %} %name myparser // 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 double #endif } %token NUMBER ID %token PLUS MINUS TIMES OVER %token LP RP EOL COMMENT %TOKEN INT DOUBLE CHAR %left PLUS MINUS %left TIMES OVER %right UMINUS %% 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 //should be complemented ; %% int main(int argc, char *argv[]) { printf("a cacluator which support +,-,*,/ and (): \n"); printf(" e.g. 12.2+3*(2+5)\n"); int n = 1; mylexer lexer; myparser parser; if (parser.yycreate(&lexer)) { if (lexer.yycreate(&parser)) { //lexer.yyin = new ifstream(argv[1]); //lexer.yyout = new ofstream(argv[2]); n = parser.yyparse(); //parse_tree.get_label(); //parse_tree.gen_code(*lexer.yyout); } } getchar(); return n; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
點擊Pargen右上角的編譯build按鈕,會生成相應的.h和.cpp代碼
新建vs2010工程,並將生成的.h和.cpp代碼加入到工程中。簡單起見,在comple\Test目錄下創建vs工程vsTest
選擇控制台工程, 工程的目錄,以及工程名稱vsTest,點擊確定按鈕后
繼續下一步配置
此處附件選項選擇空項目,然后點擊完成按鈕,即完成vsTest工程創建
工程右鍵添加現有項,即添加已經生成.h和.cpp文件
在vs界面,點擊編譯按鈕,查看當前的編譯情況,會顯示編譯錯誤,找不到yy的頭文件,這是因為並沒有將Pargen安裝后的頭文件加入到工程的包含目錄中
下面將Pargen安裝后的頭文件加入到工程include配置
在vs界面,點擊編譯按鈕,查看編譯情況。 當前頭文件可以正常找到,會出現大量的鏈接錯誤-link error。這是因為對應的lib文件還沒有加載進來。
下面加入庫文件,加入庫所在目錄
加入要使用的庫的名字:ylmtri.lib, 注意使用分號隔開
再次點擊編譯按鈕,會發現可以編譯,但是運行的時候,會出現ylmtri.dll的錯誤。這是因為我們使用動態dll庫,需要將對應的dll文件從Pargen目錄復制到工程目錄的exe文件同級目錄下
下面開始將ylmtri.dll從Pargen目錄復制到vsTest.exe同級目錄
再次點擊編譯運行按鈕,可以正常運行,效果如下
終於完結了。