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&nbsp;18037:2008, which is based
91  * on C99, ISO/IEC&nbsp;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&nbsp;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