@hackage comparse0.1.0.0

An unopiniated parser combinators library.

comparse

Tests

comparse is a parser combinator library supporting arbitrary input types. Combinators do not care about the exact type of the input stream, allowing you to use them on your own data source, as long as it supports a specific set of operations. The ParserT monad transformer can wrap around another monad, allowing you to easily implement features such as tracing, context-sensitive parsing, state machines, and so on. comparse does its best to provide meaningful error messages without sacrificing performances or the developer experience when writing parsers.

Example

The following example shows how to use the comparse library to parse a subset of the JSON data format:

import Control.Monad (void)
import Control.Monad.Parser
import Data.Char (isAlpha, isDigit)
import Data.Stream.StringLines

data JValue
  = JString String
  | JNumber Int
  | JBool Bool
  | JNull
  | JObject [(String, JValue)]
  | JArray [JValue]
  deriving (Show)

main :: IO ()
main = interact $ show . parseJValue

parseJValue :: String -> Maybe JValue
parseJValue s =
  case runStringParser (json <* eof) s of
    Parsed v (StringLines "" _) _ -> Just v
    _ -> Nothing

lexeme :: StringParser a -> StringParser a
lexeme p = spaces *> p <* spaces
  where
    spaces = void $ many $ oneOf " \n\r\t"

symbol :: String -> StringParser String
symbol = lexeme . string

json :: StringParser JValue
json =
  JString <$> stringLiteral
    <|> JNumber <$> number
    <|> JBool <$> bool
    <|> JNull <$ symbol "null"
    <|> JObject <$> object
    <|> JArray <$> array

stringLiteral :: StringParser String
stringLiteral = lexeme $ like '"' *> many (unlike '\"') <* like '"'

number :: StringParser Int
number =
  lexeme
    ( read <$> ((:) <$> like '-' <*> many1 digit)
        <|> read <$> (optional (like '+') *> many1 digit)
    )
    <* notFollowedBy (match isAlpha)
  where
    digit = match isDigit

bool :: StringParser Bool
bool = True <$ symbol "true" <|> False <$ symbol "false"

object :: StringParser [(String, JValue)]
object =
  symbol "{"
    *> sepBy ((,) <$> (stringLiteral <* symbol ":") <*> json) (symbol ",")
    <* symbol "}"

array :: StringParser [JValue]
array = symbol "[" *> sepBy json (symbol ",") <* symbol "]"

For more examples, please refer to the tests.