【问题标题】:Parsing left-recursive grammar in sum-type inifinite recursion在和型无限递归中解析左递归文法
【发布时间】:2017-11-24 14:40:15
【问题描述】:

我正在尝试从 ML 中的现代编译器实现 为 Tiger 语言编写解析器,但我被困在其中一种递归类型上。

我有以下类型

data LValue =                                                                                                                       
    Id Atom                                                                                                                         
    | RecordAccess LValue Atom                                                                                                      
    | ArraySubscript LValue Expression  

语法如下:

lvalue -> id
       -> lvalue.id
       -> lvalue[exp]
id -> atom
exp -> (the big, overarching, everything-is-an-expression type)

我正在尝试使用 Parsec 对其进行解析,但我陷入了无限递归循环。这是我当前的基本解析器:

lvalueParser :: Parsec String () LValue                                                                                             
lvalueParser =                                                                                                                      
    try (Id <$> (atomParser <* (notFollowedBy (char '.'))))                                                                         
    <|> try recordAccessParser                                                                                                      
    where recordAccessParser = (uncurry RecordAccess) <$> do {                                                                      
      record <- lvalueParser;                                                                                                         
      char '.';                                                                                                                     
      atom <- atomParser;                                                                                                           
      return (record, atom)                                                                                                      
      }

(注意:我还没有尝试实现任何东西来处理ArrayAccess 部分)

显然,当recordAccessParser 回调lvalueParser 时,就会发生无限循环。

我可以这样更改recordAccessParser

recordAccessParser = (uncurry RecordAccess) <$> do {                                                                      
          record <- atomParser;                                                                                                         
          char '.';                                                                                                                     
          atom <- atomParser;                                                                                                           
          return (Id record, atom)                                                                                                      
          }

然后它终止。但是,它解析记录访问的深度不会超过单层:

Parsec.parse lvalueParser "" "record_1.field_1.field_2"
#=> RecordAccess (Id record_1) (Id field_1)

我期待

#=> RecordAccess (RecordAccess (Id record_1) (Id field_1)) (Id field_2)

我查看了chainl1,但是链接解析器的类型是a -&gt; a -&gt; a,这与反映语法的LValue 的类型不匹配。我还看了many;但是我没有每个术语的常量前缀 - 左递归术语是我试图解析为结果类型的一部分。

我想我缺少 Parsec/解析的特定概念,并希望指出正确的方向。我正在为其编写解析器的语言中有更多类型将具有类似的结构。

【问题讨论】:

    标签: parsing haskell parsec left-recursion


    【解决方案1】:

    使用不支持左递归的工具解析左递归语法的常用方法确实是用重复替换左递归(即many)。对于记录访问,这意味着替换像

    这样的规则
    lvalue ::= lvalue '.' ID
             | primaryLValue
    

    lvalue ::= primaryLValue ('.' ID)*
    

    就 Parsec 而言,这意味着:

    record <- atomParser                                                                                                       
    fields <- many (char '.' >> idParser)
    

    现在您有一个 LValue 和一个包含 0 个或多个字段名称的列表,它们不适合您的 AST 类型,但您可以通过将 RecordAccess 构造函数折叠在列表上来解决这个问题。

    【讨论】:

    • 效果:首先使用不同的语法进行解析,然后修复解析树以反映所需的语法。
    【解决方案2】:

    虽然你不能在这里使用chainl1, 你可以像这样定义chainl1-like 组合子:

    leftRec :: (Stream s m t)
            => ParsecT s u m a -> ParsecT s u m (a -> a) -> ParsecT s u m a
    leftRec p op = rest =<< p
      where
        rest x = do f <- op
                    rest (f x)
              <|> return x
    

    我咨询了here 来实现这一点。 通过使用这个组合器,lvalueParser 可以定义如下:

    lvalueParser :: Parser LValue
    lvalueParser = leftRec idParser (recordAccessModifier <|> arraySubscriptModifier)
      where
        idParser = Id <$> atomParser
        recordAccessModifier = do
          a <- char '.' *> atomParser
          return (\l -> RecordAccess l a)
        arraySubscriptModifier = do
          e <- between (char '[') (char ']') expParser
          return (\l -> ArraySubscript l e)
    

    例子:

    main = parseTest lvalueParser "x.y[2].z"
    -- => RecordAccess (ArraySubscript (RecordAccess (Id 'x') 'y') (ENat 2)) 'z'
    

    其中AtomExpression及其解析器定义如下:

    type Atom = Char
    atomParser :: Parser Atom
    atomParser = letter <?> "atom"
    
    data Expression = ENat Int
      deriving Show
    expParser :: Parser Expression
    expParser = (ENat . read) <$> many digit
    

    【讨论】:

      猜你喜欢
      • 2015-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多