-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrace.lisp
319 lines (288 loc) · 12.2 KB
/
race.lisp
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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
;;; Copyright (c) 2009, Joshua Taylor. All rights reserved.
;;; Redistribution and use in source and binary forms, with or without
;;; modification, are permitted provided that the following conditions
;;; are met:
;;; * Redistributions of source code must retain the above copyright
;;; notice, this list of conditions and the following disclaimer.
;;; * Redistributions in binary form must reproduce the above
;;; copyright notice, this list of conditions and the following
;;; disclaimer in the documentation and/or other materials
;;; provided with the distribution.
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(in-package #:race-webservice)
(defconstant +race-webservice-url+
"http://attempto.ifi.uzh.ch/ws/race/racews.perl"
"The URL of the RACE webservice.")
(defconstant +race-prefix-uri+
"http://attempto.ifi.uzh.ch/race")
(defun invoke-race-webservice (&rest args
&key axioms
theorems
mode
si st ot sdt dodt iodt sti aux
(uri +race-webservice-url+)
(drakma-parameters '()))
"* Syntax:
invoke-race-webservice &key axioms theorems mode
si st or sdt dodt iodt sti aux
drakma-parameters uri => result*
* Arguments and Values:
- axioms --- a string
- theorems --- a string or nil
- mode --- one of the symbols :check-consistency, :prove, :answer-query
- si, st, ot, sdt, dodt, iodt, sti, aux --- generalized booleans
- uri --- a string or PURI:URI object, defaults to +race-webservice-url+
- drakma-parameters --- a plist, passed to DRAKMA:HTTP-REQUEST
- results --- results of DRAKMA:HTTP-REQUEST
* Description:
invoke-race-webservice invokes the RACE Webservice by sending a SOAP
request to URI. More documentation on the RACE Webservice is available [1].
* See Also:
- [1] {http://attempto.ifi.uzh.ch/site/docs/race_webservice.html}"
(declare (ignore axioms theorems mode si st ot sdt dodt iodt sti aux))
(remf* args :uri :drakma-parameters)
(apply 'drakma:http-request uri
:content (apply 'make-race-soap-request args)
drakma-parameters))
(defun make-race-soap-request
(&key axioms theorems mode si st ot sdt dodt iodt sti aux)
(assert (stringp axioms) (axioms)
"Axioms must be a string of ACE text, but was ~S." axioms)
(assert (or (and (member mode '("prove" "answer-query" "answer_query")
:test 'string-equal)
(stringp theorems))
(and (member mode '("check-consistency" "check_consistency")
:test 'string-equal)
(null theorems)))
(mode theorems)
"Mode must be a string designator string-equal to prove, ~
answer-query \(or answer_query\), or check-consistency ~
\(or check_consistency\). If mode is prove or answer-query, ~
then theorems much be a string. If mode is check-consistency, ~
then theorems must be null. Mode was ~S. Theorems was ~S."
mode theorems)
(with-soap-envelope ("env" ("race" +race-prefix-uri+))
(cxml:with-element* ("race" "Request")
;; Axioms
(cxml:with-element* ("race" "Axioms")
(cxml:text axioms))
;; Theorems
(unless (null theorems)
(cxml:with-element* ("race" "Theorems")
(cxml:text theorems)))
;; Mode
(cxml:with-element* ("race" "Mode")
(cxml:text
(pcase mode #'string-equal
(("prove")
"prove")
(("answer-query" "answer_query")
"answer_query")
(("check-consistency" "check_consistency")
"check_consistency"))))
;; Parameters
(loop for p in '("si" "st" "ot" "sdt" "dodt" "iodt" "sti" "aux")
for pp in (list si st ot sdt dodt iodt sti aux)
when pp do (cxml:with-element* ("race" "Parameter")
(cxml:text p))))))
(defun race (&rest args
&key
axioms theorems mode
si st ot sdt dodt iodt sti aux
(uri +race-webservice-url+)
(drakma-parameters '())
(signal-warning-messages t)
(signal-error-messages nil))
"* Syntax:
race &rest args &key signal-warning-messags signal-error-messages => reply
* Arguments and Values:
- args --- a plist of args for {defun race-webservice:invoke-race-webservice}
- signal-warning-messages, signal-error-messages --- booleans,
defaults are t and nil respectively
- reply --- a {defclass race-webservice:race-reply} object
* Description:
Race applies {defun race-webservice:invoke-race-webservice} with args,
parses the reply, and signals warnings and errors depending on the
values of signal-warning-messages and signal-error messages.
RACE messages that have importance type \"warning\" will, by default
be signalled, while messages with importance \"error\" will not.
* Notes:
Since CXML:PARSE may or may not be able to handle a stream returned by
DRAKMA:HTTP-REQUEST, any drakma-parameters is passed to
{defun race-webservice:invoke-race-webservice} with :want-stream nil.
invoke-race-webservice is called with :allow-other-keys t \(so that
signal-warning-messages and signal-error-messsages can passed
through\) and as a result, arguments that would cause an unexpected
keyword error to be signalled will not do so."
(declare (ignore axioms theorems mode si st ot
sdt dodt iodt sti aux uri))
(remf* args :signal-warning-messages :signal-error-messages)
(let ((reply (parse-race-output
(apply' invoke-race-webservice
:drakma-parameters (list* :want-stream nil
drakma-parameters)
args))))
(prog1 reply
(signal-race-reply-messages reply
:signal-warning-messages signal-warning-messages
:signal-error-messages signal-error-messages))))
(defun signal-race-reply-messages
(reply &key signal-warning-messages signal-error-messages)
(dolist (message (race-reply-messages reply))
(etypecase message
(race-error-message
(when signal-error-messages
(error message)))
(race-warning-message
(when signal-warning-messages
(warn message))))))
;;;; Classes related to the RACE Reply
(defclass race-reply ()
((messages
:type list
:reader race-reply-messages
:initform '()
:initarg :messages)
(runtime
:type integer
:reader race-reply-runtime
:initarg :runtime)
(proofs
:type list
:reader race-reply-proofs
:initform '()
:initarg :proofs)
(why-not
:type list ; of strings
:reader race-reply-why-not
:initform '()
:initarg :why-not
:documentation
"A list of strings, the content of the race:Word elements that
were in the race:WhyNot element.")))
(define-condition race-message (condition)
((importance
:type (member :warning :error)
:reader race-message-importance
:documentation "One of the symbols :warning, or :error.")
(type
:type string
:reader race-message-type
:initarg :type)
(sentence-id
:type (or null integer)
:reader race-message-sentence-id
:initarg :sentence-id
:documentation "An integer \(or left unbound\).")
(subject
:type string
:reader race-message-subject
:initarg :subject)
(description
:type string
:reader race-message-description
:initarg :description))
(:report report-race-message))
(defun report-race-message (condition stream)
(format stream
"~A ~A (This is a ~S message~@[, see sentence ~S~].)"
(race-message-subject condition)
(race-message-description condition)
(race-message-type condition)
(if (not (slot-boundp condition 'sentence-id)) nil
(race-message-sentence-id condition))))
(define-condition race-warning-message (warning race-message)
((importance :initform :warning)))
(define-condition race-error-message (error race-message)
((importance :initform :error)))
(defclass race-proof ()
((used-axioms
:type list
:reader race-proof-used-axioms
:initarg :used-axioms)
(used-aux-axioms
:type list
:reader race-proof-used-aux-axioms
:initarg :used-aux-axioms)))
;;;; Parsing the RACE Reply
;;; Some utility XML handling functions
(defun get-race-elements-by-tag-name (document tag-name)
(dom:get-elements-by-tag-name-ns
document +race-prefix-uri+ tag-name))
(defun get-race-element-by-tag-name (document tag-name)
(let ((elements (get-race-elements-by-tag-name document tag-name)))
(if (eql 0 (dom:length elements)) nil
(dom:item elements 0))))
(defun element-text-content (element)
(case (dom:length (dom:child-nodes element))
(0 "")
(1 (dom:data (dom:first-child element)))
(t (error "Don't know how to extract text content from element ~
~S with more than one child." element))))
(defun mapnodes (fn nodelist &aux (results '()))
(dom:do-node-list (x nodelist (nreverse results))
(push (funcall fn x) results)))
;;; Parsing functions proper
(defun parse-race-output (blob)
"* Arguments and Values:
- blob --- an XML blob that contains the SOAP envelope"
(let* ((response (cxml:parse blob (cxml-dom:make-dom-builder)))
(messages (get-race-elements-by-tag-name response "Message"))
(runtime (get-race-element-by-tag-name response "Runtime"))
(proofs (get-race-elements-by-tag-name response "Proof"))
(words (get-race-elements-by-tag-name response "Word")))
(make-instance 'race-reply
:proofs (extract-proofs proofs)
:messages (extract-messages messages)
:runtime (extract-runtime runtime)
:why-not (extract-words words))))
(defun extract-proofs (elements &aux (proofs '()))
"* Arguments and Values:
- elements --- a node list of race:Proof elements"
(dom:do-node-list (element elements (nreverse proofs))
(push (make-instance 'race-proof
:used-axioms (mapnodes 'element-text-content
(get-race-elements-by-tag-name
element "Axiom"))
:used-aux-axioms (mapnodes 'element-text-content
(get-race-elements-by-tag-name
element "AuxAxiom")))
proofs)))
(defun extract-messages (elements &aux (messages '()))
"* Arguments and Values:
- elements --- a node list of race:Message element nodes"
(dom:do-node-list (element elements (nreverse messages))
(push (extract-message element) messages)))
(defun extract-message (element)
"* Arguments and Values:
- element --- a race:Message element node"
(flet ((et (name)
(element-text-content
(get-race-element-by-tag-name
element name))))
(apply 'make-condition (pcase (et "Importance") #'string=
("warning" 'race-warning-message)
("error" 'race-error-message))
:type (et "Type")
:subject (et "Subject")
:description (et "Description")
(unless (string= "" (et "SentenceID"))
(list :sentence-id (parse-integer (et "SentenceID")))))))
(defun extract-runtime (element)
"* Arguments and Values:
- element --- an race:Runtime element node"
(parse-integer (element-text-content element)))
(defun extract-words (elements)
"* Arguments and Values:
- elements --- a node list of race:Word element nodes"
(mapnodes 'element-text-content elements))