我已经编写了多个编译器,并且熟悉lexer,regexs / NFA / DFA,flex / bison,JavaCC,JavaCup,antlr4等中的解析器和语义规则。
是否存在某种神奇的单子运算符,可以无缝地将令牌与Parser Char
(即Text.Megaparsec.Char
)和Parser String
的混合进行增长/组合?
是否有一种方法/最佳实践来表示词汇化标记和非最终期望的清晰区分?
答案 0 :(得分:3)
通常,人们使用应用性操作直接组合num_lines = sum(1 for line in f)
和Parser Char
,而不是“升级”前者。例如,对于必须以字母开头的字母数字标识符的解析器可能看起来像:
Parser String
例如,如果您正在做一些更复杂的事情,例如用可选的美分来分析美元金额,则可以这样写:
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
。
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 <..>
就像@leftroundabout所说的那样,dollars' :: Parser String
dollars' = char '$' <.+> some digitChar
<++> option "" (char '.' <.+> digitChar <..> digitChar)
并没有什么恶意。如果您愿意,可以写fmap (:[])
,如果它看起来更清晰。
答案 1 :(得分:2)
fmap (: [])
(或fmap pure
或pure <$>
)没有任何讨厌或骇人听闻的东西–这是很自然的事情,执行一次简洁,安全,富有表现力和透明的转换同时。
我不会真正推荐的替代方法,但是在某些情况下,它可能最能表达意图:sequence [charParser]
。这清楚表明您正在执行字符解析器列表中的“所有”解析器,并将结果“ s”收集为字符“ s”列表。