-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMake4.hs
272 lines (206 loc) · 6.28 KB
/
Make4.hs
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
-- A simple, CPSA specific make system
module Make (cpsa, shapes, sas, cleanse, get, set,
build, clean, roots) where
{- Place a copy of this source file in the directory used to store
CPSA problem statements, edit it to suit your needs, and load it into
a Haskell interpreter.
Normally, just the build and the clean command are used. It's the
build command that you usually modify.
To analyze a problem in prob.scm, type:
*Make> cpsa "prob"
If successful, the analysis is in the file prob.xhtml, which can be
viewed with a standards-compliant browser.
For a shapes only version of the analysis, type:
*Make> shapes "prob"
If successful, the shapes are in the file prob_shapes.xhtml.
*Make> sas "prob"
If successful, the shape analysis sentences are in the file
prob_sas.text.
To remove the files generated from source files, type:
*Make> cleanse "prob"
To see the command-line options used by CPSA, type:
*Make> get
To change the command-line options used by CPSA to "-b 15", type:
*Make> set "-b 15"
To analyze all source files in the directory, type:
*Make> build
To remove the files generated from source files in the directory, type:
*Make> clean
-}
import Control.Monad (mapM_)
import Data.List (sort)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import System.Exit (ExitCode (..))
import System.Process (system)
import System.IO (putStrLn)
import System.IO.Unsafe (unsafePerformIO)
import System.FilePath (FilePath, splitExtension)
import System.Directory (removeFile, doesFileExist, getModificationTime,
getCurrentDirectory, getDirectoryContents)
-- Flags for CPSA
initialCpsaFlags :: String
initialCpsaFlags = "+RTS -M512m -RTS"
graphFlags :: String
graphFlags = ""
-- To enable zooming, use:
-- graphFlags = " -z"
-- A mutable location for CPSA flags
cpsaFlags :: IORef String
cpsaFlags = unsafePerformIO $ newIORef initialCpsaFlags
-- Get the CPSA flags
get :: IO String
get =
readIORef cpsaFlags
-- Set the CPSA flags
set :: String -> IO ()
set flags =
writeIORef cpsaFlags flags
-- Transformation rules
data Rule = Rule
{ prog :: String, -- program to run
inputExt :: String, -- input file name extension
outputExt :: String } -- output file name extension
-- Graph Rule
graph :: FilePath -> IO ()
graph root =
make graphRule root -- make graph using given rule
graphRule :: Rule
graphRule =
Rule { prog = "cpsa4graph" ++ graphFlags,
inputExt = cpsaExt,
outputExt = graphExt }
-- CPSA Rule
cpsa :: FilePath -> IO ()
cpsa root =
do
cpsaBasic root
shapes root
graph root
-- CPSA using Basic rule
cpsaBasic :: FilePath -> IO ()
cpsaBasic root =
do
flags <- get -- get CPSA flags
make (cpsaBasicRule flags) root -- make CPSA output using given rule
cpsaBasicRule :: String -> Rule
cpsaBasicRule flags =
Rule { prog = "cpsa4 " ++ flags,
inputExt = sourceBasicExt,
outputExt = cpsaExt }
-- Shapes Rule
shapes :: FilePath -> IO ()
shapes root =
do
cpsaBasic root -- Run CPSA if need be
make shapesRule root
graph $ root ++ shapesRoot
shapesRule :: Rule
shapesRule =
Rule { prog = "cpsa4shapes",
inputExt = cpsaExt,
outputExt = shapesRoot ++ cpsaExt }
-- SAS Rule
sas :: FilePath -> IO ()
sas root =
do
cpsaBasic root -- Run CPSA if need be
make sasRule root
sasRule :: Rule
sasRule =
Rule { prog = "cpsa4sas",
inputExt = cpsaExt,
outputExt = sasExt }
-- Clean generated files
cleanse :: FilePath -> IO ()
cleanse root =
do
rm $ root ++ cpsaExt
rm $ root ++ graphExt
rm $ root ++ shapesRoot ++ cpsaExt
rm $ root ++ shapesRoot ++ graphExt
rm $ root ++ sasExt
-- File Extensions
sourceBasicExt :: String
sourceBasicExt = ".scm"
cpsaExt :: String
cpsaExt = ".txt"
shapesRoot :: String
shapesRoot = "_shapes"
sasExt :: String
sasExt = "_sas.text"
graphExt :: String
graphExt = ".xhtml"
-- Rule Interpreters
-- Make output for root using rule
make :: Rule -> FilePath -> IO ()
make rule root =
do
let input = root ++ inputExt rule
let output = root ++ outputExt rule
done <- made input output
case done of
True -> return () -- Nothing to do
False -> run (prog rule) input output
-- See if an output file is up-to-date
made :: FilePath -> FilePath -> IO Bool
made input output =
do
src <- doesFileExist input
dst <- doesFileExist output
case src && dst of
False -> return False
True ->
do
src <- getModificationTime input
dst <- getModificationTime output
return $ src < dst
-- Run a program with input and output from files
-- Print the command before running it. Delete the output when the
-- command fails.
run :: String -> FilePath -> FilePath -> IO ()
run prog input output =
do
let cmd = prog ++ " -o " ++ output ++ " " ++ input
putStrLn cmd
code <- system cmd
case code of
ExitSuccess -> return ()
ExitFailure _ ->
do
--rm output
fail "Command failed"
-- Remove a file
-- Prints the command when there is a file to be deleted.
rm :: FilePath -> IO ()
rm output =
do
exists <- doesFileExist output
case exists of
False -> return () -- File doesn't exist
True ->
do -- Print command before removal
putStrLn $ "rm " ++ output
removeFile output
-- Return the roots of the CPSA source files in the current directory.
roots :: [String] -> IO [FilePath]
roots exts =
do
dir <- getCurrentDirectory
files <- getDirectoryContents dir
let roots = [ root |
file <- files,
let (root, ext) = splitExtension file,
elem ext exts ] -- Filter for source files
return $ sort roots
-- Build the shapes for all the source files in the current directory.
build :: IO ()
build =
do
probs <- roots [sourceBasicExt]
mapM_ cpsa probs
-- Clean files generated for all the source files in the current directory.
clean :: IO ()
clean =
do
probs <- roots [sourceBasicExt]
mapM_ cleanse probs