cmake项目结构
包含词法分析和语法分析两部分,main.cpp用于实现更复杂功能,假设不使用。
若不使用Bison,则使用parser.h和parser.cpp替换parser.y。
cmake_minimum_required(VERSION 3.16)
project(Compiler)
set(CMAKE_CXX_STANDARD 14)
# 使用 cmake 的 flex 模块
include(FindFLEX)
if(FLEX_FOUND)
message("Info: flex found!")
else()
message("Error: flex not found!")
endif()
# 为了 flex 新增头文件搜索路径
include_directories(${CMAKE_SOURCE_DIR})
# 指定 flex 编译目标
FLEX_TARGET(MyScanner lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
add_compile_options(-pedantic)
# add_compile_options(-fsanitize=address)
# add_link_options(-fsanitize=address)
add_executable(Compiler
parser.cpp
${FLEX_MyScanner_OUTPUTS}
)
target_compile_features(Compiler PRIVATE cxx_std_14)
词法分析lexer.l
%{
c说明
%} 说明部分
正规式辅助定义
%% -----------------
模式(正则匹配) {动作} 规则部分
%% -----------------
用户代码部分
LEX中常用的一些变量和函数
考虑到flex通过FILE* yyin读取文件生成结果,文件存放在外存。若在动作中使用return结束yylex()以逐词分析,则每遇到一个词访问一次外存,运行时间较长。所以应一次性扫描完整个输入文件(即只调用一次yylex),并将生成的词法分析结果存放在内存中(使用变量out)。
可以选择定义以下scan函数,在语法分析部分声明外部变量scan。或者直接在语法分析部分声明外部变量yyin和yylex。
%{
#include "parser.h"
声明变量out存储词法分析结果
%}
%option noyywrap 或者定义函数yywrap,否则报错
delim [ \t\n]
%%
{delim}+ {}
int {out <- INT;}
... 若是单个{字符需加引号,即 "{" {动作}
[;,] {out <- END;}
[0-9]+ {out <- <CONST,yytext;}
[A-Za-z_][0-9A-Za-z_]* {out <- VARI,yytext;}
[+\-*/%<>=!&|^()]=* {out <- OPTR,yytext;}
%%
void scan(char* const& filepath) {
yyin=fopen(filepath,"r");yylex();
}
string generate() {return out;}
在词法分析部分声明out
如上述代码,可以选择上述方法,在用户代码部分定义返回值为out的函数,在parser.cpp中extern string generate();后使用该函数。或者在parser.h中声明该函数。
或者不使用函数,直接在parser.cpp中extern变量out。
在头文件声明out
若变量out声明在包含的头文件parser.h中,由于parser.cpp和lexer.l均include了该头文件,会报错out。若将out声明为静态全局变量,则可能出现bug,在lexer.l中不能正确赋值out。或者使用如下方法以解决该报错。
包含了上述两种情况的方法
若在lexer.l或parser.cpp中#define INITIAL,并在头文件中添加如下代码段,将变量out定义在#define INITIAL的文件中。
#ifdef INITIAL
string out;
#else
extern string out;
#endif
总结
总而言之,集成还是很简单的,只需要会使用extern就可以。
发这篇文章,关键是想记录一下一种错误情况——打印scanner.l的结果和正确tokens的语法分析结果均正确,但是集成在一起是没有输出且程序不终止。
可能的原因——在parser.h中声明静态全局变量static string tokens。可能会导致在scanner.l中不能成功赋值,且程序不终止。