编译工程——词法分析

2023-11-07

词法分析主要是读入源程序的输入字符,区分成词素,生成词法单元序列,序列中的每个词法单元对应一个词素。此外,它还会完成其他的任务,如过滤掉源程序中的注释和空白(空格、换行符、制表符以及在输入中用于分割词法单元的其他字符);以及将编译器生成的错误消息与源程序中的位置联系起来。

在这里插入图片描述

词法单元:由词法单元名和可选的属性组成;词法单元名可以是特定关键词,或者是代表标识符、数字常量、字符常量等。
模式:一个词法单元的词素所具有的所有可能的形式;如果是词法单元是关键字,那就是这个关键字的字符序列;对于例如标识符,那么就是更复杂的结构,可以和很多字符串匹配。
词素:源程序的一个具体的字符序列,能够匹配某个模式,并被词法分析器识别。
在这里插入图片描述

在很多程序设计语言中,下面的类别覆盖了大部分或者所有的词法单元:

每个关键字有一个词法单元;一个关键字的模式就是该关键字本身;
表示运算符的词法单元;它可以表示单个运算符;或者像上图中的comparison一样表示一类运算符;
表示所有标识符的词法单元;
一个或多个表示常量的词法单元,比如数字和字面值字符串;
每一个标点符号有一个词法单元,比如左右括号、逗号和分号。
如果有多个词素可以和一个模式匹配,那么词法分析器必须向编译器的后续阶段提供有关匹配词素的附加信息。因此,词法分析器不仅向语法分析器返回一个词法单元名字,还会返回描述该词法单元的词素的属性值。词法单元的名字影响语法分析过程中的决定,而属性则会影响语法分析之后对词法单元的翻译。

一般来说,假设词法单元至多有一个相关的属性值,当然,这个属性值可以是一个组合了多种信息的结构化数据。譬如,和标识符相关的信息包括词素、类型、第一次出现的位置等,都是它的属性,并被保存在符号表中。因此,一个标识符的属性值是一个指向了符号表中该标识符对应条目的指针。

接下来,我们正式地定义一些概念,使用一些标记,研究一下正则表达式的形式化表示方法。正规式又称正则表达式, 是一种特殊的字符串用来描述一类的字符串的集合。我们把可用正规式描述(其结构)的语言称为正规语言或正规集。

字母表:符号的有限集合, 例: [公式] = { 0, 1}
串:符号的有穷序列,例:0110, [公式]
语言:字母表上的一个串集 {
[公式] , 0, 00, 000, …}, { [公式] }, [公式]
句子:属于语言的串

字母表(alphabet)是一个有限的符号集合,符号的典型例子包括字母、数字和标点符号。集合{0,1}就是一个字母表,这个字母表能够构成所有的二进制串。ASCII是字母表的重要例子,用于很多软件系统中。

在词法分析中,最重要的语言上运算是并、连接和闭包运算。下面是运算的正规定义:
在这里插入图片描述

在定义好正则表达式之后,怎么基于正则表达式构造代码来检查输入字符串,并返回和模式匹配的词素呢?

作为构造词法分析器的中间步骤,首先将模式转换为具有特定风格的流图,称为“状态转换图”。我们首先看看如何使用手工方法来构造,然后研究如何自动从正则表达式构造状态转换图。

状态转换图(transition diagram)有组称为状态(state)的节点或圆圈,图中还包括有向边,每条边上面有一个或多个标号,代表从一个状态转到另一个状态的条件。转换图的每一个状态代表了词法分析器扫描输入串的过程中可能遇到的情况。状态图的边从图的一个状态指向另一个状态,每条边的标号包括一个或多个符号。如果我们处于某个状态s,并且下一个输入符号是 a ,那么我们会去找一条从s状态离开并且标号为 a 的边。目前假设所有的状态都是确定的,也即对任何的给定状态和给定的符号,最多只有一条从该状态离开的边包括该符号。

关于状态转换图的重要约定如下:

某些状态被称为接收状态或者最终状态,这些状态表明已经找到了一个词素,可以有多个接收状态
如果相应的词素并不包括在最后一步使我们到达接受状态的符号,那么可以在该接受状态附近加一个*;意味着最后的这个字符需要另外处理,所以一般会回退;
有一个状态被指定为开始状态,也成为初始状态,该状态由一条没有出发节点的,标号为start的边指明。在读入任何输入符号之前,状态转换图总是处于开始状态。
下面的图是识别所有与词法单元relop的词素的状态转换图,识别关系运算符。
在这里插入图片描述

词法分析是编译器工作的第一步。词法分析负责将用户的代码从左至右依次读入,识别出来其中的词素(lexeme),并将词素映射为token(role)。一个token就是一个语法的类型,譬如自然语言中,有名词、动词、形容词等;对于编程语言而言,就是标识符、整型、关键字等。譬如说,position 被识别为一个词素,然后映射成标识符(identifier);if 被识别为一个词素,被映射为IF(关键字)。另外,有一些称作whitespace的词素识别出来之后就直接丢掉;有一些token有另外的属性,譬如词素的值,词素所在的行等信息。所以,这里我们主要做的事情就是定义好token,token定义好之后,可以通过一些现成的工具或者自己写代码分析来完成token的分析。

完成词法分析有很多现成的工具,称作lexer或者scanner。我们先看一个例子,使用flex。使用现成工具的好处是可以先集中考虑下词法到底怎么定义。如果定义好了词法,相应的代码也可以写出来。Flex手册中给出的定义如下:

Flex是一个生成扫描器的工具,能够识别文本中的词法模式。Flex
读入给定的输入文件,如果没有给定文件名的话,则从标准输入读取,从而获得一个关于需要生成的扫描器的描述。此描述叫做规则,由正则表达式和
C代码对组成。Flex 的输出是一个 C 代码文件——lex.yy.c——其中定义了yylex()
函数。编译输出文件可以生成一个可执行文件。当运行可执行文件的时候,它分析输入文件,为每一个正则表达式寻找匹配。当发现一个匹配时,它执行与此正则表达式相关的C代码。Flex
不是GNU工程,但是GNU为Flex 写了手册。

yylex()是由flex创建的扫描程序的入口点,调用yylex()启动或者重新开始扫描。Lex编写的yylex()从名为yyin的FILE *文件指针中读取字符。 如果未设置yyin,则默认为标准输入。 它输出到yyout,如果未设置默认为stdout。 还可以在yywrap()函数中修改yyin,该函数在文件末尾调用。 它允许打开另一个文件,并继续解析。如果是这种情况,将其返回0。如果要结束此文件的解析,将其返回1。一般来说,每次调用yylex()都会返回一个表示标记类型的整数值。

flex的语法和结构是什么样的呢?flex通过读取一个*.l或者*.lex文件里的单词匹配规则生成C语言的实现代码。一个*.l的文件里的结构大概如下,用%%分隔开来。

%{ /*declaration*/ %} 
/* Definition */
%%
/* Rules */ 
%%
/* C Code */

定义区包含一些简单的名字定义(name definitions)来简化词法扫描器(scanner)的规则,并且还有起始条件(start condition)的定义。
规则区包含了一系列具有pattern-action形式的规则,并且模式 pattern 位于行首不能缩进,action 也应该起始于同一行。
用户代码区 的代码将被原封不动地拷贝到输出文件中,并且这些代码通常会被扫描器调用,当然,该区是可选的,如果 Flex 源文件中不存在该区,那么可以省略第二个 “%%” 。
在这里插入图片描述
最近在联系写一个简单的编译器,借助flex&bison完成前端词法分析和语法分析
在这里插入图片描述
flex部分(没有完善)

%option yylineno
%{
#include<stdio.h>
#include<string.h>
#include"struct.hpp"
%}

 /*数字定义*/ 
DECIMAL [1-9][0-9]*
OCTAL 0[0-7]*
HEXDECIMAL 0(x|X)[0-9a-fA-F]+

/*标识符*/
IDENT [a-z_A-Z][a-z_A-Z0-9]*

PUTF "putf"
STARTTIME "starttime"
STOPTIME "stoptime"

EOL (\r\n|\n|\r)
WHITE [\t ]

/*单行注释*/
LINECOMMENT \/\/[^\n]*

BLOCKCOMMENT_BEGIN "/*"
BLOCKCOMMENT_ELE .
BLOCKCOMMENT_LINE (\r\n|\n|\r)
BLOCKCOMMENT_END "*/"

%x BLOCKCOMMENT

%%

{EOL} yylineno++;
{WHITE}
{LINECOMMENT}
{BLOCKCOMMENT_BEGIN} {BEGIN BLOCKCOMMENT;}
<BLOCKCOMMENT>{BLOCKCOMMENT_ELE} {}
<BLOCKCOMMENT>{BLOCKCOMMENT_LINE} {yylineno++;}
<BLOCKCOMMENT>{BLOCKCOMMENT_END} {BEGIN INITIAL;}
{WHITE} {}

";" {yylval.no = new GrammaNode(lineno, SEMI_, yytext);return SEMI;}
"," {yylval.no = new GrammaNode(lineno, COMM_,yytext);return COMM;}
"(" {yylval.no = new GrammaNode(lineno, RDBRAL_,yytext);return RDBRAL;}
")" {yylval.no = new GrammaNode(lineno, RDBRAR_,yytext);return RDBRAR;}
"[" {yylval.no = new GrammaNode(lineno, SQBRAL_,yytext);return SQBRAL;}
"]" {yylval.no = new GrammaNode(lineno, SQBRAR_,yytext);return SQBRAR;}
"{" {yylval.no = new GrammaNode(lineno, BRAL_,yytext);return BRAL;}
"}" {yylval.no = new GrammaNode(lineno, BRAR_,yytext);return BRAR;}
"=" {yylval.no = new GrammaNode(lineno, ASSIGN_,yytext);return ASSIGN;}


"+" {yylval.no = new GrammaNode(lineno, ADD_,yytext);return ADD;}
"-" {yylval.no = new GrammaNode(lineno, SUB_,yytext);return SUB;}
"/" {yylval.no = new GrammaNode(lineno, DIV_,yytext);return DIV;}
"*" {yylval.no = new GrammaNode(lineno, MUL_,yytext);return MUL;}
"%" {yylval.no = new GrammaNode(lineno, MOD_,yytext);return MOD;}
"==" {yylval.no = new GrammaNode(lineno, EQ_,yytext);return EQ;}
"!" {yylval.no = new GrammaNode(lineno, NOT_,yytext);return NOT;}
"!=" {yylval.no = new GrammaNode(lineno, NEQ_,yytext);return NEQ;}
"||" {yylval.no = new GrammaNode(lineno, OR_,yytext);return OR;}
"&&" {yylval.no = new GrammaNode(lineno, AND_,yytext);return AND;}
"<" {yylval.no = new GrammaNode(lineno, LT_,yytext);return LT;}
">" {yylval.no = new GrammaNode(lineno, BG_,yytext);return BG;}
"<=" {yylval.no = new GrammaNode(lineno, LQ_,yytext);return LQ;}
">=" {yylval.no = new GrammaNode(lineno, BQ_,yytext);return BQ;}


"const" {yylval.no = new GrammaNode(lineno, CONST_,yytext);return CONST;}
"int" { yylval.no = new GrammaNode(lineno, INT_,yytext);return INT;}
"void" { yylval.no = new GrammaNode(lineno, VOID_,yytext);return VOID;}
"if" {  yylval.no = new GrammaNode(lineno, IF_,yytext);return IF;}
"else" {  yylval.no = new GrammaNode(lineno, ELSE_,yytext);return ELSE;}
"while" { yylval.no = new GrammaNode(lineno, WHILE_,yytext);return WHILE;}
"break" {yylval.no = new GrammaNode(lineno, BREAK_,yytext);return BREAK;}
"continue" {yylval.no = new GrammaNode(lineno, CONTINUE_,yytext);return CONTINUE;}
"return" {yylval.no = new GrammaNode(lineno, RETURN_,yytext);return RETURN;}



{OCTAL}    {yylval.no = new GrammaNode(lineno, IntConst_O_,yytext);return IntConst_O;}
{DECIMAL} {yylval.no = new GrammaNode(lineno, IntConst_D_,yytext);return IntConst_D;}
{HEXDECIMAL} {yylval.no = new GrammaNode(lineno, IntConst_H_,yytext);return IntConst_H;}
{IDENT} {yylval.no = new GrammaNode(lineno, Ident_,yytext);return Ident;}

%%

int main(int argc, char* argv[]) {
    yylex();
    return 0;
}

int yywrap() {
    return 1;
}

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

编译工程——词法分析 的相关文章

  • perl regex是什么_如何在Perl单行代码中使用Regex匹配多行

    perl regex是什么 Perl one liners with perl s regular expression statement can be a very powerful text processing tools used
  • Shell中正则表达式

    1 正则表达式介绍 1 正则表达式 通常用于判断语句中 用来检查某一字符串是否满足某一格式 2 正则表达式是由普通字符与元字符组成 3 普通字符包括大小写字母 数字 标点符号及一些其他符号 4 元字符是指在正则表达式中具有特殊意义的专用字符
  • 三款记事本替代工具 哪个最好用?

    三款记事本替代工具 哪个最好用 http www sina com cn 2008年08月27日 08 35 IT168 com Windows操作系统中自带了不少的实用小程序 但是它们大都功能简陋 有时无法满足我们的使用 此外还有一些Wi
  • java用正则表达式脱敏手机号

    一种正则形式 在Java开发中有时候需要对敏感字段数据脱敏 废话不多说 直接上代码 脱敏手机号 param str return 脱敏后字符串 public static String maskPhone String str return
  • 用Matlab作函数的图像

    函数简介 1 作图函数是plot 其调用格式如下 plot y plot x y plot x y LineSpec plot x1 y1 s1 x2 y2 s2 x3 y3 s3 说明 1 plot y 绘出以向量y为纵坐标 y的个元素的
  • Python---正则表达式

    专栏 python 个人主页 HaiFan 专栏简介 Python在学 希望能够得到各位的支持 正则表达式 前言 概念 作用和特点 使用场景 正则符号 re模块 re compile match search span findall gr
  • 常用数字电路模块之三:计数器与分频器(二))

    三 分频电路 1 简单的计数器 计数器实质是对输入的驱动时钟进行计数 所以计数器在某种意义上讲 等同于对时钟进行分频 例如一个最大计数长度为N 2 n 从0计数到N 1 的计数器 也就是寄存器位数位n 那么寄存器最高位的输出为N 2 n分频
  • js验证邮箱的正则表达式

    最近小小研究了一下正则表达式 觉得写正则表达式还挺有意思的 先想推荐一个网址 把正则表达式的基本语法都总结了 很不错 https msdn microsoft com zh cn library ae5bf541 v vs 100 aspx
  • 正则表达式基础语法大全

    正则表达式基础语法 1 普通字符 字母 数字 汉子 下划线 以及没有特殊定义的标点符号 都是 普通字符 表达式中的普通字符 在匹配一个字符串的时候 匹配与之相同的一个字符 2 简单的转义字符 3 标准字符集合 能够与 多种字符 匹配的表达式
  • 前端开发中常用的校验处理

    前端开发中常用的校验处理 1 手机号码校验 2 身份证正则校验 3 必须输入中文 必须输入英文 4 其它正则校验 1 手机号码校验 function checkPhone var phone document getElementById
  • 人人都看得懂的正则表达式教程

    编写验证规则最流行和最简单的方法就是正则表达式了 但唯一的一个问题是正则表达式的语法太隐晦了 让人蛋疼无比 很多开发者为了在项目中应用复杂的验证 经常要使用一些小抄来记住正则式的复杂语法和各种常用命令 在这篇文章中 我将试图让大家明白什么是
  • 截取oracle字符串中的数字(转载)

    截取oracle字符串中的数字 云淡风轻博客 博客园 cnblogs com 方法一 如果Oracle版本不是太低的话 使用 正则表达式函数 REGEXP SUBSTR 处理 REGEXP SUBSTR有5个参数 分别是 第一个是输入的字符
  • 常用js

    1 去掉字符串两端的空格 对字符串去两端空格 function stringTrim str if str null str undefined return null 用正则表达式将前后空格 用空字符串替代 return str repl
  • SparkSQL HiveSQL 常用正则表达式

    SparkSQL HiveSQL 常用正则表达式 目录 SparkSQL HiveSQL 常用正则表达式 1 匹配汉字 2 匹配手机号码 3 匹配身份证 4 SparkSQL HiveSQL 常用正则函数 5 SparkSQL 分组 聚合
  • Java的replaceAll()方法

    replaceAll 方法实际是采用正则表达式的规则去匹配的 在regex中 表示一个 在java中一个 也要用 表示 这样 前一个 代表regex中的 后一个 代表java中的 所以字符串转义一次 正则转义一次 那么一个斜扛要写4个 1
  • 对字符串进行正则取子串

    题目是这样的 对一段HTML网页内容 解析出其中所有的键值对 比如其中type text type为属性 text为值 二者为一个键值对 内容如下
  • Python3 如何优雅地使用正则表达式(详解五)

    非捕获组命名组 精心设计的正则表达式可能会划分很多组 这些组不仅可以匹配相关的子串 还能够对正则表达式本身进行分组和结构化 在复杂的正则表达式中 由于有太多的组 因此通过组的序号来跟踪和使用会变得困难 有两个新的功能可以帮你解决这个问题 非
  • 知道这20个正则表达式,能让你少写1,000行代码

    正则表达式 一个十分古老而又强大的文本处理工具 仅仅用一段非常简短的表达式语句 便能够快速实现一个非常复杂的业务逻辑 熟练地掌握正则表达式的话 能够使你的开发效率得到极大的提升 正则表达式经常被用于字段或任意字符串的校验 如下面这段校验基本
  • Python命令行参数定义及注意事项

    在命令行中运行python代码是很常见的 下面介绍如何定义命令后面跟的参数 常规用法 Python代码中主要使用下面几行代码来定义并获取需要在命令行中赋值的参数 import argparse parser argparse Argumen
  • 天梯赛字符串替换题 “ 6翻了” Python 正则表达式替换

    输入格式 输入在一行中给出一句话 即一个非空字符串 由不超过 1000 个英文字母 数字和空格组成 以回车结束 输出格式 从左到右扫描输入的句子 如果句子中有超过 3 个连续的 6 则将这串连续的 6 替换成 9 但如果有超过 9 个连续的

随机推荐

  • 浅谈项目售前调研

    一 概述 说到软件项目的售前调研工作 可能还得先谈谈售前顾问这个重要的角色 在IT软件行业 售前顾问位于职业金字塔顶端 是项目开发 实施人员与销售人员间的纽带和桥梁 在销售人员眼中 售前顾问扮演的是技术专家的角色 而在项目实施和开发人员眼中
  • 中兴网络设备交换机路由器查看日志命令方法

    描述 中兴网络设备交换机路由器查看日志命令方法 命令 show logfile
  • 小程序如何实现本地去水印

    自媒体时代 很多人都进行伪原创 但是有些视频本身就有水印的 这个时候我们怎么办 很多人都不懂这个方法 所以导致很多人不会使用 一般都是电脑操作 那我们就没有办法了吗 今天介绍的就是小程序如何实现本地去水印 我们也知道FFmpeg命令 去掉视
  • 波形分析软件 android,新版 PicoScope 软件提供更出色的波形分析和功能 – 免费获取!...

    全球领先的 PC 示波器制造商 Pico Technology Ltd 发布了 PicoScope 软件 6 11 7 版 此版本的 PicoScope 可为研发新一代电气和电子技术的工程师 科学家 技术人员和研究人员提供重要的新功能 本文
  • wifi感知---csi技术

    CSI在WiFi研究领域指Channel State Information 也就是通过接收到的WiFi信号来估计WiFi信号的传播信道长什么样子 它表征了一系列影响的综合 例如散射 衰落 能量随着距离的衰减 目前人们可以从CSI里提取到很
  • C#文件读写小案例

    目录 1 驱动器管理类 2 目录管理类 3 文件管理类 4 路径管理类 5 FileStream类读取文件 6 StreamReader类读取文件 7 使用FileStream类写入文件 用FileStream类写入文件可以指定要写入的位置
  • 逐个版本分析鬼火引擎

    这段时间做手游的cocos2dx的学习 和做web开发的项目 感觉很没劲 还是得研究引擎 我看到有个人的博客直接分析鬼火引擎0 1版本 这个方法不错 两万行左右代码 sourceforge里面有各个版本的代码 这样 正好可以循序渐进地进行
  • Maven导包及打包

    Maven是什么 Maven是一个跨平台的项目管理工具 作为Apache组织的一个颇为成功的开源项目 其主要服务于基于Java平台的项目创建 依赖管理和项目信息管理 是一个自动化构建工具 maven是Apache的顶级项目 解释为 专家 内
  • org.springframework.web.bind.annotation 注解详解

    处理request RequestBody RequestHeader RequestMapping RequestParam RequestPart CookieValue PathVariable 传送门 处理response Resp
  • Python算法教程:强连通分量

    强连通分量 strongly connected components SCCs 是一个能让有向路径上所有节点彼此到达的最大子图 Kosaraju的查找强连通分量算法 def strongly connected components gr
  • Windows下 VS2015编译RocksDB

    Windows下 VS2015编译RocksDB VS2015编译RocksDB RocksDB 是一个来自 facebook 的可嵌入式的支持持久化的 key value 存储系统 也可作为 C S 模式下的存储数据库 但主要目的还是嵌入
  • unity Input.GetAxis()函数

    开发手册上有相关解释 但说得很不清楚 看完也不懂 下面给出详细的解释 根据输入设备 参数分为两类 一 触屏类 1 Mouse X 鼠标沿屏幕X移动时触发 2 Mouse Y 鼠标沿屏幕Y移动时触发 3 Mouse ScrollWheel 鼠
  • Kaldi语音识别学习记录-----编译安装

    语音识别领域的开源框架有CMUSphinx HTK Kaldi等等 而目前仍然比较活跃 且工程价值较高的就数Kaldi 很多从事语音方面的公司 都使用该框架训练自己的语音识别能力 由于其内部代码逻辑较为复杂 故这里一步一步来解读 了解语音识
  • Git的安装下载基本操作与使用,git上传远程仓库gitee配置操作流程,git一站式教程

    目录 一 git的安装 gitee官网直通车 git官网 git安装流程 二 git配置与提交giee远程仓库操作方法与命令 三 本地项目导入仓库 分支操作 拷贝远程仓库 最常用 比如下载别人的仓库代码 一 git的安装 gitee官网直通
  • 2020美赛F题

    2020美赛F题 待补充 先来翻译 翻译最好用谷歌翻译 别问 问就是谷歌 研究人员确定了几个岛国 例如马尔代夫 图瓦卢 基里巴斯和 由于海平面上升 马绍尔群岛有可能完全消失 什么 岛国的土地消失后 岛上的人口会发生什么事情或应该发生什么事情
  • 对meta标签的再次认识

    META标签用来描述一个HTML网页文档的属性 例如作者 日期和时间 网页描述 关键词 页面刷新等 指定字符集 向搜索引擎说明网页的关键词 告诉搜索引擎你的站点的主要内容 告诉搜索引擎你的站点的制作的作者 响应式页面
  • 【stm32】手把手用cubemx配置血氧传感器(MAX30102)

    一 前言 网上流传血氧传感器的代码有好几个版本 听说这个不准 那个不准的 突然间我看到了一篇好文章 大概是自己用软件测试测量结果是否准确 秀的我头皮发麻呀 外部中断触发 本文将通过他的例程来手把手教大家如何配置 本文适合小白 只讲如何应用
  • c语言 请求页式存储管理,操作系统-页式内存管理

    页式内存管理上 A 段式内存管理 1 指的是一段连续的内存空间 2 段式内存管理 程序的各个部分相对独立 数据段 代码段 早期x86处理器无法通过一个寄存器访问所有内存单元 解决早期程序运行的重定位问题 段式内存管理的应用 在x86系列的处
  • STM32F767ZI-NUCLEO移植运行micropython过程记录

    注意 本教程移植microPython是通过烧写hex文件实现的 网上其他教程很多是介绍使用USB DFU方式 设备boot0至高电平 通过DfuSeDemo烧写 由于自己还不熟没有使用这种方式 后续有时间再尝试 另外本教程是基于STM32
  • 编译工程——词法分析

    词法分析主要是读入源程序的输入字符 区分成词素 生成词法单元序列 序列中的每个词法单元对应一个词素 此外 它还会完成其他的任务 如过滤掉源程序中的注释和空白 空格 换行符 制表符以及在输入中用于分割词法单元的其他字符 以及将编译器生成的错误