lentil-0.1.2.0: src/Lentil/Parse/Source.hs
-----------------------------------------------------------------------------
-- |
-- Module : Lentil.Parse.Source
-- Copyright : © 2015 Francesco Ariis
-- License : GPLv3 (see the LICENSE file)
--
-- Comments from source files
-----------------------------------------------------------------------------
module Lentil.Parse.Source where
import Lentil.Types
import Text.Parsec hiding (Line)
import qualified Data.Char as C
import qualified Data.Maybe as M
import qualified Data.List as L
import Control.Applicative hiding (many, (<|>))
import Prelude -- 7.8 hack
-----------
-- TYPES --
-----------
-- from source to comment strings
type ParSource a = Parsec String () a
data ParSyntax = ParSyntax { psLineComms :: [String],
psBlockComms :: [(String, String)],
psStringLits :: [Char],
psCharLits :: [Char] }
deriving (Show)
-----------------
-- ANCILLARIES --
-----------------
-- like many1 for manyTill
manyTill1 :: ParSource a -> ParSource b -> ParSource [a]
manyTill1 p ed = (:) <$> p <*> manyTill p ed
--------------
-- COMMENTS --
--------------
data CommentString = SingleLine Row String -- sans newline
| MultiLine Row String
deriving (Show, Eq)
-- parses stuff like "-- comment"
lineComment :: String -> ParSource CommentString
lineComment s = SingleLine <$>
(string s *> getRow) <*>
(manyTill anyChar newline)
<?> "line comment"
blockComment :: (String, String) -> ParSource CommentString
blockComment (i, e) = MultiLine <$>
(string i *> getRow) <*>
manyTill anyChar (try $ string e)
<?> "block comment"
getRow :: ParSource Row
getRow = fmap sourceLine getPosition
------------------
-- OTHER BLOCKS --
------------------
-- quoted strings, escaped by \, by mauke^
litString :: Char -> ParSource String
litString ic = q *> many ((char '\\' *> anyChar) <|> noneOf no) <* q
<?> "codestring"
where q = char ic
no = C.showLitChar ic "\\"
-- quoted single character
litChar :: Char -> ParSource Char
litChar ic = q *> ((char '\\' *> anyChar) <|> anyChar) <* q
<?> "char string sign"
where q = char ic
-- a program is instructions to the computer. Ends when you meet
-- a well formed element from above (linecomm, blockcom, stringlit,
-- charLit)
program :: ParSyntax -> ParSource String
program ps = manyTill1 anyChar (endp <|> ("" <$ eof))
<?> "program"
where
-- endp :: [ParSource String]
endp = lookAhead . choice . map try $ map string posts ++ [lchars]
-- every "post" symbol (init comments, listring char)
posts = psLineComms ps ++ -- line
map fst (psBlockComms ps) ++ -- block
map (:[]) (psStringLits ps) -- lit string
-- ambiguous symbols (pall'aaa'foo is valid haskell identifier,
-- so it should stay here
lchars = "" <$ choice (map litChar (psCharLits ps))
------------
-- SOURCE --
------------
-- given a set of lineparsers / blockparsers
sourcePart :: ParSyntax -> ParSource (Maybe CommentString)
sourcePart ps@(ParSyntax lc bc sl cl) = choice [plc, pbc, psl, pcl, ppr]
<?> "source file part"
where
ct = choice . map try
plc = Just <$> (ct . map lineComment $ lc) -- line comlm
pbc = Just <$> (ct . map blockComment $ bc) -- block comm
psl = Nothing <$ (choice . map try . map litString $ sl) -- str lit
pcl = Nothing <$ (ct . map litChar $ cl) -- char lit
ppr = Nothing <$ program ps -- program
-- ps: syntax to sever comment from the rest
source :: ParSyntax -> ParSource [CommentString]
source ps = fmap M.catMaybes (many1 (sourcePart ps)) <|> ([] <$ eof)
<?> "source file"
-------------
-- CONVERT --
-------------
type Comment = (Row, String)
comms2Tuple :: [CommentString] -> [Comment]
comms2Tuple [] = []
comms2Tuple (c:cs) | isSl c = let (a,z) = span isSl (c:cs)
in groupLineComms a ++ comms2Tuple z
| otherwise = comment2Tuple c : comms2Tuple cs
where
isSl (SingleLine _ _) = True
isSl _ = False
-- use it on Line comments **only**!
groupLineComms :: [CommentString] -> [Comment]
groupLineComms cs = let cs' = map comment2Tuple cs
f (r, s) i = (r, s, r-i)
zipped = zipWith f cs' (enumFrom 1)
grouped = L.groupBy threeEq zipped
in map (flatComm . map back) grouped
where
threeEq (_, _, x) (_, _, y) = x == y
back (a, b, _) = (a, b)
flatComm cs' = (fst . head $ cs',
unlines . map snd $ cs')
comment2Tuple :: CommentString -> Comment
comment2Tuple (SingleLine r t) = (r, t)
comment2Tuple (MultiLine r t) = (r, t)