glualint-1.25.0: src/GLua/TokenTypes.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Contains class instances and functions related to tokens
module GLua.TokenTypes where
import Data.Aeson
import Data.List
import GHC.Generics
import GLua.AG.Token
import Text.ParserCombinators.UU.BasicInstances
instance Show MToken where
show (MToken _ tok) = show tok
-- | Simple EQ instance. TODO: check for position equality
instance Eq MToken where
(MToken _ t1) == (MToken _ t2) = t1 == t2
-- | Simple Ord instance. TODO: check for position Ord
instance Ord MToken where
compare (MToken _ t1) (MToken _ t2) = compare t1 t2
deriving instance Generic MToken
instance ToJSON MToken
instance FromJSON MToken
instance Eq LineColPos where
(LineColPos l c p) == (LineColPos l' c' p') = l == l' && c == c' && p == p'
instance Ord LineColPos where
compare (LineColPos l c _) (LineColPos l' c' _) =
compare l l' `mappend` compare c c'
instance ToJSON LineColPos where
-- this generates a Value
toJSON (LineColPos line col p) =
object ["line" .= line, "column" .= col, "pos" .= p]
#if MIN_VERSION_aeson(0,10,0)
-- this encodes directly to a bytestring Builder
toEncoding (LineColPos line col p) =
pairs ("line" .= line <> "column" .= col <> "pos" .= p)
#endif
instance FromJSON LineColPos where
parseJSON = withObject "LineColPos" $ \v ->
LineColPos
<$> v .: "line"
<*> v .: "column"
<*> v .: "pos"
instance Eq Region where
Region s e == Region s' e' = s == s' && e == e'
instance Ord Region where
compare (Region s e) (Region s' e') =
compare s s' `mappend` compare e e'
instance ToJSON Region
instance FromJSON Region
instance ToJSON Token
instance FromJSON Token
-- | Metatoken algebra
type MTokenAlgebra mtok = Region -> Token -> mtok
mpos :: MToken -> Region
mpos (MToken p _) = p
mtok :: MToken -> Token
mtok (MToken _ t) = t
emptyRg :: Region
emptyRg = Region (LineColPos 0 0 0) (LineColPos 0 0 0)
rgOr :: Region -> Region -> Region
rgOr l r
| l == emptyRg = r
| otherwise = l
----------------------------------------
-- Correcting token positions
----------------------------------------
customAdvanceChr :: LineColPos -> Char -> LineColPos
customAdvanceChr (LineColPos line _ abs') '\n' = LineColPos (line + 1) 0 (abs' + 1)
customAdvanceChr (LineColPos line pos' abs') _ = LineColPos line (pos' + 1) (abs' + 1)
customAdvanceStr :: LineColPos -> String -> LineColPos
customAdvanceStr = foldl' customAdvanceChr
customAdvanceToken :: LineColPos -> Token -> LineColPos
customAdvanceToken (LineColPos line pos' abs') t = let len = tokenSize t in LineColPos line (pos' + len) (abs' + len)
-- | Whether the first region ends strictly before the second region starts
before :: Region -> Region -> Bool
before (Region _ (LineColPos _ _ p)) (Region (LineColPos _ _ p') _) = p < p'
-- | Whether the first region ends before or on the same line as the second region
beforeOrOnLine :: Region -> Region -> Bool
beforeOrOnLine (Region _ (LineColPos l _ _)) (Region (LineColPos l' _ _) _) = l <= l'
-- | Whether the first region ends before the second region ends
beforeEnd :: Region -> Region -> Bool
beforeEnd (Region _ (LineColPos _ _ p)) (Region _ (LineColPos _ _ p')) = p < p'
-- | Whether the first region ends before or on the same line as the END of the second region
beforeEndLine :: Region -> Region -> Bool
beforeEndLine (Region _ (LineColPos l _ _)) (Region _ (LineColPos l' _ _)) = l <= l'
rgStart :: Region -> LineColPos
rgStart (Region s _) = s
rgEnd :: Region -> LineColPos
rgEnd (Region _ e) = e
-- | Returns a region that starts at the start of the first region
-- and ends BEFORE the start of the second region
upto :: Region -> Region -> Region
upto lr rr = case (rgEnd lr, rgStart rr) of
(_, LineColPos 0 0 0) -> lr
(LineColPos l c _, LineColPos l' c' _)
| l' > l || (l' == l && c' > c) -> lr
| otherwise -> Region (rgStart lr) (rgStart rr)
-- | Fold over metatoken
foldMToken :: MTokenAlgebra t -> MToken -> t
foldMToken alg (MToken p t) = alg p t
-- | mFold: Apply a TokenAlgebra to an MToken
mFold :: TokenAlgebra a -> MToken -> a
mFold alg = foldMToken f
where
f _ = foldToken alg
-- | Huge token algebra
type TokenAlgebra token =
( ( -- Comments and whitespace
String -> token
, String -> token -- DashComment
, Int -> String -> token -- DashBlockComment
, String -> token -- SlashComment
, String -> token -- SlashBlockComment
, token -- Semicolon
)
, ( -- Constants
String -> token -- TNumber
, String -> token -- DQString
, String -> token -- SQString
, String -> token -- MLString
, token -- TTrue
, token -- TFalse
, token -- Nil
, token -- VarArg
) -- operators
, ( token -- Plus
, token -- Minus
, token -- Multiply
, token -- Divide
, token -- Modulus
, token -- Power
, token -- TEq
, token -- TNEq
, token -- TCNEq
, token -- TLEQ
, token -- TGEQ
, token -- TLT
, token -- TGT
, token -- Equals
, token -- Concatenate
, token -- Colon
, token -- Dot
, token -- Comma
, token -- Hash
, token -- Not
, token -- CNot
, token -- And
, token -- CAnd
, token -- Or
, token -- COr
)
, ( -- Keywords
token -- Function
, token -- Local
, token -- If
, token -- Then
, token -- Elseif
, token -- Else
, token -- For
, token -- In
, token -- Do
, token -- While
, token -- Until
, token -- Repeat
, token -- Continue
, token -- Break
, token -- Return
, token -- End
)
, ( -- Brackets
token -- LRound
, token -- RRound
, token -- LCurly
, token -- RCurly
, token -- LSquare
, token -- RSquare
)
, ( -- Other
String -> String -> String -> token -- Label
, String -> token -- Identifier
)
)
-- | Fold over token definition
foldToken :: TokenAlgebra t -> Token -> t
foldToken ((tWhitespace, tDashComment, tDashBlockComment, tSlashComment, tSlashBlockComment, tSemicolon), (tTNumber, tDQString, tSQString, tMLString, tTTrue, tTFalse, tNil, tVarArg), (tPlus, tMinus, tMultiply, tDivide, tModulus, tPower, tTEq, tTNEq, tTCNEq, tTLEQ, tTGEQ, tTLT, tTGT, tEquals, tConcatenate, tColon, tDot, tComma, tHash, tNot, tCNot, tAnd, tCAnd, tOr, tCOr), (tFunction, tLocal, tIf, tThen, tElseif, tElse, tFor, tIn, tDo, tWhile, tUntil, tRepeat, tContinue, tBreak, tReturn, tEnd), (tLRound, tRRound, tLCurly, tRCurly, tLSquare, tRSquare), (tLabel, tIdentifier)) = fold
where
fold (Whitespace str) = tWhitespace str
fold (DashComment str) = tDashComment str
fold (DashBlockComment depth str) = tDashBlockComment depth str
fold (SlashComment str) = tSlashComment str
fold (SlashBlockComment str) = tSlashBlockComment str
fold (TNumber str) = tTNumber str
fold (Label whitespaceBefore str whitespaceAfter) = tLabel whitespaceBefore str whitespaceAfter
fold (Identifier str) = tIdentifier str
fold (DQString str) = tDQString str
fold (SQString str) = tSQString str
fold (MLString str) = tMLString str
fold And = tAnd
fold CAnd = tCAnd
fold Break = tBreak
fold Do = tDo
fold Else = tElse
fold Elseif = tElseif
fold End = tEnd
fold TFalse = tTFalse
fold For = tFor
fold Function = tFunction
fold If = tIf
fold In = tIn
fold Local = tLocal
fold Nil = tNil
fold Not = tNot
fold CNot = tCNot
fold Or = tOr
fold COr = tCOr
fold Repeat = tRepeat
fold Continue = tContinue
fold Return = tReturn
fold Then = tThen
fold TTrue = tTTrue
fold Until = tUntil
fold While = tWhile
fold Plus = tPlus
fold Minus = tMinus
fold Multiply = tMultiply
fold Divide = tDivide
fold Modulus = tModulus
fold Power = tPower
fold Hash = tHash
fold TEq = tTEq
fold TNEq = tTNEq
fold TCNEq = tTCNEq
fold TLEQ = tTLEQ
fold TGEQ = tTGEQ
fold TLT = tTLT
fold TGT = tTGT
fold Equals = tEquals
fold LRound = tLRound
fold RRound = tRRound
fold LCurly = tLCurly
fold RCurly = tRCurly
fold LSquare = tLSquare
fold RSquare = tRSquare
fold Semicolon = tSemicolon
fold Colon = tColon
fold Comma = tComma
fold Dot = tDot
fold Concatenate = tConcatenate
fold VarArg = tVarArg
-- | Simple show instance
instance Show Token where
show =
foldToken
(
( id
, ("--" ++) -- DashComment
, \d s -> let n = replicate d '=' in "--[" ++ n ++ '[' : s ++ ']' : n ++ "]" -- DashBlockComment
, ("//" ++) -- SlashComment
, \s -> "/*" ++ s ++ "*/" -- SlashBlockComment
, ";" -- Semicolon
)
,
( id -- TNumber
, \s -> "\"" ++ s ++ "\"" -- DQString
, \s -> "'" ++ s ++ "'" -- SQString
, id -- MLString
, "true" -- TTrue
, "false" -- TFalse
, "nil" -- Nil
, "..." -- VarArg
)
,
( "+" -- Plus
, "-" -- Minus
, "*" -- Multiply
, "/" -- Divide
, "%" -- Modulus
, "^" -- Power
, "==" -- TEq
, "~=" -- TNEq
, "!=" -- TCNEq
, "<=" -- TLEQ
, ">=" -- TGEQ
, "<" -- TLT
, ">" -- TGT
, "=" -- Equals
, ".." -- Concatenate
, ":" -- Colon
, "." -- Dot
, "," -- Comma
, "#" -- Hash
, "not" -- Not
, "!" -- CNot
, "and" -- And
, "&&" -- CAnd
, "or" -- Or
, "||" -- COr
)
,
( "function" -- Function
, "local" -- Local
, "if" -- If
, "then" -- Then
, "elseif" -- Elseif
, "else" -- Else
, "for" -- For
, "in" -- In
, "do" -- Do
, "while" -- While
, "until" -- Until
, "repeat" -- Repeat
, "continue" -- Continue
, "break" -- Break
, "return" -- Return
, "end" -- End
)
,
( "(" -- LRound
, ")" -- RRound
, "{" -- LCurly
, "}" -- RCurly
, "[" -- LSquare
, "]" -- RSquare
)
,
( \spaceBefore ident spaceAfter -> spaceBefore ++ ident ++ spaceAfter -- Label
, id -- Identifier
)
)
-- | Whether an mtoken is a comment
isWhitespace :: MToken -> Bool
isWhitespace = mFold ((const True, const False, \_ _ -> False, const False, const False, False), (const False, const False, const False, const False, False, False, False, False), (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), (False, False, False, False, False, False), (\_ _ _ -> False, const False))
-- | Whether an mtoken is a comment
isComment :: MToken -> Bool
isComment = mFold ((const False, const True, \_ _ -> True, const True, const True, False), (const False, const False, const False, const False, False, False, False, False), (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), (False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), (False, False, False, False, False, False), (\_ _ _ -> False, const False))
-- | Split the tokens by comments and other tokens
splitComments :: [MToken] -> ([MToken], [MToken])
splitComments = partition isComment
tokenLabel :: MToken -> String
tokenLabel = mFold ((const "", const "", \_ _ -> "", const "", const "", ""), (const "", const "", const "", const "", "", "", "", ""), ("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), ("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""), ("", "", "", "", "", ""), (\_ ident _ -> ident, id))
-- | The size of a token in characters
tokenSize :: Token -> Int
tokenSize =
foldToken
(
( length -- whitespace
, (+ 2) . length -- DashComment
, \d s -> 6 + length s + 2 * d -- DashBlockComment
, (+ 2) . length -- SlashComment
, (+ 4) . length -- SlashBlockComment
, 1 -- Semicolon
)
,
( length -- TNumber
, (+ 2) . length -- DQString
, (+ 2) . length -- SQString
, length -- MLString
, 4 -- TTrue
, 5 -- TFalse
, 3 -- Nil
, 3 -- VarArg
)
,
( 1 -- Plus
, 1 -- Minus
, 1 -- Multiply
, 1 -- Divide
, 1 -- Modulus
, 1 -- Power
, 2 -- TEq
, 2 -- TNEq
, 2 -- TCNEq
, 2 -- TLEQ
, 2 -- TGEQ
, 1 -- TLT
, 1 -- TGT
, 1 -- Equals
, 2 -- Concatenate
, 1 -- Colon
, 1 -- Dot
, 1 -- Comma
, 1 -- Hash
, 3 -- Not
, 1 -- CNot
, 3 -- And
, 2 -- CAnd
, 2 -- Or
, 2 -- COr
)
,
( 8 -- Function
, 5 -- Local
, 2 -- If
, 4 -- Then
, 6 -- Elseif
, 4 -- Else
, 3 -- For
, 2 -- In
, 2 -- Do
, 5 -- While
, 5 -- Until
, 6 -- Repeat
, 8 -- Continue
, 5 -- Break
, 6 -- Return
, 3 -- End
)
,
( 1 -- LRound
, 1 -- RRound
, 1 -- LCurly
, 1 -- RCurly
, 1 -- LSquare
, 1 -- RSquare
)
,
( \spaceBefore ident spaceAfter ->
2 + length spaceBefore + length ident + length spaceAfter + 2 -- Label
, length -- Identifier
)
)
isSingleLineComment :: Token -> Bool
isSingleLineComment = \case
DashComment _ -> True
SlashComment _ -> True
_ -> False