Files
scylladb/idl-compiler.py
Paweł Dziepak 4df4994b71 idl: fix generated writers when member functions are used
When using member name in an idetifer of generated class or method
idl compiler should strip the trailing '()'.
2017-02-27 17:05:58 +00:00

997 lines
37 KiB
Python
Executable File

#!/usr/bin/python3
#
# Copyright 2016 ScyllaDB
#
#
# This file is part of Scylla.
#
# Scylla is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Scylla is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Scylla. If not, see <http://www.gnu.org/licenses/>.
import json
import sys
import re
import glob
import argparse
import os
from string import Template
import pyparsing as pp
from functools import reduce
import textwrap
from numbers import Number
from timeit import reindent
EXTENSION = '.idl.hh'
READ_BUFF = 'input_buffer'
WRITE_BUFF = 'output_buffer'
SERIALIZER = 'serialize'
DESERIALIZER = 'deserialize'
SETSIZE = 'set_size'
SIZETYPE = 'size_type'
parser = argparse.ArgumentParser(description="""Generate serializer helper function""")
parser.add_argument('-o', help='Output file', default='')
parser.add_argument('-f', help='input file', default='')
parser.add_argument('--ns', help="""namespace, when set function will be created
under the given namespace""", default='')
parser.add_argument('file', nargs='*', help="combine one or more file names for the genral include files")
config = parser.parse_args()
def reindent(indent, text):
return textwrap.indent(textwrap.dedent(text), ' ' * indent)
def fprint(f, *args):
for arg in args:
f.write(arg)
def fprintln(f, *args):
for arg in args:
f.write(arg)
f.write('\n')
def print_cw(f):
fprintln(f, """
/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is an auto-generated code, do not modify directly.
*/
#pragma once
""")
def parse_file(file_name):
number = pp.Word(pp.nums)
identifier = pp.Word(pp.alphas + "_", pp.alphanums + "_")
lbrace = pp.Literal('{').suppress()
rbrace = pp.Literal('}').suppress()
cls = pp.Keyword('class')
colon = pp.Literal(":")
semi = pp.Literal(";").suppress()
langle = pp.Literal("<")
rangle = pp.Literal(">")
equals = pp.Literal("=")
comma = pp.Literal(",")
lparen = pp.Literal("(")
rparen = pp.Literal(")")
lbrack = pp.Literal("[")
rbrack = pp.Literal("]")
mins = pp.Literal("-")
struct = pp.Keyword('struct')
template = pp.Keyword('template')
final = pp.Keyword('final')("final")
stub = pp.Keyword('stub')("stub")
with_colon = pp.Word(pp.alphanums + "_" + ":")
btype = with_colon
type = pp.Forward()
nestedParens = pp.nestedExpr('<', '>')
tmpl = pp.Group(btype("template_name") + langle.suppress() + pp.Group(pp.delimitedList(type)) + rangle.suppress())
type << (tmpl | btype)
enum_lit = pp.Keyword('enum')
enum_class = pp.Group(enum_lit + cls)
ns = pp.Keyword("namespace")
enum_init = equals.suppress() + pp.Optional(mins) + number
enum_value = pp.Group(identifier + pp.Optional(enum_init))
enum_values = pp.Group(lbrace + pp.delimitedList(enum_value) + pp.Optional(comma) + rbrace)
content = pp.Forward()
member_name = pp.Combine(pp.Group(identifier + pp.Optional(lparen + rparen)))
attrib = pp.Group(lbrack.suppress() + lbrack.suppress() + pp.SkipTo(']') + rbrack.suppress() + rbrack.suppress())
opt_attribute = pp.Optional(attrib)("attribute")
namespace = pp.Group(ns("type") + identifier("name") + lbrace + pp.Group(pp.OneOrMore(content))("content") + rbrace)
enum = pp.Group(enum_class("type") + identifier("name") + colon.suppress() + identifier("underline_type") + enum_values("enum_values") + pp.Optional(semi).suppress())
default_value = equals.suppress() + pp.SkipTo(';')
class_member = pp.Group(type("type") + member_name("name") + opt_attribute + pp.Optional(default_value)("default") + semi.suppress())("member")
template_param = pp.Group(identifier("type") + identifier("name"))
template_def = pp.Group(template + langle + pp.Group(pp.delimitedList(template_param))("params") + rangle)
class_content = pp.Forward()
class_def = pp.Group(pp.Optional(template_def)("template") + (cls | struct)("type") + with_colon("name") + pp.Optional(final) + pp.Optional(stub) + opt_attribute + lbrace + pp.Group(pp.ZeroOrMore(class_content))("members") + rbrace + pp.Optional(semi))
content << (enum | class_def | namespace)
class_content << (enum | class_def | class_member)
for varname in "enum class_def class_member content namespace template_def".split():
locals()[varname].setName(varname)
rt = pp.OneOrMore(content)
singleLineComment = "//" + pp.restOfLine
rt.ignore(singleLineComment)
rt.ignore(pp.cStyleComment)
return rt.parseFile(file_name, parseAll=True)
def combine_ns(namespaces):
return "::".join(namespaces)
def open_namespaces(namespaces):
return "".join(map(lambda a: "namespace " + a + " { ", namespaces))
def close_namespaces(namespaces):
return "".join(map(lambda a: "}", namespaces))
def set_namespace(namespaces):
ns = combine_ns(namespaces)
ns_open = open_namespaces(namespaces)
ns_close = close_namespaces(namespaces)
return [ns, ns_open, ns_close]
def declare_class(hout, name, ns_open, ns_close):
clas_def = ns_open + name + ";" + ns_close
fprintln(hout, "\n", clas_def)
def declear_methods(hout, name, template_param = ""):
if config.ns != '':
fprintln(hout, "namespace ", config.ns, " {")
fprintln(hout, Template("""
template <$tmp_param>
struct serializer<$name> {
template <typename Output>
static void write(Output& buf, const $name& v);
template <typename Input>
static $name read(Input& buf);
template <typename Input>
static void skip(Input& buf);
};
""").substitute({'name' : name, 'sizetype' : SIZETYPE, 'tmp_param' : template_param }))
if config.ns != '':
fprintln(hout, "}")
def handle_enum(enum, hout, cout, namespaces , parent_template_param = []):
[ns, ns_open, ns_close] = set_namespace(namespaces)
temp_def = ", ".join(map(lambda a: a[0] + " " + a[1], parent_template_param)) if parent_template_param else ""
template = "template <"+ temp_def +">" if temp_def else ""
name = enum["name"] if ns == "" else ns + "::" + enum["name"]
declear_methods(hout, name, temp_def)
fprintln(cout, Template("""
$template
template <typename Output>
void serializer<$name>::write(Output& buf, const $name& v) {
serialize(buf, static_cast<$type>(v));
}
$template
template<typename Input>
$name serializer<$name>::read(Input& buf) {
return static_cast<$name>(deserialize(buf, boost::type<$type>()));
}""").substitute({'name' : name, 'size_type' : SIZETYPE, 'type': enum['underline_type'], 'template' : template}))
def join_template(lst):
return "<" + ", ".join([param_type(l) for l in lst]) + ">"
def param_type(lst):
if isinstance(lst, str):
return lst
if len(lst) == 1:
return lst[0]
return lst[0] + join_template(lst[1])
def flat_template(lst):
return ", ".join([param_type(l) for l in lst])
def flat_type(lst):
if isinstance(lst, str):
return lst
if len(lst) == 1:
return flat_type(lst[0])
return (lst[0] + "__" + "_".join([flat_type(l) for l in lst[1]])).replace('::', '__')
def is_class(obj):
return obj["type"] == "class" or obj["type"] == "struct"
def is_enum(obj):
try:
return not isinstance(obj["type"], str) and "".join(obj["type"]) == 'enumclass'
except:
return False
local_types = {}
def list_types(lst):
if isinstance(lst, str):
return [lst]
if len(lst) == 1:
return lst
lt = reduce(lambda a,b: a + b, [list_types(l) for l in lst[1]])
return lt
def list_local_types(lst):
return {l for l in list_types(lst) if l in local_types}
def is_basic_type(lst):
if isinstance(lst, str):
return lst not in local_types
if len(lst) == 1:
return isinstance(lst[0], str) and lst[0] not in local_types
return False
def is_local_type(lst):
return isinstance(lst, str) and lst in local_types or len(lst) == 1 and lst[0] in local_types
def get_template_name(lst):
return lst["template_name"] if not isinstance(lst, str) and len(lst) >1 else None
def is_vector(lst):
return get_template_name(lst) == "std::vector"
def is_variant(lst):
return get_template_name(lst) == "boost::variant"
def is_optional(lst):
return get_template_name(lst) == "std::experimental::optional"
created_writers = set()
def get_member_name(name):
return name if not name.endswith('()') else name[:-2]
def get_members(cls):
return [p for p in cls["members"] if not is_class(p) and not is_enum(p)]
def is_final(cls):
return "final" in cls
def get_variant_type(lst):
if is_variant(lst):
return "variant"
return param_type(lst)
def variant_to_member(typ):
return [{"name" : get_variant_type(x), "type" : x } for x in typ if is_local_type(x) or is_variant(x)]
def variant_info(info, typ):
[cls, namespaces, parent_template_param] = info
return [{"members" : variant_to_member(typ)}, namespaces, parent_template_param]
stubs = set()
def is_stub(cls):
return cls in stubs
def handle_visitors_state(info, hout, clases = []):
[cls, namespaces, parent_template_param] = info
name = "__".join(clases) if clases else cls["name"]
frame = "empty_frame" if "final" in cls else "frame"
fprintln(hout, Template("""
template<typename Output>
struct state_of_$name {
$frame<Output> f;""").substitute({'name': name, 'frame': frame }))
if clases:
local_state = "state_of_" + "__".join(clases[:-1]) + '<Output>'
fprintln(hout, Template(" $name _parent;").substitute({'name': local_state}))
if "final" in cls:
fprintln(hout, Template(" state_of_$name($state parent) : _parent(parent) {}").substitute({'name': name, 'state' : local_state}))
fprintln(hout, "};")
members = get_members(cls)
member_class = clases if clases else [cls["name"]]
for param in members:
if is_local_type(param["type"]):
handle_visitors_state(local_types[param_type(param["type"])], hout, member_class + [param["name"]])
if is_variant(param["type"]):
handle_visitors_state(variant_info(info, param["type"][1]), hout, member_class + [param["name"]])
def get_dependency(cls):
members = get_members(cls)
return reduce(lambda a,b: a |b, [list_local_types(m["type"]) for m in members], set())
def add_vector_node(hout, cls, members, base_state, current_node, ind):
current = members[ind]
typ = current["type"][1]
fprintln(hout, Template("""
template<typename Output>
struct $node_name {
Outout& _out;
state_of_$base_state<Output> _state;
place_holder<Output> _size;
size_type _count = 0;
$node_name(Output& out, state_of_$base_state<Output> state)
: _out(out)
, _state(state)
, _size(start_place_holder(out))
{
}
$next_state<Output> end_$name() {
_size.set(_out, _count);
}""").substitute({'node_name': '', 'name': current["name"] }))
fprintln(hout, "};")
def optional_add_methods(typ):
res = reindent(4,"""
void skip() {
serialize(_out, false);
}""")
if is_basic_type(typ):
added_type = typ
elif is_local_type(typ):
added_type = param_type(typ) + "_view"
else:
print("non supported optional type ", typ)
raise "non supported optional type " + param_type(typ)
res = res + Template(reindent(4, """
void write(const $type& obj) {
serialize(_out, true);
serialize(_out, obj);
}""")).substitute({'type' : added_type})
if is_local_type(typ):
res = res + Template(reindent(4, """
writer_of_$type<Output> write() {
serialize(_out, true);
return {_out};
}""")).substitute({'type' : param_type(typ)})
return res
def vector_add_method(current, base_state):
typ = current["type"]
res = ""
if is_basic_type(typ[1]):
res = res + Template("""
void add_$name($type t) {
serialize(_out, t);
_count++;
}""").substitute({'type': param_type(typ[1][0]), 'name': current["name"]})
else:
res = res + Template("""
writer_of_$type<Output> add() {
_count++;
return {_out};
}""").substitute({'type': flat_type(typ[1][0]), 'name': current["name"]})
res = res + Template("""
void add(${type} v) {
serialize(_out, v);
_count++;
}""").substitute({'type': param_view_type(typ[1][0])})
return res + Template("""
after_${basestate}__$name<Output> end_$name() && {
_size.set(_out, _count);
return { _out, std::move(_state) };
}
vector_position pos() const {
return vector_position{_out.pos(), _count};
}
void rollback(const vector_position& vp) {
_out.retract(vp.pos);
_count = vp.count;
}""").substitute({'name': current["name"], 'basestate' : base_state})
def add_param_writer_basic_type(name, base_state, typ, var_type = "", var_index = None, root_node = False):
if isinstance(var_index, Number):
var_index = "uint32_t(" + str(var_index) +")"
set_varient_index = "serialize(_out, " + var_index +");\n" if var_index is not None else ""
set_command = "_state.f.end(_out);" if var_type is not "" else ""
return_command = "{ _out, std::move(_state._parent) }" if var_type is not "" and not root_node else "{ _out, std::move(_state) }"
if typ in ['bytes', 'sstring']:
typ += '_view'
else:
typ = 'const ' + typ + '&'
return Template(reindent(4, """
after_${base_state}__$name<Output> write_$name$var_type($typ t) && {
$set_varient_index
serialize(_out, t);
$set_command
return $return_command;
}""")).substitute(locals())
def add_param_writer_object(name, base_state, typ, var_type = "", var_index = None, root_node = False):
var_type1 = "_" + var_type if var_type != "" else ""
if isinstance(var_index, Number):
var_index = "uint32_t(" + str(var_index) +")"
set_varient_index = "serialize(_out, " + var_index +");\n" if var_index is not None else ""
ret = Template(reindent(4,"""
${base_state}__${name}$var_type1<Output> start_${name}$var_type() && {
$set_varient_index
return { _out, std::move(_state) };
}
""")).substitute(locals())
if not is_stub(typ) and is_local_type(typ):
ret += add_param_writer_basic_type(name, base_state, typ, var_type, var_index, root_node)
if is_stub(typ):
set_command = "_state.f.end(_out);" if var_type is not "" else ""
return_command = "{ _out, std::move(_state._parent) }" if var_type is not "" and not root_node else "{ _out, std::move(_state) }"
ret += Template(reindent(4, """
template<typename Serializer>
after_${base_state}__${name}<Output> ${name}$var_type(Serializer&& f) && {
$set_varient_index
f(writer_of_$typ<Output>(_out));
$set_command
return $return_command;
}""")).substitute(locals())
return ret
def add_param_write(current, base_state, vector = False, root_node = False):
typ = current["type"]
res = ""
name = get_member_name(current["name"])
if is_basic_type(typ):
res = res + add_param_writer_basic_type(name, base_state, typ)
elif is_optional(typ):
res = res + Template(reindent(4, """
after_${basestate}__$name<Output> skip_$name() && {
serialize(_out, false);
return { _out, std::move(_state) };
}""")).substitute({'type': param_type(typ), 'name': name, 'basestate' : base_state})
if is_basic_type(typ[1][0]):
res = res + add_param_writer_basic_type(name, base_state, typ[1][0], "", "true")
elif is_local_type(typ[1][0]):
res = res + add_param_writer_object(name, base_state[0][1], typ, "", "true")
else:
print("non supported optional type ", type[0][1])
elif is_vector(typ):
set_size = "_size.set(_out, 0);" if vector else "serialize(_out, size_type(0));"
res = res + Template("""
${basestate}__$name<Output> start_$name() && {
return { _out, std::move(_state) };
}
after_${basestate}__$name<Output> skip_$name() && {
$set
return { _out, std::move(_state) };
}
""").substitute({'type': param_type(typ), 'name': name, 'basestate' : base_state, 'set' : set_size})
elif is_local_type(typ):
res = res + add_param_writer_object(name, base_state, typ)
elif is_variant(typ):
for idx, p in enumerate(typ[1]):
if is_basic_type(p):
varient_type = param_type(p)
res = res + add_param_writer_basic_type(name, base_state, varient_type,"_" + varient_type, idx, root_node)
elif is_variant(p):
res = res + add_param_writer_object(name, base_state, p, '_' + "variant", idx, root_node)
elif is_local_type(p):
res = res + add_param_writer_object(name, base_state, p, '_' + param_type(p), idx, root_node)
else:
print ("something is wrong with type", typ)
return res;
def get_return_struct(variant_node, clases):
if not variant_node:
return clases
if clases[-2] == "variant":
return clases[:-2]
return clases[:-1]
def add_variant_end_method(base_state, name, clases):
return_struct = "after_" + base_state + '<Output>'
return Template("""
$return_struct end_$name() && {
_state.f.end(_out);
_state._parent.f.end(_out);
return { _out, std::move(_state._parent._parent) };
}
""").substitute({'name': name, 'return_struct':return_struct})
def add_end_method(parents, name, variant_node = False, return_value = True):
if variant_node:
return add_variant_end_method(parents, name, return_value)
base_state = parents + "__" + name
if return_value:
return_struct = "after_" + base_state + '<Output>'
return Template("""
$return_struct end_$name() && {
_state.f.end(_out);
return { _out, std::move(_state._parent) };
}
""").substitute({'name': name, 'return_struct':return_struct})
return Template("""
void end_$name() {
_state.f.end(_out);
}
""").substitute({'name': name, 'basestate':base_state})
def add_vector_placeholder():
return """ place_holder<Output> _size;
size_type _count = 0;"""
def add_node(hout, name, member, base_state, prefix, parents, fun, is_type_vector = False, is_type_final = False):
struct_name = prefix + name
if member and is_type_vector:
vector_placeholder = add_vector_placeholder()
vector_init = "\n , _size(start_place_holder(out))"
else:
vector_placeholder = ""
vector_init = ""
if vector_init != "" or prefix == "":
state_init = "_state{start_frame(out), std::move(state)}" if parents != base_state and not is_type_final else "_state(state)"
else:
if member and is_variant(member) and parents != base_state:
state_init = "_state{start_frame(out), std::move(state)}"
sate = name
else:
state_init = ""
if prefix == "writer_of_":
constructor = Template("""$name(Output& out)
: _out(out)
, _state{start_frame(out)}${vector_init}
{}""").substitute({'name': struct_name, 'vector_init' : vector_init})
elif state_init != "":
constructor = Template("""$name(Output& out, state_of_$state<Output> state)
: _out(out)
, $state_init${vector_init}
{}""").substitute({'name': struct_name, 'vector_init' : vector_init, 'state' : parents, 'state_init' : state_init})
else:
constructor = ""
fprintln(hout, Template("""
template<typename Output>
struct $name {
Output& _out;
state_of_$state<Output> _state;
${vector_placeholder}
${constructor}
$fun
};""").substitute({'name': struct_name, 'state' : base_state, 'fun' : fun, 'vector_placeholder': vector_placeholder, 'constructor': constructor}))
def add_vector_node(hout, member, base_state, parents):
if get_template_name(member["type"][1][0]):
add_template_writer_node(hout,member["type"][1][0] )
add_node(hout, base_state + "__" + member["name"], member["type"], base_state, "", parents, vector_add_method(member, base_state), True)
optional_nodes = set()
def add_optional_node(hout, typ):
global optional_nodes
full_type = flat_type(typ)
if full_type in optional_nodes:
return
optional_nodes.add(full_type)
fprintln(hout, Template(reindent(0,"""
template<typename Output>
struct writer_of_$type {
Output& _out;
$add_method
};""")).substitute({'type': full_type, 'add_method': optional_add_methods(typ[1][0])}))
def add_variant_nodes(hout, member, param, base_state, parents, classes):
vr = False
par = base_state + "__" + member["name"]
for typ in param[1]:
if is_local_type(typ):
handle_visitors_nodes(local_types[param_type(typ)], hout, True, classes + [member["name"], local_types[param_type(typ)][0]["name"]])
if is_variant(typ):
name = base_state + "__" + member["name"] + "__variant"
new_member = {"type": typ, "name" : "variant"}
return_struct = "after_" + par
end_method = Template("""
$return_struct<Output> end_variant() && {
_state.f.end(_out);
return { _out, std::move(_state._parent) };
}
""").substitute({'return_struct':return_struct})
add_node(hout, name, None, base_state + "__" + member["name"], "after_", name, end_method)
add_variant_nodes(hout, new_member, typ, par, name, classes + [member["name"]])
add_node(hout, name, typ, name, "", par, add_param_write({"type": typ, "name" : "variant"}, par))
vr = True
writers = set()
def add_template_writer_node(hout, typ):
if is_optional(typ):
add_optional_node(hout, typ)
def add_nodes_when_needed(hout, info, member, base_state_name, parents, member_classes):
if is_vector(member["type"]):
add_vector_node(hout, member, base_state_name, base_state_name)
elif is_variant(member["type"]):
add_variant_nodes(hout, member, member["type"], base_state_name, parents, member_classes)
elif is_local_type(member["type"]):
handle_visitors_nodes(local_types[member["type"]], hout, False, member_classes + [member["name"]])
def handle_visitors_nodes(info, hout, variant_node = False, clases = []):
global writers
[cls, namespaces, parent_template_param] = info
#for root node, only generate once
if not clases:
if cls["name"] in writers:
return
writers.add(cls["name"])
members = get_members(cls)
if clases:
base_state_name = "__".join(clases)
if variant_node:
parents = "__".join(clases[:-1])
else:
parents = "__".join(clases[:-1])
current_name = clases[-1]
else:
base_state_name = cls["name"]
current_name = cls["name"]
parents = ""
member_classes = clases if clases else [current_name]
prefix = "" if clases else "writer_of_"
if not members:
add_node(hout, base_state_name, None, base_state_name, prefix, parents, add_end_method(parents, current_name, variant_node, clases), False, is_final(cls))
return
add_node(hout, base_state_name + "__" + get_member_name(members[-1]["name"]), members[-1]["type"], base_state_name, "after_", base_state_name, add_end_method(parents, current_name, variant_node, clases))
# Create writer and reader for include class
if not variant_node:
for member in get_dependency(cls):
handle_visitors_nodes(local_types[member], hout)
for ind in reversed(range(1, len(members))):
member = members[ind]
add_nodes_when_needed(hout, info, member, base_state_name, parents, member_classes)
variant_state = base_state_name + "__" + get_member_name(member["name"]) if is_variant(member["type"]) else base_state_name
is_param_vector = is_vector(member["type"]) and is_basic_type(member["type"][1][0])
add_node(hout, base_state_name + "__" + get_member_name(members[ind - 1]["name"]), member["type"], variant_state, "after_", base_state_name, add_param_write(member, base_state_name), False)
member = members[0]
is_param_vector = is_vector(member["type"]) and is_basic_type(member["type"][1][0])
add_nodes_when_needed(hout, info, member, base_state_name, parents, member_classes)
add_node(hout, base_state_name, member["type"], base_state_name, prefix, parents, add_param_write(member, base_state_name, False, not clases), False, is_final(cls))
def add_to_types(cls, namespaces, parent_template_param ):
global local_types
global stubs
if "attribute" not in cls or cls["attribute"][0][0] != "writable":
return
local_types[cls["name"]] = [cls, namespaces, parent_template_param]
if "stub" in cls:
stubs.add(cls["name"])
for param in cls["members"]:
if is_class(param) or is_enum(param):
continue
def sort_dependencies():
dep_tree = {}
res = []
for k in local_types:
[cls, namespaces, parent_template_param] = local_types[k]
dep_tree[k] = get_dependency(cls)
while (len(dep_tree) > 0):
found = set()
found = found | { k for k in dep_tree if not dep_tree[k]}
res = res + [k for k in found]
for k in found:
dep_tree.pop(k)
for k in dep_tree:
if dep_tree[k]:
dep_tree[k].difference_update(found)
return res
def join_template_view(lst, more_types = []):
return "<" + ", ".join([param_view_type(l) for l in lst] + more_types) + ">"
def to_view(val):
if val in local_types:
return val +"_view"
return val
def param_view_type(lst):
if isinstance(lst, str):
return to_view(lst)
if len(lst) == 1:
return to_view(lst[0])
if lst[0] == "boost::variant":
return lst[0] + join_template_view(lst[1], ["unknown_variant_type"])
return lst[0] + join_template_view(lst[1])
read_sizes = set()
def add_variant_read_size(hout, typ):
global read_sizes
t = param_view_type(typ)
if t in read_sizes:
return
if not is_variant(typ):
return
for p in typ[1]:
if is_variant(p):
add_variant_read_size(hout, p)
read_sizes.add(t)
fprintln(hout, Template("""
template<typename Input>
inline void skip(Input& v, boost::type<${type}>) {
return seastar::with_serialized_stream(v, [] (auto& v) {
size_type ln = deserialize(v, boost::type<size_type>());
v.skip(ln - sizeof(size_type));
});
}""").substitute ({'type' : t}))
fprintln(hout, Template("""
template<typename Input>
$type deserialize(Input& v, boost::type<${type}>) {
return seastar::with_serialized_stream(v, [] (auto& v) {
auto in = v;
deserialize(in, boost::type<size_type>());
size_type o = deserialize(in, boost::type<size_type>());
""").substitute ({'type' : t}))
for index, param in enumerate(typ[1]):
fprintln(hout, Template("""
if (o == $ind) {
v.skip(sizeof(size_type)*2);
return $full_type(deserialize(v, boost::type<$type>()));
}""").substitute({'ind' : index, 'type' : param_view_type(param), 'full_type' : t}))
fprintln(hout, ' return ' + t + '(deserialize(v, boost::type<unknown_variant_type>()));\n });\n}')
def add_view(hout, info):
[cls, namespaces, parent_template_param] = info
members = get_members(cls)
for m in members:
add_variant_read_size(hout, m["type"])
fprintln(hout, Template("""struct ${name}_view {
utils::input_stream v;
""").substitute({'name' : cls["name"]}))
if not is_stub(cls["name"]) and is_local_type(cls["name"]):
fprintln(hout, Template(reindent(4, """
operator $type() const {
auto in = v;
return deserialize(in, boost::type<$type>());
}
""")).substitute({'type' : cls["name"]}))
skip = "" if is_final(cls) else "ser::skip(in, boost::type<size_type>());"
for m in members:
full_type = param_view_type(m["type"])
fprintln(hout, Template(reindent(4, """
auto $name() const {
return seastar::with_serialized_stream(v, [] (auto& v) {
auto in = v;
$skip
return deserialize(in, boost::type<$type>());
});
}
""")).substitute({'name' : get_member_name(m["name"]), 'type' : full_type, 'skip' : skip}))
skip = skip + Template("\n ser::skip(in, boost::type<${type}>());").substitute({'type': full_type})
fprintln(hout, "};")
skip_impl = "auto& in = v;\n " + skip if is_final(cls) else "v.skip(read_frame_size(v));"
if skip == "":
skip_impl = ""
fprintln(hout, Template("""
template<>
struct serializer<${type}_view> {
template<typename Input>
static ${type}_view read(Input& v) {
return seastar::with_serialized_stream(v, [] (auto& v) {
auto v_start = v;
auto start_size = v.size();
skip(v);
return ${type}_view{v_start.read_substream(start_size - v.size())};
});
}
template<typename Output>
static void write(Output& out, ${type}_view v) {
v.v.copy_to(out);
}
template<typename Input>
static void skip(Input& v) {
return seastar::with_serialized_stream(v, [] (auto& v) {
$skip_impl
});
}
};
""").substitute({'type' : param_type(cls["name"]), 'skip' : skip, 'skip_impl' : skip_impl}))
def add_views(hout):
for k in sort_dependencies():
add_view(hout, local_types[k])
def add_visitors(hout):
if not local_types:
return
add_views(hout)
fprintln(hout, "\n////// State holders")
for k in local_types:
handle_visitors_state(local_types[k], hout)
fprintln(hout, "\n////// Nodes")
for k in sort_dependencies():
handle_visitors_nodes(local_types[k], hout)
def handle_class(cls, hout, cout, namespaces=[], parent_template_param = []):
add_to_types(cls, namespaces, parent_template_param)
if "stub" in cls:
return
[ns, ns_open, ns_close] = set_namespace(namespaces)
tpl = "template" in cls
template_param_list = (cls["template"][0]["params"].asList() if tpl else [])
template_param = ", ".join(map(lambda a: a[0] + " " + a[1], template_param_list + parent_template_param)) if (template_param_list + parent_template_param) else ""
template = "template <"+ template_param +">" if tpl else ""
template_class_param = "<" + ",".join(map(lambda a: a[1], template_param_list)) + ">" if tpl else ""
temp_def = template_param if template_param != "" else ""
if ns == "":
name = cls["name"]
else:
name = ns + "::" + cls["name"]
full_name = name + template_class_param
for param in cls["members"]:
if is_class(param):
handle_class(param, hout, cout, namespaces + [cls["name"] + template_class_param], parent_template_param + template_param_list)
elif is_enum(param):
handle_enum(param, hout, cout, namespaces + [cls["name"] + template_class_param], parent_template_param + template_param_list)
declear_methods(hout, name + template_class_param, temp_def)
is_final = "final" in cls
fprintln(cout, Template("""
$template
template <typename Output>
void serializer<$name>::write(Output& buf, const $name& obj) {""").substitute({'func' : SERIALIZER, 'name' : full_name, 'template': template}))
if not is_final:
fprintln(cout, Template(""" $set_size(buf, obj);""").substitute({'func' : SERIALIZER, 'set_size' : SETSIZE, 'name' : name, 'sizetype' : SIZETYPE}))
for param in cls["members"]:
if is_class(param) or is_enum(param):
continue
fprintln(cout, Template(""" static_assert(is_equivalent<decltype(obj.$var), $type>::value, "member value has a wrong type");
$func(buf, obj.$var);""").substitute({'func' : SERIALIZER, 'var' : param["name"], 'type' : param_type(param["type"])}))
fprintln(cout, "}")
fprintln(cout, Template("""
$template
template <typename Input>
$name$temp_param serializer<$name$temp_param>::read(Input& buf) {
return seastar::with_serialized_stream(buf, [] (auto& buf) {""").substitute({'func' : DESERIALIZER, 'name' : name, 'template': template, 'temp_param' : template_class_param}))
if not is_final:
fprintln(cout, Template(""" $size_type size = $func(buf, boost::type<$size_type>());
auto in = buf.read_substream(size - sizeof($size_type));""").substitute({'func' : DESERIALIZER, 'size_type' : SIZETYPE}))
else:
fprintln(cout, """ auto& in = buf;""")
params = []
local_names = {}
for index, param in enumerate(cls["members"]):
if is_class(param) or is_enum(param):
continue
local_param = "__local_" + str(index)
local_names[param["name"]] = local_param
if "attribute" in param:
deflt = param["default"][0] if "default" in param else param_type(param["type"]) + "()"
if deflt in local_names:
deflt = local_names[deflt]
fprintln(cout, Template(""" auto $local = (in.size()>0) ?
$func(in, boost::type<$typ>()) : $default;""").substitute({'func' : DESERIALIZER, 'typ': param_type(param["type"]), 'local' : local_param, 'default': deflt}))
else:
fprintln(cout, Template(""" auto $local = $func(in, boost::type<$typ>());""").substitute({'func' : DESERIALIZER, 'typ': param_type(param["type"]), 'local' : local_param}))
params.append("std::move(" + local_param + ")")
fprintln(cout, Template("""
$name$temp_param res {$params};
return res;
});
}""").substitute({'name' : name, 'params': ", ".join(params), 'temp_param' : template_class_param}))
fprintln(cout, Template("""
$template
template <typename Input>
void serializer<$name$temp_param>::skip(Input& buf) {
seastar::with_serialized_stream(buf, [] (auto& buf) {""").substitute({'func' : DESERIALIZER, 'name' : name, 'template': template, 'temp_param' : template_class_param}))
if not is_final:
fprintln(cout, Template(""" $size_type size = $func(buf, boost::type<$size_type>());
buf.skip(size - sizeof($size_type));""").substitute({'func' : DESERIALIZER, 'size_type' : SIZETYPE}))
else:
for m in get_members(cls):
full_type = param_view_type(m["type"])
fprintln(cout, " ser::skip(buf, boost::type<%s>());" % full_type)
fprintln(cout, """ });\n}""")
def handle_objects(tree, hout, cout, namespaces=[]):
for obj in tree:
if is_class(obj):
handle_class(obj, hout, cout, namespaces)
elif is_enum(obj):
handle_enum(obj, hout, cout, namespaces)
elif obj["type"] == "namespace":
handle_objects(obj["content"], hout, cout, namespaces + [obj["name"]])
else:
print("unknown type ", obj, obj["type"])
def handle_types(tree, namespaces=[]):
for obj in tree:
if is_class(obj):
add_to_types(obj, namespaces, [])
elif is_enum(obj):
pass
elif obj["type"] == "namespace":
handle_types(obj["content"], namespaces + [obj["name"]])
else:
print("unknown type ", obj, obj["type"])
def load_file(name):
if config.o:
cout = open(config.o.replace('.hh', '.impl.hh'), "w+")
hout = open(config.o, "w+")
else:
cout = open(name.replace(EXTENSION, '.dist.impl.hh'), "w+")
hout = open(name.replace(EXTENSION, '.dist.hh'), "w+")
print_cw(hout)
fprintln(hout, """
/*
* The generate code should be included in a header file after
* The object definition
*/
""")
print_cw(cout)
fprintln(hout, "#include \"serializer.hh\"\n")
if config.ns != '':
fprintln(cout, "namespace ", config.ns, " {")
data = parse_file(name)
if data:
handle_types(data)
add_visitors(cout)
if data:
handle_objects(data, hout, cout)
if config.ns != '':
fprintln(cout, "}")
cout.close()
hout.close()
def general_include(files):
name = config.o if config.o else "serializer.dist.hh"
cout = open(name.replace('.hh', '.impl.hh'), "w+")
hout = open(name, "w+")
print_cw(cout)
print_cw(hout)
for n in files:
fprintln(hout, '#include "' + n +'"')
fprintln(cout, '#include "' + n.replace(".dist.hh", '.dist.impl.hh') +'"')
cout.close()
hout.close()
if config.file:
general_include(config.file)
elif config.f != '':
load_file(config.f)