import Control.Applicative (Alternative (empty, many, some, (<|>)), (<**>))
import Data.Char (isSpace, isDigit)
import Data.Maybe (listToMaybe)
编写一个基本的、低效的解析库实际上并不难,只需不到 50 行代码即可完成。核心类型如下所示:
newtype Parser a = Parser (String -> [(a, String)])
parse :: Parser a -> String -> Maybe a
parse (Parser p) s = listToMaybe $ fst <$> p s
此解析器部分使用字符串并返回解析结果a 以及剩余字符串。但是可能有许多解析替代方案,这就是它返回结果和余数列表的原因。
为了使用这种类型,我们需要更多的实用程序。我留下了_s 供您实施。
instance Functor Parser where
fmap (Parser p) = _
instance Applicative Parser where
pure a = Parser $ \s -> (a, s) -- Consumes nothing and returns a
Parser pf <*> Parser pa = _ -- Parse pf, then parse pa and apply the result
-- of pf to that of pa.
instance Alternative Parser where
empty = Parser $ \s -> [] -- Matches nothing
Parser p1 <|> Parser p2 = _ -- Matches either p1 or if that fails p2.
satisfy :: (Char -> Bool) -> Parser Char
satisfy = _
space :: Parser ()
space = () <$ satisfy isSpace
spaces :: Parser ()
spaces = () <$ many space
char :: Char -> Parser Char
char c = satisfy (c ==)
-- | Detects the end of file.
eof :: Parser ()
eof = _
-- | Succeeds when at the end of a word, without consuming any input
eow :: Parser ()
eow = _
现在我们可以继续使用这个解析器,就像使用任何递归下降解析器一样:
data Expr = Val Integer
| Expr :*: Expr
| Expr :+: Expr
deriving Show
parseVal :: Parser Expr
parseVal =
char '(' *> parseAdd <* char ')' <|>
Val . read <$> some (satisfy isDigit) <* eow
parseMul :: Parser Expr
parseMul = _
parseAdd :: Parser Expr
parseAdd = _
parseExpr :: Parser Expr
parseExpr = parseAdd <* eof