1 词法分析

1 词法分析

语法分析器会按需向词法分析器索要token

输入一个字符串,要输出词法单元流。

但仅仅是字符串是不够的,还需要一个隐含的且是最重要的输入:词法单元(Token)的规约

需要有一个语言来描述每次词法单元的结构,比如要从字符串中识别出ID,那必须要知道这个ID的结构才行。词法单元(Token)的规约就是完成这样的工作

image-20221114153952664

词法分析器的设计方法

image-20221114155150098

使用词法分析器生成器

Antlr输入词法单元的规约(.g4文件),Antlr作为词法分析器生成器就可以生成一个词法分析器算法,比如 Lexer.java (也支持生成其他语言的)

对于 Lexer.java,编译执行后,就可以输入字符串,从而输出字符流(string of token)

因此 Antlr 帮我们完成了生成词法分析器的过程,我们只需要写一个 .g4 文件来描述每一个词法单元的结构是什么样子的

image-20221114160241918

手写词法分析器

即上面的过程中的 .java 文件的生成需要我们自己来完成或者说需要我们自己编写词法分析器的源代码

自动化词法分析器

相当于写一个 Antlr,因为在词法分析器的生成过程中,很多步骤都是机械化的,其实可以使用自动化来实现


在生产环境中通常选择手写词法分析器,自由度更大,效率可能更高,且便于控制复杂度,如 gcc (左边为 C 语言的词法分析器,右边为 C++ 的词法分析器)

image-20221114155555537

目前如 Antlr 这类的词法分析器生成器生成的词法分析器已经能够和手写相媲美,因此使用第一种方式也是没有问题的

代码

image-20221114160842879

第一行 grammar SimpleExpr,需要和该 .g4 文件的名字一样

prog (program)描述用这个语言写成的程序的最高层的语法结构,称为语法规则名 。在 .g4 文件中语法规则名要求以小写字母开头

stat (statement)表示语句

因此第 4 行表示这个语言写成的程序是由 0 个或多个语句构成的(当然此时语句还没定义)

【*的语法和正则表达式一样】

后面的 EOF 的含义是一个程序前面必须是由 0 个或多个语句构成的,紧接着最后就是文件结束符了,不能有其他的非语句内容【如果不加 EOF,那么最后可以有非语句的内容,而仍然被编译器认为是正确的】


image-20221114162602684

ID 已经不能再用语法来表示了,它是一个词法单元了,词法单元需要用大写字母开头,一般全部写成大写

if 也是词法单元,但比较特殊,是一个字面量,意思是 if 自己就作为词法单元的一类,而 ID 则包含很多的类型。【+ * 同理】

Antlr 通过将字面量隐式的表示成词法单元,如 Token0 : ‘if’,并将这些隐式的词法单元写在我们自己定义的词法单元的前面,优先级都比显式的词法单元高,这样当发现 if 时会优先匹配前面的词法单元,而不是把它认为是一个 ID

但是隐式的表示有时会让我们产生误解,不利于后续的处理,因此通常都将这些字面量显式地表示出来,使用 alt+insert 快捷键即可生成

image-20221114205120222

这里的 EQUAL 改成 ASSIGN 表示赋值更好


image-20221114162929446

()表示子规则

两行的顺序很重要,它代表了运算的优先级


image-20221114163452458

image-20221114163641822

其中 LETTER 和 DIGIT 不属于 Token,只是在定义更复杂的 Token 时的辅助的规则。因此我们要区分它们,即在前面加上 fragment,从而避免识别这两者为 Token

image-20221114163809516


image-20221114164145811


image-20221114164256307

WS white space

-> skip 表示识别到 WS 就将其跳过


到目前为止,就定义了我们这个语言的词法结构

image-20221114164639689

叶子节点就识别出了所有的词法单元

树的层次结构对应小写字母开头的语法规则

增加更多的语法:

image-20221114172956075

单行注释:以 // 开头,必定以 \n 结尾,中的 . 表示通配符,匹配任意字符 0 个或多个

注意写 *+ 会导致贪婪的匹配,容易出现把// 后面所有的行都当作注释

* 后面加上? 即可实现非贪婪或最小匹配

image-20221114174530312

其他语法

image-20221114205329844

使用 @header{},写在这里面的内容会复制到 java 代码里面

image-20221114205932043

可以把词法单元单独的作为一个文件,与前面的语法单元(prog,stat,expr等)分开放,但需要在开始第一行加上 lexer

同时在语法文件中要 import 词法

这样的好处是,可能不同的语言对语法定义不同,但对词法定义是相同,就可以直接复用

理论

image-20221114210123710

image-20221114210528559

语言就是某种能够识别的字符串构成的集合

image-20221114210627920

image-20221114210634702

image-20221114210708297

image-20221114210939682

空集也是一种语言

包含空串的语言和空集是不一样的

image-20221114211159261

image-20221114211343450

*+ 就是前面的正则中的理论支撑

image-20221114213005414

image-20221114213145518

image-20221114213214619

image-20221114213456479

正则表达式不能很好理解,尽可能写成右边的少括号的形式,利用好优先级

image-20221114214039758

image-20221114214224877

image-20221114214408261

不同的正则语言的规则是不一样的,比如上面的 "s" ,但是在 Antlr 中是 's';上面 . 匹配除了换行符外的任何字符,而 Antlr 中匹配任何字符;上面用 ^ 表示不在 s 中的任意一个字符,而在 Antlr 中是 ~

  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
  • Copyrights © 2022-2024 zzb
  • RZ
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信