]>
Commit | Line | Data |
---|---|---|
1 | /*============================================================================= | |
2 | Copyright (c) 2001-2011 Joel de Guzman | |
3 | ||
4 | Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | ==============================================================================*/ | |
7 | #if !defined(BOOST_SPIRIT_ERROR_HANDLER_APRIL_29_2007_1042PM) | |
8 | #define BOOST_SPIRIT_ERROR_HANDLER_APRIL_29_2007_1042PM | |
9 | ||
10 | #if defined(_MSC_VER) | |
11 | #pragma once | |
12 | #endif | |
13 | ||
14 | #include <boost/spirit/home/qi/operator/expect.hpp> | |
15 | #include <boost/spirit/home/qi/nonterminal/rule.hpp> | |
16 | #include <boost/spirit/home/support/multi_pass_wrapper.hpp> | |
17 | #include <boost/function.hpp> | |
18 | #include <boost/assert.hpp> | |
19 | ||
20 | namespace boost { namespace spirit { namespace qi | |
21 | { | |
22 | enum error_handler_result | |
23 | { | |
24 | fail | |
25 | , retry | |
26 | , accept | |
27 | , rethrow | |
28 | }; | |
29 | ||
30 | namespace detail | |
31 | { | |
32 | // Helper template allowing to manage the inhibit clear queue flag in | |
33 | // a multi_pass iterator. This is the usual specialization used for | |
34 | // anything but a multi_pass iterator. | |
35 | template <typename Iterator, bool active> | |
36 | struct reset_on_exit | |
37 | { | |
38 | reset_on_exit(Iterator&) {} | |
39 | }; | |
40 | ||
41 | // For 'retry' or 'fail' error handlers we need to inhibit the flushing | |
42 | // of the internal multi_pass buffers which otherwise might happen at | |
43 | // deterministic expectation points inside the encapsulated right hand | |
44 | // side of rule. | |
45 | template <typename Iterator> | |
46 | struct reset_on_exit<Iterator, true> | |
47 | { | |
48 | reset_on_exit(Iterator& it) | |
49 | : it_(it) | |
50 | , inhibit_clear_queue_(spirit::traits::inhibit_clear_queue(it)) | |
51 | { | |
52 | spirit::traits::inhibit_clear_queue(it_, true); | |
53 | } | |
54 | ||
55 | ~reset_on_exit() | |
56 | { | |
57 | // reset inhibit flag in multi_pass on exit | |
58 | spirit::traits::inhibit_clear_queue(it_, inhibit_clear_queue_); | |
59 | } | |
60 | ||
61 | Iterator& it_; | |
62 | bool inhibit_clear_queue_; | |
63 | }; | |
64 | } | |
65 | ||
66 | template < | |
67 | typename Iterator, typename Context | |
68 | , typename Skipper, typename F, error_handler_result action | |
69 | > | |
70 | struct error_handler | |
71 | { | |
72 | typedef function< | |
73 | bool(Iterator& first, Iterator const& last | |
74 | , Context& context | |
75 | , Skipper const& skipper | |
76 | )> | |
77 | function_type; | |
78 | ||
79 | error_handler(function_type subject_, F f_) | |
80 | : subject(subject_) | |
81 | , f(f_) | |
82 | { | |
83 | } | |
84 | ||
85 | bool operator()( | |
86 | Iterator& first, Iterator const& last | |
87 | , Context& context, Skipper const& skipper) const | |
88 | { | |
89 | typedef qi::detail::reset_on_exit<Iterator | |
90 | , traits::is_multi_pass<Iterator>::value && | |
91 | (action == retry || action == fail)> on_exit_type; | |
92 | ||
93 | on_exit_type on_exit(first); | |
94 | for(;;) | |
95 | { | |
96 | try | |
97 | { | |
98 | Iterator i = first; | |
99 | bool r = subject(i, last, context, skipper); | |
100 | if (r) | |
101 | first = i; | |
102 | return r; | |
103 | } | |
104 | catch (expectation_failure<Iterator> const& x) | |
105 | { | |
106 | typedef | |
107 | fusion::vector< | |
108 | Iterator& | |
109 | , Iterator const& | |
110 | , Iterator const& | |
111 | , info const&> | |
112 | params; | |
113 | error_handler_result r = action; | |
114 | params args(first, last, x.first, x.what_); | |
115 | f(args, context, r); | |
116 | ||
117 | // The assertions below will fire if you are using a | |
118 | // multi_pass as the underlying iterator, one of your error | |
119 | // handlers forced its guarded rule to 'fail' or 'retry', | |
120 | // and the error handler has not been instantiated using | |
121 | // either 'fail' or 'retry' in the first place. Please see | |
122 | // the multi_pass docs for more information. | |
123 | switch (r) | |
124 | { | |
125 | case fail: | |
126 | BOOST_ASSERT( | |
127 | !traits::is_multi_pass<Iterator>::value || | |
128 | action == retry || action == fail); | |
129 | return false; | |
130 | case retry: | |
131 | BOOST_ASSERT( | |
132 | !traits::is_multi_pass<Iterator>::value || | |
133 | action == retry || action == fail); | |
134 | continue; | |
135 | case accept: return true; | |
136 | case rethrow: boost::throw_exception(x); | |
137 | } | |
138 | } | |
139 | } | |
140 | return false; | |
141 | } | |
142 | ||
143 | function_type subject; | |
144 | F f; | |
145 | }; | |
146 | ||
147 | template < | |
148 | error_handler_result action | |
149 | , typename Iterator, typename T0, typename T1, typename T2 | |
150 | , typename F> | |
151 | void on_error(rule<Iterator, T0, T1, T2>& r, F f) | |
152 | { | |
153 | typedef rule<Iterator, T0, T1, T2> rule_type; | |
154 | ||
155 | typedef | |
156 | error_handler< | |
157 | Iterator | |
158 | , typename rule_type::context_type | |
159 | , typename rule_type::skipper_type | |
160 | , F | |
161 | , action> | |
162 | error_handler; | |
163 | r.f = error_handler(r.f, f); | |
164 | } | |
165 | ||
166 | // Error handling support when <action> is not | |
167 | // specified. We will default to <fail>. | |
168 | template <typename Iterator, typename T0, typename T1 | |
169 | , typename T2, typename F> | |
170 | void on_error(rule<Iterator, T0, T1, T2>& r, F f) | |
171 | { | |
172 | on_error<fail>(r, f); | |
173 | } | |
174 | }}} | |
175 | ||
176 | #endif |