Commit a79db4ee authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

psl: add support for the [:*i..j] operator

This operator is to ':' what [*i..j] is to ';'.

Part of issue #51.

* doc/tl/tl.tex: Document syntax, semantic, and trivial
simplifications.
* doc/tl/spotltl.sty: Add macros for new operators.
* src/ltlast/bunop.cc, src/ltlast/bunop.hh: Implement it.
* src/ltlast/multop.cc: Add some trivial simplifications.
* src/ltlparse/ltlparse.yy, src/ltlparse/ltlscan.ll: Parse it.
* src/ltltest/equals.test, src/ltltest/latex.test,
src/tgbatest/ltl2tgba.test: Add more tests.
* src/ltlvisit/randomltl.cc: Output this operator in
random PSL formulas.
* src/ltltest/rand.test: Adjust.
* src/tgbaalgos/ltl2tgba_fm.cc: Add translation rules.
* src/ltlvisit/tostring.cc: Add pretty printing code.
* src/ltlvisit/simplify.cc, src/ltlvisit/snf.cc: Adjust
switches.
* NEWS: Mention the new operator.
parent eebbcac2
......@@ -67,7 +67,14 @@ New in spot 1.99a (not yet released)
either by Divine (as patched by the LTSmin people) or by
Spins (the LTSmin compiler for Promela).
- LTL formulas can include /* comments */.
- LTL/PSL formulas can include /* comments */.
- PSL SEREs support a new operator [:*i..j], the iterated fusion.
[:*i..j] is to the fusion operator ':' what [*i..j] is to the
concatenation operator ';'. For instance (a*;b)[:*3] is the
same as (a*;b):(a*;b):(a*;b). The operator [:+], is syntactic
sugar for [:*1..], and corresponds to the operator ⊕ introduced
by Dax et al. (ATVA'09).
- GraphViz output now uses an horizontal layout by default.
The --dot option of the various command-line tools takes an
......
......@@ -8,11 +8,11 @@
\newcommand{\ffalse}{\bot}
\newcommand{\eword}{\varepsilon} % empty word, denoted by [*0] in PSL
% These three are not declared as operator
% These three are not declared as operators
\newcommand{\F}{\mathsf{F}} % eventually
\newcommand{\G}{\mathsf{G}} % always
\newcommand{\X}{\mathsf{X}} % next
% The \mathbin tell TeX to adjust spacing for binary operators
% The \mathbin tells TeX to adjust spacing for binary operators
\newcommand{\M}{\mathbin{\mathsf{M}}} % strong release
\newcommand{\R}{\mathbin{\mathsf{R}}} % release
\newcommand{\U}{\mathbin{\mathsf{U}}} % until
......@@ -25,9 +25,11 @@
% Star-like PSL operators
\newcommand{\SereStar}[1]{^{\star#1}}
\newcommand{\SereFStar}[1]{^{\mathsf{:}\star#1}}
\newcommand{\SereEqual}[1]{^{=#1}}
\newcommand{\SereGoto}[1]{^{\to#1}}
\newcommand{\SerePlus}{^+}
\newcommand{\SereFPlus}{^{\mathsf{:}+}}
\newcommand{\SereFusion}{\mathbin{\mathsf{:}}}
\newcommand{\SereConcat}{\mathbin{\mathsf{;}}}
\newcommand{\SereOr}{\cup}
......@@ -45,5 +47,3 @@
%\newcommand{\seqXM}{\seqX}
\newcommand{\triggers}{\boxright}
\newcommand{\triggersX}{\boxRight}
......@@ -92,6 +92,7 @@
\newcommand{\EQUAL}[1]{\texttt{[=#1]}}
\newcommand{\GOTO}[1]{\texttt{[->#1]}}
\newcommand{\PLUS}{\texttt{[+]}}
\newcommand{\FPLUS}{\texttt{[:+]}}
\newcommand{\eword}{\texttt{[*0]}}
\newcommand{\Esuffix}{\texttt{<>->}}
......@@ -599,14 +600,22 @@ denote arbitrary SERE.
NLM intersection\footnotemark & $f\AND g$ \\
concatenation & $f\CONCAT g$ \\
fusion & $f\FUSION g$ \\
bounded repetition & $f\STAR{\mvar{i}..\mvar{j}}$
bounded ;-iter. & $f\STAR{\mvar{i}..\mvar{j}}$
& $f\STAR{\mvar{i}:\mvar{j}}$
& $f\STAR{\mvar{i} to \mvar{j}}$
& $f\STAR{\mvar{i},\mvar{j}}$\\
\llap{un}bounded repetition & $f\STAR{\mvar{i}..}$
\llap{un}bounded ;-iter. & $f\STAR{\mvar{i}..}$
& $f\STAR{\mvar{i}:}$
& $f\STAR{\mvar{i} to}$
& $f\STAR{\mvar{i},}$\\
bounded :-iter. & $f\FSTAR{\mvar{i}..\mvar{j}}$
& $f\FSTAR{\mvar{i}:\mvar{j}}$
& $f\FSTAR{\mvar{i} to \mvar{j}}$
& $f\FSTAR{\mvar{i},\mvar{j}}$\\
\llap{un}bounded :-iter. & $f\FSTAR{\mvar{i}..}$
& $f\FSTAR{\mvar{i}:}$
& $f\FSTAR{\mvar{i} to}$
& $f\FSTAR{\mvar{i},}$\\
\end{tabular}
\end{center}
......@@ -657,6 +666,26 @@ $a$ is an atomic proposition.
\text{or} & \mvar{i}>0 \land (\exists k\in\N,\,
(\sigma^{0..k-1}\VDash f) \land (\sigma^{k..}
\VDash f\STAR{\mvar{i-1}..}))\\
\end{cases}\\
\sigma\VDash f\FSTAR{\mvar{i}..\mvar{j}}& \iff
\begin{cases}
\text{either} & \mvar{i}=0 \land \mvar{j}=0 \land \sigma\VDash\1 \\
\text{or} & \mvar{i}=0 \land \mvar{j}>0 \land (\exists k\in\N,\,
(\sigma^{0..k}\VDash f) \land (\sigma^{k..}
\VDash f\FSTAR{\mvar{0}..\mvar{j-1}}))\\
\text{or} & \mvar{i}>0 \land \mvar{j}>0 \land (\exists k\in\N,\,
(\sigma^{0..k}\VDash f) \land (\sigma^{k..}
\VDash f\FSTAR{\mvar{i-1}..\mvar{j-1}}))\\
\end{cases}\\
\sigma\VDash f\FSTAR{\mvar{i}..} & \iff
\begin{cases}
\text{either} & \mvar{i}=0 \land \sigma\VDash\1 \\
\text{or} & \mvar{i}=0 \land (\exists k\in\N,\,
(\sigma^{0..k}\VDash f) \land (\sigma^{k..}
\VDash f\FSTAR{\mvar{0}..}))\\
\text{or} & \mvar{i}>0 \land (\exists k\in\N,\,
(\sigma^{0..k}\VDash f) \land (\sigma^{k..}
\VDash f\FSTAR{\mvar{i-1}..}))\\
\end{cases}
\end{align*}}
......@@ -668,6 +697,29 @@ operands are Boolean formulas.
regardless of the value of $f$ and $g$. For instance
$a\STAR{}\FUSION b\STAR{}$ is actually equivalent to
$a\STAR{}\CONCAT\sere{a\ANDALT b}\CONCAT b\STAR{}$.
\item The $\FSTAR{\mvar{i}..}$ and $\FSTAR{\mvar{i}..\mvar{j}}$ operators are
iterations of the $\FUSION$ operator just like
The $\STAR{\mvar{i}..}$ and $\STAR{\mvar{i}..\mvar{j}}$ are
iterations of the $\CONCAT$ operator. More graphically:
\begin{align*}
f\STAR{\mvar{i}..\mvar{j}} &=
\underbrace{f\CONCAT f\CONCAT \ldots \CONCAT f}_{\text{between $\mvar{i}$ and $\mvar{j}$ copies of $f$}} &
f\FSTAR{\mvar{i}..\mvar{j}} &=
\underbrace{f\FUSION f\FUSION \ldots \FUSION f}_{\text{between $\mvar{i}$ and $\mvar{j}$ copies of $f$}}\\
\intertext{with the convention that}
f\STAR{0..0} &= \eword &
f\FSTAR{0..0} &= \1
\end{align*}
\item The $\FSTAR{\mvar{i}..}$ and $\FSTAR{\mvar{i}..\mvar{j}}$
operators are not defined in PSL. While the bounded iteration can
be seen as syntactic sugar on $\FUSION$, the unbounded version
really is a new operator.
$\FSTAR{1..}$, for which we define the $\FPLUS$ syntactic sugar
below, actually corresponds to the $^\oplus$ operator introduced
by~\citet{dax.09.atva}. With this simple addition, it is possible
to define a subset of PSL that expresses exactly the
stutter-invariant $\omega$-regular languages.
\end{itemize}
\subsection{Syntactic Sugar}
......@@ -687,24 +739,28 @@ it for output. $b$ must be a Boolean formula.
\begin{align*}
f\STARALT &\equiv f\STAR{0..}\\
f\STAR{} &\equiv f\STAR{0..} &
f\PLUS{} &\equiv f\STAR{1..} &
f\FSTAR{} &\equiv f\FSTAR{0..} &
f\EQUAL{} &\equiv f\EQUAL{0..} &
f\GOTO{} &\equiv f\GOTO{1..1} \\
f\STAR{..} &\equiv f\STAR{0..} &
&&
f\FSTAR{..} &\equiv f\FSTAR{0..} &
f\EQUAL{..} &\equiv f\EQUAL{0..} &
f\GOTO{..} &\equiv f\GOTO{1..} \\
f\STAR{..\mvar{j}} &\equiv f\STAR{0..\mvar{j}} &
&&
f\FSTAR{..\mvar{j}} &\equiv f\FSTAR{0..\mvar{j}} &
f\EQUAL{..\mvar{j}} &\equiv f\EQUAL{0..\mvar{j}} &
f\GOTO{..\mvar{j}} &\equiv f\GOTO{1..\mvar{j}} \\
f\STAR{\mvar{k}} &\equiv f\STAR{\mvar{k}..\mvar{k}} &
&&
f\FSTAR{\mvar{k}} &\equiv f\FSTAR{\mvar{k}..\mvar{k}} &
f\EQUAL{\mvar{k}} &\equiv f\EQUAL{\mvar{k}..\mvar{k}} &
f\GOTO{\mvar{k}} &\equiv f\GOTO{\mvar{k}..\mvar{k}} \\
\STAR{} &\equiv \1\STAR{0..} &
\PLUS{} &\equiv \1\STAR{1..} \\
\STAR{\mvar{k}} &\equiv \1\STAR{\mvar{k}..\mvar{k}} &
f\PLUS{} &\equiv f\STAR{1..} &
f\FPLUS{} &\equiv f\FSTAR{1..}
\end{align*}
\begin{align*}
\STAR{\mvar{k}} &\equiv \1\STAR{\mvar{k}..\mvar{k}} &
\STAR{} &\equiv \1\STAR{0..} &
\PLUS{} &\equiv \1\STAR{1..}
\end{align*}
\subsection{Trivial Identities (Occur Automatically)}
......@@ -720,6 +776,14 @@ $b_1$, $b_2$ are assumed to be Boolean formulas.
f\STAR{\mvar{i}..\mvar{j}}\STAR{\mvar{k}..\mvar{l}} &\equiv f\STAR{\mvar{ik}..\mvar{jl}}\text{~if~}i(k+1)\le jk+1 \\
f\STAR{0}&\equiv \eword &
f\STAR{1}&\equiv f\\
b\FSTAR{0..\mvar{j}} &\equiv \1 &
b\FSTAR{\mvar{i}..\mvar{j}} &\equiv b \text{~if~}i>0 \\
\eword\FSTAR{0..\mvar{j}} &\equiv \1&
\eword\FSTAR{\mvar{i}..\mvar{j}} &\equiv \0\text{~if~}i>0 \\
&&
f\FSTAR{\mvar{i}..\mvar{j}}\FSTAR{\mvar{k}..\mvar{l}} &\equiv f\FSTAR{\mvar{ik}..\mvar{jl}}\text{~if~}i(k+1)\le jk+1 \\
f\FSTAR{0}&\equiv \1 &
f\FSTAR{1}&\equiv f\text{~if~}\varepsilon\nVDash f\\
\end{align*}
\noindent
......@@ -758,20 +822,19 @@ The following rules are all valid with the two arguments swapped.
f\AND f &\equiv f&
f\ANDALT f &\equiv f &
f\OR f &\equiv f&
&&
f\FUSION f&\equiv f\FSTAR{2}&
f\CONCAT f&\equiv f\STAR{2}\\
b_1 \AND b_2 &\equiv b_1\ANDALT b_2 &
&&
&&
b_1:b_2 &\equiv b_1\ANDALT b_2 &
f\STAR{\mvar{i}..\mvar{j}}\CONCAT f&\equiv f\STAR{\mvar{i+1}..\mvar{j+1}}\\
&&
&&
&&
&&
\mathllap{f\STAR{\mvar{i}..\mvar{j}}}\CONCAT f\STAR{\mvar{k}..\mvar{l}}&\equiv f\STAR{\mvar{i+k}..\mvar{j+l}}\\
b_1:b_2 &\equiv b_1\ANDALT b_2
\end{align*}
\begin{align*}
f\STAR{\mvar{i}..\mvar{j}}\CONCAT f&\equiv f\STAR{\mvar{i+1}..\mvar{j+1}} &
f\STAR{\mvar{i}..\mvar{j}}\CONCAT f\STAR{\mvar{k}..\mvar{l}}&\equiv f\STAR{\mvar{i+k}..\mvar{j+l}}\\
f\FSTAR{\mvar{i}..\mvar{j}}\FUSION f&\equiv f\FSTAR{\mvar{i+1}..\mvar{j+1}} &
f\FSTAR{\mvar{i}..\mvar{j}}\FUSION f\FSTAR{\mvar{k}..\mvar{l}}&\equiv f\FSTAR{\mvar{i+k}..\mvar{j+l}}
\end{align*}
\section{SERE-LTL Binding Operators}
The following operators combine a SERE $r$ with a PSL
......
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de
// Recherche et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -61,6 +61,11 @@ namespace spot
if (min_ == 0)
is.accepting_eword = true;
break;
case FStar:
is.accepting_eword = false;
if (max_ == unbounded)
is.finite = false;
break;
}
}
......@@ -106,6 +111,8 @@ namespace spot
{
case Star:
return "Star";
case FStar:
return "FStar";
}
SPOT_UNREACHABLE();
}
......@@ -120,20 +127,22 @@ namespace spot
case Star:
// Syntactic sugaring
if (min_ == 1 && max_ == unbounded)
{
out << "[+]";
return out.str();
}
return "[+]";
out << "[*";
break;
case FStar:
// Syntactic sugaring
if (min_ == 1 && max_ == unbounded)
return "[:+]";
out << "[:*";
break;
}
if (min_ != 0 || max_ != unbounded)
{
// Always print the min_, even when it is equal to
// default_min, this way we avoid ambiguities (like
// when reading a[*..3];b[->..2] which actually
// means a[*0..3];b[->1..2].
// Always print the min_, even when it is equal to 0, this
// way we avoid ambiguities (like when reading
// a[*..3];b[->..2] which actually means a[*0..3];b[->1..2].
out << min_;
if (min_ != max_)
{
......@@ -154,86 +163,110 @@ namespace spot
{
assert(min <= max);
// Some trivial simplifications.
const formula* neutral = nullptr;
switch (op)
{
case Star:
neutral = constant::empty_word_instance();
break;
case FStar:
neutral = constant::true_instance();
break;
}
// common trivial simplifications
// - [*0][*min..max] = [*0]
// - [*0][:*0..max] = 1
// - [*0][:*min..max] = 0 if min > 0
if (child == constant::empty_word_instance())
switch (op)
{
// - [*0][*min..max] = [*0]
if (child == constant::empty_word_instance())
return child;
// - 0[*0..max] = [*0]
// - 0[*min..max] = 0 if min > 0
if (child == constant::false_instance())
{
if (min == 0)
return constant::empty_word_instance();
else
return child;
}
// - Exp[*0] = [*0]
if (max == 0)
{
child->destroy();
return constant::empty_word_instance();
}
// - Exp[*1] = Exp
if (min == 1 && max == 1)
return child;
// - Exp[*i..j][*min..max] = Exp[*i(min)..j(max)]
// if i*(min+1)<=j(min)+1.
if (const bunop* s = is_bunop(child))
{
unsigned i = s->min();
unsigned j = s->max();
// Exp has to be true between i*min and j*min
// then between i*(min+1) and j*(min+1)
// ...
// finally between i*max and j*max
//
// We can merge these intervals into [i*min..j*max] iff the
// first are adjacent or overlap, i.e. iff
// i*(min+1) <= j*min+1.
// (Because i<=j, this entails that the other intervals also
// overlap).
const formula* exp = s->child();
if (j == unbounded)
{
min *= i;
max = unbounded;
// Exp[*min..max]
exp->clone();
child->destroy();
child = exp;
}
else
{
if (i * (min + 1) <= (j * min) + 1)
{
min *= i;
if (max != unbounded)
{
if (j == unbounded)
max = unbounded;
else
max *= j;
}
exp->clone();
child->destroy();
child = exp;
}
}
}
break;
case Star:
return neutral;
case FStar:
if (min == 0)
return neutral;
else
return constant::false_instance();
}
// - 0[*0..max] = [*0]
// - 0[*min..max] = 0 if min > 0
// - b[:*0..max] = 1
// - b[:*min..max] = 0 if min > 0
if (child == constant::false_instance()
|| (op == FStar && child->is_boolean()))
{
if (min == 0)
{
child->destroy();
return neutral;
}
return child;
}
// - Exp[*0] = [*0]
// - Exp[:*0] = 1
if (max == 0)
{
child->destroy();
return neutral;
}
// - Exp[*1] = Exp
// - Exp[:*1] = Exp if Exp does not accept [*0]
if (min == 1 && max == 1)
if (op == Star || !child->accepts_eword())
return child;
// - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1.
// - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1.
if (const bunop* s = is_bunop(child, op))
{
unsigned i = s->min();
unsigned j = s->max();
// Exp has to be true between i*min and j*min
// then between i*(min+1) and j*(min+1)
// ...
// finally between i*max and j*max
//
// We can merge these intervals into [i*min..j*max] iff the
// first are adjacent or overlap, i.e. iff
// i*(min+1) <= j*min+1.
// (Because i<=j, this entails that the other intervals also
// overlap).
const formula* exp = s->child();
if (j == unbounded)
{
min *= i;
max = unbounded;
// Exp[*min..max]
exp->clone();
child->destroy();
child = exp;
}
else
{
if (i * (min + 1) <= (j * min) + 1)
{
min *= i;
if (max != unbounded)
{
if (j == unbounded)
max = unbounded;
else
max *= j;
}
exp->clone();
child->destroy();
child = exp;
}
}
}
const formula* res;
......
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2011, 2012, 2013, 2014 Laboratoire de Recherche
// Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
......@@ -38,7 +38,7 @@ namespace spot
class SPOT_API bunop final : public formula
{
public:
enum type { Star };
enum type { Star, FStar };
static const unsigned unbounded = -1U;
......@@ -50,9 +50,16 @@ namespace spot
/// - 0[*0..max] = [*0]
/// - 0[*min..max] = 0 if min > 0
/// - [*0][*min..max] = [*0]
/// - Exp[*0] = [*0]
/// - Exp[*i..j][*k..l] = Exp[*ik..jl] if i*(k+1)<=jk+1.
/// - Exp[*0] = [*0]
/// - Exp[*1] = Exp
/// - b[:*0..max] = 1
/// - b[:*min..max] = b if min > 0
/// - [*0][:*0..max] = 1
/// - [*0][:*min..max] = 0 if min > 0
/// - Exp[:*i..j][:*k..l] = Exp[:*ik..jl] if i*(k+1)<=jk+1.
/// - Exp[:*0] = 1
/// - Exp[:*1] = Exp if Exp does not accept [*0]
///
/// These rewriting rules imply that it is not possible to build
/// an LTL formula object that is SYNTACTICALLY equal to one of
......
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire de
// Recherche et Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
......@@ -495,13 +495,19 @@ namespace spot
v->swap(tmp);
}
}
else if (op == Concat)
else if (op == Concat || op == Fusion)
{
// Perform an extra loop to merge starable items.
// f;f -> f[*2]
// f;f[*i..j] -> f[*i+1..j+1]
// f[*i..j];f -> f[*i+1..j+1]
// f[*i..j];f[*k..l] -> f[*i+k..j+l]
// same for FStar:
// f:f -> f[:*2]
// f:f[*i..j] -> f[:*i+1..j+1]
// f[:*i..j];f -> f[:*i+1..j+1]
// f[:*i..j];f[:*k..l] -> f[:*i+k..j+l]
bunop::type bop = op == Concat ? bunop::Star : bunop::FStar;
i = v->begin();
while (i != v->end())
{
......@@ -510,7 +516,7 @@ namespace spot
unsigned min;
unsigned max;
bool changed = false;
if (const bunop* is = is_Star(*i))
if (const bunop* is = is_bunop(*i, bop))
{
f = is->child();
min = is->min();
......@@ -528,7 +534,7 @@ namespace spot
const formula* f2;
unsigned min2;
unsigned max2;
if (const bunop* is = is_Star(*i))
if (const bunop* is = is_bunop(*i, bop))
{
f2 = is->child();
if (f2 != f)
......@@ -558,7 +564,7 @@ namespace spot
if (changed)
{
const formula* newfs =
bunop::instance(bunop::Star, f->clone(), min, max);
bunop::instance(bop, f->clone(), min, max);
(*fpos)->destroy();
*fpos = newfs;
}
......
/* -*- coding: utf-8 -*-
** Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Laboratoire de
** Recherche et Développement de l'Epita (LRDE).
** Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
** de Recherche et Développement de l'Epita (LRDE).
** Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
** Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
** Université Pierre et Marie Curie.
......@@ -176,8 +176,11 @@ using namespace spot::ltl;
%token OP_F "sometimes operator" OP_G "always operator"
%token OP_X "next operator" OP_NOT "not operator"
%token OP_STAR "star operator" OP_BSTAR "bracket star operator"
%token OP_BFSTAR "bracket fusion-star operator"
%token OP_PLUS "plus operator"
%token OP_FPLUS "fusion-plus operator"
%token OP_STAR_OPEN "opening bracket for star operator"
%token OP_FSTAR_OPEN "opening bracket for fusion-star operator"
%token OP_EQUAL_OPEN "opening bracket for equal operator"
%token OP_GOTO_OPEN "opening bracket for goto operator"
%token OP_SQBKT_CLOSE "closing bracket"
......@@ -220,7 +223,9 @@ using namespace spot::ltl;
%nonassoc OP_X
/* High priority regex operator. */
%nonassoc OP_BSTAR OP_STAR_OPEN OP_PLUS OP_EQUAL_OPEN OP_GOTO_OPEN
%nonassoc OP_BSTAR OP_STAR_OPEN OP_PLUS
OP_BFSTAR OP_FSTAR_OPEN OP_FPLUS
OP_EQUAL_OPEN OP_GOTO_OPEN
/* Not has the most important priority (after Wring's `=0' and `=1',
but as those can only attach to atomic proposition, they do not
......@@ -229,7 +234,7 @@ using namespace spot::ltl;
%type <ltl> subformula booleanatom sere lbtformula boolformula
%type <ltl> bracedsere parenthesedsubformula
%type <minmax> starargs equalargs sqbracketargs gotoargs
%type <minmax> starargs fstarargs equalargs sqbracketargs gotoargs
%destructor { delete $$; } <str>
%destructor { $$->destroy(); } <ltl>
......@@ -370,6 +375,21 @@ starargs: kleen_star
{ error_list.emplace_back(@$, "missing closing bracket for star");
$$.min = $$.max = 0U; }
fstarargs: OP_BFSTAR
{ $$.min = 0U; $$.max = bunop::unbounded; }
| OP_FPLUS
{ $$.min = 1U; $$.max = bunop::unbounded; }
| OP_FSTAR_OPEN sqbracketargs
{ $$ = $2; }
| OP_FSTAR_OPEN error OP_SQBKT_CLOSE
{ error_list.emplace_back
(@$, "treating this fusion-star block as [:*]");
$$.min = 0U; $$.max = bunop::unbounded; }
| OP_FSTAR_OPEN error_opt END_OF_INPUT
{ error_list.emplace_back
(@$, "missing closing bracket for fusion-star");
$$.min = $$.max = 0U; }
equalargs: OP_EQUAL_OPEN sqbracketargs
{ $$ = $2; }
| OP_EQUAL_OPEN error OP_SQBKT_CLOSE
......@@ -507,6 +527,16 @@ sere: booleanatom
{ $$ = multop::instance(multop::Fusion, $1, $3); }
| sere OP_FUSION error
{ missing_right_binop($$, $1, @2, "fusion operator"); }
| starargs
{
if ($1.max < $1.min)
{
error_list.emplace_back(@1, "reversed range");
std::swap($1.max, $1.min);
}
$$ = bunop::instance(bunop::Star, constant::true_instance(),
$1.min, $1.max);
}