Haskell 中的 Monadic Parse 论文中的示例
examples in the paper Monadic Parse in Haskell
在论文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
非常感谢!!
在 chainl1
中,您将对 rest
的递归调用替换为 return
。
rest a = do f <- q
b <- p
rest (f a b)
+++ return a
在论文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
非常感谢!!
在 chainl1
中,您将对 rest
的递归调用替换为 return
。
rest a = do f <- q
b <- p
rest (f a b)
+++ return a