2

我编写了几个编译器,并且熟悉 flex/bison、JavaCC、JavaCup、antlr4 等中的词法分析器、正则表达式/NFA/DFA、解析器和语义规则。

是否有某种神奇的单子运算符可以无缝地增长/组合令牌与Parser Char(ie Text.Megaparsec.Char) 与的混合Parser String

有没有一种方法/最佳实践来表示词法标记和非终端期望的清晰分离?

4

2 回答 2

3

通常,使用应用操作直接组合Parser CharParser Strings,而不是“升级”前者。例如,必须以字母开头的字母数字标识符的解析器可能如下所示:

ident :: Parser String
ident = (:) <$> letterChar <*> alphaNumChar

如果你正在做一些更复杂的事情,比如用可选的美分解析美元金额,你可以这样写:

dollars :: Parser String
dollars = (:) <$> char '$' <*> some digitChar
          <**> pure (++)
          <*> option "" ((:) <$> char '.' <*> replicateM 2 digitChar)

如果您发现自己在很多情况下都试图Parser String从复杂的序列Parser Char和解析器中构建出来,那么您可以定义一些辅助运算符。Parser String如果您发现各种运算符令人讨厌,您可以定义(<++>)和一个缩写形式charToStrlike c :: Parser Char -> Parser String

(<.+>) :: Parser Char -> Parser String -> Parser String
p <.+> q = (:) <$> p <*> q
infixr 5 <.+>

(<++>) :: Parser String -> Parser String -> Parser String
p <++> q = (++) <$> p <*> q
infixr 5 <++>

(<..>) :: Parser Char -> Parser Char -> Parser String
p <..> q = p <.+> fmap (:[]) q
infixr 5 <..>

所以你可以写这样的东西:

dollars' :: Parser String
dollars' = char '$' <.+> some digitChar 
           <++> option "" (char '.' <.+> digitChar <..> digitChar)

正如@leftroundabout 所说,关于fmap (:[]). fmap (\c -> [c])如果您愿意,如果您认为它看起来更清晰,请写下。

于 2018-08-12T23:42:28.457 回答
2

fmap (: [])(or fmap pureor )没有什么讨厌的或骇人听闻的pure <$>东西——这是很自然的事情,同时执行简洁、安全、富有表现力和透明的转换。

我不会真正推荐的替代方案,但在某些情况下,它可能最好地表达意图:. 这清楚地表明您正在执行字符解析器列表中的“所有”解析器,并将结果“s”收集为字符“s”列表。sequence [charParser]

于 2018-08-12T23:31:55.300 回答