Simplify the link grammars.
[boost:svn.git] / tools / quickbook / src / phrase_grammar.hpp
1 /*=============================================================================
2     Copyright (c) 2002 2004 2006 Joel de Guzman
3     Copyright (c) 2004 Eric Niebler
4     http://spirit.sourceforge.net/
5
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 #if !defined(BOOST_SPIRIT_QUICKBOOK_PHRASE_HPP)
11 #define BOOST_SPIRIT_QUICKBOOK_PHRASE_HPP
12
13 #include "grammar.hpp"
14 #include "actions_class.hpp"
15 #include "utils.hpp"
16 #include <boost/spirit/include/classic_core.hpp>
17 #include <boost/spirit/include/classic_confix.hpp>
18 #include <boost/spirit/include/classic_chset.hpp>
19 #include <boost/spirit/include/classic_assign_actor.hpp>
20 #include <boost/spirit/include/classic_clear_actor.hpp>
21 #include <boost/spirit/include/classic_if.hpp>
22 #include <boost/spirit/include/classic_loops.hpp>
23
24 namespace quickbook
25 {
26     namespace cl = boost::spirit::classic;
27
28     template <typename Rule, typename Action>
29     inline void
30     simple_markup(
31         Rule& simple
32       , char mark
33       , Action const& action
34       , Rule const& close
35     )
36     {
37         simple =
38             mark >>
39             (
40                 (
41                     cl::graph_p                 // A single char. e.g. *c*
42                     >> cl::eps_p(mark
43                         >> (cl::space_p | cl::punct_p | cl::end_p))
44                                                 // space_p, punct_p or end_p
45                 )                               // must follow mark
46             |
47                 (   cl::graph_p >>              // graph_p must follow mark
48                     *(cl::anychar_p -
49                         (   (cl::graph_p >> mark) // Make sure that we don't go
50                         |   close                 // past a single block
51                         )
52                     ) >> cl::graph_p            // graph_p must precede mark
53                     >> cl::eps_p(mark
54                         >> (cl::space_p | cl::punct_p | cl::end_p))
55                                                 // space_p, punct_p or end_p
56                 )                               // must follow mark
57             )                                   [action]
58             >> mark
59             ;
60     }
61
62     template <typename Scanner>
63     struct phrase_grammar::definition
64     {
65         definition(phrase_grammar const& self);
66
67         cl::rule<Scanner>
68                         space, blank, comment, phrase, phrase_markup, image,
69                         simple_phrase_end, phrase_end, bold, italic, underline, teletype,
70                         strikethrough, escape, url, common, funcref, classref,
71                         memberref, enumref, macroref, headerref, conceptref, globalref,
72                         anchor, link, hard_space, eol, inline_code, simple_format,
73                         simple_bold, simple_italic, simple_underline,
74                         simple_teletype, source_mode, template_,
75                         quote, code_block, footnote, replaceable, macro,
76                         dummy_block, cond_phrase, macro_identifier, template_args,
77                         template_args_1_4, template_arg_1_4,
78                         template_inner_arg_1_4, brackets_1_4,
79                         template_args_1_5, template_arg_1_5,
80                         template_inner_arg_1_5, brackets_1_5
81                         ;
82
83         cl::rule<Scanner> const&
84         start() const { return common; }
85     };
86
87     template <typename Scanner>
88     phrase_grammar::definition<Scanner>::definition(phrase_grammar const& self)
89     {
90         using detail::var;
91         quickbook::actions& actions = self.actions;
92
93         space =
94             *(cl::space_p | comment)
95             ;
96
97         blank =
98             *(cl::blank_p | comment)
99             ;
100
101         eol = blank >> cl::eol_p
102             ;
103
104         phrase_end =
105             ']' |
106             cl::if_p(var(self.no_eols))
107             [
108                 eol >> eol                      // Make sure that we don't go
109             ]                                   // past a single block, except
110             ;                                   // when preformatted.
111
112         // Follows an alphanumeric identifier - ensures that it doesn't
113         // match an empty space in the middle of the identifier.
114         hard_space =
115             (cl::eps_p - (cl::alnum_p | '_')) >> space
116             ;
117
118         comment =
119             "[/" >> *(dummy_block | (cl::anychar_p - ']')) >> ']'
120             ;
121
122         dummy_block =
123             '[' >> *(dummy_block | (cl::anychar_p - ']')) >> ']'
124             ;
125
126         common =
127                 macro
128             |   phrase_markup
129             |   code_block
130             |   inline_code
131             |   simple_format
132             |   escape
133             |   comment
134             ;
135
136         macro =
137             // must not be followed by alpha or underscore
138             cl::eps_p(actions.macro
139                 >> (cl::eps_p - (cl::alpha_p | '_')))
140             >> actions.macro                    [actions.do_macro]
141             ;
142
143         static const bool true_ = true;
144         static const bool false_ = false;
145
146         template_ =
147             (
148                 cl::ch_p('`')                   [cl::assign_a(actions.template_escape,true_)]
149                 |
150                 cl::eps_p                       [cl::assign_a(actions.template_escape,false_)]
151             )
152             >>
153             ( (
154                 (cl::eps_p(cl::punct_p)
155                     >> actions.templates.scope
156                 )                               [cl::assign_a(actions.template_identifier)]
157                                                 [cl::clear_a(actions.template_args)]
158                 >> !template_args
159             ) | (
160                 (actions.templates.scope
161                     >> cl::eps_p(hard_space)
162                 )                               [cl::assign_a(actions.template_identifier)]
163                                                 [cl::clear_a(actions.template_args)]
164                 >> space
165                 >> !template_args
166             ) )
167             >> cl::eps_p(']')
168             ;
169
170         template_args =
171             cl::if_p(qbk_since(105u)) [
172                 template_args_1_5
173             ].else_p [
174                 template_args_1_4
175             ]
176             ;
177
178         template_args_1_4 = template_arg_1_4 >> *(".." >> template_arg_1_4);
179
180         template_arg_1_4 =
181                 (   cl::eps_p(*cl::blank_p >> cl::eol_p)
182                                                 [cl::assign_a(actions.template_block, true_)]
183                 |   cl::eps_p                   [cl::assign_a(actions.template_block, false_)]
184                 )
185             >>  template_inner_arg_1_4          [actions.template_arg]
186             ;
187
188         template_inner_arg_1_4 =
189             +(brackets_1_4 | (cl::anychar_p - (cl::str_p("..") | ']')))
190             ;
191
192         brackets_1_4 =
193             '[' >> template_inner_arg_1_4 >> ']'
194             ;
195
196         template_args_1_5 = template_arg_1_5 >> *(".." >> template_arg_1_5);
197
198         template_arg_1_5 =
199                 (   cl::eps_p(*cl::blank_p >> cl::eol_p)
200                                                 [cl::assign_a(actions.template_block, true_)]
201                 |   cl::eps_p                   [cl::assign_a(actions.template_block, false_)]
202                 )
203             >>  (+(brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p("..") | '[' | ']'))))
204                                                 [actions.template_arg]
205             ;
206
207         template_inner_arg_1_5 =
208             +(brackets_1_5 | ('\\' >> cl::anychar_p) | (cl::anychar_p - (cl::str_p('[') | ']')))
209             ;
210
211         brackets_1_5 =
212             '[' >> template_inner_arg_1_5 >> ']'
213             ;
214
215         inline_code =
216             '`' >>
217             (
218                *(cl::anychar_p -
219                     (   '`'
220                     |   (eol >> eol)            // Make sure that we don't go
221                     )                           // past a single block
222                 ) >> cl::eps_p('`')
223             )                                   [actions.inline_code]
224             >>  '`'
225             ;
226
227         code_block =
228                 (
229                     "```" >>
230                     (
231                        *(cl::anychar_p - "```")
232                             >> cl::eps_p("```")
233                     )                           [actions.code_block]
234                     >>  "```"
235                 )
236             |   (
237                     "``" >>
238                     (
239                        *(cl::anychar_p - "``")
240                             >> cl::eps_p("``")
241                     )                           [actions.code_block]
242                     >>  "``"
243                 )
244             ;
245
246         simple_format =
247                 simple_bold
248             |   simple_italic
249             |   simple_underline
250             |   simple_teletype
251             ;
252
253         simple_phrase_end = '[' | phrase_end;
254
255         simple_markup(simple_bold,
256             '*', actions.simple_bold, simple_phrase_end);
257         simple_markup(simple_italic,
258             '/', actions.simple_italic, simple_phrase_end);
259         simple_markup(simple_underline,
260             '_', actions.simple_underline, simple_phrase_end);
261         simple_markup(simple_teletype,
262             '=', actions.simple_teletype, simple_phrase_end);
263
264         phrase =
265            *(   common
266             |   comment
267             |   (cl::anychar_p - phrase_end)    [actions.plain_char]
268             )
269             ;
270
271         phrase_markup =
272                 '['
273             >>  (   cond_phrase
274                 |   image
275                 |   url
276                 |   link
277                 |   anchor
278                 |   source_mode
279                 |   funcref
280                 |   classref
281                 |   memberref
282                 |   enumref
283                 |   macroref
284                 |   headerref
285                 |   conceptref
286                 |   globalref
287                 |   bold
288                 |   italic
289                 |   underline
290                 |   teletype
291                 |   strikethrough
292                 |   quote
293                 |   replaceable
294                 |   footnote
295                 |   template_                   [actions.do_template]
296                 |   cl::str_p("br")             [actions.break_]
297                 )
298             >>  ']'
299             ;
300
301         escape =
302                 cl::str_p("\\ ")                // ignore an escaped space
303             |   '\\' >> cl::punct_p             [actions.raw_char]
304             |   "\\u" >> cl::repeat_p(4) [cl::chset<>("0-9a-fA-F")]
305                                                 [actions.escape_unicode]
306             |   "\\U" >> cl::repeat_p(8) [cl::chset<>("0-9a-fA-F")]
307                                                 [actions.escape_unicode]
308             |   (
309                     ("'''" >> !eol)             [actions.escape_pre]
310                 >>  *(cl::anychar_p - "'''")    [actions.raw_char]
311                 >>  cl::str_p("'''")            [actions.escape_post]
312                 )
313             ;
314
315         macro_identifier =
316             +(cl::anychar_p - (cl::space_p | ']'))
317             ;
318
319         cond_phrase =
320                 '?' >> blank
321             >>  macro_identifier                [actions.cond_phrase_pre]
322             >>  (!phrase)                       [actions.cond_phrase_post]
323             ;
324
325         image =
326                 '$' >> blank                    [cl::clear_a(actions.attributes)]
327             >>  cl::if_p(qbk_since(105u)) [
328                         (+(
329                             *cl::space_p
330                         >>  +(cl::anychar_p - (cl::space_p | phrase_end | '['))
331                         ))                       [cl::assign_a(actions.image_fileref)]
332                     >>  hard_space
333                     >>  *(
334                             '['
335                         >>  (*(cl::alnum_p | '_'))  [cl::assign_a(actions.attribute_name)]
336                         >>  space
337                         >>  (*(cl::anychar_p - (phrase_end | '[')))
338                                                 [actions.attribute]
339                         >>  ']'
340                         >>  space
341                         )
342                 ].else_p [
343                         (*(cl::anychar_p - phrase_end))
344                                                 [cl::assign_a(actions.image_fileref)]
345                 ]
346             >>  cl::eps_p(']')                  [actions.image]
347             ;
348             
349         url =
350                 '@'
351             >>  (*(cl::anychar_p -
352                     (']' | hard_space)))        [actions.url_pre]
353             >>  hard_space
354             >>  phrase                          [actions.url_post]
355             ;
356
357         link =
358                 "link" >> hard_space
359             >>  (*(cl::anychar_p -
360                     (']' | hard_space)))        [actions.link_pre]
361             >>  hard_space
362             >>  phrase                          [actions.link_post]
363             ;
364
365         anchor =
366                 blank
367             >>  (*(cl::anychar_p - phrase_end)) [actions.anchor]
368             ;
369
370         funcref =
371             "funcref" >> hard_space
372             >>  (*(cl::anychar_p -
373                     (']' | hard_space)))        [actions.funcref_pre]
374             >>  hard_space
375             >>  phrase                          [actions.funcref_post]
376             ;
377
378         classref =
379             "classref" >> hard_space
380             >>  (*(cl::anychar_p -
381                     (']' | hard_space)))        [actions.classref_pre]
382             >>  hard_space
383             >>  phrase                          [actions.classref_post]
384             ;
385
386         memberref =
387             "memberref" >> hard_space
388             >>  (*(cl::anychar_p -
389                     (']' | hard_space)))        [actions.memberref_pre]
390             >>  hard_space
391             >>  phrase                          [actions.memberref_post]
392             ;
393
394         enumref =
395             "enumref" >> hard_space
396             >>  (*(cl::anychar_p -
397                     (']' | hard_space)))        [actions.enumref_pre]
398             >>  hard_space
399             >>  phrase                          [actions.enumref_post]
400             ;
401
402         macroref =
403             "macroref" >> hard_space
404             >>  (*(cl::anychar_p -
405                     (']' | hard_space)))        [actions.macroref_pre]
406             >>  hard_space
407             >>  phrase                          [actions.macroref_post]
408             ;
409
410         headerref =
411             "headerref" >> hard_space
412             >>  (*(cl::anychar_p -
413                     (']' | hard_space)))        [actions.headerref_pre]
414             >>  hard_space
415             >>  phrase                          [actions.headerref_post]
416             ;
417
418         conceptref =
419             "conceptref" >> hard_space
420             >>  (*(cl::anychar_p -
421                     (']' | hard_space)))        [actions.conceptref_pre]
422             >>  hard_space
423             >>  phrase                          [actions.conceptref_post]
424             ;
425
426         globalref =
427             "globalref" >> hard_space
428             >>  (*(cl::anychar_p -
429                     (']' | hard_space)))        [actions.globalref_pre]
430             >>  hard_space
431             >>  phrase                          [actions.globalref_post]
432             ;
433
434         bold =
435                 cl::ch_p('*')                   [actions.bold_pre]
436             >>  blank >> phrase                 [actions.bold_post]
437             ;
438
439         italic =
440                 cl::ch_p('\'')                  [actions.italic_pre]
441             >>  blank >> phrase                 [actions.italic_post]
442             ;
443
444         underline =
445                 cl::ch_p('_')                   [actions.underline_pre]
446             >>  blank >> phrase                 [actions.underline_post]
447             ;
448
449         teletype =
450                 cl::ch_p('^')                   [actions.teletype_pre]
451             >>  blank >> phrase                 [actions.teletype_post]
452             ;
453
454         strikethrough =
455                 cl::ch_p('-')                   [actions.strikethrough_pre]
456             >>  blank >> phrase                 [actions.strikethrough_post]
457             ;
458
459         quote =
460                 cl::ch_p('"')                   [actions.quote_pre]
461             >>  blank >> phrase                 [actions.quote_post]
462             ;
463
464         replaceable =
465                 cl::ch_p('~')                   [actions.replaceable_pre]
466             >>  blank >> phrase                 [actions.replaceable_post]
467             ;
468
469         source_mode =
470             (
471                 cl::str_p("c++")
472             |   "python"
473             |   "teletype"
474             )                                   [cl::assign_a(actions.source_mode)]
475             ;
476
477         footnote =
478                 cl::str_p("footnote")           [actions.footnote_pre]
479             >>  blank >> phrase                 [actions.footnote_post]
480             ;
481     }
482 }
483
484 #endif // BOOST_SPIRIT_QUICKBOOK_PHRASE_HPP
485