我正在阅读此bison介绍。
我有两个问题,如果有人可以帮助我理解,那将会很棒:
术语context free grammar
的含义是什么?
从上面的链接:Bison不是所有的无上下文语言都可以处理,只有那些是LALR(1)。简而言之,这意味着必须能够告诉如何使用单个前瞻标记来解析输入字符串的任何部分。它是什么意思“可以告诉如何只用一个前瞻标记来解析输入字符串的任何部分”
答案 0 :(得分:21)
无上下文语法是对一组字符串的描述,这些字符串严格地比正则表达式更强大,但仍然可以由机器轻松处理。更正式地说,无上下文语法包含四个方面:
一组终端符号,它们是正在生成的字符串的元素。对于野牛解析器,这通常是扫描仪生成的令牌集。对于像英语这样的自然语言的语法,这可能是所有英语单词的集合。
一组非终结符号。直觉上,非终结符号表示某种词性,例如“名词”或“动词”。
一组制作。每个制作都说明如何用其他一组终端和非终结符替换非终结符号。例如,制作Variable -> Type Name
表示如果我们看到非终结Variable
,我们可以将其替换为字符串Type Name
。
一个起始符号,它是派生开始的非终结符号。
作为一个例子,考虑这种简单的无上下文语法用于C风格的函数声明:
Function -> Type ident(Arguments)
Type -> int
Type -> Type *
Arguments -> e (the empty string)
Arguments -> ArgList
ArgList -> Type ident
ArgList -> Type ident, ArgList
此处,起始符号为Function
。鉴于此语法,我们可以通过重复选择非终结符号并将其替换为匹配作品的右侧之一来生成C样式的函数声明。在每一步中,我们到目前为止构建的字符串称为句子形式。例如,以下是一些可以从上面的语法派生的不同函数声明:
Sentential Form Production
-------------------------------------------------------------------
Function Function -> Type ident(Arguments)
Type ident(Arguments) Type -> int
int ident(Arguments) Arguments -> e
int ident()
Sentential Form Production
-------------------------------------------------------------------
Function Function -> Type ident(Arguments)
Type ident(Arguments) Type -> Type*
Type* ident(Arguments) Type -> int
int* ident(Arguments) Arguments -> ArgList
int* ident(ArgList) ArgList -> Type ident, ArgList
int* ident(Type ident, ArgList) ArgList -> Type ident
int* ident(Type ident, Type ident) Type -> Type*
int* ident(Type* ident, Type ident) Type -> Type*
int* ident(Type** ident, Type ident) Type -> int
int* ident(int** ident, Type ident) Type -> int
int* ident(int** ident, int ident)
大多数编程语言都有一个可以通过无上下文语法描述的结构。解析器的工作是获取程序和语法,并确定语法如何生成该程序。
对于LALR(1),遗憾的是LALR(1)的正式定义并非无足轻重。我刚刚完成了编译器构建课程的教学,在第一次花两个讲座讨论相关的解析技术后,我们才能谈论LALR(1)。如果您想要对材料进行正式介绍,我可以使用自下而上解析的幻灯片 at the course website 。
LALR(1)是一种称为自下而上解析算法的解析算法,这意味着它试图以相反的顺序将语法的产生应用于 reduce 程序回到开始符号。例如,让我们考虑一下这个字符串,它是由上面的语法生成的:
int** ident(int* ident)
在自下而上的解析中,我们将通过一次查看一个令牌来解析该字符串。每当我们发现可以逆转到某些非终结的东西时,我们就会这样做。 (更确切地说,LALR(1)仅在满足其他条件时才进行这些减少,以便算法具有更多上下文,但对于此示例,我们不需要担心这一点)。解析中的每个步骤都被称为 shift 或 reduce 。 shift 意味着我们会查看输入的另一个标记,以获取有关要应用的减少量的更多信息。 reduce 意味着我们会使用一些令牌和非终结符并反转生产以返回到某些非终结符。
这是字符串自下而上解析的痕迹:
Workspace Input Action
-----------------------------------------------------------------
int** ident(int* ident) Shift
int ** ident(int* ident) Reduce Type -> int
Type ** ident(int* ident) Shift
Type* * ident(int* ident) Reduce Type -> Type*
Type * ident(int* ident) Shift
Type* ident(int* ident) Reduce Type -> Type*
Type ident(int* ident) Shift
Type ident (int* ident) Shift
Type ident( int* ident) Shift
Type ident(int * ident) Reduce Type -> int
Type ident(Type * ident) Shift
Type ident(Type* ident) Reduce Type -> Type*
Type ident(Type ident) Shift
Type ident(Type ident ) Reduce ArgList -> Type ident
Type ident(ArgList ) Reduce Arguments -> ArgList
Type ident(Arguments ) Shift
Type ident(Arguments) Reduce Function -> Type ident(Arguments)
Function ACCEPT
了解轮班和减少非常重要,因为在使用野牛时,你会遇到转移/减少冲突和减少/减少冲突。这些错误意味着解析器生成器确定解析器可能进入无法判断是移位还是减少的状态,或者它应该执行的两次降低中的哪一个。有关如何处理此问题的更多详细信息,请参阅野牛手册。
如果您想要了解更多关于无上下文语法和解析算法的信息,Grune提供了一本名为 Parsing Techniques: A Practical Guide, Second Edition 的优秀书籍。到目前为止雅各布斯对我所见过的材料的最佳处理方式。它涵盖了各种解析算法,包括许多比LALR(1)更强大的技术,这些技术开始得到更广泛的使用(例如,GLR和Earley)。我强烈推荐这本书 - 这是我理解解析的主要原因!
希望这有帮助!
答案 1 :(得分:1)
1)简单来说 - 这意味着代码的格式化和上下文对于各个部分并不重要,并且您没有看到它是如何格式化以理解其含义的。例如,您可以在一行中编写C程序,或者在不同的行上编写每个单词,程序的含义相同。 Wikipedia有更正式的定义。
2)LALR(1)意味着什么更复杂,但简单来说,我会描述通过当时读一个单词来逐步理解意义 - 只看到下一个单词/符号 - 一些口语因为不是LALR而闻名(1) - 当你到达句子末尾时,你不明白句子是一个问题或一个陈述。一些编程语言也可以这样构造,但我不知道任何编程语言,因为它们出于实际原因都符合LALR(1)语法。但是我认为有一些例外情况,C / C ++需要2个符号才能正确解析语句(因此是LALR(2),但由于我不记得它们是什么,我希望有人会指出它在评论中。
在任何情况下,this book here都是理解编译器和解析器的圣经。