# # Copyright (C) 2006-2024 Matthias Koefferlein # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # require "cpp_classes.rb" require "cpp_parser_classes.rb" grammar CPP rule s [ \t\n\r]* end rule sp [ \t\n\r]+ end rule string_const '"' ( [^\\"] / "\\" . )* '"' end rule char_const "'" ( [^\\'] / "\\" . )* "'" end rule numeric_const "-"? ( [0-9]+ ( "." [0-9]* )? / "." [0-9]+ ) ( [eE] "-"? [0-9]+ )? end rule hex_const "0x" [0-9a-fA-F]+ end rule attribute_value "(" ( s ( "," s )? attribute_value )* s ")" / numeric_const / string_const / id end rule ignored_attr "__attribute__" s attribute_value / "__asm" s attribute_value / "constexpr" ![a-zA-Z0-9_] / "__extension__" / "__inline" / "__m64" / "__m128" / "__m128d" / "__m128i" / "__m256" / "__m256d" / "__m256i" / "__m512" / "__m512d" / "__m512i" / "__mmask8" / "__mmask16" / "__v8df" / "__v8di" / "__v16sf" / "__v16si" / "decltype" s attribute_value / "alignas" s "(" s block s ")" / "[[" s block s "]]" end rule a s ( ignored_attr s )* end rule unary_op "~" / "!" / "-" / "*" / "&" end rule bin_op_wo_gt "+=" / "++" / "+" / "->" / "-=" / "--" / "-" / "()" / "[]" / "&&" / "&=" / "&" / "||" / "|=" / "|" / "==" / "=" / "!=" / "*=" / "*" / "/=" / "/" / "%=" / "%" / "<=" / "<<=" / "<<" / "<" / "~=" / "^=" / "^" / "~=" / ">=" / ">>=" end rule bin_op bin_op_wo_gt / ">>" / ">" end rule id "operator" s "," / "operator" s ( bin_op / unary_op / "," ) / "operator" sp [a-zA-Z_0-9\*\& \t\n\r]+ &( s "(" ) / "~"? [a-zA-Z_] [a-zA-Z_0-9]* end rule template_arg_part_any block_wo_gt end rule template_arg_part qualified_id &( ( s ">" / "," ) ) / template_arg_part_any end rule template_args template_arg_part ( s "," s template_arg_part )* end rule id_with_template_args id:id taspec:( s "<" s ta:template_args s ">" )? end rule qualified_id globalspec:( "::" s )? id_with_template_args ( s "::" id_with_template_args )* end rule int_type_attr "signed" ![a-zA-Z0-9_] / "unsigned" ![a-zA-Z0-9_] / "long" ( sp "long" )? ![a-zA-Z0-9_] / "short" ![a-zA-Z0-9_] end rule int_type ( ( int_type_attr s )* "int" ![a-zA-Z0-9_] / int_type_attr ( s int_type_attr )* !"char" ( s "int" ![a-zA-Z0-9_] )? ) end rule char_type ( "signed" sp / "unsigned" sp )? s !"int" "char" ![a-zA-Z0-9_] end rule bool_type "bool" ![a-zA-Z0-9_] end rule void_type "void" ![a-zA-Z0-9_] end rule float_type ( "long" sp )? ( "double" / "float" ) ![a-zA-Z0-9_] end rule enum_spec a id:id a initspec:( s "=" s init:block_wo_comma )? end rule enum_body enum_spec ( s "," s enum_spec )* ","? end rule enum_type "enum" ![a-zA-Z0-9_] a is_class:(s "class" )? s id:id? ( s ":" s type )? bodyspec:( s "{" s body:enum_body s "}" )? end rule virtual_spec "virtual" ![a-zA-Z0-9_] end rule explicit_key "explicit" ![a-zA-Z0-9_] end rule mutable_key "mutable" ![a-zA-Z0-9_] end rule member_declaration_wo_semicolon template:( d:template_decl s )? attr:( ( explicit_key / mutable_key / storage_class / inline_spec / virtual_spec / ignored_attr ) s )* t:type # type declaration ends with a } .. does not need a semicolon # (i.e. nested struct or enum) &{ |seq| seq[-1].text_value_ends_with_curly_brace } s ( ":" s block_wo_curly_braces s )? ( trailing_return_type )? a ( "{" s block s "}" / ";"? ) end rule member_declaration_w_semicolon template:( d:template_decl s )? attr:( ( explicit_key / mutable_key / storage_class / inline_spec / virtual_spec / ignored_attr ) s )* t:type # opposite case (member_see declaration_wo_semicolon) # (i.e. nested struct or enum) !{ |seq| seq[-1].text_value_ends_with_curly_brace } s ( ":" s block_wo_curly_braces s )? ( trailing_return_type )? a ( "{" s block s "}" / ";" ) end rule member_declaration a ( member_declaration_wo_semicolon / member_declaration_w_semicolon ) end rule friend_decl a template:( d:template_decl s )? "friend" ![a-zA-Z0-9_] s t:member_declaration end rule class_struct_body_declarations ( s ";" / s static_assert / s friend_decl / s using / s typedef / s !( "public" / "private" / "protected" ) member_declaration )* end rule class_struct_body class_struct_body_declarations ( s "public" s ":" d:class_struct_body_declarations / s "private" s ":" d:class_struct_body_declarations / s "protected" s ":" d:class_struct_body_declarations )* end rule class_id # In order to easily distinguish between constructor methods without # a return type and class or typedef names we assume that all "name(" # constructs are considered constructor names but "name (*func_ptr) ()" or "name (class::*method_ptr) ()" is not. qualified_id s ( "..." s )? !( "(" !( s "*" / s qualified_id s "::*" ) ) end rule typeof "__typeof" s "(" s qid:qualified_id s ")" end rule base_class attr:( "public" ![a-zA-Z0-9_] s / "private" ![a-zA-Z0-9_] s / "protected" ![a-zA-Z0-9_] s / "virtual" ![a-zA-Z0-9_] s )* a cid:class_id end rule base_classes base_class ( s "," s base_classes )? end rule class_or_struct_type stype:( "union" ![a-zA-Z0-9_] / "struct" ![a-zA-Z0-9_] / "class" ![a-zA-Z0-9_] ) a idspec:( s id:class_id )? bcspec:( s ":" s bc:base_classes )? bodyspec:( s "{" s body:class_struct_body s "}" )? end rule concrete_type ( class_or_struct_type / enum_type / float_type / char_type / int_type / bool_type / void_type / typeof / class_id )? end rule cv ( "const" ![a-zA-Z0-9_] / "__const" ![a-zA-Z0-9_] / "volatile" ![a-zA-Z0-9_] / "__volatile" ![a-zA-Z0-9_] ) end rule pointer cvspec:( cv:cv s )? "*" itspec:( s it:inner_type )? end rule reference cvspec:( cv:cv s )? "&" itspec:( s it:inner_type )? end rule array_spec "[" s block_wo_comma s "]" end rule func_arg_part t:type_wo_comma !"..." end rule ellipsis "..." end rule func_args &")" / func_arg_part ( s "," s func_arg_part )* ( s "," s ellipsis )? / ellipsis end rule noexcept_spec "noexcept" ( s "(" s block s ")" )? end rule func_spec "(" s fa:( a:func_args )? s ")" cvspec:( s cv:cv )? refspec:( s ref:( "&" !"&" / "&&" ) )? ( s "throw" s "(" s ( type_wo_comma s )? ")" / s noexcept_spec / s override_key )* a end rule member_pointer cspec:( qid:qualified_id s "::*" s ) itspec:( it:inner_type )? cvspec:( s cv:cv )? refspec:( s ref:( "&" !"&" / "&&" ) )? end rule inner_type_with_cv cvspec:cv s it:inner_type end rule inner_type it:( "(" s inner_type s ")" / inner_type_with_cv / pointer / reference / member_pointer / ( "__restrict" ![a-zA-Z0-9_] s / "..." s )* qualified_id ) pfx:( s spec:( array_spec / func_spec ) )* end rule init_spec block_wo_comma / "default" / "delete" / "0" end rule type cvspec:( cv:cv s )? a ( "typename" ![a-zA-Z0-9_] s )? ct:concrete_type a il:( s t1:inner_type i1:(s "=" s is1:init_spec)? tt:( s "," s t2:inner_type i2:(s "=" s is2:init_spec)? )* )? # alternative initialization if only a concrete type is given: pi:( s "=" s is:init_spec )? end rule type_wo_comma cvspec:( cv:cv s )? a ( "typename" ![a-zA-Z0-9_] s )? ct:concrete_type il:( s t:inner_type i:(s "=" s is:init_spec)? )? # alternative initialization if only a concrete type is given: pi:( s "=" s is:init_spec )? end rule type_for_template cvspec:( cv:cv s )? a ( ( "typename" / "class" ) ![a-zA-Z0-9_] s ( "..." s )? )? ct:concrete_type il:( s t:inner_type )? end rule storage_class ( "static" ![a-zA-Z0-9_] / "extern" ![a-zA-Z0-9_] ( s '"C"' / s '"C++"' / s '"Pascal"' )? ) end rule override_key "override" ![a-zA-Z0-9_] end rule inline_spec "inline" ![a-zA-Z0-9_] end # parse over blocks as gracefully as possible ... rule block_atom_wo_gt "(" s block s ")" / "[" s block s "]" / "<" !"=" s block_wo_gt ( s "," s block_wo_gt )* s ">" / numeric_const / hex_const / string_const / char_const / id ( s "{" s block s "}" )? / unary_op / bin_op_wo_gt / "?" / "::" / "." / ":" / ";" end rule block_wo_gt ( s block_atom_wo_gt / s "{" s block s "}" )* end # parse over blocks as gracefully as possible ... rule block_atom "(" s block s ")" / "[" s block s "]" / "<" s block_wo_gt ( s "," s block_wo_gt )* s ">" / numeric_const / hex_const / string_const / char_const / id ( s "{" s block s "}" )? / unary_op / bin_op / "?" / "::" / "." / ":" end rule block ( s block_atom / s ";" / s "," / s "{" s block s "}" )* end rule block_wo_curly_braces ( s block_atom / s "," )* end rule block_wo_comma ( s block_atom / s "{" s block s "}" )* end rule using "using" ![a-zA-Z0-9_] ( s "namespace" )? ![a-zA-Z0-9_] s id:qualified_id a ";" end rule static_assert "static_assert" s "(" s block s ")" end rule typedef a "typedef" ![a-zA-Z0-9_] s t:type a ";" end rule template_decl_arg t:type_for_template dtspec:( s "=" s dt:block_wo_gt )? end rule template_decl "template" s "<" s ( !">" template_decl_arg s ( "," s template_decl_arg )* )? s ">" end rule trailing_return_type "->" s ( "decltype" s "(" s block s ")" / type ) end rule declaration_w_semicolon template:( d:template_decl s )? template_member:( d_member:template_decl s )? attr:( ( storage_class / inline_spec / ignored_attr ) s )* t:type # type declaration ends with a } .. does not need a semicolon # (i.e. nested struct or enum) !{ |seq| seq[-1].text_value_ends_with_curly_brace } s ( ":" s block_wo_curly_braces s )? ( trailing_return_type )? a ( "{" s block s "}" / ";" ) end rule declaration_wo_semicolon template:( d:template_decl s )? attr:( ( storage_class / inline_spec / ignored_attr ) s )* t:type # opposite case (see declaration_wo_semicolon) # (i.e. nested struct or enum) &{ |seq| seq[-1].text_value_ends_with_curly_brace } s ( ":" s block_wo_curly_braces s )? ( trailing_return_type )? a ( "{" s block s "}" / ";"? ) end rule declaration declaration_w_semicolon / declaration_wo_semicolon end rule namespace "namespace" ![a-zA-Z0-9_] s n:id a "{" decls:( a ( ";" / static_assert / using / typedef / namespace / declaration ) )* s "}" end rule extern_decl "extern" s n:( '"C"' / '"C++"' / '"Pascal"' ) s a "{" decls:( a ( ";" / typedef / namespace / extern_decl / declaration ) )* s "}" end rule asm "asm" s "(" s block s ")" end rule module ( a ( ";" / static_assert / using / typedef / namespace / extern_decl / declaration / asm ) )* s end end