-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathExts.hs
More file actions
180 lines (160 loc) · 7.54 KB
/
Exts.hs
File metadata and controls
180 lines (160 loc) · 7.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
-----------------------------------------------------------------------------
-- |
-- Module : Language.Haskell.Exts
-- Copyright : (c) Niklas Broberg 2004-2009
-- License : BSD-style (see the file LICENSE.txt)
--
-- Maintainer : Niklas Broberg, d00nibro@chalmers.se
-- Stability : stable
-- Portability : portable
--
-- An umbrella module for the various functionality
-- of the package. Also provides some convenient
-- functionality for dealing directly with source files.
--
-----------------------------------------------------------------------------
module Language.Haskell.Exts (
-- * Re-exported modules
module Language.Haskell.Exts.Syntax
, module Language.Haskell.Exts.Build
, module Language.Haskell.Exts.Lexer
, module Language.Haskell.Exts.Pretty
, module Language.Haskell.Exts.Fixity
, module Language.Haskell.Exts.ExactPrint
, module Language.Haskell.Exts.SrcLoc
, module Language.Haskell.Exts.Comments
, module Language.Haskell.Exts.Extension
, module Language.Haskell.Exts.Parser
-- * Parsing of Haskell source files
, parseFile
, parseFileWithMode
, parseFileWithExts
, parseFileWithComments
, parseFileWithCommentsAndPragmas
, parseFileContents
, parseFileContentsWithMode
, parseFileContentsWithExts
, parseFileContentsWithComments
-- * Read extensions declared in LANGUAGE pragmas
, readExtensions
) where
import Language.Haskell.Exts.Build
import Language.Haskell.Exts.Comments
import Language.Haskell.Exts.Parser
import Language.Haskell.Exts.Syntax
import Language.Haskell.Exts.Lexer ( lexTokenStream, lexTokenStreamWithMode, Token(..) )
import Language.Haskell.Exts.Pretty
import Language.Haskell.Exts.Fixity
import Language.Haskell.Exts.ExactPrint
import Language.Haskell.Exts.SrcLoc
import Language.Haskell.Exts.Extension
import Data.List
import Data.Maybe (fromMaybe)
import Language.Preprocessor.Unlit
import System.IO
-- | Parse a source file on disk, using the default parse mode.
parseFile :: FilePath -> IO (ParseResult (Module SrcSpanInfo))
parseFile fp = parseFileWithMode (defaultParseMode { parseFilename = fp }) fp
-- | Parse a source file on disk, with an extra set of extensions to know about
-- on top of what the file itself declares.
parseFileWithExts :: [Extension] -> FilePath -> IO (ParseResult (Module SrcSpanInfo))
parseFileWithExts exts fp =
parseFileWithMode (defaultParseMode {
extensions = exts,
parseFilename = fp }) fp
-- | Parse a source file on disk, supplying a custom parse mode.
parseFileWithMode :: ParseMode -> FilePath -> IO (ParseResult (Module SrcSpanInfo))
parseFileWithMode p fp = readUTF8File fp >>= return . parseFileContentsWithMode p
parseFileWithComments :: ParseMode -> FilePath -> IO (ParseResult (Module SrcSpanInfo, [Comment]))
parseFileWithComments p fp = readUTF8File fp >>= return . parseFileContentsWithComments p
-- | Parse a source file on disk, supplying a custom parse mode, and retaining comments
-- as well as unknown pragmas.
parseFileWithCommentsAndPragmas
:: ParseMode -> FilePath
-> IO (ParseResult (Module SrcSpanInfo, [Comment], [UnknownPragma]))
parseFileWithCommentsAndPragmas p fp =
readUTF8File fp >>= return . parseFileContentsWithCommentsAndPragmas p
-- | Parse a source file from a string using a custom parse mode retaining comments
-- as well as unknown pragmas.
parseFileContentsWithCommentsAndPragmas
:: ParseMode -> String
-> ParseResult (Module SrcSpanInfo, [Comment], [UnknownPragma])
parseFileContentsWithCommentsAndPragmas pmode str = separatePragmas parseResult
where parseResult = parseFileContentsWithComments pmode str
-- | Parse a source file from a string using the default parse mode.
parseFileContents :: String -> ParseResult (Module SrcSpanInfo)
parseFileContents = parseFileContentsWithMode defaultParseMode
-- | Parse a source file from a string, with an extra set of extensions to know about
-- on top of what the file itself declares.
parseFileContentsWithExts :: [Extension] -> String -> ParseResult (Module SrcSpanInfo)
parseFileContentsWithExts exts =
parseFileContentsWithMode (defaultParseMode { extensions = exts })
-- | Parse a source file from a string using a custom parse mode.
parseFileContentsWithMode :: ParseMode -> String -> ParseResult (Module SrcSpanInfo)
parseFileContentsWithMode p@(ParseMode fn oldLang exts ign _ _ _) rawStr =
let md = delit fn $ ppContents rawStr
(bLang, extraExts) =
case (ign, readExtensions md) of
(False, Just (mLang, es)) ->
(fromMaybe oldLang mLang, es)
_ -> (oldLang, [])
in -- trace (fn ++ ": " ++ show extraExts) $
parseModuleWithMode (p { baseLanguage = bLang, extensions = exts ++ extraExts }) md
parseFileContentsWithComments :: ParseMode -> String -> ParseResult (Module SrcSpanInfo, [Comment])
parseFileContentsWithComments p@(ParseMode fn oldLang exts ign _ _ _) rawStr =
let md = delit fn $ ppContents rawStr
(bLang, extraExts) =
case (ign, readExtensions md) of
(False, Just (mLang, es)) ->
(fromMaybe oldLang mLang, es)
_ -> (oldLang, [])
in parseModuleWithComments (p { baseLanguage = bLang, extensions = exts ++ extraExts }) md
-- | Gather the extensions declared in LANGUAGE pragmas
-- at the top of the file. Returns 'Nothing' if the
-- parse of the pragmas fails.
readExtensions :: String -> Maybe (Maybe Language, [Extension])
readExtensions str = case getTopPragmas str of
ParseOk pgms -> extractLang $ concatMap getExts pgms
_ -> Nothing
where getExts :: ModulePragma l -> [Either Language Extension]
getExts (LanguagePragma _ ns) = map readExt ns
getExts _ = []
readExt (Ident _ e) =
case classifyLanguage e of
UnknownLanguage _ -> Right $ classifyExtension e
lang -> Left lang
readExt Symbol {} = error "readExt: Symbol"
extractLang = extractLang' Nothing []
extractLang' lacc eacc [] = Just (lacc, eacc)
extractLang' Nothing eacc (Left l : rest) = extractLang' (Just l) eacc rest
extractLang' (Just l1) eacc (Left l2:rest)
| l1 == l2 = extractLang' (Just l1) eacc rest
| otherwise = Nothing
extractLang' lacc eacc (Right ext : rest) = extractLang' lacc (ext:eacc) rest
ppContents :: String -> String
ppContents = unlines . f . lines
where f (('#':_):rest) = rest
f x = x
delit :: String -> String -> String
delit fn = if ".lhs" `isSuffixOf` fn then unlit fn else id
readUTF8File :: FilePath -> IO String
readUTF8File fp = do
h <- openFile fp ReadMode
hSetEncoding h utf8
c <- hGetContents h >>= \s -> length s `seq` return s
hClose h
return c
-- | Converts a parse result with comments to a parse result with comments and
-- unknown pragmas.
separatePragmas :: ParseResult (Module SrcSpanInfo, [Comment])
-> ParseResult (Module SrcSpanInfo, [Comment], [UnknownPragma])
separatePragmas r =
case r of
ParseOk (m, comments) ->
let (pragmas, comments') = partition pragLike comments
in ParseOk (m, comments', map commentToPragma pragmas)
where commentToPragma (Comment _ l s) =
UnknownPragma l $ init $ drop 1 s
pragLike (Comment b _ s) = b && pcond s
pcond s = length s > 1 && take 1 s == "#" && last s == '#'
ParseFailed l s -> ParseFailed l s