1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
6 Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
7 Software License, Version 1.0. (See accompanying file
8 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
11 #if !defined(CPP_GRAMMAR_HPP_FEAEBC2E_2734_428B_A7CA_85E5A415E23E_INCLUDED)
12 #define CPP_GRAMMAR_HPP_FEAEBC2E_2734_428B_A7CA_85E5A415E23E_INCLUDED
14 #include <boost/spirit/include/classic_core.hpp>
15 #include <boost/spirit/include/classic_parse_tree.hpp>
16 #include <boost/spirit/include/classic_parse_tree_utils.hpp>
17 #include <boost/spirit/include/classic_confix.hpp>
18 #include <boost/spirit/include/classic_lists.hpp>
20 #include <boost/wave/wave_config.hpp>
21 #include <boost/pool/pool_alloc.hpp>
23 #if BOOST_WAVE_DUMP_PARSE_TREE != 0
25 #include <boost/spirit/include/classic_tree_to_xml.hpp>
28 #include <boost/wave/token_ids.hpp>
29 #include <boost/wave/grammars/cpp_grammar_gen.hpp>
30 #include <boost/wave/util/pattern_parser.hpp>
32 #include <boost/wave/cpp_exceptions.hpp>
34 // this must occur after all of the includes and before any code appears
35 #ifdef BOOST_HAS_ABI_HEADERS
36 #include BOOST_ABI_PREFIX
39 ///////////////////////////////////////////////////////////////////////////////
46 ///////////////////////////////////////////////////////////////////////////////
50 // The store_found_eof functor sets a given flag if the T_EOF token was
51 // found during the parsing process
53 ///////////////////////////////////////////////////////////////////////////////
55 struct store_found_eof {
57 store_found_eof(bool &found_eof_) : found_eof(found_eof_) {}
59 template <typename TokenT>
60 void operator()(TokenT const &/*token*/) const
68 ///////////////////////////////////////////////////////////////////////////////
70 // store_found_directive
72 // The store_found_directive functor stores the token_id of the recognized
75 ///////////////////////////////////////////////////////////////////////////////
77 template <typename TokenT>
78 struct store_found_directive {
80 store_found_directive(TokenT &found_directive_)
81 : found_directive(found_directive_) {}
83 void operator()(TokenT const &token) const
85 found_directive = token;
88 TokenT &found_directive;
91 ///////////////////////////////////////////////////////////////////////////////
93 // store_found_eoltokens
95 // The store_found_eoltokens functor stores the token sequence of the
96 // line ending for a particular pp directive
98 ///////////////////////////////////////////////////////////////////////////////
100 template <typename ContainerT>
101 struct store_found_eoltokens {
103 store_found_eoltokens(ContainerT &found_eoltokens_)
104 : found_eoltokens(found_eoltokens_) {}
106 template <typename IteratorT>
107 void operator()(IteratorT const &first, IteratorT const& last) const
109 std::copy(first, last,
110 std::inserter(found_eoltokens, found_eoltokens.end()));
113 ContainerT &found_eoltokens;
116 ///////////////////////////////////////////////////////////////////////////////
118 // flush_underlying_parser
120 // The flush_underlying_parser flushes the underlying
121 // multi_pass_iterator during the normal parsing process. This is
122 // used at certain points during the parsing process, when it is
123 // clear, that no backtracking is needed anymore and the input
124 // gathered so far may be discarded.
126 ///////////////////////////////////////////////////////////////////////////////
127 struct flush_underlying_parser
128 : public boost::spirit::classic::parser<flush_underlying_parser>
130 typedef flush_underlying_parser this_t;
132 template <typename ScannerT>
133 typename boost::spirit::classic::parser_result<this_t, ScannerT>::type
134 parse(ScannerT const& scan) const
136 scan.first.clear_queue();
137 return scan.empty_match();
141 flush_underlying_parser const
142 flush_underlying_parser_p = flush_underlying_parser();
144 } // anonymous namespace
146 ///////////////////////////////////////////////////////////////////////////////
147 // define, whether the rule's should generate some debug output
148 #define TRACE_CPP_GRAMMAR \
149 bool(BOOST_SPIRIT_DEBUG_FLAGS_CPP & BOOST_SPIRIT_DEBUG_FLAGS_CPP_GRAMMAR) \
152 ///////////////////////////////////////////////////////////////////////////////
153 // Encapsulation of the C++ preprocessor grammar.
154 template <typename TokenT, typename ContainerT>
156 public boost::spirit::classic::grammar<cpp_grammar<TokenT, ContainerT> >
158 typedef typename TokenT::position_type position_type;
159 typedef cpp_grammar<TokenT, ContainerT> grammar_type;
160 typedef impl::store_found_eof store_found_eof_type;
161 typedef impl::store_found_directive<TokenT> store_found_directive_type;
162 typedef impl::store_found_eoltokens<ContainerT> store_found_eoltokens_type;
164 template <typename ScannerT>
167 // non-parse_tree generating rule type
168 typedef typename ScannerT::iteration_policy_t iteration_policy_t;
169 typedef boost::spirit::classic::match_policy match_policy_t;
170 typedef typename ScannerT::action_policy_t action_policy_t;
172 boost::spirit::classic::scanner_policies<
173 iteration_policy_t, match_policy_t, action_policy_t>
176 boost::spirit::classic::scanner<typename ScannerT::iterator_t, policies_t>
179 boost::spirit::classic::rule<
180 non_tree_scanner_t, boost::spirit::classic::dynamic_parser_tag>
183 // 'normal' (parse_tree generating) rule type
185 boost::spirit::classic::rule<
186 ScannerT, boost::spirit::classic::dynamic_parser_tag>
189 rule_type pp_statement, macro_include_file;
190 // rule_type include_file, system_include_file;
191 rule_type plain_define, macro_definition, macro_parameters;
193 rule_type ppifdef, ppifndef, ppif, ppelif;
194 // rule_type ppelse, ppendif;
200 rule_type ppqualifiedname;
201 rule_type eol_tokens;
202 no_tree_rule_type ppsp;
203 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
205 rule_type ppendregion;
208 definition(cpp_grammar const &self)
210 // import the spirit and cpplexer namespaces here
211 using namespace boost::spirit::classic;
212 using namespace boost::wave;
213 using namespace boost::wave::util;
215 // set the rule id's for later use
216 pp_statement.set_id(BOOST_WAVE_PP_STATEMENT_ID);
217 // include_file.set_id(BOOST_WAVE_INCLUDE_FILE_ID);
218 // system_include_file.set_id(BOOST_WAVE_SYSINCLUDE_FILE_ID);
219 macro_include_file.set_id(BOOST_WAVE_MACROINCLUDE_FILE_ID);
220 plain_define.set_id(BOOST_WAVE_PLAIN_DEFINE_ID);
221 macro_parameters.set_id(BOOST_WAVE_MACRO_PARAMETERS_ID);
222 macro_definition.set_id(BOOST_WAVE_MACRO_DEFINITION_ID);
223 undefine.set_id(BOOST_WAVE_UNDEFINE_ID);
224 ppifdef.set_id(BOOST_WAVE_IFDEF_ID);
225 ppifndef.set_id(BOOST_WAVE_IFNDEF_ID);
226 ppif.set_id(BOOST_WAVE_IF_ID);
227 ppelif.set_id(BOOST_WAVE_ELIF_ID);
228 // ppelse.set_id(BOOST_WAVE_ELSE_ID);
229 // ppendif.set_id(BOOST_WAVE_ENDIF_ID);
230 ppline.set_id(BOOST_WAVE_LINE_ID);
231 pperror.set_id(BOOST_WAVE_ERROR_ID);
232 ppwarning.set_id(BOOST_WAVE_WARNING_ID);
233 pppragma.set_id(BOOST_WAVE_PRAGMA_ID);
234 illformed.set_id(BOOST_WAVE_ILLFORMED_ID);
235 ppsp.set_id(BOOST_WAVE_PPSPACE_ID);
236 ppqualifiedname.set_id(BOOST_WAVE_PPQUALIFIEDNAME_ID);
237 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
238 ppregion.set_id(BOOST_WAVE_REGION_ID);
239 ppendregion.set_id(BOOST_WAVE_ENDREGION_ID);
242 #if BOOST_WAVE_DUMP_PARSE_TREE != 0
243 self.map_rule_id_to_name.init_rule_id_to_name_map(self);
246 // recognizes preprocessor directives only
248 // C++ standard 16.1: A preprocessing directive consists of a sequence
249 // of preprocessing tokens. The first token in the sequence is #
250 // preprocessing token that is either the first character in the source
251 // file (optionally after white space containing no new-line
252 // characters) or that follows white space containing at least one
253 // new-line character. The last token in the sequence is the first
254 // new-line character that follows the first token in the sequence.
259 // | system_include_file
272 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
279 [ store_found_eoltokens_type(self.found_eoltokens) ]
280 // In parser debug mode it is useful not to flush the underlying stream
281 // to allow its investigation in the debugger and to see the correct
282 // output in the printed debug log..
283 // Note: this may break the parser, though.
284 #if !(defined(BOOST_SPIRIT_DEBUG) && \
285 (BOOST_SPIRIT_DEBUG_FLAGS_CPP & BOOST_SPIRIT_DEBUG_FLAGS_CPP_GRAMMAR) \
287 >> impl::flush_underlying_parser_p
288 #endif // !(defined(BOOST_SPIRIT_DEBUG) &&
292 // include_file // include "..."
293 // = ch_p(T_PP_QHEADER)
294 // [ store_found_directive_type(self.found_directive) ]
295 // #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
296 // | ch_p(T_PP_QHEADER_NEXT)
297 // [ store_found_directive_type(self.found_directive) ]
301 // system_include_file // include <...>
302 // = ch_p(T_PP_HHEADER)
303 // [ store_found_directive_type(self.found_directive) ]
304 // #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
305 // | ch_p(T_PP_HHEADER_NEXT)
306 // [ store_found_directive_type(self.found_directive) ]
310 macro_include_file // include ...anything else...
314 [ store_found_directive_type(self.found_directive) ]
315 #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
316 | ch_p(T_PP_INCLUDE_NEXT)
317 [ store_found_directive_type(self.found_directive) ]
321 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
325 // #define FOO foo (with optional parameters)
330 [ store_found_directive_type(self.found_directive) ]
333 >> ( ch_p(T_IDENTIFIER)
334 | pattern_p(KeywordTokenType,
335 TokenTypeMask|PPTokenFlag)
336 | pattern_p(OperatorTokenType|AltExtTokenType,
337 ExtTokenTypeMask|PPTokenFlag) // and, bit_and etc.
338 | pattern_p(BoolLiteralTokenType,
339 TokenTypeMask|PPTokenFlag) // true/false
341 >> ( ( no_node_d[eps_p(ch_p(T_LEFTPAREN))]
345 | !( no_node_d[+ppsp]
355 no_node_d[ch_p(T_LEFTPAREN) >> *ppsp],
358 | pattern_p(KeywordTokenType,
359 TokenTypeMask|PPTokenFlag)
360 | pattern_p(OperatorTokenType|AltExtTokenType,
361 ExtTokenTypeMask|PPTokenFlag) // and, bit_and etc.
362 | pattern_p(BoolLiteralTokenType,
363 TokenTypeMask|PPTokenFlag) // true/false
364 #if BOOST_WAVE_SUPPORT_VARIADICS_PLACEMARKERS != 0
368 no_node_d[*ppsp >> ch_p(T_COMMA) >> *ppsp]
370 no_node_d[*ppsp >> ch_p(T_RIGHTPAREN)]
374 // macro body (anything left until eol)
378 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
387 [ store_found_directive_type(self.found_directive) ]
390 >> ( ch_p(T_IDENTIFIER)
391 | pattern_p(KeywordTokenType,
392 TokenTypeMask|PPTokenFlag)
393 | pattern_p(OperatorTokenType|AltExtTokenType,
394 ExtTokenTypeMask|PPTokenFlag) // and, bit_and etc.
395 | pattern_p(BoolLiteralTokenType,
396 TokenTypeMask|PPTokenFlag) // true/false
405 [ store_found_directive_type(self.found_directive) ]
415 [ store_found_directive_type(self.found_directive) ]
425 [ store_found_directive_type(self.found_directive) ]
429 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
437 // [ store_found_directive_type(self.found_directive) ]
445 [ store_found_directive_type(self.found_directive) ]
449 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
457 // [ store_found_directive_type(self.found_directive) ]
466 [ store_found_directive_type(self.found_directive) ]
470 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
474 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
479 ch_p(T_MSEXT_PP_REGION)
480 [ store_found_directive_type(self.found_directive) ]
490 ch_p(T_MSEXT_PP_ENDREGION)
491 [ store_found_directive_type(self.found_directive) ]
496 // # something else (ill formed preprocessor directive)
497 illformed // for error reporting
500 pattern_p(T_POUND, MainTokenMask)
504 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
509 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
519 [ store_found_directive_type(self.found_directive) ]
523 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
532 [ store_found_directive_type(self.found_directive) ]
536 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
545 [ store_found_directive_type(self.found_directive) ]
548 (ch_p(T_NEWLINE) | ch_p(T_CPPCOMMENT) | ch_p(T_EOF))
554 >> ( ch_p(T_IDENTIFIER)
555 | pattern_p(KeywordTokenType,
556 TokenTypeMask|PPTokenFlag)
557 | pattern_p(OperatorTokenType|AltExtTokenType,
558 ExtTokenTypeMask|PPTokenFlag) // and, bit_and etc.
559 | pattern_p(BoolLiteralTokenType,
560 TokenTypeMask|PPTokenFlag) // true/false
564 // auxiliary helper rules
565 ppsp // valid space in a line with a preprocessor directive
566 = ch_p(T_SPACE) | ch_p(T_CCOMMENT)
569 // end of line tokens
579 [ store_found_eof_type(self.found_eof) ]
584 BOOST_SPIRIT_DEBUG_TRACE_RULE(pp_statement, TRACE_CPP_GRAMMAR);
585 // BOOST_SPIRIT_DEBUG_TRACE_RULE(include_file, TRACE_CPP_GRAMMAR);
586 // BOOST_SPIRIT_DEBUG_TRACE_RULE(system_include_file, TRACE_CPP_GRAMMAR);
587 BOOST_SPIRIT_DEBUG_TRACE_RULE(macro_include_file, TRACE_CPP_GRAMMAR);
588 BOOST_SPIRIT_DEBUG_TRACE_RULE(plain_define, TRACE_CPP_GRAMMAR);
589 BOOST_SPIRIT_DEBUG_TRACE_RULE(macro_definition, TRACE_CPP_GRAMMAR);
590 BOOST_SPIRIT_DEBUG_TRACE_RULE(macro_parameters, TRACE_CPP_GRAMMAR);
591 BOOST_SPIRIT_DEBUG_TRACE_RULE(undefine, TRACE_CPP_GRAMMAR);
592 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppifdef, TRACE_CPP_GRAMMAR);
593 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppifndef, TRACE_CPP_GRAMMAR);
594 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppif, TRACE_CPP_GRAMMAR);
595 // BOOST_SPIRIT_DEBUG_TRACE_RULE(ppelse, TRACE_CPP_GRAMMAR);
596 // BOOST_SPIRIT_DEBUG_TRACE_RULE(ppelif, TRACE_CPP_GRAMMAR);
597 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppendif, TRACE_CPP_GRAMMAR);
598 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppline, TRACE_CPP_GRAMMAR);
599 BOOST_SPIRIT_DEBUG_TRACE_RULE(pperror, TRACE_CPP_GRAMMAR);
600 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppwarning, TRACE_CPP_GRAMMAR);
601 BOOST_SPIRIT_DEBUG_TRACE_RULE(illformed, TRACE_CPP_GRAMMAR);
602 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppsp, TRACE_CPP_GRAMMAR);
603 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppqualifiedname, TRACE_CPP_GRAMMAR);
604 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
605 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppregion, TRACE_CPP_GRAMMAR);
606 BOOST_SPIRIT_DEBUG_TRACE_RULE(ppendregion, TRACE_CPP_GRAMMAR);
610 // start rule of this grammar
611 rule_type const& start() const
612 { return pp_statement; }
616 TokenT &found_directive;
617 ContainerT &found_eoltokens;
619 cpp_grammar(bool &found_eof_, TokenT &found_directive_,
620 ContainerT &found_eoltokens_)
621 : found_eof(found_eof_),
622 found_directive(found_directive_),
623 found_eoltokens(found_eoltokens_)
625 BOOST_SPIRIT_DEBUG_TRACE_GRAMMAR_NAME(*this, "cpp_grammar",
629 #if BOOST_WAVE_DUMP_PARSE_TREE != 0
630 // helper function and data to get readable names of the rules known to us
631 struct map_ruleid_to_name :
632 public std::map<boost::spirit::classic::parser_id, std::string>
634 typedef std::map<boost::spirit::classic::parser_id, std::string> base_type;
636 void init_rule_id_to_name_map(cpp_grammar const &self)
640 char const *rule_name;
642 init_ruleid_name_map[] = {
643 { BOOST_WAVE_PP_STATEMENT_ID, "pp_statement" },
644 // { BOOST_WAVE_INCLUDE_FILE_ID, "include_file" },
645 // { BOOST_WAVE_SYSINCLUDE_FILE_ID, "system_include_file" },
646 { BOOST_WAVE_MACROINCLUDE_FILE_ID, "macro_include_file" },
647 { BOOST_WAVE_PLAIN_DEFINE_ID, "plain_define" },
648 { BOOST_WAVE_MACRO_PARAMETERS_ID, "macro_parameters" },
649 { BOOST_WAVE_MACRO_DEFINITION_ID, "macro_definition" },
650 { BOOST_WAVE_UNDEFINE_ID, "undefine" },
651 { BOOST_WAVE_IFDEF_ID, "ppifdef" },
652 { BOOST_WAVE_IFNDEF_ID, "ppifndef" },
653 { BOOST_WAVE_IF_ID, "ppif" },
654 { BOOST_WAVE_ELIF_ID, "ppelif" },
655 // { BOOST_WAVE_ELSE_ID, "ppelse" },
656 // { BOOST_WAVE_ENDIF_ID, "ppendif" },
657 { BOOST_WAVE_LINE_ID, "ppline" },
658 { BOOST_WAVE_ERROR_ID, "pperror" },
659 { BOOST_WAVE_WARNING_ID, "ppwarning" },
660 { BOOST_WAVE_PRAGMA_ID, "pppragma" },
661 { BOOST_WAVE_ILLFORMED_ID, "illformed" },
662 { BOOST_WAVE_PPSPACE_ID, "ppspace" },
663 { BOOST_WAVE_PPQUALIFIEDNAME_ID, "ppqualifiedname" },
664 #if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
665 { BOOST_WAVE_REGION_ID, "ppregion" },
666 { BOOST_WAVE_ENDREGION_ID, "ppendregion" },
671 // initialize parser_id to rule_name map
672 for (int i = 0; 0 != init_ruleid_name_map[i].parser_id; ++i)
673 base_type::insert(base_type::value_type(
674 boost::spirit::classic::parser_id(init_ruleid_name_map[i].parser_id),
675 std::string(init_ruleid_name_map[i].rule_name))
679 mutable map_ruleid_to_name map_rule_id_to_name;
680 #endif // WAVE_DUMP_PARSE_TREE != 0
683 ///////////////////////////////////////////////////////////////////////////////
684 #undef TRACE_CPP_GRAMMAR
686 ///////////////////////////////////////////////////////////////////////////////
688 // Special parse function generating a parse tree using a given node_factory.
690 ///////////////////////////////////////////////////////////////////////////////
691 template <typename NodeFactoryT, typename IteratorT, typename ParserT>
692 inline boost::spirit::classic::tree_parse_info<IteratorT, NodeFactoryT>
693 parsetree_parse(IteratorT const& first_, IteratorT const& last,
694 boost::spirit::classic::parser<ParserT> const& p)
696 using namespace boost::spirit::classic;
698 typedef pt_match_policy<IteratorT, NodeFactoryT> pt_match_policy_type;
699 typedef scanner_policies<iteration_policy, pt_match_policy_type>
700 scanner_policies_type;
701 typedef scanner<IteratorT, scanner_policies_type> scanner_type;
703 scanner_policies_type policies;
704 IteratorT first = first_;
705 scanner_type scan(first, last, policies);
706 tree_match<IteratorT, NodeFactoryT> hit = p.derived().parse(scan);
707 return tree_parse_info<IteratorT, NodeFactoryT>(
708 first, hit, hit && (first == last), hit.length(), hit.trees);
711 ///////////////////////////////////////////////////////////////////////////////
713 // The following parse function is defined here, to allow the separation of
714 // the compilation of the cpp_grammar from the function using it.
716 ///////////////////////////////////////////////////////////////////////////////
718 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0
719 #define BOOST_WAVE_GRAMMAR_GEN_INLINE
721 #define BOOST_WAVE_GRAMMAR_GEN_INLINE inline
724 template <typename LexIteratorT, typename TokenContainerT>
725 BOOST_WAVE_GRAMMAR_GEN_INLINE
726 boost::spirit::classic::tree_parse_info<
728 typename cpp_grammar_gen<LexIteratorT, TokenContainerT>::node_factory_type
730 cpp_grammar_gen<LexIteratorT, TokenContainerT>::parse_cpp_grammar (
731 LexIteratorT const &first, LexIteratorT const &last,
732 position_type const &act_pos, bool &found_eof,
733 token_type &found_directive, token_container_type &found_eoltokens)
735 using namespace boost::spirit::classic;
736 using namespace boost::wave;
738 cpp_grammar<token_type, TokenContainerT> g(found_eof, found_directive, found_eoltokens);
739 tree_parse_info<LexIteratorT, node_factory_type> hit =
740 parsetree_parse<node_factory_type>(first, last, g);
742 #if BOOST_WAVE_DUMP_PARSE_TREE != 0
744 tree_to_xml (BOOST_WAVE_DUMP_PARSE_TREE_OUT, hit.trees, "",
745 g.map_rule_id_to_name, &token_type::get_token_id,
746 &token_type::get_token_value);
753 #undef BOOST_WAVE_GRAMMAR_GEN_INLINE
755 ///////////////////////////////////////////////////////////////////////////////
756 } // namespace grammars
760 // the suffix header occurs after all of the code
761 #ifdef BOOST_HAS_ABI_HEADERS
762 #include BOOST_ABI_SUFFIX
765 #endif // !defined(CPP_GRAMMAR_HPP_FEAEBC2E_2734_428B_A7CA_85E5A415E23E_INCLUDED)