1 /*=============================================================================
2 Copyright (c) 2002 2004 2006 Joel de Guzman
3 Copyright (c) 2004 Eric Niebler
4 http://spirit.sourceforge.net/
6 Use, modification and distribution is subject to the Boost Software
7 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
10 #include <boost/spirit/include/classic_core.hpp>
11 #include <boost/spirit/include/classic_confix.hpp>
12 #include <boost/spirit/include/classic_chset.hpp>
13 #include <boost/spirit/include/classic_symbols.hpp>
14 #include <boost/spirit/include/classic_loops.hpp>
15 #include "grammar.hpp"
16 #include "grammar_impl.hpp" // Just for context stuff. Should move?
18 #include "actions.hpp"
21 #include "input_path.hpp"
25 namespace cl = boost::spirit::classic;
27 template <typename T, typename Value>
28 struct member_action_value
30 typedef void(T::*member_function)(Value);
35 member_action_value(T& l, member_function mf) : l(l), mf(mf) {}
37 void operator()(Value v) const {
45 typedef void(T::*member_function)(parse_iterator, parse_iterator);
50 member_action(T& l, member_function mf) : l(l), mf(mf) {}
52 void operator()(parse_iterator first, parse_iterator last) const {
57 template <typename T, typename Arg1>
60 typedef void(T::*member_function)(parse_iterator, parse_iterator, Arg1);
65 member_action1(T& l, member_function mf) : l(l), mf(mf) {}
72 impl(member_action1& a, Arg1 value) :
76 void operator()(parse_iterator first, parse_iterator last) const {
77 (a.l.*a.mf)(first, last, value);
81 impl operator()(Arg1 a1) {
82 return impl(*this, a1);
86 // Syntax Highlight Actions
88 struct syntax_highlight_actions
90 quickbook::collector out;
91 quickbook::state& state;
92 do_macro_action do_macro_impl;
95 bool support_callouts;
96 string_ref marked_text;
98 syntax_highlight_actions(quickbook::state& state, bool is_block) :
100 do_macro_impl(out, state),
101 support_callouts(is_block && (qbk_version_n >= 107u ||
102 state.current_file->is_code_snippets)),
106 void span(parse_iterator, parse_iterator, char const*);
107 void span_start(parse_iterator, parse_iterator, char const*);
108 void span_end(parse_iterator, parse_iterator);
109 void unexpected_char(parse_iterator, parse_iterator);
110 void plain_char(parse_iterator, parse_iterator);
111 void pre_escape_back(parse_iterator, parse_iterator);
112 void post_escape_back(parse_iterator, parse_iterator);
113 void do_macro(std::string const&);
115 void mark_text(parse_iterator, parse_iterator);
116 void callout(parse_iterator, parse_iterator);
119 void syntax_highlight_actions::span(parse_iterator first,
120 parse_iterator last, char const* name)
122 out << "<phrase role=\"" << name << "\">";
123 while (first != last)
124 detail::print_char(*first++, out.get());
128 void syntax_highlight_actions::span_start(parse_iterator first,
129 parse_iterator last, char const* name)
131 out << "<phrase role=\"" << name << "\">";
132 while (first != last)
133 detail::print_char(*first++, out.get());
136 void syntax_highlight_actions::span_end(parse_iterator first,
139 while (first != last)
140 detail::print_char(*first++, out.get());
144 void syntax_highlight_actions::unexpected_char(parse_iterator first,
147 file_position const pos = state.current_file->position_of(first.base());
149 detail::outwarn(state.current_file->path, pos.line)
150 << "in column:" << pos.column
151 << ", unexpected character: " << std::string(first.base(), last.base())
154 // print out an unexpected character
155 out << "<phrase role=\"error\">";
156 while (first != last)
157 detail::print_char(*first++, out.get());
161 void syntax_highlight_actions::plain_char(parse_iterator first,
164 while (first != last)
165 detail::print_char(*first++, out.get());
168 void syntax_highlight_actions::pre_escape_back(parse_iterator,
171 state.phrase.push(); // save the stream
174 void syntax_highlight_actions::post_escape_back(parse_iterator,
177 out << state.phrase.str();
178 state.phrase.pop(); // restore the stream
181 void syntax_highlight_actions::do_macro(std::string const& v)
186 void syntax_highlight_actions::mark_text(parse_iterator first,
189 marked_text = string_ref(first.base(), last.base());
192 void syntax_highlight_actions::callout(parse_iterator, parse_iterator)
194 out << state.add_callout(qbk_value(state.current_file,
195 marked_text.begin(), marked_text.end()));
201 struct keywords_holder
203 cl::symbols<> cpp, python;
208 = "and_eq", "and", "asm", "auto", "bitand", "bitor",
209 "bool", "break", "case", "catch", "char", "class",
210 "compl", "const_cast", "const", "continue", "default",
211 "delete", "do", "double", "dynamic_cast", "else",
212 "enum", "explicit", "export", "extern", "false",
213 "float", "for", "friend", "goto", "if", "inline",
214 "int", "long", "mutable", "namespace", "new", "not_eq",
215 "not", "operator", "or_eq", "or", "private",
216 "protected", "public", "register", "reinterpret_cast",
217 "return", "short", "signed", "sizeof", "static",
218 "static_cast", "struct", "switch", "template", "this",
219 "throw", "true", "try", "typedef", "typeid",
220 "typename", "union", "unsigned", "using", "virtual",
221 "void", "volatile", "wchar_t", "while", "xor_eq", "xor"
226 "and", "del", "for", "is", "raise",
227 "assert", "elif", "from", "lambda", "return",
228 "break", "else", "global", "not", "try",
229 "class", "except", "if", "or", "while",
230 "continue", "exec", "import", "pass", "yield",
231 "def", "finally", "in", "print",
233 // Technically "as" and "None" are not yet keywords (at Python
234 // 2.4). They are destined to become keywords, and we treat them
235 // as such for syntax highlighting purposes.
243 keywords_holder keywords;
246 // Grammar for C++ highlighting
247 struct cpp_highlight : public cl::grammar<cpp_highlight>
249 cpp_highlight(syntax_highlight_actions& actions)
250 : actions(actions) {}
252 template <typename Scanner>
255 definition(cpp_highlight const& self)
256 : g(self.actions.state.grammar())
258 member_action1<syntax_highlight_actions, char const*>
259 span(self.actions, &syntax_highlight_actions::span),
260 span_start(self.actions, &syntax_highlight_actions::span_start);
261 member_action<syntax_highlight_actions>
262 span_end(self.actions, &syntax_highlight_actions::span_end),
263 unexpected_char(self.actions, &syntax_highlight_actions::unexpected_char),
264 plain_char(self.actions, &syntax_highlight_actions::plain_char),
265 pre_escape_back(self.actions, &syntax_highlight_actions::pre_escape_back),
266 post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back),
267 mark_text(self.actions, &syntax_highlight_actions::mark_text),
268 callout(self.actions, &syntax_highlight_actions::callout);
269 member_action_value<syntax_highlight_actions, std::string const&>
270 do_macro(self.actions, &syntax_highlight_actions::do_macro);
271 error_action error(self.actions.state);
275 *( (+cl::space_p) [plain_char]
278 | preprocessor [span("preprocessor")]
279 | cl::eps_p(ph::var(self.actions.support_callouts))
280 >> ( line_callout [callout]
281 | inline_callout [callout]
284 | keyword [span("keyword")]
285 | identifier [span("identifier")]
286 | special [span("special")]
287 | string_ [span("string")]
288 | char_ [span("char")]
289 | number [span("number")]
290 | u8_codepoint_p [unexpected_char]
295 // must not be followed by alpha or underscore
296 cl::eps_p(self.actions.state.macro
297 >> (cl::eps_p - (cl::alpha_p | '_')))
298 >> self.actions.state.macro
303 cl::str_p("``") [pre_escape_back]
308 (+(cl::anychar_p - "``") >> cl::eps_p("``"))
322 = '#' >> *cl::space_p >> ((cl::alpha_p | '_') >> *(cl::alnum_p | '_'))
327 "/*<" >> *cl::space_p,
328 (*cl::anychar_p) [mark_text],
335 "/*<<" >> *cl::space_p,
336 (*cl::anychar_p) [mark_text],
343 = cl::str_p("//") [span_start("comment")]
345 | (+(cl::anychar_p - (cl::eol_p | "``")))
348 >> cl::eps_p [span_end]
349 | cl::str_p("/*") [span_start("comment")]
351 | (+(cl::anychar_p - (cl::str_p("*/") | "``")))
354 >> (!cl::str_p("*/")) [span_end]
358 = keywords.cpp >> (cl::eps_p - (cl::alnum_p | '_'))
359 ; // make sure we recognize whole words only
362 = +cl::chset_p("~!%^&*()+={[}]:;,<.>?/|\\-")
365 string_char = ('\\' >> u8_codepoint_p) | (cl::anychar_p - '\\');
368 = !cl::as_lower_d['l'] >> cl::confix_p('"', *string_char, '"')
372 = !cl::as_lower_d['l'] >> cl::confix_p('\'', *string_char, '\'')
377 cl::as_lower_d["0x"] >> cl::hex_p
381 >> *cl::as_lower_d[cl::chset_p("ldfu")]
385 = (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
390 program, macro, preprocessor,
391 inline_callout, line_callout, comment,
393 char_, number, identifier, keyword, escape,
396 quickbook_grammar& g;
398 cl::rule<Scanner> const&
399 start() const { return program; }
402 syntax_highlight_actions& actions;
405 // Grammar for Python highlighting
406 // See also: The Python Reference Manual
407 // http://docs.python.org/ref/ref.html
408 struct python_highlight : public cl::grammar<python_highlight>
410 python_highlight(syntax_highlight_actions& actions)
411 : actions(actions) {}
413 template <typename Scanner>
416 definition(python_highlight const& self)
417 : g(self.actions.state.grammar())
419 member_action1<syntax_highlight_actions, char const*>
420 span(self.actions, &syntax_highlight_actions::span),
421 span_start(self.actions, &syntax_highlight_actions::span_start);
422 member_action<syntax_highlight_actions>
423 span_end(self.actions, &syntax_highlight_actions::span_end),
424 unexpected_char(self.actions, &syntax_highlight_actions::unexpected_char),
425 plain_char(self.actions, &syntax_highlight_actions::plain_char),
426 pre_escape_back(self.actions, &syntax_highlight_actions::pre_escape_back),
427 post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back),
428 mark_text(self.actions, &syntax_highlight_actions::mark_text),
429 callout(self.actions, &syntax_highlight_actions::callout);
430 member_action_value<syntax_highlight_actions, std::string const&>
431 do_macro(self.actions, &syntax_highlight_actions::do_macro);
432 error_action error(self.actions.state);
436 *( (+cl::space_p) [plain_char]
440 | keyword [span("keyword")]
441 | identifier [span("identifier")]
442 | special [span("special")]
443 | string_ [span("string")]
444 | number [span("number")]
445 | u8_codepoint_p [unexpected_char]
450 // must not be followed by alpha or underscore
451 cl::eps_p(self.actions.state.macro
452 >> (cl::eps_p - (cl::alpha_p | '_')))
453 >> self.actions.state.macro
458 cl::str_p("``") [pre_escape_back]
463 (+(cl::anychar_p - "``") >> cl::eps_p("``"))
477 = cl::str_p("#") [span_start("comment")]
479 | (+(cl::anychar_p - (cl::eol_p | "``")))
482 >> cl::eps_p [span_end]
486 = keywords.python >> (cl::eps_p - (cl::alnum_p | '_'))
487 ; // make sure we recognize whole words only
490 = +cl::chset_p("~!%^&*()+={[}]:;,<.>/|\\-")
494 = cl::as_lower_d[cl::str_p("u") >> ! cl::str_p("r")]
498 = ! string_prefix >> (long_string | short_string)
501 string_char = ('\\' >> u8_codepoint_p) | (cl::anychar_p - '\\');
504 = cl::confix_p('\'', * string_char, '\'') |
505 cl::confix_p('"', * string_char, '"')
509 // Note: the "cl::str_p" on the next two lines work around
510 // an INTERNAL COMPILER ERROR when using VC7.1
511 = cl::confix_p(cl::str_p("'''"), * string_char, "'''") |
512 cl::confix_p(cl::str_p("\"\"\""), * string_char, "\"\"\"")
517 cl::as_lower_d["0x"] >> cl::hex_p
521 >> *cl::as_lower_d[cl::chset_p("lj")]
525 = (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
530 program, macro, comment, special, string_, string_prefix,
531 short_string, long_string, number, identifier, keyword,
534 quickbook_grammar& g;
536 cl::rule<Scanner> const&
537 start() const { return program; }
540 syntax_highlight_actions& actions;
543 // Grammar for plain text (no actual highlighting)
544 struct teletype_highlight : public cl::grammar<teletype_highlight>
546 teletype_highlight(syntax_highlight_actions& actions)
547 : actions(actions) {}
549 template <typename Scanner>
552 definition(teletype_highlight const& self)
553 : g(self.actions.state.grammar())
555 member_action<syntax_highlight_actions>
556 plain_char(self.actions, &syntax_highlight_actions::plain_char),
557 pre_escape_back(self.actions, &syntax_highlight_actions::pre_escape_back),
558 post_escape_back(self.actions, &syntax_highlight_actions::post_escape_back);
559 member_action_value<syntax_highlight_actions, std::string const&>
560 do_macro(self.actions, &syntax_highlight_actions::do_macro);
561 error_action error(self.actions.state);
567 | u8_codepoint_p [plain_char]
572 // must not be followed by alpha or underscore
573 cl::eps_p(self.actions.state.macro
574 >> (cl::eps_p - (cl::alpha_p | '_')))
575 >> self.actions.state.macro
580 cl::str_p("``") [pre_escape_back]
585 (+(cl::anychar_p - "``") >> cl::eps_p("``"))
599 cl::rule<Scanner> program, macro, escape;
601 quickbook_grammar& g;
603 cl::rule<Scanner> const&
604 start() const { return program; }
607 syntax_highlight_actions& actions;
610 std::string syntax_highlight(
611 parse_iterator first,
613 quickbook::state& state,
614 std::string const& source_mode,
617 syntax_highlight_actions syn_actions(state, is_block);
619 // print the code with syntax coloring
620 if (source_mode == "c++")
622 cpp_highlight cpp_p(syn_actions);
623 boost::spirit::classic::parse(first, last, cpp_p);
625 else if (source_mode == "python")
627 python_highlight python_p(syn_actions);
628 boost::spirit::classic::parse(first, last, python_p);
630 else if (source_mode == "teletype")
632 teletype_highlight teletype_p(syn_actions);
633 boost::spirit::classic::parse(first, last, teletype_p);
641 syn_actions.out.swap(str);