def _add_has(e: UxsdElement): impl = "" if e.many: pname = utils.to_pascalcase(utils.pluralize(e.name)) else: pname = utils.to_pascalcase(e.name) impl += "return reader.has{pname}();\n".format(pname=pname) _add_field("bool", "has", e.name, _gen_reader(t) + " &reader", impl)
def _gen_write_complex_element(e: UxsdElement, parent: str) -> str: """Function to generate partial code which writes out an element with a complex type.""" assert isinstance(e.type, UxsdComplex) out = "" def _gen_write_element_body(root: str) -> str: assert isinstance(e.type, UxsdComplex) ouv = "" if e.type.attrs: for a in e.type.attrs: ouv += _gen_write_attr(a, e.type.name, root, "child_context") if e.type.content: ouv += "write_{name}_capnp_type(in, {root}, child_context);\n".format( root=root, name=e.type.name) else: if e.type.content: ouv += "write_{name}_capnp_type(in, {root}, child_context);\n".format( root=root, name=e.type.name) return ouv name = cpp._gen_stub_suffix(e, parent) if e.many: plural_name = utils.pluralize(cpp._gen_stub_suffix(e, parent)) out += "size_t num_{pname} = in.num_{name}(context);\n".format( name=name, pname=plural_name) out += "auto {name} = root.init{pname}(num_{name});\n".format( name=plural_name, pname=utils.pluralize(utils.to_pascalcase(e.name))) out += "for(size_t i = 0; i < num_{name}; i++) {{\n".format( name=plural_name) out += "\tauto {name} = {plural_name}[i];\n".format( name=name, plural_name=plural_name, ) out += "\tauto child_context = in.get_%s(i, context);\n" % cpp._gen_stub_suffix( e, parent) out += utils.indent(_gen_write_element_body(name)) out += "}\n" elif e.optional: out += "if(in.has_%s(context)){\n" % cpp._gen_stub_suffix(e, parent) out += "\tauto {name} = root.init{pname}();\n".format( name=name, pname=utils.to_pascalcase(e.name)) out += "\tauto child_context = in.get_%s(context);\n" % cpp._gen_stub_suffix( e, parent) out += utils.indent(_gen_write_element_body(name)) out += "}\n" else: out += "{\n" out += "\tauto child_context = in.get_%s(context);\n" % cpp._gen_stub_suffix( e, parent) out += "\tauto {name} = root.init{pname}();\n".format( name=name, pname=utils.to_pascalcase(e.name)) out += utils.indent(_gen_write_element_body(name)) out += "}\n" return out
def _add_add_complex(e: UxsdElement): assert isinstance(e.type, UxsdComplex) impl = "" impl += "auto {name} = capnp::Orphanage::getForMessageContaining(builder).newOrphan<ucap::{pname}>();\n".format( name=cpp.checked(e.name), pname=utils.to_pascalcase(e.type.name)) impl += "{pname}_.emplace_back(std::move({name}));\n".format( name=cpp.checked(e.name), pname=utils.pluralize(e.type.name)) impl += "auto child_builder = {pname}_.back().get();\n".format( name=e.type.name, pname=utils.pluralize(e.type.name)) impl += _gen_set_required_attrs(e) impl += "return child_builder;\n" _add_field( _gen_builder(e.type), "add", e.name, cpp._gen_required_attribute_arg_list(_gen_builder(t), e.type.attrs, context="builder"), impl) impl = "" impl = "{pname}_.reserve(size);\n".format( pname=utils.pluralize(e.type.name)) _add_field("void", "preallocate", e.name, _gen_builder(t) + "&, size_t size", impl) _add_field("void", "finish", e.name, _gen_builder(e.type) + " &builder", _gen_finish(e))
def _add_get_complex_many(e: UxsdElement): impl = "" impl += "return reader.get{pname}()[n];\n".format( name=e.type.name, pname=utils.to_pascalcase(utils.pluralize(e.name))) _add_field(_gen_reader(e.type), "get", e.name, "int n, {} &reader".format(_gen_reader(t)), impl)
def _add_get_simple(e: Union[UxsdElement, UxsdAttribute]): impl = "" impl += "return {value};\n".format(value=_gen_load_simple( e.type, "reader.get{pname}()".format( pname=utils.to_pascalcase(e.name)))) _add_field(e.type.cpp, "get", e.name, _gen_reader(t) + " &reader", impl)
def _gen_set_required_attrs(e: UxsdElement): impl = "" for attr in sorted(e.type.attrs, key=lambda attr: attr.name): if cpp.pass_at_init(attr): impl += 'child_builder.set{pname}({value});\n'.format( pname=utils.to_pascalcase(attr.name), value=_gen_set_simple(attr.type, attr.name)) return impl
def _add_set(e: Union[UxsdElement, UxsdAttribute]): impl = "" impl += "builder.set{pname}({value});\n".format( pname=utils.to_pascalcase(e.name), value=_gen_set_simple(e.type, e.name)) _add_field( "void", "set", e.name, "{}, {} &builder".format(cpp._gen_attribute_arg(e), _gen_builder(t)), impl)
def _gen_write_attr(a: UxsdAttribute, parent: str, root: str = "root", context: str = "context") -> str: """Function to generate partial code which writes out a single XML attribute.""" out = "" if not a.optional or a.default_value: # root.set{pname}(in.get_{}(context); out += "{root}.set{pname}({value});\n".format( root=root, pname=utils.to_pascalcase(a.name), value=_gen_write_simple(a, parent, context)) else: out += "if((bool)%s)\n" % _gen_check_simple(a, parent, context) out += "\t{root}.set{pname}({value});\n".format( root=root, pname=utils.to_pascalcase(a.name), value=_gen_write_simple(a, parent, context)) return out
def _gen_finish(e: UxsdElement): any_many = False if not isinstance(e.type, UxsdComplex): return "" if isinstance(e.type.content, (UxsdDfa, UxsdAll)): for el in e.type.content.children: if el.many: any_many = True break if not any_many: return "" impl = "" for el in e.type.content.children: if el.many: impl += "auto {cname} = builder.init{pname}({name}_.size());\n".format( cname=cpp.checked(el.name), name=utils.pluralize(el.type.name), pname=utils.to_pascalcase(utils.pluralize(el.name))) impl += "for(size_t i = 0; i < {name}_.size(); ++i) {{\n".format( name=utils.pluralize(el.type.name)) impl += "\t{cname}.adoptWithCaveats(i, std::move({name}_[i]));\n".format( cname=cpp.checked(el.name), name=utils.pluralize(el.type.name)) impl += "}\n" impl += "{name}_.clear();\n".format( name=utils.pluralize(el.type.name)) return impl any_many = False for e2 in e.content.children: if isinstance(e2.type, UxsdComplex): if e2.many: any_many = True break elif isinstance(e2.type, UxsdSimple): if e2.many: any_many = True break else: raise TypeError(e2) if not any_many: return "" impl = "" return impl
def _gen_required_attribute_arg_list(t: UxsdComplex, input: str) -> str: required_attrs = [] for attr in sorted(t.type.attrs, key=lambda attr: attr.name): if cpp.pass_at_init(attr): required_attrs.append( _gen_load_simple(attr.type, input='{input}.get{pname}()'.format( input=input, pname=utils.to_pascalcase(attr.name)))) if len(required_attrs) == 0: return "" else: return ', ' + ', '.join(required_attrs)
def _add_init(e: UxsdElement): assert isinstance(e.type, UxsdComplex) impl = "" impl += "auto child_builder = builder.init{pname}();\n".format( pname=utils.to_pascalcase(e.name)) impl += _gen_set_required_attrs(e) impl += "return child_builder;\n" _add_field( _gen_builder(e.type), "init", e.name, cpp._gen_required_attribute_arg_list(_gen_builder(t), e.type.attrs, context="builder"), impl) _add_field("void", "finish", e.name, _gen_builder(e.type) + " &builder", _gen_finish(e))
def write_fn_from_complex_type(t: UxsdComplex) -> str: assert isinstance(t.content, (UxsdDfa, UxsdAll, UxsdLeaf)) out = "" out += "template<class T, typename Context>\n" out += "inline void write_{name}_capnp_type(T &in, ucap::{cname}::Builder &root, Context &context) {{\n".format( name=t.name, cname=utils.to_pascalcase(t.name)) out += "\t(void)in;\n" out += "\t(void)root;\n" if isinstance(t.content, (UxsdDfa, UxsdAll)): for e in t.content.children: out += utils.indent(_gen_write_element(e, t.name)) elif isinstance(t.content, UxsdLeaf): out += "\troot.setValue(in.get_%s_value(context));\n" % t.name else: out += "\treturn;\n" out += "}\n" return out
def write_fn_from_root_element(e: UxsdElement) -> str: assert isinstance(e.type, UxsdComplex) out = "" out += "template <class T, typename Context>\n" out += "inline void write_{name}_capnp(T &in, Context &context, ucap::{cname}::Builder &root) {{\n".format( name=e.name, cname=utils.to_pascalcase(e.name)) out += "\tin.start_write();\n" if e.type.attrs: for a in e.type.attrs: out += utils.indent(_gen_write_attr(a, e.name)) out += "\twrite_{name}_capnp_type(in, root, context);\n".format( name=e.name) out += "\tin.finish_write();\n" out += "}\n" return out
def _gen_conv_enum(t: UxsdEnum) -> str: pname = utils.to_pascalcase(t.name) out = "" out += "inline enum_{name} conv_enum_{name}(ucap::{pname} e, const std::function<void(const char *)> * report_error) {{\n".format( name=t.name, pname=pname) out += "\tswitch(e) {\n" out += "\tcase ucap::{pname}::{e}:\n".format(pname=pname, e='UXSD_INVALID') out += "\t\treturn enum_{name}::{e};\n".format(name=t.name, e='UXSD_INVALID') for e in t.enumeration: out += "\tcase ucap::{pname}::{e}:\n".format(pname=pname, e=e.upper()) out += "\t\treturn enum_{name}::{e};\n".format(name=t.name, e=e.upper()) out += "\tdefault:\n" out += '\t\t(*report_error)("Unknown enum_{name}");\n'.format(name=t.name) out += "\t\tthrow std::runtime_error(\"Unreachable!\");\n" out += "\t}\n" out += "}\n" out += "\n" out += "inline ucap::{pname} conv_to_enum_{name}(enum_{name} e) {{\n".format( name=t.name, pname=pname) out += "\tswitch(e) {\n" out += "\tcase enum_{name}::{e}:\n".format(name=t.name, e='UXSD_INVALID') out += "\t\treturn ucap::{pname}::{e};\n".format(pname=pname, e='UXSD_INVALID') for e in t.enumeration: out += "\tcase enum_{name}::{e}:\n".format(name=t.name, e=e.upper()) out += "\t\treturn ucap::{pname}::{e};\n".format(pname=pname, e=e.upper()) out += "\tdefault:\n" out += '\t\tthrow std::runtime_error("Unknown enum_{name}");\n'.format( name=t.name) out += "\t}\n" out += "}\n" return out
def render_impl_header_file(schema: UxsdSchema, cmdline: str, capnp_file_name: str, interface_file_name: str, input_file: str) -> str: out = "" x = { "version": __version__, "cmdline": cmdline, "input_file": input_file, "md5": utils.md5(input_file) } out += cpp_templates.header_comment.substitute(x) out += '#include <stdexcept>\n' out += '#include <vector>\n' out += '#include "capnp/serialize.h"\n' out += '#include "{}.h"\n'.format(capnp_file_name) out += '#include "{}"\n'.format(interface_file_name) out += "\n/* All uxsdcxx functions and structs live in this namespace. */\n" out += "namespace uxsd {\n" if schema.enums: enum_converters = [_gen_conv_enum(t) for t in schema.enums] out += "\n\n/* Enum conversions from uxsd to ucap */\n" out += "\n".join(enum_converters) pname = utils.to_pascalcase(schema.root_element.name) out += "struct Capnp{pname}ContextTypes : public Default{pname}ContextTypes {{\n\t".format( pname=pname) out += "\n\t".join( "using {pname}ReadContext = ucap::{pname}::Reader;".format( pname=utils.to_pascalcase(t.name)) for t in schema.complex_types) out += "\n\t" out += "\n\t".join( "using {pname}WriteContext = ucap::{pname}::Builder;".format( pname=utils.to_pascalcase(t.name)) for t in schema.complex_types) out += "\n};\n" out += "\n" out += "class Capnp{pname} : public {pname}Base<Capnp{pname}ContextTypes> {{\n\t".format( pname=pname) out += "public:\n" out += "\tCapnp{pname}() {{}}\n\n".format(pname=pname) out += "\tvoid start_load(const std::function<void(const char *)> *report_error_in) override {\n" out += "\t\treport_error = report_error_in;\n" out += "\t}\n" out += "\tvoid finish_load() override {}\n" out += "\tvoid start_write() override {}\n" out += "\tvoid finish_write() override {}\n" out += "\tvoid error_encountered(const char * file, int line, const char *message) override {\n" out += "\t\tstd::stringstream msg;" out += "\t\tmsg << message << \" occured at file: \" << file << \" line: \" << line;\n" out += "\t\tthrow std::runtime_error(msg.str());\n" out += "\t}\n" for t in schema.complex_types: out += utils.indent( _gen_capnp_impl(t, t.name == schema.root_element.name)) out += "private:\n" out += "\tconst std::function<void(const char *)> *report_error;\n" for t in schema.complex_types: if isinstance(t.content, (UxsdDfa, UxsdAll)): for el in t.content.children: if el.many: out += "\tstd::vector<capnp::Orphan<ucap::{pname}>> {name}_;\n".format( pname=utils.to_pascalcase(el.type.name), name=utils.pluralize(el.type.name)) out += "};\n" out += "\n\n} /* namespace uxsd */\n" return out
def to_type(t: UxsdType) -> str: """For determining type in fields only.""" if isinstance(t, UxsdAtomic): return tpl.atomic_builtins[t.name] else: return utils.to_pascalcase(t.name)
def _add_num(e: UxsdElement): impl = "" impl += "return reader.get{pname}().size();\n".format( pname=utils.to_pascalcase(utils.pluralize(e.name))) _add_field("size_t", "num", e.name, _gen_reader(t) + " &reader", impl)
def _add_get_complex(e: UxsdElement): impl = "" impl += "return reader.get{pname}();\n".format( name=e.type.name, pname=utils.to_pascalcase(e.name)) _add_field(_gen_reader(e.type), "get", e.name, _gen_reader(t) + " &reader", impl)
def render_header_file(schema: UxsdSchema, cmdline: str, capnp_file_name: str, interface_file_name: str, input_file: str) -> str: """Render a C++ header file to a string.""" out = "" x = { "version": __version__, "cmdline": cmdline, "input_file": input_file, "md5": utils.md5(input_file) } out += cpp_templates.header_comment.substitute(x) out += '#include <stdexcept>\n' out += '#include <tuple>\n' out += '#include <vector>\n' out += '#include <sstream>\n' out += '#include <limits>\n' out += '#include "capnp/serialize.h"\n' out += '#include "{}.h"\n'.format(capnp_file_name) out += '#include "{}"\n'.format(interface_file_name) out += "\n/* All uxsdcxx functions and structs live in this namespace. */\n" out += "namespace uxsd {" out += "\n/* Declarations for internal load functions for the complex types. */\n" load_fn_decls = [] for t in schema.complex_types: load_fn_decls.append("template <class T, typename Context>") load_fn_decls.append( "void load_{name}_capnp_type(const ucap::{cname}::Reader &root, T &out, Context &context, const std::function<void(const char*)> * report_error, std::vector<std::pair<const char *, size_t>> * stack);" .format(name=t.name, cname=utils.to_pascalcase(t.name))) out += "\n".join(load_fn_decls) out += "\n\n/* Declarations for internal write functions for the complex types. */\n" write_fn_decls = [] for t in schema.complex_types: if t.content is None: continue write_fn_decls.append("template <class T, typename Context>") write_fn_decls.append( "inline void write_{name}_capnp_type(T &in, ucap::{cname}::Builder &root, Context &context);" .format(name=t.name, cname=utils.to_pascalcase(t.name))) out += "\n".join(write_fn_decls) if schema.enums: enum_converters = [_gen_conv_enum(t) for t in schema.enums] out += "\n\n/* Enum conversions from uxsd to ucap */\n" out += "\n".join(enum_converters) out += "\n\n/* Load function for the root element. */\n" out += load_fn_from_element(schema.root_element) out += "\n/* Write function for the root element. */\n" out += write_fn_from_root_element(schema.root_element) complex_type_loaders = [ load_fn_from_complex_type(t) for t in schema.complex_types ] out += "\n\n/* Internal loading functions, which validate and load a PugiXML DOM tree into memory. */\n" out += "\n".join(complex_type_loaders) complex_type_writers = [ write_fn_from_complex_type(t) for t in schema.complex_types if t.content is not None ] out += "\n\n/* Internal writing functions, which uxsdcxx uses to write out a class. */\n" out += "\n".join(complex_type_writers) out += "\n\n} /* namespace uxsd */\n" return out
def _gen_builder(e: UxsdComplex): return 'ucap::{}::Builder'.format(utils.to_pascalcase(e.name))
def load_fn_from_complex_type(t: UxsdComplex) -> str: """Generate a full C++ function load_foo(&root, &out) which can load an XSD complex type from DOM &root into C++ object out. """ out = "" out += "template<class T, typename Context>\n" out += "inline void load_{name}_capnp_type(const ucap::{cname}::Reader &root, T &out, Context &context, const std::function<void(const char*)> * report_error, std::vector<std::pair<const char *, size_t>> * stack){{\n".format( name=t.name, cname=utils.to_pascalcase(t.name)) out += "\t(void)root;\n" out += "\t(void)out;\n" out += "\t(void)context;\n" out += "\t(void)report_error;\n" out += "\t(void)stack;\n" out += "\n" for attr in t.attrs: if cpp.pass_at_init(attr): continue out += "\tout.set_{suffix}({data}, context);\n".format( suffix=cpp._gen_stub_suffix(attr, t.name), data=_gen_load_simple( attr.type, 'root.get{pname}()'.format( pname=utils.to_pascalcase(attr.name)))) if isinstance(t.content, (UxsdDfa, UxsdAll)): for el in t.content.children: name = utils.to_pascalcase(el.name) out += "\tstack->push_back(std::make_pair(\"get{}\", 0));\n".format( name) if el.many: out += "\t{\n" suffix = cpp._gen_stub_suffix(el, t.name) out += "\t\tauto data = root.get{pname}();\n".format( pname=utils.pluralize(name)) out += "\t\tout.preallocate_{suffix}(context, data.size());\n".format( suffix=suffix) out += "\t\tfor(const auto & el : data) {\n" if isinstance(el.type, UxsdComplex): out += "\t\t\tauto child_context = out.add_{suffix}(context{required_attrs});\n".format( suffix=suffix, required_attrs=_gen_required_attribute_arg_list( el, 'el')) out += "\t\t\tload_{suffix}_capnp_type(el, out, child_context, report_error, stack);\n".format( suffix=el.type.name) out += "\t\t\tout.finish_{suffix}(child_context);\n".format( suffix=suffix) else: out += "\t\t\tout.add_{suffix}({data}, context);\n".format( suffix=suffix, data=_gen_load_simple(el, 'el.get{}()'.format(name))) out += "\t\t\tstack->back().second += 1;\n" out += "\t\t}\n" out += "\t}\n" else: out += "\tif (root.has{pname}()) {{\n".format(pname=name) if isinstance(el.type, UxsdComplex): out += "\t\tauto child_el = root.get{pname}();\n".format( pname=name) access = 'child_el' out += "\t\tauto child_context = out.init_{suffix}(context{required_attrs});\n".format( suffix=cpp._gen_stub_suffix(el, t.name), required_attrs=_gen_required_attribute_arg_list( el, access)) out += "\t\tload_{suffix}_capnp_type({access}, out, child_context, report_error, stack);\n".format( access=access, suffix=el.type.name, pname=name) out += "\t\tout.finish_{suffix}(child_context);\n".format( suffix=cpp._gen_stub_suffix(el, t.name)) else: out += "\t\tout.init_{suffix}({data}, context);\n".format( suffix=cpp._gen_stub_suffix(el, t.name), data=_gen_load_simple(el, 'root.get{name}()'.format(name))) out += "\t}\n" out += "\tstack->pop_back();\n" elif isinstance(t.content, UxsdLeaf): out += "\tstack->push_back(std::make_pair(\"getValue\", 0));\n" out += "\tout.set_{name}_value(root.getValue().cStr(), context);\n".format( name=t.name) out += "\tstack->pop_back();\n" out += "}\n" return out