파서 컴비네이터(Parser Combinators)는 작은 파서들을 함수로 조합해 복잡한 파서를 구성하는 함수형 프로그래밍 기법이다. 파서 자체가 일급 함수이므로 모듈화와 재사용이 뛰어나다.
기본 구조
haskell
-- 파서 타입: 입력을 받아 결과와 나머지 입력을 반환
newtype Parser a = Parser
{ runParser :: String -> Maybe (a, String) }
instance Functor Parser where
fmap f (Parser p) = Parser $ \input ->
fmap (\(a, rest) -> (f a, rest)) (p input)
instance Applicative Parser where
pure a = Parser $ \input -> Just (a, input)
Parser f <*> Parser a = Parser $ \input -> do
(func, rest1) <- f input
(arg, rest2) <- a rest1
return (func arg, rest2)
instance Monad Parser where
return = pure
Parser p >>= f = Parser $ \input -> do
(a, rest) <- p input
runParser (f a) rest
기본 파서들
haskell
-- 단일 문자 파서
char :: Char -> Parser Char
char c = Parser $ \case
(x:xs) | x == c -> Just (c, xs)
_ -> Nothing
-- 조건 파서
satisfy :: (Char -> Bool) -> Parser Char
satisfy p = Parser $ \case
(x:xs) | p x -> Just (x, xs)
_ -> Nothing
digit = satisfy isDigit
letter = satisfy isAlpha
space = satisfy isSpace
-- 선택 파서 (Alternative)
(<|>) :: Parser a -> Parser a -> Parser a
Parser p1 <|> Parser p2 = Parser $ \input ->
p1 input `mplus` p2 input
-- 반복 파서
many :: Parser a -> Parser [a] -- 0회 이상
many1 :: Parser a -> Parser [a] -- 1회 이상
JSON 파서 예시
haskell
data JSON = JNull | JBool Bool | JNum Double
| JStr String | JArr [JSON] | JObj [(String, JSON)]
parseNull :: Parser JSON
parseNull = JNull <$ string "null"
parseBool :: Parser JSON
parseBool = JBool True <$ string "true"
<|> JBool False <$ string "false"
parseString :: Parser JSON
parseString = JStr <$> (char '"' *> manyTill anyChar (char '"'))
parseArray :: Parser JSON
parseArray = JArr <$>
(char '[' *> sepBy parseJSON (char ',') <* char ']')
주요 라이브러리
| 라이브러리 | 언어 | 특징 |
|---|
| Parsec/Megaparsec | Haskell | 강력한 에러 메시지 |
| attoparsec | Haskell | 고성능 |
| nom | Rust | 제로 카피 |
| pyparsing | Python | 간단한 API |
| FastParse | Scala | 고성능 |
관련 개념