FLANG
token-parsers.h
1//===-- lib/Parser/token-parsers.h ------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef FORTRAN_PARSER_TOKEN_PARSERS_H_
10#define FORTRAN_PARSER_TOKEN_PARSERS_H_
11
12// These parsers are driven by the parsers of the Fortran grammar to consume
13// the prescanned character stream and recognize context-sensitive tokens.
14
15#include "basic-parsers.h"
16#include "type-parsers.h"
17#include "flang/Common/idioms.h"
18#include "flang/Parser/char-set.h"
19#include "flang/Parser/characters.h"
20#include "flang/Parser/instrumented-parser.h"
21#include "flang/Parser/provenance.h"
22#include <cstddef>
23#include <cstring>
24#include <functional>
25#include <limits>
26#include <list>
27#include <optional>
28#include <string>
29
30namespace Fortran::parser {
31
32// "xyz"_ch matches one instance of the characters x, y, or z without skipping
33// any spaces before or after. The parser returns the location of the character
34// on success.
36public:
37 using resultType = const char *;
38 constexpr AnyOfChars(const AnyOfChars &) = default;
39 constexpr AnyOfChars(SetOfChars set) : set_{set} {}
40 std::optional<const char *> Parse(ParseState &state) const {
41 if (std::optional<const char *> at{state.PeekAtNextChar()}) {
42 if (set_.Has(**at)) {
43 state.UncheckedAdvance();
44 state.set_anyTokenMatched();
45 return at;
46 }
47 }
48 state.Say(MessageExpectedText{set_});
49 return std::nullopt;
50 }
51
52private:
53 const SetOfChars set_;
54};
55
56constexpr AnyOfChars operator""_ch(const char str[], std::size_t n) {
57 return AnyOfChars{SetOfChars(str, n)};
58}
59
60constexpr auto letter{"abcdefghijklmnopqrstuvwxyz"_ch};
61constexpr auto digit{"0123456789"_ch};
62
63// Skips over optional spaces. Always succeeds.
64struct Space {
65 using resultType = Success;
66 constexpr Space() {}
67 static std::optional<Success> Parse(ParseState &state) {
68 while (std::optional<const char *> p{state.PeekAtNextChar()}) {
69 if (**p != ' ') {
70 break;
71 }
72 state.UncheckedAdvance();
73 }
74 return {Success{}};
75 }
76};
77constexpr Space space;
78
79// Skips a space that in free form requires a warning if it precedes a
80// character that could begin an identifier or keyword. Always succeeds.
81inline void MissingSpace(ParseState &state) {
82 if (!state.inFixedForm()) {
83 state.Nonstandard(
84 LanguageFeature::OptionalFreeFormSpace, "missing space"_port_en_US);
85 }
86}
87
88struct SpaceCheck {
89 using resultType = Success;
90 constexpr SpaceCheck() {}
91 static std::optional<Success> Parse(ParseState &state) {
92 if (std::optional<const char *> p{state.PeekAtNextChar()}) {
93 char ch{**p};
94 if (ch == ' ') {
95 state.UncheckedAdvance();
96 return space.Parse(state);
97 }
98 if (IsLegalInIdentifier(ch)) {
99 MissingSpace(state);
100 }
101 }
102 return {Success{}};
103 }
104};
105constexpr SpaceCheck spaceCheck;
106
107// Matches a token string. Spaces in the token string denote where
108// spaces may appear in the source; they can be made mandatory for
109// some free form keyword sequences. Missing mandatory spaces in free
110// form elicit a warning; they are not necessary for recognition.
111// Spaces before and after the token are also skipped.
112//
113// Token strings appear in the grammar as C++ user-defined literals
114// like "BIND ( C )"_tok and "SYNC ALL"_sptok. The _tok suffix is implied
115// when a string literal appears before the sequencing operator >> or
116// after the sequencing operator /. The literal "..."_id parses a
117// token that cannot be a prefix of a longer identifier.
118template <bool MandatoryFreeFormSpace = false, bool MustBeComplete = false>
120public:
121 using resultType = Success;
122 constexpr TokenStringMatch(const TokenStringMatch &) = default;
123 constexpr TokenStringMatch(const char *str, std::size_t n)
124 : str_{str}, bytes_{n} {}
125 explicit constexpr TokenStringMatch(const char *str) : str_{str} {}
126 std::optional<Success> Parse(ParseState &state) const {
127 space.Parse(state);
128 const char *start{state.GetLocation()};
129 const char *p{str_};
130 std::optional<const char *> at; // initially empty
131 for (std::size_t j{0}; j < bytes_ && *p != '\0'; ++j, ++p) {
132 bool spaceSkipping{*p == ' '};
133 if (spaceSkipping) {
134 if (j + 1 == bytes_ || p[1] == ' ' || p[1] == '\0') {
135 continue; // redundant; ignore
136 }
137 }
138 if (!at) {
139 at = nextCh.Parse(state);
140 if (!at) {
141 return std::nullopt;
142 }
143 }
144 if (spaceSkipping) {
145 if (**at == ' ') {
146 at = nextCh.Parse(state);
147 if (!at) {
148 return std::nullopt;
149 }
150 } else if constexpr (MandatoryFreeFormSpace) {
151 MissingSpace(state);
152 }
153 // 'at' remains full for next iteration
154 } else if (**at == ToLowerCaseLetter(*p)) {
155 at.reset();
156 } else {
157 state.Say(start, MessageExpectedText{str_, bytes_});
158 return std::nullopt;
159 }
160 }
161 if constexpr (MustBeComplete) {
162 if (auto after{state.PeekAtNextChar()}) {
163 if (IsLegalInIdentifier(**after)) {
164 state.Say(start, MessageExpectedText{str_, bytes_});
165 return std::nullopt;
166 }
167 }
168 }
169 state.set_anyTokenMatched();
170 if (IsLegalInIdentifier(p[-1])) {
171 return spaceCheck.Parse(state);
172 } else {
173 return space.Parse(state);
174 }
175 }
176
177private:
178 const char *const str_;
179 const std::size_t bytes_{std::string::npos};
180};
181
182constexpr TokenStringMatch<> operator""_tok(const char str[], std::size_t n) {
183 return {str, n};
184}
185
186constexpr TokenStringMatch<true> operator""_sptok(
187 const char str[], std::size_t n) {
188 return {str, n};
189}
190
191constexpr TokenStringMatch<false, true> operator""_id(
192 const char str[], std::size_t n) {
193 return {str, n};
194}
195
196template <class PA>
197inline constexpr std::enable_if_t<std::is_class_v<PA>,
198 SequenceParser<TokenStringMatch<>, PA>>
199operator>>(const char *str, const PA &p) {
200 return SequenceParser<TokenStringMatch<>, PA>{TokenStringMatch<>{str}, p};
201}
202
203template <class PA>
204inline constexpr std::enable_if_t<std::is_class_v<PA>,
205 FollowParser<PA, TokenStringMatch<>>>
206operator/(const PA &p, const char *str) {
207 return FollowParser<PA, TokenStringMatch<>>{p, TokenStringMatch<>{str}};
208}
209
210template <class PA> inline constexpr auto parenthesized(const PA &p) {
211 return "(" >> p / ")";
212}
213
214template <class PA> inline constexpr auto bracketed(const PA &p) {
215 return "[" >> p / "]";
216}
217
218template <class PA> inline constexpr auto braced(const PA &p) {
219 return "{" >> p / "}";
220}
221
222// Quoted character literal constants.
224 using resultType = std::pair<char, bool /* was escaped */>;
225 static std::optional<resultType> Parse(ParseState &state) {
226 auto at{state.GetLocation()};
227 if (std::optional<const char *> cp{nextCh.Parse(state)}) {
228 char ch{**cp};
229 if (ch == '\n') {
230 state.Say(CharBlock{at, state.GetLocation()},
231 "Unclosed character constant"_err_en_US);
232 return std::nullopt;
233 }
234 if (ch == '\\') {
235 // Most escape sequences in character literals are processed later,
236 // but we have to look for quotes here so that doubled quotes work.
237 if (std::optional<const char *> next{state.PeekAtNextChar()}) {
238 char escaped{**next};
239 if (escaped == '\'' || escaped == '"' || escaped == '\\') {
240 state.UncheckedAdvance();
241 return std::make_pair(escaped, true);
242 }
243 }
244 }
245 return std::make_pair(ch, false);
246 }
247 return std::nullopt;
248 }
249};
250
251template <char quote> struct CharLiteral {
252 using resultType = std::string;
253 static std::optional<std::string> Parse(ParseState &state) {
254 std::string str;
255 static constexpr auto nextch{attempt(CharLiteralChar{})};
256 while (auto ch{nextch.Parse(state)}) {
257 if (ch->second) {
258 str += '\\';
259 } else if (ch->first == quote) {
260 static constexpr auto doubled{attempt(AnyOfChars{SetOfChars{quote}})};
261 if (!doubled.Parse(state)) {
262 return str;
263 }
264 }
265 str += ch->first;
266 }
267 return std::nullopt;
268 }
269};
270
271// Parse "BOZ" binary literal quoted constants.
272// As extensions, support X as an alternate hexadecimal marker, and allow
273// BOZX markers to appear as suffixes.
275 using resultType = std::string;
276 static std::optional<resultType> Parse(ParseState &state) {
277 char base{'\0'};
278 auto baseChar{[&base](char ch) -> bool {
279 switch (ch) {
280 case 'b':
281 case 'o':
282 case 'z':
283 base = ch;
284 return true;
285 case 'x':
286 base = 'z';
287 return true;
288 default:
289 return false;
290 }
291 }};
292
293 space.Parse(state);
294 const char *start{state.GetLocation()};
295 std::optional<const char *> at{nextCh.Parse(state)};
296 if (!at) {
297 return std::nullopt;
298 }
299 if (**at == 'x' &&
300 !state.IsNonstandardOk(LanguageFeature::BOZExtensions,
301 "nonstandard BOZ literal"_port_en_US)) {
302 return std::nullopt;
303 }
304 if (baseChar(**at)) {
305 at = nextCh.Parse(state);
306 if (!at) {
307 return std::nullopt;
308 }
309 }
310
311 char quote = **at;
312 if (quote != '\'' && quote != '"') {
313 return std::nullopt;
314 }
315
316 std::string content;
317 while (true) {
318 at = nextCh.Parse(state);
319 if (!at) {
320 return std::nullopt;
321 }
322 if (**at == quote) {
323 break;
324 }
325 if (**at == ' ') {
326 continue;
327 }
328 if (!IsHexadecimalDigit(**at)) {
329 return std::nullopt;
330 }
331 content += ToLowerCaseLetter(**at);
332 }
333
334 if (!base) {
335 // extension: base allowed to appear as suffix, too
336 if (!(at = nextCh.Parse(state)) || !baseChar(**at) ||
337 !state.IsNonstandardOk(LanguageFeature::BOZExtensions,
338 "nonstandard BOZ literal"_port_en_US)) {
339 return std::nullopt;
340 }
341 spaceCheck.Parse(state);
342 }
343
344 if (content.empty()) {
345 state.Say(start, "no digit in BOZ literal"_err_en_US);
346 return std::nullopt;
347 }
348 return {std::string{base} + '"' + content + '"'};
349 }
350};
351
352// R711 digit-string -> digit [digit]...
353// N.B. not a token -- no space is skipped
355 using resultType = CharBlock;
356 static std::optional<resultType> Parse(ParseState &state) {
357 if (std::optional<const char *> ch1{state.PeekAtNextChar()}) {
358 if (IsDecimalDigit(**ch1)) {
359 state.UncheckedAdvance();
360 while (std::optional<const char *> p{state.PeekAtNextChar()}) {
361 if (!IsDecimalDigit(**p)) {
362 break;
363 }
364 state.UncheckedAdvance();
365 }
366 return CharBlock{*ch1, state.GetLocation()};
367 }
368 }
369 return std::nullopt;
370 }
371};
372constexpr DigitString digitString;
373
375 using resultType = CharBlock;
376 static std::optional<resultType> Parse(ParseState &state) {
377 resultType result{state.GetLocation()};
378 static constexpr auto sign{maybe("+-"_ch / space)};
379 if (sign.Parse(state)) {
380 if (auto digits{digitString.Parse(state)}) {
381 result.ExtendToCover(*digits);
382 return result;
383 }
384 }
385 return std::nullopt;
386 }
387};
388
390 using resultType = std::uint64_t;
391 static std::optional<std::uint64_t> Parse(ParseState &state) {
392 std::optional<const char *> firstDigit{digit.Parse(state)};
393 if (!firstDigit) {
394 return std::nullopt;
395 }
396 std::uint64_t value = **firstDigit - '0';
397 bool overflow{false};
398 static constexpr auto getDigit{attempt(digit)};
399 while (auto nextDigit{getDigit.Parse(state)}) {
400 if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
401 overflow = true;
402 }
403 value *= 10;
404 int digitValue = **nextDigit - '0';
405 if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
406 overflow = true;
407 }
408 value += digitValue;
409 }
410 if (overflow) {
411 state.Say(*firstDigit, "overflow in decimal literal"_err_en_US);
412 }
413 return {value};
414 }
415};
416constexpr DigitString64 digitString64;
417
418// R707 signed-int-literal-constant -> [sign] int-literal-constant
419// N.B. Spaces are consumed before and after the sign, since the sign
420// and the int-literal-constant are distinct tokens. Does not
421// handle a trailing kind parameter.
422static std::optional<std::int64_t> SignedInteger(
423 const std::optional<std::uint64_t> &x, Location at, bool negate,
424 ParseState &state) {
425 if (!x) {
426 return std::nullopt;
427 }
428 std::uint64_t limit{std::numeric_limits<std::int64_t>::max()};
429 if (negate) {
430 limit = -(limit + 1);
431 }
432 if (*x > limit) {
433 state.Say(at, "overflow in signed decimal literal"_err_en_US);
434 }
435 std::int64_t value = *x;
436 return std::make_optional<std::int64_t>(negate ? -value : value);
437}
438
439// R710 signed-digit-string -> [sign] digit-string
440// N.B. Not a complete token -- no space is skipped.
441// Used only in the exponent parts of real literal constants.
443 using resultType = std::int64_t;
444 static std::optional<std::int64_t> Parse(ParseState &state) {
445 std::optional<const char *> sign{state.PeekAtNextChar()};
446 if (!sign) {
447 return std::nullopt;
448 }
449 bool negate{**sign == '-'};
450 if (negate || **sign == '+') {
451 state.UncheckedAdvance();
452 }
453 return SignedInteger(digitString64.Parse(state), *sign, negate, state);
454 }
455};
456
457// Variants of the above for use in FORMAT specifications, where spaces
458// must be ignored.
460 using resultType = std::uint64_t;
461 static std::optional<std::uint64_t> Parse(ParseState &state) {
462 static constexpr auto getFirstDigit{space >> digit};
463 std::optional<const char *> firstDigit{getFirstDigit.Parse(state)};
464 if (!firstDigit) {
465 return std::nullopt;
466 }
467 std::uint64_t value = **firstDigit - '0';
468 bool overflow{false};
469 static constexpr auto getDigit{space >> attempt(digit)};
470 while (auto nextDigit{getDigit.Parse(state)}) {
471 if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
472 overflow = true;
473 }
474 value *= 10;
475 int digitValue = **nextDigit - '0';
476 if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
477 overflow = true;
478 }
479 value += digitValue;
480 }
481 if (overflow) {
482 state.Say(*firstDigit, "overflow in decimal literal"_err_en_US);
483 }
484 return value;
485 }
486};
487
489 using resultType = std::int64_t;
490 static std::optional<std::int64_t> Parse(ParseState &state) {
491 Location at{state.GetLocation()};
492 return SignedInteger(
493 DigitStringIgnoreSpaces{}.Parse(state), at, false /*positive*/, state);
494 }
495};
496
498 using resultType = std::int64_t;
499 static std::optional<std::int64_t> Parse(ParseState &state) {
500 static constexpr auto getSign{space >> attempt("+-"_ch)};
501 bool negate{false};
502 if (std::optional<const char *> sign{getSign.Parse(state)}) {
503 negate = **sign == '-';
504 }
505 Location at{state.GetLocation()};
506 return SignedInteger(
507 DigitStringIgnoreSpaces{}.Parse(state), at, negate, state);
508 }
509};
510
511// Legacy feature: Hollerith literal constants
513 using resultType = std::string;
514 static std::optional<std::string> Parse(ParseState &state) {
515 space.Parse(state);
516 const char *start{state.GetLocation()};
517 std::optional<std::uint64_t> charCount{
518 DigitStringIgnoreSpaces{}.Parse(state)};
519 if (!charCount || *charCount < 1) {
520 return std::nullopt;
521 }
522 static constexpr auto letterH{"h"_ch};
523 std::optional<const char *> h{letterH.Parse(state)};
524 if (!h) {
525 return std::nullopt;
526 }
527 std::string content;
528 for (auto j{*charCount}; j-- > 0;) {
529 int chBytes{UTF_8CharacterBytes(state.GetLocation())};
530 for (int bytes{chBytes}; bytes > 0; --bytes) {
531 if (std::optional<const char *> at{nextCh.Parse(state)}) {
532 if (chBytes == 1 && !IsPrintable(**at)) {
533 state.Say(start, "Bad character in Hollerith"_err_en_US);
534 return std::nullopt;
535 }
536 content += **at;
537 } else {
538 state.Say(start, "Insufficient characters in Hollerith"_err_en_US);
539 return std::nullopt;
540 }
541 }
542 }
543 return content;
544 }
545};
546
548 using resultType = Success;
549 constexpr ConsumedAllInputParser() {}
550 static inline std::optional<Success> Parse(ParseState &state) {
551 if (state.IsAtEnd()) {
552 return {Success{}};
553 }
554 return std::nullopt;
555 }
556};
557constexpr ConsumedAllInputParser consumedAllInput;
558
559template <char goal> struct SkipPast {
560 using resultType = Success;
561 constexpr SkipPast() {}
562 constexpr SkipPast(const SkipPast &) {}
563 static std::optional<Success> Parse(ParseState &state) {
564 while (std::optional<const char *> p{state.GetNextChar()}) {
565 if (**p == goal) {
566 return {Success{}};
567 } else if (**p == '\n') {
568 break;
569 }
570 }
571 return std::nullopt;
572 }
573};
574
575template <char goal> struct SkipTo {
576 using resultType = Success;
577 constexpr SkipTo() {}
578 constexpr SkipTo(const SkipTo &) {}
579 static std::optional<Success> Parse(ParseState &state) {
580 while (std::optional<const char *> p{state.PeekAtNextChar()}) {
581 if (**p == goal) {
582 return {Success{}};
583 } else if (**p == '\n') {
584 break;
585 } else {
586 state.UncheckedAdvance();
587 }
588 }
589 return std::nullopt;
590 }
591};
592
593template <char left, char right> struct SkipPastNested {
594 using resultType = Success;
595 constexpr SkipPastNested() {}
596 constexpr SkipPastNested(const SkipPastNested &) {}
597 static std::optional<Success> Parse(ParseState &state) {
598 int nesting{1};
599 while (std::optional<const char *> p{state.GetNextChar()}) {
600 if (**p == right) {
601 if (!--nesting) {
602 return {Success{}};
603 }
604 } else if (**p == left) {
605 ++nesting;
606 } else if (**p == '\n') {
607 break;
608 }
609 }
610 return std::nullopt;
611 }
612};
613
614// A common idiom in the Fortran grammar is an optional item (usually
615// a nonempty comma-separated list) that, if present, must follow a comma
616// and precede a doubled colon. When the item is absent, the comma must
617// not appear, and the doubled colons are optional.
618// [[, xyz] ::] is optionalBeforeColons(xyz)
619// [[, xyz]... ::] is optionalBeforeColons(nonemptyList(xyz))
620template <typename PA> inline constexpr auto optionalBeforeColons(const PA &p) {
621 using resultType = std::optional<typename PA::resultType>;
622 return "," >> construct<resultType>(p) / "::" ||
623 ("::"_tok || !","_tok) >> pure<resultType>();
624}
625template <typename PA>
626inline constexpr auto optionalListBeforeColons(const PA &p) {
627 using resultType = std::list<typename PA::resultType>;
628 return "," >> nonemptyList(p) / "::" ||
629 ("::"_tok || !","_tok) >> pure<resultType>();
630}
631
632// Skip over empty lines, leading spaces, and some compiler directives (viz.,
633// the ones that specify the source form) that might appear before the
634// next statement. Skip over empty statements (bare semicolons) when
635// not in strict standard conformance mode. Always succeeds.
637 using resultType = Success;
638 static std::optional<Success> Parse(ParseState &state) {
639 if (UserState * ustate{state.userState()}) {
640 if (ParsingLog * log{ustate->log()}) {
641 // Save memory: vacate the parsing log before each statement unless
642 // we're logging the whole parse for debugging.
643 if (!ustate->instrumentedParse()) {
644 log->clear();
645 }
646 }
647 }
648 while (std::optional<const char *> at{state.PeekAtNextChar()}) {
649 if (**at == '\n' || **at == ' ') {
650 state.UncheckedAdvance();
651 } else if (**at == '!') {
652 static const char fixed[] = "!dir$ fixed\n", free[] = "!dir$ free\n";
653 static constexpr std::size_t fixedBytes{sizeof fixed - 1};
654 static constexpr std::size_t freeBytes{sizeof free - 1};
655 std::size_t remain{state.BytesRemaining()};
656 if (remain >= fixedBytes && std::memcmp(*at, fixed, fixedBytes) == 0) {
657 state.set_inFixedForm(true).UncheckedAdvance(fixedBytes);
658 } else if (remain >= freeBytes &&
659 std::memcmp(*at, free, freeBytes) == 0) {
660 state.set_inFixedForm(false).UncheckedAdvance(freeBytes);
661 } else {
662 break;
663 }
664 } else if (**at == ';' &&
665 state.IsNonstandardOk(
666 LanguageFeature::EmptyStatement, "empty statement"_port_en_US)) {
667 state.UncheckedAdvance();
668 } else {
669 break;
670 }
671 }
672 return {Success{}};
673 }
674};
675constexpr SkipStuffBeforeStatement skipStuffBeforeStatement;
676
677// R602 underscore -> _
678constexpr auto underscore{"_"_ch};
679
680// Characters besides letters and digits that may appear in names.
681// N.B. Don't accept an underscore if it is immediately followed by a
682// quotation mark, so that kindParam_"character literal" is parsed properly.
683// PGI and ifort accept '$' in identifiers, even as the initial character.
684// Cray and gfortran accept '$', but not as the first character.
685// Cray accepts '@' as well.
686constexpr auto otherIdChar{underscore / !"'\""_ch ||
687 extension<LanguageFeature::PunctuationInNames>(
688 "nonstandard usage: punctuation in name"_port_en_US, "$@"_ch)};
689
690constexpr auto logicalTRUE{
691 (".TRUE."_tok ||
692 extension<LanguageFeature::LogicalAbbreviations>(
693 "nonstandard usage: .T. spelling of .TRUE."_port_en_US,
694 ".T."_tok)) >>
695 pure(true)};
696constexpr auto logicalFALSE{
697 (".FALSE."_tok ||
698 extension<LanguageFeature::LogicalAbbreviations>(
699 "nonstandard usage: .F. spelling of .FALSE."_port_en_US,
700 ".F."_tok)) >>
701 pure(false)};
702
703// deprecated: Hollerith literals
704constexpr auto rawHollerithLiteral{
705 deprecated<LanguageFeature::Hollerith>(HollerithLiteral{})};
706
707template <typename A> constexpr decltype(auto) verbatim(A x) {
708 return sourced(construct<Verbatim>(x));
709}
710
711} // namespace Fortran::parser
712#endif // FORTRAN_PARSER_TOKEN_PARSERS_H_
Definition: token-parsers.h:35
Definition: char-block.h:28
Definition: message.h:160
Definition: parse-state.h:35
Definition: instrumented-parser.h:25
Definition: user-state.h:33
Definition: token-parsers.h:119
Definition: user-state.h:35
Definition: check-expression.h:19
Definition: token-parsers.h:274
Definition: token-parsers.h:223
Definition: token-parsers.h:251
Definition: token-parsers.h:547
Definition: token-parsers.h:389
Definition: token-parsers.h:459
Definition: token-parsers.h:354
Definition: token-parsers.h:512
Definition: char-set.h:23
Definition: token-parsers.h:497
Definition: token-parsers.h:442
Definition: token-parsers.h:593
Definition: token-parsers.h:559
Definition: token-parsers.h:636
Definition: token-parsers.h:575
Definition: token-parsers.h:88
Definition: token-parsers.h:64