-----------------------------------------------------------------------------
-- |
-- Module      :  Reader.Parser.Utils
-- License     :  MIT (see the LICENSE file)
-- Maintainer  :  Felix Klein (klein@react.uni-saarland.de)
--
-- Functions shared among the different parsers.
--
-----------------------------------------------------------------------------

module Reader.Parser.Utils
  ( stringParser
  , identifier
  , positionParser
  , getPos
  ) where

-----------------------------------------------------------------------------

import Data.Expression
  ( ExprPos(..)
  , SrcPos(..)
  )

import Reader.Parser.Data
  ( globalDef
  )

import Control.Monad
  ( void
  )

import Data.Functor.Identity
  ( Identity
  )

import Text.Parsec
  ( ParsecT
  , Stream
  , (<|>)
  , char
  , many
  , anyChar
  , noneOf
  , getPosition
  , sourceLine
  , sourceColumn
  )

import Text.Parsec.String
  ( Parser
  )

import Text.Parsec.Token
  ( identStart
  , identLetter
  )

-----------------------------------------------------------------------------

-- | Parses a string surrounded by double quotation marks.
-- Double quotation marks inside the string have to be escabed by a backslash.
-- The string may contain special characters like new line or tabs.

stringParser
  :: Parser String

stringParser = do
    void $ char '"'
    xs <- many character
    void $ char '"'
    return $ concat xs

  where
    character =
      escaped <|> nonescaped

    escaped = do
      d <- char '\\'
      c <- anyChar
      case c of
        '"' -> return [c]
        _   -> return [d,c]

    nonescaped = do
      c <- noneOf "\""
      return [c]

-----------------------------------------------------------------------------

-- | @identifier wp@ is an exteded version of its equivalent exported by
-- 'Text.Parser.Token', which additionally stores the starting source position
-- and the ending source position. The parser @wp@ is assumed to parse the
-- subsequent whitespace after the identifier.

identifier
  :: ParsecT String u Identity () -> ParsecT String u Identity (String, ExprPos)

identifier wp = positionParser wp $ do
    x <- identStart globalDef
    xr <- many $ identLetter globalDef
    return (x:xr)

-----------------------------------------------------------------------------

-- | @positionParser wp p@ parses the same as parser @p@, but additionally
-- returns the starting and ending source positions of the result parsed by
-- @p@. The parser @wp@ is assumed to parse the subsequent whitespace after
-- @p@ has been invoked.

positionParser
 :: (Stream s m t) => ParsecT s u m () -> ParsecT s u m a
    -> ParsecT s u m (a,ExprPos)

positionParser wp p = do
  x <- getPos; e <- p
  y <- getPos; wp
  return (e,ExprPos x y)

-----------------------------------------------------------------------------

-- | Return the current position of the parser in the source file.

getPos
 :: (Stream s m t) => ParsecT s u m SrcPos

getPos = do
  x <- getPosition
  return $ SrcPos (sourceLine x) (sourceColumn x - 1)

-----------------------------------------------------------------------------