大家好,欢迎大家来到Coding迪斯尼,在深入探究词法解析算法前,我们需要了解一些基本概念。了解基本概念有点像背单词,它有些无聊,但你又不得不做。好在这类事情在我们的课程里不多。大家过过眼,留个心眼就好。


阅读博客的朋友可以到我的网易云课堂中,通过视频的方式查看代码的调试和执行过程:

http://study.163.com/course/courseMain.htm?courseId=1002830012


在词法解析中有一个概念叫字母表,字母表并不是单单指字母组成的集合,它是指一组数量有限的符号的集合。例如C语言中常用的ASCII字符集就可以称作字母表。所有汉字的集合也可以称作字母表。{‘0’,‘1’}是二进制的字母表。

字符串或者叫单词,是字母表中一系列符号的组合,例如英语单词,中文的词组都可以称之为字符串。在实际运用中,字符串就是字母数组。有一种特殊的字符串叫空字符串,也就是不包含任何字母的字符串。在C语言中可以用只包含’\0’的数组作为空字符串,char buf[10] = {‘\0’}。

有一个概念叫“语言”,他是指所有字符串的集合,比“语言”小的概念叫“句子“,他是一系列字符串组成的集合。也就是说,有限个字符串组成的集合叫句子,所有可能句子组成的集合叫语言。拿我们中文来举例,我们的句子其实是一系列中文词组的集合,那么中文就是所有句子的集合。

词组必须依照某些规则组合才能形成有意义的句子,这种规则我们就称之为语法。

大家注意到,语法跟词组其实是无关的,跟词组的组合规则有关,这就是为什么在编译器中,词法解析和语法解析可以分开来,词法解析的主要任务就是识别有效词组。

前缀:一个字符串的前缀是指,将字符串尾部的0个或若干个字符删除后剩下的部分。例如“in”是单词”inconsequential” 的前缀,大家要注意,根据定义,”inconsequential”本身也是它自己的前缀。如果把整个字符串都删了,得到的空字符串,也是合法的前缀。

后缀:它是指将字符串从前头删掉0个或若干个字符后剩下的部分。”ible”是”incomprehensible” 的后缀。当然如果从前头将整个字符串删除后得到的空字符串也是合法后缀。

子字符串:它是指将字符串的前缀后后最都删除后,剩下的那部分。例如”age”是”unmanageable” 的子字符串。如果删除的前缀和后缀合在一起是整个字符串,那么删除后的空字符串也是合法的子字符串。

真子集:前缀中有两个特殊成员,一个是空字符串,一个是原来字符串本身。例如” inconsequential”的前缀中,空字符串和“inconsequential“也是它的前缀,如果把这两个特殊的字符串从前缀中去掉,所剩下的集合叫做前缀的真子集。后缀和子字符串做同样的操作后也可得到各自的真子集。

子序列:它是指在字符串中,任意删除0个或多个字符后剩下的字符串,剩下的字符,他们的位置不一定是连续的,例如:iiii,ssss 都是字符串”Mississippi”的子序列

在字符串的基础上,我们可以定义一系列操作。两个字符串的连接操作(concatenation),是将一个字符串添加到另一个字符串的后面,例如”fire” . “water” = “firewater”. 空字符串可以和任何字符串相连接,连接后得到的字符串不变,连接操作可以用符号”.”表示,大家如果用过PHP的话,PHP字符串的连接操作用的就是点号。

如果把连接操作看做是一种乘法,那么X^n,也就是X的n次幂就是将X字符串本身连接n次。假设有一个字母表如下:

L(octal) = {0,1,2,3,4,5,6,7}. 那么L^3,表示的就是有三位数的八进制数。

如果L(Binary) = {0,1}. 那么 L^n 就表示所有二进制数。

(大家要有不理解的地方可以在讨论区提出)

两个集合的连接实际上就是在第一个集合任意选取一个元素,在第二个集合任意选取一个元素,将这两个元素连接起来,所有这种操作的结果就是两个集合连接的结果,例如L(Binary) ^2 = L(Binary) . L(binary). 计算的方式是在第一个{0,1}中任意选一个元素例如0,第二个集合{0,1}中任意选一个元素例如1,得出结果01,这种选取结合的方式有4种,得到4种结果: 00,01,10,11.

这种运算,说的专业点也叫“笛卡尔乘积”

Kleene 闭包:像上面提到的,L^n,n>=0 时所形成的集合叫Kleene闭包。我们用L*来标记。

假定集合L1只包含一个字符串,”Va”,集合L2只包含字符串”Voom”,那么

L1* .L2 就是

Voom,VaVoom,VaVaVoom,VaVaVaVoom …… (Va在Voom 前面出现零次或多次)

上面提到的公式L^n,n>=0,如果把n>=0 改成n>=1,那么我们把这种情况叫做正闭包,用 L+ 表示。

我们可以把上面的连接操作和闭包操作用到下面两组集合上:

L(digit) = {0,7,8,9}.

L(alpha)= {a,b,c,…. z}.

L(digit)+ 就是C语言中的所有数字常量。

L(aPHPa) . ( L(alpha) . L(digit))* 就是C语言中,所有合法的变量名。例如a,ab12,times13 这些都是C语言变量名;

正则表达式

正则表达式实际上就是上面提到的连接和闭包操作的各种组合运用。正则表达式描述了字母组合的某种规则,然后看给定的字符串中的字母,他们的组合方式是否跟正则表达式所描述的规则相符合。

最简单的正则表达式是一系列单个字符,匹配输入中的对应字符,正则表达式中还有一些特殊字符,用来表达一些特殊的含义,正则表达式完全可以写成一本书,在这里我们只关注用得到的一些内容,我们只要关注以下几种规则:

1.单字符匹配。字母表中每一个字符都是一个正则表达式。例如字符c,匹配单个字符”c”.

2.ee 两个正则表达式前后连接,用于判断输入的字符串中,是否存在前一部分匹配第一个正则表达式,后一部分匹配第二个正则表达式。例如字符a,n,d分别匹配字母”a”,“n”,“d”,那么正则表达式and 则匹配输入字符串中的”and”

3.通用匹配符 . (括号前面有一个点) ,. 可以匹配任何单个字符,例如a . y 可以匹配any,amy,agy。

4.^开头匹配符, 例如^and 匹配任何以and开头的字符串

5.$末尾匹配符,例如and$, 用于匹配任何以and结尾的字符串。

6.字符类,任何包含在 [ 和 ] 之间的字符构成字符类,例如[0123456789]匹配任何单个数字,一般简写成[0-9]. [0-9A-Fa-f]匹配任何一个十六进制数。[a-zA-Z]匹配任何字母,符号 ^ 如果出现在括号里,那表示取反,例如[^a-z]匹配任何不是小写字母的字符。

7.三种符号*+?,如果正则表达式后面跟着*号,例如a*,它匹配字母a的零次或多次从复。a+ 匹配字母a的一次或多次重复。a? 匹配字母a的零次或一次重复。ll?ama匹配llama,lama. l+ama 匹配lama,llama,lllllllllllama(很多个l和ama). l*ama 匹配ama(l出现零次),lama,lllllllama….等等. 0[xX][0-9a-fA-F]+ 匹配所有十六进制数。

8.e | e,两个正则表达式被一个竖杆分开,如果一个字符串匹配其中任何一个表达式,那么该字符串就匹配这个表达式。例如either | or 匹配字符串either 和or. (frank | john)ie 匹配字符串frankie 和 johnie。而(frank |john)(ie)? 匹配frankie,john,frankie,johnie。

正则表达式作用有限,它可以匹配字母组成的字符串,但无法识别例如:

((()()(())) 这种括号是否正确匹配的问题,这种问题其实是语法解析器的工作。

接下来我们要谈谈一个相对重要也相当有用的概念,叫有限状态自动机(FMS)。

FMS 是 Finite statemachine 的缩写,FMS包含以下一些特点

1.一组有限的状态集合

2.一组从一个状态到另一个状态的转换,每一个转换对应一个输入字符。

3.一个特殊状态叫初始状态。

4.一组特殊状态叫接收状态。

我们看个例子:

图中每一个圆圈表示一个状态,里面的数字表示状态的编号。状态0是初始状态,状态机开始就处于状态0. 状态间的连线就是状态的转换。从初始状态开始,如果读入字符h,那么状态机就从状态0转到状态1. 如果接下来读到字符e那就从状态1转到状态2. 如果读到的字符是i那么就从状态1转到状态5. 如果读取字符c 使得状态机从状态N转到状态M,我们用next(N,c) = M 来表示。Next就叫做转换函数。有两个圈的状态叫接收状态。一旦进入接收状态,就表明当前进入状态机的字符组成的字符串可以被状态机接受。一般来说,一旦进入接受状态后会采取一些行动,根据我们以前的编译器中的词法解析器为例,进入接受状态要做的操作就是打标签。如果输入的字母没有对应的转换,那么状态机会默认的进入错误状态,例如从状态0开始,如果输入的字母是x,但图中没有对应x的转换,因此状态机进入错误状态。

在程序中,我们一般用二维数组来实现有限状态机。另外用一个数组来表述状态机中的状态,我们看看一组伪码:

Transition_table 是有限状态自动机

Accepting 数组是状态数组

下一个状态可以从输入字符input_character,和当前状态current_state读出

next_state =Transition_table[current_state][input_character].

If (Accepting[next_state] == true) {

do_an_accepting_action(next_state);

}

其中Transition_table用来表示有限状态机中的转换关系,Accepting数组用来表示各个状态,如果给定状态是接收状态,那么它在数组中的值为true.


下一节我们将用程序来实现一个有限状态自动机,它的功能是用来判断输入的字符串是整形,还是浮点型,例如识别12306是整形,3.1416926是浮点数

编译原理动手实操:词法解析算法的一些概念说明的更多相关文章

  1. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  3. ios – 使用大写符号在字符串swift中获取URL的正则表达式

    我尝试在文本中获取URL.所以,在此之前,我使用了这样一个表达式:但是当用户输入带有大写符号的URL时(例如Http://Google.com,它与它不匹配)我遇到了问题.我试过了:但什么都没发生.解决方法您可以使用正则表达式中的i内联标志关闭区分大小写,有关可用正则表达式功能的详细信息,请参阅FoundationFrameworkReference.(?ismwx-ismwx)Flagsetti

  4. ios – 如何在Swift 3中使用正则表达式?

    解决方法我相信.当没有其他选项适用时,将使用.allZeros.因此,使用Swift3,您可以传递一个空的选项列表或省略options参数,因为它默认为无选项:要么请注意,在Swift3中,您不再使用error参数.它现在抛出.

  5. ios – lldb断点在类目标c中的所有方法

    如何使用lldb在ObjectiveC类中的所有方法上自动设置断点?

  6. ios – Swift:如何从不同的swift文件中调用函数

    我的Xcode6beta-2项目中有多个类型为UIViewController的swift文件.我基本上想知道文件A中的一些数据在文件B中使用.我的文件都是UIViewControllers,我创建了一个没有参数的函数,它返回UIViewController_A中的字符串.当我尝试在UIViewController_B中调用所述函数时,intellisense为我填写,但是我必须有一个自动填充为U

  7. ios – 如何使用SwiftyJSON将字符串转换为JSON

    要转换的字符串:[{“description”:“Hi”,“id”:2,“img”:“hi.png”},{“description”:“pet”,“id”:10,“img”:“pet.png“},{”description“:”Hello!:D“,”id“:12,”img“:”hello.png“}]转换字符串的代码:varjson=JSON该字符串转换为JSON,当我尝试计算这个JSON里面有多少个块时,我得到0.打印控制台输出:0我失踪了什么帮助非常感激.解决方法实际上,在SwifyJSON中有一个内

  8. ios – 将两个字符串转换为一组布尔值的快速方法是什么?

    我有一个长字符串,我想转换为一个布尔值数组.而且它需要很多次,很快.我天真的尝试是这样的:但这比我想要的要慢很多.我的剖析告诉我,地图是减速的地方,但我不知道我能做多么简单.我觉得如果没有Swift’s/ObjC的开销,这样做会很快.在C中,我认为这是一个简单的循环,其中一个字节的内存与一个常量进行比较,但我不知道我应该看的是什么函数或语法.有更好的办法吗?

  9. 寒城攻略:Listo 教你 25 天学会 Swift 语言 - 05 Strings and Characters

    Swift所代表的字符串是字符串类型,进而代表字符类型的值的集合//Swift的String和Character类型提供了一个快速的,兼容Unicode的方式来处理代码中的文本信息。每一个字符值代表一个Unicode字符,我们可以利用for-in循环来遍历字符串中的每一个字符println}//定义一个字符常量letyenSign:Character="$"printlncharacters")//使用"countElements()"函数来获取字符串的长度//8.ConcatenatingStrings

  10. String 与 NSString 的区别

    Swift的String类型与FoundationNsstring类进行了无缝桥接。在日常开发中,绝大多数应该用StringString与Nsstring还有以下区别String类型是值类型,字符串在进行常量、变量赋值操作或在函数/方法中传递时,会进行值拷贝。任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。String可以支持字符遍历Nsstring不支持String是一个结构体,性能更高;Nsstring是一个NSObject对象,性能相对会差现在还有一些功能,用String不

随机推荐

  1. 法国电话号码的正则表达式

    我正在尝试实施一个正则表达式,允许我检查一个号码是否是一个有效的法国电话号码.一定是这样的:要么:这是我实施的但是错了……

  2. 正则表达式 – perl分裂奇怪的行为

    PSperl是5.18.0问题是量词*允许零空间,你必须使用,这意味着1或更多.请注意,F和O之间的空间正好为零.

  3. 正则表达式 – 正则表达式大于和小于

    我想匹配以下任何一个字符:或=或=.这个似乎不起作用:[/]试试这个:它匹配可选地后跟=,或者只是=自身.

  4. 如何使用正则表达式用空格替换字符之间的短划线

    我想用正则表达式替换出现在带空格的字母之间的短划线.例如,用abcd替换ab-cd以下匹配字符–字符序列,但也替换字符[即ab-cd导致d,而不是abcd,因为我希望]我如何适应以上只能取代–部分?

  5. 正则表达式 – /bb | [^ b] {2} /它是如何工作的?

    有人可以解释一下吗?我在t-shirt上看到了这个:它似乎在说:“成为或不成为”怎么样?我好像没找到’e’?

  6. 正则表达式 – 在Scala中验证电子邮件一行

    在我的代码中添加简单的电子邮件验证,我创建了以下函数:这将传递像bob@testmymail.com这样的电子邮件和bobtestmymail.com之类的失败邮件,但是带有空格字符的邮件会漏掉,就像bob@testmymail也会返回true.我可能在这里很傻……当我测试你的正则表达式并且它正在捕捉简单的电子邮件时,我检查了你的代码并看到你正在使用findFirstIn.我相信这是你的问题.findFirstIn将跳转所有空格,直到它匹配字符串中任何位置的某个序列.我相信在你的情况下,最好使用unapp

  7. 正则表达式对小字符串的暴力

    在测试小字符串时,使用正则表达式会带来性能上的好处,还是会强制它们更快?不会通过检查给定字符串的字符是否在指定范围内比使用正则表达式更快来强制它们吗?

  8. 正则表达式 – 为什么`stoutest`不是有效的正则表达式?

    isthedelimiter,thenthematch-only-onceruleof?PATTERN?

  9. 正则表达式 – 替换..与.在R

    我怎样才能替换..我尝试过类似的东西:但它并不像我希望的那样有效.尝试添加fixed=T.

  10. 正则表达式 – 如何在字符串中的特定位置添加字符?

    我正在使用记事本,并希望使用正则表达式替换在字符串中的特定位置插入一个字符.例如,在每行的第6位插入一个逗号是什么意思?如果要在第六个字符后添加字符,请使用搜索和更换从技术上讲,这将用MatchGroup1替换每行的前6个字符,后跟逗号.

返回
顶部