Vcsn  2.8
Be Rational
setalpha.hh
Go to the documentation of this file.
1 #pragma once
2 
3 #include <cassert>
4 #include <initializer_list>
5 #include <stdexcept>
6 
7 #include <boost/container/flat_set.hpp>
8 #include <boost/optional.hpp>
9 #include <boost/range/iterator_range_core.hpp>
10 #include <boost/version.hpp>
11 
12 #include <vcsn/misc/escape.hh>
13 #include <vcsn/misc/format.hh>
14 #include <vcsn/misc/raise.hh>
15 #include <vcsn/misc/set.hh>
16 #include <vcsn/misc/stream.hh> // eat.
17 #include <vcsn/misc/symbol.hh>
18 #include <vcsn/misc/type_traits.hh>
19 
20 namespace vcsn
21 {
23  template <typename Key, typename Compare, typename Allocator>
24  ATTRIBUTE_PURE
25  bool
26  has(const boost::container::flat_set<Key, Compare, Allocator>& s,
27  const Key& e)
28  {
29  return s.find(e) != s.end();
30  }
31 
35  template <typename L>
36  class set_alphabet: public L
37  {
38  public:
39  using letter_t = typename L::letter_t;
40  using word_t = typename L::word_t;
41  using letters_t
42  = boost::container::flat_set<letter_t, vcsn::less<L, letter_t>>;
45 
46  static symbol sname()
47  {
48  static auto res = L::sname();
49  return res;
50  }
51 
52  static set_alphabet make(std::istream& is)
53  {
54  // name: char_letters(abc)
55  // ^^^^^^^^^^^^ ^^^
56  // letter_type gens
57  eat(is, sname());
58 
59  // The result.
61 
62  // This labelset might be open: no initial letter is given, they
63  // will be discovered afterwards.
64  if (is.peek() == '(')
65  {
66  is.ignore();
67  // Previously read character, for intervals. We don't
68  // immediately add the letters: on 'a-z' we would firsts add
69  // 'a', and then ask for the interval from 'a' to 'z', which
70  // would add 'a' twice uselessly.
71  //
72  // Rather, keep the 'a' in \a prev, and flush prev when needed.
73  boost::optional<letter_t> prev;
74  while (true)
75  switch (is.peek())
76  {
77  case EOF:
78  raise(sname(), ": make: invalid end-of-file");
79  break;
80 
81  case ')':
82  eat(is, ')');
83  goto done;
84 
85  case '-':
86  if (prev)
87  {
88  eat(is, '-');
89  res.add_range(*prev, L::get_letter(is));
90  prev = boost::none;
91  break;
92  }
93  else
94  goto insert;
95 
96  insert:
97  default:
98  {
99  if (prev)
100  res.add_letter(*prev);
101  prev = L::get_letter(is);
102  break;
103  }
104  }
105  done:
106  if (prev)
107  res.add_letter(*prev);
108  ;
109  }
110  else // is.peek() != '('
111  res.open_ = true;
112  return res;
113  }
114 
116  {
117  alphabet_.insert(L::one_letter());
118  }
119  set_alphabet(const set_alphabet&) = default;
120  set_alphabet(std::initializer_list<letter_t> l)
121 #if 105700 <= BOOST_VERSION
122  : alphabet_{l}
123 #else
124  : alphabet_{l.begin(), l.end()}
125 #endif
126  {
127  alphabet_.insert(L::one_letter());
128  }
130  : alphabet_{l}
131  {}
132 
136  bool open(bool o) const
137  {
138  std::swap(o, open_);
139  return o;
140  }
141 
143  set_alphabet&
145  {
146  VCSN_REQUIRE(l != this->template special<letter_t>(),
147  *this,
148  ": add_letter: the special letter is reserved: ",
149  str_escape(l));
150  alphabet_.insert(l);
151  return *this;
152  }
153 
156  template <typename Letter, typename Enable = void>
157  struct has_range: std::false_type {};
158 
159  template <typename Letter>
160  struct has_range<Letter,
161  decltype((++std::declval<Letter&>(), void()))>
162  : std::true_type
163  {};
164 
167  -> set_alphabet&
168  {
169  return add_range_<letter_t>(l1, l2);
170  }
171 
172  template <typename Letter>
173  auto add_range_(Letter l1, Letter l2)
174  -> std::enable_if_t<has_range<Letter>{}, set_alphabet&>
175  {
176  for (/* empty */; L::less(l1, l2); ++l1)
177  add_letter(l1);
178  // The last letter. Do not do this in the loop, we might
179  // overflow the capacity of char. Check validity, so that 'z-a'
180  // is empty.
181  if (L::equal(l1, l2))
182  add_letter(l1);
183  return *this;
184  }
185 
186  template <typename Letter>
187  auto add_range_(Letter, Letter)
188  -> std::enable_if_t<!has_range<Letter>{}, set_alphabet&>
189  {
190  raise(*this, ": does not support letter ranges");
191  }
192 
194  bool
195  has(letter_t l) const
196  {
197  if (open_)
198  {
199  // FIXME: OMG...
200  const_cast<set_alphabet&>(*this).add_letter(l);
201  return true;
202  }
203  else
205  }
206 
208  word_t
209  get_word(std::istream& i) const
210  {
211  require(!i.bad(), *this, ": conv: invalid stream");
212  // Either an empty word: "\e", or a sequence of non-separators.
213  if (i.good() && i.peek() == '\\')
214  {
215  i.ignore();
216  int c = i.peek();
217  if (c == 'e')
218  {
219  i.ignore();
220  return {};
221  }
222  else
223  i.unget();
224  }
225 
226  // Stop as soon as it might be a special character (such as
227  // delimiters in polynomials, or tuple separators).
228  //
229  // The empty word (i.e., an empty stream) is a valid
230  // representation of the mpty word. We want to be able to call
231  // `aut.evaluate("")`, instead of mandating `aut.evaluate("\e")`.
232  word_t res;
233  int c = i.peek();
234  while (i.good()
235  && (c = i.peek()) != EOF
236  && !isspace(c)
237  && c != '+'
238  && c != ','
239  && c != '|'
240  && c != '('
241  && c != ')')
242  {
243  letter_t l = L::get_letter(i, true);
244  VCSN_REQUIRE(has(l), *this, ": invalid letter: ", str_escape(l));
245  // FIXME: in-place mul or temporary vector to build the
246  // string.
247  res = this->mul(res, l);
248  }
249  return res;
250  }
251 
252  using iterator = typename letters_t::const_iterator;
253  using const_iterator = typename letters_t::const_iterator;
254 
256  {
257  return alphabet_.begin() + 1;
258  }
259 
261  {
262  return alphabet_.end();
263  }
264 
266  {
267  return cbegin();
268  }
269 
271  {
272  return cend();
273  }
274 
276  auto pregenerators() const
277  {
278  return alphabet_;
279  }
280 
282  auto generators() const
283  {
284  return boost::make_iterator_range(cbegin(), cend());
285  }
286 
288  bool empty() const
289  {
290  return cbegin() == cend();
291  }
292 
294  size_t size() const
295  {
296  return alphabet_.size();
297  }
298 
300  {
301  return alphabet_.find(l);
302  }
303 
304  std::ostream&
305  print_set(std::ostream& o, format fmt = {}) const
306  {
307  switch (fmt.kind())
308  {
309  case format::latex:
310  {
311  o << "\\{";
312  const char *sep = "";
313  for (auto l: *this)
314  {
315  o << sep;
316  if (! this->is_letter(l))
317  o << "\\mathit{";
318  this->print(l, o, fmt);
319  if (! this->is_letter(l))
320  o << '}';
321  sep = ", ";
322  }
323  if (open_)
324  o << sep << "\\ldots";
325  o << "\\}";
326  }
327  break;
328 
329  case format::sname:
330  o << sname() << '(';
331  for (auto l: *this)
332  this->print(l, o, format::sname);
333  // FIXME: Don't display openness here, as our "make()"
334  // parser is not ready for it.
335  o << ')';
336  break;
337 
338  case format::text:
339  case format::utf8:
340  o << '{';
341  for (auto l: *this)
342  this->print(l, o, format::sname);
343  if (open_)
344  o << "...";
345  o << '}';
346  break;
347 
348  case format::raw:
349  assert(0);
350  break;
351  }
352  return o;
353  }
354 
356  friend set_alphabet
358  {
359  return {set_intersection(lhs.alphabet_, rhs.alphabet_)};
360  }
361 
363  friend set_alphabet
364  set_union(const set_alphabet& lhs, const set_alphabet& rhs)
365  {
366  return {set_union(lhs.alphabet_, rhs.alphabet_)};
367  }
368 
369  private:
370  // FIXME: OMG...
372  mutable bool open_ = false;
373  };
374 }
Print as a parsable type string.
Definition: format.hh:26
ATTRIBUTE_PURE bool has(const boost::container::flat_set< Key, Compare, Allocator > &s, const Key &e)
Whether e is member of s.
Definition: setalpha.hh:26
std::ostream & print(const Aut &aut, std::ostream &out=std::cout, const std::string &fmt="default")
Definition: print.hh:83
const_iterator cbegin() const
Definition: setalpha.hh:255
auto add_range_(Letter, Letter) -> std::enable_if_t<!has_range< Letter >
Definition: setalpha.hh:187
set_alphabet & add_letter(letter_t l)
Modify this by adding l, and return *this.
Definition: setalpha.hh:144
Whether the genset supports the range concept: whether we can use &#39;++&#39; on letters.
Definition: setalpha.hh:157
char eat(std::istream &is, char c)
Check lookahead character and advance.
Definition: stream.cc:147
auto add_range_(Letter l1, Letter l2) -> std::enable_if_t< has_range< Letter >
Definition: setalpha.hh:173
boost::container::flat_set< letter_t, vcsn::less< L, letter_t > > letters_t
Definition: setalpha.hh:42
auto generators() const
All the generators.
Definition: setalpha.hh:282
auto add_range(letter_t l1, letter_t l2) -> set_alphabet &
Add a range of letters, if it is accepted by the labelset.
Definition: setalpha.hh:166
typename L::word_t word_t
Definition: setalpha.hh:40
const_iterator find(letter_t l) const
Definition: setalpha.hh:299
std::ostream & str_escape(std::ostream &os, const std::string &str, const char *special=nullptr)
Output a string, escaping special characters.
Definition: escape.cc:51
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:21
const_iterator cend() const
Definition: setalpha.hh:260
Print as rich UTF-8 text, escaped.
Definition: format.hh:30
An input/output format for valuesets.
Definition: format.hh:13
A set of letters of type L.
Definition: setalpha.hh:36
std::ostream & print_set(std::ostream &o, format fmt={}) const
Definition: setalpha.hh:305
const_iterator begin() const
Definition: setalpha.hh:265
set_alphabet &bool has(letter_t l) const
Whether l is a letter.
Definition: setalpha.hh:195
Definition: a-star.hh:8
friend set_alphabet set_intersection(const set_alphabet &lhs, const set_alphabet &rhs)
Compute the intersection with another alphabet.
Definition: setalpha.hh:357
size_t size() const
Number of letters.
Definition: setalpha.hh:294
Print as plain (ASCII) text, escaped.
Definition: format.hh:28
set_alphabet(std::initializer_list< letter_t > l)
Definition: setalpha.hh:120
word_t get_word(std::istream &i) const
Extract and return the next word from i.
Definition: setalpha.hh:209
void swap(config::value &first, config::value &second)
typename letters_t::const_iterator const_iterator
Definition: setalpha.hh:253
typename letters_t::const_iterator iterator
Definition: setalpha.hh:252
symbol sname()
Definition: name.hh:65
Print as is. For instance, don&#39;t try to escape labels.
Definition: format.hh:24
Print for LaTeX.
Definition: format.hh:22
static set_alphabet make(std::istream &is)
Definition: setalpha.hh:52
auto pregenerators() const
All the "pregenerators", including the empty word.
Definition: setalpha.hh:276
letters_t alphabet_
Definition: setalpha.hh:371
typename L::letter_t letter_t
Definition: setalpha.hh:39
void require(Bool b, Args &&... args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:87
friend set_alphabet set_union(const set_alphabet &lhs, const set_alphabet &rhs)
Compute the union with another alphabet.
Definition: setalpha.hh:364
static symbol sname()
Definition: setalpha.hh:46
bool empty() const
Whether this alphabet has no letters.
Definition: setalpha.hh:288
STL namespace.
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:98
set_alphabet(const letters_t &l)
Definition: setalpha.hh:129
return res
Definition: multiply.hh:399
letter_t value_type
The type of our values, when seen as a container.
Definition: setalpha.hh:44
const_iterator end() const
Definition: setalpha.hh:270
bool open(bool o) const
Whether unknown letters should be added, or rejected.
Definition: setalpha.hh:136