spot/src/ltlvisit/tostring.cc
Damien Lefortier 7643c49cbd Correct LaCIM for ELTL and make it work with LBTT.
* src/eltlparse/eltlparse.yy: Adjust.
* src/ltlast/automatop.cc, src/ltlast/automatop.hh,
src/ltlvisit/clone.cc, src/ltlvisit/nenoform.cc: Clean the way we
handle the negation of automaton operators.
* src/ltlvisit/tostring.cc, src/ltlvisit/tostring.hh: Add an
optional argument to output a fully parenthesized string.
* src/tgbaalgos/eltl2tgba_lacim.cc: Fix it.
* src/tgbatest/eltl2tgba.cc: Add a new option (-L) to read formulae
from an LBTT-compatible file.
* src/tgbatest/eltl2tgba.test: A new tests.
* src/tgbatest/spotlbtt.test: Add LaCIM for ELTL.
2009-04-08 20:19:42 +02:00

396 lines
8 KiB
C++

// Copyright (C) 2003, 2004, 2008 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.
#include <cassert>
#include <sstream>
#include <ctype.h>
#include <ostream>
#include <cstring>
#include "tostring.hh"
#include "ltlast/visitor.hh"
#include "ltlast/allnodes.hh"
namespace spot
{
namespace ltl
{
namespace
{
static bool
is_bare_word(const char* str)
{
// Bare words cannot be empty, start with the letter of a unary
// operator, or be the name of an existing constant. Also they
// should start with an letter.
if (!*str
|| *str == 'F'
|| *str == 'G'
|| *str == 'X'
|| !(isalpha(*str) || *str == '_')
|| !strcasecmp(str, "true")
|| !strcasecmp(str, "false"))
return false;
// The remaining of the word must be alphanumeric.
while (*++str)
if (!(isalnum(*str) || *str == '_'))
return false;
return true;
}
class to_string_visitor: public const_visitor
{
public:
to_string_visitor(std::ostream& os, bool full_parent = false)
: os_(os), top_level_(true), full_parent_(full_parent)
{
}
virtual
~to_string_visitor()
{
}
void
visit(const atomic_prop* ap)
{
std::string str = ap->name();
if (!is_bare_word(str.c_str()))
{
os_ << '"' << str << '"';
}
else
{
os_ << str;
}
}
void
visit(const constant* c)
{
os_ << c->val_name();
}
void
visit(const binop* bo)
{
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
bo->first()->accept(*this);
switch (bo->op())
{
case binop::Xor:
os_ << " ^ ";
break;
case binop::Implies:
os_ << " => ";
break;
case binop::Equiv:
os_ << " <=> ";
break;
case binop::U:
os_ << " U ";
break;
case binop::R:
os_ << " R ";
break;
}
bo->second()->accept(*this);
if (!top_level)
os_ << ")";
}
void
visit(const unop* uo)
{
// The parser treats F0, F1, G0, G1, X0, and X1 as atomic
// propositions. So make sure we output F(0), G(1), etc.
bool need_parent = !!dynamic_cast<const constant*>(uo->child());
switch (uo->op())
{
case unop::Not:
os_ << "!";
need_parent = false;
break;
case unop::X:
os_ << "X";
break;
case unop::F:
os_ << "F";
break;
case unop::G:
os_ << "G";
break;
}
top_level_ = false;
if (need_parent || full_parent_)
os_ << "(";
uo->child()->accept(*this);
if (need_parent || full_parent_)
os_ << ")";
}
void
visit(const automatop* ao)
{
// Warning: this string isn't parsable because the automaton
// operators used may not be defined.
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
os_ << ao->nfa()->get_name() << "(";
unsigned max = ao->size();
ao->nth(0)->accept(*this);
for (unsigned n = 1; n < max; ++n)
{
os_ << ",";
ao->nth(n)->accept(*this);
}
os_ << ")";
if (!top_level)
os_ << ")";
}
void
visit(const multop* mo)
{
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
unsigned max = mo->size();
mo->nth(0)->accept(*this);
const char* ch = " ";
switch (mo->op())
{
case multop::Or:
ch = " | ";
break;
case multop::And:
ch = " & ";
break;
}
for (unsigned n = 1; n < max; ++n)
{
os_ << ch;
mo->nth(n)->accept(*this);
}
if (!top_level)
os_ << ")";
}
protected:
std::ostream& os_;
bool top_level_;
bool full_parent_;
};
class to_spin_string_visitor : public to_string_visitor
{
public:
to_spin_string_visitor(std::ostream& os)
: to_string_visitor(os)
{
}
virtual
~to_spin_string_visitor()
{
}
void
visit(const binop* bo)
{
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
switch (bo->op())
{
case binop::Xor:
os_ << "(!";
bo->first()->accept(*this);
os_ << " && ";
bo->second()->accept(*this);
os_ << ") || (";
bo->first()->accept(*this);
os_ << " && !";
bo->second()->accept(*this);
os_ << ")";
break;
case binop::Implies:
os_ << "!";
bo->first()->accept(*this);
os_ << " || ";
bo->second()->accept(*this);
break;
case binop::Equiv:
os_ << "(";
bo->first()->accept(*this);
os_ << " && ";
bo->second()->accept(*this);
os_ << ") || (!";
bo->first()->accept(*this);
os_ << " && !";
bo->second()->accept(*this);
os_ << ")";
case binop::U:
bo->first()->accept(*this);
os_ << " U ";
bo->second()->accept(*this);
break;
case binop::R:
bo->first()->accept(*this);
os_ << " V ";
bo->second()->accept(*this);
break;
}
if (!top_level)
os_ << ")";
}
void
visit(const unop* uo)
{
// The parser treats X0, and X1 as atomic propositions. So
// make sure we output X(0) and X(1).
bool need_parent = false;
switch (uo->op())
{
case unop::Not:
os_ << "!";
break;
case unop::X:
need_parent = !!dynamic_cast<const constant*>(uo->child());
os_ << "X";
break;
case unop::F:
os_ << "<>";
break;
case unop::G:
os_ << "[]";
break;
}
top_level_ = false;
if (need_parent)
os_ << "(";
uo->child()->accept(*this);
if (need_parent)
os_ << ")";
}
void
visit(const automatop* ao)
{
// Warning: this string isn't parsable because the automaton
// operators used may not be defined.
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
os_ << ao->nfa()->get_name() << "(";
unsigned max = ao->size();
ao->nth(0)->accept(*this);
for (unsigned n = 1; n < max; ++n)
{
os_ << ",";
ao->nth(n)->accept(*this);
}
os_ << ")";
if (!top_level)
os_ << ")";
}
void
visit(const multop* mo)
{
bool top_level = top_level_;
top_level_ = false;
if (!top_level)
os_ << "(";
unsigned max = mo->size();
mo->nth(0)->accept(*this);
const char* ch = " ";
switch (mo->op())
{
case multop::Or:
ch = " || ";
break;
case multop::And:
ch = " && ";
break;
}
for (unsigned n = 1; n < max; ++n)
{
os_ << ch;
mo->nth(n)->accept(*this);
}
if (!top_level)
os_ << ")";
}
};
} // anonymous
std::ostream&
to_string(const formula* f, std::ostream& os, bool full_parent)
{
to_string_visitor v(os, full_parent);
f->accept(v);
return os;
}
std::string
to_string(const formula* f, bool full_parent)
{
std::ostringstream os;
to_string(f, os, full_parent);
return os.str();
}
std::ostream&
to_spin_string(const formula* f, std::ostream& os)
{
to_spin_string_visitor v(os);
f->accept(v);
return os;
}
std::string
to_spin_string(const formula* f)
{
std::ostringstream os;
to_spin_string(f, os);
return os.str();
}
}
}