/
BirdLeon
/
ROBLOX2016
Обзор
Документация
Войти
/
BirdLeon
/
ROBLOX2016
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
Library/boost/tools/quickbook/src/main_grammar.cpp
1 320 строк
47 KB
PatoFlamejanteTV
full source code
19 дек 2024, 19:11
19 дек 2024, 19:11
05db15d
Код
Авторство
О чём код?
/*============================================================================= 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(); } } }