[英]Parsing left-recursive grammar in sum-type inifinite recursion
我正在尝试从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
时,就会发生无限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
a -> a -> a
,并且与反映语法的LValue
类型不匹配。 我也看了many
; 但是我没有为每个术语提供固定的前缀-左递归术语是我要解析为结果类型一部分的内容。
我想我错过了Parsec / parsing的特定概念,很想指出正确的方向。 我正在为其编写解析器的语言中有更多类型,它们将具有类似的构造。
使用不支持左递归的工具来解析左递归语法的通常方法,确实是用重复(即many
)代替左递归。 对于记录访问,这意味着要替换诸如
lvalue ::= lvalue '.' ID
| primaryLValue
同
lvalue ::= primaryLValue ('.' ID)*
就Parsec而言,这意味着:
record <- atomParser
fields <- many (char '.' >> idParser)
现在,您有了一个LValue
和一个0或多个字段名称的列表,该列表不适合您的AST类型,但是您可以通过将RecordAccess
构造函数折叠在列表RecordAccess
解决此问题。
尽管您不能在此处使用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
我在这里咨询以实现此目的。 通过使用此组合器,可以如下定义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
, Expression
及其解析器的定义如下:
type Atom = Char
atomParser :: Parser Atom
atomParser = letter <?> "atom"
data Expression = ENat Int
deriving Show
expParser :: Parser Expression
expParser = (ENat . read) <$> many digit
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.