spot.in 12.4 KB
Newer Older
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
#!@PYTHON@
# -*- mode: python; coding: iso-8859-1 -*-
# Copyright (C) 2011 Laboratoire de Recherche et Dveloppement de
# l'Epita (LRDE).
#
# This file is part of Spot, a model checking library.
#
# Spot is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Spot is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Spot; see the file COPYING.  If not, write to the Free
# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.

import os

# Directory for temporary files (images and other auxiliary files).
imgdir = 'spotimg'

# Location of the dot command
dot = '@DOT@'
dot_bgcolor = '-Gbgcolor=#FFFFFF00'

svg_output = False # FIXME: SVG output seems to be working well with
                   #        Firefox only.  We have to figure out how
34
                   #        to get the correct size and transparent
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
                   #        background in Chrome.

from CGIHTTPServer import CGIHTTPRequestHandler
class MyHandler(CGIHTTPRequestHandler):
    def is_cgi(self):
        if self.path.startswith('/cgi-bin/spot.py'):
            self.cgi_info = '', self.path[9:]
            return True
        return False

if not os.environ.has_key('SCRIPT_NAME'):
    # If this is not run as a cgi script, let's start an HTTP server.
    from BaseHTTPServer import HTTPServer
    server_address=('',8000)
    if not os.access(imgdir, os.F_OK):
        os.mkdir(imgdir, 0755)
        print "Directory spotimg/ created."
    httpd = HTTPServer(server_address, MyHandler)
    print "Point your browser to http://localhost:8000/ltl2tgba.html"
    httpd.serve_forever()

import sys
import cgi
import cgitb; cgitb.enable()
import signal
import uuid

print "Content-Type: text/html"
print

# Redirect stderr to stdout.
os.close(sys.stderr.fileno())
os.dup2(sys.stdout.fileno(), sys.stderr.fileno())

# Assume Spot is installed
sys.path.insert(0, '@pythondir@')

if (os.environ.has_key('SERVER_SOFTWARE') and
    os.environ['SERVER_SOFTWARE'].startswith(MyHandler.server_version)):
    # We might be running from the build tree (but it's not sure).
    # Add the build and source directories first in the search path.
    # If we are not in the right place, python will find the installed
    # libraries later.
    sys.path.insert(0, '@srcdir@/../.libs')
    sys.path.insert(0, '@srcdir@/..')
    sys.path.insert(0, '../.libs')
    sys.path.insert(0, '..')
    # Darwin needs some help in figuring out where non-installed libtool
    # libraries are (on this platform libtool encodes the expected final
    # path of dependent libraries in each library).
    m = '../.libs:@top_builddir@/src/.libs:@top_builddir@/buddy/src/.libs'
    os.environ['DYLD_LIBRARY_PATH'] = m

try:
    execfile('ltl2tgba.opt')
except IOError:
    pass

import spot
import buddy

def alarm_handler(signum, frame):
    print """<p><font color="red">The script was aborted because
it has been running for too long.</font>  Please try a shorter formula,
or different options (not drawing automata usually helps).
If you want to benchmark big formulae it is
better to install Spot on your own computer.</p>"""
    sys.stdout.flush()
    os.kill(0, signal.SIGTERM)

def reset_alarm():
    signal.alarm(30)

def render_dot(basename):
    print '<div class="dot">'
    if svg_output:
        ext = 'svg'
    else:
        ext = 'png'
    os.spawnlp(os.P_WAIT, dot, dot, dot_bgcolor, '-T' + ext,
               '-Gsize=8.2,8.2', '-o', basename + '.' + ext, basename + '.txt')
    reset_alarm()
    b = cgi.escape(basename)
    if svg_output:
        print ('<object type="image/svg+xml" data="' + b + '.svg">'
               + 'Your browser does not support SVG.</object>')
    else:
        print ('<img src="' + b + '.png"><br>(<a href="' + b
               + '.txt">dot source</a>)')
    print '</div>'
    sys.stdout.flush()

127
def render_automaton(basename, automata, dont_run_dot, issba, deco = False):
128
129
    outfile = spot.ofstream(basename + '.txt')
    if not deco:
130
        spot.dotty_reachable(outfile, automata, issba)
131
    else:
132
        spot.dotty_reachable(outfile, automata, issba, deco)
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
    del outfile
    if dont_run_dot:
        print ('<p>' + dont_run_dot + ''' to be rendered on-line.  However
you may download the <a href="''' + cgi.escape(basename)
               + '.txt">source in dot format</a> and render it yourself.')
    else:
        render_dot(basename)


def print_stats(automaton):
    stats = spot.stats_reachable(automaton)
    print "<p>", stats.states,
    if stats.states <= 1:
        print " state,",
    else:
        print " states,",
    print stats.transitions,
    if stats.transitions <= 1:
        print " transition,",
    else:
        print " transitions,",
    # compute the number of acceptance conditions
    count = automaton.number_of_acceptance_conditions()
    if count > 0:
        print count,
        if count <= 1:
            print "acceptance condition:",
        else:
            print "acceptance conditions:",
            sys.stdout.flush()
        acc = automaton.all_acceptance_conditions()
        spot.bdd_print_accset(spot.get_cout(), automaton.get_dict(), acc)
    else:
        print "no acceptance condition (all cycles are accepting)"
    print "</p>"
    sys.stdout.flush()
    # Decide whether we will render the automaton or not.
    # (A webserver is not a calcul center...)
    if stats.states > 64:
        return "Automaton has too much states"
    if float(stats.transitions)/stats.states > 10:
        return "Automaton has too much transitions per states"
    return False

form = cgi.FieldStorage()
uid = str(uuid.uuid1())
imgprefix = imgdir + '/' + uid


output_type = form.getfirst('o', 'v');

# Version requested.
185
if output_type == 'v':
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
    print 'Spot version ' + spot.version()
    exit(0)

spot.unblock_signal(signal.SIGALRM)
spot.unblock_signal(signal.SIGTERM)
os.setpgrp()
signal.signal(signal.SIGALRM, alarm_handler)
reset_alarm()

formula = form.getfirst('f', '')

env = spot.default_environment.instance()
pel = spot.empty_parse_error_list()
f = spot.parse(formula, pel, env)

if pel:
    print '<div class="parse-error">'
    err = spot.format_parse_errors(spot.get_cout(), formula, pel)
    print '</div>'

# Formula simplifications
opt = spot.Reduce_None
for r in form.getlist('r'):
    if r == 'br':
        opt = opt + spot.Reduce_Basics
    elif r == 'si':
        opt = opt + spot.Reduce_Syntactic_Implications
    elif r == 'eu':
        opt = opt + spot.Reduce_Eventuality_And_Universality
    elif r == 'lc':
        opt = opt + spot.Reduce_Containment_Checks_Stronger
if opt != spot.Reduce_None:
    f2 = spot.reduce(f, opt)
    f.destroy()
    f = f2

# Formula manipulation only.
if output_type == 'f':
224
    formula_format = form.getfirst('ff', 'o')
225
    # o = Spot, i = Spin, g = GraphViz
226

227
228
229
    if formula_format == 'o':
        print '<div class="formula spot-format">%s</div>' % f
    elif formula_format == 'i':
230
        print ('<div class="formula spin-format">'
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
               + spot.to_spin_string(f) + '</div>')
    elif formula_format == 'g':
        outfile = spot.ofstream(imgprefix + '-f.txt')
        spot.dotty(outfile, f)
        del outfile
        render_dot(imgprefix + '-f')
    exit(0)

# Formula translation.
translator = form.getfirst('t', 'fm')

dict = spot.bdd_dict()

if translator == 'fm':
    exprop = False
    symb_merge = False
    branching_postponement = False
    fair_loop_approx = False
    for fm in form.getlist('fm'):
        if fm == 'od':
            exprop = True
        elif fm == 'sm':
            symb_merge = True
        elif fm == 'bp':
            branching_postponement = True
        elif fm == 'fl':
            fair_loop_approx = True
    automaton = spot.ltl_to_tgba_fm(f, dict,
259
                                    exprop, symb_merge,
260
261
262
263
264
265
266
267
268
269
270
                                    branching_postponement, fair_loop_approx)
elif translator == 'la':
    automaton = spot.ltl_to_tgba_lacim(f, dict)
    if form.getfirst('la', '') == 'sp':
        automaton.delete_unaccepting_scc()
elif translator == 'ta':
    refined_rules = False
    if form.getfirst('ta', '') == 'lc':
        refined_rules = True
    automaton = spot.ltl_to_taa(f, dict, refined_rules)

271
272
273
# Should it be displayed as a SBA?
issba = False

274
275
276
277
278
279
280
# Monitor output
if output_type == 'm':
    automaton = spot.scc_filter(automaton)
    automaton = spot.minimize_monitor(automaton)
    print '<div class="automata-stats">'
    dont_run_dot = print_stats(automaton)
    print '</div>'
281
    render_automaton(imgprefix + '-a', automaton, dont_run_dot, issba)
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
    automaton = 0
    exit(0)

# Automaton simplifications
prune_scc = False
wdba_minimize = False
for s in form.getlist('as'):
    if s == 'ps':
        prune_scc = True
    elif s == 'wd':
        wdba_minimize = True

if output_type == 'a':
    buchi_type = form.getfirst('af', 't')
elif output_type == 'r':
    buchi_type = form.getfirst('ra', 't')
else:
    print "Unkown output type 'o=%s'." % output_type
    automaton = 0
    exit(0)

degen = False
neverclaim = False
if buchi_type == 's':
    degen = True
elif buchi_type == 'i':
    degen = True
    neverclaim = True

if prune_scc:
    # Do not suppress all useless acceptance conditions if
    # degeneralization is requested: keeping those that lead to
    # accepting states usually helps.
315
    automaton = spot.scc_filter(automaton, not degen)
316
317
318
319
320
321
322

if wdba_minimize:
    minimized = spot.minimize_obligation_new(automaton, f)
    if minimized:
        automaton = minimized
        minimized = 0
        degen = False # No need to degeneralize anymore
323
        issba = True
324
325
326

if degen or neverclaim:
    degen = spot.tgba_sba_proxy(automaton)
327
    issba = True
328
329
330
331
332
333
334
335
336
337
338
339
else:
    degen = automaton

# Buchi Automaton Output
if output_type == 'a':
    if buchi_type == 'i':
        s = spot.ostringstream()
        spot.never_claim_reachable(s, degen, f)
        print '<div class="neverclaim">%s</div>' % cgi.escape(s.str())
        del s
    else: # 't' or 's'
        dont_run_dot = print_stats(degen)
340
        render_automaton(imgprefix + '-a', degen, dont_run_dot, issba)
341
342
343
344
345
346
347
348
349
350
351
352
353
354
    degen = 0
    automaton = 0
    exit(0)

# Buchi Run Output
if output_type == 'r':

    print_acc_run = False
    draw_acc_run = False
    s = form.getfirst('rf', 'p')
    if s == 'p':
        print_acc_run = True
    elif s == 'd':
        draw_acc_run = True
355
356


357
    err = ""
358
    opt = (form.getfirst('ec', 'Cou99') + "(" +
359
360
361
362
           form.getfirst('eo', '') + ")")
    eci, err = spot.emptiness_check_instantiator.construct(opt)

    if not eci:
363
        print ('<div class="parse-error">Cannot parse "' + opt + '" near "'
364
365
366
367
368
369
               + err + '".</div>')
    else:
        ec_a = 0
        n_acc = degen.number_of_acceptance_conditions()
        n_max = eci.max_acceptance_conditions()
        if (n_acc <= n_max):
370
            ec_a = degen
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
        else:
            print ('<div class="ec-error">Cannot run ' + opt
                   + ' on automata with more than ' + str(n_max)
                   + ' acceptance condition.<br/>Please build '
                   + 'a degeneralized B&uuml;chi automaton if you '
                   + 'want to try this algorithm.</div>')
        if ec_a:
            ec = eci.instantiate(ec_a)
        else:
            ec = 0

    if ec:
        ec_res = ec.check()
        if not ec_res:
            print '<div class="ec">No accepting run found.</div>'
        else:
            ec_run = ec_res.accepting_run()
            print '<div class="ec">An accepting run was found.<br/>'
            if ec_run:
                if print_acc_run:
                    s = spot.ostringstream()
                    spot.print_tgba_run(s, ec_a, ec_run)
                    print '<div class="accrun">%s</div>' % cgi.escape(s.str())
                    del s

                if draw_acc_run:
                    deco = spot.tgba_run_dotty_decorator(ec_run)
                    dont_run_dot = print_stats(ec_a)
399
400
                    render_automaton(imgprefix + '-e', ec_a, dont_run_dot,
                                     issba, deco)
401
402
403
404
405
                    del deco
                del ec_run
            del ec_res
            print '</div>'
        del ec
406
        del ec_a
407
408
409
    degen = 0
    automaton = 0
    exit(0)