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 list_stack_itemquickbook::list_stack_item56 explicit list_stack_item(list_item_type r) 57 : type(r), indent(0), indent2(0), mark('\0') 58 { 59 } 60 list_stack_itemquickbook::list_stack_item61 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 main_grammar_localquickbook::main_grammar_local150 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 { process_element_implquickbook::process_element_impl163 process_element_impl(main_grammar_local& l_) 164 : l(l_), pushed_source_mode_(false), element_context_error_(false) 165 { 166 } 167 startquickbook::process_element_impl168 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> resultquickbook::process_element_impl204 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 successquickbook::process_element_impl229 void success(parse_iterator, parse_iterator) 230 { 231 l.element_type = info_.type; 232 } failurequickbook::process_element_impl233 void failure() { l.element_type = element_info::nothing; } 234 cleanupquickbook::process_element_impl235 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 { scoped_paragraphquickbook::scoped_paragraph248 scoped_paragraph(quickbook::state& state_) 249 : state(state_), pushed(false) 250 { 251 } 252 startquickbook::scoped_paragraph253 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 cleanupquickbook::scoped_paragraph261 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 in_list_implquickbook::in_list_impl274 explicit in_list_impl(main_grammar_local& l_) : l(l_) {} 275 operator ()quickbook::in_list_impl276 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 set_scoped_value_implquickbook::set_scoped_value_impl288 explicit set_scoped_value_impl(T& l_, member_ptr ptr_) 289 : l(l_), ptr(ptr_), saved_value() 290 { 291 } 292 startquickbook::set_scoped_value_impl293 bool start(M const& value) 294 { 295 saved_value = l.*ptr; 296 l.*ptr = value; 297 298 return true; 299 } 300 cleanupquickbook::set_scoped_value_impl301 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 set_scoped_valuequickbook::set_scoped_value313 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 init_main()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 indent_length(Iterator first,Iterator end)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 start_blocks_impl(parse_iterator,parse_iterator)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 start_nested_blocks_impl(parse_iterator,parse_iterator)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 end_blocks_impl(parse_iterator,parse_iterator)1148 void main_grammar_local::end_blocks_impl(parse_iterator, parse_iterator) 1149 { 1150 clear_stack(); 1151 list_stack.pop(); 1152 } 1153 check_indentation_impl(parse_iterator first_,parse_iterator last_)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 check_code_block_impl(parse_iterator first,parse_iterator last)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 plain_block(string_iterator first,string_iterator last)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 list_block(string_iterator first,string_iterator mark_pos,string_iterator last)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 clear_stack()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 } 1321