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

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

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

每个关键字有一个词法单元;一个关键字的模式就是该关键字本身;
表示运算符的词法单元;它可以表示单个运算符;或者像上图中的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;
}

编译工程——词法分析相关推荐

  1. 编译原理 词法分析 算符优先分析法

    编译原理 词法分析 算符优先分析法 实验目的 加深对语法分析器工作工程的理解,加强对算符优先分析法实现语法分析程序的掌握:能够采用一种编程语言实现简单的语法分析程序:能够使用自己辨析的分析程序对简单的 ...

  2. 【Binder 机制】AIDL 分析 ( 创建 AIDL 文件 | 创建 Parcelable 类 | AIDL 中使用 Parcelable 类 | 编译工程生成 AIDL 对应的Java源文件 )

    文章目录 一.创建 AIDL 文件 1.创建 AIDL 目录 2.创建 AIDL 文件 3.创建 Parcelable 类 4.AIDL 目录下声明 Parcelable 类 5.AIDL 中使用 P ...

  3. VC6命令行编译工程方法

    VC6命令行编译工程方法 昨天下载一个界面库wxWidgets,编译里面的库很痛苦,有好几个工程,我的在一边等待,痛苦!!于是上网找到命令行编译的方法,可以写一个批处理文件,呵呵,想起以前经理编译工程 ...

  4. Myeclipse编译工程用Weblogic发布的时候jdk版本问题

    最近在用Myeclipse编译的代码在Weblogic上发布的时候总是出错,调查发现原来的jdk版本的问题. 我的情况是这样的: 开发的时候我是用tomcat来发布测试的,没有任何问题,但是实际发布的 ...

  5. eclipse不能自动编译工程的解决方法

    任何时候看 Problems 找到正确原因,解决问题就没错的 =========================== eclipse出现不编译工程的现象.怎么刷新classes目录下也是空的. 具体都 ...

  6. 如何使用MDK建立STM32H7双核编译工程

    用MDK为多核的MCU创建编译工程要略比单核MCU复杂一些.本文以STM32H745双核MCU为例示范如何创建一个工程.其实,STM32CubeMX已支持自动生成MDK编译工程了,但其生成的文件树结构 ...

  7. automake编译工程

    automake编译工程 首先准备工程完整清晰的目录,cd到要生成makefile的主目录,下面将分布说明怎样用automake编译工程,所有的1,2,3,4,5,6都是在主目录操作的. 1 输入:& ...

  8. Signal Tap Logic Analyzer的stp文件制作及添加到编译工程

    1)在Quartus中打开Signal Tap Logic Analyzer 2)新建新的stp文件 3)如下为新的stp文件中,不同的观测模块命名,这样方便分模块debug抓取问题,如下为制作的CE ...

  9. makefile文件:编译工程文件

    其本质是用来编译工程文件(内部可以理解为g++命令),用make命令,实现自动化编译.格式如下: 目标:依赖 (tab) 命令 介绍makefile的最初级版本 当前目录下有 main.cpp tem ...

最新文章

  1. matlab中textread 函数
  2. C语言学习之输入一个大于三的值判断是否为素数
  3. 程序员最想要十八般武艺俱全的“保姆型”项目经理!
  4. echarts常用术语
  5. Mac IDEA 实用快捷键大全(持续更新)
  6. 浅谈防勒索病毒方案之主机加固
  7. tmap | 制作地图动画、放大局部区域
  8. Timer already cancelled 或者 Task already scheduled or cancelled
  9. 解除百度网盘下载限制的办法
  10. vue3+vite3+vant搭建移动端简易模版
  11. 网站建设视频教程,史上最精,最全。
  12. 邮箱服务之阿里云平台
  13. 【大数据分析】2021“秋招”毕业生求职的网络舆论关注度分析
  14. 未能加载文件或程序集“office, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c”或它的某一个依赖项。拒绝访问
  15. HI3861学习笔记(19)——WiFi接口使用(STA和AP模式)
  16. 3070网卡驱动linux教程,Ubuntu 12.04下RT5370无线网卡驱动安装
  17. 微机原理与接口技术 重点详解与章节总结——CPU架构详解
  18. linux 用户和工作组管理
  19. 爱德泰科普 | 单模光纤和多模光纤小知识
  20. a标签 rel属性

热门文章

  1. noi2016旷野大作战
  2. HRM- Fusion体验报告
  3. 0基础入门Linux运维,有哪些学习技巧?
  4. 学术资料账号密码全集汇总
  5. 刘昊威 尊重自己的体系——《Men’s Uno 风度》 2012年6月号
  6. 给小白的论文写作方法!实用率99%!
  7. ksxt问题集之——“请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理”
  8. mpos php,MPOS
  9. python-selenium登陆今日头条
  10. Opencv实现Sift、Surf、ORB特征提取与匹配