混合解析器字符(词法分析器?)与解析器字符串

Mixing Parser Char (lexer?) vs. Parser String

我写过几个编译器,熟悉 flex/bison、JavaCC、JavaCup、antlr4 等中的词法分析器、regexs/NFAs/DFAs、解析器和语义规则。

是否有某种神奇的单子运算符可以无缝 grows/combines 混合了 Parser Char(即 Text.Megaparsec.Char)与 Parser String 的令牌?

是否有一种方法/最佳实践来表示词法标记和非终结符期望的完全分离?

fmap (: [])(或fmap purepure <$>)没有任何令人讨厌或骇人听闻的地方——这是自然而然的事情,执行简洁、安全、富有表现力和透明的转换同时

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

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

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

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

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

如果您发现自己试图在很多情况下从复杂的 Parser CharParser String 解析器序列中构建 Parser String,那么您可以定义一些辅助运算符.如果您发现各种运算符很烦人,您可以只定义 (<++>)charToStr 的缩写形式,例如 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])