词法分析,是编译器的第一个模块,也是最简单的模块。

最简单,指的是相对于编译器这种大型程序而言,与一般的代码相比还是有点复杂的。

关于词法分析的简介,可以看之前的文章:

词法分析器的简单思路

按照通常的C代码惯例,前缀暂时设置为scf,Simple Compiler Framework,简单编译器框架。

它所需的基本数据结构,就是动态字符串和双向链表,前两篇文章已经做了简单的代码介绍。

用C语言写个动态字符串

用C语言实现Linux风格的双向链表

首先,需要定义一个枚举类型,说明词法分析要支持的单词类型,即各种运算符、常量、标识符。

因为是从C和C++中抽取了容易实现的那部分语法,单词类型还是很多的,见如下几张图片:

第一张图,是各种运算符的类型定义,与流行的编程语言基本一样。

各种运算符

第二张,大小括号、分号、逗号、冒号等语法标示符号,箭头、点号等运算符。

箭头->,一般表示指针。点号,表示取类的成员。

三个点号,表示函数的动态参数,例如printf(const char* fmt, ...)在词法分析时就会用到。

空格,space,在词法分析时作为分隔符之一。它是不需要传递到语法分析阶段的,用完之后需要忽略掉。

EOF,表示源代码文件的结尾,fgetc()之类的函数在文件结束时会返回这个值。把它也作为一个单词,用于提示词法分析过程的结束。

括号之类的也算运算符

第三张,主要是用于代码流程控制的关键字。

流程控制的关键字

第四张,是用于类型定义的关键字。

数据类型的关键字

第五张,也是这个枚举的最后一部分,是常量和标识符。

其他

定义完了这个枚举,就可以定义单词的数据结构,如下图:

list,用于把它挂载到词法分析器的链表上,按照先进先出顺序(FIFO),以备后续的语法分析时读取。

type,填写为上面那个枚举的其中之一,用于表示这个单词的类型。

data,用于存储常量的值,可以是常量数字或者常量字符串。

text,用于存储单词的原始文本,即这个词的源代码。

file、line、pos,用于存储单词所在的源码文件名,行号,行内的位置,用于打印错误信息。

接下来定义几个与单词相关的函数,alloc()、clone()、free(),等等。

然后在C文件里实现这三个函数。

最后,定义词法分析器(lexer)的数据结构,scf_lex_t。

scf_lex_key_word_t,用于定义关键字的源码字符串,和关键字对应的单词类型。

在一门编程语言中,关键字是固定的。

如果在词法分析时获得的字符串,能在关键字列表里查到,那么它就是个关键字。

关键字列表,就是scf_lex_key_word_t类型的数组。

scf_lex_escape_char_t,用于转义字符,一般是\r \n \t \0这4个,以\表示接下来的一个字符是转义字符。

如果能在转义字符列表里查到,要把这两个字符转化为对应的转义ascii码。例如'\n'要转化为一个字节的整数10。

转义字符列表,就是scf_lex_escape_char_t类型的数组。

scf_lex_error_t,用于存储错误信息。因为不一定发现一个错误就立即终止分析(打印错误信息),有可能积累到一定的数量之后再一起打印出来,所以它用于保存错误信息。

scf_lex_char_t,用于表示从源码文件里读取的一个字符。

词法分析,是一个字符一个字符的读取。

如果发现当前字符是下一个单词的,那么就要把它放回去,等分析下一个单词时再把它取出来,所以需要一个链表挂载暂时被放回的单词。

不能真把它写回源码文件再读出来,fgetc()之后文件指针已经变了,再写回去会覆盖掉下一个字符的。把它挂载一个链表上,优先从链表读,链表为空的时候再读源文件,是比较好的处理方法。

从当前单词的末尾字符多查看一个字符(即下一个单词的第一个字符),才能确定当前单词的终止位置,这叫LL1下降分析。这个多查看的字符,也叫终止符。

(这里我不是故意黑龙书,而是就这么点事,它居然真写的云山雾罩,让人一头雾水,读不下去。)

scf_lex_t,词法分析器的数据结构,它有open()、close()、push_word()、pop_word()这4个函数。

其中push_word()和pop_word()是给语法分析器(parser)用的,语法分析时也存在LL1下降分析的场景,push_word()用于放回一个单词。

word_list_head,用于存储语法分析时这个临时被放回的单词,与词法分析的场景类似。

error_list_head,用于存储还没有打印的错误信息。

char_list_head,用于存储临时被放回的字符。

FILE* fp,源码文件的文件指针,从它这里读取字符。

nb_identities,标识符的个数。随着分析过程这个值是不断增加的,要根据它的值给获取的每一个标识符编不同的号码。

所有数据结构的定义说完了,接下来是代码实现。

scf_lex_open()的path是源码文件的路径,二级指针plex用于返回创建的词法分析器。

open、close、push_word,都比较简单,真正的词法分析在pop_word()函数里。

语法分析器(parser)在需要一个单词时,就调用词法分析器(lexer)的pop_word()函数,获取下一个单词。

之所以这么设计,而不是先完成整个源码文件的词法分析,是为了减少单词的库存,毕竟分析完的单词也是要占内存的。如果源码文件比较大,万一内存不够了就尴尬了,所以采取现使用现生产的JIT模式,暂时用不着的单词就让它在源码文件里存着。

在具体的词法分析之前,先准备关键字列表和转义字符列表。

更改这个关键字数组,就可以给词法分析器添加关键字。

转义字符暂时设置了4个,也可以更改这个数组添加。

下图是两个查找函数,用于查找关键字和转义字符。

接下来,是词法分析的核心函数,scf_lex_pop_word(),它声明是:

int scf_lex_pop_word(scf_lext_t* lex, scf_lex_word_t** pword);

二级指针用于返回单词,返回值用于返回错误码,成功返回0,失败返回一个负数(一般是-1)。

pop_word1

它被语法分析器调用(也可以直接被main函数调用,只做词法分析),所以在单词链表不空的情况下(有被语法分析器放回的单词),优先返回链表头部的单词。

在单词链表空的情况下,调用_lex_pop_char()获取一个字符,开始新单词的分析。

如果是EOF,源码已经读完了,就返回一个EOF单词,获得这个词之后语法分析就会结束,同时词法分析也结束了。

如果是\n \r \t和空格,一律当作空格丢弃,但是\n时要更新行号,其他字符要更新列号。

_lex_space()函数会丢弃所有被当作空格的字符。它包含一个递归调用,用于处理连续的空格。如果当前字符不被当作空格,它就调用_lex_push_char(),把它放回到字符链表上。

丢弃完空格之后,继续分析下一个单词。

接着图片pop_word1的代码926行,如下图的pop_word2。

pop_word2

它分析+ - * / %开头的运算符,例如+ += ++,- -= --,* *=,/ /=,% %=这些运算符。

图pop_word3主要是一些单个字符的特殊运算符,各种括号,逗号,冒号,分号。

pop_word3

pop_word4,是以& | ! =开头的运算符,例如& && &=,| || |=,= ==。

pop_word4

最后一张,图pop_word5。

pop_word5

前几行是分析以>和 >> >= >>=,< << <= <<=。

然后是点号,它可能是1个点或者3个点,分别表示类的成员和函数的可变参数。

以'开始的是字符,以"开始的是字符串,它们都需要做转义字符分析。在词法分析时,它们都是起始字符和终止字符,用于标示字符和字符串的起始和终止。

转义字符在源代码里实际是2个字符,要转义成1个。

字符串,可以是包含或者不包含转义字符的多个字符。

0到9开头的是常量数字,即使是16进制以0x开头,8进制以0开头,开始也是0。

10进制的开头0到9都可能。

暂时不支持直接以点号开头的浮点数,所以0.5必须写成0.5,不能写成.5,否则报词法错误。

标识符,以下划线_或者a-z或者A-Z开头,中间可以包含数字。

总体结构说完了,里面的一些具体分析的函数,接下来几篇再说。

最后还是推荐下龙书,编译原理,因为编译器领域的著作比较少,另外两本虎书和鲸书更难读。

举报/反馈

编译原理c语言词法分析器,用C语言实现一个真正的词法分析器相关推荐

  1. 编译原理学习笔记2——高级程序设计语言概述

    编译原理学习笔记2--高级程序设计语言概述 2.1常用的高级程序设计语言 2.2程序设计语言的定义 2.2.1语法 2.2.1语法 2.2.3程序语言的基本功能和层次机构 2.2.4程序语言成分的逻辑 ...

  2. 编译原理视角下的 c c 语言左值教学,西安交通大学18年3月课程考试《编译原理》作业考核试题...

    Q.微信均是1219895388,Q:1219895388 联系:QQ:576696131 西安交通大学18年3月课程考试<编译原理>作业考核试题 共题,总分:100分 答题中 分 一.单 ...

  3. 编译原理-如何使用flex和yacc工具构造一个高级计算器

    Flex工具的使用方法 Lex 是一种生成扫描器的工具. Lex是Unix环境下非常著名的工具,主要功能是生成一个扫描器(Scanner)的C源码. 扫描器是一种识别文本中的词汇模式的程序. 这些词汇 ...

  4. python写词法分析器_用python写一个简单的词法分析器

    编译原理老师要求写一个java的词法分析器,想了想决定用python写一个. 目标 能识别出变量,数字,运算符,界符和关键字,用excel表打印出来. 有了目标,想想要怎么实现词法分析器. 1.先进行 ...

  5. 《编译原理》实验预习报告——TINY语言的词法分析

    实验目的 构造tiny语言的词法分析器(扫描器),要求利用第三方的lex工具进行构造.实验结果:构造出的扫描器,能够读入教材样例中给出的tiny语言的示例代码,分解成token输出. Experime ...

  6. 编译原理实验报告一:PL0语言编译器分析(PL0,词法分析,语法分析,中间代码生成)

    实验报告一:PL0语言编译器分析 一.实验目的 通过阅读与解析一个实际编译器(PL/0语言编译器)的源代码, 加深对编译阶段(包括词法分析.语法分析.语义分析.中间代码生成等)和编译系统软件结构的理解 ...

  7. 编译原理(四)之解析语言翻译成三地址代码

    选择部分C语言的语法成分,设计其词法语法语义分析程序. 设计并实现一个一遍扫描的词法语法语义分析程序,将部分C语言的语法成分翻译成三地址代码,要求有一定的出错提示和错误恢复功能. 例如简单赋值语句: ...

  8. 编译原理(二)文法和语言、符号和符号串、文法的类型、语法树

    要点: 符号和符号串的相关概念 文法和语言的形式定义 文法的类型 上下文无关文法及其语法树 上下文无关文法的句型分析 有关文法实用中的一些说明 目的: 掌握文法和语言的相关概念,为以后的词法分析.语法 ...

  9. 编译原理:句子、句型和语言的概念区分

    如图所示:

  10. 编译原理上机实习c语言小子集编译程序的实现报告,编译原理上机实习指导书(2015-11修改).pdf...

    <编译原理上机实习>指导书 一.上机实习目的 理解编译程序的构造原理,掌握编译程序的构造方法与技术.通过实习,使学生既加深对 编译原理基础理论的理解,又提高动手能力,特别是提高软件设计能力 ...

最新文章

  1. 【怎样写代码】确保对象的唯一性 -- 单例模式(三):单例模式
  2. 程序员因中年危机从北京回老家事业单位:工资从60万爆降到6万
  3. Caused by: java.lang.ClassNotFoundException: Cannot find class: User
  4. 在emIDE中创建STM32项目
  5. Java字符串流学习
  6. sitemap.xml生成方法(asp和php)(转)
  7. javagui点击按钮弹出另一个界面_界面设计如何提高转化率和易用性(10则)
  8. 数据结构之红黑树简介
  9. IT项目管理规范模板及IT软件招投标模板(共367份,488M)
  10. 服务器与交换机的lacp协议,IEEE 802.3ad 链路聚合与 LACP 的简单知识 EtherChannel 总结...
  11. tomcat 启动出现 org.apache.tomcat.util.compat.JreCompat.isGraalAvailable()Z错误
  12. EIA/TIA 568A 568B 标准
  13. 大于2019电大计算机应用上机表格题,2019年电大计算机应用基础试题及答案重要知识点...
  14. Starling学习笔记
  15. 程序员如何赚「睡后」收入?
  16. 月薪过2w的IT程序员都是怎么做到的?
  17. mysql 调用方差函数_mysql 函数大全
  18. 手机做显示器服务器,华为MateView体验:手机做主机,显示器也能当电脑用
  19. uniapp从开发App到上架应用市场需要经历什么?
  20. Android程序员必备的六大顶级开发工具,快加入你的清单,看完没有不懂的

热门文章

  1. 网络流(17/24)
  2. 玩转 Spring Boot 应用篇(序列号生成器服务实现)
  3. 淘宝客系统教学系列_1.简单介绍和平台搭建
  4. 常见内网穿透工具使用总结
  5. 图论及其应用 2012年 期末考试答案总结
  6. c语言实现自动编译器,实现简易的C语言编译器(part 1)
  7. C语言中b lt a lt c,销售回收上海贝尔7360业务板NGLT-A-C, FGLT-A, FANT-F,FGLT-B
  8. 微pe工具箱 系统安装教程_通用PE工具箱装系统(V4.0)——安装原版XP系统
  9. 安卓天天练练(五)CompoundButton
  10. 一个快播倒下去,千千万万个快播站起来