yacc语法树系列(一)

990 阅读6分钟

这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

本文为译文,原文链接:dinosaur.compilertools.net/yacc/

常见词汇

英文中文
The lexical analyzer词法分析器
Literal characters原义字符
Specification规范/标准
nonterminal symbol非终止符号
In either case无论发生何种情况
be assumed to被认定为
potential潜在的

yacc简介

计算机程序输入通常有一些结构,实际上,每一次电脑程序输入可以被认为是定义了一种“输入语言”来接收。一门输入语言可能像一门变成语言那么复杂,也可能像数字序列那么简单。不幸的是,常见输入设施是受限制的,难以使用的,并且经常检查输入的校验是不严格的。

Yacc提供了一种用来描述计算机程序输入的通用工具。yacc的使用者指定了需要的输入结构,跟被调用的代码一起,作为每个这样接收的结构。yacc把这样一种说明转化为子程序来处理输入过程;经常地,通过这样的子程序可以很方便很合适地用来处理用户应用的大多数流控制场景。

yacc生产的输入子程序可以称为一种用户提供的程序来返回下一个基本输入项。因此,用户可以指定他的输入按照个人输入字符,或者按照更高级别的结构比如说名称或者数字。这个用户提供的程序也可以处理惯用的特性,比如说评论和延续惯例,用来对抗简单的文法规范。

yacc是使用portableC编写的。规范类接收一个非常通用的:LALR(1)语法,使用消除规则。

除了C,APL,Pascal,RATFOR等编译器。yacc也已经被使用在量级更小、更不常见的语言,包括照相机语言,多桌面计算器语言,文档检索系统,公式编辑器调试系统。

初见

yacc为利用计算机程序输入的结构提供了一个通用的工具。yacc使用者准备了一种输入处理的规范,包括描述输入结构的规则,这些规则被识别调用的代码,一个低级别程序来执行基本的输入。yacc接着会生成一个函数来控制输入过程。这个函数,被称为一个解析器,调用用户提供的低级别的输入程序(词法分析器)从输入流挑选出基本项(被称为tokens)。这些tokens根据输入结构规则来组成,称为语法规则;当其中一个规则被识别,然后用户代码提供了这种规则,会执行相应的动作;这些动作拥有返回结果和使用其他动作的值的能力。

yacc由编写的方言的C语言编写,动作和输出的子程序也都是用C语言。此外,yacc的很多语法约定也跟随了C语言。

输入规范的主体是一个语法规则集。每一条规则描述了一个可允许的结构并赋予它一个名字。比如说,一个语法规则可能是如下:

date  :  month_name  day  ','  year   ;

在这里,date,month_name,day,year代表了输入处理关心的结构;可以推测出,month_name,day,year被定义在else的地方。符号","被单引号修饰,这意味着这个符号会在输入中的字面上出现。冒号和分号仅仅是作为规则中的标点符号,并没有意义来控制输入。因此,合理的定义,像这种输入:

July  4, 1776

会跟上述规则比较匹配。

输入处理的一个重要部分由词法分析器来实现。这个用户程序读取输入流,识别低级别结构体,把这些tokens传递给解析器。由于历史原因,一个能被词法分析器识别的结构体称之为终端符号,同时被解析器parser识别的结构体被称为非终止符号。为了避免歧义,终端符号通常作为tokens被引用。

选择词法分析器还是语法规则来识别结构体,其差别是值得被考虑的。比如说,这些规则:

month_name  :  'J' 'a' 'n'   ;
month_name  :  'F' 'e' 'b'   ;

                 . . .

month_name  :  'D' 'e' 'c'   ;

可以被使用在上面的例子中。词法分析器只会识别单独的字母,并且month_name将会是一个非终止符号。这种低级别的规则会导致浪费时间和空间,也会使得规范复杂化,导致超出yacc的处理能力。通常,词法分析器会识别月份名字,然后返回一个指示表名month_name被看见;在这种情况下,month_name将是一个token。

Literal characters(原义字符)比如说","必须也被传递到词法分析器中,也会被考虑成tokens

规范文件是非常灵活的。它非常可靠简单地添加上面的例子,这样的规则:

date  :  month '/' day '/' year   ;

允许7 / 4 / 1776作为下面July 4, 1776的同义词

在大多数情况下,这个新规则可以被使用最小的代价”运送“到工作系统,并且对于破坏已有的输入的危险性是最小的。

读输入可能不符合规范。这些输入错误采用从左到右扫描的方法,理论上可以尽可能早地被检测到;因此,不只是减少了错误的读和计算的输入数据的几率,而且错误数据通常可以被很快地发现。错误处理,作为被提供的输入规范的一部分,允许错误数据的重新进入,或者跳过错误数据之后继续进行输入处理。

在一些情况下,当制定了一系列规范后,yacc无法生成一个解析器。比如说,规范可能自我矛盾,或者他们需要一个更强力的识别机制来保证yacc可用。前者代表涉及错误;后者经常可以修正通过制定更强大的词法分析器,或者通过重写语法规则。因为yacc无法处理所有可能的规范,它的功能相比起来更倾向于在相似的系统中发挥;而且,这些结构对于yacc来说很难处理,对人类来说处理起来也很难。有些用户已经提出,为他们的输入制定有效的yacc规范的原则,揭示了程序发展的早期的概念或设计错误。

未完待续...