{-# LANGUAGE FlexibleContexts #-}

-- | Lexer/Parsec interface
module Text.Parsec.LTok
  ( tok
  , tok'
  , anyIdent
  , number
  , stringlit
  , Parser
  ) where

import Language.Lua.Annotated.Lexer (LTok, AlexPosn(..))
import Language.Lua.Token

import Text.Parsec hiding (satisfy, string)

type Parser = Parsec [LTok] ()

-- | This parser succeeds whenever the given predicate returns true when called with
-- parsed `LTok`. Same as 'Text.Parsec.Char.satisfy'.
satisfy :: (Stream [LTok] m LTok) => (LTok -> Bool) -> ParsecT [LTok] u m LToken
satisfy f = tokenPrim show nextPos tokeq
  where
    tokeq :: LTok -> Maybe LToken
    tokeq t = if f t then Just (fst t) else Nothing

satisfy' :: (Stream [LTok] m LTok) => (LTok -> Maybe a) -> ParsecT [LTok] u m a
satisfy' = tokenPrim show nextPos

nextPos :: SourcePos -> LTok -> [LTok] -> SourcePos
nextPos pos _ ((_, AlexPn _ l c):_) = setSourceColumn (setSourceLine pos l) c
nextPos pos _ []                    = pos

-- | Parses given `LToken`.
tok :: (Stream [LTok] m LTok) => LToken -> ParsecT [LTok] u m LToken
tok t = satisfy (\(t', _) -> t' == t) <?> show t

tok' :: (Stream [LTok] m LTok) => LToken -> ParsecT [LTok] u m ()
tok' p = tok p >> return ()

-- | Parses a `LTokIdent`.
anyIdent :: Monad m => ParsecT [LTok] u m String
anyIdent = satisfy' p <?> "ident"
  where p (t, _) = case t of LTokIdent i -> Just i
                             _ -> Nothing

-- | Parses a `LTokNum`.
number :: Monad m => ParsecT [LTok] u m String
number = satisfy' p <?> "number"
  where p (t, _) = case t of LTokNum n -> Just n
                             _ -> Nothing

-- | Parses a `LTokSLit`.
stringlit :: Monad m => ParsecT [LTok] u m String
stringlit = satisfy' p <?> "string"
  where p (t, _) = case t of LTokSLit s -> Just s
                             _ -> Nothing