summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/quickbook/src/main_grammar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/tools/quickbook/src/main_grammar.cpp')
-rw-r--r--src/boost/tools/quickbook/src/main_grammar.cpp1320
1 files changed, 1320 insertions, 0 deletions
diff --git a/src/boost/tools/quickbook/src/main_grammar.cpp b/src/boost/tools/quickbook/src/main_grammar.cpp
new file mode 100644
index 000000000..6d9368f3d
--- /dev/null
+++ b/src/boost/tools/quickbook/src/main_grammar.cpp
@@ -0,0 +1,1320 @@
+/*=============================================================================
+ Copyright (c) 2002 2004 2006 Joel de Guzman
+ Copyright (c) 2004 Eric Niebler
+ http://spirit.sourceforge.net/
+
+ Use, modification and distribution is subject to the Boost Software
+ License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+
+#include <boost/spirit/include/classic_attribute.hpp>
+#include <boost/spirit/include/classic_chset.hpp>
+#include <boost/spirit/include/classic_core.hpp>
+#include <boost/spirit/include/classic_if.hpp>
+#include <boost/spirit/include/classic_lazy.hpp>
+#include <boost/spirit/include/classic_loops.hpp>
+#include <boost/spirit/include/phoenix1_primitives.hpp>
+#include "actions.hpp"
+#include "block_tags.hpp"
+#include "grammar_impl.hpp"
+#include "parsers.hpp"
+#include "phrase_tags.hpp"
+#include "scoped.hpp"
+#include "state.hpp"
+#include "stream.hpp"
+#include "template_tags.hpp"
+#include "utils.hpp"
+
+namespace quickbook
+{
+ namespace cl = boost::spirit::classic;
+
+ struct list_stack_item
+ {
+ // Is this the root of the context
+ // (e.g. top, template, table cell etc.)
+ enum list_item_type
+ {
+ syntactic_list, // In a list marked up '*' or '#'
+ top_level, // At the top level of a parse
+ // (might be a template body)
+ nested_block // Nested in a block element.
+ } type;
+
+ unsigned int indent; // Indent of list marker
+ // (or paragraph if not in a list)
+ unsigned int indent2; // Indent of paragraph
+ char mark; // List mark, '\0' if not in a list.
+
+ // Example of inside a list:
+ //
+ // |indent
+ // * List item
+ // |indent2
+
+ explicit list_stack_item(list_item_type r)
+ : type(r), indent(0), indent2(0), mark('\0')
+ {
+ }
+
+ explicit list_stack_item(
+ char mark_, unsigned int indent_, unsigned int indent2_)
+ : type(syntactic_list)
+ , indent(indent_)
+ , indent2(indent2_)
+ , mark(mark_)
+ {
+ }
+ };
+
+ struct block_types
+ {
+ enum values
+ {
+ none,
+ code,
+ list,
+ paragraph
+ };
+ };
+
+ struct main_grammar_local
+ {
+ ////////////////////////////////////////////////////////////////////////
+ // Local actions
+
+ void start_blocks_impl(parse_iterator first, parse_iterator last);
+ void start_nested_blocks_impl(
+ parse_iterator first, parse_iterator last);
+ void end_blocks_impl(parse_iterator first, parse_iterator last);
+ void check_indentation_impl(parse_iterator first, parse_iterator last);
+ void check_code_block_impl(parse_iterator first, parse_iterator last);
+ void plain_block(string_iterator first, string_iterator last);
+ void list_block(
+ string_iterator first,
+ string_iterator mark_pos,
+ string_iterator last);
+ void clear_stack();
+
+ ////////////////////////////////////////////////////////////////////////
+ // Local members
+
+ cl::rule<scanner> template_phrase, top_level, indent_check,
+ paragraph_separator, inside_paragraph, code, code_line, blank_line,
+ hr, inline_code, skip_inline_code, template_, attribute_template,
+ template_body, code_block, skip_code_block, macro, template_args,
+ template_args_1_4, template_arg_1_4, template_inner_arg_1_4,
+ brackets_1_4, template_args_1_5, template_arg_1_5,
+ template_arg_1_5_content, template_inner_arg_1_5, brackets_1_5,
+ template_args_1_6, template_arg_1_6, template_arg_1_6_content,
+ break_, command_line_macro_identifier, dummy_block,
+ line_dummy_block, square_brackets, error_brackets, skip_escape;
+
+ struct block_context_closure
+ : cl::closure<block_context_closure, element_info::context>
+ {
+ // Mask used to determine whether or not an element is a block
+ // element.
+ member1 is_block_mask;
+ };
+
+ cl::rule<scanner> simple_markup, simple_markup_end;
+
+ cl::rule<scanner> paragraph;
+ cl::rule<scanner> list;
+ cl::rule<scanner, block_context_closure::context_t>
+ syntactic_block_item;
+ cl::rule<scanner> common;
+ cl::rule<scanner> element;
+
+ // state
+ std::stack<list_stack_item> list_stack;
+ unsigned int list_indent;
+ bool no_eols;
+ element_info::context context;
+ char mark; // Simple markup's deliminator
+ bool still_in_block; // Inside a syntatic block
+
+ // transitory state
+ block_types::values block_type;
+ element_info info;
+ element_info::type_enum element_type;
+
+ // state
+ quickbook::state& state_;
+
+ ////////////////////////////////////////////////////////////////////////
+ // Local constructor
+
+ main_grammar_local(quickbook::state& state)
+ : list_stack()
+ , list_indent(0)
+ , no_eols(true)
+ , context(element_info::in_top_level)
+ , mark('\0')
+ , state_(state)
+ {
+ }
+ };
+
+ struct process_element_impl : scoped_action_base
+ {
+ process_element_impl(main_grammar_local& l_)
+ : l(l_), pushed_source_mode_(false), element_context_error_(false)
+ {
+ }
+
+ bool start()
+ {
+ // This element doesn't exist in the current language version.
+ if (qbk_version_n < l.info.qbk_version) return false;
+
+ // The element is not allowed in this context.
+ if (!(l.info.type & l.context)) {
+ if (qbk_version_n < 107u) {
+ return false;
+ }
+ else {
+ element_context_error_ = true;
+ }
+ }
+
+ info_ = l.info;
+
+ if (info_.type != element_info::phrase &&
+ info_.type != element_info::maybe_block) {
+ paragraph_action para(l.state_);
+ para();
+ }
+
+ assert(l.state_.values.builder.empty());
+
+ if (l.state_.source_mode_next &&
+ info_.type != element_info::maybe_block) {
+ l.state_.push_tagged_source_mode(l.state_.source_mode_next);
+ pushed_source_mode_ = true;
+ l.state_.source_mode_next = 0;
+ }
+
+ return true;
+ }
+
+ template <typename ResultT, typename ScannerT>
+ bool result(ResultT r, ScannerT const& scan)
+ {
+ if (element_context_error_) {
+ error_message_action error(
+ l.state_, "Element not allowed in this context.");
+ error(scan.first, scan.first);
+ return true;
+ }
+ else if (r) {
+ return true;
+ }
+ else if (
+ qbk_version_n < 107u && info_.type & element_info::in_phrase) {
+ // Old versions of quickbook had a soft fail
+ // for unparsed phrase elements.
+ return false;
+ }
+ else {
+ // Parse error in body.
+ error_action error(l.state_);
+ error(scan.first, scan.first);
+ return true;
+ }
+ }
+
+ void success(parse_iterator, parse_iterator)
+ {
+ l.element_type = info_.type;
+ }
+ void failure() { l.element_type = element_info::nothing; }
+
+ void cleanup()
+ {
+ if (pushed_source_mode_) l.state_.pop_tagged_source_mode();
+ }
+
+ main_grammar_local& l;
+ element_info info_;
+ bool pushed_source_mode_;
+ bool element_context_error_;
+ };
+
+ struct scoped_paragraph : scoped_action_base
+ {
+ scoped_paragraph(quickbook::state& state_)
+ : state(state_), pushed(false)
+ {
+ }
+
+ bool start()
+ {
+ state.push_tagged_source_mode(state.source_mode_next);
+ pushed = true;
+ state.source_mode_next = 0;
+ return true;
+ }
+
+ void cleanup()
+ {
+ if (pushed) state.pop_tagged_source_mode();
+ }
+
+ quickbook::state& state;
+ bool pushed;
+ };
+
+ struct in_list_impl
+ {
+ main_grammar_local& l;
+
+ explicit in_list_impl(main_grammar_local& l_) : l(l_) {}
+
+ bool operator()() const
+ {
+ return !l.list_stack.empty() &&
+ l.list_stack.top().type == list_stack_item::syntactic_list;
+ }
+ };
+
+ template <typename T, typename M>
+ struct set_scoped_value_impl : scoped_action_base
+ {
+ typedef M T::*member_ptr;
+
+ explicit set_scoped_value_impl(T& l_, member_ptr ptr_)
+ : l(l_), ptr(ptr_), saved_value()
+ {
+ }
+
+ bool start(M const& value)
+ {
+ saved_value = l.*ptr;
+ l.*ptr = value;
+
+ return true;
+ }
+
+ void cleanup() { l.*ptr = saved_value; }
+
+ T& l;
+ member_ptr ptr;
+ M saved_value;
+ };
+
+ template <typename T, typename M>
+ struct set_scoped_value : scoped_parser<set_scoped_value_impl<T, M> >
+ {
+ typedef set_scoped_value_impl<T, M> impl;
+
+ set_scoped_value(T& l, typename impl::member_ptr ptr)
+ : scoped_parser<impl>(impl(l, ptr))
+ {
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Local grammar
+
+ void quickbook_grammar::impl::init_main()
+ {
+ main_grammar_local& local = cleanup_.add(new main_grammar_local(state));
+
+ // Global Actions
+ quickbook::element_action element_action(state);
+ quickbook::paragraph_action paragraph_action(state);
+
+ phrase_end_action end_phrase(state);
+ raw_char_action raw_char(state);
+ plain_char_action plain_char(state);
+ escape_unicode_action escape_unicode(state);
+
+ simple_phrase_action simple_markup(state);
+
+ break_action break_(state);
+ do_macro_action do_macro(state);
+
+ error_action error(state);
+ element_id_warning_action element_id_warning(state);
+
+ scoped_parser<to_value_scoped_action> to_value(state);
+ scoped_parser<scoped_paragraph> scope_paragraph(state);
+
+ quickbook_strict strict_mode(state);
+
+ // Local Actions
+ scoped_parser<process_element_impl> process_element(local);
+ in_list_impl in_list(local);
+
+ set_scoped_value<main_grammar_local, bool> scoped_no_eols(
+ local, &main_grammar_local::no_eols);
+ set_scoped_value<main_grammar_local, element_info::context>
+ scoped_context(local, &main_grammar_local::context);
+ set_scoped_value<main_grammar_local, bool> scoped_still_in_block(
+ local, &main_grammar_local::still_in_block);
+
+ member_action<main_grammar_local> check_indentation(
+ local, &main_grammar_local::check_indentation_impl);
+ member_action<main_grammar_local> check_code_block(
+ local, &main_grammar_local::check_code_block_impl);
+ member_action<main_grammar_local> start_blocks(
+ local, &main_grammar_local::start_blocks_impl);
+ member_action<main_grammar_local> start_nested_blocks(
+ local, &main_grammar_local::start_nested_blocks_impl);
+ member_action<main_grammar_local> end_blocks(
+ local, &main_grammar_local::end_blocks_impl);
+
+ // clang-format off
+
+ // phrase/phrase_start is used for an entirely self-contained
+ // phrase. For example, any remaining anchors are written out
+ // at the end instead of being saved for any following content.
+ phrase_start =
+ inline_phrase [end_phrase]
+ ;
+
+ // nested_phrase is used for a phrase nested inside square
+ // brackets.
+ nested_phrase =
+ state.values.save()
+ [
+ scoped_context(element_info::in_phrase)
+ [*(~cl::eps_p(']') >> local.common)]
+ ]
+ ;
+
+ // paragraph_phrase is like a nested_phrase but is also terminated
+ // by a paragraph end.
+ paragraph_phrase =
+ state.values.save()
+ [
+ scoped_context(element_info::in_phrase)
+ [*(~cl::eps_p(phrase_end) >> local.common)]
+ ]
+ ;
+
+ // extended_phrase is like a paragraph_phrase but allows some block
+ // elements.
+ extended_phrase =
+ state.values.save()
+ [
+ scoped_context(element_info::in_conditional)
+ [*(~cl::eps_p(phrase_end) >> local.common)]
+ ]
+ ;
+
+ // inline_phrase is used a phrase that isn't nested inside
+ // brackets, but is not self contained. An example of this
+ // is expanding a template, which is parsed separately but
+ // is part of the paragraph that contains it.
+ inline_phrase =
+ state.values.save()
+ [ qbk_ver(107u)
+ >> local.template_phrase
+ | qbk_ver(0, 107u)
+ >> scoped_context(element_info::in_phrase)
+ [*local.common]
+ ]
+ ;
+
+ table_title_phrase =
+ state.values.save()
+ [
+ scoped_context(element_info::in_phrase)
+ [ *( ~cl::eps_p(space >> (']' | '[' >> space >> '['))
+ >> local.common
+ )
+ ]
+ ]
+ ;
+
+ inside_preformatted =
+ scoped_no_eols(false)
+ [ paragraph_phrase
+ ]
+ ;
+
+ // Phrase templates can contain block tags, but can't contain
+ // syntatic blocks.
+ local.template_phrase =
+ scoped_context(element_info::in_top_level)
+ [ *( (local.paragraph_separator >> space >> cl::anychar_p)
+ [error("Paragraph in phrase template.")]
+ | local.common
+ )
+ ]
+ ;
+
+ // Top level blocks
+ block_start =
+ (*eol) [start_blocks]
+ >> ( *( local.top_level
+ >> !( qbk_ver(106u)
+ >> cl::ch_p(']')
+ >> cl::eps_p [error("Mismatched close bracket")]
+ )
+ )
+ ) [end_blocks]
+ ;
+
+ // Blocks contains within an element, e.g. a table cell or a footnote.
+ inside_paragraph =
+ state.values.save()
+ [ cl::eps_p [start_nested_blocks]
+ >> ( qbk_ver(107u)
+ >> (*eol)
+ >> (*local.top_level)
+ | qbk_ver(0, 107u)
+ >> local.inside_paragraph
+ ) [end_blocks]
+ ]
+ ;
+
+ local.top_level =
+ cl::eps_p(local.indent_check)
+ >> ( cl::eps_p(ph::var(local.block_type) == block_types::code)
+ >> local.code
+ | cl::eps_p(ph::var(local.block_type) == block_types::list)
+ >> local.list
+ | cl::eps_p(ph::var(local.block_type) == block_types::paragraph)
+ >> ( local.hr
+ | local.paragraph
+ )
+ )
+ >> *eol
+ ;
+
+ local.indent_check =
+ ( *cl::blank_p
+ >> !( (cl::ch_p('*') | '#')
+ >> *cl::blank_p)
+ ) [check_indentation]
+ ;
+
+ local.paragraph =
+ // Usually superfluous call
+ // for paragraphs in lists.
+ cl::eps_p [paragraph_action]
+ >> scope_paragraph()
+ [
+ scoped_context(element_info::in_top_level)
+ [ scoped_still_in_block(true)
+ [ local.syntactic_block_item(element_info::is_contextual_block)
+ >> *( cl::eps_p(ph::var(local.still_in_block))
+ >> local.syntactic_block_item(element_info::is_block)
+ )
+ ]
+ ]
+ ] [paragraph_action]
+ ;
+
+ local.list =
+ *cl::blank_p
+ >> (cl::ch_p('*') | '#')
+ >> (*cl::blank_p)
+ >> scoped_context(element_info::in_list_block)
+ [ scoped_still_in_block(true)
+ [ *( cl::eps_p(ph::var(local.still_in_block))
+ >> local.syntactic_block_item(element_info::is_block)
+ )
+ ]
+ ]
+ ;
+
+ local.syntactic_block_item =
+ local.paragraph_separator [ph::var(local.still_in_block) = false]
+ | (cl::eps_p(~cl::ch_p(']')) | qbk_ver(0, 107u))
+ [ph::var(local.element_type) = element_info::nothing]
+ >> local.common
+
+ // If the element is a block, then a newline will end the
+ // current syntactic block.
+ //
+ // Note that we don't do this for lists in 1.6, as it causes
+ // the list block to end. The support for nested syntactic
+ // blocks in 1.7 will fix that. Although it does mean the
+ // following line will need to be indented.
+ >> !( cl::eps_p(in_list) >> qbk_ver(106u, 107u)
+ | cl::eps_p
+ (
+ ph::static_cast_<int>(local.syntactic_block_item.is_block_mask) &
+ ph::static_cast_<int>(ph::var(local.element_type))
+ )
+ >> eol [ph::var(local.still_in_block) = false]
+ )
+ ;
+
+ local.paragraph_separator =
+ cl::eol_p
+ >> cl::eps_p
+ ( *cl::blank_p
+ >> ( cl::eol_p
+ | cl::end_p
+ | cl::eps_p(in_list) >> (cl::ch_p('*') | '#')
+ )
+ )
+ >> *eol
+ ;
+
+ // Blocks contains within an element, e.g. a table cell or a footnote.
+ local.inside_paragraph =
+ scoped_context(element_info::in_nested_block)
+ [ *( local.paragraph_separator [paragraph_action]
+ | ~cl::eps_p(']')
+ >> local.common
+ )
+ ] [paragraph_action]
+ ;
+
+ local.hr =
+ cl::str_p("----")
+ >> state.values.list(block_tags::hr)
+ [ ( qbk_ver(106u)
+ >> *(line_comment | (cl::anychar_p - (cl::eol_p | '[' | ']')))
+ | qbk_ver(0, 106u)
+ >> *(line_comment | (cl::anychar_p - (cl::eol_p | "[/")))
+ )
+ >> *eol
+ ] [element_action]
+ ;
+
+ local.element
+ = '['
+ >> ( cl::eps_p(cl::punct_p)
+ >> elements [ph::var(local.info) = ph::arg1]
+ | elements [ph::var(local.info) = ph::arg1]
+ >> (cl::eps_p - (cl::alnum_p | '_'))
+ )
+ >> process_element()
+ [ state.values.list(ph::var(local.info.tag))
+ [ cl::lazy_p(*ph::var(local.info.rule))
+ >> space
+ >> ']'
+ ] [element_action]
+ ]
+ ;
+
+ local.code =
+ state.values.list(code_tags::code_block)
+ [( local.code_line
+ >> *(*local.blank_line >> local.code_line)
+ ) [state.values.entry(ph::arg1, ph::arg2)]
+ ] [element_action]
+ >> *eol
+ ;
+
+ local.code_line =
+ ( *cl::blank_p
+ >> ~cl::eps_p(cl::eol_p)
+ ) [check_code_block]
+ >> cl::eps_p(ph::var(local.block_type) == block_types::code)
+ >> *(cl::anychar_p - cl::eol_p)
+ >> (cl::eol_p | cl::end_p)
+ ;
+
+ local.blank_line =
+ *cl::blank_p >> cl::eol_p
+ ;
+
+ local.common =
+ local.macro
+ | local.element
+ | local.template_
+ | local.break_
+ | local.code_block
+ | local.inline_code
+ | local.simple_markup
+ | escape
+ | comment
+ | strict_mode
+ >> ( local.error_brackets [error("Invalid template/tag (strict mode)")]
+ | cl::eps_p('[') [error("Mismatched open bracket (strict mode)")]
+ >> cl::anychar_p
+ | cl::eps_p(']') [error("Mismatched close bracket (strict mode)")]
+ >> cl::anychar_p
+ )
+ | qbk_ver(106u)
+ >> local.square_brackets
+ | cl::space_p [raw_char]
+ | cl::anychar_p [plain_char]
+ ;
+
+ skip_entity =
+ '['
+ // For escaped templates:
+ >> !(space >> cl::ch_p('`') >> (cl::alpha_p | '_'))
+ >> *(~cl::eps_p(']') >> skip_entity)
+ >> !cl::ch_p(']')
+ | local.skip_code_block
+ | local.skip_inline_code
+ | local.skip_escape
+ | comment
+ | (cl::anychar_p - '[' - ']')
+ ;
+
+ local.square_brackets =
+ ( cl::ch_p('[') [plain_char]
+ >> paragraph_phrase
+ >> ( cl::ch_p(']') [plain_char]
+ | cl::eps_p [error("Missing close bracket")]
+ )
+ | cl::ch_p(']') [plain_char]
+ >> cl::eps_p [error("Mismatched close bracket")]
+ )
+ ;
+
+ local.error_brackets =
+ cl::ch_p('[') [plain_char]
+ >> ( local.error_brackets
+ | (cl::anychar_p - ']')
+ )
+ >> cl::ch_p(']')
+ ;
+
+ local.macro =
+ cl::eps_p
+ ( ( state.macro
+ >> ~cl::eps_p(cl::alpha_p | '_')
+ // must not be followed by alpha or underscore
+ )
+ & macro_identifier // must be a valid macro for the current version
+ )
+ >> state.macro [do_macro]
+ ;
+
+ local.template_ =
+ ( '['
+ >> space
+ >> state.values.list(template_tags::template_)
+ [ local.template_body
+ >> ']'
+ ]
+ ) [element_action]
+ ;
+
+ local.attribute_template =
+ ( '['
+ >> space
+ >> state.values.list(template_tags::attribute_template)
+ [ local.template_body
+ >> ']'
+ ]
+ ) [element_action]
+ ;
+
+ local.template_body =
+ ( cl::str_p('`')
+ >> cl::eps_p(cl::punct_p)
+ >> state.templates.scope
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::escape)]
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
+ >> !( qbk_ver(106u)
+ [error("Templates with punctuation names can't be escaped in quickbook 1.6+")]
+ | strict_mode
+ [error("Templates with punctuation names can't be escaped (strict mode)")]
+ )
+ | cl::str_p('`')
+ >> state.templates.scope
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::escape)]
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
+
+ | cl::eps_p(cl::punct_p)
+ >> state.templates.scope
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
+
+ | state.templates.scope
+ [state.values.entry(ph::arg1, ph::arg2, template_tags::identifier)]
+ >> cl::eps_p(hard_space)
+ )
+ >> space
+ >> !local.template_args
+ ;
+
+ local.template_args =
+ qbk_ver(106u) >> local.template_args_1_6
+ | qbk_ver(105u, 106u) >> local.template_args_1_5
+ | qbk_ver(0, 105u) >> local.template_args_1_4
+ ;
+
+ local.template_args_1_4 = local.template_arg_1_4 >> *(".." >> local.template_arg_1_4);
+
+ local.template_arg_1_4 =
+ ( cl::eps_p(*cl::blank_p >> cl::eol_p)
+ >> local.template_inner_arg_1_4 [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
+ | local.template_inner_arg_1_4 [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
+ )
+ ;
+
+ local.template_inner_arg_1_4 =
+ +(local.brackets_1_4 | (cl::anychar_p - (cl::str_p("..") | ']')))
+ ;
+
+ local.brackets_1_4 =
+ '[' >> local.template_inner_arg_1_4 >> ']'
+ ;
+
+ local.template_args_1_5 = local.template_arg_1_5 >> *(".." >> local.template_arg_1_5);
+
+ local.template_arg_1_5 =
+ ( cl::eps_p(*cl::blank_p >> cl::eol_p)
+ >> local.template_arg_1_5_content [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
+ | local.template_arg_1_5_content [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
+ )
+ ;
+
+ local.template_arg_1_5_content =
+ +(local.brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p("..") | '[' | ']')))
+ ;
+
+ local.template_inner_arg_1_5 =
+ +(local.brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p('[') | ']')))
+ ;
+
+ local.brackets_1_5 =
+ '[' >> local.template_inner_arg_1_5 >> ']'
+ ;
+
+ local.template_args_1_6 = local.template_arg_1_6 >> *(".." >> local.template_arg_1_6);
+
+ local.template_arg_1_6 =
+ ( cl::eps_p(*cl::blank_p >> cl::eol_p)
+ >> local.template_arg_1_6_content [state.values.entry(ph::arg1, ph::arg2, template_tags::block)]
+ | local.template_arg_1_6_content [state.values.entry(ph::arg1, ph::arg2, template_tags::phrase)]
+ )
+ ;
+
+ local.template_arg_1_6_content =
+ + ( ~cl::eps_p("..") >> skip_entity )
+ ;
+
+ local.break_
+ = ( '['
+ >> space
+ >> "br"
+ >> space
+ >> ']'
+ ) [break_]
+ ;
+
+ local.inline_code =
+ '`' >> state.values.list(code_tags::inline_code)
+ [(
+ *(cl::anychar_p -
+ ( '`'
+ | (cl::eol_p >> *cl::blank_p >> cl::eol_p)
+ // Make sure that we don't go
+ ) // past a single block
+ ) >> cl::eps_p('`')
+ ) [state.values.entry(ph::arg1, ph::arg2)]
+ >> '`'
+ ] [element_action]
+ ;
+
+ local.skip_inline_code =
+ '`'
+ >> *(cl::anychar_p -
+ ( '`'
+ | (cl::eol_p >> *cl::blank_p >> cl::eol_p)
+ // Make sure that we don't go
+ ) // past a single block
+ )
+ >> !cl::ch_p('`')
+ ;
+
+ local.skip_code_block =
+ "```"
+ >> ~cl::eps_p("`")
+ >> ( (!( *(*cl::blank_p >> cl::eol_p)
+ >> ( *( "````" >> *cl::ch_p('`')
+ | ( cl::anychar_p
+ - (*cl::space_p >> "```" >> ~cl::eps_p("`"))
+ )
+ )
+ >> !(*cl::blank_p >> cl::eol_p)
+ )
+ >> (*cl::space_p >> "```")
+ ))
+ | *cl::anychar_p
+ )
+ | "``"
+ >> ~cl::eps_p("`")
+ >> ( ( *(*cl::blank_p >> cl::eol_p)
+ >> ( *( "```" >> *cl::ch_p('`')
+ | ( cl::anychar_p
+ - (*cl::space_p >> "``" >> ~cl::eps_p("`"))
+ )
+ )
+ >> !(*cl::blank_p >> cl::eol_p)
+ )
+ >> (*cl::space_p >> "``")
+ )
+ | *cl::anychar_p
+ )
+ ;
+
+ local.code_block =
+ "```"
+ >> ~cl::eps_p("`")
+ >> ( state.values.list(code_tags::inline_code_block)
+ [ *(*cl::blank_p >> cl::eol_p)
+ >> ( *( "````" >> *cl::ch_p('`')
+ | ( cl::anychar_p
+ - (*cl::space_p >> "```" >> ~cl::eps_p("`"))
+ )
+ )
+ >> !(*cl::blank_p >> cl::eol_p)
+ ) [state.values.entry(ph::arg1, ph::arg2)]
+ >> (*cl::space_p >> "```")
+ ] [element_action]
+ | cl::eps_p [error("Unfinished code block")]
+ >> *cl::anychar_p
+ )
+ | "``"
+ >> ~cl::eps_p("`")
+ >> ( state.values.list(code_tags::inline_code_block)
+ [ *(*cl::blank_p >> cl::eol_p)
+ >> ( *( "```" >> *cl::ch_p('`')
+ | ( cl::anychar_p
+ - (*cl::space_p >> "``" >> ~cl::eps_p("`"))
+ )
+ )
+ >> !(*cl::blank_p >> cl::eol_p)
+ ) [state.values.entry(ph::arg1, ph::arg2)]
+ >> (*cl::space_p >> "``")
+ ] [element_action]
+ | cl::eps_p [error("Unfinished code block")]
+ >> *cl::anychar_p
+ )
+ ;
+
+ local.simple_markup =
+ cl::chset<>("*/_=") [ph::var(local.mark) = ph::arg1]
+ >> cl::eps_p(cl::graph_p) // graph_p must follow first mark
+ >> lookback
+ [ cl::anychar_p // skip back over the markup
+ >> ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
+ // first mark not be preceeded by
+ // the same character.
+ >> (cl::space_p | cl::punct_p | cl::end_p)
+ // first mark must be preceeded
+ // by space or punctuation or the
+ // mark character or a the start.
+ ]
+ >> state.values.save()
+ [
+ to_value()
+ [
+ cl::eps_p((state.macro & macro_identifier) >> local.simple_markup_end)
+ >> state.macro [do_macro]
+ | ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
+ >> +( ~cl::eps_p
+ ( lookback [~cl::ch_p(boost::ref(local.mark))]
+ >> local.simple_markup_end
+ )
+ >> cl::anychar_p [plain_char]
+ )
+ ]
+ >> cl::ch_p(boost::ref(local.mark))
+ [simple_markup]
+ ]
+ ;
+
+ local.simple_markup_end
+ = ( lookback[cl::graph_p] // final mark must be preceeded by
+ // graph_p
+ >> cl::ch_p(boost::ref(local.mark))
+ >> ~cl::eps_p(cl::ch_p(boost::ref(local.mark)))
+ // final mark not be followed by
+ // the same character.
+ >> (cl::space_p | cl::punct_p | cl::end_p)
+ // final mark must be followed by
+ // space or punctuation
+ )
+ | '['
+ | "'''"
+ | '`'
+ | phrase_end
+ ;
+
+ escape =
+ cl::str_p("\\n") [break_]
+ | cl::str_p("\\ ") // ignore an escaped space
+ | '\\' >> cl::punct_p [plain_char]
+ | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
+ [escape_unicode]
+ | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
+ [escape_unicode]
+ | ("'''" >> !eol)
+ >> state.values.save()
+ [ (*(cl::anychar_p - "'''")) [state.values.entry(ph::arg1, ph::arg2, phrase_tags::escape)]
+ >> ( cl::str_p("'''")
+ | cl::eps_p [error("Unclosed boostbook escape.")]
+ ) [element_action]
+ ]
+ ;
+
+ local.skip_escape =
+ cl::str_p("\\n")
+ | cl::str_p("\\ ")
+ | '\\' >> cl::punct_p
+ | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
+ | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
+ | ("'''" >> !eol)
+ >> (*(cl::anychar_p - "'''"))
+ >> ( cl::str_p("'''")
+ | cl::eps_p
+ )
+ ;
+
+ raw_escape =
+ cl::str_p("\\n") [error("Newlines invalid here.")]
+ | cl::str_p("\\ ") // ignore an escaped space
+ | '\\' >> cl::punct_p [raw_char]
+ | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
+ [escape_unicode]
+ | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
+ [escape_unicode]
+ | ('\\' >> cl::anychar_p) [error("Invalid escape.")]
+ [raw_char]
+ | ("'''" >> !eol) [error("Boostbook escape invalid here.")]
+ >> (*(cl::anychar_p - "'''"))
+ >> ( cl::str_p("'''")
+ | cl::eps_p [error("Unclosed boostbook escape.")]
+ )
+ ;
+
+ attribute_template_body =
+ space
+ >> *( ~cl::eps_p(space >> cl::end_p | comment)
+ >> ( cl::eps_p
+ ( cl::ch_p('[')
+ >> space
+ >> ( cl::eps_p(cl::punct_p)
+ >> elements
+ | elements
+ >> (cl::eps_p - (cl::alnum_p | '_'))
+ )
+ ) [error("Elements not allowed in attribute values.")]
+ >> local.square_brackets
+ | local.attribute_template
+ | cl::eps_p(cl::ch_p('[')) [error("Unmatched template in attribute value.")]
+ >> local.square_brackets
+ | raw_escape
+ | cl::anychar_p [raw_char]
+ )
+ )
+ >> space
+ ;
+
+ attribute_value_1_7 =
+ state.values.save() [
+ +( ~cl::eps_p(']' | cl::space_p | comment)
+ >> ( cl::eps_p
+ ( cl::ch_p('[')
+ >> space
+ >> ( cl::eps_p(cl::punct_p)
+ >> elements
+ | elements
+ >> (cl::eps_p - (cl::alnum_p | '_'))
+ )
+ ) [error("Elements not allowed in attribute values.")]
+ >> local.square_brackets
+ | local.attribute_template
+ | cl::eps_p(cl::ch_p('['))[error("Unmatched template in attribute value.")]
+ >> local.square_brackets
+ | raw_escape
+ | cl::anychar_p [raw_char]
+ )
+ )
+ ]
+ ;
+
+ //
+ // Command line
+ //
+
+ command_line =
+ state.values.list(block_tags::macro_definition)
+ [ *cl::space_p
+ >> local.command_line_macro_identifier
+ [state.values.entry(ph::arg1, ph::arg2)]
+ >> *cl::space_p
+ >> !( '='
+ >> *cl::space_p
+ >> to_value() [ inline_phrase ]
+ >> *cl::space_p
+ )
+ >> cl::end_p
+ ] [element_action]
+ ;
+
+ local.command_line_macro_identifier =
+ qbk_ver(106u)
+ >> +(cl::anychar_p - (cl::space_p | '[' | '\\' | ']' | '='))
+ | +(cl::anychar_p - (cl::space_p | ']' | '='))
+ ;
+
+ // Miscellaneous stuff
+
+ // Follows an alphanumeric identifier - ensures that it doesn't
+ // match an empty space in the middle of the identifier.
+ hard_space =
+ (cl::eps_p - (cl::alnum_p | '_')) >> space
+ ;
+
+ space =
+ *(cl::space_p | comment)
+ ;
+
+ blank =
+ *(cl::blank_p | comment)
+ ;
+
+ eol = blank >> cl::eol_p
+ ;
+
+ phrase_end =
+ ']'
+ | cl::eps_p(ph::var(local.no_eols))
+ >> cl::eol_p >> *cl::blank_p >> cl::eol_p
+ ; // Make sure that we don't go
+ // past a single block, except
+ // when preformatted.
+
+ comment =
+ "[/" >> *(local.dummy_block | (cl::anychar_p - ']')) >> ']'
+ ;
+
+ local.dummy_block =
+ '[' >> *(local.dummy_block | (cl::anychar_p - ']')) >> ']'
+ ;
+
+ line_comment =
+ "[/" >> *(local.line_dummy_block | (cl::anychar_p - (cl::eol_p | ']'))) >> ']'
+ ;
+
+ local.line_dummy_block =
+ '[' >> *(local.line_dummy_block | (cl::anychar_p - (cl::eol_p | ']'))) >> ']'
+ ;
+
+ macro_identifier =
+ qbk_ver(106u)
+ >> +(cl::anychar_p - (cl::space_p | '[' | '\\' | ']'))
+ | qbk_ver(0, 106u)
+ >> +(cl::anychar_p - (cl::space_p | ']'))
+ ;
+
+ // clang-format on
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Indentation Handling
+
+ template <typename Iterator> int indent_length(Iterator first, Iterator end)
+ {
+ int length = 0;
+ for (; first != end; ++first) {
+ if (*first == '\t') {
+ // hardcoded tab to 4 for now
+ length = length + 4 - (length % 4);
+ }
+ else {
+ ++length;
+ }
+ }
+
+ return length;
+ }
+
+ void main_grammar_local::start_blocks_impl(parse_iterator, parse_iterator)
+ {
+ list_stack.push(list_stack_item(list_stack_item::top_level));
+ }
+
+ void main_grammar_local::start_nested_blocks_impl(
+ parse_iterator, parse_iterator)
+ {
+ // If this nested block is part of a list, then tell the
+ // output state.
+ state_.in_list = state_.explicit_list;
+ state_.explicit_list = false;
+
+ list_stack.push(list_stack_item(list_stack_item::nested_block));
+ }
+
+ void main_grammar_local::end_blocks_impl(parse_iterator, parse_iterator)
+ {
+ clear_stack();
+ list_stack.pop();
+ }
+
+ void main_grammar_local::check_indentation_impl(
+ parse_iterator first_, parse_iterator last_)
+ {
+ string_iterator first = first_.base();
+ string_iterator last = last_.base();
+ auto mark_pos = string_view(first, last - first).find_first_of("*#");
+
+ if (mark_pos == string_view::npos) {
+ plain_block(first, last);
+ }
+ else {
+ list_block(first, first + mark_pos, last);
+ }
+ }
+
+ void main_grammar_local::check_code_block_impl(
+ parse_iterator first, parse_iterator last)
+ {
+ unsigned int new_indent = indent_length(first.base(), last.base());
+
+ block_type = (new_indent > list_stack.top().indent2)
+ ? block_types::code
+ : block_types::none;
+ }
+
+ void main_grammar_local::plain_block(
+ string_iterator first, string_iterator last)
+ {
+ if (qbk_version_n >= 106u) {
+ unsigned int new_indent = indent_length(first, last);
+
+ if (new_indent > list_stack.top().indent2) {
+ if (list_stack.top().type != list_stack_item::nested_block) {
+ block_type = block_types::code;
+ }
+ else {
+ block_type = block_types::paragraph;
+ }
+ }
+ else {
+ while (list_stack.top().type ==
+ list_stack_item::syntactic_list &&
+ new_indent < list_stack.top().indent) {
+ state_.end_list_item();
+ state_.end_list(list_stack.top().mark);
+ list_stack.pop();
+ list_indent = list_stack.top().indent;
+ }
+
+ if (list_stack.top().type == list_stack_item::syntactic_list &&
+ new_indent == list_stack.top().indent) {
+ // If the paragraph is aligned with the list item's marker,
+ // then end the current list item if that's aligned (or to
+ // the left of) the parent's paragraph.
+ //
+ // i.e.
+ //
+ // * Level 1
+ // * Level 2
+ //
+ // Still Level 2
+ //
+ // vs.
+ //
+ // * Level 1
+ // * Level 2
+ //
+ // Back to Level 1
+
+ list_stack_item save = list_stack.top();
+ list_stack.pop();
+
+ assert(
+ list_stack.top().type != list_stack_item::syntactic_list
+ ? new_indent >= list_stack.top().indent
+ : new_indent > list_stack.top().indent);
+
+ if (new_indent <= list_stack.top().indent2) {
+ state_.end_list_item();
+ state_.end_list(save.mark);
+ list_indent = list_stack.top().indent;
+ }
+ else {
+ list_stack.push(save);
+ }
+ }
+
+ block_type = block_types::paragraph;
+ }
+
+ if (qbk_version_n == 106u &&
+ list_stack.top().type == list_stack_item::syntactic_list) {
+ detail::outerr(state_.current_file, first)
+ << "Paragraphs in lists aren't supported in quickbook 1.6."
+ << std::endl;
+ ++state_.error_count;
+ }
+ }
+ else {
+ clear_stack();
+
+ if (list_stack.top().type != list_stack_item::nested_block &&
+ last != first)
+ block_type = block_types::code;
+ else
+ block_type = block_types::paragraph;
+ }
+ }
+
+ void main_grammar_local::list_block(
+ string_iterator first, string_iterator mark_pos, string_iterator last)
+ {
+ unsigned int new_indent = indent_length(first, mark_pos);
+ unsigned int new_indent2 = indent_length(first, last);
+ char list_mark = *mark_pos;
+
+ if (list_stack.top().type == list_stack_item::top_level &&
+ new_indent > 0) {
+ block_type = block_types::code;
+ return;
+ }
+
+ if (list_stack.top().type != list_stack_item::syntactic_list ||
+ new_indent > list_indent) {
+ list_stack.push(
+ list_stack_item(list_mark, new_indent, new_indent2));
+ state_.start_list(list_mark);
+ }
+ else if (new_indent == list_indent) {
+ state_.end_list_item();
+ }
+ else {
+ // This should never reach root, since the first list
+ // has indentation 0.
+ while (list_stack.top().type == list_stack_item::syntactic_list &&
+ new_indent < list_stack.top().indent) {
+ state_.end_list_item();
+ state_.end_list(list_stack.top().mark);
+ list_stack.pop();
+ }
+
+ state_.end_list_item();
+ }
+
+ list_indent = new_indent;
+
+ if (list_mark != list_stack.top().mark) {
+ detail::outerr(state_.current_file, first)
+ << "Illegal change of list style.\n";
+ detail::outwarn(state_.current_file, first)
+ << "Ignoring change of list style." << std::endl;
+ ++state_.error_count;
+ }
+
+ state_.start_list_item();
+ block_type = block_types::list;
+ }
+
+ void main_grammar_local::clear_stack()
+ {
+ while (list_stack.top().type == list_stack_item::syntactic_list) {
+ state_.end_list_item();
+ state_.end_list(list_stack.top().mark);
+ list_stack.pop();
+ }
+ }
+}