1 /*
2 ** cdecl -- C gibberish translator
3 ** src/c_lang.h
4 **
5 ** Copyright (C) 2017-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 #ifndef cdecl_c_lang_H
22 #define cdecl_c_lang_H
23
24 /**
25 * @file
26 * Declares constants, types, and functions for C/C++ language versions.
27 */
28
29 // local
30 #include "pjl_config.h" /* must go first */
31 #include "options.h" /* for opt_lang */
32 #include "types.h"
33 #include "util.h"
34
35 /// @cond DOXYGEN_IGNORE
36
37 // standard
38 #include <assert.h>
39 #include <stdbool.h>
40
41 _GL_INLINE_HEADER_BEGIN
42 #ifndef C_LANG_INLINE
43 # define C_LANG_INLINE _GL_INLINE
44 #endif /* C_LANG_INLINE */
45
46 /// @endcond
47
48 /**
49 * @defgroup c-lang-group C/C++ Language Versions
50 * Constants, types, and functions for C/C++ language versions.
51 * @{
52 */
53
54 ///////////////////////////////////////////////////////////////////////////////
55
56 // languages supported
57 #define LANG_NONE ((c_lang_id_t)0u) /**< No languages. */
58 #define LANG_ANY (~LANGX_MASK) /**< Any supported language. */
59
60 #define LANG_C_OLD LANG_C_KNR /**< Oldest supported C language. */
61 #define LANG_C_KNR (1u << 0) /**< K&R (pre-ANSI) C. */
62 #define LANG_C_89 (1u << 1) /**< C 89 (first ANSI C). */
63 #define LANG_C_95 (1u << 2) /**< C 95. */
64 #define LANG_C_99 (1u << 3) /**< C 99. */
65 #define LANG_C_11 (1u << 4) /**< C 11. */
66 #define LANG_C_17 (1u << 5) /**< C 17. */
67 #define LANG_C_2X (1u << 6) /**< C 2X. */
68 #define LANG_C_NEW LANG_C_2X /**< Newest supported C language. */
69 #define LANG_C_ANY LANG_MAX(C_NEW) /**< Any C language. */
70
71 #define LANG_CPP_OLD LANG_CPP_98 /**< Oldest supported C++ language. */
72 #define LANG_CPP_98 (1u << 9) /**< C++ 98. */
73 #define LANG_CPP_03 (1u << 10) /**< C++ 03. */
74 #define LANG_CPP_11 (1u << 11) /**< C++ 11. */
75 #define LANG_CPP_14 (1u << 12) /**< C++ 14. */
76 #define LANG_CPP_17 (1u << 13) /**< C++ 17. */
77 #define LANG_CPP_20 (1u << 14) /**< C++ 20. */
78 #define LANG_CPP_23 (1u << 15) /**< C++ 23. */
79 #define LANG_CPP_NEW LANG_CPP_23 /**< Newest supported C++ language. */
80 #define LANG_CPP_ANY LANG_MASK_CPP /**< Any C++ language. */
81
82 /** Language eXtensions for Embedded C. */
83 #define LANGX_EMC (1u << 7)
84
85 /** Language eXtensions for Unified Parallel C. */
86 #define LANGX_UPC (1u << 8)
87
88 /**
89 * EMC: Embedded C, or more formally, _Programming languages - C - Extensions
90 * to support embedded processors_, ISO/IEC TR 18037:2008, which is based
91 * on C99, ISO/IEC 9899:1999.
92 *
93 * @note This is not a distinct language in cdecl, i.e., the user can't set the
94 * language to "Embedded C" specifically. It's used to mark keywords as being
95 * available only in the Embedded C extensions to C99 instead of "plain" C99 so
96 * that if a user does:
97 *
98 * cdecl> declare _Sat as int
99 * 9: warning: "_Sat" is a keyword in C99 (with Embedded C extensions)
100 *
101 * in a language other than C99, they'll get a warning.
102 *
103 * @sa [Information Technology — Programming languages - C - Extensions to support embedded processors](http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1169.pdf)
104 */
105 #define LANG_C_99_EMC (LANG_C_99 | LANGX_EMC)
106
107 /**
108 * UPC: _Unified Parallel C_, which is based on C99, ISO/IEC 9899:1999.
109 *
110 * @sa [Unified Parallel C](http://upc-lang.org/)
111 */
112 #define LANG_C_99_UPC (LANG_C_99 | LANGX_UPC)
113
114 // bit masks
115 #define LANG_MASK_C 0x01FFu /**< C languages bitmask. */
116 #define LANG_MASK_CPP 0xFE00u /**< C++ languages bitmask. */
117 #define LANGX_MASK 0x0180u /**< Language extensions bitmask. */
118
119 /**
120 * All languages up to and including \a L.
121 *
122 * @param L The language _without_ the `LANG_` prefix.
123 */
124 #define LANG_MAX(L) ((LANG_##L | (LANG_##L - 1u)) & ~LANGX_MASK)
125
126 /**
127 * All languages \a L and later.
128 *
129 * @param L The language _without_ the `LANG_` prefix.
130 */
131 #define LANG_MIN(L) (~(LANG_##L - 1u) & ~LANGX_MASK)
132
133 /**
134 * C-only languages up to and including \a L.
135 *
136 * @param L The language _without_ the `LANG_` prefix.
137 */
138 #define LANG_C_MAX(L) LANG_MAX( C_##L )
139
140 /**
141 * C-only languages \a L and later.
142 *
143 * @param L The language _without_ the `LANG_` prefix.
144 */
145 #define LANG_C_MIN(L) (LANG_MIN( C_##L ) & LANG_MASK_C)
146
147 /**
148 * C++-only languages up to and including \a L.
149 *
150 * @param L The language _without_ the `LANG_` prefix.
151 */
152 #define LANG_CPP_MAX(L) (LANG_MAX( CPP_##L ) & LANG_MASK_CPP)
153
154 /**
155 * C++-only languages \a L and later.
156 *
157 * @param L The language _without_ the `LANG_` prefix.
158 */
159 #define LANG_CPP_MIN(L) LANG_MIN( CPP_##L )
160
161 /**
162 * C-only languages up to and including \a CL; and C++-only languages up to and
163 * including \a CPPL.
164 *
165 * @param CL The C language _without_ the `LANG_` prefix.
166 * @param CPPL The C++ language _without_ the `LANG_` prefix.
167 */
168 #define LANG_C_CPP_MAX(CL,CPPL) (LANG_C_MAX(CL) | LANG_CPP_MAX(CPPL))
169
170 /**
171 * C-only languages \a CL and later; and C++-only languages \a CPPL and later.
172 *
173 * @param CL The C language _without_ the `LANG_` prefix.
174 * @param CPPL The C++ language _without_ the `LANG_` prefix.
175 */
176 #define LANG_C_CPP_MIN(CL,CPPL) (LANG_C_MIN(CL) | LANG_CPP_MIN(CPPL))
177
178 /**
179 * C/C++ language(s)/literal pairs: for the given language(s) only, use the
180 * given literal. This allows different languages to use different literals,
181 * e.g., `_Noreturn` for C and `noreturn` for C++.
182 */
183 struct c_lang_lit {
184 c_lang_id_t lang_ids; ///< Language(s) literal is in.
185 char const *literal; ///< The literal.
186 };
187
188 /**
189 * Convenience macro for specifying a constant array of \ref c_lang_lit.
190 *
191 * @param ... The array of \ref c_lang_lit elements.
192 */
193 #define C_LANG_LIT(...) (c_lang_lit_t const[]){ __VA_ARGS__ }
194
195 /**
196 * Shorthand for the common case of getting whether the current language is
197 * among the languages specified by \a LANG_MACRO.
198 *
199 * @param LANG_MACRO A `LANG_*` macro without the `LANG_` prefix.
200 * @return Returns `true` only if the current language is among the languages
201 * specified by \a LANG_MACRO.
202 */
203 #define OPT_LANG_IS(LANG_MACRO) opt_lang_is_any( LANG_##LANG_MACRO )
204
205 ///////////////////////////////////////////////////////////////////////////////
206
207 /**
208 * A mapping between a language name and its corresponding \ref c_lang_id_t.
209 */
210 struct c_lang {
211 char const *name; ///< Language name.
212 bool is_alias; ///< Alias for another language name?
213 c_lang_id_t lang_id; ///< Language bit.
214 };
215
216 ////////// extern functions ///////////////////////////////////////////////////
217
218 /**
219 * Gets all the language(s) \a lang_id and newer.
220 *
221 * @param lang_id The language. Exactly one language must be set.
222 * @return Returns the bitwise-or of all language(s) \a lang_id and newer.
223 *
224 * @sa c_lang_oldest()
225 * @sa c_lang_newer()
226 * @sa c_lang_newest()
227 * @sa opt_lang_and_newer()
228 */
229 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_and_newer(c_lang_id_t lang_id)230 c_lang_id_t c_lang_and_newer( c_lang_id_t lang_id ) {
231 lang_id &= ~LANGX_MASK;
232 assert( exactly_one_bit_set( lang_id ) );
233 return BITS_GE( lang_id );
234 }
235
236 /**
237 * Gets the \ref c_lang_id_t corresponding to the given string
238 * (case insensitive).
239 *
240 * @param name The language name (case insensitive) to get the corresponding
241 * \ref c_lang_id_t for.
242 * @return Returns said language or #LANG_NONE if \a name doesn't correspond to
243 * any supported language.
244 */
245 PJL_WARN_UNUSED_RESULT
246 c_lang_id_t c_lang_find( char const *name );
247
248 /**
249 * Gets whether \a lang_ids is any version of C.
250 *
251 * @param lang_ids The bitwise-or of language(s) to check.
252 * @return Returns `true` only if any one of \a lang_ids is a version of C.
253 *
254 * @sa c_lang_is_cpp()
255 */
256 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_is_c(c_lang_id_t lang_ids)257 bool c_lang_is_c( c_lang_id_t lang_ids ) {
258 return (lang_ids & LANG_MASK_C) != LANG_NONE;
259 }
260
261 /**
262 * Gets whether \a lang_ids is any version of C++.
263 *
264 * @param lang_ids The bitwise-of of language(s) to check.
265 * @return Returns `true` only if any one of \a lang_ids is a version of C++.
266 *
267 * @sa c_lang_is_c()
268 */
269 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_is_cpp(c_lang_id_t lang_ids)270 bool c_lang_is_cpp( c_lang_id_t lang_ids ) {
271 return (lang_ids & LANG_MASK_CPP) != LANG_NONE;
272 }
273
274 /**
275 * Gets the "coarse" name of \a lang_ids.
276 *
277 * @param lang_ids The bitwise-or of language(s).
278 * @return If:
279 * + \a lang_ids contains any version of both C and C++, returns NULL;
280 * otherwise:
281 * + \a lang_ids contains any version of C, returns `"C"`.
282 * + \a lang_ids contains any version of C++, returns `"C++"`.
283 *
284 * @sa c_lang_name()
285 */
286 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_coarse_name(c_lang_id_t lang_ids)287 char const* c_lang_coarse_name( c_lang_id_t lang_ids ) {
288 return c_lang_is_c( lang_ids ) ?
289 (c_lang_is_cpp( lang_ids ) ? NULL : "C") :
290 "C++";
291 }
292
293 /**
294 * Gets the literal appropriate for the current language.
295 *
296 * @param lang_lit A \ref c_lang_lit array. The last element _must_ always
297 * have a `lang_ids` value of #LANG_ANY. If the corresponding `literal` value
298 * is NULL, it means there is no appropriate literal for the current language.
299 * @return Returns said literal or NULL if there is no appropriate literal for
300 * the current language.
301 */
302 PJL_WARN_UNUSED_RESULT
303 char const* c_lang_literal( c_lang_lit_t const *lang_lit );
304
305 /**
306 * Gets the printable name of \a lang_id.
307 *
308 * @param lang_id The language to get the name of. Exactly one language must
309 * be set.
310 * @return Returns said name.
311 *
312 * @sa c_lang_coarse_name()
313 */
314 PJL_WARN_UNUSED_RESULT
315 char const* c_lang_name( c_lang_id_t lang_id );
316
317 /**
318 * Gets the bitwise-or of language(s) that are newer than \a lang_id.
319 *
320 * @param lang_id The language. Exactly one language must be set.
321 * @return Returns the bitwise-or of languages \a lang_id or newer; or
322 * #LANG_NONE if no language(s) are newer.
323 *
324 * @sa c_lang_and_newer()
325 * @sa c_lang_newest()
326 * @sa c_lang_oldest()
327 * @sa opt_lang_newer()
328 */
329 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_newer(c_lang_id_t lang_id)330 c_lang_id_t c_lang_newer( c_lang_id_t lang_id ) {
331 lang_id &= ~LANGX_MASK;
332 assert( exactly_one_bit_set( lang_id ) );
333 return BITS_GT( lang_id );
334 }
335
336 /**
337 * Gets the newest language among \a lang_ids.
338 *
339 * @param lang_ids The bitwise-or of language(s).
340 * @return Returns said language.
341 *
342 * @sa c_lang_and_newer()
343 * @sa c_lang_newer()
344 * @sa c_lang_oldest()
345 */
346 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_newest(c_lang_id_t lang_ids)347 c_lang_id_t c_lang_newest( c_lang_id_t lang_ids ) {
348 return ms_bit1_32( lang_ids & ~LANGX_MASK ) | (lang_ids & LANGX_MASK);
349 }
350
351 /**
352 * Iterates to the next C/C++ language.
353 *
354 * @param lang A pointer to the previous language. For the first iteration,
355 * NULL should be passed.
356 * @return Returns the next C/C++ language or NULL for none.
357 *
358 * @sa #FOREACH_LANG()
359 */
360 PJL_WARN_UNUSED_RESULT
361 c_lang_t const* c_lang_next( c_lang_t const *lang );
362
363 /**
364 * Convenience macro for iterating over all languages.
365 *
366 * @param VAR The \ref c_lang loop variable.
367 *
368 * @sa c_lang_next()
369 */
370 #define FOREACH_LANG(VAR) \
371 for ( c_lang_t const *VAR = NULL; (VAR = c_lang_next( VAR )) != NULL; )
372
373 /**
374 * Gets the oldest language among \a lang_ids.
375 *
376 * @param lang_ids The bitwise-or of language(s).
377 * @return Returns said language.
378 *
379 * @sa c_lang_and_newer()
380 * @sa c_lang_newer()
381 * @sa c_lang_newest()
382 */
383 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
c_lang_oldest(c_lang_id_t lang_ids)384 c_lang_id_t c_lang_oldest( c_lang_id_t lang_ids ) {
385 return ls_bit1_32( lang_ids & ~LANGX_MASK ) | (lang_ids & LANGX_MASK);
386 }
387
388 /**
389 * Sets the current language and the corresponding prompt.
390 *
391 * @param lang_id The language to set. Exactly one language must be set.
392 */
393 void c_lang_set( c_lang_id_t lang_id );
394
395 /**
396 * Gets a string specifying when a particular language feature won't be legal
397 * until or has been illegal since, if ever. It is presumed to follow `"...
398 * not supported"` (with no trailing space).
399 *
400 * @param lang_ids The bitwise-or of legal language(s).
401 * @return If:
402 * + \a lang_ids is #LANG_NONE, returns the empty string.
403 * + \a lang_ids contains exactly one language, returns `" unless "` followed
404 * by the name of that language.
405 * + The current language is C and \a lang_ids does not contain any version of
406 * C, returns `" in C"`.
407 * + The current language is C++ and \a lang_ids does not contain any version
408 * of C++, returns `" in C++"`.
409 * + If the current language is older than oldest language in \a lang_ids,
410 * returns `" until "` followed by the name of the oldest C version (if the
411 * current language is C) or the name of the oldest C++ version (if the
412 * current language is C++).
413 * + Otherwise returns `" since "` followed by the name of the newest C
414 * version (if the current language is C) or the name of the newest C++
415 * version (if the current language is C++).
416 *
417 * @warning The pointer returned is to a static buffer, so you can't do
418 * something like call this twice in the same `printf()` statement.
419 */
420 PJL_WARN_UNUSED_RESULT
421 char const* c_lang_which( c_lang_id_t lang_ids );
422
423 /**
424 * Convenience function for calling c_lang_and_newer() with \ref opt_lang.
425 *
426 * @return Returns the bitwise-or all language(s) \ref opt_lang and newer.
427 *
428 * @sa c_lang_and_newer()
429 * @sa c_lang_newer()
430 * @sa c_lang_newest()
431 * @sa opt_lang_newer()
432 */
433 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
opt_lang_and_newer(void)434 c_lang_id_t opt_lang_and_newer( void ) {
435 return c_lang_and_newer( opt_lang );
436 }
437
438 /**
439 * Convenience function for checking whether \ref opt_lang is among \a
440 * lang_ids.
441 *
442 * @param lang_ids The bitwise-or of language(s) to check.
443 * @return Returns `true` only if it is.
444 */
445 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
opt_lang_is_any(c_lang_id_t lang_ids)446 bool opt_lang_is_any( c_lang_id_t lang_ids ) {
447 return (opt_lang & lang_ids) != LANG_NONE;
448 }
449
450 /**
451 * Convenience function for calling c_lang_newer() with \ref opt_lang.
452 *
453 * @return Returns the bitwise-or of languages newer than \ref opt_lang; or
454 * #LANG_NONE if no language(s) are newer.
455 *
456 * @sa c_lang_and_newer()
457 * @sa c_lang_newer()
458 * @sa c_lang_newest()
459 * @sa opt_lang_and_newer()
460 */
461 C_LANG_INLINE PJL_WARN_UNUSED_RESULT
opt_lang_newer(void)462 c_lang_id_t opt_lang_newer( void ) {
463 return c_lang_newer( opt_lang );
464 }
465
466 ///////////////////////////////////////////////////////////////////////////////
467
468 /** @} */
469
470 _GL_INLINE_HEADER_END
471
472 #endif /* cdecl_c_lang_H */
473 /* vim:set et sw=2 ts=2: */
474