これは、例えば、regularライブラリを使用して行うことができます。応用的-関手インターフェースが付属して最も人気のパーサコンビネータライブラリの
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Applicative
import Generics.Regular
少なくとも2:このライブラリでの作業、一般的にいくつかの言語拡張を必要とuu-parsinglib、例えば、参照およびparsecが、簡単なものを維持するためにここで単純な成功リストパーサを使用しましょう。必要に応じて、あなたのレコードタイプのため、
class Parseable a where
getParser :: Parser a
instance Parseable Int where
getParser = pInt
instance Parseable Float where
getParser = pFloat
そして:
newtype Parser a = Parser {runParser :: ReadS a}
instance Functor Parser where
fmap f p = Parser $ \s -> [(f x, s') | (x, s') <- runParser p s]
instance Applicative Parser where
pure x = Parser $ \s -> [(x, s)]
p <*> q = Parser $ \s ->
[(f x, s'') | (f, s') <- runParser p s, (x, s'') <- runParser q s']
instance Alternative Parser where
empty = Parser $ \_ -> []
p <|> q = Parser $ \s -> runParser p s ++ runParser q s
(。なおtype ReadS a = String -> [(a, String)]
)
pSym :: Char -> Parser Char
pSym c = Parser $ \s -> case s of
(c' : s') | c == c' -> [(c', s')]
_ -> []
pInt :: Parser Int
pInt = Parser reads
pFloat :: Parser Float
pFloat = Parser reads
素直に、我々は今
data Record = Record {i :: Int, f :: Float}
instance Parseable Record where
getParser = Record <$> pInt <* pSym ' ' <*> pFloat
どのように一般的にそのようなパーサーを生成する?
まず、我々は(詳細についてはregularのマニュアルを参照してください)Record
のいわゆるパターンファンクタを定義します。
type instance PF Record = K Int :*: K Float
その後、我々は型クラスRegular
のRecord
インスタンスを作る:
instance Regular Record where
from (Record n r) = K n :*: K r
to (K n :*: K r) = Record n r
class ParseableF f where
getParserF :: Parser a -> Parser (f a)
instance ParseableF (K Int) where
getParserF _ = K <$> pInt
instance ParseableF (K Float) where
getParserF _ = K <$> pFloat
instance (ParseableF f, ParseableF g) => ParseableF (f :*: g) where
getParserF p = (:*:) <$> getParserF p <* pSym ' ' <*> getParserF p
:
次に、我々は一般的なパーサを定義します
(すべての通常のタイプをカバーするために、あなたには、いくつかの複数のインスタンスを提供する必要がありますが、これらはあなたの例のために行います。)
今、私たちはクラスRegular
(内のすべての種類がためParseableF
インスタンスを与えられたことを証明することができますそのパターンファンクタ)にはパーサが付属しています:
instance (Regular a, ParseableF (PF a)) => Parseable a where
getParser = to <$> getParserF getParser
スピンしてみましょう。 Parseable
の元のインスタンス(つまり、Int
、Float
、もちろんRecord
のインスタンス)を削除し、1つの汎用インスタンスのみを保持します。ここに私達は行く:
> runParser (getParser :: Parser Record) "42 3.14"
[(Record {i = 42, f = 3.14},"")]
注:これは、通常のライブラリを使用して、一般的なパーサを導出する方法のほんの非常に基本的な例です。図書館にはgeneric list-of-successes parserが付いています。これは特に素晴らしいことです。そのことを最初に確認したいかもしれません。さらに、ライブラリにはTemplate Haskellサポートが付属しており、Regular
のインスタンスを自動的に派生させることができます。これらのインスタンスには、レコードラベル用の特別な構造体型が含まれているため、汎用関数で実際にレコード型を扱うことができます。ドキュメントをチェックしてください。
だけ明確にする:あなたはあなたのために生成される '解析可能なRecord'のインスタンスをしたいですか? – kosmikus