Reference
- cmake bison command: https://cmake.org/cmake/help/v3.6/module/FindBISON.html
- cmake flex command: https://cmake.org/cmake/help/v3.6/module/FindFLEX.html
内容
CMake处理lex和yacc文件。即自动将lex&yacc文件转换成为c/c++文件,然后生成库文件或可执行文件。
lex
这里只有一个lex文件(test.ll),然后生成可执行文件ttest。——test是cmake保留字,所以这里用ttest。
目录结构
两个子目录:
- test\src: test.ll, CMakeLists.txt
- test\bin: 工作目录,一开始为空,生成的文件都放在这里。
文件
test.ll
%{
#include <string>
#include <iostream>
%}
%%
[\t ]+ /* white space */
[a-zA-Z]+ {
std::string word(yytext);
std::cout<<"word: "<<word<<std::endl;
}
[0-9]+ {
std::string number(yytext);
std::cout<<"number: "<<number<<std::endl;
}
.|\n {ECHO; /* normal default anyway */ }
%%
int main()
{
std::cout<<"Lex and C++"<<std::endl;
yylex();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(ttest)
# bison and flex
find_package(BISON)
find_package(FLEX)
FLEX_TARGET(ttest_lexer test.ll ${PROJECT_BINARY_DIR}/test.cpp)
# main
set (MAIN_SRC ${PROJECT_BINARY_DIR}/test.cpp)
add_executable(ttest ${MAIN_SRC})
# libraries
find_library(LEX_LIB l)
target_link_libraries(ttest ${LEX_LIB})
或者
cmake_minimum_required(VERSION 2.8)
project(ttest)
# bison
find_package(FLEX)
FLEX_TARGET(ttest_lexer test.ll ${CMAKE_CURRENT_BINARY_DIR}/test.cpp)
# main
add_executable(ttest ${CMAKE_CURRENT_BINARY_DIR}/test.cpp)
# libraries
find_library(LEX_LIB l)
target_link_libraries(ttest ${LEX_LIB})
Run
$ ./ttest
Lex and C++
abd 123
word: abd
number: 123
^C
lexer&parser
Files
两个文件:test.ll, test.yy. 最后生成可执行文件ttest。
test.ll
%{
//#include <string>
#include "y.tab.h"
%}
%%
[\t ]+ ;
[a-zA-Z]+ {return OBJECT;}
[0-9]+ {return PRICE;}
.|\n {ECHO;}
test.yy
%{
#include <stdio.h>
extern int yylex (void);
void yyerror(const char *s, ...);
%}
%token OBJECT PRICE
%%
description: object PRICE { printf("pass...\n"); }
;
object: OBJECT
;
%%
extern FILE *yyin;
int main(int argc, const char* argv[])
{
if (argc != 2) {
printf("Usage: %s filename\n", argv[0]);
return 0;
}
yyin = fopen(argv[1], "rb");
if (NULL == yyin) {
printf("Open file failed: %s\n", argv[1]);
return 0;
}
while(!feof(yyin)) {
yyparse();
}
fclose(yyin);
yyin = NULL;
return 0;
}
void yyerror(const char *s, ...)
{
fprintf(stderr, "%s\n", s);
}
不用cmake
执行如下命令:
yacc -d test.yy
lex test.ll
gcc -o test lex.yy.c y.tab.c -ll
运行示例:
$ ./test
Usage: ./test filename
$ echo phone 1000 > ok.txt
$ echo tom jerry > fail.txt
$ ./test ok.txt
pass...
$ ./test fail.txt
syntax error
$
用cmake
首先修改test.ll中的头文件名称:
#include "y.tab.h" --> #include "parser.h"
同样创建src/bin/CMakeLists.txt。其中cmake文件内容:
cmake_minimum_required(VERSION 3.5)
project(ttest)
# bison and flex
find_package(BISON)
find_package(FLEX)
BISON_TARGET(ttest_parser test.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.h
)
FLEX_TARGET(ttest_lexer test.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
# default header file is parser.hpp
ADD_FLEX_BISON_DEPENDENCY(ttest_lexer ttest_parser)
# main
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set (MAIN_SRC ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp
${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
# or
${BISON_ttest_parser_OUTPUTS}
${FLEX_ttest_lexer_OUTPUTS}
)
add_executable(ttest ${MAIN_SRC})
find_library(LEX_LIB l)
target_link_libraries(ttest ${LEX_LIB})
注意到对于生成的cpp文件的两种引用方式。
另,根据https://cmake.org/cmake/help/v3.6/release/3.4.html的说法,可能cmake版本要求至少是3.4:
The FindBISON module BISON_TARGET macro learned a new DEFINES_FILE
option to specify a custom output header to be generated.
lex&parser打包成库文件
假定要把lexer&parser的代码打包成lib,然后供其他的cpp调用。为此,把test.yy中的main()搬到一个新的main.cpp文件中:
#include <stdio.h>
extern FILE *yyin;
extern int yyparse();
int main(int argc, const char* argv[])
{
if (argc != 2) {
printf("Usage: %s filename\n", argv[0]);
return 0;
}
yyin = fopen(argv[1], "rb");
if (NULL == yyin) {
printf("Open file failed: %s\n", argv[1]);
return 0;
}
while(!feof(yyin)) {
yyparse();
}
fclose(yyin);
yyin = NULL;
return 0;
}
然后cmake文件改为:
cmake_minimum_required(VERSION 3.5)
project(ttest)
# bison and flex
find_package(BISON)
find_package(FLEX)
BISON_TARGET(ttest_parser test.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.h
)
FLEX_TARGET(ttest_lexer test.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
# default header file is parser.hpp
ADD_FLEX_BISON_DEPENDENCY(ttest_lexer ttest_parser)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_library(myparser ${BISON_ttest_parser_OUTPUTS}
${FLEX_ttest_lexer_OUTPUTS}
)
# main
add_executable(ttest main.cpp)
target_link_libraries(ttest myparser)
find_library(LEX_LIB l)
target_link_libraries(ttest ${LEX_LIB})