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)
 |