| /*============================================================================= |
| 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 "grammar_impl.hpp" |
| #include "actions_class.hpp" |
| #include "utils.hpp" |
| #include "template_tags.hpp" |
| #include "block_tags.hpp" |
| #include "phrase_tags.hpp" |
| #include "parsers.hpp" |
| #include "scoped.hpp" |
| #include <boost/spirit/include/classic_core.hpp> |
| #include <boost/spirit/include/classic_chset.hpp> |
| #include <boost/spirit/include/classic_if.hpp> |
| #include <boost/spirit/include/classic_loops.hpp> |
| #include <boost/spirit/include/classic_attribute.hpp> |
| #include <boost/spirit/include/classic_lazy.hpp> |
| #include <boost/spirit/include/phoenix1_primitives.hpp> |
| |
| namespace quickbook |
| { |
| namespace cl = boost::spirit::classic; |
| |
| struct main_grammar_local |
| { |
| struct process_element_impl : scoped_action_base { |
| process_element_impl(main_grammar_local& l) |
| : l(l) {} |
| |
| bool start() |
| { |
| if (!(l.info.type & l.actions_.context) || |
| qbk_version_n < l.info.qbk_version) |
| return false; |
| |
| info_ = l.info; |
| |
| if (!(info_.type & element_info::in_phrase)) |
| l.actions_.paragraph(); |
| |
| l.actions_.values.builder.reset(); |
| |
| return true; |
| } |
| |
| template <typename ResultT, typename ScannerT> |
| bool result(ResultT result, ScannerT const& scan) |
| { |
| if (result || info_.type & element_info::in_phrase) |
| return result; |
| |
| l.actions_.error(scan.first, scan.first); |
| return true; |
| } |
| |
| void success() { l.element_type = info_.type; } |
| void failure() { l.element_type = element_info::nothing; } |
| |
| main_grammar_local& l; |
| element_info info_; |
| }; |
| |
| struct is_block_type |
| { |
| typedef bool result_type; |
| template <typename Arg1 = void> |
| struct result { typedef bool type; }; |
| |
| is_block_type(main_grammar_local& l) |
| : l_(l) |
| {} |
| |
| bool operator()() const |
| { |
| return l_.element_type && !(l_.element_type & element_info::in_phrase); |
| } |
| |
| main_grammar_local& l_; |
| }; |
| |
| cl::rule<scanner> |
| top_level, blocks, paragraph_separator, |
| code, code_line, blank_line, hr, |
| list, list_item, |
| escape, |
| inline_code, |
| template_, |
| 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, |
| break_, |
| command_line_macro_identifier, command_line_phrase, |
| dummy_block |
| ; |
| |
| cl::rule<scanner> element; |
| |
| struct simple_markup_closure |
| : cl::closure<simple_markup_closure, char> |
| { |
| member1 mark; |
| }; |
| |
| cl::rule<scanner, simple_markup_closure::context_t> simple_markup; |
| cl::rule<scanner> simple_markup_end; |
| |
| element_info info; |
| element_info::type_enum element_type; |
| |
| quickbook::actions& actions_; |
| scoped_parser<process_element_impl> process_element; |
| is_block_type is_block; |
| |
| main_grammar_local(quickbook::actions& actions) |
| : actions_(actions) |
| , process_element(*this) |
| , is_block(*this) |
| {} |
| }; |
| |
| void quickbook_grammar::impl::init_main() |
| { |
| main_grammar_local& local = cleanup_.add( |
| new main_grammar_local(actions)); |
| |
| block_skip_initial_spaces = |
| *(cl::blank_p | comment) >> block_start |
| ; |
| |
| block_start = |
| local.top_level >> blank |
| ; |
| |
| local.top_level = |
| actions.scoped_context(element_info::in_block) |
| [ local.blocks |
| >> *( local.element |
| >> cl::if_p(local.is_block) |
| [ !(+eol >> local.blocks) |
| ] |
| | local.paragraph_separator >> local.blocks |
| | common |
| | cl::space_p [actions.space_char] |
| | cl::anychar_p [actions.plain_char] |
| ) |
| ] |
| ; |
| |
| local.blocks = |
| *( local.code |
| | local.list |
| | local.hr |
| | +eol |
| ) |
| ; |
| |
| local.paragraph_separator |
| = cl::eol_p |
| >> *cl::blank_p |
| >> cl::eol_p [actions.paragraph] |
| ; |
| |
| local.hr = |
| cl::str_p("----") |
| >> actions.values.list(block_tags::hr) |
| [ *(cl::anychar_p - eol) |
| >> +eol |
| ] [actions.element] |
| ; |
| |
| 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 | '_')) |
| ) |
| >> local.process_element() |
| [ actions.values.list(ph::var(local.info.tag)) |
| [ cl::lazy_p(*ph::var(local.info.rule)) |
| >> space |
| >> ']' |
| ] [actions.element] |
| ] |
| ; |
| |
| local.code = |
| ( |
| local.code_line |
| >> *(*local.blank_line >> local.code_line) |
| ) [actions.code] |
| >> *eol |
| ; |
| |
| local.code_line = |
| cl::blank_p >> *(cl::anychar_p - cl::eol_p) >> cl::eol_p |
| ; |
| |
| local.blank_line = |
| *cl::blank_p >> cl::eol_p |
| ; |
| |
| local.list = |
| cl::eps_p(cl::ch_p('*') | '#') |
| [actions.values.reset()] |
| >> actions.scoped_output() |
| [ |
| actions.values.list(block_tags::list) |
| [ +actions.values.list() |
| [ (*cl::blank_p) [actions.values.entry(ph::arg1, ph::arg2, general_tags::list_indent)] |
| >> (cl::ch_p('*') | '#') |
| [actions.values.entry(ph::arg1, ph::arg2, general_tags::list_mark)] |
| >> *cl::blank_p |
| >> local.list_item [actions.to_value] |
| ] |
| ] |
| ] [actions.element] |
| ; |
| |
| local.list_item = |
| actions.scoped_context(element_info::in_phrase) |
| [ |
| actions.values.save() |
| [ |
| *( common |
| | (cl::anychar_p - |
| ( cl::eol_p >> *cl::blank_p |
| >> (cl::ch_p('*') | '#' | cl::eol_p) |
| ) |
| ) [actions.plain_char] |
| ) |
| ] |
| ] |
| >> +eol |
| ; |
| |
| common = |
| local.macro |
| | local.element |
| | local.template_ |
| | local.break_ |
| | local.code_block |
| | local.inline_code |
| | local.simple_markup |
| | local.escape |
| | comment |
| ; |
| |
| local.macro = |
| // must not be followed by alpha or underscore |
| cl::eps_p(actions.macro |
| >> (cl::eps_p - (cl::alpha_p | '_'))) |
| >> actions.macro [actions.do_macro] |
| ; |
| |
| local.template_ = |
| ( '[' |
| >> space |
| >> actions.values.list(template_tags::template_) |
| [ !cl::str_p("`") [actions.values.entry(ph::arg1, ph::arg2, template_tags::escape)] |
| >> ( cl::eps_p(cl::punct_p) |
| >> actions.templates.scope [actions.values.entry(ph::arg1, ph::arg2, template_tags::identifier)] |
| | actions.templates.scope [actions.values.entry(ph::arg1, ph::arg2, template_tags::identifier)] |
| >> cl::eps_p(hard_space) |
| ) |
| >> space |
| >> !local.template_args |
| >> ']' |
| ] |
| ) [actions.element] |
| ; |
| |
| local.template_args = |
| cl::if_p(qbk_since(105u)) [ |
| local.template_args_1_5 |
| ].else_p [ |
| 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 [actions.values.entry(ph::arg1, ph::arg2, template_tags::block)] |
| | local.template_inner_arg_1_4 [actions.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 [actions.values.entry(ph::arg1, ph::arg2, template_tags::block)] |
| | local.template_arg_1_5_content [actions.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.break_ |
| = ( '[' |
| >> space |
| >> "br" |
| >> space |
| >> ']' |
| ) [actions.break_] |
| ; |
| |
| local.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('`') |
| ) [actions.inline_code] |
| >> '`' |
| ; |
| |
| local.code_block = |
| ( |
| "```" >> |
| ( |
| *(cl::anychar_p - "```") |
| >> cl::eps_p("```") |
| ) [actions.code_block] |
| >> "```" |
| ) |
| | ( |
| "``" >> |
| ( |
| *(cl::anychar_p - "``") |
| >> cl::eps_p("``") |
| ) [actions.code_block] |
| >> "``" |
| ) |
| ; |
| |
| local.simple_markup = |
| cl::chset<>("*/_=") [local.simple_markup.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::f_ch_p(local.simple_markup.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. |
| ] |
| >> actions.values.save() |
| [ |
| actions.scoped_output() |
| [ |
| ( cl::eps_p(actions.macro >> local.simple_markup_end) |
| >> actions.macro [actions.do_macro] |
| | ~cl::eps_p(cl::f_ch_p(local.simple_markup.mark)) |
| >> +( ~cl::eps_p |
| ( lookback [~cl::f_ch_p(local.simple_markup.mark)] |
| >> local.simple_markup_end |
| ) |
| >> cl::anychar_p [actions.plain_char] |
| ) |
| ) [actions.to_value] |
| ] |
| >> cl::f_ch_p(local.simple_markup.mark) |
| [actions.simple_markup] |
| ] |
| ; |
| |
| local.simple_markup_end |
| = ( lookback[cl::graph_p] // final mark must be preceeded by |
| // graph_p |
| >> cl::f_ch_p(local.simple_markup.mark) |
| >> ~cl::eps_p(cl::f_ch_p(local.simple_markup.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 |
| ; |
| |
| phrase = |
| actions.scoped_context(element_info::in_phrase) |
| [ |
| actions.values.save() |
| [ *( common |
| | (cl::anychar_p - phrase_end) |
| [actions.plain_char] |
| ) |
| ] |
| ] |
| ; |
| |
| extended_phrase = |
| actions.scoped_context(element_info::in_conditional) |
| [ |
| actions.values.save() |
| [ *( common |
| | (cl::anychar_p - phrase_end) |
| [actions.plain_char] |
| ) |
| ] |
| ] |
| ; |
| |
| inside_paragraph = |
| actions.scoped_context(element_info::in_nested_block) |
| [ |
| actions.values.save() |
| [ *( local.paragraph_separator [actions.paragraph] |
| | common |
| | (cl::anychar_p - phrase_end) |
| [actions.plain_char] |
| ) |
| ] [actions.paragraph] |
| ] |
| ; |
| |
| local.escape = |
| cl::str_p("\\n") [actions.break_] |
| | cl::str_p("\\ ") // ignore an escaped space |
| | '\\' >> cl::punct_p [actions.raw_char] |
| | "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")] |
| [actions.escape_unicode] |
| | "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")] |
| [actions.escape_unicode] |
| | ("'''" >> !eol) |
| >> actions.values.save() |
| [ (*(cl::anychar_p - "'''")) [actions.values.entry(ph::arg1, ph::arg2, phrase_tags::escape)] |
| >> ( cl::str_p("'''") |
| | cl::eps_p [actions.error("Unclosed boostbook escape.")] |
| ) [actions.element] |
| ] |
| ; |
| |
| // |
| // Simple phrase grammar |
| // |
| |
| simple_phrase = |
| actions.scoped_context(element_info::in_phrase) |
| [ |
| actions.values.save() |
| [ |
| *( common |
| | (cl::anychar_p - ']') [actions.plain_char] |
| ) |
| ] |
| ] |
| ; |
| |
| // |
| // Command line |
| // |
| |
| command_line = |
| actions.values.list(block_tags::macro_definition) |
| [ *cl::space_p |
| >> local.command_line_macro_identifier |
| [actions.values.entry(ph::arg1, ph::arg2)] |
| >> *cl::space_p |
| >> ( '=' |
| >> *cl::space_p |
| >> local.command_line_phrase |
| >> *cl::space_p |
| | cl::eps_p |
| ) [actions.to_value] |
| ] [actions.element] |
| ; |
| |
| local.command_line_macro_identifier = |
| +(cl::anychar_p - (cl::space_p | ']' | '=')) |
| ; |
| |
| |
| local.command_line_phrase = |
| actions.scoped_context(element_info::in_phrase) |
| [ |
| actions.values.save() |
| [ *( common |
| | (cl::anychar_p - ']') [actions.plain_char] |
| ) |
| ] |
| ] |
| ; |
| |
| // 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(actions.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 - ']')) >> ']' |
| ; |
| |
| macro_identifier = |
| +(cl::anychar_p - (cl::space_p | ']')) |
| ; |
| |
| } |
| } |