list.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_LIST_HPP_INCLUDED
5 #define LEXY_DSL_LIST_HPP_INCLUDED
6 
7 #include <lexy/dsl/base.hpp>
8 #include <lexy/dsl/choice.hpp>
9 #include <lexy/dsl/option.hpp>
10 #include <lexy/dsl/separator.hpp>
11 
12 namespace lexyd
13 {
14 template <typename Item, typename Sep>
15 struct _lst : _copy_base<Item>
16 {
17  template <typename Context, typename Reader, typename Sink>
18  LEXY_PARSER_FUNC static bool _loop(Context& context, Reader& reader, Sink& sink)
19  {
20  while (true)
21  {
22  // Parse a separator if necessary.
23  [[maybe_unused]] auto sep_begin = reader.position();
24  if constexpr (!std::is_void_v<Sep>)
25  {
27  if (!sep.try_parse(context.control_block, reader))
28  {
29  // We didn't have a separator, list is definitely finished.
30  sep.cancel(context);
31  break;
32  }
33 
34  if (!sep.template finish<lexy::sink_parser>(context, reader, sink))
35  return false;
36  }
37  [[maybe_unused]] auto sep_end = reader.position();
38 
39  // Parse the next item.
40  if constexpr (lexy::is_branch_rule<Item>)
41  {
42  // It's a branch, so try parsing it to detect loop exit.
44  if (!item.try_parse(context.control_block, reader))
45  {
46  // We don't have a next item, exit the loop.
47  // If necessary, we report a trailing separator.
48  item.cancel(context);
49  if constexpr (!std::is_void_v<Sep>)
50  Sep::report_trailing_error(context, reader, sep_begin, sep_end);
51  break;
52  }
53 
54  // We're having an item, finish it.
55  if (!item.template finish<lexy::sink_parser>(context, reader, sink))
56  return false;
57  }
58  else
59  {
60  // Not a branch, so we need one item.
61  if (!lexy::parser_for<Item, lexy::sink_parser>::parse(context, reader, sink))
62  return false;
63  }
64  }
65 
66  return true;
67  }
68 
69  template <typename NextParser>
70  struct p
71  {
72  template <typename Context, typename Reader, typename... Args>
73  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
74  {
75  // Construct the sink.
76  auto sink = context.value_callback().sink();
77 
78  // Parse the first item.
79  if (!lexy::parser_for<Item, lexy::sink_parser>::parse(context, reader, sink))
80  return false;
81 
82  // Parse the remaining items.
83  if (!_loop(context, reader, sink))
84  return false;
85 
86  // We're done with the list, finish the sink and continue.
87  return lexy::sink_finish_parser<NextParser>::parse(context, reader, sink,
88  LEXY_FWD(args)...);
89  }
90  };
91 
92  template <typename Reader>
93  struct bp
94  {
96 
97  template <typename ControlBlock>
98  constexpr bool try_parse(const ControlBlock* cb, const Reader& reader)
99  {
100  // We parse a list if we can parse its first item.
101  return item.try_parse(cb, reader);
102  }
103 
104  template <typename Context>
105  constexpr void cancel(Context& context)
106  {
107  return item.cancel(context);
108  }
109 
110  template <typename NextParser, typename Context, typename... Args>
111  LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args)
112  {
113  // At this point, we have a list so construct a sink.
114  auto sink = context.value_callback().sink();
115 
116  // Finish the first item, passing all values to the sink.
117  if (!item.template finish<lexy::sink_parser>(context, reader, sink))
118  return false;
119 
120  // Parse the remaining items.
121  if (!_loop(context, reader, sink))
122  return false;
123 
124  // We're done with the list, finish the sink and continue.
125  return lexy::sink_finish_parser<NextParser>::parse(context, reader, sink,
126  LEXY_FWD(args)...);
127  }
128  };
129 };
130 
132 template <typename Item>
133 constexpr auto list(Item)
134 {
135  LEXY_REQUIRE_BRANCH_RULE(Item, "list() without a separator");
136  return _lst<Item, void>{};
137 }
138 
140 template <typename Item, typename Sep, typename Tag>
141 constexpr auto list(Item, _sep<Sep, Tag>)
142 {
143  return _lst<Item, _sep<Sep, Tag>>{};
144 }
145 
147 template <typename Item, typename Sep>
148 constexpr auto list(Item, _tsep<Sep>)
149 {
150  LEXY_REQUIRE_BRANCH_RULE(Item, "list() with a trailing separator");
151  return _lst<Item, _tsep<Sep>>{};
152 }
153 
154 template <typename Item, typename Sep>
155 constexpr auto list(Item, _isep<Sep>)
156 {
157  static_assert(lexy::_detail::error<Item, Sep>,
158  "list() does not support `dsl::ignore_trailing_sep()`");
159  return _lst<Item, void>{};
160 }
161 } // namespace lexyd
162 
163 namespace lexyd
164 {
165 template <typename Term, typename Item, typename Sep, typename Recover>
166 struct _lstt : rule_base
167 {
168  // We're using an enum together with a switch to compensate a lack of goto in constexpr.
169  // The simple state machine goes as follows on well-formed input:
170  // terminator -> separator -> separator_trailing_check -> item -> terminator -> ... ->
171  // done
172  //
173  // The interesting case is error recovery.
174  // There we skip over characters until we either found the terminator, separator or
175  // item. We then set the enum to jump to the appropriate state of the state machine.
176  enum class _state
177  {
178  terminator,
179  separator,
181  item,
182  recovery,
183  };
184 
185  template <typename TermParser, typename Context, typename Reader, typename Sink>
186  LEXY_PARSER_FUNC static bool _loop(_state initial_state, TermParser& term, Context& context,
187  Reader& reader, Sink& sink)
188  {
189  auto state = initial_state;
190 
191  [[maybe_unused]] auto sep_pos = reader.position();
192  while (true)
193  {
194  switch (state)
195  {
196  case _state::terminator:
197  if (term.try_parse(context.control_block, reader))
198  // We had the terminator, so the list is done.
199  return true;
200  term.cancel(context);
201 
202  // Parse the following list separator next.
203  state = _state::separator;
204  break;
205 
206  case _state::separator:
207  if constexpr (!std::is_void_v<Sep>)
208  {
209  sep_pos = reader.position();
211  reader,
212  sink))
213  {
214  // Check for a trailing separator next.
216  break;
217  }
218  else if (sep_pos == reader.position())
219  {
220  // We don't have a separator at all.
221  // Assume it's missing and parse an item instead.
222 
223  if constexpr (lexy::is_branch_rule<Item>)
224  {
226  if (item.try_parse(context.control_block, reader)
227  && item.template finish<lexy::sink_parser>(context, reader, sink))
228  {
229  // Continue after an item has been parsed.
230  state = _state::terminator;
231  break;
232  }
233  else
234  {
235  // Not an item, recover.
236  item.cancel(context);
237  state = _state::recovery;
238  break;
239  }
240  }
241  else
242  {
243  // We cannot try and parse an item.
244  // To avoid generating wrong errors, immediately recover.
245  state = _state::recovery;
246  break;
247  }
248  }
249  else
250  {
251  // We did have something that looked like a separator initially, but
252  // wasn't one on closer inspection. Enter generic recovery as we've
253  // already consumed input. (If we ignore the case where the item and
254  // separator share a common prefix, we know it wasn't the start of an
255  // item so can't just pretend that there is one).
256  state = _state::recovery;
257  break;
258  }
259  }
260  else
261  {
262  // List doesn't have a separator; immediately parse item next.
263  state = _state::item;
264  break;
265  }
266 
268  if constexpr (!std::is_void_v<Sep>)
269  {
270  // We need to check whether we're having a trailing separator by checking
271  // for a terminating one.
272  if (term.try_parse(context.control_block, reader))
273  {
274  // We had the terminator, so the list is done.
275  // Report a trailing separator error if necessary.
276  Sep::report_trailing_error(context, reader, sep_pos, reader.position());
277  return true;
278  }
279  else
280  {
281  // We didn't have a separator, parse item next.
282  state = _state::item;
283  break;
284  }
285  }
286  break;
287 
288  case _state::item:
289  if (lexy::parser_for<Item, lexy::sink_parser>::parse(context, reader, sink))
290  {
291  // Loop back.
292  state = _state::terminator;
293  break;
294  }
295  else
296  {
297  // Recover from missing item.
298  state = _state::recovery;
299  break;
300  }
301 
302  case _state::recovery: {
303  auto recovery_begin = reader.position();
304  context.on(_ev::recovery_start{}, recovery_begin);
305  while (true)
306  {
307  // Recovery succeeds when we reach the next separator.
308  if constexpr (!std::is_void_v<Sep>)
309  {
310  sep_pos = reader.position();
311 
313  if (sep.try_parse(context.control_block, reader))
314  {
315  auto recovery_end = reader.position();
316  context.on(_ev::token{}, lexy::error_token_kind, recovery_begin,
317  recovery_end);
318  context.on(_ev::recovery_finish{}, recovery_end);
319 
320  if (sep.template finish<lexy::sink_parser>(context, reader, sink))
321  {
322  // Continue the list with the trailing separator check.
324  break;
325  }
326  else
327  {
328  // Need to recover from this.
329  state = _state::recovery;
330  break;
331  }
332  }
333  else
334  {
335  sep.cancel(context);
336  }
337  }
338  // When we don't have a separator, but the item is a branch, we also succeed
339  // when we reach the next item.
340  //
341  // Note that we're doing this check only if we don't have a separator.
342  // If we do have one, the heuristic "end of the invalid item" is better than
343  // "beginning of the next one".
344  else if constexpr (lexy::is_branch_rule<Item>)
345  {
347  if (item.try_parse(context.control_block, reader))
348  {
349  auto recovery_end = reader.position();
350  context.on(_ev::token{}, lexy::error_token_kind, recovery_begin,
351  recovery_end);
352  context.on(_ev::recovery_finish{}, recovery_end);
353 
354  if (item.template finish<lexy::sink_parser>(context, reader, sink))
355  {
356  // Continue the list with the next terminator check.
357  state = _state::terminator;
358  break;
359  }
360  else
361  {
362  // Need to recover from this.
363  state = _state::recovery;
364  break;
365  }
366  }
367  else
368  {
369  item.cancel(context);
370  }
371  }
372 
373  // At this point, we couldn't detect the next item.
374  // Recovery succeeds when we reach the terminator.
375  if (term.try_parse(context.control_block, reader))
376  {
377  // We're now done with the entire list.
378  auto recovery_end = reader.position();
379  context.on(_ev::token{}, lexy::error_token_kind, recovery_begin,
380  recovery_end);
381  context.on(_ev::recovery_finish{}, recovery_end);
382  return true;
383  }
384  else
385  {
386  term.cancel(context);
387  }
388 
389  // At this point, we couldn't detect the next item or a terminator.
390  // Recovery fails when we reach the limit.
391  using limit_rule = decltype(Recover{}.get_limit());
393  limit.try_parse(reader) || reader.peek() == Reader::encoding::eof())
394  {
395  // Recovery has failed, propagate error.
396  auto recovery_end = reader.position();
397  context.on(_ev::token{}, lexy::error_token_kind, recovery_begin,
398  recovery_end);
399  context.on(_ev::recovery_cancel{}, recovery_end);
400  return false;
401  }
402 
403  // Consume one code unit and try again.
404  reader.bump();
405  }
406  break;
407  }
408  }
409  }
410 
411  return false; // unreachable
412  }
413 
414  template <typename NextParser>
415  struct p
416  {
417  template <typename Context, typename Reader, typename... Args>
418  LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
419  {
421  auto sink = context.value_callback().sink();
422 
423  // Parse initial item.
424  using item_parser = lexy::parser_for<Item, lexy::sink_parser>;
425  auto result = item_parser::parse(context, reader, sink);
426 
427  // Parse the remaining items.
428  if (!_loop(result ? _state::terminator : _state::recovery, term, context, reader, sink))
429  return false;
430 
431  // At this point, we just need to finish parsing the terminator.
432  if constexpr (std::is_same_v<typename decltype(sink)::return_type, void>)
433  {
434  LEXY_MOV(sink).finish();
435  return term.template finish<NextParser>(context, reader, LEXY_FWD(args)...);
436  }
437  else
438  {
439  return term.template finish<NextParser>(context, reader, LEXY_FWD(args)...,
440  LEXY_MOV(sink).finish());
441  }
442  }
443  };
444 };
445 } // namespace lexyd
446 
447 #endif // LEXY_DSL_LIST_HPP_INCLUDED
448 
choice.hpp
LEXY_MOV
#define LEXY_MOV(...)
Definition: config.hpp:29
lexyd::_lst::bp::finish
LEXY_PARSER_FUNC bool finish(Context &context, Reader &reader, Args &&... args)
Definition: list.hpp:111
lexy::parse_events::recovery_start
Definition: dsl/base.hpp:74
lexyd::_lstt::_state
_state
Definition: list.hpp:176
lexyd::sep
constexpr auto sep(Branch)
Defines a separator for a list.
Definition: separator.hpp:91
lexyd::_lstt::_state::separator_trailing_check
@ separator_trailing_check
lexyd::_lst::bp
Definition: list.hpp:93
lexy::branch_parser_for
typename BranchRule::template bp< Reader > branch_parser_for
Definition: dsl/base.hpp:116
lexyd::_tsep
Definition: separator.hpp:98
lexyd::_lst::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: list.hpp:73
LEXY_FWD
#define LEXY_FWD(...)
Definition: config.hpp:30
lexyd::_lst
Definition: list.hpp:15
lexyd::_lstt::_state::terminator
@ terminator
lexyd::list
constexpr auto list(Item)
Parses a list of items without a separator.
Definition: list.hpp:133
lexyd::_lstt::p
Definition: list.hpp:415
lexyd::_lst::bp::cancel
constexpr void cancel(Context &context)
Definition: list.hpp:105
LEXY_REQUIRE_BRANCH_RULE
#define LEXY_REQUIRE_BRANCH_RULE(Rule, Name)
Definition: grammar.hpp:73
separator.hpp
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::rule_base
Definition: grammar.hpp:17
lexy::error_token_kind
@ error_token_kind
Definition: grammar.hpp:86
lexyd::_lstt::_state::item
@ item
lexy::parse_events::recovery_finish
Definition: dsl/base.hpp:79
lexyd::_lstt::_loop
static LEXY_PARSER_FUNC bool _loop(_state initial_state, TermParser &term, Context &context, Reader &reader, Sink &sink)
Definition: list.hpp:186
LEXY_PARSER_FUNC
#define LEXY_PARSER_FUNC
Definition: dsl/base.hpp:108
lexyd::_lst::p
Definition: list.hpp:70
lexyd::_sep
Definition: separator.hpp:69
lexyd::_lstt::p::parse
static LEXY_PARSER_FUNC bool parse(Context &context, Reader &reader, Args &&... args)
Definition: list.hpp:418
lexyd::_lstt
Definition: list.hpp:166
lexyd::_lstt::_state::separator
@ separator
base.hpp
lexy::parse_events::token
Definition: dsl/base.hpp:57
lexy::sink_finish_parser::parse
static LEXY_PARSER_FUNC auto parse(Context &context, Reader &reader, Sink &sink, Args &&... args)
Definition: dsl/base.hpp:202
lexyd::_lstt::_state::recovery
@ recovery
lexyd::_lst::_loop
static LEXY_PARSER_FUNC bool _loop(Context &context, Reader &reader, Sink &sink)
Definition: list.hpp:18
lexy::parser_for
typename Rule::template p< NextParser > parser_for
Definition: dsl/base.hpp:113
lexyd::_lst::bp::item
lexy::branch_parser_for< Item, Reader > item
Definition: list.hpp:95
lexyd::_isep
Definition: separator.hpp:118
lexy::parse_events::recovery_cancel
Definition: dsl/base.hpp:84
lexyd::_lst::bp::try_parse
constexpr bool try_parse(const ControlBlock *cb, const Reader &reader)
Definition: list.hpp:98
lexy::token_parser_for
typename TokenRule::template tp< Reader > token_parser_for
Definition: dsl/base.hpp:242
lexyd
Definition: trace.hpp:22
lexyd::eof
constexpr auto eof
Matches EOF.
Definition: eof.hpp:72
option.hpp
lexyd::_copy_base
decltype(_copy_base_impl< Rule >()) _copy_base
Definition: dsl/base.hpp:104


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