/*============================================================================= 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 #include #include #include #include #include #include #include #include #include #include "code.hpp" #include "phrase.hpp" #include "grammars.hpp" #include "actions.hpp" #include "template.hpp" #include "parse_utils.hpp" BOOST_FUSION_ADAPT_STRUCT( quickbook::anchor, (std::string, id) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::link, (quickbook::formatted_type, type) (std::string, destination) (std::string, content) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::simple_markup, (char, symbol) (std::string, raw_content) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::break_, (quickbook::file_position, position) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::image, (quickbook::file_position, position) (std::string, image_filename) (quickbook::image::attribute_map, attributes) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::cond_phrase, (std::string, macro_id) (std::string, content) ) BOOST_FUSION_ADAPT_STRUCT( quickbook::call_template, (quickbook::file_position, position) (bool, escape) (quickbook::template_symbol const*, symbol) (std::vector, args) ) namespace quickbook { namespace qi = boost::spirit::qi; namespace ph = boost::phoenix; struct phrase_grammar::rules { rules(quickbook::actions& actions, bool& no_eols); quickbook::actions& actions; bool& no_eols; qi::rule phrase; qi::rule common; qi::rule macro; qi::rule phrase_markup; qi::rule code_block; qi::rule inline_code; qi::rule > simple_format; qi::rule escape; qi::rule escape_break; qi::rule escape_punct; qi::rule escape_markup; qi::rule comment; qi::rule dummy_block; qi::rule cond_phrase; qi::rule macro_identifier; qi::rule image, image_1_4, image_1_5; qi::rule image_filename; qi::rule image_attributes; qi::rule()> image_attribute; qi::rule image_attribute_key, image_attribute_value; qi::rule url; qi::rule link; qi::symbols link_symbol; qi::rule anchor; qi::symbols source_mode; qi::rule formatted; qi::symbols format_symbol; qi::rule footnote; qi::rule call_template; qi::rule() > template_args; qi::rule template_arg_1_4; qi::rule brackets_1_4; qi::rule template_arg_1_5; qi::rule template_inner_arg_1_5; qi::rule brackets_1_5; qi::rule break_; qi::rule space, blank, eol, phrase_end, hard_space; qi::rule position; }; phrase_grammar::phrase_grammar(quickbook::actions& actions, bool& no_eols) : phrase_grammar::base_type(start, "phrase") , rules_pimpl(new rules(actions, no_eols)) { start = rules_pimpl->common; } phrase_grammar::~phrase_grammar() {} phrase_grammar::rules::rules(quickbook::actions& actions, bool& no_eols) : actions(actions), no_eols(no_eols) { phrase = qi::eps [actions.phrase_push] >> *( common | comment | (qi::char_ - phrase_end) [actions.process] ) >> qi::eps [actions.phrase_pop] ; common = macro | phrase_markup | code_block [actions.process] | inline_code [actions.process] | simple_format [actions.process] | escape | comment ; macro = ( actions.macro // must not be followed by >> !(qi::alpha | '_') // alpha or underscore ) [actions.process] ; phrase_markup = ( '[' >> ( cond_phrase | image | url | link | anchor | source_mode | formatted | footnote | call_template | break_ ) >> ']' ) [actions.process] ; code_block = ( "```" >> position >> qi::raw[*(qi::char_ - "```")] >> "```" >> qi::attr(true) ) | ( "``" >> position >> qi::raw[*(qi::char_ - "``")] >> "``" >> qi::attr(true) ) ; inline_code = '`' >> position >> qi::raw [ *( qi::char_ - ( '`' | (eol >> eol) // Make sure that we don't go ) // past a single block ) >> &qi::lit('`') ] >> '`' >> qi::attr(false) ; simple_format %= qi::char_("*/_=") [qi::_a = qi::_1] >> qi::raw [ ( ( qi::graph // A single char. e.g. *c* >> &( qi::char_(qi::_a) >> (qi::space | qi::punct | qi::eoi) ) ) | ( qi::graph // qi::graph must follow qi::lit(qi::_r1) >> *( qi::char_ - ( (qi::graph >> qi::lit(qi::_a)) | phrase_end // Make sure that we don't go ) // past a single block ) >> qi::graph // qi::graph must precede qi::lit(qi::_r1) >> &( qi::char_(qi::_a) >> (qi::space | qi::punct | qi::eoi) ) ) ) ] >> qi::omit[qi::char_(qi::_a)] ; escape = ( escape_break | "\\ " // ignore an escaped char | escape_punct | escape_markup ) [actions.process] ; escape_break = position >> "\\n" >> qi::attr(nothing()) ; escape_punct = qi::attr(formatted_type("")) >> '\\' >> qi::repeat(1)[qi::punct] ; escape_markup = ("'''" >> -eol) >> qi::attr("escape") >> *(qi::char_ - "'''") >> "'''" ; comment = "[/" >> *(dummy_block | (qi::char_ - ']')) >> ']' ; dummy_block = '[' >> *(dummy_block | (qi::char_ - ']')) >> ']' ; cond_phrase = '?' >> blank >> macro_identifier >> -phrase ; macro_identifier = +(qi::char_ - (qi::space | ']')) ; image = (qi::eps(qbk_since(105u)) >> image_1_5) | (qi::eps(qbk_before(105u)) >> image_1_4); image_1_4 = position >> '$' >> blank >> *(qi::char_ - phrase_end) >> &qi::lit(']') ; image_1_5 = position >> '$' >> blank >> image_filename >> hard_space >> image_attributes >> &qi::lit(']') ; image_filename = qi::raw[ +(qi::char_ - (qi::space | phrase_end | '[')) >> *( +qi::space >> +(qi::char_ - (qi::space | phrase_end | '[')) )]; image_attributes = *(image_attribute >> space); image_attribute = '[' >> image_attribute_key >> space >> image_attribute_value >> ']' ; image_attribute_key = *(qi::alnum | '_'); image_attribute_value = *(qi::char_ - (phrase_end | '[')); url = '@' >> qi::attr("url") >> *(qi::char_ - (']' | qi::space)) >> ( &qi::lit(']') | (hard_space >> phrase) ) ; link = link_symbol >> hard_space >> *(qi::char_ - (']' | qi::space)) >> ( &qi::lit(']') | (hard_space >> phrase) ) ; link_symbol.add ("link", formatted_type("link")) ("funcref", formatted_type("funcref")) ("classref", formatted_type("classref")) ("memberref", formatted_type("memberref")) ("enumref", formatted_type("enumref")) ("macroref", formatted_type("macroref")) ("headerref", formatted_type("headerref")) ("conceptref", formatted_type("conceptref")) ("globalref", formatted_type("globalref")) ; anchor = '#' >> blank >> *(qi::char_ - phrase_end) >> qi::attr(nothing()) ; source_mode.add ("c++", quickbook::source_mode("c++")) ("python", quickbook::source_mode("python")) ("teletype", quickbook::source_mode("teletype")) ; formatted = format_symbol >> blank >> phrase; format_symbol.add ("*", "bold") ("'", "italic") ("_", "underline") ("^", "teletype") ("-", "strikethrough") ("\"", "quote") ("~", "replaceable") ; footnote = "footnote" >> qi::attr("footnote") >> blank >> phrase ; // Template call call_template = position >> ( '`' >> qi::attr(true) | qi::attr(false) ) >> ( // Lookup the template name (&qi::punct >> actions.templates.scope) | (actions.templates.scope >> hard_space) ) >> template_args >> &qi::lit(']') ; template_args = qi::eps(qbk_before(105u)) >> -(template_arg_1_4 % "..") | qi::eps(qbk_since(105u)) >> -(template_arg_1_5 % ".."); template_arg_1_4 = qi::raw[+(brackets_1_4 | (qi::char_ - (qi::lit("..") | ']')))] ; brackets_1_4 = '[' >> +template_arg_1_4 >> ']' ; template_arg_1_5 = qi::raw[+(brackets_1_5 | ('\\' >> qi::char_) | (qi::char_ - (qi::lit("..") | '[' | ']')))] ; template_inner_arg_1_5 = +(brackets_1_5 | ('\\' >> qi::char_) | (qi::char_ - (qi::lit('[') | ']'))) ; brackets_1_5 = '[' >> +template_inner_arg_1_5 >> ']' ; break_ = position >> "br" >> qi::attr(nothing()) ; space = *(qi::space | comment) ; blank = *(qi::blank | comment) ; eol = blank >> qi::eol ; phrase_end = ']' | qi::eps(ph::ref(no_eols)) >> eol >> eol // Make sure that we don't go ; // past a single block, except // when preformatted. hard_space = !(qi::alnum | '_') >> space ; // must not be preceded by // alpha-numeric or underscore position = qi::raw[qi::eps] [get_position]; } struct simple_phrase_grammar::rules { rules(quickbook::actions& actions); quickbook::actions& actions; bool unused; phrase_grammar common; qi::rule phrase, comment, dummy_block; }; simple_phrase_grammar::simple_phrase_grammar(quickbook::actions& actions) : simple_phrase_grammar::base_type(start, "simple_phrase") , rules_pimpl(new rules(actions)) , start(rules_pimpl->phrase) {} simple_phrase_grammar::~simple_phrase_grammar() {} simple_phrase_grammar::rules::rules(quickbook::actions& actions) : actions(actions), unused(false), common(actions, unused) { phrase = *( common | comment | (qi::char_ - ']') [actions.process] ) ; comment = "[/" >> *(dummy_block | (qi::char_ - ']')) >> ']' ; dummy_block = '[' >> *(dummy_block | (qi::char_ - ']')) >> ']' ; } }