1 /*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 http://spirit.sourceforge.net/
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 =============================================================================*/
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"
26 #include "template_tags.hpp"
31 namespace cl
= boost::spirit::classic
;
33 struct list_stack_item
35 // Is this the root of the context
36 // (e.g. top, template, table cell etc.)
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.
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.
50 // Example of inside a list:
56 explicit list_stack_item(list_item_type r
)
57 : type(r
), indent(0), indent2(0), mark('\0')
61 explicit list_stack_item(
62 char mark_
, unsigned int indent_
, unsigned int indent2_
)
63 : type(syntactic_list
)
82 struct main_grammar_local
84 ////////////////////////////////////////////////////////////////////////
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
);
95 string_iterator first
,
96 string_iterator mark_pos
,
97 string_iterator last
);
100 ////////////////////////////////////////////////////////////////////////
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
;
114 struct block_context_closure
115 : cl::closure
<block_context_closure
, element_info::context
>
117 // Mask used to determine whether or not an element is a block
119 member1 is_block_mask
;
122 cl::rule
<scanner
> simple_markup
, simple_markup_end
;
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
;
132 std::stack
<list_stack_item
> list_stack
;
133 unsigned int list_indent
;
135 element_info::context context
;
136 char mark
; // Simple markup's deliminator
137 bool still_in_block
; // Inside a syntatic block
140 block_types::values block_type
;
142 element_info::type_enum element_type
;
145 quickbook::state
& state_
;
147 ////////////////////////////////////////////////////////////////////////
150 main_grammar_local(quickbook::state
& state
)
154 , context(element_info::in_top_level
)
161 struct process_element_impl
: scoped_action_base
163 process_element_impl(main_grammar_local
& l_
)
164 : l(l_
), pushed_source_mode_(false), element_context_error_(false)
170 // This element doesn't exist in the current language version.
171 if (qbk_version_n
< l
.info
.qbk_version
) return false;
173 // The element is not allowed in this context.
174 if (!(l
.info
.type
& l
.context
)) {
175 if (qbk_version_n
< 107u) {
179 element_context_error_
= true;
185 if (info_
.type
!= element_info::phrase
&&
186 info_
.type
!= element_info::maybe_block
) {
187 paragraph_action
para(l
.state_
);
191 assert(l
.state_
.values
.builder
.empty());
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;
203 template <typename ResultT
, typename ScannerT
>
204 bool result(ResultT r
, ScannerT
const& scan
)
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
);
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.
222 // Parse error in body.
223 error_action
error(l
.state_
);
224 error(scan
.first
, scan
.first
);
229 void success(parse_iterator
, parse_iterator
)
231 l
.element_type
= info_
.type
;
233 void failure() { l
.element_type
= element_info::nothing
; }
237 if (pushed_source_mode_
) l
.state_
.pop_tagged_source_mode();
240 main_grammar_local
& l
;
242 bool pushed_source_mode_
;
243 bool element_context_error_
;
246 struct scoped_paragraph
: scoped_action_base
248 scoped_paragraph(quickbook::state
& state_
)
249 : state(state_
), pushed(false)
255 state
.push_tagged_source_mode(state
.source_mode_next
);
257 state
.source_mode_next
= 0;
263 if (pushed
) state
.pop_tagged_source_mode();
266 quickbook::state
& state
;
272 main_grammar_local
& l
;
274 explicit in_list_impl(main_grammar_local
& l_
) : l(l_
) {}
276 bool operator()() const
278 return !l
.list_stack
.empty() &&
279 l
.list_stack
.top().type
== list_stack_item::syntactic_list
;
283 template <typename T
, typename M
>
284 struct set_scoped_value_impl
: scoped_action_base
286 typedef M
T::*member_ptr
;
288 explicit set_scoped_value_impl(T
& l_
, member_ptr ptr_
)
289 : l(l_
), ptr(ptr_
), saved_value()
293 bool start(M
const& value
)
295 saved_value
= l
.*ptr
;
301 void cleanup() { l
.*ptr
= saved_value
; }
308 template <typename T
, typename M
>
309 struct set_scoped_value
: scoped_parser
<set_scoped_value_impl
<T
, M
> >
311 typedef set_scoped_value_impl
<T
, M
> impl
;
313 set_scoped_value(T
& l
, typename
impl::member_ptr ptr
)
314 : scoped_parser
<impl
>(impl(l
, ptr
))
319 ////////////////////////////////////////////////////////////////////////////
322 void quickbook_grammar::impl::init_main()
324 main_grammar_local
& local
= cleanup_
.add(new main_grammar_local(state
));
327 quickbook::element_action
element_action(state
);
328 quickbook::paragraph_action
paragraph_action(state
);
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
);
335 simple_phrase_action
simple_markup(state
);
337 break_action
break_(state
);
338 do_macro_action
do_macro(state
);
340 error_action
error(state
);
341 element_id_warning_action
element_id_warning(state
);
343 scoped_parser
<to_value_scoped_action
> to_value(state
);
344 scoped_parser
<scoped_paragraph
> scope_paragraph(state
);
346 quickbook_strict
strict_mode(state
);
349 scoped_parser
<process_element_impl
> process_element(local
);
350 in_list_impl
in_list(local
);
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
);
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
);
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.
376 inline_phrase
[end_phrase
]
379 // nested_phrase is used for a phrase nested inside square
384 scoped_context(element_info::in_phrase
)
385 [*(~cl::eps_p(']') >> local
.common
)]
389 // paragraph_phrase is like a nested_phrase but is also terminated
390 // by a paragraph end.
394 scoped_context(element_info::in_phrase
)
395 [*(~cl::eps_p(phrase_end
) >> local
.common
)]
399 // extended_phrase is like a paragraph_phrase but allows some block
404 scoped_context(element_info::in_conditional
)
405 [*(~cl::eps_p(phrase_end
) >> local
.common
)]
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.
416 >> local
.template_phrase
418 >> scoped_context(element_info::in_phrase
)
426 scoped_context(element_info::in_phrase
)
427 [ *( ~cl::eps_p(space
>> (']' | '[' >> space
>> '['))
434 inside_preformatted
=
435 scoped_no_eols(false)
440 // Phrase templates can contain block tags, but can't contain
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.")]
453 (*eol
) [start_blocks
]
454 >> ( *( local
.top_level
457 >> cl::eps_p
[error("Mismatched close bracket")]
463 // Blocks contains within an element, e.g. a table cell or a footnote.
466 [ cl::eps_p
[start_nested_blocks
]
469 >> (*local
.top_level
)
471 >> local
.inside_paragraph
477 cl::eps_p(local
.indent_check
)
478 >> ( cl::eps_p(ph::var(local
.block_type
) == block_types::code
)
480 | cl::eps_p(ph::var(local
.block_type
) == block_types::list
)
482 | cl::eps_p(ph::var(local
.block_type
) == block_types::paragraph
)
492 >> !( (cl::ch_p('*') | '#')
494 ) [check_indentation
]
498 // Usually superfluous call
499 // for paragraphs in lists.
500 cl::eps_p
[paragraph_action
]
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
)
516 >> (cl::ch_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
)
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
]
533 // If the element is a block, then a newline will end the
534 // current syntactic block.
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)
543 ph::static_cast_
<int>(local
.syntactic_block_item
.is_block_mask
) &
544 ph::static_cast_
<int>(ph::var(local
.element_type
))
546 >> eol
[ph::var(local
.still_in_block
) = false]
550 local
.paragraph_separator
=
556 | cl::eps_p(in_list
) >> (cl::ch_p('*') | '#')
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
]
574 >> state
.values
.list(block_tags::hr
)
576 >> *(line_comment
| (cl::anychar_p
- (cl::eol_p
| '[' | ']')))
578 >> *(line_comment
| (cl::anychar_p
- (cl::eol_p
| "[/")))
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
| '_'))
592 [ state
.values
.list(ph::var(local
.info
.tag
))
593 [ cl::lazy_p(*ph::var(local
.info
.rule
))
601 state
.values
.list(code_tags::code_block
)
603 >> *(*local
.blank_line
>> local
.code_line
)
604 ) [state
.values
.entry(ph::arg1
, ph::arg2
)]
611 >> ~cl::eps_p(cl::eol_p
)
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
)
619 *cl::blank_p
>> cl::eol_p
629 | local
.simple_markup
633 >> ( local
.error_brackets
[error("Invalid template/tag (strict mode)")]
634 | cl::eps_p('[') [error("Mismatched open bracket (strict mode)")]
636 | cl::eps_p(']') [error("Mismatched close bracket (strict mode)")]
640 >> local
.square_brackets
641 | cl::space_p
[raw_char
]
642 | cl::anychar_p
[plain_char
]
647 // For escaped templates:
648 >> !(space
>> cl::ch_p('`') >> (cl::alpha_p
| '_'))
649 >> *(~cl::eps_p(']') >> skip_entity
)
651 | local
.skip_code_block
652 | local
.skip_inline_code
655 | (cl::anychar_p
- '[' - ']')
658 local
.square_brackets
=
659 ( cl::ch_p('[') [plain_char
]
661 >> ( cl::ch_p(']') [plain_char
]
662 | cl::eps_p
[error("Missing close bracket")]
664 | cl::ch_p(']') [plain_char
]
665 >> cl::eps_p
[error("Mismatched close bracket")]
669 local
.error_brackets
=
670 cl::ch_p('[') [plain_char
]
671 >> ( local
.error_brackets
672 | (cl::anychar_p
- ']')
680 >> ~cl::eps_p(cl::alpha_p
| '_')
681 // must not be followed by alpha or underscore
683 & macro_identifier
// must be a valid macro for the current version
685 >> state
.macro
[do_macro
]
691 >> state
.values
.list(template_tags::template_
)
692 [ local
.template_body
698 local
.attribute_template
=
701 >> state
.values
.list(template_tags::attribute_template
)
702 [ local
.template_body
708 local
.template_body
=
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
)]
715 [error("Templates with punctuation names can't be escaped in quickbook 1.6+")]
717 [error("Templates with punctuation names can't be escaped (strict mode)")]
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
)]
724 | cl::eps_p(cl::punct_p
)
725 >> state
.templates
.scope
726 [state
.values
.entry(ph::arg1
, ph::arg2
, template_tags::identifier
)]
728 | state
.templates
.scope
729 [state
.values
.entry(ph::arg1
, ph::arg2
, template_tags::identifier
)]
730 >> cl::eps_p(hard_space
)
733 >> !local
.template_args
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
742 local
.template_args_1_4
= local
.template_arg_1_4
>> *(".." >> local
.template_arg_1_4
);
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
)]
751 local
.template_inner_arg_1_4
=
752 +(local
.brackets_1_4
| (cl::anychar_p
- (cl::str_p("..") | ']')))
756 '[' >> local
.template_inner_arg_1_4
>> ']'
759 local
.template_args_1_5
= local
.template_arg_1_5
>> *(".." >> local
.template_arg_1_5
);
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
)]
768 local
.template_arg_1_5_content
=
769 +(local
.brackets_1_5
| ('\\' >> cl::anychar_p
) | (cl::anychar_p
- (cl::str_p("..") | '[' | ']')))
772 local
.template_inner_arg_1_5
=
773 +(local
.brackets_1_5
| ('\\' >> cl::anychar_p
) | (cl::anychar_p
- (cl::str_p('[') | ']')))
777 '[' >> local
.template_inner_arg_1_5
>> ']'
780 local
.template_args_1_6
= local
.template_arg_1_6
>> *(".." >> local
.template_arg_1_6
);
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
)]
789 local
.template_arg_1_6_content
=
790 + ( ~cl::eps_p("..") >> skip_entity
)
803 '`' >> state
.values
.list(code_tags::inline_code
)
807 | (cl::eol_p
>> *cl::blank_p
>> cl::eol_p
)
808 // Make sure that we don't go
809 ) // past a single block
811 ) [state
.values
.entry(ph::arg1
, ph::arg2
)]
816 local
.skip_inline_code
=
820 | (cl::eol_p
>> *cl::blank_p
>> cl::eol_p
)
821 // Make sure that we don't go
822 ) // past a single block
827 local
.skip_code_block
=
830 >> ( (!( *(*cl::blank_p
>> cl::eol_p
)
831 >> ( *( "````" >> *cl::ch_p('`')
833 - (*cl::space_p
>> "```" >> ~cl::eps_p("`"))
836 >> !(*cl::blank_p
>> cl::eol_p
)
838 >> (*cl::space_p
>> "```")
844 >> ( ( *(*cl::blank_p
>> cl::eol_p
)
845 >> ( *( "```" >> *cl::ch_p('`')
847 - (*cl::space_p
>> "``" >> ~cl::eps_p("`"))
850 >> !(*cl::blank_p
>> cl::eol_p
)
852 >> (*cl::space_p
>> "``")
861 >> ( state
.values
.list(code_tags::inline_code_block
)
862 [ *(*cl::blank_p
>> cl::eol_p
)
863 >> ( *( "````" >> *cl::ch_p('`')
865 - (*cl::space_p
>> "```" >> ~cl::eps_p("`"))
868 >> !(*cl::blank_p
>> cl::eol_p
)
869 ) [state
.values
.entry(ph::arg1
, ph::arg2
)]
870 >> (*cl::space_p
>> "```")
872 | cl::eps_p
[error("Unfinished code block")]
877 >> ( state
.values
.list(code_tags::inline_code_block
)
878 [ *(*cl::blank_p
>> cl::eol_p
)
879 >> ( *( "```" >> *cl::ch_p('`')
881 - (*cl::space_p
>> "``" >> ~cl::eps_p("`"))
884 >> !(*cl::blank_p
>> cl::eol_p
)
885 ) [state
.values
.entry(ph::arg1
, ph::arg2
)]
886 >> (*cl::space_p
>> "``")
888 | cl::eps_p
[error("Unfinished code block")]
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
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.
906 >> state
.values
.save()
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
)))
914 ( lookback
[~cl::ch_p(boost::ref(local
.mark
))]
915 >> local
.simple_markup_end
917 >> cl::anychar_p
[plain_char
]
920 >> cl::ch_p(boost::ref(local
.mark
))
925 local
.simple_markup_end
926 = ( lookback
[cl::graph_p
] // final mark must be preceeded by
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
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")]
948 | "\\U" >> cl::repeat_p(8) [cl::chset
<>("0-9a-fA-F")]
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.")]
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")]
966 >> (*(cl::anychar_p
- "'''"))
967 >> ( cl::str_p("'''")
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")]
978 | "\\U" >> cl::repeat_p(8) [cl::chset
<>("0-9a-fA-F")]
980 | ('\\' >> cl::anychar_p
) [error("Invalid escape.")]
982 | ("'''" >> !eol
) [error("Boostbook escape invalid here.")]
983 >> (*(cl::anychar_p
- "'''"))
984 >> ( cl::str_p("'''")
985 | cl::eps_p
[error("Unclosed boostbook escape.")]
989 attribute_template_body
=
991 >> *( ~cl::eps_p(space
>> cl::end_p
| comment
)
995 >> ( cl::eps_p(cl::punct_p
)
998 >> (cl::eps_p
- (cl::alnum_p
| '_'))
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
1006 | cl::anychar_p
[raw_char
]
1012 attribute_value_1_7
=
1013 state
.values
.save() [
1014 +( ~cl::eps_p(']' | cl::space_p
| comment
)
1018 >> ( cl::eps_p(cl::punct_p
)
1021 >> (cl::eps_p
- (cl::alnum_p
| '_'))
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
1029 | cl::anychar_p
[raw_char
]
1040 state
.values
.list(block_tags::macro_definition
)
1042 >> local
.command_line_macro_identifier
1043 [state
.values
.entry(ph::arg1
, ph::arg2
)]
1047 >> to_value() [ inline_phrase
]
1054 local
.command_line_macro_identifier
=
1056 >> +(cl::anychar_p
- (cl::space_p
| '[' | '\\' | ']' | '='))
1057 | +(cl::anychar_p
- (cl::space_p
| ']' | '='))
1060 // Miscellaneous stuff
1062 // Follows an alphanumeric identifier - ensures that it doesn't
1063 // match an empty space in the middle of the identifier.
1065 (cl::eps_p
- (cl::alnum_p
| '_')) >> space
1069 *(cl::space_p
| comment
)
1073 *(cl::blank_p
| comment
)
1076 eol
= blank
>> cl::eol_p
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.
1088 "[/" >> *(local
.dummy_block
| (cl::anychar_p
- ']')) >> ']'
1092 '[' >> *(local
.dummy_block
| (cl::anychar_p
- ']')) >> ']'
1096 "[/" >> *(local
.line_dummy_block
| (cl::anychar_p
- (cl::eol_p
| ']'))) >> ']'
1099 local
.line_dummy_block
=
1100 '[' >> *(local
.line_dummy_block
| (cl::anychar_p
- (cl::eol_p
| ']'))) >> ']'
1105 >> +(cl::anychar_p
- (cl::space_p
| '[' | '\\' | ']'))
1107 >> +(cl::anychar_p
- (cl::space_p
| ']'))
1113 ////////////////////////////////////////////////////////////////////////////
1114 // Indentation Handling
1116 template <typename Iterator
> int indent_length(Iterator first
, Iterator end
)
1119 for (; first
!= end
; ++first
) {
1120 if (*first
== '\t') {
1121 // hardcoded tab to 4 for now
1122 length
= length
+ 4 - (length
% 4);
1132 void main_grammar_local::start_blocks_impl(parse_iterator
, parse_iterator
)
1134 list_stack
.push(list_stack_item(list_stack_item::top_level
));
1137 void main_grammar_local::start_nested_blocks_impl(
1138 parse_iterator
, parse_iterator
)
1140 // If this nested block is part of a list, then tell the
1142 state_
.in_list
= state_
.explicit_list
;
1143 state_
.explicit_list
= false;
1145 list_stack
.push(list_stack_item(list_stack_item::nested_block
));
1148 void main_grammar_local::end_blocks_impl(parse_iterator
, parse_iterator
)
1154 void main_grammar_local::check_indentation_impl(
1155 parse_iterator first_
, parse_iterator last_
)
1157 string_iterator first
= first_
.base();
1158 string_iterator last
= last_
.base();
1159 auto mark_pos
= string_view(first
, last
- first
).find_first_of("*#");
1161 if (mark_pos
== string_view::npos
) {
1162 plain_block(first
, last
);
1165 list_block(first
, first
+ mark_pos
, last
);
1169 void main_grammar_local::check_code_block_impl(
1170 parse_iterator first
, parse_iterator last
)
1172 unsigned int new_indent
= indent_length(first
.base(), last
.base());
1174 block_type
= (new_indent
> list_stack
.top().indent2
)
1176 : block_types::none
;
1179 void main_grammar_local::plain_block(
1180 string_iterator first
, string_iterator last
)
1182 if (qbk_version_n
>= 106u) {
1183 unsigned int new_indent
= indent_length(first
, last
);
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
;
1190 block_type
= block_types::paragraph
;
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
);
1200 list_indent
= list_stack
.top().indent
;
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.
1223 list_stack_item save
= list_stack
.top();
1227 list_stack
.top().type
!= list_stack_item::syntactic_list
1228 ? new_indent
>= list_stack
.top().indent
1229 : new_indent
> list_stack
.top().indent
);
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
;
1237 list_stack
.push(save
);
1241 block_type
= block_types::paragraph
;
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."
1249 ++state_
.error_count
;
1255 if (list_stack
.top().type
!= list_stack_item::nested_block
&&
1257 block_type
= block_types::code
;
1259 block_type
= block_types::paragraph
;
1263 void main_grammar_local::list_block(
1264 string_iterator first
, string_iterator mark_pos
, string_iterator last
)
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
;
1270 if (list_stack
.top().type
== list_stack_item::top_level
&&
1272 block_type
= block_types::code
;
1276 if (list_stack
.top().type
!= list_stack_item::syntactic_list
||
1277 new_indent
> list_indent
) {
1279 list_stack_item(list_mark
, new_indent
, new_indent2
));
1280 state_
.start_list(list_mark
);
1282 else if (new_indent
== list_indent
) {
1283 state_
.end_list_item();
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
);
1295 state_
.end_list_item();
1298 list_indent
= new_indent
;
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
;
1308 state_
.start_list_item();
1309 block_type
= block_types::list
;
1312 void main_grammar_local::clear_stack()
1314 while (list_stack
.top().type
== list_stack_item::syntactic_list
) {
1315 state_
.end_list_item();
1316 state_
.end_list(list_stack
.top().mark
);