module Text.Highlighting.Kate.Syntax.Lua ( highlight, parseExpression, syntaxName, syntaxExtensions ) where
import Text.Highlighting.Kate.Definitions
import Text.Highlighting.Kate.Common
import qualified Text.Highlighting.Kate.Syntax.Doxygen
import Text.ParserCombinators.Parsec
import Data.List (nub)
import Data.Map (fromList)
import Data.Maybe (fromMaybe)
syntaxName :: String
syntaxName = "Lua"
syntaxExtensions :: String
syntaxExtensions = "*.lua"
highlight :: String -> Either String [SourceLine]
highlight input =
case runParser parseSource startingState "source" input of
Left err -> Left $ show err
Right result -> Right result
parseExpression :: GenParser Char SyntaxState LabeledSource
parseExpression = do
st <- getState
let oldLang = synStLanguage st
setState $ st { synStLanguage = "Lua" }
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 [("Lua",["Normal"])], synStLanguage = "Lua", synStCurrentLine = "", synStCharsParsedInLine = 0, synStCaseSensitive = True, synStKeywordCaseSensitive = True, synStCaptures = []}
parseSourceLine = manyTill parseExpressionInternal pEndLine
pEndLine = do
newline <|> (eof >> return '\n')
context <- currentContext
case context of
"Normal" -> return ()
"Comment" -> (popContext >> return ())
"Block Comment" -> return ()
"String_single" -> pushContext "Error"
"String_double" -> pushContext "Error"
"String_block" -> return ()
"Error" -> return ()
_ -> return ()
lineContents <- lookAhead wholeLine
updateState $ \st -> st { synStCurrentLine = lineContents, synStCharsParsedInLine = 0 }
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
updateState $ \st -> st { synStCharsParsedInLine = oldCharsParsed + length txt }
return (nub [style, attr], txt)
styles = [("Alerts","Alert"),("BFunc","Function"),("Comment","Comment"),("Constant","Keyword"),("Control","Keyword"),("Error","Error"),("Keyword","Keyword"),("Normal Text","Normal"),("Numbers","DecVal"),("Strings","String"),("Symbols","Others"),("Variable","Keyword")]
parseExpressionInternal = do
context <- currentContext
parseRules context <|> (pDefault >>= withAttribute (fromMaybe "" $ lookup context defaultAttributes))
defaultAttributes = [("Normal","Normal Text"),("Comment","Comment"),("Block Comment","Comment"),("String_single","Strings"),("String_double","Strings"),("String_block","Strings"),("Error","Error")]
parseRules "Normal" =
do (attr, result) <- (((Text.Highlighting.Kate.Syntax.Doxygen.parseExpression >>= ((withAttribute "") . snd)))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["table.foreach","table.foreachi","foreach","foreachi"] >>= withAttribute "Error"))
<|>
((pDetectSpaces >>= withAttribute "Normal Text"))
<|>
((pDetect2Chars False '-' '-' >>= withAttribute "Comment") >>~ pushContext "Comment")
<|>
((pDetect2Chars False '[' '[' >>= withAttribute "Strings") >>~ pushContext "String_block")
<|>
((pDetectChar False '\'' >>= withAttribute "Strings") >>~ pushContext "String_single")
<|>
((pDetectChar False '"' >>= withAttribute "Strings") >>~ pushContext "String_double")
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["string.byte","string.char","string.find","string.len","string.lower","string.rep","string.sub","string.upper","string.format","string.gfind","string.gsub","table.concat","table.getn","table.sort","table.insert","table.remove","table.setn","math.abs","math.sin","math.cos","math.tan","math.asin","math.acos","math.atan","math.atan2","math.ceil","math.floor","math.mod","math.frexp","math.ldexp","math.squrt","math.min","math.max","math.log","math.log10","math.exp","math.deg","math.rad","math.random","math.randomseed","io.close","io.flush","io.input","io.lines","io.open","io.output","io.read","io.stderr","io.stdin","io.stdout","io.tmpfile","io.write","os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale","os.time","os.tmpname","debug.getinfo","debug.getlocal","debug.setlocal","debug.sethook","debug.gethook","assert","collectgarbage","dofile","error","next","print","rawget","rawset","tonumber","tostring","type","_ALERT","_ERRORMESSAGE","call","getmetatable","gcinfo","ipairs","loadfile","loadstring","pairs","pcall","require","LUA_PATH","setmetatable","_LOADED","_VERSION","gettagmethod","globals","newtag","setglobal","settag","settagmethod","setlinehook","getglobals","copytagmethods","dostring","getglobal","tag","setglobals","unpack","exit","readfrom","writeto","appendto","read","write","getinfo","getlocal","setlocal","setcallhook","tinsert","tremove","flush","seek","setlocale","execute","remove","rename","tmpname","getenv","getn","sort","table.foreach","table.foreachi","foreach","foreachi","abs","sin","cos","tan","asin","acos","atan","atan2","ceil","floor","mod","frexp","ldexp","squrt","min","max","log","log10","exp","deg","rad","random","randomseed","strlen","strsub","strlower","strupper","strchar","strrep","ascii","strbyte","format","strfind","gsub","openfile","closefile","date","clock","cgilua","cgilua.lp.translate","cgilua.contentheader","cgilua.script_file","cgilua.header","cgilua.script_path","cgilua.htmlheader","cgilua.script_pdir","cgilua.redirect","cgilua.script_vdir","cgilua.mkabsoluteurl","cgilua.script_vpath","cgilua.mkurlpath","cgilua.servervariable","cgilua.put","cgilua.urlpath","cgilua.handlelp","cgilua.errorlog","cgilua.lp.compile","cgilua.seterrorhandler","cgilua.lp.include","cgilua.seterroroutput","cgilua.lp.setcompatmode","cgilua.addclosefunction","cgilua.lp.setoutfunc","cgilua.addopenfunction","cgilua.addscripthandler","cgilua.addscripthandler","cgilua.buildprocesshandler","cgilua.setmaxfilesize","cgilua.setmaxinput","cgilua.urlcode.encodetable","cgilua.urlcode.escape","cgilua.urlcode.parsequery","cgilua.urlcode.unescape","cgilua.urlcode.insertfield","cgilua.setoutfunc","cgilua.addopenfunction","cgilua.doif","cgilua.doscript","cgilua.pack","cgilua.splitpath","cgilua.cookies.get","cgilua.cookies.set","cgilua.cookies.sethtml","cgilua.cookies.delete","cgilua.serialize","cgilua.session.close","cgilua.session.data","cgilua.session.load","cgilua.session.new","cgilua.session.open","cgilua.session.save","cgilua.session.setsessiondir","cgilua.session.delete","cgilua.session","cgilua.cookies","numrows","connect","close","fetch","getcolnames","getcoltypes","commit","rollback","setautocommit","lfs","lfs.attributes","lfs.chdir","lfs.currentdir","lfs.dir","lfs.lock","lfs.mkdir","lfs.rmdir","lfs.touch","lfs.unlock","zip","zip.open","zip.openfile","files","seek","close","lines"] >>= withAttribute "BFunc"))
<|>
((pRegExpr (compileRegex "\\bfunction\\b") >>= withAttribute "Keyword"))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["and","function","in","local","not","or"] >>= withAttribute "Keyword"))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["nil","false","true"] >>= withAttribute "Keyword"))
<|>
((pRegExpr (compileRegex "\\belse\\b") >>= withAttribute "Control"))
<|>
((pRegExpr (compileRegex "\\belseif\\b") >>= withAttribute "Control"))
<|>
((pRegExpr (compileRegex "\\bdo\\b") >>= withAttribute "Control"))
<|>
((pRegExpr (compileRegex "\\bif\\b") >>= withAttribute "Control"))
<|>
((pRegExpr (compileRegex "\\bend\\b") >>= withAttribute "Control"))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["break","do","else","elseif","end","for","if","repeat","return","then","until","while"] >>= withAttribute "Control"))
<|>
((pDetectChar False '{' >>= withAttribute "Symbols"))
<|>
((pDetectChar False '}' >>= withAttribute "Symbols"))
<|>
((pRegExpr (compileRegex "\\b\\d*\\.?\\d*(e|e\\-|e\\+)?\\d+\\b") >>= withAttribute "Numbers"))
<|>
((pRegExpr (compileRegex "\\b-?0[xX][0-9a-fA-F]+\\b") >>= withAttribute "Numbers"))
<|>
((pRegExpr (compileRegex "\\b[a-zA-Z_][a-zA-Z0-9_]*(?=\\s*([({'\"]|\\[\\[))\\b") >>= withAttribute "Normal Text"))
<|>
((pRegExpr (compileRegex "\\b[A-Z_][A-Z0-9_]*\\b") >>= withAttribute "Constant"))
<|>
((pRegExpr (compileRegex "\\b[a-zA-Z_][a-zA-Z0-9_]*\\b") >>= withAttribute "Variable"))
<|>
((pDetect2Chars False '!' '=' >>= withAttribute "Error"))
<|>
((pDetect2Chars False '-' '=' >>= withAttribute "Error"))
<|>
((pDetect2Chars False '+' '=' >>= withAttribute "Error"))
<|>
((pDetect2Chars False '+' '+' >>= withAttribute "Error"))
<|>
((pDetect2Chars False '.' '=' >>= withAttribute "Error"))
<|>
((pAnyChar "[]().=~+-*/^><#;" >>= withAttribute "Symbols")))
return (attr, result)
parseRules "Comment" =
do (attr, result) <- (((pDetect2Chars False '[' '[' >>= withAttribute "Comment") >>~ pushContext "Block Comment")
<|>
((pDetect2Chars False '-' '-' >>= withAttribute "Alerts"))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["TODO","FIXME","NOTE"] >>= withAttribute "Alerts")))
return (attr, result)
parseRules "Block Comment" =
do (attr, result) <- (((pDetect2Chars False ']' ']' >>= withAttribute "Comment") >>~ (popContext >> return ()))
<|>
((pDetect2Chars False '-' '-' >>= withAttribute "Alerts"))
<|>
((pKeyword " \n\t():!+,-<=>%&*/;?[]^{|}~\\\"" ["TODO","FIXME","NOTE"] >>= withAttribute "Alerts")))
return (attr, result)
parseRules "String_single" =
do (attr, result) <- (((pRegExpr (compileRegex "\\\\(a|b|f|n|r|t|v|\\\\|\"|\\'|[|])") >>= withAttribute "Symbols"))
<|>
((pDetectChar False '\'' >>= withAttribute "Strings") >>~ (popContext >> return ())))
return (attr, result)
parseRules "String_double" =
do (attr, result) <- (((pRegExpr (compileRegex "\\\\[abfnrtv'\"\\\\\\[\\]]") >>= withAttribute "Symbols"))
<|>
((pDetectChar False '"' >>= withAttribute "Strings") >>~ (popContext >> return ())))
return (attr, result)
parseRules "String_block" =
do (attr, result) <- (((pRegExpr (compileRegex "\\\\(a|b|f|n|r|t|v|\\\\|\"|\\'|[|])") >>= withAttribute "Symbols"))
<|>
((pDetect2Chars False ']' ']' >>= withAttribute "Strings") >>~ (popContext >> return ())))
return (attr, result)
parseRules "Error" =
pzero
parseRules x = fail $ "Unknown context" ++ x