Introduce [->min..max] operator.

* src/ltlast/bunop.hh: Declare bunop::Goto
* src/ltlast/bunop.cc: Handle it.
* src/ltlparse/ltlparse.yy,
src/ltlparse/ltlscan.ll: Add rules for [->min..max].
* src/tgbaalgos/ltl2tgba_fm.cc: Handle bunop::Goto in
the translation.
* src/ltltest/equals.test: Test trivial identities.
* src/tgbatest/ltl2tgba.test: Test two more formulae using [->].
This commit is contained in:
Alexandre Duret-Lutz 2010-10-15 11:41:18 +02:00
parent 2c31e541b5
commit da74b4f180
7 changed files with 138 additions and 11 deletions

View file

@ -113,6 +113,8 @@ namespace spot
return "Equal"; return "Equal";
case Star: case Star:
return "Star"; return "Star";
case Goto:
return "Goto";
} }
// Unreachable code. // Unreachable code.
assert(0); assert(0);
@ -124,6 +126,9 @@ namespace spot
{ {
std::ostringstream out; std::ostringstream out;
unsigned default_min = 0;
unsigned default_max = unbounded;
switch (op_) switch (op_)
{ {
case Star: case Star:
@ -138,10 +143,36 @@ namespace spot
case Equal: case Equal:
out << "[="; out << "[=";
break; break;
case Goto:
out << "[->";
default_min = 1;
default_max = 1;
break;
} }
if (min_ != 0 || max_ != unbounded) // Beware that the default parameters of the Goto operator are
// not the same as Star or Equal:
//
// [->] = [->1..1]
// [->..] = [->1..unbounded]
// [*] = [*0..unbounded]
// [*..] = [*0..unbounded]
// [=] = [=0..unbounded]
// [=..] = [=0..unbounded]
//
// Strictly speaking [=] is not specified by PSL, and anyway we
// automatically rewrite Exp[=0..unbounded] as
// Exp[*0..unbounded], so we should never have to print [=]
// here.
//
// Also
// [*..] = [*0..unbounded]
if (min_ != default_min || max_ != default_max)
{ {
// Always print the min_, even when it is equal to
// default_min, this way we avoid ambiguities (like
// when reading [*..3] near [*->..2])
out << min_; out << min_;
if (min_ != max_) if (min_ != max_)
{ {
@ -208,6 +239,38 @@ namespace spot
unop::instance(unop::Not, child)); unop::instance(unop::Not, child));
break; break;
} }
case Goto:
{
// - 0[->min..max] = 0 if min>0
// - 0[->0..max] = [*0]
if (child == constant::false_instance())
{
if (min == 0)
return constant::empty_word_instance();
else
return child;
}
// - 1[->0] = [*0]
// - 1[->min..max] = 1[*min..max]
if (child == constant::true_instance())
{
if (max == 0)
return constant::empty_word_instance();
else
{
op = Star;
break;
}
}
// - Exp[->0] = [*0]
if (max == 0)
{
child->destroy();
return constant::empty_word_instance();
}
break;
}
case Star: case Star:
{ {
// - [*0][*min..max] = [*0] // - [*0][*min..max] = [*0]

View file

@ -37,7 +37,7 @@ namespace spot
class bunop : public ref_formula class bunop : public ref_formula
{ {
public: public:
enum type { Star, Equal }; enum type { Star, Equal, Goto };
static const unsigned unbounded = -1U; static const unsigned unbounded = -1U;
@ -57,6 +57,11 @@ namespace spot
/// - 1[=min..max] = 1[*min..max] if max > 0 /// - 1[=min..max] = 1[*min..max] if max > 0
/// - Exp[=0..] = [*] /// - Exp[=0..] = [*]
/// - Exp[=0] = (!Exp)[*] /// - Exp[=0] = (!Exp)[*]
/// - 0[->min..max] = 0 if min>0
/// - 0[->0..max] = [*0]
/// - 1[->0] = [*0]
/// - 1[->min..max] = 1[*min..max]
/// - Exp[->0] = [*0]
/// ///
/// These rewriting rules imply that it is not possible to build /// These rewriting rules imply that it is not possible to build
/// an LTL formula object that is SYNTACTICALLY equal to one of /// an LTL formula object that is SYNTACTICALLY equal to one of

View file

@ -94,6 +94,7 @@ using namespace spot::ltl;
%token OP_PLUS "plus operator" %token OP_PLUS "plus operator"
%token OP_STAR_OPEN "opening bracket for star operator" %token OP_STAR_OPEN "opening bracket for star operator"
%token OP_EQUAL_OPEN "opening bracket for equal 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" %token OP_SQBKT_CLOSE "closing bracket"
%token <num> OP_SQBKT_NUM "number for square bracket operator" %token <num> OP_SQBKT_NUM "number for square bracket operator"
%token OP_SQBKT_SEP "separator for square bracket operator" %token OP_SQBKT_SEP "separator for square bracket operator"
@ -126,7 +127,7 @@ using namespace spot::ltl;
%nonassoc OP_X %nonassoc OP_X
/* High priority regex operator. */ /* High priority regex operator. */
%nonassoc OP_STAR OP_STAR_OPEN OP_PLUS OP_EQUAL_OPEN %nonassoc OP_STAR OP_STAR_OPEN OP_PLUS OP_EQUAL_OPEN OP_GOTO_OPEN
/* Not has the most important priority after Wring's `=0' and `=1'. */ /* Not has the most important priority after Wring's `=0' and `=1'. */
%nonassoc OP_NOT %nonassoc OP_NOT
@ -135,7 +136,7 @@ using namespace spot::ltl;
%type <ltl> subformula booleanatom rationalexp %type <ltl> subformula booleanatom rationalexp
%type <ltl> bracedrationalexp parenthesedsubformula %type <ltl> bracedrationalexp parenthesedsubformula
%type <minmax> starargs equalargs sqbracketargs %type <minmax> starargs equalargs sqbracketargs gotoargs
%destructor { delete $$; } <str> %destructor { delete $$; } <str>
%destructor { $$->destroy(); } <ltl> %destructor { $$->destroy(); } <ltl>
@ -192,6 +193,7 @@ enderror: error END_OF_INPUT
OP_SQBKT_SEP_opt: | OP_SQBKT_SEP OP_SQBKT_SEP_opt: | OP_SQBKT_SEP
error_opt: | error error_opt: | error
/* for [*i..j] and [=i..j] */
sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $1; $$.max = $3; } { $$.min = $1; $$.max = $3; }
| OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_CLOSE | OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_CLOSE
@ -203,6 +205,28 @@ sqbracketargs: OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
| OP_SQBKT_NUM OP_SQBKT_CLOSE | OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $$.max = $1; } { $$.min = $$.max = $1; }
/* [->i..j] has default values that are different than [*] and [=]. */
gotoargs: OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $2; $$.max = $4; }
| OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_SEP OP_SQBKT_CLOSE
{ $$.min = $2; $$.max = bunop::unbounded; }
| OP_GOTO_OPEN OP_SQBKT_SEP OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = 1U; $$.max = $3; }
| OP_GOTO_OPEN OP_SQBKT_SEP OP_SQBKT_CLOSE
{ $$.min = 1U; $$.max = bunop::unbounded; }
| OP_GOTO_OPEN OP_SQBKT_CLOSE
{ $$.min = $$.max = 1U; }
| OP_GOTO_OPEN OP_SQBKT_NUM OP_SQBKT_CLOSE
{ $$.min = $$.max = $2; }
| OP_GOTO_OPEN error OP_SQBKT_CLOSE
{ error_list.push_back(parse_error(@$,
"treating this goto block as [->]"));
$$.min = $$.max = 1U; }
| OP_GOTO_OPEN error_opt END_OF_INPUT
{ error_list.push_back(parse_error(@$,
"missing closing bracket for goto operator"));
$$.min = $$.max = 0U; }
starargs: OP_STAR starargs: OP_STAR
{ $$.min = 0U; $$.max = bunop::unbounded; } { $$.min = 0U; $$.max = bunop::unbounded; }
| OP_PLUS | OP_PLUS
@ -222,11 +246,11 @@ equalargs: OP_EQUAL_OPEN sqbracketargs
{ $$ = $2; } { $$ = $2; }
| OP_EQUAL_OPEN error OP_SQBKT_CLOSE | OP_EQUAL_OPEN error OP_SQBKT_CLOSE
{ error_list.push_back(parse_error(@$, { error_list.push_back(parse_error(@$,
"treating this star block as [*]")); "treating this equal block as [*]"));
$$.min = 0U; $$.max = bunop::unbounded; } $$.min = 0U; $$.max = bunop::unbounded; }
| OP_EQUAL_OPEN error_opt END_OF_INPUT | OP_EQUAL_OPEN error_opt END_OF_INPUT
{ error_list.push_back(parse_error(@$, { error_list.push_back(parse_error(@$,
"missing closing bracket for star")); "missing closing bracket for equal operator"));
$$.min = $$.max = 0U; } $$.min = $$.max = 0U; }
@ -352,6 +376,24 @@ rationalexp: booleanatom
"be applied to a boolean expression")); "be applied to a boolean expression"));
error_list.push_back(parse_error(@$, error_list.push_back(parse_error(@$,
"treating this block as false")); "treating this block as false"));
$1->destroy();
$$ = constant::false_instance();
}
}
| rationalexp gotoargs
{
if ((kind_of($1) & Boolean_Kind) == Boolean_Kind)
{
$$ = bunop::instance(bunop::Goto, $1, $2.min, $2.max);
}
else
{
error_list.push_back(parse_error(@1,
"not a boolean expression: [->...] can only "
"be applied to a boolean expression"));
error_list.push_back(parse_error(@$,
"treating this block as false"));
$1->destroy();
$$ = constant::false_instance(); $$ = constant::false_instance();
} }
} }

View file

@ -98,6 +98,7 @@ flex_set_buffer(const char* buf, int start_tok)
"[+]" BEGIN(0); return token::OP_PLUS; "[+]" BEGIN(0); return token::OP_PLUS;
"[*" BEGIN(sqbracket); return token::OP_STAR_OPEN; "[*" BEGIN(sqbracket); return token::OP_STAR_OPEN;
"[=" BEGIN(sqbracket); return token::OP_EQUAL_OPEN; "[=" BEGIN(sqbracket); return token::OP_EQUAL_OPEN;
"[->" BEGIN(sqbracket); return token::OP_GOTO_OPEN;
<sqbracket>"]" BEGIN(0); return token::OP_SQBKT_CLOSE; <sqbracket>"]" BEGIN(0); return token::OP_SQBKT_CLOSE;
<sqbracket>[0-9]+ { <sqbracket>[0-9]+ {
unsigned num = 0; unsigned num = 0;

View file

@ -151,3 +151,12 @@ run 0 ../equals '{1[=1..2]}' '{[*1..2]}'
run 0 ../equals '{1[=..4]}' '{1[*..4]}' run 0 ../equals '{1[=..4]}' '{1[*..4]}'
run 0 ../equals '{b[=0]}' '{(!b)[*]}' run 0 ../equals '{b[=0]}' '{(!b)[*]}'
run 0 ../equals '{b[=0..]}' '{*}' run 0 ../equals '{b[=0..]}' '{*}'
run 0 ../equals '{0[->10..100];b}' '0'
run 0 ../equals '{0[->1..];b}' '0'
run 0 ../equals '{0[->0..100];b}' '{b}'
run 0 ../equals '{0[->0..];b}' '{b}'
run 0 ../equals '{1[->0];b}' '{b}'
run 0 ../equals '{1[->10,20];b}' '{[*10..20];b}'
run 0 ../equals '{1[->..];b}' '{[*1..];b}'
run 0 ../equals '{{a&!c}[->0];b}' '{b}'

View file

@ -416,28 +416,32 @@ namespace spot
return; return;
case bunop::Equal: case bunop::Equal:
case bunop::Goto:
{ {
// b[=min..max] == (!b;b[=min..max]) | (b;b[=min-1..max-1]) // b[=min..max] == (!b;b[=min..max]) | (b;b[=min-1..max-1])
// b[=0..max] == [*0] | (!b;b[=0..max]) | (b;b[=0..max-1]) // b[=0..max] == [*0] | (!b;b[=0..max]) | (b;b[=0..max-1])
// Note: b[=0] == (!b)[*] is a trivial identity, so it will // Note: b[=0] == (!b)[*] is a trivial identity, so it will
// never occur here. // never occur here.
formula* f1 = // !b;b[min..max] // b[->min..max] == (!b;b[->min..max]) | (b;b[->min-1..max-1])
// b[->0..max] == [*0] | (!b;b[->0..max]) | (b;b[->0..max-1])
// Note: b[->0] == [*0] is a trivial identity, so it will
// never occur here.
formula* f1 = // !b;b[=min..max] or !b;b[->min..max]
multop::instance(multop::Concat, multop::instance(multop::Concat,
unop::instance(unop::Not, unop::instance(unop::Not,
bo->child()->clone()), bo->child()->clone()),
bo->clone()); bo->clone());
formula* f2 = // b;b[=min-1..max-1] formula* f2 = // b;b[=min-1..max-1] or b;b[->min-1..max-1]
multop::instance(multop::Concat, multop::instance(multop::Concat,
bo->child()->clone(), bo->child()->clone(),
bunop::instance(bunop::Equal, bunop::instance(op,
bo->child()->clone(), bo->child()->clone(),
min2, max2)); min2, max2));
f = multop::instance(multop::Or, f1, f2); f = multop::instance(multop::Or, f1, f2);
res_ = recurse_and_concat(f); res_ = recurse_and_concat(f);
f->destroy(); f->destroy();
if (min == 0) if (min == 0)
res_ |= now_to_concat(); res_ |= now_to_concat();

View file

@ -84,6 +84,7 @@ check_psl '{(a* & (c;b*) & d);e}'
check_psl '{[*2];a[*2..4]}|->b' check_psl '{[*2];a[*2..4]}|->b'
check_psl '{a[*2..5] && b[*..3]}|->c' check_psl '{a[*2..5] && b[*..3]}|->c'
check_psl '{{[+];a;[+]} && {[+];b;[+]}}<>->c' check_psl '{{[+];a;[+]} && {[+];b;[+]}}<>->c'
check_psl '{(a[->3]) & {[+];b}}<>->c'
# Example from "Beyond Hardware Verification" by Glazberg, Moulin, Orni, # Example from "Beyond Hardware Verification" by Glazberg, Moulin, Orni,
# Ruah, Zarpas (2007). # Ruah, Zarpas (2007).
check_psl '{[*];req;ack}|=>{start;busy[*];done}' check_psl '{[*];req;ack}|=>{start;busy[*];done}'
@ -92,6 +93,8 @@ check_psl '{[*];req;ack}|=>{start;busy[*];done}'
check_psl '{end[=3]}(false)' # 2.27.A check_psl '{end[=3]}(false)' # 2.27.A
check_psl '{[*]; {read[=3]} && {write[=2]}} |=> check_psl '{[*]; {read[=3]} && {write[=2]}} |=>
{(!read && !write)[*]; ready}' # 3.5.A {(!read && !write)[*]; ready}' # 3.5.A
check_psl '{[*]; start && comp_data_en; !comp_data_en && good_comp;
{status_valid[->]} && {stop[=0]; true}} |-> {!data_out}' # 2.33
# Make sure 'a U (b U c)' has 3 states and 6 transitions, # Make sure 'a U (b U c)' has 3 states and 6 transitions,