packages feed

angle-0.8.1.0: src/Angle/Parse/Helpers.hs

{-|
Module      : Angle.Parse.Helpers
Description : Defines functions for working with the parser.
Copyright   : Copyright (C) 2015 Ben Moon
License     : GNU GPL, version 3
Maintainer  : GuiltyDolphin@gmail.com
Stability   : alpha

Provided functions are split into two categories:

[@basic@] the functions that work on standard types.

[@advanced@] higher-order parsers.
-}
module Angle.Parse.Helpers
    (
    -- ** Basic
      anyChar
    , char
    , charFrom
    , cond
    , notChar
    , string

    -- ** Advanced
    , choice
    , followed
    , manyTill
    , noneFrom
    , notParse
    , sepWith
    , surrounded
    , tryParse
    , within
    , lookAhead

    -- ** Other
    , evalParse
    , Parser
    , SourcePos
    , sourcePos
    , unexpectedErr
    , (<?>)
    ) where


import Control.Applicative
import Control.Monad.Except
import Control.Monad.State

import Angle.Scanner


-- | Succeeds if the predicate function returns
-- true when passed the next character.
cond :: (Char -> Bool) -> Parser Char
cond f = tryParse $ do
  ch <- scanChar
  if f ch then return ch
  else unexpectedErr ("character: " ++ show ch)


-- | Attempt to satisfy the provided parser, but revert
-- the state upon failure.
tryParse :: Parser a -> Parser a
tryParse sc = do
  st <- get
  sc `catchError` (\e -> do
    put st
    throwError e)


-- | Match the specified character.
char :: Char -> Parser Char
char ch = cond (==ch) <?> show ch


-- | Matches if character is an element of the provided string.
charFrom :: String -> Parser Char
charFrom str = cond (`elem` str)


-- | Match `str' in its entirety.
string :: String -> Parser String
string str = tryParse (mapM char str) <?> str


-- | @within start end sc@ matches @sc@ between @start@ and @end@.
within :: Parser a -> Parser b -> Parser c -> Parser c
within start end sc = start *> sc <* end


-- | @surrounded x@ is the same as @within x x@.
surrounded :: Parser a -> Parser b -> Parser b
surrounded surr = within surr surr


-- | Parses second parser before first parser, returning the result
-- of the second parser.
followed :: Parser a -> Parser b -> Parser b
followed f sc = sc <* f


-- | Use first Parser that succeeds.
choice :: [Parser a] -> Parser a
choice = msum




-- | Succeeds if it does not parse the specified character.
notChar :: Char -> Parser Char
notChar ch = cond (/=ch)


-- | Matches any character, only fails when there is no more input.
anyChar :: Parser Char
anyChar = scanChar <?> "any character"


-- | Succeeds if the parser succeeds, but does not consume
-- input upon success.
lookAhead :: Parser a -> Parser a
lookAhead sc = do
  pos <- get
  res <- sc
  put pos
  return res


-- | Succeeds only if `sc' does not succeed.
notParse :: (Show a) => Parser a -> Parser ()
notParse sc = tryParse (do
  res <- optional (tryParse (lookAhead sc))
  case res of Nothing -> return ()
              Just x -> unexpectedErr (show x))


-- | @noneFrom sc scs@ builds a list of parsers by
-- applying @sc@ to each of @scs@, the resultant parser
-- then succeeds only if all of the resultant parsers
-- fail.
noneFrom :: (Show a) => (a -> Parser a) -> [a] -> Parser ()
noneFrom scf = notParse . oneFrom
  where oneFrom xs = choice $ map scf xs


-- | List of `sc' separated with `sep'.
sepWith :: Parser a -> Parser b -> Parser [b]
sepWith sep sc = tryParse (do
  fsm <- optional sc
  case fsm of
    Nothing -> return []
    Just fs -> do
        s <- optional sep
        case s of
          Nothing -> return [fs]
          Just _ -> liftM (fs:) (sepWith sep sc))


-- | Collect sc until `ti' succeeds.
manyTill :: (Show b) => Parser b -> Parser a -> Parser [a]
manyTill ti sc = many (notParse ti *> sc)