1 /*
2 **      cdecl -- C gibberish translator
3 **      src/c_sname.c
4 **
5 **      Copyright (C) 2019-2021  Paul J. Lucas
6 **
7 **      This program is free software: you can redistribute it and/or modify
8 **      it under the terms of the GNU General Public License as published by
9 **      the Free Software Foundation, either version 3 of the License, or
10 **      (at your option) any later version.
11 **
12 **      This program is distributed in the hope that it will be useful,
13 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
14 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 **      GNU General Public License for more details.
16 **
17 **      You should have received a copy of the GNU General Public License
18 **      along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 /**
22  * @file
23  * Defines functions for dealing with "sname" (C++ scoped name) objects, e.g.,
24  * `S::T::x`.
25  */
26 
27 // local
28 #include "pjl_config.h"                 /* must go first */
29 /// @cond DOXYGEN_IGNORE
30 #define C_SNAME_INLINE _GL_EXTERN_INLINE
31 /// @endcond
32 #include "c_sname.h"
33 #include "c_keyword.h"
34 #include "c_sglob.h"
35 #include "options.h"
36 #include "strbuf.h"
37 #include "util.h"
38 
39 // standard
40 #include <assert.h>
41 #include <fnmatch.h>
42 #include <stdlib.h>                     /* for free(3) */
43 
44 ////////// local functions ////////////////////////////////////////////////////
45 
46 /**
47  * Helper function for c_sname_full_name() and c_sname_scope_name() that writes
48  * the scope names from outermost to innermost separated by `::` into a buffer.
49  *
50  * @param sbuf The buffer to write into.
51  * @param sname The scoped name to write.
52  * @param end_scope The scope to stop before or NULL for all scopes.
53  * @return If not NULL, returns \a sbuf->str; otherwise returns the empty
54  * string.
55  */
56 PJL_WARN_UNUSED_RESULT
c_sname_impl(strbuf_t * sbuf,c_sname_t const * sname,c_scope_t const * end_scope)57 static char const* c_sname_impl( strbuf_t *sbuf, c_sname_t const *sname,
58                                  c_scope_t const *end_scope ) {
59   assert( sbuf != NULL );
60   assert( sname != NULL );
61 
62   strbuf_reset( sbuf );
63   bool colon2 = false;
64 
65   FOREACH_SNAME_SCOPE_UNTIL( scope, sname, end_scope ) {
66     strbuf_sepsn( sbuf, "::", 2, &colon2 );
67     c_scope_data_t const *const data = c_scope_data( scope );
68     if ( data->type.stids != TS_NONE ) {
69       // For nested inline namespaces, e.g., namespace A::inline B::C.
70       strbuf_printf( sbuf, "%s ", c_tid_name_c( data->type.stids ) );
71     }
72     strbuf_puts( sbuf, data->name );
73   }
74 
75   return sbuf->str != NULL ? sbuf->str : "";
76 }
77 
78 ////////// extern functions ///////////////////////////////////////////////////
79 
c_scope_data_cmp(c_scope_data_t * i_data,c_scope_data_t * j_data)80 int c_scope_data_cmp( c_scope_data_t *i_data, c_scope_data_t *j_data ) {
81   assert( i_data != NULL );
82   assert( j_data != NULL );
83   return strcmp( i_data->name, j_data->name );
84 }
85 
c_scope_data_dup(c_scope_data_t const * src)86 c_scope_data_t* c_scope_data_dup( c_scope_data_t const *src ) {
87   assert( src != NULL );
88   c_scope_data_t *const dst = MALLOC( c_scope_data_t, 1 );
89   dst->name = check_strdup( src->name );
90   dst->type = src->type;
91   return dst;
92 }
93 
c_scope_data_free(c_scope_data_t * data)94 void c_scope_data_free( c_scope_data_t *data ) {
95   if ( data != NULL ) {
96     FREE( data->name );
97     free( data );
98   }
99 }
100 
c_sname_append_name(c_sname_t * sname,char * name)101 void c_sname_append_name( c_sname_t *sname, char *name ) {
102   assert( sname != NULL );
103   assert( name != NULL );
104   c_scope_data_t *const data = MALLOC( c_scope_data_t, 1 );
105   data->name = name;
106   data->type = T_NONE;
107   slist_push_tail( sname, data );
108 }
109 
c_sname_cleanup(c_sname_t * sname)110 void c_sname_cleanup( c_sname_t *sname ) {
111   slist_cleanup( sname, (slist_free_fn_t)&c_scope_data_free );
112 }
113 
c_sname_fill_in_namespaces(c_sname_t * sname)114 void c_sname_fill_in_namespaces( c_sname_t *sname ) {
115   assert( sname != NULL );
116   c_type_t const *const local_type = c_sname_local_type( sname );
117   if ( !c_type_is_tid_any( local_type, TB_NAMESPACE ) )
118     return;
119 
120   FOREACH_SNAME_SCOPE_UNTIL( scope, sname, sname->tail ) {
121     c_type_t *const type = &c_scope_data( scope )->type;
122     if ( c_type_is_none( type ) || c_type_is_tid_any( type, TB_SCOPE ) ) {
123       type->btids &= c_tid_compl( TB_SCOPE );
124       type->btids |= TB_NAMESPACE;
125     }
126   } // for
127 }
128 
c_sname_free(c_sname_t * sname)129 void c_sname_free( c_sname_t *sname ) {
130   c_sname_cleanup( sname );
131   free( sname );
132 }
133 
c_sname_full_name(c_sname_t const * sname)134 char const* c_sname_full_name( c_sname_t const *sname ) {
135   static strbuf_t sbuf;
136   return sname != NULL ? c_sname_impl( &sbuf, sname, /*end_scope=*/NULL ) : "";
137 }
138 
c_sname_is_ctor(c_sname_t const * sname)139 bool c_sname_is_ctor( c_sname_t const *sname ) {
140   assert( sname != NULL );
141   if ( c_sname_count( sname ) < 2 )
142     return false;
143   char const *const class_name = c_sname_name_atr( sname, 1 );
144   char const *const local_name = c_sname_local_name( sname );
145   return strcmp( local_name, class_name ) == 0;
146 }
147 
c_sname_list_cleanup(slist_t * list)148 void c_sname_list_cleanup( slist_t *list ) {
149   slist_cleanup( list, (slist_free_fn_t)&c_sname_free );
150 }
151 
c_sname_match(c_sname_t const * sname,c_sglob_t const * sglob)152 bool c_sname_match( c_sname_t const *sname, c_sglob_t const *sglob ) {
153   assert( sname != NULL );
154   assert( sglob != NULL );
155 
156   c_scope_t const *scope = sname->head;
157   size_t const scope_count = c_sname_count( sname );
158 
159   if ( !sglob->match_in_any_scope ) {
160     if ( sglob->count != scope_count ) {
161       //
162       // For non-any-scope matches, the number of scope globs must equal the
163       // number of scopes in sname and it doesn't so it can't possibly match.
164       //
165       return false;
166     }
167   }
168   else if ( scope_count < sglob->count ) {
169     //
170     // For any-scope matches, if the number of scopes in sname is less than the
171     // number of scope globs, it can't possibly match.
172     //
173     return false;
174   }
175   else {
176     //
177     // For any-scope matches, skip past leading scopes in sname (if necessary)
178     // since its trailing scopes are the ones that have to match.
179     //
180     // For example, if sname is `a::b::c::d` (scope_count = 4) and glob is
181     // `**::c::d` (glob_count = 2 since the `**::` is stripped), then skip past
182     // 2 scopes (4 - 2) in sname to arrive at `c::d` that will match.
183     //
184     for ( size_t diff_count = scope_count - sglob->count; diff_count > 0;
185           --diff_count, scope = scope->next ) {
186       assert( scope != NULL );
187     } // for
188     assert( scope != NULL );
189   }
190 
191   //
192   // Finally, attempt to match each scope name against each scope glob.
193   //
194   for ( size_t sglob_index = 0; scope != NULL;
195         ++sglob_index, scope = scope->next ) {
196     assert( sglob_index < sglob->count );
197     char const *const name = c_scope_data( scope )->name;
198     if ( fnmatch( sglob->pattern[ sglob_index ], name, /*flags=*/0 ) != 0 )
199       return false;
200   } // for
201 
202   return true;
203 }
204 
c_sname_parse(char const * s,c_sname_t * sname)205 bool c_sname_parse( char const *s, c_sname_t *sname ) {
206   assert( s != NULL );
207   assert( sname != NULL );
208 
209   c_sname_t rv;
210   c_sname_init( &rv );
211 
212   for ( char const *end; (end = parse_identifier( s )) != NULL; ) {
213     char *const name = check_strndup( s, STATIC_CAST( size_t, end - s ) );
214 
215     // Ensure that the name is NOT a keyword.
216     c_keyword_t const *const k =
217       c_keyword_find( name, opt_lang, C_KW_CTX_DEFAULT );
218     if ( k != NULL ) {
219       FREE( name );
220       break;
221     }
222     c_sname_append_name( &rv, name );
223 
224     SKIP_WS( end );
225     if ( *end == '\0' ) {
226       *sname = rv;
227       return true;
228     }
229     if ( strncmp( end, "::", 2 ) != 0 )
230       break;
231     s = end + 2;
232     SKIP_WS( s );
233   } // for
234 
235   c_sname_cleanup( &rv );
236   return false;
237 }
238 
c_sname_scope_name(c_sname_t const * sname)239 char const* c_sname_scope_name( c_sname_t const *sname ) {
240   static strbuf_t sbuf;
241   return sname != NULL ? c_sname_impl( &sbuf, sname, sname->tail ) : "";
242 }
243 
244 ///////////////////////////////////////////////////////////////////////////////
245 /* vim:set et sw=2 ts=2: */
246