packages feed

peggy-0.1.0: Text/Peggy/Prim.hs

{-# Language GeneralizedNewtypeDeriving #-}
{-# Language MultiParamTypeClasses #-}

module Text.Peggy.Prim (
  Parser(..),
  ParseError(..),
  Result(..),
  Derivs(..),
  runParser,
  
  getPos,
  parseError,
  backtrack,
  annot,
  
  anyChar,
  satisfy,
  char,
  string,
  empty,
  (<|>),
  many,
  some,
  optional,
  expect,
  unexpect,
  
  space,
  ) where

import Control.Applicative
import Control.Monad.State
import Control.Monad.Error
import Data.Char

import Text.Peggy.SrcLoc

newtype Parser d a
  = Parser
    { -- annotation :: String
      unParser :: d -> Result d a
    }

data ParseError = ParseError SrcLoc String
  deriving (Show)

nullError :: ParseError
nullError = ParseError (LocPos (SrcPos "" 0 1 1)) ""

data Result d a
  = Parsed d a
  | Failed ParseError

instance Show a => Show (Result d a) where
  show (Parsed _ a) = "Parsed " ++ show a
  show (Failed err) = "Failed (" ++ show err ++ ")"

class Derivs d where
  dvPos  :: d -> SrcPos
  dvChar :: d -> Result d Char
  parse  :: SrcPos -> String -> d

runParser :: Derivs d => Parser d a -> String -> String -> Result d a
runParser p sourceName source =
  unParser p $ parse (SrcPos sourceName 0 1 1) source

instance Functor (Parser d) where
  fmap f (Parser p) = Parser $ \d ->
    case p d of
      Parsed e a ->
        Parsed e (f a)
      Failed err ->
        Failed err

instance Applicative (Parser d) where
  pure a = Parser $ \d -> Parsed d a
  
  Parser p <*> Parser q = Parser $ \d ->
    case p d of
      Parsed e g ->
        case q e of
          Parsed f a ->
            Parsed f (g a)
          Failed err ->
            Failed err
      Failed err ->
        Failed err

instance Monad (Parser d) where
  return = pure
  
  Parser p >>= f = Parser $ \d -> 
    case p d of
      Parsed e a ->
        unParser (f a) e
      Failed err ->
        Failed err

instance Derivs d => Alternative (Parser d) where
  empty =
    Parser $ \d -> Failed (ParseError (LocPos $ dvPos d) "")
  Parser p <|> Parser q =
    Parser $ \d -> 
    case p d of
      Parsed e a ->
        Parsed e a
      Failed _ ->
        q d

instance MonadError ParseError (Parser d) where
  throwError e = Parser $ \_ ->
    Failed e
  catchError (Parser p) h = Parser $ \d ->
    case p d of
      Parsed e a ->
        Parsed e a
      Failed err ->
        unParser (h err) d

--

backtrack :: Derivs d => Parser d a -> Parser d a
backtrack (Parser p) = Parser $ \d ->
  case p d of
    Parsed _ r ->
      Parsed d r
    Failed e ->
      Failed e

annot :: Derivs d => String -> Parser d a -> Parser d a
annot ann p = p -- Parser ann (unParser p)

getPos :: Derivs d => Parser d SrcPos
getPos = Parser $ \d ->
  Parsed d (dvPos d)

parseError :: Derivs d => String -> Parser d a
parseError msg = do
  pos <- getPos
  throwError $ ParseError (LocPos pos) msg

-----

anyChar :: Derivs d => Parser d Char
anyChar = Parser dvChar

satisfy :: Derivs d => (Char -> Bool) -> Parser d Char
satisfy p = do
  c <- anyChar
  when (not $ p c) $
    throwError nullError
  return c

char :: Derivs d => Char -> Parser d Char
char c = annot (show c) $
  satisfy (== c)
  `catchError` 
  (const $ parseError $ "expect " ++ show c)

string :: Derivs d => String -> Parser d String
string str = annot (show str) $
  mapM char str
  `catchError`
  (const $ parseError $ "expect " ++ show str)

expect :: Derivs d => Parser d a -> Parser d ()
expect p = backtrack $ () <$ p

unexpect :: Derivs d => Parser d a -> Parser d ()
unexpect p = backtrack $ do
  b <- catchError (True <$ p) (\_ -> pure False)
  when b $ parseError $ "unexpect " -- ++ annotation p

-----

space :: Derivs d => Parser d ()
space = () <$ satisfy isSpace