【发布时间】:2017-03-29 11:06:58
【问题描述】:
在论文Monadic Parse in Haskell中,作者给出了一个解析简单算术字符串的例子。我尝试扩展term,将其应用于"1 + 2",但我仍然对解析器的递归性质感到困惑。也就是term如果我做对了会展开成下面的形式
expr = ((digit +++ do {symb "("; n <- expr; symb ")"; return n})
`chianl1` mulop) `chainl1` addop
但是在第一次用digit解析了字符串"1 + 2"中的数字"1"和addop解析了"+"之后,为什么解析器expr会继续解析"2"后面的数字呢?
此外,当我将term 应用到"1 - 2 * 3 + 4"(这是论文中给出的示例)时,我得到的是[(-5,"+ 4")] 而不是[(-1, "")]。是我的代码的问题吗?然而,我已经根据论文中的代码检查了我的代码,并没有发现任何偏差。
下面是我的代码
module Parser where
import Prelude hiding (filter)
import Data.Char (isDigit, isSpace, toUpper, ord)
newtype Parser a = Parser {
runParser :: (String -> [(a, String)])
}
instance Monad Parser where
return a = Parser $ \s -> [(a, s)]
p >>= f = Parser $ \s ->
concat [runParser (f a) s' | (a, s') <- runParser p s]
instance Applicative Parser where
pure a = Parser $ \s -> [(a, s)]
k <*> m = Parser $ \s ->
[(f a, s'') |
(f, s') <- runParser k s,
(a, s'') <- runParser m s']
instance Functor Parser where
fmap f p = Parser $ \s ->
[(f a, s') | (a, s') <- runParser p s]
applyP :: Parser a -> String -> [(a, String)]
applyP p s = runParser p s
emptyP :: Parser a
emptyP = Parser $ \s -> []
appendP :: Parser a-> Parser a-> Parser a
appendP p q = Parser $ \s ->
let xs = runParser p s
ys = runParser q s
in xs ++ ys
(+++) :: Parser a -> Parser a -> Parser a
p +++ q = Parser $ \s ->
case (runParser (p `appendP` q) s) of
[] -> []
(x:xs) -> return x
item :: Parser Char
item = Parser $ \cs ->
case cs of
[] -> []
(c:cs) -> [(c, cs)]
-- since the function tiem is of type "Parser Char"
-- it can only produce char as a result of computation
filterP :: (Char -> Bool) -> Parser Char
filterP f = item >>= \c -> if f c
then return c
else emptyP
-- returns ak result if the prefix char matches
char :: Char -> Parser Char
char c = filterP (\x -> x == c)
-- parses a specific string
string :: String -> Parser String
string [] = return "" -- why it will be an empty list if "emptyP" is used?
string (x:xs) = do c <- char x
cs <- string xs
return (c:cs)
many :: Parser a -> Parser [a]
many p = many1 p +++ (return [])
many1 :: Parser a -> Parser [a]
many1 p = do c <- p
cs <- many p
return (c:cs)
sepby :: Parser a -> Parser b -> Parser [a]
sepby p sep = sepby1 p sep +++ (return [])
sepby1 :: Parser a -> Parser b -> Parser [a]
sepby1 p sep = do c <- p
cs <- many (sep >> p)
return (c:cs)
chainl :: Parser a -> Parser (a -> a -> a) -> a -> Parser a
chainl p q a = (p `chainl1` q) +++ return a
chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a
p `chainl1` q = do {a <- p; rest a}
where rest a = (do f <- q
b <- p
return (f a b))
+++ return a
space :: Parser String
space = many (filterP isSpace)
-- parse a given value, throw away trailing space
token :: Parser a -> Parser a
token p = do {a <- p; space; return a}
-- parses a given token, throws away trailing space
symb :: String -> Parser String
symb s = token (string s)
-- throw away any prefix space, apply parser
apply :: Parser a -> String -> [(a, String)]
apply p = runParser (do {space; p})
addop :: Parser (Int -> Int -> Int)
addop = do {symb "+"; return (+)} +++ do {symb "-"; return (-)}
mulop :: Parser (Int -> Int -> Int)
mulop = do {symb "*"; return (*)} +++ do {symb "/"; return (div)}
digit = do {x <- token (filterP isDigit); return (ord x - ord '0')}
factor = digit +++ do {symb "("; n <- expr; symb ")"; return n}
term = factor `chainl1` mulop
expr = term `chainl1` addop
非常感谢!!
【问题讨论】:
-
是不是就像这里定义的函数实际上是论文“如何用成功列表替换失败”中不同语法的函数?我正在阅读那篇论文,并意识到两者之间的巨大相似之处。