Serializer.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/python3
  2. # Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  3. # All rights reserved.
  4. # Code licensed under the BSD License.
  5. # http://www.anki3d.org/LICENSE
  6. import optparse
  7. import xml.etree.ElementTree as et
  8. import os
  9. import copy
  10. class Context:
  11. __slots__ = ["input_filenames", "output_filename", "output_file", "identation_level"]
  12. def __init__(self):
  13. self.input_filenames = None
  14. self.output_filename = None
  15. self.output_file = None
  16. self.identation_level = 0
  17. ctx = Context()
  18. class MemberInfo:
  19. __slots__ = ["name", "base_type", "array_size", "comment", "pointer", "constructor"]
  20. def __init__(self):
  21. self.name = None
  22. self.base_type = None
  23. self.array_size = "1"
  24. self.comment = None
  25. self.pointer = False
  26. self.constructor = None
  27. def is_dynamic_array(self, member_arr):
  28. if not self.pointer:
  29. return False
  30. for member in member_arr:
  31. if member.name == self.array_size:
  32. return True
  33. return False
  34. def is_pointer(self, member_arr):
  35. return self.pointer and not self.is_dynamic_array(member_arr)
  36. def parse_commandline():
  37. """ Parse the command line arguments """
  38. parser = optparse.OptionParser(usage="usage: %prog [options]", description="Create serialization code using XML")
  39. parser.add_option("-i",
  40. "--input",
  41. dest="inp",
  42. type="string",
  43. help="specify the XML files to parse. Seperate with :")
  44. parser.add_option("-o", "--output", dest="out", type="string", help="specify the .h file to populate")
  45. (options, args) = parser.parse_args()
  46. if not options.inp or not options.out:
  47. parser.error("argument is missing")
  48. global ctx
  49. ctx.input_filenames = options.inp.split(":")
  50. ctx.output_filename = options.out
  51. def writeln(txt):
  52. """ Write generated code to the output """
  53. global ctx
  54. ctx.output_file.write("%s%s\n" % ("\t" * ctx.identation_level, txt))
  55. def ident(number):
  56. """ Increase or recrease identation for the writeln """
  57. global ctx
  58. ctx.identation_level += number
  59. def gen_class(root_el):
  60. """ Parse a "class" element and generate the code. """
  61. name = root_el.get("name")
  62. # Write doxygen
  63. if not root_el.get("comment"):
  64. writeln("/// %s class." % name)
  65. else:
  66. writeln("/// %s." % root_el.get("comment"))
  67. # Body start
  68. writeln("class %s" % name)
  69. writeln("{")
  70. writeln("public:")
  71. # Parse members
  72. member_arr = []
  73. members_el = root_el.find("members")
  74. for member_el in members_el:
  75. member = MemberInfo()
  76. member.name = member_el.get("name")
  77. member.base_type = member_el.get("type")
  78. member.array_size = member_el.get("array_size")
  79. if not member.array_size:
  80. member.array_size = "1"
  81. if member_el.get("pointer") and member_el.get("pointer") == "true":
  82. member.pointer = True
  83. else:
  84. member.pointer = False
  85. if member_el.get("comment"):
  86. member.comment = member_el.get("comment")
  87. if member_el.get("constructor"):
  88. member.constructor = member_el.get("constructor")
  89. member_arr.append(member)
  90. # Write members
  91. ident(1)
  92. for member in member_arr:
  93. if member.comment:
  94. comment = "///< %s." % member.comment
  95. else:
  96. comment = ""
  97. if member.constructor:
  98. constructor = " %s" % member.constructor
  99. else:
  100. constructor = ""
  101. if member.pointer:
  102. writeln("%s* %s%s; %s" % (member.base_type, member.name, constructor, comment))
  103. elif member.array_size != "1":
  104. writeln("Array<%s, %s> %s%s; %s" % (member.base_type, member.array_size, member.name, constructor, comment))
  105. else:
  106. writeln("%s %s%s; %s" % (member.base_type, member.name, constructor, comment))
  107. ident(-1)
  108. # Before serialize make sure the dynamic arrays are last
  109. member_arr_copy = member_arr.copy()
  110. member_arr.sort(key=lambda x: x.is_dynamic_array(member_arr_copy))
  111. # Write the serialization and deserialization code
  112. writeln("")
  113. ident(1)
  114. writeln("template<typename TSerializer, typename TClass>")
  115. writeln("static void serializeCommon(TSerializer& s, TClass self)")
  116. writeln("{")
  117. ident(1)
  118. for member in member_arr:
  119. if member.is_pointer(member_arr_copy):
  120. writeln("s.doPointer(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name, member.name))
  121. elif member.is_dynamic_array(member_arr_copy):
  122. writeln("s.doDynamicArray(\"%s\", offsetof(%s, %s), self.%s, self.%s);" %
  123. (member.name, name, member.name, member.name, member.array_size))
  124. elif member.array_size != "1":
  125. writeln("s.doArray(\"%s\", offsetof(%s, %s), &self.%s[0], self.%s.getSize());" %
  126. (member.name, name, member.name, member.name, member.name))
  127. else:
  128. writeln("s.doValue(\"%s\", offsetof(%s, %s), self.%s);" % (member.name, name, member.name, member.name))
  129. ident(-1)
  130. writeln("}")
  131. ident(-1)
  132. # Write the methods
  133. writeln("")
  134. ident(1)
  135. writeln("template<typename TDeserializer>")
  136. writeln("void deserialize(TDeserializer& deserializer)")
  137. writeln("{")
  138. ident(1)
  139. writeln("serializeCommon<TDeserializer, %s&>(deserializer, *this);" % name)
  140. ident(-1)
  141. writeln("}")
  142. writeln("")
  143. writeln("template<typename TSerializer>")
  144. writeln("void serialize(TSerializer& serializer) const")
  145. writeln("{")
  146. ident(1)
  147. writeln("serializeCommon<TSerializer, const %s&>(serializer, *this);" % name)
  148. ident(-1)
  149. writeln("}")
  150. ident(-1)
  151. # Body end
  152. writeln("};")
  153. writeln("")
  154. def gen_file(filename):
  155. """ Parse an XML file and generate the code. """
  156. tree = et.parse(filename)
  157. root = tree.getroot()
  158. for incs in root.iter("includes"):
  159. for inc in incs.iter("include"):
  160. writeln("#include %s" % inc.get("file"))
  161. writeln("")
  162. writeln("namespace anki")
  163. writeln("{")
  164. writeln("")
  165. doxygen_group_el = root.find("doxygen_group")
  166. if doxygen_group_el is not None:
  167. doxygen_group = doxygen_group_el.get("name")
  168. writeln("/// @addtogroup %s" % doxygen_group)
  169. writeln("/// @{")
  170. writeln("")
  171. prefix_code = root.find("prefix_code")
  172. if prefix_code is not None:
  173. writeln("%s" % prefix_code.text)
  174. for cls in root.iter("classes"):
  175. for cl in cls.iter("class"):
  176. gen_class(cl)
  177. if doxygen_group_el is not None:
  178. writeln("/// @}")
  179. writeln("")
  180. writeln("} // end namespace anki")
  181. def main():
  182. parse_commandline()
  183. ctx.output_file = open(ctx.output_filename, "w")
  184. ctx.output_file.write("""// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  185. // All rights reserved.
  186. // Code licensed under the BSD License.
  187. // http://www.anki3d.org/LICENSE
  188. // WARNING: This file is auto generated.
  189. #pragma once
  190. """)
  191. for filename in ctx.input_filenames:
  192. gen_file(filename)
  193. if __name__ == "__main__":
  194. main()