我编写了几个编译器,并且熟悉 flex/bison、JavaCC、JavaCup、antlr4 等中的词法分析器、正则表达式/NFA/DFA、解析器和语义规则。
是否有某种神奇的单子运算符可以无缝地增长/组合令牌与Parser Char
(ie Text.Megaparsec.Char
) 与的混合Parser String
?
有没有一种方法/最佳实践来表示词法标记和非终端期望的清晰分离?
我编写了几个编译器,并且熟悉 flex/bison、JavaCC、JavaCup、antlr4 等中的词法分析器、正则表达式/NFA/DFA、解析器和语义规则。
是否有某种神奇的单子运算符可以无缝地增长/组合令牌与Parser Char
(ie Text.Megaparsec.Char
) 与的混合Parser String
?
有没有一种方法/最佳实践来表示词法标记和非终端期望的清晰分离?
通常,使用应用操作直接组合Parser Char
和Parser String
s,而不是“升级”前者。例如,必须以字母开头的字母数字标识符的解析器可能如下所示:
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
如果您发现各种运算符令人讨厌,您可以定义(<++>)
和一个缩写形式charToStr
like 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])
如果您愿意,如果您认为它看起来更清晰,请写下。
fmap (: [])
(or fmap pure
or )没有什么讨厌的或骇人听闻的pure <$>
东西——这是很自然的事情,同时执行简洁、安全、富有表现力和透明的转换。
我不会真正推荐的替代方案,但在某些情况下,它可能最好地表达意图:. 这清楚地表明您正在执行字符解析器列表中的“所有”解析器,并将结果“s”收集为字符“s”列表。sequence [charParser]