mimic++ v10.0.1
Loading...
Searching...
No Matches
Parser.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_PRINTING_TYPE_PARSER_HPP
7#define MIMICPP_PRINTING_TYPE_PARSER_HPP
8
9#pragma once
10
17
18#ifndef MIMICPP_DETAIL_IS_MODULE
19 #include <algorithm>
20 #include <optional>
21 #include <span>
22 #include <utility>
23 #include <variant>
24#endif
25
27{
28 class Transaction;
29
31 {
32 public:
33 [[nodiscard]]
35 {
36 while (!std::holds_alternative<lexing::end>(lexer.peek().classification))
37 {
38 if (auto token = lexer.next();
39 !std::holds_alternative<lexing::space>(token.classification))
40 {
41 m_Tokens.emplace_back(std::move(token));
42 }
43 }
44
45 m_Tokens.emplace_back(lexer.next());
46 }
47
48 [[nodiscard]]
49 constexpr bool is_eof() const noexcept
50 {
51 return m_Index == std::ranges::size(m_Tokens) - 1u;
52 }
53
54 [[nodiscard]]
55 constexpr lexing::token const& peek() const
56 {
57 MIMICPP_ASSERT(m_Index < std::ranges::size(m_Tokens), "Stream is at end.");
58 return std::span{m_Tokens}[m_Index];
59 }
60
61 constexpr void consume()
62 {
63 MIMICPP_ASSERT(!is_eof(), "EOF cannot be consumed.");
64 ++m_Index;
65 }
66
67 [[nodiscard]]
68 constexpr std::size_t pos() const noexcept
69 {
70 return m_Index;
71 }
72
73 constexpr void seek(util::pass_key<Transaction> const /*key*/, std::size_t const pos)
74 {
75 m_Index = pos;
76 }
77
78 private:
79 std::vector<lexing::token> m_Tokens;
80 std::size_t m_Index{};
81 };
82
84 {
85 public:
86 Transaction(Transaction const&) = delete;
90
91 constexpr ~Transaction() noexcept
92 {
93 if (is_active())
94 {
95 m_Stream->seek(util::pass_key<Transaction>{}, m_Checkpoint);
96 }
97 }
98
99 [[nodiscard]]
100 explicit constexpr Transaction(TokenStream& stream) noexcept
101 : m_Stream{std::addressof(stream)},
102 m_Checkpoint{stream.pos()}
103 {
104 }
105
106 constexpr void commit() noexcept
107 {
108 m_Stream = nullptr;
109 }
110
111 [[nodiscard]]
112 constexpr bool is_active() const noexcept
113 {
114 return m_Stream != nullptr;
115 }
116
117 private:
118 TokenStream* m_Stream;
119 std::size_t m_Checkpoint;
120 };
121
122 template <typename State>
124 {
125 public:
126 StateGuard(StateGuard const&) = delete;
127 StateGuard& operator=(StateGuard const&) = delete;
130
131 ~StateGuard() = default;
132
133 template <typename... Args>
134 requires std::constructible_from<State, Args...>
135 [[nodiscard]]
136 explicit constexpr StateGuard(TokenStream& stream, Args&&... args)
137 : m_Transaction{stream},
138 m_State{std::in_place, std::forward<Args>(args)...}
139 {
140 }
141
142 [[nodiscard]]
143 constexpr State& operator*() noexcept
144 {
145 MIMICPP_ASSERT(m_State, "State was already consumed.");
146 return *m_State;
147 }
148
149 [[nodiscard]]
150 constexpr State* operator->() noexcept
151 {
152 MIMICPP_ASSERT(m_State, "State was already consumed.");
153 return &*m_State;
154 }
155
156 [[nodiscard]]
157 constexpr std::optional<State> take() &&
158 {
159 MIMICPP_ASSERT(m_State, "State was already consumed.");
160 m_Transaction.commit();
161 return std::exchange(m_State, std::nullopt);
162 }
163
164 private:
165 Transaction m_Transaction;
166 std::optional<State> m_State;
167 };
168
169 template <std::equality_comparable Key, typename Value, std::size_t length>
170 [[nodiscard]]
171 constexpr auto make_map(std::pair<Key, Value> const (&mappings)[length])
172 {
173 class Map
174 {
175 public:
176 [[nodiscard]]
177 explicit constexpr Map(std::pair<Key, Value> const (&candidates)[length])
178 : m_Candidates{std::to_array(candidates)}
179 {
180 }
181
182 [[nodiscard]]
183 constexpr std::optional<Value> operator()(Key const& key) const
184 {
185 if (auto const iter = std::ranges::find(m_Candidates, key, &std::pair<Key, Value>::first);
186 iter != std::ranges::end(m_Candidates))
187 {
188 return iter->second;
189 }
190
191 return std::nullopt;
192 }
193
194 private:
195 std::array<std::pair<Key, Value>, length> m_Candidates;
196 };
197
198 return Map{mappings};
199 }
200
201 template <typename LexerTokenClass>
202 [[nodiscard]]
203 constexpr LexerTokenClass const* peek_if(TokenStream const& stream) noexcept
204 {
205 return std::get_if<LexerTokenClass>(&stream.peek().classification);
206 }
207
208 template <std::equality_comparable LexerTokenClass>
209 [[nodiscard]]
210 constexpr std::optional<LexerTokenClass> expect(TokenStream& stream, LexerTokenClass const& expected)
211 {
212 if (auto const* const token = peek_if<LexerTokenClass>(stream);
213 token
214 && *token == expected)
215 {
216 std::optional result = *token;
217 stream.consume();
218
219 return result;
220 }
221
222 return std::nullopt;
223 }
224
225 [[nodiscard]]
226 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::FunctionDeclarator> parse_parameters_and_qualifiers(TokenStream& stream);
227
228 [[nodiscard]]
229 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::TypeId> parse_type_specifier_seq(TokenStream& stream);
230
231 [[nodiscard]]
232 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::PtrOperator> parse_ptr_operator(TokenStream& stream);
233
234 [[nodiscard]]
235 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::TypeId> parse_type_id(TokenStream& stream);
236
237 // see: https://eel.is/c++draft/class.pre#nt:class-key
238 [[nodiscard]]
239 constexpr std::optional<state::ClassKey> parse_class_key(TokenStream& stream)
240 {
241 if (auto const* const keyword = peek_if<lexing::keyword>(stream))
242 {
243 constexpr auto map = make_map<lexing::keyword, state::ClassKey>({
246 // Todo: {lexing::keyword{"union"}, tokens::class_key::id_union}
247 });
248
249 if (std::optional const classKey = map(*keyword))
250 {
251 stream.consume();
252 return classKey;
253 }
254 }
255
256 return std::nullopt;
257 }
258
259 // see: https://eel.is/c++draft/dcl.enum#nt:enum-key
260 [[nodiscard]]
261 constexpr std::optional<state::EnumKey> parse_enum_key(TokenStream& stream)
262 {
263 if (expect(stream, lexing::keyword{"enum"}))
264 {
265 if (expect(stream, lexing::keyword{"class"}))
266 {
268 }
269
270 if (expect(stream, lexing::keyword{"struct"}))
271 {
273 }
274
276 }
277
278 return std::nullopt;
279 }
280
281 [[nodiscard]]
282 constexpr std::optional<state::TypeKey> parse_type_key(TokenStream& stream)
283 {
284 if (auto const key = parse_class_key(stream))
285 {
286 return {*key};
287 }
288
289 if (auto const key = parse_enum_key(stream))
290 {
291 return {*key};
292 }
293
294 return std::nullopt;
295 }
296
297 // see: https://eel.is/c++draft/dcl.decl.general#nt:ref-qualifier
298 [[nodiscard]]
299 constexpr std::optional<state::RefQualifier> parse_ref_qualifier(TokenStream& stream)
300 {
301 if (auto const* const op = peek_if<lexing::operator_or_punctuator>(stream))
302 {
306 });
307
308 if (std::optional const qualifier = map(*op))
309 {
310 stream.consume();
311 return qualifier;
312 }
313 }
314
315 return std::nullopt;
316 }
317
318 // Constant-expressions are actually a very deeply nested set of rules,
319 // which more or less boils down to a `primary-expression` for this kind of task.
320 // see: https://eel.is/c++draft/expr.const.general#nt:constant-expression
321 // see: https://eel.is/c++draft/expr.prim.grammar#nt:primary-expression
322 // > constant-expression ::= primary-expression
323 // > primary-expression ::= literal
324 [[nodiscard]]
325 constexpr std::optional<state::ConstantExpression> parse_constant_expression(TokenStream& stream)
326 {
327 if (auto const* const literal = peek_if<lexing::literal>(stream))
328 {
329 stream.consume();
330 return {*literal};
331 }
332
333 return std::nullopt;
334 }
335
336 namespace detail
337 {
338 struct PlaceholderWrapCandidate
339 {
342 };
343
344 [[nodiscard]]
345 consteval auto make_placeholder_wrap_candidates() noexcept
346 {
347 using op = lexing::operator_or_punctuator;
348 std::array raw = {
349 PlaceholderWrapCandidate{.open = op{"{"}, .close = op{"}"}},
350 PlaceholderWrapCandidate{.open = op{"<"}, .close = op{">"}},
351 PlaceholderWrapCandidate{.open = op{"("}, .close = op{")"}},
352 PlaceholderWrapCandidate{.open = op{"`"}, .close = op{"'"}},
353 };
354
355 constexpr auto projection = [](auto const& e) { return e.open.index(); };
356 std::ranges::sort(raw, {}, projection);
357 MIMICPP_ASSERT(raw.cend() == std::ranges::unique(raw, {}, projection).begin(), "Fix your input!");
358
359 return raw;
360 }
361
362 inline constexpr std::array placeholderWrapCandidates = make_placeholder_wrap_candidates();
363
364 [[nodiscard]]
365 constexpr char const* find_end_token(TokenStream& stream, lexing::operator_or_punctuator const open, lexing::operator_or_punctuator const close)
366 {
367 while (!stream.is_eof())
368 {
369 if (auto const* const cur = std::get_if<lexing::operator_or_punctuator>(&stream.peek().classification))
370 {
371 if (open == *cur)
372 {
373 stream.consume();
374 std::ignore = find_end_token(stream, open, close);
375 continue;
376 }
377 else if (close == *cur)
378 {
379 auto const* const end = stream.peek().content.data() + stream.peek().content.size();
380 stream.consume();
381
382 return end;
383 }
384 }
385
386 // silently skip anything between open and close token.
387 stream.consume();
388 }
389
390 return nullptr;
391 }
392 }
393
394 // This is not directly reflected in the standard, but each ecosystem has their own specific kind of representing
395 // e.g. anonymous types and namespaces, or lambdas.
396 [[nodiscard]]
397 constexpr std::optional<state::Identifier> parse_synthetic_id(TokenStream& stream)
398 {
400 id->isSynthetic = true;
401
402 if (!std::holds_alternative<lexing::operator_or_punctuator>(stream.peek().classification))
403 {
404 return std::nullopt;
405 }
406
407 auto const begin = stream.peek();
408 auto const iter = util::binary_find(
409 detail::placeholderWrapCandidates,
410 std::get<lexing::operator_or_punctuator>(begin.classification).index(),
411 {},
412 [](auto const& e) { return e.open.index(); });
413 if (iter == detail::placeholderWrapCandidates.cend())
414 {
415 return std::nullopt;
416 }
417
418 stream.consume();
419
420 // This recursively searches for a matching `close` token, while handling nested `open close` token-ranges.
421 if (auto const* const end = detail::find_end_token(stream, iter->open, iter->close))
422 {
423 id->content = {begin.content.data(), end};
424 return std::move(id).take();
425 }
426
427 return std::nullopt;
428 }
429
430 // > identifier ::= lexing-id
431 // > identifier ::= synthetic-id
432 [[nodiscard]]
433 constexpr std::optional<state::Identifier> parse_identifier(TokenStream& stream)
434 {
435 if (auto const* const id = peek_if<lexing::identifier>(stream))
436 {
437 state::Identifier result{.content = id->content};
438 stream.consume();
439 return result;
440 }
441
442 return parse_synthetic_id(stream);
443 }
444
445 // see: https://eel.is/c++draft/temp.names#nt:template-argument
446 [[nodiscard]]
447 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::TemplateArgument> parse_template_argument(TokenStream& stream)
448 {
449 if (std::optional type = parse_type_id(stream))
450 {
451 return {state::Recursive{*std::move(type)}};
452 }
453 /*
454 * template-argument:
455 template-argument-name
456 constant-expression
457 type-id
458 braced-init-list
459 */
460 return std::nullopt;
461 }
462
463 // see: https://eel.is/c++draft/temp.names#nt:template-argument-list
464 [[nodiscard]]
465 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::TemplateArgumentList> parse_template_argument_list(TokenStream& stream)
466 {
467 std::optional first = parse_template_argument(stream);
468 if (!first)
469 {
470 return std::nullopt;
471 }
472
473 state::TemplateArgumentList args{*std::move(first)};
474 for (auto const* delimiter = peek_if<lexing::operator_or_punctuator>(stream);
475 delimiter && lexing::operator_or_punctuator{","} == *delimiter;
476 delimiter = peek_if<lexing::operator_or_punctuator>(stream))
477 {
478 Transaction transaction{stream};
479 stream.consume();
480
481 std::optional arg = parse_template_argument(stream);
482 if (!arg)
483 {
484 break;
485 }
486
487 args.emplace_back(*std::move(arg));
488 transaction.commit();
489 }
490
491 return args;
492 }
493
494 // This is a custom rule, which parses enclosed template-args, but without a preceding identifier.
495 // > template-arg-list ::= `<` `>`
496 // > template-arg-list ::= `<` template-argument (`,` template-argument)* `>`
497 [[nodiscard]]
498 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::TemplateArgumentList> parse_template_clause(TokenStream& stream)
499 {
501
502 if (!expect(stream, lexing::operator_or_punctuator{"<"}))
503 {
504 return std::nullopt;
505 }
506
507 if (std::optional argList = parse_template_argument_list(stream))
508 {
509 *args = *std::move(argList);
510 }
511
512 if (!expect(stream, lexing::operator_or_punctuator{">"}))
513 {
514 return std::nullopt;
515 }
516
517 return std::move(args).take();
518 }
519
520 // see: https://eel.is/c++draft/dcl.decl.general#nt:cv-qualifier
521 [[nodiscard]]
522 constexpr std::optional<state::CVQualifier> parse_cv_qualifier(TokenStream& stream)
523 {
524 if (auto const* const keyword = peek_if<lexing::keyword>(stream))
525 {
529 });
530
531 if (std::optional const qualifier = map(*keyword))
532 {
533 stream.consume();
534 return qualifier;
535 }
536 }
537
538 return std::nullopt;
539 }
540
541 // see: https://eel.is/c++draft/dcl.decl.general#nt:cv-qualifier-seq
542 // This is a simplified version of the general grammar because there are in fact just two possible qualifications.
543 [[nodiscard]]
545 {
546 bool isSet{false};
547 state::CVQualifierSeq qualifiers{};
548 while (std::optional const qualifier = parse_cv_qualifier(stream))
549 {
550 isSet = true;
551 switch (*qualifier)
552 {
554 MIMICPP_ASSERT(!qualifiers.isConst, "`const` is already applied.");
555 qualifiers.isConst = true;
556 break;
557
559 MIMICPP_ASSERT(!qualifiers.isVolatile, "`volatile` is already applied.");
560 qualifiers.isVolatile = true;
561 break;
562
563 default:
565 }
566 }
567
568 if (isSet)
569 {
570 return {qualifiers};
571 }
572
573 return std::nullopt;
574 }
575
576 namespace detail
577 {
578 [[nodiscard]]
579 consteval auto make_simple_operator_candidates() noexcept
580 {
581 std::array texts = util::concat_arrays(
588 std::to_array<std::string_view>({"->", "->*", ","}));
589
590 std::array ops = std::apply(
591 [](auto&... opTexts) { return std::array{lexing::operator_or_punctuator{opTexts}...}; },
592 texts);
593 std::ranges::sort(ops, {}, &lexing::operator_or_punctuator::index);
594 MIMICPP_ASSERT(ops.cend() == std::ranges::unique(ops).begin(), "Fix your input!");
595
596 return ops;
597 }
598
599 inline constexpr std::array simpleOpCandidates = make_simple_operator_candidates();
600
601 inline constexpr std::array doubleOpCandidates = {
602 std::tuple{lexing::operator_or_punctuator{"("}, lexing::operator_or_punctuator{")"}},
603 std::tuple{lexing::operator_or_punctuator{"["}, lexing::operator_or_punctuator{"]"}}
604 };
605 }
606
607 // unqualified-id ::= `operator` op
608 // see:: https://eel.is/c++draft/over.oper.general#nt:operator
609 template <bool requireOperatorKeyword = true>
610 [[nodiscard]]
611 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::OperatorFunctionId> parse_operator_function_id(TokenStream& stream)
612 {
613 Transaction transaction{stream};
614
615 if (!expect(stream, lexing::keyword{"operator"})
616 && requireOperatorKeyword)
617 {
618 return std::nullopt;
619 }
620
621 if (auto const* const op = peek_if<lexing::operator_or_punctuator>(stream))
622 {
623 if (std::ranges::binary_search(detail::simpleOpCandidates, op->index(), {}, &lexing::operator_or_punctuator::index))
624 {
625 // This two-step construction resolves a bogus "maybe-uninitialized" warning on certain gcc versions.
627 state::OperatorFunctionId id{.symbol = std::move(symbol)};
628 stream.consume();
629 transaction.commit();
630 return id;
631 }
632
633 for (auto&& [first, second] : detail::doubleOpCandidates)
634 {
635 if (expect(stream, first)
636 && expect(stream, second))
637 {
639 .symbol = std::array{first, second}
640 };
641 transaction.commit();
642 return id;
643 }
644 }
645
646 return std::nullopt;
647 }
648
649 if (auto const* const op = peek_if<lexing::keyword>(stream))
650 {
651 if (lexing::keyword{"new"} == *op
652 || lexing::keyword{"delete"} == *op)
653 {
654 std::pair symbol{*op, false};
655 stream.consume();
656 transaction.commit();
657
658 if (Transaction inner{stream};
660 && expect(stream, lexing::operator_or_punctuator{"]"}))
661 {
662 inner.commit();
663 symbol.second = true;
664 }
665
666 return state::OperatorFunctionId{.symbol = symbol};
667 }
668
669 if (lexing::keyword{"co_await"} == *op)
670 {
672 .symbol = std::pair{*op, false}
673 };
674 stream.consume();
675 transaction.commit();
676 return id;
677 }
678
679 return std::nullopt;
680 }
681
682 return std::nullopt;
683 }
684
685 // > conversion-function-id ::= `operator` conversion-type-id
686 // > conversion-type-id ::= type-specifier-seq ptr-operator*
687 // see: https://eel.is/c++draft/class.conv.fct#nt:conversion-function-id
688 [[nodiscard]]
689 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::ConversionFunctionId> parse_conversion_function_id(TokenStream& stream)
690 {
691 StateGuard<state::TypeId> target{stream};
692
693 if (!expect(stream, lexing::keyword{"operator"}))
694 {
695 return std::nullopt;
696 }
697
698 std::optional base = parse_type_specifier_seq(stream);
699 if (!base)
700 {
701 return std::nullopt;
702 }
703
704 *target = *std::move(base);
705 while (std::optional op = parse_ptr_operator(stream))
706 {
707 target->declarator.root.decorations.emplace_back(*std::move(op));
708 }
709
711 .target = state::Recursive{*std::move(target).take()}};
712 }
713
714 [[nodiscard]]
715 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::LambdaFunctionId> parse_lambda_function_id(TokenStream& stream)
716 {
717 StateGuard<state::LambdaFunctionId> identifier{stream};
718
719 if (!std::holds_alternative<lexing::operator_or_punctuator>(stream.peek().classification))
720 {
721 return std::nullopt;
722 }
723
724 auto const closeOp = std::invoke([&]() -> std::optional<lexing::operator_or_punctuator> {
725 if (lexing::operator_or_punctuator{"<"} == std::get<lexing::operator_or_punctuator>(stream.peek().classification))
726 {
728 }
729
730 if (lexing::operator_or_punctuator{"("} == std::get<lexing::operator_or_punctuator>(stream.peek().classification))
731 {
733 }
734
735 return std::nullopt;
736 });
737 if (!closeOp)
738 {
739 return std::nullopt;
740 }
741
742 auto const openToken = stream.peek();
743 stream.consume();
744
745 if (auto const* const id = peek_if<lexing::identifier>(stream);
746 id
747 && id->content.starts_with("lambda"))
748 {
749 // This recursively searches for a matching `close` token, while handling nested `open close` token-ranges.
750 if (auto const* const end = detail::find_end_token(
751 stream,
752 std::get<lexing::operator_or_punctuator>(openToken.classification),
753 *closeOp))
754 {
755 identifier->content = {openToken.content.data(), end};
756 identifier->isMutable = expect(stream, lexing::keyword{"mutable"}).has_value();
757
758 return std::move(identifier).take();
759 }
760 }
761
762 return std::nullopt;
763 }
764
765 // This rule is part of the more general unqualified-id rule.
766 // see: https://eel.is/c++draft/expr.prim.id.unqual#nt:unqualified-id
767 [[nodiscard]]
768 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::DestructorFunctionId> parse_destructor_function_id(TokenStream& stream)
769 {
771
772 if (expect(stream, lexing::operator_or_punctuator{"~"}))
773 {
774 if (std::optional id = parse_identifier(stream))
775 {
776 identifier->name = *std::move(id);
777 return std::move(identifier).take();
778 }
779 }
780
781 return std::nullopt;
782 }
783
784 // see: https://eel.is/c++draft/expr.prim.id.unqual#nt:unqualified-id
785 // handles
786 // > unqualified-id ::= identifier
787 // > unqualified-id ::= template-id
788 // > unqualified-id ::= operator-function-id
789 // > unqualified-id ::= conversion-function-id
790 // > unqualified-id ::= lambda-function-id
791 // > unqualified-id ::= `~`type-name
792 // but slightly rewritten
793 //
794 // These rules are unnecessary:
795 // > unqualified-id ::= literal-operator-id
796 // > unqualified-id ::= `~`computed-type-specifier
797 [[nodiscard]]
798 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::UnqualifiedId> parse_unqualified_id(TokenStream& stream)
799 {
800 StateGuard<state::UnqualifiedId> nestedId{stream};
801
802 // A destructor cannot be templated
803 if (std::optional dtor = parse_destructor_function_id(stream))
804 {
805 nestedId->name = *std::move(dtor);
806 return std::move(nestedId).take();
807 }
808
809 // A synthetic lambda-id cannot be templated
810 if (std::optional lambda = parse_lambda_function_id(stream))
811 {
812 nestedId->name = *std::move(lambda);
813 return std::move(nestedId).take();
814 }
815
816 if (std::optional op = parse_operator_function_id(stream))
817 {
818 nestedId->name = *std::move(op);
819 }
820 // A conversion-function cannot be templated
821 else if (std::optional conv = parse_conversion_function_id(stream))
822 {
823 nestedId->name = *std::move(conv);
824 return std::move(nestedId).take();
825 }
826 else if (std::optional id = parse_identifier(stream))
827 {
828 nestedId->name = *std::move(id);
829 }
830 else
831 {
832 return std::nullopt;
833 }
834
835 if (std::optional templateArgs = parse_template_clause(stream))
836 {
837 nestedId->templateArgs = *std::move(templateArgs);
838 }
839
840 return std::move(nestedId).take();
841 }
842
843 // This is more or less a custom rule, which handles all nested identifier scopes;
844 // i.e., all parts that are terminated by a `::` token.
845 // > unqualified-id ::= identifier template-clause? parameters-and-qualifiers? `::`
846 // > unqualified-id ::= operator-function-id template-clause? parameters-and-qualifiers `::`
847 // > unqualified-id ::= conversion-function-id parameters-and-qualifiers `::`
848 // > unqualified-id ::= destructor-function-id parameters-and-qualifiers `::`
849 // > unqualified-id ::= lambda-id `::`
850 //
851 // Note that the additional optional `parameters-and-qualifiers` token is not reflected in the standard,
852 // but rather an extension due to real-life requirements.
853 template <bool isFirst>
854 [[nodiscard]]
855 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::UnqualifiedId> parse_nested_id(TokenStream& stream)
856 {
857 StateGuard<state::UnqualifiedId> nestedId{stream};
858
859 // A synthetic lambda-id can neither be templated nor be suffixed by an argument list
860 if (std::optional lambda = parse_lambda_function_id(stream))
861 {
862 nestedId->name = *std::move(lambda);
863 }
864 else
865 {
866 bool requiresFunction{false};
867 bool acceptTemplate{false};
868
869 // A destructor cannot be templated
870 if (std::optional dtor = parse_destructor_function_id(stream))
871 {
872 requiresFunction = true;
873 nestedId->name = *std::move(dtor);
874 }
875 else if (std::optional opId = std::invoke([&]() -> std::optional<state::OperatorFunctionId> {
876 Transaction innerTransaction{stream};
877 if (std::optional op = parse_operator_function_id<true>(stream))
878 {
879 acceptTemplate = true;
880 innerTransaction.commit();
881 return op;
882 }
883
884 if constexpr (!isFirst)
885 {
886 // In general, the `operator` keyword is mandatory, but sometimes it is omitted (e.g., on msvc).
887 // In this case, the whole scope is just the plain operator symbol; so no template- and function-details.
888 if (std::optional op = parse_operator_function_id<false>(stream);
889 op
890 // Peek here (and thus ensure that it is immediately followed by a `::` token),
891 // because we need to prevent false-positives. For example `<tag>::` must not be treated as `operator<`.
894 {
895 innerTransaction.commit();
896 return op;
897 }
898 }
899
900 return std::nullopt;
901 }))
902 {
903 nestedId->name = *std::move(opId);
904 }
905 // A conversion-function cannot be templated
906 else if (std::optional conv = parse_conversion_function_id(stream))
907 {
908 requiresFunction = true;
909 nestedId->name = *std::move(conv);
910 }
911 else if (std::optional id = parse_identifier(stream))
912 {
913 acceptTemplate = true;
914 nestedId->name = *std::move(id);
915 }
916 else
917 {
918 return std::nullopt;
919 }
920
921 if (acceptTemplate)
922 {
923 nestedId->templateArgs = parse_template_clause(stream);
924 }
925
926 if (std::optional functionDecl = parse_parameters_and_qualifiers(stream))
927 {
928 nestedId->functionDeclarator = *std::move(functionDecl);
929 }
930 else if (requiresFunction)
931 {
932 return std::nullopt;
933 }
934 }
935
936 if (expect(stream, lexing::operator_or_punctuator{"::"}))
937 {
938 return std::move(nestedId).take();
939 }
940
941 return std::nullopt;
942 }
943
944 // see: https://eel.is/c++draft/expr.prim.id.qual#nt:nested-name-specifier
945 // > nested-name-specifier ::= `::`
946 //
947 // These rules are not distinguishable in this task.
948 // > nested-name-specifier ::= type-name `::`
949 // > nested-name-specifier ::= namespace-name `::`
950 // => nested-name-specifier ::= nested-id
951 //
952 // These rules form a left-recursion:
953 // > nested-name-specifier ::= nested-name-specifier identifier `::`
954 // > nested-name-specifier ::= nested-name-specifier `template`? simple-template-id `::`
955 // => nested-name-specifier ::= `::` nested-id*
956 // => nested-name-specifier ::= nested-id+
957 //
958 // Note that these rules are fully ignored:
959 // > computed-type-specifier `::`
960 // > splice-scope-specifier `::`
961 [[nodiscard]]
963 {
965 scopes->explicitRoot = expect(stream, lexing::operator_or_punctuator{"::"}).has_value();
966
967 std::optional initId = parse_nested_id<true>(stream);
968 if (!initId)
969 {
970 if (scopes->explicitRoot)
971 {
972 return std::move(scopes).take();
973 }
974
975 return std::nullopt;
976 }
977
978 scopes->scopes.emplace_back(*std::move(initId));
979
980 while (!stream.is_eof())
981 {
982 Transaction transaction{stream};
983
984 std::optional curId = parse_nested_id<false>(stream);
985 if (!curId)
986 {
987 break;
988 }
989
990 scopes->scopes.emplace_back(*std::move(curId));
991 transaction.commit();
992 }
993
994 return std::move(scopes).take();
995 }
996
997 // see: https://eel.is/c++draft/expr.prim.id.qual#nt:qualified-id
998 // > qualified-id ::= nested-name-specifier? unqualified-id
999 [[nodiscard]]
1001 {
1003
1004 if (std::optional scopes = parse_nested_name_specifier(stream))
1005 {
1006 id->scopes = *std::move(scopes);
1007 }
1008
1009 if (std::optional topLevelId = parse_unqualified_id(stream))
1010 {
1011 id->identifier = *std::move(topLevelId);
1012 return std::move(id).take();
1013 }
1014
1015 return std::nullopt;
1016 }
1017
1018 [[nodiscard]]
1020 {
1021 // Accept symbols like `__cdecl`.
1022 if (auto const* const id = peek_if<lexing::identifier>(stream);
1023 id
1024 && id->content.starts_with("__"))
1025 {
1026 state::CallConvention convention{.name = *id};
1027 stream.consume();
1028 return convention;
1029 }
1030
1031 return std::nullopt;
1032 }
1033
1034 // see: https://eel.is/c++draft/dcl.decl.general#nt:ptr-operator
1035 // note: fully ignores `attribute-specifier-seq`
1036 [[nodiscard]]
1038 {
1039 // > ptr-operator ::= `&`
1040 // > ptr-operator ::= `&&`
1041 if (std::optional const ref = parse_ref_qualifier(stream))
1042 {
1043 return {state::ReferenceDeclarator{.qualifier = *ref}};
1044 }
1045
1046 // > ptr-operator ::= call-convention? nested-name-specifier? `*` cv-qualifier-seq?
1047 // Note that `call-convention` will only be generated by msvc.
1049 ptr->callConvention = parse_call_convention(stream);
1050 ptr->scopes = parse_nested_name_specifier(stream);
1051 if (!expect(stream, lexing::operator_or_punctuator{"*"}))
1052 {
1053 return std::nullopt;
1054 }
1055
1056 ptr->qualifiers = parse_cv_qualifier_seq(stream);
1057
1058 return {std::move(ptr).take()};
1059 }
1060
1061 // This rule is not directly reflected by the standard, but mirrors the reality more closely than the original
1062 // `noptr-abstract-declarator` non-terminal, which allows that `params-and-qualifiers` non-terminals can appear between
1063 // multiple `[ constant-expression ]` sequences.
1064 // see: https://eel.is/c++draft/dcl.name#nt:noptr-abstract-declarator
1065 [[nodiscard]]
1066 constexpr std::optional<state::ArrayDeclarator> parse_array_declarator(TokenStream& stream)
1067 {
1068 StateGuard<state::ArrayDeclarator> declarator{stream};
1069 if (expect(stream, lexing::operator_or_punctuator{"["}))
1070 {
1071 declarator->size = parse_constant_expression(stream);
1072 if (expect(stream, lexing::operator_or_punctuator{"]"}))
1073 {
1074 return std::move(declarator).take();
1075 }
1076 }
1077
1078 return std::nullopt;
1079 }
1080
1081 // see: https://eel.is/c++draft/dcl.fct#nt:parameter-declaration
1082 // `attribute-specifier-seq`, `decl-specifier-seq` and `this` are ignored.
1084 {
1085 // This is just a very naive approach; does this hold?
1086 return parse_type_id(stream);
1087 }
1088
1089 // see: https://eel.is/c++draft/dcl.fct#nt:parameter-declaration-clause
1090 // and https://eel.is/c++draft/dcl.fct#nt:parameter-declaration-list
1091 // The `...` is fully ignored.
1092 [[nodiscard]]
1094 {
1096
1097 do
1098 {
1099 std::optional param = parse_parameter_declaration(stream);
1100 if (!param)
1101 {
1102 // zero params are valid!
1103 if (params->empty())
1104 {
1105 break;
1106 }
1107
1108 return std::nullopt;
1109 }
1110
1111 params->emplace_back(*std::move(param));
1112 }
1113 while (expect(stream, lexing::operator_or_punctuator{","}));
1114
1115 return std::move(params).take();
1116 }
1117
1119 {
1120 // 32bit clang-cl adds something like `__attribute__((thiscall))` to certain functions.
1121 if (Transaction transaction{stream};
1122 expect(stream, lexing::identifier{"__attribute__"})
1123 && expect(stream, lexing::operator_or_punctuator{"("})
1124 && expect(stream, lexing::operator_or_punctuator{"("}))
1125 {
1127 && !peek_if<lexing::end>(stream))
1128 {
1129 stream.consume();
1130 }
1131
1132 if (expect(stream, lexing::operator_or_punctuator{")"})
1133 && expect(stream, lexing::operator_or_punctuator{")"}))
1134 {
1135 transaction.commit();
1136 }
1137 }
1138 }
1139
1140 // see: https://eel.is/c++draft/dcl.decl.general#nt:parameters-and-qualifiers
1141 // `attribute-specifier-seq` is ignored
1142 [[nodiscard]]
1144 {
1145 StateGuard<state::FunctionDeclarator> declarator{stream};
1146
1147 if (!expect(stream, lexing::operator_or_punctuator{"("}))
1148 {
1149 return std::nullopt;
1150 }
1151
1152 std::optional clause = parse_parameter_declaration_clause(stream);
1153 if (!clause)
1154 {
1155 return std::nullopt;
1156 }
1157
1158 if (!expect(stream, lexing::operator_or_punctuator{")"}))
1159 {
1160 return std::nullopt;
1161 }
1162
1164
1165 if (std::optional cv = parse_cv_qualifier_seq(stream))
1166 {
1167 declarator->qualifiers = *std::move(cv);
1168 }
1169
1170 declarator->refQualifier = parse_ref_qualifier(stream);
1171 declarator->isNoexcept = expect(stream, lexing::keyword{"noexcept"}).has_value();
1172
1173 declarator->params.reserve(clause->size());
1174 std::ranges::transform(
1175 *clause,
1176 std::back_inserter(declarator->params),
1177 [](state::TypeId& id) { return state::Recursive{std::move(id)}; });
1178
1179 return {std::move(declarator).take()};
1180 }
1181
1182 [[nodiscard]]
1183 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::AbstractDeclarator> parse_abstract_declarator(TokenStream& stream)
1184 {
1185 StateGuard<state::AbstractDeclarator> declarator{stream};
1186 auto& root = declarator->root;
1187
1188 while (std::optional op = parse_ptr_operator(stream))
1189 {
1190 root.decorations.emplace_back(*std::move(op));
1191 }
1192
1193 if (Transaction nestedTransaction{stream};
1195 {
1196 if (std::optional layer = parse_abstract_declarator(stream);
1197 layer
1198 && expect(stream, lexing::operator_or_punctuator{")"}))
1199 {
1200 root.nested.emplace(std::move(layer->root));
1201 nestedTransaction.commit();
1202 }
1203 }
1204
1205 if (std::optional params = parse_parameters_and_qualifiers(stream))
1206 {
1207 root.function.emplace(*std::move(params));
1208 }
1209
1210 while (std::optional arrayDecl = parse_array_declarator(stream))
1211 {
1212 root.arrays.emplace_back(*std::move(arrayDecl));
1213 }
1214
1215 if (root.decorations.empty()
1216 && !root.nested
1217 && !root.function
1218 && root.arrays.empty())
1219 {
1220 return std::nullopt;
1221 }
1222
1223 return {std::move(declarator).take()};
1224 }
1225
1226 namespace detail
1227 {
1228 [[nodiscard]]
1229 consteval auto make_builtin_type_candidates() noexcept
1230 {
1231 std::array types = std::apply(
1232 [](auto&... keyword) { return std::array{lexing::keyword{keyword}...}; },
1233 lexing::texts::typeKeywords);
1234 std::ranges::sort(types, {}, &lexing::keyword::index);
1235 MIMICPP_ASSERT(types.cend() == std::ranges::unique(types).begin(), "Fix your input!");
1236
1237 return types;
1238 }
1239
1240 inline constexpr std::array builtinTypeCandidates = make_builtin_type_candidates();
1241 }
1242
1243 // see: https://eel.is/c++draft/dcl.type.general#nt:type-specifier-seq
1244 [[nodiscard]]
1246 {
1247 StateGuard<state::TypeId> typeId{stream};
1248
1249 std::optional<state::QualifiedId> qualifiedType{};
1250 std::optional<state::BuiltinType> builtinType{};
1251 std::optional<state::TypeKey> typeKey{};
1252
1253 while (!stream.is_eof())
1254 {
1255 Transaction transaction{stream};
1256
1257 if (auto const* const keyword = peek_if<lexing::keyword>(stream))
1258 {
1259 // just silently ignore any linkage keyword.
1260 if (lexing::keyword{"static"} == *keyword
1261 || lexing::keyword{"constexpr"} == *keyword)
1262 {
1263 stream.consume();
1264 transaction.commit();
1265 continue;
1266 }
1267
1268 if (std::optional const cv = parse_cv_qualifier(stream))
1269 {
1270 if (!typeId->qualifications.apply(*cv))
1271 {
1272 break;
1273 }
1274
1275 transaction.commit();
1276 continue;
1277 }
1278
1279 if (!qualifiedType
1280 && std::ranges::binary_search(detail::builtinTypeCandidates, keyword->index(), {}, &lexing::keyword::index))
1281 {
1282 if (!(builtinType ? *builtinType : builtinType.emplace()).try_apply(*keyword))
1283 {
1284 break;
1285 }
1286
1287 stream.consume();
1288 transaction.commit();
1289 continue;
1290 }
1291 }
1292
1293 if (!qualifiedType && !builtinType)
1294 {
1295 // msvc often prefixes types with e.g. `class`.
1296 typeKey = parse_type_key(stream);
1297 if (std::optional qualifiedId = parse_qualified_id(stream))
1298 {
1299 qualifiedType = std::move(qualifiedId);
1300 transaction.commit();
1301 continue;
1302 }
1303 }
1304
1305 break;
1306 }
1307
1308 if (qualifiedType
1309 && !builtinType)
1310 {
1311 qualifiedType->typeKey = std::move(typeKey);
1312 typeId->base = *std::move(qualifiedType);
1313 return std::move(typeId).take();
1314 }
1315
1316 if (builtinType
1317 && !qualifiedType
1318 && !typeKey)
1319 {
1320 typeId->base = *std::move(builtinType);
1321 return std::move(typeId).take();
1322 }
1323
1324 return std::nullopt;
1325 }
1326
1327 // see: https://eel.is/c++draft/dcl.name#nt:type-id
1328 [[nodiscard]]
1330 {
1331 StateGuard<state::TypeId> id{stream};
1332 std::optional base = parse_type_specifier_seq(stream);
1333 if (!base)
1334 {
1335 return std::nullopt;
1336 }
1337
1338 *id = *std::move(base);
1339
1340 if (std::optional declarator = parse_abstract_declarator(stream))
1341 {
1342 id->declarator = *std::move(declarator);
1343 }
1344
1345 return std::move(id).take();
1346 }
1347
1348 [[nodiscard]]
1349 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<state::FunctionId> parse_function(TokenStream& stream)
1350 {
1352 if (std::optional returnType = parse_type_id(stream))
1353 {
1354 id->callConvention = parse_call_convention(stream);
1355
1356 if (std::optional identifier = parse_qualified_id(stream))
1357 {
1358 if (std::optional declarator = parse_parameters_and_qualifiers(stream))
1359 {
1360 id->returnType = std::move(returnType);
1361 id->identifier = *std::move(identifier);
1362 id->identifier.identifier.functionDeclarator = *std::move(declarator);
1363
1364 return std::move(id).take();
1365 }
1366 }
1367 // Well, sometimes there is no return-type given, but the parsed identifier actually is the function-id.
1368 // E.g., `<lambda()>`
1369 // This is a workaround!
1370 else if (auto* const qualifiedId = std::get_if<state::QualifiedId>(&returnType->base))
1371 {
1372 id->identifier = std::move(*qualifiedId);
1373 id->identifier.identifier.functionDeclarator = std::move(returnType->declarator.root.function);
1374
1375 return std::move(id).take();
1376 }
1377 }
1378
1379 return std::nullopt;
1380 }
1381}
1382
1384{
1385 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<parsing::state::TypeId> parse_type(std::string_view const text)
1386 {
1387 lexing::NameLexer lexer{text};
1388 parsing::TokenStream stream{lexer};
1389 if (std::optional typeId = parsing::parse_type_id(stream);
1390 typeId
1391 && stream.is_eof())
1392 {
1393 return typeId;
1394 }
1395
1396 return std::nullopt;
1397 }
1398
1399 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional<parsing::state::FunctionId> parse_function(std::string_view const text)
1400 {
1401 lexing::NameLexer lexer{text};
1402 parsing::TokenStream stream{lexer};
1403 if (std::optional functionId = parsing::parse_function(stream))
1404 {
1405 if (stream.is_eof()
1406 // Sometimes certain template details are added in separate square-brackets
1407 || (text.ends_with(']')
1409 {
1410 return functionId;
1411 }
1412 }
1413
1414 return std::nullopt;
1415 }
1416}
1417
1418#endif
#define MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES
Definition Config.hpp:93
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
StateGuard & operator=(StateGuard const &)=delete
constexpr std::optional< State > take() &&
Definition Parser.hpp:157
StateGuard & operator=(StateGuard &&)=delete
constexpr State * operator->() noexcept
Definition Parser.hpp:150
constexpr State & operator*() noexcept
Definition Parser.hpp:143
constexpr StateGuard(TokenStream &stream, Args &&... args)
Definition Parser.hpp:136
constexpr bool is_eof() const noexcept
Definition Parser.hpp:49
constexpr void consume()
Definition Parser.hpp:61
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES TokenStream(lexing::NameLexer &lexer) noexcept
Definition Parser.hpp:34
constexpr std::size_t pos() const noexcept
Definition Parser.hpp:68
constexpr void seek(util::pass_key< Transaction > const, std::size_t const pos)
Definition Parser.hpp:73
constexpr lexing::token const & peek() const
Definition Parser.hpp:55
Transaction & operator=(Transaction const &)=delete
constexpr bool is_active() const noexcept
Definition Parser.hpp:112
constexpr ~Transaction() noexcept
Definition Parser.hpp:91
constexpr void commit() noexcept
Definition Parser.hpp:106
constexpr Transaction(TokenStream &stream) noexcept
Definition Parser.hpp:100
Transaction(Transaction const &)=delete
Transaction & operator=(Transaction &&)=delete
std::array constexpr incOrDec
Definition Lexer.hpp:65
std::array constexpr assignment
Definition Lexer.hpp:64
std::array constexpr logical
Definition Lexer.hpp:68
std::array constexpr arithmetic
Definition Lexer.hpp:66
std::array constexpr comparison
Definition Lexer.hpp:63
std::array constexpr bitArithmetic
Definition Lexer.hpp:67
@ id_enum_class
Definition ParserState.hpp:82
@ id_enum
Definition ParserState.hpp:81
@ id_enum_struct
Definition ParserState.hpp:83
std::vector< TemplateArgument > TemplateArgumentList
Definition ParserState.hpp:149
@ id_ref
Definition ParserState.hpp:119
@ id_refref
Definition ParserState.hpp:120
@ id_struct
Definition ParserState.hpp:74
@ id_class
Definition ParserState.hpp:73
Definition Parser.hpp:27
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< std::vector< state::TypeId > > parse_parameter_declaration_clause(TokenStream &stream)
Definition Parser.hpp:1093
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::OperatorFunctionId > parse_operator_function_id(TokenStream &stream)
Definition Parser.hpp:611
constexpr auto make_map(std::pair< Key, Value > const (&mappings)[length])
Definition Parser.hpp:171
constexpr std::optional< state::ConstantExpression > parse_constant_expression(TokenStream &stream)
Definition Parser.hpp:325
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::LambdaFunctionId > parse_lambda_function_id(TokenStream &stream)
Definition Parser.hpp:715
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES void ignore_function_attribute(TokenStream &stream)
Definition Parser.hpp:1118
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::DestructorFunctionId > parse_destructor_function_id(TokenStream &stream)
Definition Parser.hpp:768
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::FunctionId > parse_function(TokenStream &stream)
Definition Parser.hpp:1349
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::AbstractDeclarator > parse_abstract_declarator(TokenStream &stream)
Definition Parser.hpp:1183
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TemplateArgumentList > parse_template_argument_list(TokenStream &stream)
Definition Parser.hpp:465
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TypeId > parse_type_id(TokenStream &stream)
Definition Parser.hpp:1329
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::ConversionFunctionId > parse_conversion_function_id(TokenStream &stream)
Definition Parser.hpp:689
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::UnqualifiedId > parse_nested_id(TokenStream &stream)
Definition Parser.hpp:855
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::FunctionDeclarator > parse_parameters_and_qualifiers(TokenStream &stream)
Definition Parser.hpp:1143
constexpr std::optional< state::EnumKey > parse_enum_key(TokenStream &stream)
Definition Parser.hpp:261
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::CVQualifierSeq > parse_cv_qualifier_seq(TokenStream &stream)
Definition Parser.hpp:544
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TemplateArgument > parse_template_argument(TokenStream &stream)
Definition Parser.hpp:447
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::UnqualifiedId > parse_unqualified_id(TokenStream &stream)
Definition Parser.hpp:798
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::ScopeSequence > parse_nested_name_specifier(TokenStream &stream)
Definition Parser.hpp:962
constexpr std::optional< state::TypeKey > parse_type_key(TokenStream &stream)
Definition Parser.hpp:282
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::QualifiedId > parse_qualified_id(TokenStream &stream)
Definition Parser.hpp:1000
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::PtrOperator > parse_ptr_operator(TokenStream &stream)
Definition Parser.hpp:1037
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::CallConvention > parse_call_convention(TokenStream &stream)
Definition Parser.hpp:1019
constexpr std::optional< state::RefQualifier > parse_ref_qualifier(TokenStream &stream)
Definition Parser.hpp:299
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TypeId > parse_type_specifier_seq(TokenStream &stream)
Definition Parser.hpp:1245
constexpr std::optional< state::Identifier > parse_identifier(TokenStream &stream)
Definition Parser.hpp:433
constexpr std::optional< state::CVQualifier > parse_cv_qualifier(TokenStream &stream)
Definition Parser.hpp:522
constexpr std::optional< state::Identifier > parse_synthetic_id(TokenStream &stream)
Definition Parser.hpp:397
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TypeId > parse_parameter_declaration(TokenStream &stream)
Definition Parser.hpp:1083
constexpr std::optional< state::ClassKey > parse_class_key(TokenStream &stream)
Definition Parser.hpp:239
constexpr std::optional< LexerTokenClass > expect(TokenStream &stream, LexerTokenClass const &expected)
Definition Parser.hpp:210
constexpr std::optional< state::ArrayDeclarator > parse_array_declarator(TokenStream &stream)
Definition Parser.hpp:1066
constexpr LexerTokenClass const * peek_if(TokenStream const &stream) noexcept
Definition Parser.hpp:203
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< state::TemplateArgumentList > parse_template_clause(TokenStream &stream)
Definition Parser.hpp:498
Definition Fwd.hpp:54
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< parsing::state::FunctionId > parse_function(std::string_view const text)
Definition Parser.hpp:1399
MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES std::optional< parsing::state::TypeId > parse_type(std::string_view const text)
Definition Parser.hpp:1385
constexpr std::array< T, firstN+secondN > concat_arrays(std::array< T, firstN > const &first, std::array< T, secondN > const &second)
Concatenates the given arrays by copying all elements into a new array.
Definition Algorithm.hpp:218
void unreachable()
Invokes undefined behavior.
Definition C++23Backports.hpp:40
constexpr detail::binary_find_fn binary_find
Finds the specified value within the container and returns an iterator pointing to it....
Definition Algorithm.hpp:361
constexpr std::ptrdiff_t index() const noexcept
Definition Lexer.hpp:147
constexpr std::ptrdiff_t index() const noexcept
Definition Lexer.hpp:187
token_class classification
Definition Lexer.hpp:232
std::variant< lexing::operator_or_punctuator, std::pair< lexing::keyword, bool >, std::array< lexing::operator_or_punctuator, 2u > > Symbol
Definition ParserState.hpp:153
A helper type that enables fine-grained access control to specific functions without using friend dec...
Definition PassKey.hpp:29