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"
17 #include "actions.hpp"
18 #include "syntax_highlight.hpp"
22 #include "phrase_tags.hpp"
26 namespace cl
= boost::spirit::classic
;
28 // Syntax Highlight Actions
30 struct syntax_highlight_actions
32 quickbook::state
& state
;
33 do_macro_action do_macro_impl
;
36 bool support_callouts
;
37 quickbook::string_view marked_text
;
39 syntax_highlight_actions(quickbook::state
& state_
, bool is_block_
) :
41 do_macro_impl(state_
),
42 support_callouts(is_block_
&& (qbk_version_n
>= 107u ||
43 state
.current_file
->is_code_snippets
)),
47 void span(parse_iterator
, parse_iterator
, char const*);
48 void span_start(parse_iterator
, parse_iterator
, char const*);
49 void span_end(parse_iterator
, parse_iterator
);
50 void unexpected_char(parse_iterator
, parse_iterator
);
51 void plain_char(parse_iterator
, parse_iterator
);
52 void pre_escape_back(parse_iterator
, parse_iterator
);
53 void post_escape_back(parse_iterator
, parse_iterator
);
54 void do_macro(std::string
const&);
56 void mark_text(parse_iterator
, parse_iterator
);
57 void callout(parse_iterator
, parse_iterator
);
60 void syntax_highlight_actions::span(parse_iterator first
,
61 parse_iterator last
, char const* name
)
63 state
.phrase
<< "<phrase role=\"" << name
<< "\">";
65 detail::print_char(*first
++, state
.phrase
.get());
66 state
.phrase
<< "</phrase>";
69 void syntax_highlight_actions::span_start(parse_iterator first
,
70 parse_iterator last
, char const* name
)
72 state
.phrase
<< "<phrase role=\"" << name
<< "\">";
74 detail::print_char(*first
++, state
.phrase
.get());
77 void syntax_highlight_actions::span_end(parse_iterator first
,
81 detail::print_char(*first
++, state
.phrase
.get());
82 state
.phrase
<< "</phrase>";
85 void syntax_highlight_actions::unexpected_char(parse_iterator first
,
88 file_position
const pos
= state
.current_file
->position_of(first
.base());
90 detail::outwarn(state
.current_file
->path
, pos
.line
)
91 << "in column:" << pos
.column
92 << ", unexpected character: " << std::string(first
.base(), last
.base())
95 // print out an unexpected character
96 state
.phrase
<< "<phrase role=\"error\">";
98 detail::print_char(*first
++, state
.phrase
.get());
99 state
.phrase
<< "</phrase>";
102 void syntax_highlight_actions::plain_char(parse_iterator first
,
105 while (first
!= last
)
106 detail::print_char(*first
++, state
.phrase
.get());
109 void syntax_highlight_actions::pre_escape_back(parse_iterator
,
112 state
.push_output(); // save the stream
115 void syntax_highlight_actions::post_escape_back(parse_iterator
,
119 state
.phrase
.swap(tmp
);
120 state
.pop_output(); // restore the stream
124 void syntax_highlight_actions::do_macro(std::string
const& v
)
129 void syntax_highlight_actions::mark_text(parse_iterator first
,
132 marked_text
= quickbook::string_view(first
.base(), last
.base() - first
.base());
135 void syntax_highlight_actions::callout(parse_iterator
, parse_iterator
)
137 state
.phrase
<< state
.add_callout(qbk_value(state
.current_file
,
138 marked_text
.begin(), marked_text
.end()));
144 struct keywords_holder
146 cl::symbols
<> cpp
, python
;
151 = "alignas", "alignof", "and_eq", "and", "asm", "auto",
152 "bitand", "bitor", "bool", "break", "case", "catch",
153 "char", "char16_t", "char32_t", "class", "compl",
154 "const", "const_cast", "constexpr", "continue",
155 "decltype", "default", "delete", "do", "double",
156 "dynamic_cast", "else", "enum", "explicit", "export",
157 "extern", "false", "float", "for", "friend", "goto",
158 "if", "inline", "int", "long", "mutable", "namespace",
159 "new", "noexcept", "not_eq", "not", "nullptr",
160 "operator", "or_eq", "or", "private", "protected",
161 "public", "register", "reinterpret_cast", "return",
162 "short", "signed", "sizeof", "static", "static_assert",
163 "static_cast", "struct", "switch", "template", "this",
164 "thread_local", "throw", "true", "try", "typedef",
165 "typeid", "typename", "union", "unsigned", "using",
166 "virtual", "void", "volatile", "wchar_t", "while",
172 "and", "del", "for", "is", "raise",
173 "assert", "elif", "from", "lambda", "return",
174 "break", "else", "global", "not", "try",
175 "class", "except", "if", "or", "while",
176 "continue", "exec", "import", "pass", "yield",
177 "def", "finally", "in", "print",
179 // Technically "as" and "None" are not yet keywords (at Python
180 // 2.4). They are destined to become keywords, and we treat them
181 // as such for syntax highlighting purposes.
189 keywords_holder keywords
;
192 // Grammar for C++ highlighting
193 struct cpp_highlight
: public cl::grammar
<cpp_highlight
>
195 explicit cpp_highlight(syntax_highlight_actions
& actions_
)
196 : actions(actions_
) {}
198 template <typename Scanner
>
201 definition(cpp_highlight
const& self
)
202 : g(self
.actions
.state
.grammar())
204 member_action1
<syntax_highlight_actions
, char const*>
205 span(self
.actions
, &syntax_highlight_actions::span
),
206 span_start(self
.actions
, &syntax_highlight_actions::span_start
);
207 member_action
<syntax_highlight_actions
>
208 span_end(self
.actions
, &syntax_highlight_actions::span_end
),
209 unexpected_char(self
.actions
, &syntax_highlight_actions::unexpected_char
),
210 plain_char(self
.actions
, &syntax_highlight_actions::plain_char
),
211 pre_escape_back(self
.actions
, &syntax_highlight_actions::pre_escape_back
),
212 post_escape_back(self
.actions
, &syntax_highlight_actions::post_escape_back
),
213 mark_text(self
.actions
, &syntax_highlight_actions::mark_text
),
214 callout(self
.actions
, &syntax_highlight_actions::callout
);
215 member_action_value
<syntax_highlight_actions
, std::string
const&>
216 do_macro(self
.actions
, &syntax_highlight_actions::do_macro
);
217 error_action
error(self
.actions
.state
);
220 *( (*cl::space_p
) [plain_char
]
221 >> (line_start
| rest_of_line
)
227 preprocessor
[span("preprocessor")]
231 (+cl::blank_p
) [plain_char
]
234 | cl::eps_p(ph::var(self
.actions
.support_callouts
))
235 >> ( line_callout
[callout
]
236 | inline_callout
[callout
]
239 | keyword
[span("keyword")]
240 | identifier
[span("identifier")]
241 | special
[span("special")]
242 | string_
[span("string")]
243 | char_
[span("char")]
244 | number
[span("number")]
245 | ~cl::eps_p(cl::eol_p
)
246 >> u8_codepoint_p
[unexpected_char
]
250 // must not be followed by alpha or underscore
251 cl::eps_p(self
.actions
.state
.macro
252 >> (cl::eps_p
- (cl::alpha_p
| '_')))
253 >> self
.actions
.state
.macro
258 cl::str_p("``") [pre_escape_back
]
263 (+(cl::anychar_p
- "``") >> cl::eps_p("``"))
277 = '#' >> *cl::space_p
>> ((cl::alpha_p
| '_') >> *(cl::alnum_p
| '_'))
282 "/*<" >> *cl::space_p
,
283 (*cl::anychar_p
) [mark_text
],
290 "/*<<" >> *cl::space_p
,
291 (*cl::anychar_p
) [mark_text
],
298 = cl::str_p("//") [span_start("comment")]
300 | (+(cl::anychar_p
- (cl::eol_p
| "``")))
303 >> cl::eps_p
[span_end
]
304 | cl::str_p("/*") [span_start("comment")]
306 | (+(cl::anychar_p
- (cl::str_p("*/") | "``")))
309 >> (!cl::str_p("*/")) [span_end
]
313 = keywords
.cpp
>> (cl::eps_p
- (cl::alnum_p
| '_'))
314 ; // make sure we recognize whole words only
317 = +cl::chset_p("~!%^&*()+={[}]:;,<.>?/|\\#-")
320 string_char
= ('\\' >> u8_codepoint_p
) | (cl::anychar_p
- '\\');
323 = !cl::as_lower_d
['l'] >> cl::confix_p('"', *string_char
, '"')
327 = !cl::as_lower_d
['l'] >> cl::confix_p('\'', *string_char
, '\'')
332 cl::as_lower_d
["0x"] >> cl::hex_p
336 >> *cl::as_lower_d
[cl::chset_p("ldfu")]
340 = (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
345 program
, line_start
, rest_of_line
, macro
, preprocessor
,
346 inline_callout
, line_callout
, comment
,
348 char_
, number
, identifier
, keyword
, escape
,
351 quickbook_grammar
& g
;
353 cl::rule
<Scanner
> const&
354 start() const { return program
; }
357 syntax_highlight_actions
& actions
;
360 // Grammar for Python highlighting
361 // See also: The Python Reference Manual
362 // http://docs.python.org/ref/ref.html
363 struct python_highlight
: public cl::grammar
<python_highlight
>
365 explicit python_highlight(syntax_highlight_actions
& actions_
)
366 : actions(actions_
) {}
368 template <typename Scanner
>
371 definition(python_highlight
const& self
)
372 : g(self
.actions
.state
.grammar())
374 member_action1
<syntax_highlight_actions
, char const*>
375 span(self
.actions
, &syntax_highlight_actions::span
),
376 span_start(self
.actions
, &syntax_highlight_actions::span_start
);
377 member_action
<syntax_highlight_actions
>
378 span_end(self
.actions
, &syntax_highlight_actions::span_end
),
379 unexpected_char(self
.actions
, &syntax_highlight_actions::unexpected_char
),
380 plain_char(self
.actions
, &syntax_highlight_actions::plain_char
),
381 pre_escape_back(self
.actions
, &syntax_highlight_actions::pre_escape_back
),
382 post_escape_back(self
.actions
, &syntax_highlight_actions::post_escape_back
),
383 mark_text(self
.actions
, &syntax_highlight_actions::mark_text
),
384 callout(self
.actions
, &syntax_highlight_actions::callout
);
385 member_action_value
<syntax_highlight_actions
, std::string
const&>
386 do_macro(self
.actions
, &syntax_highlight_actions::do_macro
);
387 error_action
error(self
.actions
.state
);
391 *( (+cl::space_p
) [plain_char
]
394 | cl::eps_p(ph::var(self
.actions
.support_callouts
))
395 >> ( line_callout
[callout
]
396 | inline_callout
[callout
]
399 | keyword
[span("keyword")]
400 | identifier
[span("identifier")]
401 | special
[span("special")]
402 | string_
[span("string")]
403 | number
[span("number")]
404 | u8_codepoint_p
[unexpected_char
]
409 // must not be followed by alpha or underscore
410 cl::eps_p(self
.actions
.state
.macro
411 >> (cl::eps_p
- (cl::alpha_p
| '_')))
412 >> self
.actions
.state
.macro
417 cl::str_p("``") [pre_escape_back
]
422 (+(cl::anychar_p
- "``") >> cl::eps_p("``"))
436 = "#<" >> *cl::space_p
>>
437 (*(cl::anychar_p
- cl::eol_p
)) [mark_text
]
442 "#<<" >> *cl::space_p
,
443 (*cl::anychar_p
) [mark_text
],
444 (cl::eol_p
| cl::end_p
)
449 = cl::str_p("#") [span_start("comment")]
451 | (+(cl::anychar_p
- (cl::eol_p
| "``")))
454 >> cl::eps_p
[span_end
]
458 = keywords
.python
>> (cl::eps_p
- (cl::alnum_p
| '_'))
459 ; // make sure we recognize whole words only
462 = +cl::chset_p("~!%^&*()+={[}]:;,<.>/|\\-")
466 = cl::as_lower_d
[cl::str_p("u") >> ! cl::str_p("r")]
470 = ! string_prefix
>> (long_string
| short_string
)
473 string_char
= ('\\' >> u8_codepoint_p
) | (cl::anychar_p
- '\\');
476 = cl::confix_p('\'', * string_char
, '\'') |
477 cl::confix_p('"', * string_char
, '"')
481 // Note: the "cl::str_p" on the next two lines work around
482 // an INTERNAL COMPILER ERROR when using VC7.1
483 = cl::confix_p(cl::str_p("'''"), * string_char
, "'''") |
484 cl::confix_p(cl::str_p("\"\"\""), * string_char
, "\"\"\"")
489 cl::as_lower_d
["0x"] >> cl::hex_p
493 >> *cl::as_lower_d
[cl::chset_p("lj")]
497 = (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
502 program
, macro
, inline_callout
, line_callout
,
503 comment
, special
, string_
, string_prefix
,
504 short_string
, long_string
, number
, identifier
, keyword
,
507 quickbook_grammar
& g
;
509 cl::rule
<Scanner
> const&
510 start() const { return program
; }
513 syntax_highlight_actions
& actions
;
516 // Grammar for plain text (no actual highlighting)
517 struct teletype_highlight
: public cl::grammar
<teletype_highlight
>
519 teletype_highlight(syntax_highlight_actions
& actions_
)
520 : actions(actions_
) {}
522 template <typename Scanner
>
525 definition(teletype_highlight
const& self
)
526 : g(self
.actions
.state
.grammar())
528 member_action
<syntax_highlight_actions
>
529 plain_char(self
.actions
, &syntax_highlight_actions::plain_char
),
530 pre_escape_back(self
.actions
, &syntax_highlight_actions::pre_escape_back
),
531 post_escape_back(self
.actions
, &syntax_highlight_actions::post_escape_back
);
532 member_action_value
<syntax_highlight_actions
, std::string
const&>
533 do_macro(self
.actions
, &syntax_highlight_actions::do_macro
);
534 error_action
error(self
.actions
.state
);
540 | u8_codepoint_p
[plain_char
]
545 // must not be followed by alpha or underscore
546 cl::eps_p(self
.actions
.state
.macro
547 >> (cl::eps_p
- (cl::alpha_p
| '_')))
548 >> self
.actions
.state
.macro
553 cl::str_p("``") [pre_escape_back
]
558 (+(cl::anychar_p
- "``") >> cl::eps_p("``"))
572 cl::rule
<Scanner
> program
, macro
, escape
;
574 quickbook_grammar
& g
;
576 cl::rule
<Scanner
> const&
577 start() const { return program
; }
580 syntax_highlight_actions
& actions
;
583 void syntax_highlight(
584 parse_iterator first
,
586 quickbook::state
& state
,
587 source_mode_type source_mode
,
590 syntax_highlight_actions
syn_actions(state
, is_block
);
592 // print the code with syntax coloring
595 case source_mode_tags::cpp
: {
596 cpp_highlight
cpp_p(syn_actions
);
597 boost::spirit::classic::parse(first
, last
, cpp_p
);
600 case source_mode_tags::python
: {
601 python_highlight
python_p(syn_actions
);
602 boost::spirit::classic::parse(first
, last
, python_p
);
605 case source_mode_tags::teletype
: {
606 teletype_highlight
teletype_p(syn_actions
);
607 boost::spirit::classic::parse(first
, last
, teletype_p
);