0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-06 09:01:53 +00:00
bird/tools/objectgenerator.py
Maria Matejka 9fa4cd148a Filter types: enum, name and element_type getters generated by a new generator
First try on generating C code from a declarative YAML by Python.
2023-06-12 09:02:51 +02:00

143 lines
4.5 KiB
Python
Executable File

#!/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)