#!@PYTHON@ # -*- mode: python; coding: iso-8859-1 -*- # Copyright (C) 2007, 2009, 2010 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de # Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), # Université Pierre et Marie Curie. # # 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 sys import os 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()) sys.path.insert(0, '@pythondir@') # Directory for temporary files (images and other auxiliary files). imgdir = 'spotimg' # Extra HTML headers. extra_header = '' # extra_header = '' # Extra notice to display after the Send button. extra_form_notice = '' # Location of the dot command dot = 'dot' # in PATH. dot_bgcolor = '-Gbgcolor=#FFFFFF' # dot = '/usr/local/bin/dot' ltl2tgba_version = '@PACKAGE_VERSION@' def print_footer(): print '
' print 'ltl2tgba.py ' + ltl2tgba_version + '; Spot', spot.version() print '' try: execfile('ltl2tgba.opt') except IOError: pass import spot import buddy # We want the script to kill itself whenever it run for more than 30 # seconds. `dot' can be very long at drawing huge graphs, and may # bring a web server to its knees. Doing so also protects us from any # infinite loop in Spot. # # We use alarm(30) because 30 seconds ought to be enough for everyone. # We create a process group for the script and its children (dot). # Upon SIGALRM, we kill the whole process group. # # For all this to work, SIGALRM and SIGTERM must not be blocked. # Apache (at least in my tests with version 2.0.48) blocks them by # default. I haven't figured a Python way to unblock them, so the # spot bindings supply unblock_signal(signum) for this purpose. def alarm_handler(signum, frame): print """

The script was aborted because it has been running for too long. 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.

""" print_footer() sys.stdout.flush() os.kill(0, signal.SIGTERM) def reset_alarm(): signal.alarm(30) spot.unblock_signal(signal.SIGALRM) spot.unblock_signal(signal.SIGTERM) os.setpgrp() signal.signal(signal.SIGALRM, alarm_handler) reset_alarm() # Cleanup stale files from our image directory. os.system('find ' + imgdir + ' -type f -amin +15 -print | xargs rm -f') myself = os.environ['SCRIPT_NAME']; form = cgi.FieldStorage() filled = form.has_key('formula') uid = str(uuid.uuid1()) imgprefix = imgdir + '/' + uid def escaped_print_set(dict, what): print '
'; sys.stdout.flush()
    s = spot.ostringstream()
    spot.bdd_print_set(s, dict, what)
    print cgi.escape(s.str())
    del s
    print '
'; sys.stdout.flush() def print_stats(automaton): stats = spot.stats_reachable(automaton) print "

", 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 "

" 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 def render_dot(basename): os.spawnlp(os.P_WAIT, dot, dot, dot_bgcolor, '-Tpng', '-Gsize=14,14', '-o', basename + '.png', basename + '.txt') reset_alarm() b = cgi.escape(basename) print '
(dot source)' sys.stdout.flush() def render_automaton(basename, automata, dont_run_dot, deco = False): outfile = spot.ofstream(basename + '.txt') if not deco: spot.dotty_reachable(outfile, automata) else: spot.dotty_reachable(outfile, automata, deco) del outfile if dont_run_dot: print ('

' + dont_run_dot + ''' to be rendered on-line. However you may download the source in dot format and render it yourself.') else: render_dot(basename) def render_bdd(basename, dictionary, bdd): outfile = spot.ofstream(basename + '.txt') spot.bdd_print_dot(outfile, dictionary, bdd) del outfile render_dot(basename) print """ Spot's on-line LTL-to-TGBA translator %s

Spot's on-line LTL-to-TGBA translator

""" % extra_header formula = form.getfirst('formula', '') color_fm = "#DFC6F8" color_lacim = "#F8C6DF" color_taa = "#D8C6FF" options_common = [ ('show_formula_png', 'draw the formula', 0), ('show_automaton_png', 'draw Büchi automaton', 1), ('show_degen_png', 'draw degeneralized Büchi automaton', 0), ('show_never_claim', 'output Spin never claim', 0), ('show_lbtt', 'convert automaton for LBTT', 0), ] options_reduce = [ ('reduce_basics', 'basic rewritings', 1), ('reduce_syntimpl', 'syntactic implication', 1), ('reduce_eventuniv', 'eventuality and universality', 1), ] options_debug = [ ('show_parse', 'show traces during parsing', 0), ('show_dictionnary', 'print BDD dictionary', 0), ] options_trans_fm = [ ('opt_exprop', 'optimize determinism', 1), ('opt_symb_merge', 'merge states with same symbolic successor representation', 1), ('opt_branch_post', 'branching postponement', 0), ('opt_fair_loop_approx','fair-loop approximations', 0), ] options_trans_lacim = [ ('show_relation_set', 'print the transition relation as a BDD set', 0), ('show_relation_png', 'draw the transition relation', 0), ('show_acceptance_set', 'print the acceptance relation as a BDD set', 0), ('show_acceptance_png', 'draw the acceptance relation', 0), ] options_trans_taa = [ ('refined_rules', 'refined rules', 1) ] default_translator = 'trans_fm'; translators = [ ('trans_fm', 'Couvreur/FM', color_fm), ('trans_lacim', 'Couvreur/LaCIM', color_lacim), ('trans_taa', 'Tauriainen/TAA', color_taa), ] options_accepting_run = [ ('print_acc_run', 'print an accepting run...', 0), ('draw_acc_run', 'draw an accepting run...', 0), ('emptiness_check', ['... found using this algorithm: ', 'Cou99', 'CVWY90', 'GV04', 'SE05', 'Tau03', 'Tau03_opt'], 'no'), ('emptiness_check_options', '...with these ' + 'options:', ''), ] print ("""

Formula to translate:
""" % (myself, cgi.escape(formula, True))) if not filled: print """

Use alphanumeric identifiers or double-quoted strings for atomic propositions, and parentheses for grouping.
Identifiers cannot start with the letter of a prefix operator (F, G, or X): for instance GFa means G(F(a)). Use "GFa" if you really want to refer to GFa as a proposition.
Conversely, infix letter operators are not assumed to be operators if they are part of an identifier: aUb is an atomic proposition, unlike a U b and (a)U(b).

Unary operators
(prefix)
Binary operators
(infix)
Constants
not:! and:& && . /\ xor:^ xor true:1 true
eventually:F <> or:| || + \/ until:U false:0 false
always:G [] implies:-> => release:R V
next:X () equivalent:<-> <=>
""" column = [[], []] def add_options(opt_group, opt_column, opt_color, opt_list): if opt_color: opt_color = ' bgcolor="%s"' % opt_color if opt_group: column[opt_column].append(("" % opt_color) + opt_group + "") prefix = '' else: prefix = '    ' for opt, desc, arg, in opt_list: if not isinstance(arg, str): if formula: val = int(form.getfirst(opt, 0)) else: val = arg s = '%s' % (opt_color, prefix); if isinstance(desc, list): s += '%s ' val = desc[val] elif isinstance(arg, str): val = form.getfirst(opt, arg) s += (('%s ') % (desc, opt, cgi.escape(val, True))) else: if val: chk = ' checked' else: chk = '' s += '%s' % (opt, chk, desc) column[opt_column].append((s + '')) globals()[opt] = val column[0].append("Algorithm") trans = form.getfirst("trans", default_translator) for opt, desc, color in translators: if trans == opt: chk = "checked" else: chk = "" globals()[opt] = chk s = \ '%s' column[0].append(s % (color, opt, chk, desc)) add_options('', 0, color, globals()['options_' + opt]) add_options("Search accepting run?", 0, '', options_accepting_run) add_options("Common Options", 1, '', options_common) add_options("Formula Simplications", 1, '', options_reduce) add_options("Debugging Options", 1, '', options_debug) print '' width = range(len(column)) depth = 0 for i in width: depth = max(len(column[i]), depth) for d in range(depth): print '' for i in width: if d < len(column[i]): print column[i][d] else: print '' print '' print '
' print '%s' % extra_form_notice if not filled: print_footer() sys.exit(0) reset_alarm() print "

Output

" env = spot.default_environment.instance() print "

LTL Formula

" if show_parse: print '
'
sys.stdout.flush()
sys.stderr.flush()

pel = spot.empty_parse_error_list()
f = spot.parse(formula, pel, env, show_parse)
if show_parse: print '
' print '
'
err = spot.format_parse_errors(spot.get_cout(), formula, pel)
print '
' if not f: print '

Aborting...

' sys.exit(0) if err: print '

Continuing anyway...

' print "

Formula is", f, "

" if show_formula_png: outfile = spot.ofstream(imgprefix + '-f.txt') spot.dotty(outfile, f) del outfile render_dot(imgprefix + '-f') opt = spot.Reduce_None if reduce_basics: opt = opt + spot.Reduce_Basics if reduce_syntimpl: opt = opt + spot.Reduce_Syntactic_Implications if reduce_eventuniv: opt = opt + spot.Reduce_Eventuality_And_Universality if opt != spot.Reduce_None: f2 = spot.reduce(f, opt) f.destroy() f = f2 print "

Reduced formula is", f, "

" if show_formula_png: outfile = spot.ofstream(imgprefix + '-f2.txt') spot.dotty(outfile, f) del outfile render_dot(imgprefix + '-f2') print '

Automaton

' dict = spot.bdd_dict() print '

Building automaton...', sys.stdout.flush() if opt_exprop: exprop = 1 else: exprop = 0 if opt_symb_merge: symb_merge = 1 else: symb_merge = 0 if opt_branch_post: branching_postponement = 1 else: branching_postponement = 0 if opt_fair_loop_approx: fair_loop_approx = 1 else: fair_loop_approx = 0 if trans_lacim: automaton = spot.ltl_to_tgba_lacim(f, dict) elif trans_fm: automaton = spot.ltl_to_tgba_fm(f, dict, exprop, symb_merge, branching_postponement, fair_loop_approx) elif trans_taa: automaton = spot.ltl_to_taa(f, dict, refined_rules) print 'done.

' sys.stdout.flush() dont_run_dot = print_stats(automaton) if show_automaton_png: render_automaton(imgprefix + '-a', automaton, dont_run_dot) if show_degen_png or show_never_claim: print '

Degeneralized automaton

' degen = spot.tgba_sba_proxy(automaton) dont_run_dot = print_stats(degen) if show_degen_png: render_automaton(imgprefix + '-d', degen, dont_run_dot) else: degen = 0 if show_never_claim: print '

Never claim (for degeneralized automaton)

' print '
'
    s = spot.ostringstream()
    spot.never_claim_reachable(s, degen, f)
    print cgi.escape(s.str())
    del s
    print '
' sys.stdout.flush() if show_dictionnary: print '

BDD dictionary

' print '
'
    sys.stdout.flush()
    automaton.get_dict().dump(spot.get_cout())
    print '
' if (type(automaton) == spot.tgba_bdd_concrete and (show_relation_set or show_relation_png)): print '

Transition relation

' if show_relation_set: escaped_print_set(automaton.get_dict(), automaton.get_core_data().relation) if show_relation_png: render_bdd(imgprefix + '-b', automaton.get_dict(), automaton.get_core_data().relation) if (type(automaton) == spot.tgba_bdd_concrete and (show_acceptance_set or show_acceptance_png)): print '

Acceptance relation

' if show_acceptance_set: escaped_print_set(automaton.get_dict(), automaton.get_core_data().acceptance_conditions) if show_acceptance_png: render_bdd(imgprefix + '-c', automaton.get_dict(), automaton.get_core_data().acceptance_conditions) if show_lbtt: print '

LBTT conversion

' if degen: print '

Conversion of the generalized automaton

' print '
'
    sys.stdout.flush()
    spot.lbtt_reachable(spot.get_cout(), automaton)
    print '
' if degen: print '

Conversion of the degeneralized automaton

' print '
'
	sys.stdout.flush()
	spot.lbtt_reachable(spot.get_cout(), degen)
	print '
' if draw_acc_run or print_acc_run: print '

Accepting run

' sys.stdout.flush() err = "" opt = emptiness_check + "(" + emptiness_check_options + ")" eci, err = spot.emptiness_check_instantiator.construct(opt) ec_msg = "Running " + opt if not eci: print ('Cannot parse "' + opt + '" near "' + err + '".') else: ec_a = 0 n_acc = automaton.number_of_acceptance_conditions() n_max = eci.max_acceptance_conditions() if (n_acc <= n_max): ec_a = automaton else: if degen: ec_a = degen ec_msg += ' on degeneralized automaton' else: print ('Cannot run ' + emptiness_check + ' on automata with more than ' + n_max + ' acceptance ' + 'conditions. Please select "draw degeneralized ' + 'Büchi automaton" if you want to try this' + ' algorithm on the degeneralized version of the' + ' automaton.') if ec_a: ec = eci.instantiate(ec_a) if ec: print '%s: ' % ec_msg sys.stdout.flush() ec_res = ec.check() if not ec_res: print "no accepting run found.
" else: ec_run = ec_res.accepting_run() print "accepting run found.
" if ec_run: if print_acc_run: print '
'; sys.stdout.flush()
                    s = spot.ostringstream()
                    spot.print_tgba_run(s, ec_a, ec_run)
                    print cgi.escape(s.str())
                    del s
                    print '
'; sys.stdout.flush() if draw_acc_run: deco = spot.tgba_run_dotty_decorator(ec_run) dont_run_dot = print_stats(ec_a) render_automaton(imgprefix + '-e', ec_a, dont_run_dot, deco) del deco del ec_run del ec_res del ec del ec_a sys.stdout.flush() f.destroy() # Make sure degen is cleared before automaton. del degen del automaton del dict print_footer()