[54] | 1 | /*
|
---|
| 2 | * TOPPERS Software
|
---|
| 3 | * Toyohashi Open Platform for Embedded Real-Time Systems
|
---|
| 4 | *
|
---|
| 5 | * Copyright (C) 2005-2011 by TAKAGI Nobuhisa
|
---|
| 6 | *
|
---|
| 7 | * 上記著作権者は,以下の(1)〜(4)の条件を満たす場合に限り,本ソフトウェ
|
---|
| 8 | * ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
|
---|
| 9 | * 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
|
---|
| 10 | * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
|
---|
| 11 | * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
|
---|
| 12 | * スコード中に含まれていること.
|
---|
| 13 | * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
|
---|
| 14 | * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
|
---|
| 15 | * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
|
---|
| 16 | * の無保証規定を掲載すること.
|
---|
| 17 | * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
|
---|
| 18 | * 用できない形で再配布する場合には,次のいずれかの条件を満たすこ
|
---|
| 19 | * と.
|
---|
| 20 | * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
|
---|
| 21 | * 作権表示,この利用条件および下記の無保証規定を掲載すること.
|
---|
| 22 | * (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
|
---|
| 23 | * 報告すること.
|
---|
| 24 | * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
|
---|
| 25 | * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
|
---|
| 26 | * また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
|
---|
| 27 | * 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
|
---|
| 28 | * 免責すること.
|
---|
| 29 | *
|
---|
| 30 | * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
|
---|
| 31 | * よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
|
---|
| 32 | * に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
|
---|
| 33 | * アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
|
---|
| 34 | * の責任を負わない.
|
---|
| 35 | *
|
---|
| 36 | */
|
---|
| 37 |
|
---|
| 38 | /*!
|
---|
| 39 | * \file toppers/c_expr.hpp
|
---|
| 40 | * \brief C言語の式の構文解析に関する宣言定義
|
---|
| 41 | *
|
---|
| 42 | * このファイルで定義されるクラス
|
---|
| 43 | * \code
|
---|
| 44 | * struct c_expr_parser_base< Derived >;
|
---|
| 45 | * struct c_expr_parser;
|
---|
| 46 | * struct c_const_expr_parser;
|
---|
| 47 | * \endcode
|
---|
| 48 | */
|
---|
| 49 | #ifndef TOPPERS_C_EXPR_HPP_
|
---|
| 50 | #define TOPPERS_C_EXPR_HPP_
|
---|
| 51 |
|
---|
| 52 | #include "toppers/c_parser.hpp"
|
---|
| 53 | #include <boost/cstdint.hpp>
|
---|
| 54 |
|
---|
| 55 | namespace toppers
|
---|
| 56 | {
|
---|
| 57 |
|
---|
| 58 | /*!
|
---|
| 59 | * \struct c_expr_parser_base c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 60 | * \brief C言語の式を構文解析するための基底クラス
|
---|
| 61 | *
|
---|
| 62 | * 実際に使用する際は、 c_expr_parser_base および c_expr_parser_base::definition
|
---|
| 63 | * クラスを派生する必要があります。 c_expr_parser_base::definition の派生クラスでは、
|
---|
| 64 | * start メンバ関数を定義して、構文中の必要なルールを取り出すようにしてください。
|
---|
| 65 | * こうすることで、定数式の文法、一次式の文法といったように、C言語の文法のサブ
|
---|
| 66 | * セットを容易に作り出すことができます。
|
---|
| 67 | *
|
---|
| 68 | * \code
|
---|
| 69 | * // 一次式を取り出す例
|
---|
| 70 | * struct c_primary_expression : c_expr_parser_base< c_primary_expression >
|
---|
| 71 | * {
|
---|
| 72 | * template < class Scanner >
|
---|
| 73 | * struct definition : c_expr_parser_base< c_primary_expression >::definition
|
---|
| 74 | * {
|
---|
| 75 | * rule_t const& start() const { return primary_expression; }
|
---|
| 76 | * };
|
---|
| 77 | * };
|
---|
| 78 | * \endcode
|
---|
| 79 | */
|
---|
| 80 | template < class Derived >
|
---|
| 81 | struct c_expr_parser_base : boost::spirit::classic::grammar< Derived >
|
---|
| 82 | {
|
---|
| 83 | public:
|
---|
| 84 | /*!
|
---|
| 85 | * \struct definition c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 86 | * \brief 文法定義
|
---|
| 87 | */
|
---|
| 88 | template < class Scanner >
|
---|
| 89 | struct definition
|
---|
| 90 | {
|
---|
| 91 | typedef boost::spirit::classic::rule< Scanner > rule_t;
|
---|
| 92 | rule_t primary_expression,
|
---|
| 93 | expression,
|
---|
| 94 | constant_expression,
|
---|
| 95 | conditional_expression,
|
---|
| 96 | assignment_expression,
|
---|
| 97 | assignment_operator,
|
---|
| 98 | postfix_expression,
|
---|
| 99 | unary_expression,
|
---|
| 100 | unary_operator,
|
---|
| 101 | cast_expression,
|
---|
| 102 | multiplicative_expression,
|
---|
| 103 | additive_expression,
|
---|
| 104 | shift_expression,
|
---|
| 105 | relational_expression,
|
---|
| 106 | equality_expression,
|
---|
| 107 | AND_expression,
|
---|
| 108 | exclusive_OR_expression,
|
---|
| 109 | inclusive_OR_expression,
|
---|
| 110 | logical_AND_expression,
|
---|
| 111 | logical_OR_expression,
|
---|
| 112 | string_literal,
|
---|
| 113 | constant,
|
---|
| 114 | floating_constant,
|
---|
| 115 | decimal_floating_constant,
|
---|
| 116 | hexadecimal_floating_constant,
|
---|
| 117 | integer_constant,
|
---|
| 118 | character_constant,
|
---|
| 119 | declaration_specifiers,
|
---|
| 120 | type_name,
|
---|
| 121 | specifier_qualifier_list,
|
---|
| 122 | storage_class_specifier,
|
---|
| 123 | type_specifier,
|
---|
| 124 | type_qualifier,
|
---|
| 125 | declarator,
|
---|
| 126 | direct_declarator,
|
---|
| 127 | struct_or_union_specifier,
|
---|
| 128 | struct_declaration,
|
---|
| 129 | struct_declarator,
|
---|
| 130 | enum_specifier,
|
---|
| 131 | enumerator,
|
---|
| 132 | abstract_declarator,
|
---|
| 133 | pointer,
|
---|
| 134 | parameter_type_list,
|
---|
| 135 | parameter_list,
|
---|
| 136 | parameter_declaration,
|
---|
| 137 | direct_abstract_declarator;
|
---|
| 138 |
|
---|
| 139 | c_ident_parser_t identifier;
|
---|
| 140 | c_strlit_parser_t c_strlit_p;
|
---|
| 141 | c_chlit_parser_t c_chlit_p;
|
---|
| 142 |
|
---|
| 143 | /*!
|
---|
| 144 | * \brief コンストラクタ
|
---|
| 145 | * \param self 文法クラス( c_expr_parser_base< Derived > クラスからの継承)への参照
|
---|
| 146 | */
|
---|
| 147 | definition( Derived const& self )
|
---|
| 148 | : identifier( c_ident_parser( self.ucn_, self.c_plus_plus_ ) ),
|
---|
| 149 | c_strlit_p( c_strlit_parser( self.codeset_ ) ),
|
---|
| 150 | c_chlit_p( c_chlit_parser( self.codeset_ ) )
|
---|
| 151 | {
|
---|
| 152 | using namespace boost::spirit::classic;
|
---|
| 153 | static functor_parser< detail::c_integer_constant_parse_functor< boost::uintmax_t > > const c_int_const_p;
|
---|
| 154 | static functor_parser< detail::c_integer_suffix_parse_functor > const c_int_suffix_p;
|
---|
| 155 |
|
---|
| 156 | primary_expression = // 複合リテラル未対応
|
---|
| 157 | identifier
|
---|
| 158 | | constant
|
---|
| 159 | | string_literal
|
---|
| 160 | | ( '(' >> expression >> ')' );
|
---|
| 161 | expression =
|
---|
| 162 | assignment_expression % ',';
|
---|
| 163 | constant_expression =
|
---|
| 164 | conditional_expression;
|
---|
| 165 | conditional_expression =
|
---|
| 166 | logical_OR_expression >> *( '\?' >> expression >> ':' >> logical_OR_expression );
|
---|
| 167 | assignment_expression =
|
---|
| 168 | *( unary_expression >> assignment_operator ) >> conditional_expression;
|
---|
| 169 | assignment_operator =
|
---|
| 170 | ch_p( '=' ) | "*=" | "/=" | "%=" | "+=" | "?=" | "<<=" | ">>=" | "&=" | "^=" | "|=";
|
---|
| 171 | postfix_expression =
|
---|
| 172 | primary_expression >>
|
---|
| 173 | *(
|
---|
| 174 | ( '[' >> expression >> ']' )
|
---|
| 175 | | ( '(' >> list_p( assignment_expression, ',' ) >> ')' )
|
---|
| 176 | | ( '.' >> identifier )
|
---|
| 177 | | ( "->" >> identifier )
|
---|
| 178 | | "++"
|
---|
| 179 | | "--"
|
---|
| 180 | );
|
---|
| 181 | unary_expression =
|
---|
| 182 | *( str_p( "++" ) || "--" ) >>
|
---|
| 183 | (
|
---|
| 184 | ( "sizeof" >> unary_expression )
|
---|
| 185 | | ( str_p( "sizeof" ) >> '(' >> type_name >> ')' )
|
---|
| 186 | | postfix_expression
|
---|
| 187 | | ( unary_operator >> cast_expression )
|
---|
| 188 | );
|
---|
| 189 | unary_operator =
|
---|
| 190 | //chset<>( "&*~!+-" );
|
---|
| 191 | ch_p( '&' ) | '*' | '~' | '!' | '+' | '-';
|
---|
| 192 | cast_expression =
|
---|
| 193 | *( '(' >> type_name >> ')' ) >> unary_expression
|
---|
| 194 | | +( '(' >> ( type_name | identifier ) >> ')' ); // 構文解析に失敗する不具合対策
|
---|
| 195 | multiplicative_expression =
|
---|
| 196 | cast_expression >>
|
---|
| 197 | *(
|
---|
| 198 | ( '*' >> cast_expression )
|
---|
| 199 | | ( '/' >> cast_expression )
|
---|
| 200 | | ( '%' >> cast_expression )
|
---|
| 201 | );
|
---|
| 202 | additive_expression =
|
---|
| 203 | multiplicative_expression >>
|
---|
| 204 | *(
|
---|
| 205 | ( '+' >> multiplicative_expression )
|
---|
| 206 | | ( '-' >> multiplicative_expression )
|
---|
| 207 | );
|
---|
| 208 | shift_expression =
|
---|
| 209 | additive_expression >>
|
---|
| 210 | *(
|
---|
| 211 | ( "<<" >> additive_expression )
|
---|
| 212 | | ( ">>" >> additive_expression )
|
---|
| 213 | );
|
---|
| 214 | relational_expression =
|
---|
| 215 | shift_expression >>
|
---|
| 216 | *(
|
---|
| 217 | ( '<' >> shift_expression )
|
---|
| 218 | | ( '>' >> shift_expression )
|
---|
| 219 | | ( "<=" >> shift_expression )
|
---|
| 220 | | ( ">=" >> shift_expression )
|
---|
| 221 | );
|
---|
| 222 | equality_expression =
|
---|
| 223 | relational_expression >>
|
---|
| 224 | *(
|
---|
| 225 | ( "==" >> relational_expression )
|
---|
| 226 | | ( "!=" >> relational_expression )
|
---|
| 227 | );
|
---|
| 228 | AND_expression =
|
---|
| 229 | equality_expression >> *( '&' >> equality_expression );
|
---|
| 230 | exclusive_OR_expression =
|
---|
| 231 | AND_expression >> *( '^' >> AND_expression );
|
---|
| 232 | inclusive_OR_expression =
|
---|
| 233 | exclusive_OR_expression >> *( '|' >> exclusive_OR_expression );
|
---|
| 234 | logical_AND_expression =
|
---|
| 235 | inclusive_OR_expression >> *( "&&" >> inclusive_OR_expression );
|
---|
| 236 | logical_OR_expression =
|
---|
| 237 | logical_AND_expression >> *( "||" >> logical_AND_expression );
|
---|
| 238 | string_literal =
|
---|
| 239 | c_strlit_p
|
---|
| 240 | | lexeme_d[ 'L' >> c_strlit_p ];
|
---|
| 241 | constant =
|
---|
| 242 | floating_constant
|
---|
| 243 | | integer_constant
|
---|
| 244 | | identifier // 列挙定数
|
---|
| 245 | | character_constant;
|
---|
| 246 | floating_constant =
|
---|
| 247 | decimal_floating_constant
|
---|
| 248 | | hexadecimal_floating_constant;
|
---|
| 249 | decimal_floating_constant =
|
---|
| 250 | lexeme_d
|
---|
| 251 | [
|
---|
| 252 | as_lower_d
|
---|
| 253 | [
|
---|
| 254 | ( ( *digit_p >> '.' >> +digit_p ) | ( +digit_p >> '.' ) ) >>
|
---|
| 255 | // 'e' >> !chset<>( "+-" ) >> +digit_p >>
|
---|
| 256 | 'e' >> !( ch_p( '+' ) | '-' ) >> +digit_p >>
|
---|
| 257 | // !chset<>( "fl" )
|
---|
| 258 | ( ch_p( 'f' ) | 'l' )
|
---|
| 259 | ]
|
---|
| 260 | ];
|
---|
| 261 | hexadecimal_floating_constant =
|
---|
| 262 | lexeme_d
|
---|
| 263 | [
|
---|
| 264 | as_lower_d
|
---|
| 265 | [
|
---|
| 266 | "0x" >>
|
---|
| 267 | ( ( *xdigit_p >> '.' >> +xdigit_p ) | ( +xdigit_p >> '.' ) ) >>
|
---|
| 268 | // 'p' >> !chset<>( "+-" ) >> +digit_p >>
|
---|
| 269 | 'p' >> !( ch_p( '+' ) | '-' ) >> +digit_p >>
|
---|
| 270 | // !chset<>( "fl" )
|
---|
| 271 | ( ch_p( 'f' ) | 'l' )
|
---|
| 272 | ]
|
---|
| 273 | ];
|
---|
| 274 | integer_constant =
|
---|
| 275 | lexeme_d[ c_int_const_p >> !c_int_suffix_p ];
|
---|
| 276 | character_constant =
|
---|
| 277 | c_chlit_p
|
---|
| 278 | | lexeme_d[ 'L' >> c_chlit_p ];
|
---|
| 279 | declaration_specifiers =
|
---|
| 280 | +( storage_class_specifier | type_specifier | type_qualifier );
|
---|
| 281 | type_name =
|
---|
| 282 | specifier_qualifier_list >> !abstract_declarator;
|
---|
| 283 | specifier_qualifier_list =
|
---|
| 284 | +( type_specifier | type_qualifier );
|
---|
| 285 | storage_class_specifier =
|
---|
| 286 | str_p( "auto" )
|
---|
| 287 | | "register"
|
---|
| 288 | | "static"
|
---|
| 289 | | "extern"
|
---|
| 290 | | "typedef";
|
---|
| 291 | type_specifier =
|
---|
| 292 | str_p( "void" ) | "char" | "short" | "int" | "long" | "float" | "double"
|
---|
| 293 | | "signed" | "unsigned"
|
---|
| 294 | | identifier
|
---|
| 295 | | struct_or_union_specifier
|
---|
| 296 | | enum_specifier;
|
---|
| 297 | type_qualifier =
|
---|
| 298 | str_p( "const" ) | "volatile" | "restrict";
|
---|
| 299 | declarator =
|
---|
| 300 | !pointer >> direct_declarator;
|
---|
| 301 | direct_declarator =
|
---|
| 302 | ( identifier | ( '(' >> declarator >> ')' ) )
|
---|
| 303 | >>
|
---|
| 304 | *(
|
---|
| 305 | ( '[' >> !constant_expression >> ']' )
|
---|
| 306 | | ( '(' >> parameter_type_list >> ')' )
|
---|
| 307 | | ( '(' >> !( identifier % ',' ) >> ')' )
|
---|
| 308 | );
|
---|
| 309 | struct_or_union_specifier =
|
---|
| 310 | lexeme_d[ ( str_p( "struct" ) | "union" ) >> +space_p >> identifier ]
|
---|
| 311 | | ( lexeme_d[ ( str_p( "struct" ) | "union" ) >> +space_p >> !identifier ] >> '{' >> +struct_declaration >> '}' );
|
---|
| 312 | struct_declaration =
|
---|
| 313 | specifier_qualifier_list >> !list_p( struct_declarator, ',' ) >> ';';
|
---|
| 314 | // lisp_p( struct_declarator, ',' )を省略可能としているのは、
|
---|
| 315 | // struct_declarator の identifier を specifier_qualifier_list が
|
---|
| 316 | // typedef 名と間違うことを回避するため
|
---|
| 317 | struct_declarator =
|
---|
| 318 | ( !declarator >> ':' >> constant_expression ) // ビットフィールド
|
---|
| 319 | | declarator;
|
---|
| 320 | enum_specifier =
|
---|
| 321 | ( lexeme_d[ "enum" >> +space_p >> !identifier ] >> '{' >> list_p( enumerator, ',', ',' ) >> '}' ) // C99では末尾のカンマがあってもよい
|
---|
| 322 | | lexeme_d[ "enum" >> +space_p >> identifier ];
|
---|
| 323 | enumerator =
|
---|
| 324 | identifier >> !( '=' >> constant_expression );
|
---|
| 325 | abstract_declarator =
|
---|
| 326 | ( !pointer >> direct_abstract_declarator )
|
---|
| 327 | | pointer;
|
---|
| 328 | pointer =
|
---|
| 329 | +( '*' >> *type_qualifier );
|
---|
| 330 | parameter_type_list =
|
---|
| 331 | parameter_list >> !( ch_p( ',' ) >> "..." ); // 可変個引数
|
---|
| 332 | parameter_list =
|
---|
| 333 | parameter_declaration % ',';
|
---|
| 334 | parameter_declaration =
|
---|
| 335 | ( declaration_specifiers >> declarator )
|
---|
| 336 | | ( declaration_specifiers >> !abstract_declarator );
|
---|
| 337 | direct_abstract_declarator =
|
---|
| 338 | (
|
---|
| 339 | !( '(' >> abstract_declarator >> ')' ) >>
|
---|
| 340 | +(
|
---|
| 341 | ( '[' >> !constant_expression >> ']' )
|
---|
| 342 | | ( '(' >> !parameter_type_list >> ')' )
|
---|
| 343 | )
|
---|
| 344 | )
|
---|
| 345 | | ( '(' >> abstract_declarator >> ')' );
|
---|
| 346 | }
|
---|
| 347 | };
|
---|
| 348 | bool ucn_;
|
---|
| 349 | codeset_t codeset_;
|
---|
| 350 | bool c_plus_plus_;
|
---|
| 351 |
|
---|
| 352 | /*!
|
---|
| 353 | * \brief コンストラクタ
|
---|
| 354 | * \param ucn 国際文字名に対応する場合は true を指定する
|
---|
| 355 | * \param codeset 文字コード
|
---|
| 356 | * \param c_plus_plus C++ に対応する場合は true を指定する
|
---|
| 357 | */
|
---|
| 358 | explicit c_expr_parser_base( bool ucn = false, codeset_t codeset = ascii, bool c_plus_plus = false )
|
---|
| 359 | : ucn_( ucn ), codeset_( codeset ), c_plus_plus_( c_plus_plus )
|
---|
| 360 | {
|
---|
| 361 | }
|
---|
| 362 | };
|
---|
| 363 |
|
---|
| 364 | /*!
|
---|
| 365 | * \class c_expr_parser c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 366 | * \brief C言語の式の構文解析クラス
|
---|
| 367 | */
|
---|
| 368 | struct c_expr_parser : c_expr_parser_base< c_expr_parser >
|
---|
| 369 | {
|
---|
| 370 | typedef c_expr_parser_base< c_expr_parser > base_t;
|
---|
| 371 |
|
---|
| 372 | /*!
|
---|
| 373 | * \struct definition c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 374 | * \brief 文法定義
|
---|
| 375 | */
|
---|
| 376 | template < class Scanner >
|
---|
| 377 | struct definition : base_t::definition< Scanner >
|
---|
| 378 | {
|
---|
| 379 | typedef typename base_t::definition< Scanner >::rule_t rule_t;
|
---|
| 380 |
|
---|
| 381 | definition( c_expr_parser const& self ) : base_t::definition< Scanner >( self ) {}
|
---|
| 382 | rule_t const& start() const { return base_t::definition< Scanner >::expression; };
|
---|
| 383 | };
|
---|
| 384 |
|
---|
| 385 | /*!
|
---|
| 386 | * \brief コンストラクタ
|
---|
| 387 | * \param ucn 国際文字名に対応する場合は true を指定する
|
---|
| 388 | * \param codeset 文字コード
|
---|
| 389 | */
|
---|
| 390 | explicit c_expr_parser( bool ucn = false, codeset_t codeset = ascii )
|
---|
| 391 | : c_expr_parser_base< c_expr_parser >( ucn, codeset )
|
---|
| 392 | {
|
---|
| 393 | }
|
---|
| 394 | };
|
---|
| 395 |
|
---|
| 396 | /*!
|
---|
| 397 | * \class c_const_expr_parser c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 398 | * \brief C言語の定数式の構文解析クラス
|
---|
| 399 | */
|
---|
| 400 | struct c_const_expr_parser : c_expr_parser_base< c_const_expr_parser >
|
---|
| 401 | {
|
---|
| 402 | typedef c_expr_parser_base< c_const_expr_parser > base_t;
|
---|
| 403 |
|
---|
| 404 | /*!
|
---|
| 405 | * \struct definition c_expr.hpp "toppers/c_expr.hpp"
|
---|
| 406 | * \brief 文法定義
|
---|
| 407 | */
|
---|
| 408 | template < class Scanner >
|
---|
| 409 | struct definition : base_t::definition< Scanner >
|
---|
| 410 | {
|
---|
| 411 | typedef typename base_t::definition< Scanner >::rule_t rule_t;
|
---|
| 412 |
|
---|
| 413 | definition( c_const_expr_parser const& self ) : base_t::definition< Scanner >( self ) {}
|
---|
| 414 | rule_t const& start() const { return base_t::definition< Scanner >::constant_expression; };
|
---|
| 415 | };
|
---|
| 416 |
|
---|
| 417 | /*!
|
---|
| 418 | * \brief コンストラクタ
|
---|
| 419 | * \param ucn 国際文字名に対応する場合は true を指定する
|
---|
| 420 | * \param codeset 文字コード
|
---|
| 421 | */
|
---|
| 422 | explicit c_const_expr_parser( bool ucn = false, codeset_t codeset = ascii )
|
---|
| 423 | : c_expr_parser_base< c_const_expr_parser >( ucn, codeset )
|
---|
| 424 | {
|
---|
| 425 | }
|
---|
| 426 | };
|
---|
| 427 |
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | #endif // ! TOPPERS_C_EXPR_HPP_
|
---|