从头开始解析复数

Parsing a Complex Number from Scratch

给定一个数据类型 data CI = CI Int Int,表示一个复数,我想为 CI 构建一个解析器,可以将 "a" 转换为 CI a 0"(a,b)"CI a b。例如,我想要一个函数 parseCI 这样 runParser parseCI "(1,2)" returns 值 [(CI 1 2, "")] (理想情况下,但类似的东西很好)。我还想使 CI 成为 read 的一个实例。

我想使用下面代码中的函数和定义来执行此操作(基本上,没有任何高级功能,例如 Parsec),但我不确定从哪里开始。一些让我走上正轨的起始代码 and/or 提示会很有帮助。我不是在寻找完整的答案,因为我想自己弄清楚。

module Parser where
import Control.Applicative
import Control.Monad

newtype Parser a = Parser { runParser :: String -> [(a,String)] }

satisfy :: (Char -> Bool) -> Parser Char
satisfy f = Parser $ \s -> case s of
    [] -> []
    a:as -> [(a,as) | f a]

char :: Char -> Parser Char
char = satisfy . (==)

string :: String -> Parser String
string str = Parser $ \s -> [(t,u) | let (t,u) = splitAt (length str) s, str == t]

instance Functor Parser where
    fmap f p = Parser $ \s ->
        [ (f a,t)
        | (a,t) <- runParser p s
        ]

instance Applicative Parser where
    pure a = Parser $ \s -> [(a,s)]
    af <*> aa = Parser $ \s ->
        [ (f a,u) 
        | (f,t) <- runParser af s
        , (a,u) <- runParser aa t
        ]

instance Alternative Parser where
    empty = Parser $ \s -> []
    p1 <|> p2 = Parser $ (++) <$> runParser p1 <*> runParser p2`

instance Monad Parser where
    return = pure
    ma >>= f = Parser $ \s ->
       [ (b,u) 
       | (a,t) <- runParser ma s
       , (b,u) <- runParser (f a) t
       ]

instance MonadPlus Parser where
    mzero = empty
    mplus = (<|>)

您可能已经看过它,但如果您还没有看过:Monadic Parsing in Haskell 像这样设置解析。

由于您有两种不同的解析方式 CI,您可能希望将其作为两个问题来处理:创建一个解析器 parseCI1"a" 解析为 CI a 0并制作另一个解析器 parseCI2"(a,b)" 解析为 CI a b。然后,您可以将它们合二为一

parseCI = parseCI1 <|> parseCI2

对于这两个子解析器,您将需要一些解析整数的方法:parseInt :: Parser Int。在制作 parseInt 时,您可能希望使用 satisfyisDigit, read, and possibly some 的某种组合(取决于您如何解决这个问题)。


在完成 parseCI 后,将 CI 设为 read 的实例会更简单一些:

instance Read CI where
  readsPrec _ = runParser parseCI