LBNF参考

介绍

本文件定义了语法形式主义标签BNF(LBNF),其中在编译器构造工具中使用BNF转换器。给定一个用LBNF编写的语法,BNF转换器生成部分编译器前端由lexer、解析器、,和抽象语法定义。此外,它还产生pretty-printer和LaTeX中的语言规范,以及编译器后端的模板文件。因为LBNF是纯粹的声明性的,这些文件可以用任何编程语言生成支持适当的编译器前端工具。自2.8.3版起,代码可以用Agda、C、C++、Haskell、Java和Ocaml生成。本文件描述了独立于代码生成的LBNF形式旨在为语法作者提供手册。

LBNF语法的第一个示例

作为LBNF的第一个例子,考虑以下语法仅限于加一的表达式:

EPlus公司。Expr公司::=Expr“+”数字;ENum(欧洲)。Expr公司::=编号;没有。编号::="1" ;

除了标签 EPlus公司,ENum公司、和没有,规则很普通BNF规则,带双引号和未加引号的非终结符。标签用作构造函数用于语法树。

BNF Converter从LBNF语法中提取抽象语法和一个具体语法例如,在Haskell中,抽象语法实现为数据类型定义系统:

数据 Expr公司   = EPlus公司 Expr公司 编号 | ENum公司 编号
数据 编号 = 没有

对于其他语言,如C、C++和Java,等效表示为根据Appel系列丛书中定义的方法现代ML/Java/C中编译器的实现 [1]。具体语法为由lexer、parser和pretty-printer算法实现在其他生成的程序模块中定义。

简而言之,LBNF

基本LBNF

简而言之,LBNF文法是BNF文法,其中每个规则都有一个标签。标签用于构建抽象语法树,其子树由规则的非终结符以相同的顺序给出。

更正式地说,LBNF语法由一组规则组成,这些规则具有以下形式(用正则表达式表示附录:LBNF规范给出了符号的完整BNF定义):

标识符“。”标识符“::=“(标识符|字符串)*”;"

第一个标识符是规则标签,然后是价值类别。在右侧生产箭头(::=)是生产项目的列表。项目是带引号的字符串(终端)或类别符号(非终端). 值类别为的规则C类也是称为生产对于C类.

可以选择标识符,即规则名称和类别符号广告自由,受目标语言的限制。收件人如果满足C、Haskell和Java,则会强制执行以下规则:

标识符是由字母、数字和下划线,以大写字母开头,不以下划线结尾。

其他功能

上面定义的基本LBNF显然足以定义任何无上下文语言。然而,定义纯粹使用BNF规则的编程语言。因此,一些额外的LBNF中增加了一些功能:抽象语法约定、lexer规则、,杂注和宏。这些特征将在随后的部分中进行处理部分。

章节抽象语法约定解释抽象语法约定.创建通过为每个BNF规则添加节点类型的抽象语法可以有时变得过于详细,或者被额外的结构弄得乱七八糟。收件人为了解决这个问题,我们已经确定了最常见的问题案例,并添加了给LBNF一些额外的约定来处理它们。

章节Lexer定义解释lexer规则.语言的某些方面属于它的词汇结构而不是语法由正则表达式而非BNF规则自然描述。我们有因此,在LBNF中添加了两种规则格式来定义词法结构:代币评论.

章节LBNF杂注解释杂注.语用是指导BNFC语法编译器处理某些语法规则特殊方法:减少入口点或者治疗一些句法形式为内部的只有。

章节LBNF宏解释.宏是的语法糖潜在的大量规则,有助于简洁地编写语法。这是为了作者和读者的方便;除此之外事物、宏自然会强制某些规则组合在一起,否则会在语法中任意传播。

章节布局语法解释布局语法,这是一个非文本自由某些编程语言中存在的功能。LBNF有一套规则用于定义有限形式的布局语法的格式。它作为将布局语法转换为显式结构的预处理器标记。注意:只有Haskell后端(包括Agda)实现布局功能。

抽象语法约定

预定义的基本类型

第一个约定是预定义的基本类型。基本类型,例如整数和字符,当然可以在LBNF中定义,用于例子:

字符_字符::=“a”;字符_字符::=“b”;

然而,这既麻烦又低效。相反,我们决定用预定义的基本类型扩展我们的形式主义,并表示它们语法是词汇结构的一部分。这些类型如下:,由LBNF正则表达式定义(请参见Lexer定义对于普通人表达式语法):

  • 类型整数整数,定义数字+

  • 类型双精度浮点数的数量,已定义数字+ '.' 数字+ (“e” '-'? 数字+)?

  • 类型字符个字符(用单引号括起来),已定义'\'' ((字符 - ["'\\"]) | ('\\' [“'\\tnrf”])) '\''

  • 类型字符串字符串(双引号),已定义'"' ((字符 - ["\"\\"]) | ('\\' [“\”\\tnrf“]))* '"'

  • 类型标识(Haskell)标识符的 (字母 | 数字 | '_' | “\”)*

在抽象语法中,这些类型表示为相应的每种语言的类型,除了标识不存在此类类型的。被视为新类型对于字符串在哈斯克尔,

新类型 标识 = 标识 字符串

作为字符串在Java中,作为类型定义字符*C和香草C++,和作为类型定义标准::字符串在C++中使用标准模板库(STL)。

正如类型名称所示,lexer可以产生高精度整数和浮点的变量。应用程序的作者可以截断如果他们想改为低精度,则可以稍后使用这些数字。

注释

规则中出现的端子优先于标识例如。,if端子“在哪里”出现在任何规则中,单词哪里将永远不会被解析为标识.

语义假人

有时,语言的具体语法包括不语义差异。一个示例是使解析器接受的BNF规则语句后的额外分号:

Stm公司::=Stm“;”;

由于此规则是语义no-op,因此我们不想用抽象语法中的构造函数。相反,我们介绍以下内容约定:

规则标签可以是下划线_,它不会添加语法树中的任何内容。

因此,我们可以在LBNF中编写以下规则:

_。Stm公司::=Stm“;”;

下划线当然只有在替换值类型与参数类型。语义假人在漂亮的打印机上没有留下任何痕迹。因此,例如,漂亮的打印机会额外“正常化”分号。

优先级

(普通)BNF中的一个常见习惯用法是使用表示优先级的类别:

实验2::=整数;实验1::=实验1“*”实验2;费用::=实验“+”实验1;实验2::=“(“Exp”)”;实验1::=实验2;费用::=实验1;

优先级别调节解析的顺序,包括关联性。括号将任何级别的表达提升到最高级别。

对上述规则进行简单的标记可以创建一种语法具有所需的识别行为,如抽象语法所示类型区分杂乱(介于费用,实验1、和实验2)和没有语义内容的构造函数(来自最后三个规则)。这个BNF转换器解决方案是区分类别符号它们只是彼此的索引变体:

类别符号可以结束使用整数索引(即数字序列),然后进行处理作为相应的无索引符号的类型同义词。

因此,实验1实验2是的索引变体费用.

索引变体之间的转换是语义上的no-op,我们确实如此不想用抽象语法中的构造函数来表示它们。为了实现这样,我们将下划线的使用扩展到索引变体。这个例子上面的语法现在可以标记如下:

EInt公司。实验2::=整数;E时间。实验1::=实验1“*”实验2;EPlus公司。费用::=实验“+”实验1;_.      实验2::=“(“Exp”)”;_.      实验1::=实验2;_。费用::=实验1;

例如,在Haskell中,表达式的数据类型变得简单

数据 费用 = EInt公司 整数 | E时间 费用 费用 | EPlus公司 费用 费用

和的语法树2 * ( + 1 )

E时间 (EInt公司 2) (EPlus公司 (EInt公司 ) (EInt公司 1))

索引的类别可以用于优先权以外的其他目的,因为我们唯一可以正式检查的是类型骨架(参见部分LBNF规则的类型正确性). 解析器不需要知道索引平均优先级,但只有索引变量的值相同类型。然而,pretty-printer假设索引类别用于优先级,如果在其他方式。

提示

参见第节胁迫定义虚拟对象的简明方法强制规则。

多态列表

在LBNF中定义单态列表类型很容易:

零定义。列表定义::=;宪法定义。列表定义::=定义“;”列表定义;

然而,像Haskell这样的语言的编译器编写者可能希望使用预定义的多态列表,因为这些列表的语言支持构造。LBNF允许使用Haskell的列表构造函数作为标签和类别名称中的列表括号:

[].  [定义]::=;(:). [定义]::=定义“;”[Def];

一般来说,我们有

[中],类型列表的类别C类,

[](:)、零和Cons规则标签,

(:[]),单元素列表的规则标签。

第三个规则标签是用于设置一个东向限制,但也允许特殊在具体语法中处理单元素列表。

LaTeX文档(出于风格原因)和Happy文件(针对句法原因),类别名称[中]被替换为列表C.为了防止冲突,列表C不能同时使用在语法中明确表示。

列表符号也可以看作是克莱恩星和以及因此作为来自Extended BNF的成分。

一些编程语言(如C)没有参数多态性。然后,各个后端生成单态列表的变体。

提示

参见第节终端和分离器定义列表的简明方法只是给他们的终结符或分隔符。

LBNF规则的类型正确性

在解析器生成器中,通常会委托某些目标语言错误。例如,一个Happy源文件没有投诉的愉快流程仍然可以生成Haskell文件被Haskell拒绝。同样,BNF转换器委托对生成的语言进行一些检查(例如,解析器冲突检查)。然而,由于这对于程序员理解与源BNF相关的错误消息转换器执行一些检查,这些检查主要与抽象语法的健全性。

类型检查器使用类别骨架类型一条规则,哪种形式

A_1将A_n转换为C

哪里C类是未编制索引的左侧非终结符A_1点A_n是(可能为空)无索引右侧序列规则的非终结符。换句话说规则表示语义动作的抽象语法类型与该规则关联。

我们还需要常规类别和一个常规规则标签。简单地说,常规标签和类别是用户定义的个。更正式地说,常规类别不是[中],整数,双精度,字符,字符串标识,或类型由定义代币规则(第节令牌规则). 常规规则标签为无属于_,[],(:)、和(:[]).

类型检查规则如下:

由标记的规则_必须具有形式的类别骨架C\至C.

由标记的规则[]必须具有形式的类别骨架\至[C].

由标记的规则(:)必须具有形式的类别骨架C~[C]\至[C].

由标记的规则(:[])必须具有形式的类别骨架抄送至[C].

只有常规类别才能具有具有常规规则标签的产品。

语法中出现的每个常规类别必须至少有一个使用常规规则标签进行生产。

具有相同常规规则标签的所有规则必须具有相同类别骨架。

第二条规则对应于没有空数据Haskell中的类型。最后一条规则可以加强,以便要求所有常规规则标签都是唯一的:这需要保证无差错的精美印刷。当前违反此强化规则只生成警告,而不是类型错误。

定义的标签

LBNF支持已消除的语法树构造函数在解析期间,通过以下方式语义定义。这是一个示例:核心语句语言:

分配。Stm公司::=标识“=”Exp;区块。Stm公司::=“{”[Stm]“}”;与此同时。Stm公司::=“while”“(”Exp“)”Stm;如果Stm::=“if”“(”Exp“)”“Stm”else“Stm;

我们现在想要一些语法糖。请注意,这些标签所有规则都以小写字母开头,表示它们对应定义的函数而不是抽象语法树中的节点。

如果Stm::=“if”“(”“Exp”“)”“Stm”“endif”;的。Stm公司::=“对于”(“Stm”;“Exp”;“Stm”)“Stm;包括Stm::=标识“++”;

函数使用定义关键字。定义具有形式:

定义f x1。。xn=e

哪里e(电子)是使用规则标签的应用形式的表达式,其他定义的函数、列表和文字。

定义如果e s=如果e s(块[]);定义i c s b=块[i,而c(块[b,s])];define inc x=赋值x(EOp(EVar x)+(EInt 1));终止符Stm“;”;

定义函数的另一个用途是简化二进制运算符。我们希望每个操作符都有一个节点,而不是一个节点所有二进制运算符应用程序的通用节点(EOp)。

_。操作::=操作1;_.     操作::=操作2;更少。操作1::="<"  ;相等。操作1::="==" ;另外。操作2::="+"  ;减。操作2::="-"  ;营业费用::=实验1操作1实验1;操作实验1::=实验1操作2实验2;EInt公司。实验2::=整数;EVar公司。实验2::=标识;胁迫实验2;

必须小心确保漂亮的打印机输出足够圆括号。

内部EOp。费用::=实验1操作实验1;定义ope1 o e2=EOp e1 o e 2;

注释

通过实现语法糖定义也许对快速运动有好处原型,但对于更严肃的编程来说有一些缺点语言实现:由于解析器已经扩展了语法糖,前端的后续部分,如范围或类型检查器,不要报告糖中的错误用户可能已经写了,但什么会到达检查器。对于实例,xs公司++对于数组变量X轴不会报告中的问题X轴++,而是在扩张中x个 = x个 + 1.

Lexer定义

令牌规则

标记规则使LBNF程序员能够定义新的词法类型使用简单的正则表达式表示法。例如,以下内容规则定义以大写字母开头的标识符的类型。

标记UIdent(上部(字母|数字|'_')*);

类型U凹痕成为可用的LBNF非终结符和中的类型抽象语法。每个令牌类型都由新类型在里面Haskell,作为字符串在Java中,作为类型定义字符*C等。

附录中指定了LBNF的正则表达式语法。这个括号中有字符串的缩写需要一个解释词:

[“abc7%”]表示字符的并集b条’ ‘c(c)’ ‘7’ ‘%

{“abc7%”}表示字符的顺序’ ‘b条’ ‘c(c)’ ‘7’ ‘%

原子表达式上面的,降低,、和数字表示隔离1与名称对应的字符类。表达式烧焦匹配任何unicode字符,并且“epsilon”表达式每股收益匹配空字符串。因此每股收益相当于{""},而空语言表示为[""].

注释

出现在规则中的终端优先于任何代币.例如,如果终端“有趣”出现在任何规则中,单词有趣将永远不会被解析为U凹痕.

注释

截至2020年10月,空语言仅由Java后端处理。

位置标记规则

任何代币规则可以是由单词位置修改,其效果是数据类型定义将携带位置信息。例如,

位置标记PIdent(字母(字母|数字|'_'|'\'')*);

在Haskell中创建数据类型定义

newtype PIdent=PIdent((Int,Int),字符串)

其中,整数对表示第一个标记的字符。漂亮的打印机省略了位置组件。

(截至2020年10月cpp-鼻孔后端(不使用STL的C++)不实现位置标记。)

注释规则

评论是包含自由文本的源代码段未传递给解析器。处理这些问题的天然场所是莱克瑟。这个评论规则指示lexer生成器处理文本片段作为注释。

注释规则接受一个(用于行注释)或两个字符串参数(用于块注释)。第一个字符串定义注释的开始方式。第二个可选字符串标记结束评论;如果没有给出,那么注释将以换行符结束。例如,Java注释约定定义如下:

注释“//”;注释“/*”“*/”;

注意:生成的lexer无法识别嵌套的块注释,它也不像字符串那样尊重引号。在下面的代码片段中

/*正在注释。。。printf(“*/”);*/

lexer将考虑在之前关闭块注释");.

LBNF杂注

内部杂注

有时我们想在抽象语法结构中包括不是具体语法的一部分,因此不可解析。他们可以,例如,由类型注释类型生成的语法树检查程序。即使它们不可分割,我们也可能想要精美印刷例如,在类型检查器的错误消息中。定义此类作为一个内部构造函数,我们使用了一个pragma

“内部”规则“;”

其中Rule是正常的LBNF规则。例如,

内部EVarT。费用::=“(“标识符”:“类型”)”;

引入变量表达式的类型注释变体。

入口点杂注

默认情况下,BNF转换器为中的每个类别生成一个解析器语法。在大多数情况下,这是不必要的丰富,并使解析器大于所需大小。如果解析器的大小变得至关重要,这个入口点杂注允许用户定义解析器实际上是导出的。

例如,以下杂注定义Stm公司,实验2和列表标识符的[标识]成为唯一的入口点:

入口点Stm、Exp2、[Identit];

LBNF宏

终端和分离器

这个终止器宏通过什么标记定义了一对列表规则终止列表中的每个元素。例如,

终止符Stm“;”;

告诉每个语句(Stm公司)以分号结尾(;). 这是这对规则的简写

[].  [标准]::=;(:). [标准]::=Stm“;”[Stm];

限定符非空的在宏中,使一个元素列表成为基本情况。因此

终止符非空Stm“;”;

是的缩写

(:[]). [标准]::=Stm“;”;(:).   [标准]::=Stm“;”[Stm];

终止符可以指定为空""。未引入令牌然后,但是例如。

终止符Stm“”;

被翻译为

[].  [标准]::=;(:). [Stm]::=Stm[Stm];

这个分离器宏类似于终止器,除了分隔标记未附加到最后一个元素。因此

分隔符“;”;

方法

[].    [标准]::=;(:[]). [标准]::=Stm;(:).   [标准]::=Stm“;”[Stm];

然而

分离器非空Stm“;”;

方法

(:[]). [标准]::=Stm;(:).   [Stm]::=Stm“;”[Stm];

注意,如果空标记""使用,没有区别之间终止器分离器.

问题。从分离器没有非空的实际上也会接受以分号,而漂亮的打印机将其“正常化”。这可能会被认为是一个错误,但有一套规则禁止终止分号会更复杂:

[].    [标准]::=;_.     [标准]::=[标准]1;(:[]). [标准]1::=标准时间;(:).   [标准]1::=Stm“;”[Stm]1;

不幸的是,这不是合法的LBNF,因为不支持列表。

胁迫

这个强制转换宏是一组规则翻译的简写优先级别之间。例如,

胁迫Exp 3;

是的缩写

_. 费用::=实验1;_. 实验1::=实验2;_. 实验2::=实验3;_. 实验3::=“(“Exp”)”;

由于这些胁迫的全部覆盖范围,是否表示最高级别的整数(此处)大于实际发生的最高级别,或是否存在其他级别语法中没有结果。然而,未使用的级别可能会使生成的解析器定义文件膨胀(例如,Haskell的Happy文件),除非后端实现某种死码删除。(截至2020年10月,没有后端实现了如此巧妙的分析。)

提示

强制类别(例如。实验2)也可以用于其他规则。对于实例,给定以下语法:

EInt公司。实验2::=整数;E添加。实验1::=实验1“+”实验2;

你可能想用实验2而不是简单地费用强制使用非平凡表达式的括号。例如,富。 F类 ::= “foo” 实验2 ;将接受foo公司 42foo公司 (1 + 1)但是 foo公司 1 + 1.

您甚至可以在列表中使用强制类别,并为它们提供不同的分隔符/终止符:

分隔符Exp“,”;分隔符Exp2“;”;

规则

这个规则宏是一组规则的简写,标签来自这些规则自动生成。例如,

规则类型::=Type“[”Integer“]”|“float”|“double”| Type“*”|Ident;

是的简写

类型1.类型::=键入“[”Integer“]”;类型_浮动。类型::=“浮动”;键入double。类型::=“双重”;类型2.类型::=键入“*”;类型标识。类型::=标识;

标签是自动创建的。标签以值开头类别名称。如果产品只有一个项目作为标识符的一部分,该项可能用作后缀。其他情况下,使用整数后缀。未执行全局检查生成这些标签时。BNFC类型捕获导致类型错误的标签名称冲突检查生成的规则。

布局语法

布局语法是一种使用缩进对程序元素进行分组的方法。它在一些语言中使用,例如Agda、Haskell和Python。布局语法是BNFC的一个相当实验性的特性由Haskell系列后端支持,因此读者第一次阅读时可能会跳过这一节。

杂注布局,布局 停止、和布局 顶层定义一个布局语法对于一种语言。从这些杂注中,一个名为布局,布局解析器,被创建为插入lexer和解析器之间。这个模块将额外的标记(大括号和分号)插入到标记中根据下面解释的规则,流来自lexer。

提示

为了获得其他后端的布局分辨率,生成的Haskell布局解析器可以手动输出到所需的编程语言。或者,生成的Haskell lexer和布局解析器可以变成一个独立的预处理器将文件插入标记,根据布局规则,并将标记打印回字符lexer和解析器可以进一步处理的流用所需的编程语言编写。

的布局杂注BNFC的功能不足以处理Haskell的完整布局规则98,但对于“常规”情况,它们就足够了。

关于功能概述,请看第一个简单的示例:树语法。A类是一个数字(有效载荷),后跟br个加上一个子树列表(可能为空):

节点。::=整数“br”“{”[树]“}”;分隔符树“;”;布局“br”;

列表用大括号括起来,并用分号分隔。这是布局机制目前唯一支持的语法。

生成的解析器可以处理以下示例:

0 br1个br2个br3个br4 br5 br6 br7 br

由于布局分辨率,这与将在不支持布局的情况下处理以下输入:

0 br{1 br{2br{}; 3个br{}}; 4 br{5 br{6br{}}}; 7个br{}}

布局解析器更精确地生成以下等价项中间文本(以令牌流的形式):

0 br{1个br{2 br{}; 3个br{}}; 4 br{5 br{6 br{}}}; 7 br{}}

现在来看一个更现实的例子,它位于逻辑框架的语法中阿尔法。(注意:这个例子有点过时了……)

布局“of”、“let”、“where”、“sig”、“struct”;

第一行说“第个,共个”,“让”,“其中”,“信号”,“结构”布局词,即启动布局列表。布局列表是表达式列表,通常用花括号括起来,用分号,如Alfa示例所示:

E案例。费用::=“{”“[分支]”“}”的case“Exp”;分隔符分支“;”;

当布局解析器找到标记时属于在代码中(即在它检查下一个标记是否是打开花括号。如果是这样的话,在布局之前不会做任何特别的事情再次遇到单词。解析器需要分号和右括号以正常显示。

但是,如果代币t吨下列的属于不是开口卷曲括号,插入括号t吨(或当前布局列之后的列,以较大者为准)被记住为布局列表元素必须位于的位置开始。分号插入这些位置。当令牌为最终遇到的位置左侧t吨(或一个文件末尾),在该点插入一个右括号。

允许嵌套布局块,这意味着布局解析器保持一堆位置。将位置推到堆栈上对应于插入左括号,然后从堆栈中弹出对应于插入右括号。

以下是使用布局的Alfa源文件示例:

c::Nat=案例x真->bFalse->第y种情况错误->b两者都不超过dd=真的情况x->假的情况y->gx->by->小时

以下是布局分辨率后的外观:

c::Nat=案例x{正确->b;False->第y种情况{错误->b};两者都不超过d};d={True->case x of{False->g;x->b};y->h};

还有两个与布局相关的杂注。布局停止杂注,如

布局停止“in”;

告诉解析器可以通过一些停止退出布局列表单词,比如在里面,退出列表。分解器中没有错误退出其他类型的布局列表在里面,但将显示错误在解析器中。

布局顶层pragma告诉我们整个源文件是一个布局列表中,即使没有布局词表明这一点。位置是第一列,解析器在每个段落后添加分号他的第一个标记在这个位置。没有添加花括号。这个上面的Alfa文件就是一个例子,添加了两个这样的分号。

堆叠布局关键字

自2.9.2版起,布局处理器支持布局堆叠同一行上的关键字。考虑以下片段阿格达语语法:

模块。下降::=“模块”Ident“其中”“{”“[Decl]”“}”“;私人。下降::=“私人”“{”[拒绝]“}”;类型Sig。下降::=Ident“:”Ident;分隔符Decl“;”;布局“where”,“private”;

此语法允许我们定义包含声明列表,每个声明都可以是模块、私有块或类型签名。声明列表用大括号括起来和由分号分隔,并由布局关键字引入哪里私有的.

我们可能想定义一个私有模块,而不是随后的两个缩进:

私有的模块M,其中A:设置

我们希望将两个布局介绍堆叠在一行上:

专用模块M,其中A:设置

BNFC 2.9.1之前的布局逻辑不支持这一点,因为我们将修复的布局列私有的块到第8列标记列模块.令牌A类遵循下一个布局关键字哪里位于低于8的第2列,因此私有的这里的街区将被关闭。但是有私有的关闭,内部块模块 M(M) 哪里也需要关闭,并且然后是声明A类 : 设置只是被误解了。因此,这给出了BNFC 2.9.1的解析错误。

BNFC 2.9.2生产的布局处理器标记布局第8列私有的块作为实验性的因为代币模块遵循layout关键字私有的是一样的行。相反模块块是决定性的因为代币A类遵循layout关键字哪里在后面一行。检查新布局时列有效,临时布局列被忽略,因此它只是检查2>0,而不是2>8。有时指向模块块将被关闭,因为我们遇到令牌缩进小于2私有的街区也将关闭因为它的压痕列8也比遇到的标记。

通常,当遇到新行时,我们会切换顶部布局列到决定性的如果他们还在实验性的例如,考虑以下情况:

专用模块M,其中A:设置模块N,其中错误:设置

在第一行的末尾,我们有两个暂定布局列,8(第列,共列模块)和23(第列,共列A类). 从现在起我们没有挂起这行中的layout关键字,我们切换布局列决定性的当我们看到下一个标记时模块在一条新线路上。第一个模块到此结束,留给我们明确的布局第8列。这可以防止我们分配给后续块模块 N个带有布局关键字哪里列2(标记). 相反,此块获得默认列8+1。什么时候?处理令牌两个区块由于2<8和2<8+1而关闭。但后来错误,产生预期的解析错误。

为引入的更改堆叠布局关键字是向后的在以下意义上兼容:由生成的布局软件解析器BNFC 2.9.2接受所有被BNFC 2.9.1及之前版本生成的相应解析器。此外为这些接受的文本生成相同的语法树。

优化:左递归列表

列表的BNF表示是右递归的,紧跟在列表之后Haskell和大多数其他语言中的构造函数。右递归列表,然而,在移位减少解析器中需要线性堆栈空间(例如LR解析器系列)。堆栈受到限制(例如野牛生成的解析器)。

右递归列表定义

[].    [标准]::=;(:).   [标准]::=Stm“;”[Stm];

在左递归转换下变为左递归:

[].         [标准]::=;(翻转(:))。[标准]::=[Stm]Stm“;”;

然而,当作为另一个规则的一部分使用时,需要反转这样解析的列表。

对于目标为堆栈受限解析器(C、C++、Java)的后端,BNF转换器自动执行左递归形式规则对的变换

[].  [中]::=;(:). [中]::=C x[C];

其中C是任何类别,x是终端的任何序列(可能空)。当然,这些规则可以从终止符生成宏(节终端和分离器).

通知。如果单元素列表是基本情况。它也不在Haskell后端中执行,该后端使用堆分配堆栈通过幸福的.

附录:LBNF规范

此文档最初由BNF转换器。它是与lexer、解析器和抽象语法一起生成模块,以确保文档与语言的实现。在目前的形式中,该文件已进行了一些手动编辑。

BNF的词汇结构

标识符

标识符是以字母开头、后跟的无引号字符串保留字母、数字和字符_的任意组合词语除外。

直接常量

字符串常量字符串有这个表格" x个 ",其中x个是任何任何字符的序列,除了"除非前面有\.

整数字面值整数是非空的数字序列。

字符文字字符有这个表格' c(c) ',其中c(c)是任何单个字符。

保留文字和符号

保留字集是出现在语法。由非字母字符组成的保留字是称为符号,它们的处理方式与类似于标识符。lexer遵循熟悉的规则Haskell、C和Java等语言,包括最长匹配和间距习俗。

BNF中使用的保留字如下:

字符强制注释数字入口点eps内部的下布局字母非空的位置规则分离器挡块终结符标记顶层上面的

BNF中使用的符号如下:

; .::=[ ] _( : ), | -* + ?{ }

评论

单行注释以--.
多行注释(又名块注释)被封闭在{--}.

LBNF的句法结构

非端子封闭在<>.符号::=(生产),|(工会)和ε(空规则)属于BNF符号。所有其他符号都是端子(有时甚至::=|).

<语法> ::= <列表定义>

<列表定义>
  ::=ε|<Def(定义)>|<Def(定义)>;<列表定义>| ;<列表定义>

<Def(定义)>
  ::=入口点<ListCat(列表猫)>|<标签>.<> ::= <ListItem(列表项)>|内部<标签>.<> ::= <ListItem(列表项)>|分离器<最小尺寸> <> <字符串>|终止器<最小尺寸> <> <字符串>|胁迫<标识符> <整数>|规则<标识符> ::= <列表RHS>|评论<字符串>|评论<字符串> <字符串>|代币<标识符> <规则>|位置标记<标识符> <规则>|布局<列表字符串>|布局停止点<列表字符串>|布局顶层<ListCat(列表猫)>
  ::= <>|<>,<ListCat(列表猫)>

<ListItem(列表项)>
  ::=ε|<项目> <列表项>

<项目>
  ::= <字符串>|<>

<>
  ::=[<>]|<标识符>

<标签>
  ::= <标识符>| _| [ ]| ( : )| ( : [ ] )<列表字符串>
  ::= <字符串>|<字符串>,<列表字符串>

<列表RHS>
  ::= <右侧>|<右侧>|<列表RHS>

<右侧> ::= <ListItem(列表项)>

<最小尺寸>
  ::=ε|非空的<规则>
  ::= <规则>|<注册1>|<注册1>

<注册1>
  ::= <注册1><注册2>|<规则2>

<注册2>
  ::= <注册2> <注册号3>|<规则3>

<注册号3>
  ::= <注册号3>*|<注册号3>+|<注册号3>?|每股收益|<字符>| [<字符串>]| {<字符串>}|数字|信件|上部|较低|字符| (<规则>)