recover.hpp
Go to the documentation of this file.
1 // Copyright (C) 2020-2024 Jonathan Müller and lexy contributors
2 // SPDX-License-Identifier: BSL-1.0
3 
4 #ifndef LEXY_DSL_RECOVER_HPP_INCLUDED
5 #define LEXY_DSL_RECOVER_HPP_INCLUDED
6 
7 #include <lexy/dsl/base.hpp>
8 #include <lexy/dsl/choice.hpp>
9 #include <lexy/dsl/literal.hpp>
10 #include <lexy/dsl/lookahead.hpp>
11 
12 namespace lexyd
13 {
14 // Indicates that this rule already generates the recovery events.
16 {};
17 
18 // Parses the rule but generates the appropriate recovery_start/finish/cancel events.
19 template <typename Rule>
21 {
22  template <typename NextParser>
23  struct p
24  {
26  {
27  template <typename Context, typename Reader, typename... Args>
28  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader,
29  bool& recovery_finished, Args&&... args)
30  {
31  recovery_finished = true;
32  context.on(_ev::recovery_finish{}, reader.position());
33  return NextParser::parse(context, reader, LEXY_FWD(args)...);
34  }
35  };
36 
37  template <typename Context, typename Reader, typename... Args>
38  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
39  {
40  context.on(_ev::recovery_start{}, reader.position());
41  auto recovery_finished = false;
42 
43  // As part of the recovery, we parse the rule and whitespace.
45  auto result = parser::parse(context, reader, recovery_finished, LEXY_FWD(args)...);
46 
47  if (!recovery_finished)
48  context.on(_ev::recovery_cancel{}, reader.position());
49  return result;
50  }
51  };
52 };
53 
55 {
56  template <typename NextParser>
57  using p = NextParser;
58 };
59 } // namespace lexyd
60 
61 namespace lexyd
62 {
63 template <typename Token, typename Limit>
65 {
66  template <typename NextParser>
67  struct p
68  {
69  template <typename Context, typename Reader, typename... Args>
70  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
71  {
72  // Note that we can't use lookahead() directly as it's end position includes Needle/End,
73  // here we want to exclude it, however.
74  constexpr const auto& trie
75  = _look_trie<typename Reader::encoding, Token, decltype(get_limit())>;
77 
78  auto begin = reader.position();
79  context.on(_ev::recovery_start{}, begin);
80  while (true)
81  {
82  auto end = reader.current(); // *before* we've consumed Token/Limit
83  auto result = matcher::try_match(reader);
84  if (result == 0)
85  {
86  context.on(_ev::token{}, lexy::error_token_kind, begin, end.position());
87  context.on(_ev::recovery_finish{}, end.position());
88  reader.reset(end); // reset to before the token
89  return NextParser::parse(context, reader, LEXY_FWD(args)...);
90  }
91  else if (result == 1 || reader.peek() == Reader::encoding::eof())
92  {
93  context.on(_ev::token{}, lexy::error_token_kind, begin, end.position());
94  context.on(_ev::recovery_cancel{}, end.position());
95  reader.reset(end); // reset to before the limit
96  return false;
97  }
98  else
99  {
100  // Try again.
101  reader.bump();
102  }
103  }
104 
105  // unreachable
106  }
107  };
108 
109  //=== dsl ===//
111  template <typename... Literals>
112  constexpr auto limit(Literals... literals) const
113  {
114  static_assert(sizeof...(Literals) > 0);
115 
116  auto l = (get_limit() / ... / literals);
117  return _find<Token, decltype(l)>{};
118  }
119 
120  static constexpr auto get_limit()
121  {
122  if constexpr (std::is_void_v<Limit>)
123  return literal_set();
124  else
125  return Limit{};
126  }
127 };
128 
130 template <typename... Literals>
131 constexpr auto find(Literals... literals)
132 {
133  static_assert(sizeof...(Literals) > 0);
134 
135  auto needle = (literal_set() / ... / literals);
136  return _find<decltype(needle), void>{};
137 }
138 } // namespace lexyd
139 
140 namespace lexyd
141 {
142 template <typename Limit, typename... R>
144 {
145  template <typename NextParser>
146  struct p
147  {
148  template <typename Context, typename Reader, typename... Args>
149  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
150  {
151  auto begin = reader.position();
152  context.on(_ev::recovery_start{}, begin);
153 
154  // Try to match one of the recovery rules.
155  lexy::branch_parser_for<decltype((R{} | ...)), Reader> recovery{};
156  while (!recovery.try_parse(context.control_block, reader))
157  {
158  recovery.cancel(context);
159 
160  if (lexy::token_parser_for<decltype(get_limit()), Reader> limit(reader);
161  limit.try_parse(reader) || reader.peek() == Reader::encoding::eof())
162  {
163  // We've failed to recover as we've reached the limit.
164  auto end = reader.position();
165  context.on(_ev::token{}, lexy::error_token_kind, begin, end);
166  context.on(_ev::recovery_cancel{}, end);
167  return false;
168  }
169  else
170  {
171  // Try again.
172  reader.bump();
173  }
174  }
175 
176  auto end = reader.position();
177  context.on(_ev::token{}, lexy::error_token_kind, begin, end);
178  context.on(_ev::recovery_finish{}, end);
179 
180  // Finish with the rule that matched.
181  return recovery.template finish<NextParser>(context, reader, LEXY_FWD(args)...);
182  }
183  };
184 
185  //=== dsl ===//
187  template <typename... Literals>
188  constexpr auto limit(Literals... literals) const
189  {
190  static_assert(sizeof...(Literals) > 0);
191 
192  auto l = (get_limit() / ... / literals);
193  return _reco<decltype(l), R...>{};
194  }
195 
196  static constexpr auto get_limit()
197  {
198  if constexpr (std::is_void_v<Limit>)
199  return literal_set();
200  else
201  return Limit{};
202  }
203 };
204 
206 template <typename... Branches>
207 constexpr auto recover(Branches...)
208 {
209  static_assert(sizeof...(Branches) > 0);
210  LEXY_REQUIRE_BRANCH_RULE(Branches..., "recover");
211  return _reco<void, Branches...>{};
212 }
213 } // namespace lexyd
214 
215 namespace lexyd
216 {
217 template <typename Terminator, typename Rule, typename Recover>
218 struct _tryt : rule_base
219 {
220  template <typename NextParser>
221  struct _pc
222  {
223  template <typename Context, typename Reader, typename... Args>
224  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader,
225  bool& continuation_reached, Args&&... args)
226  {
227  continuation_reached = true;
228 
229  // We need to parse the terminator on success as well, if we have one.
230  if constexpr (std::is_void_v<Terminator>)
231  return NextParser::parse(context, reader, LEXY_FWD(args)...);
232  else
234  LEXY_FWD(args)...);
235  }
236 
237  template <typename Context, typename Reader, typename... Args>
238  LEXY_PARSER_FUNC static bool recover(Context& context, Reader& reader, Args&&... args)
239  {
240  if constexpr (std::is_void_v<Recover>)
241  {
242  using recovery_rule = _recovery_wrapper<_noop_recovery>;
244  LEXY_FWD(args)...);
245  }
246  else if constexpr (std::is_base_of_v<_recovery_base, Recover>)
247  {
248  using recovery_rule = Recover;
250  LEXY_FWD(args)...);
251  }
252  else
253  {
254  using recovery_rule = _recovery_wrapper<Recover>;
256  LEXY_FWD(args)...);
257  }
258  }
259  };
260 
261  template <typename NextParser>
262  struct p
263  {
264  template <typename Context, typename Reader, typename... Args>
265  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
266  {
268 
269  // Parse the rule and check whether it reached the continuation.
270  auto continuation_reached = false;
271  auto result = parser::parse(context, reader, continuation_reached, LEXY_FWD(args)...);
272  if (continuation_reached)
273  // Whatever happened, it is not our problem as we've reached the continuation.
274  return result;
275 
276  // We haven't reached the continuation, so need to recover.
277  LEXY_ASSERT(!result, "we've failed without reaching the continuation?!");
278  return _pc<NextParser>::recover(context, reader, LEXY_FWD(args)...);
279  }
280  };
281 };
282 
283 template <typename Rule, typename Recover>
284 struct _tryr : _copy_base<Rule>
285 {
287 
288  template <typename Reader>
289  struct bp
290  {
292 
293  template <typename ControlBlock>
294  constexpr auto try_parse(const ControlBlock* cb, const Reader& reader)
295  {
296  return rule.try_parse(cb, reader);
297  }
298 
299  template <typename Context>
300  constexpr void cancel(Context& context)
301  {
302  rule.cancel(context);
303  }
304 
305  template <typename NextParser, typename Context, typename... Args>
306  LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args)
307  {
308  // Finish the rule and check whether it reached the continuation.
309  using continuation = typename impl::template _pc<NextParser>;
310  auto continuation_reached = false;
311  auto result = rule.template finish<continuation>(context, reader, continuation_reached,
312  LEXY_FWD(args)...);
313  if (continuation_reached)
314  // Whatever happened, it is not our problem as we've reached the continuation.
315  return result;
316 
317  // We haven't reached the continuation, so need to recover.
318  LEXY_ASSERT(!result, "we've failed without reaching the continuation?!");
319  return continuation::recover(context, reader, LEXY_FWD(args)...);
320  }
321  };
322 
323  template <typename NextParser>
324  struct p : lexy::parser_for<impl, NextParser>
325  {};
326 };
327 
329 template <typename Rule>
330 constexpr auto try_(Rule)
331 {
332  return _tryr<Rule, void>{};
333 }
334 
336 template <typename Rule, typename Recover>
337 constexpr auto try_(Rule, Recover)
338 {
339  return _tryr<Rule, Recover>{};
340 }
341 } // namespace lexyd
342 
343 #endif // LEXY_DSL_RECOVER_HPP_INCLUDED
344 
choice.hpp
lexy::parse_events::recovery_start
Definition: dsl/base.hpp:74
literal.hpp
lexyd::_recovery_wrapper::p
Definition: recover.hpp:23
lexyd::_noop_recovery::p
NextParser p
Definition: recover.hpp:57
lexyd::_tryr::bp::try_parse
constexpr auto try_parse(const ControlBlock *cb, const Reader &reader)
Definition: recover.hpp:294
lexyd::_tryt
Definition: recover.hpp:218
literals
Definition: json.hpp:24461
lexy::branch_parser_for
typename BranchRule::template bp< Reader > branch_parser_for
Definition: dsl/base.hpp:116
lexyd::recover
constexpr auto recover(Branches...)
Discards input until one of the branches matches to recover from an error.
Definition: recover.hpp:207
LEXY_FWD
#define LEXY_FWD(...)
Definition: config.hpp:30
lexyd::_noop_recovery
Definition: recover.hpp:54
lexyd::_recovery_wrapper::p::_continuation
Definition: recover.hpp:25
lexyd::_reco
Definition: recover.hpp:143
lookahead.hpp
lexyd::_reco::get_limit
static constexpr auto get_limit()
Definition: recover.hpp:196
cx::end
constexpr auto end(const C &c) -> decltype(c.end())
Definition: wildcards.hpp:686
detail::void
j template void())
Definition: json.hpp:4893
LEXY_REQUIRE_BRANCH_RULE
#define LEXY_REQUIRE_BRANCH_RULE(Rule, Name)
Definition: grammar.hpp:73
lexyd::_look_trie
static constexpr auto _look_trie
Definition: lookahead.hpp:45
lexyd::try_
constexpr auto try_(Rule)
Parses Rule, if that fails, continues immediately.
Definition: recover.hpp:330
lexyd::_tryr::p
Definition: recover.hpp:324
lexyd::_tryt::_pc::recover
static LEXY_PARSER_FUNC bool recover(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:238
lexy::parse
constexpr auto parse(const Input &input, const ErrorCallback &callback)
Parses the production into a value, invoking the callback on error.
Definition: parse.hpp:171
lexyd::_find::get_limit
static constexpr auto get_limit()
Definition: recover.hpp:120
lexyd::_tryt::_pc::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, bool &continuation_reached, Args &&... args)
Definition: recover.hpp:224
lexyd::_recovery_base
Definition: recover.hpp:15
lexyd::_recovery_wrapper::p::_continuation::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, bool &recovery_finished, Args &&... args)
Definition: recover.hpp:28
lexyd::_tryr::bp::cancel
constexpr void cancel(Context &context)
Definition: recover.hpp:300
lexyd::_reco::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:149
lexyd::_reco::p
Definition: recover.hpp:146
lexyd::rule_base
Definition: grammar.hpp:17
lexyd::_tryr::bp
Definition: recover.hpp:289
lexyd::_tryr
Definition: recover.hpp:284
lexy::error_token_kind
@ error_token_kind
Definition: grammar.hpp:86
lexy::parse_events::recovery_finish
Definition: dsl/base.hpp:79
lexyd::_reco::limit
constexpr auto limit(Literals... literals) const
Fail error recovery if limiting literal tokens is found first.
Definition: recover.hpp:188
LEXY_PARSER_FUNC
#define LEXY_PARSER_FUNC
Definition: dsl/base.hpp:108
lexyd::_tryt::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:265
lexyd::_tryr::bp::rule
lexy::branch_parser_for< Rule, Reader > rule
Definition: recover.hpp:291
cx::begin
constexpr auto begin(const C &c) -> decltype(c.begin())
Definition: wildcards.hpp:661
lexyd::_recovery_wrapper
Definition: recover.hpp:20
lexyd::_find::limit
constexpr auto limit(Literals... literals) const
Fail error recovery if limiting literal tokens is found first.
Definition: recover.hpp:112
lexyd::_find::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:70
base.hpp
lexyd::_find::p
Definition: recover.hpp:67
lexy::parse_events::token
Definition: dsl/base.hpp:57
lexy::_detail::lit_trie_matcher
Definition: literal.hpp:240
lexy::parser_for
typename Rule::template p< NextParser > parser_for
Definition: dsl/base.hpp:113
lexyd::literal_set
constexpr auto literal_set(Literals...)
Matches one of the specified literals.
Definition: literal.hpp:594
lexyd::_tryr::bp::finish
LEXY_PARSER_FUNC bool finish(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:306
lexyd::_recovery_wrapper::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: recover.hpp:38
lexyd::_tryt::p
Definition: recover.hpp:262
lexy::parse_events::recovery_cancel
Definition: dsl/base.hpp:84
lexy::token_parser_for
typename TokenRule::template tp< Reader > token_parser_for
Definition: dsl/base.hpp:242
lexyd
Definition: trace.hpp:22
lexyd::_find
Definition: recover.hpp:64
lexyd::eof
constexpr auto eof
Matches EOF.
Definition: eof.hpp:72
LEXY_ASSERT
#define LEXY_ASSERT(Expr, Msg)
Definition: assert.hpp:37
lexyd::find
constexpr auto find(Literals... literals)
Recovers once it finds one of the given literal tokens (without consuming them).
Definition: recover.hpp:131
lexyd::_copy_base
decltype(_copy_base_impl< Rule >()) _copy_base
Definition: dsl/base.hpp:104
lexyd::_tryt::_pc
Definition: recover.hpp:221


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Nov 1 2024 02:20:51