Freeciv21
Develop your civilization from humble roots to a global empire
generate_specenum.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright (c) 1996-2021 Freeciv21 and Freeciv
4 # ,_, contributors. This file is part of Freeciv21.
5 # (0_0)_----------_ Freeciv21 is free software: you can
6 # (_____) |~' redistribute it and/or modify it under the
7 # `-"-"-' / terms of the GNU General Public License
8 # `|__|~-----~|__| as published by the Free Software Foundation,
9 # either version 3 of the License,
10 # or (at your option) any later version. You should
11 # have received a copy of the GNU General Public License along
12 # with Freeciv21. If not, see https://www.gnu.org/licenses/.
13 
14 # The maximum number of enumerators.
15 max_enum_values=150
16 
17 # Here are push all defined macros.
18 macros=[]
19 
20 import os, sys
21 
22 def make_header(file):
23  file.write('''
24  /**************************************************************************
25  * THIS FILE WAS GENERATED *
26  * Script: utility/generate_specenum.py *
27  * DO NOT CHANGE THIS FILE *
28  **************************************************************************/
29 
30 /* Copyright (c) 1996-2021 Freeciv21 and Freeciv
31 # ,_, contributors. This file is part of Freeciv21.
32 # (0_0)_----------_ Freeciv21 is free software: you can
33 # (_____) |~' redistribute it and/or modify it under the
34 # `-"-"-' / terms of the GNU General Public License
35 # `|__|~-----~|__| as published by the Free Software Foundation,
36 # either version 3 of the License,
37 # or (at your option) any later version. You should
38 # have received a copy of the GNU General Public License along
39 # with Freeciv21. If not, see https://www.gnu.org/licenses/.*/
40 ''')
41 
43  file.write('''
44 /*
45  * Include this file to define tools to manage enumerators. First of all,
46  * before including this file, you *MUST* define the following macros:
47  * - SPECENUM_NAME: is the name of the enumeration (e.g. 'foo' for defining
48  * 'enum foo').
49  * - SPECENUM_VALUE%d: define like this all values of your enumeration type
50  * (e.g. '#define SPECENUM_VALUE0 FOO_FIRST').
51  *
52  * The following macros *CAN* be defined:
53  * - SPECENUM_INVALID: specifies a value that your 'foo_invalid()' function
54  * will return. Note it cannot be a declared value with SPECENUM_VALUE%d.
55  * - SPECENUM_BITWISE: defines if the enumeration should be like
56  * [1, 2, 4, 8, etc...] instead of the default of [0, 1, 2, 3, etc...].
57  * - SPECENUM_ZERO: can be defined only if SPECENUM_BITWISE was also defined.
58  * It defines a 0 value. Note that if you don't declare this value, 0 passed
59  * to the 'foo_is_valid()' function will return 0.
60  * - SPECENUM_COUNT: a name for the maximum enumeration number plus 1. For
61  * enums where every element from 0 to the maximum is defined, this is the
62  * number of elements in the enum. This value is suitable to size an array
63  * indexed by the enum. It can not be used in combination with
64  * SPECENUM_BITWISE. SPECENUM_is_valid() will return the invalid element
65  * for it.
66  *
67  * SPECENUM_VALUE%dNAME, SPECENUM_ZERONAME, SPECENUM_COUNTNAME: Can be used
68  * to bind a string to the particular enumerator to be returned by
69  * SPECENUM_name(), etc. If not defined, the default name for 'FOO_FIRST'
70  * is '"FOO_FIRST"'. A name can be qualified. The qualification will only
71  * be used for its translation. The returned name will be unqualified. To
72  * mark a name as translatable use N_().
73  *
74  * SPECENUM_NAMEOVERRIDE: call callback function foo_name_cb(enum foo),
75  * defined by specnum user, to get name of the enum value. If the function
76  * returns NULL, compiled in names are used.
77  *
78  * SPECENUM_BITVECTOR: specifies the name of a bit vector for the enum
79  * values. It can not be used in combination with SPECENUM_BITWISE.
80  *
81  * Assuming SPECENUM_NAME were 'foo', including this file would provide
82  * the definition for the enumeration type 'enum foo', and prototypes for
83  * the following functions:
84  * bool foo_is_bitwise(void);
85  * enum foo foo_min(void);
86  * enum foo foo_max(void);
87  * enum foo foo_invalid(void);
88  * bool foo_is_valid(enum foo);
89  *
90  * enum foo foo_begin(void);
91  * enum foo foo_end(void);
92  * enum foo foo_next(enum foo);
93  *
94  * const char *foo_name(enum foo);
95  * const char *foo_translated_name(enum foo);
96  * enum foo foo_by_name(const char *name,
97  * int (*strcmp_func)(const char *, const char *));
98  *
99  * Example:
100  * #define SPECENUM_NAME test
101  * #define SPECENUM_BITWISE
102  * #define SPECENUM_VALUE0 TEST0
103  * #define SPECENUM_VALUE1 TEST1
104  * #define SPECENUM_VALUE3 TEST3
105  * #include "specenum_gen.h"
106  *
107  * {
108  * static const char *strings[] = {
109  * "TEST1", "test3", "fghdf", NULL
110  * };
111  * enum test e;
112  * int i;
113  *
114  * qDebug("enum test [%d; %d]%s",
115  * test_min(), test_max(), test_bitwise ? " bitwise" : "");
116  *
117  * for (e = test_begin(); e != test_end(); e = test_next(e)) {
118  * qDebug("Value %d is %s", e, test_name(e));
119  * }
120  *
121  * for (i = 0; strings[i]; i++) {
122  * e = test_by_name(strings[i], mystrcasecmp);
123  * if (test_is_valid(e)) {
124  * qDebug("Value is %d for %s", e, strings[i]);
125  * } else {
126  * qDebug("%s is not a valid name", strings[i]);
127  * }
128  * }
129  * }
130  *
131  * Will output:
132  * enum test [1, 8] bitwise
133  * Value 1 is TEST0
134  * Value 2 is TEST1
135  * Value 8 is TEST3
136  * Value is 2 for TEST1
137  * Value is 8 for test3
138  * fghdf is not a valid name
139  */
140 ''')
141 
142 def make_macros(file):
143  file.write('''
144 
145 
146 /* Utility */
147 #include "fcintl.h" // translation
148 #include "log.h" // fc_assert
149 #include "shared.h" // ARRAY_SIZE
150 
151 #ifndef SPECENUM_NAME
152 #error Must define a SPECENUM_NAME to use this header
153 #endif
154 
155 #define SPECENUM_PASTE_(x, y) x ## y
156 #define SPECENUM_PASTE(x, y) SPECENUM_PASTE_(x, y)
157 
158 #define SPECENUM_STRING_(x) #x
159 #define SPECENUM_STRING(x) SPECENUM_STRING_(x)
160 
161 #define SPECENUM_FOO(suffix) SPECENUM_PASTE(SPECENUM_NAME, suffix)
162 
163 #ifndef SPECENUM_INVALID
164 #define SPECENUM_INVALID ((enum SPECENUM_NAME) -1)
165 #endif
166 
167 #ifdef SPECENUM_BITWISE
168 #ifdef SPECENUM_COUNT
169 #error Cannot define SPECENUM_COUNT when SPECENUM_BITWISE is defined.
170 #endif
171 #define SPECENUM_VALUE(value) (1 << value)
172 #else /* SPECENUM_BITWISE */
173 #ifdef SPECENUM_ZERO
174 #error Cannot define SPECENUM_ZERO when SPECENUM_BITWISE is not defined.
175 #endif
176 #define SPECENUM_VALUE(value) (value)
177 #endif /* SPECENUM_BITWISE */
178 
179 #ifdef SPECENUM_BITVECTOR
180 #include "bitvector.h"
181 #ifdef SPECENUM_BITWISE
182 #error SPECENUM_BITWISE and SPECENUM_BITVECTOR cannot both be defined.
183 #endif /* SPECENUM_BITWISE */
184 #endif /* SPECENUM_BITVECTOR */
185 
186 #undef SPECENUM_MIN_VALUE
187 #undef SPECENUM_MAX_VALUE
188 ''')
189  macros.append("SPECENUM_NAME")
190  macros.append("SPECENUM_PASTE_")
191  macros.append("SPECENUM_PASTE")
192  macros.append("SPECENUM_STRING_")
193  macros.append("SPECENUM_STRING")
194  macros.append("SPECENUM_FOO")
195  macros.append("SPECENUM_INVALID")
196  macros.append("SPECENUM_BITWISE")
197  macros.append("SPECENUM_VALUE")
198  macros.append("SPECENUM_ZERO")
199  macros.append("SPECENUM_MIN_VALUE")
200  macros.append("SPECENUM_MAX_VALUE")
201  macros.append("SPECENUM_SIZE")
202  macros.append("SPECENUM_NAMEOVERRIDE")
203  macros.append("SPECENUM_BITVECTOR")
204 
205 def make_enum(file):
206  file.write('''
207 /* Enumeration definition. */
208 enum SPECENUM_NAME {
209 #ifdef SPECENUM_ZERO
210  SPECENUM_ZERO = 0,
211 #endif
212 ''')
213 
214  for i in range(max_enum_values):
215  file.write('''
216 #ifdef SPECENUM_VALUE%d
217  SPECENUM_VALUE%d = SPECENUM_VALUE(%d),
218 # ifndef SPECENUM_MIN_VALUE
219 # define SPECENUM_MIN_VALUE SPECENUM_VALUE%d
220 # endif
221 # ifdef SPECENUM_MAX_VALUE
222 # undef SPECENUM_MAX_VALUE
223 # endif
224 # define SPECENUM_MAX_VALUE SPECENUM_VALUE%d
225 # ifdef SPECENUM_SIZE
226 # undef SPECENUM_SIZE
227 # endif
228 # define SPECENUM_SIZE (%d + 1)
229 #endif /* SPECENUM_VALUE%d */
230 '''%(i,i,i,i,i,i,i))
231 
232  file.write('''
233 #ifdef SPECENUM_COUNT
234  SPECENUM_COUNT = (SPECENUM_MAX_VALUE + 1),
235 #endif /* SPECENUM_COUNT */
236 };
237 ''')
238 
239  macros.append("SPECENUM_COUNT")
240  for i in range(max_enum_values):
241  macros.append("SPECENUM_VALUE%d"%i)
242 
243 def make_is_bitwise(file):
244  file.write('''
245 /**************************************************************************
246  Returns TRUE if this enumeration is in bitwise mode.
247 **************************************************************************/
248 fc__attribute((const))
249 static inline bool SPECENUM_FOO(_is_bitwise)(void)
250 {
251 #ifdef SPECENUM_BITWISE
252  return true;
253 #else
254  return false;
255 #endif
256 }
257 ''')
258 
259 def make_min(file):
260  file.write('''
261 /**************************************************************************
262  Returns the value of the minimal enumerator.
263 **************************************************************************/
264 fc__attribute((const))
265 static inline enum SPECENUM_NAME SPECENUM_FOO(_min)(void)
266 {
267  return SPECENUM_MIN_VALUE;
268 }
269 ''')
270 
271 def make_max(file):
272  file.write('''
273 /**************************************************************************
274  Returns the value of the maximal enumerator.
275 **************************************************************************/
276 fc__attribute((const))
277 static inline enum SPECENUM_NAME SPECENUM_FOO(_max)(void)
278 {
279  return SPECENUM_MAX_VALUE;
280 }
281 ''')
282 
283 def make_is_valid(file):
284  file.write('''
285 /**************************************************************************
286  Returns TRUE if this enumerator was defined.
287 **************************************************************************/
288 fc__attribute((const))
289 static inline bool SPECENUM_FOO(_is_valid)(enum SPECENUM_NAME enumerator)
290 {
291 #ifdef SPECENUM_BITWISE
292  static const unsigned long valid = (
293  0''')
294 
295  for i in range(max_enum_values):
296  file.write('''
297 # ifdef SPECENUM_VALUE%d
298  | SPECENUM_VALUE%d
299 # endif'''%(i,i))
300 
301  file.write('''
302  );
303 
304  FC_STATIC_ASSERT(sizeof(valid) * 8 >= SPECENUM_SIZE,
305  valid_sizeof_check);
306 
307 # ifdef SPECENUM_ZERO
308  if (enumerator == SPECENUM_ZERO) {
309  return true;
310  }
311 # endif
312  return (enumerator & valid) == enumerator;
313 #else
314  static const bool valid[] = {''')
315 
316  for i in range(max_enum_values):
317  file.write('''
318 # if %d < SPECENUM_SIZE
319 # ifdef SPECENUM_VALUE%d
320  true,
321 # else
322  false,
323 # endif
324 # endif'''%(i,i))
325 
326  file.write('''
327  };
328 
329  FC_STATIC_ASSERT(ARRAY_SIZE(valid) == SPECENUM_SIZE,
330  valid_array_size_check);
331 
332  return (enumerator >= 0 && enumerator < ARRAY_SIZE(valid)
333  && valid[enumerator]);
334 #endif /* SPECENUM_BITWISE */
335 }
336 ''')
337 
338 def make_invalid(file):
339  file.write('''
340 /**************************************************************************
341  Returns an invalid enumerator value.
342 **************************************************************************/
343 fc__attribute((const))
344 static inline enum SPECENUM_NAME SPECENUM_FOO(_invalid)(void)
345 {
346  fc_assert(!SPECENUM_FOO(_is_valid(SPECENUM_INVALID)));
347  return SPECENUM_INVALID;
348 }
349 ''')
350 
351 def make_begin(file):
352  file.write('''
353 /**************************************************************************
354  Beginning of the iteration of the enumerators.
355 **************************************************************************/
356 fc__attribute((const))
357 static inline enum SPECENUM_NAME SPECENUM_FOO(_begin)(void)
358 {
359  return SPECENUM_FOO(_min)();
360 }
361 ''')
362 
363 def make_end(file):
364  file.write('''
365 /**************************************************************************
366  End of the iteration of the enumerators.
367 **************************************************************************/
368 fc__attribute((const))
369 static inline enum SPECENUM_NAME SPECENUM_FOO(_end)(void)
370 {
371  return SPECENUM_FOO(_invalid)();
372 }
373 ''')
374 
375 def make_next(file):
376  file.write('''
377 /**************************************************************************
378  Find the next valid enumerator value.
379 **************************************************************************/
380 fc__attribute((const))
381 static inline enum SPECENUM_NAME SPECENUM_FOO(_next)(enum SPECENUM_NAME e)
382 {
383  do {
384 #ifdef SPECENUM_BITWISE
385  e = (enum SPECENUM_NAME)(e << 1);
386 #else
387  e = (enum SPECENUM_NAME)(e + 1);
388 #endif
389 
390  if (e > SPECENUM_FOO(_max)()) {
391  /* End of the iteration. */
392  return SPECENUM_FOO(_invalid)();
393  }
394  } while (!SPECENUM_FOO(_is_valid)(e));
395 
396  return e;
397 }
398 ''')
399 
400 def make_name(file):
401  file.write('''
402 #ifdef SPECENUM_NAMEOVERRIDE
403 const char *SPECENUM_FOO(_name_cb)(enum SPECENUM_NAME value);
404 #endif /* SPECENUM_NAMEOVERRIDE */
405 
406 /**************************************************************************
407  Returns the name of the enumerator.
408 **************************************************************************/
409 #ifndef SPECENUM_NAMEOVERRIDE
410 fc__attribute((const))
411 #endif
412 static inline const char *SPECENUM_FOO(_name)(enum SPECENUM_NAME enumerator)
413 {
414 #ifdef SPECENUM_COUNT
415  static const char *names[SPECENUM_SIZE + 1];
416 #else
417  static const char *names[SPECENUM_SIZE];
418 #endif
419  static bool initialized = false;
420 
421 #ifdef SPECENUM_NAMEOVERRIDE
422  {
423  const char *name = SPECENUM_FOO(_name_cb)(enumerator);
424 
425  if (name != NULL) {
426  return Qn_(name);
427  }
428  }
429 #endif /* SPECENUM_NAMEOVERRIDE */
430 
431  if (!initialized) {''')
432 
433  for i in range(max_enum_values):
434  file.write('''
435 #if %d < SPECENUM_SIZE
436 # ifndef SPECENUM_VALUE%d
437  names[%d] = NULL;
438 # elif defined(SPECENUM_VALUE%dNAME)
439  names[%d] = Qn_(SPECENUM_VALUE%dNAME);
440 # else
441  names[%d] = SPECENUM_STRING(SPECENUM_VALUE%d);
442 # endif
443 #endif'''%(i,i,i,i,i,i,i,i))
444  macros.append("SPECENUM_VALUE%dNAME"%i)
445 
446  file.write('''
447 #ifdef SPECENUM_COUNT
448 # ifdef SPECENUM_COUNTNAME
449  names[SPECENUM_COUNT] = Qn_(SPECENUM_COUNTNAME);
450 # else
451  names[SPECENUM_COUNT] = SPECENUM_STRING(SPECENUM_COUNT);
452 # endif
453 #endif
454  initialized = true;
455  }
456 
457 #ifdef SPECENUM_BITWISE
458 # ifdef SPECENUM_ZERO
459  if (enumerator == SPECENUM_ZERO) {
460 # ifdef SPECENUM_ZERONAME
461  return Qn_(SPECENUM_ZERONAME);
462 # else
463  return SPECENUM_STRING(SPECENUM_ZERO);
464 # endif
465  }
466 # endif
467  {
468  size_t i;
469 
470  for (i = 0; i < ARRAY_SIZE(names); i++) {
471  if (1 << i == enumerator) {
472  return names[i];
473  }
474  }
475  }
476 #else
477  if (enumerator < ARRAY_SIZE(names)) {
478  return names[enumerator];
479  }
480 #endif /* SPECENUM_BITWISE */
481  return NULL;
482 }
483 ''')
484  macros.append("SPECENUM_COUNTNAME")
485  macros.append("SPECENUM_ZERONAME")
486 
487 def make_by_name(file):
488  file.write('''
489 /**************************************************************************
490  Returns the enumerator for the name or *_invalid() if not found.
491 **************************************************************************/
492 static inline enum SPECENUM_NAME SPECENUM_FOO(_by_name)
493  (const char *name, int (*strcmp_func)(const char *, const char *))
494 {
495  enum SPECENUM_NAME e;
496  const char *enum_name;
497 
498  for (e = SPECENUM_FOO(_begin)(); e != SPECENUM_FOO(_end)();
499  e = SPECENUM_FOO(_next)(e)) {
500  if ((enum_name = SPECENUM_FOO(_name)(e))
501  && 0 == strcmp_func(name, enum_name)) {
502  return e;
503  }
504  }
505 
506  return SPECENUM_FOO(_invalid)();
507 }
508 ''')
509 
511  file.write('''
512 /**************************************************************************
513  Returns the translated name of the enumerator.
514 **************************************************************************/
515 #ifndef SPECENUM_NAMEOVERRIDE
516 fc__attribute((const))
517 #endif
518 static inline const char *
519 SPECENUM_FOO(_translated_name)(enum SPECENUM_NAME enumerator)
520 {
521 #ifdef SPECENUM_COUNT
522  static const char *names[SPECENUM_SIZE + 1];
523 #else
524  static const char *names[SPECENUM_SIZE];
525 #endif
526  static bool initialized = false;
527 
528 #ifdef SPECENUM_NAMEOVERRIDE
529  {
530  const char *name = SPECENUM_FOO(_name_cb)(enumerator);
531 
532  if (name != NULL) {
533  return Q_(name);
534  }
535  }
536 #endif /* SPECENUM_NAMEOVERRIDE */
537 
538  if (!initialized) {''')
539 
540  for i in range(max_enum_values):
541  file.write('''
542 #if %d < SPECENUM_SIZE
543 # ifndef SPECENUM_VALUE%d
544  names[%d] = NULL;
545 # elif defined(SPECENUM_VALUE%dNAME)
546  names[%d] = Q_(SPECENUM_VALUE%dNAME);
547 # else
548  names[%d] = SPECENUM_STRING(SPECENUM_VALUE%d);
549 # endif
550 #endif'''%(i,i,i,i,i,i,i,i))
551  macros.append("SPECENUM_VALUE%dNAME"%i)
552 
553  file.write('''
554 #ifdef SPECENUM_COUNT
555 # ifdef SPECENUM_COUNTNAME
556  names[SPECENUM_COUNT] = Q_(SPECENUM_COUNTNAME);
557 # else
558  names[SPECENUM_COUNT] = SPECENUM_STRING(SPECENUM_COUNT);
559 # endif
560 #endif
561  initialized = true;
562  }
563 
564 #ifdef SPECENUM_BITWISE
565 # ifdef SPECENUM_ZERO
566  if (enumerator == SPECENUM_ZERO) {
567 # ifdef SPECENUM_ZERONAME
568  return Q_(SPECENUM_ZERONAME);
569 # else
570  return SPECENUM_STRING(SPECENUM_ZERO);
571 # endif
572  }
573 # endif
574  {
575  size_t i;
576 
577  for (i = 0; i < ARRAY_SIZE(names); i++) {
578  if (1 << i == enumerator) {
579  return names[i];
580  }
581  }
582  }
583 #else
584  if (enumerator < ARRAY_SIZE(names)) {
585  return names[enumerator];
586  }
587 #endif /* SPECENUM_BITWISE */
588  return NULL;
589 }
590 ''')
591 
592 def make_bitvector(file):
593  file.write('''
594 #ifdef SPECENUM_BITVECTOR
595 BV_DEFINE(SPECENUM_BITVECTOR, (SPECENUM_MAX_VALUE + 1));
596 #endif /* SPECENUM_BITVECTOR */
597 ''')
598 
599 def make_undef(file):
600  for macro in macros:
601  file.write('''
602 #undef %s'''%macro)
603 
604  file.write('''
605 ''')
606 
607 # Main function.
608 def main():
609  target_name=sys.argv[1]
610 
611  output=open(target_name,"w")
612 
613  make_header(output)
614  make_documentation(output)
615  make_macros(output)
616  make_enum(output)
617  make_is_bitwise(output)
618  make_min(output)
619  make_max(output)
620  make_is_valid(output)
621  make_invalid(output)
622  make_begin(output)
623  make_end(output)
624  make_next(output)
625  make_name(output)
626  make_by_name(output)
627  make_translated_name(output)
628  make_bitvector(output)
629  make_undef(output)
630 
631  output.write('''
632 
633 ''')
634 
635  output.close()
636 
637 main()
def make_translated_name(file)
def make_documentation(file)