module LexModule (lexmodule, nestcomment) where import Char import List #if !defined(__HASKELL98__) #define isAlphaNum isAlphanum #endif -- lexmodule takes a string (file content), removes any module header, -- and renames any function beginning in the left-most column called -- "main" to "_ain". lexmodule :: String -> String lexmodule = renameFn "main" "_ain" . rmheader . nestcomment 0 -- nestcomment removes all Haskell comments from the given string, -- both nested {- -} style comments and -- line comments, -- dealing correctly with comment characters inside strings, string -- quote marks inside comments, and all sorts of nastiness like that. nestcomment :: Int -> String -> String nestcomment n ('{':'-':cs) | n>=0 = nestcomment (n+1) cs nestcomment n ('-':'}':cs) | n>0 = nestcomment (n-1) cs nestcomment n (c:cs) | n>0 = nestcomment n cs nestcomment 0 ('-':'}':cs) = error ("found close comment -} but no matching open {-") nestcomment 0 ('-':'-':cs) = if null munch || isSpace nextchr || nextchr `elem` ",()[]{};\"'`" || isAlphaNum nextchr then nestcomment 0 (dropWhile (/='\n') munch) else '-':'-': nestcomment 0 cs where munch = dropWhile (=='-') cs nextchr = head munch nestcomment 0 ('\'':'"':'\'':cs) = '\'':'"':'\'': nestcomment 0 cs nestcomment 0 ('\\':'"':cs) = '\\':'"': nestcomment 0 cs nestcomment 0 ('"':cs) = '"': endstring cs nestcomment 0 (c:cs) = c: nestcomment 0 cs nestcomment 0 [] = [] nestcomment n [] = error ("found "++show n++" open comments {- but no matching close -}") endstring ('\\':'"':cs) = '\\':'"': endstring cs endstring ('"':cs) = '"': nestcomment 0 cs endstring (c:cs) = c : endstring cs endstring [] = [] -- rmheader simply removes a "module Name (exports) where" header -- (if present) from the beginning of the given string. rmheader :: String -> String rmheader file = let text = dropWhile isSpace file in if "module" `isPrefixOf` text then stripUntil1 "where" text else text stripUntil s (c:file) = if not (isAlphaNum c) && s `isPrefixOf` file then let rest = drop (length s) file in if null rest || not (isAlphaNum (head rest)) then rest else stripUntil s file else stripUntil s file -- stripUntil1 is intended to be a more efficient version of the basic -- stripUntil, but this has not yet been verified by profiling. stripUntil1 pat (x:xs) | not (isAlphaNum x) = stripUntil2 pat [] xs [] | otherwise = stripUntil1 pat xs -- stripUntil2 pattern storedpat searchstring storedstring stripUntil2 [] ds [] ys = [] stripUntil2 [] ds (x:xs) ys | not (isAlphaNum x) = x:xs | otherwise = stripUntil1 (reverse ds) (reverse ys++x:xs) stripUntil2 (c:cs) ds [] ys = [] stripUntil2 (c:cs) ds (x:xs) ys | c==x = stripUntil2 cs (c:ds) xs (x:ys) | c/=x = stripUntil1 (reverse ds++c:cs) (reverse ys++x:xs) -- renameFn assumes that all fn definitions begin in the leftmost column. renameFn old new = unlines . rename . lines where rename [] = [] rename (s:ss) | old `isPrefixOf` s = (new ++ drop (length old) s) : ss | otherwise = s: rename ss