diff --git a/Jamfile.v2 b/Jamfile.v2 index 0df2fa9..64a0ab0 100644 --- a/Jamfile.v2 +++ b/Jamfile.v2 @@ -33,3 +33,13 @@ install dist-lib : release ; + +# Target for quickbook toolset's auto build. + +alias quickbook-auto + : quickbook + : release + ; + +explicit quickbook-auto + ; diff --git a/src/Jamfile.v2 b/src/Jamfile.v2 index 736c19f..7b6460c 100644 --- a/src/Jamfile.v2 +++ b/src/Jamfile.v2 @@ -28,6 +28,7 @@ exe quickbook utils.cpp input_path.cpp values.cpp + id_generator.cpp post_process.cpp collector.cpp template_stack.cpp diff --git a/src/actions.cpp b/src/actions.cpp index 5023d8c..bea5cb7 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -29,6 +29,7 @@ #include "input_path.hpp" #include "block_tags.hpp" #include "phrase_tags.hpp" +#include "id_generator.hpp" namespace quickbook { @@ -58,12 +59,23 @@ namespace quickbook it != end; ++it) { tgt << ""; } actions.anchors.clear(); - } + } + + std::string add_anchor(quickbook::actions& actions, + std::string const& id, + id_generator::categories category = id_generator::explicit_id) + { + std::string placeholder = actions.ids.add(id, category); + actions.anchors.push_back(placeholder); + return placeholder; + } } void list_action(quickbook::actions&, value); @@ -261,8 +273,7 @@ namespace quickbook value_consumer values = phrase; actions.phrase << "(actions.footnote_id_count++) + << actions.ids.add(actions.doc_id + ".f", id_generator::numbered) << "\">" << values.consume().get_boostbook() << ""; @@ -297,27 +308,31 @@ namespace quickbook } namespace { - void write_bridgehead(collector& out, int level, + void write_bridgehead(quickbook::actions& actions, int level, std::string const& str, std::string const& id, bool self_link) { if (self_link && !id.empty()) { - out << ""; - out << ""; - out << ""; - out << str; - out << ""; - out << ""; + actions.out << ""; + actions.out << ""; + actions.out << ""; + actions.out << str; + actions.out << ""; + actions.out << ""; } else { - out << ""; - out << str; - out << ""; + actions.out << ""; + actions.out << str; + actions.out << ""; } } } @@ -347,20 +362,26 @@ namespace quickbook level = heading_list.get_tag() - block_tags::heading1 + 1; } - std::string anchor; write_anchors(actions, actions.out); if (!generic && qbk_version_n < 103) // version 1.2 and below { - anchor = actions.section_id + '.' + - detail::make_identifier(content.get_boostbook()); + std::string anchor = actions.ids.add( + actions.section_id + '.' + + detail::make_identifier(content.get_boostbook()), + id_generator::generated); - write_bridgehead(actions.out, level, + write_bridgehead(actions, level, content.get_boostbook(), anchor, false); } else { + id_generator::categories category = + !element_id.empty() ? + id_generator::explicit_id : + id_generator::generated; + std::string id = !element_id.empty() ? element_id.get_quickbook() : @@ -370,13 +391,14 @@ namespace quickbook content.get_boostbook() ); - anchor = fully_qualified_id(actions.doc_id, - actions.qualified_section_id, id); + std::string anchor = actions.ids.add( + fully_qualified_id(actions.doc_id, + actions.qualified_section_id, id), + category);; - write_bridgehead(actions.out, level, + write_bridgehead(actions, level, content.get_boostbook(), anchor, true); } - } void simple_phrase_action::operator()(char mark) const @@ -588,7 +610,7 @@ namespace quickbook void anchor_action(quickbook::actions& actions, value anchor) { value_consumer values = anchor; - actions.anchors.push_back(values.consume().get_quickbook()); + add_anchor(actions, values.consume().get_quickbook()); values.finish(); } @@ -1101,8 +1123,7 @@ namespace quickbook while (arg != args.end()) { if (!actions.templates.add( - template_symbol(*tpl, empty_params, arg->content, - arg->filename, &scope))) + template_symbol(*tpl, empty_params, *arg, &scope))) { detail::outerr(actions.filename, pos.line) << "Duplicate Symbol Found" << std::endl; @@ -1127,7 +1148,8 @@ namespace quickbook // // Note: this is now done in the grammar. - if (escape) + // TODO: For escape, should this be surrounded in escape comments? + if (body.type == template_body::raw_output || escape) { // escape the body of the template // we just copy out the literal body @@ -1247,24 +1269,31 @@ namespace quickbook file_position pos) { assert(symbol->body.is_block()); - + + std::vector callout_ids; std::vector args; unsigned int size = symbol->params.size(); for(unsigned int i = 0; i < size; ++i) { - std::string callout_id = actions.doc_id + - boost::lexical_cast(actions.callout_id_count + i); + std::string callout_id1 = + actions.ids.add( + actions.doc_id + ".c", + id_generator::numbered); + std::string callout_id2 = + actions.ids.add( + actions.doc_id + ".c", + id_generator::numbered); std::string code; - code += "'''"; - code += ""; - code += "'''"; + code += ""; args.push_back(template_body( qbk_value(code, pos, template_tags::phrase), - actions.filename)); + actions.filename, template_body::raw_output)); + callout_ids.push_back(callout_id1); + callout_ids.push_back(callout_id2); } call_template(actions, template_escape, symbol, args, pos); @@ -1277,10 +1306,11 @@ namespace quickbook ++actions.template_depth; block += ""; + int i = 0; BOOST_FOREACH(value c, symbol->callouts) { - std::string callout_id = actions.doc_id + - boost::lexical_cast(actions.callout_id_count++); + std::string callout_id1 = callout_ids[i++]; + std::string callout_id2 = callout_ids[i++]; std::string callout_value; { @@ -1304,8 +1334,8 @@ namespace quickbook actions.out.swap(callout_value); } - block += ""; + block += ""; block += callout_value; block += ""; } @@ -1326,6 +1356,7 @@ namespace quickbook std::string identifier = values.consume(template_tags::identifier).get_quickbook(); + std::vector callout_ids; std::vector args; BOOST_FOREACH(value arg, values) @@ -1444,15 +1475,26 @@ namespace quickbook std::string table_id; if(qbk_version_n >= 105) { if(!element_id.empty()) { - table_id = fully_qualified_id(actions.doc_id, - actions.qualified_section_id, element_id); + table_id = actions.ids.add( + fully_qualified_id(actions.doc_id, + actions.qualified_section_id, element_id), + id_generator::explicit_id); } else if(has_title) { - table_id = fully_qualified_id(actions.doc_id, - actions.qualified_section_id, - detail::make_identifier(title)); + table_id = actions.ids.add( + fully_qualified_id(actions.doc_id, + actions.qualified_section_id, + detail::make_identifier(title)), + id_generator::generated); } } + else if (has_title) + { + table_id = actions.ids.add( + fully_qualified_id(actions.doc_id, + actions.qualified_section_id, "t"), + id_generator::numbered); + } // Emulating the old behaviour which used the width of the final // row for span_count. @@ -1540,17 +1582,17 @@ namespace quickbook actions.qualified_section_id += actions.section_id; ++actions.section_level; - if (qbk_version_n < 103) // version 1.2 and below - { - actions.out << "\n
\n"; - } - else // version 1.3 and above - { - actions.out << "\n
\n"; - } + // TODO: This could be awkward if there's a clash, possibly + // needs another category, between explicit and generated. + std::string full_id = actions.ids.add( + qbk_version_n < 103 ? + actions.doc_id + "." + actions.section_id : + actions.doc_id + "." + actions.qualified_section_id, + !element_id.empty() ? + id_generator::explicit_id : + id_generator::generated); + actions.out << "\n
\n"; actions.out << ""; write_anchors(actions, actions.out); @@ -1561,8 +1603,7 @@ namespace quickbook } else // version 1.3 and above { - actions.out << "<link linkend=\"" << actions.doc_id - << "." << actions.qualified_section_id << "\">" + actions.out << "<link linkend=\"" << full_id << "\">" << content.get_boostbook() << "</link>" ; diff --git a/src/actions_class.cpp b/src/actions_class.cpp index 002f6e7..420955c 100644 --- a/src/actions_class.cpp +++ b/src/actions_class.cpp @@ -19,7 +19,8 @@ namespace quickbook { - actions::actions(fs::path const& filein_, fs::path const& xinclude_base_, string_stream& out_) + actions::actions(fs::path const& filein_, fs::path const& xinclude_base_, + string_stream& out_, id_generator& ids) : grammar_() , doc_title_qbk() @@ -32,8 +33,7 @@ namespace quickbook , warned_about_breaks(false) , context(0) , conditional(true) - , callout_id_count(0) - , footnote_id_count(0) + , ids(ids) , imported(false) , doc_type() diff --git a/src/actions_class.hpp b/src/actions_class.hpp index ce58cd9..f71652d 100644 --- a/src/actions_class.hpp +++ b/src/actions_class.hpp @@ -23,7 +23,8 @@ namespace quickbook struct actions { - actions(fs::path const& filein_, fs::path const& xinclude_base, string_stream& out_); + actions(fs::path const& filein_, fs::path const& xinclude_base, string_stream& out_, + id_generator&); private: boost::scoped_ptr<quickbook_grammar> grammar_; @@ -47,8 +48,7 @@ namespace quickbook bool warned_about_breaks; int context; bool conditional; - int callout_id_count; - int footnote_id_count; + id_generator& ids; // state saved for files and templates. bool imported; diff --git a/src/block_element_grammar.cpp b/src/block_element_grammar.cpp index 6732979..e246fad 100644 --- a/src/block_element_grammar.cpp +++ b/src/block_element_grammar.cpp @@ -34,7 +34,8 @@ namespace quickbook xinclude, include, template_, template_id, template_formal_arg, template_body, identifier, import, - element_id, element_id_1_5, element_id_1_6; + element_id, element_id_1_5, element_id_1_6, + same_line; }; void quickbook_grammar::impl::init_block_elements() @@ -44,7 +45,7 @@ namespace quickbook local.element_id = !( ':' - >> ( cl::if_p(qbk_since(105u)) [space] + >> ( !(cl::eps_p(qbk_since(105u)) >> space) >> (+(cl::alnum_p | '_')) [actions.values.entry(ph::arg1, ph::arg2, general_tags::element_id)] | cl::eps_p [actions.element_id_warning] ) @@ -52,16 +53,10 @@ namespace quickbook ; local.element_id_1_5 = - cl::if_p(qbk_since(105u)) [ - local.element_id - ] - ; + !(cl::eps_p(qbk_since(105u)) >> local.element_id); local.element_id_1_6 = - cl::if_p(qbk_since(106u)) [ - local.element_id - ] - ; + !(cl::eps_p(qbk_since(106u)) >> local.element_id); elements.add ("section", element_info(element_info::block, &local.begin_section, block_tags::begin_section)) @@ -208,10 +203,12 @@ namespace quickbook ("table", element_info(element_info::nested_block, &local.table, block_tags::table)) ; + local.same_line = (cl::eps_p(*cl::blank_p >> cl::eol_p) | space); + local.table = - (cl::eps_p(*cl::blank_p >> cl::eol_p) | space) + local.same_line >> local.element_id_1_5 - >> (cl::eps_p(*cl::blank_p >> cl::eol_p) | space) + >> local.same_line >> (*(cl::anychar_p - eol)) [actions.values.entry(ph::arg1, ph::arg2, table_tags::title)] >> (+eol) >> *local.table_row diff --git a/src/doc_info_actions.cpp b/src/doc_info_actions.cpp index cd4d2ea..edee209 100644 --- a/src/doc_info_actions.cpp +++ b/src/doc_info_actions.cpp @@ -17,6 +17,7 @@ #include "input_path.hpp" #include "actions_class.hpp" #include "doc_info_tags.hpp" +#include "id_generator.hpp" namespace quickbook { @@ -280,7 +281,7 @@ namespace quickbook out << '<' << actions.doc_type << "\n" << " id=\"" - << actions.doc_id + << actions.ids.add(actions.doc_id, id_generator::explicit_id) << "\"\n" ; @@ -374,7 +375,10 @@ namespace quickbook if (!license.empty()) { - tmp << " <legalnotice id=\"legal." << actions.doc_id << "\">\n" + tmp << " <legalnotice id=\"" + << actions.ids.add(actions.doc_id + ".legal", + id_generator::generated) + << "\">\n" << " <para>\n" << " " << doc_info_output(license, 103) << "\n" << " </para>\n" diff --git a/src/fwd.hpp b/src/fwd.hpp index 60d246d..1b63611 100644 --- a/src/fwd.hpp +++ b/src/fwd.hpp @@ -18,6 +18,7 @@ namespace quickbook struct actions; struct quickbook_grammar; struct collector; + struct id_generator; typedef position_iterator<std::string::const_iterator> iterator; diff --git a/src/id_generator.cpp b/src/id_generator.cpp new file mode 100644 index 0000000..571853e --- /dev/null +++ b/src/id_generator.cpp @@ -0,0 +1,349 @@ +/*============================================================================= + Copyright (c) 2011 Daniel James + + 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 "id_generator.hpp" +#include "markups.hpp" +#include "phrase_tags.hpp" +#include <boost/lexical_cast.hpp> +#include <algorithm> +#include <vector> + +namespace quickbook +{ + // string_ref + + class string_ref + { + public: + typedef std::string::const_iterator iterator; + + private: + iterator begin_, end_; + + public: + string_ref() : begin_(), end_() {} + + explicit string_ref(iterator b, iterator e) + : begin_(b), end_(e) {} + + explicit string_ref(std::string const& x) + : begin_(x.begin()), end_(x.end()) {} + + iterator begin() const { return begin_; } + iterator end() const { return end_; } + + std::size_t size() const + { + return static_cast<std::size_t>(end_ - begin_); + } + }; + + bool operator==(string_ref const& x, string_ref const& y); + bool operator<(string_ref const& x, string_ref const& y); + + inline bool operator==(string_ref const& x, std::string const& y) + { + return x == string_ref(y); + } + + inline bool operator==(std::string const& x, string_ref const& y) + { + return string_ref(x) == y; + } + + inline bool operator<(string_ref const& x, std::string const& y) + { + return x < string_ref(y); + } + + inline bool operator<(std::string const& x, string_ref const& y) + { + return string_ref(x) < y; + } + + bool operator==(string_ref const& x, string_ref const& y) + { + return x.size() == y.size() && + std::equal(x.begin(), x.end(), y.begin()); + } + + bool operator<(string_ref const& x, string_ref const& y) + { + return std::lexicographical_compare( + x.begin(), x.end(), y.begin(), y.end()); + } + + // id_generator + + struct id_generator::id + { + id() + : category(id_generator::default_category), + used(false), + count(0) {} + + id_generator::categories category; + + // These are updated when generating ids + bool used; + int count; + }; + + struct id_generator::placeholder + { + typedef std::pair<std::string const, id_generator::id> id_pair; + + placeholder(id_generator::categories category, id_pair& id) + : category(category), + id(id), + final_id() {} + + id_generator::categories category; + id_pair& id; + std::string final_id; // Set in the second pass. + }; + + id_generator::id_generator() + { + } + + id_generator::~id_generator() + { + } + + std::string id_generator::add( + std::string const& value, + id_generator::categories category) + { + std::string result; + id_generator::id& id = ids_[value]; + + // Doesn't check if explicit ids collide, could probably be a warning. + if (category == explicit_id) + { + id.category = category; + id.used = true; + result = value; + } + else + { + if (category < id.category) id.category = category; + + // '$' can't appear in quickbook ids, so use it indicate a + // placeholder id. + result = "$" + + boost::lexical_cast<std::string>(placeholders_.size()); + placeholders_.push_back( + id_generator::placeholder(category, *ids_.find(value))); + } + + return result; + } + + string_ref id_generator::get(string_ref value) + { + // If this isn't a placeholder id. + if (value.size() <= 1 || *value.begin() != '$') + return value; + + id_generator::placeholder& placeholder = placeholders_.at( + boost::lexical_cast<int>(std::string( + value.begin() + 1, value.end()))); + + if (placeholder.final_id.empty()) + { + if (placeholder.category < id_generator::numbered && + !placeholder.id.second.used && + placeholder.id.second.category == placeholder.category) + { + placeholder.id.second.used = true; + placeholder.final_id = placeholder.id.first; + } + else while(true) + { + int count = placeholder.id.second.count++; + placeholder.final_id = placeholder.id.first + + boost::lexical_cast<std::string>(count); + // TODO: Should add final_id to ids_, there are some + // edges cases where it could collide. + if (ids_.find(placeholder.final_id) == ids_.end()) + break; + } + } + + return string_ref(placeholder.final_id); + } + + // Very simple xml subset parser which replaces id values. + // + // I originally tried to integrate this into the post processor + // but that proved tricky. Alternatively it could use a proper + // xml parser, but I want this to be able to survive badly + // marked up escapes. + + struct xml_processor + { + xml_processor(); + + std::string escape_prefix; + std::string escape_postfix; + std::string processing_instruction_postfix; + std::string comment_postfix; + std::string tag_end; + std::string name_end; + std::vector<std::string> id_attributes; + + std::string replace(std::string const&, id_generator&); + }; + + std::string id_generator::replace_placeholders(std::string const& source) + { + xml_processor processor; + return processor.replace(source, *this); + } + + namespace + { + char const* id_attributes_[] = + { + "id", + "linkend", + "linkends", + "arearefs" + }; + } + + xml_processor::xml_processor() + : escape_prefix("!--quickbook-escape-prefix-->") + , escape_postfix("!--quickbook-escape-postfix-->") + , processing_instruction_postfix("?>") + , comment_postfix("-->") + , tag_end(" \t\n\r>") + , name_end("= \t\n\r>") + { + static int const n_id_attributes = sizeof(id_attributes_)/sizeof(char const*); + for (int i = 0; i != n_id_attributes; ++i) + { + id_attributes.push_back(id_attributes_[i]); + } + + std::sort(id_attributes.begin(), id_attributes.end()); + } + + std::string xml_processor::replace(std::string const& source, id_generator& ids) + { + std::string result; + + typedef std::string::const_iterator iterator; + iterator pos = source.begin(), end = source.end(); + iterator next = pos; + + while (true) { + next = std::find(next, end, '<'); + if (next == end) break; + + if (end - pos > escape_prefix.size() && std::equal( + escape_prefix.begin(), escape_prefix.end(), pos)) + { + next = std::search(next + escape_prefix.size(), end, + escape_postfix.begin(), escape_postfix.end()); + + if (next == end) break; + + next += escape_postfix.size(); + continue; + } + + ++next; + if (next == end) break; + + switch(*next) + { + case '?': + next = std::search(next, end, + processing_instruction_postfix.begin(), + processing_instruction_postfix.end()); + + if (next != end) next += processing_instruction_postfix.size(); + break; + case '!': + if (end - next > 3 && next[1] == '-' && next[2] == '-') + { + next = std::search(next + 3, end, + comment_postfix.begin(), comment_postfix.end()); + + if (next != end) next += comment_postfix.size(); + } + else + { + next = std::find(next + 1, end, '>'); + if (next != end) ++next; + } + break; + default: + if (*next >= 'a' || *next <= 'z' || + *next >= 'A' || *next <= 'Z' || + *next == '_' || *next == ':') + { + next = std::find_first_of( + next + 1, end, tag_end.begin(), tag_end.end()); + + while (true) { + while(next != end && (*next == ' ' || *next == '\t')) + ++next; + + iterator name_start = next; + + next = std::find_first_of( + next, end, name_end.begin(), name_end.end()); + + if (next == end || *next == '>') break; + + string_ref name(name_start, next); + ++next; + + while (next != end && + std::find(name_end.begin(), name_end.end(), *next) + != name_end.end()) + ++next; + + if (next == end || (*next != '"' && *next != '\'')) break; + + char delim = *next; + ++next; + + iterator value_start = next; + + next = std::find(next, end, delim); + string_ref value(value_start, next); + if (next == end) break; + ++next; + + if (std::find(id_attributes.begin(), + id_attributes.end(), name) + != id_attributes.end()) + { + result.append(pos, value.begin()); + string_ref x = ids.get(value); + result.append(x.begin(), x.end()); + pos = value.end(); + } + } + } + else + { + next = std::find(next + 1, end, '>'); + if (next != end) ++next; + } + } + } + + result.append(pos, source.end()); + return result; + } +} diff --git a/src/id_generator.hpp b/src/id_generator.hpp new file mode 100644 index 0000000..9995b0c --- /dev/null +++ b/src/id_generator.hpp @@ -0,0 +1,53 @@ +/*============================================================================= + Copyright (c) 2011 Daniel James + + 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) +=============================================================================*/ + +#if !defined(BOOST_QUICKBOOK_ID_GENERATOR_HPP) +#define BOOST_QUICKBOOK_ID_GENERATOR_HPP + +#include "fwd.hpp" +#include <boost/unordered/unordered_map.hpp> +#include <deque> +#include <string> + +namespace quickbook +{ + class string_ref; + + class id_generator + { + public: + + enum categories + { + explicit_id = 0, // Explicitly given by user + generated, // Generated from source, e.g. table title + numbered, // Just used to avoid random docbook ids + default_category + }; + + private: + + struct id; + struct placeholder; + typedef boost::unordered_map<std::string, id> placeholder_map; + + placeholder_map ids_; + std::deque<placeholder> placeholders_; + + public: + id_generator(); + ~id_generator(); + + std::string add(std::string const& id, categories priority); + + std::string replace_placeholders(std::string const&); + string_ref get(string_ref placeholder); + }; +} + +#endif \ No newline at end of file diff --git a/src/main_grammar.cpp b/src/main_grammar.cpp index 56776ea..d3a210a 100644 --- a/src/main_grammar.cpp +++ b/src/main_grammar.cpp @@ -145,9 +145,7 @@ namespace quickbook actions.scoped_context(element_info::in_block) [ local.blocks >> *( local.element - >> cl::if_p(local.is_block) - [ !(+eol >> local.blocks) - ] + >> !(cl::eps_p(local.is_block) >> +eol >> local.blocks) | local.paragraph_separator >> local.blocks | common | cl::space_p [actions.space_char] @@ -282,11 +280,10 @@ namespace quickbook ; local.template_args = - cl::if_p(qbk_since(105u)) [ - local.template_args_1_5 - ].else_p [ - local.template_args_1_4 - ] + cl::eps_p(qbk_since(105u)) + >> local.template_args_1_5 + | cl::eps_p(qbk_before(105u)) + >> local.template_args_1_4 ; local.template_args_1_4 = local.template_arg_1_4 >> *(".." >> local.template_arg_1_4); diff --git a/src/phrase_element_grammar.cpp b/src/phrase_element_grammar.cpp index ab5de65..b2e4821 100644 --- a/src/phrase_element_grammar.cpp +++ b/src/phrase_element_grammar.cpp @@ -51,28 +51,28 @@ namespace quickbook ; local.image = - blank - >> cl::if_p(qbk_since(105u)) [ - (+( - *cl::space_p - >> +(cl::anychar_p - (cl::space_p | phrase_end | '[')) - )) [actions.values.entry(ph::arg1, ph::arg2)] - >> hard_space - >> *actions.values.list() - [ '[' - >> (*(cl::alnum_p | '_')) - [actions.values.entry(ph::arg1, ph::arg2)] - >> space - >> (*(cl::anychar_p - (phrase_end | '['))) - [actions.values.entry(ph::arg1, ph::arg2)] - >> ']' - >> space - ] - ].else_p [ - (*(cl::anychar_p - phrase_end)) - [actions.values.entry(ph::arg1, ph::arg2)] + cl::eps_p(qbk_since(105u)) + >> blank + >> (+( + *cl::space_p + >> +(cl::anychar_p - (cl::space_p | phrase_end | '[')) + )) [actions.values.entry(ph::arg1, ph::arg2)] + >> hard_space + >> *actions.values.list() + [ '[' + >> (*(cl::alnum_p | '_')) + [actions.values.entry(ph::arg1, ph::arg2)] + >> space + >> (*(cl::anychar_p - (phrase_end | '['))) + [actions.values.entry(ph::arg1, ph::arg2)] + >> ']' + >> space ] >> cl::eps_p(']') + | cl::eps_p(qbk_before(105u)) + >> blank + >> (*(cl::anychar_p - phrase_end)) [actions.values.entry(ph::arg1, ph::arg2)] + >> cl::eps_p(']') ; elements.add diff --git a/src/quickbook.cpp b/src/quickbook.cpp index f8d4fa5..56f74d4 100644 --- a/src/quickbook.cpp +++ b/src/quickbook.cpp @@ -13,6 +13,7 @@ #include "post_process.hpp" #include "utils.hpp" #include "input_path.hpp" +#include "id_generator.hpp" #include <boost/program_options.hpp> #include <boost/filesystem/v3/path.hpp> #include <boost/filesystem/v3/operations.hpp> @@ -120,12 +121,8 @@ namespace quickbook static int parse_document( fs::path const& filein_, - fs::path const& xinclude_base, - string_stream& out) - { - actions actor(filein_, xinclude_base, out); - - set_macros(actor); + actions& actor) + { try { parse_file(filein_, actor); @@ -158,9 +155,14 @@ namespace quickbook , int linewidth , bool pretty_print) { - int result = 0; string_stream buffer; - result = parse_document(filein_, xinclude_base_, buffer); + id_generator ids; + actions actor(filein_, xinclude_base_, buffer, ids); + set_macros(actor); + + int result = parse_document(filein_, actor); + + std::string stage2 = ids.replace_placeholders(buffer.str()); if (result == 0) { @@ -170,7 +172,7 @@ namespace quickbook { try { - fileout << post_process(buffer.str(), indent, linewidth); + fileout << post_process(stage2, indent, linewidth); } catch (quickbook::post_process_failure&) { @@ -178,13 +180,13 @@ namespace quickbook ::quickbook::detail::outerr() << "Post Processing Failed." << std::endl; - fileout << buffer.str(); + fileout << stage2; return 1; } } else { - fileout << buffer.str(); + fileout << stage2; } } return result; diff --git a/src/template_stack.cpp b/src/template_stack.cpp index a2059c3..e6d916a 100644 --- a/src/template_stack.cpp +++ b/src/template_stack.cpp @@ -18,10 +18,12 @@ namespace quickbook { template_body::template_body( value const& content, - fs::path const& filename + fs::path const& filename, + content_type type ) : content(content) , filename(filename) + , type(type) { assert(content.get_tag() == template_tags::block || content.get_tag() == template_tags::phrase); @@ -32,7 +34,6 @@ namespace quickbook return content.get_tag() == template_tags::block; } - template_stack::template_stack() : scope(template_stack::parser(*this)) , scopes() diff --git a/src/template_stack.hpp b/src/template_stack.hpp index c053c71..eedee15 100644 --- a/src/template_stack.hpp +++ b/src/template_stack.hpp @@ -28,11 +28,18 @@ namespace quickbook struct template_body { - template_body(value const&, fs::path const&); + enum content_type + { + quickbook, + raw_output + }; + + template_body(value const&, fs::path const&, content_type = quickbook); bool is_block() const; stored_value content; fs::path filename; + content_type type; }; struct template_scope; @@ -40,6 +47,17 @@ namespace quickbook struct template_symbol { template_symbol( + std::string const& identifier, + std::vector<std::string> const& params, + template_body const& body, + template_scope const* parent = 0) + : identifier(identifier) + , params(params) + , body(body) + , parent(parent) + , callouts() {} + + template_symbol( std::string const& identifier, std::vector<std::string> const& params, value const& content, diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b719e9d..9c92582 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -52,6 +52,7 @@ test-suite quickbook.test : [ quickbook-test identifier_1_6 ] [ quickbook-test para-test ] [ quickbook-test variablelist ] + [ quickbook-test table_1_3 ] [ quickbook-test table_1_5 ] [ quickbook-test image_1_5 ] [ quickbook-test list_test ] diff --git a/test/anchor.gold b/test/anchor.gold index f95738a..ab7453c 100644 --- a/test/anchor.gold +++ b/test/anchor.gold @@ -9,24 +9,24 @@ <anchor id="a1"/>A paragraph containing several anchors. <anchor id="a2"/>We want to make sure they appear in the correct place. <anchor id="a3"/> </para> - <bridgehead renderas="sect3" id="anchor_test.anchors.this_heading_shouldn_t_pick_up_the_previous_anchor-heading"> + <bridgehead renderas="sect3" id="anchor_test.anchors.h0"> <phrase id="anchor_test.anchors.this_heading_shouldn_t_pick_up_the_previous_anchor"/><link linkend="anchor_test.anchors.this_heading_shouldn_t_pick_up_the_previous_anchor">This heading shouldn't pick up the previous anchor</link> </bridgehead> <anchor id="a4"/> - <bridgehead renderas="sect3" id="anchor_test.anchors.this_heading_should_pick_up_the_previous_anchor-heading"> + <bridgehead renderas="sect3" id="anchor_test.anchors.h1"> <phrase id="anchor_test.anchors.this_heading_should_pick_up_the_previous_anchor"/><link linkend="anchor_test.anchors.this_heading_should_pick_up_the_previous_anchor">This heading should pick up the previous anchor</link> </bridgehead> <anchor id="a5"/> - <bridgehead renderas="sect3" id="anchor_test.anchors.and_this_one-heading"> + <bridgehead renderas="sect3" id="anchor_test.anchors.h2"> <phrase id="anchor_test.anchors.and_this_one"/><link linkend="anchor_test.anchors.and_this_one">And this one</link> </bridgehead> <anchor id="a6"/> - <bridgehead renderas="sect3" id="anchor_test.anchors.also_this_one-heading"> + <bridgehead renderas="sect3" id="anchor_test.anchors.h3"> <phrase id="anchor_test.anchors.also_this_one"/><link linkend="anchor_test.anchors.also_this_one">Also this one</link> </bridgehead> diff --git a/test/blocks.gold b/test/blocks.gold index 2306736..2d7370b 100644 --- a/test/blocks.gold +++ b/test/blocks.gold @@ -3,7 +3,7 @@ <article id="various_blocks" last-revision="DEBUG MODE Date: 2000/12/20 12:00:00 $" xmlns:xi="http://www.w3.org/2001/XInclude"> <title>Various blocks - + Blockquotes @@ -25,7 +25,7 @@ Blockquote second paragraph. - + Admonitions @@ -61,7 +61,7 @@ Warning second paragraph. - + Blurb @@ -69,7 +69,7 @@ Blurb - + Inline blocks diff --git a/test/callouts.gold b/test/callouts.gold index 403c7e0..f816ed0 100644 --- a/test/callouts.gold +++ b/test/callouts.gold @@ -12,13 +12,13 @@ int roll_die() { - boost::uniform_int<> dist(1, 6); + boost::uniform_int<> dist(1, 6); } - + create a uniform_int distribution @@ -30,13 +30,13 @@ int roll_die() { - boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); } - + test @@ -50,13 +50,13 @@ int roll_die() { - boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); } - + test @@ -70,13 +70,13 @@ int roll_die() { - boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); } - + test @@ -90,24 +90,24 @@ int roll_die() { - boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); -boost::uniform_int<> dist(1, 6); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > die(gen, dist); +boost::uniform_int<> dist(1, 6); } - + callout 1 - + callout 2 - + create a uniform_int distribution @@ -115,16 +115,16 @@ -boost::uniform_int<> dist(1, 6); +boost::uniform_int<> dist(1, 6); - + callout 2 - + create a uniform_int distribution diff --git a/test/code-block.gold b/test/code-block.gold index 7c65577..15b9214 100644 --- a/test/code-block.gold +++ b/test/code-block.gold @@ -26,7 +26,7 @@ Code block line 2. Paragraph. - + Code blocks separated by comment diff --git a/test/doc-info/duplicates-1.1.gold b/test/doc-info/duplicates-1.1.gold index d784f75..7ff1fa4 100644 --- a/test/doc-info/duplicates-1.1.gold +++ b/test/doc-info/duplicates-1.1.gold @@ -17,7 +17,7 @@ 1963 Jane Doe - + Public Domain diff --git a/test/doc-info/duplicates-1.5.gold b/test/doc-info/duplicates-1.5.gold index 206598a..71a58a8 100644 --- a/test/doc-info/duplicates-1.5.gold +++ b/test/doc-info/duplicates-1.5.gold @@ -17,7 +17,7 @@ 1963 Jane Doe - + Public Domain diff --git a/test/doc-info/source-mode-1.4.gold b/test/doc-info/source-mode-1.4.gold index 9145e73..c105559 100644 --- a/test/doc-info/source-mode-1.4.gold +++ b/test/doc-info/source-mode-1.4.gold @@ -3,7 +3,7 @@
C++ test - + def foo(x): diff --git a/test/doc-info/source-mode-1.5.gold b/test/doc-info/source-mode-1.5.gold index 5615471..e10aee2 100644 --- a/test/doc-info/source-mode-1.5.gold +++ b/test/doc-info/source-mode-1.5.gold @@ -3,7 +3,7 @@
C++ test - + def foo(x): diff --git a/test/doc-info/source-mode-1.6.gold b/test/doc-info/source-mode-1.6.gold index b85b911..6c6cb20 100644 --- a/test/doc-info/source-mode-1.6.gold +++ b/test/doc-info/source-mode-1.6.gold @@ -3,7 +3,7 @@
C++ test - + def foo(x): diff --git a/test/heading-1.1.gold b/test/heading-1.1.gold index 17d9e2e..283d574 100644 --- a/test/heading-1.1.gold +++ b/test/heading-1.1.gold @@ -3,7 +3,7 @@
Heading Test 1.1 - + Generic header @@ -51,7 +51,7 @@ H3
- + H4
@@ -60,7 +60,7 @@ H5
- + H6
diff --git a/test/heading-1.5.gold b/test/heading-1.5.gold index f8eeef5..e41fe5a 100644 --- a/test/heading-1.5.gold +++ b/test/heading-1.5.gold @@ -3,76 +3,76 @@
Heading Test 1.5 - + Generic header - + Level 1 - + Level 2 - + Level 3 - + Level 4 - + Level 5 - + Level 6 - + Bold - + Comment - + :notanid - + :also not an id - + H1
<link linkend="heading_test_1_5.s1">S1</link> - + H2
<link linkend="heading_test_1_5.s1.s2">S2</link> - + H3
- + H4
<link linkend="heading_test_1_5.s1.s3">S3</link> - + H5
- + H6
- + H7
diff --git a/test/heading-1.6.gold b/test/heading-1.6.gold index 0154eb3..217da62 100644 --- a/test/heading-1.6.gold +++ b/test/heading-1.6.gold @@ -3,99 +3,99 @@
Heading Test 1.6 - + Generic header - + Level 1 - + Level 2 - + Level 3 - + Level 4 - + Level 5 - + Level 6 - + Bold - + Comment - + H1
<link linkend="heading_test_1_6.s1">S1</link> - + H2
<link linkend="heading_test_1_6.s1.s2">S2</link> - + H3
- + H4
<link linkend="heading_test_1_6.s1.s3">S3</link> - + H5
- + H6
- + H7 - + H1
<link linkend="heading_test_1_6.s1">S1</link> - + H2
<link linkend="heading_test_1_6.s1.s2">S2</link> - + H3
- + H4
<link linkend="heading_test_1_6.s1.s3">S3</link> - + H5
- + H6
- + H7
diff --git a/test/identifier_1_5.gold b/test/identifier_1_5.gold index 36dcfba..0255101 100644 --- a/test/identifier_1_5.gold +++ b/test/identifier_1_5.gold @@ -3,7 +3,7 @@
Identifiers in quickbook 1.5 - + Test heading with code diff --git a/test/identifier_1_6.gold b/test/identifier_1_6.gold index b2f18a7..070e914 100644 --- a/test/identifier_1_6.gold +++ b/test/identifier_1_6.gold @@ -3,7 +3,7 @@
Identifiers in quickbook 1.6 - + Test heading with code diff --git a/test/include/code-include.gold b/test/include/code-include.gold index d12dd39..07f37bc 100644 --- a/test/include/code-include.gold +++ b/test/include/code-include.gold @@ -141,28 +141,28 @@ // return 'em, foo man! return "foo"; } -std::string foo_bar() +std::string foo_bar() { - return "foo-bar"; + return "foo-bar"; } class x { public: - x() : n(0) + x() : n(0) { } - ~x() + ~x() { } - int get() const + int get() const { return n; } - void set(int n_) + void set(int n_) { n = n_; } @@ -170,33 +170,33 @@ - + The Mythical FooBar. See Foobar for details - + return 'em, foo-bar man! - + Constructor - + Destructor - + Get the n member variable - + Set the n member variable diff --git a/test/include/doc-title1-1.5.gold b/test/include/doc-title1-1.5.gold index 61813ee..f2fb532 100644 --- a/test/include/doc-title1-1.5.gold +++ b/test/include/doc-title1-1.5.gold @@ -3,25 +3,25 @@
Doc Title 1 - + a1 - + inc1 - + a2 - + inc2 - + a3 - + inc3 - + a4
diff --git a/test/include/doc-title1a-1.5.gold b/test/include/doc-title1a-1.5.gold index 30852b9..0ee226e 100644 --- a/test/include/doc-title1a-1.5.gold +++ b/test/include/doc-title1a-1.5.gold @@ -2,25 +2,25 @@
Doc Title 1a - + a1 - + inc1 - + a2 - + inc2 - + a3 - + inc3 - + a4
diff --git a/test/include/include-id-1.5.gold b/test/include/include-id-1.5.gold index 6246b52..4944d08 100644 --- a/test/include/include-id-1.5.gold +++ b/test/include/include-id-1.5.gold @@ -3,24 +3,24 @@
Include Id Test - + Simple include - + Simple include - + Title, no id - + Title, no id - + Title with id - - Title + + Title with id
diff --git a/test/include/include-id-1.6.gold b/test/include/include-id-1.6.gold index 2ae30ad..1f4cb88 100644 --- a/test/include/include-id-1.6.gold +++ b/test/include/include-id-1.6.gold @@ -3,33 +3,33 @@
Include Id Test - + Simple include - + Simple include
Include without id - + Title, no id
Include without id - + Title, no id
Include with id - + Title with id
Include with id - + Title with id
diff --git a/test/quickbook-manual.gold b/test/quickbook-manual.gold index 086219b..38a638e 100644 --- a/test/quickbook-manual.gold +++ b/test/quickbook-manual.gold @@ -15,7 +15,7 @@ 2002 2004 2006 Joel de Guzman, Eric Niebler - + Distributed under 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) @@ -103,7 +103,7 @@
<link linkend="quickbook.change_log">Change Log</link> - + Version 1.3 @@ -459,7 +459,7 @@ the true business precept.] - +
More Formatting Samples @@ -779,7 +779,7 @@ C++ comment `// looks like this` whereas a Python comment [python] A C++ comment // looks like this whereas a Python comment #looks like this. -
+
Supported Source Modes @@ -1706,27 +1706,27 @@ escape (no processing/formatting) [h5 Heading 5] [h6 Heading 6] - + Heading 1 - + Heading 2 - + Heading 3 - + Heading 4 - + Heading 5 - + Heading 6 @@ -1873,7 +1873,7 @@ sf_logo Quickbook has some predefined macros that you can already use. -
+
Predefined Macros @@ -1971,7 +1971,7 @@ Hi, my name is [name]. I am [age] years old. I am a [what]. ] - + Template Identifier @@ -1993,7 +1993,7 @@ Hi, my name is [name]. I am [age] years old. I am a [what]. - + Formal Template Arguments @@ -2013,7 +2013,7 @@ Hi, my name is [name]. I am [age] years old. I am a [what]. and what are actually templates that exist in the duration of the template call. - + Template Body @@ -2039,7 +2039,7 @@ replacement text... Phrase templates are typically expanded as part of phrases. Like macros, block level elements are not allowed in phrase templates. - + Template Expansion @@ -2082,7 +2082,7 @@ replacement text... are separated by the double dot ".." and terminated by the close parenthesis. - + Nullary Templates @@ -2174,7 +2174,7 @@ for the journey to old age.]]] You have a couple of ways to do it. I personally prefer the explicit empty brackets, though. - + Simple Arguments @@ -2248,7 +2248,7 @@ for the journey to old age.]]] what do you think man? - + Punctuation Templates @@ -2327,7 +2327,7 @@ for the journey to old age.]]] will generate: -
+
A Simple Table @@ -2436,7 +2436,7 @@ for the journey to old age.]]] and thus: -
+
Table with fat cells @@ -2510,7 +2510,7 @@ for the journey to old age.]]] ] ] -
+
Table with code @@ -2652,7 +2652,7 @@ for the journey to old age.]]] QuickBook's import facility provides a nice solution. - + Example @@ -2729,7 +2729,7 @@ for the journey to old age.]]] Some trailing text here - + Code Snippet Markup @@ -2750,7 +2750,7 @@ for the journey to old age.]]] The comment //] ends a code-snippet This too will not be visible in quickbook. - + Special Comments @@ -2772,7 +2772,7 @@ for the journey to old age.]]] slash-slash, tick and white-space shall be ignored. In the second, the initial slash-star-tick and the final star-slash shall be ignored. - + Callouts @@ -2789,20 +2789,20 @@ for the journey to old age.]]] -std::string foo_bar() +std::string foo_bar() { - return "foo-bar"; + return "foo-bar"; } - + The Mythical FooBar. See Foobar for details - + return 'em, foo-bar man! @@ -3139,7 +3139,7 @@ comment.box.end.props=]
<link linkend="quickbook.faq">Frequently Asked Questions</link> - + Can I use QuickBook for non-Boost documentation? @@ -3184,7 +3184,7 @@ boostbook standalone [cpp] -
+
Syntax Compendium diff --git a/test/table_1_3.gold b/test/table_1_3.gold new file mode 100644 index 0000000..e922bb6 --- /dev/null +++ b/test/table_1_3.gold @@ -0,0 +1,235 @@ + + +
+ Table 1.3 +
+ Table 2 + + + + + + Heading + + + + + + + + + cell + + + + + +
+ + + + + + + Heading + + + + + + + + + cell + + + + + + +
+ <link linkend="table_1_3.section1">Section 1</link> + + A & B + + + + + + A + + + + + B + + + + + + + + + a + + + + + b + + + + + +
+ + Empty Table + + + + +
+ + Table with an empty cell + + + + + + x + + + + + +
+ + Indentation + + + + + + Header 1. Paragraph 1 + + + Header 1. Paragraph 2 + + + + + Header 2 + + + + + + + + + Row 1. Cell 1. + + + + + Row 1. Cell 2. + + + Row 1. Cell 2. Paragraph 2. + + + + + +
+ + Nested Tables + + + + + + Header 1 + + + + + Header 2 + + + + + + + +
+ Inner Table + + + + + + 1.1 + + + + + 1.2 + + + + + + + + + 2.1 + + + + + 2.2 + + + + + +
+ + + + + + Something. + + + + + + + + + Table with anchors + + + + + + a + + + + + + + + + b + + + + + +
+
+ diff --git a/test/table_1_3.quickbook b/test/table_1_3.quickbook new file mode 100644 index 0000000..04b431f --- /dev/null +++ b/test/table_1_3.quickbook @@ -0,0 +1,81 @@ +[article Table 1.3 + [quickbook 1.3] + [id table_1_3] +] + +[table Table 2 + [[Heading]] + [[cell]] +] + +[table + [[Heading]] + [[cell]] +] + +[section:section1 Section 1] + +[table A & B + [[A][B]] + [[a][b]] +] + +[table Empty Table +] + +[table Table with an empty cell +[[x]]] + +[table Indentation + [ + [ + Header 1. Paragraph 1 + + Header 1. Paragraph 2 + ] + [ + Header 2 + ] + ] + [ + [ + Row 1. Cell 1. + ] + [ + Row 1. Cell 2. + + Row 1. Cell 2. Paragraph 2. + ] + ] +] + +[table Nested Tables + [ + [ + Header 1 + ] + [ + Header 2 + ] + ] + [ + [ + [table Inner Table + [[1.1][1.2]] + [[2.1][2.2]] + ] + ] + ] + [ + [ + Something. + ] + ] +] + +[#id1] +[table Table with anchors +[[[#id2]a[#id3]]][[b]] +] + +[endsect] diff --git a/test/template-section.gold b/test/template-section.gold index 6c5492e..af270ac 100644 --- a/test/template-section.gold +++ b/test/template-section.gold @@ -11,7 +11,7 @@ Hello. - + Just to test id generation diff --git a/test/unicode-escape.gold b/test/unicode-escape.gold index 7b1e594..c2a8859 100644 --- a/test/unicode-escape.gold +++ b/test/unicode-escape.gold @@ -2,7 +2,7 @@
UTF-8 test - + Iñtërnâtiônàlizætiøn diff --git a/test/unit/Jamfile.v2 b/test/unit/Jamfile.v2 index 19b4d1a..271652d 100644 --- a/test/unit/Jamfile.v2 +++ b/test/unit/Jamfile.v2 @@ -13,7 +13,7 @@ project quickbook-unit-tests ; run values_test.cpp ../../src/values.cpp ; -run post_process_test.cpp ../../src/post_process.cpp ; +run post_process_test.cpp ../../src/post_process.cpp ../../src/id_generator.cpp ; run iterator_tests.cpp ../../src/values.cpp ; # Copied from spirit diff --git a/test/utf-8-bom.gold b/test/utf-8-bom.gold index afb4235..088b314 100644 --- a/test/utf-8-bom.gold +++ b/test/utf-8-bom.gold @@ -2,7 +2,7 @@
UTF-8 test - + Iñtërnâtiônàlizætiøn diff --git a/test/utf-8.gold b/test/utf-8.gold index afb4235..088b314 100644 --- a/test/utf-8.gold +++ b/test/utf-8.gold @@ -2,7 +2,7 @@
UTF-8 test - + Iñtërnâtiônàlizætiøn diff --git a/test/xml-escape_1_2.gold b/test/xml-escape_1_2.gold index a90764b..bd28ae4 100644 --- a/test/xml-escape_1_2.gold +++ b/test/xml-escape_1_2.gold @@ -4,7 +4,7 @@ dirname="test_that__amp____lt__are_being_escaped_" last-revision="DEBUG MODE Date: 2000/12/20 12:00:00 $" xmlns:xi="http://www.w3.org/2001/XInclude"> - + & should be &amp;, < should &lt; diff --git a/test/xml-escape_1_5.gold b/test/xml-escape_1_5.gold index 181f6e2..f8f33ed 100644 --- a/test/xml-escape_1_5.gold +++ b/test/xml-escape_1_5.gold @@ -4,7 +4,7 @@ dirname="test_that__amp____lt__are_being_escaped_" last-revision="DEBUG MODE Date: 2000/12/20 12:00:00 $" xmlns:xi="http://www.w3.org/2001/XInclude"> - + & should be &amp;, < should &lt;