mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +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)
|
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
|
||||||
cf-local = $(conf-y-targets): $(s)config.Y
|
cf-local = $(conf-y-targets): $(s)config.Y
|
||||||
|
|
||||||
|
# Generate %.o requests from source declarations
|
||||||
src-o-files = $(patsubst %.c,$(o)%.o,$(src))
|
src-o-files = $(patsubst %.c,$(o)%.o,$(src))
|
||||||
tests-target-files = $(patsubst %.c,$(o)%,$(tests_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-daemon = $(daemon): $(obj)
|
||||||
all-client = $(client): $(obj)
|
all-client = $(client): $(obj)
|
||||||
|
|
||||||
@ -134,6 +138,18 @@ $(objdir)/%.S: $(objdir)/%.c | prepare
|
|||||||
$(E)echo CC -o $@ -S $<
|
$(E)echo CC -o $@ -S $<
|
||||||
$(Q)$(CC) $(CFLAGS) -MMD -MP -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:
|
# Finally include the computed dependencies:
|
||||||
DEPS = $(shell find $(objdir) -name '*.d')
|
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)
|
obj := $(src-o-files)
|
||||||
|
$(info $(pygen))
|
||||||
|
$(pygen)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
|
@ -25,61 +25,6 @@
|
|||||||
#include "filter/f-inst.h"
|
#include "filter/f-inst.h"
|
||||||
#include "filter/data.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_trie f_const_empty_trie = { .ipv4 = -1, };
|
||||||
const struct f_val f_const_empty_prefix_set = {
|
const struct f_val f_const_empty_prefix_set = {
|
||||||
.type = T_PREFIX_SET,
|
.type = T_PREFIX_SET,
|
||||||
@ -611,4 +556,3 @@ val_dump(const struct f_val *v) {
|
|||||||
val_format(v, &b);
|
val_format(v, &b);
|
||||||
return val_dump_buffer;
|
return val_dump_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,57 +12,13 @@
|
|||||||
|
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
|
|
||||||
|
/* Generated type routines */
|
||||||
|
#include "filter/types-pygen.h"
|
||||||
|
|
||||||
/* Type numbers must be in 0..0xff range */
|
/* Type numbers must be in 0..0xff range */
|
||||||
#define T_MASK 0xff
|
#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
|
#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 */
|
/* Filter value; size of this affects filter memory consumption */
|
||||||
struct f_val {
|
struct f_val {
|
||||||
enum f_type type; /* T_* */
|
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
|
#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_same(const struct f_val *v1, const struct f_val *v2);
|
||||||
int val_compare(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);
|
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()
|
function t_enum()
|
||||||
{
|
{
|
||||||
|
print(RTS_STATIC);
|
||||||
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
||||||
bt_assert(format(NET_IP4) = "(enum 36)1");
|
bt_assert(format(NET_IP4) = "(enum 36)1");
|
||||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
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