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