#!/usr/bin/env python3 import yaml import sys class ObjectGeneratorException(Exception): pass class OGAttributeSwitch: def __init__(self, attr, mandatory, **kwargs): if "default" in kwargs: self._default = kwargs["default"] else: self._default = None if not mandatory and self._default is None: raise ObjectGeneratorException("Switch must be either mandatory or with a default value supplied") self.attr = attr self._return = kwargs["return"] self._format = kwargs["format"] self.mandatory = mandatory def write_header(self, _file): _file.write(f"{self._return} {self.attr.obj.prefix}{self.attr.name}({self.attr.obj.key});\n") def write_body(self, _file, data): _file.write("\n".join([ f"{self._return} {self.attr.obj.prefix}{self.attr.name}({self.attr.obj.key} key)", "{", " switch (key)", " {", ""])) for k in data: if self.attr.name in data[k]: _file.write(f" case {k}: return {self._format.format(data=data[k][self.attr.name])};\n") elif self.mandatory: raise ObjectGeneratorException("No value for mandatory attribute {self.attr.name} in key {k}") if self.mandatory: _file.write(f' default: bug("Garbled value of {self.attr.obj.key} in {self.attr.obj.prefix}{self.attr.name}: %d", key);\n') else: _file.write(f' default: return {self._default};\n') _file.write(" }\n}\n\n") class OGAttribute: def __init__(self, obj, name, mandatory=False, switch=None): self.obj = obj self.name = name self.mandatory = mandatory if switch is not None: self.resolver = OGAttributeSwitch(attr=self, name=name, mandatory=mandatory, **switch) if self.resolver is None: raise ObjectGeneratorException("No resolver specified") def write_header(self, _file): self.resolver.write_header(_file) def write_body(self, _file, data): self.resolver.write_body(_file, data) class OGObject: def __init__(self, og, _def, _data): self.og = og self.attributes = { k: OGAttribute(name=k, obj=self, **_def["attributes"][k]) for k in _def["attributes"] } self.data = _data self.prefix = _def["prefix"] self.key = _def["key"] def write_header(self, _file): _file.write(f"{self.key} " + "{\n") for k in self.data: if "_value" in self.data[k]: _file.write(f" {k} = {self.data[k]['_value']},\n") else: _file.write(f" {k},\n") _file.write("};\n\n") for name,a in self.attributes.items(): a.write_header(_file) def write_body(self, _file): for name, a in self.attributes.items(): a.write_body(_file, self.data) class ObjectGenerator: def __init__(self, _meta, _keys, **data): try: self.objects = { k: OGObject(og=self, _def=_keys[k], _data=data[k]) for k in data } except Exception as e: raise ObjectGeneratorException(f"Failed to map data and _keys") from e self._meta = _meta def write_out(self, _file): self._write_header(_file) self._write_body(_file) def _write_header(self, _file): _file.write(f"""/* File generated by ObjectGenerator */ #ifndef {self._meta["header_guard"]} #define {self._meta["header_guard"]} """) if "include" in self._meta: for i in self._meta["include"]: _file.write(f"#include {i}\n") for k, o in self.objects.items(): header = f"/* Headers for {k} */" _file.write(header + "\n") _file.write("/" + ("*"*(len(header)-2)) + "/\n") o.write_header(_file) _file.write("\n") _file.write(f"#endif /* {self._meta['header_guard']} */\n\n\n") def _write_body(self, _file): _file.write(f"""/* Build with -DOG_BUILD_BODY=1 */ #ifdef OG_BUILD_BODY """) for k, o in self.objects.items(): header = f"/* Body for {k} */" _file.write(header + "\n") _file.write("/" + ("*"*(len(header)-2)) + "/\n") o.write_body(_file) _file.write("\n") _file.write(f"#endif /* OG_BUILD_BODY */\n") with open(sys.argv[1], "r") as f: t = ObjectGenerator(**dict(yaml.safe_load(f).items())) with open(sys.argv[2], "w") as f: t.write_out(f)