]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/quickbook/src/post_process.cpp
1 /*=============================================================================
2 Copyright (c) 2005 2006 Joel de Guzman
3 http://spirit.sourceforge.net/
5 Use, modification and distribution is subject to the Boost Software
6 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 http://www.boost.org/LICENSE_1_0.txt)
8 =============================================================================*/
9 #include "post_process.hpp"
10 #include <boost/spirit/include/classic_core.hpp>
11 #include <boost/bind.hpp>
18 namespace cl
= boost::spirit::classic
;
19 typedef std::string::const_iterator iter_type
;
23 printer(std::string
& out_
, int& current_indent_
, int linewidth_
)
24 : prev(0), out(out_
), current_indent(current_indent_
) , column(0)
25 , in_string(false), linewidth(linewidth_
) {}
29 BOOST_ASSERT(current_indent
>= 0); // this should not happen!
30 for (int i
= 0; i
< current_indent
; ++i
)
32 column
= current_indent
;
37 out
.erase(out
.find_last_not_of(' ')+1); // trim trailing spaces
47 bool line_is_empty() const
49 for (iter_type i
= out
.end()-(column
-current_indent
); i
!= out
.end(); ++i
)
59 // make sure we are at the proper indent position
60 if (column
!= current_indent
)
62 if (column
> current_indent
)
66 // trim just enough trailing spaces down to current_indent position
67 out
.erase(out
.end()-(column
-current_indent
), out
.end());
68 column
= current_indent
;
72 // nope, line is not empty. do a hard CR
78 // will this happen? (i.e. column <= current_indent)
79 while (column
!= current_indent
)
90 // Print a char. Attempt to break the line if we are exceeding
91 // the target linewidth. The linewidth is not an absolute limit.
92 // There are many cases where a line will exceed the linewidth
93 // and there is no way to properly break the line. Preformatted
94 // code that exceeds the linewidth are examples. We cannot break
95 // preformatted code. We shall not attempt to be very strict with
96 // line breaking. What's more important is to have a reproducable
97 // output (i.e. processing two logically equivalent xml files
98 // results in two lexically equivalent xml files). *** pretty
99 // formatting is a secondary goal ***
101 // Strings will occur only in tag attributes. Normal content
102 // will have " instead. We shall deal only with tag
105 in_string
= !in_string
; // don't break strings!
107 if (!in_string
&& std::isspace(static_cast<unsigned char>(ch
)))
109 // we can break spaces if they are not inside strings
110 if (!std::isspace(static_cast<unsigned char>(prev
)))
112 if (column
>= linewidth
)
115 if (column
== 0 && ch
== ' ')
130 // we can break tag boundaries and stuff after
131 // delimiters if they are not inside strings
132 // and *only-if* the preceding char is a space
134 && column
>= linewidth
135 && (ch
== '<' && std::isspace(static_cast<unsigned char>(prev
))))
145 print(iter_type f
, iter_type l
)
147 for (iter_type i
= f
; i
!= l
; ++i
)
152 print_tag(iter_type f
, iter_type l
, bool is_flow_tag
)
160 // This is not a flow tag, so, we're going to do a
161 // carriage return anyway. Let us remove extra right
163 std::string
str(f
, l
);
164 BOOST_ASSERT(f
!= l
); // this should not happen
165 iter_type i
= str
.end();
166 while (i
!= str
.begin() && std::isspace(static_cast<unsigned char>(*(i
-1))))
168 print(str
.begin(), i
);
180 printer
& operator=(printer
const&);
183 char const* block_tags_
[] =
217 char const* doc_types_
[] =
234 tidy_compiler(std::string
& out_
, int linewidth_
)
235 : out(out_
), current_indent(0), printer_(out
, current_indent
, linewidth_
)
237 static std::size_t const n_block_tags
= sizeof(block_tags_
)/sizeof(char const*);
238 for (std::size_t i
= 0; i
!= n_block_tags
; ++i
)
240 block_tags
.insert(block_tags_
[i
]);
243 static std::size_t const n_doc_types
= sizeof(doc_types_
)/sizeof(char const*);
244 for (std::size_t i
= 0; i
!= n_doc_types
; ++i
)
246 block_tags
.insert(doc_types_
[i
]);
247 block_tags
.insert(doc_types_
[i
] + std::string("info"));
248 block_tags
.insert(doc_types_
[i
] + std::string("purpose"));
252 bool is_flow_tag(std::string
const& tag
)
254 return block_tags
.find(tag
) == block_tags
.end();
257 std::set
<std::string
> block_tags
;
258 std::stack
<std::string
> tags
;
262 std::string current_tag
;
265 tidy_compiler
& operator=(tidy_compiler
const&);
268 struct tidy_grammar
: cl::grammar
<tidy_grammar
>
270 tidy_grammar(tidy_compiler
& state_
, int indent_
)
271 : state(state_
), indent(indent_
) {}
273 template <typename Scanner
>
276 definition(tidy_grammar
const& self
)
278 tag
= (cl::lexeme_d
[+(cl::alpha_p
| '_' | ':')]) [boost::bind(&tidy_grammar::do_tag
, &self
, _1
, _2
)];
282 >> *(cl::anychar_p
- "</programlisting>")
283 >> "</programlisting>"
286 // What's the business of cl::lexeme_d['>' >> *cl::space_p]; ?
287 // It is there to preserve the space after the tag that is
288 // otherwise consumed by the cl::space_p skipper.
291 cl::str_p("<!--quickbook-escape-prefix-->") >>
292 (*(cl::anychar_p
- cl::str_p("<!--quickbook-escape-postfix-->")))
294 boost::bind(&tidy_grammar::do_escape
, &self
, _1
, _2
)
298 cl::str_p("<!--quickbook-escape-postfix-->") >>
301 boost::bind(&tidy_grammar::do_escape_post
, &self
, _1
, _2
)
306 start_tag
= '<' >> tag
>> *(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
];
308 '<' >> tag
>> *(cl::anychar_p
- ("/>" | cl::ch_p('>'))) >> cl::lexeme_d
["/>" >> *cl::space_p
]
309 | "<?" >> tag
>> *(cl::anychar_p
- '?') >> cl::lexeme_d
["?>" >> *cl::space_p
]
310 | "<!--" >> *(cl::anychar_p
- "-->") >> cl::lexeme_d
["-->" >> *cl::space_p
]
311 | "<!" >> tag
>> *(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
]
313 content
= cl::lexeme_d
[ +(cl::anychar_p
- '<') ];
314 end_tag
= "</" >> +(cl::anychar_p
- '>') >> cl::lexeme_d
['>' >> *cl::space_p
];
318 | code
[boost::bind(&tidy_grammar::do_code
, &self
, _1
, _2
)]
319 | start_end_tag
[boost::bind(&tidy_grammar::do_start_end_tag
, &self
, _1
, _2
)]
320 | start_tag
[boost::bind(&tidy_grammar::do_start_tag
, &self
, _1
, _2
)]
321 | end_tag
[boost::bind(&tidy_grammar::do_end_tag
, &self
, _1
, _2
)]
322 | content
[boost::bind(&tidy_grammar::do_content
, &self
, _1
, _2
)]
328 cl::rule
<Scanner
> const&
329 start() { return tidy
; }
332 tidy
, tag
, start_tag
, start_end_tag
,
333 content
, end_tag
, markup
, code
, escape
;
336 void do_escape_post(iter_type f
, iter_type l
) const
338 for (iter_type i
= f
; i
!= l
; ++i
)
342 void do_escape(iter_type f
, iter_type l
) const
344 while (f
!= l
&& std::isspace(*f
))
346 for (iter_type i
= f
; i
!= l
; ++i
)
350 void do_code(iter_type f
, iter_type l
) const
352 state
.printer_
.trim_spaces();
353 if (state
.out
[state
.out
.size() - 1] != '\n')
355 // print the string taking care of line
356 // ending CR/LF platform issues
357 for (iter_type i
= f
; i
!= l
; ++i
)
361 state
.printer_
.trim_spaces();
364 if (i
!= l
&& *i
!= '\r')
369 state
.printer_
.trim_spaces();
372 if (i
!= l
&& *i
!= '\n')
381 state
.printer_
.indent();
384 void do_tag(iter_type f
, iter_type l
) const
386 state
.current_tag
= std::string(f
, l
);
389 void do_start_end_tag(iter_type f
, iter_type l
) const
391 bool is_flow_tag
= state
.is_flow_tag(state
.current_tag
);
393 state
.printer_
.align_indent();
394 state
.printer_
.print_tag(f
, l
, is_flow_tag
);
396 state
.printer_
.break_line();
399 void do_start_tag(iter_type f
, iter_type l
) const
401 state
.tags
.push(state
.current_tag
);
402 bool is_flow_tag
= state
.is_flow_tag(state
.current_tag
);
404 state
.printer_
.align_indent();
405 state
.printer_
.print_tag(f
, l
, is_flow_tag
);
408 state
.current_indent
+= indent
;
409 state
.printer_
.break_line();
413 void do_content(iter_type f
, iter_type l
) const
415 state
.printer_
.print(f
, l
);
418 void do_end_tag(iter_type f
, iter_type l
) const
420 if (state
.tags
.empty())
421 throw quickbook::post_process_failure("Mismatched tags.");
423 bool is_flow_tag
= state
.is_flow_tag(state
.tags
.top());
426 state
.current_indent
-= indent
;
427 state
.printer_
.align_indent();
429 state
.printer_
.print_tag(f
, l
, is_flow_tag
);
431 state
.printer_
.break_line();
435 tidy_compiler
& state
;
439 tidy_grammar
& operator=(tidy_grammar
const&);
442 std::string
post_process(
443 std::string
const& in
448 indent
= 2; // set default to 2
450 linewidth
= 80; // set default to 80
453 tidy_compiler
state(tidy
, linewidth
);
454 tidy_grammar
g(state
, indent
);
455 cl::parse_info
<iter_type
> r
= parse(in
.begin(), in
.end(), g
, cl::space_p
);
462 throw quickbook::post_process_failure("Post Processing Failed.");