tostring: add LaTeX output

* src/ltlvisit/tostring.cc, src/ltlvisit/tostring.hh
(to_latex_string): New function.
* src/bin/common_output.cc, src/bin/common_output.hh:
Add a --latex option.
* doc/tl/spotltl.sty: New file.
* doc/tl/Makefile.am: Distribute it.
* src/ltltest/latex.test: New test.
* src/ltltest/Makefile.am: Add it.
* NEWS: Mention it.
This commit is contained in:
Alexandre Duret-Lutz 2013-08-28 18:52:59 +02:00
parent 90c106f8a8
commit 9cfe1a3496
9 changed files with 301 additions and 33 deletions

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -27,6 +27,7 @@
#define OPT_SPOT 1
#define OPT_WRING 2
#define OPT_LATEX 3
output_format_t output_format = spot_output;
bool full_parenth = false;
@ -40,6 +41,7 @@ static const argp_option options[] =
{ "lbt", 'l', 0, 0, "output in LBT's syntax", -20 },
{ "wring", OPT_WRING, 0, 0, "output in Wring's syntax", -20 },
{ "utf8", '8', 0, 0, "output using UTF-8 characters", -20 },
{ "latex", OPT_LATEX, 0, 0, "output using LaTeX macros", -20 },
{ 0, 0, 0, 0, 0, 0 }
};
@ -63,6 +65,9 @@ parse_opt_output(int key, char*, struct argp_state*)
case 's':
output_format = spin_output;
break;
case OPT_LATEX:
output_format = latex_output;
break;
case OPT_SPOT:
output_format = spot_output;
break;
@ -119,6 +124,9 @@ output_formula(const spot::ltl::formula* f, const char* filename, int linenum)
case utf8_output:
spot::ltl::to_utf8_string(f, std::cout, full_parenth);
break;
case latex_output:
spot::ltl::to_latex_string(f, std::cout, full_parenth);
break;
}
// Make sure we abort if we can't write to std::cout anymore
// (like disk full or broken pipe with SIGPIPE ignored).

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2012, 2013 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -26,7 +26,7 @@
#include "ltlast/formula.hh"
enum output_format_t { spot_output, spin_output, utf8_output,
lbt_output, wring_output };
lbt_output, wring_output, latex_output };
extern output_format_t output_format;
extern bool full_parenth;

View file

@ -95,6 +95,7 @@ TESTS = \
kind.test \
remove_x.test \
ltlfilt.test \
latex.test \
lbt.test \
lenient.test \
isop.test \

62
src/ltltest/latex.test Executable file
View file

@ -0,0 +1,62 @@
#!/bin/sh
# -*- coding: utf-8 -*-
# Copyright (C) 2013 Laboratoire de Recherche et Développement
# de l'Epita (LRDE).
#
# 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 3 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 this program. If not, see <http://www.gnu.org/licenses/>.
. ./defs
set -e
# Skip this test if we don't find a sufficiently complete LaTeX
# installation
(latexmk --version) || exit 77
(pdflatex --version) || exit 77
(kpsewhich txfonts.sty) || exit 77
(kpsewhich amsmath.sty) || exit 77
cat >input <<EOF
XGFa <-> FGX(!b & !c) | (b ^ a)
a U b W c R (d & e) M f
{a;b[=2];((c:d*) && f*);e[*2..]}<>-> {((a | [*0])*;b[+]) & (c1[->2])}[]-> h
{a;b;c} []=> {d*;e} <>=> !f
!{a;b*;c}! -> d
G(uglyname->Fuglierlongname42)
EOF
(
cat <<\EOF
\documentclass{article}
\usepackage{amsmath}
\usepackage{spotltl}
\begin{document}
\begin{align}
EOF
( ../../bin/ltlfilt --latex input;
../../bin/genltl --go-theta=1..3 --latex ) | sed 's/$/\\\\/'
cat <<\EOF
\text{done}
\end{align}
\end{document}
EOF
) > output.tex
TEXINPUTS=$srcdir/../../../doc/tl: latexmk -f -silent --pdf output.tex
# latexmk will have a non-zero exit status on failure, so we will
# detect that. However the file output.pdf really ought to be
# controled by eye...

View file

@ -49,12 +49,12 @@ namespace spot
KR,
KW,
KM,
KEConcat,
KEConcatNext,
KEConcatMarked,
KEConcatMarkedNext,
KUConcat,
KUConcatNext,
KSeq,
KSeqNext,
KSeqMarked,
KSeqMarkedNext,
KTriggers,
KTriggersNext,
KNot,
KX,
KF,
@ -66,6 +66,13 @@ namespace spot
KAndNLM,
KConcat,
KFusion,
KOpenSERE,
KCloseSERE,
KCloseBunop,
KStarBunop,
KPlusBunop,
KEqualBunop,
KGotoBunop,
};
const char* spot_kw[] = {
@ -96,6 +103,13 @@ namespace spot
" & ",
";",
":",
"{",
"}",
"]",
"[*",
"[+]",
"[=",
"[->",
};
const char* spin_kw[] = {
@ -126,6 +140,13 @@ namespace spot
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[=", // not supported
"[->", // not supported
};
const char* wring_kw[] = {
@ -156,6 +177,13 @@ namespace spot
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[=", // not supported
"[->", // not supported
};
const char* utf8_kw[] = {
@ -186,6 +214,50 @@ namespace spot
" & ",
";",
":",
"{",
"}",
"]",
"[*",
"[+]",
"[=",
"[->",
};
const char* latex_kw[] = {
"\\ffalse",
"\\ttrue",
"\\eword",
" \\lxor ",
" \\limplies ",
" \\liff ",
" \\U ",
" \\R ",
" \\W ",
" \\M ",
"\\seq ",
"\\seqX ",
"\\seqM ",
"\\seqXM ",
"\\triggers ",
"\\triggersX ",
"\\lnot ",
"\\X ",
"\\F ",
"\\G ",
" \\lor ",
" \\SereOr ",
" \\land ",
" \\SereAnd ",
" \\SereAndNLM ",
" \\SereConcat ",
" \\SereFusion ",
"\\{",
"\\}",
"}",
"\\SereStar{",
"\\SerePlus{}",
"\\SereEqual{",
"\\SereGoto{",
};
static bool
@ -258,17 +330,23 @@ namespace spot
void
openp() const
{
os_ << (in_ratexp_ ? "{" : "(");
if (in_ratexp_)
emit(KOpenSERE);
else
os_ << "(";
}
void
closep() const
{
os_ << (in_ratexp_ ? "}" : ")");
if (in_ratexp_)
emit(KCloseSERE);
else
os_ << ")";
}
std::ostream&
emit(int symbol)
emit(int symbol) const
{
return os_ << kw_[symbol];
}
@ -292,7 +370,28 @@ namespace spot
}
else
{
os_ << str;
if (kw_ == latex_kw)
{
size_t s = str.size();
while (str[s - 1] >= '0' && str[s - 1] <= '9')
{
--s;
assert(s != 0); // bare words cannot start with letters
}
if (s > 1)
os_ << "\\mathit{";
os_ << str.substr(0, s);
if (s > 1)
os_ << "}";
if (s != str.size())
os_ << "_{"
<< str.substr(s)
<< "}";
}
else
{
os_ << str;
}
}
if (kw_ == wring_kw)
os_ << "=1";
@ -336,8 +435,8 @@ namespace spot
case binop::UConcat:
case binop::EConcat:
case binop::EConcatMarked:
os_ << "{";
in_ratexp_ = true;
openp();
top_level_ = true;
{
const multop* m = is_multop(bo->first(), multop::Concat);
@ -384,26 +483,26 @@ namespace spot
emit(KM);
break;
case binop::UConcat:
os_ << "}";
emit(onelast ? KUConcatNext : KUConcat);
closep();
emit(onelast ? KTriggersNext : KTriggers);
in_ratexp_ = false;
top_level_ = top_level;
top_level_ = false;
break;
case binop::EConcat:
emit(KCloseSERE);
if (bo->second() == constant::true_instance())
{
os_ << "}!";
os_ << "!";
in_ratexp_ = false;
goto second_done;
}
os_ << "}";
emit(onelast ? KEConcatNext : KEConcat);
emit(onelast ? KSeqNext : KSeq);
in_ratexp_ = false;
top_level_ = false;
break;
case binop::EConcatMarked:
os_ << "}";
emit(onelast ? KEConcatMarkedNext : KEConcatMarked);
emit(onelast ? KSeqMarkedNext : KSeqMarked);
in_ratexp_ = false;
top_level_ = false;
break;
@ -471,16 +570,16 @@ namespace spot
case Star:
if (min == 1 && max == bunop::unbounded)
{
os_ << "[+]";
emit(KPlusBunop);
return;
}
os_ << "[*";
emit(KStarBunop);
break;
case Equal:
os_ << "[=";
emit(KEqualBunop);
break;
case Goto:
os_ << "[->";
emit(KGotoBunop);
default_min = 1;
default_max = 1;
break;
@ -517,7 +616,7 @@ namespace spot
os_ << max;
}
}
os_ << "]";
emit(KCloseBunop);
}
void
@ -667,12 +766,15 @@ namespace spot
// in which case it's b[=1].
if (i + 2 < max && mo->nth(i) == mo->nth(i + 2))
{
os_ << "[=1]";
emit(KEqualBunop);
os_ << "1";
emit(KCloseBunop);
i += 2;
}
else
{
os_ << "[->]";
emit(KGotoBunop);
emit(KCloseBunop);
++i;
}
continue;
@ -685,7 +787,7 @@ namespace spot
if (b1 == b2)
{
emit_bunop_child(b1);
os_ << "[=";
emit(KEqualBunop);
unsigned min = s->min();
os_ << min;
unsigned max = s->max();
@ -695,7 +797,7 @@ namespace spot
if (max != bunop::unbounded)
os_ << max;
}
os_ << "]";
emit(KCloseBunop);
++i;
continue;
}
@ -847,5 +949,24 @@ namespace spot
return os.str();
}
std::ostream&
to_latex_string(const formula* f, std::ostream& os,
bool full_parent, bool ratexp)
{
to_string_visitor v(os, full_parent, ratexp, latex_kw);
f->accept(v);
return os;
}
std::string
to_latex_string(const formula* f,
bool full_parent, bool ratexp)
{
std::ostringstream os;
to_latex_string(f, os, full_parent, ratexp);
return os.str();
}
}
}

View file

@ -101,6 +101,27 @@ namespace spot
SPOT_API std::string
to_wring_string(const formula* f);
/// \brief Output a formula as an LaTeX string which is parsable unless
/// the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param os The stream where it should be output.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
/// \param ratexp Whether we are printing a SERE.
SPOT_API std::ostream&
to_latex_string(const formula* f, std::ostream& os,
bool full_parent = false, bool ratexp = false);
/// \brief Output a formula as a LaTeX string which is parsable
/// unless the formula contains automaton operators (used in ELTL formulae).
/// \param f The formula to translate.
/// \param full_parent Whether or not the string should by fully
/// parenthesized.
/// \param ratexp Whether we are printing a SERE.
SPOT_API std::string
to_latex_string(const formula* f,
bool full_parent = false, bool ratexp = false);
/// @}
}
}