#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 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 .
import argparse
from string import Template
import pyparsing as pp
from functools import reduce
import textwrap
from numbers import Number
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 .
*/
/*
* 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
static void write(Output& buf, const $name& v);
template
static $name read(Input& buf);
template
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
void serializer<$name>::write(Output& buf, const $name& v) {
serialize(buf, static_cast<$type>(v));
}
$template
template
$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 Exception:
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" or get_template_name(lst) == "std::variant"
def is_optional(lst):
return get_template_name(lst) == "std::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
struct state_of_$name {
$frame