blob: bacb23f5ee36e353527cc8a0ce07d5a91ab0c9d4 [file] [log] [blame]
/*=============================================================================
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 <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/classic_chset.hpp>
#include <boost/spirit/include/classic_symbols.hpp>
#include <boost/spirit/include/classic_loops.hpp>
#include "grammar.hpp"
#include "grammar_impl.hpp" // Just for context stuff. Should move?
#include "actions_class.hpp"
namespace quickbook
{
namespace cl = boost::spirit::classic;
// Grammar for C++ highlighting
struct cpp_highlight
: public cl::grammar<cpp_highlight>
{
cpp_highlight(collector& out, actions& escape_actions)
: out(out), escape_actions(escape_actions) {}
template <typename Scanner>
struct definition
{
definition(cpp_highlight const& self)
: g(self.escape_actions.grammar())
{
program
=
*( (+cl::space_p) [space(self.out)]
| macro
| escape
| preprocessor [span("preprocessor", self.out)]
| comment
| keyword [span("keyword", self.out)]
| identifier [span("identifier", self.out)]
| special [span("special", self.out)]
| string_ [span("string", self.out)]
| char_ [span("char", self.out)]
| number [span("number", self.out)]
| cl::repeat_p(1)[cl::anychar_p]
[unexpected_char(self.out, self.escape_actions)]
)
;
macro =
// must not be followed by alpha or underscore
cl::eps_p(self.escape_actions.macro
>> (cl::eps_p - (cl::alpha_p | '_')))
>> self.escape_actions.macro [do_macro_action(self.out, self.escape_actions)]
;
qbk_phrase =
self.escape_actions.scoped_context(element_info::in_phrase)
[ *( g.common
| (cl::anychar_p - cl::str_p("``"))
[self.escape_actions.plain_char]
)
]
;
escape =
cl::str_p("``") [pre_escape_back(self.escape_actions, save)]
>>
(
(
(
(+(cl::anychar_p - "``") >> cl::eps_p("``"))
& qbk_phrase
)
>> cl::str_p("``")
)
|
(
cl::eps_p [self.escape_actions.error]
>> *cl::anychar_p
)
) [post_escape_back(self.out, self.escape_actions, save)]
;
preprocessor
= '#' >> *cl::space_p >> ((cl::alpha_p | '_') >> *(cl::alnum_p | '_'))
;
comment
= cl::str_p("//") [span_start("comment", self.out)]
>> *( escape
| (+(cl::anychar_p - (cl::eol_p | "``")))
[span(0, self.out)]
)
>> cl::eps_p [span_end(self.out)]
| cl::str_p("/*") [span_start("comment", self.out)]
>> *( escape
| (+(cl::anychar_p - (cl::str_p("*/") | "``")))
[span(0, self.out)]
)
>> (!cl::str_p("*/")) [span_end(self.out)]
;
keyword
= keyword_ >> (cl::eps_p - (cl::alnum_p | '_'))
; // make sure we recognize whole words only
keyword_
= "and_eq", "and", "asm", "auto", "bitand", "bitor",
"bool", "break", "case", "catch", "char", "class",
"compl", "const_cast", "const", "continue", "default",
"delete", "do", "double", "dynamic_cast", "else",
"enum", "explicit", "export", "extern", "false",
"float", "for", "friend", "goto", "if", "inline",
"int", "long", "mutable", "namespace", "new", "not_eq",
"not", "operator", "or_eq", "or", "private",
"protected", "public", "register", "reinterpret_cast",
"return", "short", "signed", "sizeof", "static",
"static_cast", "struct", "switch", "template", "this",
"throw", "true", "try", "typedef", "typeid",
"typename", "union", "unsigned", "using", "virtual",
"void", "volatile", "wchar_t", "while", "xor_eq", "xor"
;
special
= +cl::chset_p("~!%^&*()+={[}]:;,<.>?/|\\-")
;
string_char = ('\\' >> cl::anychar_p) | (cl::anychar_p - '\\');
string_
= !cl::as_lower_d['l'] >> cl::confix_p('"', *string_char, '"')
;
char_
= !cl::as_lower_d['l'] >> cl::confix_p('\'', *string_char, '\'')
;
number
= (
cl::as_lower_d["0x"] >> cl::hex_p
| '0' >> cl::oct_p
| cl::real_p
)
>> *cl::as_lower_d[cl::chset_p("ldfu")]
;
identifier
= (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
;
}
cl::rule<Scanner>
program, macro, preprocessor, comment, special, string_,
char_, number, identifier, keyword, qbk_phrase, escape,
string_char;
cl::symbols<> keyword_;
quickbook_grammar& g;
std::string save;
cl::rule<Scanner> const&
start() const { return program; }
};
collector& out;
actions& escape_actions;
};
// Grammar for Python highlighting
// See also: The Python Reference Manual
// http://docs.python.org/ref/ref.html
struct python_highlight
: public cl::grammar<python_highlight>
{
python_highlight(collector& out, actions& escape_actions)
: out(out), escape_actions(escape_actions) {}
template <typename Scanner>
struct definition
{
definition(python_highlight const& self)
: g(self.escape_actions.grammar())
{
program
=
*( (+cl::space_p) [space(self.out)]
| macro
| escape
| comment
| keyword [span("keyword", self.out)]
| identifier [span("identifier", self.out)]
| special [span("special", self.out)]
| string_ [span("string", self.out)]
| number [span("number", self.out)]
| cl::repeat_p(1)[cl::anychar_p]
[unexpected_char(self.out, self.escape_actions)]
)
;
macro =
// must not be followed by alpha or underscore
cl::eps_p(self.escape_actions.macro
>> (cl::eps_p - (cl::alpha_p | '_')))
>> self.escape_actions.macro [do_macro_action(self.out, self.escape_actions)]
;
qbk_phrase =
self.escape_actions.scoped_context(element_info::in_phrase)
[
*( g.common
| (cl::anychar_p - cl::str_p("``"))
[self.escape_actions.plain_char]
)
]
;
escape =
cl::str_p("``") [pre_escape_back(self.escape_actions, save)]
>>
(
(
(
(+(cl::anychar_p - "``") >> cl::eps_p("``"))
& qbk_phrase
)
>> cl::str_p("``")
)
|
(
cl::eps_p [self.escape_actions.error]
>> *cl::anychar_p
)
) [post_escape_back(self.out, self.escape_actions, save)]
;
comment
= cl::str_p("#") [span_start("comment", self.out)]
>> *( escape
| (+(cl::anychar_p - (cl::eol_p | "``")))
[span(0, self.out)]
)
>> cl::eps_p [span_end(self.out)]
;
keyword
= keyword_ >> (cl::eps_p - (cl::alnum_p | '_'))
; // make sure we recognize whole words only
keyword_
=
"and", "del", "for", "is", "raise",
"assert", "elif", "from", "lambda", "return",
"break", "else", "global", "not", "try",
"class", "except", "if", "or", "while",
"continue", "exec", "import", "pass", "yield",
"def", "finally", "in", "print",
// Technically "as" and "None" are not yet keywords (at Python
// 2.4). They are destined to become keywords, and we treat them
// as such for syntax highlighting purposes.
"as", "None"
;
special
= +cl::chset_p("~!%^&*()+={[}]:;,<.>/|\\-")
;
string_prefix
= cl::as_lower_d[cl::str_p("u") >> ! cl::str_p("r")]
;
string_
= ! string_prefix >> (long_string | short_string)
;
string_char = ('\\' >> cl::anychar_p) | (cl::anychar_p - '\\');
short_string
= cl::confix_p('\'', * string_char, '\'') |
cl::confix_p('"', * string_char, '"')
;
long_string
// Note: the "cl::str_p" on the next two lines work around
// an INTERNAL COMPILER ERROR when using VC7.1
= cl::confix_p(cl::str_p("'''"), * string_char, "'''") |
cl::confix_p(cl::str_p("\"\"\""), * string_char, "\"\"\"")
;
number
= (
cl::as_lower_d["0x"] >> cl::hex_p
| '0' >> cl::oct_p
| cl::real_p
)
>> *cl::as_lower_d[cl::chset_p("lj")]
;
identifier
= (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
;
}
cl::rule<Scanner>
program, macro, comment, special, string_, string_prefix,
short_string, long_string, number, identifier, keyword,
qbk_phrase, escape, string_char;
cl::symbols<> keyword_;
quickbook_grammar& g;
std::string save;
cl::rule<Scanner> const&
start() const { return program; }
};
collector& out;
actions& escape_actions;
};
// Grammar for plain text (no actual highlighting)
struct teletype_highlight
: public cl::grammar<teletype_highlight>
{
teletype_highlight(collector& out, actions& escape_actions)
: out(out), escape_actions(escape_actions) {}
template <typename Scanner>
struct definition
{
definition(teletype_highlight const& self)
: g(self.escape_actions.grammar())
{
program
=
*( macro
| escape
| cl::repeat_p(1)[cl::anychar_p] [plain_char_action(self.out, self.escape_actions)]
)
;
macro =
// must not be followed by alpha or underscore
cl::eps_p(self.escape_actions.macro
>> (cl::eps_p - (cl::alpha_p | '_')))
>> self.escape_actions.macro [do_macro_action(self.out, self.escape_actions)]
;
qbk_phrase =
self.escape_actions.scoped_context(element_info::in_phrase)
[
*( g.common
| (cl::anychar_p - cl::str_p("``"))
[self.escape_actions.plain_char]
)
]
;
escape =
cl::str_p("``") [pre_escape_back(self.escape_actions, save)]
>>
(
(
(
(+(cl::anychar_p - "``") >> cl::eps_p("``"))
& qbk_phrase
)
>> cl::str_p("``")
)
|
(
cl::eps_p [self.escape_actions.error]
>> *cl::anychar_p
)
) [post_escape_back(self.out, self.escape_actions, save)]
;
}
cl::rule<Scanner> program, macro, qbk_phrase, escape;
quickbook_grammar& g;
std::string save;
cl::rule<Scanner> const&
start() const { return program; }
};
collector& out;
actions& escape_actions;
};
std::string syntax_highlight(
iterator first, iterator last,
actions& escape_actions,
std::string const& source_mode)
{
quickbook::collector temp;
// print the code with syntax coloring
if (source_mode == "c++")
{
cpp_highlight cpp_p(temp, escape_actions);
boost::spirit::classic::parse(first, last, cpp_p);
}
else if (source_mode == "python")
{
python_highlight python_p(temp, escape_actions);
boost::spirit::classic::parse(first, last, python_p);
}
else if (source_mode == "teletype")
{
teletype_highlight teletype_p(temp, escape_actions);
boost::spirit::classic::parse(first, last, teletype_p);
}
else
{
BOOST_ASSERT(0);
}
std::string str;
temp.swap(str);
return str;
}
}