]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/spirit/home/lex/lexer/lexertl/token.hpp
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / boost / spirit / home / lex / lexer / lexertl / token.hpp
CommitLineData
7c673cae
FG
1// Copyright (c) 2001-2011 Hartmut Kaiser
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6#if !defined(BOOST_SPIRIT_LEX_TOKEN_FEB_10_2008_0751PM)
7#define BOOST_SPIRIT_LEX_TOKEN_FEB_10_2008_0751PM
8
9#if defined(_MSC_VER)
10#pragma once
11#endif
12
13#include <boost/config.hpp>
14#include <boost/detail/workaround.hpp>
15#include <boost/spirit/home/qi/detail/assign_to.hpp>
16#include <boost/spirit/home/support/attributes.hpp>
17#include <boost/spirit/home/support/argument.hpp>
18#include <boost/spirit/home/support/detail/lexer/generator.hpp>
19#include <boost/spirit/home/support/detail/lexer/rules.hpp>
20#include <boost/spirit/home/support/detail/lexer/consts.hpp>
21#include <boost/spirit/home/support/utree/utree_traits_fwd.hpp>
22#include <boost/spirit/home/lex/lexer/terminals.hpp>
23#include <boost/fusion/include/vector.hpp>
24#include <boost/fusion/include/at.hpp>
25#include <boost/fusion/include/value_at.hpp>
26#include <boost/detail/iterator.hpp>
27#include <boost/variant.hpp>
28#include <boost/mpl/bool.hpp>
29#include <boost/mpl/vector.hpp>
30#include <boost/mpl/is_sequence.hpp>
31#include <boost/mpl/begin.hpp>
32#include <boost/mpl/insert.hpp>
33#include <boost/mpl/vector.hpp>
34#include <boost/mpl/if.hpp>
35#include <boost/mpl/or.hpp>
11fdf7f2 36#include <boost/type_traits/integral_promotion.hpp>
7c673cae
FG
37#include <boost/type_traits/is_same.hpp>
38#include <boost/range/iterator_range.hpp>
39#include <boost/static_assert.hpp>
40
41#if defined(BOOST_SPIRIT_DEBUG)
42#include <iosfwd>
43#endif
44
45namespace boost { namespace spirit { namespace lex { namespace lexertl
46{
47 ///////////////////////////////////////////////////////////////////////////
48 //
49 // The token is the type of the objects returned by default by the
50 // iterator.
51 //
52 // template parameters:
53 // Iterator The type of the iterator used to access the
54 // underlying character stream.
55 // AttributeTypes A mpl sequence containing the types of all
56 // required different token values to be supported
57 // by this token type.
58 // HasState A mpl::bool_ indicating, whether this token type
59 // should support lexer states.
60 // Idtype The type to use for the token id (defaults to
61 // std::size_t).
62 //
63 // It is possible to use other token types with the spirit::lex
64 // framework as well. If you plan to use a different type as your token
65 // type, you'll need to expose the following things from your token type
66 // to make it compatible with spirit::lex:
67 //
68 // typedefs
69 // iterator_type The type of the iterator used to access the
70 // underlying character stream.
71 //
72 // id_type The type of the token id used.
73 //
74 // methods
75 // default constructor
76 // This should initialize the token as an end of
77 // input token.
78 // constructors The prototype of the other required
79 // constructors should be:
80 //
81 // token(int)
82 // This constructor should initialize the token as
83 // an invalid token (not carrying any specific
84 // values)
85 //
86 // where: the int is used as a tag only and its value is
87 // ignored
88 //
89 // and:
90 //
91 // token(Idtype id, std::size_t state,
92 // iterator_type first, iterator_type last);
93 //
94 // where: id: token id
95 // state: lexer state this token was matched in
96 // first, last: pair of iterators marking the matched
97 // range in the underlying input stream
98 //
99 // accessors
100 // id() return the token id of the matched input sequence
101 // id(newid) set the token id of the token instance
102 //
103 // state() return the lexer state this token was matched in
104 //
105 // value() return the token value
106 //
107 // Additionally, you will have to implement a couple of helper functions
108 // in the same namespace as the token type: a comparison operator==() to
109 // compare your token instances, a token_is_valid() function and different
110 // specializations of the Spirit customization point
111 // assign_to_attribute_from_value as shown below.
112 //
113 ///////////////////////////////////////////////////////////////////////////
114 template <typename Iterator = char const*
115 , typename AttributeTypes = mpl::vector0<>
116 , typename HasState = mpl::true_
117 , typename Idtype = std::size_t>
118 struct token;
119
120 ///////////////////////////////////////////////////////////////////////////
121 // This specialization of the token type doesn't contain any item data and
122 // doesn't support working with lexer states.
123 ///////////////////////////////////////////////////////////////////////////
124 template <typename Iterator, typename Idtype>
125 struct token<Iterator, lex::omit, mpl::false_, Idtype>
126 {
127 typedef Iterator iterator_type;
128 typedef mpl::false_ has_state;
129 typedef Idtype id_type;
130 typedef unused_type token_value_type;
131
132 // default constructed tokens correspond to EOI tokens
133 token() : id_(id_type(boost::lexer::npos)) {}
134
135 // construct an invalid token
136 explicit token(int) : id_(id_type(0)) {}
137
138 token(id_type id, std::size_t) : id_(id) {}
139
140 token(id_type id, std::size_t, token_value_type)
141 : id_(id) {}
142
143 token_value_type& value() { static token_value_type u; return u; }
144 token_value_type const& value() const { return unused; }
145
146#if defined(BOOST_SPIRIT_DEBUG)
147 token(id_type id, std::size_t, Iterator const& first
148 , Iterator const& last)
149 : matched_(first, last)
150 , id_(id) {}
151#else
152 token(id_type id, std::size_t, Iterator const&, Iterator const&)
153 : id_(id) {}
154#endif
155
156 // this default conversion operator is needed to allow the direct
157 // usage of tokens in conjunction with the primitive parsers defined
158 // in Qi
11fdf7f2 159 operator id_type() const { return static_cast<id_type>(id_); }
7c673cae
FG
160
161 // Retrieve or set the token id of this token instance.
11fdf7f2 162 id_type id() const { return static_cast<id_type>(id_); }
7c673cae
FG
163 void id(id_type newid) { id_ = newid; }
164
165 std::size_t state() const { return 0; } // always '0' (INITIAL state)
166
167 bool is_valid() const
168 {
169 return 0 != id_ && id_type(boost::lexer::npos) != id_;
170 }
171
172#if defined(BOOST_SPIRIT_DEBUG)
173#if BOOST_WORKAROUND(BOOST_MSVC, == 1600)
174 // workaround for MSVC10 which has problems copying a default
175 // constructed iterator_range
176 token& operator= (token const& rhs)
177 {
178 if (this != &rhs)
179 {
180 id_ = rhs.id_;
181 if (is_valid())
182 matched_ = rhs.matched_;
183 }
184 return *this;
185 }
186#endif
187 std::pair<Iterator, Iterator> matched_;
188#endif
189
190 protected:
11fdf7f2 191 typename boost::integral_promotion<id_type>::type id_; // token id, 0 if nothing has been matched
7c673cae
FG
192 };
193
194#if defined(BOOST_SPIRIT_DEBUG)
195 template <typename Char, typename Traits, typename Iterator
196 , typename AttributeTypes, typename HasState, typename Idtype>
197 inline std::basic_ostream<Char, Traits>&
198 operator<< (std::basic_ostream<Char, Traits>& os
199 , token<Iterator, AttributeTypes, HasState, Idtype> const& t)
200 {
201 if (t.is_valid()) {
202 Iterator end = t.matched_.second;
203 for (Iterator it = t.matched_.first; it != end; ++it)
204 os << *it;
205 }
206 else {
207 os << "<invalid token>";
208 }
209 return os;
210 }
211#endif
212
213 ///////////////////////////////////////////////////////////////////////////
214 // This specialization of the token type doesn't contain any item data but
215 // supports working with lexer states.
216 ///////////////////////////////////////////////////////////////////////////
217 template <typename Iterator, typename Idtype>
218 struct token<Iterator, lex::omit, mpl::true_, Idtype>
219 : token<Iterator, lex::omit, mpl::false_, Idtype>
220 {
221 private:
222 typedef token<Iterator, lex::omit, mpl::false_, Idtype> base_type;
223
224 public:
225 typedef typename base_type::id_type id_type;
226 typedef Iterator iterator_type;
227 typedef mpl::true_ has_state;
228 typedef unused_type token_value_type;
229
230 // default constructed tokens correspond to EOI tokens
231 token() : state_(boost::lexer::npos) {}
232
233 // construct an invalid token
234 explicit token(int) : base_type(0), state_(boost::lexer::npos) {}
235
236 token(id_type id, std::size_t state)
237 : base_type(id, boost::lexer::npos), state_(state) {}
238
239 token(id_type id, std::size_t state, token_value_type)
240 : base_type(id, boost::lexer::npos, unused)
241 , state_(state) {}
242
243 token(id_type id, std::size_t state
244 , Iterator const& first, Iterator const& last)
245 : base_type(id, boost::lexer::npos, first, last)
246 , state_(state) {}
247
248 std::size_t state() const { return state_; }
249
250#if defined(BOOST_SPIRIT_DEBUG) && BOOST_WORKAROUND(BOOST_MSVC, == 1600)
251 // workaround for MSVC10 which has problems copying a default
252 // constructed iterator_range
253 token& operator= (token const& rhs)
254 {
255 if (this != &rhs)
256 {
257 this->base_type::operator=(static_cast<base_type const&>(rhs));
258 state_ = rhs.state_;
259 }
260 return *this;
261 }
262#endif
263
264 protected:
265 std::size_t state_; // lexer state this token was matched in
266 };
267
268 ///////////////////////////////////////////////////////////////////////////
269 // The generic version of the token type derives from the
270 // specialization above and adds a single data member holding the item
271 // data carried by the token instance.
272 ///////////////////////////////////////////////////////////////////////////
273 namespace detail
274 {
275 ///////////////////////////////////////////////////////////////////////
276 // Meta-function to calculate the type of the variant data item to be
277 // stored with each token instance.
278 //
279 // Note: The iterator pair needs to be the first type in the list of
280 // types supported by the generated variant type (this is being
281 // used to identify whether the stored data item in a particular
282 // token instance needs to be converted from the pair of
283 // iterators (see the first of the assign_to_attribute_from_value
284 // specializations below).
285 ///////////////////////////////////////////////////////////////////////
286 template <typename IteratorPair, typename AttributeTypes>
287 struct token_value_typesequence
288 {
289 typedef typename mpl::insert<
290 AttributeTypes
291 , typename mpl::begin<AttributeTypes>::type
292 , IteratorPair
293 >::type sequence_type;
294 typedef typename make_variant_over<sequence_type>::type type;
295 };
296
297 ///////////////////////////////////////////////////////////////////////
298 // The type of the data item stored with a token instance is defined
299 // by the template parameter 'AttributeTypes' and may be:
300 //
301 // lex::omit: no data item is stored with the token
302 // instance (this is handled by the
303 // specializations of the token class
304 // below)
305 // mpl::vector0<>: each token instance stores a pair of
306 // iterators pointing to the matched input
307 // sequence
308 // mpl::vector<...>: each token instance stores a variant being
309 // able to store the pair of iterators pointing
310 // to the matched input sequence, or any of the
311 // types a specified in the mpl::vector<>
312 //
313 // All this is done to ensure the token type is as small (in terms
314 // of its byte-size) as possible.
315 ///////////////////////////////////////////////////////////////////////
316 template <typename IteratorPair, typename AttributeTypes>
317 struct token_value_type
318 : mpl::eval_if<
319 mpl::or_<
320 is_same<AttributeTypes, mpl::vector0<> >
321 , is_same<AttributeTypes, mpl::vector<> > >
322 , mpl::identity<IteratorPair>
323 , token_value_typesequence<IteratorPair, AttributeTypes> >
324 {};
325 }
326
327 template <typename Iterator, typename AttributeTypes, typename HasState
328 , typename Idtype>
329 struct token : token<Iterator, lex::omit, HasState, Idtype>
330 {
331 private: // precondition assertions
332 BOOST_STATIC_ASSERT((mpl::is_sequence<AttributeTypes>::value ||
333 is_same<AttributeTypes, lex::omit>::value));
334 typedef token<Iterator, lex::omit, HasState, Idtype> base_type;
335
336 protected:
337 // If no additional token value types are given, the token will
338 // hold the plain pair of iterators pointing to the matched range
339 // in the underlying input sequence. Otherwise the token value is
340 // stored as a variant and will again hold the pair of iterators but
341 // is able to hold any of the given data types as well. The conversion
342 // from the iterator pair to the required data type is done when it is
343 // accessed for the first time.
344 typedef iterator_range<Iterator> iterpair_type;
345
346 public:
347 typedef typename base_type::id_type id_type;
348 typedef typename detail::token_value_type<
349 iterpair_type, AttributeTypes
350 >::type token_value_type;
351
352 typedef Iterator iterator_type;
353
354 // default constructed tokens correspond to EOI tokens
355 token() : value_(iterpair_type(iterator_type(), iterator_type())) {}
356
357 // construct an invalid token
358 explicit token(int)
359 : base_type(0)
360 , value_(iterpair_type(iterator_type(), iterator_type())) {}
361
362 token(id_type id, std::size_t state, token_value_type const& value)
363 : base_type(id, state, value)
364 , value_(value) {}
365
366 token(id_type id, std::size_t state, Iterator const& first
367 , Iterator const& last)
368 : base_type(id, state, first, last)
369 , value_(iterpair_type(first, last)) {}
370
371 token_value_type& value() { return value_; }
372 token_value_type const& value() const { return value_; }
373
374#if BOOST_WORKAROUND(BOOST_MSVC, == 1600)
375 // workaround for MSVC10 which has problems copying a default
376 // constructed iterator_range
377 token& operator= (token const& rhs)
378 {
379 if (this != &rhs)
380 {
381 this->base_type::operator=(static_cast<base_type const&>(rhs));
382 if (this->is_valid())
383 value_ = rhs.value_;
384 }
385 return *this;
386 }
387#endif
388
389 protected:
390 token_value_type value_; // token value, by default a pair of iterators
391 };
392
393 ///////////////////////////////////////////////////////////////////////////
394 // tokens are considered equal, if their id's match (these are unique)
395 template <typename Iterator, typename AttributeTypes, typename HasState
396 , typename Idtype>
397 inline bool
398 operator== (token<Iterator, AttributeTypes, HasState, Idtype> const& lhs,
399 token<Iterator, AttributeTypes, HasState, Idtype> const& rhs)
400 {
401 return lhs.id() == rhs.id();
402 }
403
404 ///////////////////////////////////////////////////////////////////////////
405 // This overload is needed by the multi_pass/functor_input_policy to
406 // validate a token instance. It has to be defined in the same namespace
407 // as the token class itself to allow ADL to find it.
408 ///////////////////////////////////////////////////////////////////////////
409 template <typename Iterator, typename AttributeTypes, typename HasState
410 , typename Idtype>
411 inline bool
412 token_is_valid(token<Iterator, AttributeTypes, HasState, Idtype> const& t)
413 {
414 return t.is_valid();
415 }
416}}}}
417
418namespace boost { namespace spirit { namespace traits
419{
420 ///////////////////////////////////////////////////////////////////////////
421 // We have to provide specializations for the customization point
422 // assign_to_attribute_from_value allowing to extract the needed value
423 // from the token.
424 ///////////////////////////////////////////////////////////////////////////
425
426 // This is called from the parse function of token_def if the token_def
427 // has been defined to carry a special attribute type
428 template <typename Attribute, typename Iterator, typename AttributeTypes
429 , typename HasState, typename Idtype>
430 struct assign_to_attribute_from_value<Attribute
431 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
432 {
433 static void
434 call(lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> const& t
435 , Attribute& attr)
436 {
437 // The goal of this function is to avoid the conversion of the pair of
438 // iterators (to the matched character sequence) into the token value
439 // of the required type being done more than once. For this purpose it
440 // checks whether the stored value type is still the default one (pair
441 // of iterators) and if yes, replaces the pair of iterators with the
442 // converted value to be returned from subsequent calls.
443
444 if (0 == t.value().which()) {
445 // first access to the token value
446 typedef iterator_range<Iterator> iterpair_type;
447 iterpair_type const& ip = boost::get<iterpair_type>(t.value());
448
449 // Interestingly enough we use the assign_to() framework defined in
450 // Spirit.Qi allowing to convert the pair of iterators to almost any
451 // required type (assign_to(), if available, uses the standard Spirit
452 // parsers to do the conversion).
453 spirit::traits::assign_to(ip.begin(), ip.end(), attr);
454
455 // If you get an error during the compilation of the following
456 // assignment expression, you probably forgot to list one or more
457 // types used as token value types (in your token_def<...>
458 // definitions) in your definition of the token class. I.e. any token
459 // value type used for a token_def<...> definition has to be listed
460 // during the declaration of the token type to use. For instance let's
461 // assume we have two token_def's:
462 //
463 // token_def<int> number; number = "...";
464 // token_def<std::string> identifier; identifier = "...";
465 //
466 // Then you'll have to use the following token type definition
467 // (assuming you are using the token class):
468 //
469 // typedef mpl::vector<int, std::string> token_values;
470 // typedef token<base_iter_type, token_values> token_type;
471 //
472 // where: base_iter_type is the iterator type used to expose the
473 // underlying input stream.
474 //
475 // This token_type has to be used as the second template parameter
476 // to the lexer class:
477 //
478 // typedef lexer<base_iter_type, token_type> lexer_type;
479 //
480 // again, assuming you're using the lexer<> template for your
481 // tokenization.
482
483 typedef lex::lexertl::token<
484 Iterator, AttributeTypes, HasState, Idtype> token_type;
485 spirit::traits::assign_to(
486 attr, const_cast<token_type&>(t).value()); // re-assign value
487 }
488 else {
489 // reuse the already assigned value
490 spirit::traits::assign_to(boost::get<Attribute>(t.value()), attr);
491 }
492 }
493 };
494
495 template <typename Attribute, typename Iterator, typename AttributeTypes
496 , typename HasState, typename Idtype>
497 struct assign_to_container_from_value<Attribute
498 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
499 : assign_to_attribute_from_value<Attribute
500 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
501 {};
502
503 template <typename Iterator, typename AttributeTypes
504 , typename HasState, typename Idtype>
505 struct assign_to_container_from_value<utree
506 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
507 : assign_to_attribute_from_value<utree
508 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
509 {};
510
511 template <typename Iterator>
512 struct assign_to_container_from_value<
513 iterator_range<Iterator>, iterator_range<Iterator> >
514 {
515 static void
516 call(iterator_range<Iterator> const& val, iterator_range<Iterator>& attr)
517 {
518 attr = val;
519 }
520 };
521
522 // These are called from the parse function of token_def if the token type
523 // has no special attribute type assigned
524 template <typename Attribute, typename Iterator, typename HasState
525 , typename Idtype>
526 struct assign_to_attribute_from_value<Attribute
527 , lex::lexertl::token<Iterator, mpl::vector0<>, HasState, Idtype> >
528 {
529 static void
530 call(lex::lexertl::token<Iterator, mpl::vector0<>, HasState, Idtype> const& t
531 , Attribute& attr)
532 {
533 // The default type returned by the token_def parser component (if
534 // it has no token value type assigned) is the pair of iterators
535 // to the matched character sequence.
536 spirit::traits::assign_to(t.value().begin(), t.value().end(), attr);
537 }
538 };
539
540// template <typename Attribute, typename Iterator, typename HasState
541// , typename Idtype>
542// struct assign_to_container_from_value<Attribute
543// , lex::lexertl::token<Iterator, mpl::vector0<>, HasState, Idtype> >
544// : assign_to_attribute_from_value<Attribute
545// , lex::lexertl::token<Iterator, mpl::vector0<>, HasState, Idtype> >
546// {};
547
548 // same as above but using mpl::vector<> instead of mpl::vector0<>
549 template <typename Attribute, typename Iterator, typename HasState
550 , typename Idtype>
551 struct assign_to_attribute_from_value<Attribute
552 , lex::lexertl::token<Iterator, mpl::vector<>, HasState, Idtype> >
553 {
554 static void
555 call(lex::lexertl::token<Iterator, mpl::vector<>, HasState, Idtype> const& t
556 , Attribute& attr)
557 {
558 // The default type returned by the token_def parser component (if
559 // it has no token value type assigned) is the pair of iterators
560 // to the matched character sequence.
561 spirit::traits::assign_to(t.value().begin(), t.value().end(), attr);
562 }
563 };
564
565// template <typename Attribute, typename Iterator, typename HasState
566// , typename Idtype>
567// struct assign_to_container_from_value<Attribute
568// , lex::lexertl::token<Iterator, mpl::vector<>, HasState, Idtype> >
569// : assign_to_attribute_from_value<Attribute
570// , lex::lexertl::token<Iterator, mpl::vector<>, HasState, Idtype> >
571// {};
572
573 // This is called from the parse function of token_def if the token type
574 // has been explicitly omitted (i.e. no attribute value is used), which
575 // essentially means that every attribute gets initialized using default
576 // constructed values.
577 template <typename Attribute, typename Iterator, typename HasState
578 , typename Idtype>
579 struct assign_to_attribute_from_value<Attribute
580 , lex::lexertl::token<Iterator, lex::omit, HasState, Idtype> >
581 {
582 static void
583 call(lex::lexertl::token<Iterator, lex::omit, HasState, Idtype> const& t
584 , Attribute& attr)
585 {
586 // do nothing
587 }
588 };
589
590 template <typename Attribute, typename Iterator, typename HasState
591 , typename Idtype>
592 struct assign_to_container_from_value<Attribute
593 , lex::lexertl::token<Iterator, lex::omit, HasState, Idtype> >
594 : assign_to_attribute_from_value<Attribute
595 , lex::lexertl::token<Iterator, lex::omit, HasState, Idtype> >
596 {};
597
598 // This is called from the parse function of lexer_def_
599 template <typename Iterator, typename AttributeTypes, typename HasState
600 , typename Idtype_, typename Idtype>
601 struct assign_to_attribute_from_value<
602 fusion::vector2<Idtype_, iterator_range<Iterator> >
603 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
604 {
605 static void
606 call(lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> const& t
607 , fusion::vector2<Idtype_, iterator_range<Iterator> >& attr)
608 {
609 // The type returned by the lexer_def_ parser components is a
610 // fusion::vector containing the token id of the matched token
611 // and the pair of iterators to the matched character sequence.
612 typedef iterator_range<Iterator> iterpair_type;
613 typedef fusion::vector2<Idtype_, iterator_range<Iterator> >
614 attribute_type;
615
616 iterpair_type const& ip = boost::get<iterpair_type>(t.value());
617 attr = attribute_type(t.id(), ip);
618 }
619 };
620
621 template <typename Iterator, typename AttributeTypes, typename HasState
622 , typename Idtype_, typename Idtype>
623 struct assign_to_container_from_value<
624 fusion::vector2<Idtype_, iterator_range<Iterator> >
625 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
626 : assign_to_attribute_from_value<
627 fusion::vector2<Idtype_, iterator_range<Iterator> >
628 , lex::lexertl::token<Iterator, AttributeTypes, HasState, Idtype> >
629 {};
630
631 ///////////////////////////////////////////////////////////////////////////
632 // Overload debug output for a single token, this integrates lexer tokens
633 // with Qi's simple_trace debug facilities
634 template <typename Iterator, typename Attribute, typename HasState
635 , typename Idtype>
636 struct token_printer_debug<
637 lex::lexertl::token<Iterator, Attribute, HasState, Idtype> >
638 {
639 typedef lex::lexertl::token<Iterator, Attribute, HasState, Idtype> token_type;
640
641 template <typename Out>
642 static void print(Out& out, token_type const& val)
643 {
644 out << '[';
645 spirit::traits::print_token(out, val.value());
646 out << ']';
647 }
648 };
649}}}
650
651#endif