mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-08 04:08:42 +00:00
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.
This commit is contained in:
parent
72f3189ca5
commit
9fa4cd148a
16
Makefile.in
16
Makefile.in
@ -83,9 +83,13 @@ conf-lex-targets := $(addprefix $(objdir)/conf/,cf-lex.o)
|
||||
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
|
||||
cf-local = $(conf-y-targets): $(s)config.Y
|
||||
|
||||
# Generate %.o requests from source declarations
|
||||
src-o-files = $(patsubst %.c,$(o)%.o,$(src))
|
||||
tests-target-files = $(patsubst %.c,$(o)%,$(tests_src))
|
||||
|
||||
# Request python generated files
|
||||
pygen = prepare: $(addprefix $(patsubst %.c,$(o)%,$(filter %-pygen.c,$(src))),.c .h)
|
||||
|
||||
all-daemon = $(daemon): $(obj)
|
||||
all-client = $(client): $(obj)
|
||||
|
||||
@ -134,6 +138,18 @@ $(objdir)/%.S: $(objdir)/%.c | prepare
|
||||
$(E)echo CC -o $@ -S $<
|
||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
|
||||
|
||||
# Generic object generator rules
|
||||
$(objdir)/%-pygen.h: $(srcdir)/%.yaml tools/objectgenerator.py | $(objdir)/.dir-stamp
|
||||
$(E)echo PYGEN -o $@ from $<
|
||||
$(Q)$(srcdir)/tools/objectgenerator.py $< $@
|
||||
|
||||
$(objdir)/%-pygen.c: $(objdir)/%-pygen.h Makefile | $(objdir)/.dir-stamp
|
||||
$(E)echo PYGEN -o $@
|
||||
$(Q)rm -f $@
|
||||
$(Q)ln -s $(notdir $<) $@
|
||||
|
||||
$(objdir)/%-pygen.o: CFLAGS += "-DOG_BUILD_BODY=1"
|
||||
|
||||
# Finally include the computed dependencies:
|
||||
DEPS = $(shell find $(objdir) -name '*.d')
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
src := filter.c data.c f-util.c tree.c trie.c inst-gen.c
|
||||
src := filter.c data.c f-util.c tree.c trie.c inst-gen.c types-pygen.c
|
||||
obj := $(src-o-files)
|
||||
$(info $(pygen))
|
||||
$(pygen)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
||||
|
@ -25,61 +25,6 @@
|
||||
#include "filter/f-inst.h"
|
||||
#include "filter/data.h"
|
||||
|
||||
static const char * const f_type_str[] = {
|
||||
[T_VOID] = "void",
|
||||
|
||||
[T_INT] = "int",
|
||||
[T_BOOL] = "bool",
|
||||
[T_PAIR] = "pair",
|
||||
[T_QUAD] = "quad",
|
||||
|
||||
[T_ENUM_RTS] = "enum rts",
|
||||
[T_ENUM_BGP_ORIGIN] = "enum bgp_origin",
|
||||
[T_ENUM_SCOPE] = "enum scope",
|
||||
[T_ENUM_RTC] = "enum rtc",
|
||||
[T_ENUM_RTD] = "enum rtd",
|
||||
[T_ENUM_ROA] = "enum roa",
|
||||
[T_ENUM_NETTYPE] = "enum nettype",
|
||||
[T_ENUM_RA_PREFERENCE] = "enum ra_preference",
|
||||
[T_ENUM_AF] = "enum af",
|
||||
|
||||
[T_IP] = "ip",
|
||||
[T_NET] = "prefix",
|
||||
[T_STRING] = "string",
|
||||
[T_PATH_MASK] = "bgpmask",
|
||||
[T_PATH] = "bgppath",
|
||||
[T_CLIST] = "clist",
|
||||
[T_EC] = "ec",
|
||||
[T_ECLIST] = "eclist",
|
||||
[T_LC] = "lc",
|
||||
[T_LCLIST] = "lclist",
|
||||
[T_RD] = "rd",
|
||||
};
|
||||
|
||||
const char *
|
||||
f_type_name(enum f_type t)
|
||||
{
|
||||
if (t < ARRAY_SIZE(f_type_str))
|
||||
return f_type_str[t] ?: "?";
|
||||
|
||||
if ((t == T_SET) || (t == T_PREFIX_SET))
|
||||
return "set";
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
enum f_type
|
||||
f_type_element_type(enum f_type t)
|
||||
{
|
||||
switch(t) {
|
||||
case T_PATH: return T_INT;
|
||||
case T_CLIST: return T_PAIR;
|
||||
case T_ECLIST: return T_EC;
|
||||
case T_LCLIST: return T_LC;
|
||||
default: return T_VOID;
|
||||
};
|
||||
}
|
||||
|
||||
const struct f_trie f_const_empty_trie = { .ipv4 = -1, };
|
||||
const struct f_val f_const_empty_prefix_set = {
|
||||
.type = T_PREFIX_SET,
|
||||
@ -611,4 +556,3 @@ val_dump(const struct f_val *v) {
|
||||
val_format(v, &b);
|
||||
return val_dump_buffer;
|
||||
}
|
||||
|
||||
|
@ -12,57 +12,13 @@
|
||||
|
||||
#include "nest/bird.h"
|
||||
|
||||
/* Generated type routines */
|
||||
#include "filter/types-pygen.h"
|
||||
|
||||
/* Type numbers must be in 0..0xff range */
|
||||
#define T_MASK 0xff
|
||||
|
||||
/* Internal types */
|
||||
enum f_type {
|
||||
/* Nothing. Simply nothing. */
|
||||
T_VOID = 0,
|
||||
|
||||
/* User visible types, which fit in int */
|
||||
T_INT = 0x10,
|
||||
T_BOOL = 0x11,
|
||||
T_PAIR = 0x12, /* Notice that pair is stored as integer: first << 16 | second */
|
||||
T_QUAD = 0x13,
|
||||
|
||||
/* Put enumerational types in 0x30..0x3f range */
|
||||
T_ENUM_LO = 0x30,
|
||||
T_ENUM_HI = 0x3f,
|
||||
|
||||
T_ENUM_RTS = 0x30,
|
||||
T_ENUM_BGP_ORIGIN = 0x31,
|
||||
T_ENUM_SCOPE = 0x32,
|
||||
T_ENUM_RTC = 0x33,
|
||||
T_ENUM_RTD = 0x34,
|
||||
T_ENUM_ROA = 0x35,
|
||||
T_ENUM_NETTYPE = 0x36,
|
||||
T_ENUM_RA_PREFERENCE = 0x37,
|
||||
T_ENUM_AF = 0x38,
|
||||
|
||||
/* new enums go here */
|
||||
T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
|
||||
|
||||
#define T_ENUM T_ENUM_LO ... T_ENUM_HI
|
||||
|
||||
/* Bigger ones */
|
||||
T_IP = 0x20,
|
||||
T_NET = 0x21,
|
||||
T_STRING = 0x22,
|
||||
T_PATH_MASK = 0x23, /* mask for BGP path */
|
||||
T_PATH = 0x24, /* BGP path */
|
||||
T_CLIST = 0x25, /* Community list */
|
||||
T_EC = 0x26, /* Extended community value, u64 */
|
||||
T_ECLIST = 0x27, /* Extended community list */
|
||||
T_LC = 0x28, /* Large community value, lcomm */
|
||||
T_LCLIST = 0x29, /* Large community list */
|
||||
T_RD = 0x2a, /* Route distinguisher for VPN addresses */
|
||||
T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
|
||||
|
||||
T_SET = 0x80,
|
||||
T_PREFIX_SET = 0x81,
|
||||
} PACKED;
|
||||
|
||||
/* Filter value; size of this affects filter memory consumption */
|
||||
struct f_val {
|
||||
enum f_type type; /* T_* */
|
||||
@ -274,10 +230,6 @@ trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found)
|
||||
|
||||
#define F_CMP_ERROR 999
|
||||
|
||||
const char *f_type_name(enum f_type t);
|
||||
|
||||
enum f_type f_type_element_type(enum f_type t);
|
||||
|
||||
int val_same(const struct f_val *v1, const struct f_val *v2);
|
||||
int val_compare(const struct f_val *v1, const struct f_val *v2);
|
||||
void val_format(const struct f_val *v, buffer *buf);
|
||||
|
@ -416,6 +416,7 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
|
||||
|
||||
function t_enum()
|
||||
{
|
||||
print(RTS_STATIC);
|
||||
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
||||
bt_assert(format(NET_IP4) = "(enum 36)1");
|
||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
||||
|
94
filter/types.yaml
Normal file
94
filter/types.yaml
Normal file
@ -0,0 +1,94 @@
|
||||
_meta:
|
||||
header_guard: _BIRD_FILTER_TYPES_H_
|
||||
include:
|
||||
- '"nest/bird.h"'
|
||||
|
||||
_keys:
|
||||
type:
|
||||
prefix: f_type_
|
||||
key: enum f_type
|
||||
attributes:
|
||||
name:
|
||||
mandatory: true
|
||||
switch:
|
||||
return: const char *
|
||||
format: '"{data}"'
|
||||
element_type:
|
||||
switch:
|
||||
return: enum f_type
|
||||
format: '{data}'
|
||||
default: T_VOID
|
||||
|
||||
type:
|
||||
T_VOID:
|
||||
name: void
|
||||
_value: 0
|
||||
|
||||
T_INT:
|
||||
name: int
|
||||
_value: 0x50
|
||||
T_BOOL:
|
||||
name: bool
|
||||
T_PAIR:
|
||||
name: pair
|
||||
T_QUAD:
|
||||
name: quad
|
||||
T_IP:
|
||||
name: ip
|
||||
T_NET:
|
||||
name: prefix
|
||||
T_STRING:
|
||||
name: string
|
||||
T_PATH_MASK:
|
||||
name: bgpmask
|
||||
T_PATH:
|
||||
name: bgppath
|
||||
element_type: T_INT
|
||||
T_CLIST:
|
||||
name: clist
|
||||
element_type: T_PAIR
|
||||
T_EC:
|
||||
name: ec
|
||||
T_ECLIST:
|
||||
name: eclist
|
||||
element_type: T_EC
|
||||
T_LC:
|
||||
name: lc
|
||||
T_LCLIST:
|
||||
name: lclist
|
||||
element_type: T_LC
|
||||
T_RD:
|
||||
name: rd
|
||||
T_PATH_MASK_ITEM:
|
||||
name: path mask item
|
||||
T_SET:
|
||||
name: set
|
||||
T_PREFIX_SET:
|
||||
name: set
|
||||
|
||||
# Put enum types aside to allow for range switch
|
||||
T_ENUM_LO:
|
||||
name: enum low bound
|
||||
_value: 0x2f
|
||||
T_ENUM_RTS:
|
||||
name: enum rts
|
||||
T_ENUM_BGP_ORIGIN:
|
||||
name: enum bgp_origin
|
||||
T_ENUM_SCOPE:
|
||||
name: enum scope
|
||||
T_ENUM_RTC:
|
||||
name: enum rtc
|
||||
T_ENUM_RTD:
|
||||
name: enum rtd
|
||||
T_ENUM_ROA:
|
||||
name: enum roa
|
||||
T_ENUM_NETTYPE:
|
||||
name: enum nettype
|
||||
T_ENUM_RA_PREFERENCE:
|
||||
name: enum ra_preference
|
||||
T_ENUM_AF:
|
||||
name: enum af
|
||||
T_ENUM_EMPTY:
|
||||
name: enum empty # Special hack for atomic_aggr
|
||||
T_ENUM_HI:
|
||||
name: enum high bound
|
142
tools/objectgenerator.py
Executable file
142
tools/objectgenerator.py
Executable file
@ -0,0 +1,142 @@
|
||||
#!/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)
|
Loading…
Reference in New Issue
Block a user