从零开始的编译原理(3):词法分析与有穷自动机

一、前言 因而,每一生物的有机形体都是一个神圣的机器,或一台无限地优越于任何人造的自动机器的自然的自动机器(automata)。因为人的技艺所制造的机器的每一部分并非机器。……而自然的机器,即有机体,在其无限小的部分仍是机器。 —— 戈特弗里德·威廉·莱布尼茨《单子论》第 64 节 本章将从词法分析所用的 3 型文法入手,引入自动机的概念,并详细介绍有穷自动机的原理及实现。最终实现编译程序的词法分析模块。 二、正则文法的解析过程 在第 1 章的最后我们介绍了 3 型文法。3 型文法也称正则文法,特征是对于文法中的每一条规则,其右侧要么是一个终结符,要么是一个终结符加上一个非终结符,且非终结符只位于终结符的一侧。根据非终结符位置的不同分为左线性和右线性。 或许有些人会这样讲:“左线性是终结符在右侧;右线性是终结符在左侧”。但是这样实在太别扭了。应该说 “左线性是非终结符在左侧;右线性是非终结符在右侧”。 为什么通过这一约束,3 型文法就能区别于 2 型文法了呢?我们可以通过正则文法的解析过程来理解。 现在有一左线性正则文法 $G_1[Z]$: $$ \begin{align*} Z & \rightarrow A1 \\ A & \rightarrow B1 | 1 \\ B & \rightarrow A0 \end{align*} $$ 现在可能不太能看得出来,但是 $G_1[Z]$ 能够接受符号串 $101011$。我们可以画出此符号串对应的文法树。 通过文法树我们可以看出,对于左线性正则文法 $G_1[Z]$,其文法树只向左增长。当然这一点从规则中也可以看出,只不过不那么直观罢了。 可为什么具有这样特性的文法就特殊了呢?接下来我们根据文法树,以归约的视角看待符号串 $101011$ 的解析过程: 读入 1,1 归约得到 A; 读入 0,A0 归约得到 B; 读入 1,B1 归约得到 A; …… 以此类推,我们就可以发现其中的规律。对于这样的文法,读入一次即归约一次。因为文法中最多只有两个符号,所以一旦读入了一个新的符号,则必定进行一次归约。 同理的,我们考虑右线性正则文法 $G_2[S]$: $$ \begin{align*} S & \rightarrow 1A \\ A & \rightarrow 0B | 1 \\ B & \rightarrow 1A \end{align*} $$ $101011$ 也能被 $G_2[S]$ 接受。其文法树如下,对于右线性正则文法 $G_2[Z]$,其文法树只向右增长...

一月 10, 2024 · 9 分钟 · 1724 字 · Wokron

基本正则表达式总结

简介 正则表达式是用于描述字符串匹配模式的表达式。利用正则表达式进行匹配,可以实现检查字符串是否符合某种规则、字符串是否含有某种子串;替换匹配的子串或者从某个串中取出符合某个条件的子串。 正则表达式的引擎是一种自动机,在根据规则完成自动机的构建后,对任意字符串的匹配都将花费 O(n) 的时间复杂度。有关正则表达式的理论及实现本文并不继续深入。 基本语法 这里将介绍正则表达式的基本语法。另外这里推荐网站 regex101,可以用于验证正则表达式。 字符与字符集 正则表达式中一般的字符用于匹配字符串中对应的相同字符。例如正则表达式 “a” 可以匹配字符串中的字符 ‘a’ 。 “abc” 可以匹配字符串中的子串 “abc” 。 对于在正则表达式中具有特殊含义的字符,需要进行转义,在原字符前加上反斜杠 “\” 用方括号将一个或多个字符括起来表示一个字符集,一个字符集匹配在该字符集中出现的字符。例如 [abc],可以匹配 ‘a’ ‘b’ 或 ‘c’。 还可以在字符集中指定要匹配的字符的范围,例如 [a-z],用来匹配所有的小写英文字母。在同一字符集内可以有多个范围,例如 [a-zA-Z0-9]。 在字符集括号内的所有字符之前添加 “^” 表示对该字符集取反,即匹配所有不在字符集内的元素。例如 [^a-b] 匹配所有不是小写英文字母的元素。 字符 “.” 可用于匹配换行符为所有字符。 限定符 限定符用来指定其前面的部分将要匹配几次。例如a{2,5}匹配2到5个a,即"aa"、“aaa”、“aaaa"和"aaaaa”。具体的限定符含义如下表所示: 限定符 解释 {n} 匹配内容n次 {n,} 匹配内容次数大于等于n次 {n,m} 匹配内容次数为n到m次 * 匹配零次或多次,同{0,} + 匹配一次或多次,同{1,} ? 匹配零次或一次,同{0,1} 注意,限定符匹配默认遵循贪婪原则,即在同样能够完成匹配的情况下,会匹配尽可能多的字符。例如要匹配<html><dir>hello,world</dir></html>中的标签,若使用正则表达式 “<.+>",则只会匹配整个字符串。 解决方法是在限定符后加上一个问号 “?” 。这样限定符的匹配模式便会切换为懒惰匹配,即在同样能够完成匹配的情况下,会匹配尽可能少的字符。 组 用括号将一部分表达式括起,可以将这部分表达式作为一个整体。 比如说,需要匹配 “aacacaaac”,可以归纳出字符串中重复的部分 “a+c”,将该部分作为一个整体,则该部分重复了三次。最终得到可以匹配该字符串的正则表达式为 “(a+c){3}"。 另外,组分为捕获组与非捕获组,捕获组即单纯的括号,非捕获组包括 “(?:exp)” “(?=exp)” “(?!exp)” “(?<=exp)” “(?<!exp)"。这里先说明 “(exp)” 和 “(?...

九月 21, 2022 · 2 分钟 · 241 字 · Wokron