From 8128d92b36413ebb42b990bed62d0f7e5ebefb2b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 1 Oct 2002 14:21:01 +0000 Subject: [PATCH 1/7] lbtt 1.0.1 -- without generated files From 06226f32278398959ae1aa4fb737dc3973a799bf Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 29 Jul 2003 12:12:15 +0000 Subject: [PATCH 2/7] lbtt 1.0.2 --- lbtt/AUTHORS | 2 +- lbtt/ChangeLog | 32 ++++++++ lbtt/NEWS | 14 +++- lbtt/README | 8 +- lbtt/configure.ac | 4 +- lbtt/doc/lbtt.texi | 41 +++++++---- lbtt/src/Alloc.h | 2 +- lbtt/src/BitArray.cc | 4 +- lbtt/src/BitArray.h | 2 +- lbtt/src/Bitset.h | 2 +- lbtt/src/BuchiAutomaton.cc | 2 +- lbtt/src/BuchiAutomaton.h | 2 +- lbtt/src/Config-lex.ll | 2 +- lbtt/src/Config-parse.yy | 2 +- lbtt/src/Configuration.cc | 2 +- lbtt/src/Configuration.h | 2 +- lbtt/src/DispUtil.cc | 2 +- lbtt/src/DispUtil.h | 2 +- lbtt/src/EdgeContainer.h | 2 +- lbtt/src/Exception.h | 2 +- lbtt/src/ExternalTranslator.cc | 2 +- lbtt/src/ExternalTranslator.h | 2 +- lbtt/src/FormulaRandomizer.cc | 2 +- lbtt/src/FormulaRandomizer.h | 2 +- lbtt/src/FormulaWriter.h | 2 +- lbtt/src/Graph.h.in | 2 +- lbtt/src/LbtWrapper.h | 2 +- lbtt/src/LtlFormula.cc | 2 +- lbtt/src/LtlFormula.h | 2 +- lbtt/src/NeverClaim-lex.ll | 2 +- lbtt/src/NeverClaim-parse.yy | 2 +- lbtt/src/NeverClaimAutomaton.cc | 4 +- lbtt/src/NeverClaimAutomaton.h | 2 +- lbtt/src/PathEvaluator.cc | 2 +- lbtt/src/PathEvaluator.h | 2 +- lbtt/src/PathIterator.cc | 2 +- lbtt/src/PathIterator.h | 2 +- lbtt/src/ProductAutomaton.cc | 16 ++-- lbtt/src/ProductAutomaton.h | 2 +- lbtt/src/Random.h | 2 +- lbtt/src/SccIterator.h | 2 +- lbtt/src/SharedTestData.h | 2 +- lbtt/src/SpinWrapper.cc | 2 +- lbtt/src/SpinWrapper.h | 2 +- lbtt/src/StatDisplay.cc | 4 +- lbtt/src/StatDisplay.h | 2 +- lbtt/src/StateSpace.cc | 2 +- lbtt/src/StateSpace.h | 2 +- lbtt/src/StateSpaceRandomizer.cc | 2 +- lbtt/src/StateSpaceRandomizer.h | 2 +- lbtt/src/StringUtil.cc | 2 +- lbtt/src/StringUtil.h | 2 +- lbtt/src/TestOperations.cc | 2 +- lbtt/src/TestOperations.h | 2 +- lbtt/src/TestRoundInfo.h | 2 +- lbtt/src/TestStatistics.cc | 2 +- lbtt/src/TestStatistics.h | 2 +- lbtt/src/TranslatorInterface.h | 2 +- lbtt/src/UserCommandReader.cc | 2 +- lbtt/src/UserCommandReader.h | 2 +- lbtt/src/UserCommands.cc | 123 ++++++++++++++++++------------- lbtt/src/UserCommands.h | 2 +- lbtt/src/main.cc | 2 +- lbtt/src/translate.cc | 2 +- lbtt/src/translate.h | 2 +- 65 files changed, 222 insertions(+), 138 deletions(-) diff --git a/lbtt/AUTHORS b/lbtt/AUTHORS index 87908cad0..dfd5d7d33 100644 --- a/lbtt/AUTHORS +++ b/lbtt/AUTHORS @@ -1 +1 @@ -lbtt was written by Heikki Tauriainen +Heikki Tauriainen diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index d15f24ea2..181f75ade 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,35 @@ +2003-07-18 Heikki Tauriainen + + * UserCommands.cc (printAutomatonAnalysisResults): Ensure that + the states in a witness for the nonemptiness of two Bûchi + automata are distinct to prevent the truth valuation for the + atomic propositions from being defined multiple times in any + state of the witness. + + * Version 1.0.2 released. + +2003-07-17 Heikki Tauriainen + + * NeverClaimAutomaton.cc (ParseErrorException::ParseErrorException): + Fix a string buffer overflow. + + * ProductAutomaton.cc (findAcceptingExecution): Concatenate + fragments of an accepting cycle in the correct order. (Thanks to + Joachim Klein for pointing out an example uncovering the bug + in previous releases.) + +2002-11-04 Heikki Tauriainen + + * StatDisplay.cc (printCollectiveStats): If using more than five + formula operators (but the total number of operators is not + a multiple of 5), insert an empty line in the output before the + last row of the operator distribution table. + +2002-10-21 Heikki Tauriainen + + * BitArray.cc (BitArray::find): Fix bug in testing whether + all accessed bytes were zero. + 2002-10-01 Heikki Tauriainen * Version 1.0.1 released. diff --git a/lbtt/NEWS b/lbtt/NEWS index 1f3705dc8..e8c820d71 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,5 +1,5 @@ -lbtt NEWS -- history of user-visible changes. 01 Oct 2002 -Copyright (C) 2002 Heikki Tauriainen +lbtt NEWS -- history of user-visible changes. 18 Jul 2003 +Copyright (C) 2003 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the @@ -12,6 +12,16 @@ Copyright (C) 2002 Heikki Tauriainen Please send bug reports to . +Version 1.0.2 + +* Bug fix release. + +* The official WWW home page of the tool is now located at + . From there you can also + access the FormulaOptions block generator for lbtt configuration + files. The generator has limited support for specifying relative + (instead of absolute) priorities for the LTL operators. + Version 1.0.1 * This release does not add new functionality to the program apart from diff --git a/lbtt/README b/lbtt/README index fcaf03f9f..3096ee211 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,7 +1,7 @@ -lbtt version 1.0.1 +lbtt version 1.0.2 ------------------ -lbtt is a tool for testing programs which translate formulas +lbtt is a tool for testing programs that translate formulas expressed in propositional linear temporal logic (LTL) into Büchi automata. The goal of the tool is to assist in the correct implementation of LTL-to-Büchi translation algorithms @@ -11,7 +11,7 @@ for very basic profiling of different LTL-to-B to evaluate their performance. The latest version of the program is available at -. +. lbtt is free software, you may change and redistribute it under the terms of the GNU General Public License. lbtt comes with @@ -82,4 +82,4 @@ Documentation: The documentation is also available in various formats at the program's home page at - . + . diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 57a31b35f..6dec5b8a0 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,8 +1,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.53]) -AC_INIT([lbtt], [1.0.1], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.1]) +AC_INIT([lbtt], [1.0.2], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.2]) AC_CONFIG_SRCDIR([src/main.cc]) AM_INIT_AUTOMAKE AM_CONFIG_HEADER([config.h]) diff --git a/lbtt/doc/lbtt.texi b/lbtt/doc/lbtt.texi index 167a02439..e70fe4455 100644 --- a/lbtt/doc/lbtt.texi +++ b/lbtt/doc/lbtt.texi @@ -14,12 +14,14 @@ This file documents how to use the LTL-to-B@"uchi translator testbench @command{lbtt}. -Copyright @copyright{} 2001 Heikki Tauriainen +Copyright @copyright{} 2003 Heikki Tauriainen @ifinfo @email{heikki.tauriainen@@hut.fi} @end ifinfo @ifnotinfo +@ifnothtml <@email{heikki.tauriainen@@hut.fi}> +@end ifnothtml @end ifnotinfo @ifhtml @@ -66,11 +68,11 @@ under the above conditions for modified versions. @author Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> @page @vskip 0pt plus 1filll -Copyright @copyright{} 2001 Heikki Tauriainen +Copyright @copyright{} 2003 Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> The latest version of this manual can be obtained from@* -<@url{http://www.tcs.hut.fi/%7Ehtauriai/lbtt/}>. +<@url{http://www.tcs.hut.fi/Software/lbtt/}>. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and @@ -101,7 +103,7 @@ under the above conditions for modified versions. for translating propositional linear temporal logic formulas into B@"uchi automata. -This is edition 1.0.0 of the @command{lbtt} documentation. This edition +This is edition 1.0.1 of the @command{lbtt} documentation. This edition applies to @command{lbtt} versions 1.0.x. @command{lbtt} is free software, you may change and redistribute it @@ -203,12 +205,12 @@ formulas as input and then performing simple consistency checks on the resulting automata to test whether the translators seem to operate correctly in practice. (See @ifnottex -@ref{[Tau00]} +@ref{[TH02]} @end ifnottex @iftex -[Tau00] +[TH02] @end iftex -for a description of the theory behind the testing methods.) If the tests +for more information on the theory behind the testing methods.) If the tests suggest an implementation error in a translator, @command{lbtt} can generate sample data which causes a test failure and which may also be useful for debugging the @@ -240,6 +242,13 @@ together with the outline of @command{lbtt}'s testing procedure. However, the chapter is not intended to be a thorough introduction to the theoretical background of the different tests; see, for example, @ifnottex +@ref{[TH02]} +@end ifnottex +@iftex +[TH02] +@end iftex +or +@ifnottex @ref{[Tau00]} @end ifnottex @iftex @@ -3821,10 +3830,10 @@ originally based on the algorithm presented in @end iftex See @ifinfo -@url{http://netlib.bell-labs.com/netlib/spin/whatispin.html} +@url{http://spinroot.com/spin/whatispin.html} @end ifinfo @ifnotinfo -<@uref{http://netlib.bell-labs.com/netlib/spin/whatispin.html}> +<@uref{http://spinroot.com/spin/whatispin.html}> @end ifnotinfo for more information. @@ -3947,19 +3956,25 @@ pages 247---263. Springer-Verlag, 2000. @item @anchor{[Tau00]} [Tau00] H. Tauriainen. Automated testing of B@"uchi automata translators -for linear temporal logic. Technical report A66, Laboratory for Theoretical +for linear temporal logic. Research report A66, Laboratory for Theoretical Computer Science, Helsinki University of Technology, Espoo, Finland, 2000. Available on the WWW at @ifinfo -@url{http://www.tcs.hut.fi/Publications/reports/A66abstract.html} +@url{http://www.tcs.hut.fi/Publications/info/bibdb.HUT-TCS-A66.shtml} @end ifinfo @ifhtml -<@uref{http://www.tcs.hut.fi/Publications/reports/A66abstract.html}>. +<@uref{http://www.tcs.hut.fi/Publications/info/bibdb.HUT-TCS-A66.shtml}>. @end ifhtml @iftex -<@url{http://www.tcs.hut.fi/Publications/reports/ A66abstract.html}>. +<@url{http://www.tcs.hut.fi/Publications/info/ bibdb.HUT-TCS-A66.shtml}>. @end iftex +@item @anchor{[TH02]} [TH02] +H. Tauriainen and K. Heljanko. Testing LTL formula translation into B@"uchi +automata. +@i{International Journal on Software Tools for Technology Transfer (STTT)} +4(1):57---70, 2002. + @item @anchor{[Var96]} [Var96] M.@: Y.@: Vardi. An automata-theoretic approach to linear temporal logic. In @i{Logics for Concurrency: Structure versus Automata}, volume 1043 of diff --git a/lbtt/src/Alloc.h b/lbtt/src/Alloc.h index 73fe13ef0..f264fee63 100644 --- a/lbtt/src/Alloc.h +++ b/lbtt/src/Alloc.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/BitArray.cc b/lbtt/src/BitArray.cc index f2086ab47..1d0342ab7 100644 --- a/lbtt/src/BitArray.cc +++ b/lbtt/src/BitArray.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -378,7 +378,7 @@ unsigned long int BitArray::find(const unsigned long int max_count) const for (i = 0; i < bsize && bits[i] == 0; ++i) ; - if (i == max_count) + if (i == bsize) return max_count; unsigned char c = bits[i]; diff --git a/lbtt/src/BitArray.h b/lbtt/src/BitArray.h index d8ef12b96..0efbac3f0 100644 --- a/lbtt/src/BitArray.h +++ b/lbtt/src/BitArray.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Bitset.h b/lbtt/src/Bitset.h index 82f87667c..f74da7251 100644 --- a/lbtt/src/Bitset.h +++ b/lbtt/src/Bitset.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/BuchiAutomaton.cc b/lbtt/src/BuchiAutomaton.cc index 7be826c8e..77d0707cf 100644 --- a/lbtt/src/BuchiAutomaton.cc +++ b/lbtt/src/BuchiAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/BuchiAutomaton.h b/lbtt/src/BuchiAutomaton.h index 7b58867b3..a9622f82e 100644 --- a/lbtt/src/BuchiAutomaton.h +++ b/lbtt/src/BuchiAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Config-lex.ll b/lbtt/src/Config-lex.ll index e09e51308..96d43b5c4 100644 --- a/lbtt/src/Config-lex.ll +++ b/lbtt/src/Config-lex.ll @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Config-parse.yy b/lbtt/src/Config-parse.yy index 07fba1c17..fec1407cc 100644 --- a/lbtt/src/Config-parse.yy +++ b/lbtt/src/Config-parse.yy @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Configuration.cc b/lbtt/src/Configuration.cc index 8392ca33f..f3ac26cb6 100644 --- a/lbtt/src/Configuration.cc +++ b/lbtt/src/Configuration.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Configuration.h b/lbtt/src/Configuration.h index 99ad8b49a..b58234e45 100644 --- a/lbtt/src/Configuration.h +++ b/lbtt/src/Configuration.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/DispUtil.cc b/lbtt/src/DispUtil.cc index 3c99f0d43..aac917d6a 100644 --- a/lbtt/src/DispUtil.cc +++ b/lbtt/src/DispUtil.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/DispUtil.h b/lbtt/src/DispUtil.h index ff0040c63..4c779aaf3 100644 --- a/lbtt/src/DispUtil.h +++ b/lbtt/src/DispUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/EdgeContainer.h b/lbtt/src/EdgeContainer.h index b0180304d..1d4674e72 100644 --- a/lbtt/src/EdgeContainer.h +++ b/lbtt/src/EdgeContainer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Exception.h b/lbtt/src/Exception.h index 274ab3be6..d3c248be4 100644 --- a/lbtt/src/Exception.h +++ b/lbtt/src/Exception.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/ExternalTranslator.cc b/lbtt/src/ExternalTranslator.cc index d73091da7..14a8e42fb 100644 --- a/lbtt/src/ExternalTranslator.cc +++ b/lbtt/src/ExternalTranslator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/ExternalTranslator.h b/lbtt/src/ExternalTranslator.h index 4835f6ef0..17faaacee 100644 --- a/lbtt/src/ExternalTranslator.h +++ b/lbtt/src/ExternalTranslator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/FormulaRandomizer.cc b/lbtt/src/FormulaRandomizer.cc index bdd5f87df..10497fac9 100644 --- a/lbtt/src/FormulaRandomizer.cc +++ b/lbtt/src/FormulaRandomizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/FormulaRandomizer.h b/lbtt/src/FormulaRandomizer.h index c23348f49..cdeca4422 100644 --- a/lbtt/src/FormulaRandomizer.h +++ b/lbtt/src/FormulaRandomizer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/FormulaWriter.h b/lbtt/src/FormulaWriter.h index 0df6ecfbf..f2e9cfe77 100644 --- a/lbtt/src/FormulaWriter.h +++ b/lbtt/src/FormulaWriter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Graph.h.in b/lbtt/src/Graph.h.in index f701b58a3..22a07ec86 100644 --- a/lbtt/src/Graph.h.in +++ b/lbtt/src/Graph.h.in @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/LbtWrapper.h b/lbtt/src/LbtWrapper.h index 70319c2da..af555df35 100644 --- a/lbtt/src/LbtWrapper.h +++ b/lbtt/src/LbtWrapper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/LtlFormula.cc b/lbtt/src/LtlFormula.cc index 5d0aa0cef..71a2aa6d0 100644 --- a/lbtt/src/LtlFormula.cc +++ b/lbtt/src/LtlFormula.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/LtlFormula.h b/lbtt/src/LtlFormula.h index fee7e8f60..3b1a0300d 100644 --- a/lbtt/src/LtlFormula.h +++ b/lbtt/src/LtlFormula.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/NeverClaim-lex.ll b/lbtt/src/NeverClaim-lex.ll index 8123ee3a8..d2f6d7bd4 100644 --- a/lbtt/src/NeverClaim-lex.ll +++ b/lbtt/src/NeverClaim-lex.ll @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/NeverClaim-parse.yy b/lbtt/src/NeverClaim-parse.yy index 8688dd00a..ea0ee5703 100644 --- a/lbtt/src/NeverClaim-parse.yy +++ b/lbtt/src/NeverClaim-parse.yy @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/NeverClaimAutomaton.cc b/lbtt/src/NeverClaimAutomaton.cc index 7f6ad3592..341fd36df 100644 --- a/lbtt/src/NeverClaimAutomaton.cc +++ b/lbtt/src/NeverClaimAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -312,7 +312,7 @@ ParseErrorException::ParseErrorException else { string space_string(msg.substr(0, error_pos)); - for (string::size_type c = 0; c < msg.length(); c++) + for (string::size_type c = 0; c < space_string.length(); c++) if (space_string[c] != ' ' && space_string[c] != '\t') space_string[c] = ' '; diff --git a/lbtt/src/NeverClaimAutomaton.h b/lbtt/src/NeverClaimAutomaton.h index 6f93881f0..69759bd7d 100644 --- a/lbtt/src/NeverClaimAutomaton.h +++ b/lbtt/src/NeverClaimAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/PathEvaluator.cc b/lbtt/src/PathEvaluator.cc index 84b769974..d42243bcd 100644 --- a/lbtt/src/PathEvaluator.cc +++ b/lbtt/src/PathEvaluator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/PathEvaluator.h b/lbtt/src/PathEvaluator.h index f8ff84091..cf0c79e0b 100644 --- a/lbtt/src/PathEvaluator.h +++ b/lbtt/src/PathEvaluator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/PathIterator.cc b/lbtt/src/PathIterator.cc index 3f468940a..740a3a19f 100644 --- a/lbtt/src/PathIterator.cc +++ b/lbtt/src/PathIterator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/PathIterator.h b/lbtt/src/PathIterator.h index 8c6f914ca..6411692a4 100644 --- a/lbtt/src/PathIterator.h +++ b/lbtt/src/PathIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/ProductAutomaton.cc b/lbtt/src/ProductAutomaton.cc index c0513404a..3e568ea92 100644 --- a/lbtt/src/ProductAutomaton.cc +++ b/lbtt/src/ProductAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -864,12 +864,15 @@ void ProductAutomaton::findAcceptingExecution all_acceptance_sets_on_path = false; } + deque cycle_fragment; while (state != bfs_root) { - cycle.push_back(make_pair(buchiState(state), - systemState(state))); + cycle_fragment.push_back(make_pair(buchiState(state), + systemState(state))); state = shortest_path_predecessor[state]; } + cycle.insert(cycle.begin(), cycle_fragment.begin(), + cycle_fragment.end()); bfs_root = (*predecessor)->targetNode(); visited.clear(nodes.size()); @@ -887,13 +890,16 @@ void ProductAutomaton::findAcceptingExecution backward_search_queue.pop_front(); } + deque cycle_fragment; while (state != bfs_root) { - cycle.push_back(make_pair(buchiState(state), - systemState(state))); + cycle_fragment.push_back(make_pair(buchiState(state), + systemState(state))); state = shortest_path_predecessor[state]; } + cycle.insert(cycle.begin(), cycle_fragment.begin(), + cycle_fragment.end()); cycle.push_back(make_pair(buchiState(search_start_state), systemState(search_start_state))); diff --git a/lbtt/src/ProductAutomaton.h b/lbtt/src/ProductAutomaton.h index ccd567468..e4c8c33fe 100644 --- a/lbtt/src/ProductAutomaton.h +++ b/lbtt/src/ProductAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Random.h b/lbtt/src/Random.h index 2650599a5..3820b30d3 100644 --- a/lbtt/src/Random.h +++ b/lbtt/src/Random.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/SccIterator.h b/lbtt/src/SccIterator.h index 838008c42..a8547bc13 100644 --- a/lbtt/src/SccIterator.h +++ b/lbtt/src/SccIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/SharedTestData.h b/lbtt/src/SharedTestData.h index ff4cd9209..1a2a9898b 100644 --- a/lbtt/src/SharedTestData.h +++ b/lbtt/src/SharedTestData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/SpinWrapper.cc b/lbtt/src/SpinWrapper.cc index 1b21aa68c..d66e45571 100644 --- a/lbtt/src/SpinWrapper.cc +++ b/lbtt/src/SpinWrapper.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/SpinWrapper.h b/lbtt/src/SpinWrapper.h index 233d62b06..a43dcc6df 100644 --- a/lbtt/src/SpinWrapper.h +++ b/lbtt/src/SpinWrapper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StatDisplay.cc b/lbtt/src/StatDisplay.cc index 6da2df696..2fabdcc47 100644 --- a/lbtt/src/StatDisplay.cc +++ b/lbtt/src/StatDisplay.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -912,6 +912,8 @@ void printCollectiveStats(ostream& stream, int indent) if (number_of_symbols_printed % 5 != 0) { + if (number_of_symbols_printed > 5) + estream << '\n'; estream << ind + " operator " + symbol_name_string + '\n' + ind + " # " + symbol_number_string + '\n' + ind + " #/formula " + symbol_distribution_string diff --git a/lbtt/src/StatDisplay.h b/lbtt/src/StatDisplay.h index 83469a460..8a6cc1a6a 100644 --- a/lbtt/src/StatDisplay.h +++ b/lbtt/src/StatDisplay.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StateSpace.cc b/lbtt/src/StateSpace.cc index 90a155f3a..4782846f8 100644 --- a/lbtt/src/StateSpace.cc +++ b/lbtt/src/StateSpace.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StateSpace.h b/lbtt/src/StateSpace.h index fc356ba1b..9b3328966 100644 --- a/lbtt/src/StateSpace.h +++ b/lbtt/src/StateSpace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StateSpaceRandomizer.cc b/lbtt/src/StateSpaceRandomizer.cc index 550f418c4..8cc928d87 100644 --- a/lbtt/src/StateSpaceRandomizer.cc +++ b/lbtt/src/StateSpaceRandomizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StateSpaceRandomizer.h b/lbtt/src/StateSpaceRandomizer.h index 03dd3ee07..44d2b583a 100644 --- a/lbtt/src/StateSpaceRandomizer.h +++ b/lbtt/src/StateSpaceRandomizer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StringUtil.cc b/lbtt/src/StringUtil.cc index e60b354fe..bb61e80ea 100644 --- a/lbtt/src/StringUtil.cc +++ b/lbtt/src/StringUtil.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/StringUtil.h b/lbtt/src/StringUtil.h index 8b1623352..dca736e9a 100644 --- a/lbtt/src/StringUtil.h +++ b/lbtt/src/StringUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 790237858..3c7ade3fc 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TestOperations.h b/lbtt/src/TestOperations.h index 314690614..b1b012634 100644 --- a/lbtt/src/TestOperations.h +++ b/lbtt/src/TestOperations.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TestRoundInfo.h b/lbtt/src/TestRoundInfo.h index b77e7f674..1c7c5beb2 100644 --- a/lbtt/src/TestRoundInfo.h +++ b/lbtt/src/TestRoundInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TestStatistics.cc b/lbtt/src/TestStatistics.cc index b791e27b4..fb04f18d9 100644 --- a/lbtt/src/TestStatistics.cc +++ b/lbtt/src/TestStatistics.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TestStatistics.h b/lbtt/src/TestStatistics.h index 75a4c7c3f..074f63779 100644 --- a/lbtt/src/TestStatistics.h +++ b/lbtt/src/TestStatistics.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/TranslatorInterface.h b/lbtt/src/TranslatorInterface.h index 8053c5bac..d158d2eee 100644 --- a/lbtt/src/TranslatorInterface.h +++ b/lbtt/src/TranslatorInterface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/UserCommandReader.cc b/lbtt/src/UserCommandReader.cc index 6365784f3..2f688b51e 100644 --- a/lbtt/src/UserCommandReader.cc +++ b/lbtt/src/UserCommandReader.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/UserCommandReader.h b/lbtt/src/UserCommandReader.h index da087daca..abe22cb77 100644 --- a/lbtt/src/UserCommandReader.h +++ b/lbtt/src/UserCommandReader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/UserCommands.cc b/lbtt/src/UserCommands.cc index ae5a6630a..c948c85c2 100644 --- a/lbtt/src/UserCommands.cc +++ b/lbtt/src/UserCommands.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -965,53 +965,73 @@ void printAutomatonAnalysisResults * intersection automaton from the result. */ - deque prefix; - deque cycle; vector path; - while (!execution.first.empty()) - { - prefix.push_back(execution.first.front().first); - path.push_back(execution.first.front().first); - execution.first.pop_front(); - } + for (deque::const_iterator + state = execution.first.begin(); + state != execution.first.end(); + ++state) + path.push_back(state->first); - while (!execution.second.empty()) - { - cycle.push_back(execution.second.front().first); - path.push_back(execution.second.front().first); - execution.second.pop_front(); - } + const vector + ::size_type loop_pos = path.size(); + + for (deque::const_iterator + state = execution.second.begin(); + state != execution.second.end(); + ++state) + path.push_back(state->first); /* * Construct an execution accepted by both of the automata. This is done * by giving suitable truth assignments for the atomic propositions in - * selected states of the state space to which the intersection automaton - * was converted. + * 'path.size()' states, where `path' corresponds to an accepting run of + * the intersection automaton. (The state space representing the + * intersection automaton is reused for this purpose, since it is not + * needed any longer.) In addition, `prefix' and `cycle' (required for + * displaying the execution) are built to refer to the reused states. */ - GraphEdgeContainer::const_iterator transition; + deque prefix, cycle; - path.push_back(cycle.front()); + /* + * Ensure that the state space is large enough to contain the execution + * (the execution may pass several times through a state in the + * intersection automaton). + */ + + if (automaton_as_statespace.size() < path.size()) + automaton_as_statespace.expand + (path.size() - automaton_as_statespace.size()); + + path.push_back(path[loop_pos]); /* use the first state of the cycle as a + * temporary sentinel element + */ for (vector ::size_type state = 0; state + 1 < path.size(); - state++) + ++state) { + GraphEdgeContainer::const_iterator transition; + for (transition = (*a)[path[state]].edges().begin(); (*transition)->targetNode() != path[state + 1]; ++transition) ; - automaton_as_statespace[path[state]].positiveAtoms() + automaton_as_statespace[state].positiveAtoms() = static_cast(*transition) ->guard().findPropositionalModel (configuration.formula_options.formula_generator. number_of_available_variables); + + (state < loop_pos ? prefix : cycle).push_back(state); } - path.pop_back(); + path.pop_back(); /* remove the sentinel element */ delete a; a = 0; @@ -1038,40 +1058,25 @@ void printAutomatonAnalysisResults deque aut_cycle; - const deque* - original_execution_states; - deque* - new_execution_states; - for (int i = 0; i < 2; i++) { aut_prefix.clear(); aut_cycle.clear(); - for (int j = 0; j < 2; j++) - { - if (j == 0) - { - original_execution_states = &prefix; - new_execution_states = &aut_prefix; - } - else - { - original_execution_states = &cycle; - new_execution_states = &aut_cycle; - } + deque* + new_execution_states = &aut_prefix; - for (deque::const_iterator - execution_state = original_execution_states->begin(); - execution_state != original_execution_states->end(); - ++execution_state) - { - new_execution_states->push_back - (i == 0 - ? intersection_state_mapping[*execution_state].first - : intersection_state_mapping[*execution_state].second); - } + for (vector + ::size_type state_id = 0; + state_id < path.size(); + ++state_id) + { + if (state_id == loop_pos) + new_execution_states = &aut_cycle; + new_execution_states->push_back + (i == 0 + ? intersection_state_mapping[path[state_id]].first + : intersection_state_mapping[path[state_id]].second); } synchronizePrefixAndCycle(aut_prefix, aut_cycle); @@ -1083,13 +1088,27 @@ void printAutomatonAnalysisResults automaton_stats[i].buchi_automaton)); } + /* + * Normalize the state identifiers in `path' to refer to the states that + * give the valuations for atomic propositions along the execution. + */ + + for (vector + ::size_type state = 0; + state < path.size(); + ++state) + path[state] = state; + + /* + * Display a proof or a refutation for the formula in the execution. + */ + estream << string(indent + 2, ' ') + "Analysis of the positive formula in the execution M:\n"; Ltl::PathEvaluator path_evaluator; bool result = path_evaluator.evaluate - (*round_info.formulae[formula], automaton_as_statespace, path, - prefix.size()); + (*round_info.formulae[formula], automaton_as_statespace, path, loop_pos); path_evaluator.print(stream, indent + 4); diff --git a/lbtt/src/UserCommands.h b/lbtt/src/UserCommands.h index 02b7a0925..feecf49b8 100644 --- a/lbtt/src/UserCommands.h +++ b/lbtt/src/UserCommands.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/main.cc b/lbtt/src/main.cc index 8cf2c665d..01c7e386d 100644 --- a/lbtt/src/main.cc +++ b/lbtt/src/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/translate.cc b/lbtt/src/translate.cc index bf2679c74..6580a4bb1 100644 --- a/lbtt/src/translate.cc +++ b/lbtt/src/translate.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/translate.h b/lbtt/src/translate.h index b0fb7e53f..b25d06caf 100644 --- a/lbtt/src/translate.h +++ b/lbtt/src/translate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002 + * Copyright (C) 1999, 2000, 2001, 2002, 2003 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or From 15618b84eaec04caf30d0d31332c6a5b44eab1aa Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 16 Feb 2004 11:35:59 +0000 Subject: [PATCH 3/7] Import of lbtt 1.0.3 --- lbtt/ChangeLog | 99 +- lbtt/NEWS | 12 +- lbtt/README | 2 +- lbtt/configure.ac | 29 +- lbtt/doc/intersectioncheck.txt | 1 + lbtt/doc/lbtt.texi | 194 +- lbtt/doc/testprocedure.txt | 1 + lbtt/doc/texinfo.tex | 6339 ++++++++++++++++-------------- lbtt/src/BitArray.cc | 6 +- lbtt/src/BitArray.h | 6 +- lbtt/src/Bitset.h | 2 +- lbtt/src/BuchiAutomaton.cc | 6 +- lbtt/src/BuchiAutomaton.h | 8 +- lbtt/src/Config-lex.ll | 2 +- lbtt/src/Config-parse.yy | 4 +- lbtt/src/Configuration.cc | 6 +- lbtt/src/Configuration.h | 8 +- lbtt/src/DispUtil.cc | 8 +- lbtt/src/DispUtil.h | 6 +- lbtt/src/EdgeContainer.h | 2 +- lbtt/src/Exception.h | 2 +- lbtt/src/ExternalTranslator.cc | 6 +- lbtt/src/ExternalTranslator.h | 8 +- lbtt/src/FormulaRandomizer.cc | 6 +- lbtt/src/FormulaRandomizer.h | 8 +- lbtt/src/FormulaWriter.h | 2 +- lbtt/src/Graph.h.in | 4 +- lbtt/src/LbtWrapper.h | 2 +- lbtt/src/LbttAlloc.h | 167 + lbtt/src/LtlFormula.cc | 6 +- lbtt/src/LtlFormula.h | 8 +- lbtt/src/Makefile.am | 24 +- lbtt/src/NeverClaim-lex.ll | 2 +- lbtt/src/NeverClaim-parse.yy | 2 +- lbtt/src/NeverClaimAutomaton.cc | 6 +- lbtt/src/NeverClaimAutomaton.h | 8 +- lbtt/src/PathEvaluator.cc | 6 +- lbtt/src/PathEvaluator.h | 8 +- lbtt/src/PathIterator.cc | 6 +- lbtt/src/PathIterator.h | 6 +- lbtt/src/ProductAutomaton.cc | 6 +- lbtt/src/ProductAutomaton.h | 8 +- lbtt/src/Random.h | 2 +- lbtt/src/SccIterator.h | 4 +- lbtt/src/SharedTestData.h | 4 +- lbtt/src/SpinWrapper.cc | 6 +- lbtt/src/SpinWrapper.h | 6 +- lbtt/src/StatDisplay.cc | 6 +- lbtt/src/StatDisplay.h | 8 +- lbtt/src/StateSpace.cc | 6 +- lbtt/src/StateSpace.h | 8 +- lbtt/src/StateSpaceRandomizer.cc | 8 +- lbtt/src/StateSpaceRandomizer.h | 6 +- lbtt/src/StringUtil.cc | 6 +- lbtt/src/StringUtil.h | 8 +- lbtt/src/TestOperations.cc | 6 +- lbtt/src/TestOperations.h | 8 +- lbtt/src/TestRoundInfo.h | 4 +- lbtt/src/TestStatistics.cc | 6 +- lbtt/src/TestStatistics.h | 8 +- lbtt/src/TranslatorInterface.h | 2 +- lbtt/src/UserCommandReader.cc | 53 +- lbtt/src/UserCommandReader.h | 8 +- lbtt/src/UserCommands.cc | 6 +- lbtt/src/UserCommands.h | 8 +- lbtt/src/main.cc | 4 +- lbtt/src/translate.cc | 6 +- lbtt/src/translate.h | 6 +- 68 files changed, 3988 insertions(+), 3257 deletions(-) create mode 100644 lbtt/src/LbttAlloc.h diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index 181f75ade..0d368e560 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,70 @@ +2004-02-13 Heikki Tauriainen + + * src/BitArray.{h,cc}, src/BuchiAutomaton.{h,cc}, + src/Configuration.{h,cc}, src/DispUtil.{h,cc}, + src/ExternalTranslator.{h,cc}, src/FormulaRandomizer.{h,cc}, + src/LtlFormula.{h,cc}, src/NeverClaimAutomaton.{h,cc}, + src/PathEvaluator.{h,cc}, src/PathIterator.{h,cc}, + src/ProductAutomaton.{h,cc}, src/SpinWrapper.{h,cc}, + src/StatDisplay.{h,cc}, src/StateSpace.{h,cc}, + src/StateSpaceRandomizer.{h,cc}, src/StringUtil.{h,cc}, + src/TestOperations.{h,cc}, src/TestStatistics.{h,cc}, + src/translate.{h,cc}, src/UserCommandReader.{h,cc}, + src/UserCommands.{h,cc}: Remove all #pragmas. + +2004-02-12 Heikki Tauriainen + + * doc/texinfo.tex: New upstream version. + + * doc/lbtt.texi: Update edition to 1.0.2. + Remove node names with commands to fix dvi file generation. + Move the node about the lbtt-translate utility to correct menu. + Reformat the automata file format section to avoid overfull lines + in dvi generation. + Fix description of the Algorithm block used with lbtt-translate. + + * doc/testprocedure.txt, doc/intersectioncheck.txt: Add initial + newlines. + +2004-02-11 Heikki Tauriainen + + * src/UserCommandReader.cc [HAVE_ISATTY]: Include the unistd.h + header. + (executeUserCommands) Stop waiting for new commands if an EOF is + detected. [HAVE_ISATTY]: If standard input is not connected to a + terminal, echo the input line. + +2004-02-10 Heikki Tauriainen + + * configure.ac: Remove duplicate checks for headers. + (GLIBC_OBSTACK_WORKAROUND): New config.h macro for checking for a + version of the obstack.h header that requires a workaround to be + compiled with g++. + Add check for the `isatty' library function. + Include the stdio.h header when testing for readline libraries. + + * src/Alloc.h: Rename to LbttAlloc.h to avoid a name conflict with + system header files on Darwin. + [GLIBC_OBSTACK_WORKAROUND] (__INT_TO_PTR): Redefine macro to + work around a C++ bug in the obstack.h header file in glibc 2.3.2. + Thanks to Alexandre Duret-Lutz for the original patch. + + * src/BuchiAutomaton.h, src/Configuration.h, src/DispUtil.cc, + src/ExternalTranslator.h, src/FormulaRandomizer.h, src/Graph.h.in, + src/LtlFormula.h, src/main.cc, src/Makefile.am, + src/NeverClaimAutomaton.h, src/PathEvaluator.h, + src/ProductAutomaton.h, src/SccIterator.h, src/SharedTestData.h, + src/StatDisplay.h, src/StateSpace.h, src/StateSpaceRandomizer.cc, + src/StringUtil.h, src/TestOperations.h, src/TestRoundInfo.h, + src/TestStatistics.h, src/UserCommandReader.h, + src/UserCommands.h: Adjust includes. + + * Update copyright information in source files. + +2003-07-04 Alexandre Duret-Lutz + + * src/Config-parse.yy: Remove stray `,' in %token arguments. + 2003-07-18 Heikki Tauriainen * UserCommands.cc (printAutomatonAnalysisResults): Ensure that @@ -7,7 +74,7 @@ state of the witness. * Version 1.0.2 released. - + 2003-07-17 Heikki Tauriainen * NeverClaimAutomaton.cc (ParseErrorException::ParseErrorException): @@ -19,17 +86,17 @@ in previous releases.) 2002-11-04 Heikki Tauriainen - + * StatDisplay.cc (printCollectiveStats): If using more than five formula operators (but the total number of operators is not a multiple of 5), insert an empty line in the output before the last row of the operator distribution table. 2002-10-21 Heikki Tauriainen - + * BitArray.cc (BitArray::find): Fix bug in testing whether all accessed bytes were zero. - + 2002-10-01 Heikki Tauriainen * Version 1.0.1 released. @@ -38,21 +105,21 @@ * Alloc.h: Use preprocessor macro HAVE_SINGLE_CLIENT_ALLOC instead of HAVE_SGI_STL in #ifdef conditionals. - + * BitArray.cc (BitArray::BitArray): Do not clear the allocated array after initialization. All callers updated to reflect the changed constructor semantics. - + * BitArray.cc (BitArray::bitwiseAnd, BitArray::bitwiseOr) (BitArray::bitwiseXor): New functions. * BitArray.cc (BitArray::equal, BitArray::subset) (BitArray::count): Fix `&' operator precedence in comparisons. - + * BitArray.cc (BitArray::hammingDistance): Use the `bit_counts' array to compute the result instead of scanning the array bit by bit. - + * BitArray.cc: Documentation fixes. * BitArray.h (BitArray::bitwiseAnd, BitArray::bitwiseOr) @@ -76,7 +143,7 @@ * BuchiAutomaton.cc: Documentation fixes. - * BuchiAutomaton.h (BuchiAutomaton::regularize): Changed + * BuchiAutomaton.h (BuchiAutomaton::regularize): Changed semantics (see above). * BuchiAutomaton.h @@ -108,14 +175,14 @@ preprocessor macro SLIST_NAMESPACE. * EdgeContainer.h: Remove uses of redundant preprocessor macros. - + * Graph.h: Renamed to Graph.h.in to implement the optional inclusion of the slist header using an autoconf substitution variable. * Graph.h.in: Use HAVE_SLIST macro instead of HAVE_SGI_STL in #ifdef conditionals. - + * Graph.h.in (Graph::Edge, Graph::Node): Make classes public (to prevent warnings from Intel C++ Compiler). @@ -172,7 +239,7 @@ * src/Makefile.am: Remove redundant references to @LEXLIB@ (the sources are independent of any external lexer library). - + * NeverClaimAutomaton.cc (NeverClaimAutomaton::write): Add detection for jumps to undefined never claim labels. @@ -185,7 +252,7 @@ * ProductAutomaton.cc (ProductAutomaton::computeProduct): Avoid creating a temporary Bitset object when checking the enabledness of a product transition. - + * ProductAutomaton.cc (ProductAutomaton::findAcceptingExecution) Use BitArrays instead of Bitsets. @@ -211,12 +278,12 @@ * TestOperations.cc (performEmptinessCheck): Added a colon to the end of the "Accepting cycles" message. - + * TestRoundInfo.h: Removed redundant inclusion of BitArray.h. * translate.cc (main): Use autoconf-generated PACKAGE_VERSION macro for displaying program version. - + * translate.cc (main): Fix bug in checking the number of command line arguments. @@ -227,7 +294,7 @@ against the internal model checking algorithm). * version.h.in: Removed. - + 2001-11-12 Heikki Tauriainen * Version 1.0.0 released. diff --git a/lbtt/NEWS b/lbtt/NEWS index e8c820d71..574e07d73 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,5 +1,5 @@ -lbtt NEWS -- history of user-visible changes. 18 Jul 2003 -Copyright (C) 2003 Heikki Tauriainen +lbtt NEWS -- history of user-visible changes. 13 Feb 2004 +Copyright (C) 2004 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the @@ -12,6 +12,14 @@ Copyright (C) 2003 Heikki Tauriainen Please send bug reports to . +Version 1.0.3 + +* This release fixes several compilation issues with GNU libc 2.3.2 + and Darwin, and documentation generation in dvi format. A problem + with reading user commands from a source that is not a terminal was + also fixed. Many thanks to Alexandre Duret-Lutz for patches and + useful suggestions. + Version 1.0.2 * Bug fix release. diff --git a/lbtt/README b/lbtt/README index 3096ee211..ad585e237 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,4 +1,4 @@ -lbtt version 1.0.2 +lbtt version 1.0.3 ------------------ lbtt is a tool for testing programs that translate formulas diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 6dec5b8a0..14c5827b7 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,8 +1,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.53]) -AC_INIT([lbtt], [1.0.2], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.2]) +AC_INIT([lbtt], [1.0.3], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.3]) AC_CONFIG_SRCDIR([src/main.cc]) AM_INIT_AUTOMAKE AM_CONFIG_HEADER([config.h]) @@ -37,7 +37,7 @@ AC_ARG_WITH([readline], # Check for the availability of headers. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h obstack.h stdlib.h unistd.h]) +AC_CHECK_HEADERS([fcntl.h]) # Check for the availability of the GNU readline headers. @@ -51,6 +51,22 @@ fi AC_LANG([C++]) +# Check for the availability of the obstack.h header. GNU libc 2.3.2 includes a +# version of this header that cannot be compiled using g++; enable a workaround +# if necessary. + +AC_CHECK_HEADERS([obstack.h], + [AC_MSG_CHECKING([whether obstack.h compilation workaround is needed]) + AC_TRY_COMPILE([#include ], + [#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 3 + obstack_alloc(0, 0); + #endif], + [AC_MSG_RESULT([no])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([GLIBC_OBSTACK_WORKAROUND], + [1], + [Define to 1 to enable an obstack.h C++ compilation workaround for GNU libc 2.3.])])]) + # Check for the availablility of the sstream or strstream header. AC_CHECK_HEADERS([sstream], @@ -131,11 +147,11 @@ AC_C_INLINE # Checks for library functions. AC_TYPE_SIGNAL -AC_CHECK_FUNCS([mkdir strchr strtod strtol strtoul getopt_long]) +AC_CHECK_FUNCS([mkdir strchr strtod strtol strtoul getopt_long isatty]) if test x"${ac_cv_func_getopt_long}" = xno; then AC_LIBOBJ([getopt]) AC_LIBOBJ([getopt1]) - AC_CHECK_HEADERS([libintl.h string.h strings.h]) + AC_CHECK_HEADERS([libintl.h]) AC_CHECK_FUNCS([memset]) fi @@ -147,7 +163,8 @@ if test "${readline}" = yes; then for READLINELIBS in "-lreadline" "-lreadline -lcurses" "-lreadline -ltermcap" error; do if test "${READLINELIBS}" != error; then LIBS="${oldlibs} ${READLINELIBS}" - AC_TRY_LINK([#include <${rl_history_h}> + AC_TRY_LINK([#include + #include <${rl_history_h}> #include <${rl_readline_h}>], [using_history(); readline(""); add_history("");], [break]) diff --git a/lbtt/doc/intersectioncheck.txt b/lbtt/doc/intersectioncheck.txt index 405f12dce..c2b7fa447 100644 --- a/lbtt/doc/intersectioncheck.txt +++ b/lbtt/doc/intersectioncheck.txt @@ -1,3 +1,4 @@ + ,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,, : LTL formula `f' :_____ : Negated LTL formula `!f' : '''''''T''''''T'''' \ ___'''''''T'''''''''''T'''''''' diff --git a/lbtt/doc/lbtt.texi b/lbtt/doc/lbtt.texi index e70fe4455..ae5b65b91 100644 --- a/lbtt/doc/lbtt.texi +++ b/lbtt/doc/lbtt.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header @setfilename lbtt.info -@settitle @command{lbtt} +@settitle @command{lbtt} @afourpaper @c %**end of header @@ -13,8 +13,8 @@ @end ifhtml This file documents how to use the LTL-to-B@"uchi translator testbench @command{lbtt}. - -Copyright @copyright{} 2003 Heikki Tauriainen + +Copyright @copyright{} 2004 Heikki Tauriainen @ifinfo @email{heikki.tauriainen@@hut.fi} @end ifinfo @@ -24,7 +24,7 @@ Copyright @copyright{} 2003 Heikki Tauriainen @end ifnothtml @end ifnotinfo -@ifhtml +@ifhtml @html
@end html @@ -32,7 +32,7 @@ Copyright @copyright{} 2003 Heikki Tauriainen Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. - + @ignore Permission is granted to process this file through TeX and print the results, provided the printed document @@ -48,7 +48,7 @@ entitled ``GNU General Public License'' is included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. - + Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions. @@ -68,7 +68,7 @@ under the above conditions for modified versions. @author Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> @page @vskip 0pt plus 1filll -Copyright @copyright{} 2003 Heikki Tauriainen +Copyright @copyright{} 2004 Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> The latest version of this manual can be obtained from@* @@ -77,7 +77,7 @@ The latest version of this manual can be obtained from@* Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. - + Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section @@ -86,7 +86,7 @@ is included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. - + Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions. @@ -103,7 +103,7 @@ under the above conditions for modified versions. for translating propositional linear temporal logic formulas into B@"uchi automata. -This is edition 1.0.1 of the @command{lbtt} documentation. This edition +This is edition 1.0.2 of the @command{lbtt} documentation. This edition applies to @command{lbtt} versions 1.0.x. @command{lbtt} is free software, you may change and redistribute it @@ -122,10 +122,8 @@ comes with NO WARRANTY. See @ref{Copying} for details. * Analyzing test results:: Working with @command{lbtt}'s internal commands. -* Interfacing with @command{lbtt}:: Interfacing LTL-to-B@"uchi translators +* Interfacing with lbtt:: Interfacing LTL-to-B@"uchi translators with @command{lbtt}. -* The @command{lbtt-translate} utility:: An interface for two LTL-to-B@"uchi - translators. * References:: List of references. @@ -271,7 +269,7 @@ for more information. formulas in the same state space using an LTL-to-B@"uchi translator should give consistent results. -* B@"uchi automata intersection emptiness check:: +* Automata intersection emptiness check:: The intersection of the languages accepted by two B@"uchi automata constructed from two complementary @@ -811,7 +809,7 @@ $$ @end ifnottex @noindent -where +where @iftex @tex $\it{op}'$ @@ -1129,16 +1127,9 @@ and the model checking result consistency check on the model checking results, and reports all detected failures. The B@"uchi automata intersection emptiness check -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}) -@end ifnottex -@iftex -(@pxref{Automata intersection emptiness check}) -@end iftex -operates as follows -(note that the LTL-to-B@"uchi translation phase is repeated in this figure -only for completeness; in reality, @command{lbtt} performs this phase only -once): +(@pxref{Automata intersection emptiness check}) operates as follows (note that +the LTL-to-B@"uchi translation phase is repeated in this figure only for +completeness; in reality, @command{lbtt} performs this phase only once): @ifhtml @* @end ifhtml @@ -1207,7 +1198,7 @@ providing an additional implementation to include in the tests. -@node Model checking result consistency check, B@"uchi automata intersection emptiness check, Model checking result cross-comparison test, Test methods +@node Model checking result consistency check, Automata intersection emptiness check, Model checking result cross-comparison test, Test methods @section Model checking result consistency check @cindex model checking result consistency check @@ -1217,7 +1208,7 @@ LTL model checking tells whether any of the infinite paths starting from some state of a state space satisfies a given LTL formula. If there are no such paths beginning from the state, it follows that all infinite paths beginning from the state must then satisfy the @emph{negation} of the same -formula. Since all state spaces used by @command{lbtt} always have at least +formula. Since all state spaces used by @command{lbtt} always have at least one path beginning from each state of the state space (guaranteed by the state space generation algorithms), at least one path beginning from any state must satisfy either the formula or its @@ -1245,9 +1236,8 @@ section. @end iftex -@node B@"uchi automata intersection emptiness check, , Model checking result consistency check, Test methods -@section B@"uchi automata intersection emptiness check -@anchor{Automata intersection emptiness check} +@node Automata intersection emptiness check, , Model checking result consistency check, Test methods +@section Automata intersection emptiness check @cindex B@"uchi automata intersection emptiness check @cindex tests, B@"uchi automata intersection emptiness check @@ -1361,21 +1351,21 @@ section specifying an LTL-to-B@"uchi translator. The other sections are optional and can be used to override the default testing parameters. @menu -* @samp{Algorithm} section:: Each LTL-to-B@"uchi translator to be +* Algorithm section:: Each LTL-to-B@"uchi translator to be tested requires a separate @samp{Algorithm} section in the configuration file. -* @samp{GlobalOptions} section:: Options for changing the general +* GlobalOptions section:: Options for changing the general behavior of @command{lbtt}. -* @samp{FormulaOptions} section:: Options controlling the way random +* FormulaOptions section:: Options controlling the way random LTL formulas are generated. -* @samp{StateSpaceOptions} section:: Options controlling the way random +* StateSpaceOptions section:: Options controlling the way random state spaces are generated. * Sample configuration file:: An example of a configuration file. @end menu -@node @samp{Algorithm} section, @samp{GlobalOptions} section, Configuration file, Configuration file +@node Algorithm section, GlobalOptions section, Configuration file, Configuration file @subsection The @samp{Algorithm} section @cindex configuration file, @samp{Algorithm} section @@ -1439,7 +1429,7 @@ which is used to run the translator. -@node @samp{GlobalOptions} section, @samp{FormulaOptions} section, @samp{Algorithm} section, Configuration file +@node GlobalOptions section, FormulaOptions section, Algorithm section, Configuration file @subsection The @samp{GlobalOptions} section @cindex configuration file, @samp{GlobalOption} section @@ -1488,14 +1478,8 @@ The default value for this option is @samp{Always}. @findex IntersectionCheck @r{[}GlobalOptions@r{]} @findex IntersectionTest @r{[}GlobalOptions@r{]} This option can be used to enable or disable the B@"uchi automata intersection -emptiness check -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}). -@end ifnottex -@iftex -(@pxref{Automata intersection emptiness check}). -@end iftex -The test is enabled by default. +emptiness check (@pxref{Automata intersection emptiness check}). The test is +enabled by default. @item ModelCheck = Local @r{|} Global @findex ModelCheck @r{[}GlobalOptions@r{]} @@ -1531,7 +1515,7 @@ the value results in more output. The default value is 3. -@node @samp{FormulaOptions} section, @samp{StateSpaceOptions} section, @samp{GlobalOptions} section, Configuration file +@node FormulaOptions section, StateSpaceOptions section, GlobalOptions section, Configuration file @subsection The @samp{FormulaOptions} section @cindex configuration file, @samp{FormulaOptions} section @@ -1796,7 +1780,7 @@ option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) -@node @samp{StateSpaceOptions} section, Sample configuration file, @samp{FormulaOptions} section, Configuration file +@node StateSpaceOptions section, Sample configuration file, FormulaOptions section, Configuration file @subsection The @samp{StateSpaceOptions} section @cindex configuration file, @samp{StateSpaceOptions} section @@ -1867,12 +1851,7 @@ the generated state spaces. The default value is 5. Usually this should probably be the same as the maximum number of different atomic propositions in the generated formulas -@ifnottex -(@pxref{@samp{FormulaOptions} section}). -@end ifnottex -@iftex -(see the previous section). -@end iftex +(@pxref{FormulaOptions section}). If the number of propositions attached to each state of the state spaces is less than the maximum number of different propositions that may occur in the generated formulas, all ``extra'' propositions in the formulas @@ -1916,7 +1895,7 @@ in any state of the state space. Note: This option has no effect if @end table -@node Sample configuration file, , @samp{StateSpaceOptions} section, Configuration file +@node Sample configuration file, , StateSpaceOptions section, Configuration file @subsection Sample configuration file @cindex configuration file, example @@ -2211,13 +2190,7 @@ between test rounds to wait for user input. @vindex --intersectiontest @vindex --nointersectiontest These options enable or disable the B@"uchi automata intersection emptiness -check -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}). -@end ifnottex -@iftex -(@pxref{Automata intersection emptiness check}). -@end iftex +check (@pxref{Automata intersection emptiness check}). @item --localmodelcheck @vindex --localmodelcheck @@ -2545,7 +2518,7 @@ This option can be used to change the size of the generated state spaces. @vindex --truthprobability This option sets the probability that @command{lbtt} uses for choosing the valuation for each atomic proposition in each state of the randomly generated -state spaces. (This option has no effect is using enumerated paths as state +state spaces. (This option has no effect if using enumerated paths as state spaces.) @end table @@ -2643,8 +2616,8 @@ Program configuration: Atomic symbols in use (priority): false (5); propositions (90); true (5) Operators used for random LTL formula generation: - operator ! /\ U V X \/ - priority 10 20 20 20 10 20 + operator ! /\ U V X \/ + priority 10 20 20 20 10 20 @end smallexample @node Test round messages, Test statistics, Configuration information, Interpreting the output @@ -2753,12 +2726,7 @@ The output of phases 4---8 will be repeated for each implementation included in the tests. After this @command{lbtt} proceeds to the model checking result cross-comparison test (@pxref{Model checking result cross-comparison test}) and the B@"uchi automata intersection emptiness test -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}). -@end ifnottex -@iftex (@pxref{Automata intersection emptiness check}). -@end iftex The model checking result cross-comparison test might result in the following output: @@ -2886,13 +2854,8 @@ state of the state space). @item Number of failures in the B@"uchi automata intersection emptiness check -@iftex -(@pxref{Automata intersection emptiness check}) -@end iftex -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}) -@end ifnottex -for each pair of implementations. +(@pxref{Automata intersection emptiness check}) for each pair of +implementations. @end itemize Note that the pairwise inconsistency results form a symmetric matrix (although @@ -2908,7 +2871,7 @@ and all LTL formulas used in the tests. -@node Analyzing test results, Interfacing with @command{lbtt}, Interpreting the output, Top +@node Analyzing test results, Interfacing with lbtt, Interpreting the output, Top @chapter Analyzing test results This chapter documents how to use @command{lbtt}'s internal commands to @@ -2921,7 +2884,7 @@ case a test failure is detected) between test rounds to wait for user input by showing a prompt of the form @smallexample - ** [Round 22 of 1000] >> + ** [Round 22 of 1000] >> @end smallexample @menu @@ -3217,12 +3180,7 @@ automata correctness tests. The second part describes the conventions that @cindex failure analysis, B@"uchi automata intersection check @cindex analyzing test failures, B@"uchi automata intersection emptiness check Analyze a failure in the B@"uchi automata intersection emptiness check -@ifnottex -(@pxref{B@"uchi automata intersection emptiness check}). -@end ifnottex -@iftex (@pxref{Automata intersection emptiness check}). -@end iftex The two implementation identifiers select the B@"uchi automata for which to perform the analysis. The B@"uchi automata intersection emptiness check always involves automata constructed from the positive and the negative @@ -3463,7 +3421,7 @@ which can be seen from the proof. -@node Interfacing with @command{lbtt}, The @command{lbtt-translate} utility, Analyzing test results, Top +@node Interfacing with lbtt, References, Analyzing test results, Top @chapter Interfacing LTL-to-B@"uchi translators with @command{lbtt} @cindex LTL-to-B@"uchi translators, interfacing with @@ -3472,12 +3430,7 @@ which can be seen from the proof. This chapter gives the details on how to use @command{lbtt} for testing LTL-to-B@"uchi translation algorithm implementations that are not supported by the basic distribution. (See -@ifnottex -@ref{The @command{lbtt-translate} utility} -@end ifnottex -@iftex -the next chapter -@end iftex +@ref{The lbtt-translate utility} for information on how to connect several publicly available LTL-to-B@"uchi translator implementations to @command{lbtt}.) @@ -3486,12 +3439,14 @@ LTL-to-B@"uchi translator implementations to @command{lbtt}.) LTL-to-B@"uchi translator. * Format for LTL formulas:: How @command{lbtt} passes LTL formulas to the translators. -* B@"uchi automata:: How @command{lbtt} expects the translators +* Format for automata:: How @command{lbtt} expects the translators to present their output. +* The lbtt-translate utility:: An interface for two LTL-to-B@"uchi + translators. @end menu -@node Translator interface, Format for LTL formulas, Interfacing with @command{lbtt}, Interfacing with @command{lbtt} +@node Translator interface, Format for LTL formulas, Interfacing with lbtt, Interfacing with lbtt @section Translator interface requirements @cindex LTL-to-B@"uchi translators, interface requirements @@ -3514,7 +3469,7 @@ The translator executable should read its input (an LTL formula) from @var{input-file} and write its output (a B@"uchi automaton) into @var{output-file} (without removing the input file); see @ifnottex -@ref{Format for LTL formulas} and @ref{B@"uchi automata} +@ref{Format for LTL formulas} and @ref{Format for automata} @end ifnottex @iftex the following two sections @@ -3542,7 +3497,7 @@ Algorithm -@node Format for LTL formulas, B@"uchi automata, Translator interface, Interfacing with @command{lbtt} +@node Format for LTL formulas, Format for automata, Translator interface, Interfacing with lbtt @section Input file format for LTL formulas @cindex LTL-to-B@"uchi translators, LTL formula input file format @@ -3626,8 +3581,8 @@ line options (@pxref{Command line options}) to prevent -@node B@"uchi automata, , Format for LTL formulas, Interfacing with @command{lbtt} -@section Output file format for B@"uchi automata +@node Format for automata, The lbtt-translate utility, Format for LTL formulas, Interfacing with lbtt +@section Output file format for automata @cindex file formats, LTL-to-B@"uchi translator output file @cindex LTL-to-B@"uchi translators, automaton output file format @@ -3644,23 +3599,23 @@ The output file generated by the translator should contain an @var{automaton} described using the following grammar: @smallexample -@var{automaton} @r{::=} @var{number-of-states} @var{sp} @var{number-of-acceptance-conditions} @var{states} +@var{automaton} @r{::=} @var{num-states} @var{sp} @var{num-conds} @var{states} -@var{number-of-states} @r{::=} @r{[}0@r{---}9@r{]+} +@var{num-states} @r{::=} @r{[}0@r{---}9@r{]+} -@var{number-of-acceptance-conditions} @r{::=} @r{[}0@r{---}9@r{]+} +@var{num-conds} @r{::=} @r{[}0@r{---}9@r{]+} @var{states} @r{::=} @var{states} @var{sp} @var{state} - @r{|} @r{// empty} + @r{|} @r{// empty} -@var{state} @r{::=} @var{state-id} @var{sp} @var{initial?} @var{acceptance-conditions} @var{sp} `-1' @var{transitions} @var{sp} `-1' +@var{state} @r{::=} @var{state-id} @var{sp} @var{initial?} @var{conds} @var{sp} `-1' @var{transitions} @var{sp} `-1' @var{state-id} @r{::=} @r{[}0@r{---}9@r{]+} @var{initial?} @r{::=} `0' @r{|} `1' -@var{acceptance-conditions} @r{::=} @var{acceptance-conditions} @var{sp} @var{acceptance-set-id} - @r{|} @r{// empty} +@var{conds} @r{::=} @var{conds} @var{sp} @var{acceptance-set-id} + @r{|} @r{// empty} @var{acceptance-set-id} @r{::=} @r{[}0@r{---}9@r{]+} @@ -3714,7 +3669,7 @@ The state and acceptance condition identifiers need not be successive, and the states or acceptance conditions can be listed in any order. The only restrictions are that the identifiers of different states and acceptance conditions should be unique and that the total number of different identifiers -should equal @var{number-of-states} or @var{number-of-acceptance-conditions}, +should equal @var{num-states} or @var{num-conds}, respectively. (The same identifiers can be shared between states and acceptance conditions, however.) @@ -3778,7 +3733,7 @@ The following example illustrates the file format. -@node The @command{lbtt-translate} utility, References, Interfacing with @command{lbtt}, Top +@node The lbtt-translate utility, , Format for automata, Interfacing with lbtt @section The @command{lbtt-translate} utility @cindex @command{lbtt-translate} (executable file) @@ -3847,17 +3802,18 @@ installation instructions. Then add the following @samp{Algorithm} section in @smallexample Algorithm @{ - Name = "@var{name for the implementation}" - Path = "@var{path-to-@command{lbtt-translate}} @var{implementation-selector} @var{path-to-executable}" + Name = "@r{[@var{name for the implementation}]}" + Path = "@r{[@var{path-to-@command{lbtt-translate}}]}" + Parameters = "@r{[@var{implementation selector}]} @r{[@var{path to executable}]}" Enabled = Yes @} @end smallexample @noindent -where @var{path-to-@command{lbtt-translate}} contains the complete path and +where [@var{path-to-@command{lbtt-translate}}] contains the complete path and file name of the @command{lbtt-translate} tool executable, -@var{implementation-selector} is either of the options @samp{--lbt} or -@samp{--spin}, and @var{path-to-executable} is the full path +[@var{implementation selector}] is either of the options @samp{--lbt} or +@samp{--spin}, and [@var{path to executable}] is the full path of the tool executable. The names of these executables are usually (assuming a normal installation) @command{lbt} and @command{spin}, respectively. @@ -3875,7 +3831,7 @@ see a short summary of available options. -@node References, Definitions, The @command{lbtt-translate} utility, Top +@node References, Definitions, Interfacing with lbtt, Top @unnumbered References @table @asis @@ -3884,7 +3840,7 @@ E.@: Clarke Jr., O.@: Grumberg and D.@: Peled. Model checking. The MIT Press, 1999. @item @anchor{[Cou99]} [Cou99] -J-M. Couvreur. On-the-fly verification of linear temporal logic. In +J.-M. Couvreur. On-the-fly verification of linear temporal logic. In @i{Proceedings of the World Congress on Formal Methods in the Development of Computing Systems (FM'99), volume I}, volume 1708 of @i{Lecture Notes in Computer Science}, pages 253---271. Springer-Verlag, 1999. @@ -3892,7 +3848,7 @@ Computing Systems (FM'99), volume I}, volume 1708 of @item @anchor{[DGV99]} [DGV99] M.@: Daniele, F.@: Giunchiglia and M.@: Y.@: Vardi. Improved automata generation for linear temporal logic. In @i{Proceedings of the 11th -International Conference on Computer Aided Verification (CAV'99)}, volume 1633 +International Conference on Computer Aided Verification (CAV'99)}, volume 1633 of @i{Lecture Notes in Computer Science}, pages 249---260. Springer-Verlag, 1999. @@ -4001,7 +3957,7 @@ manipulates. * LTL formulas:: @command{lbtt} uses traditional semantics for propositional linear temporal logic. -* Generalized B@"uchi automata:: The B@"uchi automata used by @command{lbtt} +* Generalized automata:: The B@"uchi automata used by @command{lbtt} have one initial state, labels on transitions and zero or more acceptance conditions. @@ -4010,7 +3966,7 @@ manipulates. @end menu -@node LTL formulas, Generalized B@"uchi automata, Definitions, Definitions +@node LTL formulas, Generalized automata, Definitions, Definitions @appendixsec LTL formulas @command{lbtt} uses the traditional definition for propositional linear @@ -4390,8 +4346,8 @@ $(\varphi\;{\bf B}\;\psi) \equiv_{\rm def} \neg(\neg\varphi\;\bf{U}\;\psi)$ -@node Generalized B@"uchi automata, State spaces, LTL formulas, Definitions -@appendixsec Generalized B@"uchi automata +@node Generalized automata, State spaces, LTL formulas, Definitions +@appendixsec Generalized automata @cindex B@"uchi automata, formal definition @cindex generalized B@"uchi automata, formal definition @@ -4410,7 +4366,7 @@ $2^{AP}$ with one initial state, labels on transitions and zero or more acceptance conditions. -@appendixsubsec Formal definition of generalized B@"uchi automata +@appendixsubsec Formal definition of generalized automata Formally, a generalized B@"uchi automaton can be represented as a tuple @iftex @@ -4575,7 +4531,7 @@ $Q$, @end ifnottex the automaton may have many runs on the same input. -A run +A run @iftex @tex $\langle q_0, q_1, q_2, \ldots\rangle$ @@ -4699,7 +4655,7 @@ of some states and then adjusting the transition labels appropriately. -@node State spaces, , Generalized B@"uchi automata, Definitions +@node State spaces, , Generalized automata, Definitions @appendixsec State spaces @cindex state space, formal definition diff --git a/lbtt/doc/testprocedure.txt b/lbtt/doc/testprocedure.txt index ec0b1d45e..de2e7edc4 100644 --- a/lbtt/doc/testprocedure.txt +++ b/lbtt/doc/testprocedure.txt @@ -1,3 +1,4 @@ + ,,,,,,,,,,,,,,, : State space : ''''''''''''''' diff --git a/lbtt/doc/texinfo.tex b/lbtt/doc/texinfo.tex index 9dffef421..6e4d791f2 100644 --- a/lbtt/doc/texinfo.tex +++ b/lbtt/doc/texinfo.tex @@ -3,10 +3,11 @@ % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % -\def\texinfoversion{2001-05-24.08} +\def\texinfoversion{2004-02-11.16} % -% Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, -% 2000, 01 Free Software Foundation, Inc. +% Copyright (C) 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software +% Foundation, Inc. % % This texinfo.tex file is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License as @@ -23,21 +24,17 @@ % to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, % Boston, MA 02111-1307, USA. % -% In other words, you are welcome to use, share and improve this program. -% You are forbidden to forbid anyone else to use, share and improve -% what you give them. Help stamp out software-hoarding! -% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% % Please try the latest version of texinfo.tex before submitting bug % reports; you can get the latest version from: -% ftp://ftp.gnu.org/gnu/texinfo.tex -% (and all GNU mirrors, see http://www.gnu.org/order/ftp.html) -% ftp://texinfo.org/tex/texinfo.tex -% ftp://us.ctan.org/macros/texinfo/texinfo.tex -% (and all CTAN mirrors, finger ctan@us.ctan.org for a list). -% /home/gd/gnu/doc/texinfo.tex on the GNU machines. -% The texinfo.tex in any given Texinfo distribution could well be out +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out % of date, so if that's what you're using, please check. -% Texinfo has a small home page at http://texinfo.org/. % % Send bug reports to bug-texinfo@gnu.org. Please include including a % complete document in each bug report with which we can reproduce the @@ -50,13 +47,17 @@ % texindex foo.?? % tex foo.texi % tex foo.texi -% dvips foo.dvi -o # or whatever, to process the dvi file; this makes foo.ps. -% The extra runs of TeX get the cross-reference information correct. +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. % Sometimes one run after texindex suffices, and sometimes you need more % than two; texi2dvi does it as many times as necessary. % -% It is possible to adapt texinfo.tex for other languages. You can get -% the existing language-specific files from ftp://ftp.gnu.org/gnu/texinfo/. +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + \message{Loading texinfo [version \texinfoversion]:} @@ -66,7 +67,14 @@ \everyjob{\message{[Texinfo version \texinfoversion]}% \catcode`+=\active \catcode`\_=\active} -% Save some parts of plain tex whose names we will redefine. +\message{Basics,} +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. \let\ptexb=\b \let\ptexbullet=\bullet \let\ptexc=\c @@ -76,23 +84,34 @@ \let\ptexend=\end \let\ptexequiv=\equiv \let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ \let\ptexi=\i +\let\ptexindent=\indent +\let\ptexnoindent=\noindent +\let\ptexinsert=\insert \let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexplus=+ \let\ptexrbrace=\} +\let\ptexslash=\/ \let\ptexstar=\* \let\ptext=\t -% We never want plain's outer \+ definition in Texinfo. -% For @tex, we can use \tabalign. -\let\+ = \relax - -\message{Basics,} -\chardef\other=12 - % If this character appears in an error message or help string, it % starts a new line in the output. \newlinechar = `^^J +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + % Set up fixed words for English if not already set. \ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi \ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi @@ -131,48 +150,106 @@ \ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi \ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi \ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi -\ifx\putwordDeftypevar\undefined\gdef\putwordDeftypevar{Variable}\fi \ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi -\ifx\putwordDeftypefun\undefined\gdef\putwordDeftypefun{Function}\fi + +% In some macros, we cannot use the `\? notation---the left quote is +% in some cases the escape char. +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\questChar = `\? +\chardef\semiChar = `\; +\chardef\underChar = `\_ + +\chardef\spaceChar = `\ % +\chardef\spacecat = 10 +\def\spaceisspace{\catcode\spaceChar=\spacecat} % Ignore a token. % \def\gobble#1{} +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. \hyphenation{ap-pen-dix} -\hyphenation{mini-buf-fer mini-buf-fers} \hyphenation{eshell} +\hyphenation{mini-buf-fer mini-buf-fers} +\hyphenation{time-stamp} \hyphenation{white-space} % Margin to add to right of even pages, to left of odd pages. -\newdimen \bindingoffset -\newdimen \normaloffset +\newdimen\bindingoffset +\newdimen\normaloffset \newdimen\pagewidth \newdimen\pageheight +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + % Sometimes it is convenient to have everything in the transcript file % and nothing on the terminal. We don't just call \tracingall here, -% since that produces some useless output on the terminal. +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. % \def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% -\ifx\eTeXversion\undefined -\def\loggingall{\tracingcommands2 \tracingstats2 - \tracingpages1 \tracingoutput1 \tracinglostchars1 - \tracingmacros2 \tracingparagraphs1 \tracingrestores1 - \showboxbreadth\maxdimen\showboxdepth\maxdimen +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\undefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 }% -\else -\def\loggingall{\tracingcommands3 \tracingstats2 - \tracingpages1 \tracingoutput1 \tracinglostchars1 - \tracingmacros2 \tracingparagraphs1 \tracingrestores1 - \tracingscantokens1 \tracingassigns1 \tracingifs1 - \tracinggroups1 \tracingnesting2 - \showboxbreadth\maxdimen\showboxdepth\maxdimen -}% -\fi % add check for \lastpenalty to plain's definitions. If the last thing % we did was a \nobreak, we don't want to insert more space. -% +% \def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount \removelastskip\penalty-50\smallskip\fi\fi} \def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount @@ -225,7 +302,7 @@ % the page break happens to be in the middle of an example. \shipout\vbox{% % Do this early so pdf references go to the beginning of the page. - \ifpdfmakepagedest \pdfmkdest{\the\pageno} \fi + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi % \ifcropmarks \vbox to \outervsize\bgroup \hsize = \outerhsize @@ -273,7 +350,7 @@ \egroup % \vbox from first cropmarks clause \fi }% end of \shipout\vbox - }% end of group with \turnoffactive + }% end of group with \normalturnoffactive \advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi } @@ -306,143 +383,162 @@ % the input line (except we remove a trailing comment). #1 should be a % macro which expects an ordinary undelimited TeX argument. % -\def\parsearg#1{% - \let\next = #1% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\next{#2}% \begingroup \obeylines - \futurelet\temp\parseargx + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. } -% If the next token is an obeyed space (from an @example environment or -% the like), remove it and recurse. Otherwise, we're done. -\def\parseargx{% - % \obeyedspace is defined far below, after the definition of \sepspaces. - \ifx\obeyedspace\temp - \expandafter\parseargdiscardspace - \else - \expandafter\parseargline - \fi -} - -% Remove a single space (as the delimiter token to the macro call). -{\obeyspaces % - \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} - {\obeylines % \gdef\parseargline#1^^M{% \endgroup % End of the group started in \parsearg. - % - % First remove any @c comment, then any @comment. - % Result of each macro is put in \toks0. - \argremovec #1\c\relax % - \expandafter\argremovecomment \the\toks0 \comment\relax % - % - % Call the caller's macro, saved as \next in \parsearg. - \expandafter\next\expandafter{\the\toks0}% + \argremovecomment #1\comment\ArgTerm% }% } -% Since all \c{,omment} does is throw away the argument, we can let TeX -% do that for us. The \relax here is matched by the \relax in the call -% in \parseargline; it could be more or less anything, its purpose is -% just to delimit the argument to the \c. -\def\argremovec#1\c#2\relax{\toks0 = {#1}} -\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} -% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% Each occurence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., % @end itemize @c foo -% will have two active spaces as part of the argument with the -% `itemize'. Here we remove all active spaces from #1, and assign the -% result to \toks0. +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. % -% This loses if there are any *other* active characters besides spaces -% in the argument -- _ ^ +, for example -- since they get expanded. -% Fortunately, Texinfo does not define any such commands. (If it ever -% does, the catcode of the characters in questionwill have to be changed -% here.) But this means we cannot call \removeactivespaces as part of -% \argremovec{,omment}, since @c uses \parsearg, and thus the argument -% that \parsearg gets might well have any character at all in it. -% -\def\removeactivespaces#1{% - \begingroup - \ignoreactivespaces - \edef\temp{#1}% - \global\toks0 = \expandafter{\temp}% - \endgroup +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % We cannot use \next here, as it holds the macro to run; + % thus we reuse \temp. + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm } -% Change the active space to expand to nothing. +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \next. +% (Similarily, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. % -\begingroup +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\next\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ \obeyspaces - \gdef\ignoreactivespaces{\obeyspaces\let =\empty} -\endgroup + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} \def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} -%% These are used to keep @begin/@end levels from running away -%% Call \inENV within environments (after a \begingroup) -\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} -\def\ENVcheck{% -\ifENV\errmessage{Still within an environment; press RETURN to continue} -\endgroup\fi} % This is not perfect, but it should reduce lossage +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as enviroments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) -% @begin foo is the same as @foo, for now. -\newhelp\EMsimple{Press RETURN to continue.} -\outer\def\begin{\parsearg\beginxxx} +% At runtime, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty -\def\beginxxx #1{% -\expandafter\ifx\csname #1\endcsname\relax -{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else -\csname #1\endcsname\fi} +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} -% @end foo executes the definition of \Efoo. -% -\def\end{\parsearg\endxxx} -\def\endxxx #1{% - \removeactivespaces{#1}% - \edef\endthing{\the\toks0}% - % - \expandafter\ifx\csname E\endthing\endcsname\relax - \expandafter\ifx\csname \endthing\endcsname\relax - % There's no \foo, i.e., no ``environment'' foo. - \errhelp = \EMsimple - \errmessage{Undefined command `@end \endthing'}% - \else - \unmatchedenderror\endthing - \fi +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp \else - % Everything's ok; the right environment has been started. - \csname E\endthing\endcsname + \badenverr \fi } -% There is an environment #1, but it hasn't been started. Give an error. -% -\def\unmatchedenderror#1{% +% Evironment mismatch, #1 expected: +\def\badenverr{% \errhelp = \EMsimple - \errmessage{This `@end #1' doesn't have a matching `@#1'}% + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + out of any environment% + \else + in environment \expandafter\string#1% + \fi } -% Define the control sequence \E#1 to give an unmatched @end error. +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv % -\def\defineunmatchedend#1{% - \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal, but... --kasal, 06nov03 + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi } +\newhelp\EMsimple{Press RETURN to continue.} -% Single-spacing is done by various environments (specifically, in -% \nonfillstart and \quotations). -\newskip\singlespaceskip \singlespaceskip = 12.5pt -\def\singlespace{% - % Why was this kern here? It messes up equalizing space above and below - % environments. --karl, 6may93 - %{\advance \baselineskip by -\singlespaceskip - %\kern \baselineskip}% - \setleading \singlespaceskip -} %% Simple single-character @ commands @@ -463,16 +559,22 @@ \let\{=\mylbrace \let\}=\myrbrace \begingroup - % Definitions to produce actual \{ & \} command in an index. - \catcode`\{ = 12 \catcode`\} = 12 + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux file. + \catcode`\{ = \other \catcode`\} = \other \catcode`\[ = 1 \catcode`\] = 2 - \catcode`\@ = 0 \catcode`\\ = 12 - @gdef@lbracecmd[\{]% - @gdef@rbracecmd[\}]% -@endgroup + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , % Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent -% Others are defined by plain TeX: @` @' @" @^ @~ @= @v @H. +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. \let\, = \c \let\dotaccent = \. \def\ringaccent#1{{\accent23 #1}} @@ -480,10 +582,12 @@ \let\ubaraccent = \b \let\udotaccent = \d -% Other special characters: @questiondown @exclamdown -% Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss. +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. \def\questiondown{?`} \def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} % Dotless i and dotless j, used for accents. \def\imacro{i} @@ -496,6 +600,25 @@ \fi\fi } +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=3000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{\selectfonts\lllsize A}\vss}}% + \kern-.15em + \TeX +} + % Be sure we're in horizontal mode when doing a tie, since we make space % equivalent to this in @example-like environments. Otherwise, a space % at the beginning of a line will start with \penalty -- and @@ -514,6 +637,9 @@ % @* forces a line break. \def\*{\hfil\break\hbox{}\ignorespaces} +% @/ allows a line break. +\let\/=\allowbreak + % @. is an end-of-sentence period. \def\.{.\spacefactor=3000 } @@ -536,47 +662,24 @@ % therefore, no glue is inserted, and the space between the headline and % the text is small, which looks bad. % -\def\group{\begingroup - \ifnum\catcode13=\active \else +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else \errhelp = \groupinvalidhelp \errmessage{@group invalid in context where filling is enabled}% \fi + \startsavinginserts % - % The \vtop we start below produces a box with normal height and large - % depth; thus, TeX puts \baselineskip glue before it, and (when the - % next line of text is done) \lineskip glue after it. (See p.82 of - % the TeXbook.) Thus, space below is not quite equal to space - % above. But it's pretty close. - \def\Egroup{% - \egroup % End the \vtop. - \endgroup % End the \group. - }% - % - \vtop\bgroup - % We have to put a strut on the last line in case the @group is in - % the midst of an example, rather than completely enclosing it. - % Otherwise, the interline space between the last line of the group - % and the first line afterwards is too small. But we can't put the - % strut in \Egroup, since there it would be on a line by itself. - % Hence this just inserts a strut at the beginning of each line. - \everypar = {\strut}% - % - % Since we have a strut on every line, we don't need any of TeX's - % normal interline spacing. - \offinterlineskip - % - % OK, but now we have to do something about blank - % lines in the input in @example-like environments, which normally - % just turn into \lisppar, which will insert no space now that we've - % turned off the interline space. Simplest is to make them be an - % empty paragraph. - \ifx\par\lisppar - \edef\par{\leavevmode \par}% - % - % Reset ^^M's definition to new definition of \par. - \obeylines - \fi - % + \setbox\groupbox = \vtop\bgroup % Do @comment since we are called inside an environment such as % @example, where each end-of-line in the input causes an % end-of-line in the output. We don't want the end-of-line after @@ -586,6 +689,32 @@ \comment } % +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% % TeX puts in an \escapechar (i.e., `@') at the beginning of the help % message, so this ends up printing `@group can only ...'. % @@ -598,10 +727,8 @@ where each line of input produces a line of output.} \newdimen\mil \mil=0.001in -\def\need{\parsearg\needx} - % Old definition--didn't work. -%\def\needx #1{\par % +%\parseargdef\need{\par % %% This method tries to make TeX break the page naturally %% if the depth of the box does not fit. %{\baselineskip=0pt% @@ -609,7 +736,7 @@ where each line of input produces a line of output.} %\prevdepth=-1000pt %}} -\def\needx#1{% +\parseargdef\need{% % Ensure vertical mode, so we don't make a big box in the middle of a % paragraph. \par @@ -648,37 +775,11 @@ where each line of input produces a line of output.} \fi } -% @br forces paragraph break +% @br forces paragraph break (and is undocumented). \let\br = \par -% @dots{} output an ellipsis using the current font. -% We do .5em per period so that it has the same spacing in a typewriter -% font as three actual period characters. -% -\def\dots{% - \leavevmode - \hbox to 1.5em{% - \hskip 0pt plus 0.25fil minus 0.25fil - .\hss.\hss.% - \hskip 0pt plus 0.5fil minus 0.5fil - }% -} - -% @enddots{} is an end-of-sentence ellipsis. -% -\def\enddots{% - \leavevmode - \hbox to 2em{% - \hskip 0pt plus 0.25fil minus 0.25fil - .\hss.\hss.\hss.% - \hskip 0pt plus 0.5fil minus 0.5fil - }% - \spacefactor=3000 -} - - -% @page forces the start of a new page +% @page forces the start of a new page. % \def\page{\par\vfill\supereject} @@ -690,13 +791,11 @@ where each line of input produces a line of output.} \newskip\exdentamount % This defn is used inside fill environments such as @defun. -\def\exdent{\parsearg\exdentyyy} -\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} % This defn is used inside nofill environments such as @example. -\def\nofillexdent{\parsearg\nofillexdentyyy} -\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount -\leftline{\hskip\leftskip{\rm#1}}}} +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} % @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current % paragraph. For more general purposes, use the \margin insertion @@ -727,10 +826,10 @@ where each line of input produces a line of output.} % @inmargin{TEXT [, RIGHT-TEXT]} % (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; % else use TEXT for both). -% +% \def\inmargin#1{\parseinmargin #1,,\finish} \def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. - \setbox0 = \hbox{\ignorespaces #2}% + \setbox0 = \hbox{\ignorespaces #2}% \ifdim\wd0 > 0pt \def\lefttext{#1}% have both texts \def\righttext{#2}% @@ -748,37 +847,71 @@ where each line of input produces a line of output.} } % @include file insert text of that file as input. -% Allow normal characters that we make active in the argument (a file name). -\def\include{\begingroup - \catcode`\\=12 - \catcode`~=12 - \catcode`^=12 - \catcode`_=12 - \catcode`|=12 - \catcode`<=12 - \catcode`>=12 - \catcode`+=12 - \parsearg\includezzz} -% Restore active chars for included file. -\def\includezzz#1{\endgroup\begingroup - % Read the included file in a group so nested @include's work. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack \def\thisfile{#1}% - \input\thisfile -\endgroup} + {% + \makevalueexpandable + \def\temp{\input #1 }% + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} \def\thisfile{} -% @center line outputs that line, centered - -\def\center{\parsearg\centerzzz} -\def\centerzzz #1{{\advance\hsize by -\leftskip -\advance\hsize by -\rightskip -\centerline{#1}}} +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\next\centerH + \else + \let\next\centerV + \fi + \next{\hfil \ignorespaces#1\unskip \hfil}% +} +\def\centerH#1{% + {% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break + }% +} +\def\centerV#1{\line{\kern\leftskip #1\kern\rightskip}} % @sp n outputs n lines of vertical space -\def\sp{\parsearg\spxxx} -\def\spxxx #1{\vskip #1\baselineskip} +\parseargdef\sp{\vskip #1\baselineskip} % @comment ...line which is ignored... % @c is the same as @comment @@ -793,13 +926,13 @@ where each line of input produces a line of output.} % @paragraphindent NCHARS % We'll use ems for NCHARS, close enough. -% We cannot implement @paragraphindent asis, though. -% +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% \def\asisword{asis} % no translation, these are keywords \def\noneword{none} % -\def\paragraphindent{\parsearg\doparagraphindent} -\def\doparagraphindent#1{% +\parseargdef\paragraphindent{% \def\temp{#1}% \ifx\temp\asisword \else @@ -816,8 +949,7 @@ where each line of input produces a line of output.} % We'll use ems for NCHARS like @paragraphindent. % It seems @exampleindent asis isn't necessary, but % I preserve it to make it similar to @paragraphindent. -\def\exampleindent{\parsearg\doexampleindent} -\def\doexampleindent#1{% +\parseargdef\exampleindent{% \def\temp{#1}% \ifx\temp\asisword \else @@ -829,26 +961,138 @@ where each line of input produces a line of output.} \fi } +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + % @asis just yields its argument. Used with @table, for example. % \def\asis#1{#1} -% @math means output in math mode. -% We don't use $'s directly in the definition of \math because control -% sequences like \math are expanded when the toc file is written. Then, -% we read the toc file back, the $'s will be normal characters (as they -% should be, according to the definition of Texinfo). So we must use a -% control sequence to switch into and out of math mode. +% @math outputs its argument in math mode. % -% This isn't quite enough for @math to work properly in indices, but it -% seems unlikely it will ever be needed there. +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode\underChar = \active + \gdef\mathunderscore{% + \catcode\underChar=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a \ character. +% FYI, plain.tex uses \\ as a temporary control sequence (why?), but +% this is not advertised and we don't care. Texinfo does not +% otherwise define @\. % -\let\implicitmath = $ -\def\math#1{\implicitmath #1\implicitmath} +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + } +} % @bullet and @minus need the same treatment as @math, just above. -\def\bullet{\implicitmath\ptexbullet\implicitmath} -\def\minus{\implicitmath-\implicitmath} +\def\bullet{$\ptexbullet$} +\def\minus{$-$} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in a typewriter +% font as three actual period characters. +% +\def\dots{% + \leavevmode + \hbox to 1.5em{% + \hskip 0pt plus 0.25fil + .\hfil.\hfil.% + \hskip 0pt plus 0.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=3000 +} + +% @comma{} is so commas can be inserted into text without messing up +% Texinfo's parsing. +% +\let\comma = , % @refill is a no-op. \let\refill=\relax @@ -864,20 +1108,20 @@ where each line of input produces a line of output.} % So open here the files we need to have open while reading the input. % This makes it possible to make a .fmt file for texinfo. \def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. \iflinks - \readauxfile + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux \fi % \openindices needs to do some work in any case. \openindices - \fixbackslash % Turn off hack to swallow `\input texinfo'. - \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + \let\setfilename=\comment % Ignore extra @setfilename cmds. % % If texinfo.cnf is present on the system, read it. % Useful for site-wide @afourpaper, etc. - % Just to be on the safe side, close the input stream before the \input. \openin 1 texinfo.cnf - \ifeof1 \let\temp=\relax \else \def\temp{\input texinfo.cnf }\fi - \closein1 - \temp + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 % \comment % Ignore the actual filename. } @@ -924,26 +1168,35 @@ where each line of input produces a line of output.} \pdftrue \pdfoutput = 1 \input pdfcolor + \pdfcatalog{/PageMode /UseOutlines}% \def\dopdfimage#1#2#3{% \def\imagewidth{#2}% \def\imageheight{#3}% + % without \immediate, pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) \ifnum\pdftexversion < 14 - \pdfimage + \immediate\pdfimage \else - \pdfximage + \immediate\pdfximage \fi \ifx\empty\imagewidth\else width \imagewidth \fi \ifx\empty\imageheight\else height \imageheight \fi \ifnum\pdftexversion<13 - #1.pdf% + #1.pdf% \else {#1.pdf}% \fi \ifnum\pdftexversion < 14 \else \pdfrefximage \pdflastximage \fi} - \def\pdfmkdest#1{\pdfdest name{#1} xyz} - \def\pdfmkpgn#1{#1@} + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code in a section title + % aren't expanded. + \atdummies + \normalturnoffactive + \pdfdest name{#1} xyz% + }} + \def\pdfmkpgn#1{#1} \let\linkcolor = \Blue % was Cyan, but that seems light? \def\endlink{\Black\pdfendlink} % Adding outlines to PDF; macros for calculating structure of outlines @@ -951,47 +1204,94 @@ where each line of input produces a line of output.} \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} \def\advancenumber#1{\tempnum=\expnumber{#1}\relax - \advance\tempnum by1 + \advance\tempnum by 1 \expandafter\xdef\csname#1\endcsname{\the\tempnum}} - \def\pdfmakeoutlines{{% - \openin 1 \jobname.toc - \ifeof 1\else\bgroup - \closein 1 - \indexnofonts - \def\tt{} - \let\_ = \normalunderscore - % Thanh's hack / proper braces in bookmarks + % + % #1 is the section text. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node + % text, which might be empty if this toc entry had no + % corresponding node. #4 is the page number. + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worthwhile, since most documents are normally structured. + \def\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty \def\pdfoutlinedest{#4}\fi + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{#1}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Thanh's hack / proper braces in bookmarks \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace % - \def\chapentry ##1##2##3{} - \def\unnumbchapentry ##1##2{} - \def\secentry ##1##2##3##4{\advancenumber{chap##2}} - \def\unnumbsecentry ##1##2{} - \def\subsecentry ##1##2##3##4##5{\advancenumber{sec##2.##3}} - \def\unnumbsubsecentry ##1##2{} - \def\subsubsecentry ##1##2##3##4##5##6{\advancenumber{subsec##2.##3.##4}} - \def\unnumbsubsubsecentry ##1##2{} + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \let\thissecnum\empty + \let\thissubsecnum\empty + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \let\thissubsecnum\empty + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \let\thischapnum\empty + \let\thissecnum\empty + \let\thissubsecnum\empty + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% \input \jobname.toc - \def\chapentry ##1##2##3{% - \pdfoutline goto name{\pdfmkpgn{##3}}count-\expnumber{chap##2}{##1}} - \def\unnumbchapentry ##1##2{% - \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} - \def\secentry ##1##2##3##4{% - \pdfoutline goto name{\pdfmkpgn{##4}}count-\expnumber{sec##2.##3}{##1}} - \def\unnumbsecentry ##1##2{% - \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} - \def\subsecentry ##1##2##3##4##5{% - \pdfoutline goto name{\pdfmkpgn{##5}}count-\expnumber{subsec##2.##3.##4}{##1}} - \def\unnumbsubsecentry ##1##2{% - \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} - \def\subsubsecentry ##1##2##3##4##5##6{% - \pdfoutline goto name{\pdfmkpgn{##6}}{##1}} - \def\unnumbsubsubsecentry ##1##2{% - \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % xx to do this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Right + % now, I guess we'll just let the pdf reader have its way. + \indexnofonts + \turnoffactive \input \jobname.toc - \egroup\fi - }} + \endgroup + } + % \def\makelinks #1,{% \def\params{#1}\def\E{END}% \ifx\params\E @@ -1000,7 +1300,7 @@ where each line of input produces a line of output.} \let\nextmakelinks=\makelinks \ifnum\lnkcount>0,\fi \picknum{#1}% - \startlink attr{/Border [0 0 0]} + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{\the\pgn}}% \linkcolor #1% \advance\lnkcount by 1% @@ -1040,7 +1340,7 @@ where each line of input produces a line of output.} \def\pdfurl#1{% \begingroup \normalturnoffactive\def\@{@}% - \let\value=\expandablevalue + \makevalueexpandable \leavevmode\Red \startlink attr{/Border [0 0 0]}% user{/Subtype /Link /A << /S /URI /URI (#1) >>}% @@ -1055,7 +1355,7 @@ where each line of input produces a line of output.} \ifx\first0\adn0 \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 - \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 \else \ifnum0=\countA\else\makelink\fi \ifx\first.\let\next=\done\else @@ -1068,28 +1368,62 @@ where each line of input produces a line of output.} \def\makelink{\addtokens{\toksB}% {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} \def\pdflink#1{% - \startlink attr{/Border [0 0 0]} goto name{\mkpgn{#1}} + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} \linkcolor #1\endlink} - \def\mkpgn#1{#1@} \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} \fi % \ifx\pdfoutput \message{fonts,} -% Font-change commands. + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}} +\def\tt{\fam=\ttfam \setfontstyle{tt}} % Texinfo sort of supports the sans serif font style, which plain TeX does not. -% So we set up a \sf analogous to plain's \rm, etc. +% So we set up a \sf. \newfam\sffam -\def\sf{\fam=\sffam \tensf} +\def\sf{\fam=\sffam \setfontstyle{sf}} \let\li = \sf % Sometimes we call it \li, not \sf. -% We don't need math for this one. -\def\ttsl{\tenttsl} +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} -% Use Computer Modern fonts at \magstephalf (11pt). -\newcount\mainmagstep -\mainmagstep=\magstephalf +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} % Set the font macro #1 to the font named #2, adding on the % specified font prefix (normally `cm'). @@ -1119,17 +1453,18 @@ where each line of input produces a line of output.} \def\scshape{csc} \def\scbshape{csc} +% Text fonts (11.2pt, magstep1). +\newcount\mainmagstep \ifx\bigger\relax -\let\mainmagstep=\magstep1 -\setfont\textrm\rmshape{12}{1000} -\setfont\texttt\ttshape{12}{1000} + % not really supported. + \mainmagstep=\magstep1 + \setfont\textrm\rmshape{12}{1000} + \setfont\texttt\ttshape{12}{1000} \else -\setfont\textrm\rmshape{10}{\mainmagstep} -\setfont\texttt\ttshape{10}{\mainmagstep} + \mainmagstep=\magstephalf + \setfont\textrm\rmshape{10}{\mainmagstep} + \setfont\texttt\ttshape{10}{\mainmagstep} \fi -% Instead of cmb10, you many want to use cmbx10. -% cmbx10 is a prettier font on its own, but cmb10 -% looks better when embedded in a line with cmr10. \setfont\textbf\bfshape{10}{\mainmagstep} \setfont\textit\itshape{10}{\mainmagstep} \setfont\textsl\slshape{10}{\mainmagstep} @@ -1139,10 +1474,11 @@ where each line of input produces a line of output.} \font\texti=cmmi10 scaled \mainmagstep \font\textsy=cmsy10 scaled \mainmagstep -% A few fonts for @defun, etc. -\setfont\defbf\bxshape{10}{\magstep1} %was 1314 +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1} \setfont\deftt\ttshape{10}{\magstep1} -\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} +\setfont\defttsl\ttslshape{10}{\magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} % Fonts for indices, footnotes, small examples (9pt). \setfont\smallrm\rmshape{9}{1000} @@ -1156,7 +1492,19 @@ where each line of input produces a line of output.} \font\smalli=cmmi9 \font\smallsy=cmsy9 -% Fonts for title page: +% Fonts for small examples (8pt). +\setfont\smallerrm\rmshape{8}{1000} +\setfont\smallertt\ttshape{8}{1000} +\setfont\smallerbf\bfshape{10}{800} +\setfont\smallerit\itshape{8}{1000} +\setfont\smallersl\slshape{8}{1000} +\setfont\smallersf\sfshape{8}{1000} +\setfont\smallersc\scshape{10}{800} +\setfont\smallerttsl\ttslshape{10}{800} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 + +% Fonts for title page (20.4pt): \setfont\titlerm\rmbshape{12}{\magstep3} \setfont\titleit\itbshape{10}{\magstep4} \setfont\titlesl\slbshape{10}{\magstep4} @@ -1168,6 +1516,7 @@ where each line of input produces a line of output.} \font\titlei=cmmi12 scaled \magstep3 \font\titlesy=cmsy10 scaled \magstep4 \def\authorrm{\secrm} +\def\authortt{\sectt} % Chapter (and unnumbered) fonts (17.28pt). \setfont\chaprm\rmbshape{12}{\magstep2} @@ -1193,20 +1542,6 @@ where each line of input produces a line of output.} \font\seci=cmmi12 scaled \magstep1 \font\secsy=cmsy10 scaled \magstep2 -% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad. -% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded. -% \setfont\ssecsl\slshape{10}{\magstep1} -% \setfont\ssectt\ttshape{10}{\magstep1} -% \setfont\ssecsf\sfshape{10}{\magstep1} - -%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx. -%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than -%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1. -%\setfont\ssectt\ttshape{10}{1315} -%\setfont\ssecsf\sfshape{10}{1315} - -%\let\ssecbf=\ssecrm - % Subsection fonts (13.15pt). \setfont\ssecrm\rmbshape{12}{\magstephalf} \setfont\ssecit\itbshape{10}{1315} @@ -1215,69 +1550,124 @@ where each line of input produces a line of output.} \setfont\ssecttsl\ttslshape{10}{1315} \setfont\ssecsf\sfbshape{12}{\magstephalf} \let\ssecbf\ssecrm -\setfont\ssecsc\scbshape{10}{\magstep1} +\setfont\ssecsc\scbshape{10}{1315} \font\sseci=cmmi12 scaled \magstephalf \font\ssecsy=cmsy10 scaled 1315 -% The smallcaps and symbol fonts should actually be scaled \magstep1.5, -% but that is not a standard magnification. + +% Reduced fonts for @acro in text (10pt). +\setfont\reducedrm\rmshape{10}{1000} +\setfont\reducedtt\ttshape{10}{1000} +\setfont\reducedbf\bfshape{10}{1000} +\setfont\reducedit\itshape{10}{1000} +\setfont\reducedsl\slshape{10}{1000} +\setfont\reducedsf\sfshape{10}{1000} +\setfont\reducedsc\scshape{10}{1000} +\setfont\reducedttsl\ttslshape{10}{1000} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 % In order for the font changes to affect most math symbols and letters, % we have to define the \textfont of the standard families. Since -% texinfo doesn't allow for producing subscripts and superscripts, we -% don't bother to reset \scriptfont and \scriptscriptfont (which would -% also require loading a lot more fonts). +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). % \def\resetmathfonts{% - \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy - \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf - \textfont\ttfam = \tentt \textfont\sffam = \tensf + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf } - % The font-changing commands redefine the meanings of \tenSTYLE, instead -% of just \STYLE. We do this so that font changes will continue to work -% in math mode, where it is the current \fam that is relevant in most -% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam -% \tenbf}, for example. By redefining \tenbf, we obviate the need to -% redefine \bf itself. +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% \def\textfonts{% \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc - \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl - \resetmathfonts} + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} \def\titlefonts{% \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy \let\tenttsl=\titlettsl + \def\lsize{chap}\def\lllsize{subsec}% \resetmathfonts \setleading{25pt}} \def\titlefont#1{{\titlefonts\rm #1}} \def\chapfonts{% \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl + \def\lsize{sec}\def\lllsize{text}% \resetmathfonts \setleading{19pt}} \def\secfonts{% \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc - \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\lsize{subsec}\def\lllsize{reduced}% \resetmathfonts \setleading{16pt}} \def\subsecfonts{% \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc - \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\lsize{text}\def\lllsize{small}% \resetmathfonts \setleading{15pt}} -\let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf? +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} \def\smallfonts{% \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy \let\tenttsl=\smallttsl - \resetmathfonts \setleading{11pt}} + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% +% I wish the USA used A4 paper. +% --karl, 24jan03. + % Set up the default fonts, so we can use them for creating boxes. % -\textfonts +\textfonts \rm % Define these so they can be easily changed for other fonts. \def\angleleft{$\langle$} @@ -1288,23 +1678,32 @@ where each line of input produces a line of output.} % Fonts for short table of contents. \setfont\shortcontrm\rmshape{12}{1000} -\setfont\shortcontbf\bxshape{12}{1000} +\setfont\shortcontbf\bfshape{10}{\magstep1} % no cmb12 \setfont\shortcontsl\slshape{12}{1000} +\setfont\shortconttt\ttshape{12}{1000} %% Add scribe-like font environments, plus @l for inline lisp (usually sans %% serif) and @ii for TeX italic % \smartitalic{ARG} outputs arg in italics, followed by an italic correction % unless the following character is such as not to need one. -\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} -\def\smartslanted#1{{\sl #1}\futurelet\next\smartitalicx} -\def\smartitalic#1{{\it #1}\futurelet\next\smartitalicx} +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else + \ptexslash\fi\fi\fi} +\def\smartslanted#1{{\ifusingtt\ttsl\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\ifusingtt\ttsl\it #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally uses \ttsl. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}\futurelet\next\smartitalicx} + +% like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitalicx} \let\i=\smartitalic \let\var=\smartslanted \let\dfn=\smartslanted \let\emph=\smartitalic -\let\cite=\smartslanted \def\b#1{{\bf #1}} \let\strong=\b @@ -1316,11 +1715,21 @@ where each line of input produces a line of output.} \def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} \def\restorehyphenation{\hyphenchar\font = `- } +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\frenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + } +\catcode`@=\other + \def\t#1{% {\tt \rawbackslash \frenchspacing #1}% \null } -\let\ttfont=\t \def\samp#1{`\tclose{#1}'\null} \setfont\keyrm\rmshape{8}{1000} \font\keysy=cmsy9 @@ -1361,7 +1770,7 @@ where each line of input produces a line of output.} \null } -% We *must* turn on hyphenation at `-' and `_' in \code. +% We *must* turn on hyphenation at `-' and `_' in @code. % Otherwise, it is too hard to avoid overfull hboxes % in the Emacs manual, the Library manual, etc. @@ -1379,27 +1788,30 @@ where each line of input produces a line of output.} \catcode`\_=\active \let_\codeunder \codex } - % - % If we end up with any active - characters when handling the index, - % just treat them as a normal -. - \global\def\indexbreaks{\catcode`\-=\active \let-\realdash} } \def\realdash{-} \def\codedash{-\discretionary{}{}{}} -\def\codeunder{\ifusingtt{\normalunderscore\discretionary{}{}{}}{\_}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} \def\codex #1{\tclose{#1}\endgroup} -%\let\exp=\tclose %Was temporary - % @kbd is like @code, except that if the argument is just one @key command, % then @kbd has no effect. % @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), % `example' (@kbd uses ttsl only inside of @example and friends), % or `code' (@kbd uses normal tty font always). -\def\kbdinputstyle{\parsearg\kbdinputstylexxx} -\def\kbdinputstylexxx#1{% +\parseargdef\kbdinputstyle{% \def\arg{#1}% \ifx\arg\worddistinct \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% @@ -1407,15 +1819,17 @@ where each line of input produces a line of output.} \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% \else\ifx\arg\wordcode \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle option `\arg'}% \fi\fi\fi } \def\worddistinct{distinct} \def\wordexample{example} \def\wordcode{code} -% Default is kbdinputdistinct. (Too much of a hassle to call the macro, -% the catcodes are wrong for parsearg to work.) -\gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl} +% Default is `distinct.' +\kbdinputstyle distinct \def\xkey{\key} \def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% @@ -1458,7 +1872,7 @@ where each line of input produces a line of output.} % rms does not like angle brackets --karl, 17may97. % So now @email is just like @uref, unless we are pdf. -% +% %\def\email#1{\angleleft{\tt #1}\angleright} \ifpdf \def\email#1{\doemail#1,,\finish} @@ -1497,12 +1911,29 @@ where each line of input produces a line of output.} \def\sc#1{{\smallcaps#1}} % smallcaps font \def\ii#1{{\it #1}} % italic font -% @acronym downcases the argument and prints in smallcaps. -\def\acronym#1{{\smallcaps \lowercase{#1}}} +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi +} -% @pounds{} is a sterling sign. +% @pounds{} is a sterling sign, which is in the CM italic font. +% \def\pounds{{\it\$}} +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + \message{page headings,} @@ -1521,86 +1952,102 @@ where each line of input produces a line of output.} \newif\ifsetshortcontentsaftertitlepage \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue -\def\shorttitlepage{\parsearg\shorttitlepagezzz} -\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% +\parseargdef\shorttitlepage{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% \endgroup\page\hbox{}\page} -\def\titlepage{\begingroup \parindent=0pt \textfonts - \let\subtitlerm=\tenrm - \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% - % - \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% - % - % Leave some space at the very top of the page. - \vglue\titlepagetopglue - % - % Now you can print the title using @title. - \def\title{\parsearg\titlezzz}% - \def\titlezzz##1{\leftline{\titlefonts\rm ##1} - % print a rule at the page bottom also. - \finishedtitlepagefalse - \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% - % No rule at page bottom unless we print one at the top with @title. - \finishedtitlepagetrue - % - % Now you can put text using @subtitle. - \def\subtitle{\parsearg\subtitlezzz}% - \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% - % - % @author should come last, but may come many times. - \def\author{\parsearg\authorzzz}% - \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi - {\authorfont \leftline{##1}}}% - % - % Most title ``pages'' are actually two pages long, with space - % at the top of the second. We don't want the ragged left on the second. - \let\oldpage = \page - \def\page{% +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% \iffinishedtitlepage\else - \finishtitlepage + \finishtitlepage \fi - \oldpage \let\page = \oldpage - \hbox{}}% -% \def\page{\oldpage \hbox{}} + \page + \null + }% } \def\Etitlepage{% - \iffinishedtitlepage\else - \finishtitlepage - \fi - % It is important to do the page break before ending the group, - % because the headline and footline are only empty inside the group. - % If we use the new definition of \page, we always get a blank page - % after the title page, which we certainly don't want. - \oldpage - \endgroup - % - % If they want short, they certainly want long too. - \ifsetshortcontentsaftertitlepage - \shortcontents - \contents - \global\let\shortcontents = \relax - \global\let\contents = \relax - \fi - % - \ifsetcontentsaftertitlepage - \contents - \global\let\contents = \relax - \global\let\shortcontents = \relax - \fi - % - \ifpdf \pdfmakepagedesttrue \fi - % - \HEADINGSon + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi } \def\finishtitlepage{% - \vskip4pt \hrule height 2pt width \hsize - \vskip\titlepagebottomglue - \finishedtitlepagetrue + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue } +%%% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines + \let\tt=\authortt} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rm #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + This edition of the manual is dedicated to Karl Berry who should + really make affiliations work. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\authorfont \leftline{#1}}% + \fi +} + + %%% Set up page headings and footings. \let\thispage=\folio @@ -1610,7 +2057,7 @@ where each line of input produces a line of output.} \newtoks\evenfootline % footline on even pages \newtoks\oddfootline % footline on odd pages -% Now make Tex use those variables +% Now make TeX use those variables \headline={{\textfonts\rm \ifodd\pageno \the\oddheadline \else \the\evenheadline \fi}} \footline={{\textfonts\rm \ifodd\pageno \the\oddfootline @@ -1624,32 +2071,27 @@ where each line of input produces a line of output.} % @evenfooting @thisfile|| % @oddfooting ||@thisfile + \def\evenheading{\parsearg\evenheadingxxx} -\def\oddheading{\parsearg\oddheadingxxx} -\def\everyheading{\parsearg\everyheadingxxx} - -\def\evenfooting{\parsearg\evenfootingxxx} -\def\oddfooting{\parsearg\oddfootingxxx} -\def\everyfooting{\parsearg\everyfootingxxx} - -{\catcode`\@=0 % - -\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} -\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% \global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} -\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} -\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% \global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} -\gdef\everyheadingxxx#1{\oddheadingxxx{#1}\evenheadingxxx{#1}}% +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% -\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} -\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% \global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} -\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} -\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% % % Leave some space for the footline. Hopefully ok to assume @@ -1658,9 +2100,8 @@ where each line of input produces a line of output.} \global\advance\vsize by -\baselineskip } -\gdef\everyfootingxxx#1{\oddfootingxxx{#1}\evenfootingxxx{#1}} -% -}% unbind the catcode of @. +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + % @headings double turns headings on for double-sided printing. % @headings single turns headings on for single-sided printing. @@ -1674,7 +2115,7 @@ where each line of input produces a line of output.} \def\headings #1 {\csname HEADINGS#1\endcsname} -\def\HEADINGSoff{ +\def\HEADINGSoff{% \global\evenheadline={\hfil} \global\evenfootline={\hfil} \global\oddheadline={\hfil} \global\oddfootline={\hfil}} \HEADINGSoff @@ -1683,7 +2124,7 @@ where each line of input produces a line of output.} % chapter name on inside top of right hand pages, document % title on inside top of left hand pages, and page numbers on outside top % edge of all pages. -\def\HEADINGSdouble{ +\def\HEADINGSdouble{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} @@ -1695,7 +2136,7 @@ where each line of input produces a line of output.} % For single-sided printing, chapter title goes across top left of page, % page number on top right. -\def\HEADINGSsingle{ +\def\HEADINGSsingle{% \global\pageno=1 \global\evenfootline={\hfil} \global\oddfootline={\hfil} @@ -1742,12 +2183,11 @@ where each line of input produces a line of output.} % @settitle line... specifies the title of the document, for headings. % It generates no output of its own. \def\thistitle{\putwordNoTitle} -\def\settitle{\parsearg\settitlezzz} -\def\settitlezzz #1{\gdef\thistitle{#1}} +\def\settitle{\parsearg{\gdef\thistitle}} \message{tables,} -% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). +% Tables -- @table, @ftable, @vtable, @item(x). % default indentation of table text \newdimen\tableindent \tableindent=.8in @@ -1759,7 +2199,7 @@ where each line of input produces a line of output.} % used internally for \itemindent minus \itemmargin \newdimen\itemmax -% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with % these defs. % They also define \itemindex % to index the item name in whatever manner is desired (perhaps none). @@ -1771,22 +2211,10 @@ where each line of input produces a line of output.} \def\internalBitem{\smallbreak \parsearg\itemzzz} \def\internalBitemx{\itemxpar \parsearg\itemzzz} -\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} -\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} - -\def\internalBkitem{\smallbreak \parsearg\kitemzzz} -\def\internalBkitemx{\itemxpar \parsearg\kitemzzz} - -\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% - \itemzzz {#1}} - -\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% - \itemzzz {#1}} - \def\itemzzz #1{\begingroup % \advance\hsize by -\rightskip \advance\hsize by -\tableindent - \setbox0=\hbox{\itemfont{#1}}% + \setbox0=\hbox{\itemindicate{#1}}% \itemindex{#1}% \nobreak % This prevents a break before @itemx. % @@ -1810,10 +2238,18 @@ where each line of input produces a line of output.} % \parskip glue -- logically it's part of the @item we just started. \nobreak \vskip-\parskip % - % Stop a page break at the \parskip glue coming up. Unfortunately + % Stop a page break at the \parskip glue coming up. (Unfortunately % we can't prevent a possible page break at the following - % \baselineskip glue. - \nobreak + % \baselineskip glue.) However, if what follows is an environment + % such as @example, there will be no \parskip glue; then + % the negative vskip we just would cause the example and the item to + % crash together. So we use this bizarre value of 10001 as a signal + % to \aboveenvbreak to insert \parskip glue after all. + % (Possibly there are other commands that could be followed by + % @example which need the same treatment, but not section titles; or + % maybe section titles are the only special case and they should be + % penalty 10001...) + \penalty 10001 \endgroup \itemxneedsnegativevskipfalse \else @@ -1832,97 +2268,95 @@ where each line of input produces a line of output.} \fi } -\def\item{\errmessage{@item while not in a table}} -\def\itemx{\errmessage{@itemx while not in a table}} -\def\kitem{\errmessage{@kitem while not in a table}} -\def\kitemx{\errmessage{@kitemx while not in a table}} -\def\xitem{\errmessage{@xitem while not in a table}} -\def\xitemx{\errmessage{@xitemx while not in a table}} - -% Contains a kludge to get @end[description] to work. -\def\description{\tablez{\dontindex}{1}{}{}{}{}} +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} % @table, @ftable, @vtable. -\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} -{\obeylines\obeyspaces% -\gdef\tablex #1^^M{% -\tabley\dontindex#1 \endtabley}} - -\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} -{\obeylines\obeyspaces% -\gdef\ftablex #1^^M{% -\tabley\fnitemindex#1 \endtabley -\def\Eftable{\endgraf\afterenvbreak\endgroup}% -\let\Etable=\relax}} - -\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} -{\obeylines\obeyspaces% -\gdef\vtablex #1^^M{% -\tabley\vritemindex#1 \endtabley -\def\Evtable{\endgraf\afterenvbreak\endgroup}% -\let\Etable=\relax}} - -\def\dontindex #1{} -\def\fnitemindex #1{\doind {fn}{\code{#1}}}% -\def\vritemindex #1{\doind {vr}{\code{#1}}}% - -{\obeyspaces % -\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% -\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} - -\def\tablez #1#2#3#4#5#6{% -\aboveenvbreak % -\begingroup % -\def\Edescription{\Etable}% Necessary kludge. -\let\itemindex=#1% -\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % -\ifnum 0#4>0 \tableindent=#4\mil \fi % -\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % -\def\itemfont{#2}% -\itemmax=\tableindent % -\advance \itemmax by -\itemmargin % -\advance \leftskip by \tableindent % -\exdentamount=\tableindent -\parindent = 0pt -\parskip = \smallskipamount -\ifdim \parskip=0pt \parskip=2pt \fi% -\def\Etable{\endgraf\afterenvbreak\endgroup}% -\let\item = \internalBitem % -\let\itemx = \internalBitemx % -\let\kitem = \internalBkitem % -\let\kitemx = \internalBkitemx % -\let\xitem = \internalBxitem % -\let\xitemx = \internalBxitemx % +\envdef\table{% + \let\itemindex\gobble + \tablex } +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablex +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablex +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable % This is the counter used by @enumerate, which is really @itemize \newcount \itemno -\def\itemize{\parsearg\itemizezzz} +\envdef\itemize{\parsearg\doitemize} -\def\itemizezzz #1{% - \begingroup % ended by the @end itemize - \itemizey {#1}{\Eitemize} +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + \def\itemcontents{#1}% + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + \let\item=\itemizeitem } -\def\itemizey #1#2{% -\aboveenvbreak % -\itemmax=\itemindent % -\advance \itemmax by -\itemmargin % -\advance \leftskip by \itemindent % -\exdentamount=\itemindent -\parindent = 0pt % -\parskip = \smallskipamount % -\ifdim \parskip=0pt \parskip=2pt \fi% -\def#2{\endgraf\afterenvbreak\endgroup}% -\def\itemcontents{#1}% -\let\item=\itemizeitem} - -% Set sfcode to normal for the chars that usually have another value. -% These are `.?!:;,' -\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 - \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} % \splitoff TOKENS\endmark defines \first to be the first token in % TOKENS, and \rest to be the remainder. @@ -1933,11 +2367,8 @@ where each line of input produces a line of output.} % or number, to specify the first label in the enumerated list. No % argument is the same as `1'. % -\def\enumerate{\parsearg\enumeratezzz} -\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} \def\enumeratey #1 #2\endenumeratey{% - \begingroup % ended by the @end enumerate - % % If we were given no argument, pretend we were given `1'. \def\thearg{#1}% \ifx\thearg\empty \def\thearg{1}\fi @@ -2008,13 +2439,13 @@ where each line of input produces a line of output.} }% } -% Call itemizey, adding a period to the first argument and supplying the +% Call \doitemize, adding a period to the first argument and supplying the % common last two arguments. Also subtract one from the initial value in % \itemno, since @item increments \itemno. % \def\startenumeration#1{% \advance\itemno by -1 - \itemizey{#1.}\Eenumerate\flushcr + \doitemize{#1.}\flushcr } % @alphaenumerate and @capsenumerate are abbreviations for giving an arg @@ -2025,16 +2456,6 @@ where each line of input produces a line of output.} \def\Ealphaenumerate{\Eenumerate} \def\Ecapsenumerate{\Eenumerate} -% Definition of @item while inside @itemize. - -\def\itemizeitem{% -\advance\itemno by 1 -{\let\par=\endgraf \smallbreak}% -\ifhmode \errmessage{In hmode at itemizeitem}\fi -{\parskip=0in \hskip 0pt -\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% -\vadjust{\penalty 1200}}% -\flushcr} % @multitable macros % Amy Hendrickson, 8/18/94, 3/6/96 @@ -2061,24 +2482,14 @@ where each line of input produces a line of output.} % @multitable {Column 1 template} {Column 2 template} {Column 3 template} % @item ... % using the widest term desired in each column. -% -% For those who want to use more than one line's worth of words in -% the preamble, break the line within one argument and it -% will parse correctly, i.e., -% -% @multitable {Column 1 template} {Column 2 template} {Column 3 -% template} -% Not: -% @multitable {Column 1 template} {Column 2 template} -% {Column 3 template} % Each new table line starts with @item, each subsequent new column % starts with @tab. Empty columns may be produced by supplying @tab's % with nothing between them for as many times as empty columns are needed, % ie, @tab@tab@tab will produce two empty columns. -% @item, @tab, @multitable or @end multitable do not need to be on their -% own lines, but it will not hurt if they are. +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. % Sample multitable: @@ -2122,13 +2533,12 @@ where each line of input produces a line of output.} \def\xcolumnfractions{\columnfractions} \newif\ifsetpercent -% #1 is the part of the @columnfraction before the decimal point, which -% is presumably either 0 or the empty string (but we don't check, we -% just throw it away). #2 is the decimal part, which we use as the -% percent of \hsize for this column. -\def\pickupwholefraction#1.#2 {% +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% \global\advance\colcount by 1 - \expandafter\xdef\csname col\the\colcount\endcsname{.#2\hsize}% + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% \setuptable } @@ -2145,8 +2555,8 @@ where each line of input produces a line of output.} \let\go\pickupwholefraction \else \global\advance\colcount by 1 - \setbox0=\hbox{#1\unskip }% Add a normal word space as a separator; - % typically that is always in the input, anyway. + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% \fi \fi @@ -2161,18 +2571,30 @@ where each line of input produces a line of output.} \go } -% This used to have \hskip1sp. But then the space in a template line is -% not enough. That is bad. So let's go back to just & until we -% encounter the problem it was intended to solve again. -% --karl, nathan@acm.org, 20apr99. -\def\tab{&} +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. Note that \everycr resets \everytab. +\def\headitem{\checkenv\multitable \crcr \global\everytab={\bf}\the\everytab}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we encounter the problem it was intended to solve again. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% % @multitable ... @end multitable definitions: % -\def\multitable{\parsearg\dotable} -\def\dotable#1{\bgroup +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. \let\item\crcr + % \tolerance=9500 \hbadness=9500 \setmultitablespacing @@ -2180,66 +2602,80 @@ where each line of input produces a line of output.} \parindent=\multitableparindent \overfullrule=0pt \global\colcount=0 - \def\Emultitable{\global\setpercentfalse\cr\egroup\egroup}% % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% % To parse everything between @multitable and @item: \setuptable#1 \endsetuptable % - % \everycr will reset column counter, \colcount, at the end of - % each line. Every column entry will cause \colcount to advance by one. - % The table preamble - % looks at the current \colcount to find the correct column width. - \everycr{\noalign{% - % - % \filbreak%% keeps underfull box messages off when table breaks over pages. - % Maybe so, but it also creates really weird page breaks when the table - % breaks over pages. Wouldn't \vfil be better? Wait until the problem - % manifests itself, so it can be fixed for real --karl. - \global\colcount=0\relax}}% - % % This preamble sets up a generic column definition, which will % be used as many times as user calls for columns. % \vtop will set a single line and will also let text wrap and % continue for many paragraphs if desired. - \halign\bgroup&\global\advance\colcount by 1\relax - \multistrut\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname - % - % In order to keep entries from bumping into each other - % we will add a \leftskip of \multitablecolspace to all columns after - % the first one. - % - % If a template has been used, we will add \multitablecolspace - % to the width of each template entry. - % - % If the user has set preamble in terms of percent of \hsize we will - % use that dimension as the width of the column, and the \leftskip - % will keep entries from bumping into each other. Table will start at - % left margin and final column will justify at right margin. - % - % Make sure we don't inherit \rightskip from the outer environment. - \rightskip=0pt - \ifnum\colcount=1 - % The first column will be indented with the surrounding text. - \advance\hsize by\leftskip - \else - \ifsetpercent \else - % If user has not set preamble in terms of percent of \hsize - % we will advance \hsize by \multitablecolspace. - \advance\hsize by \multitablecolspace - \fi - % In either case we will make \leftskip=\multitablecolspace: - \leftskip=\multitablecolspace - \fi - % Ignoring space at the beginning and end avoids an occasional spurious - % blank line, when TeX decides to break the line at the space before the - % box from the multistrut, so the strut ends up on a line by itself. - % For example: - % @multitable @columnfractions .11 .89 - % @item @code{#} - % @tab Legal holiday which is valid in major parts of the whole country. - % Is automatically provided with highlighting sequences respectively marking - % characters. - \noindent\ignorespaces##\unskip\multistrut}\cr + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse } \def\setmultitablespacing{% test to see if user has set \multitablelinespace. @@ -2271,282 +2707,172 @@ width0pt\relax} \fi \message{conditionals,} -% Prevent errors for section commands. -% Used in @ignore and in failing conditionals. -\def\ignoresections{% - \let\chapter=\relax - \let\unnumbered=\relax - \let\top=\relax - \let\unnumberedsec=\relax - \let\unnumberedsection=\relax - \let\unnumberedsubsec=\relax - \let\unnumberedsubsection=\relax - \let\unnumberedsubsubsec=\relax - \let\unnumberedsubsubsection=\relax - \let\section=\relax - \let\subsec=\relax - \let\subsubsec=\relax - \let\subsection=\relax - \let\subsubsection=\relax - \let\appendix=\relax - \let\appendixsec=\relax - \let\appendixsection=\relax - \let\appendixsubsec=\relax - \let\appendixsubsection=\relax - \let\appendixsubsubsec=\relax - \let\appendixsubsubsection=\relax - \let\contents=\relax - \let\smallbook=\relax - \let\titlepage=\relax + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 } +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} -% Used in nested conditionals, where we have to parse the Texinfo source -% and so want to turn off most commands, in case they are used -% incorrectly. +% Ignore @ignore, @ifhtml, @ifinfo, and the like. % -\def\ignoremorecommands{% - \let\defcodeindex = \relax - \let\defcv = \relax - \let\deffn = \relax - \let\deffnx = \relax - \let\defindex = \relax - \let\defivar = \relax - \let\defmac = \relax - \let\defmethod = \relax - \let\defop = \relax - \let\defopt = \relax - \let\defspec = \relax - \let\deftp = \relax - \let\deftypefn = \relax - \let\deftypefun = \relax - \let\deftypeivar = \relax - \let\deftypeop = \relax - \let\deftypevar = \relax - \let\deftypevr = \relax - \let\defun = \relax - \let\defvar = \relax - \let\defvr = \relax - \let\ref = \relax - \let\xref = \relax - \let\printindex = \relax - \let\pxref = \relax - \let\settitle = \relax - \let\setchapternewpage = \relax - \let\setchapterstyle = \relax - \let\everyheading = \relax - \let\evenheading = \relax - \let\oddheading = \relax - \let\everyfooting = \relax - \let\evenfooting = \relax - \let\oddfooting = \relax - \let\headings = \relax - \let\include = \relax - \let\lowersections = \relax - \let\down = \relax - \let\raisesections = \relax - \let\up = \relax - \let\set = \relax - \let\clear = \relax - \let\item = \relax -} - -% Ignore @ignore ... @end ignore. -% -\def\ignore{\doignore{ignore}} - -% Ignore @ifinfo, @ifhtml, @ifnottex, @html, @menu, and @direntry text. -% -\def\ifinfo{\doignore{ifinfo}} -\def\ifhtml{\doignore{ifhtml}} -\def\ifnottex{\doignore{ifnottex}} -\def\html{\doignore{html}} -\def\menu{\doignore{menu}} \def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} -% @dircategory CATEGORY -- specify a category of the dir file -% which this file should belong to. Ignore this in TeX. -\let\dircategory = \comment - -% Ignore text until a line `@end #1'. +% Ignore text until a line `@end #1', keeping track of nested conditionals. % +% A count to remember the depth of nesting. +\newcount\doignorecount + \def\doignore#1{\begingroup - % Don't complain about control sequences we have declared \outer. - \ignoresections - % - % Define a command to swallow text until we reach `@end #1'. - % This @ is a catcode 12 token (that is the normal catcode of @ in - % this texinfo.tex file). We change the catcode of @ below to match. - \long\def\doignoretext##1@end #1{\enddoignore}% + % Scan in ``verbatim'' mode: + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other % % Make sure that spaces turn into tokens that match what \doignoretext wants. - \catcode32 = 10 + \spaceisspace % - % Ignore braces, too, so mismatched braces don't cause trouble. - \catcode`\{ = 9 - \catcode`\} = 9 + % Count number of #1's that we've seen. + \doignorecount = 0 % - % We must not have @c interpreted as a control sequence. - \catcode`\@ = 12 - % - % Make the letter c a comment character so that the rest of the line - % will be ignored. This way, the document can have (for example) - % @c @end ifinfo - % and the @end ifinfo will be properly ignored. - % (We've just changed @ to catcode 12.) - \catcode`\c = 14 - % - % And now expand that command. - \doignoretext + % Swallow text until we reach the matching `@end #1'. + \dodoignore {#1}% } -% What we do to finish off ignored text. -% -\def\enddoignore{\endgroup\ignorespaces}% - -\newif\ifwarnedobs\warnedobsfalse -\def\obstexwarn{% - \ifwarnedobs\relax\else - % We need to warn folks that they may have trouble with TeX 3.0. - % This uses \immediate\write16 rather than \message to get newlines. - \immediate\write16{} - \immediate\write16{WARNING: for users of Unix TeX 3.0!} - \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} - \immediate\write16{If you are running another version of TeX, relax.} - \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} - \immediate\write16{ Then upgrade your TeX installation if you can.} - \immediate\write16{ (See ftp://ftp.gnu.org/pub/gnu/TeX.README.)} - \immediate\write16{If you are stuck with version 3.0, run the} - \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} - \immediate\write16{ to use a workaround.} - \immediate\write16{} - \global\warnedobstrue - \fi -} - -% **In TeX 3.0, setting text in \nullfont hangs tex. For a -% workaround (which requires the file ``dummy.tfm'' to be installed), -% uncomment the following line: -%%%%%\font\nullfont=dummy\let\obstexwarn=\relax - -% Ignore text, except that we keep track of conditional commands for -% purposes of nesting, up to an `@end #1' command. -% -\def\nestedignore#1{% - \obstexwarn - % We must actually expand the ignored text to look for the @end - % command, so that nested ignore constructs work. Thus, we put the - % text into a \vbox and then do nothing with the result. To minimize - % the change of memory overflow, we follow the approach outlined on - % page 401 of the TeXbook: make the current font be a dummy font. +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % % - \setbox0 = \vbox\bgroup - % Don't complain about control sequences we have declared \outer. - \ignoresections + \gdef\dodoignore#1{% + % #1 contains the string `ifinfo'. % - % Define `@end #1' to end the box, which will in turn undefine the - % @end command again. - \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % Define a command to find the next `@end #1', which must be on a line + % by itself. + \long\def\doignoretext##1^^M@end #1{\doignoretextyyy##1^^M@#1\_STOP_}% + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% % - % We are going to be parsing Texinfo commands. Most cause no - % trouble when they are used incorrectly, but some commands do - % complicated argument parsing or otherwise get confused, so we - % undefine them. - % - % We can't do anything about stray @-signs, unfortunately; - % they'll produce `undefined control sequence' errors. - \ignoremorecommands - % - % Set the current font to be \nullfont, a TeX primitive, and define - % all the font commands to also use \nullfont. We don't use - % dummy.tfm, as suggested in the TeXbook, because not all sites - % might have that installed. Therefore, math mode will still - % produce output, but that should be an extremely small amount of - % stuff compared to the main input. - % - \nullfont - \let\tenrm=\nullfont \let\tenit=\nullfont \let\tensl=\nullfont - \let\tenbf=\nullfont \let\tentt=\nullfont \let\smallcaps=\nullfont - \let\tensf=\nullfont - % Similarly for index fonts (mostly for their use in smallexample). - \let\smallrm=\nullfont \let\smallit=\nullfont \let\smallsl=\nullfont - \let\smallbf=\nullfont \let\smalltt=\nullfont \let\smallsc=\nullfont - \let\smallsf=\nullfont - % - % Don't complain when characters are missing from the fonts. - \tracinglostchars = 0 - % - % Don't bother to do space factor calculations. - \frenchspacing - % - % Don't report underfull hboxes. - \hbadness = 10000 - % - % Do minimal line-breaking. - \pretolerance = 10000 - % - % Do not execute instructions in @tex - \def\tex{\doignore{tex}}% - % Do not execute macro definitions. - % `c' is a comment character, so the word `macro' will get cut off. - \def\macro{\doignore{ma}}% + % And now expand that command. + \obeylines % + \doignoretext ^^M% + }% } +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +\def\enddoignore{\endgroup\ignorespaces} + + % @set VAR sets the variable VAR to an empty value. % @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. % % Since we want to separate VAR from REST-OF-LINE (which might be % empty), we can't just use \parsearg; we have to insert a space of our % own to delimit the rest of the line, and then take it out again if we -% didn't need it. Make sure the catcode of space is correct to avoid -% losing inside @example, for instance. +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. % -\def\set{\begingroup\catcode` =10 - \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. - \parsearg\setxxx} -\def\setxxx#1{\setyyy#1 \endsetyyy} +\parseargdef\set{\setyyy#1 \endsetyyy} \def\setyyy#1 #2\endsetyyy{% - \def\temp{#2}% - \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty - \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. - \fi - \endgroup + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% } -% Can't use \xdef to pre-expand #2 and save some time, since \temp or -% \next or other control sequences that we've defined might get us into -% an infinite loop. Consider `@set foo @cite{bar}'. -\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} % @clear VAR clears (i.e., unsets) the variable VAR. % -\def\clear{\parsearg\clearxxx} -\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} % @value{foo} gets the text saved in variable foo. -{ - \catcode`\_ = \active - % - % We might end up with active _ or - characters in the argument if - % we're called from @code, as @code{@value{foo-bar_}}. So \let any - % such active characters to their normal equivalents. - \gdef\value{\begingroup - \catcode`\-=12 \catcode`\_=12 - \indexbreaks \let_\normalunderscore - \valuexxx} -} +\def\value{\begingroup\makevalueexpandable\valuexxx} \def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} % We have this subroutine so that we can handle at least some @value's -% properly in indexes (we \let\value to this in \indexdummies). Ones -% whose names contain - or _ still won't work, but we can't do anything -% about that. The command has to be fully expandable, since the result -% winds up in the index file. This means that if the variable's value -% contains other Texinfo commands, it's almost certain it will fail -% (although perhaps we could fix that with sufficient work to do a -% one-level expansion on the result, instead of complete). +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). % \def\expandablevalue#1{% \expandafter\ifx\csname SET#1\endcsname\relax {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% \else \csname SET#1\endcsname \fi @@ -2555,66 +2881,36 @@ width0pt\relax} \fi % @ifset VAR ... @end ifset reads the `...' iff VAR has been defined % with @set. % -\def\ifset{\parsearg\ifsetxxx} -\def\ifsetxxx #1{% - \expandafter\ifx\csname SET#1\endcsname\relax - \expandafter\ifsetfail - \else - \expandafter\ifsetsucceed - \fi +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next } -\def\ifsetsucceed{\conditionalsucceed{ifset}} -\def\ifsetfail{\nestedignore{ifset}} -\defineunmatchedend{ifset} +\def\ifsetfail{\doignore{ifset}} % @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been % defined with @set, or has been undefined with @clear. % -\def\ifclear{\parsearg\ifclearxxx} -\def\ifclearxxx #1{% - \expandafter\ifx\csname SET#1\endcsname\relax - \expandafter\ifclearsucceed - \else - \expandafter\ifclearfail - \fi -} -\def\ifclearsucceed{\conditionalsucceed{ifclear}} -\def\ifclearfail{\nestedignore{ifclear}} -\defineunmatchedend{ifclear} - -% @iftex, @ifnothtml, @ifnotinfo always succeed; we read the text -% following, through the first @end iftex (etc.). Make `@end iftex' -% (etc.) valid only after an @iftex. +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. % -\def\iftex{\conditionalsucceed{iftex}} -\def\ifnothtml{\conditionalsucceed{ifnothtml}} -\def\ifnotinfo{\conditionalsucceed{ifnotinfo}} -\defineunmatchedend{iftex} -\defineunmatchedend{ifnothtml} -\defineunmatchedend{ifnotinfo} +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} -% We can't just want to start a group at @iftex (for example) and end it -% at @end iftex, since then @set commands inside the conditional have no -% effect (they'd get reverted at the end of the group). So we must -% define \Eiftex to redefine itself to be its previous value. (We can't -% just define it to fail again with an ``unmatched end'' error, since -% the @ifset might be nested.) -% -\def\conditionalsucceed#1{% - \edef\temp{% - % Remember the current value of \E#1. - \let\nece{prevE#1} = \nece{E#1}% - % - % At the `@end #1', redefine \E#1 to be its previous value. - \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% - }% - \temp -} - -% We need to expand lots of \csname's, but we don't want to expand the -% control sequences after we've constructed them. -% -\def\nece#1{\expandafter\noexpand\csname#1\endcsname} +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment % @defininfoenclose. \let\definfoenclose=\comment @@ -2665,10 +2961,10 @@ width0pt\relax} \fi % @synindex foo bar makes index foo feed into index bar. % Do this instead of @defindex foo if you don't want it as a separate index. -% +% % @syncodeindex foo bar similar, but put all entries made for index foo % inside @code. -% +% \def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} \def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} @@ -2707,266 +3003,388 @@ width0pt\relax} \fi \def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} \def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% \def\indexdummies{% -\def\ { }% -% Take care of the plain tex accent commands. -\def\"{\realbackslash "}% -\def\`{\realbackslash `}% -\def\'{\realbackslash '}% -\def\^{\realbackslash ^}% -\def\~{\realbackslash ~}% -\def\={\realbackslash =}% -\def\b{\realbackslash b}% -\def\c{\realbackslash c}% -\def\d{\realbackslash d}% -\def\u{\realbackslash u}% -\def\v{\realbackslash v}% -\def\H{\realbackslash H}% -% Take care of the plain tex special European modified letters. -\def\oe{\realbackslash oe}% -\def\ae{\realbackslash ae}% -\def\aa{\realbackslash aa}% -\def\OE{\realbackslash OE}% -\def\AE{\realbackslash AE}% -\def\AA{\realbackslash AA}% -\def\o{\realbackslash o}% -\def\O{\realbackslash O}% -\def\l{\realbackslash l}% -\def\L{\realbackslash L}% -\def\ss{\realbackslash ss}% -% Take care of texinfo commands likely to appear in an index entry. -% (Must be a way to avoid doing expansion at all, and thus not have to -% laboriously list every single command here.) -\def\@{@}% will be @@ when we switch to @ as escape char. -% Need these in case \tex is in effect and \{ is a \delimiter again. -% But can't use \lbracecmd and \rbracecmd because texindex assumes -% braces and backslashes are used only as delimiters. -\let\{ = \mylbrace -\let\} = \myrbrace -\def\_{{\realbackslash _}}% -\def\w{\realbackslash w }% -\def\bf{\realbackslash bf }% -%\def\rm{\realbackslash rm }% -\def\sl{\realbackslash sl }% -\def\sf{\realbackslash sf}% -\def\tt{\realbackslash tt}% -\def\gtr{\realbackslash gtr}% -\def\less{\realbackslash less}% -\def\hat{\realbackslash hat}% -\def\TeX{\realbackslash TeX}% -\def\dots{\realbackslash dots }% -\def\result{\realbackslash result}% -\def\equiv{\realbackslash equiv}% -\def\expansion{\realbackslash expansion}% -\def\print{\realbackslash print}% -\def\error{\realbackslash error}% -\def\point{\realbackslash point}% -\def\copyright{\realbackslash copyright}% -\def\tclose##1{\realbackslash tclose {##1}}% -\def\code##1{\realbackslash code {##1}}% -\def\uref##1{\realbackslash uref {##1}}% -\def\url##1{\realbackslash url {##1}}% -\def\env##1{\realbackslash env {##1}}% -\def\command##1{\realbackslash command {##1}}% -\def\option##1{\realbackslash option {##1}}% -\def\dotless##1{\realbackslash dotless {##1}}% -\def\samp##1{\realbackslash samp {##1}}% -\def\,##1{\realbackslash ,{##1}}% -\def\t##1{\realbackslash t {##1}}% -\def\r##1{\realbackslash r {##1}}% -\def\i##1{\realbackslash i {##1}}% -\def\b##1{\realbackslash b {##1}}% -\def\sc##1{\realbackslash sc {##1}}% -\def\cite##1{\realbackslash cite {##1}}% -\def\key##1{\realbackslash key {##1}}% -\def\file##1{\realbackslash file {##1}}% -\def\var##1{\realbackslash var {##1}}% -\def\kbd##1{\realbackslash kbd {##1}}% -\def\dfn##1{\realbackslash dfn {##1}}% -\def\emph##1{\realbackslash emph {##1}}% -\def\acronym##1{\realbackslash acronym {##1}}% -% -% Handle some cases of @value -- where the variable name does not -% contain - or _, and the value does not contain any -% (non-fully-expandable) commands. -\let\value = \expandablevalue -% -\unsepspaces -% Turn off macro expansion -\turnoffmacros + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % Need these in case \tex is in effect and \{ is a \delimiter again. + % But can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. + \let\{ = \mylbrace + \let\} = \myrbrace + % + % \definedummyword defines \#1 as \realbackslash #1\space, thus + % effectively preventing its expansion. This is used only for control + % words, not control letters, because the \space would be incorrect + % for control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword##1{% + \expandafter\def\csname ##1\endcsname{\realbackslash ##1\space}% + }% + \def\definedummyletter##1{% + \expandafter\def\csname ##1\endcsname{\realbackslash ##1}% + }% + % + % Do the redefinitions. + \commondummies } -% If an index command is used in an @example environment, any spaces -% therein should become regular spaces in the raw index file, not the -% expansion of \tie (\\leavevmode \penalty \@M \ ). -{\obeyspaces - \gdef\unsepspaces{\obeyspaces\let =\space}} +% For the aux file, @ is the escape character. So we want to redefine +% everything using @ instead of \realbackslash. When everything uses +% @, this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % (See comments in \indexdummies.) + \def\definedummyword##1{% + \expandafter\def\csname ##1\endcsname{@##1\space}% + }% + \def\definedummyletter##1{% + \expandafter\def\csname ##1\endcsname{@##1}% + }% + % + % Do the redefinitions. + \commondummies +} -% \indexnofonts no-ops all font-change commands. -% This is used when outputting the strings to sort the index by. -\def\indexdummyfont#1{#1} -\def\indexdummytex{TeX} -\def\indexdummydots{...} +% Called from \indexdummies and \atdummies. \definedummyword and +% \definedummyletter must be defined first. +% +\def\commondummies{% + % + \normalturnoffactive + % + \commondummiesnofonts + % + \definedummyletter{_}% + % + % Non-English letters. + \definedummyword{AA}% + \definedummyword{AE}% + \definedummyword{L}% + \definedummyword{OE}% + \definedummyword{O}% + \definedummyword{aa}% + \definedummyword{ae}% + \definedummyword{l}% + \definedummyword{oe}% + \definedummyword{o}% + \definedummyword{ss}% + \definedummyword{exclamdown}% + \definedummyword{questiondown}% + \definedummyword{ordf}% + \definedummyword{ordm}% + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword{bf}% + \definedummyword{gtr}% + \definedummyword{hat}% + \definedummyword{less}% + \definedummyword{sf}% + \definedummyword{sl}% + \definedummyword{tclose}% + \definedummyword{tt}% + % + \definedummyword{LaTeX}% + \definedummyword{TeX}% + % + % Assorted special characters. + \definedummyword{bullet}% + \definedummyword{copyright}% + \definedummyword{registeredsymbol}% + \definedummyword{dots}% + \definedummyword{enddots}% + \definedummyword{equiv}% + \definedummyword{error}% + \definedummyword{expansion}% + \definedummyword{minus}% + \definedummyword{pounds}% + \definedummyword{point}% + \definedummyword{print}% + \definedummyword{result}% + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable + % + % Normal spaces, not active ones. + \unsepspaces + % + % No macro expansion. + \turnoffmacros +} +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +% Better have this without active chars. +{ + \catcode`\~=\other + \gdef\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter{!}% + \definedummyletter{"}% + \definedummyletter{'}% + \definedummyletter{*}% + \definedummyletter{,}% + \definedummyletter{.}% + \definedummyletter{/}% + \definedummyletter{:}% + \definedummyletter{=}% + \definedummyletter{?}% + \definedummyletter{^}% + \definedummyletter{`}% + \definedummyletter{~}% + \definedummyword{u}% + \definedummyword{v}% + \definedummyword{H}% + \definedummyword{dotaccent}% + \definedummyword{ringaccent}% + \definedummyword{tieaccent}% + \definedummyword{ubaraccent}% + \definedummyword{udotaccent}% + \definedummyword{dotless}% + % + % Texinfo font commands. + \definedummyword{b}% + \definedummyword{i}% + \definedummyword{r}% + \definedummyword{sc}% + \definedummyword{t}% + % + % Commands that take arguments. + \definedummyword{acronym}% + \definedummyword{cite}% + \definedummyword{code}% + \definedummyword{command}% + \definedummyword{dfn}% + \definedummyword{emph}% + \definedummyword{env}% + \definedummyword{file}% + \definedummyword{kbd}% + \definedummyword{key}% + \definedummyword{math}% + \definedummyword{option}% + \definedummyword{samp}% + \definedummyword{strong}% + \definedummyword{tie}% + \definedummyword{uref}% + \definedummyword{url}% + \definedummyword{var}% + \definedummyword{verb}% + \definedummyword{w}% + } +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% \def\indexnofonts{% -% Just ignore accents. -\let\,=\indexdummyfont -\let\"=\indexdummyfont -\let\`=\indexdummyfont -\let\'=\indexdummyfont -\let\^=\indexdummyfont -\let\~=\indexdummyfont -\let\==\indexdummyfont -\let\b=\indexdummyfont -\let\c=\indexdummyfont -\let\d=\indexdummyfont -\let\u=\indexdummyfont -\let\v=\indexdummyfont -\let\H=\indexdummyfont -\let\dotless=\indexdummyfont -% Take care of the plain tex special European modified letters. -\def\oe{oe}% -\def\ae{ae}% -\def\aa{aa}% -\def\OE{OE}% -\def\AE{AE}% -\def\AA{AA}% -\def\o{o}% -\def\O{O}% -\def\l{l}% -\def\L{L}% -\def\ss{ss}% -\let\w=\indexdummyfont -\let\t=\indexdummyfont -\let\r=\indexdummyfont -\let\i=\indexdummyfont -\let\b=\indexdummyfont -\let\emph=\indexdummyfont -\let\strong=\indexdummyfont -\let\cite=\indexdummyfont -\let\sc=\indexdummyfont -%Don't no-op \tt, since it isn't a user-level command -% and is used in the definitions of the active chars like <, >, |... -%\let\tt=\indexdummyfont -\let\tclose=\indexdummyfont -\let\code=\indexdummyfont -\let\url=\indexdummyfont -\let\uref=\indexdummyfont -\let\env=\indexdummyfont -\let\acronym=\indexdummyfont -\let\command=\indexdummyfont -\let\option=\indexdummyfont -\let\file=\indexdummyfont -\let\samp=\indexdummyfont -\let\kbd=\indexdummyfont -\let\key=\indexdummyfont -\let\var=\indexdummyfont -\let\TeX=\indexdummytex -\let\dots=\indexdummydots -\def\@{@}% + \def\definedummyword##1{% + \expandafter\let\csname ##1\endcsname\asis + }% + \let\definedummyletter=\definedummyword + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + % how to handle braces? + \def\_{\normalunderscore}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\aa{aa}% + \def\ae{ae}% + \def\l{l}% + \def\oe{oe}% + \def\o{o}% + \def\ss{ss}% + \def\exclamdown{!}% + \def\questiondown{?}% + \def\ordf{a}% + \def\ordm{o}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\bullet{bullet}% + \def\copyright{copyright}% + \def\registeredsymbol{R}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\expansion{==>}% + \def\minus{-}% + \def\pounds{pounds}% + \def\point{.}% + \def\print{-|}% + \def\result{=>}% } -% To define \realbackslash, we must make \ not be an escape. -% We must first make another character (@) an escape -% so we do not become unable to do a definition. - -{\catcode`\@=0 \catcode`\\=\other - @gdef@realbackslash{\}} - \let\indexbackslash=0 %overridden during \printindex. \let\SETmarginindex=\relax % put index entries in margin (undocumented)? -% For \ifx comparisons. -\def\emptymacro{\empty} - % Most index entries go through here, but \dosubind is the general case. -% -\def\doind#1#2{\dosubind{#1}{#2}\empty} +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} % Workhorse for all \fooindexes. % #1 is name of index, #2 is stuff to put there, #3 is subentry -- -% \empty if called from \doind, as we usually are. The main exception -% is with defuns, which call us directly. +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). % \def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \ifvmode + \dosubindsanitize + \else + \dosubindwrite + \fi + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% % Put the index entry in the margin if desired. \ifx\SETmarginindex\relax\else - \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}% + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% \fi - {% - \count255=\lastpenalty - {% - \indexdummies % Must do this here, since \bf, etc expand at this stage - \escapechar=`\\ - {% - \let\folio = 0% We will expand all macros now EXCEPT \folio. - \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now - % so it will be output as is; and it will print as backslash. - % - \def\thirdarg{#3}% - % - % If third arg is present, precede it with space in sort key. - \ifx\thirdarg\emptymacro - \let\subentry = \empty - \else - \def\subentry{ #3}% - \fi - % - % First process the index entry with all font commands turned - % off to get the string to sort by. - {\indexnofonts \xdef\indexsorttmp{#2\subentry}}% - % - % Now the real index entry with the fonts. - \toks0 = {#2}% - % - % If the third (subentry) arg is present, add it to the index - % line to write. - \ifx\thirdarg\emptymacro \else - \toks0 = \expandafter{\the\toks0{#3}}% - \fi - % - % Set up the complete index entry, with both the sort key and - % the original text, including any font commands. We write - % three arguments to \entry to the .?? file (four in the - % subentry case), texindex reduces to two when writing the .??s - % sorted result. - \edef\temp{% - \write\csname#1indfile\endcsname{% - \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}% - }% - % - % If a skip is the last thing on the list now, preserve it - % by backing up by \lastskip, doing the \write, then inserting - % the skip again. Otherwise, the whatsit generated by the - % \write will make \lastskip zero. The result is that sequences - % like this: - % @end defun - % @tindex whatever - % @defun ... - % will have extra space inserted, because the \medbreak in the - % start of the @defun won't see the skip inserted by the @end of - % the previous defun. - % - % But don't do any of this if we're not in vertical mode. We - % don't want to do a \vskip and prematurely end a paragraph. - % - % Avoid page breaks due to these extra skips, too. - % - \iflinks - \ifvmode - \skip0 = \lastskip - \ifdim\lastskip = 0pt \else \nobreak\vskip-\lastskip \fi - \fi - % - \temp % do the write - % - % - \ifvmode \ifdim\skip0 = 0pt \else \nobreak\vskip\skip0 \fi \fi - \fi - }% - }% - \penalty\count255 + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \escapechar=`\\ + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write will make \lastskip zero. The result is that sequences +% like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +% ..., ready, GO: +% +\def\dosubindsanitize{% + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \skip0 = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \count255 = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\skip0 glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\skip0 + \fi + % + \dosubindwrite + % + \ifx\lastskipmacro\zeroskipmacro + % if \lastskip was zero, perhaps the last item was a + % penalty, and perhaps it was >=10000, e.g., a \nobreak. + % In that case, we want to re-insert the penalty; since we + % just inserted a non-discardable item, any following glue + % (such as a \parskip) would be a breakpoint. For example: + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\count255>9999 \nobreak \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\skip0 + \fi } % The index entry written in the file actually looks like @@ -3004,13 +3422,12 @@ width0pt\relax} \fi % @printindex causes a particular index (the ??s file) to get printed. % It does not print any chapter heading (usually an @unnumbered). % -\def\printindex{\parsearg\doprintindex} -\def\doprintindex#1{\begingroup +\parseargdef\printindex{\begingroup \dobreak \chapheadingskip{10000}% % \smallfonts \rm \tolerance = 9500 - \indexbreaks + \everypar = {}% don't want the \kern\-parindent from indentation suppression. % % See if the index file exists and is nonempty. % Change catcode of @ here so that if the index file contains @@ -3037,7 +3454,7 @@ width0pt\relax} \fi % Index files are almost Texinfo source, but we use \ as the escape % character. It would be better to use @, but that's too big a change % to make right now. - \def\indexbackslash{\rawbackslashxx}% + \def\indexbackslash{\backslashcurfont}% \catcode`\\ = 0 \escapechar = `\\ \begindoublecolumns @@ -3075,74 +3492,94 @@ width0pt\relax} \fi \nobreak }} -% This typesets a paragraph consisting of #1, dot leaders, and then #2 -% flush to the right margin. It is used for index and table of contents -% entries. The paragraph is indented by \leftskip. +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. % -\def\entry#1#2{\begingroup - % - % Start a new paragraph if necessary, so our assignments below can't - % affect previous text. - \par - % - % Do not fill out the last line with white space. - \parfillskip = 0in - % - % No extra space above this paragraph. - \parskip = 0in - % - % Do not prefer a separate line ending with a hyphen to fewer lines. - \finalhyphendemerits = 0 - % - % \hangindent is only relevant when the entry text and page number - % don't both fit on one line. In that case, bob suggests starting the - % dots pretty far over on the line. Unfortunately, a large - % indentation looks wrong when the entry text itself is broken across - % lines. So we use a small indentation and put up with long leaders. - % - % \hangafter is reset to 1 (which is the value we want) at the start - % of each paragraph, so we need not do anything with that. - \hangindent = 2em - % - % When the entry text needs to be broken, just fill out the first line - % with blank space. - \rightskip = 0pt plus1fil - % - % A bit of stretch before each entry for the benefit of balancing columns. - \vskip 0pt plus1pt - % - % Start a ``paragraph'' for the index entry so the line breaking - % parameters we've set above will have an effect. - \noindent - % - % Insert the text of the index entry. TeX will do line-breaking on it. - #1% - % The following is kludged to not output a line of dots in the index if - % there are no page numbers. The next person who breaks this will be - % cursed by a Unix daemon. - \def\tempa{{\rm }}% - \def\tempb{#2}% - \edef\tempc{\tempa}% - \edef\tempd{\tempb}% - \ifx\tempc\tempd\ \else% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this frozes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup % - % If we must, put the page number on a line of its own, and fill out - % this line with blank space. (The \hfil is overwhelmed with the - % fill leaders glue in \indexdotfill if the page number does fit.) - \hfil\penalty50 - \null\nobreak\indexdotfill % Have leaders before the page number. + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par % - % The `\ ' here is removed by the implicit \unskip that TeX does as - % part of (the primitive) \par. Without it, a spurious underfull - % \hbox ensues. - \ifpdf - \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#1}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd + \ % \else - \ #2% The page number ends the paragraph. + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.\ \the\toksA + \else + \ #1% + \fi \fi - \fi% - \par -\endgroup} + \par + \endgroup +} % Like \dotfill except takes at least 1 em. \def\indexdotfill{\cleaders @@ -3253,7 +3690,7 @@ width0pt\relax} \fi \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}% } -% +% % All done with double columns. \def\enddoublecolumns{% \output = {% @@ -3311,6 +3748,12 @@ width0pt\relax} \fi \message{sectioning,} % Chapters, sections, etc. +% \unnumberedno is an oxymoron, of course. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 \newcount\chapno \newcount\secno \secno=0 \newcount\subsecno \subsecno=0 @@ -3318,9 +3761,12 @@ width0pt\relax} \fi % This counter is funny since it counts through charcodes of letters A, B, ... \newcount\appendixno \appendixno = `\@ +% % \def\appendixletter{\char\the\appendixno} -% We do the following for the sake of pdftex, which needs the actual +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual % letter in the expansion, not just typeset. +% \def\appendixletter{% \ifnum\appendixno=`A A% \else\ifnum\appendixno=`B B% @@ -3358,11 +3804,12 @@ width0pt\relax} \fi % Each @chapter defines this as the name of the chapter. % page headings and footings can use it. @section does likewise. +% However, they are not reliable, because we don't use marks. \def\thischapter{} \def\thissection{} \newcount\absseclevel % used to calculate proper heading level -\newcount\secbase\secbase=0 % @raise/lowersections modify this count +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count % @raisesections: treat @section as chapter, @subsection as section, etc. \def\raisesections{\global\advance\secbase by -1} @@ -3377,260 +3824,195 @@ width0pt\relax} \fi % #2 is text for heading \def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 \ifcase\absseclevel - \chapterzzz{#2} -\or - \seczzz{#2} -\or - \numberedsubseczzz{#2} -\or - \numberedsubsubseczzz{#2} -\else - \ifnum \absseclevel<0 - \chapterzzz{#2} + \chapterzzz{#2}% + \or \seczzz{#2}% + \or \numberedsubseczzz{#2}% + \or \numberedsubsubseczzz{#2}% \else - \numberedsubsubseczzz{#2} + \ifnum \absseclevel<0 \chapterzzz{#2}% + \else \numberedsubsubseczzz{#2}% + \fi \fi -\fi + \suppressfirstparagraphindent } % like \numhead, but chooses appendix heading levels \def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 \ifcase\absseclevel - \appendixzzz{#2} -\or - \appendixsectionzzz{#2} -\or - \appendixsubseczzz{#2} -\or - \appendixsubsubseczzz{#2} -\else - \ifnum \absseclevel<0 - \appendixzzz{#2} + \appendixzzz{#2}% + \or \appendixsectionzzz{#2}% + \or \appendixsubseczzz{#2}% + \or \appendixsubsubseczzz{#2}% \else - \appendixsubsubseczzz{#2} + \ifnum \absseclevel<0 \appendixzzz{#2}% + \else \appendixsubsubseczzz{#2}% + \fi \fi -\fi + \suppressfirstparagraphindent } % like \numhead, but chooses numberless heading levels \def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 -\ifcase\absseclevel - \unnumberedzzz{#2} -\or - \unnumberedseczzz{#2} -\or - \unnumberedsubseczzz{#2} -\or - \unnumberedsubsubseczzz{#2} -\else - \ifnum \absseclevel<0 - \unnumberedzzz{#2} + \ifcase\absseclevel + \unnumberedzzz{#2}% + \or \unnumberedseczzz{#2}% + \or \unnumberedsubseczzz{#2}% + \or \unnumberedsubsubseczzz{#2}% \else - \unnumberedsubsubseczzz{#2} + \ifnum \absseclevel<0 \unnumberedzzz{#2}% + \else \unnumberedsubsubseczzz{#2}% + \fi \fi -\fi + \suppressfirstparagraphindent } -% @chapter, @appendix, @unnumbered. -\def\thischaptername{No Chapter Title} -\outer\def\chapter{\parsearg\chapteryyy} -\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz -\def\chapterzzz #1{% -\secno=0 \subsecno=0 \subsubsecno=0 -\global\advance \chapno by 1 \message{\putwordChapter\space \the\chapno}% -\chapmacro {#1}{\the\chapno}% -\gdef\thissection{#1}% -\gdef\thischaptername{#1}% -% We don't substitute the actual chapter name into \thischapter -% because we don't want its macros evaluated now. -\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% - {\the\chapno}}}% -\temp -\donoderef -\global\let\section = \numberedsec -\global\let\subsection = \numberedsubsec -\global\let\subsubsection = \numberedsubsubsec +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + \message{\putwordChapter\space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec } -\outer\def\appendix{\parsearg\appendixyyy} -\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz -\def\appendixzzz #1{% -\secno=0 \subsecno=0 \subsubsecno=0 -\global\advance \appendixno by 1 -\message{\putwordAppendix\space \appendixletter}% -\chapmacro {#1}{\putwordAppendix{} \appendixletter}% -\gdef\thissection{#1}% -\gdef\thischaptername{#1}% -\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% - {\putwordAppendix{} \appendixletter}}}% -\temp -\appendixnoderef -\global\let\section = \appendixsec -\global\let\subsection = \appendixsubsec -\global\let\subsubsection = \appendixsubsubsec +\outer\parseargdef\appendix{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + \def\appendixnum{\putwordAppendix\space \appendixletter}% + \message{\appendixnum}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec } % @centerchap is like @unnumbered, but the heading is centered. -\outer\def\centerchap{\parsearg\centerchapyyy} -\def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}} +\outer\parseargdef\centerchap{{\unnumberedyyy{#1}}} + +\outer\parseargdef\unnumbered{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} % @top is like @unnumbered. -\outer\def\top{\parsearg\unnumberedyyy} - -\outer\def\unnumbered{\parsearg\unnumberedyyy} -\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz -\def\unnumberedzzz #1{% -\secno=0 \subsecno=0 \subsubsecno=0 -% -% This used to be simply \message{#1}, but TeX fully expands the -% argument to \message. Therefore, if #1 contained @-commands, TeX -% expanded them. For example, in `@unnumbered The @cite{Book}', TeX -% expanded @cite (which turns out to cause errors because \cite is meant -% to be executed, not expanded). -% -% Anyway, we don't want the fully-expanded definition of @cite to appear -% as a result of the \message, we just want `@cite' itself. We use -% \the to achieve this: TeX expands \the only once, -% simply yielding the contents of . (We also do this for -% the toc entries.) -\toks0 = {#1}\message{(\the\toks0)}% -% -\unnumbchapmacro {#1}% -\gdef\thischapter{#1}\gdef\thissection{#1}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash unnumbchapentry{\the\toks0}}}% -\temp -\unnumbnoderef -\global\let\section = \unnumberedsec -\global\let\subsection = \unnumberedsubsec -\global\let\subsubsection = \unnumberedsubsubsec -} +\let\top\unnumbered % Sections. -\outer\def\numberedsec{\parsearg\secyyy} -\def\secyyy #1{\numhead1{#1}} % normally calls seczzz -\def\seczzz #1{% -\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % -\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% - {\the\chapno}{\the\secno}}}% -\temp -\donoderef -\nobreak +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% } -\outer\def\appendixsection{\parsearg\appendixsecyyy} -\outer\def\appendixsec{\parsearg\appendixsecyyy} -\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz -\def\appendixsectionzzz #1{% -\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % -\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% - {\appendixletter}{\the\secno}}}% -\temp -\appendixnoderef -\nobreak +\outer\parseargdef\appendixsection{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% } +\let\appendixsec\appendixsection -\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} -\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz -\def\unnumberedseczzz #1{% -\plainsecheading {#1}\gdef\thissection{#1}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsecentry{\the\toks0}}}% -\temp -\unnumbnoderef -\nobreak +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% } % Subsections. -\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} -\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz -\def\numberedsubseczzz #1{% -\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % -\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% - {\the\chapno}{\the\secno}{\the\subsecno}}}% -\temp -\donoderef -\nobreak +\outer\parseargdef\numberedsubsec{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% } -\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} -\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz -\def\appendixsubseczzz #1{% -\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % -\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% - {\appendixletter}{\the\secno}{\the\subsecno}}}% -\temp -\appendixnoderef -\nobreak +\outer\parseargdef\appendixsubsec{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% } -\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} -\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz -\def\unnumberedsubseczzz #1{% -\plainsubsecheading {#1}\gdef\thissection{#1}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsecentry% - {\the\toks0}}}% -\temp -\unnumbnoderef -\nobreak +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% } % Subsubsections. -\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} -\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz -\def\numberedsubsubseczzz #1{% -\gdef\thissection{#1}\global\advance \subsubsecno by 1 % -\subsubsecheading {#1} - {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% - {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% -\temp -\donoderef -\nobreak +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% } -\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} -\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz -\def\appendixsubsubseczzz #1{% -\gdef\thissection{#1}\global\advance \subsubsecno by 1 % -\subsubsecheading {#1} - {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% - {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% -\temp -\appendixnoderef -\nobreak +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% } -\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} -\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz -\def\unnumberedsubsubseczzz #1{% -\plainsubsubsecheading {#1}\gdef\thissection{#1}% -\toks0 = {#1}% -\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsubsecentry% - {\the\toks0}}}% -\temp -\unnumbnoderef -\nobreak +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% } % These are variants which are not "outer", so they can appear in @ifinfo. -% Actually, they should now be obsolete; ordinary section commands should work. +% Actually, they are now be obsolete; ordinary section commands should work. \def\infotop{\parsearg\unnumberedzzz} \def\infounnumbered{\parsearg\unnumberedzzz} \def\infounnumberedsec{\parsearg\unnumberedseczzz} @@ -3650,9 +4032,9 @@ width0pt\relax} \fi % These macros control what the section commands do, according % to what kind of chapter we are in (ordinary, appendix, or unnumbered). % Define them by default for a numbered chapter. -\global\let\section = \numberedsec -\global\let\subsection = \numberedsubsec -\global\let\subsubsection = \numberedsubsubsec +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec % Define @majorheading, @heading and @subheading @@ -3665,23 +4047,27 @@ width0pt\relax} \fi % if justification is not attempted. Hence \raggedright. -\def\majorheading{\parsearg\majorheadingzzz} -\def\majorheadingzzz #1{% -{\advance\chapheadingskip by 10pt \chapbreak }% -{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 - \parindent=0pt\raggedright - \rm #1\hfill}}\bigskip \par\penalty 200} +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} -\def\chapheading{\parsearg\chapheadingzzz} -\def\chapheadingzzz #1{\chapbreak % -{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 - \parindent=0pt\raggedright - \rm #1\hfill}}\bigskip \par\penalty 200} +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}% + \bigskip \par\penalty 200\relax + \suppressfirstparagraphindent +} % @heading, @subheading, @subsubheading. -\def\heading{\parsearg\plainsecheading} -\def\subheading{\parsearg\plainsubsecheading} -\def\subsubheading{\parsearg\plainsubsubsecheading} +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} % These macros generate a chapter, section, etc. heading only % (including whitespace, linebreaking, etc. around it), @@ -3714,7 +4100,7 @@ width0pt\relax} \fi \global\let\pagealignmacro=\chappager \global\def\HEADINGSon{\HEADINGSsingle}} -\def\CHAPPAGodd{ +\def\CHAPPAGodd{% \global\let\contentsalignmacro = \chapoddpage \global\let\pchapsepmacro=\chapoddpage \global\let\pagealignmacro=\chapoddpage @@ -3722,30 +4108,79 @@ width0pt\relax} \fi \CHAPPAGon -\def\CHAPFplain{ +\def\CHAPFplain{% \global\let\chapmacro=\chfplain -\global\let\unnumbchapmacro=\unnchfplain \global\let\centerchapmacro=\centerchfplain} -% Plain chapter opening. -% #1 is the text, #2 the chapter number or empty if unnumbered. -\def\chfplain#1#2{% +% Normal chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chfplain#1#2#3{% \pchapsepmacro {% \chapfonts \rm - \def\chapnum{#2}% - \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}% + % + % Have to define \thissection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\thissection{#1}% + \gdef\thischaptername{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \def\thischapter{#1}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \xdef\thischapter{}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + % We don't substitute the actual chapter name into \thischapter + % because we don't want its macros evaluated now. And we don't + % use \thissection because that changes with each section. + % + \xdef\thischapter{\putwordAppendix{} \appendixletter: + \noexpand\thischaptername}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \xdef\thischapter{\putwordChapter{} \the\chapno: + \noexpand\thischaptername}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright - \hangindent = \wd0 \centerparametersmaybe + \hangindent=\wd0 \centerparametersmaybe \unhbox0 #1\par}% }% \nobreak\bigskip % no page break after a chapter title \nobreak } -% Plain opening for unnumbered. -\def\unnchfplain#1{\chfplain{#1}{}} - % @centerchap -- centered and unnumbered. \let\centerparametersmaybe = \relax \def\centerchfplain#1{{% @@ -3754,11 +4189,14 @@ width0pt\relax} \fi \leftskip = \rightskip \parfillskip = 0pt }% - \chfplain{#1}{}% + \chfplain{#1}{Ynothing}{}% }} \CHAPFplain % The default +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% \def\unnchfopen #1{% \chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 \parindent=0pt\raggedright @@ -3776,53 +4214,96 @@ width0pt\relax} \fi \hfill {\rm #1}\hfill}}\bigskip \par\nobreak } -\def\CHAPFopen{ +\def\CHAPFopen{% \global\let\chapmacro=\chfopen -\global\let\unnumbchapmacro=\unnchfopen \global\let\centerchapmacro=\centerchfopen} -% Section titles. +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% \newskip\secheadingskip -\def\secheadingbreak{\dobreak \secheadingskip {-1000}} -\def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}} -\def\plainsecheading#1{\sectionheading{sec}{}{#1}} +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} % Subsection titles. -\newskip \subsecheadingskip -\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} -\def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}} -\def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}} +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} % Subsubsection titles. -\let\subsubsecheadingskip = \subsecheadingskip -\let\subsubsecheadingbreak = \subsecheadingbreak -\def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}} -\def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}} +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} -% Print any size section title. -% -% #1 is the section type (sec/subsec/subsubsec), #2 is the section -% number (maybe empty), #3 the text. -\def\sectionheading#1#2#3{% - {% - \expandafter\advance\csname #1headingskip\endcsname by \parskip - \csname #1headingbreak\endcsname - }% +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\sectionheading#1#2#3#4{% {% % Switch to the right set of fonts. - \csname #1fonts\endcsname \rm + \csname #2fonts\endcsname \rm % - % Only insert the separating space if we have a section number. - \def\secnum{#2}% - \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}% + % Insert space above the heading. + \csname #2headingbreak\endcsname % + % Only insert the space after the number if we have a section number. + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\thissection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \thissection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\thissection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\thissection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chfplain. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chfplain. + \donoderef{#3}% + % + % Output the actual section heading. \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright - \hangindent = \wd0 % zero if no section number - \unhbox0 #3}% + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% }% - \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) + \vskip-\parskip + % + % This \nobreak is purely so the last item on the list is a \penalty + % of 10000. This is so other code, for instance \parsebodycommon, can + % check for and avoid allowing breakpoints. Otherwise, it would + % insert a valid breakpoint between: + % @section sec-whatever + % @deffn def-whatever + \nobreak } @@ -3831,161 +4312,199 @@ width0pt\relax} \fi \newwrite\tocfile % Write an entry to the toc file, opening it if necessary. -% Called from @chapter, etc. We supply {\folio} at the end of the -% argument, which will end up as the last argument to the \...entry macro. -% -% We open the .toc file here instead of at @setfilename or any other -% given time so that @contents can be put in the document anywhere. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. % \newif\iftocfileopened -\def\writetocentry#1{% - \iftocfileopened\else - \immediate\openout\tocfile = \jobname.toc - \global\tocfileopenedtrue +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + \toks0 = {#2}% + \toks2 = \expandafter{\lastnode}% + \edef\temp{\write\tocfile{\realbackslash #1entry{\the\toks0}{#3}% + {\the\toks2}{\noexpand\folio}}}% + \temp + \fi \fi - \iflinks \write\tocfile{#1{\folio}}\fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi } \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 -% Finish up the main text and prepare to read what we've written -% to \tocfile. +% Prepare to read what we've written to \tocfile. % \def\startcontents#1{% - % If @setchapternewpage on, and @headings double, the contents should - % start on an odd page, unlike chapters. Thus, we maintain - % \contentsalignmacro in parallel with \pagealignmacro. - % From: Torbjorn Granlund - \contentsalignmacro - \immediate\closeout\tocfile - % - % Don't need to put `Contents' or `Short Contents' in the headline. - % It is abundantly clear what they are. - \unnumbchapmacro{#1}\def\thischapter{}% - \savepageno = \pageno - \begingroup % Set up to handle contents files properly. - \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 - % We can't do this, because then an actual ^ in a section - % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97. - %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi - \raggedbottom % Worry more about breakpoints than the bottom. - \advance\hsize by -\contentsrightmargin % Don't use the full line length. - % - % Roman numerals for page numbers. - \ifnum \pageno>0 \pageno = \lastnegativepageno \fi + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \def\thischapter{}% + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + % We can't do this, because then an actual ^ in a section + % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97. + %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi } % Normal (long) toc. \def\contents{% - \startcontents{\putwordTOC}% - \openin 1 \jobname.toc - \ifeof 1 \else - \closein 1 - \input \jobname.toc - \fi - \vfill \eject - \contentsalignmacro % in case @setchapternewpage odd is in effect - \pdfmakeoutlines - \endgroup - \lastnegativepageno = \pageno - \pageno = \savepageno + \startcontents{\putwordTOC}% + \openin 1 \jobname.toc + \ifeof 1 \else + \input \jobname.toc + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno } % And just the chapters. \def\summarycontents{% - \startcontents{\putwordShortTOC}% - % - \let\chapentry = \shortchapentry - \let\unnumbchapentry = \shortunnumberedentry - % We want a true roman here for the page numbers. - \secfonts - \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl - \rm - \hyphenpenalty = 10000 - \advance\baselineskip by 1pt % Open it up a little. - \def\secentry ##1##2##3##4{} - \def\unnumbsecentry ##1##2{} - \def\subsecentry ##1##2##3##4##5{} - \def\unnumbsubsecentry ##1##2{} - \def\subsubsecentry ##1##2##3##4##5##6{} - \def\unnumbsubsubsecentry ##1##2{} - \openin 1 \jobname.toc - \ifeof 1 \else - \closein 1 - \input \jobname.toc - \fi - \vfill \eject - \contentsalignmacro % in case @setchapternewpage odd is in effect - \endgroup - \lastnegativepageno = \pageno - \pageno = \savepageno + \startcontents{\putwordShortTOC}% + % + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \jobname.toc + \ifeof 1 \else + \input \jobname.toc + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno } \let\shortcontents = \summarycontents -\ifpdf - \pdfcatalog{/PageMode /UseOutlines}% -\fi +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} % These macros generate individual entries in the table of contents. % The first argument is the chapter or section name. % The last argument is the page number. % The arguments in between are the chapter number, section number, ... -% Chapter-level things, for both the long and short contents. -\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} - -% See comments in \dochapentry re vbox and related settings -\def\shortchapentry#1#2#3{% - \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#3\egroup}% +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% } -% Typeset the label for a chapter or appendix for the short contents. -% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. -% We could simplify the code here by writing out an \appendixentry -% command in the toc file for appendices, instead of using \chapentry -% for both, but it doesn't seem worth it. +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} % -\newdimen\shortappendixwidth -% -\def\shortchaplabel#1{% - % Compute width of word "Appendix", may change with language. - \setbox0 = \hbox{\shortcontrm \putwordAppendix}% - \shortappendixwidth = \wd0 - % - % We typeset #1 in a box of constant width, regardless of the text of - % #1, so the chapter titles will come out aligned. - \setbox0 = \hbox{#1}% - \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi - % - % This space should be plenty, since a single number is .5em, and the - % widest letter (M) is 1em, at least in the Computer Modern fonts. - % (This space doesn't include the extra space that gets added after - % the label; that gets put in by \shortchapentry above.) - \advance\dimen0 by 1.1em - \hbox to \dimen0{#1\hfil}% -} +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} -\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} -\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno\bgroup#2\egroup}} +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} % Sections. -\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} -\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} % Subsections. -\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} -\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} % And subsubsections. -\def\subsubsecentry#1#2#3#4#5#6{% - \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} -\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} % This parameter controls the indentation of the various levels. -\newdimen\tocindent \tocindent = 3pc +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt % Now for the actual typesetting. In all these, #1 is the text and #2 is the % page number. @@ -4016,17 +4535,8 @@ width0pt\relax} \fi \tocentry{#1}{\dopageno\bgroup#2\egroup}% \endgroup} -% Final typesetting of a toc entry; we use the same \entry macro as for -% the index entries, but we want to suppress hyphenation here. (We -% can't do that in the \entry macro, since index entries might consist -% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) -\def\tocentry#1#2{\begingroup - \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks - % Do not use \turnoffactive in these arguments. Since the toc is - % typeset in cmr, so characters such as _ would come out wrong; we - % have to do the usual translation tricks. - \entry{#1}{#2}% -\endgroup} +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry % Space between chapter (or whatever) number and the title. \def\labelspace{\hskip1em \relax} @@ -4036,72 +4546,61 @@ width0pt\relax} \fi \def\chapentryfonts{\secfonts \rm} \def\secentryfonts{\textfonts} -\let\subsecentryfonts = \textfonts -\let\subsubsecentryfonts = \textfonts +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} \message{environments,} % @foo ... @end foo. +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% % Since these characters are used in examples, it should be an even number of % \tt widths. Each \tt character is 1en, so two makes it 1em. -% Furthermore, these definitions must come after we define our fonts. -\newbox\dblarrowbox \newbox\longdblarrowbox -\newbox\pushcharbox \newbox\bullbox -\newbox\equivbox \newbox\errorbox - -%{\tentt -%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} -%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} -%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} -%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} -% Adapted from the manmac format (p.420 of TeXbook) -%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex -% depth .1ex\hfil} -%} - -% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% \def\point{$\star$} \def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} \def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} \def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} \def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} +% The @error{} command. % Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% {\tentt \global\dimen0 = 3em}% Width of the box. \dimen2 = .55pt % Thickness of rules % The text. (`r' is open on the right, `e' somewhat less so on the left.) \setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} - -\global\setbox\errorbox=\hbox to \dimen0{\hfil +% +\setbox\errorbox=\hbox to \dimen0{\hfil \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. \advance\hsize by -2\dimen2 % Rules. - \vbox{ + \vbox{% \hrule height\dimen2 \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. \kern3pt\vrule width\dimen2}% Space to right. \hrule height\dimen2} \hfil} - -% The @error{} command. +% \def\error{\leavevmode\lower.7ex\copy\errorbox} % @tex ... @end tex escapes into raw Tex temporarily. % One exception: @ is still an escape character, so that @end tex works. % But \@ or @@ will get a plain tex @ character. -\def\tex{\begingroup +\envdef\tex{% \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 - \catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie \catcode `\%=14 - \catcode 43=12 % plus - \catcode`\"=12 - \catcode`\==12 - \catcode`\|=12 - \catcode`\<=12 - \catcode`\>=12 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other \escapechar=`\\ % \let\b=\ptexb @@ -4113,20 +4612,24 @@ width0pt\relax} \fi \let\equiv=\ptexequiv \let\!=\ptexexclam \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent \let\{=\ptexlbrace \let\+=\tabalign \let\}=\ptexrbrace + \let\/=\ptexslash \let\*=\ptexstar \let\t=\ptext % \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% \def\@{@}% -\let\Etex=\endgroup} +} +% There is no need to define \Etex. -% Define @lisp ... @endlisp. -% @lisp does a \begingroup so it can rebind things, -% including the definition of @endlisp (which normally is erroneous). +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). % Amount to narrow the margins by for @lisp. \newskip\lispnarrowing \lispnarrowing=0.4in @@ -4136,34 +4639,24 @@ width0pt\relax} \fi % have any width. \def\lisppar{\null\endgraf} -% Make each space character in the input produce a normal interword -% space in the output. Don't allow a line break at this space, as this -% is used only in environments like @example, where each line of input -% should produce a line of output anyway. -% -{\obeyspaces % -\gdef\sepspaces{\obeyspaces\let =\tie}} - -% Define \obeyedspace to be our active space, whatever it is. This is -% for use in \parsearg. -{\sepspaces% -\global\let\obeyedspace= } - % This space is always present above and below environments. \newskip\envskipamount \envskipamount = 0pt % Make spacing and below environment symmetrical. We use \parskip here % to help in doing that, since in @example-like environments \parskip % is reset to zero; thus the \afterenvbreak inserts no space -- but the -% start of the next paragraph will insert \parskip +% start of the next paragraph will insert \parskip. % \def\aboveenvbreak{{% - \ifnum\lastpenalty < 10000 + % =10000 instead of <10000 because of a special case in \itemzzz, q.v. + \ifnum \lastpenalty=10000 \else \advance\envskipamount by \parskip \endgraf \ifdim\lastskip<\envskipamount \removelastskip - \penalty-50 + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi \vskip\envskipamount \fi \fi @@ -4195,54 +4688,54 @@ width0pt\relax} \fi % \newskip\lskip\newskip\rskip -\long\def\cartouche{% -\begingroup - \lskip=\leftskip \rskip=\rightskip - \leftskip=0pt\rightskip=0pt %we want these *outside*. - \cartinner=\hsize \advance\cartinner by-\lskip - \advance\cartinner by-\rskip - \cartouter=\hsize - \advance\cartouter by 18.4pt % allow for 3pt kerns on either -% side, and for 6pt waste from -% each corner char, and rule thickness - \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip - % Flag to tell @lisp, etc., not to narrow margin. - \let\nonarrowing=\comment - \vbox\bgroup - \baselineskip=0pt\parskip=0pt\lineskip=0pt - \carttop - \hbox\bgroup - \hskip\lskip - \vrule\kern3pt - \vbox\bgroup - \hsize=\cartinner - \kern3pt - \begingroup - \baselineskip=\normbskip - \lineskip=\normlskip - \parskip=\normpskip - \vskip -\parskip +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of \def\group. +} \def\Ecartouche{% - \endgroup - \kern3pt - \egroup - \kern3pt\vrule - \hskip\rskip - \egroup - \cartbot - \egroup -\endgroup -}} + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} % This macro is called at the beginning of all the @example variants, % inside a group. \def\nonfillstart{% \aboveenvbreak - \inENV % This group ends at the end of the body \hfuzz = 12pt % Don't be fussy \sepspaces % Make spaces be word-separators rather than space tokens. - \singlespace \let\par = \lisppar % don't ignore blank lines \obeylines % each line of input is a line of output \parskip = 0pt @@ -4253,115 +4746,97 @@ width0pt\relax} \fi \ifx\nonarrowing\relax \advance \leftskip by \lispnarrowing \exdentamount=\lispnarrowing - \let\exdent=\nofillexdent - \let\nonarrowing=\relax + \fi + \let\exdent=\nofillexdent +} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \smallexamplefonts \rm \fi } -% Define the \E... control sequence only if we are inside the particular -% environment, so the error checking in \end will work. -% -% To end an @example-like environment, we first end the paragraph (via -% \afterenvbreak's vertical glue), and then the group. That way we keep -% the zero \parskip that the environments set -- \parskip glue will be -% inserted at the beginning of the next paragraph in the document, after -% the environment. -% -\def\nonfillfinish{\afterenvbreak\endgroup} +% We often define two environments, @foo and @smallfoo. +% Let's do it by one command: +\def\makedispenv #1#2{ + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2} + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2} + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} -% @lisp: indented, narrowed, typewriter font. -\def\lisp{\begingroup +% Define two synonyms: +\def\maketwodispenvs #1#2#3{ + \makedispenv{#1}{#3} + \makedispenv{#2}{#3} +} + +% @lisp: indented, narrowed, typewriter font; @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvs {lisp}{example}{% \nonfillstart - \let\Elisp = \nonfillfinish \tt \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. \gobble % eat return } -% @example: Same as @lisp. -\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} - -% @small... is usually equivalent to the non-small (@smallbook -% redefines). We must call \example (or whatever) last in the -% definition, since it reads the return following the @example (or -% whatever) command. +% @display/@smalldisplay: same as @lisp except keep current font. % -% This actually allows (for example) @end display inside an -% @smalldisplay. Too bad, but makeinfo will catch the error anyway. -% -\def\smalldisplay{\begingroup\def\Esmalldisplay{\nonfillfinish\endgroup}\display} -\def\smallexample{\begingroup\def\Esmallexample{\nonfillfinish\endgroup}\lisp} -\def\smallformat{\begingroup\def\Esmallformat{\nonfillfinish\endgroup}\format} -\def\smalllisp{\begingroup\def\Esmalllisp{\nonfillfinish\endgroup}\lisp} - -% Real @smallexample and @smalllisp (when @smallbook): use smaller fonts. -% Originally contributed by Pavel@xerox. -\def\smalllispx{\begingroup - \def\Esmalllisp{\nonfillfinish\endgroup}% - \def\Esmallexample{\nonfillfinish\endgroup}% - \smallfonts - \lisp -} - -% @display: same as @lisp except keep current font. -% -\def\display{\begingroup +\makedispenv {display}{% \nonfillstart - \let\Edisplay = \nonfillfinish \gobble } -% @smalldisplay (when @smallbook): @display plus smaller fonts. +% @format/@smallformat: same as @display except don't narrow margins. % -\def\smalldisplayx{\begingroup - \def\Esmalldisplay{\nonfillfinish\endgroup}% - \smallfonts \rm - \display -} - -% @format: same as @display except don't narrow margins. -% -\def\format{\begingroup - \let\nonarrowing = t +\makedispenv{format}{% + \let\nonarrowing = t% \nonfillstart - \let\Eformat = \nonfillfinish \gobble } -% @smallformat (when @smallbook): @format plus smaller fonts. -% -\def\smallformatx{\begingroup - \def\Esmallformat{\nonfillfinish\endgroup}% - \smallfonts \rm - \format +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble } - -% @flushleft (same as @format). -% -\def\flushleft{\begingroup \def\Eflushleft{\nonfillfinish\endgroup}\format} +\let\Eflushleft = \afterenvbreak % @flushright. % -\def\flushright{\begingroup - \let\nonarrowing = t +\envdef\flushright{% + \let\nonarrowing = t% \nonfillstart - \let\Eflushright = \nonfillfinish \advance\leftskip by 0pt plus 1fill \gobble } +\let\Eflushright = \afterenvbreak % @quotation does normal linebreaking (hence we can't use \nonfillstart) % and narrows the margins. % -\def\quotation{% - \begingroup\inENV %This group ends at the end of the @quotation body +\envdef\quotation{% {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip - \singlespace \parindent=0pt - % We have retained a nonzero parskip for the environment, since we're - % doing normal filling. So to avoid extra space below the environment... - \def\Equotation{\parskip = 0pt \nonfillfinish}% % % @cartouche defines \nonarrowing to inhibit narrowing at next level down. \ifx\nonarrowing\relax @@ -4370,24 +4845,41 @@ width0pt\relax} \fi \exdentamount = \lispnarrowing \let\nonarrowing = \relax \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. So to avoid extra space below the environment... +\def\Equotation{\parskip = 0pt \afterenvbreak} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi } % LaTeX-like @verbatim...@end verbatim and @verb{...} -% If we want to allow any as delimiter, +% If we want to allow any as delimiter, % we need the curly braces so that makeinfo sees the @verb command, eg: % `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org % % [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. % -% [Knuth] p. 344; only we need to do '@' too +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. \def\dospecials{% - \do\ \do\\\do\@\do\{\do\}\do\$\do\&% - \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~} + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% +} % % [Knuth] p. 380 \def\uncatcodespecials{% - \def\do##1{\catcode`##1=12}\dospecials} + \def\do##1{\catcode`##1=\other}\dospecials} % % [Knuth] pp. 380,381,391 % Disable Spanish ligatures ?` and !` of \tt font @@ -4435,6 +4927,8 @@ width0pt\relax} \fi } \endgroup \def\setupverbatim{% + \nonfillstart + \advance\leftskip by -\defbodyindent % Easiest (and conventionally used) font for verbatim \tt \def\par{\leavevmode\egroup\box0\endgraf}% @@ -4448,15 +4942,15 @@ width0pt\relax} \fi \everypar{\starttabbox}% } -% Do the @verb magic: verbatim text is quoted by unique -% delimiter characters. Before first delimiter expect a +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a % right brace, after last delimiter expect closing brace: % % \def\doverb'{'#1'}'{#1} % % [Knuth] p. 382; only eat outer {} \begingroup - \catcode`[=1\catcode`]=2\catcode`\{=12\catcode`\}=12 + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] \endgroup % @@ -4468,620 +4962,443 @@ width0pt\relax} \fi % % \def\doverbatim#1@end verbatim{#1} % -% For Texinfo it's a lot easier than for LaTeX, +% For Texinfo it's a lot easier than for LaTeX, % because texinfo's \verbatim doesn't stop at '\end{verbatim}': -% we need not redefine '\', '{' and '}' +% we need not redefine '\', '{' and '}'. % % Inspired by LaTeX's verbatim command set [latex.ltx] -%% Include LaTeX hack for completeness -- never know -%% \begingroup -%% \catcode`|=0 \catcode`[=1 -%% \catcode`]=2\catcode`\{=12\catcode`\}=12\catcode`\ =\active -%% \catcode`\\=12|gdef|doverbatim#1@end verbatim[ -%% #1|endgroup|def|Everbatim[]|end[verbatim]] -%% |endgroup +% \begingroup \catcode`\ =\active - \gdef\doverbatim#1@end verbatim{#1\end{verbatim}} + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. \endgroup % -\def\verbatim{% - \def\Everbatim{\nonfillfinish\endgroup}% - \begingroup - \nonfillstart - \advance\leftskip by -\defbodyindent - \begingroup\setupverbatim\doverbatim +\envdef\verbatim{% + \setupverbatim\doverbatim } +\let\Everbatim = \afterenvbreak + % @verbatiminclude FILE - insert text of file in verbatim environment. % -% Allow normal characters that we make active in the argument (a file name). -\def\verbatiminclude{% - \begingroup - \catcode`\\=12 - \catcode`~=12 - \catcode`^=12 - \catcode`_=12 - \catcode`|=12 - \catcode`<=12 - \catcode`>=12 - \catcode`+=12 - \parsearg\doverbatiminclude -} -\def\setupverbatiminclude{% - \begingroup - \nonfillstart - \advance\leftskip by -\defbodyindent - \begingroup\setupverbatim -} +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} % \def\doverbatiminclude#1{% - % Restore active chars for included file. - \endgroup - \begingroup - \def\thisfile{#1}% - \expandafter\expandafter\setupverbatiminclude\input\thisfile - \endgroup\nonfillfinish\endgroup + {% + \makevalueexpandable + \setupverbatim + \input #1 + \afterenvbreak + }% } +% @copying ... @end copying. +% Save the text away for @insertcopying later. Many commands won't be +% allowed in this context, but that's ok. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\begingroup + % Define a command to swallow text until we reach `@end copying'. + % \ is the escape char in this texinfo.tex file, so it is the + % delimiter for the command; @ will be the escape char when we read + % it, but that doesn't matter. + \long\def\docopying##1\end copying{\gdef\copyingtext{##1}\enddocopying}% + % + % We must preserve ^^M's in the input file; see \insertcopying below. + \catcode`\^^M = \active + \docopying +} + +% What we do to finish off the copying text. +% +\def\enddocopying{\endgroup\ignorespaces} + +% @insertcopying. Here we must play games with ^^M's. On the one hand, +% we need them to delimit commands such as `@end quotation', so they +% must be active. On the other hand, we certainly don't want every +% end-of-line to be a \par, as would happen with the normal active +% definition of ^^M. On the third hand, two ^^M's in a row should still +% generate a \par. +% +% Our approach is to make ^^M insert a space and a penalty1 normally; +% then it can also check if \lastpenalty=1. If it does, then manually +% do \par. +% +% This messes up the normal definitions of @c[omment], so we redefine +% it. Similarly for @ignore. (These commands are used in the gcc +% manual for man page generation.) +% +% Seems pretty fragile, most line-oriented commands will presumably +% fail, but for the limited use of getting the copying text (which +% should be quite simple) inserted, we can hope it's ok. +% +{\catcode`\^^M=\active % +\gdef\insertcopying{\begingroup % + \parindent = 0pt % looks wrong on title page + \def^^M{% + \ifnum \lastpenalty=1 % + \par % + \else % + \space \penalty 1 % + \fi % + }% + % + % Fix @c[omment] for catcode 13 ^^M's. + \def\c##1^^M{\ignorespaces}% + \let\comment = \c % + % + % Don't bother jumping through all the hoops that \doignore does, it + % would be very hard since the catcodes are already set. + \long\def\ignore##1\end ignore{\ignorespaces}% + % + \copyingtext % +\endgroup}% +} \message{defuns,} % @defun etc. -% Allow user to change definition object font (\df) internally -\def\setdeffont #1 {\csname DEF#1\endcsname} - \newskip\defbodyindent \defbodyindent=.4in \newskip\defargsindent \defargsindent=50pt -\newskip\deftypemargin \deftypemargin=12pt \newskip\deflastargmargin \deflastargmargin=18pt -\newcount\parencount -% define \functionparens, which makes ( and ) and & do special things. -% \functionparens affects the group it is contained in. +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check for penalty 10002 (inserted by + % \defargscommonending) instead of 10000, since the sectioning + % commands insert a \penalty10000, and we don't want to allow a break + % between a section heading and a defun. + \ifnum\lastpenalty=10002 \penalty2000 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil + \endgraf + \nobreak\vskip -\parskip + \penalty 10002 % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remainnig is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +%%% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +%%% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +%%% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +%%% Type: +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % How we'll format the type name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % (plain.tex says that \dimen1 should be used only as global.) + \parshape 2 0in \dimen0 \defargsindent \dimen2 + % + % Put the type name to the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% return value type + \ifx\temp\empty\else \tclose{\temp} \fi + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted typewriter, prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), inconsistently with using tt for the + % name. This is because literal text is sometimes needed in the + % argument list (groff manual), and ttsl and tt are not very + % distinguishable. + % tt for the names. + \df \sl \hyphenchar\font=0 + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. Let's try @var for that. + \let\var=\ttslanted + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% \def\activeparens{% -\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active -\catcode`\[=\active \catcode`\]=\active} + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} % Make control sequences which act like normal parenthesis chars. \let\lparen = ( \let\rparen = ) -{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) - % Be sure that we always have a definition for `(', etc. For example, % if the fn name has parens in it, \boldbrax will not be in effect yet, % so TeX would otherwise complain about undefined control sequence. -\global\let(=\lparen \global\let)=\rparen -\global\let[=\lbrack \global\let]=\rbrack - -\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } -\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} -% This is used to turn on special parens -% but make & act ordinary (given that it's active). -\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr} - -% Definitions of (, ) and & used in args for functions. -% This is the definition of ( outside of all parentheses. -\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested - \global\advance\parencount by 1 -} -% -% This is the definition of ( when already inside a level of parens. -\gdef\opnested{\char`\(\global\advance\parencount by 1 } -% -\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. - % also in that case restore the outer-level definition of (. - \ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi - \global\advance \parencount by -1 } -% If we encounter &foo, then turn on ()-hacking afterwards -\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } -% -\gdef\normalparens{\boldbrax\let&=\ampnr} -} % End of definition inside \activeparens -%% These parens (in \boldbrax) actually are a little bolder than the -%% contained text. This is especially needed for [ and ] -\def\opnr{{\sf\char`\(}\global\advance\parencount by 1 } -\def\clnr{{\sf\char`\)}\global\advance\parencount by -1 } -\let\ampnr = \& -\def\lbrb{{\bf\char`\[}} -\def\rbrb{{\bf\char`\]}} - -% Active &'s sneak into the index arguments, so make sure it's defined. { - \catcode`& = 13 - \global\let& = \ampnr + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} } -% First, defname, which formats the header line itself. -% #1 should be the function name. -% #2 should be the type of definition, such as "Function". +\newcount\parencount -\def\defname #1#2{% -% Get the values of \leftskip and \rightskip as they were -% outside the @def... -\dimen2=\leftskip -\advance\dimen2 by -\defbodyindent -\noindent -\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% -\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line -\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations -\parshape 2 0in \dimen0 \defargsindent \dimen1 -% Now output arg 2 ("Function" or some such) -% ending at \deftypemargin from the right margin, -% but stuck inside a box of width 0 so it does not interfere with linebreaking -{% Adjust \hsize to exclude the ambient margins, -% so that \rightline will obey them. -\advance \hsize by -\dimen2 -\rlap{\rightline{{\rm #2}\hskip -1.25pc }}}% -% Make all lines underfull and no complaints: -\tolerance=10000 \hbadness=10000 -\advance\leftskip by -\defbodyindent -\exdentamount=\defbodyindent -{\df #1}\enskip % Generate function name +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 } -% Actually process the body of a definition -% #1 should be the terminating control sequence, such as \Edefun. -% #2 should be the "another name" control sequence, such as \defunx. -% #3 should be the control sequence that actually processes the header, -% such as \defunheader. - -\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody -\medbreak % -% Define the end token that this defining construct specifies -% so that it will exit this group. -\def#1{\endgraf\endgroup\medbreak}% -\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% -\parindent=0in -\advance\leftskip by \defbodyindent -\exdentamount=\defbodyindent -\begingroup % -\catcode 61=\active % 61 is `=' -\obeylines\activeparens\spacesplit#3} - -% #1 is the \E... control sequence to end the definition (which we define). -% #2 is the \...x control sequence for consecutive fns (which we define). -% #3 is the control sequence to call to resume processing. -% #4, delimited by the space, is the class name. -% -\def\defmethparsebody#1#2#3#4 {\begingroup\inENV % -\medbreak % -% Define the end token that this defining construct specifies -% so that it will exit this group. -\def#1{\endgraf\endgroup\medbreak}% -\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% -\parindent=0in -\advance\leftskip by \defbodyindent -\exdentamount=\defbodyindent -\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} - -% Used for @deftypemethod and @deftypeivar. -% #1 is the \E... control sequence to end the definition (which we define). -% #2 is the \...x control sequence for consecutive fns (which we define). -% #3 is the control sequence to call to resume processing. -% #4, delimited by a space, is the class name. -% #5 is the method's return type. -% -\def\deftypemethparsebody#1#2#3#4 #5 {\begingroup\inENV - \medbreak - \def#1{\endgraf\endgroup\medbreak}% - \def#2##1 ##2 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}{##2}}}% - \parindent=0in - \advance\leftskip by \defbodyindent - \exdentamount=\defbodyindent - \begingroup\obeylines\activeparens\spacesplit{#3{#4}{#5}}} - -% Used for @deftypeop. The change from \deftypemethparsebody is an -% extra argument at the beginning which is the `category', instead of it -% being the hardwired string `Method' or `Instance Variable'. We have -% to account for this both in the \...x definition and in parsing the -% input at hand. Thus also need a control sequence (passed as #5) for -% the \E... definition to assign the category name to. -% -\def\deftypeopparsebody#1#2#3#4#5 #6 {\begingroup\inENV - \medbreak - \def#1{\endgraf\endgroup\medbreak}% - \def#2##1 ##2 ##3 {% - \def#4{##1}% - \begingroup\obeylines\activeparens\spacesplit{#3{##2}{##3}}}% - \parindent=0in - \advance\leftskip by \defbodyindent - \exdentamount=\defbodyindent - \begingroup\obeylines\activeparens\spacesplit{#3{#5}{#6}}} - -\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % -\medbreak % -% Define the end token that this defining construct specifies -% so that it will exit this group. -\def#1{\endgraf\endgroup\medbreak}% -\def#2##1 ##2 {\def#4{##1}% -\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% -\parindent=0in -\advance\leftskip by \defbodyindent -\exdentamount=\defbodyindent -\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} - -% These parsing functions are similar to the preceding ones -% except that they do not make parens into active characters. -% These are used for "variables" since they have no arguments. - -\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody -\medbreak % -% Define the end token that this defining construct specifies -% so that it will exit this group. -\def#1{\endgraf\endgroup\medbreak}% -\def#2{\begingroup\obeylines\spacesplit#3}% -\parindent=0in -\advance\leftskip by \defbodyindent -\exdentamount=\defbodyindent -\begingroup % -\catcode 61=\active % -\obeylines\spacesplit#3} - -% This is used for \def{tp,vr}parsebody. It could probably be used for -% some of the others, too, with some judicious conditionals. -% -\def\parsebodycommon#1#2#3{% - \begingroup\inENV % - \medbreak % - % Define the end token that this defining construct specifies - % so that it will exit this group. - \def#1{\endgraf\endgroup\medbreak}% - \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% - \parindent=0in - \advance\leftskip by \defbodyindent - \exdentamount=\defbodyindent - \begingroup\obeylines +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 } -\def\defvrparsebody#1#2#3#4 {% - \parsebodycommon{#1}{#2}{#3}% - \spacesplit{#3{#4}}% +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi } - -% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the -% type is just `struct', because we lose the braces in `{struct -% termios}' when \spacesplit reads its undelimited argument. Sigh. -% \let\deftpparsebody=\defvrparsebody -% -% So, to get around this, we put \empty in with the type name. That -% way, TeX won't find exactly `{...}' as an undelimited argument, and -% won't strip off the braces. -% -\def\deftpparsebody #1#2#3#4 {% - \parsebodycommon{#1}{#2}{#3}% - \spacesplit{\parsetpheaderline{#3{#4}}}\empty +\def\badparencount{% + \errmessage{Unbalanced parentheses in @def}% + \global\parencount=0 } - -% Fine, but then we have to eventually remove the \empty *and* the -% braces (if any). That's what this does. -% -\def\removeemptybraces\empty#1\relax{#1} - -% After \spacesplit has done its work, this is called -- #1 is the final -% thing to call, #2 the type name (which starts with \empty), and #3 -% (which might be empty) the arguments. -% -\def\parsetpheaderline#1#2#3{% - #1{\removeemptybraces#2\relax}{#3}% -}% - -\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % -\medbreak % -% Define the end token that this defining construct specifies -% so that it will exit this group. -\def#1{\endgraf\endgroup\medbreak}% -\def#2##1 ##2 {\def#4{##1}% -\begingroup\obeylines\spacesplit{#3{##2}}}% -\parindent=0in -\advance\leftskip by \defbodyindent -\exdentamount=\defbodyindent -\begingroup\obeylines\spacesplit{#3{#5}}} - -% Split up #2 at the first space token. -% call #1 with two arguments: -% the first is all of #2 before the space token, -% the second is all of #2 after that space token. -% If #2 contains no space token, all of it is passed as the first arg -% and the second is passed as empty. - -{\obeylines -\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% -\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% -\ifx\relax #3% -#1{#2}{}\else #1{#2}{#3#4}\fi}} - -% So much for the things common to all kinds of definitions. - -% Define @defun. - -% First, define the processing that is wanted for arguments of \defun -% Use this to expand the args and terminate the paragraph they make up - -\def\defunargs#1{\functionparens \sl -% Expand, preventing hyphenation at `-' chars. -% Note that groups don't affect changes in \hyphenchar. -% Set the font temporarily and use \font in case \setfont made \tensl a macro. -{\tensl\hyphenchar\font=0}% -#1% -{\tensl\hyphenchar\font=45}% -\ifnum\parencount=0 \else \errmessage{Unbalanced parentheses in @def}\fi% -\interlinepenalty=10000 -\advance\rightskip by 0pt plus 1fil -\endgraf\nobreak\vskip -\parskip\nobreak +\def\badbrackcount{% + \errmessage{Unbalanced square braces in @def}% + \global\brackcount=0 } -\def\deftypefunargs #1{% -% Expand, preventing hyphenation at `-' chars. -% Note that groups don't affect changes in \hyphenchar. -% Use \boldbraxnoamp, not \functionparens, so that & is not special. -\boldbraxnoamp -\tclose{#1}% avoid \code because of side effects on active chars -\interlinepenalty=10000 -\advance\rightskip by 0pt plus 1fil -\endgraf\nobreak\vskip -\parskip\nobreak -} - -% Do complete processing of one @defun or @defunx line already parsed. - -% @deffn Command forward-char nchars - -\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} - -\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% -\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @defun == @deffn Function - -\def\defun{\defparsebody\Edefun\defunx\defunheader} - -\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index -\begingroup\defname {#1}{\putwordDeffunc}% -\defunargs {#2}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @deftypefun int foobar (int @var{foo}, float @var{bar}) - -\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} - -% #1 is the data type. #2 is the name and args. -\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} -% #1 is the data type, #2 the name, #3 the args. -\def\deftypefunheaderx #1#2 #3\relax{% -\doind {fn}{\code{#2}}% Make entry in function index -\begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypefun}% -\deftypefunargs {#3}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) - -\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} - -% \defheaderxcond#1\relax$$$ -% puts #1 in @code, followed by a space, but does nothing if #1 is null. -\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} - -% #1 is the classification. #2 is the data type. #3 is the name and args. -\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} -% #1 is the classification, #2 the data type, #3 the name, #4 the args. -\def\deftypefnheaderx #1#2#3 #4\relax{% -\doind {fn}{\code{#3}}% Make entry in function index -\begingroup -\normalparens % notably, turn off `&' magic, which prevents -% at least some C++ text from working -\defname {\defheaderxcond#2\relax$$$#3}{#1}% -\deftypefunargs {#4}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @defmac == @deffn Macro - -\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} - -\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index -\begingroup\defname {#1}{\putwordDefmac}% -\defunargs {#2}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @defspec == @deffn Special Form - -\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} - -\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index -\begingroup\defname {#1}{\putwordDefspec}% -\defunargs {#2}\endgroup % -\catcode 61=\other % Turn off change made in \defparsebody -} - -% @defop CATEGORY CLASS OPERATION ARG... -% -\def\defop #1 {\def\defoptype{#1}% -\defopparsebody\Edefop\defopx\defopheader\defoptype} -% -\def\defopheader#1#2#3{% -\dosubind {fn}{\code{#2}}{\putwordon\ #1}% Make entry in function index -\begingroup\defname {#2}{\defoptype\ \putwordon\ #1}% -\defunargs {#3}\endgroup % -} - -% @deftypeop CATEGORY CLASS TYPE OPERATION ARG... -% -\def\deftypeop #1 {\def\deftypeopcategory{#1}% - \deftypeopparsebody\Edeftypeop\deftypeopx\deftypeopheader - \deftypeopcategory} -% -% #1 is the class name, #2 the data type, #3 the operation name, #4 the args. -\def\deftypeopheader#1#2#3#4{% - \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index - \begingroup - \defname{\defheaderxcond#2\relax$$$#3} - {\deftypeopcategory\ \putwordon\ \code{#1}}% - \deftypefunargs{#4}% - \endgroup -} - -% @deftypemethod CLASS TYPE METHOD ARG... -% -\def\deftypemethod{% - \deftypemethparsebody\Edeftypemethod\deftypemethodx\deftypemethodheader} -% -% #1 is the class name, #2 the data type, #3 the method name, #4 the args. -\def\deftypemethodheader#1#2#3#4{% - \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index - \begingroup - \defname{\defheaderxcond#2\relax$$$#3}{\putwordMethodon\ \code{#1}}% - \deftypefunargs{#4}% - \endgroup -} - -% @deftypeivar CLASS TYPE VARNAME -% -\def\deftypeivar{% - \deftypemethparsebody\Edeftypeivar\deftypeivarx\deftypeivarheader} -% -% #1 is the class name, #2 the data type, #3 the variable name. -\def\deftypeivarheader#1#2#3{% - \dosubind{vr}{\code{#3}}{\putwordof\ \code{#1}}% entry in variable index - \begingroup - \defname{\defheaderxcond#2\relax$$$#3} - {\putwordInstanceVariableof\ \code{#1}}% - \defvarargs{#3}% - \endgroup -} - -% @defmethod == @defop Method -% -\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} -% -% #1 is the class name, #2 the method name, #3 the args. -\def\defmethodheader#1#2#3{% - \dosubind{fn}{\code{#2}}{\putwordon\ \code{#1}}% entry in function index - \begingroup - \defname{#2}{\putwordMethodon\ \code{#1}}% - \defunargs{#3}% - \endgroup -} - -% @defcv {Class Option} foo-class foo-flag - -\def\defcv #1 {\def\defcvtype{#1}% -\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} - -\def\defcvarheader #1#2#3{% -\dosubind {vr}{\code{#2}}{\putwordof\ #1}% Make entry in var index -\begingroup\defname {#2}{\defcvtype\ \putwordof\ #1}% -\defvarargs {#3}\endgroup % -} - -% @defivar CLASS VARNAME == @defcv {Instance Variable} CLASS VARNAME -% -\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} -% -\def\defivarheader#1#2#3{% - \dosubind {vr}{\code{#2}}{\putwordof\ #1}% entry in var index - \begingroup - \defname{#2}{\putwordInstanceVariableof\ #1}% - \defvarargs{#3}% - \endgroup -} - -% @defvar -% First, define the processing that is wanted for arguments of @defvar. -% This is actually simple: just print them in roman. -% This must expand the args and terminate the paragraph they make up -\def\defvarargs #1{\normalparens #1% -\interlinepenalty=10000 -\endgraf\nobreak\vskip -\parskip\nobreak} - -% @defvr Counter foo-count - -\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} - -\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% -\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} - -% @defvar == @defvr Variable - -\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} - -\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index -\begingroup\defname {#1}{\putwordDefvar}% -\defvarargs {#2}\endgroup % -} - -% @defopt == @defvr {User Option} - -\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} - -\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index -\begingroup\defname {#1}{\putwordDefopt}% -\defvarargs {#2}\endgroup % -} - -% @deftypevar int foobar - -\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} - -% #1 is the data type. #2 is the name, perhaps followed by text that -% is actually part of the data type, which should not be put into the index. -\def\deftypevarheader #1#2{% -\dovarind#2 \relax% Make entry in variables index -\begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypevar}% -\interlinepenalty=10000 -\endgraf\nobreak\vskip -\parskip\nobreak -\endgroup} -\def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}} - -% @deftypevr {Global Flag} int enable - -\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} - -\def\deftypevrheader #1#2#3{\dovarind#3 \relax% -\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} -\interlinepenalty=10000 -\endgraf\nobreak\vskip -\parskip\nobreak -\endgroup} - -% Now define @deftp -% Args are printed in bold, a slight difference from @defvar. - -\def\deftpargs #1{\bf \defvarargs{#1}} - -% @deftp Class window height width ... - -\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} - -\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% -\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} - -% These definitions are used if you use @defunx (etc.) -% anywhere other than immediately after a @defun or @defunx. -% -\def\defcvx#1 {\errmessage{@defcvx in invalid context}} -\def\deffnx#1 {\errmessage{@deffnx in invalid context}} -\def\defivarx#1 {\errmessage{@defivarx in invalid context}} -\def\defmacx#1 {\errmessage{@defmacx in invalid context}} -\def\defmethodx#1 {\errmessage{@defmethodx in invalid context}} -\def\defoptx #1 {\errmessage{@defoptx in invalid context}} -\def\defopx#1 {\errmessage{@defopx in invalid context}} -\def\defspecx#1 {\errmessage{@defspecx in invalid context}} -\def\deftpx#1 {\errmessage{@deftpx in invalid context}} -\def\deftypefnx#1 {\errmessage{@deftypefnx in invalid context}} -\def\deftypefunx#1 {\errmessage{@deftypefunx in invalid context}} -\def\deftypeivarx#1 {\errmessage{@deftypeivarx in invalid context}} -\def\deftypemethodx#1 {\errmessage{@deftypemethodx in invalid context}} -\def\deftypeopx#1 {\errmessage{@deftypeopx in invalid context}} -\def\deftypevarx#1 {\errmessage{@deftypevarx in invalid context}} -\def\deftypevrx#1 {\errmessage{@deftypevrx in invalid context}} -\def\defunx#1 {\errmessage{@defunx in invalid context}} -\def\defvarx#1 {\errmessage{@defvarx in invalid context}} -\def\defvrx#1 {\errmessage{@defvrx in invalid context}} - \message{macros,} % @macro. @@ -5089,28 +5406,33 @@ width0pt\relax} \fi % To do this right we need a feature of e-TeX, \scantokens, % which we arrange to emulate with a temporary file in ordinary TeX. \ifx\eTeXversion\undefined - \newwrite\macscribble - \def\scanmacro#1{% - \begingroup \newlinechar`\^^M - % Undo catcode changes of \startcontents and \doprintindex - \catcode`\@=0 \catcode`\\=12 \escapechar=`\@ - % Append \endinput to make sure that TeX does not see the ending newline. - \toks0={#1\endinput}% - \immediate\openout\macscribble=\jobname.tmp - \immediate\write\macscribble{\the\toks0}% - \immediate\closeout\macscribble - \let\xeatspaces\eatspaces - \input \jobname.tmp - \endgroup -} -\else -\def\scanmacro#1{% -\begingroup \newlinechar`\^^M -% Undo catcode changes of \startcontents and \doprintindex -\catcode`\@=0 \catcode`\\=12 \escapechar=`\@ -\let\xeatspaces\eatspaces\scantokens{#1\endinput}\endgroup} + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1\endinput}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } \fi +\def\scanmacro#1{% + \begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % Undo catcode changes of \startcontents and \doprintindex + \catcode`\@=0 \catcode`\\=\other \escapechar=`\@ + % ... and \example + \spaceisspace + % + % Append \endinput to make sure that TeX does not see the ending newline. + % + % I've verified that it is necessary both for e-TeX and for ordinary TeX + % --kasal, 29nov03 + \scantokens{#1\endinput}% + \endgroup +} + \newcount\paramno % Count of parameters \newtoks\macname % Macro name \newif\ifrecursive % Is it recursive? @@ -5118,7 +5440,7 @@ width0pt\relax} \fi % \do\macro1\do\macro2... % Utility routines. -% Thisdoes \let #1 = #2, except with \csnames. +% This does \let #1 = #2, except with \csnames. \def\cslet#1#2{% \expandafter\expandafter \expandafter\let @@ -5137,7 +5459,7 @@ width0pt\relax} \fi } % Trim a single trailing ^^M off a string. -{\catcode`\^^M=12\catcode`\Q=3% +{\catcode`\^^M=\other \catcode`\Q=3% \gdef\eatcr #1{\eatcra #1Q^^MQ}% \gdef\eatcra#1^^MQ{\eatcrb#1Q}% \gdef\eatcrb#1Q#2Q{#1}% @@ -5152,29 +5474,29 @@ width0pt\relax} \fi % body, and then making it the \newlinechar in \scanmacro. \def\macrobodyctxt{% - \catcode`\~=12 - \catcode`\^=12 - \catcode`\_=12 - \catcode`\|=12 - \catcode`\<=12 - \catcode`\>=12 - \catcode`\+=12 - \catcode`\{=12 - \catcode`\}=12 - \catcode`\@=12 - \catcode`\^^M=12 + \catcode`\~=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\+=\other + \catcode`\{=\other + \catcode`\}=\other + \catcode`\@=\other + \catcode`\^^M=\other \usembodybackslash} \def\macroargctxt{% - \catcode`\~=12 - \catcode`\^=12 - \catcode`\_=12 - \catcode`\|=12 - \catcode`\<=12 - \catcode`\>=12 - \catcode`\+=12 - \catcode`\@=12 - \catcode`\\=12} + \catcode`\~=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\+=\other + \catcode`\@=\other + \catcode`\\=\other} % \mbodybackslash is the definition of \ in @macro bodies. % It maps \foo\ => \csname macarg.foo\endcsname => #N @@ -5202,7 +5524,7 @@ width0pt\relax} \fi \message{Warning: redefining \the\macname}% \else \expandafter\ifx\csname \the\macname\endcsname \relax - \else \errmessage{The name \the\macname\space is reserved}\fi + \else \errmessage{Macro name \the\macname\space already defined}\fi \global\cslet{macsave.\the\macname}{\the\macname}% \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% % Add the macroname to \macrolist @@ -5215,32 +5537,32 @@ width0pt\relax} \fi \else \expandafter\parsemacbody \fi} -\def\unmacro{\parsearg\unmacroxxx} -\def\unmacroxxx#1{% +\parseargdef\unmacro{% \if1\csname ismacro.#1\endcsname \global\cslet{#1}{macsave.#1}% \global\expandafter\let \csname ismacro.#1\endcsname=0% - % Remove the macro name from \macrolist + % Remove the macro name from \macrolist: \begingroup - \edef\tempa{\expandafter\noexpand\csname#1\endcsname}% - \def\do##1{% - \def\tempb{##1}% - \ifx\tempa\tempb - % remove this - \else - \toks0 = \expandafter{\newmacrolist\do}% - \edef\newmacrolist{\the\toks0\expandafter\noexpand\tempa}% - \fi}% - \def\newmacrolist{}% - % Execute macro list to define \newmacrolist - \macrolist - \global\let\macrolist\newmacrolist + \expandafter\let\csname#1\endcsname \relax + \let\do\unmacrodo + \xdef\macrolist{\macrolist}% \endgroup \else \errmessage{Macro #1 not defined}% \fi } +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx#1\relax + % remove this + \else + \noexpand\do \noexpand #1% + \fi +} + % This makes use of the obscure feature that if the last token of a % is #, then the preceding argument is delimited by % an opening brace, and that opening brace is not consumed. @@ -5365,16 +5687,18 @@ width0pt\relax} \fi % @alias. % We need some trickery to remove the optional spaces around the equal % sign. Just make them active and then expand them all to nothing. -\def\alias{\begingroup\obeyspaces\parsearg\aliasxxx} +\def\alias{\parseargusing\obeyspaces\aliasxxx} \def\aliasxxx #1{\aliasyyy#1\relax} -\def\aliasyyy #1=#2\relax{\ignoreactivespaces -\edef\next{\global\let\expandafter\noexpand\csname#1\endcsname=% - \expandafter\noexpand\csname#2\endcsname}% -\expandafter\endgroup\next} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} \message{cross references,} -% @xref etc. \newwrite\auxfile @@ -5386,56 +5710,61 @@ width0pt\relax} \fi \def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, node \samp{\ignorespaces#1{}}} -% @node's job is to define \lastnode. -\def\node{\ENVcheck\parsearg\nodezzz} -\def\nodezzz#1{\nodexxx [#1,]} -\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. +\parseargdef\node{\checkenv{}\nodexxx #1,\finishnodeparse} +\def\nodexxx#1,#2\finishnodeparse{\gdef\lastnode{#1}} \let\nwnode=\node -\let\lastnode=\relax +\let\lastnode=\empty -% The sectioning commands (@chapter, etc.) call these. -\def\donoderef{% - \ifx\lastnode\relax\else - \expandafter\expandafter\expandafter\setref{\lastnode}% - {Ysectionnumberandtype}% - \global\let\lastnode=\relax +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty \fi } -\def\unnumbnoderef{% - \ifx\lastnode\relax\else - \expandafter\expandafter\expandafter\setref{\lastnode}{Ynothing}% - \global\let\lastnode=\relax - \fi -} -\def\appendixnoderef{% - \ifx\lastnode\relax\else - \expandafter\expandafter\expandafter\setref{\lastnode}% - {Yappendixletterandtype}% - \global\let\lastnode=\relax - \fi -} - % @anchor{NAME} -- define xref target at arbitrary point. % \newcount\savesfregister -\gdef\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} -\gdef\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} -\gdef\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} - -% \setref{NAME}{SNT} defines a cross-reference point NAME, namely -% NAME-title, NAME-pg, and NAME-SNT. Called from \foonoderef. We have -% to set \indexdummies so commands such as @code in a section title -% aren't expanded. It would be nicer not to expand the titles in the -% first place, but there's so many layers that that is hard to do. % -\def\setref#1#2{{% - \indexdummies +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \thissection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% \pdfmkdest{#1}% - \dosetq{#1-title}{Ytitle}% - \dosetq{#1-pg}{Ypagenumber}% - \dosetq{#1-snt}{#2}% -}} + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \turnoffactive + \otherbackslash + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\thissection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \writexrdef{pg}{\folio}% will be written later, during \shipout + }% + \fi +} % @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is % the node name, #2 the name of the Info cross-reference, #3 the printed @@ -5448,135 +5777,156 @@ width0pt\relax} \fi \def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup \unsepspaces \def\printedmanual{\ignorespaces #5}% - \def\printednodename{\ignorespaces #3}% - \setbox1=\hbox{\printedmanual}% - \setbox0=\hbox{\printednodename}% + \def\printedrefname{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual\unskip}% + \setbox0=\hbox{\printedrefname\unskip}% \ifdim \wd0 = 0pt % No printed node name was explicitly given. \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax % Use the node name inside the square brackets. - \def\printednodename{\ignorespaces #1}% + \def\printedrefname{\ignorespaces #1}% \else % Use the actual chapter/section title appear inside % the square brackets. Use the real section title if we have it. \ifdim \wd1 > 0pt % It is in another manual, so we don't have it. - \def\printednodename{\ignorespaces #1}% + \def\printedrefname{\ignorespaces #1}% \else \ifhavexrefs % We know the real title if we have the xref values. - \def\printednodename{\refx{#1-title}{}}% + \def\printedrefname{\refx{#1-title}{}}% \else % Otherwise just copy the Info node name. - \def\printednodename{\ignorespaces #1}% + \def\printedrefname{\ignorespaces #1}% \fi% \fi \fi \fi % - % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not - % insert empty discretionaries after hyphens, which means that it will - % not find a line break at a hyphen in a node names. Since some manuals - % are best written with fairly long node names, containing hyphens, this - % is a loss. Therefore, we give the text of the node name again, so it - % is as if TeX is seeing it for the first time. + % Make link in pdf output. \ifpdf \leavevmode \getfilename{#4}% - \ifnum\filenamelength>0 - \startlink attr{/Border [0 0 0]}% - goto file{\the\filename.pdf} name{#1@}% - \else - \startlink attr{/Border [0 0 0]}% - goto name{#1@}% - \fi + {\turnoffactive \otherbackslash + \ifnum\filenamelength>0 + \startlink attr{/Border [0 0 0]}% + goto file{\the\filename.pdf} name{#1}% + \else + \startlink attr{/Border [0 0 0]}% + goto name{\pdfmkpgn{#1}}% + \fi + }% \linkcolor \fi % - \ifdim \wd1 > 0pt - \putwordsection{} ``\printednodename'' \putwordin{} \cite{\printedmanual}% + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \otherbackslash + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd0 = 0pt + \refx{#1-snt}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd1 > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi \else - % _ (for example) has to be the character _ for the purposes of the - % control sequence corresponding to the node, but it has to expand - % into the usual \leavevmode...\vrule stuff for purposes of - % printing. So we \turnoffactive for the \refx-snt, back on for the - % printing, back off for the \refx-pg. - {\normalturnoffactive - % Only output a following space if the -snt ref is nonempty; for - % @unnumbered and @anchor, it won't be. - \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% - \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi - }% - % [mynode], - [\printednodename],\space - % page 3 - \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + % node/anchor (non-float) references. + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printedrefname'' \putwordin{} \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive \otherbackslash + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via a macro so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \otherbackslash \putwordpage\tie\refx{#1-pg}{}% + \fi \fi \endlink \endgroup} -% \dosetq is the interface for calls from other macros - -% Use \normalturnoffactive so that punctuation chars such as underscore -% and backslash work in node names. (\turnoffactive doesn't do \.) -\def\dosetq#1#2{% - {\let\folio=0% - \normalturnoffactive - \edef\next{\write\auxfile{\internalsetq{#1}{#2}}}% - \iflinks - \next - \fi - }% -} - -% \internalsetq {foo}{page} expands into -% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} -% When the aux file is read, ' is the escape character - -\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} - -% Things to be expanded by \internalsetq - -\def\Ypagenumber{\folio} - -\def\Ytitle{\thissection} - -\def\Ynothing{} - -\def\Ysectionnumberandtype{% -\ifnum\secno=0 \putwordChapter\xreftie\the\chapno % -\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % -\else \ifnum \subsubsecno=0 % -\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % -\else % -\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % -\fi \fi \fi } - -\def\Yappendixletterandtype{% -\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% -\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % -\else \ifnum \subsubsecno=0 % -\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % -\else % -\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % -\fi \fi \fi } - -\gdef\xreftie{'tie} - -% Use TeX 3.0's \inputlineno to get the line number, for better error -% messages, but if we're using an old version of TeX, don't do anything. +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). % -\ifx\inputlineno\thisisundefined - \let\linenumber = \empty % Non-3.0. -\else - \def\linenumber{\the\inputlineno:\space} -\fi +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} % Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. % If its value is nonempty, SUFFIX is output afterward. - +% \def\refx#1#2{% - \expandafter\ifx\csname X#1\endcsname\relax + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax % If not defined, say something at least. \angleleft un\-de\-fined\angleright \iflinks @@ -5591,21 +5941,49 @@ width0pt\relax} \fi \fi \else % It's defined, so just use it. - \csname X#1\endcsname + \thisrefX \fi #2% Output the suffix in any case. } -% This is the macro invoked by entries in the aux file. +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. % -\def\xrdef#1{\begingroup - % Reenable \ as an escape while reading the second argument. - \catcode`\\ = 0 - \afterassignment\endgroup - \expandafter\gdef\csname X#1\endcsname +\def\xrdef#1#2{% + \expandafter\gdef\csname XR#1\endcsname{#2}% remember this xref value. + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR#1\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0{#1}}% + \fi } % Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readauxfile + \global\havexrefstrue + \fi + \closein 1 +} + \def\readauxfile{\begingroup \catcode`\^^@=\other \catcode`\^^A=\other @@ -5634,9 +6012,7 @@ width0pt\relax} \fi \catcode`\^^]=\other \catcode`\^^^=\other \catcode`\^^_=\other - \catcode`\@=\other - \catcode`\^=\other - % It was suggested to define this as 7, which would allow ^^e4 etc. + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. % in xref tags, i.e., node names. But since ^^e4 notation isn't % supported in the main text, it doesn't seem desirable. Furthermore, % that is not enough: for node names that actually contain a ^ @@ -5649,6 +6025,9 @@ width0pt\relax} \fi % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter % and then to call \auxhat in \setq. % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... \catcode`\~=\other \catcode`\[=\other \catcode`\]=\other @@ -5660,8 +6039,19 @@ width0pt\relax} \fi \catcode`\$=\other \catcode`\#=\other \catcode`\&=\other + \catcode`\%=\other \catcode`+=\other % avoid \+ for paranoia even though we've turned it off - % Make the characters 128-255 be printing characters + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. {% \count 1=128 \def\loop{% @@ -5670,31 +6060,18 @@ width0pt\relax} \fi \ifnum \count 1<256 \loop \fi }% }% - % The aux file uses ' as the escape (for now). - % Turn off \ as an escape so we do not lose on - % entries which were dumped with control sequences in their names. - % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ - % Reference to such entries still does not work the way one would wish, - % but at least they do not bomb out when the aux file is read in. + % + % @ is our escape character in .aux files, and we need braces. \catcode`\{=1 \catcode`\}=2 - \catcode`\%=\other - \catcode`\'=0 - \catcode`\\=\other + \catcode`\@=0 % - \openin 1 \jobname.aux - \ifeof 1 \else - \closein 1 - \input \jobname.aux - \global\havexrefstrue - \global\warnedobstrue - \fi - % Open the new aux file. TeX will close it automatically at exit. - \openout\auxfile=\jobname.aux + \input \jobname.aux \endgroup} -% Footnotes. +\message{insertions,} +% including footnotes. \newcount \footnoteno @@ -5708,37 +6085,39 @@ width0pt\relax} \fi % @footnotestyle is meaningful for info output only. \let\footnotestyle=\comment -\let\ptexfootnote=\footnote - {\catcode `\@=11 % % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent \global\advance\footnoteno by \@ne \edef\thisfootno{$^{\the\footnoteno}$}% % % In case the footnote comes at the end of a sentence, preserve the % extra spacing after we do the footnote number. \let\@sf\empty - \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi % % Remove inadvertent blank space before typesetting the footnote number. \unskip \thisfootno\@sf - \footnotezzz + \dofootnote }% % Don't bother with the trickery in plain.tex to not require the % footnote text as a parameter. Our footnotes don't need to be so general. % -% Oh yes, they do; otherwise, @ifset and anything else that uses -% \parseargline fail inside footnotes because the tokens are fixed when +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when % the footnote is read. --karl, 16nov96. % -\long\gdef\footnotezzz{\insert\footins\bgroup +\gdef\dofootnote{% + \insert\footins\bgroup % We want to typeset this text as a normal paragraph, even if the % footnote reference occurs in (for example) a display environment. % So reset some parameters. + \hsize=\pagewidth \interlinepenalty\interfootnotelinepenalty \splittopskip\ht\strutbox % top baseline for broken footnotes \splitmaxdepth\dp\strutbox @@ -5751,8 +6130,15 @@ width0pt\relax} \fi % \smallfonts \rm % - % Hang the footnote text off the number. - \hang + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% \textindent{\thisfootno}% % % Don't crash into the line above the footnote text. Since this @@ -5761,66 +6147,68 @@ width0pt\relax} \fi \footstrut \futurelet\next\fo@t } -\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t - \else\let\next\f@t\fi \next} -\def\f@@t{\bgroup\aftergroup\@foot\let\next} -\def\f@t#1{#1\@foot} -\def\@foot{\strut\par\egroup} - }%end \catcode `\@=11 -% Set the baselineskip to #1, and the lineskip and strut size -% correspondingly. There is no deep meaning behind these magic numbers -% used as factors; they just match (closely enough) what Knuth defined. +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarily, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. % -\def\lineskipfactor{.08333} -\def\strutheightpercent{.70833} -\def\strutdepthpercent {.29167} -% -\def\setleading#1{% - \normalbaselineskip = #1\relax - \normallineskip = \lineskipfactor\normalbaselineskip - \normalbaselines - \setbox\strutbox =\hbox{% - \vrule width0pt height\strutheightpercent\baselineskip - depth \strutdepthpercent \baselineskip - }% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi } -% @| inserts a changebar to the left of the current line. It should -% surround any changed text. This approach does *not* work if the -% change spans more than two lines of output. To handle that, we would -% have adopt a much more difficult approach (putting marks into the main -% vertical list for the beginning and end of each change). +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. % -\def\|{% - % \vadjust can only be used in horizontal mode. - \leavevmode - % - % Append this vertical mode material after the current line in the output. - \vadjust{% - % We want to insert a rule with the height and depth of the current - % leading; that is exactly what \strutbox is supposed to record. - \vskip-\baselineskip - % - % \vadjust-items are inserted at the left edge of the type. So - % the \llap here moves out into the left-hand margin. - \llap{% - % - % For a thicker or thinner bar, change the `1pt'. - \vrule height\baselineskip width1pt - % - % This is the space between the bar and the text. - \hskip 12pt - }% - }% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% } -% For a final copy, take out the rectangles -% that mark overfull boxes (in case you have decided -% that the text looks ok even though it passes the margin). -% -\def\finalout{\overfullrule=0pt} +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + % @image. We use the macros from epsf.tex to support this. % If epsf.tex is not installed and @image is used, we complain. @@ -5830,12 +6218,12 @@ width0pt\relax} \fi % undone and the next image would fail. \openin 1 = epsf.tex \ifeof 1 \else - \closein 1 - % Do not bother showing banner with post-v2.7 epsf.tex (available in - % doc/epsf.tex until it shows up on ctan). + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). \def\epsfannounce{\toks0 = }% \input epsf.tex \fi +\closein 1 % % We will only complain once about lack of epsf.tex. \newif\ifwarnednoepsf @@ -5851,42 +6239,279 @@ width0pt\relax} \fi \global\warnednoepsftrue \fi \else - \imagexxx #1,,,\finish + \imagexxx #1,,,,,\finish \fi } % % Arguments to @image: % #1 is (mandatory) image filename; we tack on .eps extension. % #2 is (optional) width, #3 is (optional) height. -% #4 is just the usual extra ignored arg for parsing this stuff. -\def\imagexxx#1,#2,#3,#4\finish{% +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing this stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \nobreak\bigskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \line\bgroup\hss + \fi + % + % Output the image. \ifpdf - \centerline{\dopdfimage{#1}{#2}{#3}}% + \dopdfimage{#1}{#2}{#3}% \else % \epsfbox itself resets \epsf?size at each figure. \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi - \begingroup - \catcode`\^^M = 5 % in case we're inside an example - \normalturnoffactive % allow _ et al. in names - % If the image is by itself, center it. - \ifvmode - \nobreak\bigskip - % Usually we'll have text after the image which will insert - % \parskip glue, so insert it here too to equalize the space - % above and below. - \nobreak\vskip\parskip - \nobreak - \centerline{\epsfbox{#1.eps}}% - \bigbreak - \else - % In the middle of a paragraph, no extra space. - \epsfbox{#1.eps}% + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode \hss \egroup \bigbreak \fi % space after the image +\endgroup} + + +% @float FLOATTYPE,LOC ... @end float for displayed figures, tables, etc. +% We don't actually implement floating yet, we just plop the float "here". +% But it seemed the best name for the future. +% +\envparseargdef\float{\dofloat #1,,,\finish} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + \startsavinginserts + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \thissection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\thissection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline\thiscaption + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies \turnoffactive \otherbackslash + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{% + \floatident + \ifx\thisshortcaption\empty + \ifx\thiscaption\empty \else : \thiscaption \fi + \else + : \thisshortcaption + \fi + }}% + }% + \fi + % + % Space below caption, if we printed anything. + \ifx\printedsomething\empty \else \vskip\parskip \fi + \egroup % end of \vtop + \checkinserts +} + +% Append the tokens #2 to the macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \toksA = \expandafter{#1}% + \toksB = {#2}% + \edef#1{\the\toksA \the\toksB}% +} + +% @caption, @shortcaption are easy. +% +\def\caption#1{\checkenv\float \def\thiscaption{#1}} +\def\shortcaption#1{\checkenv\float \def\thisshortcaption{#1}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \thissection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname \endgroup \fi } +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{% + % use the same \entry we use for the TOC. + \entry{\csname XR#1-lof\endcsname}{\csname XR#1-pg\endcsname}% +} \message{localization,} % and i18n. @@ -5896,19 +6521,17 @@ width0pt\relax} \fi % properly. Single argument is the language abbreviation. % It would be nice if we could set up a hyphenation file here. % -\def\documentlanguage{\parsearg\dodocumentlanguage} -\def\dodocumentlanguage#1{% +\parseargdef\documentlanguage{% \tex % read txi-??.tex file in plain TeX. - % Read the file if it exists. - \openin 1 txi-#1.tex - \ifeof1 - \errhelp = \nolanghelp - \errmessage{Cannot read language file txi-#1.tex}% - \let\temp = \relax - \else - \def\temp{\input txi-#1.tex }% - \fi - \temp + % Read the file if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \input txi-#1.tex + \fi + \closein 1 \endgroup } \newhelp\nolanghelp{The given language definition file cannot be found or @@ -5954,10 +6577,13 @@ should work if nowhere else does.} } % Parameters in order: 1) textheight; 2) textwidth; 3) voffset; -% 4) hoffset; 5) binding offset; 6) topskip. Then whoever calls us can -% set \parskip and call \setleading for \baselineskip. +% 4) hoffset; 5) binding offset; 6) topskip; 7) physical page height; 8) +% physical page width. % -\def\internalpagesizes#1#2#3#4#5#6{% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% \voffset = #3\relax \topskip = #6\relax \splittopskip = \topskip @@ -5976,108 +6602,136 @@ should work if nowhere else does.} \normaloffset = #4\relax \bindingoffset = #5\relax % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + \fi + % + \setleading{\textleading} + % \parindent = \defaultparindent \setemergencystretch } -% Use `small' versions. -% -\def\smallenvironments{% - \let\smalldisplay = \smalldisplayx - \let\smallexample = \smalllispx - \let\smallformat = \smallformatx - \let\smalllisp = \smalllispx -} - % @letterpaper (the default). \def\letterpaper{{\globaldefs = 1 \parskip = 3pt plus 2pt minus 1pt - \setleading{13.2pt}% + \textleading = 13.2pt % % If page is nothing but text, make it come out even. - \internalpagesizes{46\baselineskip}{6in}{\voffset}{.25in}{\bindingoffset}{36pt}% + \internalpagesizes{46\baselineskip}{6in}% + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% }} % Use @smallbook to reset parameters for 7x9.5 (or so) format. \def\smallbook{{\globaldefs = 1 \parskip = 2pt plus 1pt - \setleading{12pt}% + \textleading = 12pt % - \internalpagesizes{7.5in}{5.in}{\voffset}{.25in}{\bindingoffset}{16pt}% + \internalpagesizes{7.5in}{5in}% + {\voffset}{.25in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% % \lispnarrowing = 0.3in \tolerance = 700 \hfuzz = 1pt \contentsrightmargin = 0pt - \deftypemargin = 0pt \defbodyindent = .5cm - \smallenvironments }} % Use @afourpaper to print on European A4 paper. \def\afourpaper{{\globaldefs = 1 - \setleading{12pt}% \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt % - \internalpagesizes{53\baselineskip}{160mm}{\voffset}{4mm}{\bindingoffset}{44pt}% + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{51\baselineskip}{160mm} + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% % \tolerance = 700 \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm }} % Use @afivepaper to print on European A5 paper. % From romildo@urano.iceb.ufop.br, 2 July 2000. % He also recommends making @example and @lisp be small. \def\afivepaper{{\globaldefs = 1 - \setleading{12.5pt}% \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt % - \internalpagesizes{166mm}{120mm}{\voffset}{-8mm}{\bindingoffset}{8pt}% + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% % \lispnarrowing = 0.2in \tolerance = 800 \hfuzz = 1.2pt - \contentsrightmargin = 0mm - \deftypemargin = 0pt + \contentsrightmargin = 0pt \defbodyindent = 2mm \tableindent = 12mm - % - \smallenvironments }} -% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin -% 29mm, hence bottom margin 28mm, nominal side margin 3cm. +% A specific text layout, 24x15cm overall, intended for A4 paper. \def\afourlatex{{\globaldefs = 1 - \setleading{13.6pt}% - % \afourpaper - \internalpagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}% + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% % + % Must explicitly reset to 0 because we call \afourpaper. \globaldefs = 0 }} -% Use @afourwide to print on European A4 paper in wide format. -\def\afourwide{% +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 \afourpaper - \internalpagesizes{6.5in}{9.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}% - % + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% \globaldefs = 0 -} +}} % @pagesizes TEXTHEIGHT[,TEXTWIDTH] % Perhaps we should allow setting the margins, \topskip, \parskip, % and/or leading, also. Or perhaps we should compute them somehow. % -\def\pagesizes{\parsearg\pagesizesxxx} -\def\pagesizesxxx#1{\pagesizesyyy #1,,\finish} +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} \def\pagesizesyyy#1,#2,#3\finish{{% \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi \globaldefs = 1 % \parskip = 3pt plus 2pt minus 1pt - \setleading{13.2pt}% + \setleading{\textleading}% % - \internalpagesizes{#1}{\hsize}{\voffset}{\normaloffset}{\bindingoffset}{44pt}% + \dimen0 = #1 + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% }} % Set default to letter. @@ -6105,10 +6759,10 @@ should work if nowhere else does.} \def\normalless{<} \def\normalgreater{>} \def\normalplus{+} -\def\normaldollar{$} +\def\normaldollar{$}%$ font-lock fix -% This macro is used to make a character print one way in ttfont -% where it can probably just be output, and another way in other fonts, +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, % where something hairier probably needs to be done. % % #1 is what to print if we are indeed using \tt; #2 is what to print @@ -6141,7 +6795,7 @@ should work if nowhere else does.} \catcode`\_=\active \def_{\ifusingtt\normalunderscore\_} % Subroutine for the previous macro. -\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}} +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } \catcode`\|=\active \def|{{\tt\char124}} @@ -6154,16 +6808,7 @@ should work if nowhere else does.} \catcode`\+=\active \def+{{\tt \char 43}} \catcode`\$=\active -\def${\ifusingit{{\sl\$}}\normaldollar} -%\catcode 27=\active -%\def^^[{$\diamondsuit$} - -% Set up an active definition for =, but don't enable it most of the time. -{\catcode`\==\active -\global\def={{\tt \char 61}}} - -\catcode`+=\active -\catcode`\_=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix % If a .fmt file is being used, characters that might appear in a file % name cannot be active until we have parsed the command line. @@ -6173,44 +6818,48 @@ should work if nowhere else does.} \catcode`\@=0 -% \rawbackslashxx output one backslash character in current font -\global\chardef\rawbackslashxx=`\\ -%{\catcode`\\=\other -%@gdef@rawbackslashxx{\}} +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work -% \rawbackslash redefines \ as input to do \rawbackslashxx. +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. {\catcode`\\=\active -@gdef@rawbackslash{@let\=@rawbackslashxx }} + @gdef@rawbackslash{@let\=@backslashcurfont} + @gdef@otherbackslash{@let\=@realbackslash} +} + +% \realbackslash is an actual character `\' with catcode other. +{\catcode`\\=\other @gdef@realbackslash{\}} % \normalbackslash outputs one backslash in fixed width font. -\def\normalbackslash{{\tt\rawbackslashxx}} +\def\normalbackslash{{\tt\backslashcurfont}} -% \catcode 17=0 % Define control-q \catcode`\\=\active % Used sometimes to turn off (effectively) the active characters % even after parsing them. -@def@turnoffactive{@let"=@normaldoublequote -@let\=@realbackslash -@let~=@normaltilde -@let^=@normalcaret -@let_=@normalunderscore -@let|=@normalverticalbar -@let<=@normalless -@let>=@normalgreater -@let+=@normalplus -@let$=@normaldollar} +@def@turnoffactive{% + @let"=@normaldoublequote + @let\=@realbackslash + @let~=@normaltilde + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let<=@normalless + @let>=@normalgreater + @let+=@normalplus + @let$=@normaldollar %$ font-lock fix + @unsepspaces +} -@def@normalturnoffactive{@let"=@normaldoublequote -@let\=@normalbackslash -@let~=@normaltilde -@let^=@normalcaret -@let_=@normalunderscore -@let|=@normalverticalbar -@let<=@normalless -@let>=@normalgreater -@let+=@normalplus -@let$=@normaldollar} +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. (Thus, \ is not expandable when this is in +% effect.) +% +@def@normalturnoffactive{@turnoffactive @let\=@normalbackslash} % Make _ and + \other characters, temporarily. % This is canceled by @fixbackslash. @@ -6238,15 +6887,11 @@ should work if nowhere else does.} % Say @foo, not \foo, in error messages. @escapechar = `@@ -% These look ok in all fonts, so just make them not special. +% These look ok in all fonts, so just make them not special. @catcode`@& = @other @catcode`@# = @other @catcode`@% = @other -@c Set initial fonts. -@textfonts -@rm - @c Local variables: @c eval: (add-hook 'write-file-hooks 'time-stamp) @@ -6255,3 +6900,9 @@ should work if nowhere else does.} @c time-stamp-format: "%:y-%02m-%02d.%02H" @c time-stamp-end: "}" @c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore diff --git a/lbtt/src/BitArray.cc b/lbtt/src/BitArray.cc index 1d0342ab7..374e3ba39 100644 --- a/lbtt/src/BitArray.cc +++ b/lbtt/src/BitArray.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #ifdef HAVE_SSTREAM #include diff --git a/lbtt/src/BitArray.h b/lbtt/src/BitArray.h index 0efbac3f0..ec725d7fc 100644 --- a/lbtt/src/BitArray.h +++ b/lbtt/src/BitArray.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef BITARRAY_H #define BITARRAY_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/Bitset.h b/lbtt/src/Bitset.h index f74da7251..a97c40354 100644 --- a/lbtt/src/Bitset.h +++ b/lbtt/src/Bitset.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/BuchiAutomaton.cc b/lbtt/src/BuchiAutomaton.cc index 77d0707cf..d63667dd8 100644 --- a/lbtt/src/BuchiAutomaton.cc +++ b/lbtt/src/BuchiAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/BuchiAutomaton.h b/lbtt/src/BuchiAutomaton.h index a9622f82e..dcd034bd8 100644 --- a/lbtt/src/BuchiAutomaton.h +++ b/lbtt/src/BuchiAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,15 +20,11 @@ #ifndef BUCHIAUTOMATON_H #define BUCHIAUTOMATON_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "EdgeContainer.h" #include "Exception.h" diff --git a/lbtt/src/Config-lex.ll b/lbtt/src/Config-lex.ll index 96d43b5c4..aa60a08f3 100644 --- a/lbtt/src/Config-lex.ll +++ b/lbtt/src/Config-lex.ll @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Config-parse.yy b/lbtt/src/Config-parse.yy index fec1407cc..b51a8c942 100644 --- a/lbtt/src/Config-parse.yy +++ b/lbtt/src/Config-parse.yy @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -368,7 +368,7 @@ static inline bool isLocked(int option) /* Punctuation symbols. */ %token CFG_LBRACE CFG_RBRACE CFG_EQUALS -%token CFG_BLOCK_ID, CFG_OPTION_ID +%token CFG_BLOCK_ID CFG_OPTION_ID /* The `unknown' token. */ diff --git a/lbtt/src/Configuration.cc b/lbtt/src/Configuration.cc index f3ac26cb6..ff4b8fb67 100644 --- a/lbtt/src/Configuration.cc +++ b/lbtt/src/Configuration.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/Configuration.h b/lbtt/src/Configuration.h index b58234e45..bdfeda739 100644 --- a/lbtt/src/Configuration.h +++ b/lbtt/src/Configuration.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef CONFIGURATION_H #define CONFIGURATION_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include @@ -31,7 +27,7 @@ #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" #include "FormulaRandomizer.h" #include "StateSpaceRandomizer.h" diff --git a/lbtt/src/DispUtil.cc b/lbtt/src/DispUtil.cc index aac917d6a..4bfa825e8 100644 --- a/lbtt/src/DispUtil.cc +++ b/lbtt/src/DispUtil.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,14 +17,10 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "DispUtil.h" /****************************************************************************** diff --git a/lbtt/src/DispUtil.h b/lbtt/src/DispUtil.h index 4c779aaf3..ce3e08141 100644 --- a/lbtt/src/DispUtil.h +++ b/lbtt/src/DispUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef DISPUTIL_H #define DISPUTIL_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/EdgeContainer.h b/lbtt/src/EdgeContainer.h index 1d4674e72..2ebbc089f 100644 --- a/lbtt/src/EdgeContainer.h +++ b/lbtt/src/EdgeContainer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Exception.h b/lbtt/src/Exception.h index d3c248be4..a429704b7 100644 --- a/lbtt/src/Exception.h +++ b/lbtt/src/Exception.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/ExternalTranslator.cc b/lbtt/src/ExternalTranslator.cc index 14a8e42fb..adda19ea1 100644 --- a/lbtt/src/ExternalTranslator.cc +++ b/lbtt/src/ExternalTranslator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/ExternalTranslator.h b/lbtt/src/ExternalTranslator.h index 17faaacee..609617591 100644 --- a/lbtt/src/ExternalTranslator.h +++ b/lbtt/src/ExternalTranslator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef EXTERNALTRANSLATOR_H #define EXTERNALTRANSLATOR_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include @@ -33,7 +29,7 @@ #else #include #endif /* HAVE_SSTREAM */ -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" #include "LtlFormula.h" #include "translate.h" diff --git a/lbtt/src/FormulaRandomizer.cc b/lbtt/src/FormulaRandomizer.cc index 10497fac9..712e269c1 100644 --- a/lbtt/src/FormulaRandomizer.cc +++ b/lbtt/src/FormulaRandomizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "FormulaRandomizer.h" #include "Random.h" diff --git a/lbtt/src/FormulaRandomizer.h b/lbtt/src/FormulaRandomizer.h index cdeca4422..a0391d499 100644 --- a/lbtt/src/FormulaRandomizer.h +++ b/lbtt/src/FormulaRandomizer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,14 +20,10 @@ #ifndef FORMULARANDOMIZER_H #define FORMULARANDOMIZER_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "LtlFormula.h" namespace Ltl diff --git a/lbtt/src/FormulaWriter.h b/lbtt/src/FormulaWriter.h index f2e9cfe77..9eba9f868 100644 --- a/lbtt/src/FormulaWriter.h +++ b/lbtt/src/FormulaWriter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/Graph.h.in b/lbtt/src/Graph.h.in index 22a07ec86..29c8e52e6 100644 --- a/lbtt/src/Graph.h.in +++ b/lbtt/src/Graph.h.in @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -34,7 +34,7 @@ using SLIST_NAMESPACE::slist; #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "Exception.h" diff --git a/lbtt/src/LbtWrapper.h b/lbtt/src/LbtWrapper.h index af555df35..03a2d8a80 100644 --- a/lbtt/src/LbtWrapper.h +++ b/lbtt/src/LbtWrapper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/LbttAlloc.h b/lbtt/src/LbttAlloc.h new file mode 100644 index 000000000..b4019d922 --- /dev/null +++ b/lbtt/src/LbttAlloc.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LBTTALLOC_H +#define LBTTALLOC_H + +#include + +#ifdef HAVE_SINGLE_CLIENT_ALLOC +#define ALLOC(typename) single_client_alloc +#else +#define ALLOC(typename) allocator +#endif /* HAVE_SINGLE_CLIENT_ALLOC */ + +#ifdef HAVE_OBSTACK_H + +/* GNU libc 2.3.2's copy of obstack.h uses a definition of __INT_TO_PTR + which does not compile in C++. Fortunately it will not override + an existing definition. */ + +#ifdef GLIBC_OBSTACK_WORKAROUND +#define __INT_TO_PTR(P) ((P) + (char *) 0) +#endif + +#include +#include +#include + +/****************************************************************************** + * + * A wrapper class for allocating memory through an obstack. + * + *****************************************************************************/ + +class ObstackAllocator +{ +public: + ObstackAllocator(); /* Constructor. */ + + ~ObstackAllocator(); /* Destructor. */ + + void* alloc(int size); /* Allocates memory. */ + + void free(void* obj); /* Deallocates memory. */ + + static void failure(); /* Callback function for + * reporting a memory + * allocation failure. + */ +private: + ObstackAllocator(const ObstackAllocator&); /* Prevent copying and */ + ObstackAllocator& operator= /* assignment of */ + (const ObstackAllocator&); /* ObstackAllocator + * objects. + */ + + struct obstack store; /* The obstack. */ +}; + +#define obstack_chunk_alloc std::malloc +#define obstack_chunk_free std::free + + + +/****************************************************************************** + * + * Inline function definitions for class ObstackAllocator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ObstackAllocator::ObstackAllocator() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ObstackAllocator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_init(&store); +} + +/* ========================================================================= */ +inline ObstackAllocator::~ObstackAllocator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ObstackAllocator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_free(&store, NULL); +} + +/* ========================================================================= */ +inline void* ObstackAllocator::alloc(int size) +/* ---------------------------------------------------------------------------- + * + * Description: Interface to the memory allocator. + * + * Argument: size -- Number of bytes to allocate. + * + * Returns: A pointer to the beginning of the newly allocated memory. + * + * ------------------------------------------------------------------------- */ +{ + return obstack_alloc(&store, size); +} + +/* ========================================================================= */ +inline void ObstackAllocator::free(void* obj) +/* ---------------------------------------------------------------------------- + * + * Description: Interface to the memory deallocation function. + * + * Argument: obj -- A pointer to the object to deallocate. (Because the + * underlying memory allocator is an obstack, freeing + * an object also releases all objects allocated after + * the given object.) + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_free(&store, obj); +} + +/* ========================================================================= */ +inline void ObstackAllocator::failure() +/* ---------------------------------------------------------------------------- + * + * Description: Callback function for reporting memory allocation failures. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + throw std::bad_alloc(); +} + +#endif /* HAVE_OBSTACK_H */ + +#endif /* !LBTTALLOC_H */ diff --git a/lbtt/src/LtlFormula.cc b/lbtt/src/LtlFormula.cc index 71a2aa6d0..7c4034722 100644 --- a/lbtt/src/LtlFormula.cc +++ b/lbtt/src/LtlFormula.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "FormulaWriter.h" #include "LtlFormula.h" diff --git a/lbtt/src/LtlFormula.h b/lbtt/src/LtlFormula.h index 3b1a0300d..c5b2efea7 100644 --- a/lbtt/src/LtlFormula.h +++ b/lbtt/src/LtlFormula.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,17 +20,13 @@ #ifndef LTLFORMULA_H #define LTLFORMULA_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "Exception.h" diff --git a/lbtt/src/Makefile.am b/lbtt/src/Makefile.am index 4c45bc70d..76f3a8992 100644 --- a/lbtt/src/Makefile.am +++ b/lbtt/src/Makefile.am @@ -1,21 +1,21 @@ bin_PROGRAMS = lbtt lbtt-translate -lbtt_SOURCES = Alloc.h BitArray.h Bitset.h BitArray.cc BuchiAutomaton.h \ +lbtt_SOURCES = BitArray.h Bitset.h BitArray.cc BuchiAutomaton.h \ BuchiAutomaton.cc Config-parse.yy Config-lex.ll Configuration.h \ Configuration.cc DispUtil.h DispUtil.cc EdgeContainer.h Exception.h \ -FormulaRandomizer.h FormulaRandomizer.cc FormulaWriter.h LtlFormula.h \ -LtlFormula.cc main.cc PathEvaluator.h PathEvaluator.cc PathIterator.h \ -PathIterator.cc ProductAutomaton.h ProductAutomaton.cc Random.h SccIterator.h \ -SharedTestData.h StatDisplay.h StatDisplay.cc StateSpace.h StateSpace.cc \ -StateSpaceRandomizer.h StateSpaceRandomizer.cc StringUtil.h StringUtil.cc \ -TestOperations.h TestOperations.cc TestRoundInfo.h TestStatistics.h \ -TestStatistics.cc UserCommandReader.h UserCommandReader.cc UserCommands.h \ -UserCommands.cc +FormulaRandomizer.h FormulaRandomizer.cc FormulaWriter.h LbttAlloc.h \ +LtlFormula.h LtlFormula.cc main.cc PathEvaluator.h PathEvaluator.cc \ +PathIterator.h PathIterator.cc ProductAutomaton.h ProductAutomaton.cc \ +Random.h SccIterator.h SharedTestData.h StatDisplay.h StatDisplay.cc \ +StateSpace.h StateSpace.cc StateSpaceRandomizer.h StateSpaceRandomizer.cc \ +StringUtil.h StringUtil.cc TestOperations.h TestOperations.cc TestRoundInfo.h \ +TestStatistics.h TestStatistics.cc UserCommandReader.h UserCommandReader.cc \ +UserCommands.h UserCommands.cc EXTRA_lbtt_SOURCES = gnu-getopt.h Config-parse.h lbtt_LDADD = @LIBOBJS@ @READLINELIBS@ -lbtt_translate_SOURCES = Alloc.h BitArray.h BitArray.cc Exception.h \ -ExternalTranslator.h ExternalTranslator.cc FormulaWriter.h LbtWrapper.h \ -LtlFormula.h LtlFormula.cc NeverClaim-parse.yy NeverClaim-lex.ll \ +lbtt_translate_SOURCES = BitArray.h BitArray.cc Exception.h \ +ExternalTranslator.h ExternalTranslator.cc FormulaWriter.h LbttAlloc.h \ +LbtWrapper.h LtlFormula.h LtlFormula.cc NeverClaim-parse.yy NeverClaim-lex.ll \ NeverClaimAutomaton.h NeverClaimAutomaton.cc SpinWrapper.h SpinWrapper.cc \ StringUtil.h StringUtil.cc translate.h translate.cc TranslatorInterface.h EXTRA_lbtt_translate_SOURCES = gnu-getopt.h NeverClaim-parse.h diff --git a/lbtt/src/NeverClaim-lex.ll b/lbtt/src/NeverClaim-lex.ll index d2f6d7bd4..ba60fc1bf 100644 --- a/lbtt/src/NeverClaim-lex.ll +++ b/lbtt/src/NeverClaim-lex.ll @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/NeverClaim-parse.yy b/lbtt/src/NeverClaim-parse.yy index ea0ee5703..3fd0e54df 100644 --- a/lbtt/src/NeverClaim-parse.yy +++ b/lbtt/src/NeverClaim-parse.yy @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/NeverClaimAutomaton.cc b/lbtt/src/NeverClaimAutomaton.cc index 341fd36df..57494c7c5 100644 --- a/lbtt/src/NeverClaimAutomaton.cc +++ b/lbtt/src/NeverClaimAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/NeverClaimAutomaton.h b/lbtt/src/NeverClaimAutomaton.h index 69759bd7d..0ab31b905 100644 --- a/lbtt/src/NeverClaimAutomaton.h +++ b/lbtt/src/NeverClaimAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,16 +20,12 @@ #ifndef NEVERCLAIMAUTOMATON_H #define NEVERCLAIMAUTOMATON_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" using namespace std; diff --git a/lbtt/src/PathEvaluator.cc b/lbtt/src/PathEvaluator.cc index d42243bcd..583a2490b 100644 --- a/lbtt/src/PathEvaluator.cc +++ b/lbtt/src/PathEvaluator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/PathEvaluator.h b/lbtt/src/PathEvaluator.h index cf0c79e0b..7b7a178ef 100644 --- a/lbtt/src/PathEvaluator.h +++ b/lbtt/src/PathEvaluator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,13 +20,9 @@ #ifndef PATHEVALUATOR_H #define PATHEVALUATOR_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "Exception.h" #include "LtlFormula.h" diff --git a/lbtt/src/PathIterator.cc b/lbtt/src/PathIterator.cc index 740a3a19f..81c939512 100644 --- a/lbtt/src/PathIterator.cc +++ b/lbtt/src/PathIterator.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "BitArray.h" #include "PathIterator.h" diff --git a/lbtt/src/PathIterator.h b/lbtt/src/PathIterator.h index 6411692a4..e194c60f2 100644 --- a/lbtt/src/PathIterator.h +++ b/lbtt/src/PathIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef PATHITERATOR_H #define PATHITERATOR_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include "StateSpace.h" diff --git a/lbtt/src/ProductAutomaton.cc b/lbtt/src/ProductAutomaton.cc index 3e568ea92..c733e57d7 100644 --- a/lbtt/src/ProductAutomaton.cc +++ b/lbtt/src/ProductAutomaton.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "ProductAutomaton.h" #include "SccIterator.h" diff --git a/lbtt/src/ProductAutomaton.h b/lbtt/src/ProductAutomaton.h index e4c8c33fe..060491c7d 100644 --- a/lbtt/src/ProductAutomaton.h +++ b/lbtt/src/ProductAutomaton.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,15 +20,11 @@ #ifndef PRODUCTAUTOMATON_H #define PRODUCTAUTOMATON_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "BuchiAutomaton.h" #include "EdgeContainer.h" diff --git a/lbtt/src/Random.h b/lbtt/src/Random.h index 3820b30d3..0096e64a4 100644 --- a/lbtt/src/Random.h +++ b/lbtt/src/Random.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/SccIterator.h b/lbtt/src/SccIterator.h index a8547bc13..fceaceeea 100644 --- a/lbtt/src/SccIterator.h +++ b/lbtt/src/SccIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -25,7 +25,7 @@ #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Graph.h" using namespace std; diff --git a/lbtt/src/SharedTestData.h b/lbtt/src/SharedTestData.h index 1a2a9898b..6403c52d4 100644 --- a/lbtt/src/SharedTestData.h +++ b/lbtt/src/SharedTestData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -21,7 +21,7 @@ #define SHAREDTESTDATA_H #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" #include "TestRoundInfo.h" #include "TestStatistics.h" diff --git a/lbtt/src/SpinWrapper.cc b/lbtt/src/SpinWrapper.cc index d66e45571..74a09c887 100644 --- a/lbtt/src/SpinWrapper.cc +++ b/lbtt/src/SpinWrapper.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #ifdef HAVE_SSTREAM #include diff --git a/lbtt/src/SpinWrapper.h b/lbtt/src/SpinWrapper.h index a43dcc6df..edbd0b6b1 100644 --- a/lbtt/src/SpinWrapper.h +++ b/lbtt/src/SpinWrapper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef SPINWRAPPER_H #define SPINWRAPPER_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include "ExternalTranslator.h" diff --git a/lbtt/src/StatDisplay.cc b/lbtt/src/StatDisplay.cc index 2fabdcc47..0d1074d49 100644 --- a/lbtt/src/StatDisplay.cc +++ b/lbtt/src/StatDisplay.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include "DispUtil.h" diff --git a/lbtt/src/StatDisplay.h b/lbtt/src/StatDisplay.h index 8a6cc1a6a..a4a947ad5 100644 --- a/lbtt/src/StatDisplay.h +++ b/lbtt/src/StatDisplay.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,14 +20,10 @@ #ifndef STATDISPLAY_H #define STATDISPLAY_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Configuration.h" #include "TestStatistics.h" diff --git a/lbtt/src/StateSpace.cc b/lbtt/src/StateSpace.cc index 4782846f8..78e2238e7 100644 --- a/lbtt/src/StateSpace.cc +++ b/lbtt/src/StateSpace.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include "DispUtil.h" diff --git a/lbtt/src/StateSpace.h b/lbtt/src/StateSpace.h index 9b3328966..338a5a4d1 100644 --- a/lbtt/src/StateSpace.h +++ b/lbtt/src/StateSpace.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,12 +20,8 @@ #ifndef STATESPACE_H #define STATESPACE_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "EdgeContainer.h" #include "Graph.h" diff --git a/lbtt/src/StateSpaceRandomizer.cc b/lbtt/src/StateSpaceRandomizer.cc index 8cc928d87..951b4567c 100644 --- a/lbtt/src/StateSpaceRandomizer.cc +++ b/lbtt/src/StateSpaceRandomizer.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,14 +17,10 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BitArray.h" #include "Exception.h" #include "StateSpaceRandomizer.h" diff --git a/lbtt/src/StateSpaceRandomizer.h b/lbtt/src/StateSpaceRandomizer.h index 44d2b583a..0aff152ba 100644 --- a/lbtt/src/StateSpaceRandomizer.h +++ b/lbtt/src/StateSpaceRandomizer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef STATESPACERANDOMIZER_H #define STATESPACERANDOMIZER_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include "Random.h" #include "StateSpace.h" diff --git a/lbtt/src/StringUtil.cc b/lbtt/src/StringUtil.cc index bb61e80ea..048db9542 100644 --- a/lbtt/src/StringUtil.cc +++ b/lbtt/src/StringUtil.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include "StringUtil.h" diff --git a/lbtt/src/StringUtil.h b/lbtt/src/StringUtil.h index dca736e9a..65a9fa790 100644 --- a/lbtt/src/StringUtil.h +++ b/lbtt/src/StringUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef STRINGUTIL_H #define STRINGUTIL_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include @@ -33,7 +29,7 @@ #include #endif /* HAVE_SSTREAM */ #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" using namespace std; diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 3c7ade3fc..12f2f91ec 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/TestOperations.h b/lbtt/src/TestOperations.h index b1b012634..f96ccdac3 100644 --- a/lbtt/src/TestOperations.h +++ b/lbtt/src/TestOperations.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,16 +20,12 @@ #ifndef TESTOPERATIONS_H #define TESTOPERATIONS_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Configuration.h" #include "Exception.h" #include "StateSpace.h" diff --git a/lbtt/src/TestRoundInfo.h b/lbtt/src/TestRoundInfo.h index 1c7c5beb2..23351792a 100644 --- a/lbtt/src/TestRoundInfo.h +++ b/lbtt/src/TestRoundInfo.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -25,7 +25,7 @@ #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Exception.h" #include "LtlFormula.h" #include "ProductAutomaton.h" diff --git a/lbtt/src/TestStatistics.cc b/lbtt/src/TestStatistics.cc index fb04f18d9..b3c82bd54 100644 --- a/lbtt/src/TestStatistics.cc +++ b/lbtt/src/TestStatistics.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "TestStatistics.h" diff --git a/lbtt/src/TestStatistics.h b/lbtt/src/TestStatistics.h index 074f63779..70cc6110e 100644 --- a/lbtt/src/TestStatistics.h +++ b/lbtt/src/TestStatistics.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,14 +20,10 @@ #ifndef TESTSTATISTICS_H #define TESTSTATISTICS_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BuchiAutomaton.h" #include "Configuration.h" #include "ProductAutomaton.h" diff --git a/lbtt/src/TranslatorInterface.h b/lbtt/src/TranslatorInterface.h index d158d2eee..ac70bfc55 100644 --- a/lbtt/src/TranslatorInterface.h +++ b/lbtt/src/TranslatorInterface.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or diff --git a/lbtt/src/UserCommandReader.cc b/lbtt/src/UserCommandReader.cc index 2f688b51e..364b7024b 100644 --- a/lbtt/src/UserCommandReader.cc +++ b/lbtt/src/UserCommandReader.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include @@ -50,6 +46,13 @@ #include #endif /* HAVE_READLINE */ +#ifdef HAVE_ISATTY +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#endif /* HAVE_ISATTY */ + + /****************************************************************************** * @@ -131,20 +134,41 @@ void executeUserCommands() input_line = line; else { - round_info.cout << '\n'; - round_info.cout.flush(); - } #else round_info.cout << prompt; round_info.cout.flush(); getline(cin, input_line, '\n'); if (cin.eof()) { - round_info.cout << '\n'; - round_info.cout.flush(); cin.clear(); +#endif /* HAVE_READLINE */ +#ifdef HAVE_ISATTY + /* + * If standard input is not bound to a terminal, act on EOF as if the + * `continue' command had been issued. Otherwise act as if an empty + * line was given as input. + */ + if (!isatty(STDIN_FILENO)) + input_line = "continue"; + else + { + round_info.cout << '\n'; + round_info.cout.flush(); + } +#else + input_line = "continue"; + round_info.cout << input_line << '\n'; + round_info.cout.flush(); +#endif /* HAVE_ISATTY */ } -#endif /* HAVE_READLINE */ + +#ifdef HAVE_ISATTY + if (!isatty(STDIN_FILENO)) + { + round_info.cout << input_line << '\n'; + round_info.cout.flush(); + } +#endif /* HAVE_ISATTY */ external_command = ""; string::size_type pipe_pos = input_line.find_first_of('|'); @@ -166,8 +190,11 @@ void executeUserCommands() if (!input_tokens.empty()) { #ifdef HAVE_READLINE - add_history(line); - free(line); + if (line != static_cast(0)) /* line may be 0 on EOF */ + { + add_history(line); + free(line); + } #endif /* HAVE_READLINE */ round_info.cout << '\n'; round_info.cout.flush(); diff --git a/lbtt/src/UserCommandReader.h b/lbtt/src/UserCommandReader.h index abe22cb77..dfe489291 100644 --- a/lbtt/src/UserCommandReader.h +++ b/lbtt/src/UserCommandReader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,15 +20,11 @@ #ifndef USERCOMMANDREADER_H #define USERCOMMANDREADER_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "Configuration.h" #include "Exception.h" diff --git a/lbtt/src/UserCommands.cc b/lbtt/src/UserCommands.cc index c948c85c2..baf7e456b 100644 --- a/lbtt/src/UserCommands.cc +++ b/lbtt/src/UserCommands.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include "DispUtil.h" #include "Exception.h" diff --git a/lbtt/src/UserCommands.h b/lbtt/src/UserCommands.h index feecf49b8..31167eae2 100644 --- a/lbtt/src/UserCommands.h +++ b/lbtt/src/UserCommands.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,17 +20,13 @@ #ifndef USERCOMMANDS_H #define USERCOMMANDS_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include #include #include #include #include -#include "Alloc.h" +#include "LbttAlloc.h" #include "BuchiAutomaton.h" #include "Configuration.h" #include "ProductAutomaton.h" diff --git a/lbtt/src/main.cc b/lbtt/src/main.cc index 01c7e386d..a8b02892c 100644 --- a/lbtt/src/main.cc +++ b/lbtt/src/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -28,7 +28,7 @@ #include #include #endif /* HAVE_READLINE */ -#include "Alloc.h" +#include "LbttAlloc.h" #include "Configuration.h" #include "DispUtil.h" #include "Exception.h" diff --git a/lbtt/src/translate.cc b/lbtt/src/translate.cc index 6580a4bb1..645e3a37f 100644 --- a/lbtt/src/translate.cc +++ b/lbtt/src/translate.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -17,10 +17,6 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifdef __GNUC__ -#pragma implementation -#endif /* __GNUC__ */ - #include #include #include diff --git a/lbtt/src/translate.h b/lbtt/src/translate.h index b25d06caf..4d7971859 100644 --- a/lbtt/src/translate.h +++ b/lbtt/src/translate.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003 + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 * Heikki Tauriainen * * This program is free software; you can redistribute it and/or @@ -20,10 +20,6 @@ #ifndef TRANSLATE_H #define TRANSLATE_H -#ifdef __GNUC__ -#pragma interface -#endif /* __GNUC__ */ - #include #include From d7b3d97422f0efe3666be07b1fae93b47e24f286 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 7 Jul 2004 16:40:50 +0000 Subject: [PATCH 4/7] Import of lbtt 1.1.0 --- lbtt/ChangeLog | 743 ++++++++ lbtt/INSTALL | 2 +- lbtt/NEWS | 132 +- lbtt/README | 14 +- lbtt/configure.ac | 220 ++- lbtt/doc/lbtt.texi | 1217 ++++++++----- lbtt/src/BuchiAutomaton.cc | 598 ++----- lbtt/src/BuchiAutomaton.h | 196 ++- lbtt/src/BuchiProduct.cc | 98 ++ lbtt/src/BuchiProduct.h | 512 ++++++ lbtt/src/Config-lex.ll | 290 +--- lbtt/src/Config-parse.yy | 1300 ++++---------- lbtt/src/Configuration.cc | 1610 +++++++++-------- lbtt/src/Configuration.h | 263 ++- lbtt/src/DispUtil.h | 20 +- lbtt/src/Exception.h | 52 + lbtt/src/ExternalTranslator.cc | 123 +- lbtt/src/ExternalTranslator.h | 120 +- lbtt/src/FormulaRandomizer.h | 1 + lbtt/src/FormulaWriter.h | 4 +- lbtt/src/Graph.h.in | 198 ++- lbtt/src/IntervalList.cc | 187 ++ lbtt/src/IntervalList.h | 499 ++++++ lbtt/src/LbttAlloc.h | 4 - lbtt/src/Ltl-parse.yy | 610 +++++++ lbtt/src/LtlFormula.cc | 194 +-- lbtt/src/LtlFormula.h | 70 +- lbtt/src/Makefile.am | 104 +- lbtt/src/NeverClaim-lex.ll | 1 + lbtt/src/PathEvaluator.cc | 47 +- lbtt/src/PathEvaluator.h | 10 +- lbtt/src/Product.h | 2936 ++++++++++++++++++++++++++++++++ lbtt/src/SccCollection.h | 1174 +++++++++++++ lbtt/src/StatDisplay.cc | 550 +++--- lbtt/src/StatDisplay.h | 19 +- lbtt/src/StateSpaceProduct.h | 430 +++++ lbtt/src/StringUtil.cc | 513 +++++- lbtt/src/StringUtil.h | 169 +- lbtt/src/TempFsysName.cc | 125 ++ lbtt/src/TempFsysName.h | 157 ++ lbtt/src/TestOperations.cc | 1082 +++++++----- lbtt/src/TestOperations.h | 14 +- lbtt/src/TestRoundInfo.h | 33 +- lbtt/src/TestStatistics.h | 44 +- lbtt/src/UserCommandReader.cc | 146 +- lbtt/src/UserCommands.cc | 1482 +++++++--------- lbtt/src/UserCommands.h | 67 +- lbtt/src/main.cc | 335 ++-- lbtt/src/translate.cc | 77 +- 49 files changed, 13388 insertions(+), 5404 deletions(-) create mode 100644 lbtt/src/BuchiProduct.cc create mode 100644 lbtt/src/BuchiProduct.h create mode 100644 lbtt/src/IntervalList.cc create mode 100644 lbtt/src/IntervalList.h create mode 100644 lbtt/src/Ltl-parse.yy create mode 100644 lbtt/src/Product.h create mode 100644 lbtt/src/SccCollection.h create mode 100644 lbtt/src/StateSpaceProduct.h create mode 100644 lbtt/src/TempFsysName.cc create mode 100644 lbtt/src/TempFsysName.h diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index 0d368e560..f0b8db9e4 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,744 @@ +2004-07-07 Heikki Tauriainen + + * Version 1.1.0 released. + +2004-07-06 Heikki Tauriainen + + * configure.ac: Make detection of the need for obstack.h + compilation workaround with glibc 2.3.2 more accurate. + Remove check for single_client_alloc. + * src/LbttAlloc.h: Remove conditional definition of + the ALLOC macro. + * src/UserCommandReader.cc: Remove workaround for + compilation with gcc 2.95. + +2004-07-02 Heikki Tauriainen + + * doc/texinfo.tex: New upstream version. + * doc/lbtt.texi: Update to edition 1.1.0. + * NEWS: Update. + +2004-07-02 Heikki Tauriainen + + * src/UserCommandReader.cc (parseCommand): Recognize + `implementations' and `translators' as aliases of the + `algorithms' command. + * src/UserCommands.cc (printCommandHelp): List + `implementations' and `translators' as aliases of the + `algorithms' command. + +2004-06-30 Heikki Tauriainen + + * src/Configuration.h (CommandLineOptionType): Rearrange the + values to list constants with an explicit character value + first. Change the value of OPT_VERSION to `V'. + * src/Configuration.cc (Configuration::read): Treat the single- + character option `V' as an alias of the `--version' option. + (Configuration::showCommandLineHelp): Describe the `-V' option. + * src/translate.cc (main): Rename the single character option + `v' to `V'. + +2004-06-29 Heikki Tauriainen + + * src/BuchiAutomaton.h (BuchiAutomaton::regularize) + (BuchiAutomaton::intersect): Remove. + (BuchiAutomaton::BuchiTransition::print(_, _, _)): Override + the corresponding virtual function in parent class explicitly. + * src/BuchiAutomaton.cc (BuchiAutomaton::regularize) + (BuchiAutomaton::intersect): Remove. + +2004-05-31 Heikki Tauriainen + + * src/LtlFormula.h: Do not include the map header. Include the + set header instead. + (LtlFormula::refcount): New variable. + (LtlFormula::formula_storage): Change type to a set of + pointers to LtlFormulas. + (LtlFormula::LtlFormula): Initialize `this->refcount' to 1. Do + not initialize `info_flags' explicitly (this is always done in + subclasses). + (LtlFormula::destruct): Update the reference counts of formulas + directly. + (LtlFormula::clone, LtlFormula::insertToStorage): Likewise. + (Atom::Atom): Do not call parent class constructor explicitly. + (UnaryFormula::UnaryFormula): Likewise. + (BinaryFormula::BinaryFormula): Likewise. + * src/LtlFormula.cc: Update definition of the static member + `LtlFormula::formula_storage'. + * src/FormulaRandomizer.h: Include the map header. + +2004-05-19 Heikki Tauriainen + + * configure.ac (BIGUINT): Define as unsigned long long int + if the compiler supports it (unsigned long int otherwise). + * src/TestStatistics.h + (TestStatistics::total_number_of_buchi_states) + (TestStatistics::total_number_of_buchi_transitions) + (TestStatistics::total_number_of_acceptance_sets) + (TestStatistics::total_number_of_product_states) + (TestStatistics::total_number_of_product_transitions): + Change type to BIGUINT. + (TestStatistics::total_number_of_msccs): Remove. + (TestStatistics::TestStatistics): Remove initialization + of `total_number_of_msccs'. + * src/StatDisplay.cc (printCollectiveStats): Use BIGUINTs + for referencing total state, transition and acceptance set + counts. + +2004-05-18 Heikki Tauriainen + + * src/ProductAutomaton.h, src/ProductAutomaton.cc, + src/SccIterator.h: Remove. The functionality provided by + these files has been rearranged into more general product + computation and emptiness checking routines in: + * src/Product.h, src/SccCollection.h: New files. + * src/BuchiProduct.h, src/BuchiProduct.cc, src/StateSpaceProduct.h: + New files for providing specializations of the general product + computation operation applicable to Büchi automata and state + spaces. + + * src/Makefile.am: Add BuchiProduct.h, BuchiProduct.cc, + Product.h, SccCollection.h and StateSpaceProduct.h to + lbtt_SOURCES. + Remove ProductAutomaton.h, ProductAutomaton.cc and + SccIterator.h from lbtt_SOURCES. + + * src/Graph.h.in (Graph::EdgeContainerType, Graph::Path): New + type definitions. + (Graph::PathElement): New class. + + * src/PathEvaluator.h (evaluate): Change prototype to accept + a path prefix and a cycle as parameters. + * src/PathEvaluator.cc (evaluate): Rewrite to support the + changed prototype. + + * src/TestRoundInfo.h: Do not include the ProductAutomaton.h + header. + (TestRoundInfo::product_automaton): Remove. + (TestRoundInfo::TestRoundInfo): Remove initialization of + `product_automaton'. + + * src/TestStatistics.h: Do not include the ProductAutomaton.h + header. Include the EdgeContainer.h and Graph.h headers. + (AutomatonStats::number_of_product_states): Change type to + Graph::size_type. + + * src/main.cc (testLoop): Remove references to + `round_info.product_automaton' and `generateProductAutomaton'. + + * src/TestOperations.h (generateProductAutomaton): Remove. + * src/TestOperations.cc: Include the BuchiProduct.h, Product.h, + SccCollection.h and StateSpaceProduct.h headers. Do not + include the ProductAutomaton.h and SccIterator.h headers. + Define static members of + Product and Product. + (generateProductAutomaton): Remove. + (performEmptinessCheck): Display output messages from the + removed `generateProductAutomaton' function here. Do the + same for updating product automaton statistics. Use the + Product interface for constructing the product and checking + it for emptiness. + (performBuchiIntersectionCheck): Use the Product interface + for constructing the intersection of Büchi automata and + checking it for emptiness. + + * src/UserCommandReader.cc: Do not include the + ProductAutomaton.h header. + (executeUserCommands): Remove the variables `product_automaton' + and `last_computed_product_automaton'. + + * src/UserCommands.h: Do not include the ProductAutomaton.h + header. Include the EdgeContainer.h, Graph.h and Product.h + headers. + (computeProductAutomaton): Remove. + (synchronizePrefixAndCycle): Remove. + (printCrossComparisonAnalysisResults): Remove + `product_automaton' and `last_product_automaton' from + prototype. + (printPath): Change the types of `prefix' and `cycle' to + constant references to StateSpace::Path objects. + (printAcceptingCycle). Expect also a prefix and cycle of a + state space as parameters. Represent the path segments as + constant references to {BuchiAutomaton,StateSpace}::Path + objects. + * src/UserCommands.cc: Include the BuchiProduct.h, Product.h + and StateSpaceProduct.h headers. + (computeProductAutomaton): Remove. + (synchronizePrefixAndCycle): Remove. + (printCrossComparisonAnalysisResults): Update parameter list and + documentation. Use the Product interface for constructing and + analyzing a witness for the nonemptiness of a product automaton. + (printAutomatonAnalysisResults): Use the Product interface + for constructing and analyzing a witness for the nonemptiness + of the intersection of two Büchi automata. + (printPath): Update parameter list and documentation. Standardize + the output to resemble that produced by printAcceptingCycle. + (printAcceptingCycle): Update parameter list and documentation. + Display all relevant information about an accepting execution + of a Büchi automaton. + +2004-05-18 Heikki Tauriainen + + * configure.ac (YACC): Do not add `-d' here. + + * src/Makefile.am: Rearrange to make future updates easier. + (BUILT_SOURCES): New variable. + (AM_YFLAGS): New variable for adding the `-d' flag to be + passed to YACC. + Move Config-parse.h and NeverClaim-parse.h from + EXTRA_lbtt_SOURCES and EXTRA_lbtt_translate_SOURCES to + BUILT_SOURCES. + + Thanks to Alexandre Duret-Lutz for patches. + +2004-05-13 Heikki Tauriainen + + * configure.ac: Add checks for the sys/times.h and sys/wait.h + headers. Add checks for the strerror, mkstemp, open, read, write, + close, popen, pclose, pipe, fork, execvp, getpid, waitpid, alarm, + sigaction, sigprocmask, sigemptyset, sigaddset, times, sysconf, + and strsignal library functions. Remove check for the return type + of signal. + +2004-05-13 Heikki Tauriainen + + * src/NeverClaim-lex.ll: Add %option nounput to avoid a + compiler warning. + + * src/ExternalTranslator.h: Include the TempFsysName.h header. + (ExternalTranslator::TempFileObject): Remove. + (ExternalTranslator::registerTempFileObject): Change + prototype to match that of TempFsysName::allocate. + (ExternalTranslator::temporary_file_objects): Change to a + stack of pointers to TempFsysName objects. + * src/ExternalTranslator.cc + (ExternalTranslator::registerTempFileObject): Use the + TempFsysName interface for creating temporary names. + (ExternalTranslator::translate): Handle temporary file names + as C-style strings. + (ExternalTranslator::TempFileObject): Remove. + * src/translate.cc: Make `translator' a static variable. + (signalHandler): Delete `translator' if necessary to remove + any temporary files before aborting. Restore the default + signal handler using sigaction. + (installSignalHandler): New function. + (main): Fix the number of dashes in the descriptions of + single-character command line options. + Install handler for signals that terminate the + process. + + * src/Makefile.am: Add TempFsysName.h and TempFsysName.cc to + lbtt_translate_SOURCES. + +2004-05-12 Heikki Tauriainen + + * src/Graph.h.in (Graph::Edge) Fix return + type of targetNode() and the type of the `target_node' + variable to allow compilation with gcc 3.4.0. + (Graph::stats): Fix type definition of + `result' to allow compilation with gcc 3.4.0. + * src/FormulaWriter.h: Include the LtlFormula.h header + to allow compilation with gcc 3.4.0. Remove forward + declarations of class LtlFormula and class Atom. + +2004-03-18 Heikki Tauriainen + + * src/UserCommandReader.cc (executeUserCommands): Print a + newline after each nonempty input line regardless of whether + the line consists only of an external command or not. Don't + print a newline after piping the output of a command to an + external program. Print a newline after the output of a + command. Do not interpret failures when writing to pipe as + errors to allow external programs to ignore some of the input. + + * src/UserCommands.cc (printAlgorithmList) + (printCrossComparisonAnalysisResults) + (printConsistencyAnalysisResults, printAutomatonAnalysisResults) + (printAcceptingCycle, printBuchiAutomaton, evaluateFormula) + (printCommandHelp, printInconsistencies, printStateSpace): + Remove extra newlines from the end of output. + + * src/StatDisplay.cc (printBuchiIntersectionCheckStats): + Remove extra newlines from the end of output. + + * src/TestOperations.cc (performBuchiIntersectionCheck): Print + a newline after the results to transcript file and to console + in verbosity modes >= 3. + +2004-03-17 Heikki Tauriainen + + * src/BuchiAutomaton.h: Include the cstdlib header. + Fix description of the BuchiAutomaton class. + (BuchiAutomaton::connect): Add parameters for the acceptance + sets associated with a transition. + (BuchiAutomaton::BuchiTransition::BuchiTransition): Add + parameters for the acceptance sets associated with a transition. + Remove copy constructor. + (BuchiAutomaton::BuchiTransition::print): Remove default values + for arguments. Add parameter for the number of acceptance sets + associated with a transition. + (BuchiAutomaton::BuchiTransition::acceptanceSets) New functions. + (BuchiAutomaton::BuchiTransition::acceptance_sets): New + variable. + + * src/BuchiAutomaton.cc + (BuchiAutomaton::BuchiAutomaton(const BuchiAutomaton&): Copy + also the acceptance sets of each transition. + (BuchiAutomaton::operator=): Copy also the acceptance sets of + each transition. + (BuchiAutomaton::regularize): Handle also acceptance sets on + transitions. + (BuchiAutomaton::read): Update description. Accept automata with + acceptance sets on states and/or transitions. + (BuchiAutomaton::intersect): Take care of acceptance sets + associated with transitions when forming the intersection of two + automata. + (BuchiAutomaton::print): Change output to refer to "acceptance + sets" instead of "sets of accepting states". Pass the number of + acceptance sets to BuchiAutomaton::BuchiTransition::print when + displaying the automaton in dot format. + (BuchiAutomaton::BuchiTransition::print): List the acceptance + sets associated with each transition in all output modes. + (BuchiAutomaton::BuchiState::print): Pass the number of + acceptance sets to BuchiAutomaton::BuchiTransition::print. + +2004-03-15 Heikki Tauriainen + + * src/UserCommands.h (printFormula): Change prototype to accept + the vector of input tokens as an additional parameter. + * src/UserCommands.cc (printFormula): Accept the vector of + input tokens as an additional parameter. Use the optional + parameter "nnf" or "normal" to choose the display mode. + Display only the formula to accommodate feeding the output (if + redirected to a file) back to lbtt using the --formulafile + command line option. + (printCommandHelp): Update the description of the `formula' + command. + + * src/UserCommandReader.cc (executeUserCommands): Accept one + optional parameter for the `formula' command. Pass the input + tokens as an additional parameter in the printFormula call. + Print an additional newline after the call if the output is + not redirected to a file. + +2004-03-12 Heikki Tauriainen + + * src/Exception.h: Include the istream header. + (Exceptional_istream::get, Exceptional_istream::read): New + functions. + + * src/Ltl-parse.yy: New file. + + * src/LtlFormula.h (parseFormula): Declare. + (LtlFormula::read(istream&)): Read the formula by calling + parseFormula (provided by Ltl-parse.yy). + (LtlFormula::read(Exceptional_istream&)): Remove. + * src/LtlFormula.cc (LtlFormula::read(Exceptional_istream&)): + Remove. + + * src/Makefile.am: Add Ltl-parse.yy to lbtt_SOURCES and + lbtt_translate_SOURCES. + +2004-03-08 Heikki Tauriainen + + * src/TempFsysName.h, src/TempFsysName.cc New files. + * src/Makefile.am: Add TempFsysName.h and TempFsysName.cc to + lbtt_SOURCES. + + * src/TestRoundInfo.h: Include the TempFsysName.h header. + (TestRoundInfo::formula_file_name[]) + (TestRoundInfo::automaton_file_name) + (TestRoundInfo::{cout,cerr}_capture_file): Change type to + TempFsysName*. + (TestRoundInfo::TestRoundInfo): Initialize temporary file + name pointers to 0. + + * src/main.cc: Include the TempFsysName.h header. + (allocateTempFilenames, deallocateTempFilenames): New functions. + (abortHandler): New signal handler. + (installSignalHandler): New function. + (testLoop): Do not explicitly remove the temporary formula files. + (main): Use the above functions for allocating and deallocating + names for temporary files. Handle signals that terminate the + process by cleaning up temporary files before aborting; however, + for SIGINT, do this only if user breaks are not handled by lbtt. + + * src/TestOperations.h (removeFile): Rename as truncateFile. + * src/TestOperations.cc: Include the TempFsysName.h header. + (removeFile): Rename as truncateFile; truncate file instead of + removing it. + (writeFormulaeToFiles): Use the TempFsysName interface for + referring to the names of temporary files. + (generateBuchiAutomaton): Truncate all temporary files before + exec'ing an external process. Use the TempFsysName interface + for referring to the names of temporary files. + + * src/ExternalTranslator.cc: Include the cstring header. + (ExternalTranslator::TempFileObject::TempFileObject): Use + mkstemp instead of tmpnam to allocate a name for a temporary + file. + +2004-03-08 Heikki Tauriainen + + * src/StringUtil.h (findInQuotedString): New function prototype. + * src/StringUtil.cc (findInQuotedString): New function. + + * src/UserCommandReader.cc (executeUserCommands): + [HAVE_READLINE] Add the input line to history before extracting + the external command from the line. + Ignore white space within quotes when splitting input line into + tokens. + Move parsing the algorithm identifiers given for the + `buchianalysis' command to + src/UserCommands.cc (printAutomatonAnalysisResults). + (parseRedirection): Explicitly unquote the file name. + + * src/UserCommands.h: Include the map and the IntervalList.h + headers. + (parseAlgorithmId, parseAlgorithmIdList): New function prototypes. + (printAutomatonAnalysisResults): Change function prototype to + accept a list of tokens instead of two algorithm identifiers. + + * src/ProductAutomaton.h: Include the string header. Rewrite + the friend declaration of + UserCommands::printAutomatonAnalysisResults. + + * src/UserCommands.cc (parseAlgorithmId, parseAlgorithmIdList): + New functions. + (printCrossComparisonAnalysisResults) + (printConsistencyAnalysisResults): Parse algorithm identifier + using the parseAlgorithmId function. + (printAutomatonAnalysisResults): Parse algorithm identifiers + using the parseAlgorithmId function. Throw a CommandErrorException + in the case one of the algorithm identifiers refers to lbtt's + internal model checking algorithm. + (printBuchiAutomaton): Parse algorithm identifier using the + parseAlgorithmId function. Throw a CommandErrorException in the + case the identifier refers to lbtt's internal model checking + algorithm. + (printCommandHelp): Do not treat the internal model checking + algorithm as a special case in command descriptions. + (evaluateFormula, printInconsistencies, printTestResults) + (printStateSpace, changeAlgorithmState): Parse lists of + algorithms and states using the parseAlgorithmIdList and the + StringUtil::parseIntervalList functions, respectively. Access the + numeric algorithm and state identifiers via IntervalLists. Make + error messages more consistent. + +2004-03-07 Heikki Tauriainen + + * src/StatDisplay.h: Include the IntervalList.h header. + (printStatTableHeader): New function prototype. + (printCrossComparisonStats, printBuchiIntersectionCheckStats): + Change the function prototypes to accept a reference to an + IntervalList for the algorithm identifiers. + * src/StatDisplay.cc (printStatTableHeader): New function. + * src/StatDisplay.cc (printBuchiAutomatonStats) + (printProductAutomatonStats, printAcceptanceCycleStats) + (printConsistencyCheckStats): Display statistics in a table in + verbosity mode <= 2. + (printCrossComparisonStats): Iterate over the set of algorithms + using an IntervalList. Do not handle the internal model checking + algorithm as a special case. + (printBuchiIntersectionCheckStats) Iterate over the set of + algorithms using an IntervalList. Skip the internal model + checking algorithm when displaying automata generation or + intersection check statistics. + (printAllStats): Display statistics in a table in verbosity + mode <= 2. + (printCollectiveStats): Skip the internal algorithm when + displaying the rows of the cross-comparison result table. Use + the changed semantics of Configuration::AlgorithmInformation + to find the name of an implementation. + + * src/main.cc (testLoop): Display test statistics in a table in + verbosity modes 1 and 2. + + * src/TestOperations.cc: Include the IntervalList.h header. + Remove inclusion of the TestStatistics.h header. + (generateBuchiAutomaton, generateProductAutomaton) + (performEmptinessCheck): Display the test statistics in a table + in verbosity modes 1 and 2. + (compareResults, performBuchiIntersectionCheck): Display the + result of the test also in verbosity mode 2. Show test statistics + using the changed semantics of + StatDisplay::printCrossComparisonStats and + StatDisplay::printBuchiIntersectionCheckStats. + (verifyFormulaOnPath, generateProductAutomaton) + (performEmptinessCheck, performBuchiIntersectionCheck): More + consistent error messages when the test is aborted. + + * src/UserCommands.cc (evaluateFormula): Use the changed + semantics of Configuration::AlgorithmInformation to find the + name of an implementation. + (printTestResults): Display the test results using the changed + semantics of StatDisplay::printCrossComparisonStats and + StatDisplay::printBuchiIntersectionCheckStats. + +2004-03-04 Heikki Tauriainen + + * src/TestOperations.h (openFile(const char*, int&, int, int)): + New function prototype. + * src/TestOperations.cc: Include the csignal, cstring, cerrno, + [HAVE_SYS_TYPES_H] sys/types.h, [HAVE_SYS_STAT_H] sys/stat.h, + [HAVE_FCNTL_H] fcntl.h, and [HAVE_SYS_WAIT_H] sys/wait.h headers. + (timeout): New variable. + (timeoutHandler): New function. + (openFile(const char*, int&, int, int)): New function. + (verifyFormulaOnPath): Fix references to the internal model + checking algorithm. + (generateBuchiAutomaton): Execute external program using fork/exec + instead of `system' to improve the detection of the case when an + external program was terminated by a signal (the behavior of + "system" in the presence of signals varies between architectures). + Handle timeouts when running the external programs. Discard I/O + exceptions while displaying the stdout/stderr capture file. + (performBuchiIntersectionCheck): Skip the internal model checking + algorithm when iterating over algorithms. + +2004-03-04 Heikki Tauriainen + + * src/main.cc (breakHandler): Make function static and change + return type to void. + (testLoop): Do not add the internal model checking algorithm to + the list of implementations (it is done in Configuration::read). + Apply the internal model checking algorithm if it is enabled. + Otherwise skip the automata generation loop as it is not + applicable for this algorithm. + (main): Enable interrupt handler only if mandated by the program + configuration. Use sigaction instead of signal to register the + signal handler. + +2004-03-04 Heikki Tauriainen + + * src/Config-lex.ll, src/Config-parse.yy, src/Configuration.h, + src/Configuration.cc: Rewrite the configuration file parser to + allow reusing a common set of functions for changing the various + settings independently of whether the settings were given in the + configuration file or as command-line options. Rearrange the + structure of Configuration::AlgorithmInformation, remove the + "nopause" and "pauseonbreak" command line options (and make + "pause" an alias of "interactive"), recognize lists of + interactivity modes (with the new mode "onbreak") both in the + configuration file and command line, and add the new + configuration setting "translatortimeout". In more detail, + the changes are: + + * src/Config-lex.ll: Do not include any standard headers. Add + %option nounput to suppress a compiler warning. Accept + "Implementation" or "Translator" as an alias of the Algorithm + block identifier. Do not accept newlines in "option = value" + strings. Do not interpret option values in the lexer. Make + specifying option values more flexible: quotes are now needed + only for values including white space. Both single and double + quotes may be used (as before, \ works as an escape character in + double quotes). Reject values with unmatched quotes. Remove the + getCharacter function (add rules for matching unknown tokens in + full). + + * src/Config-parse.yy: Do not include the cstdio header. Add + namespace StringUtil to the current namespace. + ([declarations]) Change %union declaration to make all semantic + values constant C-style strings. Remove tokens CFG_TRUTH_VALUE, + CFG_INTERACTIVITY_VALUE, CFG_FORMULA_MODE_VALUE, + CFG_STATESPACE_MODE_VALUE, CFG_PRODUCT_TYPE_VALUE, CFG_INTEGER, + CFG_REAL, CFG_INTEGER_INTERVAL and CFG_STRING_CONSTANT. Add + new token CFG_VALUE and nonterminal equals_value. + (algorithm_information): Replaced with `algorithm_name', + `algorithm_path', `algorithm_parameters' and `algorithm_enabled'. + (current_block_type, current_option_type): Removed. + (yyerror): Make parameter const. Remove the loop for reading the + remainder of the current token (the token is recognized in full + directly by the lexer). Remove error messages for all tokens in + the above list. + (checkIntegerRange, checkProbability, isLocked): Removed. + ([grammar rule `equals_value']): New rule. + ([grammar rules for the "Algorithm" configuration file section]): + Read the implementation information into `algorithm_name', + `algorithm_path', `algorithm_parameters' and `algorithm_enabled'. + Use the functions provided by the Configuration class to update + the configuration. + ([grammar rules for the other configuration file sections]): Use + the functions provided by the Configuration class to update the + configuration. Remove the rules `formula_size' and + `statespace_size'. + + * src/Configuration.h: Include the cstdio header. Add + declarations for variables and functions provided by the parser. + Declare yyparse() as a friend. + (Configuration::AlgorithmInformation::path_to_program) + (Configuration::IntPair, Configuration::locked_options) + (Configuration::GENERATION_RANGE, Configuration::PRIORITY_RANGE) + (Configuration::PROPOSITION_COUNT_RANGE) + (Configuration::FORMULA_SIZE_RANGE) + (Configuration::FORMULA_MAX_SIZE_RANGE) + (Configuration::STATESPACE_SIZE_RANGE) + (Configuration::STATESPACE_MAX_SIZE_RANGE) + (Configuration::OPT_NOCOMPARISONTEST) + (Configuration::OPT_NOCONSISTENCYTEST) + (Configuration::OPT_NOINTERSECTIONTEST) + (Configuration::OPT_NOABBREVIATEDOPERATORS) + (Configuration::OPT_PAUSE, Configuration::OPT_NOPAUSE) + (Configuration::OPT_PAUSEONERROR): + Remove. + (Configuration::AlgorithmInformation::extra_parameters): Redefine + as `char** parameters'. + (Configuration::AlgorithmInformation::num_parameters) + (Configuration::GlobalConfiguration::handle_breaks) + (Configuration::GlobalConfiguration::translator_timeout) + (Configuration::algorithm_names: New variables. + (Configuration::IntegerRange): Change the type of lower and upper + bounds to unsigned long int. Make `error_message' const. + (Configuration::DEFAULT_RANGE, Configuration::RANDOM_SEED_RANGE) + (Configuration::ATOMIC_PRIORITY_RANGE) + (Configuration::OPERATOR_PRIORITY_RANGE): New IntegerRange + declarations. + (Configuration::OPT_TRANSLATORTIMEOUT): New OPT_ constant. + (Configuration::registerAlgorithm, Configuration::readProbability) + (Configuration::readSize, Configuration::readTruthValue) + (Configuration::readInteractivity, Configuration::readProductType) + (Configuration::readFormulaMode, Configuration::readStateSpaceMode) + (Configuration::readTranslatorTimeout): New function + prototypes. + (Configuration::isInternalAlgorithm): New function. + (Configuration::readInteger): New template function. + + * src/Configuration.cc: Do not include the cstdio and + Config-parse.h headers. Include the IntervalList.h header. Remove + declarations for checkIntegerRange and checkProbability. Move + remaining extern declarations to Configuration.h. + (Configuration::GENERATION_RANGE, Configuration::PRIORITY_RANGE) + (Configuration::PROPOSITION_COUNT_RANGE) + (Configuration::FORMULA_SIZE_RANGE) + (Configuration::FORMULA_MAX_SIZE_RANGE) + (Configuration::STATESPACE_SIZE_RANGE) + (Configuration::STATESPACE_MAX_SIZE_RANGE) + (Configuration::parseCommandLineInteger): Remove. + (Configuration::DEFAULT_RANGE, Configuration::RANDOM_SEED_RANGE) + (Configuration::ATOMIC_PRIORITY_RANGE) + (Configuration::OPERATOR_PRIORITY_RANGE): New IntegerRange + definitions. + (Configuration::ROUND_COUNT_RANGE): Change upper bound to + ULONG_MAX. + (Configuration::~Configuration): Use the changed semantics + of Configuration::AlgorithmInformation. + (Configuration::read): The + --{comparison,consistency,intersection}{test,check} and + --abbreviatedoperators command line options now take an optional + argument. + The options + --no{{comparison,consistency,intersection}{test,check}, + abbreviatedoperators} return the same OPT_ constant as the + corresponding positive option. + The --interactive and --pause options take an optional argument. + Remove the --nopause and --pauseonerror command line options. + Store the command line parameters into a vector of + pairs (preprocess the special options --configfile, --formulafile, + --help, --logfile, --showconfig, --showoperatordistribution, + --skip and --version; preprocess also the options accepting an + optional argument). Then read the configuration file. Add the + internal model checking algorithm to the set of algorithms if + necessary here instead of in src/main.cc (testLoop). Finally + process the parameter vector again to override any settings made + in the configuration file. + Use the new functions for modifying the configuration. + Allow symbolic algorithm identifiers for the options --enable + and --disable. + Make error messages more consistent. + Exit with the status 2 if an unknown command line option was + encountered or an argument for an option was missing. + (Configuration::print): Tell whether the user break handler is + enabled; tell also the value of the timeout for translators. + (Configuration::algorithmString): Use the new structure of + Configuration::AlgorithmInformation. + (Configuration::showCommandLineHelp): Rewrite the descriptions of + the --{comparison,consistency,intersection}test, + --abbreviatedoperators, --interactive and --pause command line + options. Correct typos in the description of the + --abbreviatedoperators option. Remove descriptions of the + --nopause and --pauseonerror options. + (Configuration::reset): Initialize the values of + `global_options.handle_breaks' and + `global_options.translator_timeout' to false and 0, respectively. + (Configuration::registerAlgorithm): New function for adding a + new implementation to the configuration. The names of + implementations should be unique and different from the reserved + name "lbtt". + (Configuration::readProbability, Configuration::readSize) + (Configuration::readTruthValue, Configuration::readInteractivity) + (Configuration::readProductType, Configuration::readFormulaMode) + (Configuration::readStateSpaceMode) + (Configuration::readTranslatorTimeout): New functions. The + interactivity can now be specified using a comma-separated list + of modes that may include the new keyword "onbreak". + +2004-03-02 Heikki Tauriainen + + * src/StringUtil.h (toLowerCase, unquoteString) + (substituteInQuotedString, parseTime): New function prototypes. + (QuoteMode): New enumeration type. + * src/StringUtil.cc: Include the cctype header. + (toLowerCase, interpretSpecialCharacters, unquoteString) + (substituteInQuotedString, parseTime): New functions. + +2004-02-19 Heikki Tauriainen + + * src/TestRoundInfo.h (TestRoundInfo::all_tests_successful): New + variable. + (TestRoundInfo::TestRoundInfo): Initialize the value of + all_tests_successful to true. + + * src/main.cc (testLoop): Update the value of + round_info.all_tests_successful to indicate the occurrence of an + error. Use the value of this variable as return value from + testLoop. + (main): Return 1 if an error occurred during testing. Return 2 if + there was an error in program configuration; remove extra space + after colon in output. Return 3 if an unexpected exception + occurred. In this case print an additional newline before the + error message. + +2004-02-19 Heikki Tauriainen + + * src/StringUtil.h: Include the IntervalList.h header. + (IntervalStringType): New enumeration type for describing the + boundedness of interval strings. + (parseInterval): New function prototype. Renamed the old prototype + to `parseIntervalList'. + * src/StringUtil.cc: Include the climits header. + (parseNumber): Be more strict about ensuring that the numbers are + positive. + (parseInterval): New function for parsing interval strings. + (parseIntervalList): Use the `parseInterval' function as a + subroutine when parsing a list of intervals. Add a parameter for + optionally storing tokens not recognized as intervals into a + vector of strings. Store the result into an IntervalList. Throw an + IntervalRangeException if an interval does not fit within the + given bounds. + +2004-02-18 Heikki Tauriainen + + * src/IntervalList.h, src/IntervalList.cc: New files. + * src/Makefile.am: Add IntervalList.h and IntervalList.cc to + lbtt_SOURCES. + +2004-02-17 Heikki Tauriainen + + * src/TestOperations.cc (writeFormulaeToFiles): Make the order of + debugging messages (verbosity level 5) more consistent. + +2004-02-15 Heikki Tauriainen + + * src/DispUtil.h (printText): Move function declarations to their + correct place (out of struct StreamFormatting). + +2004-02-15 Heikki Tauriainen + + * configure.ac: Require Autoconf 2.59. + Remove use of deprecated macros. + --with-readline-prefix, --with-readline-includes, + --with-readline-libs: New options. + 2004-02-13 Heikki Tauriainen * src/BitArray.{h,cc}, src/BuchiAutomaton.{h,cc}, @@ -12,6 +753,8 @@ src/translate.{h,cc}, src/UserCommandReader.{h,cc}, src/UserCommands.{h,cc}: Remove all #pragmas. + * Version 1.0.3 released. + 2004-02-12 Heikki Tauriainen * doc/texinfo.tex: New upstream version. diff --git a/lbtt/INSTALL b/lbtt/INSTALL index a4b34144d..54caf7c19 100644 --- a/lbtt/INSTALL +++ b/lbtt/INSTALL @@ -1,4 +1,4 @@ -Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives diff --git a/lbtt/NEWS b/lbtt/NEWS index 574e07d73..4abdfd71b 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,4 +1,4 @@ -lbtt NEWS -- history of user-visible changes. 13 Feb 2004 +lbtt NEWS -- history of user-visible changes. 7 Jul 2004 Copyright (C) 2004 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies @@ -12,6 +12,136 @@ Copyright (C) 2004 Heikki Tauriainen Please send bug reports to . +Version 1.1.0 + +* File formats + + - The file format for automata description files has changed to + accommodate automata with acceptance conditions on both states + and transitions. The old format for automata remains supported + with the restriction that each guard formula of a transition + should be followed by a newline (with optional preceding white + space). + + - In addition to the prefix format for LTL formulas, the input + files used with the `--formulafile' command line option may now + contain formulas in a variety of other formats, such as in the + infix format used by lbtt for log messages, together with formats + used by some LTL-to-Büchi translator implementations (Spin, + LTL2BA, LTL2AUT, Temporal Massage Parlor, Wring, Spot, LBT). + These formats can also be used for guard formulas in automaton + description files (however, lbtt still uses the prefix format in + the input files for the translators). + + Thanks to Alexandre Duret-Lutz for useful suggestions for + enhancements. + +* Support for symbolic names of implementations + + - Beside the numeric identifiers of implementations, lbtt now + accepts also the symbolic names of implementations (as defined in + a configuration file) as parameters for command line options and + internal commands. Consequently, the names of implementations + defined in the configuration file have to be unique. + + - The name `lbtt' is now reserved for lbtt's internal model checking + algorithm and cannot be used as a name for an implementation in + the configuration file. + +* User commands + + - For consistency, numeric intervals in state or implementation + identifier lists can now be specified using either - or ... as a + separator between the bounds of the interval. + + - The user command `formula' now accepts an additional parameter + (`normal' or `nnf') for choosing whether to display a formula in + the form in which it was generated or in negation normal form. + + - The internal model checking algorithm is now referred to with the + keyword "lbtt" instead of "p" as was the case with previous + versions of lbtt. The internal model checking algorithm can now be + enabled or disabled similarly to the external translators. + + - The `consistencyanalysis' and `buchianalysis' commands now show + more information about the accepting runs of Büchi automata to + help examining the runs. (Because of this change, the runs and + witnesses may be longer than in previous versions of lbtt.) + + - The `implementations' and `translators' commands are now recognized + as synonyms of the `algorithms' command. + +* Configuration files + + - Quotes are no longer required for enclosing string values + containing no white space. + + - Numeric intervals in formula or state space sizes can now be + specified using either - or ... as a separator between the bounds + of the interval. + + - The keywords "Implementation" and "Translator" are now recognized + as synonyms of the "Algorithm" block identifier. + +* User interrupts + + Keyboard interrupt handling is now enabled only at explicit request + (if not enabled, lbtt simply aborts on keyboard interrupts). The + interrupt handler is enabled by combining the keyword `onbreak' with + any of the three standard interactivity modes (`always', `never', or + `onerror') in the arguments for the `GlobalOptions.Interactive' + configuration file option or the `--interactive' command line option. + For example, use the command line option + `--interactive=onerror,onbreak' to pause testing in case of an error + or on a user interrupt. + +* Command line options + + - The `--pause' command line option now works identically to the + `--interactive' option. + + - The command-line options `--nopause' and `--pauseonerror' are no + longer supported. Use the `--interactive' or the `--pause' + option instead with an optional argument of a comma-separated list + of interactivity modes (`always', `never', `onerror', `onbreak'). + +* Timeouts + + lbtt now supports specifying a time (in wall-clock time) after + which the execution of a translator is aborted if it has not yet + produced a result. The timeout can be set using either the new + configuration file option `GlobalOptions.TranslatorTimeout' or the + equivalent command line option `--translatortimeout'. Both options + require a parameter of the form [hours]h[minutes]min[seconds]s; for + example, use the command line option `--translatortimeout=1h30min' + to set the timeout at one hour and thirty minutes. + +* Reporting + + - lbtt now reports test statistics also in verbosity modes 1 and 2. + The output of the user command `results' also reflects the active + verbosity mode more accurately. + + - lbtt now exits with status 0 only if no test failures were + detected; otherwise the exit status is either 1 (at least + one failure occurred), 2 (error in program configuration or + command line parameters) or 3 (lbtt exited due to an internal + error). + +* Internal changes + + Due to the changes in the supported file formats, this version + includes a rewrite of the product computation and emptiness checking + algorithms. As this is a major internal change, any information about + unexpected changes in the stability (*) of the tool is welcomed at the + e-mail address given above. + + (*) Unfortunately, the above changes in the source code are known to + cause build problems with GCC 2.95. Therefore, this compiler is no + longer officially supported for building the tool. + +--------------------------------------------------------------------------- + Version 1.0.3 * This release fixes several compilation issues with GNU libc 2.3.2 diff --git a/lbtt/README b/lbtt/README index ad585e237..c400bf804 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,14 +1,14 @@ -lbtt version 1.0.3 +lbtt version 1.1.0 ------------------ lbtt is a tool for testing programs that translate formulas expressed in propositional linear temporal logic (LTL) into -Büchi automata. The goal of the tool is to assist in the -correct implementation of LTL-to-Büchi translation algorithms -by providing an automated testing environment for LTL-to-Büchi -translators. Additionally, the testing environment can be used -for very basic profiling of different LTL-to-Büchi translators -to evaluate their performance. +Büchi automata. The goal of the tool is to assist implementing +LTL-to-Büchi translation algorithms correctly by providing an +automated testing environment for LTL-to-B@"uchi translators. +Additionally, the testing environment can be used for very basic +profiling of different LTL-to-Büchi translators to evaluate their +performance. The latest version of the program is available at . diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 14c5827b7..52119c3b4 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,11 +1,11 @@ # Process this file with autoconf to produce a configure script. -AC_PREREQ([2.53]) -AC_INIT([lbtt], [1.0.3], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.3]) +AC_PREREQ([2.59]) +AC_INIT([lbtt], [1.1.0], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.4]) AC_CONFIG_SRCDIR([src/main.cc]) +AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE -AM_CONFIG_HEADER([config.h]) @@ -18,35 +18,68 @@ AC_PROG_CXXCPP AM_PROG_LEX AC_PROG_YACC -YACC="${YACC} -d" - -# Check whether the user has explicitly disabled the test for the availability -# of the GNU readline library. +# Check whether the user has explicitly disabled support for the GNU readline +# library. readline=yes +readline_includedir= +readline_libdir= -AC_ARG_WITH([readline], - [AC_HELP_STRING([--without-readline], - [disable check for the GNU readline library])], - [if test x"${withval}" = xno; then readline=no; fi]) +AC_ARG_WITH( + [readline], + [AS_HELP_STRING( + [--without-readline], + [disable support for the GNU readline library (default enable)])], + [if test x"${withval}" = xno; then readline=no; fi]) + +AC_ARG_WITH( + [readline-prefix], + [AS_HELP_STRING( + [--with-readline-prefix=DIR], + [location where GNU readline is installed (optional)])], + [readline_includedir="${withval}/include" + readline_libdir="${withval}/lib"]) + +AC_ARG_WITH( + [readline-includes], + [AS_HELP_STRING( + [--with-readline-includes=DIR], + [location where GNU readline headers are installed (optional)])], + [readline_includedir="${withval}"]) + +AC_ARG_WITH( + [readline-libs], + [AS_HELP_STRING( + [--with-readline-libs=DIR], + [location where GNU readline libraries are installed (optional)])], + [readline_libdir="${withval}"]) + +old_CPPFLAGS=${CPPFLAGS} +old_LDFLAGS=${LDFLAGS} + +if test -n "${readline_includedir}"; then + CPPFLAGS="${CPPFLAGS} -I${readline_includedir}" +fi +if test -n "${readline_libdir}"; then + LDFLAGS="${LDFLAGS} -L${readline_libdir}" +fi # Check for the availability of headers. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h]) +AC_CHECK_HEADERS([libintl.h fcntl.h sys/times.h]) +AC_HEADER_SYS_WAIT # Check for the availability of the GNU readline headers. if test "${readline}" = yes; then rl_history_h="readline/history.h" rl_readline_h="readline/readline.h" - AC_CHECK_HEADERS([${rl_history_h} ${rl_readline_h}], - [], - [readline=no]) + AC_CHECK_HEADERS([${rl_history_h} ${rl_readline_h}], [], [readline=no]) fi AC_LANG([C++]) @@ -55,25 +88,36 @@ AC_LANG([C++]) # version of this header that cannot be compiled using g++; enable a workaround # if necessary. -AC_CHECK_HEADERS([obstack.h], - [AC_MSG_CHECKING([whether obstack.h compilation workaround is needed]) - AC_TRY_COMPILE([#include ], - [#if __GLIBC__ == 2 && __GLIBC_MINOR__ == 3 - obstack_alloc(0, 0); - #endif], - [AC_MSG_RESULT([no])], - [AC_MSG_RESULT([yes]) - AC_DEFINE([GLIBC_OBSTACK_WORKAROUND], - [1], - [Define to 1 to enable an obstack.h C++ compilation workaround for GNU libc 2.3.])])]) +AC_CHECK_HEADERS( + [obstack.h], + [AC_MSG_CHECKING([whether obstack.h compilation workaround is needed]) + old_cxxflags=${CXXFLAGS} + CXXFLAGS="${CXXFLAGS} -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[ + #ifdef __GLIBC__ == 2 && __GLIBC_MINOR__ = 3 + obstack_alloc(0, 0); + #endif + ]])], + [AC_MSG_RESULT([no])], + [AC_MSG_RESULT([yes]) + AC_DEFINE( + [GLIBC_OBSTACK_WORKAROUND], + [1], + [Define to 1 to enable an obstack.h C++ compilation workaround for GNU libc 2.3.])]) + CXXFLAGS=${old_cxxflags}]) # Check for the availablility of the sstream or strstream header. -AC_CHECK_HEADERS([sstream], - [], - [AC_CHECK_HEADERS([strstream], - [], - [AC_MSG_ERROR([missing one or more standard C++ headers])])]) +AC_CHECK_HEADERS( + [sstream], + [], + [AC_CHECK_HEADERS( + [strstream], + [], + [AC_MSG_ERROR([missing one or more standard C++ headers])])]) @@ -86,30 +130,35 @@ AC_CHECK_HEADERS([sstream], AC_MSG_CHECKING([for slist]) for slist_header in slist ext/slist no; do if test "${slist_header}" != no; then - AC_TRY_CPP([#include <${slist_header}>], [break]) + AC_PREPROC_IFELSE( + [AC_LANG_SOURCE([[#include <${slist_header}>]])], + [break]) fi done # Try to determine the C++ namespace in which the class slist resides. -# (For example, GCC versions 3.1, 3.1.1 and 3.2 put slist into the -# __gnu_cxx namespace.) +# (For example, GCC versions >= 3.1 put slist into the __gnu_cxx namespace.) if test "${slist_header}" != no; then for slist_namespace in std __gnu_cxx error; do if test "${slist_namespace}" != error; then - AC_TRY_LINK([#include <${slist_header}>], - [${slist_namespace}::slist s;], - [break]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include <${slist_header}>]], + [[${slist_namespace}::slist s;]])], + [break]) fi done if test "${slist_namespace}" != error; then AC_MSG_RESULT([header <${slist_header}>, typename ${slist_namespace}::slist]) - AC_DEFINE([HAVE_SLIST], - [1], - [Define to 1 if you have the or header file.]) - AC_DEFINE_UNQUOTED([SLIST_NAMESPACE], - [${slist_namespace}], - [Define as the name of the C++ namespace containing slist.]) + AC_DEFINE( + [HAVE_SLIST], + [1], + [Define to 1 if you have the or header file.]) + AC_DEFINE_UNQUOTED( + [SLIST_NAMESPACE], + [${slist_namespace}], + [Define as the name of the C++ namespace containing slist.]) AC_SUBST([INCLUDE_SLIST_HEADER], ["#include <${slist_header}>"]) else slist_header=no @@ -119,26 +168,21 @@ fi if test "${slist_header}" = no; then AC_MSG_RESULT([no]) -# Check for the availability of the single_client_alloc memory allocator -# available in some compilers supporting the SGI extensions to the Standard -# template library (for example, pre-3.0 versions of gcc). This check is not -# needed if no suitable slist header was found above, since in that case the -# compiler does not support the SGI extensions. - -else - AC_MSG_CHECKING([for single_client_alloc]) - AC_TRY_LINK([#include <${slist_header}>], - [using namespace std; - ${slist_namespace}::slist s;], - [AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_SINGLE_CLIENT_ALLOC], - [1], - [Define if your C++ compiler supports the single_client_allocator memory allocator.])], - [AC_MSG_RESULT([no])]) fi AC_LANG(C) +AC_CHECK_TYPES( + [unsigned long long int], + [AC_DEFINE( + [BIGUINT], + [unsigned long long int], + [Define to an unsigned integer type supported by your compiler.])], + [AC_DEFINE( + [BIGUINT], + [unsigned long int], + [Define to an unsigned integer type supported by your compiler.])]) + AC_C_CONST AC_C_INLINE @@ -146,12 +190,14 @@ AC_C_INLINE # Checks for library functions. -AC_TYPE_SIGNAL -AC_CHECK_FUNCS([mkdir strchr strtod strtol strtoul getopt_long isatty]) +AC_CHECK_FUNCS( + [strchr strtod strtol strtoul strerror mkdir mkstemp open read write close popen pclose pipe fork execvp getpid waitpid alarm sigaction sigprocmask sigemptyset sigaddset times sysconf], + [], + [AC_MSG_ERROR([missing one of the library functions required for compilation])]) +AC_CHECK_FUNCS([strsignal isatty getopt_long]) if test x"${ac_cv_func_getopt_long}" = xno; then AC_LIBOBJ([getopt]) AC_LIBOBJ([getopt1]) - AC_CHECK_HEADERS([libintl.h]) AC_CHECK_FUNCS([memset]) fi @@ -163,42 +209,56 @@ if test "${readline}" = yes; then for READLINELIBS in "-lreadline" "-lreadline -lcurses" "-lreadline -ltermcap" error; do if test "${READLINELIBS}" != error; then LIBS="${oldlibs} ${READLINELIBS}" - AC_TRY_LINK([#include - #include <${rl_history_h}> - #include <${rl_readline_h}>], - [using_history(); readline(""); add_history("");], - [break]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM( + [[ + #include + #include <${rl_history_h}> + #include <${rl_readline_h}> + ]], + [[using_history(); readline(""); add_history("");]])], + [break]) fi done LIBS=${oldlibs} if test "${READLINELIBS}" != error; then - AC_DEFINE([HAVE_READLINE], - [1], - [Define if you have the GNU readline library.]) + AC_DEFINE( + [HAVE_READLINE], + [1], + [Define if you have the GNU readline library.]) AC_SUBST([READLINELIBS]) AC_MSG_RESULT([${READLINELIBS}]) else AC_MSG_RESULT([no suitable libraries found, readline support disabled]) READLINELIBS="" + readline=no fi fi +if test "${readline}" = no; then + CPPFLAGS=${old_CPPFLAGS} + LDFLAGS=${old_LDFLAGS} +fi + # Check for the availability of the `rand48' family of random number # generation functions. have_rand48=yes -AC_CHECK_FUNCS([srand48 lrand48 seed48], - [], - [have_rand48=no - AC_CHECK_FUNCS([rand srand], - [], - [AC_MSG_ERROR([missing library functions for random number generation])])]) +AC_CHECK_FUNCS( + [srand48 lrand48 seed48], + [], + [have_rand48=no + AC_CHECK_FUNCS( + [rand srand], + [], + [AC_MSG_ERROR([missing library functions for random number generation])])]) if test "${have_rand48}" = yes; then - AC_DEFINE([HAVE_RAND48], - [1], - [Define if you have the `rand48' family of random number generation functions.]) + AC_DEFINE( + [HAVE_RAND48], + [1], + [Define if you have the `rand48' family of random number generation functions.]) fi diff --git a/lbtt/doc/lbtt.texi b/lbtt/doc/lbtt.texi index ae5b65b91..1d0064048 100644 --- a/lbtt/doc/lbtt.texi +++ b/lbtt/doc/lbtt.texi @@ -64,7 +64,7 @@ under the above conditions for modified versions. @title @command{lbtt} @subtitle LTL-to-B@"uchi Translator Testbench -@subtitle @today, @command{lbtt} Versions 1.0.x +@subtitle @today, @command{lbtt} Versions 1.1.x @author Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> @page @vskip 0pt plus 1filll @@ -103,8 +103,8 @@ under the above conditions for modified versions. for translating propositional linear temporal logic formulas into B@"uchi automata. -This is edition 1.0.2 of the @command{lbtt} documentation. This edition -applies to @command{lbtt} versions 1.0.x. +This is edition 1.1.0 of the @command{lbtt} documentation. This edition +applies to @command{lbtt} versions 1.1.x. @command{lbtt} is free software, you may change and redistribute it under the terms of the GNU General Public License. @command{lbtt} @@ -148,7 +148,7 @@ comes with NO WARRANTY. See @ref{Copying} for details. @chapter Overview @cindex model checking -@command{lbtt} is a tool for automatic testing of programs which translate +@command{lbtt} is a tool for testing programs that translate formulas expressed in propositional linear temporal logic (@dfn{LTL}) into B@"uchi automata. These finite-state automata over infinite words are used e.g.@: in automata-theoretic model checking @@ -163,7 +163,7 @@ where they can help in detecting errors in the specifications of finite-state hardware or software systems. Usually the model checking procedure involves first composing an automaton with a formal model of a given system, and the result of the -composition reveals whether any execution path of the system violates +composition reveals whether any computation path of the system violates some property that the automaton represents. (For an introduction to model checking techniques in general, see, for example, @@ -174,12 +174,14 @@ for example, [CGP99].) @end iftex -The property to be model checked is often specified as an +The property to be model checked can be specified as an LTL formula, and the B@"uchi automaton used for model checking is obtained -automatically from the formula with a translation algorithm. (Detailed -descriptions and optimization techniques for such algorithms can be found, for -example, in +automatically from the formula with a translation algorithm. +(For descriptions and optimization techniques for such algorithms, see the +references, for example, @ifnottex +@ref{[VW86]}, +@ref{[Isl94]}, @ref{[GPVW95]}, @ref{[Cou99]} @ref{[DGV99]}, @@ -187,20 +189,33 @@ example, in @ref{[SB00]}, @ref{[EH00]}, @ref{[EWS01]}, -@ref{[GO01]}.) +@ref{[GO01]}, +@ref{[Gei01]}, +@ref{[Sch01]}, +@ref{[Wol01]}, +@ref{[Ete02]}, +@ref{[GL02]}, +@ref{[GSB02]}, +@ref{[Thi02]}, +@ref{[Fri03]}, +@ref{[GO03]}, +@ref{[Lat03]}, +@ref{[ST03]}.) @end ifnottex @iftex -[GPVW95, Cou99, DGV99, Ete99, SB00, EH00, EWS01, GO01].) +[VW86, Isl94, GPVW95, Cou99, DGV99, Ete99, SB00, EH00, EWS01, GO01, +Gei01, Sch01, Wol01, Ete02, GL02, GSB02, Thi02, Fri03, GO03, Lat03, ST03].) @end iftex -In practice, the implementation correctness of such a translation algorithm -is crucial to the soundness of the model checking procedure. +In practice, ensuring the correctness of the implementation of such a +translation algorithm is crucial to guarantee the soundness of the +implementation of a model checking procedure. -The goal of @command{lbtt} is to assist in the correct implementation of -LTL-to-B@"uchi translation algorithms by providing an automated +The goal of @command{lbtt} is to assist implementing LTL-to-B@"uchi +translation algorithms correctly by providing an automated testing environment for LTL-to-B@"uchi translators. Testing consists of running LTL-to-B@"uchi translators on randomly generated (or user-specified) LTL formulas as input and then performing simple consistency checks on the -resulting automata to test whether the translators seem to operate correctly +resulting automata to test whether the translators seem to function correctly in practice. (See @ifnottex @ref{[TH02]} @@ -208,9 +223,9 @@ in practice. (See @iftex [TH02] @end iftex -for more information on the theory behind the testing methods.) If the tests -suggest an implementation -error in a translator, @command{lbtt} can generate sample data which causes +for more information on the theory behind the testing methods.) If the test +results suggest that there is an error in an implementation, +@command{lbtt} can generate sample data which causes a test failure and which may also be useful for debugging the implementation. @@ -218,12 +233,12 @@ Additionally, the testing environment can be used for very basic profiling of different LTL-to-B@"uchi translators to evaluate their performance. @noindent -@emph{ -Note: although @command{lbtt} might be able to detect implementation errors -in an LTL-to-B@"uchi translator, it is only a testing tool and is therefore -incapable of formally proving any translation algorithm implementation to be -correct. Therefore, the test results should never be used as the sole basis -for any formal conclusions about the correctness of an implementation. +@emph{Note: although @command{lbtt} might be able to detect inconsistent +behavior in an LTL-to-B@"uchi translator, it is only a testing tool and is +therefore incapable of formally proving any translation algorithm +implementation to be correct. Therefore, the test results should never be used +as the sole basis for any formal conclusions about the correctness of an +implementation. } @@ -289,12 +304,12 @@ read LTL formulas from a file @cindex state space Additionally, some of the tests make use of randomly generated -``state spaces'', which are basically directed labelled graphs with labels on +``state spaces'', which are basically directed labeled graphs with labels on nodes with the additional requirement of having at least one arc leaving each node. The label of each node is a subset of a finite collection of atomic propositions @cindex atomic proposition -(representing a set of conditions which may or may not hold in a +(an uninterpreted set of assertions which may or may not hold in a state), which occur also in the LTL formulas used in the tests. The following sections describe how @command{lbtt} generates input for the @@ -589,9 +604,9 @@ number of @emph{different} atomic propositions.) @cindex operators, priorities for Priorities for the Boolean constants and atomic propositions. The priority of a symbol determines the relative likelihood of its occurrence in a generated -formula. The higher the priority of a symbol, the more likely it will occur -(with respect to the other symbols) in a generated formula; a zero priority -will exclude the symbol altogether. +formula. The higher the priority of a symbol, the more likely it is that the +symbol will occur (with respect to the other symbols) in a generated formula; a +zero priority will exclude the symbol altogether. @item Priorities for the logical and temporal operators. @@ -602,7 +617,8 @@ Priorities for the logical and temporal operators. Note that the priorities for atomic symbols (Boolean constants and atomic propositions) and the priorities of the logical and temporal operators are independent, i.e., changing the priority of an atomic symbol does not affect -the occurrence likelihood of any logical or temporal operator and vice versa. +the likelihood of the occurrence of any logical or temporal operator and vice +versa. @ifnottex For further details, see @ref{The formula generation algorithm} for a @@ -878,6 +894,16 @@ distribution for a given combination of operator priorities; see the (@pxref{--showoperatordistribution,,@samp{--showoperatordistribution} command line option}) for more information. +See also the web page@* +@ifinfo +@url{http://www.tcs.hut.fi/Software/lbtt/formulaoptions.php} +@end ifinfo +@ifnotinfo +<@uref{http://www.tcs.hut.fi/Software/lbtt/formulaoptions.php>} +@end ifnotinfo +for an interface to a small database for adjusting the operator priorities +towards certain simple distributions. + @node Random state spaces, , Random LTL formulas, Random input generation @@ -891,7 +917,7 @@ model checking result cross comparison test (@pxref{Model checking result cross-comparison test}) and the model checking result consistency check (@pxref{Model checking result consistency check}). -The state spaces are directed labelled graphs, each node of which is labelled +The state spaces are directed labeled graphs, each node of which is labeled with a randomly chosen set of atomic propositions (the propositions that hold in the state corresponding to the graph node). In addition, each state of the state space always has at least one successor. @@ -1009,11 +1035,11 @@ $|\it{AP}|$. @math{|AP|}. @end ifnottex -In practice, you should always start testing using only very small state spaces +In practice, testing should be started using only very small state spaces (say, with only 10--50 states and a small density) regardless of the particular algorithm chosen for generating the state spaces. The size of the state spaces -can then be increased if @command{lbtt}'s memory consumption stays within -acceptable limits. +can then be increased if @command{lbtt}'s memory consumption and the time spent +running the tests stay within acceptable limits. @@ -1242,7 +1268,7 @@ section. @cindex B@"uchi automata intersection emptiness check @cindex tests, B@"uchi automata intersection emptiness check -The semantics of LTL guarantee that no model of an LTL formula +The semantics of LTL guarantees that no model of an LTL formula can be the model of the negation of the same formula. In terms of B@"uchi automata, this implies that the languages accepted by automata constructed from two complementary LTL formulas should be disjoint. @@ -1271,7 +1297,7 @@ to be tested and defines additional testing parameters. if the configuration file cannot be processed successfully, @command{lbtt} exits with an error message. -After reading the configuration file @command{lbtt} starts tests on the +After reading the configuration file, @command{lbtt} starts tests on the LTL-to-B@"uchi translators listed in the configuration file (for details about the testing procedure, see @ref{Test methods}). The program exits after a predetermined number of test rounds. @@ -1331,10 +1357,10 @@ format of the configuration file is @noindent Section and option names are case-insensitive. Values can be numbers, strings or truth values (@samp{yes} and @samp{no}, or equivalently, -@samp{true} and @samp{false}). String values are case-sensitive and must be -always enclosed in quotes (@samp{"}). -The backslash (@samp{\}) is treated as an escape character (to be used -e.g.@: if the string itself contains quotes; use @samp{\\} to get a backslash). +@samp{true} and @samp{false}). String values are case-sensitive and are subject +to common quoting and escaping rules (i.e., string values containing white +space should be enclosed in quotes, or the white space characters should be +escaped with @samp{\}). @cindex comments in configuration file @cindex configuration file, comments @@ -1346,14 +1372,14 @@ configuration file. @cindex configuration file, minimal requirements @cindex minimal requirements for configuration files -The configuration file must contain at least one @samp{Algorithm} +The configuration file must contain at least one @samp{Translator} section specifying an LTL-to-B@"uchi translator. The other sections are optional and can be used to override the default testing parameters. @menu -* Algorithm section:: Each LTL-to-B@"uchi translator to be +* Translator section:: Each LTL-to-B@"uchi translator to be tested requires a separate - @samp{Algorithm} section in the + @samp{Translator} section in the configuration file. * GlobalOptions section:: Options for changing the general behavior of @command{lbtt}. @@ -1365,16 +1391,24 @@ are optional and can be used to override the default testing parameters. @end menu -@node Algorithm section, GlobalOptions section, Configuration file, Configuration file -@subsection The @samp{Algorithm} section +@node Translator section, GlobalOptions section, Configuration file, Configuration file +@subsection The @samp{Translator} section @cindex configuration file, @samp{Algorithm} section +@cindex configuration file, @samp{Implementation} section +@cindex configuration file, @samp{Translator} section @cindex @samp{Algorithm} section (configuration file) +@cindex @samp{Implementation} section (configuration file) +@cindex @samp{Translator} section (configuration file) @cindex configuration file, minimal requirements Each LTL-to-B@"uchi translator to be tested requires a separate -@samp{Algorithm} section in the configuration file; there must be at least -one such section in the file. The translators are assumed to be accessible +@samp{Translator} +section@footnote{The @samp{Algorithm} and @samp{Implementation} keywords are +recognized as aliases of the @samp{Translator} keyword.} in the configuration +file; there must be at least one such section in the file. + +The translators are assumed to be accessible through external executable files. Therefore, this section must at a minimum specify the full file name of the executable to run in order to invoke the translator; see @ref{Translator interface}, for information about the @@ -1384,10 +1418,11 @@ LTL-to-B@"uchi translators. @cindex LTL-to-B@"uchi translators, identifiers @cindex identifiers for LTL-to-B@"uchi translators Translators specified in the configuration file are given unique -integer identifiers in the order they are listed in the configuration file, -starting from zero. These numbers must be used when referring to the +integer identifiers in the order they are listed in the file, +starting from zero. These numbers can be used when referring to the different translators when using @command{lbtt}'s internal commands -(@pxref{Analyzing test results}). +(@pxref{Analyzing test results}). Alternatively, the translators can be +referred to using the names specified in the configuration file. The following options (in alphabetical order) are available within this section: @@ -1398,29 +1433,35 @@ section: @cindex LTL-to-B@"uchi translators, enabling @cindex disabling LTL-to-B@"uchi translators @cindex LTL-to-B@"uchi translators, disabling -@findex Enabled @r{[}Algorithm@r{]} +@findex Enabled @r{[}Translator@r{]} This option determines whether the translator should be initially included in or excluded from the tests. The default value is @samp{Yes}. The translator can be enabled or disabled during testing with @command{lbtt}'s internal commands (@pxref{Test control commands}). @item Name = @var{STRING} -@findex Name @r{[}Algorithm@r{]} -This option can be used to specify a textual identifier for the LTL-to-B@"uchi -translator. This identifier will then be used when displaying various messages -concerning the implementation. (If no name has been explicitly given for the -translator, @command{lbtt} assigns the translator a name of the form -@samp{Algorithm @var{n}}, where @var{n} is the running integer identifier for -the translators.) +@findex Name @r{[}Translator@r{]} +This option can be used to specify a unique textual identifier for the +LTL-to-B@"uchi translator. @command{lbtt} will use this identifier when +displaying various messages concerning the implementation; the identifier can +also be used to refer to the implementation when working with @command{lbtt}'s +internal commands (@pxref{Analyzing test results}). (If no name has been +explicitly given for the translator, @command{lbtt} assigns the translator a +name of the +form @samp{Algorithm @var{n}}, where @var{n} is the running integer identifier +for the translators.) + +The identifier @samp{lbtt} is reserved for @command{lbtt}'s internal model +checking algorithm (@pxref{Model checking result cross-comparison test}). @item Parameters = @var{STRING} -@findex Parameters @r{[}Algorithm@r{]} +@findex Parameters @r{[}Translator@r{]} This option can be used to specify any additional parameters that should -be passed to the translator executable whenever running it. (The default -value of this option is the empty string.) +be passed to the translator executable whenever running it. (The parameter +string defaults to the empty string if the option is not used.) @item Path = @var{STRING} -@findex Path @r{[}Algorithm@r{]} +@findex Path @r{[}Translator@r{]} This option must be given a value for each translator specified in the configuration file. The value should be the complete file name of the program which is used to run the translator. @@ -1429,7 +1470,7 @@ which is used to run the translator. -@node GlobalOptions section, FormulaOptions section, Algorithm section, Configuration file +@node GlobalOptions section, FormulaOptions section, Translator section, Configuration file @subsection The @samp{GlobalOptions} section @cindex configuration file, @samp{GlobalOption} section @@ -1460,16 +1501,32 @@ This option can be used to enable or disable the model checking result consistency check (@pxref{Model checking result consistency check}). The test is enabled by default. -@item @anchor{Interactivity modes}Interactive = Always @r{|} OnError @r{|} Never +@item @anchor{Interactivity modes}Interactive = @var{MODE-LIST} @cindex interactivity modes @findex Interactive @r{[}GlobalOptions@r{]} -The interactivity mode determines whether @command{lbtt} should pause to wait -for user input between test rounds. A value of @samp{Always} causes -@command{lbtt} to pause unconditionally after each test round, @samp{OnError} -will interrupt testing only after failed test rounds, and @samp{Never} will -simply run all tests without interruption. +This option determines when @command{lbtt} should pause to wait for user input +between test rounds. The @var{MODE-LIST} is a comma-separated list of the +following modes (with no spaces in between the modes): +@table @samp +@item Always +Pause unconditionally after each test round. -The default value for this option is @samp{Always}. +@item OnError +Pause testing only after failed test rounds. + +@item Never +Run all tests without interruption. + +@item OnBreak +Pause testing when requested by the user (for example, after receiving a break +signal from the keyboard). If this mode is not specified, @samp{lbtt} will +respond to break signals by aborting. +@end table + +(Since the first three interactivity modes are mutually exclusive, it does not +make sense to combine these modes with each other.) The default mode list +consists of the value @samp{Always}, that is, testing is paused after +every test round, and signalling a break will abort testing. @item IntersectionCheck = @var{TRUTH-VALUE} @itemx IntersectionTest = @var{TRUTH-VALUE} @@ -1501,6 +1558,18 @@ model checking tests is enabled.) The @samp{Rounds} option can be used to specify the number of test rounds to run; the default value is 10. +@item @anchor{Timeouts}TranslatorTimeout = @var{TIME-SPECIFICATION} +@findex TranslatorTimeout @r{[}GlobalOptions@r{]} +@cindex timeouts for translators +This option can be used to specify a time limit (in wall-clock time) after +which the execution of a translator is aborted if it fails to produce a +result within the given limit. A timeout is considered a test failure. The time +specification is of the form +@samp{@r{[}@var{hours}@r{]}h@r{[}@var{minutes}@r{]}min@r{[}@var{seconds}@r{]}s} +where @var{hours}, @var{minutes} and @var{seconds} specify the time limit in +the obvious way (time units having value 0 can be omitted). For example, +a limit of @samp{1h30min} sets the limit at one hour and thirty minutes. + @item Verbosity = @var{INTEGER} @findex Verbosity @r{[}GlobalOptions@r{]} @cindex verbosity, changing @@ -1680,8 +1749,7 @@ The following table illustrates the effects of the ! <> p0 @r{No} Nor@r{:} ! <> p0 NNF@r{:} (false V ! p0) - @r{* only if} AbbreviatedOperators=Yes -} + @r{* only if} AbbreviatedOperators=Yes} @end smallformat @item PropositionPriority = @var{INTEGER} @@ -1727,11 +1795,13 @@ but with state spaces of different size.) Priority of the temporal ``(weak) release'' operator (@samp{V}). @item Size = @var{INTEGER} +@itemx Size = @var{MINIMUM-SIZE}-@var{MAXIMUM-SIZE} @itemx Size = @var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} @findex Size @r{[}FormulaOptions@r{]} This option defines how many nodes are allowed in the parse tree of each randomly generated LTL formula. If the size is given as an interval (by -separating the bounds with three dots with no white space in between), +separating the bounds with @samp{-} or @samp{...} with no white space in +between), @command{lbtt} chooses the size of each formula randomly in the interval using a uniform random distribution. The default size is 5. @@ -1868,6 +1938,7 @@ used to initialize the random number generator for the random LTL formula generation algorithm. @item Size = @var{INTEGER} +@itemx Size = @var{MINIMUM-SIZE}-@var{MAXIMUM-SIZE} @itemx Size = @var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} @findex Size @r{[}StateSpaceOptions@r{]} This option sets the number of states in the generated state spaces. If the @@ -1906,20 +1977,20 @@ imaginary LTL-to-B@"uchi translators. @smallexample # Sample configuration file for lbtt -Algorithm +Translator @{ - Name = "Translator 1" - Path = "~/lbtt/bin/t-1" # location of the translator + Name = Translator\ 1 + Path = /home/lbtt-user/bin/t-1 # location of the translator # executable Enabled = Yes @} -Algorithm +Translator @{ Name = "Translator 2" - Path = "~/lbtt/bin/t-2" + Path = /home/lbtt-user/bin/t-2 Parameters = "-x -y 3 -v 0" # parameters to be passed to the - # `~/lbtt/bin/t-2' executable + # executable Enabled = Yes @} @@ -1927,9 +1998,10 @@ GlobalOptions @{ Rounds = 100 # 100 test rounds - Interactive = OnError # pause only in case of an error + Interactive = OnError,OnBreak # pause testing in case of an error + # or when receiving a break signal - Verbosity = 1 # suppress most of the output + Verbosity = 1 # show only numeric statistics ComparisonTest = Yes # enable all tests except the ConsistencyTest = Yes # B@"uchi automata intersection @@ -1938,24 +2010,29 @@ GlobalOptions ModelCheck = Local # perform the tests only in a # single state of each state # space + + TranslatorTimeout = 30s # abort the execution of a + # translator if it fails to give + # a result in 30 seconds @} FormulaOptions @{ AbbreviatedOperators = Yes # formula generation mode GenerateMode = Normal - OutputMode = Normal + OutputMode = NNF # rewrite formulas in negation + # normal form before passing + # them to the translators - ChangeInterval = 1 # new formula after each test - # round + ChangeInterval = 1 # new formula after each round RandomSeed = 4632912 # random seed - Size = 5...15 # 5 to 15 nodes in the parse + Size = 5-15 # 5 to 15 nodes in the parse # tree of each formula - Propositions = 3 # max. 3 different propositions - # in each LTL formula + Propositions = 3 # allow at most three different + # propositions in each LTL formula PropositionPriority = 50 # priorities for propositional TruePriority = 1 # symbols @@ -1964,10 +2041,12 @@ FormulaOptions AndPriority = 10 # priorities for some logical OrPriority = 10 # operators NotPriority = 10 + EquivalencePriority = 5 NextPriority = 5 # priorities for some temporal UntilPriority = 5 # operators ReleasePriority = 5 + FinallyPriority = 2 DefaultOperatorPriority = 0 # disable all the remaining # operators @@ -1986,8 +2065,8 @@ StatespaceOptions Size = 50 # 50 states in each state space - Propositions = 3 # 3 propositions in each state - # of each state space + Propositions = 3 # three propositions in each + # state of each state space EdgeProbability = 0.1 # approximate probability of # having a transition between @@ -1997,7 +2076,6 @@ StatespaceOptions # atomic proposition is true in # a state @} - @end smallexample @@ -2046,14 +2124,29 @@ file @samp{config} in the current working directory. @cindex LTL formula, reading from a file @cindex file formats, formula input file for @command{lbtt} This option instructs @command{lbtt} to read the LTL formulas used in the tests -from a file instead of generating them randomly. The file should contain -LTL formulas separated from each other with white space. Currently the -only supported format for the formulas is the same prefix notation that -@command{lbtt} uses for its output files (@pxref{Format for LTL formulas}, -for details). +from a file instead of generating them randomly. The file should contain a +list of formulas, each on its own line in the file. The formulas can be +specified either in @command{lbtt}'s own prefix notation +(@pxref{Format for LTL formulas}; also the infix notation used in output +messages is supported) or in a variety of formats found in +some LTL-to-B@"uchi translator implementations (Spin, LTL2BA, LTL2AUT, +Temporal Massage Parlor, Wring, Spot, LBT), however with the restriction that +all atomic propositions should have names of the form +@samp{p@var{N}} for some nonnegative integer @var{N}. -If this option is used, all LTL formula generation parameters in the command -line or in the configuration file are ignored. +@cindex operators, precedence in input files +(When using one of the alternative +formats, it is recommended to use parentheses to avoid possible ambiguities in +the precedence and associativity of the various operators; in @command{lbtt}, +the unary operators have the highest precedence, @samp{/\} has higher +precedence than @samp{\/}, which in turn has higher precedence than any of +@samp{->}, @samp{<->} or @samp{xor}, and the binary temporal operators have +the lowest precedence. All binary logical operators are left-associative; +all binary temporal operators are nonassociative.) + +If this option is used, all command line or configuration file parameters +affecting the generation of random LTL formulas (excluding their mode of +output) are ignored. @item --h @itemx --help @@ -2074,7 +2167,8 @@ encountered during testing. By default no log will be created. @cindex enabling and disabling tests @cindex tests, profiling LTL-to-B@"uchi translators This option can be used as a shorthand for disabling all B@"uchi automata -correctness tests. The test report generated at the end of testing then shows +correctness tests. +The test report generated at the end of testing then shows only the running times of each tested LTL-to-B@"uchi translator and the sizes of the generated automata. @@ -2084,8 +2178,7 @@ of the generated automata. @vindex --silent @cindex suppressing output These options suppress any messages that are normally displayed during -testing. These options also imply the @samp{--nopause} option, i.e., all -tests will be run without interruption. Use the @samp{--logfile} option (see +testing. Use the @samp{--logfile} option (see above) with these options to save a test failure report into a log file. @item @anchor{--showconfig}--showconfig @@ -2114,7 +2207,9 @@ configuration information when the program starts. This option can be used to skip the first @var{NUMBER-OF-ROUNDS} test rounds, i.e., begin testing from round @var{NUMBER-OF-ROUNDS}+1. -@item --version +@item -V +@itemx --version +@vindex -V @vindex --version This option displays the version of the @command{lbtt} executable. @@ -2130,7 +2225,7 @@ values specified in the @samp{GlobalOptions} section of the configuration file. @table @samp -@item --comparisontest +@item --comparisontest@r{[}=yes | no@r{]} @itemx --nocomparisontest @cindex tests, enabling and disabling @cindex enabling and disabling tests @@ -2139,7 +2234,7 @@ file. These options enable or disable the model checking result cross-comparison test (@pxref{Model checking result cross-comparison test}). -@item --consistencytest +@item --consistencytest@r{[}=yes | no@r{]} @itemx --noconsistencytest @cindex tests, enabling and disabling @cindex enabling and disabling tests @@ -2153,10 +2248,10 @@ These options enable or disable the model checking result consistency check @cindex LTL-to-B@"uchi translators, disabling @vindex --disable This option can be used to exclude some implementations from the tests by -specifying a comma-separated list of integers (the implementation identifiers). -The implementations are numbered in the order in which they appear in the -configuration file, starting from zero. (The @samp{--showconfig} -option, see @ref{Special options}, can be used to obtain a list of the +specifying a comma-separated list of implementation names or their numeric +identifiers. (The implementations are numbered in the order in which they +appear in the configuration file, starting from zero. Use the +@samp{--showconfig} option, see @ref{Special options}, to obtain a list of the implementations specified in the configuration file, together with their identifiers.) @@ -2177,14 +2272,19 @@ checking result cross-comparison test and the model checking result consistency check. Using a global check increases the number of possible tests. -@item --interactive=always | onerror | never +@item --interactive@r{[}=@var{MODE-LIST}@r{]} +@itemx --pause@r{[}=@var{MODE-LIST}@r{]} @vindex --interactive @cindex interactivity modes -This option can be used to override whether @command{lbtt} should pause -between test rounds to wait for user input. +These options can be used to override whether @command{lbtt} should pause +between test rounds to wait for user input. The optional @var{MODE-LIST} is a +comma-separated list of interactivity modes (@samp{Always}, @samp{OnError}, +@samp{Never}, @samp{OnBreak}) with no spaces in between +(@pxref{Interactivity modes}, for the mode descriptions). If omitted, the +mode list defaults to @samp{Always}. -@item --intersectiontest -@item --nointersectiontest +@item --intersectiontest@r{[}=yes | no@r{]} +@itemx --nointersectiontest @cindex tests, enabling and disabling @cindex enabling and disabling tests @vindex --intersectiontest @@ -2208,29 +2308,19 @@ check. @cindex global model checking This option can be used to select the model checking mode. -@item --nopause -@vindex --nopause -@cindex interactivity modes -This option forces @command{lbtt} to run all tests without interruption. - -@item --pause +@item --pause@r{[}=@var{MODE-LIST}@r{]} @vindex --pause -@cindex interactivity modes -This option forces @command{lbtt} to pause between each test round to -wait for user commands. - -@item --pauseonerror -@vindex --pauseonerror -@cindex interactivity modes -Using this option will cause testing to be paused each time an error occurs -during testing, -giving the user an opportunity to analyze the error situation using -@command{lbtt}'s internal commands before proceeding to the next test round. +See @samp{--interactive}. @item --rounds=@var{NUMBER-OF-ROUNDS} @vindex --rounds This option can be used to override the number of test rounds to run. +@item --translatortimeout=@var{TIME-SPECIFICATION} +@vindex --translatortimeout +This option can be used to override the running time limit (in wall-clock +time) for translators (@pxref{Timeouts}, for more information). + @item --verbosity=@var{INTEGER} @vindex --verbosity @cindex verbosity, changing @@ -2259,7 +2349,7 @@ configuration file. @cindex operators, priorities for @table @samp -@item --abbreviatedoperators +@item --abbreviatedoperators@r{[}=yes | no@r{]} @itemx --noabbreviatedoperators @vindex --abbreviatedoperators @vindex --noabbreviatedoperators @@ -2338,6 +2428,7 @@ This option gives a seed value for generating random numbers used by the random LTL formula generation algorithm. @item --formulasize=@var{INTEGER} +@itemx --formulasize=@var{MINIMUM-SIZE}-@var{MAXIMUM-SIZE} @itemx --formulasize=@var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} @vindex --formulasize This option sets the size of the random LTL formulas generated for the tests. @@ -2510,6 +2601,7 @@ This option gives a seed value for generating random numbers required by the random state space generation algorithm. @item --statespacesize=@var{INTEGER} +@itemx --statespacesize=@var{MINIMUM-SIZE}-@var{MAXIMUM-SIZE} @itemx --statespacesize=@var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} @vindex --statespacesize This option can be used to change the size of the generated state spaces. @@ -2528,11 +2620,12 @@ spaces.) @node Interpreting the output, Analyzing test results, Invocation, Top @chapter Interpreting the output -This chapter briefly intoduces the most typical messages that -@command{lbtt} outputs during testing when running in its default output -verbosity mode (3). In lower verbosity modes some (or all) of these -messages will be suppressed; higher verbosity modes show information about -@command{lbtt}'s internal behavior. +This chapter briefly introduces the most typical messages that +@command{lbtt} outputs during testing. Most of the examples in this section +illustrate the output when @command{lbtt} is running in its default output +verbosity mode (3). In lower verbosity modes some (or in verbosity mode 0, all) +of these messages will be suppressed; in higher verbosity modes, some +additional information about @command{lbtt}'s internal behavior is shown. @menu * Configuration information:: The current configuration is shown @@ -2548,7 +2641,8 @@ messages will be suppressed; higher verbosity modes show information about @cindex configuration information -Before starting tests, @command{lbtt} shows a short summary of the current +Before starting tests, @command{lbtt} outputs (in verbosity modes 2 and above) +a summary of the current program configuration as obtained by reading the program configuration file and interpreting the command line parameters. The same summary can be obtained without running any tests by using the @@ -2590,6 +2684,7 @@ Program configuration: 1000 test rounds. Testing will be interrupted in case of an error. + Signalling a break will interrupt testing. Using global model checking for tests. Writing error log to `error.log'. @@ -2597,13 +2692,15 @@ Program configuration: 0: `Implementation 0' 1: `Implementation 1' + Timeout for translators is set to 30 seconds. + Enabled tests: Model checking result cross-comparison test Model checking result consistency check B@"uchi automata intersection emptiness check Random state spaces: - Random connected graphs (50 states, 5 atomic propositions) + Random graphs (50 states, 5 atomic propositions) New state space will be generated after every 5th round. Random seed: 98 Random edge probability: 0.10 @@ -2617,14 +2714,38 @@ Program configuration: false (5); propositions (90); true (5) Operators used for random LTL formula generation: operator ! /\ U V X \/ - priority 10 20 20 20 10 20 + priority 10 10 20 20 10 20 + @end smallexample @node Test round messages, Test statistics, Configuration information, Interpreting the output @section Test round messages +In verbosity modes 1 and 2, @command{lbtt} reports numeric statistics on the +generated automata in tabular form. Each row of this table contains the +following information (in this order): +@itemize +@item +number of the current test round (verbosity mode 1 only); +@item +numeric identifier of an implementation; +@item +formula identifier (@samp{+} or @samp{-}); +@item +time consumed when generating an automaton from the formula using the +implementation; +@item +number of states, transitions and acceptance conditions in the automaton; +@item +number of states and transitions in the product automaton +@item +number of accepting cycles in the state space (see below), and +@item +result of the consistency check (verbosity mode 2 only). +@end itemize + The following example shows a fragment of the output that @command{lbtt} might -produce during a test round: +produce during a test round when running in the default verbosity mode 3. @cindex tests, output example @@ -2681,15 +2802,15 @@ configuration; if the state space size is allowed to vary in an interval, space.) @item -@cindex operators, precedence -Information about a random LTL formula and its negation. Note that in the -printed output, it is assumed (to remove some parentheses) that all unary -formula operators have strictly higher precedence than binary operators. +@cindex operators, precedence in output messages +Information about a random LTL formula and its negation. To simplify the +notation, it is assumed that all unary formula operators have higher +precedence than binary operators. @item Information about the B@"uchi automaton that `Implementation 0' generated from the positive LTL formula (number of states, transitions and acceptance -conditions). +conditions, and the amount of user time elapsed in generating the automaton). @item Information about the synchronous product of the state space and the @@ -2703,11 +2824,6 @@ space contains no states with an infinite path beginning from the state such that the B@"uchi automaton accepts the temporal interpretation of the path (the infinite sequence of state labels on the path). -(If the implementation used for translating the positive formula -@samp{((p1 <-> p0) U (p0 \/ ! p3))} into a B@"uchi automaton is known to be -correct, the result would then also indicate that no infinite path in the state -space satisfies the formula.) - @item The model checking process is repeated using the negated formula as input for the LTL-to-B@"uchi translator `Implementation 0'. @@ -2715,7 +2831,7 @@ input for the LTL-to-B@"uchi translator `Implementation 0'. @item @command{lbtt} performs the model checking result consistency check (@pxref{Model checking result consistency check}) using the model checking -results computed for the positive and the negative formula. In this case, +results computed for the positive and the negative formula. In this example, the result consistency check fails in 75 states of the state space. This implies that `Implementation 0' failed to translate one (or both) of the formulas into a B@"uchi automaton correctly. @@ -2729,7 +2845,7 @@ the B@"uchi automata intersection emptiness test (@pxref{Automata intersection emptiness check}). The model checking result cross-comparison test might result in the following -output: +output (shown in verbosity modes greater than 1): @smallformat @t{ Model checking result cross-comparison:} @@ -2754,8 +2870,7 @@ the test is @samp{N/A}. @cindex tests, against internal model checking algorithm If using enumerated or randomly generated paths as state spaces, the model checking results are also compared against those given by -@command{lbtt}'s internal model checking algorithm. In the output, the -internal implementation has the name @samp{lbtt} and no numeric identifier. +@command{lbtt}'s internal model checking algorithm. A similar convention is used to report failures in the B@"uchi automata intersection emptiness check. However, because this test is always performed @@ -2789,7 +2904,7 @@ format specified above. @cindex tests, statistics At the end of testing, @command{lbtt} outputs some simple statistics computed -over all tests. If using an error log file +over all tests in verbosity modes 2 and above. If using an error log file (@pxref{--logfile,,@samp{--logfile} command line option}), the statistics will be stored also in the log file. These statistics can be also accessed during interactive testing by using the internal command @samp{statistics} @@ -2858,8 +2973,8 @@ Number of failures in the B@"uchi automata intersection emptiness check implementations. @end itemize -Note that the pairwise inconsistency results form a symmetric matrix (although -@command{lbtt} might display the matrix in several parts), which means that +Note that the pairwise inconsistency results form a symmetric matrix (possibly +shown in several parts), which means that the same information is repeated on both sides of the matrix diagonal. @end itemize @@ -2879,8 +2994,8 @@ analyze test results. To use the internal commands, @command{lbtt} must be started in one of its interactive modes (@pxref{Interactivity modes}). Depending on the mode, -@command{lbtt} may occasionally pause (either after each test round or only in -case a test failure is detected) between test rounds to wait for user input by +@command{lbtt} may occasionally pause (for example, after each test round, or +when a test failure is detected) between test rounds to wait for user input by showing a prompt of the form @smallexample @@ -2919,7 +3034,7 @@ command unambiguously (for example, @samp{h} could be used in place of the @samp{help} command). @cindex commands, entering lists of numbers -Some of the commands accept or require lists of implementation or state +Some of the commands expect lists of implementation or state identifiers as parameters. The lists can be specified as comma-separated numbers (for example, @samp{8}) or intervals (for example, @samp{3-11}) with no white space between the commas and the numbers or intervals that @@ -2932,6 +3047,11 @@ or equal to 5, together with information about state 8, states 14 to 18 @samp{*} symbol can be used as a shorthand for all identifiers in the available range. +@command{lbtt} also recognizes the symbolic names of implementations (defined +in the configuration file) in implementation identifier lists. The names can be +used in place of the numeric identifiers. Quotes or the escape character +(@samp{\}) should be used to handle white space in identifiers. + @cindex LTL formula, identifiers in commands @cindex commands, LTL formula identifiers Some of the commands require a formula identifier as a parameter for choosing @@ -2949,13 +3069,12 @@ respectively. @cindex commands, invoking external programs @cindex commands, writing output to a pipe -Optionally, the output can be written to a pipe (to be processed by an external -program) instead of a file. This is done by ending the command line with -@samp{| @var{command}}, where @var{command} is the command line for the -external program intended to process the output produced by @command{lbtt}. +Optionally, the output can be handed over to an external program by ending the +command line with @samp{| @var{command}}, where @var{command} is the command +line used for invoking the external program. For example, the output of the (@command{lbtt}'s internal) command can be piped to a pager application if the entire output does not fit on the screen by -itself. Using the pipe construct @emph{without} specifying any internal command +itself. Using the pipe construct without specifying any internal command will simply invoke the external program. @@ -2971,9 +3090,9 @@ general conventions for using the commands. The @samp{help} command can be optionally given a command name as a parameter to access command-specific help. -In command-specific help, arguments in angle brackets (@samp{<}, @samp{>}) +In command-specific help, arguments in angle brackets (@r{<}, @r{>}) denote obligatory command parameters, while arguments in square brackets -(@samp{[}, @samp{]}) are optional. A vertical bar (@samp{|}) denotes +(@r{[}, @r{]}) are optional. A vertical bar (@r{|}) denotes selection between several alternatives. Arguments in double quotes should be entered literally (without the quotes themselves). @@ -2992,18 +3111,18 @@ verbosity of @command{lbtt}'s output messages. @item continue @r{[}@var{number-of-rounds}@r{]} @kindex continue Continue testing. If no argument is given, testing will be interrupted again -when the current interactivity mode requires it +when mandated by the current interactivity mode (@pxref{Interactivity modes}). The optional argument @var{number-of-rounds} can be used to specify a number of rounds to run; testing is then interrupted again after the given number of test rounds (or in case of a new -test failure if this is required by the current interactivity mode). +test failure if mandated by the current interactivity mode). @item disable @r{[}@var{implementation-id-list}@r{]} @kindex disable @cindex disabling LTL-to-B@"uchi translators @cindex LTL-to-B@"uchi translators, disabling -Disable testing of a list of implementations or all implementations if -no list of implementations is specified. @command{lbtt} will not include these +Disable testing of a list of implementations (all implementations if +no list of implementations is specified). @command{lbtt} will not include these implementations in the tests in subsequent test rounds. (See @ref{Command conventions}, for the syntax used for the list of implementations.) @@ -3052,20 +3171,22 @@ current test round. @table @samp @item algorithms +@itemx implementations +@itemx translators @kindex algorithms +@kindex implementations +@kindex translators Show a list of implementations declared in the program configuration file and -whether they are currently enabled for testing. The list also shows the -numeric identifiers of the implementations. These identifiers -should be used with commands that accept or require implementation identifier -lists as arguments. +tell whether they are currently enabled for testing. The list also shows the +numeric identifiers of the implementations. -@item buchi @r{[}+ @r{|} -@r{]} @var{implementation-id} @r{[}@var{state-id-list} | dot@r{]} +@item buchi @r{[``}+@r{''} @r{|} @r{``}-@r{'']} @r{<}@var{implementation-id}@r{>} @r{[}@var{state-id-list} | @r{``}dot@r{'']} @kindex buchi Display information about the structure of the B@"uchi automaton generated by the implementation @var{implementation-id} from the positive (@samp{+}) or negative (@samp{-}) LTL formula used in the current test round. The implementation identifier may be optionally followed by a list of state -identifiers to display only certain states of the automaton (see +identifiers to display specific states of the automaton (see @ref{Command conventions}, for details on how the list should be formatted), or the keyword @samp{dot} to display the automaton in a format that can be given as input for the @samp{dot} tool of the @@ -3079,7 +3200,7 @@ GraphViz graph visualization package @end iftex to obtain a graphical representation of the automaton. -@item evaluate @r{[}+ @r{|} -@r{]} @r{[}@var{implementation-id-list}@r{]} @r{[}@var{state-id-list}@r{]} +@item evaluate @r{[``}+@r{''} @r{|} @r{``}-@r{'']} @r{[}@var{implementation-id-list}@r{]} @r{[}@var{state-id-list}@r{]} @kindex evaluate Display the model checking results for the positive (@samp{+}) or the negative (@samp{-}) formula computed using a given set of implementations for @@ -3097,22 +3218,25 @@ as input for the @samp{resultanalysis} command (@pxref{Failure analysis commands}). Note 1: Observe that the model checking results shown do not follow the -``traditional'' semantics of LTL, by which a formula is usually considered to +``universal'' semantics of LTL (common in model checking), by which a formula +is usually considered to hold in a set of infinite paths beginning from a state only if @emph{all} paths in the set are accepted by the B@"uchi automaton constructed from the -formula to be model checked. Instead, @command{lbtt} will mark the result as -true if even @emph{one} of these paths is accepted by the automaton. +formula to be model checked. Instead, @command{lbtt} will mark the result +true if @emph{any} of these paths is accepted by the automaton. Note 2: If using random or enumerated paths as state spaces, @command{lbtt} -accepts also the symbol @samp{p} in the implementation identifier list. This -symbol can be used for accessing the model checking results computed using -@command{lbtt}'s internal ``path'' model checking algorithm. +accepts also the identifier @samp{lbtt} in the implementation identifier list. +This identifier can be used for accessing the model checking results computed +using @command{lbtt}'s internal model checking algorithm for paths. -@item formula @r{[}+ @r{|} -@r{]} +@item formula @r{[``}+@r{''} @r{|} @r{``}-@r{'']} @r{[``}normal@r{''} @r{|} @r{``}nnf@r{'']} @kindex formula @cindex LTL formula, displaying with user command Display the positive (@samp{+}) or the negative (@samp{-}) LTL formula used -for tests in the current test round. +for tests in the current test round either in the form in which it was +generated (@samp{normal} -- the default) or in negation normal form +(@samp{nnf}). @item inconsistencies @r{[}@var{implementation-id-list}@r{]} @kindex inconsistencies @@ -3125,14 +3249,14 @@ The state identifiers can then be used as input for the @item results @r{[}@var{implementation-id-list}@r{]} @kindex results -Display test results for each implementation in the list (or all -implementations if the list is omitted) in a format very similar to test -messages shown when running @command{lbtt} in its default verbosity mode 3. +Display test results (in the current test round) for each implementation in the +list (or all +implementations if the list is omitted). For more information about the output, see @ref{Test round messages}; see @ref{Command conventions}, for information on how to specify the implementations. -@item statespace @r{[}@var{state-id-list} @r{|} dot@r{]} +@item statespace @r{[}@var{state-id-list} @r{|} @r{``}dot@r{'']} @kindex statespace @cindex state space, displaying with an user command Display information about the structure of the state space used for model @@ -3149,7 +3273,7 @@ the GraphViz graph visualization package @iftex [GViz] @end iftex -to obtain a graphical representation of the state space. +that can be used to obtain a graphical representation of the state space. @item @anchor{statistics}statistics @kindex statistics @@ -3173,7 +3297,7 @@ automata correctness tests. The second part describes the conventions that @subsection Alphabetical list of failure analysis commands @table @samp -@item buchianalysis @var{implementation-id} @var{implementation-id} +@item buchianalysis @r{<}@var{implementation-id}@r{>} @r{<}@var{implementation-id}@r{>} @kindex buchianalysis @cindex B@"uchi automata intersection emptiness check, failure analysis @cindex tests, failure analysis @@ -3194,9 +3318,9 @@ be equal if one of the tested implementations failed the check against itself.) A failure in the B@"uchi automata intersection emptiness check implies that there exists an input sequence over subsets of atomic propositions that is accepted by both automata included in the analysis. @command{lbtt} examines the -intersection of the automata to find a witness of such an input, model -checks the positive formula in the witness, and tells which one of the automata -is likely to be incorrect according to the following rules: +intersection of the automata to find a witness of such an input, checks whether +this witness is a model of the positive formula, and tells which one of the +automata is likely to be incorrect according to the following rules: @itemize @bullet @item @@ -3208,7 +3332,7 @@ If the witness is not a model for the positive formula, then the automaton constructed from the positive formula probably accepts the witness incorrectly. @end itemize -@item consistencyanalysis @var{implementation-id} @r{[}@var{state-id}@r{]} +@item consistencyanalysis @r{<}@var{implementation-id}@r{>} @r{[}@var{state-id}@r{]} @kindex consistencyanalysis @cindex model checking result consistency check, failure analysis @cindex tests, failure analysis @@ -3218,10 +3342,10 @@ Analyze a failure in the model checking result consistency check (@pxref{Model checking result consistency check}). The @var{implementation-id} parameter chooses the implementation to analyze. In addition, the optional @var{state-id} parameter can be used to specify a -state space state in which to perform the analysis (use the +state (in the state space) in which to perform the analysis (use the @samp{inconsistencies} command, @ref{Data display commands}, to see a list -of all state space states in which the check failed). If the state identifier -is not present, @command{lbtt} will try to find a state where +of all states in which the check failed). If the state identifier +is omitted, @command{lbtt} will try to find a state where the check failed. @cindex witness @@ -3229,11 +3353,11 @@ A failure in the model checking result consistency check implies the existence of a witness (i.e., a path in the state space used for the tests in the current test round) whose temporal interpretation is not accepted by either of two automata constructed from two complementary LTL formulas. In the analysis, -@command{lbtt} finds such a witness, model checks the positive formula in the -witness separately, and tells which one of the automata seems to reject the -witness incorrectly. +@command{lbtt} finds such a witness, checks separately whether it is a model of +the positive formula, and then tells which one of the automata seems to reject +the witness incorrectly. -@item resultanalysis @r{[}+ @r{|} -@r{]} @var{implementation-id} @var{implementation-id} @r{[}@var{state-id}@r{]} +@item resultanalysis @r{[``}+@r{''} @r{|} @r{``}-@r{'']} @r{<}@var{implementation-id}@r{>} @r{<}@var{implementation-id}@r{>} @r{[}@var{state-id}@r{]} @kindex resultanalysis @cindex model checking result cross-comparison test, failure analysis @cindex tests, failure analysis @@ -3243,8 +3367,9 @@ Analyze a failure in the model checking result cross-comparison test (@pxref{Model checking result cross-comparison test}) between two implementations on either the positive (@samp{+}) or the negative (@samp{-}) LTL formula used in the current test round. The implementation -identifiers can be optionally followed by a state space state identifier -to specify a state in which the analysis should be performed. (Suitable +identifiers can be optionally followed by an identifier of a state in the +state space to specify a state in which the analysis should be performed. +(Suitable state identifiers can be found by looking for inconsistencies in the model checking results accessible with the @samp{evaluate} command, @ref{Data display commands}; by omitting the state identifier, @@ -3252,9 +3377,9 @@ checking results accessible with the @samp{evaluate} command, checking result comparison failed between the implementations.) If using randomly generated or enumerated paths as state spaces, @command{lbtt} -also accepts the symbol @samp{p} in place of either of the implementation -identifiers. This instructs @command{lbtt} to perform the analysis against -@command{lbtt}'s internal model checking algorithm. +also accepts the identifier @samp{lbtt} in place of either of the +implementation identifiers. This instructs @command{lbtt} to perform the +analysis against @command{lbtt}'s internal model checking algorithm. @cindex witness A failure in the model checking result cross-comparison test suggests that @@ -3275,25 +3400,32 @@ All of the above analysis commands use @command{lbtt}'s internal model checking algorithm to determine which one of the two automata involved in each test is incorrect by checking whether an LTL formula holds in a witness path extracted from the state space used in the current test round or from the -intersection of two B@"uchi automata. The witness path is a linear sequence of -states that ends in a loop, and is represented in two parts as an initial +intersection of two B@"uchi automata. The witness path is a sequence of +consecutive states that ends in a loop, and is represented in two parts as an +initial ``prefix'' (which may be empty) and a ``cycle'' that is considered to repeat -itself indefinitely. The witness might look as follows: +itself indefinitely. The witness might, for example, look as follows: @smallexample Execution M: - prefix: < s0 @{p0, p4@}, - s2 @{p1, p2, p3, p4@} > - cycle: < s34 @{p0, p1, p2, p3, p4@}, - s42 @{p4@}, - s44 @{p1, p4@} > + prefix: + s3 @{p0,p2,p4@} --> s4 + cycle: + s4 @{p1,p3@} --> s5 + s5 @{p3@} --> s6 + s6 @{p1,p2,p3@} --> s7 + s7 @{p3,p4@} --> s8 + s8 @{p1@} --> s9 + s9 @{@} --> s2 + s2 @{@} --> s3 + s3 @{p0,p2,p4@} --> s4 @end smallexample @noindent In this case, the witness (or ``execution'' as displayed in the output) -@math{M} consists of a prefix of two states followed by a cycle of three -states. Each state is associated with a set of atomic propositions that hold -in the state. +@math{M} consists of a single-state prefix followed by a cycle of eight +states. The atomic propositions that hold in each state are also shown in +the output. (The witness can be considered a small state space @iftex @@ -3307,30 +3439,34 @@ $M = \langle S, \rho, {\cal L} \rangle$ following the definition in @ref{State spaces}; in the example above, @iftex @tex -$S = \{s_0, s_2, s_{34}, s_{42}, s_{44}\}$, +$S = \{s_2, s_3, s_4, s_5, s_6, s_7, s_8, s_9\}$, @end tex @end iftex @ifnottex -@math{S = @{s0, s2, s34, s42, s44@}}, +@math{S = @{s2, s3, s4, s5, s6, s7, s8, s9@}}, @end ifnottex @iftex @tex -$\rho = \{(s_0, s_2), (s_2, s_{34}), (s_{34}, s_{42}), (s_{42}, s_{44}), - (s_{44}, s_{34})\}$, +$\rho = \{(s_2, s_3), (s_3, s_4), (s_4, s_5), (s_5, s_6), (s_6, s_7), + (s_7, s_8), (s_8, s_9), (s_9, s_2)\}$, @end tex @end iftex @ifnottex -@math{R = @{(s0, s2), (s2, s34), (s34, s42), (s42, s44), (s44, s34)@}}, +@math{R = @{(s2, s3), (s3, s4), (s4, s5), (s5, s6), (s6, s7), (s7, s8), (s8, s9), (s9, s2)@}}, @end ifnottex @iftex @tex -${\cal L}(s_0) = \{p_0, p_4\}, {\cal L}(s_2) = \{p_1, p_2, p_3, p_4\}, - {\cal L}(s_{34}) = \{p_0, p_1, p_2, p_3, p_4\}, {\cal L}(s_{42}) = \{p_4\},$ - and ${\cal L}(s_{44}) = \{p_1, p_4\}$.) +${\cal L}(s_2) = {\cal L}(s_9) = \emptyset$, +${\cal L}(s_3) = \{p_0, p_2, p_4\}$, +${\cal L}(s_4) = \{p_1, p_3\}$, +${\cal L}(s_5) = \{p_3\}$, +${\cal L}(s_6) = \{p_1, p_2, p_3\}$, +${\cal L}(s_7) = \{p_3, p_4\}$, and +${\cal L}(s_8) = \{p_1\}$.) @end tex @end iftex @ifnottex -@math{L(s0) = @{p0, p4@}, L(s2) = @{p1, p2, p3, p4@}, L(s34) = @{p0, p1, p2, p3, p4@}, L(s42) = @{p4@},} and @math{L(s44) = @{p1, p4@}}.) +@math{L(s2) = L(s_9) = @{@}, L(s3) = @{p0, p2, p4@}, L(s4) = @{p1, p3@}, L(s5) = @{p3@}, L(s6) = @{p1, p2, p3@}, L(s7) = @{p3, p4@},} and @math{L(s8) = @{p1@}}.) @end ifnottex @@ -3351,21 +3487,14 @@ LTL and might look as follows: @smallexample Analysis of the formula in the execution: - M, |== ((p1 U p4) <-> (! p1 -> [] p4)) : - +-> M, |== (p1 U p4) : - | +-> M, |== p4 - +-> M, |== (! p1 -> [] p4) : - +-> M, |== [] p4 : - +-> M, |== p4 - +-> s0 --> s2 - +-> M, |== p4 - +-> s2 --> s34 - +-> M, |== p4 - +-> s34 --> s42 - +-> M, |== p4 - +-> s42 --> s44 - +-> M, |== p4 - +-> s44 --> s34 + M, |/= ((X p0 U ! p4) <-> p0) : + +-> M, |/= (X p0 U ! p4) : + | +-> M, |/= X p0 : + | | +-> s3 --> s4 + | | +-> M, |/= p0 + | +-> M, |/= ! p4 : + | +-> M, |== p4 + +-> M, |== p0 @end smallexample @noindent @@ -3377,57 +3506,81 @@ subsequence beginning at state @samp{s} of the witness, and the relational symbol @samp{|/=} denotes the opposite. The children of each proof tree node give justification for the claim in their parent node; the children might be further expanded if the claims in them do not directly follow from the -definition of @math{M}. In the presence of temporal operators, the proofs may +definition of +@iftex +@tex +${\cal L}$. +@end tex +@end iftex +@ifnottex +@math{L}. +@end ifnottex +In the presence of temporal operators, the proofs may need to be based also on the structural properties of @math{M}. These are shown as statements of the form @samp{sn --> sm} to indicate that @math{M} contains a transition from the state @samp{sn} to the state @samp{sm} (and, -since the states in @math{M} are connected into a linear sequence, that this -is the @emph{only} transition originating from @samp{sn}). +since the states in @math{M} are connected into a non-branching sequence, that +this is the @emph{only} transition originating from @samp{sn}). In the above example, @command{lbtt} claims that the formula -@samp{((p1 U p4) <-> (! p1 -> [] p4))} holds in the witness presented earlier +@samp{((X p0 U ! p4) <-> p0)} does not hold in the witness presented earlier in this section, and that this follows (by the semantics of logical -equivalence) from the claims that the subformulas @samp{(p1 U p4)} and -@samp{(! p1 -> [] p4)} both hold in the same witness. -@samp{(p1 U p4)} holds in the witness because of the fact that +equivalence) from the claims that the subformula @samp{(X p0 U ! p4)} does not +hold, but the subformula @samp{p0} holds in this witness. +@samp{(X p0 U ! p4)} does not hold in the witness, because neither +@samp{X p0} nor @samp{! p4} holds in the first state of the witness @iftex @tex -$p_4 \in {\cal L}(s_0)$, +($p_4 \in {\cal L}(s_3)$, and $p_0 \notin {\cal L}(s_4)$, where $s_4$ is the +only successor of $s_3$). @end tex @end iftex @ifnottex -@math{p4} is included in @math{L(s0)}, +(@math{p4} is included in @math{L(s3)}, and @math{p0} is not included in +@math{L(s4)}, where @math{s4} is the only successor of @math{s3}). @end ifnottex -and the truth of the implication @samp{(! p1 -> [] p4)} is justified by the -property that +On the other hand, +@samp{p0} holds in the witness because of the fact that @iftex @tex -$p_4$ +$p_0 \in {\cal L}(s_3)$. @end tex @end iftex @ifnottex -@math{p4} +@math{p0} is included in @math{L(s3)}. @end ifnottex -holds in all states of the (only) infinite sequence beginning at the state -@iftex -@tex -$s_0$, -@end tex -@end iftex -@ifnottex -s0, -@end ifnottex -which can be seen from the proof. @node Interfacing with lbtt, References, Analyzing test results, Top -@chapter Interfacing LTL-to-B@"uchi translators with @command{lbtt} +@chapter Interfacing with @command{lbtt} @cindex LTL-to-B@"uchi translators, interfacing with @cindex interfacing LTL-to-B@"uchi translators with @command{lbtt} -This chapter gives the details on how to use @command{lbtt} for +The output generated by @command{lbtt} consists of textual messages +and an optional error log file (@pxref{--logfile,,@samp{--logfile} command line +option}). The format of the output messages is determined by the verbosity +mode; +for more information, see @ref{Interpreting the output}. In addition, +@command{lbtt} returns one of the following three values as its exit +status upon normal termination: +@itemize +@item 0: +@command{lbtt} exited successfully; no errors were detected during testing. + +@item 1: +@command{lbtt} exited successfully; errors were detected during testing. + +@item 2: +An error was found when reading the program configuration or when processing +the command line options. + +@item 3: +@command{lbtt} exited due to an unrecoverable internal error. +@end itemize + +The rest of this chapter gives the details on how to use @command{lbtt} for testing LTL-to-B@"uchi translation algorithm implementations that are not supported by the basic distribution. (See @ref{The lbtt-translate utility} @@ -3446,8 +3599,9 @@ LTL-to-B@"uchi translator implementations to @command{lbtt}.) @end menu -@node Translator interface, Format for LTL formulas, Interfacing with lbtt, Interfacing with lbtt -@section Translator interface requirements + +@node Translator interface, Format for LTL formulas, , Interfacing with lbtt +@section Requirements for translator executables @cindex LTL-to-B@"uchi translators, interface requirements @command{lbtt} assumes each tested LTL-to-B@"uchi translator to be @@ -3478,18 +3632,19 @@ for a description on how these files should be formatted. The translator executable should always create an output file and then return with a zero exit status in case no errors occur during the translation. -@command{lbtt} interprets a missing output file or any non-zero exit status as +@command{lbtt} interprets a missing output file or a nonzero exit status as an error and will not in this case try to run any tests, even if an automaton were successfully saved in an output file. -To start testing the translator, add a new @samp{Algorithm} section for it into +To start testing the translator, add a new @samp{Translator} section for it +into @command{lbtt}'s configuration file (@pxref{Configuration file}), for example @smallexample -Algorithm +Translator @{ Name = "LTL-to-B@"uchi translator" - Path = "~/bin/ltl-to-buchi-translator" + Path = /home/lbtt-user/bin/ltl-to-buchi-translator Parameters = "-x -y -z" Enabled = Yes @} @@ -3591,38 +3746,86 @@ line options (@pxref{Command line options}) to prevent @command{lbtt} expects the B@"uchi automata generated by each LTL-to-B@"uchi translator implementation to be in the format specified below. The format encodes a generalized B@"uchi automaton (a B@"uchi automaton with zero or -more acceptance conditions) with a single initial state and labels on -transitions. For the full formal definition and some general guidelines on how -to convert your automata to support the definition, see @ref{Definitions}. +more acceptance conditions) with a single initial state and labels (guards) on +transitions. For the full formal definition and examples on how +to reduce other definitions into the one used by @command{lbtt}, see +@ref{Definitions}. The output file generated by the translator should contain an @var{automaton} -described using the following grammar: +described using the following grammar +(as before, quoted characters denote the characters themselves, @var{sp} +denotes any nonempty string of white space, lines containing a // are +comments that are not part of the grammar, and @samp{\n} corresponds to the +newline character). @smallexample -@var{automaton} @r{::=} @var{num-states} @var{sp} @var{num-conds} @var{states} +@var{automaton} @r{::=} @var{num-states} @var{sp} @var{cond-specifier} @var{state-list} @var{num-states} @r{::=} @r{[}0@r{---}9@r{]+} -@var{num-conds} @r{::=} @r{[}0@r{---}9@r{]+} +@var{cond-specifier} @r{::=} @r{[}0@r{---}9@r{]+}@r{[}st@r{]}* -@var{states} @r{::=} @var{states} @var{sp} @var{state} - @r{|} @r{// empty} +@var{state-list} @r{::=} @var{state-list} @var{sp} @var{state} + @r{|} @r{// empty} +@end smallexample -@var{state} @r{::=} @var{state-id} @var{sp} @var{initial?} @var{conds} @var{sp} `-1' @var{transitions} @var{sp} `-1' +@noindent +The automaton description begins with a nonnegative number that gives the +number of states in the automaton. If the number of +states is 0, the automaton will not accept any input. If the number is +positive, it should be followed by a @var{cond-specifier} that determines the +number +and placement of acceptance conditions in the automaton. If the number of +acceptance conditions is 0, the automaton accepts an input word if and only if +it has a run on that word according to the definition given in the +Appendix (@pxref{Definitions}). + +The placement of acceptance conditions is specified by concatenating a +string formed from the symbols @samp{s} and @samp{t} to the number of +acceptance conditions (with no white space in between). The interpretation of +this string is as follows: +@itemize +@item +If the string is empty or does not include the symbol @samp{t}, the acceptance +conditions of the automaton are placed exclusively on its states. (This +alternative corresponds to the definition supported by @command{lbtt} 1.0.x.) + +@item +If the string is nonempty but does not include the symbol @samp{s}, the +automaton has acceptance conditions exclusively on its transitions. + +@item +Otherwise, the automaton has acceptance conditions on both states and +transitions. +@end itemize + +The @var{cond-specifier} is followed by a list of the descriptions of states in +the automaton. The format of this list is affected by the choice of the +placement of the acceptance conditions. +More precisely, the choice affects the interpretation of the +@var{cond-list} nonterminal symbol in the following fragment of the grammar: we +indicate this by prefixing the nonterminal with either ``'' or +``'' to denote that the list (together with its terminating @samp{-1}) +should be +omitted in automata that do not associate acceptance conditions with states or +transitions, respectively. + +@smallexample +@var{state} @r{::=} @var{state-id} @var{sp} @var{initial?} @r{}@var{cond-list} @var{transition-list} @var{state-id} @r{::=} @r{[}0@r{---}9@r{]+} @var{initial?} @r{::=} `0' @r{|} `1' -@var{conds} @r{::=} @var{conds} @var{sp} @var{acceptance-set-id} - @r{|} @r{// empty} +@var{cond-list} @r{::=} @var{sp} @var{acceptance-condition-id} @var{cond-list} + @r{|} @var{sp} `-1' -@var{acceptance-set-id} @r{::=} @r{[}0@r{---}9@r{]+} +@var{acceptance-condition-id} @r{::=} @r{[}0@r{---}9@r{]+} -@var{transitions} @r{::=} @var{transitions} @var{sp} @var{transition} - @r{|} @r{// empty} +@var{transition-list} @r{::=} @var{sp} @var{transition} @var{transition-list} + @r{|} @var{sp} `-1' -@var{transition} @r{::=} @var{state-id} @var{sp} @var{guard-formula} +@var{transition} @r{::=} @var{state-id} @r{}@var{cond-list} @var{sp} @var{guard-formula} `\n' @var{guard-formula} @r{::=} `t' @r{// ``true''} @@ -3644,39 +3847,25 @@ described using the following grammar: @r{// exclusive or} @end smallexample @noindent -(The quoted characters denote the characters themselves; @var{sp} denotes any -non-empty string of white space. Lines containing a // are comments and are not -part of the grammar.) -The automaton description begins with two nonnegative numbers, the first one -of which gives the number of states in the automaton, and the second one -tells the number of acceptance conditions in the automaton. If the number of -states is 0, the automaton will not accept any input. If the number of -acceptance conditions is 0, the automaton accepts an input word if and only if -it has a run on that word according to the definition given in the -Appendix (@pxref{Definitions}). +The description of each state begins with a numeric state identifier, which can +be any nonnegative integer. The state identifier should be followed by a number +telling whether the state is initial (@samp{1} if yes). The automaton should +have exactly one initial state. If the automaton has acceptance conditions +associated with its states, this number should then be followed by a list of +acceptance condition identifiers separated by white space. This list should be +terminated with @samp{-1}. -If the number of states is greater than zero, the acceptance condition count -should be followed by a list of states. The description of each state begins -with a numeric state identifier, which can be any nonnegative integer. The -state identifier should be followed by a number telling whether the state is -initial (@samp{1} if yes). The automaton should have exactly one initial state. -This number is then followed by a list of the identifiers of the acceptance -conditions to which the state belongs. This list should be terminated with -@samp{-1}. - -The state and acceptance condition identifiers need not be successive, and the -states or acceptance conditions can be listed in any order. The only -restrictions are that the identifiers of different states and acceptance -conditions should be unique and that the total number of different identifiers -should equal @var{num-states} or @var{num-conds}, -respectively. (The same identifiers can be shared between states and acceptance -conditions, however.) - -Finally, the acceptance condition list of each state should be followed by a -list of transitions (terminated again by @samp{-1}). Each transition consists -of a state identifier (the target state of the transition) and a propositional -formula that encodes the symbols of the alphabet +The state description should be followed by the list of transitions starting +from the state (terminated again by @samp{-1}). Each transition consists of a +state identifier (the target state of the transition), a list of acceptance +condition identifiers (if the automaton has acceptance conditions on +transitions), and a propositional formula @footnote{Although not described +formally in the grammar, the guard formulas can be specified in any +of the formats @command{lbtt} supports in its formula input files +(@pxref{--formulafile,,@samp{--formulafile} command line option}). Note that +the formula always needs to be terminated with a newline, though.} that encodes +the symbols of the alphabet @iftex @tex $2^{AP}$ @@ -3686,17 +3875,32 @@ $2^{AP}$ @math{2^AP} @end ifnottex (where @var{AP} is a finite set of atomic propositions) on which the -automaton is allowed to take the transition. +automaton is allowed to take the transition. The propositional formula should +be terminated with a newline. + +The state and acceptance condition identifiers need not be successive, and the +states or acceptance conditions can be listed in any order. The only +restrictions are that the identifiers of different states and acceptance +conditions should be unique and that the total number of different identifiers +should equal @var{num-states} or @var{num-conds}, +respectively. (The same identifiers can be shared between states and acceptance +conditions, however.) + Note that the output file should always contain a valid automaton description if the LTL-to-B@"uchi translation was successful, even in the case that the resulting automaton is empty (@command{lbtt} interprets a missing automaton description file as an error). -The following example illustrates the file format. +The following examples illustrate the file format. The first example +gives the description of an automaton with acceptance conditions on +states. Note that in this case the @samp{s} is optional for describing the +placement of acceptance conditions; therefore, the automaton files used with +@command{lbtt} 1.0.x are upwards compatible with newer versions of the tool +(provided that each guard of a transition is terminated by a newline). @smallexample -6 2 @r{// an automaton with six states and two acceptance conditions} +6 2s @r{// an automaton with six states and two acc.@ conditions on states} 0 1 -1 @r{// state 0: initial state, no acceptance conditions} 2 p1 @r{// transition to state 2, guard} @samp{p1} 5 p2 @r{// transition to state 5, guard} @samp{p2} @@ -3731,7 +3935,37 @@ The following example illustrates the file format. -1 @r{// end of state 12} @end smallexample +@noindent +The following example illustrates an automaton in which acceptance conditions +are placed on transitions. +@smallexample +4 3t @r{// four states, three acceptance conditions on transitions} +5 0 @r{// state 5: non-initial state} +84 0 -1 p1 @r{// transition to state 84, condition 0, guard} @samp{p1} +27 0 -1 & p1 ! p2 @r{// tr. to state 27, condition 0, guard} @samp{p1 /\ ! p2} +5 -1 t @r{// transition to state 5, no conditions, guard} @samp{true} +-1 @r{// end of state 5} +84 1 @r{// state 84: initial state} +5 1 -1 t @r{// transition to state 5, condition 1, guard} @samp{true} +27 0 -1 p1 @r{// transition to state 27, condition 0, guard} @samp{p1} +-1 @r{// end of state 84} +49 0 @r{// state 49: non-initial state} +5 -1 t @r{// transition to state 5, no conditions, guard} @samp{true} +49 1 4 -1 & p1 ! p2 @r{// tr.@ to state 49, conds.@ 1 and 4, guard} @samp{p1 /\ ! p2} +84 -1 p1 @r{// transition to state 84, no conditions, guard} @samp{p1} +-1 @r{// end of state 49} +27 0 @r{// state 27: non-initial state} +49 -1 & p1 p3 @r{// transition to state 49, no conds., guard} @samp{p1 /\ p3} +-1 @r{// end of state 27} +@end smallexample + +@noindent +Automata with acceptance conditions on both states and transitions can be +specified using a combination of the above two formats, that is, by using +@samp{st} as the acceptance condition placement specifier and including a list +of acceptance conditions both after the value determining the initialness of a +state, and after the identifier of the target state of each transition. @node The lbtt-translate utility, , Format for automata, Interfacing with lbtt @section The @command{lbtt-translate} utility @@ -3746,7 +3980,7 @@ translator algorithm implementations: @itemize @bullet @item @cindex @command{lbt} -@command{lbt} --- a free LTL-to-B@"uchi translation algorithm implementation +@command{lbt} --- an LTL-to-B@"uchi translation algorithm implementation based on the algorithm described in @ifnottex @ref{[GPVW95]}. @@ -3791,26 +4025,25 @@ See <@uref{http://spinroot.com/spin/whatispin.html}> @end ifnotinfo for more information. - @end itemize To use @command{lbtt} for testing the LTL-to-B@"uchi translators included in these tools, you should first install the tool normally by following its -installation instructions. Then add the following @samp{Algorithm} section in +installation instructions. Then add the following @samp{Translator} section in @command{lbtt}'s configuration file: @smallexample -Algorithm +Translator @{ Name = "@r{[@var{name for the implementation}]}" - Path = "@r{[@var{path-to-@command{lbtt-translate}}]}" + Path = "@r{[@var{path to @command{lbtt-translate}}]}" Parameters = "@r{[@var{implementation selector}]} @r{[@var{path to executable}]}" Enabled = Yes @} @end smallexample @noindent -where [@var{path-to-@command{lbtt-translate}}] contains the complete path and +where [@var{path to @command{lbtt-translate}}] contains the complete path and file name of the @command{lbtt-translate} tool executable, [@var{implementation selector}] is either of the options @samp{--lbt} or @samp{--spin}, and [@var{path to executable}] is the full path @@ -3822,7 +4055,9 @@ LTL formula operators available for generating random LTL formulas with @command{lbtt}. See the documentation of each translator for information about which operators are supported, and then change the parameters in @command{lbtt}'s configuration file accordingly to disable the unsupported -operators. +operators (or instruct @command{lbtt} to read the formulas from an external +file by invoking @command{lbtt} with the +@ref{--formulafile,,@samp{--formulafile} command line option}). The @command{lbtt-translate} utility can also be invoked directly from the shell to translate an LTL formula into a B@"uchi automaton using either of the @@ -3840,7 +4075,7 @@ E.@: Clarke Jr., O.@: Grumberg and D.@: Peled. Model checking. The MIT Press, 1999. @item @anchor{[Cou99]} [Cou99] -J.-M. Couvreur. On-the-fly verification of linear temporal logic. In +J.-M.@: Couvreur. On-the-fly verification of linear temporal logic. In @i{Proceedings of the World Congress on Formal Methods in the Development of Computing Systems (FM'99), volume I}, volume 1708 of @i{Lecture Notes in Computer Science}, pages 253---271. Springer-Verlag, 1999. @@ -3855,33 +4090,70 @@ of @i{Lecture Notes in Computer Science}, pages 249---260. Springer-Verlag, @item @anchor{[EH00]} [EH00] K.@: Etessami and G.@: Holzmann. Optimizing B@"uchi automata. In @i{Proceedings of the 11th International Conference on Concurrency Theory -(CONCUR'2000)}, volume 1877 of @i{Lecture Notes in Computer Science}, +(CONCUR 2000)}, volume 1877 of @i{Lecture Notes in Computer Science}, pages 153---167. Springer-Verlag, 2000. @item @anchor{[Ete99]} [Ete99] K.@: Etessami. Stutter-invariant languages, omega-automata, and temporal -logic. In @i{Proceedings of the 11th Conference on Computer Aided Verification -(CAV'99)}, volume 1633 of @i{Lecture Notes in Computer Science}, pages -236---248. Springer-Verlag, 1999. +logic. In @i{Proceedings of the 11th International Conference on Computer Aided +Verification (CAV'99)}, volume 1633 of @i{Lecture Notes in Computer Science}, +pages 236---248. Springer-Verlag, 1999. + +@item @anchor{[Ete02]} [Ete02] +K.@: Etessami. A hierarchy of polynomial-time computable simulations for +automata. In @i{Proceedings of the 13th International Conference on +Concurrency Theory (CONCUR 2002)}, volume 2421 of +@i{Lecture Notes in Computer Science}, pages 131---144. Springer-Verlag, 2002. @item @anchor{[EWS01]} [EWS01] K.@: Etessami, Th.@: Wilke and R.@: Schuller. Fair simulation relations, parity games, and state space reduction for B@"uchi automata. In @i{Proceedings of the 28th International Colloquium on Automata, Languages and -Programming (ICALP'2001)}, volume 2076 of +Programming (ICALP 2001)}, volume 2076 of @i{Lecture Notes in Computer Science}, pages 694---707. Springer-Verlag, 2001. +@item @anchor{[Fri03]} [Fri03] +C.@: Fritz. Constructing B@"uchi automata from linear temporal logic using +simulation relations for alternating B@"uchi automata. In +@i{Proceedings of the 8th International Conference on Implementation and +Application of Automata (CIAA 2003)}, volume 2759 of +@i{Lecture Notes in Computer Science}, pages 35---48. Springer-Verlag, 2003. + @item @anchor{[GO01]} [GO01] P.@: Gastin and D.@: Oddoux. Fast LTL to B@"uchi automata translation. In @i{Proceedings of the 13th International Conference on Computer -Aided Verification (CAV'01)}, volume 2102 of @i{Lecture Notes in Computer +Aided Verification (CAV 2001)}, volume 2102 of @i{Lecture Notes in Computer Science}, pages 53---65. Springer-Verlag, 2001. +@item @anchor{[GO03]} [GO03] +P.@: Gastin and D.@: Oddoux. LTL with past and two-way weak alternating +automata. In @i{Proceedings of the 28th International Symposium on Mathematical +Foundations of Computer Science (MFCS 2003)}, volume 2747 of +@i{Lecture Notes in Computer Science}, pages 439---448. Springer-Verlag, 2003. + +@item @anchor{[Gei01]} [Gei01] +M.@: C.@: W.@: Geilen. On the construction of monitors for temporal logic +properties. @i{Electronic Notes for Theoretical Computer Science}, 55(2), 2001. + @item @anchor{[GPVW95]} [GPVW95] R.@: Gerth, D.@: Peled, M.@: Y.@: Vardi and P.@: Wolper. Simple on-the-fly automatic verification of linear temporal logic. In -@i{Proceedings of 15th Workshop Protocol Specification, Testing, and -Verification (PSTV'95)}, pages 3---18. Chapman & Hall, 1995. +@i{Proceedings of 15th IFIP WG6.1 International Symposium on Protocol +Specification, Testing, and Verification (PSTV'95)}, pages 3---18. +Chapman & Hall, 1995. + +@item @anchor{[GL02]} [GL02] +D.@: Giannakopoulou and F.@: Lerda. From states to transitions: Improving +translation of LTL formulae to B@"uchi automata. In +@i{Proceedings of the 22nd IFIP WG6.1 International Conference on Formal +Techniques for Networked and Distributed Systems (FORTE 2002)}, volume 2529 of +@i{Lecture Notes in Computer Science}, pages 308---326. Springer-Verlag, 2002. + +@item @anchor{[GSB02]} [GSB02] +S.@: Gurumurthy, F.@: Somenzi and R.@: Bloem. Fair simulation minimization. +In @i{Proceedings of the 14th International Conference on Computer Aided +Verification (CAV 2002)}, volume 2404 of @i{Lecture Notes in Computer Science}, +pages 610---624. Springer-Verlag, 2002. @item @anchor{[GViz]} [GViz] GraphViz - open source graph drawing software. See @@ -3893,7 +4165,7 @@ GraphViz - open source graph drawing software. See @end ifnotinfo @item @anchor{[Hol97]} [Hol97] -G. Holzmann. The model checker +G.@: J.@: Holzmann. The model checker @ifnottex SPIN. @end ifnottex @@ -3904,14 +4176,40 @@ SPIN. @end iftex @i{IEEE Transactions on Software Engineering}, 23(5):279---295, 1997. +@item @anchor{[Isl94]} [Isl94] +A.@: Isli. Mapping an LPTL formula into a B@"uchi alternating automaton +accepting its models. In @i{Temporal Logic: Proceedings of the ICTL Workshop}, +pages 85---90. Research Report MPI-I-94-230, Max-Planck-Institut f@"ur +Informatik, 1994. + +@item @anchor{[Lat03]} [Lat03] +T.@: Latvala. Efficient model checking of safety properties. In +@i{Proceedings of the 10th Spin Workshop on Model Checking of Software +(SPIN 2003)}, volume 2648 of @i{Lecture Notes in Computer Science}, pages +74---88. Springer-Verlag, 2003. + +@item @anchor{[Sch01]} [Sch01] +K.@: Schneider. Improving automata generation for linear temporal logic by +considering the automaton hierarchy. In @i{Proceedings of the 8th International +Conference on Logic for Programming, Artificial Intelligence and Reasoning +(LPAR 2001)}, volume 2250 of @i{Lecture Notes in Computer Science}, pages +39---54. Springer-Verlag, 2001. + +@item @anchor{[ST03]} [ST03] +R.@: Sebastiani and S.@: Tonetta. ``More deterministic'' vs.@: ``smaller'' +B@"uchi automata for efficient LTL model checking. In +@i{Proceedings of the 12th Advanced Research Working Conference on Correct +Hardware Design and Verification Methods (CHARME 2003)}, volume 2860 of +@i{Lecture Notes in Computer Science}, pages 126---140. Springer-Verlag, 2003. + @item @anchor{[SB00]} [SB00] F.@: Somenzi and R.@: Bloem. Efficient B@"uchi automata from LTL formulae. In @i{Proceedings of the 12th International Conference on Computer Aided -Verification (CAV'00)}, volume 1855 of @i{Lecture Notes in Computer Science}, +Verification (CAV 2000)}, volume 1855 of @i{Lecture Notes in Computer Science}, pages 247---263. Springer-Verlag, 2000. @item @anchor{[Tau00]} [Tau00] -H. Tauriainen. Automated testing of B@"uchi automata translators +H.@: Tauriainen. Automated testing of B@"uchi automata translators for linear temporal logic. Research report A66, Laboratory for Theoretical Computer Science, Helsinki University of Technology, Espoo, Finland, 2000. Available on the WWW at @@ -3926,11 +4224,15 @@ Computer Science, Helsinki University of Technology, Espoo, Finland, @end iftex @item @anchor{[TH02]} [TH02] -H. Tauriainen and K. Heljanko. Testing LTL formula translation into B@"uchi +H.@: Tauriainen and K.@: Heljanko. Testing LTL formula translation into B@"uchi automata. @i{International Journal on Software Tools for Technology Transfer (STTT)} 4(1):57---70, 2002. +@item @anchor{[Thi02]} [Thi02] +X.@: Thirioux. Simple and efficient translation from LTL formulas to B"uchi +automata. @i{Electronic Notes in Theoretical Computer Science}, 66(2), 2002. + @item @anchor{[Var96]} [Var96] M.@: Y.@: Vardi. An automata-theoretic approach to linear temporal logic. In @i{Logics for Concurrency: Structure versus Automata}, volume 1043 of @@ -3939,10 +4241,17 @@ In @i{Logics for Concurrency: Structure versus Automata}, volume 1043 of @item @anchor{[VW86]} [VW86] M.@: Y.@: Vardi and P.@: Wolper. An automata-theoretic approach to -automatic program verification. In @i{Proceedings of the 1st IEEE +automatic program verification. In @i{Proceedings of the First IEEE Symposium on Logic in Computer Science (LICS'86)}, pages 332---344. IEEE Computer Society Press, 1986. +@item @anchor{[Wol01]} [Wol01] +P.@: Wolper. Constructing automata from temporal logic formulas: A tutorial. +In @i{Lectures on Formal Methods and Performance Analysis: First EEF/Euro +Summer School on Trends in Computer Science, Revised Lectures}, volume 2090 +of @i{Lecture Notes in Computer Science}, pages 261---277. Springer-Verlag, +2001. + @end table @@ -4368,14 +4677,21 @@ conditions. @appendixsubsec Formal definition of generalized automata -Formally, a generalized B@"uchi automaton can be represented as a tuple +Formally, a generalized B@"uchi automaton can be represented as a +tuple@footnote{This definition differs from those commonly found in +the literature by specifying the acceptance conditions in terms of a separate +set that is independent of the other components of the automaton, together with +an +explicit labeling function for the states. This is to allow the definition to +correspond more accurately to the automata that can be described in +input files.} @iftex @tex -$\langle \Sigma, Q, \Delta, q^0, \cal{F}\rangle$, +$\langle \Sigma, Q, \Delta, q_I, \cal{F}, \lambda\rangle$, @end tex @end iftex @ifnottex -@math{}, +@math{}, @end ifnottex where @@ -4389,7 +4705,7 @@ $\Sigma$ @ifnottex @math{S} @end ifnottex -is the @emph{alphabet} +is the finite @emph{alphabet} @iftex @tex ($\Sigma = 2^{AP}$ in this case), @@ -4400,42 +4716,55 @@ is the @emph{alphabet} @end ifnottex @item -@math{Q} is the set of @emph{states}, +@math{Q} is the finite set of @emph{states}, @item @iftex @tex -$\Delta \subseteq Q \times 2^\Sigma \times Q$ +$\Delta \subseteq Q \times 2^\Sigma \times 2^{\cal{F}} \times Q$ @end tex @end iftex @ifnottex -@math{R} (a subset of @math{Q x 2^S x Q}) +@math{R} (a subset of @math{Q x 2^S x 2^F x Q}) @end ifnottex -is the -@emph{transition relation}, +is the set of @emph{transitions} (each of which consists of four components +called the @emph{start state}, the @emph{guard}, the +@emph{acceptance component}, and the @emph{target state}, respectively), @item @iftex @tex -$q^0$ +$q_I$ @end tex @end iftex @ifnottex @math{q} @end ifnottex -is the @emph{initial state}, and +is the @emph{initial state}, @item @iftex @tex -${\cal F} \subseteq 2^Q$ +${\cal F} = \{f_1,f_2,\ldots,f_n\}$ (for some finite $n$) @end tex @end iftex @ifnottex -@math{F}, a collection of subsets of @math{Q}, +@math{F = @{f1, f2, ..., fn@} (for some finite @math{n})} @end ifnottex -is the set of @emph{acceptance conditions}. (A ``nongeneralized'' -B@"uchi automaton has exactly one acceptance condition.) +is the set of @emph{acceptance conditions} (a ``nongeneralized'' +B@"uchi automaton has exactly one acceptance condition), and + +@item +@iftex +@tex +$\lambda: Q \rightarrow 2^{\cal F}$ +@end tex +@end iftex +@ifnottex +@math{L: Q -> 2^F} +@end ifnottex +is a @emph{labeling function} that associates each state of the automaton +with a set of acceptance conditions. @end itemize @@ -4457,19 +4786,22 @@ $2^{AP}$ @ifnottex @math{2^AP} @end ifnottex -is an infinite sequence of states +is an infinite sequence of pairs of states and transitions @iftex @tex -$\langle q_0, q_1, q_2, \ldots \rangle \in Q^\omega$ +$\langle (q_0, t_0), (q_1, t_1), (q_2, t_2), \ldots \rangle \in +(Q\times\Delta)^\omega$ @end tex @end iftex @ifnottex -@math{} (where each @math{q(i)} is a state in @math{Q}) +@math{<(q(0),t(0)), (q(1),t(1)), (q(2),t(2)) ...>} +(where each @math{q(i)} is a state in @math{Q} and each @math{t(i)} is a +transition in @math{R}) @end ifnottex such that @iftex @tex -$q_0 = q^0$ +$q_0 = q_I$ @end tex @end iftex @ifnottex @@ -4484,25 +4816,24 @@ $i \geq 0$, @ifnottex @math{i >= 0}, @end ifnottex -there is a triple @iftex @tex -$\langle q_i, X, q_{i+1}\rangle \in \Delta$ +$t_i = \langle q_i, X_i, Y_i, q_{i+1}\rangle \in \Delta$ @end tex @end iftex @ifnottex -@math{} in @math{R} +@math{t(i) = } in @math{R} @end ifnottex such that @iftex @tex -$x_i \in X$. +$x_i \in X_i$. @end tex @end iftex @ifnottex -@math{x(i)} belongs to @math{X}. +@math{x(i)} belongs to @math{X(i)}. @end ifnottex -Because +(Because the relation @iftex @tex $\Delta$ @@ -4514,11 +4845,11 @@ $\Delta$ is not necessarily a function from @iftex @tex -$Q \times 2^\Sigma$ +$Q \times 2^\Sigma \times 2^{\cal{F}}$ @end tex @end iftex @ifnottex -@math{Q x 2^S} +@math{Q x 2^S x 2^F} @end ifnottex to @iftex @@ -4529,38 +4860,63 @@ $Q$, @ifnottex @math{Q}, @end ifnottex -the automaton may have many runs on the same input. +the automaton may have many runs on the same input.) A run @iftex @tex -$\langle q_0, q_1, q_2, \ldots\rangle$ +$\langle (q_0,t_0), (q_1,t_1), (q_2,t_2), \ldots\rangle$ @end tex @end iftex @ifnottex -@math{} +@math{<(q(0),t(0)), (q(1),t(1)), (q(2),t(2)), ...>} @end ifnottex -is @emph{accepting} if and only if additionally -for each acceptance condition +(where @iftex @tex -$F_j \in {@cal F}$, +$t_i = \langle q_i,X_i,Y_i,q_{i+1}\rangle\in\Delta$ for all $i$) @end tex @end iftex @ifnottex -@math{C(j)} in @math{F}, +@math{t(i) = < q(i), X(i), Y(i), q(i+1) >} for all @math{i}) @end ifnottex -there is a state +is @emph{accepting} if and only if additionally, for each acceptance condition @iftex @tex -$q_j \in F_j$ +$f \in {@cal F}$, @end tex @end iftex @ifnottex -@math{q(j)} in @math{C(j)} +@math{f} in @math{F}, @end ifnottex -that occurs infinitely often in the run. The automaton @emph{accepts} an -infinite sequence +@iftex +@tex +$f \in \lambda(q_i)$ +@end tex +@end iftex +@ifnottex +@math{f} is in @math{L(q(i))} +@end ifnottex +or +@iftex +@tex +$f \in Y_i$ +@end tex +@end iftex +@ifnottex +@math{f} is in @math{Y(i)} +@end ifnottex +for infinitely many +@iftex +@tex +$i$. +@end tex +@end iftex +@ifnottex +@math{i}. +@end ifnottex + +The automaton @emph{accepts} an infinite sequence @iftex @tex $\langle x_0, x_1, x_2, \ldots\rangle \in 2^{AP}$ @@ -4573,9 +4929,7 @@ if and only if the automaton has at least one accepting run on this sequence. @appendixsubsec Transition label encoding -In practice, a transition label of a B@"uchi automaton can be expressed as a -propositional formula, since these formulas readily encode sets of subsets of -the alphabet +When working with automata on words over the alphabet @iftex @tex $2^{AP}$, @@ -4584,8 +4938,9 @@ $2^{AP}$, @ifnottex @math{2^AP}, @end ifnottex -namely, the models of the formula. A transition can then be seen as a rule -``if in state +the guards of transitions can be expressed as propositional formulas by +identifying a set of symbols from this alphabet with the set of models of a +propositional formula. A transition can then be seen as a rule ``if in state @iftex @tex $q_i$ @@ -4626,32 +4981,74 @@ Many LTL-to-B@"uchi translation algorithms presented in the literature @iftex [GPVW95]) @end iftex -use a slightly different definition for generalized B@"uchi automata, -which permits a B@"uchi automaton to have several initial states and -places the labels on states instead of transitions. However, these B@"uchi -automata can be easily converted into an equivalent B@"uchi automaton in the -above format with the following steps (we assume here that -each state of the automaton is labelled with a set of LTL formulas that -should hold in that state): +are based on a slightly different definition for generalized B@"uchi automata, +where the automata can have several initial states, acceptance is determined +using a family of sets of states, and the guards of transitions are replaced +with an additional state labeling that associates a set of LTL +formulas with each state. These automata can easily be described using the +above definition through the following steps: @enumerate @item -Add a new state (with an empty label) into the automaton and add transitions -from it to each initial state of the original automaton. Make the new state -the (only) initial state of the automaton. +Add a new state (associated with an empty set of LTL formulas) into the +automaton and add transitions from it to each initial state of the original +automaton. Make the new state the (only) initial state of the automaton. @item For each state of the (modified) automaton, construct a conjunction of all -propositional -constraints (i.e., all formulas with no temporal operators) in the label of -the state and copy the conjunction onto each transition coming into the -state. Then remove all labels from the states. -@end enumerate +propositional constraints (all formulas with no temporal operators) +associated with the state and make the conjunction the guard of each transition +coming into the state (the acceptance component of each transition remains +empty). Then remove the association between states and sets of formulas. -Conversions from other definitions can be handled in a similar way. In -some cases (e.g., if the acceptance conditions are subsets of transitions -instead of subsets of states), the conversion may also require making copies -of some states and then adjusting the transition labels appropriately. +@item +If +@iftex +@tex +$Q_1,Q_2,\ldots,Q_k \in 2^Q$ +@end tex +@end iftex +@ifnottex +@math{Q1, Q2, ..., Qk} (subsets of @math{Q}) +@end ifnottex +are the sets of states determining acceptance in the original automaton, +let +@iftex +@tex +$f_i = Q_i$ +@end tex +@end iftex +@ifnottex +@math{fi = Qi} +@end ifnottex +for all +@iftex +@tex +$1 \leq i \leq k$, +@end tex +@end iftex +@ifnottex +@math{1 <= 1 <= k}, +@end ifnottex +and let +@iftex +@tex +$\lambda(q) = \{f_i\mid q \in f_i\}$ +@end tex +@end iftex +@ifnottex +@math{L(q) = @{fi | q is a member of fi@}} +@end ifnottex +for all states +@iftex +@tex +$q$. +@end tex +@end iftex +@ifnottex +@math{q}. +@end ifnottex +@end enumerate @@ -4664,7 +5061,7 @@ model checking result cross-comparison test (@pxref{Model checking result cross-comparison test}) and the model checking result consistency check (@pxref{Model checking result consistency check}). Formally, -the state spaces are Kripke structures with a total transition +the state spaces are (finite) Kripke structures with a total transition relation, i.e., directed graphs with a set of atomic propositions attached to each state, with each state having at least one immediate successor (which may be the state itself). The precise @@ -4685,7 +5082,7 @@ where @itemize @bullet @item -@math{S} is the set of @emph{states}, +@math{S} is the finite set of @emph{states}, @item @iftex @@ -4705,7 +5102,7 @@ ${\cal L}: S \rightarrow 2^{AP}$ @end tex @end iftex @ifnottex -@math{L} (a function from @math{S} to @math{2^AP}) +@math{L: S -> 2^AP} @end ifnottex is the @emph{labeling function} which maps each state to a set of atomic propositions that hold in the state. diff --git a/lbtt/src/BuchiAutomaton.cc b/lbtt/src/BuchiAutomaton.cc index d63667dd8..1b6c6d18e 100644 --- a/lbtt/src/BuchiAutomaton.cc +++ b/lbtt/src/BuchiAutomaton.cc @@ -92,7 +92,9 @@ BuchiAutomaton::BuchiAutomaton(const BuchiAutomaton& automaton) : ++transition) connect(state, static_cast(*transition)->targetNode(), - static_cast(*transition)->guard()); + static_cast(*transition)->guard(), + static_cast(*transition) + ->acceptanceSets()); operator[](state).acceptanceSets().copy(automaton[state].acceptanceSets(), number_of_acceptance_sets); @@ -129,7 +131,9 @@ BuchiAutomaton& BuchiAutomaton::operator=(const BuchiAutomaton& automaton) ++transition) connect(state, static_cast(*transition)->targetNode(), - static_cast(*transition)->guard()); + static_cast(*transition)->guard(), + static_cast(*transition) + ->acceptanceSets()); operator[](state).acceptanceSets().copy (automaton[state].acceptanceSets(), number_of_acceptance_sets); @@ -190,113 +194,13 @@ BuchiAutomaton::size_type BuchiAutomaton::expand(size_type node_count) return nodes.size() - 1; } -/* ========================================================================= */ -BuchiAutomaton* BuchiAutomaton::regularize() const -/* ---------------------------------------------------------------------------- - * - * Description: Converts a generalized Büchi automaton (i.e., an automaton - * with any number of accepting state sets) into an automaton - * with only one set of accepting states. - * - * Arguments: None. - * - * Returns: A pointer to an equivalent BuchiAutomaton with exactly one - * set of accepting states. - * - * ------------------------------------------------------------------------- */ -{ - /* - * If `this' automaton already has exactly one set of accepting states, - * return a copy of `this' automaton. - */ - - if (number_of_acceptance_sets == 1) - return new BuchiAutomaton(*this); - - /* - * Otherwise construct the result using a depth-first search in `this' - * automaton. - */ - - typedef pair ExpandedState; - - BuchiAutomaton* result_automaton = new BuchiAutomaton(0, 0, 1); - - if (empty()) - return result_automaton; - - stack > - states_to_process; - - map, ALLOC(size_type) > - state_mapping; - - const GraphEdgeContainer* transitions; - - size_type result_source_state, result_target_state; - map, ALLOC(size_type) > - ::const_iterator check_state; - - ExpandedState state = make_pair(initial_state, 0); - - states_to_process.push(state); - state_mapping[state] = result_automaton->expand(); - - while (!states_to_process.empty()) - { - state = states_to_process.top(); - states_to_process.pop(); - - result_source_state = state_mapping[state]; - transitions = &operator[](state.first).edges(); - - if (number_of_acceptance_sets == 0 - || operator[](state.first).acceptanceSets().test(state.second)) - { - if (state.second == 0) - (*result_automaton)[result_source_state].acceptanceSets().setBit(0); - - if (number_of_acceptance_sets > 0) - { - ++state.second; - state.second %= number_of_acceptance_sets; - } - } - - for (GraphEdgeContainer::const_iterator transition = transitions->begin(); - transition != transitions->end(); - ++transition) - { - state.first = (*transition)->targetNode(); - - check_state = state_mapping.find(state); - - if (check_state == state_mapping.end()) - { - result_target_state = result_automaton->expand(); - state_mapping[state] = result_target_state; - states_to_process.push(state); - } - else - result_target_state = check_state->second; - - result_automaton->connect(result_source_state, result_target_state, - static_cast - (*transition)->guard()); - } - } - - return result_automaton; -} - /* ========================================================================= */ void BuchiAutomaton::read(istream& input_stream) /* ---------------------------------------------------------------------------- * * Description: Reads an automaton description (which may represent a * generalized Büchi automaton) from a stream and stores it - * into the automaton object, converting it to a regular - * Büchi automaton if necessary. + * into the automaton object. * * Argument: input_stream -- A reference to an input stream. * @@ -312,20 +216,63 @@ void BuchiAutomaton::read(istream& input_stream) try { - /* - * Read in the number of states in the generalized Büchi automaton. - */ + /* Read the number of states in the generalized Büchi automaton. */ einput_stream >> number_of_states; - /* - * If the automaton is empty, do nothing. - */ + /* If the automaton is empty, do nothing. */ if (number_of_states == 0) return; - einput_stream >> number_of_acceptance_sets; + /* + * Determine the number and placement of acceptance sets. + * (Acceptance sets are described using strings described by the regular + * expression [0-9]+(s|S|t|T)*, where the [0-9]+ part corresponds to the + * number of the sets, and the (s|S|t|T)* part corresponds to the + * placement of the sets -- s or S for states, t or T for transitions. + * To retain compatibility with lbtt 1.0.x, the acceptance set placement + * defaults to acceptance sets on states if is not given explicitly.) + */ + + bool acceptance_sets_on_states = false; + bool acceptance_sets_on_transitions = false; + + string tok; + einput_stream >> tok; + + string::size_type s_pos = string::npos; + string::size_type t_pos = string::npos; + string::size_type pos = tok.find_first_not_of("0123456789"); + if (pos == 0) + throw AutomatonParseException + ("invalid specification for acceptance sets"); + + number_of_acceptance_sets = strtoul(tok.substr(0, pos).c_str(), 0, 10); + + for ( ; pos < tok.length(); ++pos) + { + if (tok[pos] == 's' || tok[pos] == 'S') + { + s_pos = pos; + acceptance_sets_on_states = true; + } + else if (tok[pos] == 't' || tok[pos] == 'T') + { + t_pos = pos; + acceptance_sets_on_transitions = true; + } + else + throw AutomatonParseException + ("invalid specification for acceptance sets"); + } + if (s_pos == string::npos && t_pos == string::npos) + { + acceptance_sets_on_states = true; + acceptance_sets_on_transitions = false; + } + + BitArray acc_sets(number_of_acceptance_sets); /* * Allocate space for the regular Büchi automaton that will be constructed @@ -446,36 +393,41 @@ void BuchiAutomaton::read(istream& input_stream) operator[](current_state).acceptanceSets().clear (number_of_acceptance_sets); - while (1) + if (acceptance_sets_on_states) { - einput_stream >> acceptance_set_mapping.first; - - if (acceptance_set_mapping.first == -1) - break; - - acceptance_set_finder = - acceptance_set_map.insert(acceptance_set_mapping); - - if (!acceptance_set_finder.second) - acceptance_set = (acceptance_set_finder.first)->second; - else + while (1) { - if (acceptance_set_mapping.second >= number_of_acceptance_sets) - throw AutomatonParseException("number of acceptance sets " - "does not match automaton state " - "definitions"); + einput_stream >> acceptance_set_mapping.first; - acceptance_set = acceptance_set_mapping.second; - ++acceptance_set_mapping.second; - } + if (acceptance_set_mapping.first == -1) + break; - operator[](current_state).acceptanceSets().setBit(acceptance_set); + acceptance_set_finder = + acceptance_set_map.insert(acceptance_set_mapping); + + if (!acceptance_set_finder.second) + acceptance_set = (acceptance_set_finder.first)->second; + else + { + if (acceptance_set_mapping.second >= number_of_acceptance_sets) + throw AutomatonParseException("number of acceptance sets " + "does not match automaton state " + "definitions"); + + acceptance_set = acceptance_set_mapping.second; + ++acceptance_set_mapping.second; + } + + operator[](current_state).acceptanceSets().setBit(acceptance_set); + } } /* * Process the transitions from the state to other states. Read a * target state id and add a mapping for it in the translation table if - * necessary. Then, read the propositional formula guarding the + * necessary. If the automaton is allowed to have acceptance sets + * associated with transitions, read an additional list of acceptance + * sets. Finally, read the propositional formula guarding the * transition and connect the current state to the target using the * guard formula. */ @@ -501,6 +453,42 @@ void BuchiAutomaton::read(istream& input_stream) state_mapping.second++; } + acc_sets.clear(number_of_acceptance_sets); + + /* + * If automata with acceptance sets on transitions are accepted, read + * the acceptance sets associated with the transition. + */ + + if (acceptance_sets_on_transitions) + { + while (1) + { + einput_stream >> acceptance_set_mapping.first; + + if (acceptance_set_mapping.first == -1) + break; + + acceptance_set_finder = + acceptance_set_map.insert(acceptance_set_mapping); + + if (!acceptance_set_finder.second) + acceptance_set = (acceptance_set_finder.first)->second; + else + { + if (acceptance_set_mapping.second >= number_of_acceptance_sets) + throw AutomatonParseException("number of acceptance sets " + "does not match automaton state " + "definitions"); + + acceptance_set = acceptance_set_mapping.second; + ++acceptance_set_mapping.second; + } + + acc_sets.setBit(acceptance_set); + } + } + try { guard = ::Ltl::LtlFormula::read(input_stream); @@ -516,7 +504,7 @@ void BuchiAutomaton::read(istream& input_stream) throw AutomatonParseException("illegal operators in guard formula"); } - connect(current_state, neighbor_state, guard); + connect(current_state, neighbor_state, guard, acc_sets); } processed_states.setBit(current_state); @@ -544,297 +532,6 @@ void BuchiAutomaton::read(istream& input_stream) } } -/* ========================================================================= */ -BuchiAutomaton* BuchiAutomaton::intersect - (const BuchiAutomaton& a1, const BuchiAutomaton& a2, - map, ALLOC(StateIdPair) >* - intersection_state_mapping) -/* ---------------------------------------------------------------------------- - * - * Description: Computes the intersection of two Büchi automata and returns - * a pointer to the intersection of the two automata. - * - * Arguments: a1, a2 -- References to two constant - * Büchi automata. - * intersection_state_mapping -- An (optional) pointer to a - * map which can be used to find - * out the state identifiers of - * the original automata to - * which a particular state in - * the intersection corresponds. - * - * Returns: A newly allocated BuchiAutomaton representing the - * intersection of the two automata. - * - * ------------------------------------------------------------------------- */ -{ - if (intersection_state_mapping != 0) - intersection_state_mapping->clear(); - - /* - * If either of the original automata is empty, the intersection is also - * empty. - */ - - if (a1.empty() || a2.empty()) - return new BuchiAutomaton(0, 0, 0); - - BuchiAutomaton* automaton; - - /* - * Determine the number of acceptance sets in the intersection. - */ - - const bool a1_has_no_acceptance_sets = (a1.number_of_acceptance_sets == 0); - const bool a2_has_no_acceptance_sets = (a2.number_of_acceptance_sets == 0); - - unsigned long int number_of_intersection_acceptance_sets; - - if (a1_has_no_acceptance_sets && a2_has_no_acceptance_sets) - number_of_intersection_acceptance_sets = 0; - else - number_of_intersection_acceptance_sets = a1.number_of_acceptance_sets - + a2.number_of_acceptance_sets; - - automaton = new BuchiAutomaton(1, 0, number_of_intersection_acceptance_sets); - - /* - * A stack for processing pairs of states of the original automata. - */ - - stack > - unprocessed_states; - - /* - * `state_mapping' maps pairs of states of the original automata to the - * states of the new automaton. - */ - - map, ALLOC(size_type) > - state_mapping; - - size_type first_free_id = 1; /* First free identifier for a - * new state in the intersection - * automaton. - */ - - const StateIdPair* state_pair; /* Pointer to pair of two state - * identifiers of the original - * automata. - */ - - size_type intersect_state; /* `Current' state in the - * intersection automaton. - */ - - bool intersect_state_valid; /* True if the current state has - * been determined by using the - * mapping. - */ - - BitArray* intersect_acceptance_sets; /* Pointers for accessing the */ - const BitArray* acceptance_sets; /* acceptance sets of the new - * and the original automata. - */ - - const GraphEdgeContainer* transitions1; /* Pointers for accessing the */ - const GraphEdgeContainer* transitions2; /* transitions of the two - * original automata. - */ - - ::Ltl::LtlFormula* guard1; /* Pointers for accessing the */ - ::Ltl::LtlFormula* guard2; /* guard formulas of the */ - ::Ltl::LtlFormula* new_guard; /* transitions of the - * automata. - */ - - /* - * Insert the initial state into the state mapping. - */ - - state_mapping.insert(make_pair(make_pair(a1.initial_state, a2.initial_state), - 0)); - unprocessed_states.push(&(state_mapping.begin()->first)); - - /* - * Adjust the acceptance sets of the initial state of the intersection. - */ - - intersect_acceptance_sets = &((*automaton)[0].acceptanceSets()); - - if (!a1_has_no_acceptance_sets) - { - acceptance_sets = &(a1[a1.initial_state].acceptanceSets()); - - for (unsigned long int accept_set = 0; - accept_set < a1.number_of_acceptance_sets; - accept_set++) - { - if (acceptance_sets->test(accept_set)) - intersect_acceptance_sets->setBit(accept_set); - } - } - - if (!a2_has_no_acceptance_sets) - { - acceptance_sets = &(a2[a2.initial_state].acceptanceSets()); - - for (unsigned long int accept_set = 0; - accept_set < a2.number_of_acceptance_sets; - accept_set++) - { - if (acceptance_sets->test(accept_set)) - intersect_acceptance_sets->setBit(a1.number_of_acceptance_sets - + accept_set); - } - } - - /* - * Pop pairs of states of the two original automata until all states have - * been processed. - */ - - try - { - while (!unprocessed_states.empty()) - { - if (::user_break) - throw UserBreakException(); - - intersect_state_valid = false; - state_pair = unprocessed_states.top(); - unprocessed_states.pop(); - - /* - * Loop through the transitions of the two original automata. If the - * conjunction of the guard formulae of any two transitions is - * satisfiable, insert a new transition into the intersection automaton. - * Create new states in the intersection automaton if necessary. - */ - - transitions1 = &a1[state_pair->first].edges(); - transitions2 = &a2[state_pair->second].edges(); - - for (GraphEdgeContainer::const_iterator tr1 = transitions1->begin(); - tr1 != transitions1->end(); - ++tr1) - { - guard1 = &(static_cast(*tr1)->guard()); - - for (GraphEdgeContainer::const_iterator tr2 = transitions2->begin(); - tr2 != transitions2->end(); - ++tr2) - { - guard2 = &(static_cast(*tr2)->guard()); - - new_guard = &Ltl::And::construct(*guard1, *guard2); - - if (new_guard->satisfiable()) - { - /* - * Determine the `current' state of the intersection automaton. - */ - - if (!intersect_state_valid) - { - intersect_state = state_mapping[*state_pair]; - intersect_state_valid = true; - } - - /* - * Test whether the state pair pointed to by the two transitions - * is already in the intersection. - */ - - pair, - ALLOC(size_type) >::iterator, bool> - check_state; - - check_state - = state_mapping.insert(make_pair(make_pair((*tr1)->targetNode(), - (*tr2)->targetNode()), - first_free_id)); - - if (check_state.second) /* insertion occurred? */ - { - automaton->expand(); - - /* - * Determine the acceptance sets to which the new state in the - * intersection automaton belongs. - */ - - intersect_acceptance_sets - = &((*automaton)[first_free_id].acceptanceSets()); - - if (!a1_has_no_acceptance_sets) - { - acceptance_sets = &(a1[check_state.first->first.first]. - acceptanceSets()); - - for (unsigned long int accept_set = 0; - accept_set < a1.number_of_acceptance_sets; - accept_set++) - { - if (acceptance_sets->test(accept_set)) - intersect_acceptance_sets->setBit(accept_set); - } - } - - if (!a2_has_no_acceptance_sets) - { - acceptance_sets = &(a2[check_state.first->first.second]. - acceptanceSets()); - - for (unsigned long int accept_set = 0; - accept_set < a2.number_of_acceptance_sets; - accept_set++) - { - if (acceptance_sets->test(accept_set)) - intersect_acceptance_sets->setBit - (a1.number_of_acceptance_sets + accept_set); - } - } - - /* - * Connect the `current' state of the intersection automaton to - * the new state. - */ - - automaton->connect(intersect_state, first_free_id, new_guard); - first_free_id++; - unprocessed_states.push(&(check_state.first->first)); - } - else - automaton->connect(intersect_state, check_state.first->second, - new_guard); - } - else - ::Ltl::LtlFormula::destruct(new_guard); - } - } - } - - if (intersection_state_mapping != 0) - { - for (map, ALLOC(size_type) > - ::const_iterator mapping = state_mapping.begin(); - mapping != state_mapping.end(); - ++mapping) - intersection_state_mapping->insert(make_pair(mapping->second, - mapping->first)); - } - } - catch (...) - { - delete automaton; - throw; - } - - return automaton; -} - /* ========================================================================= */ void BuchiAutomaton::print (ostream& stream, const int indent, const GraphOutputFormat fmt) const @@ -876,7 +573,7 @@ void BuchiAutomaton::print << " transitions.\n" + string(indent, ' ') + "The automaton has " << number_of_acceptance_sets - << " sets of accepting states.\n" + string(indent, ' ') + << " acceptance sets.\n" + string(indent, ' ') + "The reachable part of the automaton contains\n" + string(indent + 4, ' ') << reachable_part_statistics.first @@ -937,7 +634,8 @@ void BuchiAutomaton::print ++transition) { estream << string(indent + 2, ' ') + 'n' << state; - (*transition)->print(stream, indent, fmt); + static_cast(*transition) + ->print(stream, indent, fmt, number_of_acceptance_sets); estream << ";\n"; } } @@ -960,16 +658,23 @@ void BuchiAutomaton::print /* ========================================================================= */ void BuchiAutomaton::BuchiTransition::print - (ostream& stream, const int indent, const GraphOutputFormat fmt) const + (ostream& stream, const int indent, const GraphOutputFormat fmt, + const unsigned long int number_of_acceptance_sets) const /* ---------------------------------------------------------------------------- * * Description: Writes information about a transition between two states of * a Büchi automaton. * - * Arguments: stream -- A reference to an output stream. - * indent -- Number of spaces to leave to the left of output. - * fmt -- Determines the format of output. - * + * Arguments: stream -- A reference to an output + * stream. + * indent -- Number of spaces to leave to + * the left of output. + * fmt -- Determines the format of + * output. + * number_of_acceptance_sets -- Number of acceptance sets in + * the automaton to which the + * transition belongs. + * * Returns: Nothing. * * ------------------------------------------------------------------------- */ @@ -977,11 +682,9 @@ void BuchiAutomaton::BuchiTransition::print Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); if (fmt == NORMAL) - { estream << string(indent, ' ') + "Transition to state " << targetNode() - << " [ guard: " << *guard_formula << " ]\n"; - } + << " [ acc.: "; else if (fmt == DOT) { string formula(StringUtil::toString(*guard_formula)); @@ -1002,9 +705,33 @@ void BuchiAutomaton::BuchiTransition::print else estream << formula[i]; } - estream << "\",fontsize=10,fontname=\"Courier-Bold\"]"; + + estream << "\\n"; } + estream << '{'; + bool first_printed = false; + for (unsigned long int accept_set = 0; + accept_set < number_of_acceptance_sets; + ++accept_set) + { + if (acceptance_sets[accept_set]) + { + if (first_printed) + estream << ", "; + else + first_printed = true; + + estream << accept_set; + } + } + estream << '}'; + + if (fmt == NORMAL) + estream << ", guard: " << *guard_formula << " ]\n"; + else if (fmt == DOT) + estream << "\",fontsize=10,fontname=\"Courier-Bold\"]"; + estream.flush(); } @@ -1069,7 +796,8 @@ void BuchiAutomaton::BuchiState::print GraphEdgeContainer::const_iterator edge; for (edge = edges().begin(); edge != edges().end(); ++edge) - (*edge)->print(stream, indent); + static_cast(*edge) + ->print(stream, indent, fmt, number_of_acceptance_sets); } else estream << string(indent, ' ') + "No transitions to other states.\n"; diff --git a/lbtt/src/BuchiAutomaton.h b/lbtt/src/BuchiAutomaton.h index dcd034bd8..cec62163c 100644 --- a/lbtt/src/BuchiAutomaton.h +++ b/lbtt/src/BuchiAutomaton.h @@ -21,6 +21,7 @@ #define BUCHIAUTOMATON_H #include +#include #include #include #include @@ -38,8 +39,7 @@ namespace Graph /****************************************************************************** * - * A class for representing Büchi automata with a single set of accepting - * states. + * A class for representing generalized Büchi automata. * *****************************************************************************/ @@ -139,15 +139,19 @@ public: void connect /* Connects two states */ (const size_type father, /* of the automaton with */ const size_type child); /* an unguarded - * transition. + * transition with no + * associated acceptance + * sets. */ void connect /* Connects two states */ (const size_type father, const size_type child, /* of the automaton with */ - ::Ltl::LtlFormula& guard); /* a transition guarded */ - void connect /* by a propositional */ - (const size_type father, const size_type child, /* formula. */ - ::Ltl::LtlFormula* guard); + ::Ltl::LtlFormula& guard, /* a transition guarded */ + const BitArray& acc_sets); /* by a propositional */ + void connect /* formula. */ + (const size_type father, const size_type child, + ::Ltl::LtlFormula* guard, + const BitArray& acc_sets); /* `disconnect' inherited from Graph */ @@ -167,12 +171,6 @@ public: * automaton. */ - BuchiAutomaton* regularize() const; /* Converts a generalized - * automaton to an - * automaton with one set - * of accepting states. - */ - void read(istream& input_stream); /* Reads the automaton * from a stream. */ @@ -181,17 +179,10 @@ public: (ostream& stream = cout, /* about the automaton */ const int indent = 0, /* to a stream in */ const GraphOutputFormat fmt = NORMAL) const; /* various formats - * (determined by the - * `fmt' argument). + * (determined by the + * `fmt' argument). */ - static BuchiAutomaton* intersect /* Computes the */ - (const BuchiAutomaton& a1, /* intersection of two */ - const BuchiAutomaton& a2, /* Büchi automata. */ - map, ALLOC(StateIdPair) >* - intersection_state_mapping = 0); - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ class AutomatonParseException; /* Class for reporting @@ -227,14 +218,12 @@ class BuchiAutomaton::BuchiTransition : public Graph::Edge public: BuchiTransition /* Constructor. */ (const size_type target, - ::Ltl::LtlFormula* formula); + ::Ltl::LtlFormula* formula, + const BitArray& acc_sets, + unsigned long int num_acc_sets); ~BuchiTransition(); /* Destructor. */ -private: - -public: - /* `targetNode' inherited from Graph::Edge */ bool enabled /* These functions test */ @@ -249,14 +238,24 @@ public: * propositional formula * guarding the transition. */ + + BitArray& acceptanceSets(); /* Returns the */ + const BitArray& acceptanceSets() const; /* acceptance sets + * associated with the + * the transition. + */ + + void print /* Writes information */ + (ostream& stream, /* about the transition */ + const int indent, /* (as a plain graph */ + const GraphOutputFormat fmt) const; /* edge) to a stream. */ void print /* Writes information */ - (ostream& stream = cout, /* about the transition */ - const int indent = 0, /* to a stream in */ - const GraphOutputFormat fmt = NORMAL) const; /* various formats - * (determined by the - * `fmt' argument). - */ + (ostream& stream, /* about the transition */ + const int indent, /* to a stream in */ + const GraphOutputFormat fmt, /* various formats */ + const unsigned long int /* (determined by the */ + number_of_acceptance_sets) const; /* `fmt' argument). */ private: BuchiTransition(const BuchiTransition&); /* Prevent copying and */ @@ -281,6 +280,11 @@ private: * formula guarding the * transition. */ + + BitArray acceptance_sets; /* Acceptance sets + * associated with the + * transition. + */ }; @@ -372,7 +376,8 @@ inline void BuchiAutomaton::connect * * Description: Connects two states of a BuchiAutomaton to each other with an * unguarded transition (actually, a transition with a guard - * that is always true). + * that is always true) with an empty set of acceptance + * conditions. * * Arguments: father -- Source state identifier. * child -- Target state identifier. @@ -381,50 +386,59 @@ inline void BuchiAutomaton::connect * * ------------------------------------------------------------------------- */ { - connect(father, child, &(::Ltl::True::construct())); + BitArray acc_sets(number_of_acceptance_sets); + acc_sets.clear(number_of_acceptance_sets); + connect(father, child, &(::Ltl::True::construct()), acc_sets); } /* ========================================================================= */ inline void BuchiAutomaton::connect - (const size_type father, const size_type child, ::Ltl::LtlFormula& guard) + (const size_type father, const size_type child, ::Ltl::LtlFormula& guard, + const BitArray& acc_sets) /* ---------------------------------------------------------------------------- * * Description: Connects two states of a BuchiAutomaton to each other, using * a LtlFormula (which is actually a propositional formula) to * guard the transition between the states. * - * Arguments: father -- Source state. - * child -- Target state. - * guard -- A reference to an LtlFormula (a propositional - * formula) guarding the transition. + * Arguments: father -- Source state. + * child -- Target state. + * guard -- A reference to an LtlFormula (a propositional + * formula) guarding the transition. + * acc_sets -- A reference to a BitArray giving the + * acceptance sets associated with the transition. * * Returns: Nothing. * * ------------------------------------------------------------------------- */ { - connect(father, child, guard.clone()); + connect(father, child, guard.clone(), acc_sets); } /* ========================================================================= */ inline void BuchiAutomaton::connect - (const size_type father, const size_type child, ::Ltl::LtlFormula* guard) + (const size_type father, const size_type child, ::Ltl::LtlFormula* guard, + const BitArray& acc_sets) /* ---------------------------------------------------------------------------- * * Description: Connects two states of a BuchiAutomaton to each other, using * a LtlFormula (which is actually a propositional formula) to * guard the transition between the states. * - * Arguments: father -- Source state. - * child -- Target state. - * guard -- A pointer to an LtlFormula (a propositional - * formula) guarding the transition. The transition - * will "own" the guard formula. + * Arguments: father -- Source state. + * child -- Target state. + * guard -- A pointer to an LtlFormula (a propositional + * formula) guarding the transition. The + * transition will "own" the guard formula. + * acc_sets -- A reference to a BitArray giving the acceptance + * sets associated with the transition. * * Returns: Nothing. * * ------------------------------------------------------------------------- */ { - BuchiTransition* new_buchi_transition = new BuchiTransition(child, guard); + BuchiTransition* new_buchi_transition + = new BuchiTransition(child, guard, acc_sets, number_of_acceptance_sets); try { @@ -512,39 +526,28 @@ inline istream& operator>>(istream& stream, BuchiAutomaton& automaton) /* ========================================================================= */ inline BuchiAutomaton::BuchiTransition::BuchiTransition - (const size_type target, ::Ltl::LtlFormula* formula) : - Edge(target), guard_formula(formula) + (const size_type target, ::Ltl::LtlFormula* formula, + const BitArray& acc_sets, unsigned long int num_acc_sets) : + Edge(target), guard_formula(formula), acceptance_sets(num_acc_sets) /* ---------------------------------------------------------------------------- * * Description: Constructor for class BuchiAutomaton::BuchiTransition. * Initializes a new transition to a BuchiState, guarded by an * LtlFormula (which is actually a propositional formula). * - * Arguments: target -- Identifier of the target state of the automaton. - * formula -- A pointer to a propositional formula guarding - * the transition. - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ -} - -/* ========================================================================= */ -inline BuchiAutomaton::BuchiTransition::BuchiTransition - (const BuchiTransition& transition) : - Edge(transition), guard_formula(transition.guard_formula->clone()) -/* ---------------------------------------------------------------------------- - * - * Description: Copy constructor for class BuchiAutomaton::BuchiTransition. - * Creates a copy of a BuchiTransition object. - * - * Arguments: transition -- BuchiTransition to be copied. + * Arguments: target -- Identifier of the target state of the + * automaton. + * formula -- A pointer to a propositional formula guarding + * the transition. + * acc_sets -- A reference to a constant BitArray containing + * the acceptance sets associated with the + * transition. * * Returns: Nothing. * * ------------------------------------------------------------------------- */ { + acceptance_sets.copy(acc_sets, num_acc_sets); } /* ========================================================================= */ @@ -688,6 +691,59 @@ inline ::Ltl::LtlFormula& BuchiAutomaton::BuchiTransition::guard() const return *guard_formula; } +/* ========================================================================= */ +inline BitArray& BuchiAutomaton::BuchiTransition::acceptanceSets() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the acceptance sets associated with a + * BuchiTransition. + * + * Arguments: None. + * + * Returns: A reference to the BitArray storing the acceptance sets + * associated with the transition. + * + * ------------------------------------------------------------------------- */ +{ + return acceptance_sets; +} + +/* ========================================================================= */ +inline const BitArray& BuchiAutomaton::BuchiTransition::acceptanceSets() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the acceptance sets associated with a + * BuchiTransition. + * + * Arguments: None. + * + * Returns: A constant reference to the BitArray storing the acceptance + * sets associated with the transition. + * + * ------------------------------------------------------------------------- */ +{ + return acceptance_sets; +} + +/* ========================================================================= */ +inline void BuchiAutomaton::BuchiTransition::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a transition (as a plain graph edge + * without any associated information) to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the output format of the transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::print(stream, indent, fmt); +} + /****************************************************************************** diff --git a/lbtt/src/BuchiProduct.cc b/lbtt/src/BuchiProduct.cc new file mode 100644 index 000000000..c5f5c51d5 --- /dev/null +++ b/lbtt/src/BuchiProduct.cc @@ -0,0 +1,98 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "BuchiProduct.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Static member definitions for class BuchiProduct. + * + *****************************************************************************/ + +map< ::Ltl::LtlFormula*, BuchiProduct::SatisfiabilityMapping, + less< ::Ltl::LtlFormula*>, ALLOC(BuchiProduct::SatisfiabilityMapping) > + BuchiProduct::sat_cache; + + + +/****************************************************************************** + * + * Function definitions for class BuchiProduct. + * + *****************************************************************************/ + +/* ========================================================================= */ +bool BuchiProduct::synchronizable + (const Graph::Edge& transition_1, + const Graph::Edge& transition_2) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether two transitions of two Büchi automata are + * synchronizable by checking whether the conjunction of their + * guard formulas is satisfiable. + * + * Arguments: transition_1, -- Constant references to the transitions. + * transition_2 + * + * Returns: true iff the transitions are synchronizable. The result is + * also stored into `this->sat_cache' for later reference. + * + * ------------------------------------------------------------------------- */ +{ + using ::Ltl::LtlFormula; + using ::Ltl::And; + + LtlFormula* guard_1 = &static_cast + (transition_1).guard(); + LtlFormula* guard_2 = &static_cast + (transition_2).guard(); + + if (guard_2 > guard_1) + { + LtlFormula* swap_guard = guard_2; + guard_2 = guard_1; + guard_1 = swap_guard; + } + + map, + ALLOC(SatisfiabilityMapping) >::iterator + sat_cache_element = sat_cache.find(guard_1); + + if (sat_cache_element == sat_cache.end()) + sat_cache_element = sat_cache.insert + (make_pair(guard_1, SatisfiabilityMapping())).first; + else + { + SatisfiabilityMapping::const_iterator sat_result + = sat_cache_element->second.find(guard_2); + if (sat_result != sat_cache_element->second.end()) + return sat_result->second; + } + + LtlFormula* f = &And::construct(*guard_1, *guard_2); + const bool result = f->satisfiable(); + LtlFormula::destruct(f); + sat_cache_element->second.insert(make_pair(guard_2, result)); + return result; +} + +} diff --git a/lbtt/src/BuchiProduct.h b/lbtt/src/BuchiProduct.h new file mode 100644 index 000000000..43200f521 --- /dev/null +++ b/lbtt/src/BuchiProduct.h @@ -0,0 +1,512 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef BUCHIPRODUCT_H +#define BUCHIPRODUCT_H + +#include +#include +#include +#include +#include +#include +#include "BitArray.h" +#include "BuchiAutomaton.h" +#include "EdgeContainer.h" +#include "Graph.h" +#include "LtlFormula.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * A class with operations for checking the intersection of two Büchi automata + * (represented as two BuchiAutomaton objects) for emptiness. + * + *****************************************************************************/ + +class BuchiProduct +{ +public: + BuchiProduct /* Constructor. */ + (const Graph& a1, + const Graph& a2); + + /* default copy constructor */ + + ~BuchiProduct(); /* Destructor. */ + + /* default assignment operator */ + + bool empty() const; /* Tells whether the + * intersection of the + * Büchi automata + * associated with the + * product object is + * (trivially) empty. + */ + + unsigned long int numberOfAcceptanceSets() const; /* Tells the number of + * acceptance sets in the + * intersection of the + * automata associated with + * the object. + */ + + const BuchiAutomaton::BuchiState& firstComponent /* Mappings between an */ + (const Graph::size_type /* intersection state */ + state_id) const; /* identifier and states */ + const BuchiAutomaton::BuchiState& secondComponent /* of the underlying */ + (const Graph::size_type /* automata. */ + state_id) const; + + void mergeAcceptanceInformation /* Merges the acceptance */ + (const Graph::Node& state1, /* sets associated with */ + const Graph::Node& state2, /* a pair of states into */ + BitArray& acceptance_sets) const; /* a collection of sets. */ + + void mergeAcceptanceInformation /* Merges the acceptance */ + (const Graph::Edge& /* sets associated with */ + transition1, /* a pair of */ + const Graph::Edge& /* transitions into a */ + transition2, /* collection of sets. */ + BitArray& acceptance_sets) const; + + void validateEdgeIterators /* Ensures that a pair */ + (const Graph::Node& /* of transition */ + state_1, /* iterators points to a */ + const Graph::Node& /* transition beginning */ + state_2, /* from a given state in */ + GraphEdgeContainer::const_iterator& /* the intersection of */ + transition_1, /* two Büchi automata. */ + GraphEdgeContainer::const_iterator& + transition_2); + + void incrementEdgeIterators /* Updates a pair of */ + (const Graph::Node& /* transition iterators */ + state_1, /* to make them point to */ + const Graph::Node& /* the "next" transition */ + state_2, /* starting from a given */ + GraphEdgeContainer::const_iterator& /* state in the */ + transition_1, /* intersection of two */ + GraphEdgeContainer::const_iterator& /* Büchi automata. */ + transition_2); + + static void clearSatisfiabilityCache(); /* Clears information about + * the satisfiability of + * the guards of product + * transitions. + */ +private: + void mergeAcceptanceInformation /* Bitwise or between */ + (const BitArray& sets1, const BitArray& sets2, /* two "component" */ + BitArray& result) const; /* acceptance set + * vectors and a result + * vector. + */ + + + + bool synchronizable /* Tests whether a pair */ + (const Graph::Edge& /* of transitions of two */ + transition_1, /* Büchi automata is */ + const Graph::Edge& /* synchronizable. */ + transition_2); + + const BuchiAutomaton& automaton_1; /* Automata associated */ + const BuchiAutomaton& automaton_2; /* with the BuchiProduct */ + /* object. */ + + const unsigned long int /* Number of acceptance */ + number_of_acceptance_sets; /* sets in the + * intersection of the + * automata. + */ + + typedef map< ::Ltl::LtlFormula*, bool, /* Type definition for */ + less< ::Ltl::LtlFormula*>, /* storing information */ + ALLOC(bool) > /* about the */ + SatisfiabilityMapping; /* satisfiability of the + * guards of product + * transitions. + */ + + static map< ::Ltl::LtlFormula*, /* Result cache for */ + SatisfiabilityMapping, /* satisfiability tests. */ + less< ::Ltl::LtlFormula*>, + ALLOC(SatisfiabilityMapping) > + sat_cache; +}; + + + +/****************************************************************************** + * + * Inline function definitions for class BuchiProduct. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiProduct::BuchiProduct + (const Graph& a1, const Graph& a2) : + automaton_1(static_cast(a1)), + automaton_2(static_cast(a2)), + number_of_acceptance_sets(static_cast(a1) + .numberOfAcceptanceSets() + + static_cast(a2) + .numberOfAcceptanceSets()) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BuchiProduct. Initializes a new object + * with operations for checking the emptiness of two Büchi + * automata. + * + * Arguments: a1, a2 -- Constant references to two + * Graph objects, assumed to be + * BüchiAutomaton objects to which to apply the + * operations. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiProduct::~BuchiProduct() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiProduct. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline bool BuchiProduct::empty() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the intersection of the Büchi automata + * associated with a BuchiProduct object is (trivially) empty. + * + * Arguments: None. + * + * Returns: true iff either of the automata associated with the + * BuchiProduct object is (trivially) empty (i.e., whether + * either of the automata has no states). + * + * ------------------------------------------------------------------------- */ +{ + return (automaton_1.empty() || automaton_2.empty()); +} + + +/* ========================================================================= */ +inline unsigned long int BuchiProduct::numberOfAcceptanceSets() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of acceptance sets in the intersection of + * the two Büchi automata associated with a BuchiProduct object. + * + * Arguments: None. + * + * Returns: The number of acceptance sets in the intersection. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_acceptance_sets; +} + +/* ========================================================================= */ +inline const BuchiAutomaton::BuchiState& BuchiProduct::firstComponent + (const Graph::size_type state_id) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing states of the "first" component + * automaton in the intersection of two Büchi automata. + * + * Argument: state_id -- Identifier of a state in the component + * automaton. + * + * Returns: A constant reference to a state in the component automaton. + * + * ------------------------------------------------------------------------- */ +{ + return automaton_1[state_id]; +} + +/* ========================================================================= */ +inline const BuchiAutomaton::BuchiState& BuchiProduct::secondComponent + (const Graph::size_type state_id) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing states of the "second" component + * automaton in the intersection of two Büchi automata. + * + * Argument: state_id -- Identifier of a state in the component + * automaton. + * + * Returns: A constant reference to a state in the component automaton. + * + * ------------------------------------------------------------------------- */ +{ + return automaton_2[state_id]; +} + +/* ========================================================================= */ +inline void BuchiProduct::mergeAcceptanceInformation + (const Graph::Node& state1, + const Graph::Node& state2, + BitArray& acceptance_sets) const +/* ---------------------------------------------------------------------------- + * + * Description: Merges the acceptance sets associated with a pair of states + * of two Büchi automata into a collection of sets. + * + * Arguments: state1, state2 -- Constant references to the states of the + * automata. + * acceptance_sets -- A reference to a BitArray for storing + * the result. The BitArray is assumed to + * have capacity for + * `this->number_of_acceptance_sets' bits. + * + * Returns: Nothing. Let n=`this->automaton_1.numberOfAcceptanceSets()'; + * after the operation, `acceptance_sets[i] = true' holds if + * either + * 0 <= i < n and + * `state1.acceptanceSets().test(i) == true' + * or 0 <= i - n < `this->automaton_2.numberOfAcceptanceSets()' + * and `state2.acceptanceSets().test(i - n) == true'. + * + * ------------------------------------------------------------------------- */ +{ + mergeAcceptanceInformation + (static_cast(state1).acceptanceSets(), + static_cast(state2).acceptanceSets(), + acceptance_sets); +} + +/* ========================================================================= */ +inline void BuchiProduct::mergeAcceptanceInformation + (const Graph::Edge& transition1, + const Graph::Edge& transition2, + BitArray& acceptance_sets) const +/* ---------------------------------------------------------------------------- + * + * Description: Merges the acceptance sets associated with a pair of + * transitions of two Büchi automata into a collection of + * acceptance sets. + * + * Arguments: transition1, -- Constant references to the transitions + * transition2 of the automata. + * acceptance_sets -- A reference to a BitArray for storing + * the result. The BitArray is assumed to + * have capacity for + * `this->number_of_acceptance_sets' bits. + * + * Returns: Nothing. Let n=`this->automaton_1.numberOfAcceptanceSets()'; + * after the operation, `acceptance_sets[i] = true' holds if + * either + * 0 <= i < n and + * `transition1.acceptanceSets().test(i) == true' + * or 0 <= i - n < `this->automaton_2.numberOfAcceptanceSets()' + * and `transition2.acceptanceSets().test(i - n) == true'. + * + * ------------------------------------------------------------------------- */ +{ + mergeAcceptanceInformation + (static_cast(transition1) + .acceptanceSets(), + static_cast(transition2) + .acceptanceSets(), + acceptance_sets); +} + +/* ========================================================================= */ +inline void BuchiProduct::mergeAcceptanceInformation + (const BitArray& sets1, const BitArray& sets2, BitArray& result) const +/* ---------------------------------------------------------------------------- + * + * Description: Bitwise or between two acceptance set vectors and a result + * vector. + * + * Arguments: sets1, -- Constant references to two BitArrays having (at + * sets2 least) capacities + * `automaton_1.numberOfAcceptanceSets()' and + * `automaton_2.numberOfAcceptanceSets()', + * respectively. + * result -- A BitArray for storing the result, assumed to + * have room for at least + * `this->number_of_acceptance_sets' bits. + * + * Returns: Nothing. Let n=`this->automaton_1.numberOfAcceptanceSets()'; + * after the operation, `result[i] = true' holds if + * either + * 0 <= i < n and `sets1[i] == true' + * or 0 <= i - n < `this->automaton_2.numberOfAcceptanceSets()' + * and `sets2[i - n] == true'. + * + * ------------------------------------------------------------------------- */ +{ + const unsigned long int shift + = automaton_1.numberOfAcceptanceSets(); + unsigned long int acceptance_set; + for (acceptance_set = 0; acceptance_set < shift; ++acceptance_set) + { + if (sets1[acceptance_set]) + result.setBit(acceptance_set); + } + for ( ; acceptance_set < number_of_acceptance_sets; ++acceptance_set) + { + if (sets2[acceptance_set - shift]) + result.setBit(acceptance_set); + } +} + +/* ========================================================================= */ +inline void BuchiProduct::validateEdgeIterators + (const Graph::Node& state_1, + const Graph::Node& state_2, + GraphEdgeContainer::const_iterator& transition_1, + GraphEdgeContainer::const_iterator& transition_2) +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether a pair of transition iterators corresponds to + * a transition beginning from a state in the intersection of + * two Büchi automata; if this is not the case, increments the + * iterators to make them point to a valid transition beginning + * from the state in the intersection (or to the "end" of the + * collection of transitions beginning from the state if no + * valid transition can be found by incrementing the iterators). + * + * Arguments: state_1, -- These variables determine the state in + * state_2 the intersection automaton; `state_1' and + * `state_2' should both be references to + * BuchiAutomaton::BuchiState objects. + * transition_1, -- References to the transition iterators. + * transition_2 Initially, `transition_1' and + * `transition_2' should point to two + * transitions starting from `state_1' and + * `state_2', respectively. + * + * Returns: Nothing. Upon return, `transition_1' and `transition_2' will + * either equal `state_1.edges().end()' and + * `state_2.edges().end()', respectively, or they will point to + * a pair of transitions beginning from `state_1' and `state_2' + * such that this pair of transitions corresponds to a + * transition starting from the intersection state determined by + * `state_1' and `state_2'. + * + * ------------------------------------------------------------------------- */ +{ + const GraphEdgeContainer& transitions_1 = state_1.edges(); + const GraphEdgeContainer& transitions_2 = state_2.edges(); + + if (transition_1 == transitions_1.end()) + { + transition_2 = transitions_2.end(); + return; + } + if (transition_2 == transitions_2.end()) + { + transition_1 = transitions_1.end(); + return; + } + + if (!synchronizable(**transition_1, **transition_2)) + incrementEdgeIterators(state_1, state_2, transition_1, transition_2); +} + +/* ========================================================================= */ +inline void BuchiProduct::incrementEdgeIterators + (const Graph::Node& state_1, + const Graph::Node& state_2, + GraphEdgeContainer::const_iterator& transition_1, + GraphEdgeContainer::const_iterator& transition_2) +/* ---------------------------------------------------------------------------- + * + * Description: Increments a pair of transition iterators to point to the + * "next" transition beginning from a state in the intersection + * of two Büchi automata. If no "next" transition exists, makes + * the iterators point to the "end" of the collection of + * transitions beginning from the state. + * + * Arguments: state_1, -- These variables determine the state in + * state_2 the intersection automaton; `state_1' and + * `state_2' should both be references to + * BuchiAutomaton::BuchiState objects. + * transition_1, -- References to the transition iterators. + * transition_2 Initially, `transition_1' and + * `transition_2' should point to two + * transitions starting from `state_1' and + * `state_2', respectively. + * + * Returns: Nothing. Upon return, `transition_1' and `transition_2' will + * either equal `state_1.edges().end()' and + * `state_2.edges().end()', respectively, or they will point to + * a pair of transitions beginning from `state_1' and `state_2' + * such that this pair of transitions corresponds to a + * transition starting from the intersection state determined by + * `state_1' and `state_2'. + * + * ------------------------------------------------------------------------- */ +{ + const GraphEdgeContainer& transitions_1 = state_1.edges(); + const GraphEdgeContainer& transitions_2 = state_2.edges(); + + do + { + ++transition_2; + if (transition_2 == transitions_2.end()) + { + ++transition_1; + if (transition_1 == transitions_1.end()) + return; + transition_2 = transitions_2.begin(); + } + } + while (!synchronizable(**transition_1, **transition_2)); +} + +/* ========================================================================= */ +inline void BuchiProduct::clearSatisfiabilityCache() +/* ---------------------------------------------------------------------------- + * + * Description: Clears information about the satisfiability of the guard + * formulas of product transitions. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + sat_cache.clear(); +} + +} + +#endif /* !BUCHIPRODUCT_H */ diff --git a/lbtt/src/Config-lex.ll b/lbtt/src/Config-lex.ll index aa60a08f3..145126a89 100644 --- a/lbtt/src/Config-lex.ll +++ b/lbtt/src/Config-lex.ll @@ -19,11 +19,6 @@ %{ #include -#include -#include -#include -#include -#include #include "Configuration.h" #include "Config-parse.h" @@ -34,225 +29,102 @@ extern int config_file_line_number; %option case-insensitive %option never-interactive %option noyywrap +%option nounput + +%x ATTR EQ VAL + +SQSTR [^\'\n]* +DQSTR ([^\"\\\n]|\\.)* +UQC [^\'\"\\ \t\n] +UQSTR ({UQC}+|\\.)({UQC}*|\\.)* +OKVAL \'{SQSTR}\'|\"{DQSTR}\"|{UQSTR} %% -[ \t]* { /* Skip whitespace. */ } -"#"[^\n]* { /* Skip comments. */ } +<*>[ \t]* { /* Skip whitespace everywhere. */ } +<*>"#".*$ { /* Skip comments everywhere. */ } -"\n" { /* Skip newlines, but update the line number. */ +"\n" { /* Skip newlines, but update the line number. */ config_file_line_number++; } -"{" { return CFG_LBRACE; } -"}" { return CFG_RBRACE; } -"=" { return CFG_EQUALS; } - -algorithm { return CFG_ALGORITHM; } -enabled { return CFG_ENABLED; } -name { return CFG_NAME; } -parameters { return CFG_PARAMETERS; } -path { return CFG_PROGRAMPATH; } +"{" { BEGIN(ATTR); return CFG_LBRACE; } +algorithm|implementation|translator { return CFG_ALGORITHM; } globaloptions { return CFG_GLOBALOPTIONS; } -comparisoncheck { return CFG_COMPARISONTEST; } -comparisontest { return CFG_COMPARISONTEST; } -consistencycheck { return CFG_CONSISTENCYTEST; } -consistencytest { return CFG_CONSISTENCYTEST; } -interactive { return CFG_INTERACTIVE; } -intersectioncheck { return CFG_INTERSECTIONTEST; } -intersectiontest { return CFG_INTERSECTIONTEST; } -modelcheck { return CFG_MODELCHECK; } -rounds { return CFG_ROUNDS; } -verbosity { return CFG_VERBOSITY; } - statespaceoptions { return CFG_STATESPACEOPTIONS; } -edgeprobability { return CFG_EDGEPROBABILITY; } -propositions { return CFG_PROPOSITIONS; } -size { return CFG_SIZE; } -truthprobability { return CFG_TRUTHPROBABILITY; } -changeinterval { return CFG_CHANGEINTERVAL; } -randomseed { return CFG_RANDOMSEED; } - formulaoptions { return CFG_FORMULAOPTIONS; } -abbreviatedoperators { return CFG_ABBREVIATEDOPERATORS; } -andpriority { return CFG_ANDPRIORITY; } -beforepriority { return CFG_BEFOREPRIORITY; } -defaultoperatorpriority { return CFG_DEFAULTOPERATORPRIORITY; } -equivalencepriority { return CFG_EQUIVALENCEPRIORITY; } -falsepriority { return CFG_FALSEPRIORITY; } -finallypriority { return CFG_FINALLYPRIORITY; } -generatemode { return CFG_GENERATEMODE; } -globallypriority { return CFG_GLOBALLYPRIORITY; } -implicationpriority { return CFG_IMPLICATIONPRIORITY; } -nextpriority { return CFG_NEXTPRIORITY; } -notpriority { return CFG_NOTPRIORITY; } -orpriority { return CFG_ORPRIORITY; } -outputmode { return CFG_OUTPUTMODE; } -propositionpriority { return CFG_PROPOSITIONPRIORITY; } -releasepriority { return CFG_RELEASEPRIORITY; } -strongreleasepriority { return CFG_STRONGRELEASEPRIORITY; } -truepriority { return CFG_TRUEPRIORITY; } -untilpriority { return CFG_UNTILPRIORITY; } -weakuntilpriority { return CFG_WEAKUNTILPRIORITY; } -xorpriority { return CFG_XORPRIORITY; } -true|yes { - yylval.truth_value = true; - return CFG_TRUTH_VALUE; +[^ \t\n]+ { return CFG_UNKNOWN; } + +enabled { BEGIN(EQ); return CFG_ENABLED; } +name { BEGIN(EQ); return CFG_NAME; } +parameters { BEGIN(EQ); return CFG_PARAMETERS; } +path { BEGIN(EQ); return CFG_PROGRAMPATH; } + +comparisoncheck { BEGIN(EQ); return CFG_COMPARISONTEST; } +comparisontest { BEGIN(EQ); return CFG_COMPARISONTEST; } +consistencycheck { BEGIN(EQ); return CFG_CONSISTENCYTEST; } +consistencytest { BEGIN(EQ); return CFG_CONSISTENCYTEST; } +interactive { BEGIN(EQ); return CFG_INTERACTIVE; } +intersectioncheck { BEGIN(EQ); return CFG_INTERSECTIONTEST; } +intersectiontest { BEGIN(EQ); return CFG_INTERSECTIONTEST; } +modelcheck { BEGIN(EQ); return CFG_MODELCHECK; } +rounds { BEGIN(EQ); return CFG_ROUNDS; } +translatortimeout { BEGIN(EQ); return CFG_TRANSLATORTIMEOUT; } +verbosity { BEGIN(EQ); return CFG_VERBOSITY; } + +edgeprobability { BEGIN(EQ); return CFG_EDGEPROBABILITY; } +propositions { BEGIN(EQ); return CFG_PROPOSITIONS; } +size { BEGIN(EQ); return CFG_SIZE; } +truthprobability { BEGIN(EQ); return CFG_TRUTHPROBABILITY; } +changeinterval { BEGIN(EQ); return CFG_CHANGEINTERVAL; } +randomseed { BEGIN(EQ); return CFG_RANDOMSEED; } + +abbreviatedoperators { BEGIN(EQ); return CFG_ABBREVIATEDOPERATORS; } +andpriority { BEGIN(EQ); return CFG_ANDPRIORITY; } +beforepriority { BEGIN(EQ); return CFG_BEFOREPRIORITY; } +defaultoperatorpriority { + BEGIN(EQ); return CFG_DEFAULTOPERATORPRIORITY; + } +equivalencepriority { BEGIN(EQ); return CFG_EQUIVALENCEPRIORITY; } +falsepriority { BEGIN(EQ); return CFG_FALSEPRIORITY; } +finallypriority { BEGIN(EQ); return CFG_FINALLYPRIORITY; } +generatemode { BEGIN(EQ); return CFG_GENERATEMODE; } +globallypriority { BEGIN(EQ); return CFG_GLOBALLYPRIORITY; } +implicationpriority { BEGIN(EQ); return CFG_IMPLICATIONPRIORITY; } +nextpriority { BEGIN(EQ); return CFG_NEXTPRIORITY; } +notpriority { BEGIN(EQ); return CFG_NOTPRIORITY; } +orpriority { BEGIN(EQ); return CFG_ORPRIORITY; } +outputmode { BEGIN(EQ); return CFG_OUTPUTMODE; } +propositionpriority { BEGIN(EQ); return CFG_PROPOSITIONPRIORITY; } +releasepriority { BEGIN(EQ); return CFG_RELEASEPRIORITY; } +strongreleasepriority { BEGIN(EQ); return CFG_STRONGRELEASEPRIORITY; } +truepriority { BEGIN(EQ); return CFG_TRUEPRIORITY; } +untilpriority { BEGIN(EQ); return CFG_UNTILPRIORITY; } +weakuntilpriority { BEGIN(EQ); return CFG_WEAKUNTILPRIORITY; } +xorpriority { BEGIN(EQ); return CFG_XORPRIORITY; } + +"}" { BEGIN(INITIAL); return CFG_RBRACE; } + +"="?[^= \t\n]* { return CFG_UNKNOWN; } + +"=" { BEGIN(VAL); return CFG_EQUALS; } + +. { return CFG_UNKNOWN; } + +\\|{OKVAL}+(\\)? { + yylval.value = yytext; + BEGIN(ATTR); + return CFG_VALUE; } -false|no { - yylval.truth_value = false; - return CFG_TRUTH_VALUE; - } - -always { - yylval.interactivity_value = - Configuration::ALWAYS; - return CFG_INTERACTIVITY_VALUE; - } - -never { - yylval.interactivity_value = - Configuration::NEVER; - return CFG_INTERACTIVITY_VALUE; - } - -onerror { - yylval.interactivity_value = - Configuration::ONERROR; - return CFG_INTERACTIVITY_VALUE; - } - -normal { - yylval.formula_mode_value = - Configuration::NORMAL; - return CFG_FORMULA_MODE_VALUE; - } - -nnf { - yylval.formula_mode_value = Configuration::NNF; - return CFG_FORMULA_MODE_VALUE; - } - -local { - yylval.product_type_value = Configuration::LOCAL; - return CFG_PRODUCT_TYPE_VALUE; - } - -global { - yylval.product_type_value = - Configuration::GLOBAL; - return CFG_PRODUCT_TYPE_VALUE; - } - -randomgraph { - yylval.statespace_mode_value - = Configuration::RANDOMGRAPH; - return CFG_STATESPACE_MODE_VALUE; - } - -randomconnectedgraph { - yylval.statespace_mode_value - = Configuration::RANDOMCONNECTEDGRAPH; - return CFG_STATESPACE_MODE_VALUE; - } - -randompath { - yylval.statespace_mode_value - = Configuration::RANDOMPATH; - return CFG_STATESPACE_MODE_VALUE; - } - -enumeratedpath { - yylval.statespace_mode_value - = Configuration::ENUMERATEDPATH; - return CFG_STATESPACE_MODE_VALUE; - } - - -"-"?[0-9]+"...""-"?[0-9]+ { - char* dot_ptr; - yylval.integer_interval.min - = strtol(yytext, &dot_ptr, 10); - - if (yylval.integer_interval.min == LONG_MIN - || yylval.integer_interval.min == LONG_MAX) - throw Configuration::ConfigurationException - (config_file_line_number, - "integer out of range"); - - dot_ptr += 3; - yylval.integer_interval.max - = strtol(dot_ptr, 0, 10); - - if (yylval.integer_interval.max == LONG_MIN - || yylval.integer_interval.max == LONG_MAX) - throw Configuration::ConfigurationException - (config_file_line_number, - "integer out of range"); - - return CFG_INTEGER_INTERVAL; - } - -"-"?[0-9]+ { - yylval.integer = strtol(yytext, 0, 10); - if (yylval.integer == LONG_MIN - || yylval.integer == LONG_MAX) - throw Configuration::ConfigurationException - (config_file_line_number, - "integer out of range"); - return CFG_INTEGER; - } - -"-"?[0-9]*"."[0-9]+ { - yylval.real = strtod(yytext, 0); - - if (yylval.real == HUGE_VAL - || yylval.real == -HUGE_VAL) - throw Configuration::ConfigurationException - (config_file_line_number, - "real number out of range"); - return CFG_REAL; - } - -\"([^\n\"\\]*(\\[^\n])?)*\" { - unsigned long int len = strlen(yytext); - bool escape = false; - yylval.str = new string; - for (unsigned long int i = 1; i < len - 1; i++) - { - if (!escape && yytext[i] == '\\') - escape = true; - else - { - escape = false; - (*yylval.str) += yytext[i]; - } - } - return CFG_STRING_CONSTANT; - } - -. { - return CFG_UNKNOWN; +{OKVAL}*(\'{SQSTR}|\"{DQSTR})(\\)? { + throw Configuration::ConfigurationException + (config_file_line_number, + "unmatched quotes"); } +"\n" { return CFG_UNKNOWN; } %% - -/* ========================================================================= */ -int getCharacter() -/* ---------------------------------------------------------------------------- - * - * Description: Reads the next character from the lexer input stream. - * - * Arguments: None. - * - * Returns: The next character in the lexer input stream or EOF if there - * are no more characters to read. - * - * ------------------------------------------------------------------------- */ -{ - return yyinput(); -} diff --git a/lbtt/src/Config-parse.yy b/lbtt/src/Config-parse.yy index b51a8c942..43e228fcb 100644 --- a/lbtt/src/Config-parse.yy +++ b/lbtt/src/Config-parse.yy @@ -20,11 +20,13 @@ %{ #include -#include #include #include "Configuration.h" #include "StringUtil.h" +using namespace ::StringUtil; + + /****************************************************************************** @@ -33,12 +35,12 @@ * *****************************************************************************/ -static Configuration::AlgorithmInformation /* Stores all the */ - algorithm_information; /* information in a - * single `Algorithm' - * block in the - * configuration file. - */ +static string algorithm_name, algorithm_path, /* Implementation */ + algorithm_parameters; /* attributes read from */ +static bool algorithm_enabled; /* an `Algorithm' block + * in the configuration + * file. + */ static int algorithm_begin_line; /* Input file line number * denoting the beginning @@ -53,14 +55,6 @@ static int expected_token; /* Type of a token to be * configuration file. */ -static int current_block_type; /* Type of the current - * configuration block. - */ - -static int current_option_type; /* Type of the current - * option name. - */ - static Configuration* parser_cfg; /* Pointer to a * Configuration data * structure in which @@ -100,11 +94,6 @@ extern int yylex(); /* Reads the next token * the lexer). */ -extern int getCharacter(); /* Returns the next - * character in the lexer - * input stream. - */ - /****************************************************************************** @@ -114,7 +103,7 @@ extern int getCharacter(); /* Returns the next *****************************************************************************/ /* ========================================================================= */ -void yyerror(char* error_message) +void yyerror(const char* error_message) /* ---------------------------------------------------------------------------- * * Description: Function for reporting parse errors. @@ -127,26 +116,19 @@ void yyerror(char* error_message) * * ------------------------------------------------------------------------- */ { - string unknown_token(yytext); - int c; - - do - { - c = getCharacter(); - if (c != EOF && c != ' ' && c != '\t' && c != '\n') - unknown_token += static_cast(c); - } - while (c != EOF && c != ' ' && c != '\t' && c != '\n'); - + const string unknown_token(yytext); string msg; switch (expected_token) { case CFG_BLOCK_ID : - msg = string("unrecognized block identifier (`") + unknown_token + "')"; + msg = "`" + unknown_token + "' is not a valid block identifier"; break; case CFG_OPTION_ID : - msg = string("unrecognized option identifier (`") + unknown_token + "')"; + if (!unknown_token.empty()) + msg = "`" + unknown_token + "' is not a valid option identifier"; + else + msg = "'}' expected at the end of block"; break; case CFG_LBRACE : msg = "`{' expected after block identifier"; @@ -154,33 +136,8 @@ void yyerror(char* error_message) case CFG_EQUALS : msg = "`=' expected after option identifier"; break; - case CFG_TRUTH_VALUE : - msg = "truth value expected as option argument"; - break; - case CFG_INTERACTIVITY_VALUE : - msg = "interactivity mode expected as option argument"; - break; - case CFG_FORMULA_MODE_VALUE : - msg = "formula generation mode expected as option argument"; - break; - case CFG_STATESPACE_MODE_VALUE : - msg = "state space generation mode expected as option argument"; - break; - case CFG_PRODUCT_TYPE_VALUE : - msg = "model checking mode expected as option argument"; - break; - case CFG_INTEGER : - msg = "nonnegative integer expected as option argument"; - break; - case CFG_INTEGER_INTERVAL : - msg = "nonnegative integer or an integer interval expected as option" - " argument"; - break; - case CFG_REAL : - msg = "nonnegative real number expected as option argument"; - break; - case CFG_STRING_CONSTANT : - msg = "string constant expected as option argument"; + case CFG_VALUE : + msg = "value for option expected"; break; default : msg = error_message; @@ -190,92 +147,6 @@ void yyerror(char* error_message) throw Configuration::ConfigurationException(config_file_line_number, msg); } - - -/****************************************************************************** - * - * Functions for performing various bound checks for the values of different - * options in the configuration file. - * - *****************************************************************************/ - -/* ========================================================================= */ -void checkIntegerRange - (long int value, const struct Configuration::IntegerRange& range, - bool show_line_number_if_error = true) -/* ---------------------------------------------------------------------------- - * - * Description: Checks that a value given to a configuration is within the - * acceptable range. - * - * Arguments: value -- Integer value for a - * configuration option. - * range -- A reference to a constant - * struct - * Configuration::IntegerRange. - * show_line_number_if_error -- If the value is not within the - * specified range, this - * parameter determines whether a - * configuration file line number - * is shown together with the - * error message. - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ - if (value < range.min || value > range.max) - throw Configuration::ConfigurationException - ((show_line_number_if_error ? config_file_line_number : -1), - range.error_message); -} - -/* ========================================================================= */ -void checkProbability(double value, bool show_line_number_if_error = true) -/* ---------------------------------------------------------------------------- - * - * Description: Checks whether a probability value specified in the program - * configuration is between 0.0 and 1.0 inclusive. - * - * Argument: value -- A value supposed to denote a - * probability. - * show_line_number_if_error -- If the value is not within the - * specified range, this - * parameter determines whether a - * configuration file line number - * is shown together with the - * error message. - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ - if (value < 0.0 || value > 1.0) - throw Configuration::ConfigurationException - ((show_line_number_if_error ? config_file_line_number : -1), - "probability must be between 0.0 and 1.0 inclusive"); -} - -/* ========================================================================= */ -static inline bool isLocked(int option) -/* ---------------------------------------------------------------------------- - * - * Description: Checks whether the value of a configuration option can be - * initialized from the configuration file. (This should not be - * done if the option was present on the program command line.) - * - * Argument: option -- The command line option. - * - * Returns: `true' if the value of the option has been overridden in the - * command line. - * - * ------------------------------------------------------------------------- */ -{ - return (parser_cfg->locked_options.find(make_pair(current_block_type, - option)) - != parser_cfg->locked_options.end()); -} - %} @@ -286,39 +157,19 @@ static inline bool isLocked(int option) * *****************************************************************************/ -/* Data types for configuration file option values. */ +/* Data type for configuration file option values. */ %union { - bool truth_value; - - long int integer; - - Configuration::InteractionMode interactivity_value; - - Configuration::FormulaMode formula_mode_value; - - Configuration::StateSpaceMode statespace_mode_value; - - Configuration::ProductMode product_type_value; - - double real; - - string *str; - - struct - { - long int min; - long int max; - } integer_interval; + const char* value; } /* Keywords. */ %token CFG_ALGORITHM CFG_ENABLED CFG_NAME CFG_PARAMETERS CFG_PROGRAMPATH -%token CFG_GLOBALOPTIONS CFG_COMPARISONTEST CFG_CONSISTENCYTEST - CFG_INTERACTIVE CFG_INTERSECTIONTEST CFG_MODELCHECK CFG_ROUNDS +%token CFG_GLOBALOPTIONS CFG_COMPARISONTEST CFG_CONSISTENCYTEST CFG_INTERACTIVE + CFG_INTERSECTIONTEST CFG_MODELCHECK CFG_ROUNDS CFG_TRANSLATORTIMEOUT CFG_VERBOSITY %token CFG_STATESPACEOPTIONS CFG_EDGEPROBABILITY CFG_PROPOSITIONS CFG_SIZE @@ -332,44 +183,19 @@ static inline bool isLocked(int option) CFG_RELEASEPRIORITY CFG_STRONGRELEASEPRIORITY CFG_TRUEPRIORITY CFG_UNTILPRIORITY CFG_WEAKUNTILPRIORITY CFG_XORPRIORITY -/* Boolean constants. */ - -%token CFG_TRUTH_VALUE - -/* Interactivity mode constants. */ - -%token CFG_INTERACTIVITY_VALUE - -/* Formula output/generation mode constants. */ - -%token CFG_FORMULA_MODE_VALUE - -/* Statespace generation mode constants. */ - -%token CFG_STATESPACE_MODE_VALUE - -/* Constants controlling the product space computation. */ - -%token CFG_PRODUCT_TYPE_VALUE - -/* Numbers. */ - -%token CFG_INTEGER -%token CFG_REAL - -/* Intervals of integers. */ - -%token CFG_INTEGER_INTERVAL - -/* String constants. */ - -%token CFG_STRING_CONSTANT - /* Punctuation symbols. */ %token CFG_LBRACE CFG_RBRACE CFG_EQUALS + +/* Block and option identifiers. */ + %token CFG_BLOCK_ID CFG_OPTION_ID +/* Uninterpreted option values. */ + +%token CFG_VALUE +%type equals_value + /* The `unknown' token. */ %token CFG_UNKNOWN @@ -387,11 +213,16 @@ static inline bool isLocked(int option) configuration_file: configuration_blocks ; +equals_value: { expected_token = CFG_EQUALS; } + CFG_EQUALS + { expected_token = CFG_VALUE; } + CFG_VALUE + { $$ = $4; } + ; + configuration_blocks: /* empty */ | configuration_blocks - { - expected_token = CFG_BLOCK_ID; - } + { expected_token = CFG_BLOCK_ID; } configuration_block ; @@ -403,947 +234,398 @@ configuration_block: algorithm_option_block algorithm_option_block: CFG_ALGORITHM { - current_block_type = CFG_ALGORITHM; - + algorithm_name = ""; + algorithm_path = ""; + algorithm_parameters = ""; + algorithm_enabled = true; algorithm_begin_line = config_file_line_number; - - algorithm_information.name = 0; - algorithm_information.path_to_program = 0; - algorithm_information.extra_parameters = 0; - algorithm_information.enabled = true; - expected_token = CFG_LBRACE; } CFG_LBRACE algorithm_options CFG_RBRACE { - if (algorithm_information.name == 0) - { - algorithm_information.name = - new string("Algorithm "); - algorithm_information.name->append - (::StringUtil::toString - (parser_cfg->algorithms.size())); - } - parser_cfg->algorithms.push_back - (algorithm_information); - if (algorithm_information.path_to_program == 0) - { - throw Configuration::ConfigurationException - (::StringUtil::toString - (algorithm_begin_line) - + "--" - + ::StringUtil::toString - (config_file_line_number), - "missing path to executable (`" - + *(algorithm_information.name) - + "')"); - } + parser_cfg->registerAlgorithm + (algorithm_name, algorithm_path, + algorithm_parameters, algorithm_enabled, + algorithm_begin_line); } ; algorithm_options: /* empty */ | algorithm_options - { - expected_token = CFG_OPTION_ID; - } + { expected_token = CFG_OPTION_ID; } algorithm_option ; -algorithm_option: CFG_ENABLED +algorithm_option: CFG_ENABLED equals_value { - current_option_type = CFG_ENABLED; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_TRUTH_VALUE; - } - CFG_TRUTH_VALUE - { - algorithm_information.enabled = $5; - } - - | CFG_NAME - { - current_option_type = CFG_NAME; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_STRING_CONSTANT; - } - CFG_STRING_CONSTANT - { - algorithm_information.name = $5; + parser_cfg->readTruthValue + (algorithm_enabled, + $2); } - | CFG_PARAMETERS + | CFG_NAME equals_value + { algorithm_name = unquoteString($2); } + + | CFG_PARAMETERS equals_value { - current_option_type = CFG_PARAMETERS; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_STRING_CONSTANT; - } - CFG_STRING_CONSTANT - { - algorithm_information.extra_parameters = $5; + algorithm_parameters = unquoteString($2); } - | CFG_PROGRAMPATH - { - current_option_type = CFG_PROGRAMPATH; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_STRING_CONSTANT; - } - CFG_STRING_CONSTANT - { - algorithm_information.path_to_program = $5; - } + | CFG_PROGRAMPATH equals_value + { algorithm_path = unquoteString($2); } ; global_option_block: CFG_GLOBALOPTIONS - { - current_block_type = CFG_GLOBALOPTIONS; - expected_token = CFG_LBRACE; - } + { expected_token = CFG_LBRACE; } CFG_LBRACE global_options CFG_RBRACE ; global_options: /* empty */ | global_options - { - expected_token = CFG_OPTION_ID; - } + { expected_token = CFG_OPTION_ID; } global_option ; -global_option: CFG_COMPARISONTEST +global_option: CFG_COMPARISONTEST equals_value { - current_option_type = CFG_COMPARISONTEST; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_TRUTH_VALUE; - } - CFG_TRUTH_VALUE - { - if (!isLocked(CFG_COMPARISONTEST)) - parser_cfg->global_options.do_comp_test = $5; - } - - | CFG_CONSISTENCYTEST - { - current_option_type = CFG_CONSISTENCYTEST; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_TRUTH_VALUE; - } - CFG_TRUTH_VALUE - { - if (!isLocked(CFG_CONSISTENCYTEST)) - parser_cfg->global_options.do_cons_test = $5; - } - - | CFG_INTERACTIVE - { - current_option_type = CFG_INTERACTIVE; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTERACTIVITY_VALUE; - } - CFG_INTERACTIVITY_VALUE - { - if (!isLocked(CFG_INTERACTIVE)) - parser_cfg->global_options.interactive = $5; + parser_cfg->readTruthValue + (parser_cfg->global_options. + do_comp_test, + $2); } - | CFG_INTERSECTIONTEST + | CFG_CONSISTENCYTEST equals_value { - current_option_type = CFG_INTERSECTIONTEST; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_TRUTH_VALUE; - } - CFG_TRUTH_VALUE - { - if (!isLocked(CFG_INTERSECTIONTEST)) - parser_cfg->global_options.do_intr_test = $5; + parser_cfg->readTruthValue + (parser_cfg->global_options. + do_cons_test, + $2); } - | CFG_MODELCHECK - { - current_option_type = CFG_MODELCHECK; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_PRODUCT_TYPE_VALUE; - } - CFG_PRODUCT_TYPE_VALUE - { - if (!isLocked(CFG_MODELCHECK)) - parser_cfg->global_options.product_mode = $5; - } + | CFG_INTERACTIVE equals_value + { parser_cfg->readInteractivity($2); } - | CFG_ROUNDS + | CFG_INTERSECTIONTEST equals_value { - current_option_type = CFG_ROUNDS; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_ROUNDS)) - { - checkIntegerRange - ($5, Configuration::ROUND_COUNT_RANGE, - true); - parser_cfg->global_options.number_of_rounds - = $5; - } + parser_cfg->readTruthValue + (parser_cfg->global_options. + do_intr_test, + $2); } - | CFG_VERBOSITY + | CFG_MODELCHECK equals_value + { parser_cfg->readProductType($2); } + + | CFG_ROUNDS equals_value { - current_option_type = CFG_VERBOSITY; - expected_token = CFG_EQUALS; + parser_cfg->readInteger + (parser_cfg->global_options.number_of_rounds, + $2, + Configuration::ROUND_COUNT_RANGE); } - CFG_EQUALS + + | CFG_TRANSLATORTIMEOUT equals_value + { parser_cfg->readTranslatorTimeout($2); } + + | CFG_VERBOSITY equals_value { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_VERBOSITY)) - { - checkIntegerRange - ($5, Configuration::VERBOSITY_RANGE, true); - parser_cfg->global_options.verbosity = $5; - } + parser_cfg->readInteger + (parser_cfg->global_options.verbosity, + $2, + Configuration::VERBOSITY_RANGE); } ; statespace_option_block: CFG_STATESPACEOPTIONS - { - current_block_type = CFG_STATESPACEOPTIONS; - expected_token = CFG_LBRACE; - } + { expected_token = CFG_LBRACE; } CFG_LBRACE statespace_options CFG_RBRACE ; statespace_options: /* empty */ | statespace_options - { - expected_token = CFG_OPTION_ID; - } + { expected_token = CFG_OPTION_ID; } statespace_option ; -statespace_option: CFG_CHANGEINTERVAL +statespace_option: CFG_CHANGEINTERVAL equals_value { - current_option_type = CFG_CHANGEINTERVAL; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_CHANGEINTERVAL)) - { - checkIntegerRange - ($5, Configuration::GENERATION_RANGE, true); - parser_cfg->global_options. - statespace_change_interval = $5; - } + parser_cfg->readInteger + (parser_cfg->global_options. + statespace_change_interval, + $2); } - | CFG_EDGEPROBABILITY + | CFG_EDGEPROBABILITY equals_value { - current_option_type = CFG_EDGEPROBABILITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_REAL; - } - CFG_REAL - { - if (!isLocked(CFG_EDGEPROBABILITY)) - { - checkProbability($5); - parser_cfg->statespace_generator. - edge_probability = $5; - } + parser_cfg->readProbability + (parser_cfg->statespace_generator. + edge_probability, + $2); } - | CFG_GENERATEMODE - { - current_option_type = CFG_GENERATEMODE; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_STATESPACE_MODE_VALUE; - } - CFG_STATESPACE_MODE_VALUE - { - if (!isLocked(CFG_GENERATEMODE)) - parser_cfg->global_options. - statespace_generation_mode = $5; - } + | CFG_GENERATEMODE equals_value + { parser_cfg->readStateSpaceMode($2); } - | CFG_PROPOSITIONS + | CFG_PROPOSITIONS equals_value { - current_option_type = CFG_PROPOSITIONS; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_PROPOSITIONS)) - { - checkIntegerRange - ($5, Configuration::PROPOSITION_COUNT_RANGE, - true); - parser_cfg->statespace_generator. - atoms_per_state = $5; - } + parser_cfg->readInteger + (parser_cfg->statespace_generator. + atoms_per_state, + $2); } - | CFG_RANDOMSEED + | CFG_RANDOMSEED equals_value { - current_option_type = CFG_RANDOMSEED; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_RANDOMSEED)) - parser_cfg->global_options. - statespace_random_seed = $5; + parser_cfg->readInteger + (parser_cfg->global_options. + statespace_random_seed, + $2, + Configuration::RANDOM_SEED_RANGE); } - | CFG_SIZE + | CFG_SIZE equals_value { - current_option_type = CFG_SIZE; - expected_token = CFG_EQUALS; + parser_cfg->readSize + (Configuration::OPT_STATESPACESIZE, + $2); } - CFG_EQUALS - { - expected_token = CFG_INTEGER_INTERVAL; - } - statespace_size - | CFG_TRUTHPROBABILITY + | CFG_TRUTHPROBABILITY equals_value { - current_option_type = CFG_TRUTHPROBABILITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_REAL; - } - CFG_REAL - { - if (!isLocked(CFG_TRUTHPROBABILITY)) - { - checkProbability($5); - parser_cfg->statespace_generator. - truth_probability = $5; - } + parser_cfg->readProbability + (parser_cfg->statespace_generator. + truth_probability, + $2); } ; formula_option_block: CFG_FORMULAOPTIONS - { - current_block_type = CFG_FORMULAOPTIONS; - expected_token = CFG_LBRACE; - } + { expected_token = CFG_LBRACE; } CFG_LBRACE formula_options CFG_RBRACE ; formula_options: /* empty */ | formula_options - { - expected_token = CFG_OPTION_ID; - } + { expected_token = CFG_OPTION_ID; } formula_option ; -formula_option: CFG_ABBREVIATEDOPERATORS +formula_option: CFG_ABBREVIATEDOPERATORS equals_value { - current_option_type = CFG_ABBREVIATEDOPERATORS; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_TRUTH_VALUE; - } - CFG_TRUTH_VALUE - { - if (!isLocked(CFG_ABBREVIATEDOPERATORS)) - parser_cfg->formula_options. - allow_abbreviated_operators = $5; + parser_cfg->readTruthValue + (parser_cfg->formula_options. + allow_abbreviated_operators, + $2); } - | CFG_ANDPRIORITY - { - current_option_type = CFG_ANDPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_ANDPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_CONJUNCTION] = $5; - } + | CFG_ANDPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_CONJUNCTION], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_BEFOREPRIORITY - { - current_option_type = CFG_BEFOREPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_BEFOREPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_BEFORE] = $5; - } + | CFG_BEFOREPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_BEFORE], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_CHANGEINTERVAL + | CFG_CHANGEINTERVAL equals_value { - current_option_type = CFG_CHANGEINTERVAL; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_CHANGEINTERVAL)) - { - checkIntegerRange - ($5, Configuration::GENERATION_RANGE, true); - parser_cfg->global_options. - formula_change_interval = $5; - } + parser_cfg->readInteger + (parser_cfg->global_options. + formula_change_interval, + $2); } - | CFG_DEFAULTOPERATORPRIORITY - { - current_option_type - = CFG_DEFAULTOPERATORPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_DEFAULTOPERATORPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options. - default_operator_priority = $5; - } + | CFG_DEFAULTOPERATORPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options. + default_operator_priority, + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_EQUIVALENCEPRIORITY - { - current_option_type = CFG_EQUIVALENCEPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_EQUIVALENCEPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_EQUIVALENCE] = $5; - } + | CFG_EQUIVALENCEPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_EQUIVALENCE], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_FALSEPRIORITY - { - current_option_type = CFG_FALSEPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_FALSEPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_FALSE] = $5; - } + | CFG_FALSEPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_FALSE], + $2, + Configuration::ATOMIC_PRIORITY_RANGE); } - | CFG_FINALLYPRIORITY - { - current_option_type = CFG_FINALLYPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_FINALLYPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_FINALLY] = $5; - } + | CFG_FINALLYPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_FINALLY], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_GENERATEMODE + | CFG_GENERATEMODE equals_value { - current_option_type = CFG_GENERATEMODE; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_FORMULA_MODE_VALUE; - } - CFG_FORMULA_MODE_VALUE - { - if (!isLocked(CFG_GENERATEMODE)) - parser_cfg->formula_options.generate_mode - = $5; + parser_cfg->readFormulaMode + (parser_cfg->formula_options.generate_mode, + $2); } - | CFG_GLOBALLYPRIORITY - { - current_option_type = CFG_GLOBALLYPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_GLOBALLYPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_GLOBALLY] = $5; - } + | CFG_GLOBALLYPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_GLOBALLY], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_IMPLICATIONPRIORITY - { - current_option_type = CFG_IMPLICATIONPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_IMPLICATIONPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_IMPLICATION] = $5; - } + | CFG_IMPLICATIONPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_IMPLICATION], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_NEXTPRIORITY - { - current_option_type = CFG_NEXTPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_NEXTPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_NEXT] = $5; - } + | CFG_NEXTPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_NEXT], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_NOTPRIORITY - { - current_option_type = CFG_NOTPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_NOTPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_NEGATION] = $5; - } + | CFG_NOTPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_NEGATION], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_ORPRIORITY - { - current_option_type = CFG_ORPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_ORPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_DISJUNCTION] = $5; - } + | CFG_ORPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_DISJUNCTION], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_OUTPUTMODE + | CFG_OUTPUTMODE equals_value { - current_option_type = CFG_OUTPUTMODE; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_FORMULA_MODE_VALUE; - } - CFG_FORMULA_MODE_VALUE - { - if (!isLocked(CFG_OUTPUTMODE)) - parser_cfg->formula_options.output_mode = $5; + parser_cfg->readFormulaMode + (parser_cfg->formula_options.output_mode, + $2); } - | CFG_PROPOSITIONPRIORITY - { - current_option_type = CFG_PROPOSITIONPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_PROPOSITIONPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_ATOM] = $5; - } + | CFG_PROPOSITIONPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_ATOM], + $2, + Configuration::ATOMIC_PRIORITY_RANGE); } - | CFG_PROPOSITIONS + | CFG_PROPOSITIONS equals_value { - current_option_type = CFG_PROPOSITIONS; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_PROPOSITIONS)) - { - checkIntegerRange - ($5, Configuration::PROPOSITION_COUNT_RANGE, - true); - parser_cfg->formula_options. - formula_generator. - number_of_available_variables = $5; - } + parser_cfg->readInteger + (parser_cfg->formula_options. + formula_generator. + number_of_available_variables, + $2); } - | CFG_RANDOMSEED + | CFG_RANDOMSEED equals_value { - current_option_type = CFG_RANDOMSEED; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_RANDOMSEED)) - parser_cfg->global_options.formula_random_seed - = $5; + parser_cfg->readInteger + (parser_cfg->global_options. + formula_random_seed, + $2, + Configuration::RANDOM_SEED_RANGE); } - | CFG_RELEASEPRIORITY - { - current_option_type = CFG_RELEASEPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_RELEASEPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_V] = $5; - } + | CFG_RELEASEPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_V], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_SIZE + | CFG_SIZE equals_value { - current_option_type = CFG_SIZE; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER_INTERVAL; - } - formula_size - - | CFG_STRONGRELEASEPRIORITY - { - current_option_type = CFG_STRONGRELEASEPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_STRONGRELEASEPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_STRONG_RELEASE] = $5; - } + parser_cfg->readSize + (Configuration::OPT_FORMULASIZE, + $2); } - | CFG_TRUEPRIORITY - { - current_option_type = CFG_TRUEPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_TRUEPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_TRUE] = $5; - } + | CFG_STRONGRELEASEPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_STRONG_RELEASE], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_UNTILPRIORITY - { - current_option_type = CFG_UNTILPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_UNTILPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_UNTIL] = $5; - } + | CFG_TRUEPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_TRUE], + $2, + Configuration::ATOMIC_PRIORITY_RANGE); } - | CFG_WEAKUNTILPRIORITY - { - current_option_type = CFG_WEAKUNTILPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_WEAKUNTILPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_WEAK_UNTIL] = $5; - } + | CFG_UNTILPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_UNTIL], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_XORPRIORITY - { - current_option_type = CFG_XORPRIORITY; - expected_token = CFG_EQUALS; - } - CFG_EQUALS - { - expected_token = CFG_INTEGER; - } - CFG_INTEGER - { - if (!isLocked(CFG_XORPRIORITY)) - { - checkIntegerRange - ($5, Configuration::PRIORITY_RANGE, true); - parser_cfg->formula_options.symbol_priority - [::Ltl::LTL_XOR] = $5; - } - } - ; - -formula_size: CFG_INTEGER - { - if (!isLocked(CFG_SIZE)) - { - checkIntegerRange - ($1, Configuration::FORMULA_SIZE_RANGE, - true); - parser_cfg->formula_options.formula_generator. - size = $1; - parser_cfg->formula_options.formula_generator. - max_size = $1; - } + | CFG_WEAKUNTILPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_WEAK_UNTIL], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - | CFG_INTEGER_INTERVAL - { - if (!isLocked(CFG_SIZE)) - { - checkIntegerRange - ($1.min, Configuration::FORMULA_SIZE_RANGE, - true); - - Configuration::IntegerRange max_len_range - (Configuration::FORMULA_MAX_SIZE_RANGE); - - max_len_range.min = $1.min; - - checkIntegerRange($1.max, max_len_range, - true); - - parser_cfg->formula_options.formula_generator. - size = $1.min; - parser_cfg->formula_options.formula_generator. - max_size = $1.max; - } - } - ; - -statespace_size: CFG_INTEGER - { - if (!isLocked(CFG_SIZE)) - { - checkIntegerRange - ($1, Configuration::STATESPACE_SIZE_RANGE, - true); - parser_cfg->statespace_generator.min_size - = $1; - parser_cfg->statespace_generator.max_size - = $1; - } + | CFG_XORPRIORITY equals_value + { + parser_cfg->readInteger + (parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_XOR], + $2, + Configuration::OPERATOR_PRIORITY_RANGE); } - - | CFG_INTEGER_INTERVAL - { - if (!isLocked(CFG_SIZE)) - { - checkIntegerRange - ($1.min, - Configuration::STATESPACE_SIZE_RANGE, - true); - - Configuration::IntegerRange max_size_range - (Configuration::STATESPACE_MAX_SIZE_RANGE); - - checkIntegerRange($1.max, max_size_range, - true); - - parser_cfg->statespace_generator.min_size - = $1.min; - parser_cfg->statespace_generator.max_size - = $1.max; - } - } ; %% @@ -1370,7 +652,7 @@ int parseConfiguration(FILE* stream, Configuration& configuration) * configuration -- A reference to a Configuration object in * which the configuration should be stored. * - * Returns: + * Returns: The result of yyparse() on the file. * * ------------------------------------------------------------------------- */ { diff --git a/lbtt/src/Configuration.cc b/lbtt/src/Configuration.cc index ff4b8fb67..9061e3a1d 100644 --- a/lbtt/src/Configuration.cc +++ b/lbtt/src/Configuration.cc @@ -18,10 +18,11 @@ */ #include -#include +#include #include #include #include "Configuration.h" +#include "IntervalList.h" #ifdef HAVE_GETOPT_LONG #include #define OPTIONSTRUCT struct option @@ -34,67 +35,30 @@ #define getopt_long gnu_getopt_long #endif /* HAVE_GETOPT_LONG */ -/****************************************************************************** - * - * Declarations for functions and variables provided by the parser. - * - *****************************************************************************/ - -#include "Config-parse.h" /* Include declarations for - * the tokens that may be - * present in the - * configuration file. - */ - -extern int parseConfiguration /* Parser interface. */ - (FILE*, Configuration&); - -extern void checkIntegerRange /* Range checking */ - (long int, /* functions. */ - const struct Configuration::IntegerRange&, - bool); - -extern void checkProbability(double, bool); - - - /****************************************************************************** * * Definitions for ranges of certain integer-valued configuration options. * *****************************************************************************/ + +const struct Configuration::IntegerRange Configuration::DEFAULT_RANGE + = {0, ULONG_MAX, "value out of range"}; const struct Configuration::IntegerRange Configuration::VERBOSITY_RANGE = {0, 5, "verbosity must be between 0 and 5 (inclusive)"}; - + const struct Configuration::IntegerRange Configuration::ROUND_COUNT_RANGE - = {1, LONG_MAX, "number of rounds must be positive"}; + = {1, ULONG_MAX, "number of rounds must be positive"}; -const struct Configuration::IntegerRange Configuration::GENERATION_RANGE - = {0, LONG_MAX, "length of interval must be nonnegative"}; - -const struct Configuration::IntegerRange Configuration::PRIORITY_RANGE - = {0, INT_MAX / 14, "priority value out of range"}; - -const struct Configuration::IntegerRange - Configuration::PROPOSITION_COUNT_RANGE - = {0, LONG_MAX, "number of propositions must be nonnegative"}; - -const struct Configuration::IntegerRange Configuration::FORMULA_SIZE_RANGE - = {1, LONG_MAX, "formula size must be always positive"}; - -const struct Configuration::IntegerRange - Configuration::FORMULA_MAX_SIZE_RANGE - = {1, LONG_MAX, "minimum formula size exceeds the maximum formula size"}; - -const struct Configuration::IntegerRange Configuration::STATESPACE_SIZE_RANGE - = {1, LONG_MAX, "state space size must be always positive"}; - -const struct Configuration::IntegerRange - Configuration::STATESPACE_MAX_SIZE_RANGE - = {1, LONG_MAX, "minimum state space size exceeds the maximum state space " - "size"}; +const struct Configuration::IntegerRange Configuration::RANDOM_SEED_RANGE + = {0, UINT_MAX, "random seed out of range"}; + +const struct Configuration::IntegerRange Configuration::ATOMIC_PRIORITY_RANGE + = {0, INT_MAX / 3, "priority out of range"}; +const struct Configuration::IntegerRange Configuration::OPERATOR_PRIORITY_RANGE + = {0, INT_MAX / 14, "priority out of range"}; + /****************************************************************************** @@ -136,9 +100,9 @@ Configuration::~Configuration() ::const_iterator it = algorithms.begin(); it != algorithms.end(); ++it) { - delete it->name; - delete it->path_to_program; - delete it->extra_parameters; + for (vector::size_type p = 0; p <= it->num_parameters; ++p) + delete[] it->parameters[p]; + delete[] it->parameters; } } @@ -160,17 +124,15 @@ void Configuration::read(int argc, char* argv[]) reset(); - /* - * Command line option declarations. - */ + /* Command line option declarations. */ static OPTIONSTRUCT command_line_options[] = { - {"comparisontest", no_argument, 0, OPT_COMPARISONTEST}, - {"comparisoncheck", no_argument, 0, OPT_COMPARISONTEST}, + {"comparisontest", optional_argument, 0, OPT_COMPARISONTEST}, + {"comparisoncheck", optional_argument, 0, OPT_COMPARISONTEST}, {"configfile", required_argument, 0, OPT_CONFIGFILE}, - {"consistencytest", no_argument, 0, OPT_CONSISTENCYTEST}, - {"consistencycheck", no_argument, 0, OPT_CONSISTENCYTEST}, + {"consistencytest", optional_argument, 0, OPT_CONSISTENCYTEST}, + {"consistencycheck", optional_argument, 0, OPT_CONSISTENCYTEST}, {"disable", required_argument, 0, OPT_DISABLE}, {"enable", required_argument, 0, OPT_ENABLE}, {"formulachangeinterval", required_argument, 0, OPT_FORMULACHANGEINTERVAL}, @@ -178,21 +140,19 @@ void Configuration::read(int argc, char* argv[]) {"formularandomseed", required_argument, 0, OPT_FORMULARANDOMSEED}, {"globalmodelcheck", no_argument, 0, OPT_GLOBALPRODUCT}, {"help", no_argument, 0, OPT_HELP}, - {"interactive", required_argument, 0, OPT_INTERACTIVE}, - {"intersectiontest", no_argument, 0, OPT_INTERSECTIONTEST}, - {"intersectioncheck", no_argument, 0, OPT_INTERSECTIONTEST}, + {"interactive", optional_argument, 0, OPT_INTERACTIVE}, + {"intersectiontest", optional_argument, 0, OPT_INTERSECTIONTEST}, + {"intersectioncheck", optional_argument, 0, OPT_INTERSECTIONTEST}, {"localmodelcheck", no_argument, 0, OPT_LOCALPRODUCT}, {"logfile", required_argument, 0, OPT_LOGFILE}, {"modelcheck", required_argument, 0, OPT_MODELCHECK}, - {"nocomparisontest", no_argument, 0, OPT_NOCOMPARISONTEST}, - {"nocomparisoncheck", no_argument, 0, OPT_NOCOMPARISONTEST}, - {"noconsistencytest", no_argument, 0, OPT_NOCONSISTENCYTEST}, - {"noconsistencycheck", no_argument, 0, OPT_NOCONSISTENCYTEST}, - {"nointersectiontest", no_argument, 0, OPT_NOINTERSECTIONTEST}, - {"nointersectioncheck", no_argument, 0, OPT_NOINTERSECTIONTEST}, - {"nopause", no_argument, 0, OPT_NOPAUSE}, - {"pause", no_argument, 0, OPT_PAUSE}, - {"pauseonerror", no_argument, 0, OPT_PAUSEONERROR}, + {"nocomparisontest", no_argument, 0, OPT_COMPARISONTEST}, + {"nocomparisoncheck", no_argument, 0, OPT_COMPARISONTEST}, + {"noconsistencytest", no_argument, 0, OPT_CONSISTENCYTEST}, + {"noconsistencycheck", no_argument, 0, OPT_CONSISTENCYTEST}, + {"nointersectiontest", no_argument, 0, OPT_INTERSECTIONTEST}, + {"nointersectioncheck", no_argument, 0, OPT_INTERSECTIONTEST}, + {"pause", optional_argument, 0, OPT_INTERACTIVE}, {"profile", no_argument, 0, OPT_PROFILE}, {"quiet", no_argument, 0, OPT_QUIET}, {"rounds", required_argument, 0, OPT_ROUNDS}, @@ -203,10 +163,11 @@ void Configuration::read(int argc, char* argv[]) {"statespacechangeinterval", required_argument, 0, OPT_STATESPACECHANGEINTERVAL}, {"statespacerandomseed", required_argument, 0, OPT_STATESPACERANDOMSEED}, + {"translatortimeout", required_argument, 0 ,OPT_TRANSLATORTIMEOUT}, {"verbosity", required_argument, 0, OPT_VERBOSITY}, {"version", no_argument, 0, OPT_VERSION}, - {"abbreviatedoperators", no_argument, 0, OPT_ABBREVIATEDOPERATORS}, + {"abbreviatedoperators", optional_argument, 0, OPT_ABBREVIATEDOPERATORS}, {"andpriority", required_argument, 0, OPT_ANDPRIORITY}, {"beforepriority", required_argument, 0, OPT_BEFOREPRIORITY}, {"defaultoperatorpriority", required_argument, 0, @@ -222,7 +183,7 @@ void Configuration::read(int argc, char* argv[]) {"globallypriority", required_argument, 0, OPT_GLOBALLYPRIORITY}, {"implicationpriority", required_argument, 0, OPT_IMPLICATIONPRIORITY}, {"nextpriority", required_argument, 0, OPT_NEXTPRIORITY}, - {"noabbreviatedoperators", no_argument, 0, OPT_NOABBREVIATEDOPERATORS}, + {"noabbreviatedoperators", no_argument, 0, OPT_ABBREVIATEDOPERATORS}, {"nogeneratennf", no_argument, 0, OPT_NOGENERATENNF}, {"nooutputnnf", no_argument, 0, OPT_NOOUTPUTNNF}, {"notpriority", required_argument, 0, OPT_NOTPRIORITY}, @@ -251,185 +212,52 @@ void Configuration::read(int argc, char* argv[]) {0, 0, 0, 0} }; - opterr = 1; + opterr = 1; /* enable error messages from getopt_long */ + + const char* false_value = "false", *true_value = "true", + *always_value = "always"; int opttype; int option_index; - bool error = false, print_config = false, - print_operator_distribution = false; + bool print_config = false, print_operator_distribution = false; + config_file_line_number = -1; - string enabled_or_disabled_algorithms[2]; - - locked_options.clear(); + typedef pair Parameter; + vector parameters; /* - * Read the command line parameters. + * Preprocess the command line parameters. At this point only those special + * options that do not override settings in the configuration file are + * processed completely; all other options are stored in the vector + * `parameters' to be handled only after reading the configuration file. + * The arguments of all parameters taking optional parameters are + * adjusted here. */ do { option_index = 0; - opttype = getopt_long(argc, argv, "h", command_line_options, + opttype = getopt_long(argc, argv, "hV", command_line_options, &option_index); switch (opttype) { - case OPT_COMPARISONTEST : - case OPT_NOCOMPARISONTEST : - global_options.do_comp_test = (opttype == OPT_COMPARISONTEST); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_COMPARISONTEST)); - break; - case OPT_CONFIGFILE : global_options.cfg_filename = optarg; break; - case OPT_CONSISTENCYTEST : - case OPT_NOCONSISTENCYTEST : - global_options.do_cons_test = (opttype == OPT_CONSISTENCYTEST); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_CONSISTENCYTEST)); - break; - - case OPT_DISABLE : - case OPT_ENABLE : - { - int i = (opttype == OPT_DISABLE ? 1 : 0); - - if (!enabled_or_disabled_algorithms[i].empty()) - enabled_or_disabled_algorithms[i] += ','; - enabled_or_disabled_algorithms[i] += optarg; - } - break; - - case OPT_FORMULACHANGEINTERVAL : - case OPT_STATESPACECHANGEINTERVAL : - { - long int interval_length - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - checkIntegerRange(interval_length, GENERATION_RANGE, false); - - if (opttype == OPT_FORMULACHANGEINTERVAL) - { - global_options.formula_change_interval = interval_length; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_CHANGEINTERVAL)); - } - else - { - global_options.statespace_change_interval = interval_length; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_CHANGEINTERVAL)); - } - } - - break; - case OPT_FORMULAFILE : global_options.formula_input_filename = optarg; break; - case OPT_FORMULARANDOMSEED : - global_options.formula_random_seed - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_RANDOMSEED)); - break; - - case OPT_GLOBALPRODUCT : - global_options.product_mode = GLOBAL; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); - break; - case OPT_HELP : showCommandLineHelp(argv[0]); exit(0); - case OPT_INTERACTIVE : - if (strcmp(optarg, "always") == 0) - global_options.interactive = ALWAYS; - else if (strcmp(optarg, "onerror") == 0) - global_options.interactive = ONERROR; - else if (strcmp(optarg, "never") == 0) - global_options.interactive = NEVER; - else - error = true; - - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); - break; - - case OPT_INTERSECTIONTEST : - case OPT_NOINTERSECTIONTEST : - global_options.do_intr_test = (opttype == OPT_INTERSECTIONTEST); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_INTERSECTIONTEST)); - break; - - case OPT_LOCALPRODUCT : - global_options.product_mode = LOCAL; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); - break; - case OPT_LOGFILE : global_options.transcript_filename = optarg; break; - case OPT_MODELCHECK : - if (strcmp(optarg, "global") == 0) - global_options.product_mode = GLOBAL; - else if (strcmp(optarg, "local") == 0) - global_options.product_mode = LOCAL; - else - error = true; - - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); - break; - - case OPT_PAUSE : - case OPT_NOPAUSE : - global_options.interactive = (opttype == OPT_PAUSE ? ALWAYS : NEVER); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); - break; - - case OPT_PAUSEONERROR : - global_options.interactive = ONERROR; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); - break; - - case OPT_PROFILE : - global_options.do_comp_test - = global_options.do_cons_test - = global_options.do_intr_test - = false; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_COMPARISONTEST)); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_CONSISTENCYTEST)); - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, - CFG_INTERSECTIONTEST)); - break; - - case OPT_QUIET : - global_options.verbosity = 0; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_VERBOSITY)); - global_options.interactive = NEVER; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); - break; - - case OPT_ROUNDS : - { - long int number_of_rounds - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - checkIntegerRange(number_of_rounds, ROUND_COUNT_RANGE, false); - global_options.number_of_rounds = number_of_rounds; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_ROUNDS)); - } - - break; - case OPT_SHOWCONFIG : print_config = true; break; @@ -439,421 +267,69 @@ void Configuration::read(int argc, char* argv[]) break; case OPT_SKIP : - { - long int rounds_to_skip - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - checkIntegerRange(rounds_to_skip, ROUND_COUNT_RANGE, false); - global_options.init_skip = rounds_to_skip; - } - - break; - - case OPT_STATESPACERANDOMSEED : - global_options.statespace_random_seed - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_RANDOMSEED)); - break; - - case OPT_VERBOSITY : - { - long int verbosity - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - checkIntegerRange(verbosity, VERBOSITY_RANGE, false); - global_options.verbosity = verbosity; - locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_VERBOSITY)); - } - + readInteger(global_options.init_skip, optarg); break; case OPT_VERSION : cout << "lbtt " PACKAGE_VERSION "\n" "lbtt is free software; you may change and " - "redistribute it under the terms of\n" + "redistribute it under the terms of\n" "the GNU General Public License. lbtt comes with " "NO WARRANTY. See the file\n" "COPYING for details.\n"; exit(0); break; - case OPT_ABBREVIATEDOPERATORS : - case OPT_NOABBREVIATEDOPERATORS : - formula_options.allow_abbreviated_operators - = (opttype == OPT_ABBREVIATEDOPERATORS); - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_ABBREVIATEDOPERATORS)); - break; - - case OPT_ANDPRIORITY : - case OPT_BEFOREPRIORITY : - case OPT_DEFAULTOPERATORPRIORITY : - case OPT_EQUIVALENCEPRIORITY : - case OPT_FALSEPRIORITY : - case OPT_FINALLYPRIORITY : - case OPT_GLOBALLYPRIORITY : - case OPT_IMPLICATIONPRIORITY : - case OPT_NEXTPRIORITY : - case OPT_NOTPRIORITY : - case OPT_ORPRIORITY : - case OPT_PROPOSITIONPRIORITY : - case OPT_RELEASEPRIORITY : - case OPT_STRONGRELEASEPRIORITY : - case OPT_TRUEPRIORITY : - case OPT_UNTILPRIORITY : - case OPT_WEAKUNTILPRIORITY : - case OPT_XORPRIORITY : - { - long int priority - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - - checkIntegerRange(priority, PRIORITY_RANGE, false); - - int symbol = -1; - - switch (opttype) - { - case OPT_ANDPRIORITY : - symbol = ::Ltl::LTL_CONJUNCTION; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_ANDPRIORITY)); - break; - - case OPT_BEFOREPRIORITY : - symbol = ::Ltl::LTL_BEFORE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_BEFOREPRIORITY)); - break; - - case OPT_DEFAULTOPERATORPRIORITY : - formula_options.default_operator_priority = priority; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_DEFAULTOPERATORPRIORITY)); - break; - - case OPT_EQUIVALENCEPRIORITY : - symbol = ::Ltl::LTL_EQUIVALENCE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_EQUIVALENCEPRIORITY)); - break; - - case OPT_FALSEPRIORITY : - symbol = ::Ltl::LTL_FALSE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_FALSEPRIORITY)); - break; - - case OPT_FINALLYPRIORITY : - symbol = ::Ltl::LTL_FINALLY; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_FINALLYPRIORITY)); - break; - - case OPT_GLOBALLYPRIORITY : - symbol = ::Ltl::LTL_GLOBALLY; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_GLOBALLYPRIORITY)); - break; - - case OPT_IMPLICATIONPRIORITY : - symbol = ::Ltl::LTL_IMPLICATION; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_IMPLICATIONPRIORITY)); - break; - - case OPT_NEXTPRIORITY : - symbol = ::Ltl::LTL_NEXT; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_NEXTPRIORITY)); - break; - - case OPT_NOTPRIORITY : - symbol = ::Ltl::LTL_NEGATION; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_NOTPRIORITY)); - break; - - case OPT_ORPRIORITY : - symbol = ::Ltl::LTL_DISJUNCTION; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_ORPRIORITY)); - break; - - case OPT_PROPOSITIONPRIORITY : - symbol = ::Ltl::LTL_ATOM; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_PROPOSITIONPRIORITY)); - break; - - case OPT_RELEASEPRIORITY : - symbol = ::Ltl::LTL_V; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_RELEASEPRIORITY)); - break; - - case OPT_STRONGRELEASEPRIORITY : - symbol = ::Ltl::LTL_STRONG_RELEASE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_STRONGRELEASEPRIORITY)); - break; - - case OPT_TRUEPRIORITY : - symbol = ::Ltl::LTL_TRUE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_TRUEPRIORITY)); - break; - - case OPT_UNTILPRIORITY : - symbol = ::Ltl::LTL_UNTIL; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_UNTILPRIORITY)); - break; - - case OPT_WEAKUNTILPRIORITY : - symbol = ::Ltl::LTL_WEAK_UNTIL; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_WEAKUNTILPRIORITY)); - break; - - case OPT_XORPRIORITY : - symbol = ::Ltl::LTL_XOR; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_XORPRIORITY)); - - default : - break; - } - - if (symbol != -1) - formula_options.symbol_priority[symbol] = priority; - } - - break; - - case OPT_FORMULAGENERATEMODE : - if (strcmp(optarg, "nnf") == 0) - formula_options.generate_mode = NNF; - else if (strcmp(optarg, "normal") == 0) - formula_options.generate_mode = NORMAL; - else - error = true; - - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_GENERATEMODE)); - break; - - case OPT_FORMULAOUTPUTMODE : - if (strcmp(optarg, "nnf") == 0) - formula_options.output_mode = NNF; - else if (strcmp(optarg, "normal") == 0) - formula_options.output_mode = NORMAL; - else - error = true; - - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_OUTPUTMODE)); - break; - - case OPT_FORMULAPROPOSITIONS : - case OPT_STATESPACEPROPOSITIONS : - { - long int num_propositions - = parseCommandLineInteger - (command_line_options[option_index].name, optarg); - - checkIntegerRange(num_propositions, PROPOSITION_COUNT_RANGE, false); - - if (opttype == OPT_STATESPACEPROPOSITIONS) - { - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_PROPOSITIONS)); - statespace_generator.atoms_per_state = num_propositions; - } - else - { - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, - CFG_PROPOSITIONS)); - formula_options.formula_generator.number_of_available_variables - = num_propositions; - } - } - - break; - - case OPT_FORMULASIZE : - case OPT_STATESPACESIZE : - { - IntegerRange min_size_range, max_size_range; - - if (opttype == OPT_FORMULASIZE) - { - min_size_range = FORMULA_SIZE_RANGE; - max_size_range = FORMULA_MAX_SIZE_RANGE; - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_SIZE)); - } - else - { - min_size_range = STATESPACE_SIZE_RANGE; - max_size_range = STATESPACE_MAX_SIZE_RANGE; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, CFG_SIZE)); - } - - string value(optarg); - string::size_type index = value.find("..."); - - if (index == string::npos) - { - long int size - = parseCommandLineInteger - (command_line_options[option_index].name, value); - - checkIntegerRange(size, min_size_range, false); - - if (opttype == OPT_FORMULASIZE) - formula_options.formula_generator.size - = formula_options.formula_generator.max_size - = size; - else - statespace_generator.min_size - = statespace_generator.max_size - = size; - } - else - { - string option_name(command_line_options[option_index].name); - - long int min = parseCommandLineInteger(option_name + " (min)", - value.substr(0, index)); - long int max = parseCommandLineInteger(option_name + " (max)", - value.substr(index + 3)); - - checkIntegerRange(min, min_size_range, false); - max_size_range.min = min; - checkIntegerRange(max, max_size_range, false); - - if (opttype == OPT_FORMULASIZE) - { - formula_options.formula_generator.size = min; - formula_options.formula_generator.max_size = max; - } - else - { - statespace_generator.min_size = min; - statespace_generator.max_size = max; - } - } - } - - break; - - case OPT_GENERATENNF : - case OPT_NOGENERATENNF : - formula_options.generate_mode - = (opttype == OPT_GENERATENNF ? NNF : NORMAL); - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_GENERATEMODE)); - break; - - case OPT_OUTPUTNNF : - case OPT_NOOUTPUTNNF : - formula_options.output_mode - = (opttype == OPT_OUTPUTNNF ? NNF : NORMAL); - locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_OUTPUTMODE)); - break; - - case OPT_EDGEPROBABILITY : - case OPT_TRUTHPROBABILITY : - { - char* endptr; - double probability = strtod(optarg, &endptr); - - if (*endptr != '\0') - probability = -1.0; - - checkProbability(probability, false); - - if (opttype == OPT_EDGEPROBABILITY) - { - statespace_generator.edge_probability = probability; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_EDGEPROBABILITY)); - } - else - { - statespace_generator.truth_probability = probability; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_TRUTHPROBABILITY)); - } - } - - break; - - case OPT_ENUMERATEDPATH : - global_options.statespace_generation_mode = ENUMERATEDPATH; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_GENERATEMODE)); - break; - - case OPT_RANDOMCONNECTEDGRAPH : - global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_GENERATEMODE)); - break; - - case OPT_RANDOMGRAPH : - global_options.statespace_generation_mode = RANDOMGRAPH; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_GENERATEMODE)); - break; - - case OPT_RANDOMPATH : - global_options.statespace_generation_mode = RANDOMPATH; - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_GENERATEMODE)); - break; - - case OPT_STATESPACEGENERATEMODE : - if (strcmp(optarg, "randomconnectedgraph") == 0) - global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; - else if (strcmp(optarg, "randomgraph") == 0) - global_options.statespace_generation_mode = RANDOMGRAPH; - else if (strcmp(optarg, "randompath") == 0) - global_options.statespace_generation_mode = RANDOMPATH; - else if (strcmp(optarg, "enumeratedpath") == 0) - global_options.statespace_generation_mode = ENUMERATEDPATH; - else - error = true; - - locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, - CFG_GENERATEMODE)); - break; - case '?' : case ':' : - exit(-1); - } + exit(2); - if (error) - throw ConfigurationException - ("", string("unrecognized argument (`") + optarg - + "') for option `--" - + command_line_options[option_index].name + "'"); + case -1 : + break; + + case OPT_CONSISTENCYTEST : + case OPT_COMPARISONTEST : + case OPT_INTERSECTIONTEST : + case OPT_ABBREVIATEDOPERATORS : + { + const char* val; + if (command_line_options[option_index].name[0] == 'n') + val = false_value; + else if (optarg == 0) + val = true_value; + else + val = optarg; + parameters.push_back(make_pair(&command_line_options[option_index], + val)); + break; + } + + case OPT_INTERACTIVE : + { + const char* val; + if (optarg == 0) + val = always_value; + else + val = optarg; + parameters.push_back(make_pair(&command_line_options[option_index], + val)); + break; + } + + default : + parameters.push_back(make_pair(&command_line_options[option_index], + optarg)); + break; + } } while (opttype != -1); - if (optind != argc) - throw ConfigurationException - ("", string("unrecognized command line option `") - + argv[optind] + "'"); - - /* - * Read the configuration file. - */ + /* Read the configuration file. */ FILE* configuration_file = fopen(global_options.cfg_filename.c_str(), "r"); if (configuration_file == NULL) throw ConfigurationException - ("", "error opening configuration file `" + (-1, "error opening configuration file `" + global_options.cfg_filename + "'"); try @@ -867,53 +343,370 @@ void Configuration::read(int argc, char* argv[]) throw; } + config_file_line_number = -1; /* Suppress configuration file line number in + * any future error messages */ + /* - * Use the information gathered from command line options to enable or - * disable some of the implementations. + * Process the command line parameters that override settings made in the + * configuration file. */ - set, ALLOC(unsigned long int) > - algorithm_id_set; + vector::const_iterator parameter; - for (int i = 0; i < 2; i++) + try { - if (enabled_or_disabled_algorithms[i].empty()) - continue; - - try + for (parameter = parameters.begin(); parameter != parameters.end(); + ++parameter) { - parseInterval(enabled_or_disabled_algorithms[i], algorithm_id_set, 0, - algorithms.size() - 1); + switch (parameter->first->val) + { + /* Remaining special options (excluding "--enable" and "--disable"). */ - if (algorithm_id_set.empty()) - throw Exception(); - } - catch (const Exception&) - { - throw ConfigurationException("", - string("invalid argument (`") - + enabled_or_disabled_algorithms[i] - + "') for option `--" - + (i == 0 ? "en" : "dis") - + "able'"); + case OPT_ENABLE : case OPT_DISABLE : /* These options can be */ + break; /* processed only after + * determining whether the + * internal model checking + * algorithm might be + * included in the tests. + */ + + case OPT_PROFILE : + global_options.do_comp_test + = global_options.do_cons_test + = global_options.do_intr_test + = false; + break; + + case OPT_QUIET : + global_options.verbosity = 0; + global_options.interactive = NEVER; + break; + + /* + * Options corresponding to the GlobalOptions section in the + * configuration file. + */ + + case OPT_COMPARISONTEST : + readTruthValue(global_options.do_comp_test, parameter->second); + break; + + case OPT_CONSISTENCYTEST : + readTruthValue(global_options.do_cons_test, parameter->second); + break; + + case OPT_GLOBALPRODUCT : + readProductType("global"); + break; + + case OPT_INTERACTIVE : + readInteractivity(parameter->second); + break; + + case OPT_INTERSECTIONTEST : + readTruthValue(global_options.do_intr_test, parameter->second); + break; + + case OPT_LOCALPRODUCT : + readProductType("local"); + break; + + case OPT_MODELCHECK : + readProductType(parameter->second); + break; + + case OPT_ROUNDS : + readInteger(global_options.number_of_rounds, parameter->second, + ROUND_COUNT_RANGE); + break; + + case OPT_TRANSLATORTIMEOUT : + readTranslatorTimeout(parameter->second); + break; + + case OPT_VERBOSITY : + readInteger(global_options.verbosity, parameter->second, + VERBOSITY_RANGE); + break; + + /* + * Options corresponding to the StatespaceOptions section in the + * configuration file. + */ + + case OPT_EDGEPROBABILITY : + readProbability(statespace_generator.edge_probability, + parameter->second); + break; + + case OPT_ENUMERATEDPATH : + readStateSpaceMode("enumeratedpath"); + break; + + case OPT_RANDOMCONNECTEDGRAPH : + readStateSpaceMode("randomconnectedgraph"); + break; + + case OPT_RANDOMGRAPH : + readStateSpaceMode("randomgraph"); + break; + + case OPT_RANDOMPATH : + readStateSpaceMode("randompath"); + break; + + case OPT_STATESPACECHANGEINTERVAL : + readInteger(global_options.statespace_change_interval, + parameter->second); + break; + + case OPT_STATESPACEGENERATEMODE : + readStateSpaceMode(parameter->second); + break; + + case OPT_STATESPACEPROPOSITIONS : + readInteger(statespace_generator.atoms_per_state, parameter->second); + break; + + case OPT_STATESPACERANDOMSEED : + readInteger(global_options.statespace_random_seed, + parameter->second, RANDOM_SEED_RANGE); + break; + + case OPT_STATESPACESIZE : + readSize(parameter->first->val, parameter->second); + break; + + case OPT_TRUTHPROBABILITY : + readProbability(statespace_generator.truth_probability, + parameter->second); + break; + + /* + * Options corresponding to the FormulaOptions section in the + * configuration file. + */ + + case OPT_ABBREVIATEDOPERATORS : + readTruthValue(formula_options.allow_abbreviated_operators, + parameter->second); + break; + + case OPT_ANDPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_CONJUNCTION], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_BEFOREPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_BEFORE], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_DEFAULTOPERATORPRIORITY : + readInteger(formula_options.default_operator_priority, + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_EQUIVALENCEPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_EQUIVALENCE], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_FALSEPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_FALSE], + parameter->second, ATOMIC_PRIORITY_RANGE); + break; + + case OPT_FINALLYPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_FINALLY], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_FORMULACHANGEINTERVAL : + readInteger(global_options.formula_change_interval, + parameter->second); + break; + + case OPT_FORMULAGENERATEMODE : + readFormulaMode(formula_options.generate_mode, parameter->second); + break; + + case OPT_FORMULAOUTPUTMODE : + readFormulaMode(formula_options.output_mode, parameter->second); + break; + + case OPT_FORMULAPROPOSITIONS : + readInteger(formula_options.formula_generator. + number_of_available_variables, + parameter->second); + break; + + case OPT_FORMULARANDOMSEED : + readInteger(global_options.formula_random_seed, parameter->second, + RANDOM_SEED_RANGE); + break; + + case OPT_FORMULASIZE : + readSize(parameter->first->val, parameter->second); + break; + + case OPT_GENERATENNF : + readFormulaMode(formula_options.generate_mode, "nnf"); + break; + + case OPT_GLOBALLYPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_GLOBALLY], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_IMPLICATIONPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_IMPLICATION], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_NEXTPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_NEXT], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_NOGENERATENNF : + readFormulaMode(formula_options.generate_mode, "normal"); + break; + + case OPT_NOOUTPUTNNF : + readFormulaMode(formula_options.output_mode, "normal"); + break; + + case OPT_NOTPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_NEGATION], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_ORPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_DISJUNCTION], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_OUTPUTNNF : + readFormulaMode(formula_options.output_mode, "nnf"); + break; + + case OPT_PROPOSITIONPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_ATOM], + parameter->second, ATOMIC_PRIORITY_RANGE); + break; + + case OPT_RELEASEPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_V], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_STRONGRELEASEPRIORITY : + readInteger(formula_options.symbol_priority + [::Ltl::LTL_STRONG_RELEASE], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_TRUEPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_TRUE], + parameter->second, ATOMIC_PRIORITY_RANGE); + break; + + case OPT_UNTILPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_UNTIL], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_WEAKUNTILPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_WEAK_UNTIL], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + + case OPT_XORPRIORITY : + readInteger(formula_options.symbol_priority[::Ltl::LTL_XOR], + parameter->second, OPERATOR_PRIORITY_RANGE); + break; + } } - for (set, - ALLOC(unsigned long int) >::const_iterator - id = algorithm_id_set.begin(); - id != algorithm_id_set.end(); - ++id) - { - if (*id >= algorithms.size()) - throw ConfigurationException - ("", - string("invalid implementation identifier (") + toString(*id) - + ") in the argument for `--" - + (i == 0 ? "en" : "dis") - + "able'"); + /* + * If using paths as state spaces, include the internal model checking + * algorithm in the set of algorithms. + */ - algorithms[*id].enabled = (i == 0); + if (global_options.statespace_generation_mode & Configuration::PATH) + { + AlgorithmInformation lbtt_info = {"lbtt", new char*[1], 0, true}; + lbtt_info.parameters[0] = new char[1]; + + algorithm_names["lbtt"] = algorithms.size(); + algorithms.push_back(lbtt_info); } + + /* Process "--enable" and "--disable" options. */ + + for (parameter = parameters.begin(); parameter != parameters.end(); + ++parameter) + { + switch (parameter->first->val) + { + case OPT_DISABLE : + case OPT_ENABLE : + try + { + IntervalList algorithm_ids; + vector nonnumeric_algorithm_ids; + string id_string + = substituteInQuotedString(parameter->second, ",", "\n", + INSIDE_QUOTES); + + parseIntervalList(id_string, algorithm_ids, 0, + algorithms.size() - 1, + &nonnumeric_algorithm_ids); + + for (vector::iterator + id = nonnumeric_algorithm_ids.begin(); + id != nonnumeric_algorithm_ids.end(); + ++id) + { + *id = unquoteString(substituteInQuotedString(*id, "\n", ",")); + map, + ALLOC(unsigned long int) >::const_iterator id_finder + = algorithm_names.find(*id); + if (id_finder == algorithm_names.end()) + throw ConfigurationException + (-1, + string("unknown implementation identifier (`") + + *id + + "')"); + algorithm_ids.merge(id_finder->second); + } + + for (IntervalList::const_iterator id = algorithm_ids.begin(); + id != algorithm_ids.end(); + ++id) + algorithms[*id].enabled = (parameter->first->val == OPT_ENABLE); + } + catch (const IntervalRangeException& e) + { + throw ConfigurationException + (-1, + string("invalid implementation identifier (") + + toString(e.getNumber()) + + ")"); + } + + break; + + default : + break; + } + } + } + catch (ConfigurationException& e) + { + e.changeMessage(string("[--") + parameter->first->name + "]: " + e.what()); + throw e; } /* @@ -929,8 +722,7 @@ void Configuration::read(int argc, char* argv[]) if (global_options.number_of_rounds <= global_options.init_skip) throw ConfigurationException - ("", "the argument for `--skip' must be less than the total " - "number of test rounds"); + (-1, "[--skip]: number of rounds is less than skip count"); /* * Check that there is at least one algorithm available for use. @@ -938,7 +730,7 @@ void Configuration::read(int argc, char* argv[]) if (algorithms.empty()) throw ConfigurationException - ("", "no implementations defined in the configuration file"); + (-1, "no implementations defined in the configuration file"); /* * The case where the number of available variables for propositional @@ -957,8 +749,8 @@ void Configuration::read(int argc, char* argv[]) if (formula_options.symbol_priority[::Ltl::LTL_ATOM] == 0 && formula_options.symbol_priority[::Ltl::LTL_TRUE] == 0 && formula_options.symbol_priority[::Ltl::LTL_FALSE] == 0) - throw ConfigurationException("", "at least one atomic symbol must have " - "nonzero priority"); + throw ConfigurationException(-1, "at least one atomic symbol should have " + "a nonzero priority"); /* * If the operators ->, <->, xor, <>, [], W and M are disallowed, set their @@ -1000,7 +792,7 @@ void Configuration::read(int argc, char* argv[]) } if (!unary_operator_allowed) - throw ConfigurationException("", "at least one unary operator must have " + throw ConfigurationException(-1, "at least one unary operator should have " "a nonzero priority"); /* @@ -1175,6 +967,10 @@ void Configuration::print(ostream& stream, int indent) const estream << "Testing will be interrupted in case of an error.\n"; else estream << "Running in batch mode.\n"; + estream << string(indent + 2, ' ') + + "Signalling a break will " + + (global_options.handle_breaks ? "interrupt" : "abort") + + " testing.\n"; estream << string(indent + 2, ' ') + "Using " @@ -1206,6 +1002,43 @@ void Configuration::print(ostream& stream, int indent) const algorithm_number++; } + estream << '\n' + string(indent + 2, ' '); + if (global_options.translator_timeout > 0) + { + estream << "Timeout for translators is set to " + + toString(global_options.translator_timeout) + + " seconds"; + if (global_options.translator_timeout >= 60) + { + bool first_printed = false; + estream << " ("; + if (global_options.translator_timeout >= 3600) + { + first_printed = true; + estream << toString(global_options.translator_timeout / 3600) + " h"; + } + if (global_options.translator_timeout % 3600 > 60) + { + if (first_printed) + estream << ' '; + else + first_printed = true; + estream << toString((global_options.translator_timeout % 3600) / 60) + + " min"; + } + if (global_options.translator_timeout % 60 != 0) + { + if (first_printed) + estream << ' '; + estream << toString(global_options.translator_timeout % 60) + " s"; + } + estream << ')'; + } + estream << ".\n"; + } + else + estream << "Translators are allowed to run until their termination.\n"; + estream << '\n' + string(indent + 2, ' '); if (global_options.do_comp_test || global_options.do_cons_test @@ -1521,10 +1354,8 @@ string Configuration::algorithmString * * ------------------------------------------------------------------------- */ { - using namespace ::StringUtil; - - return toString(algorithm_id) + ": `" + *(algorithms[algorithm_id].name) - + '\''; + using ::StringUtil::toString; + return toString(algorithm_id) + ": `" + algorithms[algorithm_id].name + '\''; } /* ========================================================================= */ @@ -1541,11 +1372,13 @@ void Configuration::showCommandLineHelp(const char* program_name) { cout << string("Usage: ") + program_name + " [OPTION]...\n\nGeneral options:\n" - " --[no]comparisontest Enable or disable the model " + " --comparisontest[=VALUE], --nocomparisontest\n" + " Enable or disable the model " "checking result\n" " cross-comparison test\n" " --configfile=FILE Read configuration from FILE\n" - " --[no]consistencytest Enable or disable the model " + " --consistencytest[=VALUE], --noconsistencytest\n" + " Enable or disable the model " "checking result\n" " consistency test\n" " --disable=IMPLEMENTATION-ID[,IMPLEMENTATION-ID...]\n" @@ -1560,10 +1393,12 @@ void Configuration::showCommandLineHelp(const char* program_name) " (equivalent to " "`--modelcheck=global')\n" " -h, --help Show this help and exit\n" - " --interactive=MODE Set the interactivity mode " + " --interactive[=MODE[,MODE]], --pause[=MODE[,MODE]]\n" + " Set the interactivity mode " "(`always', `onerror', \n" - " `never')\n" - " --[no]intersectiontest Enable or disable the Büchi " + " `never', `onbreak')\n" + " --intersectiontest[=VALUE], --nointersectiontest\n" + " Enable or disable the Büchi " "automata\n" " intersection emptiness test\n" " --localmodelcheck Use local model checking in tests" @@ -1573,21 +1408,10 @@ void Configuration::showCommandLineHelp(const char* program_name) " --logfile=FILE Write error log to FILE\n" " --modelcheck=MODE Set model checking mode " "(`global' or `local')\n" - " --nopause Do not pause between test rounds " - "(equivalent to\n" - " `--interactive=never')\n" - " --pause Pause unconditionally after every " - "test round\n" - " (equivalent to " - "`--interactive=always')\n" - " --pauseonerror Pause between test rounds only in " - "case of an\n" - " error (equivalent to " - "`--interactive=onerror')\n" " --profile Disable all automata correctness " "tests\n" " --quiet, --silent Run all tests silently without " - "interruption\n" + "pausing\n" " --rounds=NUMBER-OF-ROUNDS Set number of test rounds (1-)\n" " --showconfig Display current configuration and " "exit\n" @@ -1597,13 +1421,15 @@ void Configuration::showCommandLineHelp(const char* program_name) " --skip=NUMBER-OF-ROUNDS Set number of test rounds to skip " "before\n" " starting tests\n" + " --translatortimeout=TIME Set timeout for translators\n" " --verbosity=INTEGER Set the verbosity of output (0-5)\n" - " --version Display program version and exit" + " -V,--version Display program version and exit" "\n\n" "LTL formula generation options:\n" - " --[no]abbreviatedoperators Allow or disallow operators ->, " - "<->, xor, <>,\n" - " [], u, w in the generated " + " --abbreviatedoperators[=VALUE], --noabbreviatedoperators\n" + " Allow or disallow operators ->, " + "<->, xor, <>, [],\n" + " W, M, and B in the generated " "formulas\n" " --andpriority=INTEGER Set priority for the /\\ operator\n" " --beforepriority=INTEGER Set priority for the Before " @@ -1735,6 +1561,7 @@ void Configuration::reset() { global_options.verbosity = 3; global_options.interactive = ALWAYS; + global_options.handle_breaks = false; global_options.number_of_rounds = 10; global_options.init_skip = 0; global_options.statespace_change_interval = 1; @@ -1749,6 +1576,7 @@ void Configuration::reset() global_options.do_intr_test = true; global_options.statespace_random_seed = 1; global_options.formula_random_seed = 1; + global_options.translator_timeout = 0; formula_options.default_operator_priority = 0; formula_options.symbol_priority.clear(); @@ -1779,40 +1607,418 @@ void Configuration::reset() formula_options.formula_generator.reset(); statespace_generator.reset(); - - locked_options.clear(); } /* ========================================================================= */ -long int Configuration::parseCommandLineInteger - (const string& option, const string& value) const +void Configuration::registerAlgorithm + (const string& name, const string& path, const string& parameters, + bool enabled, const int block_begin_line) /* ---------------------------------------------------------------------------- * - * Description: Converts an integer (given as a string) into a long int. Used - * when processing command line parameters. + * Description: Adds a new implementation to the configuration. * - * Arguments: option -- A reference to a constant string giving a name of - * a command line option. - * value -- A reference to a string which is supposed to - * contain an integer. + * Arguments: name -- Name of the implementation. If empty, + * the implementation will be given the + * name `Algorithm n', where n is the + * number of previously registered + * algorithms. The name "lbtt" is + * reserved and cannot be used as a name + * for an implementation. In addition, + * `name' should be unique among the set + * of the names of previously registered + * implementations. + * path -- Path to the executable file used for + * invoking the implementation. This + * string should not be empty. + * parameters -- Parameters for the implementation. + * Parameters containing white space + * should be quoted. + * enabled -- Whether the implementation is initially + * enabled. + * block_begin_line -- Number of the first line of the most + * recently encountered Algorithm block in + * the configuration file. * - * Returns: The value of the integer. + * Returns: Nothing. The function throws a ConfigurationException if + * `name' or `path' fails to satisfy one of the above + * requirements. + * + * ------------------------------------------------------------------------- */ +{ + using namespace ::StringUtil; + string error; + + AlgorithmInformation algorithm_information; + + if (!name.empty()) + algorithm_information.name = name; + else + algorithm_information.name = "Algorithm " + toString(algorithms.size()); + + if (algorithm_information.name == "lbtt") + error = "`lbtt' is a reserved name for an implementation"; + else if (algorithm_names.find(algorithm_information.name) + != algorithm_names.end()) + error = "multiple definitions for implementation `" + + algorithm_information.name + "'"; + else if (path.empty()) + error = "missing path to executable for implementation `" + + algorithm_information.name + "'"; + + if (!error.empty()) + throw ConfigurationException + (toString(block_begin_line) + + (config_file_line_number > block_begin_line + ? "-" + toString(config_file_line_number) + : string("")), + error); + + vector params; + sliceString(unquoteString(substituteInQuotedString(parameters, " \t", "\n\n", + OUTSIDE_QUOTES)), + "\n", + params); + + /* + * Initialize the parameter array for the implementation. This array is + * arranged into a standard argv-style array of C-style strings (ready to be + * used as a parameter for one of the exec functions) and has the following + * structure: + * Index Description + * 0 -- Path to the executable for invoking the + * implementation (obtained from `path'). + * 1...params.size() -- Optional parameters (obtained from the + * `params' vector). + * params.size() + 1, -- Reserved for storing the input and output + * params.size() + 2 file names given as the last two parameters + * for the implementation. + * params.size() + 3 -- A 0 pointer terminating the parameter list. + */ + + algorithm_information.parameters = new char*[params.size() + 4]; + algorithm_information.num_parameters = params.size(); + + algorithm_information.parameters[0] = new char[path.size() + 1]; + memcpy(static_cast(algorithm_information.parameters[0]), + static_cast(path.c_str()), path.size() + 1); + + for (vector::size_type p = 0; + p < algorithm_information.num_parameters; + ++p) + { + algorithm_information.parameters[p + 1] = new char[params[p].size() + 1]; + memcpy(static_cast(algorithm_information.parameters[p + 1]), + static_cast(params[p].c_str()), params[p].size() + 1); + } + + algorithm_information.parameters + [algorithm_information.num_parameters + 3] = 0; + + algorithm_information.enabled = enabled; + + algorithm_names[algorithm_information.name] = algorithms.size(); + algorithms.push_back(algorithm_information); +} + +/* ========================================================================= */ +void Configuration::readProbability(double& target, const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Reads a probability and stores it into `target'. + * + * Arguments: target -- A reference to a double for storing the result. + * value -- The probability as a string. + * + * Returns: Nothing; the result is stored into `target'. The function + * throws a ConfigurationException if `value' is not a valid + * probability (a number between 0.0 and 1.0). * * ------------------------------------------------------------------------- */ { char* endptr; - long int val = strtol(value.c_str(), &endptr, 10); + string error; - if (*endptr != '\0' || value.empty()) + target = strtod(value.c_str(), &endptr); + if (*endptr != '\0') + error = "`" + value + "' is not a valid real number"; + else if (target < 0.0 || target > 1.0) + error = "probability must be between 0.0 and 1.0 (inclusive)"; + + if (!error.empty()) + throw ConfigurationException(config_file_line_number, error); +} + +/* ========================================================================= */ +void Configuration::readSize(int valtype, const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes formula or state space size ranges from `value'. + * + * Arguments: valtype -- If == OPT_STATESPACESIZE, store the result in + * `this->statespace_generator.min_size' and + * `this->statespace_generator.max_size'; otherwise + * store the result in + * `this->formula_options.formula_generator.size' + * and + * `this->formula_options.formula_generator. + * max_size'. + * value -- Size range as a string (a single integer or a + * closed integer interval). + * + * Returns: Nothing; the result is stored into the Configuration object. + * The function throws a ConfigurationException if `value' is + * not a valid positive integer or a closed nonempty integer + * interval. + * + * ------------------------------------------------------------------------- */ +{ + string error; + unsigned long int min, max; + + try + { + int interval_type = ::StringUtil::parseInterval(value, min, max); + if (!(interval_type & ::StringUtil::LEFT_BOUNDED) || + !(interval_type & ::StringUtil::RIGHT_BOUNDED)) + throw Exception(); + + if (min < 1) + { + if (valtype == OPT_STATESPACESIZE) + error = "state space size must be positive"; + else + error = "formula size must be positive"; + } + else if (min > max) + { + if (valtype == OPT_STATESPACESIZE) + error = "minimum state space size exceeds maximum state space size"; + else + error = "minimum formula size exceeds maximum formula size"; + } + } + catch (const Exception&) + { + error = "`" + value + "' is neither a valid positive integer nor a closed " + "integer interval"; + } + + if (!error.empty()) + throw ConfigurationException(config_file_line_number, error); + + if (valtype == OPT_STATESPACESIZE) + { + statespace_generator.min_size = min; + statespace_generator.max_size = max; + } + else + { + formula_options.formula_generator.size = min; + formula_options.formula_generator.max_size = max; + } +} + +/* ========================================================================= */ +void Configuration::readTruthValue(bool& target, const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Interprets a symbolic truth value and stores it into + * `target'. + * + * Arguments: target -- A reference to a Boolean variable whose value + * should be set according to the given value. + * value -- The symbolic truth value. + * + * Returns: Nothing; the interpreted value is stored in `target'. If + * `value' is not a valid truth value, the function throws a + * ConfigurationException. + * + * ------------------------------------------------------------------------- */ +{ + const string value_in_lower_case = ::StringUtil::toLowerCase(value); + + if (value_in_lower_case == "yes" || value_in_lower_case == "true") + target = true; + else if (value_in_lower_case == "no" || value_in_lower_case == "false") + target = false; + else throw ConfigurationException - ("", "the argument for `--" + option + "' must be a nonnegative " - "integer"); + (config_file_line_number, + "`" + value + "' is not a valid truth value"); +} - if (val == LONG_MIN || val == LONG_MAX) +/* ========================================================================= */ +void Configuration::readInteractivity(const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Interprets a symbolic list of interactivity modes and updates + * `this->global_options.interactive' and + * `this->global_options.handle_breaks' accordingly. + * + * Argument: value -- The symbolic mode (a comma-separated list of + * "always", "onerror", "never" or "onbreak"; the + * case is not relevant). + * + * Returns: Nothing; the result is stored in + * `this->global_options.interactive' and/or + * `this->global_options.handle_breaks'. The function throws a + * ConfigurationException is `value' is not a valid + * interactivity mode. + * + * ------------------------------------------------------------------------- */ +{ + /* + * Reset the interactivity mode to NEVER and disable break handling to allow + * the interactivity specification to be interpreted correctly. + */ + + global_options.interactive = NEVER; + global_options.handle_breaks = false; + + vector modes; + ::StringUtil::sliceString(value, ",", modes); + for (vector::const_iterator mode = modes.begin(); + mode != modes.end(); + ++mode) + { + string mode_in_lower_case = ::StringUtil::toLowerCase(*mode); + + if (mode_in_lower_case == "always") + global_options.interactive = ALWAYS; + else if (mode_in_lower_case == "onerror") + global_options.interactive = ONERROR; + else if (mode_in_lower_case == "never") + global_options.interactive = NEVER; + else if (mode_in_lower_case == "onbreak") + global_options.handle_breaks = true; + else + throw ConfigurationException + (config_file_line_number, + "`" + *mode + "' is not a valid interactivity mode"); + } +} + +/* ========================================================================= */ +void Configuration::readProductType(const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Interprets a symbolic model checking mode and updates + * `this->global_options.product_mode' accordingly. + * + * Argument: value -- The symbolic mode (one of "local" or "global"; the + * case of characters is not relevant). + * + * Returns: Nothing; the result is stored in + * `this->global_options.product_mode'. The function throws a + * ConfigurationException is `value' is not a valid model + * checking mode. + * + * ------------------------------------------------------------------------- */ +{ + const string value_in_lower_case = ::StringUtil::toLowerCase(value); + + if (value_in_lower_case == "global") + global_options.product_mode = GLOBAL; + else if (value_in_lower_case == "local") + global_options.product_mode = LOCAL; + else throw ConfigurationException - ("", "the argument for `--" + option + "' is out of range"); + (config_file_line_number, + "`" + value + "' is not a valid model checking mode"); +} - return val; +/* ========================================================================= */ +void Configuration::readFormulaMode(FormulaMode& target, const string& mode) +/* ---------------------------------------------------------------------------- + * + * Description: Interprets a symbolic formula mode and updates `target' + * accordingly. + * + * Argument: mode -- Symbolic formula mode (one of "normal" or "nnf"; + * the case of characters is not relevant). + * + * Returns: Nothing; the result is stored into `target'. The function + * throws a ConfigurationException if `mode' is not a valid mode + * string. + * + * ------------------------------------------------------------------------- */ +{ + const string mode_in_lower_case = ::StringUtil::toLowerCase(mode); + + if (mode_in_lower_case == "nnf") + target = NNF; + else if (mode_in_lower_case == "normal") + target = NORMAL; + else + throw ConfigurationException + (config_file_line_number, + "`" + mode + "' is not a valid formula mode"); +} + +/* ========================================================================= */ +void Configuration::readStateSpaceMode(const string& mode) +/* ---------------------------------------------------------------------------- + * + * Description: Interprets a symbolic state space generation mode and updates + * `global_options.statespace_generation_mode' accordingly. + * + * Argument: mode -- Symbolic state space generation mode (one of + * "randomconnectedgraph", "randomgraph", "randompath" + * and "enumeratedpath"; the case of characters is + * not relevant). + * + * Returns: Nothing; the result is stored into + * `global_options.statespace_generation_mode'. The function + * throws a ConfigurationException if `mode' is not one of the + * above keywords. + * + * ------------------------------------------------------------------------- */ +{ + const string mode_in_lower_case = ::StringUtil::toLowerCase(mode); + + if (mode_in_lower_case == "randomconnectedgraph") + global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; + else if (mode_in_lower_case == "randomgraph") + global_options.statespace_generation_mode = RANDOMGRAPH; + else if (mode_in_lower_case == "randompath") + global_options.statespace_generation_mode = RANDOMPATH; + else if (mode_in_lower_case == "enumeratedpath") + global_options.statespace_generation_mode = ENUMERATEDPATH; + else + throw ConfigurationException + (config_file_line_number, + "`" + mode + "' is not a valid state space generation mode"); +} + +/* ========================================================================= */ +void Configuration::readTranslatorTimeout(const string& value) +/* ---------------------------------------------------------------------------- + * + * Description: Reads a time specification from a string into + * `this->global_options.translator_timeout'. + * + * Argument: value -- A time specification in the format expected by + * ::StringUtil::parseTime. + * + * Returns: Nothing; the result is stored into + * `this->global_options.translator_timeout'. The function + * throws a ConfigurationException if `value' is not a valid + * time specification. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int hours, minutes, seconds; + try + { + ::StringUtil::parseTime(value, hours, minutes, seconds); + } + catch (const Exception&) + { + throw ConfigurationException + (config_file_line_number, + "`" + value + "' is not a valid time specification"); + } + global_options.translator_timeout = (hours * 60 + minutes) * 60 + seconds; } /* ========================================================================= */ diff --git a/lbtt/src/Configuration.h b/lbtt/src/Configuration.h index bdfeda739..8d491eb8f 100644 --- a/lbtt/src/Configuration.h +++ b/lbtt/src/Configuration.h @@ -21,6 +21,7 @@ #define CONFIGURATION_H #include +#include #include #include #include @@ -35,8 +36,6 @@ using namespace std; - - /****************************************************************************** * * A class for storing program configuration information. @@ -69,15 +68,23 @@ public: * string. */ + bool isInternalAlgorithm(unsigned long int id) /* Tests whether a given */ + const; /* algorithm identifier + * refers to lbtt's + * internal model + * checking algorithm. + */ + static void showCommandLineHelp /* Prints the list of */ (const char* program_name); /* command line options. */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - enum InteractionMode {NEVER, ALWAYS, ONERROR}; /* Enumeration constants - * affecting the behaviour - * of the program as - * regards user control. + enum InteractionMode {NEVER, ALWAYS, ONERROR, /* Enumeration constants */ + ONBREAK}; /* affecting the + * behavior of the + * program as regards + * user control. */ enum FormulaMode {NORMAL, NNF}; /* Enumeration constants @@ -109,19 +116,23 @@ public: * parameters). */ { - string* name; /* Name of the algorithm. + string name; /* Name of the algorithm. */ - string* path_to_program; /* Path to the executable + char** parameters; /* Command-line parameters * required for running - * the algorithm. - */ - - string* extra_parameters; /* Additional command-line - * parameters required for - * running the executable. + * the executable. See + * the documentation for + * the registerAlgorithm + * function (in + * Configuration.cc) for + * more information. */ + vector::size_type num_parameters; /* Number of command-line + * parameters. + */ + bool enabled; /* Determines whether the * algorithm is enabled * (whether it will be used @@ -169,6 +180,12 @@ public: * commands. */ + bool handle_breaks; /* If true, pause testing + * also on interrupt + * signals instead of + * simply aborting. + */ + unsigned long int number_of_rounds; /* Number of test rounds. */ @@ -278,6 +295,10 @@ public: * formula generation * algorithms. */ + + unsigned int translator_timeout; /* Timeout (in seconds) for + * translators. + */ }; struct FormulaConfiguration /* A structure for storing @@ -360,6 +381,12 @@ public: * the tests. */ + map, /* Mapping between */ + ALLOC(unsigned long int) > /* algorithm names and */ + algorithm_names; /* their numeric + * identifiers. + */ + GlobalConfiguration global_options; /* General configuration * information. */ @@ -376,15 +403,6 @@ public: * algorithms. */ - typedef pair IntPair; - - set, ALLOC(IntPair) > /* Configuration options */ - locked_options; /* the values of which - * should not be - * initialized from the - * configuration file. - */ - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ class ConfigurationException : public Exception /* A class for reporting @@ -419,6 +437,9 @@ public: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +private: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + struct IntegerRange /* Data structure for * representing integer- * valued ranges of certain @@ -426,11 +447,11 @@ public: * options. */ { - long int min; /* Lower bound. */ + unsigned long int min; /* Lower bound. */ - long int max; /* Upper bound. */ + unsigned long int max; /* Upper bound. */ - char* error_message; /* Error message to be + const char* error_message; /* Error message to be * displayed if the value * is not within the * specified range. @@ -439,31 +460,30 @@ public: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - /* - * Ranges for certain integer-valued configuration options. - */ + /* Ranges for certain integer-valued configuration options. */ static const struct IntegerRange - VERBOSITY_RANGE, ROUND_COUNT_RANGE, GENERATION_RANGE, PRIORITY_RANGE, - PROPOSITION_COUNT_RANGE, FORMULA_SIZE_RANGE, FORMULA_MAX_SIZE_RANGE, - STATESPACE_SIZE_RANGE, STATESPACE_MAX_SIZE_RANGE; + DEFAULT_RANGE, VERBOSITY_RANGE, + ROUND_COUNT_RANGE, RANDOM_SEED_RANGE, + ATOMIC_PRIORITY_RANGE, OPERATOR_PRIORITY_RANGE; -private: - enum CommandLineOptionType /* Command line options. */ - {OPT_COMPARISONTEST = 10000, OPT_CONFIGFILE, + /* Command line options. */ + + enum CommandLineOptionType + {OPT_HELP = 'h', OPT_VERSION = 'V', + + OPT_COMPARISONTEST = 10000, OPT_CONFIGFILE, OPT_CONSISTENCYTEST, OPT_DISABLE, OPT_ENABLE, - OPT_FORMULACHANGEINTERVAL, OPT_FORMULAFILE, - OPT_FORMULARANDOMSEED, OPT_HELP = 'h', - OPT_GLOBALPRODUCT = 20000, OPT_INTERACTIVE, + OPT_FORMULACHANGEINTERVAL, + OPT_FORMULAFILE, OPT_FORMULARANDOMSEED, + OPT_GLOBALPRODUCT, OPT_INTERACTIVE, OPT_INTERSECTIONTEST, OPT_LOGFILE, - OPT_MODELCHECK, OPT_NOCOMPARISONTEST, - OPT_NOCONSISTENCYTEST, OPT_NOINTERSECTIONTEST, - OPT_NOPAUSE, OPT_PAUSE, OPT_PAUSEONERROR, - OPT_PROFILE, OPT_QUIET, OPT_ROUNDS, - OPT_SHOWCONFIG, OPT_SHOWOPERATORDISTRIBUTION, - OPT_SKIP, OPT_STATESPACECHANGEINTERVAL, - OPT_STATESPACERANDOMSEED, OPT_VERBOSITY, - OPT_VERSION, + OPT_MODELCHECK, OPT_PROFILE, OPT_QUIET, + OPT_ROUNDS, OPT_SHOWCONFIG, + OPT_SHOWOPERATORDISTRIBUTION, OPT_SKIP, + OPT_STATESPACECHANGEINTERVAL, + OPT_STATESPACERANDOMSEED, + OPT_TRANSLATORTIMEOUT, OPT_VERBOSITY, OPT_LOCALPRODUCT, @@ -476,7 +496,6 @@ private: OPT_FORMULAPROPOSITIONS, OPT_FORMULASIZE, OPT_GENERATENNF, OPT_GLOBALLYPRIORITY, OPT_IMPLICATIONPRIORITY, OPT_NEXTPRIORITY, - OPT_NOABBREVIATEDOPERATORS, OPT_NOGENERATENNF, OPT_NOOUTPUTNNF, OPT_NOTPRIORITY, OPT_ORPRIORITY, OPT_OUTPUTNNF, OPT_PROPOSITIONPRIORITY, @@ -503,6 +522,8 @@ private: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + friend int yyparse(); + Configuration(const Configuration& cfg); /* Prevent copying and */ Configuration& operator= /* assignment of */ (const Configuration& cfg); /* Configuration @@ -514,10 +535,59 @@ private: * to default values. */ - long int parseCommandLineInteger /* Converts an integer */ - (const string& option, const string& value) /* to a string with */ - const; /* some additional - * validity checks. + void registerAlgorithm /* Adds a new algorithm */ + (const string& name, const string& path, /* to the configuration. */ + const string& parameters, bool enabled, + const int block_begin_line); + + template /* Reads an integer, */ + void readInteger /* checks that it is */ + (T& target, const string& value, /* within given bounds */ + const IntegerRange& range = DEFAULT_RANGE); /* and stores it into + * an unsigned integer + * type variable. + */ + + void readProbability /* Reads a probability */ + (double& target, const string& value); /* and stores it into + * a given variable of + * type double. + */ + + void readSize(int valtype, const string& value); /* Initializes formula or + * state space size + * ranges from a given + * string. + */ + + void readTruthValue /* Interprets a symbolic */ + (bool& target, const string& value); /* truth value. */ + + void readInteractivity(const string& value); /* Interprets a symbolic + * interactivity mode. + */ + + void readProductType(const string& value); /* Interprets a symbolic + * model checking mode. + */ + + void readFormulaMode /* Interprets a symbolic */ + (FormulaMode& target, const string& mode); /* formula mode. + */ + + void readStateSpaceMode(const string& mode); /* Initializes + * `global_options. + * statespace_generation + * _mode' from a + * symbolic mode + * identifier. + */ + + void readTranslatorTimeout(const string& value); /* Initializes + * `global_options. + * translator_timeout' + * from a symbolic time + * specification. */ double operatorProbability /* Computes the */ @@ -532,6 +602,97 @@ private: +/****************************************************************************** + * + * Declarations for functions and variables provided by the parser. + * + *****************************************************************************/ + +extern int config_file_line_number; /* Number of the current + * line in the + * configuration file. + */ + +extern int parseConfiguration /* Parser interface. */ + (FILE*, Configuration&); + + + +/****************************************************************************** + * + * Inline function definitions for class Configuration. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool Configuration::isInternalAlgorithm(unsigned long int id) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether a given algorithm identifier refers to lbtt's + * internal model checking algorithm. + * + * Argument: id -- Identifier to test. + * + * Returns: True if `id' is the identifier of lbtt's internal model + * checking algorithm. + * + * ------------------------------------------------------------------------- */ +{ + return ((global_options.statespace_generation_mode & Configuration::PATH) + && id == algorithms.size() - 1); +} + + + +/****************************************************************************** + * + * Template function definitions for class Configuration. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void Configuration::readInteger + (T& target, const string& value, const IntegerRange& range) +/* ---------------------------------------------------------------------------- + * + * Description: Reads an integer and stores it into `target'. + * + * Arguments: target -- A reference to an unsigned integer type variable + * for storing the result. + * value -- The integer as a string. + * range -- A reference to a constant IntegerRange object + * giving the bounds for the value. + * + * Returns: Nothing; the result is stored into `target'. The function + * throws a ConfigurationException if `value' is not a valid + * integer within the bounds specified by `range'. + * + * ------------------------------------------------------------------------- */ +{ + string error; + unsigned long int val; + + try + { + val = ::StringUtil::parseNumber(value); + } + catch (const ::StringUtil::NotANumberException&) + { + error = "`" + value + "' is not a valid nonnegative integer"; + } + + if (error.empty() && (val < range.min || val > range.max)) + error = range.error_message; + + if (!error.empty()) + throw ConfigurationException(config_file_line_number, error); + + target = val; +} + + + /****************************************************************************** * * Inline function definitions for class Configuration::ConfigurationException. diff --git a/lbtt/src/DispUtil.h b/lbtt/src/DispUtil.h index ce3e08141..a7a8bc2f5 100644 --- a/lbtt/src/DispUtil.h +++ b/lbtt/src/DispUtil.h @@ -58,6 +58,16 @@ void printTextBlock /* Writes an indented */ * a stream. */ + bool printText /* "Verbosity-aware" */ + (const char* text, /* functions for writing */ + const int verbosity_threshold, /* text to standard */ + const int indent); /* output. */ + + bool printText + (const string& text, + const int verbosity_threshold, + const int indent); + /****************************************************************************** @@ -78,16 +88,6 @@ struct StreamFormatting * e.g. the justification * of output. */ - - bool printText /* "Verbosity-aware" */ - (const char* text, /* functions for writing */ - const int verbosity_threshold, /* text to standard */ - const int indent); /* output. */ - - bool printText - (const string& text, - const int verbosity_threshold, - const int indent); }; diff --git a/lbtt/src/Exception.h b/lbtt/src/Exception.h index a429704b7..e6a88d98b 100644 --- a/lbtt/src/Exception.h +++ b/lbtt/src/Exception.h @@ -23,6 +23,7 @@ #include #include #include +#include #include using namespace std; @@ -299,6 +300,16 @@ public: template /* Operator for reading */ Exceptional_istream &operator>>(T &t); /* from the stream. */ + Exceptional_istream& get(istream::char_type& C); /* Reads a character from + * the stream. + */ + + Exceptional_istream& read /* Reads a given number */ + (istream::char_type* buffer, streamsize count); /* of characters from + * the stream into a + * buffer. + */ + operator istream&(); /* Casts the exception- * aware input stream into * a regular input stream. @@ -992,6 +1003,47 @@ inline Exceptional_istream::~Exceptional_istream() { } +/* ========================================================================= */ +inline Exceptional_istream& Exceptional_istream::get(istream::char_type& c) +/* ---------------------------------------------------------------------------- + * + * Description: Reads a character from the input stream. + * + * Argument: c -- A reference to a character to extract. + * + * Returns: A reference to the stream. + * + * ------------------------------------------------------------------------- */ +{ + stream->get(c); + if (stream->rdstate() & exception_mask) + throw IOException("error reading from stream"); + + return *this; +} + +/* ========================================================================= */ +inline Exceptional_istream& Exceptional_istream::read + (istream::char_type* buffer, streamsize count) +/* ---------------------------------------------------------------------------- + * + * Description: Reads a given number of characters from the stream into a + * buffer. + * + * Arguments: buffer -- A pointer to the buffer. + * count -- Number of characters to read. + * + * Returns: A reference to the stream. + * + * ------------------------------------------------------------------------- */ +{ + stream->read(buffer, count); + if (stream->rdstate() & exception_mask) + throw IOException("error reading from stream"); + + return *this; +} + /* ========================================================================= */ inline Exceptional_istream::operator istream&() /* ---------------------------------------------------------------------------- diff --git a/lbtt/src/ExternalTranslator.cc b/lbtt/src/ExternalTranslator.cc index adda19ea1..5f8da60ad 100644 --- a/lbtt/src/ExternalTranslator.cc +++ b/lbtt/src/ExternalTranslator.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #ifdef HAVE_FCNTL_H @@ -62,25 +63,30 @@ ExternalTranslator::~ExternalTranslator() } /* ========================================================================= */ -ExternalTranslator::TempFileObject& -ExternalTranslator::registerTempFileObject - (const string& filename, TempFileObject::Type type) +const char* ExternalTranslator::registerTempFileObject + (const string& filename, const TempFsysName::NameType type, + const bool literal) /* ---------------------------------------------------------------------------- * * Description: Registers a temporary file or directory such that it will be * automatically deleted when the ExternalTranslator object is * destroyed. * - * Arguments: filename -- Name of the temporary file or directory. + * Arguments: filename -- Name of the temporary file or directory. If + * empty, a new name will be created. * type -- Type of the object (TempFileObject::FILE or * TempFileObject::DIRECTORY). + * literal -- Tells whether the file name should be + * interpreted literally. * - * Returns: A reference to the file object. + * Returns: Nothing. * * ------------------------------------------------------------------------- */ { - temporary_file_objects.push(new TempFileObject(filename, type)); - return *temporary_file_objects.top(); + TempFsysName* name = new TempFsysName; + name->allocate(filename.c_str(), type, literal); + temporary_file_objects.push(name); + return name->get(); } /* ========================================================================= */ @@ -99,19 +105,19 @@ void ExternalTranslator::translate * * ------------------------------------------------------------------------- */ { - TempFileObject& external_program_input_file = registerTempFileObject(); + const char* external_program_input_file + = registerTempFileObject("lbtt-translate"); - TempFileObject& external_program_output_file = registerTempFileObject(); + const char* external_program_output_file + = registerTempFileObject("lbtt-translate"); string translated_formula; translateFormula(formula, translated_formula); ofstream input_file; - input_file.open(external_program_input_file.getName().c_str(), - ios::out | ios::trunc); + input_file.open(external_program_input_file, ios::out | ios::trunc); if (!input_file.good()) - throw FileCreationException(string("`") - + external_program_input_file.getName() + throw FileCreationException(string("`") + external_program_input_file + "'"); Exceptional_ostream einput_file(&input_file, ios::failbit | ios::badbit); @@ -121,96 +127,11 @@ void ExternalTranslator::translate input_file.close(); string command_line = string(command_line_arguments[2]) - + commandLine(external_program_input_file.getName(), - external_program_output_file.getName()); + + commandLine(external_program_input_file, + external_program_output_file); if (!execSuccess(system(command_line.c_str()))) throw ExecFailedException(command_line_arguments[2]); - parseAutomaton(external_program_output_file.getName(), filename); -} - - - -/****************************************************************************** - * - * Function definitions for class ExternalTranslator::TempFileObject. - * - *****************************************************************************/ - -/* ========================================================================= */ -ExternalTranslator::TempFileObject::TempFileObject - (const string& filename, Type t) -/* ---------------------------------------------------------------------------- - * - * Description: Constructor for class TempFileObject. Creates a temporary - * file or a directory and tests whether it can really be - * written to (if not, a FileCreationException is thrown). - * - * Arguments: filename -- Name of the temporary file or directory. - * If the filename is an empty string, the - * filename is obtained by a call to tmpnam(3). - * t -- Type of the object (TempFileObject::FILE or - * TempFileObject::DIRECTORY). - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ - if (filename.empty()) - { - char tempname[L_tmpnam + 1]; - - if (tmpnam(tempname) == 0) - throw FileCreationException("a temporary file"); - - name = tempname; - } - else - name = filename; - - if (t == FILE) - { - ofstream tempfile; - tempfile.open(name.c_str(), ios::out | ios::trunc); - if (!tempfile.good()) - throw FileCreationException("a temporary file"); - tempfile.close(); - } - else - { - if (mkdir(name.c_str(), 0700) != 0) - throw FileCreationException("a temporary directory"); - } - - type = t; -} - -/* ========================================================================= */ -ExternalTranslator::TempFileObject::~TempFileObject() -/* ---------------------------------------------------------------------------- - * - * Description: Destructor for class TempFileObject. Deletes the file or - * the directory associated with the object (displays a warning - * if this fails). - * - * Arguments: None. - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ - if (remove(name.c_str()) != 0) - { - string msg("error removing temporary "); - - if (type == TempFileObject::FILE) - msg += "file"; - else - msg += "directory"; - - msg += " `" + name + "'"; - - printWarning(msg); - } + parseAutomaton(external_program_output_file, filename); } diff --git a/lbtt/src/ExternalTranslator.h b/lbtt/src/ExternalTranslator.h index 609617591..12171674b 100644 --- a/lbtt/src/ExternalTranslator.h +++ b/lbtt/src/ExternalTranslator.h @@ -33,6 +33,7 @@ #include "Exception.h" #include "LtlFormula.h" #include "translate.h" +#include "TempFsysName.h" #include "TranslatorInterface.h" /****************************************************************************** @@ -98,13 +99,21 @@ * from the names of the input/output files. Each of these files should be * "registered" before calling the external program with the function * - * void registerTempFileObject - * (const string& filename, TempFileObject::Type t) + * const char* registerTempFileObject + * (const string& filename, const TempFsysName::NameType t, + * const bool literal) * - * where `filename' is the full name of the temporary file and `t' is a type - * of the object (TempFileObject::FILE or TempFileObject::DIRECTORY). + * where `filename' is the prefix of a temporary file name, `t' is a type + * of the object (TempFsysName::FILE or TempFsysName::DIRECTORY), and + * `literal' specifies whether `filename' should be interpreted literally or + * not (if not, `filename' will be treated as a suggestion for the name + * of the temporary file). If the name is to be interpreted literally, + * `filename' should contain the full path name of the temporary file to be + * created. In all cases, the function returns the full path name of the + * temporary file or directory, or it throws an IOException (defined in + * Exception.h) if the creation fails. * - * All files or directories registered using this function will then be + * All files or directories registered using this function will be * automatically deleted after the translation is finished or aborted. * The files or directories will be deleted in the reverse order of * registration, i.e., the most recently registered file/directory will be @@ -115,55 +124,15 @@ class ExternalTranslator : public TranslatorInterface { public: - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - class TempFileObject /* A class for storing */ - { /* information about */ - public: /* temporary files and - * directories. - */ - - enum Type {FILE, DIRECTORY}; /* Types for a temporary - * file object. - */ - - TempFileObject /* Constructor. */ - (const string& filename = "", Type t = FILE); - - ~TempFileObject(); /* Destructor. */ - - const string& getName() const; /* Returns the filename - * associated with the - * object. - */ - - Type getType() const; /* Returns the type of - * the object. - */ - - private: - string name; /* Name of the file object. - */ - - Type type; /* Type of the file object. - */ - - TempFileObject(const TempFileObject&); /* Prevent copying and */ - TempFileObject& operator= /* assignment of */ - (const TempFileObject&); /* TempFileObjects. */ - }; - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - ExternalTranslator(); /* Constructor. */ ~ExternalTranslator(); /* Destructor. */ - TempFileObject& registerTempFileObject /* Registers a temporary */ + const char* registerTempFileObject /* Registers a temporary */ (const string& filename = "", /* file or directory */ - TempFileObject::Type /* such that it will be */ - t = TempFileObject::FILE); /* automatically deleted - * when the translation + const TempFsysName::NameType /* such that it will be */ + t = TempFsysName::FILE, /* automatically deleted */ + const bool literal = false); /* when the translation * is complete. */ @@ -205,17 +174,12 @@ private: * objects. */ - stack > /* information. */ + stack > /* information. */ temporary_file_objects; - friend class KecWrapper; /* Friend declarations. */ - friend class Ltl2AutWrapper; - friend class Ltl2BaWrapper; - friend class ProdWrapper; - friend class SpinWrapper; - friend class WringWrapper; + friend class SpinWrapper; /* Friend declarations. */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -316,46 +280,6 @@ inline bool ExternalTranslator::execSuccess(int exitcode) -/****************************************************************************** - * - * Inline function definitions for class ExternalTranslator::TempFileObject. - * - *****************************************************************************/ - -/* ========================================================================= */ -inline const string& ExternalTranslator::TempFileObject::getName() const -/* ---------------------------------------------------------------------------- - * - * Description: Returns the name associated with the - * ExternalTranslator::TempFileObject. - * - * Arguments: None. - * - * Returns: The name associated with the object. - * - * ------------------------------------------------------------------------- */ -{ - return name; -} - -/* ========================================================================= */ -inline ExternalTranslator::TempFileObject::Type -ExternalTranslator::TempFileObject::getType() const -/* ---------------------------------------------------------------------------- - * - * Description: Returns the type of the ExternalTranslator::TempFileObject. - * - * Arguments: None. - * - * Returns: The type associated with the object. - * - * ------------------------------------------------------------------------- */ -{ - return type; -} - - - /****************************************************************************** * * Inline function definitions for class diff --git a/lbtt/src/FormulaRandomizer.h b/lbtt/src/FormulaRandomizer.h index a0391d499..cf40472db 100644 --- a/lbtt/src/FormulaRandomizer.h +++ b/lbtt/src/FormulaRandomizer.h @@ -21,6 +21,7 @@ #define FORMULARANDOMIZER_H #include +#include #include #include #include "LbttAlloc.h" diff --git a/lbtt/src/FormulaWriter.h b/lbtt/src/FormulaWriter.h index 9eba9f868..34b3c2c8b 100644 --- a/lbtt/src/FormulaWriter.h +++ b/lbtt/src/FormulaWriter.h @@ -21,13 +21,11 @@ #define FORMULAWRITER_H #include "Exception.h" +#include "LtlFormula.h" namespace Ltl { -class LtlFormula; -class Atom; - /****************************************************************************** * * A function template class for writing the formula to a stream. diff --git a/lbtt/src/Graph.h.in b/lbtt/src/Graph.h.in index 29c8e52e6..9b7c4a6b7 100644 --- a/lbtt/src/Graph.h.in +++ b/lbtt/src/Graph.h.in @@ -186,6 +186,10 @@ public: * graph nodes. */ + class PathElement; /* A class for representing + * (node, edge) pairs + */ + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ protected: @@ -205,6 +209,14 @@ public: * the graph nodes. */ + typedef EdgeContainer EdgeContainerType; /* Type definition for + * containers of graph + * edges. + */ + + typedef deque /* Type definition for */ + Path; /* paths in a graph. */ + typedef pair StateIdPair; /* Type definition for a * pair of state * identifiers in a graph. @@ -323,8 +335,8 @@ public: /* default assignment operator */ - Graph::size_type targetNode() /* Returns the index of */ - const; /* the target node of + size_type targetNode() const; /* Returns the index of */ + /* the target node of * the directed edge. */ @@ -353,7 +365,7 @@ protected: */ private: - Graph::size_type target_node; /* Identifier of the edge's + size_type target_node; /* Identifier of the edge's * target node. */ }; @@ -404,6 +416,62 @@ protected: +/****************************************************************************** + * + * A template class for representing (node identifier, edge) pairs in a graph. + * + *****************************************************************************/ + +template +class Graph::PathElement +{ +public: + explicit PathElement /* Constructors. */ + (const typename Graph::size_type + n, + const typename Graph::Edge* + e = 0); + + PathElement + (const typename Graph::size_type + n, + const typename Graph::Edge& e); + + /* default copy constructor */ + + ~PathElement(); /* Destructor. */ + + /* default assignment operator */ + + size_type node() const; /* Returns the identifier + * of the node associated + * with the path element. + */ + + bool hasEdge() const; /* Tells whether there is + * an edge associated with + * the path element. + */ + + const Edge& edge() const; /* Returns the edge + * associated with the + * path element. + */ + +private: + typename Graph::size_type node_id; /* Identifier of the node + * associated with the path + * element. + */ + + const typename Graph::Edge* /* Pointer to the edge */ + edge_pointer; /* associated with the + * path element. + */ +}; + + + /****************************************************************************** * * An exception class for reporting errors when indexing graph nodes. @@ -924,7 +992,7 @@ Graph::stats() const * * ------------------------------------------------------------------------- */ { - pair::size_type, unsigned long int> result; + pair result; result.first = nodes.size(); result.second = 0; @@ -1468,6 +1536,128 @@ void Graph::Node::print +/****************************************************************************** + * + * Inline function definitions for template class + * Graph::PathElement. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Graph::PathElement::PathElement + (const typename Graph::size_type n, + const typename Graph::Edge* e) : + node_id(n), edge_pointer(e) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Graph::PathElement. + * Creates a (node identifier, edge) pair from a node identifier + * and a pointer to an edge. + * + * Arguments: n -- Numeric identifier of a graph node. + * e -- A constant pointer to a graph edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Graph::PathElement::PathElement + (const typename Graph::size_type n, + const typename Graph::Edge& e) : + node_id(n), edge_pointer(&e) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Graph::PathElement. + * Creates a (node identifier, edge) pair from a node identifier + * and an edge. + * + * Arguments: n -- Numeric identifier of a graph node. + * e -- A constant reference to a graph edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Graph::PathElement::~PathElement() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Graph::PathElement. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline typename Graph::size_type +Graph::PathElement::node() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the identifier of the node associated with a + * Graph::PathElement object. + * + * Arguments: None. + * + * Returns: Identifier of the node associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return node_id; +} + +/* ========================================================================= */ +template +inline bool Graph::PathElement::hasEdge() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether there is an edge associated with a + * Graph::PathElement object. + * + * Arguments: None. + * + * Returns: true iff there is an edge associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return (edge != 0); +} + +/* ========================================================================= */ +template +inline const typename Graph::Edge& +Graph::PathElement::edge() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the edge associated with a + * Graph::PathElement object. The function + * assumes that there is such an edge; it is an error to call + * this function for a PathElement object `element' for which + * `element.hasEdge() == false'. + * + * Arguments: None. + * + * Returns: The edge associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return *edge_pointer; +} + + + /****************************************************************************** * * Inline function definitions for class NodeIndexException. diff --git a/lbtt/src/IntervalList.cc b/lbtt/src/IntervalList.cc new file mode 100644 index 000000000..52a5ebb35 --- /dev/null +++ b/lbtt/src/IntervalList.cc @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "IntervalList.h" +#include "StringUtil.h" + +/****************************************************************************** + * + * Function definitions for class IntervalList. + * + *****************************************************************************/ + +/* ========================================================================= */ +void IntervalList::merge(unsigned long int min, unsigned long int max) +/* ---------------------------------------------------------------------------- + * + * Description: Merges a new interval with a list of intervals. + * + * Arguments: min, max -- Upper and lower bound of the new interval. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (min > max) + return; + + list::iterator interval; + for (interval = intervals.begin(); + interval != intervals.end() && interval->second + 1 < min; + ++interval) + ; + + if (interval == intervals.end()) + { + intervals.insert(interval, make_pair(min, max)); + return; + } + + if (interval->first <= min && max <= interval->second) + return; + + if (max + 1 < interval->first) + { + intervals.insert(interval, make_pair(min, max)); + return; + } + + if (min < interval->first) + interval->first = min; + + if (interval->second < max) + { + interval->second = max; + list::iterator interval2 = interval; + ++interval2; + while (interval2 != intervals.end() + && interval2->first <= interval->second + 1) + { + if (interval->second < interval2->second) + interval->second = interval2->second; + list::iterator interval_to_erase = interval2; + ++interval2; + intervals.erase(interval_to_erase); + } + } +} + +/* ========================================================================= */ +void IntervalList::remove(unsigned long int min, unsigned long int max) +/* ---------------------------------------------------------------------------- + * + * Description: Removes a closed interval from an interval list. + * + * Arguments: min, max -- Bounds for the interval to remove. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (min > max) + return; + + list::iterator interval; + for (interval = intervals.begin(); + interval != intervals.end() && interval->second < min; + ++interval) + ; + + while (interval != intervals.end()) + { + if (max < interval->first) /* min <= max < imin <= imax */ + return; + + if (interval->first < min) + { + if (max < interval->second) /* imin < min <= max < imax */ + { + intervals.insert(interval, make_pair(interval->first, min - 1)); + interval->first = max + 1; + return; + } + interval->second = min - 1; /* imin < min <= imax <= max */ + ++interval; + } + else if (max < interval->second) /* min <= imin <= max < imax */ + { + interval->first = max + 1; + return; + } + else /* min <= imin <= imax <= max */ + { + list::iterator interval_to_erase = interval; + ++interval; + intervals.erase(interval_to_erase); + } + } +} + +/* ========================================================================= */ +bool IntervalList::covers(unsigned long int min, unsigned long int max) const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether an interval list covers a given interval. + * + * Arguments: min, max -- Upper and lower bound for the interval to test. + * + * Returns: True if the IntervalList covers the given interval. + * + * ------------------------------------------------------------------------- */ +{ + if (min > max) + return true; /* empty interval is always covered */ + + list::const_iterator interval; + for (interval = intervals.begin(); + interval != intervals.end() && min > interval->second; + ++interval) + ; + + if (interval == intervals.end()) + return false; + + return (min >= interval->first && max <= interval->second); +} + +/* ========================================================================= */ +string IntervalList::toString() const +/* ---------------------------------------------------------------------------- + * + * Description: Converts the interval list to a string. + * + * Arguments: None. + * + * Returns: A string listing the intervals in the interval list. + * + * ------------------------------------------------------------------------- */ +{ + string s; + for (list::const_iterator + interval = intervals.begin(); + interval != intervals.end(); + ++interval) + { + if (interval != intervals.begin()) + s += ','; + s += StringUtil::toString(interval->first) + "..." + + StringUtil::toString(interval->second); + } + return s; +} diff --git a/lbtt/src/IntervalList.h b/lbtt/src/IntervalList.h new file mode 100644 index 000000000..2a55b9641 --- /dev/null +++ b/lbtt/src/IntervalList.h @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef INTERVALLIST_H +#define INTERVALLIST_H + +#include +#include +#include +#include +#include "LbttAlloc.h" + +using namespace std; + +/****************************************************************************** + * + * The IntervalList class represents a list of disjoint closed intervals + * formed from pairs of unsigned long integers. The class supports merging + * a new interval with a list of intervals, removing an interval from a list + * of intervals and checking whether the interval list covers a given element + * (or a given interval). The elements of the intervals can also be accessed + * in increasing order via IntervalList::const_iterator. + * + *****************************************************************************/ + +class IntervalList +{ +private: + typedef pair + Interval; + +public: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class const_iterator /* A class for iterating */ + { /* over the elements of */ + /* an IntervalList. */ + public: + const_iterator(); /* Constructor. */ + + /* default copy constructor */ + + ~const_iterator(); /* Destructor. */ + + /* default assignment operator */ + + bool operator==(const const_iterator& it) /* Comparison operators. */ + const; + + bool operator!=(const const_iterator& it) + const; + + unsigned long int operator*() const; /* Dereference operator. */ + + unsigned long int operator++(); /* Prefix increment. */ + + unsigned long int operator++(int); /* Postfix increment. */ + + private: + const list* /* The interval list */ + interval_list; /* associated with the */ + /* iterator. */ + + list /* An iterator pointing */ + ::const_iterator interval; /* at the current */ + /* interval list. */ + + unsigned long int element; /* Element currently + * pointed to by the + * iterator. + */ + + friend class IntervalList; + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + IntervalList(); /* Constructor. */ + + ~IntervalList(); /* Destructor. */ + + /* default copy constructor */ + + /* default assignment operator */ + + void merge(unsigned long int element); /* Merges a point interval + * with the list of + * intervals. + */ + + void merge /* Merges a new interval */ + (unsigned long int min, unsigned long int max); /* with the list of + * intervals. + */ + + void remove(unsigned long int element); /* Removes an element from + * the list of intervals. + */ + + void remove /* Removes an interval */ + (unsigned long int min, unsigned long int max); /* from the list of + * intervals. + */ + + bool covers(unsigned long int element) const; /* Tests whether the + * interval list covers an + * element. + */ + + bool covers /* Tests whether the */ + (unsigned long int min, unsigned long int max) /* interval list covers */ + const; /* an interval. */ + + const_iterator begin() const; /* Returns an iterator to + * the beginning of the + * interval list. + */ + + const_iterator end() const; /* Returns an iterator to + * the end of the interval + * list. + */ + + typedef const_iterator iterator; /* The interval list + * cannot be modified with + * iterators. + */ + + typedef list /* Size type. */ + ::size_type size_type; + + size_type size() const; /* Tell the number of + * disjoint intervals in + * the interval list. + */ + + size_type max_size() const; /* Tell the maximum + * allowable number of + * disjoint intervals in + * the interval list. + */ + + bool empty() const; /* Tell whether the + * interval list is empty. + */ + + void clear(); /* Makes the interval list + * empty. + */ + + string toString() const; /* Converts the interval + * list to a string. + */ + +private: + list intervals; /* List of intervals. */ + + friend class const_iterator; +}; + + + +/****************************************************************************** + * + * Inline function definitions for class IntervalList. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline IntervalList::IntervalList() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class IntervalList. Creates an empty list of + * intervals. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline IntervalList::~IntervalList() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class IntervalList. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void IntervalList::merge(unsigned long int element) +/* ---------------------------------------------------------------------------- + * + * Description: Merges an element (a point interval) with an IntervalList. + * + * Arguments: element -- Element to merge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + merge(element, element); +} + +/* ========================================================================= */ +inline bool IntervalList::covers(unsigned long int element) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether an interval list covers an element. + * + * Arguments: element -- Element to test. + * + * Returns: True if the IntervalList covers the element. + * + * ------------------------------------------------------------------------- */ +{ + return covers(element, element); +} + +/* ========================================================================= */ +inline IntervalList::size_type IntervalList::size() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of disjoint intervals in an IntervalList. + * + * Arguments: None. + * + * Returns: Number of disjoint intervals in the IntervalList. + * + * ------------------------------------------------------------------------- */ +{ + return intervals.size(); +} + +/* ========================================================================= */ +inline IntervalList::size_type IntervalList::max_size() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the maximum allowable number of disjoint intervals in + * an IntervalList. + * + * Arguments: None. + * + * Returns: Maximum allowable number of disjoint intervals in the + * IntervalList. + * + * ------------------------------------------------------------------------- */ +{ + return intervals.max_size(); +} + +/* ========================================================================= */ +inline bool IntervalList::empty() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether an IntervalList is empty. + * + * Arguments: None. + * + * Returns: True if the IntervalList is empty. + * + * ------------------------------------------------------------------------- */ +{ + return intervals.empty(); +} + +/* ========================================================================= */ +inline void IntervalList::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes an IntervalList empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + intervals.clear(); +} + +/* ========================================================================= */ +inline IntervalList::const_iterator IntervalList::begin() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an IntervalList::const_iterator pointing to the + * beginning of an IntervalList. + * + * Arguments: None. + * + * Returns: An IntervalList::const_iterator pointing to the beginning of + * the IntervalList. + * + * ------------------------------------------------------------------------- */ +{ + const_iterator it; + it.interval_list = &this->intervals; + it.interval = intervals.begin(); + if (it.interval != intervals.end()) + it.element = it.interval->first; + else + it.element = 0; + return it; +} + +/* ========================================================================= */ +inline IntervalList::const_iterator IntervalList::end() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an IntervalList::const_iterator pointing to the end + * of an IntervalList. + * + * Arguments: None. + * + * Returns: An IntervalList::const_iterator pointing to the end of the + * IntervalList. + * + * ------------------------------------------------------------------------- */ +{ + const_iterator it; + it.interval_list = &this->intervals; + it.interval = intervals.end(); + it.element = 0; + return it; +} + + + +/****************************************************************************** + * + * Inline function definitions for class IntervalList::const_iterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline IntervalList::const_iterator::const_iterator() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class IntervalList::const_iterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline IntervalList::const_iterator::~const_iterator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class IntervalList::const_iterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline bool IntervalList::const_iterator::operator== + (const const_iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality test for two IntervalList::const_iterators. Two + * IntervalList::const_iterators are equal if and only if they + * point to the same interval of an interval list and the same + * element in the interval. + * + * Argument: it -- A constant reference to another + * IntervalList::const_iterator. + * + * Returns: Result of the equality test (a truth value). + * + * ------------------------------------------------------------------------- */ +{ + return (interval_list == it.interval_list && interval == it.interval + && element == it.element); +} + +/* ========================================================================= */ +inline bool IntervalList::const_iterator::operator!= + (const const_iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Inequality test for two IntervalList::const_iterators. Two + * IntervalList::const_iterators are not equal if and only if + * they point to different intervals of an interval list or to + * different elements of the same interval in the list. + * + * Argument: it -- A constant reference to another + * IntervalList::const_iterator. + * + * Returns: Result of the inequality test (a truth value). + * + * ------------------------------------------------------------------------- */ +{ + return (interval_list != it.interval_list || interval != it.interval + || element != it.element); +} + +/* ========================================================================= */ +inline unsigned long int IntervalList::const_iterator::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for IntervalList::const_iterator. + * + * Arguments: None. + * + * Returns: The element currently pointed to by the iterator. + * + * ------------------------------------------------------------------------- */ +{ + return element; +} + +/* ========================================================================= */ +inline unsigned long int IntervalList::const_iterator::operator++() +/* ---------------------------------------------------------------------------- + * + * Description: Prefix increment operator for IntervalList::const_iterator. + * + * Arguments: None. + * + * Returns: The element following the "current" element in the interval + * list. + * + * ------------------------------------------------------------------------- */ +{ + if (element == interval->second) + { + ++interval; + if (interval != interval_list->end()) + element = interval->first; + else + element = 0; + } + else + ++element; + + return element; +} + +/* ========================================================================= */ +inline unsigned long int IntervalList::const_iterator::operator++(int) +/* ---------------------------------------------------------------------------- + * + * Description: Postfix increment operator for IntervalList::const_iterator. + * + * Arguments: None. + * + * Returns: The "current" element in the interval list. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int current_element = element; + if (element == interval->second) + { + ++interval; + if (interval != interval_list->end()) + element = interval->first; + else + element = 0; + } + else + ++element; + + return current_element; +} + +#endif /* INTERVALLIST_H */ diff --git a/lbtt/src/LbttAlloc.h b/lbtt/src/LbttAlloc.h index b4019d922..2f8ef8cc3 100644 --- a/lbtt/src/LbttAlloc.h +++ b/lbtt/src/LbttAlloc.h @@ -22,11 +22,7 @@ #include -#ifdef HAVE_SINGLE_CLIENT_ALLOC -#define ALLOC(typename) single_client_alloc -#else #define ALLOC(typename) allocator -#endif /* HAVE_SINGLE_CLIENT_ALLOC */ #ifdef HAVE_OBSTACK_H diff --git a/lbtt/src/Ltl-parse.yy b/lbtt/src/Ltl-parse.yy new file mode 100644 index 000000000..d558db0b7 --- /dev/null +++ b/lbtt/src/Ltl-parse.yy @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +%{ +#include +#include +#include +#include +#include +#include +#include "Exception.h" +#include "LbttAlloc.h" +#include "LtlFormula.h" + +using namespace Ltl; + +/****************************************************************************** + * + * Variables and functions used for parsing an LTL formula. + * + *****************************************************************************/ + +static Exceptional_istream* estream; /* Pointer to input stream. + */ + +static LtlFormula* result; /* This variable stores the + * result after a call to + * ltl_parse. + */ + +static std::set, /* Intermediate results. */ + ALLOC(LtlFormula*) > /* (This set is used */ + intermediate_results; /* for keeping track of + * the subformulas of a + * partially constructed + * formula in case the + * memory allocated for + * the subformulas needs + * to be freed because + * of a parse error.) + */ + +static int ltl_lex(); /* The lexical scanner. */ + + + +/****************************************************************************** + * + * Function for reporting parse errors. + * + *****************************************************************************/ + +static void ltl_error(const char*) +{ + throw LtlFormula::ParseErrorException("error parsing LTL formula"); +} + + + +/****************************************************************************** + * + * Function for updating the set of intermediate results. + * + *****************************************************************************/ + +inline LtlFormula* newFormula(LtlFormula& f) +{ + intermediate_results.insert(&f); + return &f; +} + +%} + + + +%name-prefix="ltl_" + + + +/****************************************************************************** + * + * Declarations for terminal and nonterminal symbols used in the grammar rules + * below. + * + *****************************************************************************/ + +%union { + class LtlFormula* formula; +} + +/* Uninterpreted symbols. */ + +%token LTLPARSE_LPAR LTLPARSE_RPAR LTLPARSE_FALSE LTLPARSE_TRUE + LTLPARSE_UNKNOWN + +/* Atomic propositions. */ + +%token LTLPARSE_ATOM + +/* Operators. */ + +%nonassoc LTLPARSE_UNTIL LTLPARSE_RELEASE LTLPARSE_WEAK_UNTIL + LTLPARSE_STRONG_RELEASE LTLPARSE_BEFORE +%left LTLPARSE_IMPLY LTLPARSE_EQUIV LTLPARSE_XOR +%left LTLPARSE_OR +%left LTLPARSE_AND +%nonassoc LTLPARSE_NOT LTLPARSE_NEXT LTLPARSE_FINALLY LTLPARSE_GLOBALLY +%nonassoc LTLPARSE_EQUALS + +/* Compound formulas. */ + +%type formula atomic_formula unary_formula prefix_op_formula + binary_formula prefix_b_formula infix_b_formula + + + +/****************************************************************************** + * + * Grammar rule definitions. + * + *****************************************************************************/ + +%% + +ltl_formula: formula + { result = $1; } + ; + +formula: atomic_formula + { $$ = $1; } + + | unary_formula + { $$ = $1; } + + | binary_formula + { $$ = $1; } + + | LTLPARSE_LPAR formula LTLPARSE_RPAR + { $$ = $2; } + ; + +atomic_formula: LTLPARSE_ATOM + { $$ = $1; } + + | LTLPARSE_ATOM LTLPARSE_EQUALS LTLPARSE_FALSE + { + intermediate_results.erase($1); + $$ = newFormula(Not::construct($1)); + } + + | LTLPARSE_ATOM LTLPARSE_EQUALS LTLPARSE_TRUE + { $$ = $1; } + + | LTLPARSE_FALSE + { $$ = newFormula(False::construct()); } + + | LTLPARSE_TRUE + { $$ = newFormula(True::construct()); } + ; + +unary_formula: LTLPARSE_NOT formula + { + intermediate_results.erase($2); + $$ = newFormula(Not::construct($2)); + } + + | LTLPARSE_NEXT formula + { + intermediate_results.erase($2); + $$ = newFormula(Next::construct($2)); + } + + | LTLPARSE_FINALLY formula + { + intermediate_results.erase($2); + $$ = newFormula(Finally::construct($2)); + } + + | LTLPARSE_GLOBALLY formula + { + intermediate_results.erase($2); + $$ = newFormula(Globally::construct($2)); + } + ; + +prefix_op_formula: atomic_formula + { $$ = $1; } + + | unary_formula + { $$ = $1; } + + | prefix_b_formula + { $$ = $1; } + + | LTLPARSE_LPAR formula LTLPARSE_RPAR + { $$ = $2; } + ; + +binary_formula: prefix_b_formula + { $$ = $1; } + | infix_b_formula + { $$ = $1; } + ; + +prefix_b_formula: LTLPARSE_AND prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(And::construct($2, $3)); + } + + | LTLPARSE_OR prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Or::construct($2, $3)); + } + + | LTLPARSE_IMPLY prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Imply::construct($2, $3)); + } + + | LTLPARSE_EQUIV prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Equiv::construct($2, $3)); + } + + | LTLPARSE_XOR prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Xor::construct($2, $3)); + } + + | LTLPARSE_UNTIL prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Until::construct($2, $3)); + } + + | LTLPARSE_RELEASE prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(V::construct($2, $3)); + } + + | LTLPARSE_WEAK_UNTIL prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(WeakUntil::construct($2, $3)); + } + + | LTLPARSE_STRONG_RELEASE prefix_op_formula + prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(StrongRelease::construct($2, $3)); + } + + | LTLPARSE_BEFORE prefix_op_formula prefix_op_formula + { + intermediate_results.erase($2); + intermediate_results.erase($3); + $$ = newFormula(Before::construct($2, $3)); + } + ; + +infix_b_formula: formula LTLPARSE_AND formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(And::construct($1, $3)); + } + + | formula LTLPARSE_OR formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Or::construct($1, $3)); + } + + | formula LTLPARSE_IMPLY formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Imply::construct($1, $3)); + } + + | formula LTLPARSE_EQUIV formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Equiv::construct($1, $3)); + } + + | formula LTLPARSE_XOR formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Xor::construct($1, $3)); + } + + | formula LTLPARSE_UNTIL formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Until::construct($1, $3)); + } + + | formula LTLPARSE_RELEASE formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(V::construct($1, $3)); + } + + | formula LTLPARSE_WEAK_UNTIL formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(WeakUntil::construct($1, $3)); + } + + | formula LTLPARSE_STRONG_RELEASE formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(StrongRelease::construct($1, $3)); + } + + | formula LTLPARSE_BEFORE formula + { + intermediate_results.erase($1); + intermediate_results.erase($3); + $$ = newFormula(Before::construct($1, $3)); + } + ; + +%% + + + +/****************************************************************************** + * + * Helper function for reading lexical tokens from a stream. + * + *****************************************************************************/ + +static inline size_t matchCharactersFromStream + (istream& stream, char* chars) +{ + size_t num_matched; + for (num_matched = 0; *chars != '\0' && stream.peek() == *chars; ++chars) + { + stream.ignore(1); + ++num_matched; + } + return num_matched; +} + + +/****************************************************************************** + * + * Main interface to the parser. + * + *****************************************************************************/ + +namespace Ltl +{ + +/* ========================================================================= */ +LtlFormula* parseFormula(istream& stream) +/* ---------------------------------------------------------------------------- + * + * Description: Parses an LTL formula from a stream. The formula should be + * in one of the formats used by the tools lbtt 1.0.x (both + * prefix and infix form), Spin/Temporal Massage Parlor/LTL2BA, + * LTL2AUT or Wring 1.1.0 (actually, the grammar is basically + * a combination of the grammars of the above tools with the + * exception that propositions should always be written in the + * form `pN' for some integer N; in principle, it is possible to + * use a mixed syntax for the formula). The input should be + * terminated with a newline. + * + * Argument: stream -- A reference to the input stream. + * + * Returns: A pointer to the formula. The function throws an + * LtlFormula::ParseErrorException if the syntax is incorrect, + * or an IOException in case of an end-of-file or another I/O + * error. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_istream es(&stream, ios::badbit | ios::failbit | ios::eofbit); + estream = &es; + intermediate_results.clear(); + + try + { + ltl_parse(); + } + catch (...) + { + for (std::set, ALLOC(LtlFormula*) > + ::const_iterator f = intermediate_results.begin(); + f != intermediate_results.end(); + ++f) + LtlFormula::destruct(*f); + throw; + } + return result; +} + +} + + + +/****************************************************************************** + * + * The lexical scanner. + * + *****************************************************************************/ + +static int ltl_lex() +{ + char c; + std::istream& stream = static_cast(*estream); + + do + { + estream->get(c); + } + while (isspace(c) && c != '\n'); + + switch (c) + { + case '\n' : return 0; + + case '(' : + return (matchCharactersFromStream(stream, ")") == 1 + ? LTLPARSE_NEXT + : LTLPARSE_LPAR); + + case ')' : return LTLPARSE_RPAR; + + case 'f' : + switch (matchCharactersFromStream(stream, "alse")) + { + case 0 : case 4 : + return LTLPARSE_FALSE; + default: + break; + } + return LTLPARSE_UNKNOWN; + + case '0' : return LTLPARSE_FALSE; + + case 't' : + switch (matchCharactersFromStream(stream, "rue")) + { + case 0 : case 3 : + return LTLPARSE_TRUE; + default : + return LTLPARSE_UNKNOWN; + } + + case 'T' : + return (matchCharactersFromStream(stream, "RUE") == 3 + ? LTLPARSE_TRUE + : LTLPARSE_UNKNOWN); + + case '1' : return LTLPARSE_TRUE; + + case '!' : case '~' : return LTLPARSE_NOT; + + case '&' : + matchCharactersFromStream(stream, "&"); + return LTLPARSE_AND; + + case '/' : + return (matchCharactersFromStream(stream, "\\") == 1 + ? LTLPARSE_AND + : LTLPARSE_UNKNOWN); + + case '*' : return LTLPARSE_AND; + + case '|' : + matchCharactersFromStream(stream, "|"); + return LTLPARSE_OR; + + case '\\' : + return (matchCharactersFromStream(stream, "/") == 1 + ? LTLPARSE_OR + : LTLPARSE_UNKNOWN); + + case '+' : return LTLPARSE_OR; + + case '=' : + return (matchCharactersFromStream(stream, ">") == 1 + ? LTLPARSE_IMPLY + : LTLPARSE_EQUALS); + + case '-' : + return (matchCharactersFromStream(stream, ">") == 1 + ? LTLPARSE_IMPLY + : LTLPARSE_UNKNOWN); + + case 'i' : return LTLPARSE_IMPLY; + + case '<' : + if (matchCharactersFromStream(stream, ">") == 1) + return LTLPARSE_FINALLY; + return (matchCharactersFromStream(stream, "->") == 2 + || matchCharactersFromStream(stream, "=>") == 2 + ? LTLPARSE_EQUIV + : LTLPARSE_UNKNOWN); + + case 'e' : return LTLPARSE_EQUIV; + + case 'x' : + return (matchCharactersFromStream(stream, "or") == 2 + ? LTLPARSE_XOR + : LTLPARSE_UNKNOWN); + + case '^' : return LTLPARSE_XOR; + + case 'X' : return LTLPARSE_NEXT; + + case 'U' : return LTLPARSE_UNTIL; + + case 'V' : case 'R' : return LTLPARSE_RELEASE; + + case 'W' : return LTLPARSE_WEAK_UNTIL; + + case 'M' : return LTLPARSE_STRONG_RELEASE; + + case 'B' : return LTLPARSE_BEFORE; + + case 'F' : + switch (matchCharactersFromStream(stream, "ALSE")) + { + case 0 : + return LTLPARSE_FINALLY; + case 4 : + return LTLPARSE_FALSE; + default : + return LTLPARSE_UNKNOWN; + } + + case '[' : + return (matchCharactersFromStream(stream, "]") == 1 + ? LTLPARSE_GLOBALLY + : LTLPARSE_UNKNOWN); + + case 'G' : return LTLPARSE_GLOBALLY; + + case 'p' : + { + long int id = 0; + bool id_ok = false; + int ch = stream.peek(); + while (ch >= '0' && ch <= '9') + { + id_ok = true; + estream->get(c); + if (LONG_MAX / 10 < id) + throw LtlFormula::ParseErrorException + ("error parsing LTL formula (proposition identifier out of " + "range)"); + id *= 10; + id += (c - '0'); + ch = stream.peek(); + } + + if (id_ok) + { + ltl_lval.formula = newFormula(Atom::construct(id)); + return LTLPARSE_ATOM; + } + return LTLPARSE_UNKNOWN; + } + + default : return LTLPARSE_UNKNOWN; + } +} diff --git a/lbtt/src/LtlFormula.cc b/lbtt/src/LtlFormula.cc index 7c4034722..2fa3d4723 100644 --- a/lbtt/src/LtlFormula.cc +++ b/lbtt/src/LtlFormula.cc @@ -24,9 +24,8 @@ namespace Ltl { -map +set /* LTL formulae. */ LtlFormula::formula_storage; unsigned long int /* Upper limit for the */ @@ -38,8 +37,6 @@ unsigned long int /* Upper limit for the */ * truth assignment). */ - - /****************************************************************************** * * Function for obtaining the infix symbol associated with a given @@ -875,193 +872,6 @@ Bitset LtlFormula::findPropositionalModel(long int max_atom) const return model; } -/* ========================================================================= */ -LtlFormula* LtlFormula::read(Exceptional_istream& stream) -/* ---------------------------------------------------------------------------- - * - * Description: Recursively constructs an LtlFormula by parsing input from an - * exception-aware input stream. - * - * Argument: stream -- A reference to an exception-aware input stream. - * - * Returns: The constructed LtlFormula. - * - * ------------------------------------------------------------------------- */ -{ - string token; - LtlFormula* formula; - - try - { - stream >> token; - } - catch (const IOException&) - { - if (static_cast(stream).eof()) - throw ParseErrorException("error parsing LTL formula (unexpected end of " - "input)"); - else - throw ParseErrorException("error parsing LTL formula (I/O error)"); - } - - if (token[0] == 'p') - { - if (token.length() == 1) - throw ParseErrorException("error parsing LTL formula (unrecognized " - "token: `" + token + "')"); - - long int id; - char* endptr; - - id = strtol(token.c_str() + 1, &endptr, 10); - - if (*endptr != '\0' || id < 0 || id == LONG_MIN || id == LONG_MAX) - throw ParseErrorException("error parsing LTL formula (unrecognized " - "token: `" + token + "')"); - - formula = &Atom::construct(id); - } - else - { - if (token.length() > 1) - throw ParseErrorException("error parsing LTL formula (unrecognized " - "token: `" + token + "')"); - - switch (token[0]) - { - case LTL_TRUE : - formula = &True::construct(); - break; - - case LTL_FALSE : - formula = &False::construct(); - break; - - case LTL_NEGATION : - case LTL_NEXT : - case LTL_FINALLY : - case LTL_GLOBALLY : - { - LtlFormula* g = read(stream); - - try - { - switch (token[0]) - { - case LTL_NEGATION : - formula = &Not::construct(g); - break; - - case LTL_NEXT : - formula = &Next::construct(g); - break; - - case LTL_FINALLY : - formula = &Finally::construct(g); - break; - - default : /* LTL_GLOBALLY */ - formula = &Globally::construct(g); - break; - } - } - catch (...) - { - LtlFormula::destruct(g); - throw; - } - - break; - } - - case LTL_CONJUNCTION : - case LTL_DISJUNCTION : - case LTL_IMPLICATION : - case LTL_EQUIVALENCE : - case LTL_XOR : - case LTL_UNTIL : - case LTL_V : - case LTL_WEAK_UNTIL : - case LTL_STRONG_RELEASE : - case LTL_BEFORE : - { - LtlFormula* g = read(stream); - LtlFormula* h; - - try - { - h = read(stream); - } - catch (...) - { - LtlFormula::destruct(g); - throw; - } - - try - { - switch (token[0]) - { - case LTL_CONJUNCTION : - formula = &And::construct(g, h); - break; - - case LTL_DISJUNCTION : - formula = &Or::construct(g, h); - break; - - case LTL_IMPLICATION : - formula = &Imply::construct(g, h); - break; - - case LTL_EQUIVALENCE : - formula = &Equiv::construct(g, h); - break; - - case LTL_XOR : - formula = &Xor::construct(g, h); - break; - - case LTL_UNTIL : - formula = &Until::construct(g, h); - break; - - case LTL_V : - formula = &V::construct(g, h); - break; - - case LTL_WEAK_UNTIL : - formula = &WeakUntil::construct(g, h); - break; - - case LTL_STRONG_RELEASE : - formula = &StrongRelease::construct(g, h); - break; - - default : /* LTL_BEFORE */ - formula = &Before::construct(g, h); - break; - } - } - catch (...) - { - LtlFormula::destruct(g); - LtlFormula::destruct(h); - throw; - } - - break; - } - - default : - throw ParseErrorException("error parsing LTL formula (unrecognized " - "token: `" + token + "')"); - } - } - - return formula; -} - /* ========================================================================= */ void LtlFormula::print(Exceptional_ostream& estream, OutputMode mode) const /* ---------------------------------------------------------------------------- diff --git a/lbtt/src/LtlFormula.h b/lbtt/src/LtlFormula.h index c5b2efea7..a9209be7c 100644 --- a/lbtt/src/LtlFormula.h +++ b/lbtt/src/LtlFormula.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include "LbttAlloc.h" @@ -188,13 +188,6 @@ public: * input stream. */ - static LtlFormula* read /* Constructs an */ - (Exceptional_istream& stream); /* LtlFormula by parsing - * input from an - * exception-aware input - * stream. - */ - void print /* Writes the formula to */ (ostream& stream = cout, /* an output stream. */ OutputMode mode = LTL_INFIX) const; @@ -250,6 +243,10 @@ protected: unsigned int is_constant : 1; /* operators and atomic */ } info_flags; /* propositions. */ + unsigned long int refcount; /* Number of references to + * `this' LtlFormula. + */ + static LtlFormula& /* Updates the shared */ insertToStorage(LtlFormula* f); /* formula storage with * a new formula. @@ -288,8 +285,8 @@ private: * formula. */ - static map /* LTL formulae. */ + static set /* LTL formulae. */ formula_storage; static unsigned long int /* Upper limit for the */ @@ -330,6 +327,16 @@ private: +/****************************************************************************** + * + * Interface to the formula parser. + * + *****************************************************************************/ + +extern LtlFormula* parseFormula(istream& stream); + + + /****************************************************************************** * * A class for atomic propositions. @@ -474,7 +481,7 @@ private: * to satisfy the * LtlFormula member * function interface. - */ + */ }; @@ -1076,7 +1083,7 @@ typedef BinaryFormula Before; *****************************************************************************/ /* ========================================================================= */ -inline LtlFormula::LtlFormula() +inline LtlFormula::LtlFormula() : refcount(1) /* ---------------------------------------------------------------------------- * * Description: Constructor for class LtlFormula. Initializes the attributes @@ -1088,8 +1095,6 @@ inline LtlFormula::LtlFormula() * * --------------------------------------------------------------------------*/ { - info_flags.is_propositional = 0; - info_flags.is_constant = 0; } /* ========================================================================= */ @@ -1122,14 +1127,9 @@ inline void LtlFormula::destruct(LtlFormula* f) * * ------------------------------------------------------------------------- */ { - map::iterator - deleter; - - deleter = formula_storage.find(f); - if (--deleter->second == 0) + if (--f->refcount == 0) { - formula_storage.erase(deleter); + formula_storage.erase(f); delete f; } } @@ -1147,7 +1147,7 @@ inline LtlFormula* LtlFormula::clone() * * ------------------------------------------------------------------------- */ { - formula_storage.find(this)->second++; + ++refcount; return this; } @@ -1241,8 +1241,7 @@ inline LtlFormula* LtlFormula::read(istream& stream) * * ------------------------------------------------------------------------- */ { - Exceptional_istream estream(&stream, ios::badbit | ios::failbit); - return read(estream); + return parseFormula(stream); } /* ========================================================================= */ @@ -1303,8 +1302,6 @@ inline Exceptional_ostream& operator<< return stream; } - - /* ========================================================================= */ inline LtlFormula& LtlFormula::insertToStorage(LtlFormula* f) /* ---------------------------------------------------------------------------- @@ -1317,19 +1314,16 @@ inline LtlFormula& LtlFormula::insertToStorage(LtlFormula* f) * * ------------------------------------------------------------------------- */ { - map::iterator - inserter; - - inserter = formula_storage.find(f); + set::iterator inserter + = formula_storage.find(f); if (inserter != formula_storage.end()) { delete f; - inserter->second++; - return *(inserter->first); + ++(*inserter)->refcount; + return **inserter; } - formula_storage.insert(make_pair(f, 1)); + formula_storage.insert(f); return *f; } @@ -1571,8 +1565,7 @@ inline Atom& Atom::construct(long int a) } /* ========================================================================= */ -inline Atom::Atom(long int a) : - LtlFormula(), atom(a) +inline Atom::Atom(long int a) : atom(a) /* ---------------------------------------------------------------------------- * * Description: Constructor for class Atom. Creates a new propositional atom. @@ -1886,8 +1879,7 @@ inline UnaryFormula& UnaryFormula::construct(LtlFormula& f) /* ========================================================================= */ template -inline UnaryFormula::UnaryFormula(LtlFormula* f) : - LtlFormula(), subformula(f) +inline UnaryFormula::UnaryFormula(LtlFormula* f) : subformula(f) /* ---------------------------------------------------------------------------- * * Description: Constructs an LTL formula with a unary operator. @@ -2189,7 +2181,7 @@ BinaryFormula::construct(LtlFormula& f1, LtlFormula* f2) /* ========================================================================= */ template inline BinaryFormula::BinaryFormula(LtlFormula* f1, LtlFormula* f2) : - LtlFormula(), subformula1(f1), subformula2(f2) + subformula1(f1), subformula2(f2) /* ---------------------------------------------------------------------------- * * Description: Constructs a binary LTL formula. diff --git a/lbtt/src/Makefile.am b/lbtt/src/Makefile.am index 76f3a8992..6ca8f0e46 100644 --- a/lbtt/src/Makefile.am +++ b/lbtt/src/Makefile.am @@ -1,22 +1,90 @@ +BUILT_SOURCES = Config-parse.h NeverClaim-parse.h +AM_YFLAGS = -d + bin_PROGRAMS = lbtt lbtt-translate -lbtt_SOURCES = BitArray.h Bitset.h BitArray.cc BuchiAutomaton.h \ -BuchiAutomaton.cc Config-parse.yy Config-lex.ll Configuration.h \ -Configuration.cc DispUtil.h DispUtil.cc EdgeContainer.h Exception.h \ -FormulaRandomizer.h FormulaRandomizer.cc FormulaWriter.h LbttAlloc.h \ -LtlFormula.h LtlFormula.cc main.cc PathEvaluator.h PathEvaluator.cc \ -PathIterator.h PathIterator.cc ProductAutomaton.h ProductAutomaton.cc \ -Random.h SccIterator.h SharedTestData.h StatDisplay.h StatDisplay.cc \ -StateSpace.h StateSpace.cc StateSpaceRandomizer.h StateSpaceRandomizer.cc \ -StringUtil.h StringUtil.cc TestOperations.h TestOperations.cc TestRoundInfo.h \ -TestStatistics.h TestStatistics.cc UserCommandReader.h UserCommandReader.cc \ -UserCommands.h UserCommands.cc -EXTRA_lbtt_SOURCES = gnu-getopt.h Config-parse.h +lbtt_SOURCES = \ + BitArray.h \ + Bitset.h \ + BitArray.cc \ + BuchiAutomaton.h \ + BuchiAutomaton.cc \ + BuchiProduct.h \ + BuchiProduct.cc \ + Config-parse.yy \ + Config-lex.ll \ + Configuration.h \ + Configuration.cc \ + DispUtil.h \ + DispUtil.cc \ + EdgeContainer.h \ + Exception.h \ + FormulaRandomizer.h \ + FormulaRandomizer.cc \ + FormulaWriter.h \ + IntervalList.h \ + IntervalList.cc \ + LbttAlloc.h \ + LtlFormula.h \ + LtlFormula.cc \ + Ltl-parse.yy \ + main.cc \ + PathEvaluator.h \ + PathEvaluator.cc \ + PathIterator.h \ + PathIterator.cc \ + Product.h \ + Random.h \ + SccCollection.h \ + SharedTestData.h \ + StatDisplay.h \ + StatDisplay.cc \ + StateSpace.h \ + StateSpace.cc \ + StateSpaceProduct.h \ + StateSpaceRandomizer.h \ + StateSpaceRandomizer.cc \ + StringUtil.h \ + StringUtil.cc \ + TempFsysName.h \ + TempFsysName.cc \ + TestOperations.h \ + TestOperations.cc \ + TestRoundInfo.h \ + TestStatistics.h \ + TestStatistics.cc \ + UserCommandReader.h \ + UserCommandReader.cc \ + UserCommands.h \ + UserCommands.cc +EXTRA_lbtt_SOURCES = gnu-getopt.h lbtt_LDADD = @LIBOBJS@ @READLINELIBS@ -lbtt_translate_SOURCES = BitArray.h BitArray.cc Exception.h \ -ExternalTranslator.h ExternalTranslator.cc FormulaWriter.h LbttAlloc.h \ -LbtWrapper.h LtlFormula.h LtlFormula.cc NeverClaim-parse.yy NeverClaim-lex.ll \ -NeverClaimAutomaton.h NeverClaimAutomaton.cc SpinWrapper.h SpinWrapper.cc \ -StringUtil.h StringUtil.cc translate.h translate.cc TranslatorInterface.h -EXTRA_lbtt_translate_SOURCES = gnu-getopt.h NeverClaim-parse.h +lbtt_translate_SOURCES = \ + BitArray.h \ + BitArray.cc \ + Exception.h \ + ExternalTranslator.h \ + ExternalTranslator.cc \ + FormulaWriter.h \ + IntervalList.h \ + IntervalList.cc \ + LbttAlloc.h \ + LbtWrapper.h \ + LtlFormula.h \ + LtlFormula.cc \ + Ltl-parse.yy \ + NeverClaim-parse.yy \ + NeverClaim-lex.ll \ + NeverClaimAutomaton.h \ + NeverClaimAutomaton.cc \ + SpinWrapper.h \ + SpinWrapper.cc \ + StringUtil.h \ + StringUtil.cc \ + TempFsysName.h \ + TempFsysName.cc \ + translate.h \ + translate.cc \ + TranslatorInterface.h +EXTRA_lbtt_translate_SOURCES = gnu-getopt.h lbtt_translate_LDADD = @LIBOBJS@ diff --git a/lbtt/src/NeverClaim-lex.ll b/lbtt/src/NeverClaim-lex.ll index ba60fc1bf..e59a56ea6 100644 --- a/lbtt/src/NeverClaim-lex.ll +++ b/lbtt/src/NeverClaim-lex.ll @@ -30,6 +30,7 @@ extern int current_neverclaim_line_number; %option never-interactive %option noyywrap +%option nounput %% diff --git a/lbtt/src/PathEvaluator.cc b/lbtt/src/PathEvaluator.cc index 583a2490b..936d1ff98 100644 --- a/lbtt/src/PathEvaluator.cc +++ b/lbtt/src/PathEvaluator.cc @@ -61,26 +61,25 @@ void PathEvaluator::reset() /* ========================================================================= */ bool PathEvaluator::evaluate - (const LtlFormula& formula, const StateSpace& statespace, - const vector& - states_on_path, - StateSpace::size_type loop_state) + (const LtlFormula& formula, const StateSpace::Path& prefix, + const StateSpace::Path& cycle, const StateSpace& statespace) /* ---------------------------------------------------------------------------- * - * Description: Evaluates an LTL formula in a state space in which the states - * are connected into a non-branching sequence that ends in a - * loop. + * Description: Evaluates an LTL formula in a path formed from a prefix and + * an infinitely repeating cycle of states in a state space. * - * Arguments: formula -- Formula to be evaluated. - * statespace -- State space from which the path is - * extracted. - * states_on_path -- Mapping between states in the path and - * the states in `statespace' such that - * `statespace[states_on_path[i]]' - * corresponds to the ith state of the path. - * loop_state -- Number of the state in the path to which - * the ``last'' state of the path is - * connected. + * Arguments: formula -- Formula to be evaluated. + * prefix -- A StateSpace::Path object corresponding to + * the prefix of the path. Only the state + * identifiers in the path elements are used; + * the function will not require `prefix' to + * actually represent a path in `statespace'. + * cycle -- A StateSpace::Path object corresponding to + * the infinitely repeating cycle. Only the + * state identifiers in the path elements are + * relevant. + * statespace -- State space to which the state identifiers in + * `path' and `cycle' refer. * * Returns: `true' if and only if the LTL formula holds in the path. * @@ -88,13 +87,21 @@ bool PathEvaluator::evaluate { reset(); - if (states_on_path.empty() || loop_state >= states_on_path.size()) + if (cycle.empty()) return false; current_formula = &formula; current_path = &statespace; - current_loop_state = loop_state; - path_states = states_on_path; + current_loop_state = prefix.size(); + path_states.reserve(prefix.size() + cycle.size()); + for (StateSpace::Path::const_iterator state = prefix.begin(); + state != prefix.end(); + ++state) + path_states.push_back(state->node()); + for (StateSpace::Path::const_iterator state = cycle.begin(); + state != cycle.end(); + ++state) + path_states.push_back(state->node()); return eval(); } diff --git a/lbtt/src/PathEvaluator.h b/lbtt/src/PathEvaluator.h index 7b7a178ef..794e7ea59 100644 --- a/lbtt/src/PathEvaluator.h +++ b/lbtt/src/PathEvaluator.h @@ -54,11 +54,11 @@ public: bool evaluate /* Tests whether an */ (const LtlFormula& formula, /* LtlFormula holds in a */ - const StateSpace& statespace, /* state space. */ - const vector& - states_on_path, - StateSpace::size_type loop_state); + const StateSpace::Path& prefix, /* path described by a */ + const StateSpace::Path& cycle, /* prefix and a cycle of */ + const StateSpace& statespace); /* states from a state + * space. + */ bool evaluate /* Same as above. */ (const LtlFormula& formula, diff --git a/lbtt/src/Product.h b/lbtt/src/Product.h new file mode 100644 index 000000000..f7b49a677 --- /dev/null +++ b/lbtt/src/Product.h @@ -0,0 +1,2936 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PRODUCT_H +#define PRODUCT_H + +#include +#include +#include +#include +#include +#include "LbttAlloc.h" +#include "BitArray.h" +#include "EdgeContainer.h" +#include "Exception.h" +#include "Graph.h" +#include "SccCollection.h" + +using namespace std; + +extern bool user_break; + +namespace Graph +{ + +/****************************************************************************** + * + * A template class for representing the product of two + * Graph objects (which, in lbtt, are always either two + * BuchiAutomaton objects, or a BuchiAutomaton and a StateSpace). + * + * The class provides functions for checking the products of these objects for + * emptiness (i.e., for two Büchi automata, whether the intersection of their + * languages is (non)empty; for a Büchi automaton and a state space, whether + * some infinite path in the state space is accepted by the automaton). The + * functions are as follows: + * + * * bool localEmptinessCheck + * (Graph::size_type, + * Graph::size_type) + * Checks whether the subproduct rooted at a product state + * determined by a pair of component state identifiers is not + * empty and returns true if this is the case. + * + * * pair::size_type, unsigned long int> + * globalEmptinessCheck + * (Graph::size_type state, Bitset&, + * unsigned long int emptiness_check_size) + * Checks a set of subproducts for emptiness and stores the + * results in a bit set that should have room for (at least) + * `emptiness_check_size' bits (this number is assumed to be less + * than the number of states in the second component of the + * product). The first parameter `state' identifies a state in + * the first component of the product. After the call, the i'th + * bit (for all 0 <= i < emptiness_check_size) in the bit set will + * then be 1 iff the subproduct rooted at the product state + * determined by the pair of state identifiers (state, i) is + * nonempty. The function returns a pair of numbers corresponding + * to the number of product states and transitions generated + * during the emptiness check. + * + * * void findWitness + * (Graph::size_type, + * Graph::size_type, + * Product::Witness&) + * Checks whether the subproduct rooted at a product state + * determined by a pair of component state identifiers is not + * empty. If this is the case, the function constructs a + * certificate (a "witness") for the nonemptiness. For the + * product of two Büchi automata, the witness is an accepting + * execution from both automata on the same input; for the product + * of a Büchi automaton and a state space, the witness is a path + * in the state space that is accepted by the automaton. + * + * All of these functions construct the product "on the fly" with the help of + * operations provided by the class Operations used for instantiating the + * template. The public interface of this class must support the following + * operations: + * + * * Operations(const Graph&, + * const Graph&) + * Constructor that accepts references to the first and second + * component of the product (in this order) as parameters. + * + * * bool empty() + * A predicate which returns "true" iff either of the product + * components is (trivially) empty, i.e., iff either component has + * no states. + * + * * unsigned long int numberOfAcceptanceSets() + * Returns the number of acceptance sets associated with a state + * or a transition in the product. + * + * * const Graph::Node& firstComponent + * (Graph::size_type), + * const Graph::Node& secondComponent + * (Graph::size_type) + * Functions for accessing the states in the individual product + * components such that firstComponent(i) (secondComponent(i)) + * returns a reference to the i'th state of the first (second) + * component in the product. + * + * * void mergeAcceptanceInformation + * (const Graph::Node&, + * const Graph::Node&, + * BitArray&) + * Updates the acceptance information of a product state + * (determined by a state of the first and the second component, + * respectively) into a BitArray that is guaranteed to have room + * for at least numberOfAcceptanceSets() bits. The function + * should not clear bits in the array. + * + * * void mergeAcceptanceInformation + * (const Graph::Edge&, + * const Graph::Edge&, + * BitArray& + * Updates the acceptance information of a product transition + * (corresponding to a pair of transitions of the first and second + * product component) into a BitArray (guaranteed to have room for + * at least numberOfAcceptanceSets() bits). The function should + * not clear bits in the array. + * + * * void validateEdgeIterators + * (const Graph::Node& node_1, + * const Graph::Node& node_2, + * GraphEdgeContainer::const_iterator iterator_1&, + * GraphEdgeContainer::const_iterator iterator_2&) + * Checks whether a pair of edges determined from a pair of + * iterators corresponds to an edge starting from a given state + * (node_1, node_2) in the product. If yes, the function should + * leave the iterators intact; otherwise the iterators should be + * updated such that they point to a pair of edges corresponding + * to an edge in the product (or to `node_1.edges().end()' and + * `node_2.edges().end()' if this is not possible). + * Calling the function with the iterators initialized to + * `node_1.edges().begin()' and `node_2.edges().begin()' should + * update the iterators such that repeated calls to + * `incrementEdgeIterators' (see below) result in an enumeration + * of all product edges beginning from the product state + * (node_1, node_2). + * + * * void incrementEdgeIterators + * (const Graph::Node& node_1, + * const Graph::Node& node_2, + * GraphEdgeContainer::const_iterator iterator_1&, + * GraphEdgeContainer::const_iterator iterator_2&) + * Updates a pair of edge iterators to point to the "next" edge + * starting from a given state (node_1, node_2) in the product + * (or to (node_1.edges().end(), node_2.edges().end()) if this is + * not possible). + * + * See the files BuchiProduct.h and StateSpaceProduct.h for examples of classes + * used for instantiating the template. + * + * Given a class suitable for instantiating the Product template, a product is + * built with the constructor + * Product::Product + * (const Graph& graph_1, + * const Graph& graph_2). + * The product can be then analyzed by calling one of the emptiness checking + * functions described above. + * + * Note: All emptiness checking functions fail by throwing an exception of type + * Product::SizeException if + * `graph_1.size() * graph_2.size()' exceeds the maximum integer + * representables using Graph::size_type. The + * implementation does not support such products. + * + * Note: Operations in the Product class are not re-entrant. + * + *****************************************************************************/ + +template +class Product +{ +public: + Product /* Constructor. */ + (const Graph& g1, + const Graph& g2); + + ~Product(); /* Destructor. */ + + typedef typename Graph /* Type of product state */ + ::size_type size_type; /* identifiers. */ + + class ProductState; /* A class for accessing + * states in the product. + */ + + const ProductState operator[] /* Indexing operator. */ + (const size_type index) const; + + size_type stateId /* Constructs a product */ + (const size_type state_1, /* state identifier from */ + const size_type state_2) const; /* the identifiers of + * the state components. + */ + + const Graph::Node& /* Functions for */ + firstComponent(const size_type state) const; /* accessing the */ + const Graph::Node& /* components of a */ + secondComponent(const size_type state) const; /* product state. */ + + bool empty() const; /* Tells whether the + * product is (trivially) + * empty. + */ + + struct Witness /* Structure for */ + { /* representing witness */ + pair::Path, /* paths for */ + Graph::Path> /* the nonemptiness of */ + prefix; /* the product. */ + pair::Path, + Graph::Path> + cycle; + }; + + bool localEmptinessCheck /* Checks whether the */ + (const typename Graph /* subproduct rooted at */ + ::size_type s1_id, /* a product state */ + const typename Graph /* determined by a pair */ + ::size_type s2_id); /* of component state + * identifiers is empty. + */ + + const pair /* Checks a set of */ + globalEmptinessCheck /* subproducts for */ + (const typename Graph /* emptiness (see */ + ::size_type state_id, /* above). */ + Bitset& result, + const unsigned long int emptiness_check_size); + + void findWitness + (const size_type s1_id, const size_type s2_id, /* Checks whether the */ + Witness& witness); /* subproduct rooted at + * a product state + * determined by a pair + * of component state + * identifiers is empty. + * If this is the case, + * constructs also a + * witness for the + * nonemptiness. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ProductEdge; /* Classes for */ + class ProductEdgePointer; /* representing + * transitions in the + * product and + * "pointer-like" + * objects to them. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ProductEdgeCollection /* A class that mimics + * a container for + * transitions starting + * from a product state. + * (The container does + * not actually store the + * transitions; instead, + * it provides functions + * for constructing + * iterators that can be + * used to generate the + * transitions.) + */ + { + public: + explicit ProductEdgeCollection /* Constructor. */ + (const size_type state); + + /* default copy constructor */ + + ~ProductEdgeCollection(); /* Destructor. */ + + /* default assignment operator */ + + class const_iterator /* Iterator for generating + * the transitions starting + * from a product state. + */ + { + public: + const_iterator(); /* Default constructor. */ + + const_iterator + (const size_type state, /* Constructor. */ + const GraphEdgeContainer::const_iterator& + e1, + const GraphEdgeContainer::const_iterator& + e2); + + /* default copy constructor */ + + ~const_iterator(); /* Destructor. */ + + /* default assignment operator */ + + bool operator==(const const_iterator& it) /* Equality test between */ + const; /* iterators. */ + + bool operator!=(const const_iterator& it) /* Inequality test */ + const; /* between iterators. */ + + const ProductEdgePointer operator*() const; /* Dereferencing */ + const ProductEdge operator->() const; /* operators. */ + + const ProductEdgePointer operator++(); /* Prefix and postfix */ + const ProductEdgePointer operator++(int); /* increment operators. */ + + private: + size_type product_state; /* Product state */ + /* associated with the + * iterator. + */ + + GraphEdgeContainer::const_iterator edge_1; /* Pair of iterators */ + GraphEdgeContainer::const_iterator edge_2; /* from which product + * edges are determined. + */ + }; + + const const_iterator begin() const; /* Returns an iterator to + * the "beginning" of the + * list of transitions + * starting from the + * product state + * `this->product_state'. + */ + + const const_iterator end() const; /* Returns an iterator to + * the "end" of the list of + * transitions starting + * from the product state + * `this->product_state'. + */ + + private: + size_type product_state; /* Product state associated + * with the transition + * container. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class SizeException : public Exception /* An exception class to */ + /* be used in cases */ + /* where `size_type' */ + /* cannot hold values */ + /* large enough to */ + /* accommodate the */ + /* largest identifier */ + /* for a product state. */ + { + public: + SizeException(); /* Constructor. */ + + /* default copy constructor */ + + ~SizeException() throw(); /* Destructor. */ + + SizeException& operator= /* Assignment operator. */ + (const SizeException& e); + + /* `what' inherited from class Exception */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + typedef ProductEdge Edge; /* Type definitions */ + typedef ProductEdgeCollection EdgeContainerType; /* required for making */ + /* Product */ + struct PathElement; /* suitable for */ + typedef deque /* instantiating the */ + Path; /* SccCollection + * template (see + * SccCollection.h). + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +private: + Product(const Product&); /* Prevent copying and */ + Product& operator=(const Product&); /* assignment of Product + * objects. + */ + + class AcceptanceTracker; /* Callback operations */ + class SimpleEmptinessChecker; /* used when searching */ + class AcceptanceReachabilityTracker; /* the product for */ + class AcceptingComponentFinder; /* strongly connected + * components. + */ + + void addCycleSegment /* Helper function for */ + (pair::Path, /* constructing a */ + Graph::Path >& cycle, /* segment of the cycle */ + size_type source_state_id, Edge transition, /* in a witness for the */ + const size_type root_id, /* nonemptiness of the */ + const map, + ALLOC(PathElement) >& + predecessor) const; + + Operations operations; /* Operations for + * building the product + * on the fly. + */ + + bool too_large; /* Will be set to true + * if `size_type' cannot + * hold the maximum value + * that may be required for + * product state + * identifiers. Calling + * one of the emptiness + * checking operations on + * such a product results + * in a run-time exception. + */ + + const size_type state_id_multiplier; /* Size of the "second" + * component of the + * product. + */ + + static Product* product; /* Pointer to the "current" + * product (i.e., the + * product for which one + * of the emptiness + * checking operations was + * last called) to allow + * accessing it from + * member classes. + */ +}; + + + +/****************************************************************************** + * + * A template class for providing a Graph<>::Node-like interface to the states + * in a product (needed for accessing the transitions leaving from a state). + * + *****************************************************************************/ + +template +class Product::ProductState +{ +public: + ProductState(const size_type state); /* Constructor. */ + + /* default copy constructor */ + + ~ProductState(); /* Destructor. */ + + /* default assignment operator */ + + const EdgeContainerType& edges() const; /* Returns an object for + * generating the + * transitions starting + * from the state. + */ + +private: + EdgeContainerType outgoing_edges; /* Object for generating + * the transitions starting + * from the state. + */ +}; + + + +/****************************************************************************** + * + * A template class for providing a Graph<>::Edge-like interface to the + * transitions in a product. + * + *****************************************************************************/ + +template +class Product::ProductEdge +{ +public: + ProductEdge /* Constructor. */ + (const GraphEdgeContainer::const_iterator& e1, + const GraphEdgeContainer::const_iterator& e2); + + /* default copy constructor */ + + ~ProductEdge(); /* Destructor. */ + + /* default assignment operator */ + + const Graph::Edge& /* Functions for */ + firstComponent() const; /* accessing the */ + const Graph::Edge& /* components of a */ + secondComponent() const; /* product transition. */ + + size_type targetNode() const; /* Returns the target state + * of the transition. + */ + +private: + const Graph::Edge* edge_1; /* Components of the */ + const Graph::Edge* edge_2; /* transition. */ +}; + + + +/****************************************************************************** + * + * A template class for providing a constant pointer -like interface to + * ProductEdge objects. + * + *****************************************************************************/ + +template +class Product::ProductEdgePointer +{ +public: + ProductEdgePointer /* Constructor. */ + (const GraphEdgeContainer::const_iterator& e1, + const GraphEdgeContainer::const_iterator& e2); + + /* default copy constructor */ + + ~ProductEdgePointer(); /* Destructor. */ + + /* default assignment operator */ + + const ProductEdge& operator*() const; /* Dereferencing */ + const ProductEdge* operator->() const; /* operators. */ + +private: + ProductEdge edge; /* The product transition. + */ +}; + + + +/****************************************************************************** + * + * A template class for representing (product state, product transition) pairs + * in a path in the product. + * + *****************************************************************************/ + +template +struct Product::PathElement +{ + PathElement(const size_type s, const Edge& t); /* Constructor. */ + + /* default copy constructor */ + + ~PathElement(); /* Destructor. */ + + /* default assignment operator */ + + size_type state; /* Product state and */ + Edge transition; /* transition. */ +}; + + + +/****************************************************************************** + * + * A template class for tracking acceptance information in strongly connected + * product components by (essentially) recording the information into roots of + * the components. This is done using the method described by Couvreur in + * [J.-M. Couvreur. On-the-fly verification of linear temporal logic. + * In Proceedings of the FM'99 World Congress on Formal Methods in the + * Development of Computing Systems, Volume I, LNCS 1708, pp. 253--271. + * Springer-Verlag, 1999]. + * + *****************************************************************************/ + +template +class Product::AcceptanceTracker : + public VisitorInterface > +{ +public: + explicit AcceptanceTracker /* Constructor. */ + (const unsigned long int num_accept_sets); + + virtual ~AcceptanceTracker(); /* Destructor. */ + + /* `enter' inherited */ + + /* `backtrack' inherited */ + + /* `touch' inherited */ + + /* `leave' inherited */ + + virtual void addEdgeToComponent /* Adds the acceptance */ + (const Edge& t, const size_type scc_id); /* sets associated with + * a product transition + * to a nontrivial + * strongly connected + * component of the + * product. + */ + + virtual void addNodeToComponent /* Adds the acceptance */ + (const size_type state_id, /* sets associated with */ + const size_type scc_id); /* a product state to a */ + /* nontrivial strongly + * connected component + * of the product. + */ + + /* `beginComponent' inherited */ + + /* `insert' inherited */ + + virtual void endComponent /* Removes the */ + (const size_type scc_id); /* association between a + * nontrivial strongly + * connected product + * component and a set + * of acceptance sets + * when the component is + * not needed any + * longer. + */ + +protected: + typedef pair /* Association between */ + AcceptanceStackElement; /* a strongly connected + * component identifier + * and a collection of + * acceptance sets. + */ + + typedef deque /* the above */ + AcceptanceStack; /* associations. */ + + AcceptanceStack acceptance_stack; /* Stack for storing the + * dfs numbers of roots + * of strongly connected + * components and + * acceptance sets + * associated with them. + */ + + BitArray* acceptance_sets; /* Used for manipulating + * the stack. + */ + + const unsigned long int /* Number of acceptance */ + number_of_acceptance_sets; /* sets in the product. */ + +private: + AcceptanceTracker(const AcceptanceTracker&); /* Prevent copying and */ + AcceptanceTracker& operator= /* assignment of */ + (const AcceptanceTracker&); /* AcceptanceTracker + * objects. + */ +}; + + + +/****************************************************************************** + * + * A template class for checking a product for emptiness. + * + *****************************************************************************/ + +template +class Product::SimpleEmptinessChecker : public AcceptanceTracker +{ +public: + explicit SimpleEmptinessChecker /* Constructor. */ + (const unsigned long int num_accept_sets); + + ~SimpleEmptinessChecker(); /* Destructor. */ + + typedef int SccType; /* Dummy type definition + * required for supporting + * the expected class + * interface. + */ + + const SccType& operator()() const; /* Dummy function required + * for supporting the + * expected class + * interface. + */ + + /* `enter' inherited */ + + /* `backtrack' inherited */ + + /* `touch' inherited */ + + /* `leave' inherited */ + + void addEdgeToComponent /* Adds the acceptance */ + (const Edge& t, const size_type scc_id); /* sets associated with + * a product transition + * to a nontrivial + * strongly connected + * component of the + * product and aborts + * the emptiness check + * if an accepting + * strongly connected + * component is + * detected. + */ + + void addNodeToComponent /* Adds the acceptance */ + (const size_type state, /* sets associated with */ + const size_type scc_id); /* a product state to a + * nontrivial strongly + * connected component + * of the product and + * aborts the emptiness + * check if an accepting + * strongly connected + * component is + * detected. + */ + + /* `beginComponent' inherited */ + + /* `insert' inherited */ + + /* `endComponent' inherited */ + +private: + SimpleEmptinessChecker /* Prevent copying and */ + (const SimpleEmptinessChecker&); /* assignment of */ + SimpleEmptinessChecker& /* SimpleEmptiness- */ + operator=(const SimpleEmptinessChecker&); /* Checker objects. */ + + void abortIfNonempty() const; /* Aborts the search when + * an accepting strongly + * connected component is + * found. + */ + + SccType dummy; /* Dummy variable needed + * for implementing the + * operator() function. + */ +}; + + + +/****************************************************************************** + * + * A template class for tracking the reachability of accepting strongly + * connected components in a product. + * + *****************************************************************************/ + +template +class Product::AcceptanceReachabilityTracker + : public Product::AcceptanceTracker +{ +public: + explicit AcceptanceReachabilityTracker /* Constructor. */ + (const unsigned long int num_accept_sets); + + ~AcceptanceReachabilityTracker(); /* Destructor. */ + + typedef int SccType; /* Dummy type definition + * required for supporting + * the expected class + * interface. + */ + + const SccType& operator()() const; /* Dummy function required + * for supporting the + * expected class + * interface. + */ + + void enter(const size_type); /* Function called when + * entering a new product + * state. + */ + + void backtrack /* Function called when */ + (const size_type source, const Edge&, /* backtracking from a */ + const size_type target); /* product state. */ + + void touch /* Function called when */ + (const size_type source, const Edge& edge, /* processing an edge */ + const size_type target); /* with a target state + * that has already been + * visited during the + * search. + */ + + /* `leave' inherited */ + + /* `addEdgeToComponent' inherited */ + + /* `addNodeToComponent' inherited */ + + void beginComponent /* Tests whether the */ + (const size_type, const size_type state_id); /* strongly connected + * component about to + * be extracted from the + * product is an + * accepting component, + * or if it contains a + * state from which such + * a component is known + * to be reachable. + */ + + void insert(const size_type state); /* Function used for + * updating accepting + * component reachability + * information while + * extracting states from + * a product component. + */ + + /* `endComponent' inherited */ + + bool isMarked(const size_type state) const; /* Tests whether an + * accepting component is + * known to be reachable + * from a product state. + */ + + size_type numberOfStates() const; /* Tells the number of + * product states explored + * during the search. + */ + + unsigned long int numberOfTransitions() const; /* Tells the number of + * product transitions + * explored during the + * search. + */ + +private: + AcceptanceReachabilityTracker /* Prevent copying and */ + (const AcceptanceReachabilityTracker&); /* assignment of */ + AcceptanceReachabilityTracker& /* AcceptanceSet- */ + operator= /* ReachabilityTracker */ + (const AcceptanceReachabilityTracker&); /* objects. */ + + void markState(const size_type state); /* Adds a product state to + * the set of states from + * which an accepting + * component is known to be + * reachable. + */ + + set, /* Set of states from */ + ALLOC(size_type) > /* which an accepting */ + reachability_info; /* component is known to + * be reachable in the + * product. + */ + + size_type number_of_states; /* Number of states + * explored during the + * search. + */ + + unsigned long int number_of_transitions; /* Number of transitions + * explored during the + * search. + */ + + bool mark_scc; /* Used for determining + * whether to insert states + * into `this-> + * reachability_info' while + * extracting a strongly + * connected component from + * the product. + */ + + SccType dummy; /* Dummy variable needed + * for implementing the + * operator() function. + */ +}; + + + +/****************************************************************************** + * + * A template class for finding accepting maximal strongly connected components + * in a product. + * + *****************************************************************************/ + +template +class Product::AcceptingComponentFinder : + public Product::AcceptanceTracker +{ +public: + explicit AcceptingComponentFinder /* Constructor. */ + (const unsigned long int num_accept_sets); + + ~AcceptingComponentFinder(); /* Destructor. */ + + typedef set, /* Type definition for */ + ALLOC(size_type) > /* the set of product */ + SccType; /* state identifiers in + * an accepting + * strongly connected + * component. + */ + + const SccType& operator()() const; /* Returns the last + * accepting maximal + * strongly connected + * component found in the + * product. + */ + + /* `enter' inherited */ + + /* `backtrack' inherited */ + + /* `touch' inherited */ + + /* `leave' inherited */ + + /* `addEdgeToComponent' inherited */ + + /* `addNodeToComponent' inherited */ + + void beginComponent /* Tests whether the */ + (const size_type, const size_type); /* maximal strongly + * connected component + * that is about to be + * extracted from the + * product is an + * accepting component. + */ + + void insert(const size_type state); /* Inserts a state to an + * accepting component. + */ + + /* `endComponent' inherited */ + +private: + AcceptingComponentFinder /* Prevent copying and */ + (const AcceptingComponentFinder&); /* assignment of */ + AcceptingComponentFinder& /* AcceptingComponent- */ + operator=(const AcceptingComponentFinder&); /* Finder objects. */ + + SccType scc; /* Set of product state + * identifiers forming the + * last accepting strongly + * connected component + * found in the product. + */ + + bool construct_component; /* Used for determining + * whether the states + * extracted from a + * strongly connected + * component in the product + * should be inserted into + * `this->scc'. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for template class Product. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::Product + (const Graph& g1, const Graph& g2) + : operations(g1, g2), state_id_multiplier(g2.size()) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product. + * + * Arguments: g1, g2 -- Constant references to the components of the + * product. + * + * Returns: Nothing. If `Product::size_type' cannot hold + * values large enough to accommodate the largest identifier for + * a product state, `this->too_large' is set to true to cause + * all emptiness checking operations on the product to fail by + * throwing a Product::SizeException. + * + * ------------------------------------------------------------------------- */ +{ + too_large = (!g2.empty() && + g1.size() > (static_cast(-1) / g2.size())); +} + +/* ========================================================================= */ +template +inline Product::~Product() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::ProductState +Product::operator[] + (const typename Product::size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Indexing operator for class Product. + * + * Argument: index -- Index of a state of the product. + * + * Returns: A ProductState object corresponding to the state with the + * given index. + * + * ------------------------------------------------------------------------- */ +{ + return ProductState(index); +} + +/* ========================================================================= */ +template +inline typename Product::size_type Product + ::stateId + (const size_type state_1, const size_type state_2) const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the product state identifier corresponding to + * identifiers of the state components. + * + * Arguments: state_1, state_2 -- Identifiers for the product state + * components. + * + * Returns: Identifier of the product state corresponding to the + * components. + * + * ------------------------------------------------------------------------- */ +{ + return (state_1 * state_id_multiplier) + state_2; +} + +/* ========================================================================= */ +template +inline const Graph::Node& +Product::firstComponent(const size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing the "first" component state of a + * product state. + * + * Arguments: state -- Identifier of a product state. + * + * Returns: A constant reference to the state corresponding to the + * "first" component of the product state. + * + * ------------------------------------------------------------------------- */ +{ + return operations.firstComponent(state / state_id_multiplier); +} + +/* ========================================================================= */ +template +inline const Graph::Node& +Product::secondComponent(const size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing the "second" component state of a + * product state. + * + * Arguments: state -- Identifier of a product state. + * + * Returns: A constant reference to the state corresponding to the + * "second" component of the product state. + * + * ------------------------------------------------------------------------- */ +{ + return operations.secondComponent(state % state_id_multiplier); +} + +/* ========================================================================= */ +template +inline bool Product::empty() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the product is (trivially) empty, i.e., if + * either of its components has no states. + * + * Arguments: None. + * + * Returns: true iff the product is trivially empty. + * + * ------------------------------------------------------------------------- */ +{ + return operations.empty(); +} + + + +/****************************************************************************** + * + * Function definitions for template class Product. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +bool Product::localEmptinessCheck + (const Graph::size_type s1_id, + const Graph::size_type s2_id) +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether the subproduct rooted at a product state + * determined by a pair of component state identifiers is not + * empty. + * + * Arguments: s1_id, s2_id -- Identifiers for the product state + * components. + * + * Returns: true iff the subproduct rooted at the product state + * determined by `s1_id' and `s2_id' is not empty. Throws a + * Product::SizeException if the product is too + * large to handle. + * + * ------------------------------------------------------------------------- */ +{ + if (too_large) + throw SizeException(); + + if (empty()) + return false; + + product = this; + + SimpleEmptinessChecker ec(operations.numberOfAcceptanceSets()); + typedef SccCollection, SimpleEmptinessChecker> + ProductSccCollection; + + ProductSccCollection sccs(*this, ec); + + try + { + for (typename ProductSccCollection::iterator scc + = sccs.begin(stateId(s1_id, s2_id)); + scc != sccs.end(); + ++scc) + { + if (::user_break) + throw UserBreakException(); + } + } + catch (const int) + { + return true; + } + + return false; +} + +/* ========================================================================= */ +template +const pair::size_type, unsigned long int> +Product::globalEmptinessCheck + (const Graph::size_type state_id, + Bitset& result, const unsigned long int emptiness_check_size) +/* ---------------------------------------------------------------------------- + * + * Description: Checks a set of subproducts of the product for emptiness. + * + * Arguments: state_id -- Identifier of a state in the first + * component of the product. + * result -- A reference to a Bitset for storing + * the result of the emptiness check. + * The set should have room for at + * least `emptiness_check_size' bits. + * emptiness_check_size -- Determines the scope of the + * emptiness check (see below). + * + * Returns: A pair giving the numbers of product states and transitions + * generated during the emptiness check. The result of the + * emptiness check itself is stored into `result' such that + * the i'th bit (for all 0 <= i < emptiness_check_size) in the + * bit set will be 1 iff the subproduct rooted at the product + * state determined by the pair of state identifiers + * (state_id, i) is nonempty. + * + * The function throws a Product::SizeException if + * the product may be too large to handle. + * + * ------------------------------------------------------------------------- */ +{ + if (too_large) + throw SizeException(); + + result.clear(); + + if (empty()) + return make_pair(0, 0); + + product = this; + + AcceptanceReachabilityTracker rt(operations.numberOfAcceptanceSets()); + + typedef SccCollection, AcceptanceReachabilityTracker> + ProductSccCollection; + + ProductSccCollection sccs(*this, rt); + + for (Graph::size_type state = 0; + state < emptiness_check_size; + ++state) + { + for (typename ProductSccCollection::iterator scc + = sccs.begin(stateId(state_id, state)); + scc != sccs.end(); + ++scc) + { + if (::user_break) + throw UserBreakException(); + } + } + + for (Graph::size_type state = 0; + state < emptiness_check_size; + ++state) + { + if (rt.isMarked(stateId(state_id, state))) + result.setBit(state); + } + + return make_pair(rt.numberOfStates(), rt.numberOfTransitions()); +} + +/* ========================================================================= */ +template +void Product::findWitness + (const typename Graph::size_type s1_id, + const typename Graph::size_type s2_id, + typename Product::Witness& witness) +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether the subproduct rooted at a product state + * determined by a pair of component state identifiers is not + * empty. If this is the case, constructs a witness for the + * nonemptiness. + * + * Arguments: s1_id, s2_id -- Identifiers for the product state + * components. + * witness -- A reference to an object for storing a + * witness if such a witness exists. + * + * Returns: Nothing. A witness was found iff + * `!witness.cycle.first.empty() + * && !witness.cycle.second.empty()' holds after the call. + * + * The function throws a Product::SizeException if + * the product may be too large to handle. + * + * ------------------------------------------------------------------------- */ +{ + if (too_large) + throw SizeException(); + + witness.prefix.first.clear(); + witness.prefix.second.clear(); + witness.cycle.first.clear(); + witness.cycle.second.clear(); + + if (empty()) + return; + + product = this; + const unsigned long int number_of_acceptance_sets + = operations.numberOfAcceptanceSets(); + const size_type start_state = stateId(s1_id, s2_id); + + AcceptingComponentFinder acf(number_of_acceptance_sets); + typedef SccCollection, AcceptingComponentFinder> + ProductSccCollection; + + ProductSccCollection sccs(*this, acf); + + for (typename ProductSccCollection::iterator scc = sccs.begin(start_state); + scc != sccs.end(); + ++scc) + { + if (::user_break) + throw UserBreakException(); + + if (!scc->empty()) + { + /* + * The prefix of the witness consists of a path from the given product + * state to a state in an accepting strongly connected product + * component. + */ + + Path path; + scc.getPath(path); + + for (typename Path::const_iterator path_element = path.begin(); + path_element != path.end(); + ++path_element) + { + witness.prefix.first.push_back + (Graph::PathElement + (path_element->state / state_id_multiplier, + path_element->transition.firstComponent())); + witness.prefix.second.push_back + (Graph::PathElement + (path_element->state % state_id_multiplier, + path_element->transition.secondComponent())); + } + + /* + * Construct an accepting cycle by performing a breadth-first search + * in the MSCC. + */ + + const size_type search_start_state + = path.empty() ? start_state : path.back().transition.targetNode(); + + BitArray collected_acceptance_sets(number_of_acceptance_sets); + collected_acceptance_sets.clear(number_of_acceptance_sets); + operations.mergeAcceptanceInformation + (firstComponent(search_start_state), + secondComponent(search_start_state), collected_acceptance_sets); + + unsigned long int number_of_collected_acceptance_sets + = collected_acceptance_sets.count(number_of_acceptance_sets); + + deque search_queue; + set, ALLOC(size_type) > visited; + map, ALLOC(PathElement) > + shortest_path_predecessor; + + size_type bfs_root = search_start_state; + +continue_bfs: + search_queue.clear(); + search_queue.push_back(bfs_root); + visited.clear(); + visited.insert(bfs_root); + shortest_path_predecessor.clear(); + + while (!search_queue.empty()) + { + const EdgeContainerType transitions + = ProductState(search_queue.front()).edges(); + + for (typename EdgeContainerType::const_iterator transition + = transitions.begin(); + transition != transitions.end(); + ++transition) + { + const size_type target = (*transition)->targetNode(); + if (scc->find(target) == scc->end()) + continue; + + if (visited.find(target) == visited.end()) + { + visited.insert(target); + shortest_path_predecessor.insert + (make_pair(target, PathElement(search_queue.front(), + **transition))); + search_queue.push_back(target); + + if (number_of_collected_acceptance_sets + < number_of_acceptance_sets) + operations.mergeAcceptanceInformation + (firstComponent(target), secondComponent(target), + collected_acceptance_sets); + } + + if (number_of_collected_acceptance_sets < number_of_acceptance_sets) + { + /* + * Test whether the current product transition or the target + * state of the transition covers new acceptance sets. If + * this is the case, construct the next segment of the cycle + * and begin a new breadth-first search in the target state of + * the transition. + */ + + operations.mergeAcceptanceInformation + ((*transition)->firstComponent(), + (*transition)->secondComponent(), collected_acceptance_sets); + + const unsigned long int num + = collected_acceptance_sets.count(number_of_acceptance_sets); + if (num > number_of_collected_acceptance_sets) + { + number_of_collected_acceptance_sets = num; + + addCycleSegment(witness.cycle, search_queue.front(), + **transition, bfs_root, + shortest_path_predecessor); + + if (number_of_collected_acceptance_sets + == number_of_acceptance_sets + && target == search_start_state) + return; + + bfs_root = target; + goto continue_bfs; + } + } + else if (target == search_start_state) + { + /* + * If all acceptance sets have been collected and the current + * product transition points to the first state of the cycle, + * the cycle is complete. + */ + + addCycleSegment(witness.cycle, search_queue.front(), **transition, + bfs_root, shortest_path_predecessor); + return; + } + } + + search_queue.pop_front(); + } + + throw Exception + ("Product::findWitness(...): internal error [cycle construction " + "failed]"); + } + } +} + +/* ========================================================================= */ +template +void Product::addCycleSegment + (pair::Path, Graph::Path>& + cycle, + size_type source_state_id, Edge transition, const size_type root_id, + const map, ALLOC(PathElement) >& + predecessor) const +/* ---------------------------------------------------------------------------- + * + * Description: Helper function for constructing a segment of an accepting + * cycle in the product. + * + * Arguments: cycle -- A reference to a pair of paths for + * storing the result. + * source_state_id -- Identifier of the last product state in + * the cycle segment. + * transition -- Last product transition in the cycle + * segment. + * root_id -- Identifier of the first product state in + * the cycle segment. + * predecessor -- Mapping between states and their + * predecessors in the cycle segment. + * + * Returns: Nothing. The segment of the cycle is appended to `cycle'. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Path first_segment; + Graph::Path second_segment; + + while (1) + { + first_segment.push_front + (Graph::PathElement + (source_state_id / state_id_multiplier, transition.firstComponent())); + second_segment.push_front + (Graph::PathElement + (source_state_id % state_id_multiplier, + transition.secondComponent())); + + if (source_state_id == root_id) + { + cycle.first.insert(cycle.first.end(), first_segment.begin(), + first_segment.end()); + cycle.second.insert(cycle.second.end(), second_segment.begin(), + second_segment.end()); + return; + } + + const PathElement& p = predecessor.find(source_state_id)->second; + source_state_id = p.state; + transition = p.transition; + } +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::ProductState. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::ProductState::ProductState + (const size_type state) : outgoing_edges(state) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product::ProductState. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::ProductState::~ProductState() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product::ProductState. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::EdgeContainerType& +Product::ProductState::edges() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an object for generating the transitions starting + * from a product state. + * + * Arguments: None. + * + * Returns: A constant reference to an object that can be used for + * generating the transitions starting from the state. + * + * ------------------------------------------------------------------------- */ +{ + return outgoing_edges; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::ProductEdge. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::ProductEdge::ProductEdge + (const GraphEdgeContainer::const_iterator& e1, + const GraphEdgeContainer::const_iterator& e2) + : edge_1(*e1), edge_2(*e2) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product::ProductEdge. + * + * Arguments: e1, e2 -- Iterators pointing to the components of the + * product transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::ProductEdge::~ProductEdge() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product::ProductEdge. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const Graph::Edge& +Product::ProductEdge::firstComponent() const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing the "first" component of a product + * transition. + * + * Arguments: None. + * + * Returns: A constant reference to the "first" component of the + * transition. + * + * ------------------------------------------------------------------------- */ +{ + return *edge_1; +} + +/* ========================================================================= */ +template +inline const Graph::Edge& +Product::ProductEdge::secondComponent() const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing the "second" component of a product + * transition. + * + * Arguments: None. + * + * Returns: A constant reference to the "second" component of the + * transition. + * + * ------------------------------------------------------------------------- */ +{ + return *edge_2; +} + +/* ========================================================================= */ +template +inline typename Product::size_type +Product::ProductEdge::targetNode() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the target state of a product transition. + * + * Arguments: None. + * + * Returns: Identifier of the target state of the product transition. + * + * ------------------------------------------------------------------------- */ +{ + return product->stateId(edge_1->targetNode(), edge_2->targetNode()); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::ProductEdgePointer. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::ProductEdgePointer::ProductEdgePointer + (const GraphEdgeContainer::const_iterator& e1, + const GraphEdgeContainer::const_iterator& e2) + : edge(e1, e2) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::ProductEdgePointer. + * + * Arguments: e1, e2 -- Iterators pointing to the components of the + * product transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::ProductEdgePointer::~ProductEdgePointer() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::ProductEdgePointer. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdge& +Product::ProductEdgePointer::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * Product::ProductEdgePointer. + * + * Arguments: None. + * + * Returns: A reference to the product transition associated with the + * object. + * + * ------------------------------------------------------------------------- */ +{ + return edge; +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdge* +Product::ProductEdgePointer::operator->() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * Product::ProductEdgePointer. + * + * Arguments: None. + * + * Returns: A pointer to the product transition associated with the + * object. + * + * ------------------------------------------------------------------------- */ +{ + return &edge; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::ProductEdgeCollection. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::ProductEdgeCollection::ProductEdgeCollection + (const size_type state) : product_state(state) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::ProductEdgeCollection. + * + * Argument: state -- Identifier of a product state. The + * ProductEdgeCollection object will mimic a + * container for the product transitions starting + * from this state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::ProductEdgeCollection::~ProductEdgeCollection() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::ProductEdgeCollection. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdgeCollection + ::const_iterator +Product::ProductEdgeCollection::begin() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an iterator for generating the transitions starting + * from the product state identified by `this->product_state'. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return const_iterator + (product_state, + product->firstComponent(product_state).edges().begin(), + product->secondComponent(product_state).edges().begin()); +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdgeCollection + ::const_iterator +Product::ProductEdgeCollection::end() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an iterator pointing to the "end" of the collection + * of transitions starting from the product state identified by + * `this->product_state'. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return const_iterator + (product_state, + product->firstComponent(product_state).edges().end(), + product->secondComponent(product_state).edges().end()); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::ProductEdgeCollection::const_iterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::ProductEdgeCollection::const_iterator + ::const_iterator() : product_state(0) +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::ProductEdgeCollection::const_iterator + ::const_iterator + (const size_type state, + const GraphEdgeContainer::const_iterator& e1, + const GraphEdgeContainer::const_iterator& e2) : + product_state(state), edge_1(e1), edge_2(e2) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: state -- Identifier of a product state to associate with + * the iterator. + * e1, e1 -- Constant references to a pair of iterators + * pointing to a pair of transitions starting from + * the component states of the product state. These + * iterators are used to determine where to start + * iterating over the product transitions. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + product->operations.validateEdgeIterators + (product->firstComponent(product_state), + product->secondComponent(product_state), + edge_1, edge_2); +} + +/* ========================================================================= */ +template +inline Product::ProductEdgeCollection::const_iterator + ::~const_iterator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline bool Product::ProductEdgeCollection::const_iterator + ::operator== + (const const_iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality test between two + * Product::ProductEdgeCollection::const_iterators. + * + * Argument: it -- A constant reference to an iterator to compare for + * equality. It is assumed that the iterators are + * associated with the same product state; the result of + * a comparison between iterators associated with + * different product states is undefined. + * + * Returns: true iff `it' and `*this' point to the same transition in the + * product. + * + * ------------------------------------------------------------------------- */ +{ + return (it.edge_1 == edge_1 && it.edge_2 == edge_2); +} + +/* ========================================================================= */ +template +inline bool Product::ProductEdgeCollection::const_iterator + ::operator!= + (const const_iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Inequality test between two + * Product::ProductEdgeCollection::const_iterators. + * + * Argument: it -- A constant reference to an iterator to compare for + * equality. It is assumed that the iterators are + * associated with the same product state; the result of + * a comparison between iterators associated with + * different product states is undefined. + * + * Returns: true iff `it' and `*this' point to different transitions in + * the product. + * + * ------------------------------------------------------------------------- */ +{ + return (it.edge_1 != edge_1 || it.edge_2 != edge_2); +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdgePointer +Product::ProductEdgeCollection::const_iterator::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: An object of type Product::ProductEdgePointer + * that allows access to the product transition pointed to by + * the iterator. + * + * ------------------------------------------------------------------------- */ +{ + return ProductEdgePointer(edge_1, edge_2); +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdge +Product::ProductEdgeCollection::const_iterator::operator->() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: A Product::ProductEdge corresponding to the + * product transition pointed to by the iterator. + * + * ------------------------------------------------------------------------- */ +{ + return ProductEdge(edge_1, edge_2); +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdgePointer +Product::ProductEdgeCollection::const_iterator::operator++() +/* ---------------------------------------------------------------------------- + * + * Description: Prefix increment operator for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: An object of type Product::ProductEdgePointer + * that behaves like a pointer to the product transition + * obtained by advancing the iterator in the sequence of product + * transitions. + * + * ------------------------------------------------------------------------- */ +{ + product->operations.incrementEdgeIterators + (product->firstComponent(product_state), + product->secondComponent(product_state), + edge_1, edge_2); + return ProductEdgePointer(edge_1, edge_2); +} + +/* ========================================================================= */ +template +inline const typename Product::ProductEdgePointer +Product::ProductEdgeCollection::const_iterator::operator++(int) +/* ---------------------------------------------------------------------------- + * + * Description: Postfix increment operator for class + * Product::ProductEdgeCollection::const_iterator. + * + * Arguments: None. + * + * Returns: An object of type Product::ProductEdgePointer + * that behaves like a pointer to the product transition pointed + * to by the iterator before advancing it in the sequence of + * product transitions. + * + * ------------------------------------------------------------------------- */ +{ + const typename Product::ProductEdgePointer edge(edge_1, edge_2); + product->operations.incrementEdgeIterators + (product->firstComponent(product_state), + product->secondComponent(product_state), + edge_1, edge_2); + return edge; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::PathElement. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::PathElement::PathElement + (const size_type s, const Edge& t) : state(s), transition(t) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product::PathElement. + * + * Arguments: s, t -- Product state and transition associated with the + * element. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::PathElement::~PathElement() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product::PathElement. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::SizeException. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::SizeException::SizeException() : + Exception("product may be too large") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product::SizeException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::SizeException::~SizeException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product::SizeException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline typename Product::SizeException& +Product::SizeException::operator= + (const typename Product::SizeException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * Product::SizeException. Assigns the value of + * another Product::SizeException to `this' one. + * + * Arguments: e -- A reference to a constant object of type + * ProductSizeException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::AcceptanceTracker. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::AcceptanceTracker::AcceptanceTracker + (const unsigned long int num_accept_sets) + : number_of_acceptance_sets(num_accept_sets) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Product::AcceptanceTracker. + * + * Arguments: num_accept_sets -- Number of acceptance sets in the + * product. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + /* Initialize `acceptance_stack' with a sentinel element. */ + acceptance_stack.push_front(make_pair(0, new BitArray(0))); +} + +/* ========================================================================= */ +template +inline Product::AcceptanceTracker::~AcceptanceTracker() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Product::AcceptanceTracker. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (AcceptanceStack::iterator a = acceptance_stack.begin(); + a != acceptance_stack.end(); + ++a) + delete a->second; +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceTracker::addEdgeToComponent + (const typename Product::Edge& t, + const typename Product::size_type scc_id) +/* ---------------------------------------------------------------------------- + * + * Description: Adds the acceptance sets of the product transition `t' + * (inside a strongly connected product component) to the + * collection of acceptance sets associated with the strongly + * connected component identifier (a dfs number of a product + * state in Tarjan's algorithm). (The component is therefore + * nontrivial, because it contains a transition.) This is done + * as described by Couvreur + * [J.-M. Couvreur. On-the-fly verification of linear + * temporal logic. In Proceedings of the FM'99 World + * Congress on Formal Methods in the Development of + * Computing Systems, Volume I, LNCS 1708, pp. 253--271. + * Springer-Verlag, 1999] + * by first collapsing all elements in the top part of + * `this->acceptance_stack' (*) with strongly connected + * component identifiers greater than or equal to `scc_id' into + * a single element by taking the union of their acceptance sets + * and then merging the acceptance sets of the transition `t' + * with this element. + * After the call, the top element of `this->acceptance_stack' + * will have the scc id `scc_id', and `this->acceptance_sets' + * points to the acceptance sets associated with this stack + * element. + * + * (*) It is assumed that the contents of + * `this->acceptance_stack' form (from top to bottom) a sequence + * of elements (id1,a1), (id2,a2), (id3,a3), ..., where + * id1 > id2 > id3 > ... . The function maintains this + * invariant. + * + * Arguments: t -- A constant reference to the product transition. + * scc_id -- Identifier of the strongly connected component + * (assumed to be >= 1). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + acceptance_sets = 0; + while (acceptance_stack.front().first >= scc_id) + { + if (acceptance_sets != 0) + { + acceptance_stack.front().second->bitwiseOr + (*acceptance_sets, number_of_acceptance_sets); + delete acceptance_sets; + } + acceptance_sets = acceptance_stack.front().second; + if (acceptance_stack.front().first == scc_id) + goto merge_sets; + + acceptance_stack.pop_front(); + } + + if (acceptance_sets == 0) + { + acceptance_sets = new BitArray(number_of_acceptance_sets); + acceptance_sets->clear(number_of_acceptance_sets); + } + acceptance_stack.push_front(make_pair(scc_id, acceptance_sets)); +merge_sets: + product->operations.mergeAcceptanceInformation + (t.firstComponent(), t.secondComponent(), *acceptance_sets); +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceTracker::addNodeToComponent + (const typename Product::size_type state_id, + const typename Product::size_type scc_id) +/* ---------------------------------------------------------------------------- + * + * Description: Adds the acceptance sets of the product state `state_id' to + * the collection of acceptance sets associated with a + * nontrivial strongly connected component identifier (a dfs + * number of a product state in Tarjan's algorithm). + * + * Arguments: state_id -- Identifier of the product state. + * scc_id -- Identifier of the strongly connected component + * (assumed to be >= 1). + * + * Returns: Nothing. Upon return, `this->acceptance_sets' either points + * to the acceptance sets associated with the topmost element of + * `this->acceptance_sets', or, if the component is a + * trivial strongly connected component, + * `this->acceptance_sets == 0'. + * + * ------------------------------------------------------------------------- */ +{ + /* + * When this function gets called, then depth-first search guarantees that + * the strongly connected component identifier of the topmost element (if + * such an element exists) of `this->acceptance_stack' is <= 'scc_id'. + * Furthermore, the strongly connected is nontrivial only if equality holds + * in the above test. + */ + + if (acceptance_stack.front().first < scc_id) + { + acceptance_sets = 0; + return; + } + + acceptance_sets = acceptance_stack.front().second; + product->operations.mergeAcceptanceInformation + (product->firstComponent(state_id), product->secondComponent(state_id), + *acceptance_sets); +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceTracker::endComponent + (const typename Product::size_type scc_id) +/* ---------------------------------------------------------------------------- + * + * Description: Removes the association between a nontrivial strongly + * connected component identifier and a collection of acceptance + * sets when the component is not needed any longer. (This + * function gets called after extracting a maximal strongly + * connected component from the product. It is safe to remove + * the association at this point, because the search will not + * enter the component afterwards, nor can any state visited + * in the future belong to the strongly connected component + * identified by `scc_id'.) + * + * Argument: scc_id -- Identifier of the strongly connected component. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + /* + * Because the depth-first search made a call to `addNodeToComponent' before + * extracting the component from the product, the topmost element (if any) + * of `this->acceptance_stack' is guaranteed to have scc id <= `scc_id' at + * this point. + */ + + if (acceptance_stack.front().first == scc_id) + { + delete acceptance_stack.front().second; + acceptance_stack.pop_front(); + } +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::SimpleEmptinessChecker. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::SimpleEmptinessChecker::SimpleEmptinessChecker + (const unsigned long int num_accept_sets) + : AcceptanceTracker(num_accept_sets), dummy(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::SimpleEmptinessChecker. + * + * Arguments: num_accept_sets -- Number of acceptance sets in the + * product. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::SimpleEmptinessChecker::~SimpleEmptinessChecker() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::SimpleEmptinessChecker. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::SimpleEmptinessChecker::SccType& +Product::SimpleEmptinessChecker::operator()() const +/* ---------------------------------------------------------------------------- + * + * Description: Dummy function required for supporting the expected class + * interface. + * + * Arguments: None. + * + * Returns: A constant reference to `this->dummy'. + * + * ------------------------------------------------------------------------- */ +{ + return dummy; +} + +/* ========================================================================= */ +template +inline void Product::SimpleEmptinessChecker::addEdgeToComponent + (const typename Product::Edge& t, + const typename Product::size_type scc_id) +/* ---------------------------------------------------------------------------- + * + * Description: Adds a transition to a nontrivial strongly connected product + * component and aborts the search if the addition of the + * transition in the component makes the component accepting. + * + * Arguments: t -- A constant reference to the product transition to + * be added to the component. + * scc_id -- Identifier of the strongly connected component. + * + * Returns: Nothing; aborts the search by throwing the constant 0 if the + * addition of the transition in the component makes the + * component accepting. + * + * ------------------------------------------------------------------------- */ +{ + AcceptanceTracker::addEdgeToComponent(t, scc_id); + /* `this->acceptance_sets' points to the acceptance sets associated with + * the nontrivial SCC `scc_id' at this point. */ + abortIfNonempty(); +} + +/* ========================================================================= */ +template +inline void Product::SimpleEmptinessChecker::addNodeToComponent + (const typename Product::size_type state, + const typename Product::size_type scc_id) +/* ---------------------------------------------------------------------------- + * + * Description: Adds a state to a nontrivial strongly connected product + * component and aborts the search if the addition of the state + * in the component makes the component accepting. + * + * Arguments: state -- Identifier of a product state to be added to the + * component. + * scc_id -- Identifier of the strongly connected component. + * + * Returns: Nothing; aborts the search by throwing the constant 0 if the + * addition of the state in the component makes the component + * accepting. + * + * ------------------------------------------------------------------------- */ +{ + AcceptanceTracker::addNodeToComponent(state, scc_id); + /* If `this->acceptance_sets != 0', then `this->acceptance_sets' points to + * the acceptance sets associated with the nontrivial SCC `scc_id'; + * otherwise the component `scc_id' is a trivial SCC. */ + if (this->acceptance_sets != 0) + abortIfNonempty(); +} + +/* ========================================================================= */ +template +inline void Product::SimpleEmptinessChecker::abortIfNonempty() + const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the strongly connected component with + * acceptance sets pointed to by `this->acceptance_sets' is an + * accepting component. This holds if all bits in the + * acceptance set bit vector pointed to by + * `this->acceptance_sets' are set to 1. + * + * Arguments: None. + * + * Returns: Nothing; throws the constant 0 if the component with + * acceptance sets pointed to by `this->acceptance_sets' is an + * accepting strongly connected component. + * + * ------------------------------------------------------------------------- */ +{ + if (this->acceptance_sets->count(this->number_of_acceptance_sets) + == this->number_of_acceptance_sets) + throw 0; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::AcceptanceReachabilityTracker. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::AcceptanceReachabilityTracker + ::AcceptanceReachabilityTracker + (const unsigned long int num_accept_sets) : + AcceptanceTracker(num_accept_sets), number_of_states(0), + number_of_transitions(0), mark_scc(false), dummy(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::AcceptanceReachabilityTracker. + * + * Arguments: num_accept_sets -- Number of acceptance sets in the + * product. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::AcceptanceReachabilityTracker + ::~AcceptanceReachabilityTracker() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::AcceptanceReachabilityTracker. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline +const typename Product::AcceptanceReachabilityTracker::SccType& +Product::AcceptanceReachabilityTracker::operator()() const +/* ---------------------------------------------------------------------------- + * + * Description: Dummy function required for supporting the expected class + * interface. + * + * Arguments: None. + * + * Returns: A constant reference to `this->dummy'. + * + * ------------------------------------------------------------------------- */ +{ + return dummy; +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::enter + (const typename Product::size_type) +/* ---------------------------------------------------------------------------- + * + * Description: Function called when entering a new state in the product. + * Increments the number of product states that have been + * explored. + * + * Arguments: The single argument is required to support the expected class + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ++number_of_states; +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::backtrack + (const typename Product::size_type source, + const typename Product::Edge&, + const typename Product::size_type target) +/* ---------------------------------------------------------------------------- + * + * Description: Function called when backtracking from a state in the + * product. Increments the number of product edges that have + * been explored. Additionally, if the state from which the + * search backtracks belongs to the set of states from which an + * accepting strongly connected component is known to be + * reachable in the product, adds also the state to which the + * search backtracks into this set of states. + * + * Arguments: (source, target) describe the endpoints of the product + * transition along which the backtrack occurs (i.e., `source' + * is the state _to_ which the search backtracks). The second + * argument is only needed to support the expected function call + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ++number_of_transitions; + if (isMarked(target)) + markState(source); +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::touch + (const typename Product::size_type source, + const typename Product::Edge& edge, + const typename Product::size_type target) +/* ---------------------------------------------------------------------------- + * + * Description: Function called when the search encounters an edge with a + * target node that has already been explored. Increments the + * number of explored product transitions and updates accepting + * strongly connected reachability information by calling + * `this->backtrack()'. (This function is needed for supporting + * the expected class interface.) + * + * Arguments: (source, edge, target) describe the product transition that + * "touches" the state `target'. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + backtrack(source, edge, target); +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::beginComponent + (const typename Product::size_type, + const typename Product::size_type state_id) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the maximal strongly connected component that + * is about to be extracted from the product is an accepting + * component, or if the component contains a state from which + * such a component is known to be reachable. If either of + * these properties holds, `this->mark_scc' is set to true to + * cause all states referred to in subsequent calls to + * `this->insert' that occur before the next call to + * `this->endComponent' to be added into the set of states from + * which an accepting strongly connected component in the + * product is known to be reachable. + * + * A component is accepting iff `this->acceptance_sets' points + * to a bit vector in which all bits are set to 1. + * + * Arguments: state_id -- Identifier of a product state (in the + * component) that was encountered first during + * the search. + * The first argument is needed to support the expected function + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (isMarked(state_id)) /* If the component itself is not accepting, but */ + mark_scc = true; /* it contains a state from which such a component */ + /* is reachable, then the fact that `state_id' is */ + /* the first state of the component encountered */ + /* during the search and the operation of the */ + /* backtrack and touch functions guarantee that */ + /* `isMarked(state_id) == true' holds at this */ + /* point (the search is about to backtrack from */ + /* this state when this function gets called). */ + else /* test whether the component is an accepting component */ + { + /* + * The dfs search guarantees (by having made a call to + * AcceptanceTracker::addNodeToComponent before calling this function) that + * `this->acceptance_sets' is either equal to 0 (in which case the + * component to be extracted is trivial), or it points to the acceptance + * sets associated with the component to be extracted. + */ + mark_scc = (this->acceptance_sets != 0 + && this->acceptance_sets->count + (this->number_of_acceptance_sets) + == this->number_of_acceptance_sets); + } +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::insert + (const typename Product::size_type state) +/* ---------------------------------------------------------------------------- + * + * Description: If `this->mark_scc == true', inserts a product state + * identifier to the set of states from which an accepting + * strongly connected component is known to be reachable in the + * product. Discards the state otherwise. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (mark_scc) + markState(state); +} + +/* ========================================================================= */ +template +inline bool Product::AcceptanceReachabilityTracker::isMarked + (const typename Product::size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether an accepting strongly connected component is + * known to be reachable from a state in the product. + * + * Argument: state -- Identifier of the product state to test. + * + * Returns: true iff an accepting strongly connected component is known + * to be reachable from the product state. + * + * ------------------------------------------------------------------------- */ +{ + return (reachability_info.find(state) != reachability_info.end()); +} + +/* ========================================================================= */ +template +inline typename Product::size_type +Product::AcceptanceReachabilityTracker::numberOfStates() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of product states explored during the + * search. + * + * Arguments: None. + * + * Returns: Number of product states explored. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_states; +} + +/* ========================================================================= */ +template +inline unsigned long int +Product::AcceptanceReachabilityTracker::numberOfTransitions() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of product transitions explored during the + * search. + * + * Arguments: None. + * + * Returns: Number of transitions explored. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_transitions; +} + +/* ========================================================================= */ +template +inline void Product::AcceptanceReachabilityTracker::markState + (const typename Product::size_type state) +/* ---------------------------------------------------------------------------- + * + * Description: Adds a product state to the set of states from which an + * accepting strongly connected component is known to be + * reachable in the product. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reachability_info.insert(state); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class + * Product::AcceptingComponentFinder. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Product::AcceptingComponentFinder::AcceptingComponentFinder + (const unsigned long int num_accept_sets) + : AcceptanceTracker(num_accept_sets), construct_component(false) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * Product::AcceptingComponentFinder. + * + * Arguments: num_accept_sets -- Number of acceptance sets in the + * product. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Product::AcceptingComponentFinder + ::~AcceptingComponentFinder() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * Product::AcceptingComponentFinder. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename Product::AcceptingComponentFinder::SccType& +Product::AcceptingComponentFinder::operator()() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the latest accepting maximal strongly connected + * component found in the product. + * + * Arguments: None. + * + * Returns: A constant reference to a set of states containing the + * identifiers of product states forming an accepting maximal + * strongly connected component in the product. This set is + * empty if no such component has yet been found during the + * emptiness check. + * + * ------------------------------------------------------------------------- */ +{ + return scc; +} + +/* ========================================================================= */ +template +inline void Product::AcceptingComponentFinder::beginComponent + (const typename Product::size_type, + const typename Product::size_type) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the maximal strongly connected component that + * is about to be extracted from the product is an accepting + * component. If this is the case, `this->construct_component' + * is set to true to cause all states referred to in subsequent + * calls to `this->insert' that occur before the next call to + * `this->endComponent' to be inserted into `this->scc'. + * + * A component is accepting iff `this->acceptance_sets' points + * to a bit vector in which all bits are set to 1. + * + * Arguments: The arguments are required to support the expected class + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + scc.clear(); + /* + * The dfs search guarantees (by having made a call to + * AcceptanceTracker::addNodeToComponent before calling this function) that + * `this->acceptance_sets' is either equal to 0 (in which case the component + * to be extracted is trivial), or it points to the acceptance sets + * associated with the component to be extracted. + */ + construct_component = (this->acceptance_sets != 0 + && this->acceptance_sets->count + (this->number_of_acceptance_sets) + == this->number_of_acceptance_sets); +} + +/* ========================================================================= */ +template +inline void Product::AcceptingComponentFinder::insert + (const typename Product::size_type state) +/* ---------------------------------------------------------------------------- + * + * Description: If `this->construct_component == true', inserts a product + * state identifier to the set of identifiers representing an + * accepting maximal strongly connected component. Discards the + * state otherwise. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (construct_component) + scc.insert(state); +} + +} + +#endif /* !PRODUCT_H */ diff --git a/lbtt/src/SccCollection.h b/lbtt/src/SccCollection.h new file mode 100644 index 000000000..ea31ac97f --- /dev/null +++ b/lbtt/src/SccCollection.h @@ -0,0 +1,1174 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SCCCOLLECTION_H +#define SCCCOLLECTION_H + +#include +#include +#include +#include +#include +#include +#include "EdgeContainer.h" +#include "LbttAlloc.h" +#include "Graph.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * Interface for a "node visitor" object that provides implementations of + * callback functions to be used during a depth-first search for strongly + * connected components. The VisitorInterface class provides empty + * implementations for the operations; visitor objects can be defined by making + * them inherit the VisitorInterface and then overriding some (or all) of the + * interface functions. + * + * A "node visitor" object must always implement the following type definition + * and operation: + * + * SccType + * A type definition for storing data associated with a maximal strongly + * connected component. This data type should support copying and + * assignment; no other assumptions are made about the internals of this + * data type. It is intended that a node visitor object has a member of + * type SccType (which need not be public); this member can then be + * manipulated freely during the search for strongly connected + * components by giving implementations for the other interface + * functions listed below. + * + * const SccType& operator()() + * Function for accessing the data associated with a maximal strongly + * connected component. This function is called when dereferencing a + * strongly connected component iterator (see below). + * + * In addition, a node visitor can overload the default (empty) implementations + * for the following operations: + * + * void enter(typename GraphType::size_type node_id) + * This function is called whenever the search enters a new node of the + * graph, using the identifier of the node as a parameter. + * + * void backtrack + * (typename GraphType::size_type source_id, + * const typename GraphType::Edge& edge, + * typename GraphType::size_type target_id) + * This function is called when the search backtracks from a node with + * the identifier `target_id' to the node `source_id'. `edge' is a + * reference to the edge between graph nodes identified by `source_id' + * and `target_id'. + * + * void touch + * (typename GraphType::size_type source_id, + * const typename GraphType::Edge& edge, + * typename GraphType::size_type target_id) + * This function is called when the search (in node `source_id') + * encounters an edge (`edge') with a target node (with identifier + * `target_id') that has already been visited during the search. + * + * void leave(typename GraphType::size_type node_id) + * This function is called when the search leaves a node with the + * identifier `node_id'. + * + * void addEdgeToComponent + * (const typename GraphType::Edge& edge, + * typename GraphType::size_type component_id) + * This function is called when the search finds an edge belonging to a + * strongly connected component of the graph (i.e., an edge between two + * states in the component). The function is called with a constant + * reference to the edge and a strongly connected component identifier + * as parameters. + * + * void addNodeToComponent + * (typename GraphType::size_type node_id, + * typename GraphType::size_type component_id) + * This function is called when the search finds a state belonging to a + * strongly connected component of the graph. + * + * void beginComponent + * (typename GraphType::size_type component_id, + * typename GraphType::size_type component_root_id) + * This function is called when the SCC algorithm is about to extract a + * new maximal strongly connected component from the graph. Here, + * `component_id' corresponds to the identifier of the component, and + * `component_root_id' is the identifier of the node in the component + * which was first encountered during the search (the depth-first search + * is about to backtrack from this node). + * + * void insert(typename GraphType::size_type node_id) + * After a call to `beginComponent', the SCC search algorithm will call + * `insert' for each identifier of a node in the maximal strongly + * connected component. + * + * void endComponent(typename GraphType::size_type component_id) + * This function is called after the SCC algorithm has finished + * `insert''ing nodes into a maximal strongly connected component. The + * component identifier is given as a parameter. + * + *****************************************************************************/ + +template +class VisitorInterface +{ +public: + VisitorInterface(); /* Constructor. */ + + /* default copy constructor */ + + virtual ~VisitorInterface(); /* Destructor. */ + + /* default assignment operator */ + + virtual void enter /* Interface operations. */ + (const typename GraphType::size_type); + + virtual void backtrack + (const typename GraphType::size_type, + const typename GraphType::Edge&, + const typename GraphType::size_type); + + virtual void touch + (const typename GraphType::size_type, + const typename GraphType::Edge&, + const typename GraphType::size_type); + + virtual void leave + (const typename GraphType::size_type); + + virtual void addEdgeToComponent + (const typename GraphType::Edge&, + const typename GraphType::size_type); + + virtual void addNodeToComponent + (const typename GraphType::size_type, + const typename GraphType::size_type); + + virtual void beginComponent + (const typename GraphType::size_type, + const typename GraphType::size_type); + + virtual void insert + (const typename GraphType::size_type); + + virtual void endComponent + (const typename GraphType::size_type); +}; + + + +/****************************************************************************** + * + * A template class defining a node visitor for collecting the identifiers of + * nodes in a maximal strongly connected component into a set. + * + *****************************************************************************/ + +template +class SccCollector : public VisitorInterface +{ +public: + SccCollector(); /* Constructor. */ + + /* default copy constructor */ + + ~SccCollector(); /* Destructor. */ + + /* default assignment operator */ + + typedef set, /* a set of node id's. */ + ALLOC(typename GraphType::size_type) > + SccType; + + const SccType& operator()() const; /* Returns the set of node + * identifiers in a + * maximal strongly + * connected component. + */ + + /* `enter' inherited */ + + /* `backtrack' inherited */ + + /* `touch' inherited */ + + /* `leave' inherited */ + + /* `addEdgeToComponent' inherited */ + + /* `addNodeToComponent' inherited */ + + void beginComponent /* Function called */ + (const typename GraphType::size_type, /* before inserting */ + const typename GraphType::size_type); /* nodes in a component. */ + + void insert /* Function for */ + (const typename GraphType::size_type node_id); /* inserting nodes into + * a component. + */ + + /* `endComponent' inherited */ + +private: + SccType scc; /* A set of node + * identifiers representing + * a maximal strongly + * connected component. + */ +}; + + + +/****************************************************************************** + * + * A template class for defining a "container" of maximal strongly connected + * components of a graph. The template should be instantiated with two classes + * GraphType and NodeVisitor, where the NodeVisitor type should support the + * interface required of a node visitor (see the above documentation of + * VisitorInterface), and GraphType (which defaults to + * Graph) should support the following type definitions and + * operations: + * + * size_type + * A type that can be used for identifying nodes (uniquely) in the + * graph. This type should have a constructor taking no parameters, + * and it should support copying, assignment, and comparison using the + * `less than' operator. + * + * EdgeContainerType + * A type that represents a container of objects behaving similarly to + * pointers to edges in the graph. This type is expected to have an + * STL-like container interface with `begin()' and `end()' operations + * and a const_iterator with a constructor having no parameters and + * copying and assignment operations. + * + * The objects in the container should behave similarly to pointers + * that can be dereferenced using the * and -> operators to get access + * to objects (the edges in the graph, e.g., + * Graph::Edge) that support the operation + * size_type targetNode() + * that returns the identifier of an edge's target node in the graph. + * + * PathElement + * An object for representing (node_id, edge) pairs of the graph. + * The object should provide a constructor that can be called with two + * parameters: a parameter of type `size_type' and another parameter + * corresponding to the type obtained by dereferencing a pointer-like + * object stored in an object of type EdgeContainerType. + * + * Path + * A type that supports the `clear' operation (with no arguments) and + * the `push_front' operation with an argument of type PathElement. + * + * operator[](size_type node_id) + * This function should return an object that provides access to the + * edges starting from the graph node identified by `node_id' through + * the member function + * const EdgeContainerType& edges(), + * which returns the collection of (pointer-like objects to) edges + * beginning from the graph node with the identifier `node_id'. + * + * bool empty() + * This function should return true iff the graph contains no nodes. + * + * The NodeVisitor type defaults to SccCollector. + * + * The SccCollection class provides the following operations: + * + * SccCollection(const GraphType& g, NodeVisitor& node_visitor) + * Constructor that binds the SccCollection object to the graph `g' + * using the operations defined by `node_visitor' when visiting nodes + * of the graph during the search for strongly connected components. + * + * iterator begin(const typename GraphType::size_type initial_node_id) + * Returns an iterator to the strongly connected components of the + * graph. The function returns an iterator that points to the "first" + * maximal strongly connected component of the graph when starting the + * search for maximal strongly connected components from the node with + * the identifier `initial_node_id'. + * + * Note: If `initial_node_id' belongs to a strongly connected component + * of the graph which has alredy been visited by an iterator obtained + * by a previous call to `begin', the returned iterator is equal to + * `end()'. If this is not desired (i.e., if you wish to repeat the + * search for strongly connected components in a part of the graph, + * initialize a new SccCollection to the graph). + * + * iterator end() + * Returns an iterator pointing "past the end" of the collection of + * strongly connected components. + * + * The class `SccCollection::iterator' provides standard dereferencing and + * prefix and postfix increment operators. It also supports comparison for + * equality and inequality. See below for more detailed information. + * + *****************************************************************************/ + +template , + class NodeVisitor = SccCollector > +class SccCollection +{ +public: + SccCollection /* Constructor. */ + (const GraphType& g, + NodeVisitor& node_visitor); + + ~SccCollection(); /* Destructor. */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + typedef map, /* identifiers and the */ + ALLOC(typename GraphType::size_type) >/* order in which they */ + DfsOrdering; /* were encountered in + * the search for + * strongly connected + * components. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class iterator /* Iterator for */ + { /* accessing the maximal + * strongly connected + * components. + */ + public: + iterator(); /* Default constructor. */ + + /* default copy constructor */ + + ~iterator(); /* Destructor. */ + + /* default assignment operator */ + + bool operator==(const iterator& it) const; /* Comparison functions. */ + bool operator!=(const iterator& it) const; + + const typename NodeVisitor::SccType& /* Dereferencing */ + operator*() const; /* operators. */ + const typename NodeVisitor::SccType* + operator->() const; + + const typename NodeVisitor::SccType& /* Prefix and postfix */ + operator++(); /* increment operators. */ + const typename NodeVisitor::SccType + operator++(int); + + void getPath(typename GraphType::Path& path); /* Function for accessing + * the path from the + * initial node of the + * search to a node in + * the most recently found + * strongly connected + * component. + */ + + private: + iterator /* Constructor. */ + (const GraphType& g, + NodeVisitor& node_visitor, + DfsOrdering& ordering); + + void initialize /* Instructs the */ + (const typename GraphType::size_type /* iterator to continue */ + node_id); /* the search for + * strongly connected + * components from a + * given node. + */ + + const GraphType& graph; /* Reference to the graph + * with which the iterator + * is associated. + */ + + NodeVisitor& visitor; /* Reference to an object + * that provides + * implementations for the + * functions listed in the + * VisitorInterface class. + */ + + DfsOrdering& dfs_ordering; /* Mapping between node + * identifiers and their + * dfs numbers. + */ + + typename GraphType::size_type initial_node; /* Node from which the + * search was started. + */ + + typename GraphType::size_type dfs_number; /* Number of graph nodes + * processed by the + * iterator. + */ + + struct NodeStackElement /* Structure for */ + { /* storing information */ + typename GraphType::size_type id; /* needed for */ + typename GraphType::EdgeContainerType /* backtracking during */ + ::const_iterator edge; /* the search. */ + typename GraphType::size_type lowlink; + }; + + deque /* backtracking stack. */ + node_stack; + + NodeStackElement* current_node; /* Pointer to the top + * element of the + * backtracking stack. + */ + + deque /* collecting the nodes */ + scc_stack; /* in a strongly + * connected component, + * excluding the root + * nodes of the + * components. + */ + + void computeNextScc(); /* Updates the iterator to + * point to the next + * strongly connected + * component. + */ + + friend class SccCollection; + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + iterator begin /* Returns an iterator */ + (const typename GraphType::size_type /* pointing to the */ + node_id); /* "first" maximal + * strongly connected + * component reachable + * from a given node. + */ + + const iterator& end() const; /* Returns an iterator + * pointing past the "end" + * of the maximal strongly + * connected components. + */ + +private: + SccCollection(const SccCollection&); /* Prevent copying and */ + SccCollection& operator=(const SccCollection&); /* assignment of + * SccCollection + * objects. + */ + + const GraphType& graph; /* Reference to the graph + * associated with the + * container. + */ + + NodeVisitor& visitor; /* Reference to an object + * that provides + * implementations for + * callback operations + * needed during the + * search for strongly + * connected components. + */ + + const iterator end_iterator; /* Iterator pointing past + * the "end" of the + * collection of strongly + * connected components in + * the graph. This + * iterator is special by + * having both + * `initial_node' and + * `dfs_number' set to 0. + */ + + DfsOrdering dfs_ordering; /* Mapping between node + * identifiers and their + * visiting order during + * the search for strongly + * connected components. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for template class VisitorInterface. This class + * provides default empty implementations for node visitor operations. + * + *****************************************************************************/ + +template +inline VisitorInterface::VisitorInterface() +{ +} + +template +inline VisitorInterface::~VisitorInterface() +{ +} + +template +inline void VisitorInterface::enter + (const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::backtrack + (const typename GraphType::size_type, const typename GraphType::Edge&, + const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::touch + (const typename GraphType::size_type, const typename GraphType::Edge&, + const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::leave + (const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::addEdgeToComponent + (const typename GraphType::Edge&, const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::addNodeToComponent + (const typename GraphType::size_type, const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::beginComponent + (const typename GraphType::size_type, const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::insert + (const typename GraphType::size_type) +{ +} + +template +inline void VisitorInterface::endComponent + (const typename GraphType::size_type) +{ +} + + + +/****************************************************************************** + * + * Inline function definitions for template class SccCollector. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline SccCollector::SccCollector() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class SccCollector. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline SccCollector::~SccCollector() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class SccCollector. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const typename SccCollector::SccType& +SccCollector::operator()() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the set of identifiers of nodes in the maximal + * strongly connected component. + * + * Arguments: None. + * + * Returns: A constant reference to the set of identifiers of nodes in + * the component. + * + * ------------------------------------------------------------------------- */ +{ + return scc; +} + +/* ========================================================================= */ +template +inline void SccCollector::beginComponent + (const typename GraphType::size_type, const typename GraphType::size_type) +/* ---------------------------------------------------------------------------- + * + * Description: Clears the set of node identifiers to make it empty before + * filling it with (identifiers of) nodes of a new maximal + * strongly connected component. + * + * Arguments: The arguments are needed to support the expected function + * calling interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + scc.clear(); +} + +/* ========================================================================= */ +template +inline void SccCollector::insert + (const typename GraphType::size_type node_id) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts an identifier into the set of node identifiers in a + * maximal strongly connected component. + * + * Arguments: node_id -- Node identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + scc.insert(node_id); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class SccCollection. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline SccCollection::SccCollection + (const GraphType& g, NodeVisitor& node_visitor) : + graph(g), visitor(node_visitor), end_iterator(g, node_visitor, dfs_ordering) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class SccCollection. + * + * Arguments: g -- A constant reference to an object of type + * GraphType. See above for the description + * of the interface that this object should + * support. + * node_visitor -- A reference to an object that provides + * node visiting operations. See the + * documentation of VisitorInterface for more + * information. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline SccCollection::~SccCollection() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class SccCollection. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline typename SccCollection::iterator +SccCollection::begin + (const typename GraphType::size_type node_id) +/* ---------------------------------------------------------------------------- + * + * Description: Returns an iterator pointing to the "first" strongly + * connected component reachable from a given node of + * `this->graph'. + * + * Argument: node_id -- Identifier of the node. + * + * Returns: An iterator pointing to the "first" strongly connected + * component reachable from the node, or an iterator equal to + * `this->end()' if the node with the identifier `node_id' has + * already been included in a strongly connected component + * returned by another iterator to the same collection of + * strongly connected components. + * + * ------------------------------------------------------------------------- */ +{ + iterator it(graph, visitor, dfs_ordering); + it.initialize(node_id); + return it; +} + +/* ========================================================================= */ +template +inline const typename SccCollection::iterator& +SccCollection::end() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns an iterator pointing past the "end" of the collection + * of strongly connected components in `this->graph'. + * + * Arguments: None. + * + * Returns: A constant reference to `this->end_iterator'. + * + * ------------------------------------------------------------------------- */ +{ + return end_iterator; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class SccCollection::iterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline SccCollection::iterator::iterator + (const GraphType& g, NodeVisitor& node_visitor, + SccCollection::DfsOrdering& ordering) : + graph(g), visitor(node_visitor), dfs_ordering(ordering), initial_node(0), + dfs_number(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * SccCollection::iterator. Initializes + * a new iterator for scanning the maximal strongly connected + * components of a graph. + * + * Arguments: g -- The graph with which the iterator is to be + * associated. + * node_visitor -- A reference to a object that implements + * callback functions to be invoked during the + * search for strongly connected components. + * ordering -- A reference to a mapping between node + * identifiers and their dfs numbers. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline SccCollection::iterator::~iterator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * SccCollection::iterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline bool SccCollection::iterator::operator== + (const SccCollection::iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality comparison between two SccCollection::iterators. Two + * iterators are equal iff they have the same initial state and + * they have visited the same amount of graph nodes. + * + * Argument: it -- A constant reference to an iterator. + * + * Returns: true iff the iterators are equal. + * + * ------------------------------------------------------------------------- */ +{ + return (it.initial_node == initial_node && it.dfs_number == dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccCollection::iterator::operator!= + (const SccCollection::iterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Inequality comparison between two SccCollection::iterators. + * Two iterators are not equal iff they have different initial + * states or if they have visited different numbers of graph + * nodes. + * + * Argument: it -- A constant reference to an iterator. + * + * Returns: true iff the iterators are not equal. + * + * ------------------------------------------------------------------------- */ +{ + return (it.initial_node != initial_node || it.dfs_number != dfs_number); +} + +/* ========================================================================= */ +template +inline const typename NodeVisitor::SccType& +SccCollection::iterator::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * SccCollection::iterator. Returns the + * data associated with the maximal strongly connected component + * currently pointed to by the iterator. + * + * Arguments: None. + * + * Returns: A constant reference to the data associated with the strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + return visitor(); +} + +/* ========================================================================= */ +template +inline const typename NodeVisitor::SccType* +SccCollection::iterator::operator->() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class + * SccCollection::iterator. Returns a + * pointer to the data associated with the maximal strongly + * connected component currently pointed to by the iterator. + * + * Arguments: None. + * + * Returns: A constant pointer to the data associated with the strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + return &visitor(); +} + +/* ========================================================================= */ +template +inline const typename NodeVisitor::SccType& +SccCollection::iterator::operator++() +/* ---------------------------------------------------------------------------- + * + * Description: Prefix increment operator for class + * SccCollection::iterator. Computes + * the next maximal strongly connected component of + * `this->graph' and then returns the data associated with it. + * + * Arguments: None. + * + * Returns: A reference to the data associated with the maximal strongly + * connected component found by incrementing the iterator. + * + * ------------------------------------------------------------------------- */ +{ + computeNextScc(); + return visitor(); +} + +/* ========================================================================= */ +template +inline const typename NodeVisitor::SccType +SccCollection::iterator::operator++(int) +/* ---------------------------------------------------------------------------- + * + * Description: Postfix increment operator for class + * SccCollection::iterator. Computes + * the next strongly connected component of + * `this->graph', but returns the data associated with the + * strongly connected component that the iterator pointed to + * _before_ this operation. + * + * Arguments: None (the `int' is only required to distinguish this operator + * from the prefix increment operator). + * + * Returns: The data associated with the maximal strongly connected + * component pointed to by the iterator before incrementing it. + * + * ------------------------------------------------------------------------- */ +{ + const typename NodeVisitor::SccType old_scc = visitor(); + computeNextScc(); + return old_scc; +} + + + +/****************************************************************************** + * + * Function definitions for template class + * SccCollection. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void SccCollection::iterator::getPath + (typename GraphType::Path& path) +/* ---------------------------------------------------------------------------- + * + * Description: Constructs a path from the initial state of the search for + * the strongly connected components to a node in the most + * recently found maximal strongly connected graph component. + * + * Argument: path -- A reference to a GraphType::Path object for storing + * the (node_id, edge) pairs in the path. + * + * Returns: Nothing. Assuming that `path' is a standard STL container + * type object with the `push_front' operation, `*path.begin()' + * will correspond to the first element on the path after the + * call. + * + * ------------------------------------------------------------------------- */ +{ + path.clear(); + + /* + * When this function is called after extracting a maximal strongly connected + * component from the graph, `node_stack.front()' corresponds to the root + * node of the component. This node will not be included in the path + * (that is, the edge component of `path.back()' will point to this node + * when exiting from this function). + */ + + typename deque::const_iterator + n = node_stack.begin(); + if (n != node_stack.end()) + { + for (++n; n != node_stack.end(); ++n) + { + const typename GraphType::PathElement element(n->id, **n->edge); + path.push_front(element); + } + } +} + +/* ========================================================================= */ +template +void SccCollection::iterator::initialize + (const typename GraphType::size_type node_id) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes a + * SccCollection::iterator for scanning + * the maximal strongly connected components of `this->graph'. + * + * Argument: node_id -- Identifier of a graph node from which to start + * the search for maximal strongly connected + * components. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + node_stack.clear(); + scc_stack.clear(); + + /* + * If `node_id' is an identifier of a node that has not yet been visited, + * make the iterator point to the next strongly connected component of + * `this->graph'. + */ + + if (!graph.empty() && dfs_ordering.find(node_id) == dfs_ordering.end()) + { + initial_node = node_id; + visitor.enter(node_id); + dfs_ordering[node_id] = dfs_number = 1; + const NodeStackElement element + = { node_id, graph[node_id].edges().begin(), 1 }; + node_stack.push_front(element); + current_node = &node_stack.front(); + computeNextScc(); + } +} + +/* ========================================================================= */ +template +void SccCollection::iterator::computeNextScc() +/* ---------------------------------------------------------------------------- + * + * Description: Makes an SccCollection::iterator + * point to the "next" maximal strongly connected component of + * `this->graph', using an algorithm based on the depth-first + * search algorithm of Tarjan + * [R. J. Tarjan. Depth-first search and linear graph + * algorithms. SIAM Journal on Computing 1(2):146--160, + * 1972] + * for computing the maximal strongly connected components of + * the graph. + * + * The implementation includes the optimization in the first + * improved algorithm found in + * [E. Nuutila and E. Soisalon-Soininen. On finding the + * strongly connected components in a directed graph. + * Information Processing Letters 49(1):9--14, 1994]. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (current_node->lowlink == 0) + { + /* Backtrack from the root of an SCC that was extracted from the graph */ + const typename GraphType::size_type child_node = current_node->id; + node_stack.pop_front(); + if (node_stack.empty()) + { + initial_node = dfs_number = 0; + return; + } + current_node = &node_stack.front(); + visitor.backtrack(current_node->id, **current_node->edge, child_node); + ++current_node->edge; /* prepare to process the next edge */ + } + +next_edge: + while (current_node->edge != graph[current_node->id].edges().end()) + { + const typename GraphType::size_type child_node + = (*current_node->edge)->targetNode(); + const typename DfsOrdering::const_iterator child_dfs_number_finder + = dfs_ordering.find(child_node); + + if (child_dfs_number_finder == dfs_ordering.end()) /* child not visited */ + { + ++dfs_number; + dfs_ordering[child_node] = dfs_number; + visitor.enter(child_node); + const NodeStackElement element + = { child_node, graph[child_node].edges().begin(), dfs_number }; + node_stack.push_front(element); + current_node = &node_stack.front(); + goto next_edge; + } + + visitor.touch(current_node->id, **current_node->edge, child_node); + if (child_dfs_number_finder->second != 0) /* child in the same SCC */ + { + if (child_dfs_number_finder->second < current_node->lowlink) + current_node->lowlink = child_dfs_number_finder->second; + visitor.addEdgeToComponent(**current_node->edge, current_node->lowlink); + } + + ++current_node->edge; + } + + visitor.addNodeToComponent(current_node->id, current_node->lowlink); + visitor.leave(current_node->id); + + if (dfs_ordering.find(current_node->id)->second != current_node->lowlink) + { + scc_stack.push_front(current_node->id); + + /* Backtrack from a node into a node in the same SCC */ + const typename GraphType::size_type child_node = current_node->id; + const typename GraphType::size_type child_lowlink = current_node->lowlink; + node_stack.pop_front(); + current_node = &node_stack.front(); + if (current_node->lowlink > child_lowlink) + current_node->lowlink = child_lowlink; + visitor.backtrack(current_node->id, **current_node->edge, child_node); + visitor.addEdgeToComponent(**current_node->edge, current_node->lowlink); + ++current_node->edge; /* prepare to process the next edge */ + goto next_edge; + } + + /* + * `current_node' is a root of a maximal strongly connected graph component. + * Extract the component from the graph. + */ + + visitor.beginComponent(current_node->lowlink, current_node->id); + visitor.insert(current_node->id); + dfs_ordering.find(current_node->id)->second = 0; + while (!scc_stack.empty()) + { + const typename GraphType::size_type node = scc_stack.front(); + typename GraphType::size_type& node_dfs_number + = dfs_ordering.find(node)->second; + if (node_dfs_number > current_node->lowlink) + { + scc_stack.pop_front(); + visitor.insert(node); + node_dfs_number = 0; + } + else + break; + } + visitor.endComponent(current_node->lowlink); + current_node->lowlink = 0; +} + +} + +#endif /* !SCCCOLLECTION_H */ diff --git a/lbtt/src/StatDisplay.cc b/lbtt/src/StatDisplay.cc index 0d1074d49..8d6250626 100644 --- a/lbtt/src/StatDisplay.cc +++ b/lbtt/src/StatDisplay.cc @@ -21,6 +21,7 @@ #include #include "DispUtil.h" #include "Exception.h" +#include "IntervalList.h" #include "SharedTestData.h" #include "StatDisplay.h" #include "StringUtil.h" @@ -33,6 +34,47 @@ using namespace ::DispUtil; using namespace ::SharedTestData; using namespace ::StringUtil; +/* ========================================================================= */ +void printStatTableHeader(ostream& stream, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Displays a table header for test statistics (used in + * verbosity mode 2). + * + * Arguments: stream -- A reference to the output stream to which the + * header should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + int num_dashes = 39; + estream << string(indent, ' ') + " # F Elapsed Büchi Büchi Acc."; + if (configuration.global_options.do_cons_test + || configuration.global_options.do_comp_test) + { + num_dashes += 31; + estream << " Product Product Acc."; + if (configuration.global_options.do_cons_test) + { + num_dashes += 3; + estream << " CC"; + } + } + estream << '\n' + string(indent + 10, ' ') + + "time states trans. sets"; + if (configuration.global_options.do_cons_test + || configuration.global_options.do_comp_test) + estream << " states trans. cycles"; + estream << "\n" + string(indent, ' ') + string(num_dashes, '-') + '\n'; + estream.flush(); +} + + /* ========================================================================= */ void printBuchiAutomatonStats (ostream& stream, int indent, @@ -48,7 +90,7 @@ void printBuchiAutomatonStats * Arguments: stream -- A reference to the output stream to which the * information should be written. * indent -- Number of spaces to leave on the left of the - * output. + * output in verbosity modes >= 3. * algorithm -- Identifier of the algorithm used for * generating the automaton. * result_id -- Selects between the automata constructed from @@ -63,35 +105,68 @@ void printBuchiAutomatonStats const AutomatonStats& automaton_stats = test_results[algorithm].automaton_stats[result_id]; - estream << string(indent, ' '); - - if (!automaton_stats.buchiAutomatonComputed()) - estream << "not computed"; + if (configuration.global_options.verbosity <= 2) + { + if (!automaton_stats.buchiAutomatonComputed()) + estream << " N/A N/A N/A N/A"; + else + { + if (automaton_stats.buchi_generation_time >= 0.0) + { + changeStreamFormatting(stream, 9, 2, ios::fixed | ios::right); + estream << automaton_stats.buchi_generation_time; + restoreStreamFormatting(stream); + } + else + estream << " N/A"; + estream << ' '; + changeStreamFormatting(stream, 9, 0, ios::right); + estream << automaton_stats.number_of_buchi_states; + restoreStreamFormatting(stream); + estream << ' '; + changeStreamFormatting(stream, 9, 0, ios::right); + estream << automaton_stats.number_of_buchi_transitions; + restoreStreamFormatting(stream); + estream << ' '; + changeStreamFormatting(stream, 4, 0, ios::right); + estream << automaton_stats.number_of_acceptance_sets; + restoreStreamFormatting(stream); + } + estream << ' '; + } else { - estream << "number of states:" + string(6, ' ') - + toString(automaton_stats.number_of_buchi_states) - + '\n' + string(indent, ' ') + "number of transitions: " - + toString(automaton_stats.number_of_buchi_transitions) - + '\n' + string(indent, ' ') + "acceptance sets:" - + string(7, ' ') - + toString(automaton_stats.number_of_acceptance_sets) - + '\n' + string(indent, ' ') + "computation time:" - + string(6, ' '); - - if (automaton_stats.buchi_generation_time != -1.0) - { - changeStreamFormatting(stream, 9, 2, ios::fixed | ios::left); - estream << automaton_stats.buchi_generation_time; - restoreStreamFormatting(stream); + estream << string(indent, ' '); - estream << " seconds (user time)"; - } + if (!automaton_stats.buchiAutomatonComputed()) + estream << "not computed"; else - estream << "N/A"; + { + estream << "number of states:" + string(6, ' ') + + toString(automaton_stats.number_of_buchi_states) + + '\n' + string(indent, ' ') + "number of transitions: " + + toString(automaton_stats.number_of_buchi_transitions) + + '\n' + string(indent, ' ') + "acceptance sets:" + + string(7, ' ') + + toString(automaton_stats.number_of_acceptance_sets) + + '\n' + string(indent, ' ') + "computation time:" + + string(6, ' '); + + if (automaton_stats.buchi_generation_time != -1.0) + { + changeStreamFormatting(stream, 9, 2, ios::fixed | ios::left); + estream << automaton_stats.buchi_generation_time; + restoreStreamFormatting(stream); + + estream << " seconds (user time)"; + } + else + estream << "N/A"; + } + + estream << '\n'; } - estream << '\n'; estream.flush(); } @@ -110,7 +185,7 @@ void printProductAutomatonStats * Arguments: stream -- A reference to the output stream to which the * information should be written. * indent -- Number of spaces to leave on the left of the - * output. + * output in verbosity modes >= 3. * algorithm -- Identifier of the algorithm used for * generating the product automaton. * result_id -- Selects between the automata constructed from @@ -125,43 +200,64 @@ void printProductAutomatonStats const AutomatonStats& automaton_stats = test_results[algorithm].automaton_stats[result_id]; - estream << string(indent, ' '); - - if (!automaton_stats.productAutomatonComputed()) - estream << "not computed"; + if (configuration.global_options.verbosity <= 2) + { + if (!automaton_stats.productAutomatonComputed()) + estream << " N/A N/A"; + else + { + changeStreamFormatting(stream, 11, 0, ios::right); + estream << automaton_stats.number_of_product_states; + restoreStreamFormatting(stream); + estream << ' '; + changeStreamFormatting(stream, 11, 0, ios::right); + estream << automaton_stats.number_of_product_transitions; + restoreStreamFormatting(stream); + } + estream << ' '; + } else { - estream << "number of states:" + string(6, ' '); + estream << string(indent, ' '); - changeStreamFormatting(stream, 9, 0, ios::left); - estream << automaton_stats.number_of_product_states; - restoreStreamFormatting(stream); - - estream << " ["; - - if (automaton_stats.number_of_product_states != 0) + if (!automaton_stats.productAutomatonComputed()) + estream << "not computed"; + else { - changeStreamFormatting(stream, 0, 2, ios::fixed); - estream << static_cast - (automaton_stats.number_of_product_states) - / static_cast(automaton_stats.number_of_buchi_states) - / static_cast(round_info.statespace->size()) - * 100.0; + estream << "number of states:" + string(6, ' '); + + changeStreamFormatting(stream, 9, 0, ios::left); + estream << automaton_stats.number_of_product_states; restoreStreamFormatting(stream); - estream << "% of worst case (" - << automaton_stats.number_of_buchi_states - * round_info.statespace->size() - << ')'; - } - else - estream << "empty automaton"; + estream << " ["; - estream << "]\n" + string(indent, ' ') + "number of transitions: " - + toString(automaton_stats.number_of_product_transitions); + if (automaton_stats.number_of_product_states != 0) + { + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << static_cast + (automaton_stats.number_of_product_states) + / static_cast + (automaton_stats.number_of_buchi_states) + / static_cast(round_info.statespace->size()) + * 100.0; + restoreStreamFormatting(stream); + + estream << "% of worst case (" + << automaton_stats.number_of_buchi_states + * round_info.statespace->size() + << ')'; + } + else + estream << "empty automaton"; + + estream << "]\n" + string(indent, ' ') + "number of transitions: " + + toString(automaton_stats.number_of_product_transitions); + } + + estream << '\n'; } - estream << '\n'; estream.flush(); } @@ -181,7 +277,7 @@ void printAcceptanceCycleStats * Arguments: stream -- A reference to the output stream to which the * information should be written. * indent -- Number of spaces to leave on the left of the - * output. + * output in verbosity mode >= 3. * algorithm -- Identifier of the algorithm used for * computing the Büchi automaton whose accepting * cycles are to be considered. @@ -197,41 +293,57 @@ void printAcceptanceCycleStats const AutomatonStats& automaton_stats = test_results[algorithm].automaton_stats[result_id]; - estream << string(indent, ' '); - - if (!automaton_stats.emptiness_check_performed) - estream << "not computed"; - else if (configuration.global_options.product_mode == Configuration::LOCAL) + if (configuration.global_options.verbosity <= 2) { - estream << string("cycle "); - - if (automaton_stats.emptiness_check_result[0]) - estream << "reachable "; + if (!automaton_stats.emptiness_check_performed) + estream << " N/A"; else - estream << "not reachable"; - - estream << " (from the initial state)"; + { + changeStreamFormatting(stream, 6, 0, ios::right); + estream << automaton_stats.emptiness_check_result.count(); + restoreStreamFormatting(stream); + } + estream << ' '; } else { - estream << "cycle reachable from "; + estream << string(indent, ' '); - changeStreamFormatting(stream, 9, 0, ios::left); - estream << automaton_stats.emptiness_check_result.count(); - restoreStreamFormatting(stream); + if (!automaton_stats.emptiness_check_performed) + estream << "not computed"; + else if (configuration.global_options.product_mode == Configuration::LOCAL) + { + estream << string("cycle "); - estream << " states\n" + string(indent, ' ') - + "not reachable from "; + if (automaton_stats.emptiness_check_result[0]) + estream << "reachable "; + else + estream << "not reachable"; - changeStreamFormatting(stream, 9, 0, ios::left); - estream << (round_info.real_emptiness_check_size - - automaton_stats.emptiness_check_result.count()); - restoreStreamFormatting(stream); + estream << " (from the initial state)"; + } + else + { + estream << "cycle reachable from "; - estream << " states"; + changeStreamFormatting(stream, 9, 0, ios::left); + estream << automaton_stats.emptiness_check_result.count(); + restoreStreamFormatting(stream); + + estream << " states\n" + string(indent, ' ') + + "not reachable from "; + + changeStreamFormatting(stream, 9, 0, ios::left); + estream << (round_info.real_emptiness_check_size + - automaton_stats.emptiness_check_result.count()); + restoreStreamFormatting(stream); + + estream << " states"; + } + + estream << '\n'; } - estream << '\n'; estream.flush(); } @@ -249,7 +361,7 @@ void printConsistencyCheckStats * Arguments: stream -- A reference to an output stream to which the * information should be written. * indent -- Number of spaces to leave on the left of the - * output. + * output in verbosity mode >= 3. * algorithm -- Identifier of the algorithm whose consistency * check result should be displayed. * @@ -260,100 +372,114 @@ void printConsistencyCheckStats Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); const AlgorithmTestResults& test_result = test_results[algorithm]; - estream << string(indent, ' '); + if (configuration.global_options.verbosity <= 2) + { + switch (test_result.consistency_check_result) + { + case -1 : + estream << "NA"; + break; - if (test_result.consistency_check_result == -1) - estream << "not performed"; + case 0 : + estream << " F"; + break; + + default: + estream << " P"; + break; + } + } else { - estream << "result:" + string(18, ' '); + estream << string(indent, ' '); - if (test_result.consistency_check_result == 0) - { - estream << "failed [" - + toString(test_result.failed_consistency_check_comparisons) - + " ("; - - changeStreamFormatting(stream, 0, 2, ios::fixed); - estream << ((test_result.consistency_check_comparisons == 0) - ? 0.0 - : static_cast - (test_result.failed_consistency_check_comparisons) - / test_result.consistency_check_comparisons * 100.0); - restoreStreamFormatting(stream); - - estream << "%) of " - + toString(test_result.consistency_check_comparisons) - + " test cases]"; - } + if (test_result.consistency_check_result == -1) + estream << "not performed"; else - estream << "passed"; + { + estream << "result:" + string(18, ' '); + + if (test_result.consistency_check_result == 0) + { + estream << "failed [" + + toString(test_result.failed_consistency_check_comparisons) + + " ("; + + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << ((test_result.consistency_check_comparisons == 0) + ? 0.0 + : static_cast + (test_result.failed_consistency_check_comparisons) + / test_result.consistency_check_comparisons * 100.0); + restoreStreamFormatting(stream); + + estream << "%) of " + + toString(test_result.consistency_check_comparisons) + + " test cases]"; + } + else + estream << "passed"; + } + + estream << '\n'; } - estream << '\n'; estream.flush(); } /* ========================================================================= */ void printCrossComparisonStats - (ostream& stream, int indent, - vector::size_type - algorithm) + (ostream& stream, int indent, const IntervalList& algorithms) /* ---------------------------------------------------------------------------- * * Description: Displays information about the model checking result cross- * comparison check, extracting the information from a vector of * TestStatistics structures stored in the UserInterface object. * - * Arguments: stream -- A reference to an output stream to which the - * information should be written. - * indent -- Number of spaces to leave on the left of the - * output. - * algorithm -- If less than the number of algorithms, show - * only the results for the algorithm with this - * identifier. + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithms -- A reference to a constant IntervalList + * storing the numeric identifiers of the + * algorithms for which the statistics should + * be shown. * * Returns: Nothing. * * ------------------------------------------------------------------------- */ { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - bool no_errors_to_report = true; - - if (algorithm < configuration.algorithms.size() - && !configuration.algorithms[algorithm].enabled) - { - estream << string(indent, ' ') + "not performed\n\n"; - estream.flush(); - return; - } - - estream << string(indent, ' ') + "result:"; + bool no_errors_to_report = true, nothing_to_report = true; const AutomatonStats* alg_1_pos_results; const AutomatonStats* alg_1_neg_results; - for (vector::size_type - alg_1 = 0; - alg_1 < test_results.size(); - alg_1++) + for (IntervalList::const_iterator alg_1 = algorithms.begin(); + alg_1 != algorithms.end(); + ++alg_1) { - alg_1_pos_results = &test_results[alg_1].automaton_stats[0]; - alg_1_neg_results = &test_results[alg_1].automaton_stats[1]; + alg_1_pos_results = &test_results[*alg_1].automaton_stats[0]; + alg_1_neg_results = &test_results[*alg_1].automaton_stats[1]; for (vector::size_type - alg_2 = alg_1 + 1; - alg_2 < test_results.size(); + alg_2 = 0; + alg_2 < round_info.number_of_translators; alg_2++) { - if ((algorithm >= configuration.algorithms.size() - || alg_1 == algorithm - || alg_2 == algorithm) - && configuration.algorithms[alg_1].enabled + if (*alg_1 != alg_2 + && (alg_2 > *alg_1 || !algorithms.covers(alg_2)) + && configuration.algorithms[*alg_1].enabled && configuration.algorithms[alg_2].enabled) { bool pos_test, neg_test; + if (nothing_to_report) + { + nothing_to_report = false; + estream << string(indent, ' ') + "result:"; + } + for (int counter = 0; counter < 2; counter++) { if (counter == 0) @@ -381,89 +507,76 @@ void printCrossComparisonStats else estream << "-) "; - estream << ' '; - if (alg_1 == round_info.number_of_translators) - estream << "lbtt"; - else - estream << configuration.algorithmString(alg_1); - estream << ", "; - if (alg_2 == round_info.number_of_translators) - estream << "lbtt"; - else - estream << configuration.algorithmString(alg_2); + estream << " " + configuration.algorithmString(*alg_1) + ", " + + configuration.algorithmString(alg_2); } } } } } - if (no_errors_to_report) + if (nothing_to_report) + estream << string(indent, ' ') + "not performed"; + else if (no_errors_to_report) estream << string(20, ' ') + "no failures detected"; - estream << "\n\n"; estream.flush(); } /* ========================================================================= */ void printBuchiIntersectionCheckStats - (ostream& stream, int indent, - vector::size_type - algorithm) + (ostream& stream, int indent, const IntervalList& algorithms) /* ---------------------------------------------------------------------------- * * Description: Displays information about the Büchi automaton intersection * emptiness check results, extracting the information from a * TestStatistics structure stored in the UserInterface object. * - * Arguments: stream -- A reference to an output stream to which the - * information should be written. - * indent -- Number of spaces to leave on the left of the - * output. - * algorithm -- If less than the number of algorithms, show - * only the results for the algorithm with this - * identifier. + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithms -- A reference to a constant IntervalList + * storing the numeric identifiers of the + * algorithms for which the statistics should + * be shown. * * Returns: Nothing. * * ------------------------------------------------------------------------- */ { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - bool no_errors_to_report = true; - - if (algorithm < round_info.number_of_translators - && !configuration.algorithms[algorithm].enabled) - { - estream << string(indent, ' ') + "not performed\n\n"; - estream.flush(); - return; - } - - estream << string(indent, ' ') + "result:"; + bool no_errors_to_report = true, nothing_to_report = true; const AutomatonStats* alg_1_pos_results; const AutomatonStats* alg_1_neg_results; - for (vector::size_type - alg_1 = 0; - alg_1 < round_info.number_of_translators; - alg_1++) + for (IntervalList::const_iterator alg_1 = algorithms.begin(); + alg_1 != algorithms.end(); + ++alg_1) { - alg_1_pos_results = &test_results[alg_1].automaton_stats[0]; - alg_1_neg_results = &test_results[alg_1].automaton_stats[1]; + alg_1_pos_results = &test_results[*alg_1].automaton_stats[0]; + alg_1_neg_results = &test_results[*alg_1].automaton_stats[1]; for (vector::size_type - alg_2 = alg_1; + alg_2 = 0; alg_2 < round_info.number_of_translators; alg_2++) { - if ((algorithm >= round_info.number_of_translators - || alg_1 == algorithm - || alg_2 == algorithm) - && configuration.algorithms[alg_1].enabled - && configuration.algorithms[alg_2].enabled) + if (configuration.algorithms[*alg_1].enabled + && configuration.algorithms[alg_2].enabled + && (alg_2 >= *alg_1 || !algorithms.covers(alg_2)) + && !configuration.isInternalAlgorithm(*alg_1) + && !configuration.isInternalAlgorithm(alg_2)) { bool pos_test, neg_test; + if (nothing_to_report) + { + nothing_to_report = false; + estream << string(indent, ' ') + "result:"; + } + for (int counter = -1; counter < 1; counter++) { pos_test = (alg_1_pos_results->buchi_intersection_check_stats[alg_2] @@ -478,7 +591,7 @@ void printBuchiIntersectionCheckStats estream << string(counter == -1 ? "N/A " : "failed") + ' '; - if (alg_1 != alg_2) + if (*alg_1 != alg_2) { estream << '('; if (pos_test) @@ -489,9 +602,9 @@ void printBuchiIntersectionCheckStats else estream << " "; - estream << ' ' + configuration.algorithmString(alg_1); + estream << ' ' + configuration.algorithmString(*alg_1); - if (alg_1 != alg_2) + if (*alg_1 != alg_2) { estream << ", ("; if (pos_test) @@ -507,10 +620,11 @@ void printBuchiIntersectionCheckStats } } - if (no_errors_to_report) + if (nothing_to_report) + estream << string(indent, ' ') + "not performed"; + else if (no_errors_to_report) estream << string(20, ' ') + "no failures detected"; - - estream << "\n\n"; + estream << "\n"; estream.flush(); } @@ -537,30 +651,46 @@ void printAllStats { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - estream << string(indent, ' ') + configuration.algorithmString(algorithm) - + '\n'; - estream.flush(); + if (configuration.global_options.verbosity >= 3) + estream << string(indent, ' ') + configuration.algorithmString(algorithm) + + '\n'; for (int counter = 0; counter < 2; counter++) { - estream << string(indent + 2, ' ') - + (counter == 0 ? "Positive" : "Negated") + " formula:\n" - + string(indent + 4, ' ') + "Büchi automaton:\n"; + if (configuration.global_options.verbosity <= 2) + { + if (counter == 1) + estream << '\n'; + estream << string(indent, ' '); + changeStreamFormatting(stream, 2, 0, ios::right); + estream << algorithm << ' '; + restoreStreamFormatting(stream); + estream << (counter == 0 ? '+' : '-') << ' '; + } + else + { + estream << string(indent + 2, ' ') + + (counter == 0 ? "Positive" : "Negated") + " formula:\n" + + string(indent + 4, ' ') + "Büchi automaton:\n"; + } printBuchiAutomatonStats(stream, indent + 6, algorithm, counter); if (configuration.global_options.do_comp_test || configuration.global_options.do_cons_test) { - estream << string(indent + 4, ' ') + "Product automaton:\n"; + if (configuration.global_options.verbosity >= 3) + estream << string(indent + 4, ' ') + "Product automaton:\n"; printProductAutomatonStats(stream, indent + 6, algorithm, counter); - estream << string(indent + 4, ' ') + "Accepting cycles:\n"; + if (configuration.global_options.verbosity >= 3) + estream << string(indent + 4, ' ') + "Accepting cycles:\n"; printAcceptanceCycleStats(stream, indent + 6, algorithm, counter); } } if (configuration.global_options.do_cons_test) { - estream << string(indent + 2, ' ') + "Result consistency check:\n"; + if (configuration.global_options.verbosity >= 3) + estream << string(indent + 2, ' ') + "Result consistency check:\n"; printConsistencyCheckStats(stream, indent + 4, algorithm); } @@ -626,10 +756,8 @@ void printCollectiveCrossComparisonStats break; default : - if (configuration.global_options.statespace_generation_mode - & Configuration::PATH - && (algorithm_x == round_info.number_of_translators - || algorithm_y == round_info.number_of_translators)) + if (configuration.isInternalAlgorithm(algorithm_x) + || configuration.isInternalAlgorithm(algorithm_y)) { estream << string(21, ' '); return; @@ -951,8 +1079,11 @@ void printCollectiveStats(ostream& stream, int indent) algorithm < round_info.number_of_translators; ++algorithm) { + if (configuration.isInternalAlgorithm(algorithm)) + continue; + estream << '\n' + string((i > 0 ? 4 : 2) + indent, ' ') - + *(configuration.algorithms[algorithm].name) + '\n'; + + configuration.algorithms[algorithm].name + '\n'; switch (i) { @@ -965,14 +1096,14 @@ void printCollectiveStats(ostream& stream, int indent) unsigned long int failures_to_compute_automaton; unsigned long int automaton_count; unsigned long int number_of_successful_instances; - unsigned long int total_number_of_states; - unsigned long int total_number_of_transitions; + BIGUINT total_number_of_states; + BIGUINT total_number_of_transitions; const TestStatistics& stats = final_statistics[algorithm]; estream << string(2 + indent, ' ') - + string(configuration.algorithms[algorithm].name - ->length(), '='); + + string(configuration.algorithms[algorithm].name. + length(), '='); for (int k = 0; k < 2; k++) { @@ -1117,7 +1248,7 @@ void printCollectiveStats(ostream& stream, int indent) if (k == 0) { - unsigned long int total_number_of_acceptance_sets; + BIGUINT total_number_of_acceptance_sets; double buchi_generation_time; estream << '\n' + string(22 + indent, ' ') @@ -1350,7 +1481,7 @@ void printCollectiveStats(ostream& stream, int indent) if (algorithm_x + i < number_of_algorithms) { algorithm_name = - (*(configuration.algorithms[algorithm_x + i].name)).substr(0, 20); + configuration.algorithms[algorithm_x + i].name.substr(0, 20); estream << "| " + algorithm_name + string(21 - algorithm_name.length(), ' '); } @@ -1360,6 +1491,9 @@ void printCollectiveStats(ostream& stream, int indent) for (algorithm_y = 0; algorithm_y < number_of_algorithms; algorithm_y++) { + if (configuration.isInternalAlgorithm(algorithm_y)) + continue; + estream << "\n " + ind + string(26, '-'); for (int i = 0; i < 2; ++i) @@ -1370,7 +1504,7 @@ void printCollectiveStats(ostream& stream, int indent) estream << '+'; algorithm_name - = (*(configuration.algorithms[algorithm_y].name)).substr(0, 20); + = configuration.algorithms[algorithm_y].name.substr(0, 20); bool algorithm_name_printed = false; legend = 1; diff --git a/lbtt/src/StatDisplay.h b/lbtt/src/StatDisplay.h index a4a947ad5..212aa6091 100644 --- a/lbtt/src/StatDisplay.h +++ b/lbtt/src/StatDisplay.h @@ -42,6 +42,11 @@ extern Configuration configuration; namespace StatDisplay { +void printStatTableHeader /* Displays a table */ + (ostream& stream, int indent); /* header for test + * statistics. + */ + void printBuchiAutomatonStats /* Displays information */ (ostream& stream, /* about a Büchi */ int indent, /* automaton. */ @@ -74,17 +79,15 @@ void printConsistencyCheckStats /* Displays the result */ algorithm); void printCrossComparisonStats /* Displays information */ - (ostream& stream, /* about the model */ - int indent, /* checking result */ - vector::size_type /* check. */ - algorithm); + (ostream& stream, int indent, /* about the model */ + const IntervalList& algorithms); /* checking result */ + /* cross-comparison */ + /* check. */ void printBuchiIntersectionCheckStats /* Displays the results */ (ostream& stream, int indent, /* of the Büchi automata */ - vector::size_type /* emptiness checks. */ - algorithm); + const IntervalList& algorithms); /* intersection */ + /* emptiness checks. */ void printAllStats /* A shorthand for */ (ostream& stream, /* showing all the */ diff --git a/lbtt/src/StateSpaceProduct.h b/lbtt/src/StateSpaceProduct.h new file mode 100644 index 000000000..87fab2fbd --- /dev/null +++ b/lbtt/src/StateSpaceProduct.h @@ -0,0 +1,430 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STATESPACEPRODUCT_H +#define STATESPACEPRODUCT_H + +#include +#include "BitArray.h" +#include "BuchiAutomaton.h" +#include "EdgeContainer.h" +#include "Graph.h" +#include "StateSpace.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * A class with operations for checking the product of a Büchi automaton and + * a state space for emptiness. + * + *****************************************************************************/ + +class StateSpaceProduct +{ +public: + StateSpaceProduct /* Constructor. */ + (const Graph& a, + const Graph& s); + + /* default copy constructor */ + + ~StateSpaceProduct(); /* Destructor. */ + + /* default assignment operator */ + + bool empty() const; /* Tells whether the + * product of a Büchi + * automaton and a state + * space is (trivially) + * empty. + */ + + unsigned long int numberOfAcceptanceSets() const; /* Tells the number of + * acceptance sets in a + * Büchi automaton in its + * product with a state + * space. + */ + + const BuchiAutomaton::BuchiState& firstComponent /* Mappings between a */ + (const Graph::size_type /* product state */ + state_id) const; /* identifier and */ + const StateSpace::State& secondComponent /* states of the Büchi */ + (const Graph::size_type /* automaton and the */ + state_id) const; /* state space forming + * the product. + */ + + void mergeAcceptanceInformation /* Merges the acceptance */ + (const Graph::Node& state, /* sets associated with */ + const Graph::Node&, /* a state in the Büchi */ + BitArray& acceptance_sets) const; /* automaton into a + * collection of sets. + */ + + void mergeAcceptanceInformation /* Merges the acceptance */ + (const Graph::Edge& /* sets associated with */ + buchi_transition, /* a transition in the */ + const Graph::Edge&, /* Büchi automaton into */ + BitArray& acceptance_sets) const; /* a collection of sets. */ + + void validateEdgeIterators /* Ensures that a pair */ + (const Graph::Node& /* of transition */ + buchi_state, /* iterators points to a */ + const Graph::Node& /* transition beginning */ + system_state, /* from a given state in */ + GraphEdgeContainer::const_iterator& /* the product of a */ + buchi_transition, /* Büchi automaton and */ + GraphEdgeContainer::const_iterator& /* a state space. */ + system_transition) const; + + void incrementEdgeIterators /* Updates a pair of */ + (const Graph::Node& /* transition iterators */ + buchi_state, /* to make them point to */ + const Graph::Node& /* the "next" transition */ + system_state, /* starting from a given */ + GraphEdgeContainer::const_iterator& /* state in the product */ + buchi_transition, /* of a Büchi automaton */ + GraphEdgeContainer::const_iterator& /* and a state space. */ + system_transition) const; + +private: + const BuchiAutomaton& buchi_automaton; /* Büchi automaton + * associated with the + * product. + */ + + const StateSpace& statespace; /* State space associated + * with the product. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class StateSpaceProduct. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline StateSpaceProduct::StateSpaceProduct + (const Graph& a, const Graph& s) : + buchi_automaton(static_cast(a)), + statespace(static_cast(s)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpaceProduct. Initializes a new + * object with operations for checking the emptiness of the + * product of a Büchi automaton and a state space. + * + * Arguments: a -- A constant reference to a Graph + * object, assumed to be a BuchiAutomaton. + * s -- A constant reference to a Graph + * object, assumed to be a StateSpace. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline StateSpaceProduct::~StateSpaceProduct() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class StateSpaceProduct. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline bool StateSpaceProduct::empty() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the product of `this->buchi_automaton' and + * `this->statespace' is (trivially) empty. + * + * Arguments: None. + * + * Returns: true iff either the Büchi automaton or the state space has + * no states. + * + * ------------------------------------------------------------------------- */ +{ + return (buchi_automaton.empty() || statespace.empty()); +} + +/* ========================================================================= */ +inline unsigned long int StateSpaceProduct::numberOfAcceptanceSets() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of acceptance sets in the Büchi automaton + * associated with a StateSpaceProduct object. + * + * Arguments: None. + * + * Returns: The number of acceptance sets in the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return buchi_automaton.numberOfAcceptanceSets(); +} + +/* ========================================================================= */ +inline const BuchiAutomaton::BuchiState& StateSpaceProduct::firstComponent + (const Graph::size_type state_id) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing states in the Büchi automaton + * associated with a StateSpaceProduct object. + * + * Argument: state_id -- Identifier of a state in the automaton. + * + * Returns: A constant reference to a state in the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return buchi_automaton[state_id]; +} + +/* ========================================================================= */ +inline const StateSpace::State& StateSpaceProduct::secondComponent + (const Graph::size_type state_id) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing states in the state space associated + * with a StateSpaceProduct object. + * + * Argument: state_id -- Identifier of a state in the state space. + * + * Returns: A constant reference to a state in the state space. + * + * ------------------------------------------------------------------------- */ +{ + return statespace[state_id]; +} + +/* ========================================================================= */ +inline void StateSpaceProduct::mergeAcceptanceInformation + (const Graph::Node& state, + const Graph::Node&, BitArray& acceptance_sets) const +/* ---------------------------------------------------------------------------- + * + * Description: Merges the acceptance sets associated with a state of a Büchi + * automaton into a collection of sets. + * + * Arguments: state -- A constant reference to a state in the + * automaton. + * acceptance_sets -- A reference to a BitArray for storing + * the result. The BitArray should have + * capacity for (at least) + * `this->buchi_automaton + * .numberOfAcceptanceSets()' bits. + * (The second argument is needed to allow the class + * StateSpaceProduct to be used for instantiating the Product + * template; see file Product.h.) + * + * Returns: Nothing. After the operation, `acceptance_sets[i] == true' + * holds if `state.acceptanceSets().test(i) == true' for all + * 0 <= i < `this->buchi_automaton.numberOfAcceptanceSets()'. + * + * ------------------------------------------------------------------------- */ +{ + acceptance_sets.bitwiseOr + (static_cast(state).acceptanceSets(), + numberOfAcceptanceSets()); +} + +/* ========================================================================= */ +inline void StateSpaceProduct::mergeAcceptanceInformation + (const Graph::Edge& buchi_transition, + const Graph::Edge&, BitArray& acceptance_sets) const +/* ---------------------------------------------------------------------------- + * + * Description: Merges the acceptance sets associated with a transition of a + * Büchi automaton into a collection of sets. + * + * Arguments: transition -- A constant reference to a transition in + * the automaton. + * acceptance_sets -- A reference to a BitArray for storing + * the result. The BitArray should have + * capacity for (at least) + * `this->buchi_automaton + * .numberOfAcceptanceSets()' bits. + * (The second argument is needed to allow the class + * StateSpaceProduct to be used for instantiating the Product + * template; see file Product.h.) + * + * Returns: Nothing. After the operation, `acceptance_sets[i] == true' + * holds if `transition.acceptanceSets().test(i) == true' for + * all + * 0 <= i < `this->buchi_automaton.numberOfAcceptanceSets()'. + * + * ------------------------------------------------------------------------- */ +{ + acceptance_sets.bitwiseOr + (static_cast(buchi_transition) + .acceptanceSets(), + numberOfAcceptanceSets()); +} + +/* ========================================================================= */ +inline void StateSpaceProduct::validateEdgeIterators + (const Graph::Node& buchi_state, + const Graph::Node& system_state, + GraphEdgeContainer::const_iterator& buchi_transition, + GraphEdgeContainer::const_iterator& system_transition) const +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether a pair of transition iterators corresponds to + * a transition beginning from a state in the product of a Büchi + * automaton and a state space; if this is not the case, makes + * the iterators point to a valid transition beginning from the + * product state (or to the "end" of the collection of + * transitions beginning from the product state if no valid + * transition can be found by incrementing the iterators). + * + * Arguments: buchi_state, -- These variables determine the state + * system_state, in the product; `buchi_state' and + * `system_state' should be references to + * states in `this->buchi_automaton' and + * `this->statespace', respectively. + * buchi_transition, -- References to the transition + * system_transition iterators. It is assumed that + * `buchi_transition' and + * `system_transition' initially point to + * two transitions starting from + * `buchi_state' and `system_state', + * respectively. + * + * Returns: Nothing. Upon return, `buchi_transition' and + * `system_transition' will either equal + * `buchi_state.edges().end()' and `system_state.edges().end()', + * respectively, or they will point to a pair of transitions + * beginning from `buchi_state' and `system_state' such that + * this pair of transitions corresponds to a transition starting + * from the product state determined by `buchi_state' and + * `system_state'. + * + * ------------------------------------------------------------------------- */ +{ + const GraphEdgeContainer& buchi_transitions = buchi_state.edges(); + const GraphEdgeContainer& system_transitions = system_state.edges(); + + if (buchi_transition == buchi_transitions.end()) + { + system_transition = system_transitions.end(); + return; + } + if (system_transition == system_transitions.end()) + { + buchi_transition = buchi_transitions.end(); + return; + } + + while (!static_cast + (*buchi_transition)->enabled + (static_cast(system_state) + .positiveAtoms(), + statespace.numberOfPropositions())) + { + ++buchi_transition; + if (buchi_transition == buchi_transitions.end()) + { + system_transition = system_transitions.end(); + return; + } + } + + system_transition = system_transitions.begin(); +} + +/* ========================================================================= */ +inline void StateSpaceProduct::incrementEdgeIterators + (const Graph::Node& buchi_state, + const Graph::Node& system_state, + GraphEdgeContainer::const_iterator& buchi_transition, + GraphEdgeContainer::const_iterator& system_transition) const +/* ---------------------------------------------------------------------------- + * + * Description: Increments a pair of transition iterators to point to the + * "next" transition beginning from a state in the product of a + * Büchi automaton and a state space. If no "next" transition + * exists, makes the iterators point to the "end" of the + * collection of transitions beginning from the product state. + * + * Arguments: buchi_state, -- These variables determine the state + * system_state, in the product; `buchi_state' and + * `system_state' should be references to + * states in `this->buchi_automaton' and + * `this->statespace', respectively. + * buchi_transition, -- References to the transition + * system_transition iterators. It is assumed that + * `buchi_transition' and + * `system_transition' initially point to + * two transitions starting from + * `buchi_state' and `system_state', + * respectively. + * + * Returns: Nothing. Upon return, `buchi_transition' and + * `system_transition' will either equal + * `buchi_state.edges().end()' and `system_state.edges().end()', + * respectively, or they will point to a pair of transitions + * beginning from `buchi_state' and `system_state' such that + * this pair of transitions corresponds to a transition starting + * from the product state determined by `buchi_state' and + * `system_state'. + * + * ------------------------------------------------------------------------- */ +{ + const GraphEdgeContainer& buchi_transitions = buchi_state.edges(); + const GraphEdgeContainer& system_transitions = system_state.edges(); + + ++system_transition; + if (system_transition == system_transitions.end()) + { + do + { + ++buchi_transition; + if (buchi_transition == buchi_transitions.end()) + return; + } + while (!static_cast + (*buchi_transition)->enabled + (static_cast(system_state) + .positiveAtoms(), + statespace.numberOfPropositions())); + + system_transition = system_transitions.begin(); + } +} + +} + +#endif /* !STATESPACEPRODUCT_H */ diff --git a/lbtt/src/StringUtil.cc b/lbtt/src/StringUtil.cc index 048db9542..c1ff6d428 100644 --- a/lbtt/src/StringUtil.cc +++ b/lbtt/src/StringUtil.cc @@ -18,6 +18,8 @@ */ #include +#include +#include #include #include "StringUtil.h" @@ -100,6 +102,211 @@ void sliceString while (last_non_slicechar_pos != s.npos && last_slicechar_pos != s.npos); } +/* ========================================================================= */ +string toLowerCase(const string& s) +/* ---------------------------------------------------------------------------- + * + * Description: Converts a string to lower case. + * + * Argument: s -- String to process. + * + * Returns: The string in lower case. + * + * ------------------------------------------------------------------------- */ +{ + string result; + for (string::size_type pos = 0; pos < s.length(); ++pos) + result += tolower(s[pos]); + return result; +} + +/* ========================================================================= */ +bool interpretSpecialCharacters(const char c, bool& escape, char& quotechar) +/* ---------------------------------------------------------------------------- + * + * Description: Updates the values of `escape' and `quotechar' based on their + * original values and the value of `c'. Used for scanning + * through a string possibly containing quotes and escaped + * characters. + * + * Arguments: c -- A character. + * escape -- A truth value telling whether `c' was escaped. + * quotechar -- 0 == `c' was read outside of quotes. + * `'' == `c' was read inside single quotes. + * `"' == `c' was read inside double quotes. + * + * Returns: True if `c' had a special meaning (for example, if it was a + * begin/end quote character) in the state determined by the + * original values of `escape' and `quotechar'. + * + * ------------------------------------------------------------------------- */ +{ + if (escape) + { + escape = false; + return false; + } + + switch (c) + { + case '\\' : + if (quotechar != '\'') + { + escape = true; + return true; + } + break; + + case '\'' : case '"' : + if (quotechar == 0) + { + quotechar = c; + return true; + } + else if (c == quotechar) + { + quotechar = 0; + return true; + } + break; + + default : + break; + } + + return false; +} + +/* ========================================================================= */ +string unquoteString(const string& s) +/* ---------------------------------------------------------------------------- + * + * Description: Removes (unescaped) single and double quotes and escape + * characters from a string. + * + * Argument: s -- String to process. + * + * Returns: A string with the quotes and escape characters removed. + * + * ------------------------------------------------------------------------- */ +{ + string result; + char quotechar = 0; + bool escape = false; + + for (string::size_type pos = 0; pos < s.size(); ++pos) + { + if (!interpretSpecialCharacters(s[pos], escape, quotechar)) + result += s[pos]; + } + + return result; +} + +/* ========================================================================= */ +string::size_type findInQuotedString + (const string& s, const string& chars, QuoteMode type) +/* ---------------------------------------------------------------------------- + * + * Description: Finds a character in a string (respecting quotes). + * + * Arguments: s -- String to process. + * chars -- A sting of characters to be searched in `s'. + * type -- The extent of the search. + * GLOBAL - Apply the search to the entire + * string. + * INSIDE_QUOTES - Restrict the search to + * unescaped characters between + * quotes. + * OUTSIDE_QUOTES - Restrict the search to + * unescaped characters outside + * quotes. + * + * Returns: If `s' contains one of the characters in `chars' in a part + * of the string that matches `type', the position of the + * character in `s', and string::npos otherwise. + * + * ------------------------------------------------------------------------- */ +{ + char quotechar = 0; + bool escape = false; + + for (string::size_type pos = 0; pos < s.size(); ++pos) + { + if ((type == GLOBAL || (!escape && + ((type == INSIDE_QUOTES && quotechar != 0) + || (type == OUTSIDE_QUOTES && quotechar == 0)))) + && chars.find_first_of(s[pos]) != string::npos) + return pos; + + interpretSpecialCharacters(s[pos], escape, quotechar); + } + + return string::npos; +} + +/* ========================================================================= */ +string substituteInQuotedString + (const string& s, const string& chars, const string& substitutions, + QuoteMode type) +/* ---------------------------------------------------------------------------- + * + * Description: Substitutes characters in a string with other characters. + * + * Arguments: s -- String to process. + * chars -- A string of characters, each of which + * should be substituted in `s' with the + * character at the corresponding + * position of the string `substitutions'. + * substitutions -- Characters to substitute. The length of + * this string should equal the length of + * `chars'. + * type -- The extent of substitution. + * GLOBAL - Apply the substitutions + * globally (the default). + * INSIDE_QUOTES - Apply the substitutions + * to unescaped characters + * only inside quotes that + * have not been escaped + * with a backslash. + * OUTSIDE_QUOTES - Apply the substitutions + * to unescaped characters + * only outside quotes + * that have not been + * escaped with a + * backslash. + * It is not recommended to substitute the + * special characters ', " and \ with other + * characters if they have special meaning in + * `s'. + * + * Returns: A string with the substitutions. + * + * ------------------------------------------------------------------------- */ +{ + string result; + char quotechar = 0; + bool escape = false; + + for (string::size_type pos = 0; pos < s.size(); ++pos) + { + char c = s[pos]; + if (type == GLOBAL || (!escape && + ((type == INSIDE_QUOTES && quotechar != 0) + || (type == OUTSIDE_QUOTES && quotechar == 0)))) + { + string::size_type subst_pos = chars.find_first_of(c); + if (subst_pos != string::npos) + c = substitutions[subst_pos]; + } + result += c; + + interpretSpecialCharacters(s[pos], escape, quotechar); + } + + return result; +} + /* ========================================================================= */ unsigned long int parseNumber(const string& number_string) /* ---------------------------------------------------------------------------- @@ -115,9 +322,11 @@ unsigned long int parseNumber(const string& number_string) * ------------------------------------------------------------------------- */ { char* endptr; + unsigned long int number = strtoul(number_string.c_str(), &endptr, 10); - if (*endptr != '\0') + if (*endptr != '\0' || number_string.empty() + || number_string.find_first_of("-") != string::npos) throw NotANumberException("expected a nonnegative integer, got `" + number_string + "'"); @@ -125,98 +334,254 @@ unsigned long int parseNumber(const string& number_string) } /* ========================================================================= */ -void parseInterval - (const string& token, - set, ALLOC(unsigned long int) >& - number_set, - unsigned long int min, unsigned long int max) +int parseInterval + (const string& token, unsigned long int& min, unsigned long int& max) /* ---------------------------------------------------------------------------- * - * Description: Parses a string for a list of number intervals, storing all - * the numbers into a set. + * Description: Reads the lower and upper bound from an "interval string" + * into two unsigned long integer variables. * - * Arguments: token -- A reference to a constant string containing - * the list of intervals. A legal list of - * intervals has the following syntax: + * Arguments: token -- A reference to a constant "interval string" of + * the format + * + * ::= "*" // 0 + * | // 1 + * | // 2 + * | // 3 + * | // 4 + * where is an unsigned long integer (not + * containing a minus sign), and is either + * "-" or "...". The meaning of the various cases + * is as follows: + * 0 All integers between 0 and ULONG_MAX. + * 1 A point interval consisting of a single + * value. + * 2 An interval from 0 to a given upper + * bound. + * 3 An interval from a given lower bound to + * ULONG_MAX. + * 4 A bounded interval. + * min, max -- References to two unsigned long integers for + * storing the lower and upper bound of the + * interval. * - * - * ::= - * | '*' // all numbers in the - * // interval - * // [min, max] - * | '-' // all numbers in the - * // interval - * // [min, number] - * | '-' // all numbers in the - * // interval - * // [number, max] - * | '-' - * | ',' - * - * number_set -- A reference to a set of unsigned long - * integers for storing the result. - * min -- Minimum bound for the numbers. - * max -- Maximum bound for the numbers. - * - * Note: `min' and `max' are only used to determine the limits - * for only partially defined intervals. The check that - * the explicitly specified values in the interval list - * are within these bounds is left to the caller when - * examining the result set. - * - * Returns: Nothing. + * Returns: A value telling the type of the specified interval, which is + * a bitwise or of the values LEFT_BOUNDED and RIGHT_BOUNDED + * depending on which bounds were given explicitly for the + * interval. (The lower and upper bounds of the interval itself + * are stored in the variables `min' and `max', respectively.) + * The function will throw a NotANumberException if the + * interval string is of an invalid format. * * ------------------------------------------------------------------------- */ { - vector intervals; + unsigned long int tmp_min = 0; + unsigned long int tmp_max = ULONG_MAX; + int interval_type = UNBOUNDED; - sliceString(token, ",", intervals); - string::size_type dash_pos; + if (token != "*") + { + string::size_type pos(token.find_first_of("-")); + if (pos == string::npos) + pos = token.find("..."); + string value(token.substr(0, pos)); - number_set.clear(); + if (!value.empty()) + { + tmp_min = parseNumber(value); + if (pos == string::npos) + tmp_max = tmp_min; + interval_type |= LEFT_BOUNDED; + } + + if (pos != string::npos) + value = token.substr(pos + (token[pos] == '-' ? 1 : 3)); + + if (!value.empty()) + { + tmp_max = parseNumber(value); + interval_type |= RIGHT_BOUNDED; + } + else if (!(interval_type & LEFT_BOUNDED)) + throw NotANumberException("invalid format for interval"); + } + + min = tmp_min; + max = tmp_max; + + return interval_type; +} + +/* ========================================================================= */ +void parseIntervalList + (const string& token, IntervalList& intervals, unsigned long int min, + unsigned long int max, vector* extra_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Parses a string of number intervals into an IntervalList. + * + * Arguments: token -- A reference to a constant comma-separated + * list of interval strings (see documentation + * for the parseInterval function). + * intervals -- A reference to an IntervalList to be used + * for storing the result. + * min -- Absolute lower bound for the numbers. + * Numbers lower than this bound will not be + * stored in the result set. + * max -- Absolute upper bound for the numbers. + * Numbers greater than this bound will not be + * stored in the result set. + * extra_tokens -- If not 0, all tokens that cannot be + * recognized as valid interval strings will + * be stored in the vector of strings to which + * this variable points. Otherwise the + * function will throw a NotANumberException. + * + * Returns: Nothing. Throws an IntervalRangeException if any of the + * intervals in the list does not fit in the closed range + * [min,max]. + * + * ------------------------------------------------------------------------- */ +{ + vector interval_strings; + int interval_type; + + intervals.clear(); + sliceString(token, ",", interval_strings); for (vector::const_iterator - interval = intervals.begin(); - interval != intervals.end(); - ++interval) + i = interval_strings.begin(); + i != interval_strings.end(); + ++i) { - if (*interval == "*") + unsigned long int i_start, i_end; + + try { - for (unsigned long int i = min; i <= max; i++) - number_set.insert(i); - break; + interval_type = parseInterval(*i, i_start, i_end); } - - dash_pos = (*interval).find_first_of("-"); - - if (dash_pos == (*interval).npos) - number_set.insert(parseNumber(*interval)); - else + catch (const NotANumberException&) { - if (dash_pos == 0) + if (extra_tokens != 0) { - unsigned long int number = parseNumber((*interval).substr(1)); - for (unsigned long int i = min; i <= number; i++) - number_set.insert(i); - } - else if (dash_pos == (*interval).length() - 1) - { - unsigned long int number = - parseNumber((*interval).substr(0, (*interval).length() - 1)); - for (unsigned long int i = number; i <= max; i++) - number_set.insert(i); + extra_tokens->push_back(*i); + continue; } else - { - unsigned long int min_bound = - parseNumber((*interval).substr(0, dash_pos)); - unsigned long int max_bound = - parseNumber((*interval).substr(dash_pos + 1)); - - for (unsigned long int i = min_bound; i <= max_bound; i++) - number_set.insert(i); - } + throw; } + + if (interval_type & LEFT_BOUNDED) + { + if (i_start < min || i_start > max) + throw IntervalRangeException(i_start); + } + else if (i_start < min) + i_start = min; + + if (interval_type & RIGHT_BOUNDED) + { + if (i_end < min || i_end > max) + throw IntervalRangeException(i_end); + } + else if (i_end > max) + i_end = max; + + intervals.merge(i_start, i_end); + } +} + +/* ========================================================================= */ +void parseTime + (const string& time_string, unsigned long int& hours, + unsigned long int& minutes, unsigned long int& seconds) +/* ---------------------------------------------------------------------------- + * + * Description: Parses a "time string", i.e., a string of the form + * ([0-9]+"h")([0-9]+"min")?([0-9]+"s")? + * | ([0-9]+"min")([0-9]+"s")? + * | ([0-9]+"s") + * (where 'h', 'min' and 's' correspond to hours, minutes and + * seconds, respectively) and stores the numbers into three + * unsigned long integers. The case of the unit symbols is not + * relevant. + * + * Arguments: time_string -- String to process. + * hours -- A reference to an unsigned long integer for + * storing the number of hours. + * minutes -- A reference to an unsigned long integer for + * storing the number of minutes. + * seconds -- A reference to an unsigned long integer for + * storing the number of seconds. + * + * Time components left unspecified in `time_string' will get + * the value 0. + * + * Returns: Nothing. Throws an Exception if the given string is not of + * the correct format. + * + * ------------------------------------------------------------------------- */ +{ + bool hours_present = false, minutes_present = false, seconds_present = false; + hours = minutes = seconds = 0; + + if (time_string.empty()) + throw Exception("invalid time format"); + + string::size_type pos1 = 0; + string s; + + while (pos1 < time_string.length()) + { + string::size_type pos2 = time_string.find_first_not_of("0123456789", pos1); + if (pos2 >= time_string.length()) + throw Exception("invalid time format"); + + unsigned long int val; + + try + { + val = parseNumber(time_string.substr(pos1, pos2 - pos1)); + } + catch (const NotANumberException&) + { + throw Exception("invalid time format"); + } + + switch (tolower(time_string[pos2])) + { + case 'h' : + if (hours_present || minutes_present || seconds_present) + throw Exception("invalid time format"); + hours_present = true; + hours = val; + break; + + case 'm' : + if (minutes_present + || seconds_present + || pos2 + 2 >= time_string.length() + || tolower(time_string[pos2 + 1]) != 'i' + || tolower(time_string[pos2 + 2]) != 'n') + throw Exception("invalid time format"); + minutes_present = true; + minutes = val; + pos2 += 2; + break; + + case 's' : + if (seconds_present) + throw Exception("invalid time format"); + seconds_present = true; + seconds = val; + break; + + default : /* 's' */ + throw Exception("invalid time format"); + break; + } + + pos1 = pos2 + 1; } } diff --git a/lbtt/src/StringUtil.h b/lbtt/src/StringUtil.h index 65a9fa790..4965cf64f 100644 --- a/lbtt/src/StringUtil.h +++ b/lbtt/src/StringUtil.h @@ -31,6 +31,7 @@ #include #include "LbttAlloc.h" #include "Exception.h" +#include "IntervalList.h" using namespace std; @@ -62,17 +63,65 @@ void sliceString /* Breaks a string into */ * separators. */ +string toLowerCase(const string& s); /* Converts a string to + * lower case. + */ + +string unquoteString(const string& s); /* Removes unescaped quotes + * from a string and + * interprets escaped + * characters. + */ + +enum QuoteMode {GLOBAL, INSIDE_QUOTES, /* Enumeration type used */ + OUTSIDE_QUOTES}; /* for controlling the + * behavior of the + * substitute function. + */ + +string::size_type findInQuotedString /* Finds a character in */ + (const string& s, const string& chars, /* a string (respecting */ + QuoteMode type = GLOBAL); /* quotes). */ + +string substituteInQuotedString /* Replaces characters */ + (const string& s, const string& chars, /* in a string with */ + const string& substitutions, /* other characters. */ + QuoteMode type = GLOBAL); + unsigned long int parseNumber /* Converts a string to */ (const string& number_string); /* an unsigned long * integer. */ -void parseInterval /* Converts a string of */ - (const string& token, /* number intervals to */ - set, /* the corresponding set */ - ALLOC(unsigned long int) >& number_set, /* of unsigned long */ - unsigned long int min, /* integers. */ - unsigned long int max); + enum IntervalStringType {UNBOUNDED = 0, /* Type for an interval */ + LEFT_BOUNDED = 1, /* string (see the */ + RIGHT_BOUNDED = 2}; /* documentation of + * the parseInterval + * function in + * StringUtil.cc). + */ + +int parseInterval /* Reads the lower and */ + (const string& token, /* upper bounds from a */ + unsigned long int& min, /* "number interval */ + unsigned long int& max); /* string" into two + * unsigned long integer + * variables. + */ + +void parseIntervalList /* Converts a list of */ + (const string& token, /* number intervals to */ + IntervalList& intervals, /* the set of unsigned */ + unsigned long int min, /* long integers */ + unsigned long int max, /* corresponding to the */ + vector* extra_tokens /* union of the */ + = 0); /* intervals. */ + +void parseTime /* Parses a time string. */ + (const string& time_string, + unsigned long int& hours, + unsigned long int& minutes, + unsigned long int& seconds); @@ -133,6 +182,38 @@ public: +/****************************************************************************** + * + * A class for reporting "out of range" errors for numbers when parsing + * intervals. + * + *****************************************************************************/ + +class IntervalRangeException : public Exception +{ +public: + IntervalRangeException /* Constructor. */ + (const unsigned long int number, + const string& message = "number out of range"); + + /* default copy constructor */ + + ~IntervalRangeException() throw(); /* Destructor. */ + + IntervalRangeException& operator= /* Assignment operator. */ + (const IntervalRangeException& e); + + unsigned long int getNumber() const; /* Returns the number + * associated with the + * exception object. + */ + +private: + const unsigned long int invalid_number; +}; + + + /****************************************************************************** * * Inline function definitions for class NotANumberException. @@ -189,6 +270,82 @@ inline NotANumberException& NotANumberException::operator= return *this; } + + +/****************************************************************************** + * + * Inline function definitions for class IntervalRangeException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline IntervalRangeException::IntervalRangeException + (const unsigned long int number, const string& message) : + Exception(message), invalid_number(number) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class IntervalRangeException. Creates an + * exception object. + * + * Arguments: number -- A constant unsigned long integer specifying a + * number that does not fit in an interval. + * message -- A reference to a constant string containing an + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline IntervalRangeException::~IntervalRangeException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class IntervalRangeException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline unsigned long int IntervalRangeException::getNumber() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number associated with the IntervalRangeException + * object. + * + * Arguments: None. + * + * Returns: The number associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return invalid_number; +} + +/* ========================================================================= */ +inline IntervalRangeException& IntervalRangeException::operator= + (const IntervalRangeException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class IntervalRangeException. + * Copies the contents of an exception object to another. + * + * Arguments: e -- A reference to a constant IntervalRangeException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + } #endif /* !STRINGUTIL_H */ diff --git a/lbtt/src/TempFsysName.cc b/lbtt/src/TempFsysName.cc new file mode 100644 index 000000000..51dc9c335 --- /dev/null +++ b/lbtt/src/TempFsysName.cc @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#include +#include "Exception.h" +#include "StringUtil.h" +#include "TempFsysName.h" + +/****************************************************************************** + * + * Number of generated temporary names. + * + *****************************************************************************/ + +unsigned long int TempFsysName::number_of_allocated_names = 0; + + + +/****************************************************************************** + * + * Function definitions for class TempFsysName. + * + *****************************************************************************/ + +/* ========================================================================= */ +const char* TempFsysName::allocate + (const char* prefix, const NameType t, const bool literal) +/* ---------------------------------------------------------------------------- + * + * Description: Associates a TempFsysName object with a temporary name. (As + * a side effect, the function actually creates an empty + * temporary file or a directory to reserve the name in the file + * system. The file or directory should never be removed + * explicitly; it is removed automatically when the TempFsysName + * object is destroyed or another call is made to + * `this->allocate'.) + * + * Arguments: prefix -- Pointer to a C-style string containing a prefix + * for the temporary name (empty by default). If + * `literal == true', `prefix' (if nonempty) is + * assumed to contain the full path for the + * temporary file or directory; otherwise the + * function will reserve a temporary name in the + * `P_tmpdir' directory. This name will consist + * of the value of `prefix' (if nonempty), followed + * by the current value of + * `TempFsysName::number_of_allocated_names' and + * the current process id, separated by dots. + * t -- Determines the type of the name (file or a + * directory). + * literal -- See above. + * + * Returns: A pointer to a constant C-style string containing the + * temporary name. The function throws an IOException if the + * name allocation or the file or directory creation fails. + * + * ------------------------------------------------------------------------- */ +{ + releaseName(); + + using ::StringUtil::toString; + string tempname; + + try + { + if (!literal || strlen(prefix) == 0) + { + tempname = toString(P_tmpdir) + "/"; + if (strlen(prefix)) + tempname += string(prefix) + "."; + tempname += toString(number_of_allocated_names) + "." + + toString(getpid()); + ++number_of_allocated_names; + } + else + tempname = prefix; + + name = new char[tempname.length() + 1]; + strcpy(name, tempname.c_str()); + } + catch (const bad_alloc&) + { + name = 0; + throw IOException + ("unable to allocate a temporary name in the file system"); + } + + type = t; + if (t == FILE) + { + int fd = open(name, O_RDWR | O_CREAT | O_EXCL, S_IREAD | S_IWRITE); + if (fd == -1) + throw IOException("unable to create a temporary file"); + close(fd); + } + else if (mkdir(name, S_IRWXU) == -1) + throw IOException("unable to create a temporary directory"); + + return name; +} diff --git a/lbtt/src/TempFsysName.h b/lbtt/src/TempFsysName.h new file mode 100644 index 000000000..f2845ebb6 --- /dev/null +++ b/lbtt/src/TempFsysName.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2004 + * Heikki Tauriainen + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TEMPFSYSNAME_H +#define TEMPFSYSNAME_H + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +/****************************************************************************** + * + * A class for temporary file system names. + * + *****************************************************************************/ + +class TempFsysName +{ +public: + TempFsysName(); /* Constructor. */ + + ~TempFsysName(); /* Destructor. */ + + enum NameType { FILE, DIRECTORY }; /* Types of temporary + * file system names. + */ + + const char* allocate /* Allocates a name */ + (const char* prefix = "", /* in the file system. */ + const NameType t = FILE, + const bool literal = false); + + const char* get() const; /* Tells the name. */ + +private: + TempFsysName(const TempFsysName&); /* Prevent copying and */ + TempFsysName& operator=(const TempFsysName&); /* assignment of + * TempFsysName objects. + */ + + void releaseName(); /* Frees a name in the file + * system. + */ + + char* name; /* Temporary name. */ + + NameType type; /* Tells whether the name + * refers to a file or a + * directory. + */ + + static unsigned long int /* Counter for the */ + number_of_allocated_names; /* number of generated + * temporary names. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class TempFsysName. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline TempFsysName::TempFsysName() : name(static_cast(0)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class TempFsysName. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline TempFsysName::~TempFsysName() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class TempFsysName. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + releaseName(); +} + +/* ========================================================================= */ +inline const char* TempFsysName::get() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the name associated with the TempFsysName object. + * + * Arguments: None. + * + * Returns: A pointer to a constant C-style string containing the name + * associated with the TempFsysName object. If the TempFsysName + * object has not yet been (successfully) associated with a file + * using TempFsysName::allocate, this pointer has the value 0. + * + * ------------------------------------------------------------------------- */ +{ + return name; +} + +/* ========================================================================= */ +inline void TempFsysName::releaseName() +/* ---------------------------------------------------------------------------- + * + * Description: Deallocates the memory reserved for `this->name' and frees + * the name also in the file system by removing the file or + * directory associated with the object. If the name + * is associated with a directory, the directory is assumed to + * be empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (name == static_cast(0)) + return; + if (type == FILE) + remove(name); + else + rmdir(name); + delete[] name; + name = 0; +} + +#endif /* !TEMPFSYSNAME_H */ diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 12f2f91ec..5e3b3ed64 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -18,28 +18,50 @@ */ #include +#include +#include #include #include +#include +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ +#ifdef HAVE_SYS_TIMES_H #include +#endif /* HAVE_SYS_TIMES_H */ +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ +#ifdef HAVE_SYS_WAIT_H +#include +#endif /* HAVE_SYS_WAIT_H */ #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ #include "BitArray.h" #include "BuchiAutomaton.h" +#include "BuchiProduct.h" #include "DispUtil.h" +#include "Product.h" +#include "IntervalList.h" #include "LtlFormula.h" #include "PathEvaluator.h" -#include "ProductAutomaton.h" #include "Random.h" -#include "SccIterator.h" +#include "SccCollection.h" #include "SharedTestData.h" #include "PathIterator.h" #include "StateSpace.h" +#include "StateSpaceProduct.h" #include "StatDisplay.h" #include "StringUtil.h" +#include "TempFsysName.h" #include "TestOperations.h" #include "TestRoundInfo.h" -#include "TestStatistics.h" + + /****************************************************************************** * @@ -55,6 +77,19 @@ using namespace ::StatDisplay; using namespace ::StringUtil; using namespace ::DispUtil; +/****************************************************************************** + * + * Timeout handler. + * + *****************************************************************************/ + +bool timeout = false; + +void timeoutHandler(int) +{ + timeout = true; +} + /* ========================================================================= */ void openFile (const char* filename, ifstream& stream, ios::openmode mode, int indent) @@ -129,13 +164,19 @@ void openFile } /* ========================================================================= */ -void removeFile(const char* filename, int indent) +void openFile(const char* filename, int& fd, int flags, int indent) /* ---------------------------------------------------------------------------- * - * Description: Removes a file. + * Description: Function for opening a file for input/output using file + * descriptors. * * Arguments: filename -- A pointer to a constant C-style string with - * the name of the file to be removed. + * the name of the file to be opened. + * fd -- A reference to an int that should be associated + * with the file descriptor of the file. This + * variable will have the value -1 if the + * operation fails. + * flags -- An integer specifying the open mode. * indent -- Number of spaces to leave to the left of * messages given to the user. * @@ -143,10 +184,52 @@ void removeFile(const char* filename, int indent) * * ------------------------------------------------------------------------- */ { - printText(string("", 5, indent); + printText(string("<") + (flags & O_CREAT ? "creat" : "open") + "ing `" + + filename + "'>", + 5, + indent); - if (remove(filename) == 0) + if (flags & O_CREAT) + fd = open(filename, flags, S_IRUSR | S_IWUSR); + else + fd = open(filename, flags); + + if (fd == -1) + { + printText(" error\n", 5); + + if (flags & O_CREAT) + throw FileCreationException(string("`") + filename + "'"); + else + throw FileOpenException(string("`") + filename + "'"); + } + else printText(" ok\n", 5); +} + +/* ========================================================================= */ +void truncateFile(const char* filename, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Truncates a file. + * + * Arguments: filename -- A pointer to a constant C-style string with + * the name of the file to be truncated. + * indent -- Number of spaces to leave to the left of + * messages given to the user. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + printText(string("", 5, indent); + + int fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd != -1) + { + close(fd); + printText(" ok\n", 5); + } else printText(" error\n", 5); } @@ -617,9 +700,9 @@ void verifyFormulaOnPath() if (printText("Model checking formula using internal algorithm\n", 2, 4)) printText("", 4, 6); - test_results[round_info.number_of_translators].automaton_stats[0]. + test_results[round_info.number_of_translators - 1].automaton_stats[0]. emptiness_check_result.clear(); - test_results[round_info.number_of_translators].automaton_stats[1]. + test_results[round_info.number_of_translators - 1].automaton_stats[1]. emptiness_check_result.clear(); try @@ -637,17 +720,18 @@ void verifyFormulaOnPath() s++) { if (path_evaluator.getResult(s)) - test_results[round_info.number_of_translators].automaton_stats[0]. + test_results[round_info.number_of_translators - 1].automaton_stats[0]. emptiness_check_result.setBit(s); else - test_results[round_info.number_of_translators].automaton_stats[1]. + test_results[round_info.number_of_translators - 1].automaton_stats[1]. emptiness_check_result.setBit(s); } } catch (const UserBreakException&) { - if (!printText(" user break\n\n", 4)) - printText("[User break]\n\n", 2, 6); + if (!printText(" aborted (user break)", 4)) + printText("[User break]", 2, 6); + printText("\n\n", 2); if (round_info.transcript_file.is_open()) writeToTranscript("User break while model checking formulas. No tests " @@ -657,8 +741,9 @@ void verifyFormulaOnPath() } catch (const bad_alloc&) { - if (!printText(" out of memory\n\n", 4)) - printText("[Out of memory]\n\n", 2, 6); + if (!printText(" aborted (out of memory)", 4)) + printText("[Out of memory]", 2, 6); + printText("\n\n", 2); if (round_info.transcript_file.is_open()) writeToTranscript("Out of memory while model checking formulas. No " @@ -671,9 +756,9 @@ void verifyFormulaOnPath() printText(" ok\n", 4); printText("\n", 2); - test_results[round_info.number_of_translators].automaton_stats[0]. + test_results[round_info.number_of_translators - 1].automaton_stats[0]. emptiness_check_performed = true; - test_results[round_info.number_of_translators].automaton_stats[1]. + test_results[round_info.number_of_translators - 1].automaton_stats[1]. emptiness_check_performed = true; } @@ -699,17 +784,18 @@ void writeFormulaeToFiles() if (!round_info.formula_in_file[f]) { - printText(string("\n", - 5, - 6); - try { - openFile(round_info.formula_file_name[f], formula_file, + openFile(round_info.formula_file_name[f]->get(), formula_file, ios::out | ios::trunc, 6); + printText(string("get() + + "'>\n", + 5, + 6); + round_info.formulae[configuration.formula_options. output_mode == Configuration::NNF ? f @@ -763,11 +849,10 @@ void generateBuchiAutomaton = test_results[algorithm_id].automaton_stats[f]; if (automaton_stats.buchiAutomatonComputed()) - printText("Büchi automaton (cached):\n", 2, 8); + printText("Büchi automaton (cached):\n", 3, 8); else { - if (!printText("Büchi automaton:\n", 3, 8)) - printText("Computing Büchi automaton\n", 2, 8); + printText("Büchi automaton:\n", 3, 8); const Configuration::AlgorithmInformation& algorithm = configuration.algorithms[algorithm_id]; @@ -778,129 +863,340 @@ void generateBuchiAutomaton struct tms timing_information_begin, timing_information_end; + string failure_reason; + int stdout_capture_fileno = -1, stderr_capture_fileno = -1; int exitcode; - string command_line; + + sigset_t sigint_mask; + sigemptyset(&sigint_mask); + sigaddset(&sigint_mask, SIGINT); + + struct sigaction timeout_sa; + timeout_sa.sa_handler = timeoutHandler; + sigemptyset(&timeout_sa.sa_mask); + timeout_sa.sa_flags = 0; + + pid_t pid = 0; + + truncateFile(round_info.automaton_file_name->get(), 10); + truncateFile(round_info.cout_capture_file->get(), 10); + truncateFile(round_info.cerr_capture_file->get(), 10); try { - /* - * Generate the command line to be used for executing the program that - * is supposed to generate the automaton from a formula. The name of the - * executable and any extra command line parameters are obtained from - * the program configuration data structures. The command line will be - * of the form - * [path to executable] [additional parameters] [input file] - * [output file] 1>[stdout capture file] - * 2>[stderr capture file], - * so the program used for generating the automaton is assumed to follow - * this format for passing the necessary input and output filenames. - * Note: This kind of output redirection requires that the call to - * `system' invokes a POSIX compatible shell! - * - * The output of stdout and stderr will be redirected to two temporary - * files. - */ - - command_line = *(algorithm.path_to_program) + ' '; - - if (algorithm.extra_parameters != 0) - command_line += *(algorithm.extra_parameters) + ' '; - - command_line += string(round_info.formula_file_name[f]) - + ' ' + round_info.automaton_file_name - + " 1>" + round_info.cout_capture_file - + " 2>" + round_info.cerr_capture_file; - - if (!printText("", 5, 10)) - printText("", 4, 10); - - /* - * Execute the translator and record its running time (user time) into - * `automaton_stats.buchi_generation_time'. (The variable will have a - * negative value if the time could not be determined.) - */ - automaton_stats.buchi_generation_time = -1.0; - times(&timing_information_begin); - - exitcode = system(command_line.c_str()); - - times(&timing_information_end); - - if (timing_information_begin.tms_utime != static_cast(-1) - && timing_information_begin.tms_cutime != static_cast(-1) - && timing_information_end.tms_utime != static_cast(-1) - && timing_information_end.tms_cutime != static_cast(-1)) - automaton_stats.buchi_generation_time - = static_cast(timing_information_end.tms_utime - + timing_information_end.tms_cutime - - timing_information_begin.tms_utime - - timing_information_begin.tms_cutime) - / sysconf(_SC_CLK_TCK); - /* - * Nonzero exit codes from the external program are interpreted as - * errors. In this case, throw an exception indicating that the program - * execution failed. + * Redirect standard output and standard error to files. */ - if (exitcode != 0) + try { + openFile(round_info.cout_capture_file->get(), stdout_capture_fileno, + O_CREAT | O_WRONLY | O_TRUNC, 10); + + openFile(round_info.cerr_capture_file->get(), stderr_capture_fileno, + O_CREAT | O_WRONLY | O_TRUNC, 10); + } + catch (const FileCreationException&) + { + if (stdout_capture_fileno != -1) + { + close(stdout_capture_fileno); + stdout_capture_fileno = -1; + } + printText(string(9, ' '), 4); + throw Exception(string("redirection of standard ") + + (stdout_capture_fileno == -1 ? "output" : "error") + + " failed (" + + string(strerror(errno)) + ")"); + } + + /* Execute the external program. */ + + if (!printText("", 5, 10)) + printText("", 4, 10); + + int error_number; + int error_pipe[2]; /* used for communicating errors in exec() */ + + double elapsed_time = -1.0; + + if (pipe(error_pipe) == -1) + error_number = errno; + else + { + algorithm.parameters[algorithm.num_parameters + 1] + = const_cast(round_info.formula_file_name[f]->get()); + algorithm.parameters[algorithm.num_parameters + 2] + = const_cast(round_info.automaton_file_name->get()); + + times(&timing_information_begin); + pid = fork(); + switch (pid) + { + case 0 : /* child */ + close(error_pipe[0]); + + if (dup2(stdout_capture_fileno, STDOUT_FILENO) != -1 + && dup2(stderr_capture_fileno, STDERR_FILENO) != -1) + execvp(algorithm.parameters[0], algorithm.parameters); + + /* dup2 or exec failed: write the value of errno to error_pipe */ + + write(error_pipe[1], static_cast(&errno), + sizeof(int)); + close(error_pipe[1]); + exit(0); + + case -1 : /* fork failed */ + pid = 0; + error_number = errno; + close(error_pipe[0]); + close(error_pipe[1]); + break; + + default : /* parent */ + /* Block SIGINT signals while the child process is running. */ + + if (configuration.global_options.handle_breaks) + sigprocmask(SIG_BLOCK, &sigint_mask, static_cast(0)); + + /* Install handler for timeouts if necessary. */ + + if (configuration.global_options.translator_timeout > 0) + { + sigaction(SIGALRM, &timeout_sa, + static_cast(0)); + timeout = false; + alarm(configuration.global_options.translator_timeout); + } + + close(error_pipe[1]); + + if (waitpid(pid, &exitcode, 0) == -1) /* waitpid failed */ + { + error_number = errno; + if (error_number == EINTR /* failure due to timeout */ + && configuration.global_options.translator_timeout > 0 + && timeout) + { + /* + * Try to terminate the child process three times with SIGTERM + * (sleeping for one second between the tries). If the child + * fails to respond, try to terminate the child one more time + * with SIGKILL. + */ + + int sig = SIGTERM; + unsigned int delay = 1; + for (int attempts_to_terminate = 0; attempts_to_terminate < 4; + ++attempts_to_terminate) + { + kill(pid, sig); + sleep(delay); + if (waitpid(pid, &exitcode, WNOHANG) != 0) + { + times(&timing_information_end); + pid = 0; + break; + } + if (attempts_to_terminate == 2) + { + sig = SIGKILL; + delay = 5; + } + } + } + else + pid = 0; + } + else /* child exited successfully */ + { + times(&timing_information_end); + pid = 0; + + /* + * If there is something to be read from error_pipe, then there + * was an error in replacing the child process with the external + * program (and the pipe contains the value of errno in this + * case). + */ + + if (read(error_pipe[0], static_cast(&error_number), + sizeof(int)) == 0) + error_number = 0; + } + + close(error_pipe[0]); + + /* Restore signal handlers and remove any pending alarms. */ + + if (configuration.global_options.translator_timeout > 0) + { + timeout_sa.sa_handler = SIG_DFL; + sigaction(SIGALRM, &timeout_sa, + static_cast(0)); + alarm(0); + } + + if (configuration.global_options.handle_breaks) + sigprocmask(SIG_UNBLOCK, &sigint_mask, + static_cast(0)); + + if (pid == 0 + && timing_information_begin.tms_utime + != static_cast(-1) + && timing_information_begin.tms_cutime + != static_cast(-1) + && timing_information_end.tms_utime != static_cast(-1) + && timing_information_end.tms_cutime + != static_cast(-1)) + elapsed_time = static_cast + (timing_information_end.tms_utime + + timing_information_end.tms_cutime + - timing_information_begin.tms_utime + - timing_information_begin.tms_cutime) + / sysconf(_SC_CLK_TCK); + + break; + } + } + + close(stdout_capture_fileno); + close(stderr_capture_fileno); + + /* + * If pid != 0 at this point, then a timeout occurred, but lbtt was + * unable to terminate the child process. The exception handler will + * in this case throw an unexpected exception (see below) so that lbtt + * will terminate (for example, it is not safe to use the temporary + * file names any longer if the (still running) child process happens to + * write to them). + */ + + if (pid != 0) + { + stdout_capture_fileno = stderr_capture_fileno = -1; + throw Exception("could not terminate child process"); + } + + if (error_number != 0) /* pipe, fork, dup2, execvp or waitpid failed */ + { + stdout_capture_fileno = stderr_capture_fileno = -1; ExecFailedException e; - e.changeMessage("Execution of `" + *(algorithm.path_to_program) - + "' failed" - + (automaton_stats.buchi_generation_time >= 0.0 - ? " (" - + toString(automaton_stats.buchi_generation_time, - 2) - + " seconds elapsed)" - : string("")) - + " with exit status " + toString(exitcode)); + + if (configuration.global_options.translator_timeout > 0 && timeout) + e.changeMessage("Timeout after " + toString(elapsed_time, 2) + + " seconds (user time)."); + else + e.changeMessage("Execution of `" + string(algorithm.parameters[0]) + + "' failed (" + string(strerror(error_number)) + + ")"); throw e; } - printText(" ok\n", 5); + automaton_stats.buchi_generation_time = elapsed_time; + + /* + * Nonzero exit codes from the external program are interpreted as + * errors. The same holds if the program was aborted by a signal. In + * these cases, throw an exception indicating that the program + * execution failed. + */ + + if (WIFSIGNALED(exitcode) + || (WIFEXITED(exitcode) && WEXITSTATUS(exitcode) != 0)) + { + ExecFailedException e; + failure_reason = "`" + string(algorithm.parameters[0]) + "' "; + + if (WIFSIGNALED(exitcode)) + { + failure_reason += "aborted by signal " + + toString(WTERMSIG(exitcode)) +#ifdef HAVE_STRSIGNAL + + " (" + strsignal(WTERMSIG(exitcode)) + ")" +#endif /* HAVE_STRSIGNAL */ + ; + if (WTERMSIG(exitcode) == SIGINT || WTERMSIG(exitcode) == SIGQUIT) + raise(WTERMSIG(exitcode)); + } + else + failure_reason += "exited with exit status " + + toString(WEXITSTATUS(exitcode)); + + e.changeMessage(failure_reason + + (automaton_stats.buchi_generation_time >= 0.0 + ? " after " + + toString(automaton_stats.buchi_generation_time, + 2) + + " seconds" + : string(""))); + + throw e; + } + + printText(" ok\n", 4); /* * Read the automaton description into memory from the result file. */ ifstream automaton_file; - openFile(round_info.automaton_file_name, automaton_file, ios::in, 10); + openFile(round_info.automaton_file_name->get(), automaton_file, ios::in, + 10); - printText("", 5, 10); + printText("", 4, 10); - automaton_file >> *buchi_automaton; - - printText(" ok\n", 4); + try + { + automaton_file >> *buchi_automaton; + } + catch (const bad_alloc&) + { + throw Exception("out of memory"); + } automaton_file.close(); + printText(" ok\n", 4); + automaton_stats.buchi_automaton = buchi_automaton; } catch (...) { delete buchi_automaton; - printText(" error\n", 4); - printText("Error", 2, 10); + if (user_break) + { + if (!printText(" aborted (user break)", 4)) + printText("[User break]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n\n", 1); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break while generating Büchi automaton (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)\n"); + + throw UserBreakException(); + } if (round_info.transcript_file.is_open()) - { writeToTranscript("Büchi automaton generation failed (" + configuration.algorithmString(algorithm_id) + ", " + (f == 0 ? "posi" : "nega") - + "tive formula)"); - - if (automaton_stats.buchi_generation_time >= 0.0) - round_info.transcript_file << string(8, ' ') + "Elapsed time: " - + toString(automaton_stats. - buchi_generation_time, - 2) - + " seconds (user time)\n"; - } + + "tive formula)" + + (automaton_stats.buchi_generation_time >= 0.0 + ? "\n" + string(8, ' ') + "Elapsed time: " + + toString(automaton_stats. + buchi_generation_time, + 2) + + " seconds (user time)" + : string(""))); try { @@ -908,95 +1204,104 @@ void generateBuchiAutomaton } catch (const ExecFailedException& e) { - printText(string(": ") + e.what(), 2); + if (configuration.global_options.translator_timeout > 0 && timeout) + { + if (!printText(" aborted (timeout)", 4)) + printText("[Timeout]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + } + else + { + if (!printText(string(" error: ") + e.what(), 4)) + printText("[Failed to execute translator]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + } + printText("\n", 3); if (round_info.transcript_file.is_open()) - round_info.transcript_file << string(8, ' ') - + "Program execution failed with exit " - "status " - + toString(exitcode); + round_info.transcript_file << string(8, ' ') + e.what() + "\n"; } catch (const BuchiAutomaton::AutomatonParseException& e) { - printText(string(" parsing input: ") + e.what(), 2); + if (!printText(string(" error parsing input: ") + e.what(), 4)) + printText("[Error parsing automaton]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n", 3); if (round_info.transcript_file.is_open()) round_info.transcript_file << string(8, ' ') + "Error reading automaton: " - + e.what(); + + e.what() + + "\n"; } catch (const Exception& e) { - printText(string(": ") + e.what(), 2); + if (!printText(string(" lbtt internal error: ") + e.what(), 4)) + printText("[lbtt internal error]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n", 3); if (round_info.transcript_file.is_open()) round_info.transcript_file << string(8, ' ') + "lbtt internal error: " - + e.what(); - } - catch (const bad_alloc&) - { - printText(": out of memory", 2); - if (round_info.transcript_file.is_open()) - round_info.transcript_file << string(8, ' ') - + "Out of memory while reading " - "automaton"; - } - - printText("\n", 2); - - if (round_info.transcript_file.is_open()) - round_info.transcript_file << '\n'; - - removeFile(round_info.automaton_file_name, 10); - - try - { - const char* msg = "Contents of stdout"; - - if (configuration.global_options.verbosity >= 3) - printFileContents(cout, msg, round_info.cout_capture_file, 10, "> "); - if (round_info.transcript_file.is_open()) - printFileContents(round_info.transcript_file, msg, - round_info.cout_capture_file, 8, "> "); - } - catch (const IOException&) - { + + e.what() + + "\n"; } try { - const char* msg = "Contents of stderr:"; + if (stdout_capture_fileno != -1) + { + const char* msg = "Contents of stdout"; - if (configuration.global_options.verbosity >= 3) - printFileContents(cout, msg, round_info.cerr_capture_file, 10, "> "); - if (round_info.transcript_file.is_open()) - printFileContents(round_info.transcript_file, msg, - round_info.cerr_capture_file, 8, "> "); + if (configuration.global_options.verbosity >= 3) + printFileContents(cout, msg, round_info.cout_capture_file->get(), + 10, "> "); + if (round_info.transcript_file.is_open()) + printFileContents(round_info.transcript_file, msg, + round_info.cout_capture_file->get(), 10, "> "); + } + + if (stderr_capture_fileno != -1) + { + const char* msg = "Contents of stderr:"; + + if (configuration.global_options.verbosity >= 3) + printFileContents(cout, msg, round_info.cerr_capture_file->get(), + 10, "> "); + if (round_info.transcript_file.is_open()) + printFileContents(round_info.transcript_file, msg, + round_info.cerr_capture_file->get(), 10, "> "); + } } catch (const IOException&) { } if (round_info.transcript_file.is_open()) + { round_info.transcript_file << '\n'; + round_info.transcript_file.flush(); + } - removeFile(round_info.cout_capture_file, 10); - removeFile(round_info.cerr_capture_file, 10); + if (pid != 0) /* fatal error, lbtt should be terminated */ + throw Exception + ("fatal internal error while generating Büchi automaton"); throw BuchiAutomatonGenerationException(); } - removeFile(round_info.automaton_file_name, 10); - if (configuration.global_options.verbosity >= 3) { - printFileContents(cout, "Contents of stdout:", - round_info.cout_capture_file, 10, "> "); - printFileContents(cout, "Contents of stderr:", - round_info.cerr_capture_file, 10, "> "); + try + { + printFileContents(cout, "Contents of stdout:", + round_info.cout_capture_file->get(), 10, "> "); + printFileContents(cout, "Contents of stderr:", + round_info.cerr_capture_file->get(), 10, "> "); + } + catch (const IOException&) + { + } } - removeFile(round_info.cout_capture_file, 10); - removeFile(round_info.cerr_capture_file, 10); - printText("", 4, 10); pair buchi_stats @@ -1028,143 +1333,10 @@ void generateBuchiAutomaton printText(" ok\n", 4); } - if (configuration.global_options.verbosity >= 3) + if (configuration.global_options.verbosity >= 1) printBuchiAutomatonStats(cout, 10, algorithm_id, f); } -/* ========================================================================= */ -void generateProductAutomaton - (int f, - vector::size_type algorithm_id) -/* ---------------------------------------------------------------------------- - * - * Description: Computes the product of a Büchi automaton with a state - * space. - * - * Arguments: f -- Indicates the Büchi automaton with which - * the product should be computed. 0 - * corresponds to the automaton obtained from - * the positive, 1 corresponds to the one - * obtained from the negated formula. - * algorithm_id -- Identifier of the LTL-to-Büchi translator - * used for generating the Büchi automata. - * - * Returns: Nothing. The result is stored in - * 'round_info.product_automaton'. - * - * ------------------------------------------------------------------------- */ -{ - using ::Graph::ProductAutomaton; - - AutomatonStats& automaton_stats - = test_results[algorithm_id].automaton_stats[f]; - - if (automaton_stats.emptiness_check_performed) - return; - - if (printText("Product automaton:\n", 3, 8)) - printText("", 4, 10); - else - printText("Computing product automaton\n", 2, 8); - - final_statistics[algorithm_id].product_automaton_count[f]++; - - ProductAutomaton* product_automaton = 0; - - /* - * Initialize the product automaton. The variable - * `configuration.global_options.product_mode' is used to determine whether - * the product is to be computed with respect to all the states of the - * state space or only the initial state of the state space. - */ - - try - { - product_automaton = new ProductAutomaton(); - product_automaton->computeProduct - (*automaton_stats.buchi_automaton, *round_info.statespace, - configuration.global_options.product_mode == Configuration::GLOBAL); - } - catch (const ProductAutomaton::ProductSizeException&) - { - if (!printText(" error (product may be too large)\n\n", 4)) - printText("[Product may be too large]\n\n", 2, 10); - - if (round_info.transcript_file.is_open()) - writeToTranscript("Product automaton generation aborted (" - + configuration.algorithmString(algorithm_id) - + ", " - + (f == 0 ? "posi" : "nega") + "tive formula)" - + ". Product may be too large.\n"); - - delete product_automaton; - - throw ProductAutomatonGenerationException(); - } - catch (const UserBreakException&) - { - if (!printText(" user break\n\n", 4)) - printText("[User break]\n\n", 2, 10); - - if (round_info.transcript_file.is_open()) - writeToTranscript("User break while generating product automaton (" - + configuration.algorithmString(algorithm_id) - + ", " - + (f == 0 ? "posi" : "nega") + "tive formula)\n"); - - delete product_automaton; - - throw; - } - catch (const bad_alloc&) - { - if (product_automaton != 0) - delete product_automaton; - - if (!printText(" out of memory\n", 4)) - printText("[Out of memory]\n", 2, 10); - - if (round_info.transcript_file.is_open()) - writeToTranscript("Out of memory while generating product " - "automaton (" - + configuration.algorithmString(algorithm_id) - + ", " - + (f == 0 ? "posi" : "nega") + "tive formula)\n"); - - throw ProductAutomatonGenerationException(); - } - - round_info.product_automaton = product_automaton; - - /* - * Determine the number of states and transitions in the product. - */ - - if (printText(" ok\n", 4)) - printText("", 4, 10); - - pair product_stats = - product_automaton->stats(); - - automaton_stats.number_of_product_states = product_stats.first; - automaton_stats.number_of_product_transitions = product_stats.second; - - /* - * Update product automaton statistics for the given algorithm. - */ - - final_statistics[algorithm_id].total_number_of_product_states[f] - += automaton_stats.number_of_product_states; - final_statistics[algorithm_id].total_number_of_product_transitions[f] - += automaton_stats.number_of_product_transitions; - - printText(" ok\n", 4); - - if (configuration.global_options.verbosity >= 3) - printProductAutomatonStats(cout, 10, algorithm_id, f); -} - /* ========================================================================= */ void performEmptinessCheck (int f, @@ -1194,30 +1366,71 @@ void performEmptinessCheck AutomatonStats& automaton_stats = test_results[algorithm_id].automaton_stats[f]; - if (automaton_stats.emptiness_check_performed) - printText("Accepting cycles (cached):\n", 2, 8); - else + const bool result_cached = automaton_stats.emptiness_check_performed; + + printText("Product automaton" + + string(result_cached ? " (cached)" : "") + + ":\n", + 3, + 8); + + if (!result_cached) { - if (printText("Accepting cycles:\n", 3, 8)) - printText("", 4, 10); - else - printText("Searching for accepting cycles\n", 2, 8); + printText("", 4, 10); + + final_statistics[algorithm_id].product_automaton_count[f]++; + + using ::Graph::StateSpaceProduct; + using ::Graph::Product; try { - round_info.product_automaton->emptinessCheck - (automaton_stats.emptiness_check_result); - + Product + product(*automaton_stats.buchi_automaton, *round_info.statespace); + + const pair::size_type, unsigned long int> + product_stats = product.globalEmptinessCheck + (automaton_stats.buchi_automaton->initialState(), + automaton_stats.emptiness_check_result, + round_info.real_emptiness_check_size); + + printText(" ok\n", 4); + + automaton_stats.number_of_product_states = product_stats.first; + automaton_stats.number_of_product_transitions = product_stats.second; + + final_statistics[algorithm_id].total_number_of_product_states[f] + += automaton_stats.number_of_product_states; + final_statistics[algorithm_id].total_number_of_product_transitions[f] + += automaton_stats.number_of_product_transitions; automaton_stats.emptiness_check_performed = true; } + catch (const Product::SizeException&) + { + if (!printText(" aborted (product may be too large)", 4)) + printText("[Product may be too large]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n", 3); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Product automaton generation aborted (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)" + + ". Product may be too large.\n"); + + throw ProductAutomatonGenerationException(); + } catch (const UserBreakException&) { - if (!printText(" user break\n\n", 4)) - printText("[User break]\n\n", 2, 10); - + if (!printText(" aborted (user break)", 4)) + printText("[User break]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n\n", 1); + if (round_info.transcript_file.is_open()) - writeToTranscript("User break while searching for accepting cycles (" - + configuration.algorithmString(algorithm_id) + writeToTranscript("User break while generating product automaton (" + + configuration.algorithmString(algorithm_id) + ", " + (f == 0 ? "posi" : "nega") + "tive formula)\n"); @@ -1225,24 +1438,32 @@ void performEmptinessCheck } catch (const bad_alloc&) { - if (!printText(" out of memory\n", 4)) - printText("[Out of memory]\n", 2, 10); + if (!printText(" aborted (out of memory)", 4)) + printText("[Out of memory]", 1, + configuration.global_options.verbosity <= 2 ? 0 : 10); + printText("\n", 3); if (round_info.transcript_file.is_open()) - writeToTranscript("Out of memory while searching for accepting cycles " - "(" + writeToTranscript("Out of memory while generating product " + "automaton (" + configuration.algorithmString(algorithm_id) + ", " + (f == 0 ? "posi" : "nega") + "tive formula)\n"); - throw EmptinessCheckFailedException(); + throw ProductAutomatonGenerationException(); } - - printText(" ok\n", 4); } - if (configuration.global_options.verbosity >= 3) + if (configuration.global_options.verbosity >= 1) + printProductAutomatonStats(cout, 10, algorithm_id, f); + + printText("Accepting cycles" + string(result_cached ? " (cached)" : "") + + ":\n", + 3, + 8); + if (configuration.global_options.verbosity >= 1) printAcceptanceCycleStats(cout, 10, algorithm_id, f); + } /* ========================================================================= */ @@ -1277,8 +1498,6 @@ void performConsistencyCheck if (printText("Result consistency check:\n", 3, 6)) printText("", 4, 8); - else - printText("Checking consistency of the model checking results\n", 2, 6); /* * The consistency check will succeed if `result' is still true at the end @@ -1319,7 +1538,7 @@ void performConsistencyCheck printText((result ? " ok\n" : " failed\n"), 4); - if (configuration.global_options.verbosity >= 3) + if (configuration.global_options.verbosity >= 2) printConsistencyCheckStats(cout, 8, algorithm_id); } @@ -1340,7 +1559,7 @@ void compareResults() if (printText("Model checking result cross-comparison:\n", 3, 4)) printText("", 4, 6); else - printText("Comparing model checking results\n", 2, 4); + printText("Comparing model checking results", 2, 4); bool result = true; AutomatonStats* alg_1_stats; @@ -1408,6 +1627,9 @@ void compareResults() } } + IntervalList algorithms; + algorithms.merge(0, round_info.number_of_translators - 1); + if (!result) { round_info.error = true; @@ -1416,13 +1638,20 @@ void compareResults() { writeToTranscript("Model checking result cross-comparison check failed"); printCrossComparisonStats(round_info.transcript_file, 8, - configuration.algorithms.size()); + algorithms); } } printText((result ? " ok\n" : " failed\n"), 4); - if (configuration.global_options.verbosity >= 3) - printCrossComparisonStats(cout, 6, test_results.size()); + if (configuration.global_options.verbosity == 2) + { + if (!result) + round_info.cout << " [failed]"; + round_info.cout << '\n'; + round_info.cout.flush(); + } + else if (configuration.global_options.verbosity >= 3) + printCrossComparisonStats(cout, 6, algorithms); } /* ========================================================================= */ @@ -1438,15 +1667,14 @@ void performBuchiIntersectionCheck() * * ------------------------------------------------------------------------- */ { - using ::Graph::SccIterator; - if (printText("Büchi automata intersection emptiness check:\n", 3, 4)) printText("\n", 4, 6); else - printText("Checking Büchi automata intersections for emptiness\n", 2, 4); + printText("Checking Büchi automata intersections for emptiness", 2, 4); bool result = true; - BuchiAutomaton* automaton_intersection; + + ::Graph::BuchiProduct::clearSatisfiabilityCache(); for (vector::size_type alg_1 = 0; @@ -1458,6 +1686,10 @@ void performBuchiIntersectionCheck() alg_2 < round_info.number_of_translators; ++alg_2) { + if (configuration.isInternalAlgorithm(alg_1) + || configuration.isInternalAlgorithm(alg_2)) + continue; + try { if (test_results[alg_1].automaton_stats[0]. @@ -1477,12 +1709,9 @@ void performBuchiIntersectionCheck() && test_results[alg_2].automaton_stats[1]. buchiAutomatonComputed()) { - automaton_intersection = 0; - - automaton_intersection - = BuchiAutomaton::intersect - (*(test_results[alg_1].automaton_stats[0].buchi_automaton), - *(test_results[alg_2].automaton_stats[1].buchi_automaton)); + using ::Graph::BuchiAutomaton; + using ::Graph::BuchiProduct; + using ::Graph::Product; /* * Scan the nontrivial maximal strongly connected components of @@ -1492,85 +1721,15 @@ void performBuchiIntersectionCheck() * fails. */ - for (SccIterator scc(*automaton_intersection); - !scc.atEnd(); ++scc) - { - /* - * MSCC nontriviality check (an MSCC is nontrivial iff it has at - * least two states or if it consists of a single state - * connected to itself). - */ + const BuchiAutomaton& a1 + = *(test_results[alg_1].automaton_stats[0].buchi_automaton); + const BuchiAutomaton& a2 + = *(test_results[alg_2].automaton_stats[1].buchi_automaton); - if (scc->size() > 1 - || (scc->size() == 1 - && automaton_intersection->connected(*(scc->begin()), - *(scc->begin())))) - { - const unsigned long int number_of_acceptance_sets - = automaton_intersection->numberOfAcceptanceSets(); - BitArray acceptance_sets(number_of_acceptance_sets); - acceptance_sets.clear(number_of_acceptance_sets); + Product product(a1, a2); - unsigned long int accept_set; - unsigned long int acceptance_set_counter = 0; - - for (set, - ALLOC(GraphEdgeContainer::size_type) >::const_iterator - state = scc->begin(); - state != scc->end() - && acceptance_set_counter < number_of_acceptance_sets; - ++state) - { - accept_set = acceptance_set_counter; - while (accept_set < number_of_acceptance_sets) - { - if ((*automaton_intersection)[*state].acceptanceSets(). - test(accept_set)) - { - acceptance_sets.setBit(accept_set); - if (accept_set == acceptance_set_counter) - { - do - acceptance_set_counter++; - while (acceptance_set_counter - < number_of_acceptance_sets - && acceptance_sets[acceptance_set_counter]); - accept_set = acceptance_set_counter; - continue; - } - } - accept_set++; - } - } - - if (acceptance_set_counter == number_of_acceptance_sets) - { - test_results[alg_1].automaton_stats[0]. - buchi_intersection_check_stats[alg_2] = 0; - test_results[alg_2].automaton_stats[1]. - buchi_intersection_check_stats[alg_1] = 0; - - final_statistics[alg_1]. - buchi_intersection_check_failures[alg_2]++; - if (alg_1 != alg_2) - final_statistics[alg_2]. - buchi_intersection_check_failures[alg_1]++; - - result = false; - - printText(": failed\n", 4); - - break; - } - } - } - - delete automaton_intersection; - automaton_intersection = 0; - - if (test_results[alg_1].automaton_stats[0]. - buchi_intersection_check_stats[alg_2] == -1) + if (!product.localEmptinessCheck(a1.initialState(), + a2.initialState())) { test_results[alg_1].automaton_stats[0]. buchi_intersection_check_stats[alg_2] = 1; @@ -1579,6 +1738,23 @@ void performBuchiIntersectionCheck() printText(": ok\n", 4); } + else + { + test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] = 0; + test_results[alg_2].automaton_stats[1]. + buchi_intersection_check_stats[alg_1] = 0; + + final_statistics[alg_1] + .buchi_intersection_check_failures[alg_2]++; + if (alg_1 != alg_2) + final_statistics[alg_2] + .buchi_intersection_check_failures[alg_1]++; + + result = false; + + printText(": failed\n", 4); + } final_statistics[alg_1]. buchi_intersection_checks_performed[alg_2]++; @@ -1595,8 +1771,9 @@ void performBuchiIntersectionCheck() } catch (const UserBreakException&) { - if (!printText(" user break\n\n", 4)) - printText("[User break]\n\n", 2, 6); + if (!printText(": aborted (user break)", 4)) + printText(" [User break]", 2, 6); + printText("\n\n", 2); if (round_info.transcript_file.is_open()) writeToTranscript("User break during Büchi automata intersection " @@ -1607,30 +1784,19 @@ void performBuchiIntersectionCheck() + configuration.algorithmString(alg_2) + "\n\n"; - if (automaton_intersection != 0) - { - delete automaton_intersection; - automaton_intersection = 0; - } - throw; } catch (const bad_alloc&) { - if (automaton_intersection != 0) - { - delete automaton_intersection; - automaton_intersection = 0; - } - - if (!printText(" out of memory\n", 4)) - printText("[Out of memory: (+) " + if (!printText(": aborted (out of memory)", 4)) + printText(" [Out of memory: (+) " + configuration.algorithmString(alg_1) + ", (-) " + configuration.algorithmString(alg_2) - + "]\n", + + "]", 2, 6); + printText("\n", 2); if (round_info.transcript_file.is_open()) writeToTranscript("Out of memory during Büchi automata " @@ -1644,6 +1810,9 @@ void performBuchiIntersectionCheck() } } + IntervalList algorithms; + algorithms.merge(0, round_info.number_of_translators - 1); + if (!result) { round_info.error = true; @@ -1652,13 +1821,44 @@ void performBuchiIntersectionCheck() { writeToTranscript("Büchi automata intersection emptiness check failed"); printBuchiIntersectionCheckStats - (round_info.transcript_file, 8, round_info.number_of_translators); + (round_info.transcript_file, 8, algorithms); + round_info.transcript_file << '\n'; } + + if (configuration.global_options.verbosity == 2) + round_info.cout << " [failed]"; } - if (configuration.global_options.verbosity >= 3) - printBuchiIntersectionCheckStats - (cout, 6, round_info.number_of_translators); + if (configuration.global_options.verbosity == 2) + { + round_info.cout << '\n'; + round_info.cout.flush(); + } + else if (configuration.global_options.verbosity >= 3) + { + printBuchiIntersectionCheckStats(cout, 6, algorithms); + round_info.cout << '\n'; + round_info.cout.flush(); + } } } + + + +/****************************************************************************** + * + * Definitions for static members for specializations of the Product template. + * + *****************************************************************************/ + +namespace Graph +{ + +template <> +Product* Product::product = 0; + +template <> +Product* Product::product = 0; + +} diff --git a/lbtt/src/TestOperations.h b/lbtt/src/TestOperations.h index f96ccdac3..9c31a0ad0 100644 --- a/lbtt/src/TestOperations.h +++ b/lbtt/src/TestOperations.h @@ -29,6 +29,7 @@ #include "Configuration.h" #include "Exception.h" #include "StateSpace.h" +#include "TestStatistics.h" using namespace std; @@ -53,7 +54,11 @@ void openFile /* Opens a file for */ (const char* filename, ofstream& stream, /* writing. */ ios::openmode mode, int indent = 0); -void removeFile /* Removes a file. */ +void openFile /* Opens a file for */ + (const char* filename, int& fd, int flags, /* input/output using */ + int indent = 0); /* file descriptors. */ + +void truncateFile /* Truncates a file. */ (const char* filename, int indent = 0); void printFileContents /* Displays the contents */ @@ -97,13 +102,6 @@ void generateBuchiAutomaton /* Generates a B * for the conversion. */ -void generateProductAutomaton /* Computes the */ - (int f, /* synchronous product */ - vector /* and a state space. */ - ::size_type - algorithm_id); - void performEmptinessCheck /* Performs an emptiness */ (int f, /* check on a product */ vector(0)), - formula_in_file(2, false) + formula_in_file(2, false), automaton_file_name(0), cout_capture_file(0), + cerr_capture_file(0) /* ---------------------------------------------------------------------------- * * Description: Constructor for class TestRoundInfo. Creates a new @@ -225,6 +225,7 @@ inline TestRoundInfo::TestRoundInfo() : * * ------------------------------------------------------------------------- */ { + formula_file_name[0] = formula_file_name[1] = 0; } /* ========================================================================= */ diff --git a/lbtt/src/TestStatistics.h b/lbtt/src/TestStatistics.h index 70cc6110e..71fb30de8 100644 --- a/lbtt/src/TestStatistics.h +++ b/lbtt/src/TestStatistics.h @@ -23,16 +23,16 @@ #include #include #include +#include "EdgeContainer.h" +#include "Graph.h" #include "LbttAlloc.h" #include "BuchiAutomaton.h" #include "Configuration.h" -#include "ProductAutomaton.h" #include "StateSpace.h" using namespace std; using Graph::BuchiAutomaton; using Graph::StateSpace; -using Graph::ProductAutomaton; /****************************************************************************** * @@ -107,7 +107,7 @@ struct AutomatonStats * Büchi automaton. */ - ProductAutomaton::size_type /* Number of stats in a */ + ::Graph::Graph::size_type /* Number of stats in a */ number_of_product_states; /* product automaton. */ unsigned long int number_of_product_transitions; /* Number of transitions in @@ -270,29 +270,21 @@ struct TestStatistics * checks performed. */ - unsigned long int /* Total number of */ - total_number_of_buchi_states[2]; /* states in all the - * generated Büchi - * automata. + BIGUINT total_number_of_buchi_states[2]; /* Total number of states + * in all the generated + * Büchi automata. */ - unsigned long int /* Total number of */ - total_number_of_buchi_transitions[2]; /* transitions in all + BIGUINT total_number_of_buchi_transitions[2]; /* Total number of + * transitions in all * the generated Büchi * automata. */ - unsigned long int /* Total number of sets */ - total_number_of_acceptance_sets[2]; /* of accepting states - * in all the generated - * Büchi automata. - */ - - unsigned long int /* Total number of */ - total_number_of_msccs[2]; /* maximal strongly - * connected components - * in the generated - * Büchi automata. + BIGUINT total_number_of_acceptance_sets[2]; /* Total number of sets of + * accepting states in all + * the generated Büchi + * automata. */ double total_buchi_generation_time[2]; /* Total time used when @@ -300,14 +292,13 @@ struct TestStatistics * automata. */ - unsigned long int /* Total number of */ - total_number_of_product_states[2]; /* states in all the - * generated product - * automata. + BIGUINT total_number_of_product_states[2]; /* Total number of states + * in all the generated + * product automata. */ - unsigned long int /* Total number of */ - total_number_of_product_transitions[2]; /* transitions in all the + BIGUINT total_number_of_product_transitions[2]; /* Total number of + * transitions in all the * generated product * automata. */ @@ -556,7 +547,6 @@ inline TestStatistics::TestStatistics total_number_of_buchi_states[i] = 0; total_number_of_buchi_transitions[i] = 0; total_number_of_acceptance_sets[i] = 0; - total_number_of_msccs[i] = 0; total_number_of_product_states[i] = 0; total_number_of_product_transitions[i] = 0; total_buchi_generation_time[i] = 0.0; diff --git a/lbtt/src/UserCommandReader.cc b/lbtt/src/UserCommandReader.cc index 364b7024b..a91b71403 100644 --- a/lbtt/src/UserCommandReader.cc +++ b/lbtt/src/UserCommandReader.cc @@ -28,7 +28,6 @@ #include #endif /* HAVE_SSTREAM */ #include "DispUtil.h" -#include "ProductAutomaton.h" #include "SharedTestData.h" #include "StatDisplay.h" #include "StringUtil.h" @@ -118,9 +117,6 @@ void executeUserCommands() { #endif /* HAVE_READLINE */ - ProductAutomaton* product_automaton = 0; - pair last_computed_product_automaton; - signal(SIGPIPE, SIG_IGN); while (1) @@ -170,35 +166,50 @@ void executeUserCommands() } #endif /* HAVE_ISATTY */ +#ifdef HAVE_READLINE + if (line != static_cast(0) /* line may be 0 on EOF */ + && input_line.find_first_not_of(" \t") != string::npos) + { + add_history(line); + free(line); + } +#endif /* HAVE_READLINE */ + + /* + * If the input line contains an unescaped pipe symbol ('|') outside + * quotes, extract an external command from the line. + */ + external_command = ""; - string::size_type pipe_pos = input_line.find_first_of('|'); - if (pipe_pos != string::npos) + string::size_type pos + = findInQuotedString(input_line, "|", OUTSIDE_QUOTES); + + if (pos != string::npos) { string::size_type nonspace_pos - = input_line.find_first_not_of(" \t", pipe_pos + 1); + = input_line.find_first_not_of(" \t", pos + 1); if (nonspace_pos != string::npos) { external_command = input_line.substr(nonspace_pos); - input_line = input_line.substr(0, pipe_pos); + input_line = input_line.substr(0, pos); } } - sliceString(input_line, " \t", input_tokens); + sliceString(substituteInQuotedString(input_line, " \t", "\n\n", + OUTSIDE_QUOTES), + "\n", + input_tokens); user_break = false; + + if (!input_tokens.empty() || !external_command.empty()) + { + round_info.cout << '\n'; + round_info.cout.flush(); + } if (!input_tokens.empty()) { -#ifdef HAVE_READLINE - if (line != static_cast(0)) /* line may be 0 on EOF */ - { - add_history(line); - free(line); - } -#endif /* HAVE_READLINE */ - round_info.cout << '\n'; - round_info.cout.flush(); - token = parseCommand(input_tokens[0]); if (token == CONTINUE || token == SKIP) @@ -406,8 +417,7 @@ void executeUserCommands() case BUCHIANALYZE : verifyArgumentCount(input_tokens, 2, 2); printAutomatonAnalysisResults(*output_stream, indent, - parseNumber(input_tokens[1]), - parseNumber(input_tokens[2])); + input_tokens); if (output_file != 0) round_info.cout << " Büchi automaton intersection emptiness " "check analysis"; @@ -430,8 +440,8 @@ void executeUserCommands() break; case FORMULA : - verifyArgumentCount(input_tokens, 0, 0); - printFormula(*output_stream, indent, formula_type); + verifyArgumentCount(input_tokens, 0, 1); + printFormula(*output_stream, indent, formula_type, input_tokens); if (output_file != 0) round_info.cout << string(" ") + (formula_type ? "Formula" @@ -458,8 +468,7 @@ void executeUserCommands() case RESULTANALYZE : verifyArgumentCount(input_tokens, 2, 3); printCrossComparisonAnalysisResults - (*output_stream, indent, formula_type, input_tokens, - product_automaton, last_computed_product_automaton); + (*output_stream, indent, formula_type, input_tokens); if (output_file != 0) round_info.cout << " Model checking result cross-comparison " "analysis"; @@ -501,8 +510,8 @@ void executeUserCommands() break; default : - throw CommandErrorException("Unknown command (`" - + input_tokens[0] + "')."); + throw CommandErrorException("Unknown command (`" + input_tokens[0] + + "')."); } if (output_string != 0) @@ -513,21 +522,17 @@ void executeUserCommands() FILE* output_pipe = popen(external_command.c_str(), "w"); if (output_pipe == NULL) throw ExecFailedException(external_command); - int status = fputs(outstring.c_str(), output_pipe); - if (status != EOF) - fflush(output_pipe); + fputs(outstring.c_str(), output_pipe); pclose(output_pipe); - round_info.cout << '\n'; - round_info.cout.flush(); - if (status == EOF) - throw IOException("Error writing to pipe."); } - else if (output_file != 0) + else { - round_info.cout << string(redirection_info.second - ? " appended" - : " written") - + " to `" + redirection_info.first + "'.\n\n"; + if (output_file != 0) + round_info.cout << string(redirection_info.second + ? " appended" + : " written") + + " to `" + redirection_info.first + "'.\n"; + round_info.cout << '\n'; round_info.cout.flush(); } } @@ -560,16 +565,6 @@ void executeUserCommands() } } - if (product_automaton != 0) - { - ::DispUtil::printText - ("", 4, 2); - - delete product_automaton; - - ::DispUtil::printText(" ok\n", 4); - } - #ifdef HAVE_READLINE } catch (...) @@ -582,7 +577,7 @@ void executeUserCommands() #endif /* HAVE_READLINE */ signal(SIGPIPE, SIG_DFL); -} +} /* ========================================================================= */ TokenType parseCommand(const string& token) @@ -597,19 +592,6 @@ TokenType parseCommand(const string& token) * * ------------------------------------------------------------------------- */ { - -/* - * gcc versions prior to version 3 do not conform to the C++ standard in their - * support for the string::compare functions. Use a macro to fix this if - * necessary. - */ - -#ifdef __GNUC__ -#if __GNUC__ < 3 -#define compare(start,end,str,dummy) compare(str,start,end) -#endif -#endif - TokenType token_type = UNKNOWN; string::size_type len = token.length(); bool ambiguous = false; @@ -706,8 +688,19 @@ TokenType parseCommand(const string& token) break; case 'i' : - if (token.compare(1, len - 1, "nconsistencies", len - 1) == 0) - token_type = INCONSISTENCIES; + if (len < 2) + ambiguous = true; + else if (token[1] == 'm') + { + if (token.compare(2, len - 2, "plementations", len - 2) == 0) + token_type = ALGORITHMS; + } + else if (token[1] == 'n') + { + if (token.compare(2, len - 2, "consistencies", len - 2) == 0) + token_type = INCONSISTENCIES; + } + break; case 'q' : @@ -787,6 +780,11 @@ TokenType parseCommand(const string& token) } break; + case 't' : + if (token.compare(1, len - 1, "ranslators", len - 1) == 0) + token_type = ALGORITHMS; + break; + case 'v' : if (token.compare(1, len - 1, "erbosity", len - 1) == 0) token_type = VERBOSITY; @@ -797,12 +795,6 @@ TokenType parseCommand(const string& token) throw CommandErrorException("Ambiguous command."); return token_type; - -#ifdef __GNUC__ -#if __GNUC__ < 3 -#undef compare -#endif -#endif } /* ========================================================================= */ @@ -867,13 +859,13 @@ pair parseRedirection if (token.length() > 2) { append = true; - filename = token.substr(2); + filename = unquoteString(token.substr(2)); input_tokens.pop_back(); } } else { - filename = token.substr(1); + filename = unquoteString(token.substr(1)); input_tokens.pop_back(); } } @@ -882,10 +874,9 @@ pair parseRedirection { string& token = *(input_tokens.rbegin() + 1); - if (token[0] == '>' && (token.length() == 1 - || (token.length() == 2 && token[1] == '>'))) + if (token == ">" || token == ">>") { - filename = input_tokens.back(); + filename = unquoteString(input_tokens.back()); append = (token.length() == 2); input_tokens.pop_back(); input_tokens.pop_back(); @@ -921,8 +912,7 @@ bool parseFormulaType(vector& input_tokens) { formula_type = (input_tokens[1] != "-"); - if (input_tokens[1].length() == 1 - && (input_tokens[1][0] == '+' || input_tokens[1][0] == '-')) + if (input_tokens[1] == "+" || input_tokens[1] == "-") input_tokens.erase(input_tokens.begin()); } diff --git a/lbtt/src/UserCommands.cc b/lbtt/src/UserCommands.cc index baf7e456b..e5ae8c72e 100644 --- a/lbtt/src/UserCommands.cc +++ b/lbtt/src/UserCommands.cc @@ -18,11 +18,14 @@ */ #include +#include "BuchiProduct.h" #include "DispUtil.h" #include "Exception.h" #include "PathEvaluator.h" +#include "Product.h" #include "SharedTestData.h" #include "StatDisplay.h" +#include "StateSpaceProduct.h" #include "StringUtil.h" #include "TestRoundInfo.h" #include "TestStatistics.h" @@ -45,84 +48,98 @@ using namespace ::StringUtil; using namespace ::UserCommandInterface; /* ========================================================================= */ -void computeProductAutomaton - (ProductAutomaton*& product_automaton, - const BuchiAutomaton& buchi_automaton, - pair& last_automaton, - const pair& new_automaton) +unsigned long int parseAlgorithmId(const string& id) /* ---------------------------------------------------------------------------- * - * Description: Computes a product automaton. + * Description: Parses an algorithm identifier (either a symbolic or a + * numeric one). * - * Arguments: product_automaton -- A reference to a pointer giving the - * storage location of the generated - * automaton. - * buchi_automaton -- The Büchi automaton to be used for - * computing the product automaton. - * last_automaton -- A pair telling the algorithm and the - * formula last used for computing an - * automaton (for testing whether the - * result is already available). - * new_automaton -- A pair telling the algorithm and the - * formula which are to be used for - * computing the automaton. + * Argument: id -- String containing the identifier. * - * Returns: Nothing. + * Returns: The numeric identifier of the algorithm. Throws a + * CommandErrorException if the identifier is not recognizable + * as a proper algorithm identifier. * * ------------------------------------------------------------------------- */ { - if (product_automaton == 0 || last_automaton != new_automaton) + unsigned long int result; + string unquoted_id = unquoteString(id); + + try { - if (product_automaton != 0) + result = parseNumber(unquoted_id); + verifyNumber(result, round_info.number_of_translators, + "Implementation identifier out of range"); + } + catch (const NotANumberException&) + { + map, ALLOC(unsigned long int) > + ::const_iterator id_finder + = configuration.algorithm_names.find(unquoted_id); + if (id_finder == configuration.algorithm_names.end()) + throw CommandErrorException + ("Unknown implementation identifier (`" + unquoted_id + "')."); + result = id_finder->second; + } + + return result; +} + +/* ========================================================================= */ +void parseAlgorithmIdList(const string& ids, IntervalList& algorithms) +/* ---------------------------------------------------------------------------- + * + * Description: Parses a list of algorithm identifiers specified either as + * comma-separated intervals or algorithm identifiers. + * + * Arguments: ids -- A constant reference to a string containing + * the list of algorithm identifiers. + * algorithms -- A reference to an IntervalList for storing + * the numeric identifiers of the algorithms. + * + * Returns: Nothing. Throws a CommandErrorException if the identifier + * list includes a string not recognizable as an algorithm + * identifier. + * + * ------------------------------------------------------------------------- */ +{ + /* + * Make a copy of `ids' in which each comma (',') within double quotes is + * substituted with a newline. This is necessary to handle symbolic + * algorithm identifiers with commas correctly. + */ + + string id_string = substituteInQuotedString(ids, ",", "\n", INSIDE_QUOTES); + + try + { + vector nonnumeric_algorithm_ids; + + parseIntervalList(id_string, algorithms, 0, + round_info.number_of_translators - 1, + &nonnumeric_algorithm_ids); + + for (vector::iterator + id = nonnumeric_algorithm_ids.begin(); + id != nonnumeric_algorithm_ids.end(); + ++id) { - printText("", 4, 2); - - delete product_automaton; - product_automaton = 0; - - printText(" ok\n", 4); + *id = unquoteString(substituteInQuotedString(*id, "\n", ",")); + map, ALLOC(unsigned long int) > + ::const_iterator + id_finder = configuration.algorithm_names.find(*id); + if (id_finder == configuration.algorithm_names.end()) + throw CommandErrorException + ("Unknown implementation identifier (`" + *id + "')."); + algorithms.merge(id_finder->second); } - - printText("", 0, 2); - - try - { - product_automaton = new ProductAutomaton(); - product_automaton->computeProduct - (buchi_automaton, *(round_info.statespace), - configuration.global_options.product_mode == Configuration::GLOBAL); - } - catch (...) - { - if (product_automaton != 0) - { - delete product_automaton; - product_automaton = 0; - } - - printText(" error\n", 0); - - try - { - throw; - } - catch (const ::Graph::ProductAutomaton::ProductSizeException&) - { - throw CommandErrorException("Product may be too large"); - } - catch (const UserBreakException&) - { - throw CommandErrorException("User break"); - } - catch (const bad_alloc&) - { - throw CommandErrorException("Out of memory"); - } - } - - printText(" ok\n", 0); - - last_automaton = new_automaton; + } + catch (const IntervalRangeException& e) + { + throw CommandErrorException + (string("Implementation identifier out of range (") + + toString(e.getNumber()) + + ")."); } } @@ -158,64 +175,13 @@ void printAlgorithmList(ostream& stream, int indent) + "abled)\n"; } - estream << '\n'; estream.flush(); } -/* ========================================================================= */ -void synchronizePrefixAndCycle - (deque::size_type, - ALLOC(Graph::Graph::size_type) >& prefix, - deque::size_type, - ALLOC(Graph::Graph::size_type) >& cycle) -/* ---------------------------------------------------------------------------- - * - * Description: Function for `synchronizing' a sequence of states consisting - * of a prefix and a repeating cycle. This means removing from - * the end of the prefix the longest sequence of states that - * forms a postfix of the cycle. The states in the cycle will be - * `rotated' accordingly. - * - * Arguments: prefix -- A reference to a deque of state identifiers - * forming the prefix of the state sequence. - * cycle -- A reference to a deque of state identifiers - * representing the states in the repeating cycle. - * - * Returns: Nothing. - * - * ------------------------------------------------------------------------- */ -{ - if (cycle.empty()) - throw CommandErrorException("internal error"); - - while (!prefix.empty() && prefix.back() == cycle.back()) - { - prefix.pop_back(); - cycle.push_front(cycle.back()); - cycle.pop_back(); - } - - Graph::Graph::size_type state_id = cycle.front(); - deque::size_type, - ALLOC(Graph::Graph::size_type) >::const_iterator - s; - - for (s = cycle.begin() + 1; s != cycle.end() && *s == state_id; ++s) - ; - - if (s == cycle.end()) - { - cycle.clear(); - cycle.push_front(state_id); - } -} - /* ========================================================================= */ void printCrossComparisonAnalysisResults (ostream& stream, int indent, bool formula_type, - const vector& input_tokens, - ProductAutomaton*& product_automaton, - pair& last_product_automaton) + const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `resultanalysis', i.e., analyzes @@ -232,14 +198,6 @@ void printCrossComparisonAnalysisResults * input_tokens -- A reference to a vector * containing the arguments of the * command. - * product_automaton -- A reference to a pointer telling - * the storage location of the - * generated product automaton. - * last_product_automaton -- A pair telling the algorithm and - * the formula last used for - * computing a product automaton - * (for testing whether the result - * is already available). * * Returns: Nothing. * @@ -255,33 +213,36 @@ void printCrossComparisonAnalysisResults "model checking result cross-comparison test " "is enabled."); - if (input_tokens[1] == "p") + algorithm1 = parseAlgorithmId(input_tokens[1]); + algorithm2 = parseAlgorithmId(input_tokens[2]); + + if (algorithm1 == algorithm2) + throw CommandErrorException("Implementation identifiers must be " + "different."); + + /* + * Arrange the algorithm identifiers such that `algorithm1' never refers to + * the internal model checking algorithm (swap `algorithm1' and `algorithm2' + * if necessary). + */ + + if (configuration.isInternalAlgorithm(algorithm1)) { path_compare = true; - algorithm1 = parseNumber(input_tokens[2]); - algorithm2 = round_info.number_of_translators; - } - else - { - algorithm1 = parseNumber(input_tokens[1]); - if (input_tokens[2] == "p") - { - path_compare = true; - algorithm2 = round_info.number_of_translators; - } - else - algorithm2 = parseNumber(input_tokens[2]); + algorithm1 ^= algorithm2; + algorithm2 ^= algorithm1; + algorithm1 ^= algorithm2; } + if (configuration.isInternalAlgorithm(algorithm2)) + path_compare = true; + if (path_compare && !(configuration.global_options.statespace_generation_mode & Configuration::PATH)) throw CommandErrorException("This feature is available only when using " "paths as state spaces."); - verifyNumber(algorithm1, round_info.number_of_translators, - "No such implementation"); - int formula = (formula_type ? 0 : 1); int generator_formula = formula; @@ -292,17 +253,6 @@ void printCrossComparisonAnalysisResults const AutomatonStats* stats2; stats1 = &test_results[algorithm1].automaton_stats[formula]; - - if (!path_compare) - { - verifyNumber(algorithm2, round_info.number_of_translators, - "No such implementation"); - - if (algorithm1 == algorithm2) - throw CommandErrorException("Implementation identifiers must be " - "different."); - } - stats2 = &test_results[algorithm2].automaton_stats[formula]; if (!stats1->crossComparisonPerformed(algorithm2)) @@ -312,7 +262,7 @@ void printCrossComparisonAnalysisResults "performed between " + configuration.algorithmString(algorithm1) + " and " + configuration.algorithmString(algorithm2) - + ".\n", + + ".", 78); return; } @@ -324,7 +274,7 @@ void printCrossComparisonAnalysisResults "results given by " + configuration.algorithmString(algorithm1) + " and " - + configuration.algorithmString(algorithm2) + ".\n", + + configuration.algorithmString(algorithm2) + ".", 78); return; } @@ -374,7 +324,8 @@ void printCrossComparisonAnalysisResults state = parseNumber(input_tokens[3]); - verifyNumber(state, round_info.statespace->size(), "No such state"); + verifyNumber(state, round_info.statespace->size(), + "State identifier out of range"); if (state >= round_info.real_emptiness_check_size) { @@ -386,7 +337,7 @@ void printCrossComparisonAnalysisResults + configuration.algorithmString(algorithm2) + " in state " + toString(state) - + " of the state space.\n", + + " of the state space.", 78); return; } @@ -401,7 +352,7 @@ void printCrossComparisonAnalysisResults + configuration.algorithmString(algorithm2) + " in state " + toString(state) - + " of the state space.\n", + + " of the state space.", 78); return; } @@ -420,40 +371,37 @@ void printCrossComparisonAnalysisResults ? algorithm2 : algorithm1); - deque system_prefix; - deque system_cycle; - deque - automaton_prefix; - deque - automaton_cycle; + ::Graph::Product::Witness witness; if (!path_compare || accepting_algorithm == algorithm1) { - /* - * Compute the synchronous product of the automaton and the state space. - */ - - computeProductAutomaton(product_automaton, - *(test_results[accepting_algorithm]. - automaton_stats[formula].buchi_automaton), - last_product_automaton, - make_pair(accepting_algorithm, formula_type)); - /* * Search the product automaton for an accepting cycle. */ - pair, - deque > - execution; + const BuchiAutomaton& automaton + = *test_results[accepting_algorithm].automaton_stats[formula] + .buchi_automaton; + + ::Graph::Product product + (automaton, *round_info.statespace); try { printText("", 0, 2); - product_automaton->findAcceptingExecution(state, execution); + + product.findWitness(automaton.initialState(), state, witness); + + if (witness.cycle.first.empty()) + throw Exception + ("UserCommands::printCrossComparisonAnalysisResults(...): internal " + "error [witness construction failed]"); + } + catch (const UserBreakException&) + { + printText(" user break\n", 0); + throw; } catch (...) { @@ -462,71 +410,27 @@ void printCrossComparisonAnalysisResults } printText(" ok\n\n", 0); - - /* - * Separate the parallel executions of the system and the automaton into - * prefixes and cycles. - */ - - while (!execution.first.empty()) - { - automaton_prefix.push_back(execution.first.front().first); - system_prefix.push_back(execution.first.front().second); - execution.first.pop_front(); - } - - while (!execution.second.empty()) - { - automaton_cycle.push_back(execution.second.front().first); - system_cycle.push_back(execution.second.front().second); - execution.second.pop_front(); - } - - synchronizePrefixAndCycle(automaton_prefix, automaton_cycle); - synchronizePrefixAndCycle(system_prefix, system_cycle); } else { - StateSpace::size_type loop_state + const StateSpace::size_type loop_state = (*((*round_info.statespace)[round_info.statespace->size() - 1]. edges().begin()))->targetNode(); + const StateSpace::size_type loop_length + = round_info.statespace->size() - loop_state; - if (state < loop_state) + for ( ; state < loop_state; ++state) + witness.prefix.second.push_back(StateSpace::PathElement(state, 0)); + + state -= loop_state; + for (StateSpace::size_type s = 0; s < loop_length; ++s) { - for (StateSpace::size_type path_state = state; path_state < loop_state; - path_state++) - system_prefix.push_back(path_state); - - for (StateSpace::size_type path_state = loop_state; - path_state < round_info.statespace->size(); - path_state++) - system_cycle.push_back(path_state); - } - else - { - for (StateSpace::size_type path_state = state; - path_state < round_info.statespace->size(); - path_state++) - system_cycle.push_back(path_state); - - for (StateSpace::size_type path_state = loop_state; - path_state < state; - path_state++) - system_cycle.push_back(path_state); + witness.cycle.second.push_back + (StateSpace::PathElement(state + loop_state, 0)); + state = (state + 1) % loop_length; } } - state = system_prefix.size(); - - /* - * Construct a path in the state space from the system prefix and the cycle. - */ - - vector path; - - path.insert(path.end(), system_prefix.begin(), system_prefix.end()); - path.insert(path.end(), system_cycle.begin(), system_cycle.end()); - /* * Write information about the execution to the output stream. That is, * display the states in the infinite execution (a prefix and a cycle) @@ -538,7 +442,7 @@ void printCrossComparisonAnalysisResults estream << string(indent + 2, ' ') + "Execution M:\n"; - printPath(stream, indent + 4, system_prefix, system_cycle, + printPath(stream, indent + 4, witness.prefix.second, witness.cycle.second, *(round_info.statespace)); estream << string(indent + 4, ' ') @@ -557,8 +461,8 @@ void printCrossComparisonAnalysisResults Ltl::PathEvaluator path_evaluator; bool result = path_evaluator.evaluate - (*round_info.formulae[generator_formula], *(round_info.statespace), path, - state); + (*round_info.formulae[generator_formula], witness.prefix.second, + witness.cycle.second, *round_info.statespace); path_evaluator.print(stream, indent + 4); @@ -573,14 +477,19 @@ void printCrossComparisonAnalysisResults : configuration.algorithmString(result ? rejecting_algorithm : accepting_algorithm)) - + ".\n", + + ".", 78); if (!result) + { + estream << '\n'; printAcceptingCycle(stream, indent + 2, accepting_algorithm, - automaton_prefix, automaton_cycle, + witness.prefix.first, witness.cycle.first, *(test_results[accepting_algorithm]. - automaton_stats[formula].buchi_automaton)); + automaton_stats[formula].buchi_automaton), + witness.prefix.second, witness.cycle.second, + *round_info.statespace); + } } /* ========================================================================= */ @@ -610,10 +519,7 @@ void printConsistencyAnalysisResults "model checking result consistency check is " "enabled."); - unsigned long int algorithm_id = parseNumber(input_tokens[1]); - - verifyNumber(algorithm_id, round_info.number_of_translators, - "No such implementation"); + unsigned long int algorithm_id = parseAlgorithmId(input_tokens[1]); if (test_results[algorithm_id].consistency_check_result == -1) { @@ -621,7 +527,7 @@ void printConsistencyAnalysisResults "Model checking result consistency check was not performed " "on implementation " + configuration.algorithmString(algorithm_id) - + ".\n", + + ".", 78); return; } @@ -631,7 +537,7 @@ void printConsistencyAnalysisResults printTextBlock(stream, indent, "Implementation " + configuration.algorithmString(algorithm_id) - + " passed the model checking result consistency check.\n", + + " passed the model checking result consistency check.", 78); return; } @@ -671,7 +577,8 @@ void printConsistencyAnalysisResults */ state = parseNumber(input_tokens[2]); - verifyNumber(state, round_info.statespace->size(), "No such state"); + verifyNumber(state, round_info.statespace->size(), + "State identifier out of range"); if (state >= round_info.real_emptiness_check_size) { @@ -680,7 +587,7 @@ void printConsistencyAnalysisResults "performed on implementation " + configuration.algorithmString(algorithm_id) + " in state " + toString(state) + " of the state " - "space.\n", + "space.", 78); return; } @@ -694,14 +601,14 @@ void printConsistencyAnalysisResults "consistency check on implementation " + configuration.algorithmString(algorithm_id) + " in state " + toString(state) + " of the state " - "space.\n", + "space.", 78); return; } } vector path; - deque prefix, cycle; + StateSpace::Path prefix, cycle; map, ALLOC(StateSpace::size_type) > ordering; @@ -728,10 +635,10 @@ void printConsistencyAnalysisResults loop_state = ordering[state]; for (StateSpace::size_type s = 0; s < loop_state; s++) - prefix.push_back(path[s]); + prefix.push_back(StateSpace::PathElement(path[s], 0)); for (StateSpace::size_type s = loop_state; s < path.size(); s++) - cycle.push_back(path[s]); + cycle.push_back(StateSpace::PathElement(path[s], 0)); estream << string(indent + 2, ' ') + "Execution M:\n"; printPath(stream, indent + 4, prefix, cycle, *(round_info.statespace)); @@ -741,8 +648,7 @@ void printConsistencyAnalysisResults Ltl::PathEvaluator path_evaluator; bool result = path_evaluator.evaluate - (*round_info.formulae[formula], *(round_info.statespace), path, - loop_state); + (*round_info.formulae[formula], prefix, cycle, *round_info.statespace); path_evaluator.print(stream, indent + 4); @@ -751,14 +657,14 @@ void printConsistencyAnalysisResults + "satisfied in the execution. It seems that the automaton " "constructed for the " + (result ? "posi" : "nega") - + "tive formula rejects the execution incorrectly.\n", + + "tive formula rejects the execution incorrectly.", 78); } /* ========================================================================= */ void printAutomatonAnalysisResults - (ostream& stream, int indent, unsigned long int algorithm1, - unsigned long int algorithm2) + (ostream& stream, int indent, + const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `buchianalysis', i.e., analyzes @@ -766,11 +672,11 @@ void printAutomatonAnalysisResults * for two Büchi automata constructed for the formula and its * negation. * - * Arguments: stream -- A reference to an output stream. - * indent -- Number of spaces to leave on the left of the - * output. - * algorithm1, -- Identifiers of the algorithms for which the - * algorithm2 should be performed. + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a vector containing the + * arguments of the command. * * Returns: Nothing. * @@ -783,10 +689,14 @@ void printAutomatonAnalysisResults "Büchi automata intersection emptiness check " "is enabled."); - verifyNumber(algorithm1, round_info.number_of_translators, - "No such implementation"); - verifyNumber(algorithm2, round_info.number_of_translators, - "No such implementation"); + unsigned long int algorithm1 = parseAlgorithmId(input_tokens[1]); + unsigned long int algorithm2 = parseAlgorithmId(input_tokens[2]); + + if (configuration.isInternalAlgorithm(algorithm1) + || configuration.isInternalAlgorithm(algorithm2)) + throw CommandErrorException + ("This feature is not available for lbtt's internal " + "model checking algorithm."); int test_result = test_results[algorithm1].automaton_stats[0]. @@ -806,7 +716,7 @@ void printAutomatonAnalysisResults : " (positive formula) and " + configuration.algorithmString(algorithm2) + " (negative formula)") - + ".\n", + + ".", 78); return; @@ -838,318 +748,170 @@ void printAutomatonAnalysisResults estream << "\n\n"; estream.flush(); - /* - * Compute the intersection of the two automata. - */ - - BuchiAutomaton* a = 0; + ::Graph::Product::Witness witness; try { - map, ALLOC(BuchiAutomaton::StateIdPair) > - intersection_state_mapping; + printText("", 0, 2); - try - { - printText("", 0, 2); + const BuchiAutomaton& automaton_1 + = *(test_results[algorithm1].automaton_stats[0].buchi_automaton); + const BuchiAutomaton& automaton_2 + = *(test_results[algorithm2].automaton_stats[1].buchi_automaton); - a = BuchiAutomaton::intersect - (*(test_results[algorithm1].automaton_stats[0].buchi_automaton), - *(test_results[algorithm2].automaton_stats[1].buchi_automaton), - &intersection_state_mapping); - } - catch (...) - { - printText(" error\n", 0); - throw; - } + ::Graph::Product product(automaton_1, automaton_2); + product.findWitness(automaton_1.initialState(), automaton_2.initialState(), + witness); - printText(" ok\n", 0); - - /* - * Search the intersection automaton for an accepting execution. This is - * done `indirectly' as follows: - * - * 1. Convert the intersection automaton into a StateSpace, i.e., - * construct a StateSpace which shares a similar transition - * relation with the automaton. - * 2. Construct a `dummy' single-state Büchi automaton which - * accepts all inputs. - * 3. Compute the synchronous product of the state space and the - * automaton. The obtained product automaton again has the same - * transition relation as the original intersection automaton - * (state identifiers may have been permuted; however, the - * product automaton contains information about the - * correspondence between the state identifiers of the product - * automaton and the original intersection automaton). - * 4. Switch the roles of the state space and the Büchi automaton in - * the product to effectively transfer state acceptance - * information to the product automaton from the automaton - * intersection. - * 5. Search an accepting cycle in the product automaton. This then - * corresponds to an accepting execution of the intersection - * automaton. - */ - - StateSpace automaton_as_statespace - (configuration.formula_options.formula_generator. - number_of_available_variables, - a->size()); - - /* - * 1. - */ - - for (BuchiAutomaton::size_type state = 0; state < a->size(); state++) - { - for (GraphEdgeContainer::const_iterator transition - = (*a)[state].edges().begin(); - transition != (*a)[state].edges().end(); - ++transition) - automaton_as_statespace.connect(state, (*transition)->targetNode()); - } - - /* - * 2. - */ - - BuchiAutomaton dummy_automaton(1, 0, 0); - dummy_automaton.connect(0, 0, &Ltl::True::construct()); - - /* - * 3. - */ - - ProductAutomaton p; - p.computeProduct(dummy_automaton, automaton_as_statespace, false); - - /* - * 4. - */ - - p.buchi_automaton = a; - p.statespace_size = 1; - - /* - * 5. - */ - - pair, - deque > - execution; - - try - { - printText("", - 0, 2); - - p.findAcceptingExecution(0, execution); - } - catch (...) - { - printText(" error\n", 0); - throw; - } - - printText(" ok\n\n", 0); - - /* - * Extract the state identifiers belonging to the execution of the - * intersection automaton from the result. - */ - - vector path; - - for (deque::const_iterator - state = execution.first.begin(); - state != execution.first.end(); - ++state) - path.push_back(state->first); - - const vector - ::size_type loop_pos = path.size(); - - for (deque::const_iterator - state = execution.second.begin(); - state != execution.second.end(); - ++state) - path.push_back(state->first); - - /* - * Construct an execution accepted by both of the automata. This is done - * by giving suitable truth assignments for the atomic propositions in - * 'path.size()' states, where `path' corresponds to an accepting run of - * the intersection automaton. (The state space representing the - * intersection automaton is reused for this purpose, since it is not - * needed any longer.) In addition, `prefix' and `cycle' (required for - * displaying the execution) are built to refer to the reused states. - */ - - deque prefix, cycle; - - /* - * Ensure that the state space is large enough to contain the execution - * (the execution may pass several times through a state in the - * intersection automaton). - */ - - if (automaton_as_statespace.size() < path.size()) - automaton_as_statespace.expand - (path.size() - automaton_as_statespace.size()); - - path.push_back(path[loop_pos]); /* use the first state of the cycle as a - * temporary sentinel element - */ - - for (vector - ::size_type state = 0; - state + 1 < path.size(); - ++state) - { - GraphEdgeContainer::const_iterator transition; - - for (transition = (*a)[path[state]].edges().begin(); - (*transition)->targetNode() != path[state + 1]; - ++transition) - ; - - automaton_as_statespace[state].positiveAtoms() - = static_cast(*transition) - ->guard().findPropositionalModel - (configuration.formula_options.formula_generator. - number_of_available_variables); - - (state < loop_pos ? prefix : cycle).push_back(state); - } - - path.pop_back(); /* remove the sentinel element */ - - delete a; - a = 0; - - /* - * Display the input sequence accepted by both automata. - */ - - estream << string(indent + 2, ' ') - + "Execution M accepted by both automata:\n"; - - printPath(stream, indent + 4, prefix, cycle, automaton_as_statespace); - - estream << '\n'; - - /* - * For each of the original automata, display the accepting runs that - * these automata have on the input sequence. - */ - - deque - aut_prefix; - - deque - aut_cycle; - - for (int i = 0; i < 2; i++) - { - aut_prefix.clear(); - aut_cycle.clear(); - - deque* - new_execution_states = &aut_prefix; - - for (vector - ::size_type state_id = 0; - state_id < path.size(); - ++state_id) - { - if (state_id == loop_pos) - new_execution_states = &aut_cycle; - new_execution_states->push_back - (i == 0 - ? intersection_state_mapping[path[state_id]].first - : intersection_state_mapping[path[state_id]].second); - } - - synchronizePrefixAndCycle(aut_prefix, aut_cycle); - - printAcceptingCycle(stream, indent + 2, - (i == 0 ? algorithm1 : algorithm2), - aut_prefix, aut_cycle, - *(test_results[(i == 0 ? algorithm1 : algorithm2)]. - automaton_stats[i].buchi_automaton)); - } - - /* - * Normalize the state identifiers in `path' to refer to the states that - * give the valuations for atomic propositions along the execution. - */ - - for (vector - ::size_type state = 0; - state < path.size(); - ++state) - path[state] = state; - - /* - * Display a proof or a refutation for the formula in the execution. - */ - - estream << string(indent + 2, ' ') - + "Analysis of the positive formula in the execution M:\n"; - - Ltl::PathEvaluator path_evaluator; - bool result = path_evaluator.evaluate - (*round_info.formulae[formula], automaton_as_statespace, path, loop_pos); - - path_evaluator.print(stream, indent + 4); - - printTextBlock(stream, indent + 2, - " \n The positive formula is " - + string(result ? "" : "not ") - + "satisfied in the execution. This suggests that the " - "Büchi automaton constructed for the " - + (result ? "nega" : "posi") + "tive formula " - + (algorithm1 == algorithm2 - ? "" - : "(the automaton constructed by implementation " - + configuration.algorithmString(result ? algorithm2 - : algorithm1) - + ") ") - + "is incorrect.\n", - 78); + if (witness.cycle.first.empty()) + throw Exception + ("UserCommands::printAutomatonAnalysisResults(...): internal error " + "[witness construction failed]"); + } + catch (const UserBreakException&) + { + printText(" user break\n", 0); + throw; } catch (...) { - if (a != 0) - delete a; + printText(" error\n", 0); + throw; } + + printText(" ok\n", 0); + + const unsigned long int valuation_size + = configuration.formula_options.formula_generator + .number_of_available_variables; + const StateSpace::size_type path_length + = witness.prefix.first.size() + witness.cycle.first.size(); + StateSpace path(valuation_size, path_length); + + StateSpace::size_type state = 0; + StateSpace::Path::const_iterator p1, p2; + StateSpace::Path path_prefix, path_cycle; + + for (int i = 0; i < 2; ++i) + { + const pair* witness_segment; + StateSpace::Path* path_segment; + + if (i == 0) + { + witness_segment = &witness.prefix; + path_segment = &path_prefix; + } + else + { + witness_segment = &witness.cycle; + path_segment = &path_cycle; + } + + for (p1 = witness_segment->first.begin(), + p2 = witness_segment->second.begin(); + p1 != witness_segment->first.end(); + ++p1, ++p2, ++state) + { + ::Ltl::LtlFormula* f + = &::Ltl::And::construct + (static_cast(p1->edge()) + .guard(), + static_cast(p2->edge()) + .guard()); + + path[state].positiveAtoms().copy + (f->findPropositionalModel(valuation_size - 1), valuation_size); + + path_segment->push_back(StateSpace::PathElement(state, 0)); + + ::Ltl::LtlFormula::destruct(f); + } + } + + /* + * Display the input sequence accepted by both automata. + */ + + estream << string(indent + 2, ' ') + + "Execution M accepted by both automata:\n"; + + printPath(stream, indent + 4, path_prefix, path_cycle, path); + + estream << '\n'; + + /* + * For each of the original automata, display the accepting runs that + * these automata have on the input sequence. + */ + + for (int i = 0; i < 2; ++i) + { + const BuchiAutomaton::Path* prefix; + const BuchiAutomaton::Path* cycle; + + if (i == 0) + { + prefix = &witness.prefix.first; + cycle = &witness.cycle.first; + } + else + { + prefix = &witness.prefix.second; + cycle = &witness.cycle.second; + } + + printAcceptingCycle(stream, indent + 2, (i == 0 ? algorithm1 : algorithm2), + *prefix, *cycle, + *(test_results[i == 0 ? algorithm1 : algorithm2] + .automaton_stats[i].buchi_automaton), + path_prefix, path_cycle, path); + + estream << '\n'; + } + + /* + * Display a proof or a refutation for the formula in the execution. + */ + + estream << string(indent + 2, ' ') + + "Analysis of the positive formula in the execution M:\n"; + + Ltl::PathEvaluator path_evaluator; + bool result = path_evaluator.evaluate(*round_info.formulae[formula], + path_prefix, path_cycle, path); + + path_evaluator.print(stream, indent + 4); + + printTextBlock(stream, indent + 2, + " \n The positive formula is " + string(result ? "" : "not ") + + "satisfied in the execution. This suggests that the " + "Büchi automaton constructed for the " + + (result ? "nega" : "posi") + "tive formula " + + (algorithm1 == algorithm2 + ? "" + : "(the automaton constructed by implementation " + + configuration.algorithmString(result ? algorithm2 + : algorithm1) + + ") ") + + "is incorrect.", + 78); } /* ========================================================================= */ void printPath - (ostream& stream, int indent, - const deque& prefix, - const deque& cycle, - const StateSpace& path) + (ostream& stream, int indent, const StateSpace::Path& prefix, + const StateSpace::Path& cycle, const StateSpace& path) /* ---------------------------------------------------------------------------- * - * Description: Writes information about a single execution path to a stream. + * Description: Writes information about a path in a state space to a stream. * * Arguments: stream -- A reference to an output stream. * indent -- Number of spaces to leave to the left of output. - * prefix -- A reference to a constant deque of state - * identifiers forming the prefix of the execution. - * cycle -- A reference to a constant deque of state - * identifiers forming the infinitely repeating - * cycle in the execution. - * path -- A reference to the constant state space from - * which the state identifiers in the deques are - * taken. + * prefix -- The prefix of the path. + * cycle -- The cycle of the path. + * path -- The state space to which the state identifiers in + * the prefix and the cycle refer. * * Returns: Nothing. * @@ -1157,65 +919,59 @@ void printPath { Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); - const deque* - execution_states; + const StateSpace::Path* path_segment; - for (int counter = 0; counter < 2; counter++) + for (int i = 0; i < 2; ++i) { + if (i == 0 && prefix.empty()) + continue; + estream << string(indent, ' '); - - if (counter == 0) + if (i == 0) { - execution_states = &prefix; - estream << "prefix:"; + path_segment = &prefix; + estream << "prefix"; } else { - execution_states = &cycle; - estream << "cycle: "; + path_segment = &cycle; + estream << "cycle"; } + estream << ":\n"; - estream << string(6, ' ') + "< "; + bool first_printed; - if (!execution_states->empty()) + for (StateSpace::Path::const_iterator path_element = path_segment->begin(); + path_element != path_segment->end(); + ++path_element) { - bool first_printed; + if (path_element != path_segment->begin()) + estream << toString(path_element->node()) + "\n"; + estream << string(indent + 2, ' ') + 's' + toString(path_element->node()) + + " {"; - for (deque - ::const_iterator execution_state = execution_states->begin(); - execution_state != execution_states->end(); - ++execution_state) + first_printed = false; + for (unsigned long int proposition = 0; + proposition < path.numberOfPropositions(); + ++proposition) { - if (execution_state != execution_states->begin()) - estream << ",\n" + string(indent + 15, ' '); - - estream << 's' + toString(*execution_state) + " {"; - - first_printed = false; - - for (unsigned long int proposition = 0; - proposition < path.numberOfPropositions(); - proposition++) + if (path[path_element->node()].positiveAtoms().test(proposition)) { - if (path[*execution_state].positiveAtoms().test(proposition)) - { - if (first_printed) - estream << ", "; - else - first_printed = true; + if (first_printed) + estream << ','; + else + first_printed = true; - estream << 'p' + toString(proposition); - } + estream << 'p' + toString(proposition); } - - estream << '}'; } + + estream << "} --> s"; } - else - estream << "empty"; - - estream << " >\n"; + estream << toString(cycle.begin()->node()) + "\n"; } + + estream.flush(); } /* ========================================================================= */ @@ -1224,29 +980,34 @@ void printAcceptingCycle vector::size_type algorithm_id, - const deque& - prefix, - const deque& - cycle, - const BuchiAutomaton& automaton) + const BuchiAutomaton::Path& aut_prefix, + const BuchiAutomaton::Path& aut_cycle, + const BuchiAutomaton& automaton, const StateSpace::Path& path_prefix, + const StateSpace::Path& path_cycle, const StateSpace& path) /* ---------------------------------------------------------------------------- * - * Description: Writes information about a single automaton execution to a - * stream. + * Description: Writes information about an execution of a Büchi automaton to + * a stream. * * Arguments: stream -- A reference to an output stream. * indent -- Number of spaces to leave to the left of * output. * algorithm_id -- Identifier for an algorithm. - * prefix -- A reference to a constant deque of state - * identifiers forming the prefix of the - * execution. - * cycle -- A reference to a constant deque of state - * identifiers forming the infinitely - * repeating cycle in the execution. - * automaton -- A reference to a constant BuchiAutomaton - * from which the state identifiers in the - * deques are taken. + * aut_prefix -- The prefix of a path in a Büchi automaton. + * aut_cycle -- The cycle of a path in a Büchi automaton. + * automaton -- The Büchi automaton to which the state + * identifiers in `aut_prefix' and `aut_cycle' + * refer. + * path_prefix -- The prefix of a path in a state space. + * (This path is interpreted as the input for + * the Büchi automaton.) It is assumed that + * `path_prefix.size() == aut_prefix.size()'. + * path_cycle -- The cycle of a path in a state space. It + * is assumed that `path_cycle.size() == + * aut_cycle.size()'. + * path -- The state space to which the state + * identifiers in `path_prefix' and + * `path_cycle' refer. * * Returns: Nothing. * @@ -1254,10 +1015,6 @@ void printAcceptingCycle { Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); - const deque* - execution_states; - bool first_printed; - printTextBlock(stream, indent, "On input M, the automaton constructed by implementation " + configuration.algorithmString(algorithm_id) + " (with " @@ -1267,76 +1024,97 @@ void printAcceptingCycle + ") has the following accepting execution:", 78); - for (int counter = 0; counter < 2; counter++) - { - estream << string(indent + 2, ' '); + const BuchiAutomaton::Path* aut_segment; + const StateSpace::Path* path_segment; + BuchiAutomaton::Path::const_iterator aut_state; + StateSpace::Path::const_iterator path_state; - if (counter == 0) + for (int i = 0; i < 2; ++i) + { + if (i == 0 && aut_prefix.empty()) + continue; + + estream << string(indent + 2, ' '); + if (i == 0) { estream << "prefix"; - execution_states = &prefix; + aut_segment = &aut_prefix; + path_segment = &path_prefix; } else { estream << "cycle"; - execution_states = &cycle; + aut_segment = &aut_cycle; + path_segment = &path_cycle; } - estream << ":\n"; - string execution_string = "<"; - - if (!execution_states->empty()) + bool first_printed = false; + for (aut_state = aut_segment->begin(), path_state = path_segment->begin(); + aut_state != aut_segment->end(); + ++aut_state, ++path_state) { - first_printed = false; - - for (deque::const_iterator - execution_state = execution_states->begin(); - execution_state != execution_states->end(); - ++execution_state) + estream << string(indent + 4, ' ') + toString(aut_state->node()) + ' '; + + const BitArray* bits; + for (int j = 0; j < 2; ++j) { - if (first_printed) - execution_string += ", "; - else - first_printed = true; - execution_string += toString(*execution_state); + bits = (j == 0 + ? &automaton[aut_state->node()].acceptanceSets() + : &static_cast + (aut_state->edge()).acceptanceSets()); + first_printed = false; - if (counter == 1) - { - bool first_acceptance_set_printed = false; - - for (unsigned long int accept_set = 0; - accept_set < automaton.numberOfAcceptanceSets(); - accept_set++) + for (unsigned long int accept_set = 0; + accept_set < automaton.numberOfAcceptanceSets(); + ++accept_set) + { + if (bits->test(accept_set)) { - if (automaton[*execution_state].acceptanceSets().test(accept_set)) + if (first_printed) + estream << ','; + else { - if (first_acceptance_set_printed) - execution_string += ", "; - else - { - first_acceptance_set_printed = true; - execution_string += " [acceptance sets: {"; - } - execution_string += toString(accept_set); + first_printed = true; + if (j == 1) + estream << "--"; + estream << '{'; } + estream << accept_set; } + } - if (first_acceptance_set_printed) - execution_string += "}]"; + if (first_printed) + { + estream << "}"; + if (j == 0) + estream << ' '; } } + + estream << "--> " + toString(aut_state->edge().targetNode()) + " [ {"; + bits = &path[path_state->node()].positiveAtoms(); + first_printed = false; + for (unsigned long int proposition = 0; + proposition < path.numberOfPropositions(); + ++proposition) + { + if (bits->test(proposition)) + { + if (first_printed) + estream << ','; + else + first_printed = true; + estream << 'p' + toString(proposition); + } + } + estream << "} |== "; + static_cast(aut_state->edge()) + .guard().print(estream); + estream << " ]\n"; } - else - execution_string += "empty"; - - execution_string += ">"; - - printTextBlock(stream, indent + 4, execution_string, 78); } - estream << '\n'; estream.flush(); } @@ -1367,16 +1145,17 @@ void printBuchiAutomaton { Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); - unsigned long int algorithm = parseNumber(input_tokens[1]); - - verifyNumber(algorithm, round_info.number_of_translators, - "No such implementation"); - + unsigned long int algorithm = parseAlgorithmId(input_tokens[1]); int formula = (configuration.formula_options.output_mode == Configuration::NNF ? 0 : 2) + (formula_type ? 0 : 1); + if (configuration.isInternalAlgorithm(algorithm)) + throw CommandErrorException + ("This feature is not available for lbtt's internal " + "model checking algorithm."); + if (!test_results[algorithm].automaton_stats[formula_type ? 0 : 1]. buchiAutomatonComputed()) { @@ -1385,7 +1164,7 @@ void printBuchiAutomaton + configuration.algorithmString(algorithm) + " for the formula `" + toString(*round_info.formulae[formula]) - + "'.\n", + + "'.", 78); return; } @@ -1415,22 +1194,27 @@ void printBuchiAutomaton estream << string(indent + 2, ' ') + "The automaton is empty.\n"; else { - set, - ALLOC(unsigned long int) > states; + IntervalList states; if (input_tokens.size() == 2) input_tokens.push_back("*"); - parseInterval(input_tokens[2], states, 0, automaton->size() - 1); - - for (set, - ALLOC(unsigned long int) >::const_iterator - state = states.begin(); - state != states.end(); ++state) + try { - verifyNumber(*state, automaton->size(), - "State identifier out of range"); + parseIntervalList(input_tokens[2], states, 0, automaton->size() - 1); + } + catch (const IntervalRangeException& e) + { + throw CommandErrorException + (string("State identifier out of range (") + + toString(e.getNumber()) + + ")."); + } + for (IntervalList::const_iterator state = states.begin(); + state != states.end(); + ++state) + { estream << string(indent + 2, ' ') + "State " + toString(*state) + (*state == automaton->initialState() ? " (initial state)" : "") + ":\n"; @@ -1442,7 +1226,6 @@ void printBuchiAutomaton else if (fmt == Graph::DOT) automaton->print(stream, indent, Graph::DOT); - estream << '\n'; estream.flush(); } @@ -1472,13 +1255,10 @@ void evaluateFormula { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - set, ALLOC(unsigned long int) > - algorithms; - set, ALLOC(unsigned long int) > - system_states; + IntervalList algorithms; + IntervalList system_states; string algorithm_name; int formula = (formula_type ? 0 : 1); - bool show_path_eval_results = false; if (!configuration.global_options.do_comp_test && !configuration.global_options.do_cons_test) @@ -1488,55 +1268,16 @@ void evaluateFormula if (round_info.statespace == 0) throw CommandErrorException("No state space was generated in this test " "round."); - string algorithm_list; + + /* + * If no list of algorithms was given as an argument, show the results of + * all algorithms. + */ if (input_tokens.size() < 2) - { - /* - * If no list of algorithms was given as an argument, show the results of - * all algorithms. - */ - - algorithm_list = "*"; input_tokens.push_back("*"); - show_path_eval_results - = ((configuration.global_options.statespace_generation_mode - & Configuration::PATH) != 0); - } - else - { - /* - * Otherwise parse the list of algorithms. Test also whether the internal - * model checking algorithm was included in the list. - */ - vector algorithm_list_elements; - sliceString(input_tokens[1], ",", algorithm_list_elements); - - for (vector::const_iterator alg - = algorithm_list_elements.begin(); - alg != algorithm_list_elements.end(); - ++alg) - { - if (algorithm_list.length() > 0) - algorithm_list += ','; - - if (*alg == "p") - { - if ((configuration.global_options.statespace_generation_mode - & Configuration::PATH) == 0) - throw CommandErrorException("This feature is available only when " - "using paths as state spaces."); - else - show_path_eval_results = true; - } - else - algorithm_list += *alg; - } - } - - parseInterval(algorithm_list, algorithms, 0, - round_info.number_of_translators - 1); + parseAlgorithmIdList(input_tokens[1], algorithms); /* * If no list of states was given, show information about all states. @@ -1547,8 +1288,18 @@ void evaluateFormula if (input_tokens.size() < 3) input_tokens.push_back("*"); - parseInterval(input_tokens[2], system_states, 0, - round_info.real_emptiness_check_size - 1); + try + { + parseIntervalList(input_tokens[2], system_states, 0, + round_info.real_emptiness_check_size - 1); + } + catch (const IntervalRangeException& e) + { + throw CommandErrorException + (string("State identifier out of range (") + + toString(e.getNumber()) + + ")."); + } estream << string(indent, ' ') + "Acceptance information:\n" + string(indent + 2, ' ') + "CTL* formula: E "; @@ -1560,35 +1311,26 @@ void evaluateFormula estream << '\n'; - for (set, - ALLOC(unsigned long int) >::const_iterator - state = system_states.begin(); + for (IntervalList::const_iterator state = system_states.begin(); state != system_states.end(); ++state) { - verifyNumber(*state, round_info.real_emptiness_check_size, - "State identifier out of range"); - estream << string(indent + 2, ' ') + "State " + toString(*state) + ":\n"; - for (set, - ALLOC(unsigned long int) >::const_iterator - algorithm = algorithms.begin(); - algorithm != algorithms.end(); - ++algorithm) + for (IntervalList::const_iterator algorithm = algorithms.begin(); + algorithm != algorithms.end(); + ++algorithm) { - verifyNumber(*algorithm, round_info.number_of_translators, - "No such implementation"); - - algorithm_name = configuration.algorithms[*algorithm].name - ->substr(0, 20); + algorithm_name = configuration.algorithms[*algorithm].name.substr(0, 20); estream << string(indent + 4, ' ') + toString(*algorithm) + ": " + algorithm_name + ':' + string(21 - algorithm_name.length(), ' '); if (!test_results[*algorithm].automaton_stats[formula]. emptiness_check_performed) - estream << "emptiness check not performed\n"; + estream << (configuration.isInternalAlgorithm(*algorithm) + ? "model checking result not available\n" + : "emptiness check not performed\n"); else estream << (test_results[*algorithm].automaton_stats[formula]. emptiness_check_result[*state] @@ -1596,28 +1338,15 @@ void evaluateFormula : "false") << '\n'; } - - if (show_path_eval_results) - { - estream << string(indent + 4, ' ') + "lbtt: "; - if (!test_results[round_info.number_of_translators]. - automaton_stats[formula].emptiness_check_performed) - estream << "no model checking result available\n"; - else - estream << (test_results[round_info.number_of_translators]. - automaton_stats[formula].emptiness_check_result[*state] - ? "true" - : "false") - << '\n'; - } } - estream << '\n'; estream.flush(); } /* ========================================================================= */ -void printFormula(ostream& stream, int indent, bool formula_type) +void printFormula + (ostream& stream, int indent, bool formula_type, + const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `formula', i.e., displays the @@ -1630,6 +1359,9 @@ void printFormula(ostream& stream, int indent, bool formula_type) * indent -- Number of spaces to leave on the left of * the output. * formula_type -- Identifies the formula to be displayed. + * input_tokens -- A reference to a vector of strings + * containing the arguments of the user + * command. * * Returns: Nothing. * @@ -1641,16 +1373,29 @@ void printFormula(ostream& stream, int indent, bool formula_type) Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - estream << string(indent, ' ') + "Formula:" + string(17, ' '); - round_info.formulae[formula_type ? 2 : 3]->print(estream); - - if (configuration.formula_options.output_mode == Configuration::NNF) + if (input_tokens.size() <= 1 || input_tokens[1] == "normal") { - estream << '\n' + string(indent, ' ') + "In negation normal form: "; - round_info.formulae[formula_type ? 0 : 1]->print(estream); + estream << string(indent, ' '); + round_info.formulae[formula_type ? 2 : 3]->print(estream); } + else if (input_tokens[1] == "nnf") + { + estream << string(indent, ' '); + if (configuration.formula_options.output_mode != Configuration::NNF) + { + ::Ltl::LtlFormula* f + = round_info.formulae[formula_type ? 2 : 3]->nnfClone(); + f->print(estream); + ::Ltl::LtlFormula::destruct(f); + } + else + round_info.formulae[formula_type ? 0 : 1]->print(estream); + } + else + throw CommandErrorException + ("`" + input_tokens[1] + "' is not a valid formula mode."); - estream << "\n\n"; + estream << '\n'; estream.flush(); } @@ -1679,16 +1424,19 @@ void printCommandHelp TokenType command = _NO_INPUT; if (input_tokens.size() > 1) + { command = parseCommand(input_tokens[1]); - - if (command == UNKNOWN) - estream << string(indent, ' ') + "Unknown command (`" + input_tokens[1] - + "').\n\n"; + if (command == UNKNOWN) + estream << string(indent, ' ') + "Unknown command (`" + input_tokens[1] + + "').\n\n"; + } switch (command) { case ALGORITHMS : - estream << string(indent, ' ') + "algorithms\n"; + estream << string(indent, ' ') + "algorithms\n" + + string(indent, ' ') + "implementations\n" + + string(indent, ' ') + "translators\n"; printTextBlock(stream, indent + 4, "List all implementations currently available for " @@ -1794,21 +1542,21 @@ void printCommandHelp "positive formula. Leaving the list of implementations " "or the list of states unspecified will display the " "results for all implementations or all system states, " - "respectively. If using paths as state spaces, the " - "special symbol \"p\" in the implementation list will " - "show also the results obtained with the internal " - "model checking algorithm.", + "respectively.", 78); break; case FORMULA : - estream << string(indent, ' ') + "formula [\"+\"|\"-\"]\n"; + estream << string(indent, ' ') + + "formula [\"+\"|\"-\"] [\"normal\"|\"nnf\"]\n"; printTextBlock(stream, indent + 4, "Display the LTL formula used in this test round for " "generating Büchi automata (\"+\" denotes the positive " - "formula, \"-\" the negated formula). If no formula is " - "specified, show the positive formula.", + "formula, \"-\" the negated formula; \"normal\" and " + "\"nnf\" select between the display mode). If no formula " + "(display mode) is specified, show the positive formula " + "(the formula as generated).", 78); break; @@ -1852,14 +1600,9 @@ void printCommandHelp "Analyze a contradiction in the model checking results " "of two implementations on a formula (\"+\" denotes the " "positive formula, \"-\" the negated formula). If no " - "formula is specified, use the positive formula. When " - "using paths as state spaces, one of implementation " - "identifiers can be replaced by the symbol \"p\" to " - "analyze the result given by some implementation against " - "that given by the internal model checking algorithm. " - "The optional argument `state' can be used to specify " - "the state of the state space in which to do the " - "analysis.", + "formula is specified, use the positive formula. The " + "optional argument `state' can be used to specify the " + "state of the state space in which to do the analysis.", 78); break; @@ -1933,6 +1676,7 @@ void printCommandHelp "evaluate\n" "formula\n" "help\n" + "implementations\n" "inconsistencies\n" "quit\n" "resultanalysis\n" @@ -1940,6 +1684,7 @@ void printCommandHelp "skip\n" "statespace\n" "statistics\n" + "translators\n" "verbosity\n", 78); @@ -1964,7 +1709,6 @@ void printCommandHelp break; } - estream << '\n'; estream.flush(); } @@ -1990,14 +1734,18 @@ void printInconsistencies { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - set, ALLOC(unsigned long int) > - number_set; + IntervalList algorithms; if (!configuration.global_options.do_cons_test) throw CommandErrorException("This command is available only when the " "model checking result consistency check is " "enabled."); + if (input_tokens.size() == 1) + input_tokens.push_back("*"); + + parseAlgorithmIdList(input_tokens[1], algorithms); + estream << string(indent, ' ') + "Model checking result consistency check " "results for round " + toString(round_info.current_round) + ":\n"; @@ -2011,23 +1759,13 @@ void printInconsistencies : 2] << '\n'; - if (input_tokens.size() == 1) - input_tokens.push_back("*"); - - parseInterval(input_tokens[1], number_set, 0, - round_info.number_of_translators - 1); - - for (set, - ALLOC(unsigned long int) >::const_iterator - algorithm = number_set.begin(); - algorithm != number_set.end(); ++algorithm) + for (IntervalList::const_iterator algorithm = algorithms.begin(); + algorithm != algorithms.end(); + ++algorithm) { - estream << '\n'; - - verifyNumber(*algorithm, round_info.number_of_translators, - "No such implementation"); - - estream << string(indent, ' ') + configuration.algorithmString(*algorithm) + estream << '\n' + + string(indent, ' ') + + configuration.algorithmString(*algorithm) + '\n'; if (test_results[*algorithm].consistency_check_result == -1) @@ -2075,7 +1813,6 @@ void printInconsistencies } } - estream << '\n'; estream.flush(); } @@ -2100,43 +1837,42 @@ void printTestResults * ------------------------------------------------------------------------- */ { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - set, ALLOC(unsigned long int) > - number_set; - - estream << string(indent, ' ') + "Test results for round " - + toString(round_info.current_round) << ":\n"; + IntervalList algorithms; if (input_tokens.size() == 1) input_tokens.push_back("*"); - parseInterval(input_tokens[1], number_set, 0, - round_info.number_of_translators - 1); + parseAlgorithmIdList(input_tokens[1], algorithms); - for (set, - ALLOC(unsigned long int) >::const_iterator - algorithm = number_set.begin(); - algorithm != number_set.end(); ++algorithm) - { - verifyNumber(*algorithm, round_info.number_of_translators, - "No such implementation"); + estream << string(indent, ' ') + "Test results for round " + + toString(round_info.current_round) << ":\n\n"; + if (configuration.global_options.verbosity <= 2) + printStatTableHeader(stream, indent); + + for (IntervalList::const_iterator algorithm = algorithms.begin(); + algorithm != algorithms.end(); + ++algorithm) printAllStats(stream, indent, *algorithm); - if (configuration.global_options.do_comp_test) - { - estream << string(indent, ' ') - + "Model checking result cross-comparison:\n"; - printCrossComparisonStats(stream, indent + 2, *algorithm); - } + if (configuration.global_options.verbosity <= 2) + estream << '\n'; - if (configuration.global_options.do_intr_test) - { - estream << string(indent, ' ') - + "Büchi automata intersection emptiness check:\n"; - printBuchiIntersectionCheckStats(stream, indent + 2, *algorithm); - } - estream.flush(); + if (configuration.global_options.do_comp_test) + { + estream << string(indent, ' ') + + "Model checking result cross-comparison:\n"; + printCrossComparisonStats(stream, indent + 2, algorithms); } + + if (configuration.global_options.do_intr_test) + { + estream << string(indent, ' ') + + "Büchi automata intersection emptiness check:\n"; + printBuchiIntersectionCheckStats(stream, indent + 2, algorithms); + } + + estream.flush(); } /* ========================================================================= */ @@ -2161,8 +1897,7 @@ void printStateSpace * ------------------------------------------------------------------------- */ { Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); - set, ALLOC(unsigned long int) > - number_set; + IntervalList states; if (!configuration.global_options.do_comp_test && !configuration.global_options.do_cons_test) @@ -2175,22 +1910,28 @@ void printStateSpace if (fmt == Graph::NORMAL) { - estream << string(indent, ' ') + "State space information:\n"; - if (input_tokens.size() == 1) input_tokens.push_back("*"); - parseInterval(input_tokens[1], number_set, 0, - round_info.statespace->size() - 1); - - for (set, - ALLOC(unsigned long int) >::const_iterator - state = number_set.begin(); - state != number_set.end(); ++state) + try { - verifyNumber(*state, round_info.statespace->size(), - "State identifier out of range"); + parseIntervalList(input_tokens[1], states, 0, + round_info.statespace->size() - 1); + } + catch (const IntervalRangeException& e) + { + throw CommandErrorException + (string("State identifier out of range (") + + toString(e.getNumber()) + + ")."); + } + estream << string(indent, ' ') + "State space information:\n"; + + for (IntervalList::const_iterator state = states.begin(); + state != states.end(); + ++state) + { estream << string(indent, ' ') + "State " + toString(*state) + (*state == round_info.statespace->initialState() ? " (initial state)" @@ -2205,7 +1946,6 @@ void printStateSpace else if (fmt == Graph::DOT) round_info.statespace->print(stream, indent, Graph::DOT); - estream << '\n'; estream.flush(); } @@ -2263,31 +2003,25 @@ void changeAlgorithmState * * ------------------------------------------------------------------------- */ { - set, ALLOC(unsigned long int) > - algorithms; + IntervalList algorithms; if (input_tokens.size() < 2) input_tokens.push_back("*"); - parseInterval(input_tokens[1], algorithms, 0, - round_info.number_of_translators - 1); + parseAlgorithmIdList(input_tokens[1], algorithms); - for (set, - ALLOC(unsigned long int) >::const_iterator - alg = algorithms.begin(); - alg != algorithms.end(); alg++) + for (IntervalList::const_iterator algorithm = algorithms.begin(); + algorithm != algorithms.end(); + ++algorithm) { - verifyNumber(*alg, round_info.number_of_translators, - "No such implementation"); - printText(string(enable ? "En" : "Dis") + "abling implementation " - + configuration.algorithmString(*alg) + + configuration.algorithmString(*algorithm) + ".\n", 0, 2); - configuration.algorithms[*alg].enabled = enable; + configuration.algorithms[*algorithm].enabled = enable; } round_info.cout << '\n'; diff --git a/lbtt/src/UserCommands.h b/lbtt/src/UserCommands.h index 31167eae2..756717387 100644 --- a/lbtt/src/UserCommands.h +++ b/lbtt/src/UserCommands.h @@ -23,13 +23,17 @@ #include #include #include +#include #include #include #include #include "LbttAlloc.h" #include "BuchiAutomaton.h" #include "Configuration.h" -#include "ProductAutomaton.h" +#include "EdgeContainer.h" +#include "Graph.h" +#include "IntervalList.h" +#include "Product.h" #include "StateSpace.h" using namespace std; @@ -45,38 +49,26 @@ extern Configuration configuration; namespace UserCommands { -void computeProductAutomaton /* Computes a product */ - (ProductAutomaton*& product_automaton, /* automaton. */ - const BuchiAutomaton& buchi_automaton, - pair& last_automaton, - const pair& - new_automaton); +unsigned long int parseAlgorithmId /* Parses an */ + (const string& id); /* implementation + * identifier. + */ + +void parseAlgorithmId /* Parses a list of */ + (const string& ids, IntervalList& algorithms); /* implementation + * identifiers. + */ void printAlgorithmList /* Displays a list of */ (ostream& stream, int indent); /* algorithms used in * the tests. */ -void synchronizePrefixAndCycle /* Synchronizes a prefix */ - (deque /* with a cycle in a */ - ::size_type, /* sequence of graph */ - ALLOC(Graph::Graph /* state identifiers. */ - ::size_type) >& - prefix, - deque - ::size_type, - ALLOC(Graph::Graph - ::size_type) >& - cycle); - void printCrossComparisonAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradiction between */ bool formula_type, /* test results of two */ const vector& /* implementations. */ - input_tokens, - ProductAutomaton*& product_automaton, - pair& - last_product_automaton); + input_tokens); void printConsistencyAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradicition in the */ @@ -87,19 +79,15 @@ void printConsistencyAnalysisResults /* Analyzes a */ void printAutomatonAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradiction in the */ - unsigned long int algorithm1, /* Büchi automata */ - unsigned long int algorithm2); /* intersection + const vector& /* Büchi automata */ + input_tokens); /* intersection * emptiness check. */ void printPath /* Displays information */ (ostream& stream, int indent, /* about a single */ - const deque& - prefix, - const deque& - cycle, + const StateSpace::Path& prefix, /* system execution. */ + const StateSpace::Path& cycle, const StateSpace& path); void printAcceptingCycle /* Displays information */ @@ -108,13 +96,12 @@ void printAcceptingCycle /* Displays information */ ALLOC(Configuration::AlgorithmInformation) > ::size_type algorithm_id, - const deque& - prefix, - const deque& - cycle, - const BuchiAutomaton& automaton); + const BuchiAutomaton::Path& aut_prefix, + const BuchiAutomaton::Path& aut_cycle, + const BuchiAutomaton& automaton, + const StateSpace::Path& path_prefix, + const StateSpace::Path& path_cycle, + const StateSpace& statespace); void printBuchiAutomaton /* Displays information */ (ostream& stream, int indent, /* about a Büchi */ @@ -129,7 +116,9 @@ void evaluateFormula /* Displays information */ void printFormula /* Displays a formula */ (ostream& stream, int indent, /* used for testing. */ - bool formula_type); + bool formula_type, + const vector& + input_tokens); void printCommandHelp /* Displays help about */ (ostream& stream, int indent, /* user commands. */ diff --git a/lbtt/src/main.cc b/lbtt/src/main.cc index a8b02892c..c217ac4f7 100644 --- a/lbtt/src/main.cc +++ b/lbtt/src/main.cc @@ -36,6 +36,7 @@ #include "Random.h" #include "SharedTestData.h" #include "StatDisplay.h" +#include "TempFsysName.h" #include "TestOperations.h" #include "TestRoundInfo.h" #include "TestStatistics.h" @@ -43,17 +44,6 @@ using namespace std; -/****************************************************************************** - * - * Handler for the SIGINT signal. - * - *****************************************************************************/ - -RETSIGTYPE breakHandler(int) -{ - user_break = true; -} - /****************************************************************************** @@ -105,13 +95,120 @@ vector /* Overall test */ +/****************************************************************************** + * + * Functions for allocating and deallocating temporary file names. + * + *****************************************************************************/ + +static void allocateTempFilenames() +{ + using SharedTestData::round_info; + round_info.formula_file_name[0] = new TempFsysName; + round_info.formula_file_name[0]->allocate("lbtt"); + round_info.formula_file_name[1] = new TempFsysName; + round_info.formula_file_name[1]->allocate("lbtt"); + round_info.automaton_file_name = new TempFsysName; + round_info.automaton_file_name->allocate("lbtt"); + round_info.cout_capture_file = new TempFsysName; + round_info.cout_capture_file->allocate("lbtt"); + round_info.cerr_capture_file = new TempFsysName; + round_info.cerr_capture_file->allocate("lbtt"); +} + +static void deallocateTempFilenames() +{ + using SharedTestData::round_info; + if (round_info.formula_file_name[0] != 0) + { + delete round_info.formula_file_name[0]; + round_info.formula_file_name[0] = 0; + } + if (round_info.formula_file_name[1] != 0) + { + delete round_info.formula_file_name[1]; + round_info.formula_file_name[1] = 0; + } + if (round_info.automaton_file_name != 0) + { + delete round_info.automaton_file_name; + round_info.automaton_file_name = 0; + } + if (round_info.cout_capture_file != 0) + { + delete round_info.cout_capture_file; + round_info.cout_capture_file = 0; + } + if (round_info.cerr_capture_file != 0) + { + delete round_info.cerr_capture_file; + round_info.cerr_capture_file = 0; + } +} + + + +/****************************************************************************** + * + * Handler for the SIGINT signal. + * + *****************************************************************************/ + +static void breakHandler(int) +{ + user_break = true; +} + + + +/****************************************************************************** + * + * Default handler for signals that terminate the process. + * + *****************************************************************************/ + +static void abortHandler(int signum) +{ + deallocateTempFilenames(); + struct sigaction s; + s.sa_handler = SIG_DFL; + sigemptyset(&s.sa_mask); + s.sa_flags = 0; + sigaction(signum, &s, static_cast(0)); + raise(signum); +} + + + +/****************************************************************************** + * + * Function for installing signal handlers. + * + *****************************************************************************/ + +static void installSignalHandler(int signum, void (*handler)(int)) +{ + struct sigaction s; + sigaction(signum, static_cast(0), &s); + + if (s.sa_handler != SIG_IGN) + { + s.sa_handler = handler; + sigemptyset(&s.sa_mask); + s.sa_flags = 0; + sigaction(signum, &s, static_cast(0)); + } +} + + + /****************************************************************************** * * Test loop. * *****************************************************************************/ -void testLoop() +bool testLoop() { using namespace DispUtil; using namespace SharedTestData; @@ -136,13 +233,6 @@ void testLoop() ? round_info.next_round_to_run : global_options.number_of_rounds + 1); - if (tmpnam(round_info.formula_file_name[0]) == 0 - || tmpnam(round_info.formula_file_name[1]) == 0 - || tmpnam(round_info.automaton_file_name) == 0 - || tmpnam(round_info.cout_capture_file) == 0 - || tmpnam(round_info.cerr_capture_file) == 0) - throw Exception("unable to allocate names for temporary files"); - /* * If a name for the error log file was given in the configuration, create * the file. @@ -221,19 +311,6 @@ void testLoop() formula_random_state[i] = static_cast(LRAND(0, LONG_MAX)); #endif /* HAVE_RAND48 */ - /* - * If using paths as state spaces, include the internal model checking - * algorithm in the set of algorithms. - */ - - if (global_options.statespace_generation_mode & Configuration::PATH) - { - Configuration::AlgorithmInformation lbtt_info - = {new string("lbtt"), new string(), new string(), true}; - - configuration.algorithms.push_back(lbtt_info); - } - /* * Intialize the vector for storing the test results for each * implementation and the vector for collecting overall test statistics for @@ -271,21 +348,9 @@ void testLoop() = (round_info.current_round < round_info.next_round_to_run); if (!round_info.skip) - { - if (!printText(string("Round ") + toString(round_info.current_round) - + " of " + toString(global_options.number_of_rounds) - + "\n\n", - 2)) - { - if (global_options.verbosity == 1) - { - if (round_info.current_round > 1) - round_info.cout << ' '; - round_info.cout << round_info.current_round; - round_info.cout.flush(); - } - } - } + printText(string("Round ") + toString(round_info.current_round) + + " of " + toString(global_options.number_of_rounds) + "\n\n", + 2); try { @@ -399,7 +464,7 @@ void testLoop() if (user_break) { - printText("[User break]\n\n", 2, 4); + printText("[User break]\n\n", 1, 4); throw UserBreakException(); } @@ -414,12 +479,17 @@ void testLoop() if (global_options.statespace_generation_mode & Configuration::PATH && (global_options.do_cons_test || global_options.do_comp_test) - && (!test_results[round_info.number_of_translators]. - automaton_stats[0].emptiness_check_performed)) + && (!test_results[round_info.number_of_translators - 1]. + automaton_stats[0].emptiness_check_performed) + && configuration.algorithms[round_info.number_of_translators - 1]. + enabled) verifyFormulaOnPath(); if (!round_info.error) { + if (global_options.verbosity == 2) + ::StatDisplay::printStatTableHeader(round_info.cout, 4); + unsigned long int num_enabled_implementations = 0; for (unsigned long int algorithm_id = 0; @@ -431,81 +501,61 @@ void testLoop() num_enabled_implementations++; + if (configuration.isInternalAlgorithm(algorithm_id)) + continue; + printText(configuration.algorithmString(algorithm_id) + '\n', - 2, 4); + 3, 4); for (int counter = 0; counter < 2; counter++) { if (user_break) { - printText("[User break]\n\n", 2, 4); + printText("[User break]\n\n", 1, 4); throw UserBreakException(); } - printText(string(counter == 1 ? "Negated" : "Positive") - + " formula:\n", - 2, - 6); + if (global_options.verbosity == 1 + || global_options.verbosity == 2) + { + if (counter == 1) + round_info.cout << '\n'; + if (global_options.verbosity == 1) + round_info.cout << round_info.current_round << ' '; + else + round_info.cout << string(4, ' '); + changeStreamFormatting(cout, 2, 0, ios::right); + round_info.cout << algorithm_id << ' '; + restoreStreamFormatting(cout); + round_info.cout << (counter == 0 ? '+' : '-') << ' '; + round_info.cout.flush(); + } + else + printText(string(counter == 1 ? "Negated" : "Positive") + + " formula:\n", + 3, + 6); try { - try - { - round_info.product_automaton = 0; + /* + * Generate a Büchi automaton using the current algorithm. + * `counter' determines the formula which is to be + * translated into an automaton; 0 denotes the positive and + * 1 the negated formula. + */ + generateBuchiAutomaton(counter, algorithm_id); + + if (global_options.do_cons_test || global_options.do_comp_test) + { /* - * Generate a Büchi automaton using the current algorithm. - * `counter' determines the formula which is to be - * translated into an automaton; 0 denotes the positive and - * 1 the negated formula. + * Find the system states from which an accepting + * execution cycle can be reached by checking the product + * automaton for emptiness. */ - generateBuchiAutomaton(counter, algorithm_id); - - if (global_options.do_cons_test - || global_options.do_comp_test) - { - /* - * Compute the product of the Büchi automaton with the - * state space. - */ - - generateProductAutomaton(counter, algorithm_id); - - /* - * Find the system states from which an accepting - * execution cycle can be reached by checking the product - * automaton for emptiness. - */ - - performEmptinessCheck(counter, algorithm_id); - - /* - * If a product automaton was computed in this test round - * (it might have not if the emptiness checking result was - * already available), release the memory allocated for - * the product automaton. - */ - - if (round_info.product_automaton != 0) - { - printText("", 4, 8); - - delete round_info.product_automaton; - round_info.product_automaton = 0; - - printText(" ok\n", 4); - } - } - } - catch (...) - { - if (round_info.product_automaton != 0) - { - delete round_info.product_automaton; - round_info.product_automaton = 0; - } - throw; + performEmptinessCheck(counter, algorithm_id); } } catch (const BuchiAutomatonGenerationException&) @@ -541,7 +591,13 @@ void testLoop() emptiness_check_performed) performConsistencyCheck(algorithm_id); - printText("\n", 2); + printText("\n", 1); + } + + if (global_options.verbosity == 2) + { + round_info.cout << '\n'; + round_info.cout.flush(); } if (num_enabled_implementations > 0) @@ -553,10 +609,7 @@ void testLoop() * results obtained using the different algorithms. */ - if (num_enabled_implementations >= 2 - || (num_enabled_implementations == 1 - && global_options.statespace_generation_mode - & Configuration::PATH)) + if (num_enabled_implementations >= 2) compareResults(); } @@ -592,25 +645,17 @@ void testLoop() * the testing should be paused to wait for user commands. */ + if (round_info.error) + round_info.all_tests_successful = false; + if (round_info.error && global_options.interactive == Configuration::ONERROR) round_info.next_round_to_stop = round_info.current_round; if (round_info.next_round_to_stop == round_info.current_round) - { - if (global_options.verbosity == 1) - { - round_info.cout << '\n'; - round_info.cout.flush(); - } - ::UserCommandInterface::executeUserCommands(); - } } - for (int i = 0; i < 2; i++) - removeFile(round_info.formula_file_name[i], 2); - if (round_info.path_iterator != 0) delete round_info.path_iterator; else if (round_info.statespace != 0) @@ -661,11 +706,13 @@ void testLoop() round_info.transcript_file.close(); } - if (global_options.verbosity >= 1) + if (global_options.verbosity >= 2) printCollectiveStats(cout, 0); if (round_info.formula_input_file.is_open()) round_info.formula_input_file.close(); + + return round_info.all_tests_successful; } @@ -688,15 +735,27 @@ int main(int argc, char* argv[]) if (!e.line_info.empty()) cerr << ":" << configuration.global_options.cfg_filename << ":" << e.line_info; - cerr << ": " << e.what() << endl; - exit(-1); + cerr << ":" << e.what() << endl; + exit(2); } if (configuration.global_options.verbosity >= 3) configuration.print(cout); user_break = false; - signal(SIGINT, breakHandler); + + installSignalHandler(SIGHUP, abortHandler); + installSignalHandler(SIGINT, + configuration.global_options.handle_breaks + ? breakHandler + : abortHandler); + installSignalHandler(SIGQUIT, abortHandler); + installSignalHandler(SIGABRT, abortHandler); + installSignalHandler(SIGPIPE, abortHandler); + installSignalHandler(SIGALRM, abortHandler); + installSignalHandler(SIGTERM, abortHandler); + installSignalHandler(SIGUSR1, abortHandler); + installSignalHandler(SIGUSR2, abortHandler); #ifdef HAVE_OBSTACK_H obstack_alloc_failed_handler = &ObstackAllocator::failure; @@ -708,18 +767,26 @@ int main(int argc, char* argv[]) try { - testLoop(); + allocateTempFilenames(); + if (!testLoop()) + { + deallocateTempFilenames(); + return 1; + } } catch (const Exception& e) { - cerr << argv[0] << ": " << e.what() << endl; - exit(-1); + deallocateTempFilenames(); + cerr << endl << argv[0] << ": " << e.what() << endl; + exit(3); } catch (const bad_alloc&) { - cerr << argv[0] << ": out of memory" << endl; - exit(-1); + deallocateTempFilenames(); + cerr << endl << argv[0] << ": out of memory" << endl; + exit(3); } + deallocateTempFilenames(); return 0; } diff --git a/lbtt/src/translate.cc b/lbtt/src/translate.cc index 645e3a37f..eab9a9037 100644 --- a/lbtt/src/translate.cc +++ b/lbtt/src/translate.cc @@ -45,6 +45,17 @@ char** command_line_arguments; +/****************************************************************************** + * + * Pointer to an object providing operations for translating a formula into an + * automaton. + * + *****************************************************************************/ + +static TranslatorInterface* translator = 0; + + + /****************************************************************************** * * A function for showing warnings to the user. @@ -60,21 +71,46 @@ void printWarning(const string& msg) /****************************************************************************** * - * Signal handler for debugging purposes. + * Handler for SIGINT, SIGQUIT, SIGABRT and SIGTERM. * *****************************************************************************/ -RETSIGTYPE signalHandler(int signal_number) +static void signalHandler(int signal_number) { - cerr << string(command_line_arguments[0]) + ": received signal " - << signal_number - << endl; - signal(signal_number, SIG_DFL); + if (translator != 0) + delete translator; + struct sigaction s; + s.sa_handler = SIG_DFL; + sigemptyset(&s.sa_mask); + s.sa_flags = 0; + sigaction(signal_number, &s, static_cast(0)); raise(signal_number); } +/****************************************************************************** + * + * Function for installing signal handlers. + * + *****************************************************************************/ + +static void installSignalHandler(int signum) +{ + struct sigaction s; + sigaction(signum, static_cast(0), &s); + + if (s.sa_handler != SIG_IGN) + { + s.sa_handler = signalHandler; + sigemptyset(&s.sa_mask); + s.sa_flags = 0; + sigaction(signum, &s, static_cast(0)); + } +} + + + /****************************************************************************** * * Main function. @@ -83,7 +119,7 @@ RETSIGTYPE signalHandler(int signal_number) int main(int argc, char** argv) { - typedef enum {OPT_HELP = 'h', OPT_LBT, OPT_SPIN, OPT_VERSION = 'v'} + typedef enum {OPT_HELP = 'h', OPT_LBT, OPT_SPIN, OPT_VERSION = 'V'} OptionType; static OPTIONSTRUCT command_line_options[] = @@ -100,12 +136,10 @@ int main(int argc, char** argv) opterr = 1; int opttype, option_index; - TranslatorInterface* translator = 0; - do { option_index = 0; - opttype = getopt_long(argc, argv, "hv", command_line_options, + opttype = getopt_long(argc, argv, "hV", command_line_options, &option_index); switch (opttype) @@ -115,8 +149,8 @@ int main(int argc, char** argv) << " [translator] [command line for translator] [formula " "file] [automaton file]\n" "General options:\n" - " --h, --help Show this help\n" - " --v, --version Show version and exit\n\n" + " -h, --help Show this help\n" + " -V, --version Show version and exit\n\n" "Translator options:\n" " --lbt lbt\n" " --spin Spin\n" @@ -168,16 +202,15 @@ int main(int argc, char** argv) int exitstatus = 0; - signal(SIGHUP, signalHandler); - signal(SIGINT, signalHandler); - signal(SIGQUIT, signalHandler); - signal(SIGILL, signalHandler); - signal(SIGABRT, signalHandler); - signal(SIGFPE, signalHandler); - signal(SIGSEGV, signalHandler); - signal(SIGPIPE, signalHandler); - signal(SIGALRM, signalHandler); - signal(SIGTERM, signalHandler); + installSignalHandler(SIGHUP); + installSignalHandler(SIGINT); + installSignalHandler(SIGQUIT); + installSignalHandler(SIGABRT); + installSignalHandler(SIGPIPE); + installSignalHandler(SIGALRM); + installSignalHandler(SIGTERM); + installSignalHandler(SIGUSR1); + installSignalHandler(SIGUSR2); ::Ltl::LtlFormula* formula(0); From 894050ed906a685eac72c39cf365abf6e17ad097 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 30 Jul 2004 11:37:46 +0000 Subject: [PATCH 5/7] Import of lbtt 1.1.1 --- lbtt/ChangeLog | 22 ++++++++++++++++++++++ lbtt/NEWS | 7 ++++++- lbtt/README | 6 +++--- lbtt/configure.ac | 8 ++++---- lbtt/src/TestOperations.cc | 36 +++++++++++++++++++++++++++++++++--- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index f0b8db9e4..56664b92f 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,25 @@ +2004-07-30 Heikki Tauriainen + + * Version 1.1.1 released. + +2004-07-30 Heikki Tauriainen + + * src/TestOperations.cc (generateBuchiAutomaton): Protect + against referencing a null pointer returned by strsignal on + some platforms. + (performBuchiIntersectionCheck): Handle product size + exceptions gracefully. Skip redundant writes to an unopened + transcript file. + + * configure.ac: Update version. + * NEWS: Update. + * README: Update. + +2004-07-16 Alexandre Duret-Lutz + + * configure.ac: Call AC_GNU_SOURCE to make glibc's strsignal + definition visible even to non-GNU compilers. + 2004-07-07 Heikki Tauriainen * Version 1.1.0 released. diff --git a/lbtt/NEWS b/lbtt/NEWS index 4abdfd71b..0621efd5a 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,4 +1,4 @@ -lbtt NEWS -- history of user-visible changes. 7 Jul 2004 +lbtt NEWS -- history of user-visible changes. 30 Jul 2004 Copyright (C) 2004 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies @@ -12,6 +12,11 @@ Copyright (C) 2004 Heikki Tauriainen Please send bug reports to . +Version 1.1.1 + +* This release includes fixes to build problems with non-GNU + compilers on GNU libc systems and a few minor bug fixes. + Version 1.1.0 * File formats diff --git a/lbtt/README b/lbtt/README index c400bf804..41506e366 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,11 +1,11 @@ -lbtt version 1.1.0 +lbtt version 1.1.1 ------------------ lbtt is a tool for testing programs that translate formulas expressed in propositional linear temporal logic (LTL) into Büchi automata. The goal of the tool is to assist implementing LTL-to-Büchi translation algorithms correctly by providing an -automated testing environment for LTL-to-B@"uchi translators. +automated testing environment for LTL-to-Büchi translators. Additionally, the testing environment can be used for very basic profiling of different LTL-to-Büchi translators to evaluate their performance. @@ -18,7 +18,7 @@ the terms of the GNU General Public License. lbtt comes with NO WARRANTY. See the file COPYING for details. -Quick installation instructions: +Brief installation instructions: -------------------------------- The basic procedure to build lbtt, the associated tools diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 52119c3b4..5c9df77bc 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,8 +1,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.59]) -AC_INIT([lbtt], [1.1.0], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.4]) +AC_INIT([lbtt], [1.1.1], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.5]) AC_CONFIG_SRCDIR([src/main.cc]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE @@ -70,6 +70,7 @@ fi # Check for the availability of headers. +AC_GNU_SOURCE AC_HEADER_STDC AC_CHECK_HEADERS([libintl.h fcntl.h sys/times.h]) AC_HEADER_SYS_WAIT @@ -148,7 +149,7 @@ if test "${slist_header}" != no; then [[${slist_namespace}::slist s;]])], [break]) fi - done + done if test "${slist_namespace}" != error; then AC_MSG_RESULT([header <${slist_header}>, typename ${slist_namespace}::slist]) AC_DEFINE( @@ -167,7 +168,6 @@ fi if test "${slist_header}" = no; then AC_MSG_RESULT([no]) - fi AC_LANG(C) diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 5e3b3ed64..5389cdbef 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -1114,11 +1114,14 @@ void generateBuchiAutomaton if (WIFSIGNALED(exitcode)) { failure_reason += "aborted by signal " - + toString(WTERMSIG(exitcode)) + + toString(WTERMSIG(exitcode)); + #ifdef HAVE_STRSIGNAL - + " (" + strsignal(WTERMSIG(exitcode)) + ")" + const char* signame = strsignal(WTERMSIG(exitcode)); + if (signame != 0) + failure_reason += " (" + string(signame) + ")"; #endif /* HAVE_STRSIGNAL */ - ; + if (WTERMSIG(exitcode) == SIGINT || WTERMSIG(exitcode) == SIGQUIT) raise(WTERMSIG(exitcode)); } @@ -1776,6 +1779,7 @@ void performBuchiIntersectionCheck() printText("\n\n", 2); if (round_info.transcript_file.is_open()) + { writeToTranscript("User break during Büchi automata intersection " "emptiness check"); round_info.transcript_file << string(8, ' ') + "(+) " @@ -1783,9 +1787,33 @@ void performBuchiIntersectionCheck() + ", (-) " + configuration.algorithmString(alg_2) + "\n\n"; + } throw; } + catch (const ::Graph::Product< ::Graph::BuchiProduct>::SizeException&) + { + if (!printText(": aborted (product may be too large)", 4)) + printText(" [Product may be too large: (+) " + + configuration.algorithmString(alg_1) + + ", (-) " + + configuration.algorithmString(alg_2) + + "]", + 2, + 6); + printText("\n", 2); + + if (round_info.transcript_file.is_open()) + { + writeToTranscript("Automata intersection emptiness check aborted " + "(product may be too large)"); + round_info.transcript_file << string(8, ' ') + "(+) " + + configuration.algorithmString(alg_1) + + ", (-) " + + configuration.algorithmString(alg_2) + + "\n\n"; + } + } catch (const bad_alloc&) { if (!printText(": aborted (out of memory)", 4)) @@ -1799,6 +1827,7 @@ void performBuchiIntersectionCheck() printText("\n", 2); if (round_info.transcript_file.is_open()) + { writeToTranscript("Out of memory during Büchi automata " "intersection emptiness check"); round_info.transcript_file << string(8, ' ') + "(+) " @@ -1806,6 +1835,7 @@ void performBuchiIntersectionCheck() + ", (-) " + configuration.algorithmString(alg_2) + "\n\n"; + } } } } From e4befcecc7ae9cfc4b9b2f92494315aa93c05358 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 2 Aug 2004 08:59:00 +0000 Subject: [PATCH 6/7] Import of lbtt 1.1.2 --- lbtt/ChangeLog | 26 ++++++++++++++++++++++++++ lbtt/NEWS | 7 ++++++- lbtt/README | 2 +- lbtt/configure.ac | 6 +++--- lbtt/src/Product.h | 12 ++++++------ lbtt/src/TestOperations.cc | 14 ++++++++------ 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index 56664b92f..1184042b1 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,29 @@ +2004-08-02 Heikki Tauriainen + + * Version 1.1.2 released. + +2004-08-01 Heikki Tauriainen + + * src/TestOperations.cc (generateBuchiAutomaton): Use + process groups to terminate the child process along with + all of its children (if any) on timeouts. + Do not write the time elapsed before a timeout into the + log file. + + * configure.ac: Add test for the setsid library function. + +2004-07-31 Heikki Tauriainen + + * src/Product.h (ProductEdge::edge_1, ProductEdge::edge_2): + Change type to GraphEdgeContainer::const_iterators to move all + edge dereference operations to member functions instead of the + class constructor (where dereferencing is not always safe). + (ProductEdge::ProductEdge, ProductEdge::firstComponent) + (ProductEdge::secondComponent, ProductEdge::targetNode): + Change member initialization and access accordingly. + + * configure.ac, NEWS, README: Update version. + 2004-07-30 Heikki Tauriainen * Version 1.1.1 released. diff --git a/lbtt/NEWS b/lbtt/NEWS index 0621efd5a..5e168d954 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,4 +1,4 @@ -lbtt NEWS -- history of user-visible changes. 30 Jul 2004 +lbtt NEWS -- history of user-visible changes. 02 Aug 2004 Copyright (C) 2004 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies @@ -12,6 +12,11 @@ Copyright (C) 2004 Heikki Tauriainen Please send bug reports to . +Version 1.1.2 + +* Another bug fix release that fixes memory access and job control + problems. + Version 1.1.1 * This release includes fixes to build problems with non-GNU diff --git a/lbtt/README b/lbtt/README index 41506e366..f6e9927b6 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,4 +1,4 @@ -lbtt version 1.1.1 +lbtt version 1.1.2 ------------------ lbtt is a tool for testing programs that translate formulas diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 5c9df77bc..863b97792 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,8 +1,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.59]) -AC_INIT([lbtt], [1.1.1], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.5]) +AC_INIT([lbtt], [1.1.2], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.6]) AC_CONFIG_SRCDIR([src/main.cc]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE @@ -191,7 +191,7 @@ AC_C_INLINE # Checks for library functions. AC_CHECK_FUNCS( - [strchr strtod strtol strtoul strerror mkdir mkstemp open read write close popen pclose pipe fork execvp getpid waitpid alarm sigaction sigprocmask sigemptyset sigaddset times sysconf], + [strchr strtod strtol strtoul strerror mkdir mkstemp open read write close popen pclose pipe fork execvp setsid getpid waitpid alarm sigaction sigprocmask sigemptyset sigaddset times sysconf], [], [AC_MSG_ERROR([missing one of the library functions required for compilation])]) AC_CHECK_FUNCS([strsignal isatty getopt_long]) diff --git a/lbtt/src/Product.h b/lbtt/src/Product.h index f7b49a677..9a856cadf 100644 --- a/lbtt/src/Product.h +++ b/lbtt/src/Product.h @@ -524,8 +524,8 @@ public: */ private: - const Graph::Edge* edge_1; /* Components of the */ - const Graph::Edge* edge_2; /* transition. */ + GraphEdgeContainer::const_iterator edge_1; /* Components of the */ + GraphEdgeContainer::const_iterator edge_2; /* transition. */ }; @@ -1598,7 +1598,7 @@ template inline Product::ProductEdge::ProductEdge (const GraphEdgeContainer::const_iterator& e1, const GraphEdgeContainer::const_iterator& e2) - : edge_1(*e1), edge_2(*e2) + : edge_1(e1), edge_2(e2) /* ---------------------------------------------------------------------------- * * Description: Constructor for class Product::ProductEdge. @@ -1643,7 +1643,7 @@ Product::ProductEdge::firstComponent() const * * ------------------------------------------------------------------------- */ { - return *edge_1; + return **edge_1; } /* ========================================================================= */ @@ -1662,7 +1662,7 @@ Product::ProductEdge::secondComponent() const * * ------------------------------------------------------------------------- */ { - return *edge_2; + return **edge_2; } /* ========================================================================= */ @@ -1679,7 +1679,7 @@ Product::ProductEdge::targetNode() const * * ------------------------------------------------------------------------- */ { - return product->stateId(edge_1->targetNode(), edge_2->targetNode()); + return product->stateId((*edge_1)->targetNode(), (*edge_2)->targetNode()); } diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 5389cdbef..8dd515b66 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -938,11 +938,13 @@ void generateBuchiAutomaton case 0 : /* child */ close(error_pipe[0]); - if (dup2(stdout_capture_fileno, STDOUT_FILENO) != -1 + if (setsid() != -1 + && dup2(stdout_capture_fileno, STDOUT_FILENO) != -1 && dup2(stderr_capture_fileno, STDERR_FILENO) != -1) execvp(algorithm.parameters[0], algorithm.parameters); - /* dup2 or exec failed: write the value of errno to error_pipe */ + /* setsid, dup2 or exec failed: write the value of errno to + * error_pipe */ write(error_pipe[1], static_cast(&errno), sizeof(int)); @@ -993,7 +995,7 @@ void generateBuchiAutomaton for (int attempts_to_terminate = 0; attempts_to_terminate < 4; ++attempts_to_terminate) { - kill(pid, sig); + kill(-pid, sig); sleep(delay); if (waitpid(pid, &exitcode, WNOHANG) != 0) { @@ -1081,14 +1083,14 @@ void generateBuchiAutomaton throw Exception("could not terminate child process"); } - if (error_number != 0) /* pipe, fork, dup2, execvp or waitpid failed */ + if (error_number != 0) /* pipe, fork, setsid, dup2, execvp or waitpid + * failed */ { stdout_capture_fileno = stderr_capture_fileno = -1; ExecFailedException e; if (configuration.global_options.translator_timeout > 0 && timeout) - e.changeMessage("Timeout after " + toString(elapsed_time, 2) - + " seconds (user time)."); + e.changeMessage("Automaton generation aborted due to timeout."); else e.changeMessage("Execution of `" + string(algorithm.parameters[0]) + "' failed (" + string(strerror(error_number)) From 0a12b942a4c826d29af8a548a61249b54475545a Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 31 Aug 2005 15:14:51 +0000 Subject: [PATCH 7/7] Import of lbtt 1.2.0 --- lbtt/AUTHORS | 2 +- lbtt/ChangeLog | 113 ++++++++++++++++++++++++ lbtt/NEWS | 22 ++++- lbtt/README | 2 +- lbtt/configure.ac | 52 +---------- lbtt/doc/lbtt.texi | 69 ++++++++++----- lbtt/src/BitArray.cc | 4 +- lbtt/src/BitArray.h | 14 ++- lbtt/src/Bitset.h | 4 +- lbtt/src/BuchiAutomaton.cc | 19 ++-- lbtt/src/BuchiAutomaton.h | 4 +- lbtt/src/BuchiProduct.cc | 10 +-- lbtt/src/BuchiProduct.h | 16 ++-- lbtt/src/Config-lex.ll | 4 +- lbtt/src/Config-parse.yy | 4 +- lbtt/src/Configuration.cc | 62 +++++++------- lbtt/src/Configuration.h | 44 +++++----- lbtt/src/DispUtil.cc | 12 +-- lbtt/src/DispUtil.h | 4 +- lbtt/src/EdgeContainer.h | 4 +- lbtt/src/Exception.h | 4 +- lbtt/src/ExternalTranslator.cc | 4 +- lbtt/src/ExternalTranslator.h | 13 +-- lbtt/src/FormulaRandomizer.cc | 6 +- lbtt/src/FormulaRandomizer.h | 55 ++++++------ lbtt/src/FormulaWriter.h | 4 +- lbtt/src/Graph.h.in | 127 ++++++++++++--------------- lbtt/src/IntervalList.cc | 19 ++-- lbtt/src/IntervalList.h | 23 ++--- lbtt/src/LbtWrapper.h | 4 +- lbtt/src/LbttAlloc.h | 6 +- lbtt/src/Ltl-parse.yy | 14 +-- lbtt/src/LtlFormula.cc | 29 +++---- lbtt/src/LtlFormula.h | 23 ++--- lbtt/src/Makefile.am | 2 + lbtt/src/NeverClaim-lex.ll | 4 +- lbtt/src/NeverClaim-parse.yy | 4 +- lbtt/src/NeverClaimAutomaton.cc | 14 ++- lbtt/src/NeverClaimAutomaton.h | 29 +++---- lbtt/src/PathEvaluator.cc | 16 ++-- lbtt/src/PathEvaluator.h | 12 +-- lbtt/src/PathIterator.cc | 4 +- lbtt/src/PathIterator.h | 4 +- lbtt/src/Product.h | 41 ++++----- lbtt/src/Random.h | 4 +- lbtt/src/SccCollection.h | 34 ++++---- lbtt/src/SharedTestData.h | 18 ++-- lbtt/src/SpinWrapper.cc | 4 +- lbtt/src/SpinWrapper.h | 4 +- lbtt/src/SpotWrapper.cc | 102 ++++++++++++++++++++++ lbtt/src/SpotWrapper.h | 143 +++++++++++++++++++++++++++++++ lbtt/src/StatDisplay.cc | 46 ++++------ lbtt/src/StatDisplay.h | 30 +++---- lbtt/src/StateSpace.cc | 7 +- lbtt/src/StateSpace.h | 4 +- lbtt/src/StateSpaceProduct.h | 4 +- lbtt/src/StateSpaceRandomizer.cc | 8 +- lbtt/src/StateSpaceRandomizer.h | 4 +- lbtt/src/StringUtil.cc | 14 ++- lbtt/src/StringUtil.h | 11 +-- lbtt/src/TempFsysName.cc | 4 +- lbtt/src/TempFsysName.h | 4 +- lbtt/src/TestOperations.cc | 116 ++++++++++++++----------- lbtt/src/TestOperations.h | 24 +++--- lbtt/src/TestRoundInfo.h | 18 ++-- lbtt/src/TestStatistics.cc | 9 +- lbtt/src/TestStatistics.h | 72 +++++++--------- lbtt/src/TranslatorInterface.h | 4 +- lbtt/src/UserCommandReader.cc | 20 ++--- lbtt/src/UserCommandReader.h | 17 ++-- lbtt/src/UserCommands.cc | 56 +++++------- lbtt/src/UserCommands.h | 44 +++++----- lbtt/src/main.cc | 68 ++++++++++----- lbtt/src/translate.cc | 15 +++- lbtt/src/translate.h | 4 +- 75 files changed, 1069 insertions(+), 769 deletions(-) create mode 100644 lbtt/src/SpotWrapper.cc create mode 100644 lbtt/src/SpotWrapper.h diff --git a/lbtt/AUTHORS b/lbtt/AUTHORS index dfd5d7d33..5fd9b83c0 100644 --- a/lbtt/AUTHORS +++ b/lbtt/AUTHORS @@ -1 +1 @@ -Heikki Tauriainen +Heikki Tauriainen diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog index 1184042b1..3d865a4e8 100644 --- a/lbtt/ChangeLog +++ b/lbtt/ChangeLog @@ -1,3 +1,116 @@ +2005-08-30 Heikki Tauriainen + + * src/main.cc: [HAVE_ISATTY && HAVE_UNISTD_H]: Include the + unistd.h header. + (testLoop): Add support for reading LTL formulas from standard + input using the new variable `round_info.formula_input_stream'. + (main) [HAVE_ISATTY]: If formulas are to be read from the + standard input which is not a terminal, force lbtt to work in + non-interactive mode. + * src/TestOperations.cc (generateFormula): + Use `round_info.formula_input_stream' instead of + `round_info.formula_input_file'. + * src/TestRoundInfo.h (TestRoundInfo::formula_input_stream): New + variable. + * src/Configuration.cc (Configuration::showCommandLineHelp): + Update description of the --formulafile command line option. + (Configuration::print): Do not display a file name when reading + formulas from standard input. + + * src/SpotWrapper.h, src/SpotWrapper.cc: Merge files from + Spot 0.2 (contributed by Alexandre Duret-Lutz); remove #pragma + definitions. + * src/ExternalTranslator.h, src/Makefile.in, src/translate.cc: + Merge changes from Spot 0.2 (contributed by Alexandre Duret-Lutz). + + * doc/lbtt.texi: Fix typo in URL of the FormulaOptions block + generator. Update version, add documentation and references about + support for Spot. Describe the new semantics of the --formulafile + command line option. + + * NEWS, README, configure.ac: Update. + + * Version 1.2.0 released. + +2005-08-18 Heikki Tauriainen + + * NEWS, README: Update to next version. + + * Version 1.1.3 released. + +2005-08-18 Heikki Tauriainen + + * src/TestOperations.cc (generateBuchiAutomaton): Do not + block interrupt signals while running a child process; if lbtt + is currently in the foreground, transfer the controlling terminal + to the child instead. + * configure.ac: Add tests for the getpgrp, tcgetpgrp and + tcsetpgrp functions. + +2005-08-17 Heikki Tauriainen + + * src/BitArray.h (BitArray::set, BitArray::clear): Do not + set/clear more bits than specified. + + * src/main.cc (main): Add a space before error message. + +2005-08-16 Heikki Tauriainen + + * src/main.cc: Include the sys/types.h header. + (translator_process): New global variable. + (abortHandler): If a translator process is still active when + aborting, terminate it. + * src/TestOperations.cc: Declare the external translator_process + variable. + (generateBuchiAutomaton): Replace the pid variable with the + translator_process variable. + Use setpgid instead of setsid. Always try to terminate the + subprocess if waitpid fails (not only in case of timeouts). + * configure.ac: Replace test for setsid with test for + setpgid. + +2005-08-15 Heikki Tauriainen + + * configure.ac: Update version and e-mail address. + Remove test for the slist header. + + * AUTHORS, doc/lbtt.texi: Update e-mail address. + + * src/LbttAlloc.h: Remove definition for the ALLOC macro. + Update copyright information. + * src/BuchiAutomaton.cc, src/BuchiProduct.cc, src/BuchiProduct.h, + src/Configuration.cc, src/Configuration.h, src/DispUtil.cc, + src/ExternalTranslator.h, src/FormulaRandomizer.cc, + src/FormulaRandomizer.h, src/Graph.h.in, src/IntervalList.cc, + src/IntervalList.h, src/Ltl-parse.yy, src/LtlFormula.cc, + src/LtlFormula.h, src/main.cc, src/NeverClaimAutomaton.cc, + src/NeverClaimAutomaton.h, src/PathEvaluator.cc, + src/PathEvaluator.h, src/Product.h, src/SccCollection.h, + src/SharedTestData.h, src/StatDisplay.cc, src/StatDisplay.h, + src/StateSpace.cc, src/StateSpaceRandomizer.cc, + src/StringUtil.cc, src/StringUtil.h, src/TestOperations.cc, + src/TestOperations.h, src/TestRoundInfo.h, src/TestStatistics.cc, + src/TestStatistics.h, src/UserCommandReader.cc, + src/UserCommandReader.h, src/UserCommands.cc, + src/UserCommands.h: + Remove uses of the ALLOC macro. + Update copyright information. + * src/BitArray.cc, src/BitArray.h, src/Bitset.h, + src/BuchiAutomaton.h, src/Config-lex.ll, src/Config-parse.yy, + src/DispUtil.h, src/EdgeContainer.h, src/Exception.h, + src/ExternalTranslator.cc, src/FormulaWriter.h, src/LbtWrapper.h, + src/NeverClaim-lex.ll, src/NeverClaim-parse.yy, + src/PathIterator.cc, src/PathIterator.h, src/Random.h, + src/SpinWrapper.cc, src/SpinWrapper.h, src/StateSpace.h, + src/StateSpaceProduct.h, src/StateSpaceRandomizer.h, + src/TempFsysName.cc, src/TempFsysName.h, src/translate.cc, + src/translate.h, src/TranslatorInterface.h: + Update copyright information. + + * src/Configuration.cc (Configuration::showCommandLineHelp): + Use the PACKAGE_BUGREPORT macro instead of a hard-coded + e-mail address. + 2004-08-02 Heikki Tauriainen * Version 1.1.2 released. diff --git a/lbtt/NEWS b/lbtt/NEWS index 5e168d954..1f63955f8 100644 --- a/lbtt/NEWS +++ b/lbtt/NEWS @@ -1,5 +1,5 @@ -lbtt NEWS -- history of user-visible changes. 02 Aug 2004 -Copyright (C) 2004 Heikki Tauriainen +lbtt NEWS -- history of user-visible changes. 30 Aug 2005 +Copyright (C) 2005 Heikki Tauriainen Permission is granted to anyone to make or distribute verbatim copies of this document as received, in any medium, provided that the @@ -10,7 +10,23 @@ Copyright (C) 2004 Heikki Tauriainen provided also that they carry prominent notices stating who last changed them. -Please send bug reports to . +Please send bug reports to . + +Version 1.2.0 + +* This release adds direct support (contributed by Alexandre Duret-Lutz) + for the LTL-to-Büchi translator distributed with the Spot model + checking library (available at ). + + lbtt 1.2.0 also supports reading input formulas from standard input + (by using the command-line option `--formulafile=-'; when reading input + formulas from an actual file, the filename needs to be different from + "-"). + +Version 1.1.3 + +* This release fixes build problems with GCC 4 and more job control + problems. Version 1.1.2 diff --git a/lbtt/README b/lbtt/README index f6e9927b6..3fdb6f194 100644 --- a/lbtt/README +++ b/lbtt/README @@ -1,4 +1,4 @@ -lbtt version 1.1.2 +lbtt version 1.2.0 ------------------ lbtt is a tool for testing programs that translate formulas diff --git a/lbtt/configure.ac b/lbtt/configure.ac index 863b97792..5cc044e60 100644 --- a/lbtt/configure.ac +++ b/lbtt/configure.ac @@ -1,8 +1,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.59]) -AC_INIT([lbtt], [1.1.2], [heikki.tauriainen@hut.fi]) -AC_REVISION([Revision: 1.6]) +AC_INIT([lbtt], [1.2.0], [heikki.tauriainen@tkk.fi]) +AC_REVISION([Revision: 1.8]) AC_CONFIG_SRCDIR([src/main.cc]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE @@ -124,52 +124,6 @@ AC_CHECK_HEADERS( # Checks for typedefs, structures, and compiler characteristics. -# Check for the availability of the slist header (an extension to the C++ -# Standard Template Library). (In GCC 3.x the header is in the ext/ -# subdirectory of the directory containing the standard C++ headers.) - -AC_MSG_CHECKING([for slist]) -for slist_header in slist ext/slist no; do - if test "${slist_header}" != no; then - AC_PREPROC_IFELSE( - [AC_LANG_SOURCE([[#include <${slist_header}>]])], - [break]) - fi -done - -# Try to determine the C++ namespace in which the class slist resides. -# (For example, GCC versions >= 3.1 put slist into the __gnu_cxx namespace.) - -if test "${slist_header}" != no; then - for slist_namespace in std __gnu_cxx error; do - if test "${slist_namespace}" != error; then - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#include <${slist_header}>]], - [[${slist_namespace}::slist s;]])], - [break]) - fi - done - if test "${slist_namespace}" != error; then - AC_MSG_RESULT([header <${slist_header}>, typename ${slist_namespace}::slist]) - AC_DEFINE( - [HAVE_SLIST], - [1], - [Define to 1 if you have the or header file.]) - AC_DEFINE_UNQUOTED( - [SLIST_NAMESPACE], - [${slist_namespace}], - [Define as the name of the C++ namespace containing slist.]) - AC_SUBST([INCLUDE_SLIST_HEADER], ["#include <${slist_header}>"]) - else - slist_header=no - fi -fi - -if test "${slist_header}" = no; then - AC_MSG_RESULT([no]) -fi - AC_LANG(C) AC_CHECK_TYPES( @@ -191,7 +145,7 @@ AC_C_INLINE # Checks for library functions. AC_CHECK_FUNCS( - [strchr strtod strtol strtoul strerror mkdir mkstemp open read write close popen pclose pipe fork execvp setsid getpid waitpid alarm sigaction sigprocmask sigemptyset sigaddset times sysconf], + [strchr strtod strtol strtoul strerror mkdir mkstemp open read write close popen pclose pipe fork execvp getpgrp setpgid tcgetpgrp tcsetpgrp getpid waitpid alarm sigaction sigprocmask sigemptyset sigaddset times sysconf], [], [AC_MSG_ERROR([missing one of the library functions required for compilation])]) AC_CHECK_FUNCS([strsignal isatty getopt_long]) diff --git a/lbtt/doc/lbtt.texi b/lbtt/doc/lbtt.texi index 1d0064048..06a9fd2f6 100644 --- a/lbtt/doc/lbtt.texi +++ b/lbtt/doc/lbtt.texi @@ -14,13 +14,13 @@ This file documents how to use the LTL-to-B@"uchi translator testbench @command{lbtt}. -Copyright @copyright{} 2004 Heikki Tauriainen +Copyright @copyright{} 2005 Heikki Tauriainen @ifinfo -@email{heikki.tauriainen@@hut.fi} +@email{heikki.tauriainen@@tkk.fi} @end ifinfo @ifnotinfo @ifnothtml -<@email{heikki.tauriainen@@hut.fi}> +<@email{heikki.tauriainen@@tkk.fi}> @end ifnothtml @end ifnotinfo @@ -64,12 +64,12 @@ under the above conditions for modified versions. @title @command{lbtt} @subtitle LTL-to-B@"uchi Translator Testbench -@subtitle @today, @command{lbtt} Versions 1.1.x -@author Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> +@subtitle @today, @command{lbtt} Versions 1.2.x +@author Heikki Tauriainen <@email{heikki.tauriainen@@tkk.fi}> @page @vskip 0pt plus 1filll -Copyright @copyright{} 2004 Heikki Tauriainen -<@email{heikki.tauriainen@@hut.fi}> +Copyright @copyright{} 2005 Heikki Tauriainen +<@email{heikki.tauriainen@@tkk.fi}> The latest version of this manual can be obtained from@* <@url{http://www.tcs.hut.fi/Software/lbtt/}>. @@ -103,8 +103,8 @@ under the above conditions for modified versions. for translating propositional linear temporal logic formulas into B@"uchi automata. -This is edition 1.1.0 of the @command{lbtt} documentation. This edition -applies to @command{lbtt} versions 1.1.x. +This is edition 1.2.0 of the @command{lbtt} documentation. This edition +applies to @command{lbtt} versions 1.2.x. @command{lbtt} is free software, you may change and redistribute it under the terms of the GNU General Public License. @command{lbtt} @@ -299,7 +299,7 @@ for more information. By default, all tests @command{lbtt} makes are based on randomly generated input. However, the LTL formulas used as input for the LTL-to-B@"uchi translators can be optionally given by the user by telling @command{lbtt} to -read LTL formulas from a file +read LTL formulas from a file or from standard input (@pxref{--formulafile,,@samp{--formulafile} command line option}). @cindex state space @@ -899,7 +899,7 @@ See also the web page@* @url{http://www.tcs.hut.fi/Software/lbtt/formulaoptions.php} @end ifinfo @ifnotinfo -<@uref{http://www.tcs.hut.fi/Software/lbtt/formulaoptions.php>} +<@uref{http://www.tcs.hut.fi/Software/lbtt/formulaoptions.php}> @end ifnotinfo for an interface to a small database for adjusting the operator priorities towards certain simple distributions. @@ -2121,12 +2121,13 @@ file @samp{config} in the current working directory. @item @anchor{--formulafile}--formulafile=@var{FILE-NAME} @vindex --formulafile -@cindex LTL formula, reading from a file +@cindex LTL formula, reading from a file or standard input @cindex file formats, formula input file for @command{lbtt} This option instructs @command{lbtt} to read the LTL formulas used in the tests -from a file instead of generating them randomly. The file should contain a -list of formulas, each on its own line in the file. The formulas can be -specified either in @command{lbtt}'s own prefix notation +from a file (or standard input) instead of generating them randomly. The +special filename @samp{-} refers to standard input. Each +input formula should be followed by a newline. The formulas can be specified +either in @command{lbtt}'s own prefix notation (@pxref{Format for LTL formulas}; also the infix notation used in output messages is supported) or in a variety of formats found in some LTL-to-B@"uchi translator implementations (Spin, LTL2BA, LTL2AUT, @@ -2522,8 +2523,8 @@ This option sets the priority for the logical ``exclusive or'' operator. Note also the @samp{--formulafile=@var{FILE-NAME}} option (@pxref{--formulafile,,@samp{--formulafile} option}), which can be used to -instruct @command{lbtt} to read LTL formulas from a file instead of generating -them randomly. +instruct @command{lbtt} to read LTL formulas from a file (or standard input) +instead of generating them randomly. @@ -2665,9 +2666,9 @@ Random state space generation parameters. @item Random LTL formula generation parameters (unless reading LTL formulas from -a file; @pxref{--formulafile,,@samp{--formulafile} command line option}). -This includes information about all enabled formula operators and their -priorities. When using the command line option +an external source; @pxref{--formulafile,,@samp{--formulafile} command line +option}). This includes information about all enabled formula operators and +their priorities. When using the command line option @samp{--showoperatordistribution} (@pxref{--showoperatordistribution,,@samp{--showoperatordistribution} option}), @command{lbtt} shows also the expected number of occurrence of each @@ -4025,6 +4026,25 @@ See <@uref{http://spinroot.com/spin/whatispin.html}> @end ifnotinfo for more information. + +@item +@cindex Spot +@ifnottex +Spot @ref{[DP04]} +@end ifnottex +@iftex +Spot [DP04] +@end iftex +--- a model checking library that includes a module for translating LTL +formulas into B@"uchi automata incorporating optimization techniques from +several different sources. See +@ifinfo +@url{http://spot.lip6.fr/} +@end ifinfo +@ifnotinfo +<@uref{http://spot.lip6.fr/}> +@end ifnotinfo +for more information. @end itemize To use @command{lbtt} for testing the LTL-to-B@"uchi translators included in @@ -4056,7 +4076,7 @@ LTL formula operators available for generating random LTL formulas with about which operators are supported, and then change the parameters in @command{lbtt}'s configuration file accordingly to disable the unsupported operators (or instruct @command{lbtt} to read the formulas from an external -file by invoking @command{lbtt} with the +source by invoking @command{lbtt} with the @ref{--formulafile,,@samp{--formulafile} command line option}). The @command{lbtt-translate} utility can also be invoked directly from the @@ -4087,6 +4107,13 @@ International Conference on Computer Aided Verification (CAV'99)}, volume 1633 of @i{Lecture Notes in Computer Science}, pages 249---260. Springer-Verlag, 1999. +@item @anchor{[DP04]} [DP04] +A.@: Duret-Lutz and D.@: Poitrenaud. SPOT: An Extensible Model Checking Library +Using Transition-Based Generalized B@"uchi Automata. In +@i{Proceedings of the 12th IEEE/ACM International Symposium on Modeling, +Analysis, and Simulation of Computer and Telecommunication Systems +(MASCOTS 2004)}, pages 76--83. IEEE Computer Society Press, 2004. + @item @anchor{[EH00]} [EH00] K.@: Etessami and G.@: Holzmann. Optimizing B@"uchi automata. In @i{Proceedings of the 11th International Conference on Concurrency Theory diff --git a/lbtt/src/BitArray.cc b/lbtt/src/BitArray.cc index 374e3ba39..3c840c6a1 100644 --- a/lbtt/src/BitArray.cc +++ b/lbtt/src/BitArray.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/BitArray.h b/lbtt/src/BitArray.h index ec725d7fc..68ee05285 100644 --- a/lbtt/src/BitArray.h +++ b/lbtt/src/BitArray.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -359,7 +359,10 @@ inline void BitArray::set(const unsigned long int bit_count) * * ------------------------------------------------------------------------- */ { - memset(static_cast(bits), 0xFF, (bit_count + 7) >> 3); + unsigned long int bsize = bit_count >> 3; + memset(static_cast(bits), 0xFF, bsize); + if ((bit_count & 0x07) != 0) + bits[bsize] |= (1 << (bit_count & 7)) - 1; } /* ========================================================================= */ @@ -390,7 +393,10 @@ inline void BitArray::clear(const unsigned long int bit_count) * * ------------------------------------------------------------------------- */ { - memset(static_cast(bits), 0, (bit_count + 7) >> 3); + unsigned long int bsize = bit_count >> 3; + memset(static_cast(bits), 0, bsize); + if ((bit_count & 0x07) != 0) + bits[bsize] &= ~((1 << (bit_count & 7)) - 1); } /* ========================================================================= */ diff --git a/lbtt/src/Bitset.h b/lbtt/src/Bitset.h index a97c40354..a7536d5ad 100644 --- a/lbtt/src/Bitset.h +++ b/lbtt/src/Bitset.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/BuchiAutomaton.cc b/lbtt/src/BuchiAutomaton.cc index 1b6c6d18e..318c75b67 100644 --- a/lbtt/src/BuchiAutomaton.cc +++ b/lbtt/src/BuchiAutomaton.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -288,27 +288,20 @@ void BuchiAutomaton::read(istream& input_stream) * to the interval [0...(number of states - 1)]. */ - map, ALLOC(size_type) > - state_number_map; + map state_number_map; pair state_mapping(0, 0); - pair, ALLOC(size_type) > - ::const_iterator, - bool> - state_finder; + pair::const_iterator, bool> state_finder; /* * Also the acceptance set numbers will be mapped to the interval * [0...(number of acceptance sets - 1)]. */ - map, ALLOC(unsigned long int) > - acceptance_set_map; + map acceptance_set_map; pair acceptance_set_mapping(0, 0); - pair, - ALLOC(unsigned long int) >::const_iterator, - bool> + pair::const_iterator, bool> acceptance_set_finder; /* diff --git a/lbtt/src/BuchiAutomaton.h b/lbtt/src/BuchiAutomaton.h index cec62163c..cfd212323 100644 --- a/lbtt/src/BuchiAutomaton.h +++ b/lbtt/src/BuchiAutomaton.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/BuchiProduct.cc b/lbtt/src/BuchiProduct.cc index c5f5c51d5..d3a6157b6 100644 --- a/lbtt/src/BuchiProduct.cc +++ b/lbtt/src/BuchiProduct.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,8 +28,7 @@ namespace Graph * *****************************************************************************/ -map< ::Ltl::LtlFormula*, BuchiProduct::SatisfiabilityMapping, - less< ::Ltl::LtlFormula*>, ALLOC(BuchiProduct::SatisfiabilityMapping) > +map< ::Ltl::LtlFormula*, BuchiProduct::SatisfiabilityMapping> BuchiProduct::sat_cache; @@ -73,8 +72,7 @@ bool BuchiProduct::synchronizable guard_1 = swap_guard; } - map, - ALLOC(SatisfiabilityMapping) >::iterator + map::iterator sat_cache_element = sat_cache.find(guard_1); if (sat_cache_element == sat_cache.end()) diff --git a/lbtt/src/BuchiProduct.h b/lbtt/src/BuchiProduct.h index 43200f521..c431d8d36 100644 --- a/lbtt/src/BuchiProduct.h +++ b/lbtt/src/BuchiProduct.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -142,18 +142,16 @@ private: * automata. */ - typedef map< ::Ltl::LtlFormula*, bool, /* Type definition for */ - less< ::Ltl::LtlFormula*>, /* storing information */ - ALLOC(bool) > /* about the */ - SatisfiabilityMapping; /* satisfiability of the + typedef map< ::Ltl::LtlFormula*, bool> /* Type definition for */ + SatisfiabilityMapping; /* storing information + * about the + * satisfiability of the * guards of product * transitions. */ static map< ::Ltl::LtlFormula*, /* Result cache for */ - SatisfiabilityMapping, /* satisfiability tests. */ - less< ::Ltl::LtlFormula*>, - ALLOC(SatisfiabilityMapping) > + SatisfiabilityMapping> /* satisfiability tests. */ sat_cache; }; diff --git a/lbtt/src/Config-lex.ll b/lbtt/src/Config-lex.ll index 145126a89..ea0af40f9 100644 --- a/lbtt/src/Config-lex.ll +++ b/lbtt/src/Config-lex.ll @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/Config-parse.yy b/lbtt/src/Config-parse.yy index 43e228fcb..b5539afbe 100644 --- a/lbtt/src/Config-parse.yy +++ b/lbtt/src/Config-parse.yy @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/Configuration.cc b/lbtt/src/Configuration.cc index 9061e3a1d..0dd3e99be 100644 --- a/lbtt/src/Configuration.cc +++ b/lbtt/src/Configuration.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -96,8 +96,7 @@ Configuration::~Configuration() * * ------------------------------------------------------------------------- */ { - for (vector - ::const_iterator it = algorithms.begin(); + for (vector::const_iterator it = algorithms.begin(); it != algorithms.end(); ++it) { for (vector::size_type p = 0; p <= it->num_parameters; ++p) @@ -223,7 +222,7 @@ void Configuration::read(int argc, char* argv[]) config_file_line_number = -1; typedef pair Parameter; - vector parameters; + vector parameters; /* * Preprocess the command line parameters. At this point only those special @@ -351,7 +350,7 @@ void Configuration::read(int argc, char* argv[]) * configuration file. */ - vector::const_iterator parameter; + vector::const_iterator parameter; try { @@ -655,7 +654,7 @@ void Configuration::read(int argc, char* argv[]) try { IntervalList algorithm_ids; - vector nonnumeric_algorithm_ids; + vector nonnumeric_algorithm_ids; string id_string = substituteInQuotedString(parameter->second, ",", "\n", INSIDE_QUOTES); @@ -664,14 +663,13 @@ void Configuration::read(int argc, char* argv[]) algorithms.size() - 1, &nonnumeric_algorithm_ids); - for (vector::iterator + for (vector::iterator id = nonnumeric_algorithm_ids.begin(); id != nonnumeric_algorithm_ids.end(); ++id) { *id = unquoteString(substituteInQuotedString(*id, "\n", ",")); - map, - ALLOC(unsigned long int) >::const_iterator id_finder + map::const_iterator id_finder = algorithm_names.find(*id); if (id_finder == algorithm_names.end()) throw ConfigurationException @@ -777,8 +775,7 @@ void Configuration::read(int argc, char* argv[]) bool unary_operator_allowed = false; - for (map, ALLOC(int) >::iterator - it = formula_options.symbol_priority.begin(); + for (map::iterator it = formula_options.symbol_priority.begin(); it != formula_options.symbol_priority.end(); ++it) { if (it->second == -1) @@ -804,7 +801,7 @@ void Configuration::read(int argc, char* argv[]) int total_long_unary_priority = 0; int total_binary_priority = 0; - for (map, ALLOC(int) >::const_iterator + for (map::const_iterator it = formula_options.symbol_priority.begin(); it != formula_options.symbol_priority.end(); ++it) { @@ -873,7 +870,7 @@ void Configuration::read(int argc, char* argv[]) k <= formula_options.formula_generator.max_size; k++) { - for (map, ALLOC(int) >::const_iterator + for (map::const_iterator op = formula_options.symbol_priority.begin(); op != formula_options.symbol_priority.end(); ++op) @@ -984,11 +981,9 @@ void Configuration::print(ostream& stream, int indent) const estream << '\n' + string(indent + 2, ' ') + "Implementations:\n"; - vector::size_type - algorithm_number = 0; + vector::size_type algorithm_number = 0; - for (vector - ::const_iterator a = algorithms.begin(); + for (vector::const_iterator a = algorithms.begin(); a != algorithms.end(); ++a) { @@ -1161,9 +1156,13 @@ void Configuration::print(ostream& stream, int indent) const number_of_available_variables == 1 ? "" : "s"); } else - estream << "Reading LTL formulas from `" - + global_options.formula_input_filename - + "'."; + { + estream << "Reading LTL formulas from "; + if (global_options.formula_input_filename == "-") + estream << "standard input."; + else + estream << "`" + global_options.formula_input_filename + "'."; + } estream << '\n' + string(indent + 4, ' '); @@ -1222,7 +1221,7 @@ void Configuration::print(ostream& stream, int indent) const bool first_printed = false; - for (map, ALLOC(int) >::const_iterator + for (map::const_iterator op = formula_options.symbol_priority.begin(); op != formula_options.symbol_priority.end(); ++op) @@ -1263,7 +1262,7 @@ void Configuration::print(ostream& stream, int indent) const int max_operators_per_line = (formula_options.symbol_distribution.empty() ? 7 : 6); - for (map, ALLOC(int) >::const_iterator op + for (map::const_iterator op = formula_options.symbol_priority.begin(); op != formula_options.symbol_priority.end(); ++op) @@ -1340,9 +1339,7 @@ void Configuration::print(ostream& stream, int indent) const /* ========================================================================= */ string Configuration::algorithmString - (vector::size_type - algorithm_id) const + (vector::size_type algorithm_id) const /* ---------------------------------------------------------------------------- * * Description: Constructs a string with an algorithm identifer and the name @@ -1387,7 +1384,8 @@ void Configuration::showCommandLineHelp(const char* program_name) " --enable=IMPLEMENTATION-ID[,IMPLEMENTATION-ID,...]\n" " Include implementation(s) into " "tests\n" - " --formulafile=FILE Read LTL formulas from FILE\n" + " --formulafile=FILE Read LTL formulas from FILE " + "(- = standard input)\n" " --globalmodelcheck Use global model checking in " "tests\n" " (equivalent to " @@ -1543,7 +1541,7 @@ void Configuration::showCommandLineHelp(const char* program_name) " --truthprobability=PROBABILITY\n" " Set truth probability of " "propositions (0.0--1.0)\n\n" - "Report bugs to .\n"; + "Report bugs to <" PACKAGE_BUGREPORT ">.\n"; } /* ========================================================================= */ @@ -1673,7 +1671,7 @@ void Configuration::registerAlgorithm : string("")), error); - vector params; + vector params; sliceString(unquoteString(substituteInQuotedString(parameters, " \t", "\n\n", OUTSIDE_QUOTES)), "\n", @@ -1702,7 +1700,7 @@ void Configuration::registerAlgorithm memcpy(static_cast(algorithm_information.parameters[0]), static_cast(path.c_str()), path.size() + 1); - for (vector::size_type p = 0; + for (vector::size_type p = 0; p < algorithm_information.num_parameters; ++p) { @@ -1875,9 +1873,9 @@ void Configuration::readInteractivity(const string& value) global_options.interactive = NEVER; global_options.handle_breaks = false; - vector modes; + vector modes; ::StringUtil::sliceString(value, ",", modes); - for (vector::const_iterator mode = modes.begin(); + for (vector::const_iterator mode = modes.begin(); mode != modes.end(); ++mode) { diff --git a/lbtt/src/Configuration.h b/lbtt/src/Configuration.h index 8d491eb8f..e7d9a1b56 100644 --- a/lbtt/src/Configuration.h +++ b/lbtt/src/Configuration.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,9 +62,9 @@ public: struct AlgorithmInformation; /* See below. */ string algorithmString /* Formats the the id */ - (vector::size_type/* the name of the */ - algorithm_id) const; /* algorithm into a + (vector::size_type /* of an algorithm and */ + algorithm_id) const; /* the name of the + * algorithm into a * string. */ @@ -311,11 +311,12 @@ public: * LTL formula symbols. */ - map, ALLOC(int) > /* Priorities for LTL */ - symbol_priority; /* formula symbols. */ + map symbol_priority; /* Priorities for LTL + * formula symbols. + */ - map, ALLOC(double) > /* Expected numbers of */ - symbol_distribution; /* occurrence for the + map symbol_distribution; /* Expected numbers of + * occurrence for the * different formula * operators. */ @@ -375,15 +376,15 @@ public: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - vector algorithms; /* information about the + vector algorithms; /* A vector containing + * information about the * algorithms used in * the tests. */ - map, /* Mapping between */ - ALLOC(unsigned long int) > /* algorithm names and */ - algorithm_names; /* their numeric + map algorithm_names; /* Mapping between + * algorithm names and + * their numeric * identifiers. */ @@ -511,14 +512,13 @@ private: OPT_STATESPACEPROPOSITIONS, OPT_STATESPACESIZE, OPT_TRUTHPROBABILITY}; - typedef map, double, /* Type definitions for */ - less >, /* the result cache used */ - ALLOC(double) > /* for computing the */ - ProbabilityMapElement; /* probability */ - typedef map, /* formula operators. */ - ALLOC(ProbabilityMapElement) > - ProbabilityMap; + typedef map, double> /* Type definitions for */ + ProbabilityMapElement; /* the result cache used */ + /* for computing the */ + typedef map /* probability */ + ProbabilityMap; /* distribution of LTL + * formula operators. + */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ diff --git a/lbtt/src/DispUtil.cc b/lbtt/src/DispUtil.cc index 4bfa825e8..39f0b767f 100644 --- a/lbtt/src/DispUtil.cc +++ b/lbtt/src/DispUtil.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,10 +32,10 @@ namespace DispUtil { -stack > /* output stream. */ - stream_formatting_stack; +stack > /* Stack for storing the */ + stream_formatting_stack; /* previous states of an + * output stream. + */ /* ========================================================================= */ void changeStreamFormatting diff --git a/lbtt/src/DispUtil.h b/lbtt/src/DispUtil.h index a7a8bc2f5..6f42e97d1 100644 --- a/lbtt/src/DispUtil.h +++ b/lbtt/src/DispUtil.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/EdgeContainer.h b/lbtt/src/EdgeContainer.h index 2ebbc089f..954eb8a36 100644 --- a/lbtt/src/EdgeContainer.h +++ b/lbtt/src/EdgeContainer.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/Exception.h b/lbtt/src/Exception.h index e6a88d98b..3ab2f3987 100644 --- a/lbtt/src/Exception.h +++ b/lbtt/src/Exception.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/ExternalTranslator.cc b/lbtt/src/ExternalTranslator.cc index 5f8da60ad..60cd80b9b 100644 --- a/lbtt/src/ExternalTranslator.cc +++ b/lbtt/src/ExternalTranslator.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/ExternalTranslator.h b/lbtt/src/ExternalTranslator.h index 12171674b..758ea67fe 100644 --- a/lbtt/src/ExternalTranslator.h +++ b/lbtt/src/ExternalTranslator.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -174,12 +174,13 @@ private: * objects. */ - stack > /* information. */ - temporary_file_objects; + stack > /* Stack for storing */ + temporary_file_objects; /* temporary file + * information. + */ friend class SpinWrapper; /* Friend declarations. */ + friend class SpotWrapper; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ diff --git a/lbtt/src/FormulaRandomizer.cc b/lbtt/src/FormulaRandomizer.cc index 712e269c1..964360c1b 100644 --- a/lbtt/src/FormulaRandomizer.cc +++ b/lbtt/src/FormulaRandomizer.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -92,7 +92,7 @@ LtlFormula* FormulaRandomizer::recGenerate(unsigned long int target_size) * * ------------------------------------------------------------------------- */ { - vector::const_iterator symbol_priority; + vector::const_iterator symbol_priority; LtlFormula* formula; long int x; diff --git a/lbtt/src/FormulaRandomizer.h b/lbtt/src/FormulaRandomizer.h index cf40472db..915625e58 100644 --- a/lbtt/src/FormulaRandomizer.h +++ b/lbtt/src/FormulaRandomizer.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -87,16 +87,16 @@ public: * `reset'. */ - const map, /* different atomic */ - ALLOC(unsigned long int) >& /* propositions */ - propositionStatistics() const; /* generated since the + const map& /* Get the numbers of */ + propositionStatistics() const; /* different atomic + * propositions + * generated since the * last call to `reset'. */ - const map, /* Get the numbers of */ - ALLOC(unsigned long int) >& /* different symbols */ - symbolStatistics() const; /* generated since the + const map& /* Get the numbers of */ + symbolStatistics() const; /* different symbols + * generated since the * last call to `reset'. */ @@ -124,21 +124,20 @@ private: typedef pair IntegerPair; - vector /* Operand symbols and */ + vector /* Operand symbols and */ propositional_symbol_priorities; /* their priorities in * random formulae. */ - vector /* Operators and their */ - short_formula_operators; /* priorities in random - * formulae of size - * two. + vector short_formula_operators; /* Operators and their + * priorities in random + * formulae of size two. */ - vector /* Operators and their */ - long_formula_operators; /* priorities in random - * formulae of size - * greater than two. + vector long_formula_operators; /* Operators and their + * priorities in random + * formulae of size greater + * than two. */ unsigned long int number_of_generated_formulas; /* Number of generated @@ -146,14 +145,15 @@ private: * last call to `reset'. */ - map, /* atomic propositions */ - ALLOC(unsigned long int) > /* generated since the */ - proposition_statistics; /* last call to `reset' */ + map /* Number of different */ + proposition_statistics; /* atomic propositions + * generated since the + * last call to `reset' + */ - map, /* Number of different */ - ALLOC(unsigned long int) > /* formula symbols */ - symbol_statistics; /* generated since the + map symbol_statistics; /* Number of different + * formula symbols + * generated since the * last call to `reset'. */ }; @@ -289,8 +289,7 @@ inline unsigned long int FormulaRandomizer::numberOfFormulas() const } /* ========================================================================= */ -inline const map, - ALLOC(unsigned long int) >& +inline const map& FormulaRandomizer::propositionStatistics() const /* ---------------------------------------------------------------------------- * @@ -308,7 +307,7 @@ FormulaRandomizer::propositionStatistics() const } /* ========================================================================= */ -inline const map, ALLOC(unsigned long int) >& +inline const map& FormulaRandomizer::symbolStatistics() const /* ---------------------------------------------------------------------------- * diff --git a/lbtt/src/FormulaWriter.h b/lbtt/src/FormulaWriter.h index 34b3c2c8b..feaf2121a 100644 --- a/lbtt/src/FormulaWriter.h +++ b/lbtt/src/FormulaWriter.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/Graph.h.in b/lbtt/src/Graph.h.in index 9b7c4a6b7..ec484f01f 100644 --- a/lbtt/src/Graph.h.in +++ b/lbtt/src/Graph.h.in @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -193,16 +193,16 @@ public: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ protected: - vector nodes; /* Nodes of the graph. + vector nodes; /* Nodes of the graph. * Derived classes can * access this vector * directly. */ public: - typedef typename /* Type definition for */ - vector::size_type /* the size of the */ - size_type; /* graph. The size can + typedef typename vector::size_type /* Type definition for */ + size_type; /* the size of the + * graph. The size can * be no greater than * the maximum size of * the vector containing @@ -214,8 +214,9 @@ public: * edges. */ - typedef deque /* Type definition for */ - Path; /* paths in a graph. */ + typedef deque Path; /* Type definition for + * paths in a graph. + */ typedef pair StateIdPair; /* Type definition for a * pair of state @@ -502,8 +503,7 @@ public: * *****************************************************************************/ -class EdgeList : public list::Edge*, - ALLOC(Graph::Edge*) > +class EdgeList : public list::Edge*> { public: EdgeList(); /* Constructor. */ @@ -518,14 +518,12 @@ public: * the end of the list. */ - list::Edge*, /* Functions for finding */ - ALLOC(Graph::Edge*) > /* an element in the */ - ::const_iterator /* list. */ - find(const Graph::Edge* edge) const; - - list::Edge*, - ALLOC(Graph::Edge*) > - ::iterator + list::Edge*>::const_iterator /* Functions for finding */ + find(const Graph::Edge* edge) const; /* an element in the + * list. + */ + + list::Edge*>::iterator find(const Graph::Edge* edge); }; @@ -540,8 +538,7 @@ public: #ifdef HAVE_SLIST -class EdgeSlist : public slist::Edge*, - ALLOC(Graph::Edge*) > +class EdgeSlist : public slist::Edge*> { public: EdgeSlist(); /* Constructor. */ @@ -557,14 +554,12 @@ public: * list. */ - slist::Edge*, /* Functions for finding */ - ALLOC(Graph::Edge*) > /* an element in the */ - ::const_iterator /* list. */ - find(const Graph::Edge* edge) const; + slist::Edge*>::const_iterator /* Functions for finding */ + find(const Graph::Edge* edge) const; /* an element in the + * list. + */ - slist::Edge*, - ALLOC(Graph::Edge*) > - ::iterator + slist::Edge*>::iterator find(const Graph::Edge* edge); }; @@ -578,8 +573,7 @@ public: * *****************************************************************************/ -class EdgeVector : public vector::Edge*, - ALLOC(Graph::Edge*) > +class EdgeVector : public vector::Edge*> { public: EdgeVector(); /* Constructor. */ @@ -595,15 +589,11 @@ public: * to edges. */ - vector::Edge*, /* Functions for finding */ - ALLOC(Graph::Edge*) > /* an element in the */ - ::const_iterator /* container. */ - find(const Graph::Edge* edge) - const; + vector::Edge*>::const_iterator /* Functions for finding */ + find(const Graph::Edge* edge) /* an element in the */ + const; /* container. */ - vector::Edge*, - ALLOC(Graph::Edge*) > - ::iterator + vector::Edge*>::iterator find(const Graph::Edge* edge); }; @@ -616,8 +606,7 @@ public: *****************************************************************************/ class EdgeSet : public set::Edge*, - Graph::Edge::ptr_less, - ALLOC(Graph::Edge*) > + Graph::Edge::ptr_less> { }; @@ -630,8 +619,7 @@ class EdgeSet : public set::Edge*, *****************************************************************************/ class EdgeMultiSet : public multiset::Edge*, - Graph::Edge::ptr_less, - ALLOC(Graph::Edge*) > + Graph::Edge::ptr_less> { }; @@ -757,7 +745,7 @@ Graph::Graph(const size_type initial_number_of_nodes) : { nodes.reserve(initial_number_of_nodes); - for (typename vector::iterator node = nodes.begin(); + for (typename vector::iterator node = nodes.begin(); node != nodes.end(); ++node) *node = new Node(); @@ -779,8 +767,7 @@ Graph::Graph(const Graph& graph) * ------------------------------------------------------------------------- */ { nodes.reserve(graph.nodes.size()); - for (typename vector::const_iterator - node = graph.nodes.begin(); + for (typename vector::const_iterator node = graph.nodes.begin(); node != graph.nodes.end(); ++node) nodes.push_back(new Node(**node)); } @@ -806,8 +793,7 @@ Graph& Graph::operator= clear(); nodes.reserve(graph.nodes.size()); - for (typename vector::const_iterator - node = graph.nodes.begin(); + for (typename vector::const_iterator node = graph.nodes.begin(); node != graph.nodes.end(); ++node) nodes.push_back(new Node(**node)); @@ -850,8 +836,7 @@ void Graph::clear() * * ------------------------------------------------------------------------- */ { - for (typename vector::reverse_iterator - node = nodes.rbegin(); + for (typename vector::reverse_iterator node = nodes.rbegin(); node != nodes.rend(); ++node) delete *node; @@ -997,8 +982,7 @@ Graph::stats() const result.first = nodes.size(); result.second = 0; - for (typename vector::const_iterator - node = nodes.begin(); + for (typename vector::const_iterator node = nodes.begin(); node != nodes.end(); ++node) result.second += (*node)->edges().size(); @@ -1032,7 +1016,7 @@ Graph::subgraphStats(const size_type index) const if (index >= s) throw NodeIndexException(); - stack > unprocessed_nodes; + stack > unprocessed_nodes; BitArray visited_nodes(s); visited_nodes.clear(s); @@ -1764,8 +1748,7 @@ inline void EdgeList::insert(Graph::Edge* edge) } /* ========================================================================= */ -inline list::Edge*, ALLOC(Graph::Edge*) > - ::const_iterator +inline list::Edge*>::const_iterator EdgeList::find(const Graph::Edge* edge) const /* ---------------------------------------------------------------------------- * @@ -1776,9 +1759,9 @@ EdgeList::find(const Graph::Edge* edge) const * between the actual values of the edges (not the * pointers). * - * Returns: A list::Edge*, ALLOC>::const_iterator + * Returns: A list::Edge*>::const_iterator * pointing to the edge in the list or - * list::Edge*, ALLOC>::end() if the edge is + * list::Edge*>::end() if the edge is * not found in the list. * * ------------------------------------------------------------------------- */ @@ -1793,7 +1776,7 @@ EdgeList::find(const Graph::Edge* edge) const } /* ========================================================================= */ -inline list::Edge*, ALLOC(Graph::Edge*) >::iterator +inline list::Edge*>::iterator EdgeList::find(const Graph::Edge* edge) /* ---------------------------------------------------------------------------- * @@ -1804,9 +1787,9 @@ EdgeList::find(const Graph::Edge* edge) * between the actual values of the edges (not the * pointers). * - * Returns: A list::Edge*, ALLOC>::iterator pointing + * Returns: A list::Edge*>::iterator pointing * to the edge in the list or - * list::Edge*, ALLOC>::end() if the edge is + * list::Edge*>::end() if the edge is * not found in the list. * * ------------------------------------------------------------------------- */ @@ -1875,8 +1858,7 @@ inline void EdgeSlist::insert(Graph::Edge* edge) } /* ========================================================================= */ -inline slist::Edge*, ALLOC(Graph::Edge*) > - ::const_iterator +inline slist::Edge*>::const_iterator EdgeSlist::find(const Graph::Edge* edge) const /* ---------------------------------------------------------------------------- * @@ -1887,9 +1869,9 @@ EdgeSlist::find(const Graph::Edge* edge) const * between the actual values of the edges (not the * pointers). * - * Returns: A slist::Edge*, ALLOC>::const_iterator + * Returns: A slist::Edge*>::const_iterator * pointing to the edge in the list or - * slist::Edge*, ALLOC>::end() if the edge + * slist::Edge*>::end() if the edge * is not found in the list. * * ------------------------------------------------------------------------- */ @@ -1904,8 +1886,7 @@ EdgeSlist::find(const Graph::Edge* edge) const } /* ========================================================================= */ -inline slist::Edge*, ALLOC(Graph::Edge*) > - ::iterator +inline slist::Edge*>::iterator EdgeSlist::find(const Graph::Edge* edge) /* ---------------------------------------------------------------------------- * @@ -1916,9 +1897,9 @@ EdgeSlist::find(const Graph::Edge* edge) * between the actual values of the edges (not the * pointers). * - * Returns: A slist::Edge*, ALLOC>::iterator + * Returns: A slist::Edge*>::iterator * pointing to the edge in the list or - * slist::Edge*, ALLOC>::end() if the edge + * slist::Edge*>::end() if the edge * is not found in the list. * * ------------------------------------------------------------------------- */ @@ -1987,8 +1968,7 @@ inline void EdgeVector::insert(Graph::Edge* edge) } /* ========================================================================= */ -inline vector::Edge*, ALLOC(Graph::Edge*) > - ::const_iterator +inline vector::Edge*>::const_iterator EdgeVector::find(const Graph::Edge* edge) const /* ---------------------------------------------------------------------------- * @@ -1999,9 +1979,9 @@ EdgeVector::find(const Graph::Edge* edge) const * between the actual values of the edges (not the * pointers). * - * Returns: A vector::Edge*, ALLOC>::const_iterator + * Returns: A vector::Edge*>::const_iterator * pointing to the edge in the container or - * vector::Edge*, ALLOC>::end() if the + * vector::Edge*>::end() if the * edge is not found in the container. * * ------------------------------------------------------------------------- */ @@ -2016,8 +1996,7 @@ EdgeVector::find(const Graph::Edge* edge) const } /* ========================================================================= */ -inline vector::Edge*, ALLOC(Graph::Edge*) > - ::iterator +inline vector::Edge*>::iterator EdgeVector::find(const Graph::Edge* edge) /* ---------------------------------------------------------------------------- * @@ -2028,9 +2007,9 @@ EdgeVector::find(const Graph::Edge* edge) * between the actual values of the edges (not the * pointers). * - * Returns: A vector::Edge*, ALLOC>::iterator + * Returns: A vector::Edge*>::iterator * pointing to the edge in the container or - * vector::Edge*, ALLOC>::end() if the edge + * vector::Edge*>::end() if the edge * is not found in the container. * * ------------------------------------------------------------------------- */ diff --git a/lbtt/src/IntervalList.cc b/lbtt/src/IntervalList.cc index 52a5ebb35..d35c9b66e 100644 --- a/lbtt/src/IntervalList.cc +++ b/lbtt/src/IntervalList.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 2004 - * Heikki Tauriainen + * Copyright (C) 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ void IntervalList::merge(unsigned long int min, unsigned long int max) if (min > max) return; - list::iterator interval; + list::iterator interval; for (interval = intervals.begin(); interval != intervals.end() && interval->second + 1 < min; ++interval) @@ -68,14 +68,14 @@ void IntervalList::merge(unsigned long int min, unsigned long int max) if (interval->second < max) { interval->second = max; - list::iterator interval2 = interval; + list::iterator interval2 = interval; ++interval2; while (interval2 != intervals.end() && interval2->first <= interval->second + 1) { if (interval->second < interval2->second) interval->second = interval2->second; - list::iterator interval_to_erase = interval2; + list::iterator interval_to_erase = interval2; ++interval2; intervals.erase(interval_to_erase); } @@ -97,7 +97,7 @@ void IntervalList::remove(unsigned long int min, unsigned long int max) if (min > max) return; - list::iterator interval; + list::iterator interval; for (interval = intervals.begin(); interval != intervals.end() && interval->second < min; ++interval) @@ -126,7 +126,7 @@ void IntervalList::remove(unsigned long int min, unsigned long int max) } else /* min <= imin <= imax <= max */ { - list::iterator interval_to_erase = interval; + list::iterator interval_to_erase = interval; ++interval; intervals.erase(interval_to_erase); } @@ -148,7 +148,7 @@ bool IntervalList::covers(unsigned long int min, unsigned long int max) const if (min > max) return true; /* empty interval is always covered */ - list::const_iterator interval; + list::const_iterator interval; for (interval = intervals.begin(); interval != intervals.end() && min > interval->second; ++interval) @@ -173,8 +173,7 @@ string IntervalList::toString() const * ------------------------------------------------------------------------- */ { string s; - for (list::const_iterator - interval = intervals.begin(); + for (list::const_iterator interval = intervals.begin(); interval != intervals.end(); ++interval) { diff --git a/lbtt/src/IntervalList.h b/lbtt/src/IntervalList.h index 2a55b9641..b8276bf34 100644 --- a/lbtt/src/IntervalList.h +++ b/lbtt/src/IntervalList.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2004 - * Heikki Tauriainen + * Copyright (C) 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -74,13 +74,15 @@ public: unsigned long int operator++(int); /* Postfix increment. */ private: - const list* /* The interval list */ - interval_list; /* associated with the */ - /* iterator. */ + const list* interval_list; /* The interval list + * associated with the + * iterator. + */ - list /* An iterator pointing */ - ::const_iterator interval; /* at the current */ - /* interval list. */ + list::const_iterator interval; /* An iterator pointing at + * the current intrerval + * list. + */ unsigned long int element; /* Element currently * pointed to by the @@ -143,8 +145,7 @@ public: * iterators. */ - typedef list /* Size type. */ - ::size_type size_type; + typedef list::size_type size_type; /* Size type. */ size_type size() const; /* Tell the number of * disjoint intervals in @@ -170,7 +171,7 @@ public: */ private: - list intervals; /* List of intervals. */ + list intervals; /* List of intervals. */ friend class const_iterator; }; diff --git a/lbtt/src/LbtWrapper.h b/lbtt/src/LbtWrapper.h index 03a2d8a80..bd889b18d 100644 --- a/lbtt/src/LbtWrapper.h +++ b/lbtt/src/LbtWrapper.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/LbttAlloc.h b/lbtt/src/LbttAlloc.h index 2f8ef8cc3..c98ceaa07 100644 --- a/lbtt/src/LbttAlloc.h +++ b/lbtt/src/LbttAlloc.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,8 +22,6 @@ #include -#define ALLOC(typename) allocator - #ifdef HAVE_OBSTACK_H /* GNU libc 2.3.2's copy of obstack.h uses a definition of __INT_TO_PTR diff --git a/lbtt/src/Ltl-parse.yy b/lbtt/src/Ltl-parse.yy index d558db0b7..805d5965b 100644 --- a/lbtt/src/Ltl-parse.yy +++ b/lbtt/src/Ltl-parse.yy @@ -1,6 +1,6 @@ /* - * Copyright (C) 2004 - * Heikki Tauriainen + * Copyright (C) 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -44,9 +44,9 @@ static LtlFormula* result; /* This variable stores the * ltl_parse. */ -static std::set, /* Intermediate results. */ - ALLOC(LtlFormula*) > /* (This set is used */ - intermediate_results; /* for keeping track of +static std::set intermediate_results; /* Intermediate results. + * (This set is used + * for keeping track of * the subformulas of a * partially constructed * formula in case the @@ -426,8 +426,8 @@ LtlFormula* parseFormula(istream& stream) } catch (...) { - for (std::set, ALLOC(LtlFormula*) > - ::const_iterator f = intermediate_results.begin(); + for (std::set::const_iterator + f = intermediate_results.begin(); f != intermediate_results.end(); ++f) LtlFormula::destruct(*f); diff --git a/lbtt/src/LtlFormula.cc b/lbtt/src/LtlFormula.cc index 2fa3d4723..faad76f56 100644 --- a/lbtt/src/LtlFormula.cc +++ b/lbtt/src/LtlFormula.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,9 +24,8 @@ namespace Ltl { -set /* LTL formulae. */ - LtlFormula::formula_storage; +set /* Shared storage for */ + LtlFormula::formula_storage; /* LTL formulae. */ unsigned long int /* Upper limit for the */ LtlFormula::eval_proposition_id_limit; /* atomic proposition @@ -140,12 +139,10 @@ public: */ private: - stack > + stack > formula_stack; - stack > - negation_stack; + stack > negation_stack; NnfConverter(const NnfConverter&); /* Prevent copying and */ NnfConverter& operator=(const NnfConverter&); /* assignment of @@ -196,8 +193,7 @@ class SubformulaCollector public: SubformulaCollector /* Constructor. */ (stack >& + deque >& result_stack); ~SubformulaCollector(); /* Destructor. */ @@ -209,8 +205,7 @@ public: private: stack >& + deque >& subformula_stack; SubformulaCollector(const SubformulaCollector&); /* Prevent copying and */ @@ -382,9 +377,7 @@ inline void FormulaSizeCounter::operator()(const LtlFormula*, int) /* ========================================================================= */ inline SubformulaCollector::SubformulaCollector - (stack >& - result_stack) : + (stack >& result_stack) : subformula_stack(result_stack) /* ---------------------------------------------------------------------------- * @@ -766,9 +759,7 @@ unsigned long int LtlFormula::size() const /* ========================================================================= */ void LtlFormula::collectSubformulae - (stack >& - result_stack) const + (stack >& result_stack) const /* ---------------------------------------------------------------------------- * * Description: Collects the subformulae of a LtlFormula into a stack. After diff --git a/lbtt/src/LtlFormula.h b/lbtt/src/LtlFormula.h index a9209be7c..a53303ba7 100644 --- a/lbtt/src/LtlFormula.h +++ b/lbtt/src/LtlFormula.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -261,21 +261,18 @@ private: void collectSubformulae /* Builds a stack of the */ (stack >& + deque >& /* formula. */ result_stack) const; typedef pair /* Shorthand type */ FormulaStackElement; /* definitions for the */ typedef stack >/* checking algorithm. */ - FormulaStack; + deque > /* satisfiability */ + FormulaStack; /* checking algorithm. */ typedef pair TableauStackElement; typedef stack > + deque > TableauStack; bool sat_eval /* Helper function for */ @@ -285,9 +282,8 @@ private: * formula. */ - static set /* LTL formulae. */ - formula_storage; + static set /* Shared storage for */ + formula_storage; /* LTL formulae. */ static unsigned long int /* Upper limit for the */ eval_proposition_id_limit; /* atomic proposition @@ -1314,8 +1310,7 @@ inline LtlFormula& LtlFormula::insertToStorage(LtlFormula* f) * * ------------------------------------------------------------------------- */ { - set::iterator inserter - = formula_storage.find(f); + set::iterator inserter = formula_storage.find(f); if (inserter != formula_storage.end()) { delete f; diff --git a/lbtt/src/Makefile.am b/lbtt/src/Makefile.am index 6ca8f0e46..bc244ba83 100644 --- a/lbtt/src/Makefile.am +++ b/lbtt/src/Makefile.am @@ -79,6 +79,8 @@ lbtt_translate_SOURCES = \ NeverClaimAutomaton.cc \ SpinWrapper.h \ SpinWrapper.cc \ + SpotWrapper.h \ + SpotWrapper.cc \ StringUtil.h \ StringUtil.cc \ TempFsysName.h \ diff --git a/lbtt/src/NeverClaim-lex.ll b/lbtt/src/NeverClaim-lex.ll index e59a56ea6..54f8d3cb9 100644 --- a/lbtt/src/NeverClaim-lex.ll +++ b/lbtt/src/NeverClaim-lex.ll @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/NeverClaim-parse.yy b/lbtt/src/NeverClaim-parse.yy index 3fd0e54df..bdbdfb4da 100644 --- a/lbtt/src/NeverClaim-parse.yy +++ b/lbtt/src/NeverClaim-parse.yy @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/NeverClaimAutomaton.cc b/lbtt/src/NeverClaimAutomaton.cc index 57494c7c5..122f389b7 100644 --- a/lbtt/src/NeverClaimAutomaton.cc +++ b/lbtt/src/NeverClaimAutomaton.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -62,8 +62,7 @@ void NeverClaimAutomaton::clear() * * ------------------------------------------------------------------------- */ { - for (vector::iterator - state = state_list.begin(); + for (vector::iterator state = state_list.begin(); state != state_list.end(); ++state) delete (*state); @@ -154,8 +153,7 @@ void NeverClaimAutomaton::write(const char* output_filename) * `-1'. */ - for (vector::const_iterator - state = state_list.begin(); + for (vector::const_iterator state = state_list.begin(); state != state_list.end(); ++state) { @@ -163,7 +161,7 @@ void NeverClaimAutomaton::write(const char* output_filename) + ((*state)->initial() ? "1" : "0") + ' ' + ((*state)->accepting() ? "0 " : "") + "-1\n"; - for (multimap, ALLOC(Cstr*) >::const_iterator + for (multimap::const_iterator transition = (*state)->transitions().begin(); transition != (*state)->transitions().end(); ++transition) { @@ -268,7 +266,7 @@ NeverClaimAutomaton::StateInfo::~StateInfo() * * ------------------------------------------------------------------------- */ { - for (multimap, ALLOC(Cstr*) >::const_iterator + for (multimap::const_iterator transition = state_transitions.begin(); transition != state_transitions.end(); ++transition) diff --git a/lbtt/src/NeverClaimAutomaton.h b/lbtt/src/NeverClaimAutomaton.h index 0ab31b905..49cf37324 100644 --- a/lbtt/src/NeverClaimAutomaton.h +++ b/lbtt/src/NeverClaimAutomaton.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -93,12 +93,13 @@ public: */ private: - vector /* States of the */ - state_list; /* automaton. */ + vector state_list; /* States of the automaton. + */ - map, /* Mapping from state */ - ALLOC(StateInfo*) > /* labels to the states */ - label_mapping; /* itself. */ + map label_mapping; /* Mapping from state + * labels to the states + * itself. + */ StateInfo* current_state; /* Pointer to the state * introduced most recently @@ -142,9 +143,9 @@ public: /* of the state. */ - const multimap, /* Returns the labels of */ - ALLOC(Cstr*) >& /* the state's successor */ - transitions() const; /* states, including the + const multimap& transitions() const; /* Returns the labels of + * the state's successor + * states, including the * conditions controlling * the enabledness of the * transition. @@ -174,8 +175,8 @@ private: * accepting state? */ - multimap, ALLOC(Cstr*) > /* Labels of the state's */ - state_transitions; /* successors, including + multimap state_transitions; /* Labels of the state's + * successors, including * the guard formulae * controlling the * enabledness of the @@ -373,9 +374,7 @@ inline bool& NeverClaimAutomaton::StateInfo::accepting() } /* ========================================================================= */ -inline const multimap, - ALLOC(NeverClaimAutomaton::Cstr*) >& +inline const multimap& NeverClaimAutomaton::StateInfo::transitions() const /* ---------------------------------------------------------------------------- * diff --git a/lbtt/src/PathEvaluator.cc b/lbtt/src/PathEvaluator.cc index 936d1ff98..7737ae251 100644 --- a/lbtt/src/PathEvaluator.cc +++ b/lbtt/src/PathEvaluator.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,9 +49,8 @@ void PathEvaluator::reset() current_loop_state = 0; path_states.clear(); - for (map::iterator it - = eval_info.begin(); + for (map::iterator + it = eval_info.begin(); it != eval_info.end(); ++it) delete it->second; @@ -131,8 +130,7 @@ bool PathEvaluator::evaluate current_formula = &formula; current_path = &statespace; - map, ALLOC(StateSpace::size_type) > ordering; + map ordering; StateSpace::size_type state = statespace.initialState(); StateSpace::size_type state_count = 0; @@ -176,9 +174,7 @@ bool PathEvaluator::eval() * * ------------------------------------------------------------------------- */ { - stack > - subformula_stack; + stack > subformula_stack; const LtlFormula* f; BitArray* val; diff --git a/lbtt/src/PathEvaluator.h b/lbtt/src/PathEvaluator.h index 794e7ea59..4d361d8f8 100644 --- a/lbtt/src/PathEvaluator.h +++ b/lbtt/src/PathEvaluator.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -105,15 +105,15 @@ private: * the current path. */ - vector /* between states of the */ - path_states; /* path and the states + vector path_states; /* Correspondence + * between states of the + * path and the states * of the current state * space. */ map /* truth values of the */ + LtlFormula::ptr_less> /* truth values of the */ eval_info; /* subformulae of the * formula to be * evaluated. diff --git a/lbtt/src/PathIterator.cc b/lbtt/src/PathIterator.cc index 81c939512..6489b7b1b 100644 --- a/lbtt/src/PathIterator.cc +++ b/lbtt/src/PathIterator.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/PathIterator.h b/lbtt/src/PathIterator.h index e194c60f2..28e02a2f5 100644 --- a/lbtt/src/PathIterator.h +++ b/lbtt/src/PathIterator.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/Product.h b/lbtt/src/Product.h index 9a856cadf..041ff5d36 100644 --- a/lbtt/src/Product.h +++ b/lbtt/src/Product.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -393,8 +393,8 @@ public: typedef ProductEdgeCollection EdgeContainerType; /* required for making */ /* Product */ struct PathElement; /* suitable for */ - typedef deque /* instantiating the */ - Path; /* SccCollection + typedef deque Path; /* instantiating the + * SccCollection * template (see * SccCollection.h). */ @@ -419,9 +419,7 @@ private: Graph::Path >& cycle, /* segment of the cycle */ size_type source_state_id, Edge transition, /* in a witness for the */ const size_type root_id, /* nonemptiness of the */ - const map, - ALLOC(PathElement) >& + const map& /* product. */ predecessor) const; Operations operations; /* Operations for @@ -655,9 +653,10 @@ protected: * acceptance sets. */ - typedef deque /* the above */ - AcceptanceStack; /* associations. */ + typedef deque /* Stack formed from */ + AcceptanceStack; /* the above + * associations. + */ AcceptanceStack acceptance_stack; /* Stack for storing the * dfs numbers of roots @@ -877,9 +876,9 @@ private: * reachable. */ - set, /* Set of states from */ - ALLOC(size_type) > /* which an accepting */ - reachability_info; /* component is known to + set reachability_info; /* Set of states from + * which an accepting + * component is known to * be reachable in the * product. */ @@ -928,9 +927,9 @@ public: ~AcceptingComponentFinder(); /* Destructor. */ - typedef set, /* Type definition for */ - ALLOC(size_type) > /* the set of product */ - SccType; /* state identifiers in + typedef set SccType; /* Type definition for + * the set of product + * state identifiers in * an accepting * strongly connected * component. @@ -1369,10 +1368,9 @@ void Product::findWitness unsigned long int number_of_collected_acceptance_sets = collected_acceptance_sets.count(number_of_acceptance_sets); - deque search_queue; - set, ALLOC(size_type) > visited; - map, ALLOC(PathElement) > - shortest_path_predecessor; + deque search_queue; + set visited; + map shortest_path_predecessor; size_type bfs_root = search_start_state; @@ -1475,8 +1473,7 @@ void Product::addCycleSegment (pair::Path, Graph::Path>& cycle, size_type source_state_id, Edge transition, const size_type root_id, - const map, ALLOC(PathElement) >& - predecessor) const + const map& predecessor) const /* ---------------------------------------------------------------------------- * * Description: Helper function for constructing a segment of an accepting diff --git a/lbtt/src/Random.h b/lbtt/src/Random.h index 0096e64a4..bd69ec27c 100644 --- a/lbtt/src/Random.h +++ b/lbtt/src/Random.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/SccCollection.h b/lbtt/src/SccCollection.h index ea31ac97f..32c5fc294 100644 --- a/lbtt/src/SccCollection.h +++ b/lbtt/src/SccCollection.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -195,10 +195,8 @@ public: /* default assignment operator */ - typedef set, /* a set of node id's. */ - ALLOC(typename GraphType::size_type) > - SccType; + typedef set /* Type definition for */ + SccType; /* a set of node id's. */ const SccType& operator()() const; /* Returns the set of node * identifiers in a @@ -338,10 +336,10 @@ public: /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ typedef map, /* identifiers and the */ - ALLOC(typename GraphType::size_type) >/* order in which they */ - DfsOrdering; /* were encountered in + typename GraphType::size_type> /* mapping between node */ + DfsOrdering; /* identifiers and the + * order in which they + * were encountered in * the search for * strongly connected * components. @@ -433,18 +431,19 @@ public: typename GraphType::size_type lowlink; }; - deque /* backtracking stack. */ - node_stack; + deque node_stack; /* Depth-first search + * backtracking stack. + */ + NodeStackElement* current_node; /* Pointer to the top * element of the * backtracking stack. */ - deque /* collecting the nodes */ - scc_stack; /* in a strongly + deque scc_stack; /* Stack used for + * collecting the nodes + * in a strongly * connected component, * excluding the root * nodes of the @@ -998,8 +997,7 @@ void SccCollection::iterator::getPath * when exiting from this function). */ - typename deque::const_iterator - n = node_stack.begin(); + typename deque::const_iterator n = node_stack.begin(); if (n != node_stack.end()) { for (++n; n != node_stack.end(); ++n) diff --git a/lbtt/src/SharedTestData.h b/lbtt/src/SharedTestData.h index 6403c52d4..6820d310d 100644 --- a/lbtt/src/SharedTestData.h +++ b/lbtt/src/SharedTestData.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -42,14 +42,14 @@ extern TestRoundInfo round_info; /* Data structure for * round. */ -extern vector /* implementation. */ - test_results; - -extern vector /* statistics for each */ - final_statistics; /* implementation. */ +extern vector test_results; /* Test results for each + * implementation. + */ +extern vector final_statistics; /* Overall test + * statistics for each + * implementation. + */ } #endif /* !SHAREDTESTDATA_H */ diff --git a/lbtt/src/SpinWrapper.cc b/lbtt/src/SpinWrapper.cc index 74a09c887..f98d59792 100644 --- a/lbtt/src/SpinWrapper.cc +++ b/lbtt/src/SpinWrapper.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/SpinWrapper.h b/lbtt/src/SpinWrapper.h index edbd0b6b1..1e4a7ed52 100644 --- a/lbtt/src/SpinWrapper.h +++ b/lbtt/src/SpinWrapper.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/SpotWrapper.cc b/lbtt/src/SpotWrapper.cc new file mode 100644 index 000000000..03731763e --- /dev/null +++ b/lbtt/src/SpotWrapper.cc @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2003, 2004 + * Heikki Tauriainen + * + * Derived from SpinWrapper.cc by Alexandre Duret-Lutz . + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include "Exception.h" +#include "FormulaWriter.h" +#include "NeverClaimAutomaton.h" +#include "SpotWrapper.h" + +/****************************************************************************** + * + * Definitions for operator symbols specific to Spot. + * + *****************************************************************************/ + +const char SpotWrapper::SPOT_AND[] = "&&"; +const char SpotWrapper::SPOT_OR[] = "||"; +const char SpotWrapper::SPOT_XOR[] = "^"; + + + +/****************************************************************************** + * + * Function definitions for class SpotWrapper. + * + *****************************************************************************/ + +/* ========================================================================= */ +void SpotWrapper::translateFormula + (const ::Ltl::LtlFormula& formula, string& translated_formula) +/* ---------------------------------------------------------------------------- + * + * Description: Translates an LtlFormula into a string which contains the + * formula in the input syntax of Spot. + * + * Arguments: formula -- The LtlFormula to be translated. + * translated_formula -- A reference to a string for storing + * the results. + * + * Returns: Nothing. The result of the translation can be found in + * the string `translated_formula'. + * + * ------------------------------------------------------------------------- */ +{ + using namespace Ltl; + +#ifdef HAVE_SSTREAM + ostringstream translated_formula_stream; +#else + ostrstream translated_formula_stream; +#endif /* HAVE_SSTREAM */ + Exceptional_ostream estream(&translated_formula_stream, ios::goodbit); + + FormulaWriter, + ConstantWriter, + AtomWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + WriterErrorReporter, + WriterErrorReporter, + WriterErrorReporter> + fw(estream); + + formula.traverse(fw, LTL_PREORDER | LTL_INORDER | LTL_POSTORDER); + + translated_formula = translated_formula_stream.str(); +#ifndef HAVE_SSTREAM + translated_formula_stream.freeze(0); +#endif /* HAVE_SSTREAM */ +} diff --git a/lbtt/src/SpotWrapper.h b/lbtt/src/SpotWrapper.h new file mode 100644 index 000000000..206c5f601 --- /dev/null +++ b/lbtt/src/SpotWrapper.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2003, 2004 + * Heikki Tauriainen + * + * Derived from SpinWrapper.h by Alexandre Duret-Lutz . + * + * This program 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. + * + * This program 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SPOTWRAPPER_H +#define SPOTWRAPPER_H + +#include +#include +#include "ExternalTranslator.h" +#include "LtlFormula.h" + +/****************************************************************************** + * + * Interface class for Spot. + * + *****************************************************************************/ + +class SpotWrapper : public ExternalTranslator +{ +public: + SpotWrapper(); /* Constructor. */ + + ~SpotWrapper(); /* Destructor. */ + + void translateFormula /* Translates a formula */ + (const ::Ltl::LtlFormula& formula, /* into a Büchi */ + string& translated_formula); /* automaton. */ + + /* `formatInput' inherited from ExternalTranslator */ + + string commandLine /* Prepares the command */ + (const string& input_filename, /* line for executing */ + const string& output_filename); /* Spot. */ + + /* `execSuccess' inherited from ExternalTranslator */ + + void parseAutomaton /* Translates the output */ + (const string& input_filename, /* of the translation */ + const string& output_filename); /* algorithm into lbtt + * format. + */ + +private: + SpotWrapper(const SpotWrapper&); /* Prevent copying and */ + SpotWrapper& operator=(const SpotWrapper&); /* assignment of + * SpotWrapper objects. + */ + + static const char SPOT_AND[]; /* Symbols for */ + static const char SPOT_OR[]; /* operators. */ + static const char SPOT_XOR[]; +}; + + + +/****************************************************************************** + * + * Inline function definitions for class SpotWrapper. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline SpotWrapper::SpotWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class SpotWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline SpotWrapper::~SpotWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class SpotWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline string SpotWrapper::commandLine + (const string& input_filename, const string&) +/* ---------------------------------------------------------------------------- + * + * Description: Prepares the command line for Spot. + * + * Arguments: input_filename -- Name of the input file. + * The other argument is only needed for supporting the + * ExternalTranslator interface; the output will be written to + * the filename stored in `command_line_arguments[4]'. + * + * Returns: The command line string. + * + * ------------------------------------------------------------------------- */ +{ + return (string(" ") + input_filename + + " >" + string(command_line_arguments[4])); +} + +/* ========================================================================= */ +inline void SpotWrapper::parseAutomaton(const string&, const string&) +/* ---------------------------------------------------------------------------- + * + * Description: Dummy function which is needed to support the + * ExternalTranslator interface. + * + * Arguments: References to two constant strings. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +#endif /* !SPOTWRAPPER_H */ diff --git a/lbtt/src/StatDisplay.cc b/lbtt/src/StatDisplay.cc index 8d6250626..8f8b772d0 100644 --- a/lbtt/src/StatDisplay.cc +++ b/lbtt/src/StatDisplay.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -78,8 +78,7 @@ void printStatTableHeader(ostream& stream, int indent) /* ========================================================================= */ void printBuchiAutomatonStats (ostream& stream, int indent, - vector::size_type - algorithm, + vector::size_type algorithm, int result_id) /* ---------------------------------------------------------------------------- * @@ -173,8 +172,7 @@ void printBuchiAutomatonStats /* ========================================================================= */ void printProductAutomatonStats (ostream& stream, int indent, - vector::size_type - algorithm, + vector::size_type algorithm, int result_id) /* ---------------------------------------------------------------------------- * @@ -264,8 +262,7 @@ void printProductAutomatonStats /* ========================================================================= */ void printAcceptanceCycleStats (ostream& stream, int indent, - vector::size_type - algorithm, + vector::size_type algorithm, int result_id) /* ---------------------------------------------------------------------------- * @@ -350,8 +347,7 @@ void printAcceptanceCycleStats /* ========================================================================= */ void printConsistencyCheckStats (ostream& stream, int indent, - vector::size_type - algorithm) + vector::size_type algorithm) /* ---------------------------------------------------------------------------- * * Description: Displays information about the consistency check result for @@ -462,8 +458,7 @@ void printCrossComparisonStats alg_1_pos_results = &test_results[*alg_1].automaton_stats[0]; alg_1_neg_results = &test_results[*alg_1].automaton_stats[1]; - for (vector::size_type - alg_2 = 0; + for (vector::size_type alg_2 = 0; alg_2 < round_info.number_of_translators; alg_2++) { @@ -558,8 +553,7 @@ void printBuchiIntersectionCheckStats alg_1_pos_results = &test_results[*alg_1].automaton_stats[0]; alg_1_neg_results = &test_results[*alg_1].automaton_stats[1]; - for (vector::size_type - alg_2 = 0; + for (vector::size_type alg_2 = 0; alg_2 < round_info.number_of_translators; alg_2++) { @@ -631,8 +625,7 @@ void printBuchiIntersectionCheckStats /* ========================================================================= */ void printAllStats (ostream& stream, int indent, - vector::size_type - algorithm) + vector::size_type algorithm) /* ---------------------------------------------------------------------------- * * Description: Displays all test information (Büchi automaton and product @@ -701,8 +694,8 @@ void printAllStats /* ========================================================================= */ void printCollectiveCrossComparisonStats (ostream& stream, - vector::size_type algorithm_y, - vector::size_type algorithm_x, + vector::size_type algorithm_y, + vector::size_type algorithm_x, int data_type) /* ---------------------------------------------------------------------------- * @@ -892,13 +885,12 @@ void printCollectiveStats(ostream& stream, int indent) if (round_info.num_processed_formulae > 0 && configuration.global_options.formula_input_filename.empty()) { - const map, - ALLOC(unsigned long int) >& + const map& proposition_statistics = configuration.formula_options.formula_generator. propositionStatistics(); - const map, ALLOC(unsigned long int) > + const map symbol_statistics = configuration.formula_options.formula_generator.symbolStatistics(); @@ -944,9 +936,8 @@ void printCollectiveStats(ostream& stream, int indent) number_of_symbols_printed++; } - for (map, ALLOC(unsigned long int) > - ::const_iterator proposition = proposition_statistics.begin(); + for (map::const_iterator + proposition = proposition_statistics.begin(); proposition != proposition_statistics.end(); ++proposition) { @@ -993,8 +984,8 @@ void printCollectiveStats(ostream& stream, int indent) = ""; number_of_symbols_printed = 0; - for (map, ALLOC(unsigned long int) > - ::const_iterator op = symbol_statistics.begin(); + for (map::const_iterator + op = symbol_statistics.begin(); op != symbol_statistics.end(); ++op) { @@ -1468,8 +1459,7 @@ void printCollectiveStats(ostream& stream, int indent) estream << ind + " Result inconsistency statistics\n" + ind + " " + string(31, '=') + '\n'; - vector::size_type - algorithm_x, algorithm_y; + vector::size_type algorithm_x, algorithm_y; for (algorithm_x = 0; algorithm_x < number_of_algorithms; algorithm_x += 2) diff --git a/lbtt/src/StatDisplay.h b/lbtt/src/StatDisplay.h index 212aa6091..a5cb99b45 100644 --- a/lbtt/src/StatDisplay.h +++ b/lbtt/src/StatDisplay.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,32 +50,28 @@ void printStatTableHeader /* Displays a table */ void printBuchiAutomatonStats /* Displays information */ (ostream& stream, /* about a Büchi */ int indent, /* automaton. */ - vector::size_type + vector::size_type algorithm, int result_id); void printProductAutomatonStats /* Displays information */ (ostream& stream, /* about a product */ int indent, /* automaton. */ - vector::size_type + vector::size_type algorithm, int result_id); void printAcceptanceCycleStats /* Displays information */ (ostream& stream, /* about the acceptance */ int indent, /* cycles of a product */ - vector::size_type + vector::size_type /* automaton. */ algorithm, int result_id); void printConsistencyCheckStats /* Displays the result */ (ostream& stream, /* of the consistency */ int indent, /* check for a given */ - vector::size_type + vector::size_type /* algorithm. */ algorithm); void printCrossComparisonStats /* Displays information */ @@ -92,18 +88,14 @@ void printBuchiIntersectionCheckStats /* Displays the results */ void printAllStats /* A shorthand for */ (ostream& stream, /* showing all the */ int indent, /* information displayed */ - vector::size_type /* functions. */ - algorithm); + vector::size_type algorithm); /* by the previous five + * functions. + */ void printCollectiveCrossComparisonStats /* Displays a single */ (ostream& stream, /* `cell' of the final */ - vector::size_type /* comparison table. */ - algorithm_y, - vector::size_type - algorithm_x, + vector::size_type algorithm_y, /* result cross- */ + vector::size_type algorithm_x, /* comparison table. */ int data_type); void printCollectiveStats /* Displays average test */ diff --git a/lbtt/src/StateSpace.cc b/lbtt/src/StateSpace.cc index 78e2238e7..3c16064ea 100644 --- a/lbtt/src/StateSpace.cc +++ b/lbtt/src/StateSpace.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -144,8 +144,7 @@ void StateSpace::clear() initial_state = 0; #ifdef HAVE_OBSTACK_H - for (vector::iterator state = nodes.begin(); - state != nodes.end(); + for (vector::iterator state = nodes.begin(); state != nodes.end(); ++state) static_cast(*state)->~State(); diff --git a/lbtt/src/StateSpace.h b/lbtt/src/StateSpace.h index 338a5a4d1..cb67a6782 100644 --- a/lbtt/src/StateSpace.h +++ b/lbtt/src/StateSpace.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/StateSpaceProduct.h b/lbtt/src/StateSpaceProduct.h index 87fab2fbd..25a5c8a4b 100644 --- a/lbtt/src/StateSpaceProduct.h +++ b/lbtt/src/StateSpaceProduct.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/StateSpaceRandomizer.cc b/lbtt/src/StateSpaceRandomizer.cc index 951b4567c..f836d72a1 100644 --- a/lbtt/src/StateSpaceRandomizer.cc +++ b/lbtt/src/StateSpaceRandomizer.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -174,9 +174,7 @@ StateSpace* StateSpaceRandomizer::generateConnectedGraph() const StateSpace::size_type first_unreachable_state = 1, random_node; - multimap, - ALLOC(StateSpace::size_type) > - reachable_but_unprocessed; + multimap reachable_but_unprocessed; reachable_but_unprocessed.insert(make_pair(0, 0)); diff --git a/lbtt/src/StateSpaceRandomizer.h b/lbtt/src/StateSpaceRandomizer.h index 0aff152ba..f74474490 100644 --- a/lbtt/src/StateSpaceRandomizer.h +++ b/lbtt/src/StateSpaceRandomizer.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/StringUtil.cc b/lbtt/src/StringUtil.cc index c1ff6d428..45d31b2ec 100644 --- a/lbtt/src/StringUtil.cc +++ b/lbtt/src/StringUtil.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -61,8 +61,7 @@ string toString(const double d, const int precision, const ios::fmtflags flags) /* ========================================================================= */ void sliceString - (const string& s, const char* slice_chars, - vector& slices) + (const string& s, const char* slice_chars, vector& slices) /* ---------------------------------------------------------------------------- * * Description: Slices a string into a vector of strings, using a given set @@ -415,7 +414,7 @@ int parseInterval /* ========================================================================= */ void parseIntervalList (const string& token, IntervalList& intervals, unsigned long int min, - unsigned long int max, vector* extra_tokens) + unsigned long int max, vector* extra_tokens) /* ---------------------------------------------------------------------------- * * Description: Parses a string of number intervals into an IntervalList. @@ -443,14 +442,13 @@ void parseIntervalList * * ------------------------------------------------------------------------- */ { - vector interval_strings; + vector interval_strings; int interval_type; intervals.clear(); sliceString(token, ",", interval_strings); - for (vector::const_iterator - i = interval_strings.begin(); + for (vector::const_iterator i = interval_strings.begin(); i != interval_strings.end(); ++i) { diff --git a/lbtt/src/StringUtil.h b/lbtt/src/StringUtil.h index 4965cf64f..954f936d6 100644 --- a/lbtt/src/StringUtil.h +++ b/lbtt/src/StringUtil.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -58,7 +58,7 @@ template string toString(const T& t); /* Template function for void sliceString /* Breaks a string into */ (const string& s, const char* slice_chars, /* `slices', using a */ - vector& slices); /* given set of + vector& slices); /* given set of * characters as * separators. */ @@ -114,8 +114,9 @@ void parseIntervalList /* Converts a list of */ IntervalList& intervals, /* the set of unsigned */ unsigned long int min, /* long integers */ unsigned long int max, /* corresponding to the */ - vector* extra_tokens /* union of the */ - = 0); /* intervals. */ + vector* extra_tokens = 0); /* union of the + * intervals. + */ void parseTime /* Parses a time string. */ (const string& time_string, diff --git a/lbtt/src/TempFsysName.cc b/lbtt/src/TempFsysName.cc index 51dc9c335..45f9ad676 100644 --- a/lbtt/src/TempFsysName.cc +++ b/lbtt/src/TempFsysName.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 2004 - * Heikki Tauriainen + * Copyright (C) 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/TempFsysName.h b/lbtt/src/TempFsysName.h index f2845ebb6..d9fa07994 100644 --- a/lbtt/src/TempFsysName.h +++ b/lbtt/src/TempFsysName.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2004 - * Heikki Tauriainen + * Copyright (C) 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc index 8dd515b66..01e41b2a2 100644 --- a/lbtt/src/TestOperations.cc +++ b/lbtt/src/TestOperations.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -63,6 +63,8 @@ +extern pid_t translator_process; + /****************************************************************************** * * Implementations for the operations used in the main test loop. @@ -595,7 +597,7 @@ void generateFormulae(istream* formula_input_stream) { bool fatal_io_error = (configuration.global_options.formula_input_filename.empty() - || !round_info.formula_input_file.eof()); + || !round_info.formula_input_stream->eof()); printText(string("[") + (fatal_io_error ? "Error reading formula" @@ -823,8 +825,7 @@ void writeFormulaeToFiles() /* ========================================================================= */ void generateBuchiAutomaton (int f, - vector::size_type algorithm_id) + vector::size_type algorithm_id) /* ---------------------------------------------------------------------------- * * Description: Constructs a BuchiAutomaton by invoking an external program @@ -867,17 +868,11 @@ void generateBuchiAutomaton int stdout_capture_fileno = -1, stderr_capture_fileno = -1; int exitcode; - sigset_t sigint_mask; - sigemptyset(&sigint_mask); - sigaddset(&sigint_mask, SIGINT); - struct sigaction timeout_sa; timeout_sa.sa_handler = timeoutHandler; sigemptyset(&timeout_sa.sa_mask); timeout_sa.sa_flags = 0; - pid_t pid = 0; - truncateFile(round_info.automaton_file_name->get(), 10); truncateFile(round_info.cout_capture_file->get(), 10); truncateFile(round_info.cerr_capture_file->get(), 10); @@ -932,13 +927,13 @@ void generateBuchiAutomaton = const_cast(round_info.automaton_file_name->get()); times(&timing_information_begin); - pid = fork(); - switch (pid) + translator_process = fork(); + switch (translator_process) { case 0 : /* child */ close(error_pipe[0]); - if (setsid() != -1 + if (setpgid(0, 0) != -1 && dup2(stdout_capture_fileno, STDOUT_FILENO) != -1 && dup2(stderr_capture_fileno, STDERR_FILENO) != -1) execvp(algorithm.parameters[0], algorithm.parameters); @@ -952,17 +947,32 @@ void generateBuchiAutomaton exit(0); case -1 : /* fork failed */ - pid = 0; + translator_process = 0; error_number = errno; close(error_pipe[0]); close(error_pipe[1]); break; default : /* parent */ - /* Block SIGINT signals while the child process is running. */ + setpgid(translator_process, translator_process); if (configuration.global_options.handle_breaks) - sigprocmask(SIG_BLOCK, &sigint_mask, static_cast(0)); + { + /* If lbtt is currently in the foreground (and has a controlling + terminal), transfer the controlling terminal to the translator + process. */ + + const pid_t foreground_pgrp = tcgetpgrp(STDIN_FILENO); + if (foreground_pgrp != -1 && foreground_pgrp == getpgrp()) + { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTTOU); + sigprocmask(SIG_BLOCK, &mask, 0); + tcsetpgrp(STDIN_FILENO, translator_process); + sigprocmask(SIG_UNBLOCK, &mask, 0); + } + } /* Install handler for timeouts if necessary. */ @@ -976,12 +986,11 @@ void generateBuchiAutomaton close(error_pipe[1]); - if (waitpid(pid, &exitcode, 0) == -1) /* waitpid failed */ + if (waitpid(translator_process, &exitcode, 0) == -1) + /* waitpid failed */ { error_number = errno; - if (error_number == EINTR /* failure due to timeout */ - && configuration.global_options.translator_timeout > 0 - && timeout) + if (kill(translator_process, 0) == 0) /* child still running */ { /* * Try to terminate the child process three times with SIGTERM @@ -995,12 +1004,12 @@ void generateBuchiAutomaton for (int attempts_to_terminate = 0; attempts_to_terminate < 4; ++attempts_to_terminate) { - kill(-pid, sig); + kill(-translator_process, sig); sleep(delay); - if (waitpid(pid, &exitcode, WNOHANG) != 0) + if (waitpid(translator_process, &exitcode, WNOHANG) != 0) { times(&timing_information_end); - pid = 0; + translator_process = 0; break; } if (attempts_to_terminate == 2) @@ -1010,13 +1019,13 @@ void generateBuchiAutomaton } } } - else - pid = 0; + else if (errno != EPERM) + translator_process = 0; } else /* child exited successfully */ { times(&timing_information_end); - pid = 0; + translator_process = 0; /* * If there is something to be read from error_pipe, then there @@ -1043,10 +1052,21 @@ void generateBuchiAutomaton } if (configuration.global_options.handle_breaks) - sigprocmask(SIG_UNBLOCK, &sigint_mask, - static_cast(0)); + { + /* Put lbtt again in the foreground. */ - if (pid == 0 + if (tcgetpgrp(STDIN_FILENO) != -1) + { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTTOU); + sigprocmask(SIG_BLOCK, &mask, 0); + tcsetpgrp(STDIN_FILENO, getpgrp()); + sigprocmask(SIG_UNBLOCK, &mask, 0); + } + } + + if (translator_process == 0 && timing_information_begin.tms_utime != static_cast(-1) && timing_information_begin.tms_cutime @@ -1069,15 +1089,15 @@ void generateBuchiAutomaton close(stderr_capture_fileno); /* - * If pid != 0 at this point, then a timeout occurred, but lbtt was - * unable to terminate the child process. The exception handler will - * in this case throw an unexpected exception (see below) so that lbtt - * will terminate (for example, it is not safe to use the temporary - * file names any longer if the (still running) child process happens to - * write to them). + * If translator_process != 0 at this point, then a timeout occurred, + * but lbtt was unable to terminate the child process. The exception + * handler will in this case throw an unexpected exception (see below) + * so that lbtt will terminate (for example, it is not safe to use the + * temporary file names any longer if the (still running) child process + * happens to write to them). */ - if (pid != 0) + if (translator_process != 0) { stdout_capture_fileno = stderr_capture_fileno = -1; throw Exception("could not terminate child process"); @@ -1286,7 +1306,7 @@ void generateBuchiAutomaton round_info.transcript_file.flush(); } - if (pid != 0) /* fatal error, lbtt should be terminated */ + if (translator_process != 0) /* fatal error, lbtt should be terminated */ throw Exception ("fatal internal error while generating Büchi automaton"); @@ -1345,9 +1365,7 @@ void generateBuchiAutomaton /* ========================================================================= */ void performEmptinessCheck (int f, - vector::size_type - algorithm_id) + vector::size_type algorithm_id) /* ---------------------------------------------------------------------------- * * Description: Performs the emptiness check on a ProductAutomaton, i.e., @@ -1473,9 +1491,7 @@ void performEmptinessCheck /* ========================================================================= */ void performConsistencyCheck - (vector::size_type - algorithm_id) + (vector::size_type algorithm_id) /* ---------------------------------------------------------------------------- * * Description: Checks the model checking results for consistency for a @@ -1570,8 +1586,7 @@ void compareResults() AutomatonStats* alg_1_stats; AutomatonStats* alg_2_stats; - for (vector::size_type - alg_1 = 0; + for (vector::size_type alg_1 = 0; alg_1 < test_results.size(); ++alg_1) { @@ -1579,8 +1594,7 @@ void compareResults() { alg_1_stats = &test_results[alg_1].automaton_stats[counter]; - for (vector - ::size_type alg_2 = alg_1 + 1; + for (vector::size_type alg_2 = alg_1 + 1; alg_2 < test_results.size(); ++alg_2) { @@ -1681,13 +1695,11 @@ void performBuchiIntersectionCheck() ::Graph::BuchiProduct::clearSatisfiabilityCache(); - for (vector::size_type - alg_1 = 0; + for (vector::size_type alg_1 = 0; alg_1 < round_info.number_of_translators; ++alg_1) { - for (vector::size_type - alg_2 = 0; + for (vector::size_type alg_2 = 0; alg_2 < round_info.number_of_translators; ++alg_2) { diff --git a/lbtt/src/TestOperations.h b/lbtt/src/TestOperations.h index 9c31a0ad0..74b9cdfd1 100644 --- a/lbtt/src/TestOperations.h +++ b/lbtt/src/TestOperations.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -95,25 +95,25 @@ void writeFormulaeToFiles(); /* Writes LTL formulas */ void generateBuchiAutomaton /* Generates a Büchi */ (int f, /* automaton from a LTL */ - vector /* given file, using a */ - ::size_type /* given LTL-to-Büchi */ - algorithm_id); /* translation algorithm + vector /* formula stored in a */ + ::size_type /* given file, using a */ + algorithm_id); /* given LTL-to-Büchi + * translation algorithm * for the conversion. */ void performEmptinessCheck /* Performs an emptiness */ (int f, /* check on a product */ - vector + vector /* automaton. */ ::size_type algorithm_id); void performConsistencyCheck /* Performs a */ - (vector /* the test results */ - ::size_type /* for a formula and its */ - algorithm_id); /* negation. */ + (vector /* consistency check on */ + ::size_type /* the test results */ + algorithm_id); /* for a formula and its + * negation. + */ void compareResults(); /* Compares the model * checking results diff --git a/lbtt/src/TestRoundInfo.h b/lbtt/src/TestRoundInfo.h index 6a2f94b38..8b396b9d7 100644 --- a/lbtt/src/TestRoundInfo.h +++ b/lbtt/src/TestRoundInfo.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -55,7 +55,11 @@ public: * stream for messages. */ - ifstream formula_input_file; /* Stream for reading input + istream* formula_input_stream; /* Stream for reading input + * formulae. + */ + + ifstream formula_input_file; /* File for reading input * formulae. */ @@ -149,9 +153,9 @@ public: * current round. */ - vector /* current round: */ - formulae; /* formulae[0]: + vector formulae; /* Formulae used in the + * current round: + * formulae[0]: * positive formula in * negation normal * form @@ -167,7 +171,7 @@ public: * generated */ - vector formula_in_file; /* The values in this + vector formula_in_file; /* The values in this * vector will be set to * true when the * corresponding diff --git a/lbtt/src/TestStatistics.cc b/lbtt/src/TestStatistics.cc index b3c82bd54..053df05b0 100644 --- a/lbtt/src/TestStatistics.cc +++ b/lbtt/src/TestStatistics.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -50,8 +50,7 @@ void AlgorithmTestResults::emptinessReset() automaton_stats[i].emptiness_check_result.clear(); automaton_stats[i].emptiness_check_performed = false; - for (vector::iterator it + for (vector::iterator it = automaton_stats[i].cross_comparison_stats.begin(); it != automaton_stats[i].cross_comparison_stats.end(); ++it) @@ -87,7 +86,7 @@ void AlgorithmTestResults::fullReset() automaton_stats[i].number_of_msccs = 0; automaton_stats[i].buchi_generation_time = 0.0; - for (vector::iterator it + for (vector::iterator it = automaton_stats[i].buchi_intersection_check_stats.begin(); it != automaton_stats[i].buchi_intersection_check_stats.end(); ++it) diff --git a/lbtt/src/TestStatistics.h b/lbtt/src/TestStatistics.h index 71fb30de8..e6a16500c 100644 --- a/lbtt/src/TestStatistics.h +++ b/lbtt/src/TestStatistics.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,9 +43,8 @@ using Graph::StateSpace; struct AutomatonStats { explicit AutomatonStats /* Constructor. */ - (vector - ::size_type number_of_algorithms, + (vector + ::size_type number_of_algorithms, StateSpace::size_type max_statespace_size); /* default copy constructor */ @@ -127,9 +126,9 @@ struct AutomatonStats typedef pair CrossComparisonStats; - vector /* cross-comparison */ - cross_comparison_stats; /* results. The `first' + vector /* Emptiness check */ + cross_comparison_stats; /* cross-comparison + * results. The `first' * element of the pair * tells whether a cross- * comparison with a given @@ -142,8 +141,8 @@ struct AutomatonStats * differ. */ - vector /* Büchi automaton */ - buchi_intersection_check_stats; /* intersection + vector buchi_intersection_check_stats; /* Büchi automaton + * intersection * emptiness check * results. The elements * of the vector tell @@ -173,8 +172,7 @@ struct AutomatonStats struct AlgorithmTestResults { explicit AlgorithmTestResults /* Constructor. */ - (vector + (vector ::size_type number_of_algorithms, StateSpace::size_type max_statespace_size); @@ -214,8 +212,8 @@ struct AlgorithmTestResults * check. */ - vector /* A two-element vector */ - automaton_stats; /* storing test results + vector automaton_stats; /* A two-element vector + * storing test results * for an algorithm. */ }; @@ -232,8 +230,7 @@ struct AlgorithmTestResults struct TestStatistics { explicit TestStatistics /* Constructor. */ - (vector::size_type + (vector::size_type number_of_algorithms); /* default copy constructor */ @@ -303,24 +300,26 @@ struct TestStatistics * automata. */ - vector /* result cross- */ - cross_comparison_mismatches; /* comparisons. */ + vector /* Number of failed */ + cross_comparison_mismatches; /* result cross- + * comparisons. + */ - vector /* result cross- */ - initial_cross_comparison_mismatches; /* comparisons in the + vector /* Number of failed */ + initial_cross_comparison_mismatches; /* result cross- + * comparisons in the * initial state of the * state space. */ - vector /* cross-comparisons */ - cross_comparisons_performed; /* performed. */ + vector /* Number of result */ + cross_comparisons_performed; /* cross-comparisons + * performed. + */ - vector /* Büchi automaton */ - buchi_intersection_check_failures; /* emptiness checks + vector /* Number of failed */ + buchi_intersection_check_failures; /* Büchi automaton + * emptiness checks * against the automata * constructed from the * negated formula @@ -328,9 +327,9 @@ struct TestStatistics * algorithms. */ - vector /* automaton emptiness */ - buchi_intersection_checks_performed; /* checks performed + vector /* Number of Büchi */ + buchi_intersection_checks_performed; /* automaton emptiness + * checks performed * against the automata * constructed from the * negated formula using @@ -348,9 +347,7 @@ struct TestStatistics /* ========================================================================= */ inline AutomatonStats::AutomatonStats - (vector::size_type - number_of_algorithms, + (vector::size_type number_of_algorithms, StateSpace::size_type max_statespace_size) : buchi_automaton(0), number_of_buchi_states(0), number_of_buchi_transitions(0), number_of_acceptance_sets(0), @@ -472,9 +469,7 @@ AutomatonStats::buchiIntersectionCheckPerformed(unsigned long int algorithm) /* ========================================================================= */ inline AlgorithmTestResults::AlgorithmTestResults - (vector::size_type - number_of_algorithms, + (vector::size_type number_of_algorithms, StateSpace::size_type max_statespace_size) : consistency_check_result(-1), consistency_check_comparisons(0), failed_consistency_check_comparisons(0), @@ -519,8 +514,7 @@ inline AlgorithmTestResults::~AlgorithmTestResults() /* ========================================================================= */ inline TestStatistics::TestStatistics - (vector::size_type - number_of_algorithms) : + (vector::size_type number_of_algorithms) : consistency_check_failures(0), consistency_checks_performed(0), cross_comparison_mismatches(number_of_algorithms, 0), initial_cross_comparison_mismatches(number_of_algorithms, 0), diff --git a/lbtt/src/TranslatorInterface.h b/lbtt/src/TranslatorInterface.h index ac70bfc55..eca8e172d 100644 --- a/lbtt/src/TranslatorInterface.h +++ b/lbtt/src/TranslatorInterface.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/lbtt/src/UserCommandReader.cc b/lbtt/src/UserCommandReader.cc index a91b71403..051c56306 100644 --- a/lbtt/src/UserCommandReader.cc +++ b/lbtt/src/UserCommandReader.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -86,7 +86,7 @@ void executeUserCommands() * ------------------------------------------------------------------------ */ { string input_line; - vector input_tokens; + vector input_tokens; TokenType token; bool formula_type = true; @@ -230,9 +230,7 @@ void executeUserCommands() { bool all_algorithms_disabled = true; - for (vector - ::const_iterator + for (vector::const_iterator algorithm = configuration.algorithms.begin(); algorithm != configuration.algorithms.end(); ++algorithm) @@ -799,9 +797,8 @@ TokenType parseCommand(const string& token) /* ========================================================================= */ void verifyArgumentCount - (const vector& command, - vector::size_type min_arg_count, - vector::size_type max_arg_count) + (const vector& command, vector::size_type min_arg_count, + vector::size_type max_arg_count) /* ---------------------------------------------------------------------------- * * Description: Verifies that the number of arguments given for a user @@ -824,8 +821,7 @@ void verifyArgumentCount } /* ========================================================================= */ -pair parseRedirection - (vector& input_tokens) +pair parseRedirection(vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Tests whether the last argument to a user command specifies @@ -888,7 +884,7 @@ pair parseRedirection } /* ========================================================================= */ -bool parseFormulaType(vector& input_tokens) +bool parseFormulaType(vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Tests whether the first argument of a command specifies a diff --git a/lbtt/src/UserCommandReader.h b/lbtt/src/UserCommandReader.h index dfe489291..b9a510519 100644 --- a/lbtt/src/UserCommandReader.h +++ b/lbtt/src/UserCommandReader.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -65,22 +65,19 @@ TokenType parseCommand(const string& token); /* Translates a command */ void verifyArgumentCount /* Checks that the */ - (const vector& /* number of arguments */ - arguments, /* for a command is */ - vector::size_type /* within given limits. */ - min_arg_count, - vector::size_type - max_arg_count); + (const vector& arguments, /* number of arguments */ + vector::size_type min_arg_count, /* for a command is */ + vector::size_type max_arg_count); /* within given limits. */ pair parseRedirection /* Checks whether an */ - (vector& input_tokens); /* user command given + (vector& input_tokens); /* user command given * will require * redirecting its * output to a file. */ bool parseFormulaType /* Checks whether an */ - (vector& input_tokens); /* user command + (vector& input_tokens); /* user command * specified a positive * or a negative * formula. diff --git a/lbtt/src/UserCommands.cc b/lbtt/src/UserCommands.cc index e5ae8c72e..08980d028 100644 --- a/lbtt/src/UserCommands.cc +++ b/lbtt/src/UserCommands.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -73,8 +73,7 @@ unsigned long int parseAlgorithmId(const string& id) } catch (const NotANumberException&) { - map, ALLOC(unsigned long int) > - ::const_iterator id_finder + map::const_iterator id_finder = configuration.algorithm_names.find(unquoted_id); if (id_finder == configuration.algorithm_names.end()) throw CommandErrorException @@ -113,20 +112,18 @@ void parseAlgorithmIdList(const string& ids, IntervalList& algorithms) try { - vector nonnumeric_algorithm_ids; + vector nonnumeric_algorithm_ids; parseIntervalList(id_string, algorithms, 0, round_info.number_of_translators - 1, &nonnumeric_algorithm_ids); - for (vector::iterator - id = nonnumeric_algorithm_ids.begin(); + for (vector::iterator id = nonnumeric_algorithm_ids.begin(); id != nonnumeric_algorithm_ids.end(); ++id) { *id = unquoteString(substituteInQuotedString(*id, "\n", ",")); - map, ALLOC(unsigned long int) > - ::const_iterator + map::const_iterator id_finder = configuration.algorithm_names.find(*id); if (id_finder == configuration.algorithm_names.end()) throw CommandErrorException @@ -181,7 +178,7 @@ void printAlgorithmList(ostream& stream, int indent) /* ========================================================================= */ void printCrossComparisonAnalysisResults (ostream& stream, int indent, bool formula_type, - const vector& input_tokens) + const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `resultanalysis', i.e., analyzes @@ -494,8 +491,7 @@ void printCrossComparisonAnalysisResults /* ========================================================================= */ void printConsistencyAnalysisResults - (ostream& stream, int indent, - const vector& input_tokens) + (ostream& stream, int indent, const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `consistencyanalysis', i.e., @@ -607,11 +603,9 @@ void printConsistencyAnalysisResults } } - vector path; + vector path; StateSpace::Path prefix, cycle; - map, ALLOC(StateSpace::size_type) > - ordering; + map ordering; StateSpace::size_type state_count = 0; StateSpace::size_type loop_state; @@ -663,8 +657,7 @@ void printConsistencyAnalysisResults /* ========================================================================= */ void printAutomatonAnalysisResults - (ostream& stream, int indent, - const vector& input_tokens) + (ostream& stream, int indent, const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `buchianalysis', i.e., analyzes @@ -977,9 +970,7 @@ void printPath /* ========================================================================= */ void printAcceptingCycle (ostream& stream, int indent, - vector::size_type - algorithm_id, + vector::size_type algorithm_id, const BuchiAutomaton::Path& aut_prefix, const BuchiAutomaton::Path& aut_cycle, const BuchiAutomaton& automaton, const StateSpace::Path& path_prefix, @@ -1121,8 +1112,7 @@ void printAcceptingCycle /* ========================================================================= */ void printBuchiAutomaton (ostream& stream, int indent, bool formula_type, - vector& input_tokens, - Graph::GraphOutputFormat fmt) + vector& input_tokens, Graph::GraphOutputFormat fmt) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `buchi', i.e., writes information @@ -1231,8 +1221,8 @@ void printBuchiAutomaton /* ========================================================================= */ void evaluateFormula - (ostream& stream, int indent, bool formula_type, - vector& input_tokens) + (ostream& stream, int indent, bool formula_type, + vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `evaluate', i.e., tells whether @@ -1346,7 +1336,7 @@ void evaluateFormula /* ========================================================================= */ void printFormula (ostream& stream, int indent, bool formula_type, - const vector& input_tokens) + const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `formula', i.e., displays the @@ -1401,8 +1391,7 @@ void printFormula /* ========================================================================= */ void printCommandHelp - (ostream& stream, int indent, - const vector& input_tokens) + (ostream& stream, int indent, const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `help', i.e., gives instructions @@ -1714,7 +1703,7 @@ void printCommandHelp /* ========================================================================= */ void printInconsistencies - (ostream& stream, int indent, vector& input_tokens) + (ostream& stream, int indent, vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `inconsistencies', i.e., lists @@ -1818,7 +1807,7 @@ void printInconsistencies /* ========================================================================= */ void printTestResults - (ostream& stream, int indent, vector& input_tokens) + (ostream& stream, int indent, vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `results', i.e., displays the @@ -1877,7 +1866,7 @@ void printTestResults /* ========================================================================= */ void printStateSpace - (ostream& stream, int indent, vector& input_tokens, + (ostream& stream, int indent, vector& input_tokens, Graph::GraphOutputFormat fmt) /* ---------------------------------------------------------------------------- * @@ -1950,7 +1939,7 @@ void printStateSpace } /* ========================================================================= */ -void changeVerbosity(const vector& input_tokens) +void changeVerbosity(const vector& input_tokens) /* ---------------------------------------------------------------------------- * * Description: Implements the user command `verbosity', i.e., displays or @@ -1987,8 +1976,7 @@ void changeVerbosity(const vector& input_tokens) } /* ========================================================================= */ -void changeAlgorithmState - (vector& input_tokens, bool enable) +void changeAlgorithmState(vector& input_tokens, bool enable) /* ---------------------------------------------------------------------------- * * Description: Changes the enabledness of a set of algorithms used in the diff --git a/lbtt/src/UserCommands.h b/lbtt/src/UserCommands.h index 756717387..51ed74ef5 100644 --- a/lbtt/src/UserCommands.h +++ b/lbtt/src/UserCommands.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -67,20 +67,19 @@ void printAlgorithmList /* Displays a list of */ void printCrossComparisonAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradiction between */ bool formula_type, /* test results of two */ - const vector& /* implementations. */ - input_tokens); + const vector& input_tokens); /* implementations. */ void printConsistencyAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradicition in the */ - const vector& /* model checking result */ - input_tokens); /* consistency check for + const vector& input_tokens); /* model checking result + * consistency check for * an implementation. */ void printAutomatonAnalysisResults /* Analyzes a */ (ostream& stream, int indent, /* contradiction in the */ - const vector& /* Büchi automata */ - input_tokens); /* intersection + const vector& input_tokens); /* Büchi automata + * intersection * emptiness check. */ @@ -92,8 +91,7 @@ void printPath /* Displays information */ void printAcceptingCycle /* Displays information */ (ostream& stream, int indent, /* a single automaton */ - vector + vector /* execution. */ ::size_type algorithm_id, const BuchiAutomaton::Path& aut_prefix, @@ -106,47 +104,47 @@ void printAcceptingCycle /* Displays information */ void printBuchiAutomaton /* Displays information */ (ostream& stream, int indent, /* about a Büchi */ bool formula_type, /* automaton. */ - vector& input_tokens, + vector& input_tokens, Graph::GraphOutputFormat fmt); void evaluateFormula /* Displays information */ (ostream& stream, int indent, /* about existence of */ bool formula_type, /* accepting system */ - vector& input_tokens); /* executions. */ + vector& input_tokens); /* executions. */ void printFormula /* Displays a formula */ (ostream& stream, int indent, /* used for testing. */ bool formula_type, - const vector& - input_tokens); + const vector& input_tokens); void printCommandHelp /* Displays help about */ (ostream& stream, int indent, /* user commands. */ - const vector& - input_tokens); + const vector& input_tokens); void printInconsistencies /* Lists the system */ (ostream& stream, int indent, /* states failing the */ - vector& input_tokens); /* consistency check + vector& input_tokens); /* consistency check * for an algorihm. */ void printTestResults /* Displays the test */ (ostream& stream, int indent, /* results of the last */ - vector& input_tokens); /* round performed. */ + vector& input_tokens); /* round performed. */ void printStateSpace /* Displays information */ (ostream& stream, int indent, /* about a state space. */ - vector& input_tokens, + vector& input_tokens, Graph::GraphOutputFormat fmt); void changeVerbosity /* Displays or changes */ - (const vector& /* the verbosity of */ - input_tokens); /* output. */ + (const vector& input_tokens); /* the verbosity of + * output. + */ void changeAlgorithmState /* Enables or disables a */ - (vector& input_tokens, /* set of algorithms */ - bool enable); /* used in the tests. */ + (vector& input_tokens, bool enable); /* set of algorithms + * used in the tests. + */ } diff --git a/lbtt/src/main.cc b/lbtt/src/main.cc index c217ac4f7..4c4093da2 100644 --- a/lbtt/src/main.cc +++ b/lbtt/src/main.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,6 +21,9 @@ #include #include #include +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ #include #include #ifdef HAVE_READLINE @@ -28,6 +31,11 @@ #include #include #endif /* HAVE_READLINE */ +#ifdef HAVE_ISATTY +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#endif /* HAVE_ISATTY */ #include "LbttAlloc.h" #include "Configuration.h" #include "DispUtil.h" @@ -82,12 +90,12 @@ TestRoundInfo round_info; /* Data structure for * round. */ -vector /* individual algorithm. */ - test_results; +vector test_results; /* Test results for each + * individual algorithm. + */ -vector /* Overall test */ - final_statistics; /* statistics for each +vector final_statistics; /* Overall test + * statistics for each * algorithm. */ @@ -167,9 +175,13 @@ static void breakHandler(int) * *****************************************************************************/ +pid_t translator_process = 0; /* Process group for translator process */ + static void abortHandler(int signum) { deallocateTempFilenames(); + if (translator_process != 0 && kill(translator_process, 0) == 0) + kill(-translator_process, SIGTERM); struct sigaction s; s.sa_handler = SIG_DFL; sigemptyset(&s.sa_mask); @@ -274,16 +286,24 @@ bool testLoop() /* * If a formula file name was given in the configuration, open the file for - * reading. + * reading. The special filename "-" refers to the standard input. */ try { if (!global_options.formula_input_filename.empty()) - openFile(global_options.formula_input_filename.c_str(), - round_info.formula_input_file, - ios::in, - 0); + { + if (global_options.formula_input_filename == "-") + round_info.formula_input_stream = &cin; + else + { + openFile(global_options.formula_input_filename.c_str(), + round_info.formula_input_file, + ios::in, + 0); + round_info.formula_input_stream = &round_info.formula_input_file; + } + } } catch (const FileOpenException& e) { @@ -383,8 +403,7 @@ bool testLoop() round_info.next_round_to_change_statespace += global_options.statespace_change_interval; - for (vector - ::iterator it = test_results.begin(); + for (vector::iterator it = test_results.begin(); it != test_results.end(); ++it) it->emptinessReset(); @@ -437,8 +456,7 @@ bool testLoop() round_info.formula_in_file[0] = round_info.formula_in_file[1] = false; - for (vector - ::iterator it = test_results.begin(); + for (vector::iterator it = test_results.begin(); it != test_results.end(); ++it) it->fullReset(); @@ -449,8 +467,8 @@ bool testLoop() { try { - generateFormulae(round_info.formula_input_file.is_open() - ? &round_info.formula_input_file + generateFormulae(!global_options.formula_input_filename.empty() + ? round_info.formula_input_stream : 0); } catch (const FormulaGenerationException&) @@ -667,8 +685,7 @@ bool testLoop() ::Ltl::LtlFormula::destruct(round_info.formulae[f]); } - for (vector - ::iterator it = test_results.begin(); + for (vector::iterator it = test_results.begin(); it != test_results.end(); ++it) it->fullReset(); @@ -735,10 +752,19 @@ int main(int argc, char* argv[]) if (!e.line_info.empty()) cerr << ":" << configuration.global_options.cfg_filename << ":" << e.line_info; - cerr << ":" << e.what() << endl; + cerr << ": " << e.what() << endl; exit(2); } +#ifdef HAVE_ISATTY + if (configuration.global_options.formula_input_filename == "-" + && !isatty(STDIN_FILENO)) + { + configuration.global_options.interactive = Configuration::NEVER; + configuration.global_options.handle_breaks = false; + } +#endif /* HAVE_ISATTY */ + if (configuration.global_options.verbosity >= 3) configuration.print(cout); diff --git a/lbtt/src/translate.cc b/lbtt/src/translate.cc index eab9a9037..173f796dc 100644 --- a/lbtt/src/translate.cc +++ b/lbtt/src/translate.cc @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,6 +25,7 @@ #include "LbtWrapper.h" #include "LtlFormula.h" #include "SpinWrapper.h" +#include "SpotWrapper.h" #ifdef HAVE_GETOPT_LONG #include #define OPTIONSTRUCT struct option @@ -59,7 +60,7 @@ static TranslatorInterface* translator = 0; /****************************************************************************** * * A function for showing warnings to the user. - * + * *****************************************************************************/ void printWarning(const string& msg) @@ -119,7 +120,7 @@ static void installSignalHandler(int signum) int main(int argc, char** argv) { - typedef enum {OPT_HELP = 'h', OPT_LBT, OPT_SPIN, OPT_VERSION = 'V'} + typedef enum {OPT_HELP = 'h', OPT_LBT, OPT_SPIN, OPT_SPOT, OPT_VERSION = 'V'} OptionType; static OPTIONSTRUCT command_line_options[] = @@ -127,6 +128,7 @@ int main(int argc, char** argv) {"help", no_argument, 0, OPT_HELP}, {"lbt", no_argument, 0, OPT_LBT}, {"spin", no_argument, 0, OPT_SPIN}, + {"spot", no_argument, 0, OPT_SPOT}, {"version", no_argument, 0, OPT_VERSION}, {0, 0, 0, 0} }; @@ -154,6 +156,7 @@ int main(int argc, char** argv) "Translator options:\n" " --lbt lbt\n" " --spin Spin\n" + " --spot Spot\n" "The command line for these translators must be given as a " "single argument\n" "including the name (and location) of an external program to " @@ -171,6 +174,10 @@ int main(int argc, char** argv) translator = new SpinWrapper(); break; + case OPT_SPOT : + translator = new SpotWrapper(); + break; + case OPT_VERSION : cout << "lbtt-translate " PACKAGE_VERSION "\n" "lbtt-translate is free software; you may change and " diff --git a/lbtt/src/translate.h b/lbtt/src/translate.h index 4d7971859..af028f4f4 100644 --- a/lbtt/src/translate.h +++ b/lbtt/src/translate.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 - * Heikki Tauriainen + * Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 + * Heikki Tauriainen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License