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