简单用用flex和bison

2023-11-20

参考博文

IBM社区对于lex和Yacc的快速入门,附带例子
简书:初识flex
简书:初识bison
bison %code使用
bison声明细则

词法、语法分析器简介

我们使用的词法分析器为flex,语法分析器为bison。

定义

flex

词法分析器会将输入序列与定义的常规表达式匹配,匹配往往会返回标记

bison

语法分析器在查看到某个标记序列时,可能会触发某些动作,这便是语法。

文件间关系

在这里插入图片描述
bas.y通过yacc生成y.tab.h包含了语法规则需要的词法声明(终结符声明)。
bas.ly.tab.h通过lex生成了lex.yy.c,用来定义y.tab.c里语标记序列的语法规则中每个标记的语法定义规则。(终结符匹配规则
bas.y通过yacc和lex.yy.c,会生成y.tab.c,包含了标记序列的语法规则

(PS:终结符和我们自定义的“标记”等同。)

具体例子

接下来我们会用两个例子来演示一下flex和bison的配合。
第一个例子:输入name=age时触发,输出name is age years old !!
第二个例子:输入带有括号的满足乘法加法的表达式,输出运算后的值。

例1 输入name=age时触发,输出name is age years old !!

当我们输入 tom=10 的输入序列后,输入序列会被flex的scanner获取。
flex对scanner中的字符一一解析匹配,name和age被存储到类型为char*的yytext中再返回,eq则会直接返回一个终结符给语法分析器。

Name.lex

//声明部分 %{%}之内的内容会被复制到Name.tab.c中
%{
#include    "Name.tab.h"
#include    <stdio.h>
#include    <string.h>
%}

char    [A-Za-z]    
num [0-9]
eq  [=]
name    {char}+
age {num}+

//规则部分	输入序列与常规表达式的匹配,可能会触发标记返回。
//yylval Yacc的变量,默认为int类型,可以被重定义为char*
//yytext 匹配模式的文本存储在这一变量中(char*)。
%%
{name}  {yylval=strdup(yytext);return NAME;}    //匹配到名字,返回NAME
{eq}    {return EQ;}    //匹配到=,返回EQ
{age}   {yylval=strdup(yytext);return AGE;} //匹配到年龄,返回AGE
%%

//C代码部分
int yywrap()	//返回为1时代表分析结束。
{
    return 1;
}

当scanner中的字符都被词法分析器解析完,并将终结符返回给了语法分析器之后,接着语法分析。
我们递归地读取记录,当NAME EQ AGE这个终结符序列被匹配时,我们触发打印 name is age years old!!

Name.y

%{
#include    <stdio.h>
typedef char *string;
#define YYSTYPE string
%}
%token NAME EQ AGE	//词法分析得到的结果,用于传入语法分析器进行分析

%%
file:record file|record;
record:NAME EQ AGE  {printf("%s is %s years old!!!\n",$1,$3);};	//某个标记序列,触发动作
%%

int main()
{
    yyparse();	//语法分析
    return 0;
}

int yyerror(char *msg)
{
    printf("Error encountered: %s\n",msg);
}

Name.tab.h
包含了语法规则需要的词法声明,即Name.y第一部分的声明内容。

extern int yydebug;
#endif

/* Token type.  */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
  enum yytokentype	
  {
    NAME = 258,
    EQ = 259,
    AGE = 260
  };
#endif

/* Value type.  */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef char * YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif


extern YYSTYPE yylval;

int yyparse (void);

build.sh

bison -d Name.y # bison -d 由.y文件产生.tab.c .tab.h
sed -i 's/typedef int YYSTYPE/typedef char * YYSTYPE/g' Name.tab.h #sed -i 's/原字符串/新字符串/' /home/1.txt
flex Name.lex #flex 由.lex文件产生.yy.c文件
cc -o test2 lex.yy.c Name.tab.c #cc为gcc

运行结果
在这里插入图片描述

例2 输入带有括号的满足乘法加法的表达式,输出运算后的值。

我们从需求由顶向下分析,从main开始:

实际上是main得到输入序列->放到getAST,先被拷贝进scanner,经过自定义的yyparse解析获得expression表达式->evaluate中解析得到值。

int yyparse(SExpression **expression,yyscan_t scanner);

SExpresion *getAST(const char *expr)
{
    SExpression *expression;
    yyscan_t scanner;
    YY_BUFFER_STATE state;

    //如果初始化失败则返回
    if(yylex_init(&scanner))    return NULL;

    //将输入序列复制进语法分析器的扫描器中
    state=yy_scan_string(expr,scanner);

    //如果解析失败则返回
    if(yyparse(&expression,scanner))    return NULL;

    //将scanner分配的buffer清除
    yy_delete_buffer(state,scanner);
	
	yylex_destroy(scanner);

    return expression;
}

int evaluate(SExpression *e)
{
    switch (e->type)
    {
    case eVALUE:
        return e->value;
    case eMULTIPLY:
        return evaluate(e->left)*evaluate(e->right);
    case ePLUS:
        return evaluate(e->left)+evaluate(e->right);
    
    default:
        return 0;
    }
}



int main(void)
{
    SExpression *e=NULL;
    char test[]=" 4 + 2 *10 + 3 * (5 + 1 )";
    int result=0;

    //将输入序列放到getAST中,经过抽象树处理生成expression
    e=getAST(test);

    //由评估函数将expression的值得出
    result=evaluate(e);

    printf("Result of '%s' is %d\n",test,result);

    deleteExpression(e);

    return 0;
}

expression的定义:

//表达式的操作符类型
typedef enum tagEOperationType
{
    eVALUE,
    eMULTIPLY,
    ePLUS
}EOperationType;

//多么令人熟悉的二叉树
typedef struct tagSExpression
{
    EOperationType type;
    int value;
    struct tagSExpression *left;
    struct tagSExpression *right;
}SExpression;

//该表达式节点就一个值
SExpression *createNumber(int value);

//该表达式节点还有左右操作符
SExpression *createOperation(EOperationType type,SExpression *left,SExpression *right);

void deleteExpression(SExpression *b);

现在让我们进入真正进行yyparse的Parser.y

我们希望从词法分析器获得这些终结符
左圆括号、右圆括号、加号、乘号、数字。

我们希望将这些终结符定义为可左结合
加号、乘号。

我们将expr定义为非终结符

我们希望定义这样的语法规则

  1. 表达式=表达式1 + 表达式2 -> 触发返回createOperation(ePLUS,表达式1,表达式2)
  2. 表达式=表达式1 * 表达式2 -> 触发返回createOperation(eMULTIPLY,表达式1,表达式2)
  3. 表达式=左圆括号 表达式1 右圆括号 -> 就等于表达式1
  4. 表达式 = 数字 -> 就等于数字
%{
#include    "Expression.h"
#include    "Parser.h"
#include    "Lexer.h"

int yyerror(SExpression **Expression,yyscan_t scanner,const char *msg){}
%}

//会被放到defines,output中定义YYSTYPE、YYLTYPE之前
%code requires{

#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
}

//将原本的.tab.c / .tab.h改成.c / .h
%output     "Parser.c"
%defines    "Parser.h"

//将指定所有符号的语义类型
%define api.pure
//lex parse要用到的一些参数
%lex-param      {yyscan_t scanner}
%parse-param    {SExpression **expression}
%parse-param    {yyscan_t scanner}

//指定一个联合类型,不指定名字则是YYSTYPE
%union{
    int value;
    SExpression *expression;
}

//声明 左结合 的符号类型
%left '+'   TOKEN_PLUS
%left '*'   TOKEN_MULTIPLY

//声明终结符
%token  TOKEN_LPAREN
%token  TOKEN_RPAREN
%token  TOKEN_PLUS
%token  TOKEN_MULTIPLY
//栈类型为联合时,指定有语义值类型的终结符
%token <value>  TOKEN_NUMBER

//栈类型为联合时,有语义值类型的非终结符
%type <expression> expr

%%

//yyparse
input:expr {*expression=$1};

expr
	// 表达式+表达式
    :expr TOKEN_PLUS expr {$$=createOperation(ePLUS,$1,$3);}
    //表达式*表达式
    |expr TPKON_MULTIPLAY expr {$$=createOperation(eMULTIPLY,$1,$3);}
    //(表达式)
    |TOKEN_LPAREN expr TOKEN_RPAREN {$$=$2;}
    //值
    |TOKEN_NUMBER{$$=createNumber($1);}
    ;

%%

有了语法规则,需要对终结符的词法规则进行定义,进入Lexer.l

我们将左圆括号、右圆括号、加号、乘号、数字分别进行匹配定义。(注意空白我们也有进行匹配定义,只不过在词法规则中无需返回)

我们在匹配左圆括号、右圆括号、加号、乘号、数字、空白时,加入了触发动作,大多是直接返回,但数字还需要被存储到yytext中,空白无需返回。

%{
    
#include    "Expression.h"
#include    "Parser.h"

#include    <stdio.h>
%}

%option outfile="Lexer.c" header-file="Lexer.h"
%option warn nodefault

%option reentrant noyywrap never-interactive nounistd
%option bison-bridge

LPAREN      "("
RPAPEN      ")"
PLUS        "+"
MULTIPLY    "*"

NUMBER      [0-9]+
WS          [ \r\n\y]*

%%

{WS}            {}
{NUMBER}        {sscanf(yytext,"%d",&yylval->value);return TOKEN_NUMBER;}
{MULTIPLY}      {return TOKEN_MULTIPLY;}
{PLUS}          {return TOKEN_PLUS;}
{LPAREN}        {return TOKEN_LPAREN;}
{RPAPEN}        {return TOKEN_RPAREN;}
.               {}

%%

int yyerror(const char *msg){
    fprintf(stderr,"Error:%s\n",msg);return 0;
}

Expression.c main.c是由我们写的。
Lexer.l(Parser.h)通过flex生成Lexer.c。
Parser.y (Lexer.c)通过bison生成Parser.c。
最后Lexer.c Parser.c Expression.c main.c生成test。

#Makefile

#宏定义
FILES   = Lexer.c Parser.c Expression.c main.c
CC      = g++
CFLAGS  = -g -ansi

#待生成文件:需要什么文件\
        #生成方式
test:   $(FILES)
        $(CC) $(CFLAGS) $(FILES) -o test

Lexer.c: Lexer.l
        flex Lexer.l

Parser.c: Parser.y Lexer.c
        bison Parser.y

clean:
        rm -f *.o *~ Lexer.c Lexer.h Parser.c Parser.h test

运行结果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简单用用flex和bison 的相关文章

  • 迁移学习 & 凯明初始化

    前言 这一章其实就是之前没做完的事 来补一下 两者其实没啥关系 迁移学习 以下内容学习自迁移学习 斯坦福21秋季 实用机器学习中文版 迁移学习包括什么 feature extraction train a model on a relate
  • 由于缺少调试目标 E:a\b\c\串口配置工具\bin\Debug\串口配置工具.exe“,visual Studio无法开始调试。请生成项目并重试,或者相应OutputPath和AssemblyNa

    最近做一个窗体程序时候出现这个错误 我的项目名称是串口配置工具 建议为英文来命名 项目名称下面有这两个 发现 没有这个串口配置工具 exe 然后再这个 这里面发现这个串口配置工具 exe 最后直接 exe文件把这个复制到 项目名称 bin
  • C++基础——const成员函数

    目录 一 Const成员函数 1 定义 2 格式 3 代码示例 h文件 definition cpp文件 特性 例 那么const对象既可以调用非const型成员函数吗 问题3 const成员函数内可以调用其它的非const成员函数吗 问题
  • 手机运行python 神器,pydroid3 包含库的版本

    初次安装pydroid 或者qpython的同学运行爬虫时是不是蛋疼的一比 lxml根本装不了 虽然可以下载whl折腾 可是也很麻烦 后来我不死心 终于找到了包含库的版本 只有pydroid 64位 https lanzous com id
  • msa2000映射到服务器,HPmsa2000i官方详细的设置操作流程步骤.doc

    HPmsa2000i官方详细的设置操作流程步骤 从本地管理主机登录进入 SMU 如要从本地管理主机登录进入 SMU 在网络浏览器的地址栏中 键入某个控制器机柜的以太网管理端口的 IP 地址 然后按Enter 此时显示 SMU Login 页
  • IDEA java.lang.NullPointerException (no error message)

    今天在不停启动debug 停止debug后无法再启动debug 提示java lang NullPointerException no error message 经百度 删除 project下 gradle无效 恢复代码后无效 且未更改配
  • 【C语言】合并两个数组,降序排列并删除重复元素(通俗易懂)

    问题描述 试着写一个程序 具体内容如下 建立两个整型数组 int n scanf d n int a n 将其合并 对他们进行降序排序 去掉相同项 输出处理过后的数组 输入形式 首先第一行输入第一个数组中的长度n 然后输入n个整型数 然后在
  • MYSQL进阶-msql日志-慢查询日志

    2 慢查询日志 慢查询日志主要用来记录执行时间超过设置的某个时长的SQL语句 能够帮助数据库维护人员找出执行时间比较长 执行效率比较低的SQL语句 并对这些SQL语句进行针对性优化 2 1 开启慢查询日志 可以在my cnf文件或者my i
  • ant design pro 代码学习(七) ----- 组件封装(登录模块)

    以登录模块为例 对ant design pro的组件封装进行相关分析 登录模块包含基础组件的封装 组件按模块划分 同类组件通过配置文件生成 跨层级组件直接数据通信等 相对来说还是具有一定的代表性 1 登录模块流程图 首先 全局了解一下登录模
  • 在idea中安装并且使用easy code插件 ,以及在idea中配置mysql数据库

    在idea中安装并且使用easy code插件 以及在idea中配置mysql数据库 1 从导航栏进入设置页面 2 点击plugins选项 在输入框中输入easy code查找 并点击installed安装 下载安装好了以后需要重启软件 点
  • GNURadio报错Unable to create context(windows10环境)

    GNURadio报错Unable to create context windows10环境 这里本人使用的是GNU Radio3 7 11 iiosupport win64 版本 外设是ADI的ADALM PLUTO 这里本人使用的是GN
  • 多维时序

    多维时序 MATLAB实现ELM极限学习机多维时序预测 股票价格预测 目录 多维时序 MATLAB实现ELM极限学习机多维时序预测 股票价格预测 效果一览 基本介绍 程序设计 结果输出 参考资料 效果一览 基本介绍
  • 2018-12-13 LeetCode Q5 最长回文子串

    5 最长回文子串 给定一个字符串 s 找到 s 中最长的回文子串 你可以假设 s 的最大长度为 1000 示例 1 输入 babad 输出 bab 注意 aba 也是一个有效答案 示例 2 输入 cbbd 输出 bb 暴力解法 6004ms
  • 关于linux进程间的close-on-exec机制

    转载请注明出处 帘卷西风的专栏 http blog csdn net ljxfblog 前几天写了一篇博客 讲述了端口占用情况的查看和解决 关于linux系统端口查看和占用的解决方案 大部分这种问题都能够解决 在文章的最后 提到了一种特殊情
  • 判断字符串的两半是否相似(1704.leetcode)-------------------c++实现

    判断字符串的两半是否相似 1704 leetcode unordered map c 实现 题目表述 给你一个偶数长度的字符串 s 将其拆分成长度相同的两半 前一半为 a 后一半为 b 两个字符串 相似 的前提是它们都含有相同数目的元音 a
  • Tensorflow学习(五)——多任务学习验证码识别实战

    一 验证码生成 验证码生成脚本 使用captcha包提供的ImageCaptcha方法 from captcha image import ImageCaptcha import sys import random import numpy
  • 成功解决笔记本重装系统后没有无线网

    笔记本重装系统后没有无线网 一般的解决思路就是下载笔记本厂家官方提供的各类驱动程序 重新安装驱动 驱动下载的地址 建议到各个厂家的官网上去寻找驱动资源 以本人的笔记本型号为例 点击window R 然后输入dxdiag运行 然后找到该官方开
  • 用js/react写一个计算器

    效果图如下 直接上代码 目录如下 Button和Text是自己封装的Button组件和Input组件 Button index js import react Component from react import index css cl
  • 图像风格迁移cvpr2020_今日 Paper

    目录 CurricularFace 深度人脸识别的适应性课程学习损失 MaskGAN 多样和交互的面部图像操作 结合检测和跟踪的视频人体姿态估计 通过解纠缠表示的局部面部妆容迁移 基于自动生成的训练数据进行大规模事件抽取学习 Curricu
  • 【UiBot】RPA定时触发:机器人如何在指定时间执行任务?

    Q RPA机器人如何在指定时间点执行任务 A 用流程机器人 UiBot Worker 设置定时触发 人机交互的流程机器人 UiBot Worker 除了手动运行流程之外 还提供了 触发器 的功能 可以在满足一定条件时 有计划 有选择性地执行

随机推荐

  • C++定义二维数组并赋值,亲测可用

    代码在这里 拿走不谢 include
  • IDEA springboot项目导出jar包及运行jar包

    首先点击Terminal 在Terminal下输入 mvn clean package 看到以下这个提示则是打包成功 接着咱们可以在自己的项目下看到打包成功的jar包 如图 运行jar包 我的jar包位置如下 写一个bat文件 里面的内容为
  • 实时监控Cat之旅~配置Cat集群需要注意的问题

    在配置cat集群时 有一些设置是我们应该注意的 从它的部署文档中我们可以看到相关信息 但说的还不够明确和重要 大叔今天总结一下Cat集群配置的注意事项 服务端datasources xml用来设置连接的mysql 集群里的服务器对这项配置是
  • pyecharts运行后产生的html文件用浏览器打开空白

    引用 https github com pyecharts pyecharts issues 503 使用logging日志打印代码错误 coding utf 8 from future import unicode literals im
  • Mageia 9 发布:搭载 Linux 内核 6.4,支持 PulseAudio

    导读 Mageia 最初是 Mandriva Linux 的一个分支 但现在已经发展成全面的 独立 Linux 发行版 从 2010 年以来 Mageia 已经成为一个用于桌面或服务器的稳定且安全的操作系统 并且定期更新 它的近期的发布公告
  • C语言排序算法实现

    C语言实现各种排序算法 冒泡排序 选择排序 插入排序 希尔排序 插入方式 非交换方式 快速排序 归并排序 分治思想 基数排序 桶排序 基数排序的基本思想 典型的空间换时间方式 冒泡排序 include
  • moviepy 生成的视频只有声音没有图像

    问题描述 PDF转成视频 用moviepy 将图片生成视频的时候 生成的视频 有些播放器 播放只有声音没有图像 解决方案 查看源码后发现在 ffmpeg writer py 文件里面有一段这样的代码 if codec libx264 and
  • 在线AI日语视频音频翻译中文字幕

    新番视频没有字幕 AI日语字幕识别让你先睹为快 蓝海智能在线AI工具 niceaitools com languageTra 目前价格是每视频音频分钟0 42元 具体使用方法如下 1 格式工厂处理 把你的日语视频或音频文件 通过格式工厂处理
  • VBA 自定义函数 库存消耗截止

    两个自定义函数 按照库存 计算哪一周库存能消耗完 标出周 自定义函数 按照库存 计算消耗完的那一周实际消耗多少数量 自定义函数 从消耗完的那一周开始算起 后面的周消耗数量都变为0 按钮 Function HaoJin StockQty As
  • Pytorch中常用的损失函数

    Pytorch中常用的损失函数 回归 nn L1Loss nn MSELoss 分类 nn CrossEntropyLoss 回归 nn L1Loss 平均绝对误差 也称L1范数损失 计算预测值与真实值之间的误差绝对值 L 1 L o s
  • python中模块,包,库的概念

    https www cnblogs com mlgjb p 7875494 html 模块 就是 py文件 里面定义了一些函数和变量 需要的时候就可以导入这些模块 包 在模块之上的概念 为了方便管理而将文件进行打包 包目录下第一个文件便是
  • OpenGL入门教程之 深入理解

    一 OpenGL简介 OpenGL是一种用于渲染2D 3D矢量图形的跨语言 跨平台的应用程序编程规范 OpenGL包含一系列可以操作图形和图像的函数 但OpenGL没有实现这些函数 OpenGL仅规定每个函数应该如何执行以及其输出值 类似接
  • 若依开源框架登录扩展Springboot+security,密码、验证码多种登录

    自定义登录扩展类 继承DaoAuthenticationProvider类型 重写additionalAuthenticationChecks方法 CustomLoginAuthenticationProvider java package
  • 是面试官放水,还是公司实在是太缺人?这都没挂,华为原来这么容易进...

    华为是大企业 是不是很难进去啊 在华为做软件测试 能得到很好的发展吗 一进去就有9 5K 其实也没有想的那么难 直到现在 心情都还是无比激动 本人211非科班 之前在字节和腾讯实习过 这次其实没抱着什么特别大的希望投递 没想到华为可以再给我
  • 2023计算机毕业设计SSM最新选题之java个人微博网站1x930

    2023计算机毕业设计SSM最新选题之java个人微博网站1x930 做毕业设计一定要选好题目 毕设想简单 其实很简单 这里给几点建议 1 首先 学会收集整理 年年专业都一样 岁岁毕业人不同 很多人在做毕业设计的时候 都犯了一个错误 那就是
  • 数组理论干货

    数组 如果看文字麻烦就直接跳到最后一个代码块 Array 1 Java语言中的数组是一种引用数据类型 不属于基本数据类型 数组的父类是Object 2 数组实际上是一个容器 可以同时容纳多个元素 数组是一个数据的集合 数组 字面意思是 一组
  • JAVA输出一个爱心

    在 Java 中输出一个爱心可以使用字符画的方式 如下代码所示 public class LoveHeart public static void main String args System out println System out
  • discuz 论坛配置 QQ/163 网易邮箱

    步骤 在 discuz 后台的 站长 邮件设置 里按如下配置 SMTP 服务器固定 163 网易填 ssl smtp 163 com QQ 邮箱则填 ssl smtp qq com 端口 465 验证 勾选 配置完成后 会进行测试验证 发信
  • clamp 函数

    clamp 函数 返回范围内的一个数值 可以使用 clamp 函数将不断增加 减小或随机变化的数值限制在一系列的值中 float clamp float minnumber float maxnumber float parameter 最
  • 简单用用flex和bison

    简单用用flex和bison 参考博文 词法 语法分析器简介 定义 flex bison 文件间关系 具体例子 例1 输入name age时触发 输出name is age years old 例2 输入带有括号的满足乘法加法的表达式 输出