mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2025-01-06 09:01:53 +00:00
9fa4cd148a
First try on generating C code from a declarative YAML by Python.
143 lines
4.5 KiB
Python
Executable File
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)
|