{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings          #-}

module FastTags.Token where

import Control.DeepSeq (NFData, rnf)
import Data.Text (Text)
import qualified Data.Text as Text
import GHC.Generics (Generic)

data Pos a = Pos {
    posOf   :: {-# UNPACK #-} !SrcPos
    , valOf :: !a
    } deriving (Eq, Ord)

instance (NFData a) => NFData (Pos a) where
    rnf (Pos x y) = rnf x `seq` rnf y

instance Show a => Show (Pos a) where
    show (Pos pos val) = show pos ++ ":" ++ show val

newtype Line = Line { unLine :: Int }
    deriving (Show, Eq, Ord, NFData, Num)

newtype Offset = Offset { unOffset :: Int }
    deriving (Show, Eq, Ord, NFData, Num)

increaseLine :: Line -> Line
increaseLine (Line n) = Line $! n + 1

data SrcPos = SrcPos {
    posFile     :: !FilePath
    , posLine   :: {-# UNPACK #-} !Line
    , posOffset :: {-# UNPACK #-} !Offset
    -- | No need to keep prefix strict since most of the prefixes will not be
    -- used.
    , posPrefix :: Text
    , posSuffix :: Text
    } deriving (Eq, Ord)

instance NFData SrcPos where
    rnf (SrcPos v w x y z) = rnf v `seq` rnf w `seq` rnf x `seq` rnf y `seq` rnf z

instance Show SrcPos where
    show (SrcPos fn line offset prefix suffix) =
        fn ++ ":" ++ show (unLine line) ++ ":" ++ show (unOffset offset) ++ prefix' ++ suffix'
        where
        prefix' = clean prefix
        suffix' = clean suffix
        clean s | Text.null s = ""
                | otherwise   = ":/" ++ Text.unpack s ++ "/"

forallTokenVal :: TokenVal
forallTokenVal = T "forall"

patternTokenVal :: TokenVal
patternTokenVal = T "pattern"

data TokenVal =
    KWCase
    | KWClass
    | KWData
    | KWDefault
    | KWDeriving
    | KWDo
    | KWElse
    | KWFamily
    | KWForeign
    | KWIf
    | KWImport
    | KWIn
    | KWInfix
    | KWInfixl
    | KWInfixr
    | KWInstance
    | KWLet
    | KWModule
    | KWNewtype
    | KWOf
    | KWThen
    | KWType
    | KWWhere
    | Arrow
    | At
    | Backtick
    | Comma
    | Dot
    | DoubleColon
    | Equals
    | ExclamationMark
    | Implies
    | LBrace
    | LBracket
    | LParen
    | Pipe
    | RBrace
    | RBracket
    | RParen
    | Tilde
    | Semicolon
    | T {-# UNPACK #-} !Text
    -- | Special token, not part of Haskell spec. Stores indentation.
    | Newline {-# UNPACK #-} !Int
    -- | String contents is not tracked since it's irrelevant.
    | String
    -- | Actual character not tracked since it's irrelevant.
    | Character
    -- | Actual value not tracked since it's irrelevant.
    | Number
    | QuasiquoterStart
    | QuasiquoterEnd
    | SpliceStart -- \$(, ends with RParen
    | ToplevelSplice -- e.g. \$foo
    | LambdaBackslash -- \
    | CppDefine {-# UNPACK #-} !Text
    | HSCEnum      -- #{enum...}
    | HSCDirective -- e.g. #define foo bar...
    | HSCDirectiveBraced
      -- ^ e.g. #{define foo...\nbar}, #{\ndefine foo...\nbar}, ends with RBrace
    | LBanana      -- Arrows: (|
    | RBanana      -- Arrows: |)
    | Error Text
    | DQuote -- '"' when not part of string in Alex or Happy
    | EOF
    deriving (Show, Eq, Ord, Generic)

instance NFData TokenVal

type Token = Pos TokenVal