利用FLEX BISON 快速实现简单的C 语言编译器前端

Flex
Flex是一个生成词法分析器的工具,它可以利用正则表达式来生成匹配相应字符串的C语言代码,其语法格式基本同Lex相同。

格式
LEX的源文件由三个部份组成,每个部分之间用顶行的 `%%’ 分割,其格式如下:

定义部份

%%

规则部份

%%

用户附加C语言部份
1
2
3
4
5
6
7
8
9
手册->Flex, version 2.5
怎么用就不赘述了…

主要就是用这个词法生成器,给输入的源文件中的每一个关键字打上tag,以便后面的语法分析器能够识别.

GNU Bison
GNU bison 是属于 GNU 项目的一个语法分析器生成器。Bison 把一个关于“向前查看 从左到右 最右 `LALR’ 上下文无关文法的描述转化成可以分析该文法的 C 或 C++ 程序。它也可以为二义文法生成 “通用的 从左到右 最右 `(GLR)’ 语法分析器。
这里主要使用的就是普通的LALR…

手册->Bison 3.0.5

格式
与flex的格式大致相同…

声明部分
%%
语法规则
%%
C语言附加部分
1
2
3
4
5
源码
flex 部分
/* filename -> scanner.l */
%option noyywrap yylineno

%{
#include <stdio.h>   
#include <stdlib.h>
#include <string.h>
#include "parser.tab.h"
int old_status;
void yyerror(char *s, ...);

%}

%x COMMENT

%%
    /* 下面都是正则表达式 */
int         { return INT; }
float       { return FLOAT; }
double      { return DOUBLE; }
auto        { return AUTO; }
break       { return BREAK; }
case        { return CASE; }
const       { return CONST; }
else        { return ELSE; }
for         { return FOR; }
if          { return IF; }
long        { return LONG; }
return      { return RETURN; }
short       { return SHORT; }
signed      { return SIGNED; }
char        { return CHAR; }
unsigned    { return UNSIGNED; }
    
    /* user variable */
[a-zA-Z][a-zA-Z0-9_]*       { yylval.strval = strdup(yytext); return IDENTITY; }
    /* integer numbers */
-?[0-9]+                    { yylval.intval = atoi(yytext); return INT_NUMBER; }
-?[0-9]+\.[0-9]+            { yylval.floatval = atof(yytext); return FLOAT_NUMBER; }
    /* real numbers */
\"(\\.|\"\"|[^"\n"])*\"     { yylval.strval = strdup(yytext); return STRING; }
    /* C-type strings */
\"(\\.|[^"\n])*$            { yyerror("Unterminated string %s", yytext); }

    /* operators */
[-+&~|^/%*(),.;!]       { return yytext[0]; }
"&&"                    { return AND; }
"||"                    { return OR; }
"="                     { return ASSIGN; }

    /* comments */
"//".*;
"/*"                    { old_status = YY_START; BEGIN COMMENT; }
<COMMENT>"*/"           { BEGIN old_status; }
    /* space & tab */
[ \t\n]
    /* prevent flex jam */
.           { yyerror("something goes wrong...\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
##Bison 部分

 /* filename -> parser.y */
%require "3.0.4"

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void yyerror(char *s, ...);
int yylex();
%}

%debug

%locations

%union {
    int intval;
    double floatval;
    char *strval;
    int subtok;
}

    /* token 关键字 */
%token INT
%token FLOAT
%token DOUBLE
%token AUTO
%token BREAK
%token CASE
%token CONST
%token ELSE
%token FOR
%token IF
%token LONG
%token RETURN
%token SHORT
%token SIGNED
%token CHAR
%token UNSIGNED
%token <strval> IDENTITY
%token <intval> INT_NUMBER
%token <floatval> FLOAT_NUMBER
%token <strval> STRING

%left OR
%left AND
%left ASSIGN
%left '!'
%left '+' '-'
%left '*' '/' '%'

%type <intval> decl_stmt bin_expr factor expr sentence id_list assign_stmt

%start statement

%%

/* 语法规则 */
statement: sentence ';'         { printf("STMT\n"); }
    | statement sentence ';'
    ;

sentence: decl_stmt     { $$ = $1; }
    | assign_stmt       { $$ = $1; }
    | /* empty rule */  { $$ = 0; }
    ;


decl_stmt: type_dec id_list { printf("stmt_decl\n"); }
    | type_dec assign_stmt  { printf("stmt_decl & assignment\n"); }
    ;

id_list: IDENTITY           { printf("id: %s\n", $1); $$ = $1; }
    | id_list ',' IDENTITY  { printf("id: %s\n", $3); $$ = $3; }
    ;

assign_stmt: IDENTITY ASSIGN expr    { printf("id: %s\nASSIGNMENT\n", $1); }
    ;

expr: factor    { $$ = $1; }
    | bin_expr  { $$ = $1; }
    ;

factor: INT_NUMBER      { printf("VALUE: %d\n", $1); $$ = $1; }
    | FLOAT_NUMBER      { printf("VALUE: %d\n", $1); $$ = $1; }
    | IDENTITY          { printf("VALUE: %s\n", $1); $$ = $1; }
    ;

bin_expr: expr '+' expr     { printf("PLUS.\n"); }
    | expr '-' expr         { printf("SUB.\n"); }
    | expr '*' expr         { printf("MUL.\n"); }
    | expr '/' expr         { printf("DIV.\n"); }
    ;

type_dec: INT       { printf("TYPE:INT\n"); }
    | FLOAT         { printf("TYPE:FLOAT\n"); }
    | SHORT         { printf("TYPE:SHORT\n"); }
    | LONG          { printf("TYPE:LONG\n"); }
    | UNSIGNED LONG { printf("TYPE:UNSIGNED\n"); }
    ;


%%

int main(int argc, const char *args[])
{
    /* 将注释去掉就能看到stack具体是怎么工作的.. */
    /* yydebug = 1; */

    extern FILE *yyin;
    if(argc > 1 && (yyin = fopen(args[1], "r")) == NULL) {
        fprintf(stderr, "can not open %s\n", args[1]);
        exit(1);
    }
    if(yyparse()) {
        exit(-1);
    }
    
    return 0;
}

void yyerror(char *s, ...)
{
    extern int yylineno;

    va_list ap;
    va_start(ap, s);

    fprintf(stderr, "%d: error: ", yylineno);
    vfprintf(stderr, s, ap);
    fprintf(stderr, "\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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
当然还有Makefile

LEX = flex
YYAC = bison
CC = gcc

all: test

test: parser.tab.o scanner.o
        $(CC) -o $@ parser.tab.o scanner.o

parser.tab.c parser.tab.h: parser.y
        $(YYAC) -vd parser.y

scanner.c: scanner.l
        $(LEX) -o $@ $<

scanner.o: scanner.c parser.tab.h

.PYHONY: clean

clean:
        -@ rm parser.tab.c parser.tab.h scanner.c parser.output *.o

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
上面的bison代码中语法部分仅仅只写了赋值语句和声明语句的语法解析规则.连错误处理都没有,23333
也没有生成中间树…但这不重要…
打印的输出是逆波兰式`RPN’…

不打开debug模式


int a;
TYPE:INT
id: a
stmt_decl
STMT
int a, b, c;
TYPE:INT
id: a
id: b
id: c
stmt_decl
int a = b + 1;
TYPE:INT
VALUE: b
VALUE: 1
PLUS.
id: a
ASSIGNMENT
stmt_decl & assignment
float a, b, c;
TYPE:FLOAT
id: a
id: b
id: c
stmt_decl
unsigned long a, b, c;
TYPE:UNSIGNED
id: a
id: b
id: c
stmt_decl
unsigned long a = b + c;
TYPE:UNSIGNED
VALUE: b
VALUE: c
PLUS.
id: a
ASSIGNMENT
stmt_decl & assignment
int a, b, c + 1;
TYPE:INT
id: a
id: b
id: c
stmt_decl
7: error: syntax error

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
打开debug模式

Starting parse
Entering state 0
Reading a token: int a, b, c; 
Next token is token INT (1.1: )
Shifting token INT (1.1: )
Entering state 1
Reducing stack by rule 20 (line 95):
   $1 = token INT (1.1: )
TYPE:INT
-> $$ = nterm type_dec (1.1: )
Stack now 0
Entering state 11
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 17
Reading a token: Next token is token ',' (1.1: )
Reducing stack by rule 8 (line 73):
   $1 = token IDENTITY (1.1: )
id: a
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Next token is token ',' (1.1: )
Shifting token ',' (1.1: )
Entering state 27
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 32
Reducing stack by rule 9 (line 74):
   $1 = nterm id_list (1.1: )
   $2 = token ',' (1.1: )
   $3 = token IDENTITY (1.1: )
id: b
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Reading a token: Next token is token ',' (1.1: )
Shifting token ',' (1.1: )
Entering state 27
Reading a token: Next token is token IDENTITY (1.1: )
Shifting token IDENTITY (1.1: )
Entering state 32
Reducing stack by rule 9 (line 74):
   $1 = nterm id_list (1.1: )
   $2 = token ',' (1.1: )
   $3 = token IDENTITY (1.1: )
id: c
-> $$ = nterm id_list (1.1: )
Stack now 0 11
Entering state 18
Reading a token: Next token is token ';' (1.1: )
Reducing stack by rule 6 (line 69):
   $1 = nterm type_dec (1.1: )
   $2 = nterm id_list (1.1: )
stmt_decl
-> $$ = nterm decl_stmt (1.1: )
Stack now 0
Entering state 9
Reducing stack by rule 3 (line 63):
   $1 = nterm decl_stmt (1.1: )
-> $$ = nterm sentence (1.1: )
Stack now 0
Entering state 8
Next token is token ';' (1.1: )
Shifting token ';' (1.1: )
Entering state 16
Reducing stack by rule 1 (line 59):
   $1 = nterm sentence (1.1: )
   $2 = token ';' (1.1: )
STMT
-> $$ = nterm statement (1.1: )
Stack now 0
Entering state 7
Reading a token: Now at end of input.
Shifting token $end (1.1: )
Entering state 14
Stack now 0 7 14
Cleanup: popping token $end (1.1: )
Cleanup: popping nterm statement (1.1: )

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
能够清楚的看到栈的状态…

##CmakeList.txt
cmake在较新的版本已经可以直接使用了.之前的版本是不支持找flex & bison的,只能通过`add_custom_command’ 来使用flex & bison…这非常的麻烦

通过看CMake的手册才知道了可以这样写…

cmake_minimum_required(VERSION 3.4.3)

project(tincompiler)

find_package(BISON)
find_package(FLEX)

INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})

FIND_LIBRARY(LEX_LIB l)

FLEX_TARGET(scanner scanner.l   ${CMAKE_CURRENT_BINARY_DIR}/scanner.c)
BISON_TARGET(parser parser.y    ${CMAKE_CURRENT_BINARY_DIR}/parser.c)

ADD_FLEX_BISON_DEPENDENCY(scanner parser)

ADD_EXECUTABLE(tincompiler
        ${BISON_parser_OUTPUTS}
        ${FLEX_scanner_OUTPUTS})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
如果使用CMake作为构建工具需要将 `scanner.l’ 里面的 ` #include “parser.tab.h” ’ 改成 ` #include “parser.h” '

参考资料:

《flex & bison》(John Levine), O’Reilly Media
CMake Manual, FindFLEX
CMake Manual, FindBISON
《Compilers Principles, Techniques, & Tools》(龙书), (Alfred V. Aho / Monica S.Lam / Ravi Sethi / Jeffrey D. Ullman)
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/sinat_37806635/article/details/81138771

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/776687.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s 如何获取加入节点命名

当k8s集群初始化成功的时候&#xff0c;就会出现 加入节点 的命令如下&#xff1a; 但是如果忘记了就需要找回这条命令了。 kubeadm join 的命令格式如下&#xff1a;kubeadm join --token <token> --discovery-token-ca-cert-hash sha256:<hash>--token 令牌--…

虚拟机-从头配置Ubuntu18.04(包括anaconda,cuda,cudnn,pycharm,ros,vscode)

最好先安装anaconda后cuda和cudnn&#xff0c;因为配置环境的时候可能conda会覆盖cuda的路径&#xff08;不确定这种说法对不对&#xff0c;这里只是给大家的建议&#xff09; 准备工作&#xff1a; 1.Ubuntu18.04&#xff0c;x86_64&#xff0c;amd64 虚拟机下载和虚拟机Ubu…

[密码学] 密码学基础

目录 一 为什么要加密? 二 常见的密码算法 三 密钥 四 密码学常识 五 密码信息威胁 六 凯撒密码 一 为什么要加密? 在互联网的通信中&#xff0c;数据是通过很多计算机或者通信设备相互转发&#xff0c;才能够到达目的地,所以在这个转发的过程中&#xff0c;如果通信包…

康耐视visionpro-CogToolBlock工具详细说明

CogToolBlock功能: 将多个工具组合在一起完成某个功能&#xff0c;接口简单且可以重用 CogToolBlock操作说明&#xff1a; 1.打开工具栏&#xff0c;双击或点击鼠标拖拽添加CogToolBlock CogToolBlock操作说明 ②.添加输入图像&#xff0c;右键“链接到”或以连线拖拽的方式选…

Games104 听后笔记

1、为什么UE5要自己写一套STL库 因为传统的STL库中&#xff0c;例如&#xff1a;vector&#xff0c;它一般采用的是双倍扩容法&#xff0c;加入1000个数据装满了&#xff0c;现在需要又加一个&#xff0c;那么就开辟了2000个数据的空间&#xff0c;那么当前就又999的数据空间暂…

Python爬虫:爬虫常用伪装手段

目录 前言 一、设置User-Agent 二、设置Referer 三、使用代理IP 四、限制请求频率 总结 前言 随着互联网的快速发展&#xff0c;爬虫技术在网络数据采集方面发挥着重要的作用。然而&#xff0c;由于爬虫的使用可能会对被爬取的网站造成一定的压力&#xff0c;因此&#…

UE RPC 外网联机(1)

技术&#xff1a;RPC TCP通信 设计&#xff1a;大厅服务<---TCP--->房间服务<---RPC--->客户端&#xff08;Creator / Participator&#xff09; 1. PlayerController 用于RPC通信控制 2.GameMode 用于数据同步 3.类图 4. 注意 &#xff08;1&#xff09;RPC&a…

记录一次数组越界导致的线程死锁问题

1. 问题描述 在一次代码调试的过程中&#xff0c;遇到过一个问题&#xff0c;线程在调用pthread_cancel时&#xff0c;提示未找到目标线程&#xff0c;然后程序阻塞在了与目标线程相关的条件变量的释放上&#xff0c;造成了死锁的现象。 2. 问题复现 #include <pthread.h…

微机原理与接口技术-精选复习题

1、ADC0809的START和EOC引脚的功能是什么&#xff1f;在查询方式和中断方式中&#xff0c;EOC引脚分别如何处理&#xff1f; START是转换启动信号&#xff0c;EOC信号是转换结束信号。在查询方式中&#xff0c;EOC可作为状态信息输入至CPU以供查询&#xff1b;在中断方式中EOC…

python--初学函数

函数&#xff08;function&#xff09;&#xff1a; 什么是函数&#xff1f; 具有名称的&#xff0c;是为了解决某一问题&#xff0c;功能代码的集合&#xff0c;叫做函数 python中函数如何定义&#xff1a;def>define function定义函数 def function_name([args临时变量…

JavaScript 常用方法(1):JS日期格式化函数、JS实现并列排序、JS实现数字每三位添加逗号、JS 实现根据日期和时间进行排序

1、JS日期格式化函数 JS日期格式化转换方法 /*** description 格式化时间* param fmt 格式 如&#xff1a;yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy年MM月dd日 W HH:mm:ss等* param {String} date 时间戳* returns {string|null}* 对 Date 的扩展&#xff0c;将 Date 转化为指…

如何在CentOS使用Docker搭建MinIO容器并实现无公网ip远程访问本地服务

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

拥有超小型领先工艺射频微波电子元器件厂商兆讯授权世强硬创代理

射频前端芯片在模拟芯片中&#xff0c;属于进入门槛较高、设计难度较大的细分领域&#xff0c;由于国内射频前端芯片行业起步较晚&#xff0c;其市场份额主要被外企所占据&#xff0c;而在国产化浪潮的推动下&#xff0c;上游厂商的射频前端产品及技术逐渐具备领先的竞争优势。…

【C++初阶】之类和对象(下)

【C初阶】之类和对象&#xff08;下&#xff09; ✍ 再谈构造函数&#x1f3c4; 初始化列表的引入&#x1f498; 初始化列表的语法&#x1f498; 初始化列表初始化元素的顺序 &#x1f3c4; explicit关键字 ✍ Static成员&#x1f3c4; C语言中的静态变量&#x1f3c4; C中的静…

Leveled mode of TFHE

参考文献&#xff1a; [CGGI16] Chillotti I, Gama N, Georgieva M, et al. Faster fully homomorphic encryption: Bootstrapping in less than 0.1 seconds[C]//Advances in Cryptology–ASIACRYPT 2016: 22nd International Conference on the Theory and Application of C…

谷歌浏览器驱动Chromedriver(114-120版本)文件以及驱动下载教程

ChromeDriver 官方网站 GitHub || GoogleChromeLabs/chrome-for-testing Chrome Driver 113-125_JSONChrome for Testing availability 123-125 zip 白月黑羽 Python基础 | 进阶 | Qt图形界面 | Django| 自动化测试 | 性能测试 |JS语言 | JS前端 |原理与安装

蓝桥杯嵌入式学习笔记(6):IIC程序设计

目录 前言 1. IIC基本原理 2. 电路原理 3. 代码编程 3.1 预备工作 3.2 AT24C02写读功能编写 3.2.1 AT24C02写操作实现 3.2.2 AT24C02读操作实现 3.3 MCP4017写读功能编写 3.3.1 MCP4017写操作实现 3.3.2 MCP4017读操作实现 3.4 main.c编写 3.4.1 头文件引用 3.4.…

蓝桥杯每日一题(floyd算法)

4074 铁路与公路 如果两个城市之间有铁路t11&#xff0c;公路就会t2>1,没铁路的时候t1>1,公路t21。也就是公路铁路永远都不会相等。我们只需要计算通过公路和铁路从1到n最大的那个即可。 floyd是直接在数组上更新距离。不需要新建dis数组。另外一定要记得把邻接矩阵初始…

【数据结构】链表习题之环形链表的约瑟夫问题

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;道阻且长&#xff0c;行则将至 前言 今天这道题目时牛客上的题目&#xff0c;名为环形链表的约瑟夫问题&#xff0c;很有趣的的一道题目 环形链表的约瑟…

vue中实现超出一行 展开和收起的功能

html中: <divclass="txttype"ref="txttype"style="margin-bottom: 6px":class="hidetext == true ? hidetext : "><div style="width: 96%"><el-tagtype="info"style="margin-right: 10px&…