from re import compile, MULTILINE from os import walk, getcwd notice = ('''/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ ''') if not getcwd().endswith('src'): print('Run this from the src directory!') print('(Invoke as \'python ../apps/EnumUtils/enumutils_describe.py\')') exit(1) EnumPattern = compile(r'//\s*EnumUtils: DESCRIBE THIS(?:\s*\(in ([^\)]+)\))?\s+enum\s+([0-9A-Za-z]+)[^\n]*\s*{([^}]+)};') EnumValuesPattern = compile(r'\s+\S.+?(,|$)[^\n]*') EnumValueNamePattern = compile(r'^\s*([a-zA-Z0-9_]+)', flags=MULTILINE) EnumValueSkipLinePattern = compile(r'^\s*//') EnumValueCommentPattern = compile(r'//,?[ \t]*([^\n]+)$') CommentMatchFormat = compile(r'^(((TITLE +(.+?))|(DESCRIPTION +(.+?))) *){1,2}$') CommentSkipFormat = compile(r'^SKIP *$') def strescape(str): res = '' for char in str: if char in ('\\', '"') or not (32 <= ord(char) < 127): res += ('\\%03o' % ord(char)) else: res += char return '"' + res + '"' def processFile(path, filename): input = open('%s/%s.h' % (path, filename),'r') if input is None: print('Failed to open %s.h' % filename) return file = input.read() enums = [] for enum in EnumPattern.finditer(file): prefix = enum.group(1) or '' name = enum.group(2) values = [] for value in EnumValuesPattern.finditer(enum.group(3)): valueData = value.group(0) valueNameMatch = EnumValueNamePattern.search(valueData) if valueNameMatch is None: if EnumValueSkipLinePattern.search(valueData) is None: print('Name of value not found: %s' % repr(valueData)) continue valueName = valueNameMatch.group(1) valueCommentMatch = EnumValueCommentPattern.search(valueData) valueComment = None if valueCommentMatch: valueComment = valueCommentMatch.group(1) valueTitle = None valueDescription = None if valueComment is not None: if CommentSkipFormat.match(valueComment) is not None: continue commentMatch = CommentMatchFormat.match(valueComment) if commentMatch is not None: valueTitle = commentMatch.group(4) valueDescription = commentMatch.group(6) else: valueDescription = valueComment if valueTitle is None: valueTitle = valueName if valueDescription is None: valueDescription = '' values.append((valueName, valueTitle, valueDescription)) enums.append((prefix + name, prefix, values)) print('%s.h: Enum %s parsed with %d values' % (filename, name, len(values))) if not enums: return print('Done parsing %s.h (in %s)\n' % (filename, path)) output = open('%s/enuminfo_%s.cpp' % (path, filename), 'w') if output is None: print('Failed to create enuminfo_%s.cpp' % filename) return # write output file output.write(notice) output.write('#include "%s.h"\n' % filename) output.write('#include "Define.h"\n') output.write('#include "SmartEnum.h"\n') output.write('#include \n') output.write('\n') output.write('namespace Acore::Impl::EnumUtilsImpl\n') output.write('{\n') for name, prefix, values in enums: tag = ('data for enum \'%s\' in \'%s.h\' auto-generated' % (name, filename)) output.write('\n') output.write('/*' + ('*'*(len(tag)+2)) + '*\\\n') output.write('|* ' + tag + ' *|\n') output.write('\\*' + ('*'*(len(tag)+2)) + '*/\n') output.write('template <>\n') output.write('AC_API_EXPORT EnumText EnumUtils<%s>::ToString(%s value)\n' % (name, name)) output.write('{\n') output.write(' switch (value)\n') output.write(' {\n') for label, title, description in values: output.write(' case %s: return { %s, %s, %s };\n' % (prefix + label, strescape(label), strescape(title), strescape(description))) output.write(' default: throw std::out_of_range("value");\n') output.write(' }\n') output.write('}\n') output.write('\n') output.write('template <>\n') output.write('AC_API_EXPORT size_t EnumUtils<%s>::Count() { return %d; }\n' % (name, len(values))) output.write('\n') output.write('template <>\n') output.write('AC_API_EXPORT %s EnumUtils<%s>::FromIndex(size_t index)\n' % (name, name)) output.write('{\n') output.write(' switch (index)\n') output.write(' {\n') for (i, (label, title, description)) in enumerate(values): output.write(' case %d: return %s;\n' % (i, prefix + label)) output.write(' default: throw std::out_of_range("index");\n') output.write(' }\n') output.write('}\n') output.write('\n') output.write('template <>\n') output.write('AC_API_EXPORT size_t EnumUtils<%s>::ToIndex(%s value)\n' % (name, name)) output.write('{\n') output.write(' switch (value)\n') output.write(' {\n') for (i, (label, title, description)) in enumerate(values): output.write(' case %s: return %d;\n' % (prefix + label, i)) output.write(' default: throw std::out_of_range("value");\n') output.write(' }\n') output.write('}\n') output.write('}\n') FilenamePattern = compile(r'^(.+)\.h$') for root, dirs, files in walk('.'): for n in files: nameMatch = FilenamePattern.match(n) if nameMatch is not None: processFile(root, nameMatch.group(1))