-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathXMLstate.hs
274 lines (242 loc) · 8.92 KB
/
XMLstate.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
273
274
{- |
Module : $Header$
Description : after parsing XML message a list of XMLcommands is produced,
containing commands that need to be executed
Copyright : uni-bremen and DFKI
License : GPLv2 or higher, see LICENSE.txt
Maintainer : r.pascanu@jacobs-university.de
Stability : provisional
Portability : portable
PGIP.XMLstate contains the description of the XMLstate and a function
that produces such a state
-}
module PGIP.XMLstate where
import Common.Utils (getEnvDef, trim)
import Common.ToXml
import Driver.Options
import Text.XML.Light
import Data.List (find, intercalate)
import Data.Time.Clock.POSIX (getPOSIXTime)
import System.IO (Handle, hPutStrLn, hFlush)
{- Converts any line text that does not stand for a
comment into a theoryitem element -}
genProofStep :: String -> Element
genProofStep str = let
iname = case trim str of
"" -> "whitespace" -- empty line generates a whitespace element
'#' : _ -> "comment" -- comments start with #
_ -> "theoryitem" -- convert line into a theoryitem element
in unode iname $ mkText $ str ++ "\n"
-- | adds xml structure to unstructured code
addPGIPMarkup :: String -> Element
addPGIPMarkup str = case lines str of
[] -> error "addPgipMarkUp.empty"
hd : tl ->
unode "parseresult"
$ add_attr (mkAttr "thyname" "whatever") (unode "opentheory" $ mkText hd)
: map genProofStep tl
++ [unode "closetheory" ()]
{-
- other types of mark ups :
- opengoal / closegoal
- openblock / closeblock
-
-}
-- generates a pgipelem element that contains the input text
genPgipElem :: String -> Element
genPgipElem = unode "pgipelem" . mkText
{- generates a normalresponse element that has a pgml element
containing the output text -}
genNormalResponse :: Node t => String -> t -> Element
genNormalResponse areaValue = unode "normalresponse"
. add_attr (mkAttr "area" areaValue)
. unode "pgml"
-- same as above, just for an error instead of normal output
genErrorResponse :: Bool -> String -> Element
genErrorResponse fatality =
add_attrs [ mkAttr "fatality" "fatal" | fatality]
. unode "errorresponse"
. unode "pgmltext" . mkText
{- | It inserts a given string into the XML packet as
normal output -}
addPGIPAnswer :: String -> String -> CmdlPgipState -> CmdlPgipState
addPGIPAnswer msgtxt errmsg st =
if useXML st
then let resp = if null msgtxt then st else
addPGIPElement st $ genNormalResponse "message"
$ mkText msgtxt
in if null errmsg then resp
else addPGIPElement resp $ genErrorResponse False errmsg
else addToMsg errmsg $ addToMsg msgtxt st
{- | It inserts a given string into the XML packet as
error output -}
addPGIPError :: String -> CmdlPgipState -> CmdlPgipState
addPGIPError str st = case str of
"" -> st
_ | useXML st -> addPGIPElement st $ genErrorResponse True str
_ -> addToMsg str st
-- extracts the xml package in XML.Light format (namely the Content type)
addPGIPAttributes :: CmdlPgipState -> Element -> Element
addPGIPAttributes pgipData e = (add_attrs
((case refSeqNb pgipData of
Nothing -> []
Just v -> [mkAttr "refseq" v])
++ [ mkAttr "tag" $ name pgipData
, mkAttr "class" "pg"
, mkAttr "id" $ pgipId pgipData
, mkAttr "seq" $ show $ seqNb pgipData ])
$ unode "pgip" ()) { elContent = [Elem e]}
{- adds one element to the end of the content of the xml packet that represents
the current output of the interface to the broker -}
addPGIPElement :: CmdlPgipState -> Element -> CmdlPgipState
addPGIPElement pgData el =
pgData { xmlElements = addPGIPAttributes pgData el : xmlElements pgData
, seqNb = seqNb pgData + 1 }
{- adds a ready element at the end of the xml packet that represents the
current output of the interface to the broker -}
addPGIPReady :: CmdlPgipState -> CmdlPgipState
addPGIPReady pgData = addPGIPElement pgData $ unode "ready" ()
-- | State that keeps track of the comunication between Hets and the Broker
data CmdlPgipState = CmdlPgipState
{ pgipId :: String
, name :: String
, seqNb :: Int
, refSeqNb :: Maybe String
, msgs :: [String]
, xmlElements :: [Element]
, hout :: Handle
, hin :: Handle
, stop :: Bool
, resendMsgIfTimeout :: Bool
, useXML :: Bool
, maxWaitTime :: Int }
-- | Generates an empty CmdlPgipState
genCMDLPgipState :: Bool -> Handle -> Handle -> Int -> IO CmdlPgipState
genCMDLPgipState swXML h_in h_out timeOut = do
pgId <- genPgipID
return CmdlPgipState
{ pgipId = pgId
, name = "Hets"
, seqNb = 1
, refSeqNb = Nothing
, msgs = []
, xmlElements = []
, hin = h_in
, hout = h_out
, stop = False
, resendMsgIfTimeout = True
, useXML = swXML
, maxWaitTime = timeOut }
-- | Generates the id of the session between Hets and the Broker
genPgipID :: IO String
genPgipID =
do
t1 <- getEnvDef "HOSTNAME" ""
t2 <- getEnvDef "USER" ""
t3 <- getPOSIXTime
return $ t1 ++ "/" ++ t2 ++ "/" ++ show t3
-- | Concatenates the input string to the message stored in the state
addToMsg :: String -> CmdlPgipState -> CmdlPgipState
addToMsg str pgD = if null str then pgD else pgD { msgs = str : msgs pgD }
-- | Resets the content of the message stored in the state
resetPGIPData :: CmdlPgipState -> CmdlPgipState
resetPGIPData pgD = pgD
{ msgs = []
, xmlElements = [] }
{- the PGIP protocol defines the pgip element as containing a single
subelement. -}
convertPGIPDataToString :: CmdlPgipState -> String
convertPGIPDataToString =
intercalate "\n" . reverse . map showElement . xmlElements
isRemote :: HetcatsOpts -> Bool
isRemote opts = connectP opts /= -1 || listen opts /= -1
sendPGIPData :: HetcatsOpts -> CmdlPgipState -> IO CmdlPgipState
sendPGIPData opts pgData =
do
let xmlMsg = convertPGIPDataToString pgData
pgData' = addToMsg xmlMsg pgData
sendMSGData opts pgData'
sendMSGData :: HetcatsOpts -> CmdlPgipState -> IO CmdlPgipState
sendMSGData opts pgData = do
let msg = intercalate "\n" $ reverse $ msgs pgData
if isRemote opts
then hPutStrLn (hout pgData) msg
else putIfVerbose opts 1 msg
hFlush $ hout pgData
return pgData
-- | List of all possible commands inside an XML packet
data CmdlXMLcommands =
XmlExecute String
| XmlExit
| XmlProverInit
| XmlAskpgip
| XmlStartQuiet
| XmlStopQuiet
| XmlOpenGoal String
| XmlCloseGoal String
| XmlGiveUpGoal String
| XmlUnknown String
| XmlParseScript String
| XmlUndo
| XmlRedo
| XmlForget String
| XmlOpenTheory String
| XmlCloseTheory String
| XmlCloseFile String
| XmlLoadFile String deriving (Eq, Show)
-- extracts the seq attribute value to be used as reference number elsewhere
getRefseqNb :: String -> Maybe String
getRefseqNb input =
let xmlTree = parseXML input
elRef = find (\ x -> case x of
Elem dt -> qName (elName dt) == "pgip"
_ -> False) xmlTree
in case elRef of
Nothing -> Nothing
Just el -> case el of
Elem dt -> case find (\ x -> qName (attrKey x) == "seq") $
elAttribs dt of
Nothing -> Nothing
Just elatr -> Just $ attrVal elatr
_ -> Nothing
{- | parses the xml message creating a list of commands that it needs to
execute -}
parseXMLTree :: [Content] -> [CmdlXMLcommands] -> [CmdlXMLcommands]
parseXMLTree xmltree acc = case xmltree of
Elem info : ls -> case parseXMLElement info of
Just c -> parseXMLTree ls (c : acc)
Nothing -> parseXMLTree (elContent info ++ ls) acc
_ : ls -> parseXMLTree ls acc
[] -> acc
parseXMLElement :: Element -> Maybe CmdlXMLcommands
parseXMLElement info = let cnt = strContent info in
case qName $ elName info of
"proverinit" -> Just XmlProverInit
"proverexit" -> Just XmlExit
"startquiet" -> Just XmlStartQuiet
"stopquiet" -> Just XmlStopQuiet
"opengoal" -> Just $ XmlOpenGoal cnt
"proofstep" -> Just $ XmlExecute cnt
"closegoal" -> Just $ XmlCloseGoal cnt
"giveupgoal" -> Just $ XmlGiveUpGoal cnt
"spurioscmd" -> Just $ XmlExecute cnt
"dostep" -> Just $ XmlExecute cnt
"editobj" -> Just $ XmlExecute cnt
"undostep" -> Just XmlUndo
"redostep" -> Just XmlRedo
"forget" -> Just $ XmlForget cnt
"opentheory" -> Just $ XmlOpenTheory cnt
"theoryitem" -> Just $ XmlExecute cnt
"closetheory" -> Just $ XmlCloseTheory cnt
"closefile" -> Just $ XmlCloseFile cnt
"loadfile" -> Just $ XmlLoadFile cnt
"askpgip" -> Just XmlAskpgip
"parsescript" -> Just $ XmlParseScript cnt
"pgip" -> Nothing
s -> Just $ XmlUnknown s
{- | Given a packet (a normal string or a xml formated string), the function
converts it into a list of commands -}
parseMsg :: CmdlPgipState -> String -> [CmdlXMLcommands]
parseMsg st input = if useXML st
then parseXMLTree (parseXML input) []
else concatMap (\ x -> [ XmlExecute x | not $ null $ trim x ]) $ lines input