{- This module was generated from data in the Kate syntax highlighting file xharbour.xml, version 1.04,
   by  Giancarlo Niccolai (giancarlo@niccolai.ws) -}

module Text.Highlighting.Kate.Syntax.Xharbour ( highlight, parseExpression, syntaxName, syntaxExtensions ) where
import Text.Highlighting.Kate.Definitions
import Text.Highlighting.Kate.Common
import Text.ParserCombinators.Parsec
import Control.Monad (when)
import Data.Map (fromList)
import Data.Maybe (fromMaybe, maybeToList)

import qualified Data.Set as Set
-- | Full name of language.
syntaxName :: String
syntaxName = "xHarbour"

-- | Filename extensions for this language.
syntaxExtensions :: String
syntaxExtensions = "*.prg;*.PRG;*.ch"

-- | 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 = "xHarbour" }
  context <- currentContext <|> (pushContext "TopLevel" >> 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 [("xHarbour",["TopLevel"])], synStLanguage = "xHarbour", synStCurrentLine = "", synStCharsParsedInLine = 0, synStPrevChar = '\n', synStCaseSensitive = True, synStKeywordCaseSensitive = False, synStCaptures = []}

parseSourceLine = manyTill parseExpressionInternal pEndLine

pEndLine = do
  lookAhead $ newline <|> (eof >> return '\n')
  context <- currentContext
  case context of
    "TopLevel" -> return () >> pHandleEndLine
    "comment" -> (popContext) >> pEndLine
    "ml_comment" -> return () >> pHandleEndLine
    "string" -> (popContext) >> pEndLine
    "stringc" -> (popContext) >> pEndLine
    "logic" -> (popContext) >> pEndLine
    "ClassContext" -> return () >> pHandleEndLine
    _ -> pHandleEndLine

withAttribute attr txt = do
  when (null txt) $ fail "Parser matched no text"
  let labs = attr : maybeToList (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 (labs, txt)

styles = [("Keyword","kw"),("Comment","co"),("Operator","bn"),("Number","dv"),("Function","fu"),("String","st"),("Preprocessor","ot")]

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

list_keywords = Set.fromList $ words $ "local global extern field each as set clear screen databases all close color date else elseif in to do loop catch exit box say case switch self super say get read use select otherwise index alias like has return static on off nil ? class external"
list_context'5fbeginners = Set.fromList $ words $ "for if switch while try begin procedure function method"
list_context'5fterminators = Set.fromList $ words $ "next end endif enddo endcase"
list_class'5fkeywords = Set.fromList $ words $ "data inline method classdata init from hidden"
list_set'5fcommands = Set.fromList $ words $ "invalid exact fixed decimals dateformat epoch path default exclusive softseek unique deleted cancel debug typeahead color cursor console alternate altfile device extra extrafile printer printfile margin bell confirm escape insert exit intensity scoreboard delimiters delimchars wrap message mcenter scrollbreak eventmask videomode mblocksize mfileext strictread optimize autopen autorder autoshare language idlerepeat trace tracefile tracestack filecase dircase dirseparator"
list_functions = Set.fromList $ words $ "aadd adel achoice aclone aeval ains ascan asize adir afill atail asort array tassociativearray bin21 bin2l bin2u bin2w i2bin l2bin u2bin w2bin eval fieldblock fieldwblock inkey lastkey mcol mrow nextkey empty word descend __dbdelim __dbsdf os __run alert browse dbedit outerr outstd readkey readvar __atprompt __input __menuto __nonoallert __typefile __xrestscreen __xsavescreen dbappend dbclearfilter dbcloseall dbclosearea dbcommit dbcommitall dbcreate dbdelete dbeval dbf dbfilter dbgobottom dbgoto dbgotop dbrecall dbrlock dbrlocklist dbrunlock dbseek dbselectarea dbsetdriver dbsetfilter dbskip dbstruct dbunlock dbunlockall dbusearea indexext indexkey indexord ordbagext ordbagname ordcondset ordcreate orddestroy ordfor ordkey ordlistadd ordlistclear ordlistrebuild ordname ordnumber ordsetfocus rddlist rddname rddsetdefault __dbcontinue __dbzap __fledit __rddsetdefault __dbcopystruct __dbcopyxstruct __dbcreate __dbstructfilter dbskipper cdow cmonth ctod date day days dow dtoc dtos month year getenv set setmode settypeahead version __setcentury __setfunction break errorsys throw errornew hb_setkeysave hb_setkeycheck hb_setkeyget setkey __quit __wait file frename __dir col maxcol maxrow row hb_colorindex curdir dirchange dirremove diskspace fclose fcreate ferase ferror fopen fread freadstr fseek fwrite hb_diskspace hb_feof isdisk makedir abs exp int log max min mod round sqrt hb_isbyref procfile procline procname type valtype valtoprg tone hb_langname hb_langselect isaffirm isnegative nationmsg pcount hb_pvalue alltrim asc at chr hardcr hb_ansitooem hb_oemtoansi hb_valtostr isalpha isdigit islower isupper left len lower ltrim memotran padc padl padr rat replicate right rtrim space str strtran strzero substr transform trim upper val devoutpict elaptime seconds secs time do threadstart threadstop threadsleep threadkill threadjoin createmutex destroymutex mutexlock mutexunlock subscribe subscribenow notify notifyall waitforthreads killallthreads inetinit inetcleanup inetcreate inetdestroy inetconnect inetserver inetaccept inetsettimeout inetgettimeout inetcleartimeout inetrecv inetrecvall inetsend inetsendall inetdgram inetdgramrecv inetdgramsend inetaddress inetport ineterror ineterrordesc inetgethosts inetconnectip hb_regex hb_regexmatch hb_regexsplit hb_regexcomp hb_readini hb_writeini hb_random hb_chechsum hb_crypt hb_decrypt hb_hextonum hb_numtohex hb_exec hb_execfromarray hb_class hb_keyput hb_osnewline"
list_pragma = Set.fromList $ words $ "#include #if #ifdef #ifndef #endif #else #define"

regex_CLASS'5b'5ct_'5d'2b = compileRegex "CLASS[\\t ]+"
regex_DO'5b'5ct_'5d'2bCASE'5b'5ct_'5d'2a'24 = compileRegex "DO[\\t ]+CASE[\\t ]*$"
regex_return_'3f = compileRegex "return ?"
regex_'5cd'2b = compileRegex "\\d+"
regex_END'28CLASS'29'3f_'2a'24 = compileRegex "END(CLASS)? *$"

defaultAttributes = [("TopLevel","Normal Text"),("comment","Comment"),("ml_comment","Comment"),("string","String"),("stringc","String"),("logic","Operator"),("ClassContext","Normal Text")]

parseRules "TopLevel" = 
  do (attr, result) <- (((pDetect2Chars False '/' '*' >>= withAttribute "Comment") >>~ pushContext "ml_comment")
                        <|>
                        ((pFirstNonSpace >> pDetectChar False '*' >>= withAttribute "Comment") >>~ pushContext "comment")
                        <|>
                        ((pDetect2Chars False '/' '/' >>= withAttribute "Comment") >>~ pushContext "comment")
                        <|>
                        ((pDetectChar False '"' >>= withAttribute "String") >>~ pushContext "string")
                        <|>
                        ((pDetectChar False '\'' >>= withAttribute "String") >>~ pushContext "stringc")
                        <|>
                        ((pString False ".and." >>= withAttribute "Operator"))
                        <|>
                        ((pString False ".or." >>= withAttribute "Operator"))
                        <|>
                        ((pString False ".not." >>= withAttribute "Operator"))
                        <|>
                        ((pString False ".f." >>= withAttribute "Operator"))
                        <|>
                        ((pString False ".t." >>= withAttribute "Operator"))
                        <|>
                        ((pAnyChar ":=!" >>= withAttribute "Operator"))
                        <|>
                        ((pDetectChar False '@' >>= withAttribute "Keyword"))
                        <|>
                        ((pFirstNonSpace >> pRegExpr regex_CLASS'5b'5ct_'5d'2b >>= withAttribute "Keyword") >>~ pushContext "ClassContext")
                        <|>
                        ((pFirstNonSpace >> pRegExpr regex_DO'5b'5ct_'5d'2bCASE'5b'5ct_'5d'2a'24 >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_context'5fbeginners >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_context'5fterminators >>= withAttribute "Keyword"))
                        <|>
                        ((pColumn 0 >> pRegExpr regex_return_'3f >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_keywords >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_set'5fcommands >>= withAttribute "Keyword"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_functions >>= withAttribute "Function"))
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_pragma >>= withAttribute "Preprocessor"))
                        <|>
                        ((pDetect2Chars False '-' '>' >>= withAttribute "Operator"))
                        <|>
                        ((pRegExpr regex_'5cd'2b >>= withAttribute "Number")))
     return (attr, result)

parseRules "comment" = 
  pzero

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

parseRules "string" = 
  do (attr, result) <- ((pDetectChar False '"' >>= withAttribute "String") >>~ (popContext))
     return (attr, result)

parseRules "stringc" = 
  do (attr, result) <- ((pDetectChar False '\'' >>= withAttribute "String") >>~ (popContext))
     return (attr, result)

parseRules "logic" = 
  do (attr, result) <- ((pDetectChar False '.' >>= withAttribute "Operator") >>~ (popContext))
     return (attr, result)

parseRules "ClassContext" = 
  do (attr, result) <- (((pDetect2Chars False '/' '*' >>= withAttribute "Comment") >>~ pushContext "ml_comment")
                        <|>
                        ((pFirstNonSpace >> pDetectChar False '*' >>= withAttribute "Comment") >>~ pushContext "comment")
                        <|>
                        ((pDetect2Chars False '/' '/' >>= withAttribute "Comment") >>~ pushContext "comment")
                        <|>
                        ((pKeyword " \n\t.():!+,-<=>%&*/;?[]^{|}~\\" list_class'5fkeywords >>= withAttribute "Keyword"))
                        <|>
                        ((pFirstNonSpace >> pRegExpr regex_END'28CLASS'29'3f_'2a'24 >>= withAttribute "Keyword") >>~ (popContext)))
     return (attr, result)

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