blob: 01091ce5171a4c31de28b6d499152af936e9396d [file] [log] [blame]
/*=============================================================================
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)
=============================================================================*/
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_actor.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include "template_stack.hpp"
#include "actions.hpp"
namespace quickbook
{
namespace cl = 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
: cl::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 =
(cl::alpha_p | '_') >> *(cl::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
| cl::anychar_p [boost::bind(&actions_type::pass_thru_char, &actions, _1)]
;
start_snippet =
"#[" >> *cl::space_p
>> identifier [cl::assign_a(actions.id)]
;
end_snippet =
cl::str_p("#]")
;
ignore
= cl::confix_p(
*cl::blank_p >> "#<-",
*cl::anychar_p,
"#->" >> *cl::blank_p >> cl::eol_p
)
| cl::confix_p(
"\"\"\"<-\"\"\"",
*cl::anychar_p,
"\"\"\"->\"\"\""
)
| cl::confix_p(
"\"\"\"<-",
*cl::anychar_p,
"->\"\"\""
)
;
escaped_comment =
cl::confix_p(
*cl::space_p >> "#`",
(*cl::anychar_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)],
cl::eol_p
)
| cl::confix_p(
*cl::space_p >> "\"\"\"`",
(*cl::anychar_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)],
"\"\"\""
)
;
}
cl::rule<Scanner>
start_, identifier, code_elements, start_snippet, end_snippet,
escaped_comment, ignore;
cl::rule<Scanner> const&
start() const { return start_; }
};
actions_type& actions;
};
struct cpp_code_snippet_grammar
: cl::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 =
(cl::alpha_p | '_') >> *(cl::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
| cl::anychar_p [boost::bind(&actions_type::pass_thru_char, &actions, _1)]
;
start_snippet =
"//[" >> *cl::space_p
>> identifier [cl::assign_a(actions.id)]
|
"/*[" >> *cl::space_p
>> identifier [cl::assign_a(actions.id)]
>> *cl::space_p >> "*/"
;
end_snippet =
cl::str_p("//]") | "/*]*/"
;
inline_callout
= cl::confix_p(
"/*<" >> *cl::space_p,
(*cl::anychar_p) [boost::bind(&actions_type::callout, &actions, _1, _2)],
">*/"
)
;
line_callout
= cl::confix_p(
"/*<<" >> *cl::space_p,
(*cl::anychar_p) [boost::bind(&actions_type::callout, &actions, _1, _2)],
">>*/"
)
>> *cl::space_p
;
ignore
= cl::confix_p(
*cl::blank_p >> "//<-",
*cl::anychar_p,
"//->"
)
>> *cl::blank_p
>> cl::eol_p
| cl::confix_p(
"/*<-*/",
*cl::anychar_p,
"/*->*/"
)
| cl::confix_p(
"/*<-",
*cl::anychar_p,
"->*/"
)
;
escaped_comment
= cl::confix_p(
*cl::space_p >> "//`",
(*cl::anychar_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)],
cl::eol_p
)
| cl::confix_p(
*cl::space_p >> "/*`",
(*cl::anychar_p) [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)],
"*/"
)
;
}
cl::rule<Scanner>
start_, identifier, code_elements, start_snippet, end_snippet,
escaped_comment, inline_callout, line_callout, ignore;
cl::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
iterator first(code.begin(), code.end(), file.c_str());
iterator 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());
}
}
}