Parsing 换行符敏感语言中if语句的语法

Parsing 换行符敏感语言中if语句的语法,parsing,grammar,bison,Parsing,Grammar,Bison,我正在研究一种与英语读起来非常相似的语言,并且对if语句的语法有问题。如果你好奇的话,这门语言的灵感来自于,所以我试图确保我匹配了这门语言中所有的有效结构。我使用的示例输入演示了可以查看的所有可能的if结构。有很多,所以我不想内联代码 我从语法中删除了大多数其他结构,使其更易于阅读,但语句基本上如下所示: start : statementList ; statementList : '\n' | statement '\n' | statementList '

我正在研究一种与英语读起来非常相似的语言,并且对
if
语句的语法有问题。如果你好奇的话,这门语言的灵感来自于,所以我试图确保我匹配了这门语言中所有的有效结构。我使用的示例输入演示了可以查看的所有可能的
if
结构。有很多,所以我不想内联代码

我从语法中删除了大多数其他结构,使其更易于阅读,但语句基本上如下所示:

start
    : statementList
;

statementList
    : '\n'
    | statement '\n'
    | statementList '\n'
    | statementList statement '\n'
;

statement
    : ID
    | ifStatement
;
我看到的移位/减少冲突在ifStatement规则中:

ifStatement
    : ifCondition THEN statement
    | ifCondition THEN statement ELSE statement
    | ifCondition THEN statement ELSE '\n' statementList END IF
    | ifCondition THEN '\n' statementList END IF
    | ifCondition THEN '\n' END IF
    | ifCondition THEN '\n' ELSE statement
    | ifCondition THEN '\n' ELSE '\n' statementList END IF
    | ifCondition THEN '\n' statementList ELSE statement
    | ifCondition THEN '\n' statementList ELSE '\n' statementList END IF
// The following rules cause issues, but should be legal:
    | ifCondition THEN statement newlines ELSE statement
    | ifCondition THEN statement newlines ELSE '\n' statementList END IF
;

ifCondition
    : IF expression
    | IF expression '\n'
;

expression
    : TRUE
    | FALSE
;

newlines
    : '\n'
    | newlines '\n'
;
问题是我需要支持这个构造:

if true then statement # <- Any number of newlines
else statement

如果为true,那么语句#要正确理解这一点非常困难,因此我尝试对这些步骤进行注释。有很多令人讨厌的细节

在其核心,这只是悬空的else歧义的一种表现形式,其解决方案是众所周知的(强制解析器总是移动
else
)。下面的解决方案解决了语法本身的歧义,这是明确的

我在这里使用的基本原则是几十年前阿尔弗雷德·阿霍(Alfred Aho)和杰弗里·厄尔曼(Jeffrey Ullman)在书中概述的原则(所谓的“龙书”,我之所以提到它,是因为它的作者最近正是为了这一点和他们其他有影响力的作品)。特别是,我使用“匹配”和“不匹配”(而不是“打开”和“关闭”,这两个词也很流行),因为这是我学习的方式

使用优先级声明也可以解决这个语法问题;事实上,这往往要简单得多。但是在这种特殊情况下,使用运算符优先级并不容易,因为相关的标记(
else
)前面可以有任意数量的换行符。我很确定您仍然可以构造基于优先级的解决方案,但是使用明确的语法有很多优点,包括易于移植到不使用相同优先级算法的解析器生成器,以及可以进行机械分析的事实

解决方案的基本要点是将所有语句分为两类:

  • “matched”(或“closed”)语句,在不可能使用
    else
    子句扩展语句的意义上是完整的。(换句话说,每个
    if…then
    由相应的
    else
    匹配)这些
  • “unmatched”(或“open”)语句,可以用
    else
    子句进行扩展。(换句话说,至少有一个
    if…then
    子句未与
    else
    匹配)由于未匹配的语句是一个完整语句,因此不能立即后跟
    else
    标记;如果出现
    else
    标记,它将用于扩展语句
一旦我们设法为这两类语句构造语法,就只需要找出在歧义语法中
语句
的哪些用法后面可以跟
else
。在所有这些上下文中,非终结符
语句
必须替换为非终结符
匹配语句
,因为只有匹配语句后面可以跟
else
,而不与之交互。在其他上下文中,如果
else
不能作为下一个标记,则两种类型的语句都是有效的

因此,基本的语法风格是(摘自《龙书》):

stmt→ 匹配测试
|无与伦比的
匹配测试→ 如果“expr”,则“matched”或“else”匹配
|其他
无与伦比的→ 如果是“expr”,则是“matched_stmt”否则是“unmatched_stmt”
|如果“expr”则“stmt”
other\u stmt
不是条件语句。或者,更准确地说,除了以
stmt
结尾的复合语句之外的任何语句

在Hypertalk中,据我所知,
if
语句是唯一可以以语句结尾的复合语句。其他复合语句以
end X
精确终止,从而有效地关闭语句。但是在其他语言中,比如C语言,有各种各样的复合语句,其中大多数需要被分为“匹配”和“不匹配”,这取决于它们的终止子语句是(递归地)匹配还是不匹配

我想在这里注意一件事,如果你稍微从侧面看一下,从大纲语法中可以明显看出,
if…then…else
语句的
if
部分在语法上类似于括号中的前缀操作符。也就是说,
matched_stmt
unmatched_stmt
都类似于一元负号的右递归规则:

一元→ '-' 一元
|原子
反过来,可以用扩展的BNF方言书写,允许Kleene stars作为

一元→ (“-”)*原子
如果我们对Aho&Ullman的语法进行转换,我们最终会得到:

  if_then_else → "if" expr "then" matched_stmt "else"
  matched_stmt → (if_then_else)* other_stmt
unmatched_stmt → (if_then_else)* "if" expr "then" stmt
这相当清楚地说明了如何使用自顶向下的递归下降解析器实现该语法。(需要一点左因子分解,但它仍然类似于一元减语法。)我不打算在这个答案中进一步发展这个想法,但我认为EBNF转换有助于引导直觉,了解这个语法实际上是如何工作的,从而解开其他语法

这对于弄清楚如何处理新词也很有帮助。(对我来说)关键的见解是语句必须以换行符结尾。一个例外是
if
命令的压缩单行版本。但该异常仅发生在
else
标记之前(并且仅当
then
与该标记匹配时)
ifCondition THEN EOL ELSE statement