词法与语法分析器介绍

概述

词法和语法可以使用正则表达式和BNF范式表达,而最终描述文法含义的事状态转换图

Lex与YACC

词法分析器Lex

词法分析词Lex,是一种生成词法分析的工具,描述器是识别文本中词汇模式的程序,这些词汇模式是在特殊的句子结构中定义的

Lex 接收到文件或文本形式的输入时,会将文本与常规表达式进行匹配:一次读入一个输入字符,直到找到一个匹配的模式

如果能够找到一个匹配的模式,Lex就执行相关的动作(比如返回一个标记Token)。另外,如果没有可以匹配的常规表达式,将会停止停止进一步的处理,Lex将显示一个错误信息

Lex 和 C语言是强耦合的,一个.lex文件通过Lex解析并生成C的输出文件,这些文件被编译为词法分析器的可执行版本

lex 或 .l 文件在格式上分为以下3段

    1. 全局变量声明部分
    1. 词法规则部分
    1. 函数定义部分

Lex 变量表

变量名详细解释
yyinFILE* 类型。它指向lexer 正在解析的当前文件
yyoutFILE* 类型。它指向记录lexer输出的位置。默认情况下,yyin 和 yyout 都指向标准输入和输出
yytext匹配模式的文本存储在这一变量中(char*)
yyleng给出匹配模式的长度
yylineno提供当前的行数信息(lexer不一定支持)

Lex 函数表

函数名详细解释
yylex()这一函数开始分析。它由Lex自动生成
yywrap()这一函数在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析。它可以用来解析多个文件
yyless(int)这一函数可以用来输出除来前n个字符外的所有读出标记
yymore()这一函数告诉Lexer将下一个标记附加到当前标记后

代码

文件 a.l

%{
#include <stdio.h>
extern char *yytext;
extern FILE *yyin;
int count = 0;
%}%%// 两个百分号标记指出了 Lex 程序中这一段的结束和第二段的开始
\$[a-zA-Z][a-zA-Z0-9]*  {count++; printf("  变量%s", yytext);}
[0-9\/.-]+  printf("数字%s", yytext);
=           printf("被赋值为");
\n          printf("\n");
[ \t]+      /* 忽略空格 */;
%%// 函数定义部分
int main(int avgs, char *avgr[]) {yyin = fopen(avgr[1], "r");if (!yyin) {return 0;}yylex();printf("变量总数为:%d\n", count);fclose(yyin);return 1;
}

对于以上代码,解释如下

  • 全局变量声明部分: 声明了一个int型全局变量count,用来记录变量的个数
  • 规则部分: 第1个规则是找 符号开头、第 2 个符号为字母且后面为字符或数字的变量,类似于 符号开头、第2个符号为字母且后面为字符或数字的变量,类似于 符号开头、第2个符号为字母且后面为字符或数字的变量,类似于a,并计数加1. 同时,将满足条件的yytext输出;第2个规则是找数字;第3个规则是找"=" 号;第4个规则是输出"\n"; 第5个规则是忽略空格
  • 函数定义部分: 打开一个文件,然后调用yylex 函数进行词法解析,输出变量的技术,最后调用fclose关闭文件

lex 代码编译

lex a.l
gcc lex.yy.c -o test -ll

测试文件 file

$a = 1
$b = 2

执行如下命令

./test file
变量$a被赋值为数字1
变量$b被赋值为数字2
变量总数为:2

语法分析词YACC

YACC(Yet Another Compiler-Compiler) 是 UNIX/Linux 上一个用来生成编译器的编译器(编译器代码生成器). YACC使用BNF范式定义语法,能处理上下文无关文法

YACC 语法规则

YACC 语法包括3部分,即定义段、规则段和用户代码段

… 定义段 …
%%
… 规则段 …
%%
… 用户代码段 …

代码

词法分析文件: cal.l

%{
#include "y.tab.h"
#include <math.h>
%}%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {yylval.dval = atof(yytext);return NUMBER;
}
[ \t]   ;
\n |
. return yytext[0];
%%

语法分析文件: calc.y

%{
#include <stdio.h>
#include <string.h>
#include <math.h>
int yylex(void);
void yyerror(char *);
%}%union {double dval;
}
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression
%%
statement_list: statement '\n'| statement_list statement '\n';statement: expression { printf("= %g\n", $1); };expression: expression '+' expression {$$ = $1 + $3;}|   expression '-' expression { $$ = $1 - $3; }|   expression '*' expression { $$ = $1 * $3; }|   expression '/' expression{if ($3 == 0.0)yyerror("divide by zero");else$$ = $1 / $3;}|   '-' expression %prec UMINUS { $$ = -$2; }|   '(' expression ')' { $$ = $2; }|   NUMBER { $$ = $1; }
%%void yyerror(char *str) {fprintf(stderr, "error:%s\n", str);
}int yywrap() { return 1;
}int main() {yyparse();
}

从代码中可以看出,规则部分使用BNF范式

  • expression 最终是NUMBER,以及使用+、-、* 、/ 和 ()的组合,对加、减、乘、除、括号、负号进行表达
  • statement 是由expression 组合而成的,可以输出计算结果
  • statement 是由expression 组合而成的,可以输出计算结果
  • statement_list 是statement的组合

lex 编译

lex cal.l
  • 通过这个命令会生成lex.yy.c,里面维护了NUMBER这个Token的有穷自动机

使用 YACC对 calc.y

yacc -d calc.y
  • 会生成y.tab.c、y.tab.h

最终执行结果

gcc -o calc y.tab.c lex.yy.c
./calc1+2= 33+6= 9

Re2C 与 Bison

词法分析器 Re2c

Re2c 是一个词法编译器,可以将符合Re2c规范的生成高效的C/C++代码,Re2c会将正则表达式生成对应的有穷状态机

代码

num.l

#include <stdio.h>
enum num_t {ERR, DEC};static num_t lex(const char *YYCURSOR)
{const char *YYMARKER;/*!re2cre2c:define:YYCTYPE = char;re2c:yyfill:enable = 0;end = "\x00";dec = [1-9][0-9]*;*   {return ERR;}dec end {return DEC;}*/
}int main(int argc, char **argv)
{for (int i = 1; i < argc; ++i) {switch (lex(argv[i])) {case ERR: printf("error\n"); break;case DEC: printf("十进制表示\n"); break;}}return 0;
}

执行以下命令,转换成c代码

re2c num.l -o num.c

num.c

/* Generated by re2c 3.0 on Sun May 26 21:58:19 2024 */
#line 1 "num.l"
#include <stdio.h>
enum num_t {ERR, DEC};static num_t lex(const char *YYCURSOR)
{const char *YYMARKER;#line 11 "num.c"
{char yych;yych = *YYCURSOR;switch (yych) {case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': goto yy3;default: goto yy1;}
yy1:++YYCURSOR;
yy2:
#line 14 "num.l"{return ERR;}
#line 32 "num.c"
yy3:yych = *(YYMARKER = ++YYCURSOR);switch (yych) {case 0x00: goto yy4;case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': goto yy5;default: goto yy2;}
yy4:++YYCURSOR;
#line 15 "num.l"{return DEC;}
#line 53 "num.c"
yy5:yych = *++YYCURSOR;switch (yych) {case 0x00: goto yy4;case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': goto yy5;default: goto yy6;}
yy6:YYCURSOR = YYMARKER;goto yy2;
}
#line 16 "num.l"}int main(int argc, char **argv)
{for (int i = 1; i < argc; ++i) {switch (lex(argv[i])) {case ERR: printf("error\n"); break;case DEC: printf("十进制表示\n"); break;}}return 0;
}

从上面代码中可以看出,这个状态机一共有8种状态,分别是开始状态、yy3至yy9状态,其中yy3状态是错误输出,返回ERR

yy5 状态是对应的正则匹配状态,返回DEC

在这里插入图片描述

YYCURSOR 是指向输入的指针,根据状态的流转,指针加1

语法编译器Bison

对于一条BNF文法规则,其左边是一个非终结符(symbol 或者 non-terminal),其右边则定义该非终结符是如何构成的,也称为产生式(production)

产生式可能包含非终结符,也可能包含终结符(terminal),还可能二者都有

利用BNF文来分析目标文本,比较流行的算法有LL分析(自顶向下的分析,top-down parsing),LR分析(自底向上的分析,bottom-up parsing;或者叫移进-归约分析, shift-down parsing)

其中LR算法有很多不同的变种,按照复杂度和能力递增的顺序依次是LR(0)、SLR、SLR、LALR和LR(1)

Bison是基于LALR分析法实现的,适合上下文无关文法

当Bison读入一个终结符TOKEN时,会将该终结符及其语意值一起压榨,其中这个栈叫做分析器栈(parse stack)

把一个TOKEN压入栈叫作移进。举个例子,对于计算1+2 * 3, 假设现已经读入来1 + 2 * ,那么下一个准备读入的是3,这个栈当前就有4个元素,即1、+、2和*

当已经移进的后n个终结符和组(grouping)与一个文法规则相匹配时,它们会根据该规则结合起来,这叫归约(reduction)

栈中哪些终结符和组会被单个的组(grouping)替换。同样,以1 + 2 * 3;为例,最后一个输入的字符为分号,表示结束,那么按照下面的规则进行归约:

expression '*' expression { $$ = $1 * $3 }

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

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

相关文章

二叉树的实现(递归实现)

前言&#xff1a;本文讲解通过递归的方式实现二叉树的一些基本接口。 目录 通过左右子树的方式实现二叉树&#xff1a; 二叉树的遍历&#xff1a; 求二叉树结点的个数&#xff1a; 二叉树所有节点的个数&#xff1a; 二叉树叶子节点的个数&#xff1a; 求第k层节点的节点…

4,八种GPIO模式

资料来源:【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 【STM32基础学习】八种GPIO模式总结-云社区-华为云 (huaweicloud.com) 仅作个人自学笔记&#xff0c;如有冒犯&#xf…

电子阅览室解决方案

一.方案概述 “电子阅览室”概念一经提出&#xff0c;就得到了广泛的关注&#xff0c;纷纷组织力量进行探讨、研究和开发&#xff0c;进行各种模型的试验。随着数字地球概念、技术、应用领域的发展&#xff0c;电子阅览室已成为数字地球家庭的成员&#xff0c;为信息高速公路提…

uniCloud云存储uni-cdn七牛云扩展存储-开发uniapp项目节约开发成本

为什么要使用uniCloud的扩展存储&#xff0c;那就是省钱&#xff0c;而且DCloud也一直在推uni-cdn&#xff0c;我在项目中也使用七牛云的扩展存储&#xff0c;确实是省钱&#xff0c;如果你的项目使用到大量的图片后者音视频&#xff0c;这些的算计可以帮你省不少钱。下面就通过…

【状态机动态规划】3129. 找出所有稳定的二进制数组 I

本文涉及知识点 动态规划汇总 LeetCode 3129. 找出所有稳定的二进制数组 I 给你 3 个正整数 zero &#xff0c;one 和 limit 。 一个 二进制数组 arr 如果满足以下条件&#xff0c;那么我们称它是 稳定的 &#xff1a; 0 在 arr 中出现次数 恰好 为 zero 。 1 在 arr 中出现…

dp背包问题

英雄联盟游戏中新出n个英雄&#xff0c;用长度为n的教组 costs 表示每个英雄的定价&#xff0c;其中 costs[i]表示第i个英雄的点券价格。假如你一共有coins点券可以用于消费&#xff0c;且想要买尽可能多的英雄并日选择英雄按costs[i]给出顺序获取。给你价格数组 costs 和金币量…

Golang | Leetcode Golang题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; func connect(root *Node) *Node {if root nil {return root}// 每次循环从该层的最左侧节点开始for leftmost : root; leftmost.Left ! nil; leftmost leftmost.Left {// 通过 Next 遍历这一层节点&#xff0c;为下一层的节点更新 Next …

vue3 uni-app 中小程序实现 底部tabbar 中间凸起部分 或者说自定义底部tabbar [保姆级别教程]

1、先来看一下效果 2、代码实现 我们还是在 pages.json 中正常配置我们底部的tabbar 但是需要 添加一个字段 "custom": true, //开启自定义tabBar 不填每次原来的tabbar在重新加载时都回闪现 3、 在 pages同一级 或者 里面创建一个 子组件 用来放我们的模版 4、 …

MPLS原理与配置

1.MPLS概述 &#xff08;1&#xff09;传统IP路由转发 &#xff08;2&#xff09;MPLS基本概念 ⦁ MPLS起源于IPv4&#xff08;Internet Protocol version 4&#xff09;&#xff0c;其核心技术可扩展到多种网络协议&#xff0c;包括IPv6&#xff08;Internet Protocol ver…

单片机的内存映射和重映射

内存映射 在单片机内&#xff0c;不管是RAM还是ROM还是寄存器&#xff0c;他们都是真实存在的物理存储器&#xff0c;为了方便操作&#xff0c;单片机会给每一个存储单元分配地址&#xff0c;这就叫做内存映射。 单片机的内存映射是指将外部设备或外部存储器映射到单片…

【软件设计师】——5.数据库系统

目录 5.1 基本概念 5.2 三级模式两级映射 5.3 设计过程和数据模型 5.4 关系代数 5.5 完整性约束 5.6 规范化和反规范化 5.7 控制功能 5.8 SQL语言 5.9 数据库安全 5.10 数据备份 5.11 数据库故障与恢复 5.12 数据仓库、数据挖掘和大数据 5.1 基本概念 相关术语 候选…

三分钟“手撕”顺序表与ArrayList

前言&#xff1a; 实现顺序表的代码放开头&#xff0c;供大家更好的查阅&#xff0c;每个方法都有代码的实现。 其次我会讲解Java自带的ArrayList的实例&#xff0c;扩容机制ArrayList使用方法&#xff0c;遍历以及它的优缺点。 目录 一、自己实现的顺序表 二、Java的ArrayLi…

Python | Leetcode Python题解之第115题不同的子序列

题目&#xff1a; 题解&#xff1a; class Solution:def numDistinct(self, s: str, t: str) -> int:m, n len(s), len(t)if m < n:return 0dp [[0] * (n 1) for _ in range(m 1)]for i in range(m 1):dp[i][n] 1for i in range(m - 1, -1, -1):for j in range(n …

Docker快速安装SQL Server 2022

说明&#xff1a; 系统&#xff1a;Ubuntu 24.04 LTS 拉取SQL Server Docker镜像 docker pull mcr.microsoft.com/mssql/server:2022-CU12-ubuntu-22.04创建数据目录 sudo mkdir /var/mssql_data sudo chmod 777 /var/mssql_data说明&#xff1a; 权限设置为777&#xff0…

ubuntu下vscode的安装包

1.引言 ubuntu下面安装vscode&#xff0c;编码调用接口时可以跳到接口api的位置&#xff0c;因此在ubuntu下安装vscode还是非常有意义的。 2.下载地址 链接&#xff1a;https://pan.baidu.com/s/1j3XNmvbL574p_hYeBqO6dg?pwdryr7 提取码&#xff1a;ryr7 --来自百度网盘超…

如何应对触摸一体机触摸屏失灵问题?怎么校准?

触摸一体机是一种功能强大的设备&#xff0c;集成了电脑、电视和触摸屏等多种功能。其中&#xff0c;触摸屏是其重要组成部分之一。然而&#xff0c;当触摸屏突然失灵时&#xff0c;我们该如何应对呢&#xff1f;以下是一些建议&#xff0c;以帮助您排除问题并重新获得正常触摸…

2024 年你应该选择哪个开源大模型?

自2017年发表的论文《Attention Is All You Need》发明了Transformer架构以来&#xff0c;自然语言处理&#xff08;NLP&#xff09;取得了巨大的进展。随着2022年11月ChatGPT的发布&#xff0c;大型语言模型&#xff08;LLM&#xff09;引起了广泛关注。 你是否想在自己的用例…

通过Zerossl给IP申请免费SSL证书, 实现https ip访问

参考通过Zerossl给IP申请免费SSL证书 | LogDicthttps://www.logdict.com/archives/tong-guo-zerosslgei-ipshen-qing-mian-fei-sslzheng-shu

授权调用: 介绍 Transformers 智能体 2.0

简要概述 我们推出了 Transformers 智能体 2.0&#xff01; ⇒ &#x1f381; 在现有智能体类型的基础上&#xff0c;我们新增了两种能够 根据历史观察解决复杂任务的智能体。 ⇒ &#x1f4a1; 我们致力于让代码 清晰、模块化&#xff0c;并确保最终提示和工具等通用属性透明化…

关于指针和数组的一些经典笔试题解析

前言 大家好&#xff0c;本篇博客将为大家展示一些曾经考过的关于指针的经典笔试题&#xff0c;里面有些题目的难度还是不小的&#xff0c;所以希望大家可以认真理解&#xff1b;如果你点开了本篇博客&#xff0c;麻烦各位大佬一键三连&#xff0c;多多支持&#xff0c;感谢&a…