#!/usr/bin/env python3 # A script to generate enums in different languages from a CSV file. # A CSV File contains # enum1, 0 # enum2, .. # ... # enumn, n # where 0,...,n are numbers. # # Written by Supakorn "Jamie" Rassameemasmuang from typing import List, Tuple, Any, Union from datetime import datetime, timezone import io import argparse import os import sys import time import re def cleanComment(s): return re.sub(r' *#',' ',s) def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('-language', '--language', type=str, required=True) parser.add_argument('-o', '--output', type=str, required=True) parser.add_argument('-i', '--input', type=str, required=True) parser.add_argument('-name', '--name', type=str, required=True) parser.add_argument('-xopt', '--xopt', type=str, nargs='*') return parser.parse_args() def create_enums(filename: str) -> List[Union[Tuple[str, int, str], Tuple[str, int]]]: final_list=[] with io.open(filename,newline='') as rawfile: for line in rawfile.readlines(): if line.startswith('#') or line.strip() == '': continue raw_line=line.strip().split(',') raw_str, raw_number=raw_line[0:2] comment=None if len(raw_line)>=3: comment=raw_line[-1] final_list.append((raw_str.strip(), int(raw_number.strip()),comment)) else: final_list.append((raw_str.strip(), int(raw_number.strip()))) return final_list def datetime_now(): return datetime.fromtimestamp( int(os.environ.get('SOURCE_DATE_EPOCH', time.time())),tz=timezone.utc) def generate_enum_cpp(outname, enums, name, comment=None, *args, **kwargs): with io.open(outname, 'w') as fil: fil.write('// Enum class for {0}\n'.format(name)) if comment is not None: fil.write('// {0}\n'.format(comment)) fil.write('// Generated at {0}\n\n'.format(datetime_now())) if 'namespace' in kwargs: fil.write('namespace {0}\n'.format(kwargs['namespace'])) fil.write('{\n') fil.write('enum {0} : uint32_t\n'.format(name)) fil.write('{\n') for enumTxt, enumNum, *ar in enums: fil.write('{0}={1},\n'.format(enumTxt, enumNum)) if len(ar) > 0: comment=cleanComment(ar[-1]) if comment is not None: fil.write('// {0}\n\n'.format(comment.strip())) fil.write('};\n\n') if 'namespace' in kwargs: fil.write('}} // namespace {0}\n'.format(kwargs['namespace'])) fil.write('// End of File\n') def generate_enum_java(outname, enums, name, comment=None, *args, **kwargs): with io.open(outname, 'w') as fil: fil.write('// Enum class for {0}\n'.format(name)) if comment is not None: fil.write('// {0}\n'.format(comment)) fil.write('// Generated at {0}\n\n'.format(datetime_now())) if 'package' in kwargs: fil.write('package {0};\n'.format(kwargs['package'])) fil.write('\n') fil.write('public enum {0} {{\n'.format(name)) spaces = kwargs.get('spaces', 4) spaces_tab=' '*spaces for i in range(len(enums)): enumTxt, enumNum, *ar = enums[i] endsep=',' if i < len(enums) - 1 else ';' fil.write('{2}{0}({1}){3}\n'.format(enumTxt,enumNum,spaces_tab,endsep)) if len(ar) > 0: comment=cleanComment(ar[-1]) if comment is not None: fil.write('// {0}\n\n'.format(comment.strip())) out_lines=[ '', '{0}(int value) {{'.format(name), '{0}this.value=value;'.format(spaces_tab), '}', 'public String toString() {', '{0}return Integer.toString(value);'.format(spaces_tab), '}', 'private int value;'] for line in out_lines: fil.write(spaces_tab) fil.write(line) fil.write('\n') fil.write('};\n\n') fil.write('// End of File\n') def generate_enum_asy(outname, enums, name, comment=None, *args, **kwargs): with io.open(outname, 'w') as fil: fil.write('// Enum class for {0}\n'.format(name)) if comment is not None: fil.write('// {0}\n'.format(comment)) fil.write('// Generated at {0}\n\n'.format(datetime_now())) fil.write('struct {0}\n'.format(name)) fil.write('{\n') for enumTxt, enumNum, *ar in enums: fil.write(' int {0}={1};\n'.format(enumTxt, enumNum)) if len(ar) > 0: comment=cleanComment(ar[-1]) if comment is not None: fil.write('// {0}\n\n'.format(comment.strip())) fil.write('};\n\n') fil.write('{0} {0};'.format(name)) fil.write('// End of File\n') def generate_enum_py(outname, enums, name, comment=None, *args, **kwargs): with io.open(outname, 'w') as fil: fil.write('#!/usr/bin/env python3\n') fil.write('# Enum class for {0}\n'.format(name)) if comment is not None: fil.write('""" {0} """\n'.format(comment)) fil.write('# Generated at {0}\n\n'.format(datetime_now())) fil.write('class {0}:\n'.format(name)) for enumTxt, enumNum, *ar in enums: fil.write(' {0}_{2}={1}\n'.format(name, enumNum, enumTxt)) if len(ar) > 0: comment=cleanComment(ar[-1]) if comment is not None: fil.write(' # {0}\n\n'.format(comment.strip())) fil.write('# End of File\n') def main(): arg = parse_args() if arg.language in {'python', 'py'}: fn = generate_enum_py elif arg.language in {'cxx', 'c++', 'cpp'}: fn = generate_enum_cpp elif arg.language in {'asy', 'asymptote'}: fn = generate_enum_asy elif arg.language in {'java'}: fn = generate_enum_java else: return 1 custom_args = dict() if arg.xopt is not None: for xopt in arg.xopt: key, val = xopt.split('=') custom_args[key] = val enums = create_enums(arg.input) fn(arg.output, enums, arg.name, 'AUTO-GENERATED from '+arg.input, **custom_args) if __name__ == '__main__': sys.exit(main() or 0)