mirror of
https://github.com/boostorg/quickbook.git
synced 2026-02-02 21:12:15 +00:00
Move most of code_snippet into its own file.
[SVN r65036]
This commit is contained in:
411
src/code_snippet.cpp
Normal file
411
src/code_snippet.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/*=============================================================================
|
||||
Copyright (c) 2006 Joel de Guzman
|
||||
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)
|
||||
=============================================================================*/
|
||||
#if !defined(BOOST_SPIRIT_QUICKBOOK_CODE_SNIPPET_HPP)
|
||||
#define BOOST_SPIRIT_QUICKBOOK_CODE_SNIPPET_HPP
|
||||
|
||||
#include <boost/spirit/include/classic_core.hpp>
|
||||
#include <boost/spirit/include/classic_actor.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "template_stack.hpp"
|
||||
#include "actions.hpp"
|
||||
|
||||
namespace quickbook
|
||||
{
|
||||
using namespace boost::spirit::classic;
|
||||
|
||||
struct code_snippet_actions
|
||||
{
|
||||
code_snippet_actions(std::vector<template_symbol>& storage,
|
||||
std::string const& doc_id,
|
||||
char const* source_type)
|
||||
: callout_id(0)
|
||||
, storage(storage)
|
||||
, doc_id(doc_id)
|
||||
, source_type(source_type)
|
||||
{}
|
||||
|
||||
void pass_thru_char(char);
|
||||
void pass_thru(iterator first, iterator last);
|
||||
void escaped_comment(iterator first, iterator last);
|
||||
void start_snippet(iterator first, iterator last);
|
||||
void end_snippet(iterator first, iterator last);
|
||||
void callout(iterator first, iterator last);
|
||||
|
||||
void append_code();
|
||||
void close_code();
|
||||
|
||||
struct snippet_data
|
||||
{
|
||||
snippet_data(std::string const& id, int callout_base_id)
|
||||
: id(id)
|
||||
, callout_base_id(callout_base_id)
|
||||
, content()
|
||||
, start_code(false)
|
||||
, end_code(false)
|
||||
{}
|
||||
|
||||
std::string id;
|
||||
int callout_base_id;
|
||||
std::string content;
|
||||
bool start_code;
|
||||
bool end_code;
|
||||
std::vector<template_body> callouts;
|
||||
};
|
||||
|
||||
int callout_id;
|
||||
std::stack<snippet_data> snippet_stack;
|
||||
std::string code;
|
||||
std::string id;
|
||||
std::vector<template_symbol>& storage;
|
||||
std::string const doc_id;
|
||||
char const* const source_type;
|
||||
};
|
||||
|
||||
struct python_code_snippet_grammar
|
||||
: grammar<python_code_snippet_grammar>
|
||||
{
|
||||
typedef code_snippet_actions actions_type;
|
||||
|
||||
python_code_snippet_grammar(actions_type & actions)
|
||||
: actions(actions)
|
||||
{}
|
||||
|
||||
template <typename Scanner>
|
||||
struct definition
|
||||
{
|
||||
typedef code_snippet_actions actions_type;
|
||||
|
||||
definition(python_code_snippet_grammar const& self)
|
||||
{
|
||||
|
||||
actions_type& actions = self.actions;
|
||||
|
||||
start_ = *code_elements;
|
||||
|
||||
identifier =
|
||||
(alpha_p | '_') >> *(alnum_p | '_')
|
||||
;
|
||||
|
||||
code_elements =
|
||||
start_snippet [boost::bind(&actions_type::start_snippet, &actions, _1, _2)]
|
||||
| end_snippet [boost::bind(&actions_type::end_snippet, &actions, _1, _2)]
|
||||
| escaped_comment
|
||||
| ignore
|
||||
| anychar_p [boost::bind(&actions_type::pass_thru_char, &actions, _1)]
|
||||
;
|
||||
|
||||
start_snippet =
|
||||
"#[" >> *space_p
|
||||
>> identifier [assign_a(actions.id)]
|
||||
;
|
||||
|
||||
end_snippet =
|
||||
str_p("#]")
|
||||
;
|
||||
|
||||
ignore =
|
||||
*blank_p >> "#<-"
|
||||
>> (*(anychar_p - "#->"))
|
||||
>> "#->" >> *blank_p >> eol_p
|
||||
| "\"\"\"<-\"\"\""
|
||||
>> (*(anychar_p - "\"\"\"->\"\"\""))
|
||||
>> "\"\"\"->\"\"\""
|
||||
| "\"\"\"<-"
|
||||
>> (*(anychar_p - "->\"\"\""))
|
||||
>> "->\"\"\""
|
||||
;
|
||||
|
||||
escaped_comment =
|
||||
*space_p >> "#`"
|
||||
>> ((*(anychar_p - eol_p))
|
||||
>> eol_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
|
||||
| *space_p >> "\"\"\"`"
|
||||
>> (*(anychar_p - "\"\"\"")) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
|
||||
>> "\"\"\""
|
||||
;
|
||||
}
|
||||
|
||||
rule<Scanner>
|
||||
start_, identifier, code_elements, start_snippet, end_snippet,
|
||||
escaped_comment, ignore;
|
||||
|
||||
rule<Scanner> const&
|
||||
start() const { return start_; }
|
||||
};
|
||||
|
||||
actions_type& actions;
|
||||
};
|
||||
|
||||
struct cpp_code_snippet_grammar
|
||||
: grammar<cpp_code_snippet_grammar>
|
||||
{
|
||||
typedef code_snippet_actions actions_type;
|
||||
|
||||
cpp_code_snippet_grammar(actions_type & actions)
|
||||
: actions(actions)
|
||||
{}
|
||||
|
||||
template <typename Scanner>
|
||||
struct definition
|
||||
{
|
||||
definition(cpp_code_snippet_grammar const& self)
|
||||
{
|
||||
actions_type& actions = self.actions;
|
||||
|
||||
start_ = *code_elements;
|
||||
|
||||
identifier =
|
||||
(alpha_p | '_') >> *(alnum_p | '_')
|
||||
;
|
||||
|
||||
code_elements =
|
||||
start_snippet [boost::bind(&actions_type::start_snippet, &actions, _1, _2)]
|
||||
| end_snippet [boost::bind(&actions_type::end_snippet, &actions, _1, _2)]
|
||||
| escaped_comment
|
||||
| ignore
|
||||
| line_callout
|
||||
| inline_callout
|
||||
| anychar_p [boost::bind(&actions_type::pass_thru_char, &actions, _1)]
|
||||
;
|
||||
|
||||
start_snippet =
|
||||
"//[" >> *space_p
|
||||
>> identifier [assign_a(actions.id)]
|
||||
|
|
||||
"/*[" >> *space_p
|
||||
>> identifier [assign_a(actions.id)]
|
||||
>> *space_p >> "*/"
|
||||
;
|
||||
|
||||
end_snippet =
|
||||
str_p("//]") | "/*]*/"
|
||||
;
|
||||
|
||||
inline_callout =
|
||||
"/*<"
|
||||
>> *space_p
|
||||
>> (*(anychar_p - ">*/")) [boost::bind(&actions_type::callout, &actions, _1, _2)]
|
||||
>> ">*/"
|
||||
;
|
||||
|
||||
line_callout =
|
||||
"/*<<"
|
||||
>> *space_p
|
||||
>> (*(anychar_p - ">>*/")) [boost::bind(&actions_type::callout, &actions, _1, _2)]
|
||||
>> ">>*/"
|
||||
>> *space_p
|
||||
;
|
||||
|
||||
ignore =
|
||||
*blank_p >> "//<-"
|
||||
>> (*(anychar_p - "//->"))
|
||||
>> "//->" >> *blank_p >> eol_p
|
||||
| "/*<-*/"
|
||||
>> (*(anychar_p - "/*->*/"))
|
||||
>> "/*->*/"
|
||||
| "/*<-"
|
||||
>> (*(anychar_p - "->*/"))
|
||||
>> "->*/"
|
||||
;
|
||||
|
||||
escaped_comment =
|
||||
*space_p >> "//`"
|
||||
>> ((*(anychar_p - eol_p))
|
||||
>> eol_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
|
||||
| *space_p >> "/*`"
|
||||
>> (*(anychar_p - "*/")) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
|
||||
>> "*/"
|
||||
;
|
||||
}
|
||||
|
||||
rule<Scanner>
|
||||
start_, identifier, code_elements, start_snippet, end_snippet,
|
||||
escaped_comment, inline_callout, line_callout, ignore;
|
||||
|
||||
rule<Scanner> const&
|
||||
start() const { return start_; }
|
||||
};
|
||||
|
||||
actions_type& actions;
|
||||
};
|
||||
|
||||
int load_snippets(
|
||||
std::string const& file
|
||||
, std::vector<template_symbol>& storage // snippets are stored in a
|
||||
// vector of template_symbols
|
||||
, std::string const& extension
|
||||
, std::string const& doc_id)
|
||||
{
|
||||
std::string code;
|
||||
int err = detail::load(file, code);
|
||||
if (err != 0)
|
||||
return err; // return early on error
|
||||
|
||||
typedef position_iterator<std::string::const_iterator> iterator_type;
|
||||
iterator_type first(code.begin(), code.end(), file);
|
||||
iterator_type last(code.end(), code.end());
|
||||
|
||||
size_t fname_len = file.size();
|
||||
bool is_python = fname_len >= 3
|
||||
&& file[--fname_len]=='y' && file[--fname_len]=='p' && file[--fname_len]=='.';
|
||||
code_snippet_actions a(storage, doc_id, is_python ? "[python]" : "[c++]");
|
||||
// TODO: Should I check that parse succeeded?
|
||||
if(is_python) {
|
||||
boost::spirit::classic::parse(first, last, python_code_snippet_grammar(a));
|
||||
}
|
||||
else {
|
||||
boost::spirit::classic::parse(first, last, cpp_code_snippet_grammar(a));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void code_snippet_actions::append_code()
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
snippet_data& snippet = snippet_stack.top();
|
||||
|
||||
if (!code.empty())
|
||||
{
|
||||
detail::unindent(code); // remove all indents
|
||||
|
||||
if(snippet.content.empty())
|
||||
{
|
||||
snippet.start_code = true;
|
||||
}
|
||||
else if(!snippet.end_code)
|
||||
{
|
||||
snippet.content += "\n\n";
|
||||
snippet.content += source_type;
|
||||
snippet.content += "```\n";
|
||||
}
|
||||
|
||||
snippet.content += code;
|
||||
snippet.end_code = true;
|
||||
|
||||
code.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void code_snippet_actions::close_code()
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
snippet_data& snippet = snippet_stack.top();
|
||||
|
||||
if(snippet.end_code)
|
||||
{
|
||||
snippet.content += "```\n\n";
|
||||
snippet.end_code = false;
|
||||
}
|
||||
}
|
||||
|
||||
void code_snippet_actions::pass_thru(iterator first, iterator last)
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
code += *first;
|
||||
}
|
||||
|
||||
void code_snippet_actions::pass_thru_char(char c)
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
code += c;
|
||||
}
|
||||
|
||||
void code_snippet_actions::callout(iterator first, iterator last)
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
code += "``[[callout" + boost::lexical_cast<std::string>(callout_id) + "]]``";
|
||||
|
||||
snippet_stack.top().callouts.push_back(
|
||||
template_body(std::string(first, last), first.get_position(), true));
|
||||
++callout_id;
|
||||
}
|
||||
|
||||
void code_snippet_actions::escaped_comment(iterator first, iterator last)
|
||||
{
|
||||
if(snippet_stack.empty()) return;
|
||||
snippet_data& snippet = snippet_stack.top();
|
||||
append_code();
|
||||
close_code();
|
||||
|
||||
std::string temp(first, last);
|
||||
detail::unindent(temp); // remove all indents
|
||||
if (temp.size() != 0)
|
||||
{
|
||||
snippet.content += "\n" + temp; // add a linebreak to allow block markups
|
||||
}
|
||||
}
|
||||
|
||||
void code_snippet_actions::start_snippet(iterator first, iterator last)
|
||||
{
|
||||
append_code();
|
||||
snippet_stack.push(snippet_data(id, callout_id));
|
||||
id.clear();
|
||||
}
|
||||
|
||||
void code_snippet_actions::end_snippet(iterator first, iterator last)
|
||||
{
|
||||
// TODO: Error?
|
||||
if(snippet_stack.empty()) return;
|
||||
|
||||
append_code();
|
||||
|
||||
snippet_data snippet = snippet_stack.top();
|
||||
snippet_stack.pop();
|
||||
|
||||
std::string body;
|
||||
if(snippet.start_code) {
|
||||
body += "\n\n";
|
||||
body += source_type;
|
||||
body += "```\n";
|
||||
}
|
||||
body += snippet.content;
|
||||
if(snippet.end_code) {
|
||||
body += "```\n\n";
|
||||
}
|
||||
|
||||
std::vector<std::string> params;
|
||||
for (size_t i = 0; i < snippet.callouts.size(); ++i)
|
||||
{
|
||||
params.push_back("[callout" + boost::lexical_cast<std::string>(snippet.callout_base_id + i) + "]");
|
||||
}
|
||||
|
||||
// TODO: Save position in start_snippet
|
||||
template_symbol symbol(snippet.id, params, body, first.get_position(), true);
|
||||
symbol.callout = true;
|
||||
symbol.callouts = snippet.callouts;
|
||||
storage.push_back(symbol);
|
||||
|
||||
// Merge the snippet into its parent
|
||||
|
||||
if(!snippet_stack.empty())
|
||||
{
|
||||
snippet_data& next = snippet_stack.top();
|
||||
if(!snippet.content.empty()) {
|
||||
if(!snippet.start_code) {
|
||||
close_code();
|
||||
}
|
||||
else if(!next.end_code) {
|
||||
next.content += "\n\n";
|
||||
next.content += source_type;
|
||||
next.content += "```\n";
|
||||
}
|
||||
|
||||
next.content += snippet.content;
|
||||
next.end_code = snippet.end_code;
|
||||
}
|
||||
|
||||
next.callouts.insert(next.callouts.end(), snippet.callouts.begin(), snippet.callouts.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BOOST_SPIRIT_QUICKBOOK_CODE_SNIPPET_HPP
|
||||
|
||||
Reference in New Issue
Block a user