Parsing 高阶函数的无歧义文法

Parsing 高阶函数的无歧义文法,parsing,grammar,language-design,higher-order-functions,Parsing,Grammar,Language Design,Higher Order Functions,我的语法是这样的: <type> ::= <base_type> <optional_array_size> <optional_array_size> ::= "[" <INTEGER_LITERAL> "]" | "" <base_type> ::= <integer_type> | <real_type> | <function_type> <function_type&g

我的语法是这样的:

<type> ::= <base_type> <optional_array_size>
<optional_array_size> ::= "[" <INTEGER_LITERAL> "]" | ""

<base_type> ::= <integer_type> | <real_type> | <function_type>

<function_type> ::= "(" <params> ")" "->" <type>

<params> ::= <type> <params_tail> | ""
<params_tail> ::= "," <type> <params_tail> | ""

问题是
first(params)
包含
”(“
,因为函数类型可以作为函数参数传递:
((Integer)->Real,Real)->Integer
。在我修改语法之前,此语法是有效的,但现在不再有效。如何修改语法以获得所需内容?

这绝对是一个挑战

为该语言编写LR语法要容易得多,尽管这仍然是一个挑战。首先,有必要消除其中的歧义

<type> ::= <base_type> <optional_array_size>
<base_type> ::= <function_type>
<function_type> ::= "(" <params> ")" "->" <type>
不幸的是,该语法是LR(2),而不是LR(1)

( Integer   ) [ 42 ]
( Integer   ) -> Integer
          ^
          |
          +----------------- Lookahead
在先行点,解析器仍然不知道它是在查看(冗余的)括号类型还是函数类型中的参数列表。在看到以下符号(除了上面的两个选项外,可能是输入的结尾)之前,它不会知道。在这两种情况下,它都需要将
整数
减少到
,然后再减少到
。但是,在第一种情况下,它可以只移动右括号,而在第二种情况下,它需要继续减少,先减少到
,然后减少到
。这是一个移位-减少冲突。当然,它可以通过l轻松解决多看一个将来的标记,但需要看到两个将来的标记,这就是语法LR(2)的原因

幸运的是,LR(k)语法总是可以简化为LR(1)语法。(顺便说一句,LL(k)语法不是这样的。)它只是变得有点混乱,因为有必要引入一点冗余。我们这样做是通过避免减少
直到我们知道我们有一个参数列表,这意味着我们需要接受
()“”“
而不提交到一个或另一个解析。这会导致以下情况,其中在
中添加了明显冗余的规则,并且
被修改为接受0或至少两个参数:

<type>        ::= <atomic_type> <optional_array_size>
              |   <function_type>
<atomic_type> ::= <integer_type>
              |   <real_type>
              |   "(" <type> ")"
<function_type> ::= "(" <opt_params> ")" "->" <type>
              |   "(" <type> ")" "->" <type>
<opt_params>  ::= ""
              |   <params2>
<params2>     ::= <type> "," <type>
              |   <params2> "," <type>
:=
|   
::= 
|   
|   "("  ")"
::= "("  ")" "->" 
|   "("  ")" "->" 
::= ""
|   
::=  "," 
|    "," 
现在,我个人就到此为止。这里有很多LR解析器生成器,上面的语法是LALR(1),并且仍然相当容易阅读。但是,可以将其转换为LL(1)语法,需要做大量的工作。(我使用语法转换工具来做一些转换。)

直接删除左递归,然后左因子语法:

# Not LL(1)
<type>          ::= <atomic_type> <opt_size>
                |   <function_type>
<opt_size>      ::= ""
                |   "[" integer "]"
<atomic_type>   ::= <integer_type>
                |   <real_type>
                |   "(" <type> ")"
<function_type> ::= "(" <fop>
<fop>           ::= <opt_params> ")" to <type>
                |   <type> ")" to <type>
<opt_params>    ::= ""
                |   <params2>
<params2>       ::= <type> "," <type> <params_tail>
<params_tail>   ::= "," <type> <params_tail>
                |   ""
#非LL(1)
::=  
|   
::= ""
|“[“整数”]”
::= 
|   
|   "("  ")"
::= "(" 
::=”)至
|“)至
::= ""
|   
::=  ","  
::= ","  
|   ""
但这还不够,因为
都可以以
开头(“
。在参数列表的产品之间也存在类似的问题。为了解决这些问题,我们需要另一种技术:将非终端扩展到同一个非终端中,以便将冲突保留到同一个非终端中。与本例一样,这通常以一些重复为代价

通过扩展
,我们得到:

<type>     ::= <integer_type> <opt_size>
           |   <real_type> <opt_size>
           |   "(" <type> ")" <opt_size>
           |   "(" ")" "->" <type>
           |   "(" <type> ")" "->" <type>
           |   "(" <type> "," <type> <params2> ")" "->" <type>
<opt_size> ::= "" 
           |   "[" INTEGER_LITERAL "]"
<params2>  ::= ""
           |   "," <type> <params2>
:=
|    
|   "("  ")" 
|   "(" ")" "->" 
|   "("  ")" "->" 
|   "("  ","   ")" "->" 
::= "" 
|“[“整型文字”]”
::= ""
|   ","  
然后我们就可以留下生产要素

<type>     ::= <integer_type> <opt_size>
           |   <real_type> <opt_size>
           |   "(" <fop>
<fop>      ::= <type> <ftype>
           |   ")" "->" <type>
<ftype>    ::= ") <fcp>
           |   "," <type> <params2> ")" "->" <type>
<fcp>      ::= <opt_size>
           |   "->" <type>
<opt_size> ::= ""
           |   "[" INTEGER_LITERAL "]"
<params2>  ::= ""
           |   "," <type> <params2>
:=
|    
|   "(" 
::=  
|   ")" "->" 
::= ") 
|   ","   ")" "->" 
::= 
|   "->" 
::= ""
|“[“整型文字”]”
::= ""
|   ","  

这就是LL(1)。我将把它作为一个练习,将所有适当的操作重新附加到这些产品上。

这绝对是一个挑战

为该语言编写LR语法要容易得多,尽管这仍然是一个挑战。首先,有必要消除其中的歧义

<type> ::= <base_type> <optional_array_size>
<base_type> ::= <function_type>
<function_type> ::= "(" <params> ")" "->" <type>
不幸的是,该语法是LR(2),而不是LR(1)

( Integer   ) [ 42 ]
( Integer   ) -> Integer
          ^
          |
          +----------------- Lookahead
在先行点,解析器仍然不知道它是在查看(冗余的)括号类型还是函数类型中的参数列表。在看到以下符号(除了上面的两个选项外,可能是输入的结尾)之前,它不会知道。在这两种情况下,它都需要将
整数
减少到
,然后再减少到
。但是,在第一种情况下,它可以只移动右括号,而在第二种情况下,它需要继续减少,先减少到
,然后减少到
。这是一个移位-减少冲突。当然,它可以通过l轻松解决多看一个将来的标记,但需要看到两个将来的标记,这就是语法LR(2)的原因

幸运的是,LR(k)语法总是可以简化为LR(1)语法。(顺便说一句,LL(k)语法不是这样的。)它只是变得有点混乱,因为有必要引入一点冗余。我们这样做是通过避免减少
直到我们知道我们有一个参数列表,这意味着我们需要接受
”(“”)“
而不提交一个或另一个分析。这导致了以下情况,其中向
添加了明显冗余的规则,并且
被修改为接受0或至少两个参数:

<type>        ::= <atomic_type> <optional_array_size>
              |   <function_type>
<atomic_type> ::= <integer_type>
              |   <real_type>
              |   "(" <type> ")"
<function_type> ::= "(" <opt_params> ")" "->" <type>
              |   "(" <type> ")" "->" <type>
<opt_params>  ::= ""
              |   <params2>
<params2>     ::= <type> "," <type>
              |   <params2> "," <type>
:=
|   
::= 
|   
|   "("  ")"
::= "("  ")" "->" 
|   "("  ")" "->" 
::= ""
|   
::=  "," 
|    ","