{- This module was generated from data in the Kate syntax highlighting file sql-mysql.xml, version 1.09,
   by  Shane Wright (me@shanewright.co.uk) -}

module Text.Highlighting.Kate.Syntax.SqlMysql ( highlight, parseExpression, syntaxName, syntaxExtensions ) where
import Text.Highlighting.Kate.Definitions
import Text.Highlighting.Kate.Common
import Text.ParserCombinators.Parsec
import Data.List (nub)
import qualified Data.Set as Set
import Data.Map (fromList)
import Data.Maybe (fromMaybe)

-- | Full name of language.
syntaxName :: String
syntaxName = "SQL (MySQL)"

-- | Filename extensions for this language.
syntaxExtensions :: String
syntaxExtensions = "*.sql;*.SQL"

-- | Highlight source code using this syntax definition.
highlight :: String -> Either String [SourceLine]
highlight input =
  case runParser parseSource startingState "source" input of
    Left err     -> Left $ show err
    Right result -> Right result

-- | Parse an expression using appropriate local context.
parseExpression :: GenParser Char SyntaxState LabeledSource
parseExpression = do
  st <- getState
  let oldLang = synStLanguage st
  setState $ st { synStLanguage = "SQL (MySQL)" }
  context <- currentContext <|> (pushContext "Normal" >> currentContext)
  result <- parseRules context
  updateState $ \st -> st { synStLanguage = oldLang }
  return result

parseSource = do 
  lineContents <- lookAhead wholeLine
  updateState $ \st -> st { synStCurrentLine = lineContents }
  result <- manyTill parseSourceLine eof
  return $ map normalizeHighlighting result

startingState = SyntaxState {synStContexts = fromList [("SQL (MySQL)",["Normal"])], synStLanguage = "SQL (MySQL)", synStCurrentLine = "", synStCharsParsedInLine = 0, synStPrevChar = '\n', synStCaseSensitive = False, synStKeywordCaseSensitive = False, synStCaptures = []}

parseSourceLine = manyTill parseExpressionInternal pEndLine

pEndLine = do
  newline <|> (eof >> return '\n')
  context <- currentContext
  case context of
    "Normal" -> return ()
    "String" -> return ()
    "String2" -> return ()
    "Name" -> return ()
    "SingleLineComment" -> (popContext >> return ())
    "MultiLineComment" -> return ()
    "Preprocessor" -> (popContext >> return ())
    _ -> return ()
  lineContents <- lookAhead wholeLine
  updateState $ \st -> st { synStCurrentLine = lineContents, synStCharsParsedInLine = 0, synStPrevChar = '\n' }

withAttribute attr txt = do
  if null txt
     then fail "Parser matched no text"
     else return ()
  let style = fromMaybe "" $ lookup attr styles
  st <- getState
  let oldCharsParsed = synStCharsParsedInLine st
  let prevchar = if null txt then '\n' else last txt
  updateState $ \st -> st { synStCharsParsedInLine = oldCharsParsed + length txt, synStPrevChar = prevchar } 
  return (nub [style, attr], txt)

styles = [("Normal Text","Normal"),("Keyword","Keyword"),("Operator","Normal"),("Function","Function"),("Data Type","DataType"),("Decimal","DecVal"),("Float","Float"),("String","String"),("Name","String"),("String Char","Char"),("Comment","Comment"),("Symbol","Char"),("Preprocessor","Others")]

parseExpressionInternal = do
  context <- currentContext
  parseRules context <|> (pDefault >>= withAttribute (fromMaybe "" $ lookup context defaultAttributes))

list_keywords = Set.fromList $ words $ "access add all alter analyze and as asc auto_increment bdb berkeleydb between both by cascade case change charset column columns constraint create cross current_date current_time current_timestamp database databases day_hour day_minute day_second dec default delayed delete desc describe distinct distinctrow drop else enclosed escaped exists explain fields for foreign from fulltext function grant group having high_priority if ignore in index infile inner innodb insert interval into is join key keys kill leading left like limit lines load lock low_priority master_server_id match mrg_myisam natural national not null numeric on optimize option optionally or order outer outfile partial precision primary privileges procedure purge read references regexp rename replace require restrict returns revoke right rlike select set show soname sql_big_result sql_calc_found_rows sql_small_result ssl starting straight_join striped table tables terminated then to trailing truncate type union unique unlock unsigned update usage use user_resources using values varying when where with write xor year_month zerofill"
list_operators = Set.fromList $ words $ "+ - * / || = != <> < <= > >= ~= ^= := => ** .."
list_functions = Set.fromList $ words $ "ascii ord conv bin oct hex char concat concat_ws length octet_length char_length character_length bit_length locate position instr lpad rpad left right substring substring_index mid ltrim rtrim trim soundex space replace repeat reverse insert elt field find_in_set make_set export_set lcase lower ucase upper load_file quote abs sign mod floor ceiling round exp ln log log2 log10 pow power sqrt pi cos sin tan acos asin atan atan2 cot rand least greatest degrees radians dayofweek weekday dayofmonth dayofyear month dayname monthname quarter week year yearweek hour minute second period_add period_diff date_add date_sub adddate subdate extract to_days from_days date_format time_format curdate current_date curtime current_time now sysdate current_timestamp unix_timestamp from_unixtime sec_to_time time_to_sec cast convert bit_count database user system_user session_user password encrypt encode decode md5 sha1 sha aes_encrypt aes_decrypt des_encrypt des_decrypt last_insert_id format version connection_id get_lock release_lock is_free_lock benchmark inet_ntoa inet_aton master_pos_wait found_rows count avg min max sum std stddev bit_or bit_and"
list_types = Set.fromList $ words $ "char character varchar binary varbinary tinyblob mediumblob blob longblob tinytext mediumtext text longtext enum bit bool boolean tinyint smallint mediumint middleint int integer bigint float double real decimal dec fixed numeric long serial date datetime time timestamp year"

regex_SET'28'3f'3d'5cs'2a'5c'28'29 = compileRegex "SET(?=\\s*\\()"
regex_'5cbCHARACTER_SET'5cb = compileRegex "\\bCHARACTER SET\\b"
regex_'25bulk'5fexceptions'5cb = compileRegex "%bulk_exceptions\\b"
regex_'25bulk'5frowcount'5cb = compileRegex "%bulk_rowcount\\b"
regex_'25found'5cb = compileRegex "%found\\b"
regex_'25isopen'5cb = compileRegex "%isopen\\b"
regex_'25notfound'5cb = compileRegex "%notfound\\b"
regex_'25rowcount'5cb = compileRegex "%rowcount\\b"
regex_'25rowtype'5cb = compileRegex "%rowtype\\b"
regex_'25type'5cb = compileRegex "%type\\b"
regex_rem'5cb = compileRegex "rem\\b"
regex_'2f'24 = compileRegex "/$"
regex_'40'40'3f'5b'5e'40_'5ct'5cr'5cn'5d = compileRegex "@@?[^@ \\t\\r\\n]"

defaultAttributes = [("Normal","Normal Text"),("String","String"),("String2","String"),("Name","Name"),("SingleLineComment","Comment"),("MultiLineComment","Comment"),("Preprocessor","Preprocessor")]

parseRules "Normal" = 
  do (attr, result) <- (((pRegExpr regex_SET'28'3f'3d'5cs'2a'5c'28'29 >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'5cbCHARACTER_SET'5cb >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t(),%&;?[]{}\\" list_keywords >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t(),%&;?[]{}\\" list_operators >>= withAttribute "Operator"))
                        <|>
                        ((pKeyword " \n\t(),%&;?[]{}\\" list_functions >>= withAttribute "Function"))
                        <|>
                        ((pKeyword " \n\t(),%&;?[]{}\\" list_types >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25bulk'5fexceptions'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25bulk'5frowcount'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25found'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25isopen'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25notfound'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25rowcount'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25rowtype'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pRegExpr regex_'25type'5cb >>= withAttribute "Data Type"))
                        <|>
                        ((pFloat >>= withAttribute "Float"))
                        <|>
                        ((pInt >>= withAttribute "Decimal"))
                        <|>
                        ((pDetectChar False '\'' >>= withAttribute "String") >>~ pushContext "String")
                        <|>
                        ((pDetectChar False '"' >>= withAttribute "String") >>~ pushContext "String2")
                        <|>
                        ((pDetectChar False '`' >>= withAttribute "Name") >>~ pushContext "Name")
                        <|>
                        ((pDetectChar False '#' >>= withAttribute "Comment") >>~ pushContext "SingleLineComment")
                        <|>
                        ((pDetect2Chars False '-' '-' >>= withAttribute "Comment") >>~ pushContext "SingleLineComment")
                        <|>
                        ((pDetect2Chars False '/' '*' >>= withAttribute "Comment") >>~ pushContext "MultiLineComment")
                        <|>
                        ((pColumn 0 >> pRegExpr regex_rem'5cb >>= withAttribute "Comment") >>~ pushContext "SingleLineComment")
                        <|>
                        ((pAnyChar ":&" >>= withAttribute "Symbol"))
                        <|>
                        ((pColumn 0 >> pRegExpr regex_'2f'24 >>= withAttribute "Symbol"))
                        <|>
                        ((pColumn 0 >> pRegExpr regex_'40'40'3f'5b'5e'40_'5ct'5cr'5cn'5d >>= withAttribute "Preprocessor") >>~ pushContext "Preprocessor")
                        <|>
                        ((pDetectChar False '.' >>= withAttribute "String Char")))
     return (attr, result)

parseRules "String" = 
  do (attr, result) <- (((pLineContinue >>= withAttribute "String") >>~ (popContext >> return ()))
                        <|>
                        ((pHlCStringChar >>= withAttribute "String Char"))
                        <|>
                        ((pDetectChar False '&' >>= withAttribute "Symbol"))
                        <|>
                        ((pDetectChar False '\'' >>= withAttribute "String") >>~ (popContext >> return ())))
     return (attr, result)

parseRules "String2" = 
  do (attr, result) <- (((pLineContinue >>= withAttribute "String") >>~ (popContext >> return ()))
                        <|>
                        ((pHlCStringChar >>= withAttribute "String Char"))
                        <|>
                        ((pDetectChar False '&' >>= withAttribute "Symbol"))
                        <|>
                        ((pDetectChar False '"' >>= withAttribute "String") >>~ (popContext >> return ())))
     return (attr, result)

parseRules "Name" = 
  do (attr, result) <- (((pLineContinue >>= withAttribute "Name") >>~ (popContext >> return ()))
                        <|>
                        ((pHlCStringChar >>= withAttribute "String Char"))
                        <|>
                        ((pDetectChar False '`' >>= withAttribute "Name") >>~ (popContext >> return ())))
     return (attr, result)

parseRules "SingleLineComment" = 
  pzero

parseRules "MultiLineComment" = 
  do (attr, result) <- (((pLineContinue >>= withAttribute "Comment") >>~ (popContext >> return ()))
                        <|>
                        ((pDetect2Chars False '*' '/' >>= withAttribute "Comment") >>~ (popContext >> return ())))
     return (attr, result)

parseRules "Preprocessor" = 
  pzero

parseRules x = fail $ "Unknown context" ++ x