混合解析器字符(词法分析器?)与解析器字符串
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 pure
或pure <$>
)没有任何令人讨厌或骇人听闻的地方——这是自然而然的事情,执行简洁、安全、富有表现力和透明的转换同时
我不会真正推荐的替代方案,但在某些情况下它可能最能表达意图:sequence [<em>charParser</em>]
.这清楚地表明您正在执行字符解析器列表中的“所有”解析器,并将结果“s”收集为字符“s”列表。
通常,使用应用操作直接组合 Parser Char
和 Parser String
,而不是 "upgrading" 前者。例如,必须以字母开头的字母数字标识符的解析器可能如下所示:
ident :: Parser String
ident = (:) <$> letterChar <*> alphaNumChar
如果你正在做一些更复杂的事情,比如用可选的美分解析美元金额,例如,你可以写:
dollars :: Parser String
dollars = (:) <$> char '$' <*> some digitChar
<**> pure (++)
<*> option "" ((:) <$> char '.' <*> replicateM 2 digitChar)
如果您发现自己试图在很多情况下从复杂的 Parser Char
和 Parser 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])
。
我写过几个编译器,熟悉 flex/bison、JavaCC、JavaCup、antlr4 等中的词法分析器、regexs/NFAs/DFAs、解析器和语义规则。
是否有某种神奇的单子运算符可以无缝 grows/combines 混合了 Parser Char
(即 Text.Megaparsec.Char
)与 Parser String
的令牌?
是否有一种方法/最佳实践来表示词法标记和非终结符期望的完全分离?
fmap (: [])
(或fmap pure
或pure <$>
)没有任何令人讨厌或骇人听闻的地方——这是自然而然的事情,执行简洁、安全、富有表现力和透明的转换同时
我不会真正推荐的替代方案,但在某些情况下它可能最能表达意图:sequence [<em>charParser</em>]
.这清楚地表明您正在执行字符解析器列表中的“所有”解析器,并将结果“s”收集为字符“s”列表。
通常,使用应用操作直接组合 Parser Char
和 Parser String
,而不是 "upgrading" 前者。例如,必须以字母开头的字母数字标识符的解析器可能如下所示:
ident :: Parser String
ident = (:) <$> letterChar <*> alphaNumChar
如果你正在做一些更复杂的事情,比如用可选的美分解析美元金额,例如,你可以写:
dollars :: Parser String
dollars = (:) <$> char '$' <*> some digitChar
<**> pure (++)
<*> option "" ((:) <$> char '.' <*> replicateM 2 digitChar)
如果您发现自己试图在很多情况下从复杂的 Parser Char
和 Parser 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])
。