From a8f5e7fd8b0fca699a5666ca528cf5f67ab3eb0c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 5 Jun 2015 23:45:09 +0200 Subject: [PATCH] org: Update tut01 * doc/org/tut01.org: Update. * doc/org/g++wrap.in: Include BuDDy's header. --- doc/org/g++wrap.in | 6 +- doc/org/tut01.org | 275 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 231 insertions(+), 50 deletions(-) diff --git a/doc/org/g++wrap.in b/doc/org/g++wrap.in index 4ed248848..0add0a5ec 100755 --- a/doc/org/g++wrap.in +++ b/doc/org/g++wrap.in @@ -1,6 +1,6 @@ #!/bin/sh # This is a wrapper around the compiler, to ensure that the code # example run from the org-mode file are all linked with Spot. -exec @top_builddir@/libtool link \ - @CXX@ -std=c++11 -Wall -I@abs_top_srcdir@/src -I@abs_top_builddir@/src \ - "$@" @abs_top_builddir@/src/libspot.la +exec @top_builddir@/libtool link @CXX@ -std=c++11 -Wall \ + -I@abs_top_srcdir@/src -I@abs_top_srcdir@/buddy/src \ + -I@abs_top_builddir@/src "$@" @abs_top_builddir@/src/libspot.la diff --git a/doc/org/tut01.org b/doc/org/tut01.org index 541d36c87..53687e6f6 100644 --- a/doc/org/tut01.org +++ b/doc/org/tut01.org @@ -14,21 +14,21 @@ using different options such as (=--spin=, =--lbt=, =--latex=, etc.). Full parentheses can also be requested using =-p=. #+BEGIN_SRC sh :results verbatim :exports both -formula='[]<>p0 || <>[]p1' -ltlfilt -f "$formula" --lbt -ltlfilt -f "$formula" --spin -p -ltlfilt --lbt-input -f '& & G p0 p1 p2' --latex +ltlfilt -f '[]<>p0 || <>[]p1' --latex +formula='& & G p0 p1 p2' +ltlfilt --lbt-input -f "$formula" --lbt +ltlfilt --lbt-input -f "$formula" --spin -p #+END_SRC #+RESULTS: -: | G F p0 F G p1 -: ([](<>(p0))) || (<>([](p1))) -: p_{1} \land p_{2} \land \G p_{0} +: \G \F p_{0} \lor \F \G p_{1} +: & & p1 p2 G p0 +: (p1) && (p2) && ([](p0)) The reason the LBT parser has to be explicitly enabled is because of some corner cases that have different meanings in the two syntaxes. -(For instance =t= and =f= are the true constant in LBT's syntax, but they -are considered as atomic propositions in all the other syntaxes.) +(For instance =t= and =f= are the true constant in LBT's syntax, but +they are considered as atomic propositions in all the other syntaxes.) * Python bindings @@ -37,20 +37,21 @@ Here are the same operation in Python #+BEGIN_SRC python :results output :exports both import spot f = spot.formula('[]<>p0 || <>[]p1') +print(f.to_str('latex')) +f = spot.formula('& & G p0 p1 p2') print(f.to_str('lbt')) print(f.to_str('spin', parenth=True)) -print(spot.formula('& & G p0 p1 p2').to_str('latex')) #+END_SRC #+RESULTS: -: | G F p0 F G p1 -: ([](<>(p0))) || (<>([](p1))) -: p_{1} \land p_{2} \land \G p_{0} +: \G \F p_{0} \lor \F \G p_{1} +: & & p1 p2 G p0 +: (p1) && (p2) && ([](p0)) The =spot.formula= function wraps the calls to the two formula parsers of Spot. It first tries to parse the formula using infix syntaxes, and if it fails, it tries to parse it with the prefix parser. (So -this this might fail to correctly interpret =t= or =f= if you are +this might fail to correctly interpret =t= or =f= if you are processing a list of LBT formulas.) Using =spot.formula=, parse errors are returned as an exception. @@ -69,26 +70,24 @@ exceptions. int main() { - const spot::ltl::formula* f = spot::ltl::parse_formula("[]<>p0 || <>[]p1"); + print_latex_psl(std::cout, spot::ltl::parse_formula("[]<>p0 || <>[]p1")) << '\n'; + const spot::ltl::formula* f = spot::ltl::parse_formula("& & G p0 p1 p2"); print_lbt_ltl(std::cout, f) << '\n'; print_spin_ltl(std::cout, f, true) << '\n'; - print_latex_psl(std::cout, spot::ltl::parse_formula("& & G p0 p1 p2")); f->destroy(); + return 0; } #+END_SRC #+RESULTS: -: | G F p0 F G p1 -: ([](<>(p0))) || (<>([](p1))) -: p_{1} \land p_{2} \land \G p_{0} +: \G \F p_{0} \lor \F \G p_{1} +: & & p1 p2 G p0 +: (p1) && (p2) && ([](p0)) Notice that the different output routines specify in their name the -syntax the output, and the type of formula they expect. Here we are -only using LTL formulas for demonstration, so those three functions -are OK with that (LTL is a subset of PSL as far as Spot is concerned). -However if we input PSL formula, it's clear that the above code will -be a problem. If you want to add additional checks, you can easily -tests whether =f->is_ltl_formula()= returns true. +syntax the output, and the type of formula they can output. Here we +are only using LTL formulas for demonstration, so those three +functions are OK with that. Did you notice the calls to =f->destroy()= at the end? The LTL formula objects are implemented as DAG with sharing of subformulas. @@ -106,6 +105,8 @@ parser. Additionally, this give you control over how to print errors. ** Calling the infix parser explicitly +Here is how to call the infix parser explicitly,: + #+BEGIN_SRC C++ :results verbatim :exports both #include #include @@ -123,15 +124,14 @@ parser. Additionally, this give you control over how to print errors. f->destroy(); return 1; } - print_lbt_ltl(std::cout, f) << '\n'; - print_spin_ltl(std::cout, f, true) << '\n'; + print_latex_psl(std::cout, f) << '\n'; f->destroy(); + return 0; } #+END_SRC #+RESULTS: -: | G F p0 F G p1 -: ([](<>(p0))) || (<>([](p1))) +: \G \F p_{0} \lor \F \G p_{1} So =parse_infix_psl()= processes =input=, and stores any diagnostic in =pel=, which is a list of pairs associating each error to a location. @@ -170,25 +170,22 @@ with the "fixed" formula if you wish. Here is an example: (void) spot::ltl::format_parse_errors(std::cout, input, pel); if (f == nullptr) return 1; - print_lbt_ltl(std::cout, f) << '\n'; - print_spin_ltl(std::cout, f, true) << '\n'; + print_latex_psl(std::cout, f) << '\n'; f->destroy(); + return 0; } #+END_SRC #+RESULTS: -#+begin_example ->>> (a U b)) - ^ -syntax error, unexpected closing parenthesis - ->>> (a U b)) - ^ -ignoring trailing garbage - -U "a" "b" -(a) U (b) -#+end_example +: >>> (a U b)) +: ^ +: syntax error, unexpected closing parenthesis +: +: >>> (a U b)) +: ^ +: ignoring trailing garbage +: +: a \U b The formula =f= is only returned as null when the parser really cannot @@ -196,8 +193,8 @@ recover anything. ** Calling the prefix parser explicitly -The only difference here is the call to =parse_prefix_ltl()= instead of -=parse_infix_psl()=. +The only difference here is the call to =parse_prefix_ltl()= instead +of =parse_infix_psl()=. #+BEGIN_SRC C++ :results verbatim :exports both #include @@ -216,10 +213,194 @@ The only difference here is the call to =parse_prefix_ltl()= instead of f->destroy(); return 1; } - print_latex_psl(std::cout, f) << '\n'; + print_lbt_ltl(std::cout, f) << '\n'; + print_spin_ltl(std::cout, f, true) << '\n'; f->destroy(); + return 0; } #+END_SRC #+RESULTS: -: p_{1} \land p_{2} \land \G p_{0} +: & & p1 p2 G p0 +: (p1) && (p2) && ([](p0)) + + +* Additional Comments + +** PSL vs LTL + +LTL is a subset of PSL as far as Spot is concerned, so you can parse +an LTL formula with =parse_infix_psl()=, and later print it with for +instance =print_spin_ltl()= (which, as its name implies, can only +print LTL formulas). There is no =parse_infix_ltl()= function because +you can simply use =parse_infix_psl()= to parse LTL formulas. + +There is a potential problem if you design a tool that only works with +LTL formulas, but call =parse_infix_psl()= to parse user input. In +that case, the user might well input a PSL formula and cause problem +down the line. + +For instance, let's see what happens if a PSL formulas is passed to +=print_spin_ltl=: + +#+BEGIN_SRC C++ :results verbatim :exports both + #include + #include + #include "ltlparse/public.hh" + #include "ltlvisit/print.hh" + + int main() + { + std::string input = "{a*;b}<>->(a U (b & GF c))"; + spot::ltl::parse_error_list pel; + const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + { + if (f) + f->destroy(); + return 1; + } + print_spin_ltl(std::cout, f) << '\n'; + f->destroy(); + return 0; + } +#+END_SRC + +#+RESULTS: +: {a[*];b}<>-> (a U (b && []<>c)) + +The output is a 'best effort' output. The LTL subformulas have been +rewritten, but the PSL-specific part (the SERE and =<>->= operator) +are output in the only syntax Spot knows, definitively not +Spin-compatible. + +If that is unwanted, here are two possible solutions. + +The first is to simply diagnose non-LTL formulas. + +#+BEGIN_SRC C++ :results verbatim :exports code + #include + #include + #include "ltlparse/public.hh" + #include "ltlvisit/print.hh" + + int main() + { + std::string input = "{a*;b}<>->(a U (b & GF c))"; + spot::ltl::parse_error_list pel; + const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + { + if (f) + f->destroy(); + return 1; + } + if (!f->is_ltl_formula()) + { + f->destroy(); + std::cerr << "Only LTL formulas are supported.\n"; + return 1; + } + print_spin_ltl(std::cout, f) << '\n'; + f->destroy(); + return 0; + } +#+END_SRC + +A second (but slightly weird) idea would be to try to simplify the PSL +formula, and hope that the PSL simplify is able to come up with an +equivalent LTL formula. This does not always work, so you need to be +prepared to reject the formula any way. In our example, we are lucky +(maybe because it was carefully chosen...): + +#+BEGIN_SRC C++ :results verbatim :exports code + #include + #include + #include "ltlparse/public.hh" + #include "ltlvisit/print.hh" + #include "ltlvisit/simplify.hh" + + int main() + { + std::string input = "{a*;b}<>->(a U (b & GF c))"; + spot::ltl::parse_error_list pel; + const spot::ltl::formula* f = spot::ltl::parse_infix_psl(input, pel); + if (spot::ltl::format_parse_errors(std::cerr, input, pel)) + { + if (f) + f->destroy(); + return 1; + } + if (!f->is_ltl_formula()) + { + spot::ltl::ltl_simplifier simp; + const formula* g = simp.simplify(f); + f->destroy(); + f = g; + } + if (!f->is_ltl_formula()) + { + f->destroy(); + std::cerr << "Only LTL formulas are supported.\n"; + return 1; + } + print_spin_ltl(std::cout, f) << '\n'; + f->destroy(); + return 0; + } +#+END_SRC + +#+RESULTS: +: a U (b && (a U (b && []<>c))) + +** Lenient parsing + +In version 6, Spin extended its command-line LTL parser to accept +arbitrary atomic propositions to be specified. For instance =(a > 4) +U (b < 5)= would be correct input, with =a > 4= and =b < 5= considered +as two atomic propositions. Of course the atomic proposition could be +arbitrarily complex, and there is no way we can teach Spot about the +syntax for atomic propositions supported by any tool. The usual +workaround in Spot is to double-quote any arbitrary atomic +proposition: + +#+BEGIN_SRC sh :results verbatim :exports both +echo compare +ltlfilt -f '"a > 4" U "b < 5"' +echo and +ltlfilt -f '"a > 4" U "b < 5"' --spin +#+END_SRC + +#+RESULTS: +: compare +: "a > 4" U "b < 5" +: and +: (a > 4) U (b < 5) + +When the Spin output is requested, these atomic propositions are +atomically output in a way that Spin can parse. + +This Spin syntax is not accepted by default by the infix parser, but +it has an option for that. This is called /lenient parsing/: when the +parser finds a parenthetical block it does not understand, it simply +assume that this block represents an atomic proposition. + +#+BEGIN_SRC sh :results verbatim :exports both +ltlfilt --lenient -f '(a > 4) U (b < 5)' +#+END_SRC + +#+RESULTS: +: "a > 4" U "b < 5" + +Lenient parsing is risky, because any parenthesized sub-formula that +is a syntax-error will be treated as an atomic proposition: + +#+BEGIN_SRC sh :results verbatim :exports both +ltlfilt --lenient -f '(a U ) U c' +#+END_SRC + +#+RESULTS: +: "a U" U c + +In C++ you can enable lenient using one of the Boolean arguments of +=parse_infix_psl()=.