]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/quickbook/src/main_grammar.cpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / tools / quickbook / src / main_grammar.cpp
1 /*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 http://spirit.sourceforge.net/
5
6 Use, modification and distribution is subject to the Boost Software
7 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
10
11 #include <boost/spirit/include/classic_attribute.hpp>
12 #include <boost/spirit/include/classic_chset.hpp>
13 #include <boost/spirit/include/classic_core.hpp>
14 #include <boost/spirit/include/classic_if.hpp>
15 #include <boost/spirit/include/classic_lazy.hpp>
16 #include <boost/spirit/include/classic_loops.hpp>
17 #include <boost/spirit/include/phoenix1_primitives.hpp>
18 #include "actions.hpp"
19 #include "block_tags.hpp"
20 #include "grammar_impl.hpp"
21 #include "parsers.hpp"
22 #include "phrase_tags.hpp"
23 #include "scoped.hpp"
24 #include "state.hpp"
25 #include "stream.hpp"
26 #include "template_tags.hpp"
27 #include "utils.hpp"
28
29 namespace quickbook
30 {
31 namespace cl = boost::spirit::classic;
32
33 struct list_stack_item
34 {
35 // Is this the root of the context
36 // (e.g. top, template, table cell etc.)
37 enum list_item_type
38 {
39 syntactic_list, // In a list marked up '*' or '#'
40 top_level, // At the top level of a parse
41 // (might be a template body)
42 nested_block // Nested in a block element.
43 } type;
44
45 unsigned int indent; // Indent of list marker
46 // (or paragraph if not in a list)
47 unsigned int indent2; // Indent of paragraph
48 char mark; // List mark, '\0' if not in a list.
49
50 // Example of inside a list:
51 //
52 // |indent
53 // * List item
54 // |indent2
55
56 explicit list_stack_item(list_item_type r)
57 : type(r), indent(0), indent2(0), mark('\0')
58 {
59 }
60
61 explicit list_stack_item(
62 char mark_, unsigned int indent_, unsigned int indent2_)
63 : type(syntactic_list)
64 , indent(indent_)
65 , indent2(indent2_)
66 , mark(mark_)
67 {
68 }
69 };
70
71 struct block_types
72 {
73 enum values
74 {
75 none,
76 code,
77 list,
78 paragraph
79 };
80 };
81
82 struct main_grammar_local
83 {
84 ////////////////////////////////////////////////////////////////////////
85 // Local actions
86
87 void start_blocks_impl(parse_iterator first, parse_iterator last);
88 void start_nested_blocks_impl(
89 parse_iterator first, parse_iterator last);
90 void end_blocks_impl(parse_iterator first, parse_iterator last);
91 void check_indentation_impl(parse_iterator first, parse_iterator last);
92 void check_code_block_impl(parse_iterator first, parse_iterator last);
93 void plain_block(string_iterator first, string_iterator last);
94 void list_block(
95 string_iterator first,
96 string_iterator mark_pos,
97 string_iterator last);
98 void clear_stack();
99
100 ////////////////////////////////////////////////////////////////////////
101 // Local members
102
103 cl::rule<scanner> template_phrase, top_level, indent_check,
104 paragraph_separator, inside_paragraph, code, code_line, blank_line,
105 hr, inline_code, skip_inline_code, template_, attribute_template,
106 template_body, code_block, skip_code_block, macro, template_args,
107 template_args_1_4, template_arg_1_4, template_inner_arg_1_4,
108 brackets_1_4, template_args_1_5, template_arg_1_5,
109 template_arg_1_5_content, template_inner_arg_1_5, brackets_1_5,
110 template_args_1_6, template_arg_1_6, template_arg_1_6_content,
111 break_, command_line_macro_identifier, dummy_block,
112 line_dummy_block, square_brackets, error_brackets, skip_escape;
113
114 struct block_context_closure
115 : cl::closure<block_context_closure, element_info::context>
116 {
117 // Mask used to determine whether or not an element is a block
118 // element.
119 member1 is_block_mask;
120 };
121
122 cl::rule<scanner> simple_markup, simple_markup_end;
123
124 cl::rule<scanner> paragraph;
125 cl::rule<scanner> list;
126 cl::rule<scanner, block_context_closure::context_t>
127 syntactic_block_item;
128 cl::rule<scanner> common;
129 cl::rule<scanner> element;
130
131 // state
132 std::stack<list_stack_item> list_stack;
133 unsigned int list_indent;
134 bool no_eols;
135 element_info::context context;
136 char mark; // Simple markup's deliminator
137 bool still_in_block; // Inside a syntatic block
138
139 // transitory state
140 block_types::values block_type;
141 element_info info;
142 element_info::type_enum element_type;
143
144 // state
145 quickbook::state& state_;
146
147 ////////////////////////////////////////////////////////////////////////
148 // Local constructor
149
150 main_grammar_local(quickbook::state& state)
151 : list_stack()
152 , list_indent(0)
153 , no_eols(true)
154 , context(element_info::in_top_level)
155 , mark('\0')
156 , state_(state)
157 {
158 }
159 };
160
161 struct process_element_impl : scoped_action_base
162 {
163 process_element_impl(main_grammar_local& l_)
164 : l(l_), pushed_source_mode_(false), element_context_error_(false)
165 {
166 }
167
168 bool start()
169 {
170 // This element doesn't exist in the current language version.
171 if (qbk_version_n < l.info.qbk_version) return false;
172
173 // The element is not allowed in this context.
174 if (!(l.info.type & l.context)) {
175 if (qbk_version_n < 107u) {
176 return false;
177 }
178 else {
179 element_context_error_ = true;
180 }
181 }
182
183 info_ = l.info;
184
185 if (info_.type != element_info::phrase &&
186 info_.type != element_info::maybe_block) {
187 paragraph_action para(l.state_);
188 para();
189 }
190
191 assert(l.state_.values.builder.empty());
192
193 if (l.state_.source_mode_next &&
194 info_.type != element_info::maybe_block) {
195 l.state_.push_tagged_source_mode(l.state_.source_mode_next);
196 pushed_source_mode_ = true;
197 l.state_.source_mode_next = 0;
198 }
199
200 return true;
201 }
202
203 template <typename ResultT, typename ScannerT>
204 bool result(ResultT r, ScannerT const& scan)
205 {
206 if (element_context_error_) {
207 error_message_action error(
208 l.state_, "Element not allowed in this context.");
209 error(scan.first, scan.first);
210 return true;
211 }
212 else if (r) {
213 return true;
214 }
215 else if (
216 qbk_version_n < 107u && info_.type & element_info::in_phrase) {
217 // Old versions of quickbook had a soft fail
218 // for unparsed phrase elements.
219 return false;
220 }
221 else {
222 // Parse error in body.
223 error_action error(l.state_);
224 error(scan.first, scan.first);
225 return true;
226 }
227 }
228
229 void success(parse_iterator, parse_iterator)
230 {
231 l.element_type = info_.type;
232 }
233 void failure() { l.element_type = element_info::nothing; }
234
235 void cleanup()
236 {
237 if (pushed_source_mode_) l.state_.pop_tagged_source_mode();
238 }
239
240 main_grammar_local& l;
241 element_info info_;
242 bool pushed_source_mode_;
243 bool element_context_error_;
244 };
245
246 struct scoped_paragraph : scoped_action_base
247 {
248 scoped_paragraph(quickbook::state& state_)
249 : state(state_), pushed(false)
250 {
251 }
252
253 bool start()
254 {
255 state.push_tagged_source_mode(state.source_mode_next);
256 pushed = true;
257 state.source_mode_next = 0;
258 return true;
259 }
260
261 void cleanup()
262 {
263 if (pushed) state.pop_tagged_source_mode();
264 }
265
266 quickbook::state& state;
267 bool pushed;
268 };
269
270 struct in_list_impl
271 {
272 main_grammar_local& l;
273
274 explicit in_list_impl(main_grammar_local& l_) : l(l_) {}
275
276 bool operator()() const
277 {
278 return !l.list_stack.empty() &&
279 l.list_stack.top().type == list_stack_item::syntactic_list;
280 }
281 };
282
283 template <typename T, typename M>
284 struct set_scoped_value_impl : scoped_action_base
285 {
286 typedef M T::*member_ptr;
287
288 explicit set_scoped_value_impl(T& l_, member_ptr ptr_)
289 : l(l_), ptr(ptr_), saved_value()
290 {
291 }
292
293 bool start(M const& value)
294 {
295 saved_value = l.*ptr;
296 l.*ptr = value;
297
298 return true;
299 }
300
301 void cleanup() { l.*ptr = saved_value; }
302
303 T& l;
304 member_ptr ptr;
305 M saved_value;
306 };
307
308 template <typename T, typename M>
309 struct set_scoped_value : scoped_parser<set_scoped_value_impl<T, M> >
310 {
311 typedef set_scoped_value_impl<T, M> impl;
312
313 set_scoped_value(T& l, typename impl::member_ptr ptr)
314 : scoped_parser<impl>(impl(l, ptr))
315 {
316 }
317 };
318
319 ////////////////////////////////////////////////////////////////////////////
320 // Local grammar
321
322 void quickbook_grammar::impl::init_main()
323 {
324 main_grammar_local& local = cleanup_.add(new main_grammar_local(state));
325
326 // Global Actions
327 quickbook::element_action element_action(state);
328 quickbook::paragraph_action paragraph_action(state);
329
330 phrase_end_action end_phrase(state);
331 raw_char_action raw_char(state);
332 plain_char_action plain_char(state);
333 escape_unicode_action escape_unicode(state);
334
335 simple_phrase_action simple_markup(state);
336
337 break_action break_(state);
338 do_macro_action do_macro(state);
339
340 error_action error(state);
341 element_id_warning_action element_id_warning(state);
342
343 scoped_parser<to_value_scoped_action> to_value(state);
344 scoped_parser<scoped_paragraph> scope_paragraph(state);
345
346 quickbook_strict strict_mode(state);
347
348 // Local Actions
349 scoped_parser<process_element_impl> process_element(local);
350 in_list_impl in_list(local);
351
352 set_scoped_value<main_grammar_local, bool> scoped_no_eols(
353 local, &main_grammar_local::no_eols);
354 set_scoped_value<main_grammar_local, element_info::context>
355 scoped_context(local, &main_grammar_local::context);
356 set_scoped_value<main_grammar_local, bool> scoped_still_in_block(
357 local, &main_grammar_local::still_in_block);
358
359 member_action<main_grammar_local> check_indentation(
360 local, &main_grammar_local::check_indentation_impl);
361 member_action<main_grammar_local> check_code_block(
362 local, &main_grammar_local::check_code_block_impl);
363 member_action<main_grammar_local> start_blocks(
364 local, &main_grammar_local::start_blocks_impl);
365 member_action<main_grammar_local> start_nested_blocks(
366 local, &main_grammar_local::start_nested_blocks_impl);
367 member_action<main_grammar_local> end_blocks(
368 local, &main_grammar_local::end_blocks_impl);
369
370 // clang-format off
371
372 // phrase/phrase_start is used for an entirely self-contained
373 // phrase. For example, any remaining anchors are written out
374 // at the end instead of being saved for any following content.
375 phrase_start =
376 inline_phrase [end_phrase]
377 ;
378
379 // nested_phrase is used for a phrase nested inside square
380 // brackets.
381 nested_phrase =
382 state.values.save()
383 [
384 scoped_context(element_info::in_phrase)
385 [*(~cl::eps_p(']') >> local.common)]
386 ]
387 ;
388
389 // paragraph_phrase is like a nested_phrase but is also terminated
390 // by a paragraph end.
391 paragraph_phrase =
392 state.values.save()
393 [
394 scoped_context(element_info::in_phrase)
395 [*(~cl::eps_p(phrase_end) >> local.common)]
396 ]
397 ;
398
399 // extended_phrase is like a paragraph_phrase but allows some block
400 // elements.
401 extended_phrase =
402 state.values.save()
403 [
404 scoped_context(element_info::in_conditional)
405 [*(~cl::eps_p(phrase_end) >> local.common)]
406 ]
407 ;
408
409 // inline_phrase is used a phrase that isn't nested inside
410 // brackets, but is not self contained. An example of this
411 // is expanding a template, which is parsed separately but
412 // is part of the paragraph that contains it.
413 inline_phrase =
414 state.values.save()
415 [ qbk_ver(107u)
416 >> local.template_phrase
417 | qbk_ver(0, 107u)
418 >> scoped_context(element_info::in_phrase)
419 [*local.common]
420 ]
421 ;
422
423 table_title_phrase =
424 state.values.save()
425 [
426 scoped_context(element_info::in_phrase)
427 [ *( ~cl::eps_p(space >> (']' | '[' >> space >> '['))
428 >> local.common
429 )
430 ]
431 ]
432 ;
433
434 inside_preformatted =
435 scoped_no_eols(false)
436 [ paragraph_phrase
437 ]
438 ;
439
440 // Phrase templates can contain block tags, but can't contain
441 // syntatic blocks.
442 local.template_phrase =
443 scoped_context(element_info::in_top_level)
444 [ *( (local.paragraph_separator >> space >> cl::anychar_p)
445 [error("Paragraph in phrase template.")]
446 | local.common
447 )
448 ]
449 ;
450
451 // Top level blocks
452 block_start =
453 (*eol) [start_blocks]
454 >> ( *( local.top_level
455 >> !( qbk_ver(106u)
456 >> cl::ch_p(']')
457 >> cl::eps_p [error("Mismatched close bracket")]
458 )
459 )
460 ) [end_blocks]
461 ;
462
463 // Blocks contains within an element, e.g. a table cell or a footnote.
464 inside_paragraph =
465 state.values.save()
466 [ cl::eps_p [start_nested_blocks]
467 >> ( qbk_ver(107u)
468 >> (*eol)
469 >> (*local.top_level)
470 | qbk_ver(0, 107u)
471 >> local.inside_paragraph
472 ) [end_blocks]
473 ]
474 ;
475
476 local.top_level =
477 cl::eps_p(local.indent_check)
478 >> ( cl::eps_p(ph::var(local.block_type) == block_types::code)
479 >> local.code
480 | cl::eps_p(ph::var(local.block_type) == block_types::list)
481 >> local.list
482 | cl::eps_p(ph::var(local.block_type) == block_types::paragraph)
483 >> ( local.hr
484 | local.paragraph
485 )
486 )
487 >> *eol
488 ;
489
490 local.indent_check =
491 ( *cl::blank_p
492 >> !( (cl::ch_p('*') | '#')
493 >> *cl::blank_p)
494 ) [check_indentation]
495 ;
496
497 local.paragraph =
498 // Usually superfluous call
499 // for paragraphs in lists.
500 cl::eps_p [paragraph_action]
501 >> scope_paragraph()
502 [
503 scoped_context(element_info::in_top_level)
504 [ scoped_still_in_block(true)
505 [ local.syntactic_block_item(element_info::is_contextual_block)
506 >> *( cl::eps_p(ph::var(local.still_in_block))
507 >> local.syntactic_block_item(element_info::is_block)
508 )
509 ]
510 ]
511 ] [paragraph_action]
512 ;
513
514 local.list =
515 *cl::blank_p
516 >> (cl::ch_p('*') | '#')
517 >> (*cl::blank_p)
518 >> scoped_context(element_info::in_list_block)
519 [ scoped_still_in_block(true)
520 [ *( cl::eps_p(ph::var(local.still_in_block))
521 >> local.syntactic_block_item(element_info::is_block)
522 )
523 ]
524 ]
525 ;
526
527 local.syntactic_block_item =
528 local.paragraph_separator [ph::var(local.still_in_block) = false]
529 | (cl::eps_p(~cl::ch_p(']')) | qbk_ver(0, 107u))
530 [ph::var(local.element_type) = element_info::nothing]
531 >> local.common
532
533 // If the element is a block, then a newline will end the
534 // current syntactic block.
535 //
536 // Note that we don't do this for lists in 1.6, as it causes
537 // the list block to end. The support for nested syntactic
538 // blocks in 1.7 will fix that. Although it does mean the
539 // following line will need to be indented.
540 >> !( cl::eps_p(in_list) >> qbk_ver(106u, 107u)
541 | cl::eps_p
542 (
543 ph::static_cast_<int>(local.syntactic_block_item.is_block_mask) &
544 ph::static_cast_<int>(ph::var(local.element_type))
545 )
546 >> eol [ph::var(local.still_in_block) = false]
547 )
548 ;
549
550 local.paragraph_separator =
551 cl::eol_p
552 >> cl::eps_p
553 ( *cl::blank_p
554 >> ( cl::eol_p
555 | cl::end_p
556 | cl::eps_p(in_list) >> (cl::ch_p('*') | '#')
557 )
558 )
559 >> *eol
560 ;
561
562 // Blocks contains within an element, e.g. a table cell or a footnote.
563 local.inside_paragraph =
564 scoped_context(element_info::in_nested_block)
565 [ *( local.paragraph_separator [paragraph_action]
566 | ~cl::eps_p(']')
567 >> local.common
568 )
569 ] [paragraph_action]
570 ;
571
572 local.hr =
573 cl::str_p("----")
574 >> state.values.list(block_tags::hr)
575 [ ( qbk_ver(106u)
576 >> *(line_comment | (cl::anychar_p - (cl::eol_p | '[' | ']')))
577 | qbk_ver(0, 106u)
578 >> *(line_comment | (cl::anychar_p - (cl::eol_p | "[/")))
579 )
580 >> *eol
581 ] [element_action]
582 ;
583
584 local.element
585 = '['
586 >> ( cl::eps_p(cl::punct_p)
587 >> elements [ph::var(local.info) = ph::arg1]
588 | elements [ph::var(local.info) = ph::arg1]
589 >> (cl::eps_p - (cl::alnum_p | '_'))
590 )
591 >> process_element()
592 [ state.values.list(ph::var(local.info.tag))
593 [ cl::lazy_p(*ph::var(local.info.rule))
594 >> space
595 >> ']'
596 ] [element_action]
597 ]
598 ;
599
600 local.code =
601 state.values.list(code_tags::code_block)
602 [( local.code_line
603 >> *(*local.blank_line >> local.code_line)
604 ) [state.values.entry(ph::arg1, ph::arg2)]
605 ] [element_action]
606 >> *eol
607 ;
608
609 local.code_line =
610 ( *cl::blank_p
611 >> ~cl::eps_p(cl::eol_p)
612 ) [check_code_block]
613 >> cl::eps_p(ph::var(local.block_type) == block_types::code)
614 >> *(cl::anychar_p - cl::eol_p)
615 >> (cl::eol_p | cl::end_p)
616 ;
617
618 local.blank_line =
619 *cl::blank_p >> cl::eol_p
620 ;
621
622 local.common =
623 local.macro
624 | local.element
625 | local.template_
626 | local.break_
627 | local.code_block
628 | local.inline_code
629 | local.simple_markup
630 | escape
631 | comment
632 | strict_mode
633 >> ( local.error_brackets [error("Invalid template/tag (strict mode)")]
634 | cl::eps_p('[') [error("Mismatched open bracket (strict mode)")]
635 >> cl::anychar_p
636 | cl::eps_p(']') [error("Mismatched close bracket (strict mode)")]
637 >> cl::anychar_p
638 )
639 | qbk_ver(106u)
640 >> local.square_brackets
641 | cl::space_p [raw_char]
642 | cl::anychar_p [plain_char]
643 ;
644
645 skip_entity =
646 '['
647 // For escaped templates:
648 >> !(space >> cl::ch_p('`') >> (cl::alpha_p | '_'))
649 >> *(~cl::eps_p(']') >> skip_entity)
650 >> !cl::ch_p(']')
651 | local.skip_code_block
652 | local.skip_inline_code
653 | local.skip_escape
654 | comment
655 | (cl::anychar_p - '[' - ']')
656 ;
657
658 local.square_brackets =
659 ( cl::ch_p('[') [plain_char]
660 >> paragraph_phrase
661 >> ( cl::ch_p(']') [plain_char]
662 | cl::eps_p [error("Missing close bracket")]
663 )
664 | cl::ch_p(']') [plain_char]
665 >> cl::eps_p [error("Mismatched close bracket")]
666 )
667 ;
668
669 local.error_brackets =
670 cl::ch_p('[') [plain_char]
671 >> ( local.error_brackets
672 | (cl::anychar_p - ']')
673 )
674 >> cl::ch_p(']')
675 ;
676
677 local.macro =
678 cl::eps_p
679 ( ( state.macro
680 >> ~cl::eps_p(cl::alpha_p | '_')
681 // must not be followed by alpha or underscore
682 )
683 & macro_identifier // must be a valid macro for the current version
684 )
685 >> state.macro [do_macro]
686 ;
687
688 local.template_ =
689 ( '['
690 >> space
691 >> state.values.list(template_tags::template_)
692 [ local.template_body
693 >> ']'
694 ]
695 ) [element_action]
696 ;
697
698 local.attribute_template =
699 ( '['
700 >> space
701 >> state.values.list(template_tags::attribute_template)
702 [ local.template_body
703 >> ']'
704 ]
705 ) [element_action]
706 ;
707
708 local.template_body =
709 ( cl::str_p('`')
710 >> cl::eps_p(cl::punct_p)
711 >> state.templates.scope
712 [state.values.entry(ph::arg1, ph::arg2, template_tags::escape)]
713 [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
714 >> !( qbk_ver(106u)
715 [error("Templates with punctuation names can't be escaped in quickbook 1.6+")]
716 | strict_mode
717 [error("Templates with punctuation names can't be escaped (strict mode)")]
718 )
719 | cl::str_p('`')
720 >> state.templates.scope
721 [state.values.entry(ph::arg1, ph::arg2, template_tags::escape)]
722 [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
723
724 | cl::eps_p(cl::punct_p)
725 >> state.templates.scope
726 [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
727
728 | state.templates.scope
729 [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
730 >> cl::eps_p(hard_space)
731 )
732 >> space
733 >> !local.template_args
734 ;
735
736 local.template_args =
737 qbk_ver(106u) >> local.template_args_1_6
738 | qbk_ver(105u, 106u) >> local.template_args_1_5
739 | qbk_ver(0, 105u) >> local.template_args_1_4
740 ;
741
742 local.template_args_1_4 = local.template_arg_1_4 >> *(".." >> local.template_arg_1_4);
743
744 local.template_arg_1_4 =
745 ( cl::eps_p(*cl::blank_p >> cl::eol_p)
746 >> local.template_inner_arg_1_4 [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
747 | local.template_inner_arg_1_4 [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
748 )
749 ;
750
751 local.template_inner_arg_1_4 =
752 +(local.brackets_1_4 | (cl::anychar_p - (cl::str_p("..") | ']')))
753 ;
754
755 local.brackets_1_4 =
756 '[' >> local.template_inner_arg_1_4 >> ']'
757 ;
758
759 local.template_args_1_5 = local.template_arg_1_5 >> *(".." >> local.template_arg_1_5);
760
761 local.template_arg_1_5 =
762 ( cl::eps_p(*cl::blank_p >> cl::eol_p)
763 >> local.template_arg_1_5_content [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
764 | local.template_arg_1_5_content [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
765 )
766 ;
767
768 local.template_arg_1_5_content =
769 +(local.brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p("..") | '[' | ']')))
770 ;
771
772 local.template_inner_arg_1_5 =
773 +(local.brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p('[') | ']')))
774 ;
775
776 local.brackets_1_5 =
777 '[' >> local.template_inner_arg_1_5 >> ']'
778 ;
779
780 local.template_args_1_6 = local.template_arg_1_6 >> *(".." >> local.template_arg_1_6);
781
782 local.template_arg_1_6 =
783 ( cl::eps_p(*cl::blank_p >> cl::eol_p)
784 >> local.template_arg_1_6_content [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
785 | local.template_arg_1_6_content [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
786 )
787 ;
788
789 local.template_arg_1_6_content =
790 + ( ~cl::eps_p("..") >> skip_entity )
791 ;
792
793 local.break_
794 = ( '['
795 >> space
796 >> "br"
797 >> space
798 >> ']'
799 ) [break_]
800 ;
801
802 local.inline_code =
803 '`' >> state.values.list(code_tags::inline_code)
804 [(
805 *(cl::anychar_p -
806 ( '`'
807 | (cl::eol_p >> *cl::blank_p >> cl::eol_p)
808 // Make sure that we don't go
809 ) // past a single block
810 ) >> cl::eps_p('`')
811 ) [state.values.entry(ph::arg1, ph::arg2)]
812 >> '`'
813 ] [element_action]
814 ;
815
816 local.skip_inline_code =
817 '`'
818 >> *(cl::anychar_p -
819 ( '`'
820 | (cl::eol_p >> *cl::blank_p >> cl::eol_p)
821 // Make sure that we don't go
822 ) // past a single block
823 )
824 >> !cl::ch_p('`')
825 ;
826
827 local.skip_code_block =
828 "```"
829 >> ~cl::eps_p("`")
830 >> ( (!( *(*cl::blank_p >> cl::eol_p)
831 >> ( *( "````" >> *cl::ch_p('`')
832 | ( cl::anychar_p
833 - (*cl::space_p >> "```" >> ~cl::eps_p("`"))
834 )
835 )
836 >> !(*cl::blank_p >> cl::eol_p)
837 )
838 >> (*cl::space_p >> "```")
839 ))
840 | *cl::anychar_p
841 )
842 | "``"
843 >> ~cl::eps_p("`")
844 >> ( ( *(*cl::blank_p >> cl::eol_p)
845 >> ( *( "```" >> *cl::ch_p('`')
846 | ( cl::anychar_p
847 - (*cl::space_p >> "``" >> ~cl::eps_p("`"))
848 )
849 )
850 >> !(*cl::blank_p >> cl::eol_p)
851 )
852 >> (*cl::space_p >> "``")
853 )
854 | *cl::anychar_p
855 )
856 ;
857
858 local.code_block =
859 "```"
860 >> ~cl::eps_p("`")
861 >> ( state.values.list(code_tags::inline_code_block)
862 [ *(*cl::blank_p >> cl::eol_p)
863 >> ( *( "````" >> *cl::ch_p('`')
864 | ( cl::anychar_p
865 - (*cl::space_p >> "```" >> ~cl::eps_p("`"))
866 )
867 )
868 >> !(*cl::blank_p >> cl::eol_p)
869 ) [state.values.entry(ph::arg1, ph::arg2)]
870 >> (*cl::space_p >> "```")
871 ] [element_action]
872 | cl::eps_p [error("Unfinished code block")]
873 >> *cl::anychar_p
874 )
875 | "``"
876 >> ~cl::eps_p("`")
877 >> ( state.values.list(code_tags::inline_code_block)
878 [ *(*cl::blank_p >> cl::eol_p)
879 >> ( *( "```" >> *cl::ch_p('`')
880 | ( cl::anychar_p
881 - (*cl::space_p >> "``" >> ~cl::eps_p("`"))
882 )
883 )
884 >> !(*cl::blank_p >> cl::eol_p)
885 ) [state.values.entry(ph::arg1, ph::arg2)]
886 >> (*cl::space_p >> "``")
887 ] [element_action]
888 | cl::eps_p [error("Unfinished code block")]
889 >> *cl::anychar_p
890 )
891 ;
892
893 local.simple_markup =
894 cl::chset<>("*/_=") [ph::var(local.mark) = ph::arg1]
895 >> cl::eps_p(cl::graph_p) // graph_p must follow first mark
896 >> lookback
897 [ cl::anychar_p // skip back over the markup
898 >> ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
899 // first mark not be preceeded by
900 // the same character.
901 >> (cl::space_p | cl::punct_p | cl::end_p)
902 // first mark must be preceeded
903 // by space or punctuation or the
904 // mark character or a the start.
905 ]
906 >> state.values.save()
907 [
908 to_value()
909 [
910 cl::eps_p((state.macro & macro_identifier) >> local.simple_markup_end)
911 >> state.macro [do_macro]
912 | ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
913 >> +( ~cl::eps_p
914 ( lookback [~cl::ch_p(boost::ref(local.mark))]
915 >> local.simple_markup_end
916 )
917 >> cl::anychar_p [plain_char]
918 )
919 ]
920 >> cl::ch_p(boost::ref(local.mark))
921 [simple_markup]
922 ]
923 ;
924
925 local.simple_markup_end
926 = ( lookback[cl::graph_p] // final mark must be preceeded by
927 // graph_p
928 >> cl::ch_p(boost::ref(local.mark))
929 >> ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
930 // final mark not be followed by
931 // the same character.
932 >> (cl::space_p | cl::punct_p | cl::end_p)
933 // final mark must be followed by
934 // space or punctuation
935 )
936 | '['
937 | "'''"
938 | '`'
939 | phrase_end
940 ;
941
942 escape =
943 cl::str_p("\\n") [break_]
944 | cl::str_p("\\ ") // ignore an escaped space
945 | '\\' >> cl::punct_p [plain_char]
946 | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
947 [escape_unicode]
948 | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
949 [escape_unicode]
950 | ("'''" >> !eol)
951 >> state.values.save()
952 [ (*(cl::anychar_p - "'''")) [state.values.entry(ph::arg1, ph::arg2, phrase_tags::escape)]
953 >> ( cl::str_p("'''")
954 | cl::eps_p [error("Unclosed boostbook escape.")]
955 ) [element_action]
956 ]
957 ;
958
959 local.skip_escape =
960 cl::str_p("\\n")
961 | cl::str_p("\\ ")
962 | '\\' >> cl::punct_p
963 | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
964 | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
965 | ("'''" >> !eol)
966 >> (*(cl::anychar_p - "'''"))
967 >> ( cl::str_p("'''")
968 | cl::eps_p
969 )
970 ;
971
972 raw_escape =
973 cl::str_p("\\n") [error("Newlines invalid here.")]
974 | cl::str_p("\\ ") // ignore an escaped space
975 | '\\' >> cl::punct_p [raw_char]
976 | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
977 [escape_unicode]
978 | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
979 [escape_unicode]
980 | ('\\' >> cl::anychar_p) [error("Invalid escape.")]
981 [raw_char]
982 | ("'''" >> !eol) [error("Boostbook escape invalid here.")]
983 >> (*(cl::anychar_p - "'''"))
984 >> ( cl::str_p("'''")
985 | cl::eps_p [error("Unclosed boostbook escape.")]
986 )
987 ;
988
989 attribute_template_body =
990 space
991 >> *( ~cl::eps_p(space >> cl::end_p | comment)
992 >> ( cl::eps_p
993 ( cl::ch_p('[')
994 >> space
995 >> ( cl::eps_p(cl::punct_p)
996 >> elements
997 | elements
998 >> (cl::eps_p - (cl::alnum_p | '_'))
999 )
1000 ) [error("Elements not allowed in attribute values.")]
1001 >> local.square_brackets
1002 | local.attribute_template
1003 | cl::eps_p(cl::ch_p('[')) [error("Unmatched template in attribute value.")]
1004 >> local.square_brackets
1005 | raw_escape
1006 | cl::anychar_p [raw_char]
1007 )
1008 )
1009 >> space
1010 ;
1011
1012 attribute_value_1_7 =
1013 state.values.save() [
1014 +( ~cl::eps_p(']' | cl::space_p | comment)
1015 >> ( cl::eps_p
1016 ( cl::ch_p('[')
1017 >> space
1018 >> ( cl::eps_p(cl::punct_p)
1019 >> elements
1020 | elements
1021 >> (cl::eps_p - (cl::alnum_p | '_'))
1022 )
1023 ) [error("Elements not allowed in attribute values.")]
1024 >> local.square_brackets
1025 | local.attribute_template
1026 | cl::eps_p(cl::ch_p('['))[error("Unmatched template in attribute value.")]
1027 >> local.square_brackets
1028 | raw_escape
1029 | cl::anychar_p [raw_char]
1030 )
1031 )
1032 ]
1033 ;
1034
1035 //
1036 // Command line
1037 //
1038
1039 command_line =
1040 state.values.list(block_tags::macro_definition)
1041 [ *cl::space_p
1042 >> local.command_line_macro_identifier
1043 [state.values.entry(ph::arg1, ph::arg2)]
1044 >> *cl::space_p
1045 >> !( '='
1046 >> *cl::space_p
1047 >> to_value() [ inline_phrase ]
1048 >> *cl::space_p
1049 )
1050 >> cl::end_p
1051 ] [element_action]
1052 ;
1053
1054 local.command_line_macro_identifier =
1055 qbk_ver(106u)
1056 >> +(cl::anychar_p - (cl::space_p | '[' | '\\' | ']' | '='))
1057 | +(cl::anychar_p - (cl::space_p | ']' | '='))
1058 ;
1059
1060 // Miscellaneous stuff
1061
1062 // Follows an alphanumeric identifier - ensures that it doesn't
1063 // match an empty space in the middle of the identifier.
1064 hard_space =
1065 (cl::eps_p - (cl::alnum_p | '_')) >> space
1066 ;
1067
1068 space =
1069 *(cl::space_p | comment)
1070 ;
1071
1072 blank =
1073 *(cl::blank_p | comment)
1074 ;
1075
1076 eol = blank >> cl::eol_p
1077 ;
1078
1079 phrase_end =
1080 ']'
1081 | cl::eps_p(ph::var(local.no_eols))
1082 >> cl::eol_p >> *cl::blank_p >> cl::eol_p
1083 ; // Make sure that we don't go
1084 // past a single block, except
1085 // when preformatted.
1086
1087 comment =
1088 "[/" >> *(local.dummy_block | (cl::anychar_p - ']')) >> ']'
1089 ;
1090
1091 local.dummy_block =
1092 '[' >> *(local.dummy_block | (cl::anychar_p - ']')) >> ']'
1093 ;
1094
1095 line_comment =
1096 "[/" >> *(local.line_dummy_block | (cl::anychar_p - (cl::eol_p | ']'))) >> ']'
1097 ;
1098
1099 local.line_dummy_block =
1100 '[' >> *(local.line_dummy_block | (cl::anychar_p - (cl::eol_p | ']'))) >> ']'
1101 ;
1102
1103 macro_identifier =
1104 qbk_ver(106u)
1105 >> +(cl::anychar_p - (cl::space_p | '[' | '\\' | ']'))
1106 | qbk_ver(0, 106u)
1107 >> +(cl::anychar_p - (cl::space_p | ']'))
1108 ;
1109
1110 // clang-format on
1111 }
1112
1113 ////////////////////////////////////////////////////////////////////////////
1114 // Indentation Handling
1115
1116 template <typename Iterator> int indent_length(Iterator first, Iterator end)
1117 {
1118 int length = 0;
1119 for (; first != end; ++first) {
1120 if (*first == '\t') {
1121 // hardcoded tab to 4 for now
1122 length = length + 4 - (length % 4);
1123 }
1124 else {
1125 ++length;
1126 }
1127 }
1128
1129 return length;
1130 }
1131
1132 void main_grammar_local::start_blocks_impl(parse_iterator, parse_iterator)
1133 {
1134 list_stack.push(list_stack_item(list_stack_item::top_level));
1135 }
1136
1137 void main_grammar_local::start_nested_blocks_impl(
1138 parse_iterator, parse_iterator)
1139 {
1140 // If this nested block is part of a list, then tell the
1141 // output state.
1142 state_.in_list = state_.explicit_list;
1143 state_.explicit_list = false;
1144
1145 list_stack.push(list_stack_item(list_stack_item::nested_block));
1146 }
1147
1148 void main_grammar_local::end_blocks_impl(parse_iterator, parse_iterator)
1149 {
1150 clear_stack();
1151 list_stack.pop();
1152 }
1153
1154 void main_grammar_local::check_indentation_impl(
1155 parse_iterator first_, parse_iterator last_)
1156 {
1157 string_iterator first = first_.base();
1158 string_iterator last = last_.base();
1159 auto mark_pos = string_view(first, last - first).find_first_of("*#");
1160
1161 if (mark_pos == string_view::npos) {
1162 plain_block(first, last);
1163 }
1164 else {
1165 list_block(first, first + mark_pos, last);
1166 }
1167 }
1168
1169 void main_grammar_local::check_code_block_impl(
1170 parse_iterator first, parse_iterator last)
1171 {
1172 unsigned int new_indent = indent_length(first.base(), last.base());
1173
1174 block_type = (new_indent > list_stack.top().indent2)
1175 ? block_types::code
1176 : block_types::none;
1177 }
1178
1179 void main_grammar_local::plain_block(
1180 string_iterator first, string_iterator last)
1181 {
1182 if (qbk_version_n >= 106u) {
1183 unsigned int new_indent = indent_length(first, last);
1184
1185 if (new_indent > list_stack.top().indent2) {
1186 if (list_stack.top().type != list_stack_item::nested_block) {
1187 block_type = block_types::code;
1188 }
1189 else {
1190 block_type = block_types::paragraph;
1191 }
1192 }
1193 else {
1194 while (list_stack.top().type ==
1195 list_stack_item::syntactic_list &&
1196 new_indent < list_stack.top().indent) {
1197 state_.end_list_item();
1198 state_.end_list(list_stack.top().mark);
1199 list_stack.pop();
1200 list_indent = list_stack.top().indent;
1201 }
1202
1203 if (list_stack.top().type == list_stack_item::syntactic_list &&
1204 new_indent == list_stack.top().indent) {
1205 // If the paragraph is aligned with the list item's marker,
1206 // then end the current list item if that's aligned (or to
1207 // the left of) the parent's paragraph.
1208 //
1209 // i.e.
1210 //
1211 // * Level 1
1212 // * Level 2
1213 //
1214 // Still Level 2
1215 //
1216 // vs.
1217 //
1218 // * Level 1
1219 // * Level 2
1220 //
1221 // Back to Level 1
1222
1223 list_stack_item save = list_stack.top();
1224 list_stack.pop();
1225
1226 assert(
1227 list_stack.top().type != list_stack_item::syntactic_list
1228 ? new_indent >= list_stack.top().indent
1229 : new_indent > list_stack.top().indent);
1230
1231 if (new_indent <= list_stack.top().indent2) {
1232 state_.end_list_item();
1233 state_.end_list(save.mark);
1234 list_indent = list_stack.top().indent;
1235 }
1236 else {
1237 list_stack.push(save);
1238 }
1239 }
1240
1241 block_type = block_types::paragraph;
1242 }
1243
1244 if (qbk_version_n == 106u &&
1245 list_stack.top().type == list_stack_item::syntactic_list) {
1246 detail::outerr(state_.current_file, first)
1247 << "Paragraphs in lists aren't supported in quickbook 1.6."
1248 << std::endl;
1249 ++state_.error_count;
1250 }
1251 }
1252 else {
1253 clear_stack();
1254
1255 if (list_stack.top().type != list_stack_item::nested_block &&
1256 last != first)
1257 block_type = block_types::code;
1258 else
1259 block_type = block_types::paragraph;
1260 }
1261 }
1262
1263 void main_grammar_local::list_block(
1264 string_iterator first, string_iterator mark_pos, string_iterator last)
1265 {
1266 unsigned int new_indent = indent_length(first, mark_pos);
1267 unsigned int new_indent2 = indent_length(first, last);
1268 char list_mark = *mark_pos;
1269
1270 if (list_stack.top().type == list_stack_item::top_level &&
1271 new_indent > 0) {
1272 block_type = block_types::code;
1273 return;
1274 }
1275
1276 if (list_stack.top().type != list_stack_item::syntactic_list ||
1277 new_indent > list_indent) {
1278 list_stack.push(
1279 list_stack_item(list_mark, new_indent, new_indent2));
1280 state_.start_list(list_mark);
1281 }
1282 else if (new_indent == list_indent) {
1283 state_.end_list_item();
1284 }
1285 else {
1286 // This should never reach root, since the first list
1287 // has indentation 0.
1288 while (list_stack.top().type == list_stack_item::syntactic_list &&
1289 new_indent < list_stack.top().indent) {
1290 state_.end_list_item();
1291 state_.end_list(list_stack.top().mark);
1292 list_stack.pop();
1293 }
1294
1295 state_.end_list_item();
1296 }
1297
1298 list_indent = new_indent;
1299
1300 if (list_mark != list_stack.top().mark) {
1301 detail::outerr(state_.current_file, first)
1302 << "Illegal change of list style.\n";
1303 detail::outwarn(state_.current_file, first)
1304 << "Ignoring change of list style." << std::endl;
1305 ++state_.error_count;
1306 }
1307
1308 state_.start_list_item();
1309 block_type = block_types::list;
1310 }
1311
1312 void main_grammar_local::clear_stack()
1313 {
1314 while (list_stack.top().type == list_stack_item::syntactic_list) {
1315 state_.end_list_item();
1316 state_.end_list(list_stack.top().mark);
1317 list_stack.pop();
1318 }
1319 }
1320 }