cfgfile 0.2.11
Loading...
Searching...
No Matches
parser.hpp
Go to the documentation of this file.
1
31#ifndef CFGFILE__PARSER_HPP__INCLUDED
32#define CFGFILE__PARSER_HPP__INCLUDED
33
34// cfgfile include.
35#include "types.hpp"
36#include "input_stream.hpp"
37#include "tag.hpp"
38#include "exceptions.hpp"
39#include "lex.hpp"
40#include "parser_info.hpp"
41#include "const.hpp"
42#include "string_format.hpp"
43
44// C++ include.
45#include <memory>
46#include <stack>
47
48#if defined( CFGFILE_QT_SUPPORT ) && defined( CFGFILE_XML_SUPPORT )
49// Qt include.
50#include <QDomDocument>
51#include <QDomElement>
52#endif
53
54
55namespace cfgfile {
56
57namespace details {
58
59//
60// parser_base_t
61//
62
64template< typename Trait = string_trait_t >
66public:
68 : m_tag( tag )
69 {
70 }
71
73 {
74 }
75
77 virtual void parse( const typename Trait::string_t & file_name ) = 0;
78
79protected:
80
82 {
83 if( !m_stack.empty() )
85 Trait::from_ascii( "Unexpected end of file. "
86 "Still unfinished tag \"" ) + m_stack.top()->name() +
87 Trait::from_ascii( "\"." ) );
88
91 Trait::from_ascii( "Undefined mandatory tag: \"" ) +
92 m_tag.name() + Trait::from_ascii( "\"." ) );
93 }
94
95protected:
99 std::stack< tag_t< Trait > * > m_stack;
100}; // class parser_base_t
101
102
103//
104// parser_conffile_impl_t
105//
106
108template< typename Trait = string_trait_t >
110 : public parser_base_t< Trait >
111{
112public:
119
123
125 void parse( const typename Trait::string_t & file_name ) override
126 {
127 if( !start_first_tag_parsing() )
128 return;
129
130 lexeme_t< Trait > lexeme = m_lex.next_lexeme();
131
132 while( !lexeme.is_null() )
133 {
134 if( !this->m_stack.empty() )
135 {
136 switch( lexeme.type() )
137 {
139 start_tag_parsing( *this->m_stack.top(),
140 this->m_stack.top()->children() );
141 break;
142
144 this->m_stack.top()->on_string( parser_info_t< Trait >(
145 file_name,
146 m_lex.line_number(),
147 m_lex.column_number() ),
148 lexeme.value() );
149 break;
150
152 {
153 this->m_stack.top()->on_finish( parser_info_t< Trait >(
154 file_name,
155 m_lex.line_number(),
156 m_lex.column_number() ) );
157 this->m_stack.pop();
158 }
159 break;
160
161 default:
162 break;
163 }
164 }
165 else
167 Trait::from_ascii( "Unexpected content. "
168 "We've finished parsing, but we've got this: \"" ) +
169 lexeme.value() + Trait::from_ascii( "\". " ) +
170 Trait::from_ascii( "In file \"" ) + file_name +
171 Trait::from_ascii( "\" on line " ) +
172 Trait::to_string( m_lex.line_number() ) +
173 Trait::from_ascii( "." ) );
174
175
176 lexeme = m_lex.next_lexeme();
177 }
178
180 }
181
182private:
183
184 bool start_first_tag_parsing()
185 {
186 lexeme_t< Trait > lexeme = m_lex.next_lexeme();
187
188 if( this->m_tag.is_mandatory() && lexeme.type() == lexeme_type_t::null )
190 Trait::from_ascii( "Unexpected end of file. "
191 "Undefined mandatory tag \"" ) + this->m_tag.name() +
192 Trait::from_ascii( "\". In file \"" ) +
193 m_lex.input_stream().file_name() +
194 Trait::from_ascii( "\" on line " ) +
195 Trait::to_string( m_lex.line_number() ) +
196 Trait::from_ascii( "." ) );
197 else if( !this->m_tag.is_mandatory() &&
198 lexeme.type() == lexeme_type_t::null )
199 return false;
200 else if( lexeme.type() != lexeme_type_t::start )
202 Trait::from_ascii( "Expected start curl brace, "
203 "but we've got \"" ) + lexeme.value() +
204 Trait::from_ascii( "\". In file \"" ) +
205 m_lex.input_stream().file_name() +
206 Trait::from_ascii( "\" on line " ) +
207 Trait::to_string( m_lex.line_number() ) +
208 Trait::from_ascii( "." ) );
209
210 lexeme = m_lex.next_lexeme();
211
212 if( !start_tag_parsing( lexeme, this->m_tag ) )
214 Trait::from_ascii( "Unexpected tag name. "
215 "We expected \"" ) + this->m_tag.name() +
216 Trait::from_ascii( "\", but we've got \"" ) + lexeme.value() +
217 Trait::from_ascii( "\". In file \"" ) +
218 m_lex.input_stream().file_name() +
219 Trait::from_ascii( "\" on line " ) +
220 Trait::to_string( m_lex.line_number() ) +
221 Trait::from_ascii( "." ) );
222
223 return true;
224 }
225
226 bool start_tag_parsing( const lexeme_t< Trait > & lexeme,
228 {
229 if( lexeme.type() == lexeme_type_t::start )
231 Trait::from_ascii( "Unexpected start curl brace. "
232 "We expected tag name, but we've got start curl brace. "
233 "In file \"" ) + m_lex.input_stream().file_name() +
234 Trait::from_ascii( "\" on line " ) +
235 Trait::to_string( m_lex.line_number() ) +
236 Trait::from_ascii( "." ) );
237 else if( lexeme.type() == lexeme_type_t::finish )
239 Trait::from_ascii( "Unexpected finish curl brace. "
240 "We expected tag name, but we've got finish curl brace. "
241 "In file \"" ) + m_lex.input_stream().file_name() +
242 Trait::from_ascii( "\" on line " ) +
243 Trait::to_string( m_lex.line_number() ) +
244 Trait::from_ascii( "." ) );
245 else if( lexeme.type() == lexeme_type_t::null )
247 Trait::from_ascii( "Unexpected end of file. "
248 "In file \"" ) + m_lex.input_stream().file_name() +
249 Trait::from_ascii( "\" on line " ) +
250 Trait::to_string( m_lex.line_number() ) +
251 Trait::from_ascii( "." ) );
252 else if( tag.name() == lexeme.value() )
253 {
254 this->m_stack.push( &tag );
255
256 tag.on_start( parser_info_t< Trait >(
257 m_lex.input_stream().file_name(),
258 m_lex.line_number(),
259 m_lex.column_number() ) );
260
261 return true;
262 }
263
264 return false;
265 }
266
267 void start_tag_parsing( const tag_t< Trait > & parent,
268 const typename tag_t< Trait >::child_tags_list_t & list )
269 {
270 lexeme_t< Trait > lexeme = m_lex.next_lexeme();
271
272 bool tag_found = false;
273
274 for( tag_t< Trait > * tag : list )
275 {
276 if( start_tag_parsing( lexeme, *tag ) )
277 {
278 tag_found = true;
279
280 break;
281 }
282 }
283
284 if( !tag_found )
285 throw exception_t< Trait >(
286 Trait::from_ascii( "Unexpected tag name. "
287 "We expected one child tag of tag \"" ) +
288 parent.name() +
289 Trait::from_ascii( "\", but we've got \"" ) + lexeme.value() +
290 Trait::from_ascii( "\". In file \"" ) +
291 m_lex.input_stream().file_name() +
292 Trait::from_ascii( "\" on line " ) +
293 Trait::to_string( m_lex.line_number() ) +
294 Trait::from_ascii( "." ) );
295 }
296
297private:
299 lexical_analyzer_t< Trait > m_lex;
300}; // class parser_conffile_impl_t
301
302#if defined( CFGFILE_QT_SUPPORT ) && defined( CFGFILE_XML_SUPPORT )
303
304//
305// parser_dom_impl_t
306//
307
309template< typename Trait = qstring_trait_t >
311 : public parser_base_t< Trait >
312{
313public:
315 : parser_base_t< Trait >( tag )
316 , m_dom( dom )
317 {
318 }
319
321 {
322 }
323
325 void parse( const typename Trait::string_t & file_name ) override
326 {
327 QDomElement element = m_dom.documentElement();
328
329 if( element.isNull() && this->m_tag.is_mandatory() )
331 Trait::from_ascii( "Unexpected end of file. "
332 "Undefined mandatory tag \"" ) + this->m_tag.name() +
333 Trait::from_ascii( "\". In file \"" ) + file_name +
334 Trait::from_ascii( "\" on line " ) +
335 Trait::to_string( element.lineNumber() ) +
336 Trait::from_ascii( "." ) );
337
338 if( !element.isNull() )
339 {
340 if( element.tagName() != this->m_tag.name() )
342 Trait::from_ascii( "Unexpected tag name. "
343 "We expected \"" ) + this->m_tag.name() +
344 Trait::from_ascii( "\", but we've got \"" ) +
345 typename Trait::string_t( element.tagName() ) +
346 Trait::from_ascii( "\". In file \"" ) + file_name +
347 Trait::from_ascii( "\" on line " ) +
348 Trait::to_string( element.lineNumber() ) +
349 Trait::from_ascii( "." ) );
350
351 this->m_stack.push( &this->m_tag );
352
353 this->m_tag.on_start( parser_info_t< Trait >( file_name,
354 element.lineNumber(),
355 element.columnNumber() ) );
356 }
357
358 parse_tag( element, file_name );
359
360 if( !element.isNull() )
361 {
362 this->m_stack.top()->on_finish( parser_info_t< Trait >(
363 file_name,
364 element.lineNumber(),
365 element.columnNumber() ) );
366
367 this->m_stack.pop();
368 }
369
371 }
372
373private:
375 void parse_tag( const QDomElement & e,
376 const typename Trait::string_t & file_name )
377 {
378 for( QDomNode n = e.firstChild(); !n.isNull();
379 n = n.nextSibling() )
380 {
381 QDomElement child = n.toElement();
382
383 if( !child.isNull() )
384 {
385 tag_t< Trait > * tag = find_tag( child.tagName(),
386 this->m_stack.top()->children() );
387
388 if( !tag )
390 Trait::from_ascii( "Unexpected tag name. "
391 "We expected one child tag of tag \"" ) +
392 this->m_stack.top()->name() +
393 Trait::from_ascii( "\", but we've got \"" ) +
394 typename Trait::string_t( child.tagName() ) +
395 Trait::from_ascii( "\". In file \"" ) + file_name +
396 Trait::from_ascii( "\" on line " ) +
397 Trait::to_string( child.lineNumber() ) +
398 Trait::from_ascii( "." ) );
399
400 this->m_stack.push( tag );
401
402 tag->on_start( parser_info_t< Trait >( file_name,
403 child.lineNumber(),
404 child.columnNumber() ) );
405
406 const QDomNamedNodeMap attributes = child.attributes();
407
408 for( int i = 1; i <= attributes.count(); ++i )
409 {
410 const QDomAttr attr = attributes.namedItem(
411 QString( "a" ) + QString::number( i ) ).toAttr();
412
413 if( !attr.isNull() )
414 {
415 typename Trait::string_t value;
416
417 try {
418 value = from_cfgfile_format< Trait >( attr.value()
420 .append( const_t< Trait >::c_quotes ) );
421 }
422 catch( const exception_t< Trait > & x )
423 {
424 throw exception_t< Trait > ( x.desc() +
425 Trait::from_ascii( " In file \"" ) + file_name +
426 Trait::from_ascii( "\" on line " ) +
427 Trait::to_string( attr.lineNumber() ) +
428 Trait::from_ascii( "." ) );
429 }
430
431 tag->on_string( parser_info_t< Trait >(
432 file_name,
433 attr.lineNumber(),
434 attr.columnNumber() ),
435 value );
436 }
437 }
438
439 parse_tag( child, file_name );
440
441 tag->on_finish( parser_info_t< Trait >(
442 file_name,
443 child.lineNumber(),
444 child.columnNumber() ) );
445
446 this->m_stack.pop();
447 }
448 else
449 {
450 if( !n.toComment().isNull() )
451 continue;
452
453 QDomText text = n.toText();
454
455 if( !text.isNull() )
456 {
457 typename Trait::string_t value;
458
459 try {
460 value = from_cfgfile_format< Trait >( text.data() );
461 }
462 catch( const exception_t< Trait > & x )
463 {
464 throw exception_t< Trait > ( x.desc() +
465 Trait::from_ascii( " In file \"" ) + file_name +
466 Trait::from_ascii( "\" on line " ) +
467 Trait::to_string( text.lineNumber() ) +
468 Trait::from_ascii( "." ) );
469 }
470
471 this->m_stack.top()->on_string( parser_info_t< Trait >(
472 file_name,
473 text.lineNumber(),
474 text.columnNumber() ),
475 value );
476 }
477 else
478 throw exception_t< Trait >(
479 Trait::from_ascii( "Unexpected tag name. "
480 "We expected one child tag of tag \"" ) +
481 this->m_stack.top()->name() +
482 Trait::from_ascii( "\", but we've got \"" ) +
483 typename Trait::string_t( n.nodeName() ) +
484 Trait::from_ascii( "\". In file \"" ) + file_name +
485 Trait::from_ascii( "\" on line " ) +
486 Trait::to_string( n.lineNumber() ) +
487 Trait::from_ascii( "." ) );
488 }
489 }
490 }
491
493 tag_t< Trait > * find_tag( const typename Trait::string_t & name,
494 const typename tag_t< Trait >::child_tags_list_t & list )
495 {
496 for( tag_t< Trait > * tag : list )
497 {
498 if( tag->name() == name )
499 return tag;
500 }
501
502 return 0;
503 }
504
505private:
506 const QDomDocument & m_dom;
507}; // class parser_dom_impl_t
508
509#endif
510
511} /* namespace details */
512
513
514//
515// parser_t
516//
517
519template< typename Trait = string_trait_t >
521public:
523 : m_d( std::make_unique< details::parser_conffile_impl_t< Trait > >
524 ( tag, stream ) )
525 {
526 }
527
528#if defined( CFGFILE_QT_SUPPORT ) && defined( CFGFILE_XML_SUPPORT )
530 : m_d( std::make_unique< details::parser_dom_impl_t< Trait > >
531 ( tag, dom ) )
532 {
533 }
534#endif
535
540 void parse( const typename Trait::string_t & file_name )
541 {
542 m_d->parse( file_name );
543 }
544
545private:
547
548 std::unique_ptr< details::parser_base_t< Trait > > m_d;
549}; // class parser_t
550
551} /* namespace cfgfile */
552
553#endif // CFGFILE__PARSER_HPP__INCLUDED
Base implementation of parser.
Definition parser.hpp:65
tag_t< Trait > & m_tag
Tag.
Definition parser.hpp:97
void check_parser_state_after_parsing()
Definition parser.hpp:81
std::stack< tag_t< Trait > * > m_stack
Stack of tags.
Definition parser.hpp:99
parser_base_t(tag_t< Trait > &tag)
Definition parser.hpp:67
virtual ~parser_base_t()
Definition parser.hpp:72
virtual void parse(const typename Trait::string_t &file_name)=0
Do parsing.
Implementation of parser in cfgfile format.
Definition parser.hpp:111
~parser_conffile_impl_t()
Definition parser.hpp:120
parser_conffile_impl_t(tag_t< Trait > &tag, input_stream_t< Trait > &stream)
Definition parser.hpp:113
void parse(const typename Trait::string_t &file_name) override
Do parsing.
Definition parser.hpp:125
Implementation of parser in XML format.
Definition parser.hpp:312
parser_dom_impl_t(tag_t< Trait > &tag, const QDomDocument &dom)
Definition parser.hpp:314
~parser_dom_impl_t()
Definition parser.hpp:320
void parse(const typename Trait::string_t &file_name) override
Do parsing.
Definition parser.hpp:325
Exception in the library.
Definition exceptions.hpp:51
const Trait::string_t & desc() const noexcept
Definition exceptions.hpp:65
Parser of the configuration file.
Definition parser.hpp:520
parser_t(tag_t< Trait > &tag, input_stream_t< Trait > &stream)
Definition parser.hpp:522
parser_t(tag_t< Trait > &tag, const QDomDocument &dom)
Definition parser.hpp:529
void parse(const typename Trait::string_t &file_name)
Parse input stream.
Definition parser.hpp:540
virtual void on_string(const parser_info_t< Trait > &info, const typename Trait::string_t &str)=0
Called when string found.
bool is_mandatory() const
Definition tag.hpp:146
virtual void on_start(const parser_info_t< Trait > &info)
Called when tag parsing started.
Definition tag.hpp:197
bool is_defined() const
Definition tag.hpp:152
const Trait::string_t & name() const
Definition tag.hpp:140
virtual void on_finish(const parser_info_t< Trait > &info)=0
Called when tag parsing finished.
Definition const.hpp:38
@ finish
Finish tag lexeme "}".
@ string
String lexeme.
@ start
Start tag lexeme "{".
Definition const.hpp:45
#define DISABLE_COPY(Class)
Macro for disabling copy.
Definition types.hpp:580