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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
module Main where
import System (getProgName, getArgs)
import System.CurryPath (lookupModuleSourceInLoadPath, stripCurrySuffix)
import Directory (doesFileExist, getHomeDirectory)
import FilePath ( (</>) )
import Curry.Files (readFullAST)
import Curry.Types
import Curry.Position
import Curry.SpanInfo
import Curry.Span
import Curry.Ident
import Parse.CommandLine (parseOpts, usageText)
import Parse.Config (parseConfig, defaultConfig)
import State
import Types
import Check
import Pretty.ToString (renderMessagesToString)
import Pretty.ToJson (renderMessagesToJson)
import Pretty.ShowOptions (showOptions)
scBanner :: String
scBanner = unlines [bannerLine,bannerText,bannerLine]
where
bannerText = "Curry Style Checker (Version of 22/10/2019)"
bannerLine = take (length bannerText) (repeat '-')
styleGuideURL :: String
styleGuideURL = "http://www.informatik.uni-kiel.de/~curry/style/"
main :: IO ()
main = getCheckOpts >>= styleCheck
styleCheck :: Arguments -> IO ()
styleCheck a@(_, flags, _) = do
config <- getConfig flags >>= updateConfigWithOpts flags
restrict config 1 scBanner
restrict config 3 (showOptions config)
if Help `elem` flags
then putStrLn $ usageText ++ "\nSee also the Curry Style Guide at\n\n "
++ styleGuideURL
else styleCheck' a config
styleCheck' :: Arguments -> Config -> IO ()
styleCheck' (_, _, []) config =
restrict config 1 "All given files checked.\n"
styleCheck' (p, o, (fileName:files)) config = do
let modName = stripCurrySuffix fileName
restrict config 2 $ "--------------------------------\n"
++ "INFO: Reading module " ++ modName
filePaths <- lookupModuleSourceInLoadPath modName
case filePaths of
Nothing -> do putStrLn $ "WARNING: "
++ modName
++ " does not exist\n"
styleCheck' (p, o, files) config
Just (_,filePath) -> do
ast <- getAST modName config
src <- getSrc filePath config
restrict config 2 $ "INFO: Checking style of file " ++ modName
messages <- return (checkAll src ast config modName (getOutputOption config))
restrict config 1 $ "--------------------------------\n"
++ modName ++ "\n"
++ "--------------------------------\n"
restrict config 0 $ messages ++"\n"
styleCheck' (p, o, files) config
getOutputOption :: Config -> (Config -> String -> [SrcLine] -> [Message] -> String)
getOutputOption c = case oType c of
JSON -> renderMessagesToJson
TEXT -> renderMessagesToString
updateConfigWithOpts :: [Flag] -> Config -> IO Config
updateConfigWithOpts [] conf = return conf
updateConfigWithOpts (f:fs) conf@(Config checks out verb hint code maxLength) = case f of
(Ignore s) -> do
newCheckl <- updateChecks s checks False conf
updateConfigWithOpts fs (Config newCheckl out verb hint code maxLength)
(Add s) -> do
newCheckl <- updateChecks s checks True conf
updateConfigWithOpts fs (Config newCheckl out verb hint code maxLength)
(OType "JSON") ->
updateConfigWithOpts fs (conf {oType = JSON})
(OType "TEXT") ->
updateConfigWithOpts fs (conf {oType = TEXT})
(Verbosity i) ->
updateConfigWithOpts fs (conf {verbosity = (if ((i < 4) && (i > -1)) then i else 1)})
_ -> updateConfigWithOpts fs conf
updateChecks :: String -> CheckList -> Bool -> Config -> IO CheckList
updateChecks s checkl b c = case s of
"tabs" -> return checkl {tab = b}
"lineLength" -> return checkl {lineLength = b}
"tabs" -> return checkl {tab = b}
"ifThenElse" -> return checkl {ifThenElse = b}
"case" -> return checkl {caseIndent = b}
"do" -> return checkl {doIndent = b}
"let" -> return checkl {letIndent = b}
"guard" -> return checkl {guardIndent = b}
"functionRhs" -> return checkl {rhsAlign = b}
"equalsTrue" -> return checkl {equalstrue = b}
"signatures" -> return checkl {topLevelSig = b}
"blankLines" -> return checkl {blankLines = b}
"trailingSpaces" -> return checkl {trailingS = b}
"whiteSpaces" -> return checkl {whiteSpace = b}
"moduleHeader" -> return checkl {moduleheader = b}
"imports" -> return checkl {imports = b}
"data" -> return checkl {dataIndent = b}
"list" -> return checkl {listIndent = b}
"thentrueelsefalse" -> return checkl {thenTrueElseFalse = b}
"notEqual" -> return checkl {notEqual = b}
"notOrd" -> return checkl {notOrd = b}
"equalsEmptyList" -> return checkl {equalsEmptyList = b}
"identFunc" -> return checkl {identFunc = b}
"constFunc" -> return checkl {constFunc = b}
"andOr" -> return checkl {andOr = b}
"print" -> return checkl {printCheck = b}
"deriving" -> return checkl {derivingIndent = b}
"class" -> return checkl {classIndent = b}
"instance" -> return checkl {instanceIndent = b}
_ -> do restrict c 2
( "WARNING: tried to "
++ (if b then "add" else "ignore")
++ " an invalid check \180"
++ s
++ "\180, passing over")
return checkl
restrict :: Config -> Int -> String -> IO ()
restrict conf i s = whenM ((verbosity conf) >= i)
(putStrLn s)
configFileName :: String
configFileName = "currystylecheckrc"
getConfig :: [Flag] -> IO Config
getConfig flags = do
iconfig <- updateConfigWithOpts flags defaultConfig
home <- getHomeDirectory
configExistsHere <- doesFileExist (configFileName)
if configExistsHere
then parseConfig (verbosity iconfig > 1) (configFileName)
else do
restrict iconfig 2 $ "INFO: config file not found in current directory,"
++ " searching home directory"
configExistsHome <- doesFileExist $ home </> configFileName
if configExistsHome
then parseConfig (verbosity iconfig > 1) $ home </> configFileName
else do
restrict iconfig 2 $ "INFO: config file not found in home directory,"
++ " using default settings"
return defaultConfig
getAST :: String -> Config -> IO (Module ())
getAST fileName config =
if (anyAST config)
then do restrict config 2 $ "INFO: Getting SpanAST of " ++ fileName
ast <- readFullAST fileName
const done $!! ast
return ast
else return (Module
(SpanInfo (Span (Position 1 1) (Position 1 1)) [])
[]
(ModuleIdent NoSpanInfo ["NoAST"])
Nothing
[]
[]
)
getSrc :: String -> Config -> IO [(Int,String)]
getSrc fileName config =
if (anySrc config)
then do restrict config 2 $ "INFO: Parsing file " ++ fileName
ls <- readFile (fileName) >>= return . lines
let src = zip [1..(length ls)] ls
return $ filter (\(_,l) -> (length l) > 0) src
else return []
getCheckOpts :: IO Arguments
getCheckOpts = do
args <- getArgs
prog <- getProgName
(o,n) <- parseOpts args
return (prog, o, n)
|