在sum-type无限递归中解析左递归语法

问题描述 投票:2回答:2

我正在尝试从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 -> a -> a,这与反映语法的LValue的类型不匹配。我也看了many;但是我没有每个术语的常量前缀 - 左递归术语是我试图解析成结果类型的一部分。

我想我错过了Parsec /解析的特定概念,并希望指向正确的方向。我正在编写解析器的语言中有更多类型,它们具有相似的结构。

parsing haskell parsec left-recursion
2个回答
1
投票

虽然你不能在这里使用qazxsw poi,你可以像这样定义qazxsw poi-like combinator:

chainl1

我咨询了chainl1来实现这一点。通过使用这个组合器,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' 及其解析器的定义如下:

Atom

3
投票

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

type Atom = Char
atomParser :: Parser Atom
atomParser = letter <?> "atom"

data Expression = ENat Int
  deriving Show
expParser :: Parser Expression
expParser = (ENat . read) <$> many digit

many

就Parsec而言意味着:

lvalue ::= lvalue '.' ID
         | primaryLValue

现在你有一个lvalue ::= primaryLValue ('.' ID)* 和一个0或更多字段名称的列表,它们不适合你的AST类型,但你可以通过在列表上折叠record <- atomParser fields <- many (char '.' >> idParser) 构造函数来解决这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.