Example #1
0
def _gen_write_complex(t: UxsdComplex, name: str, container: str) -> str:
    """Partial function to generate code which writes out a simple type."""
    out = ""
    if t.attrs:
        out += "os << \"<%s\";\n" % name
        for a in t.attrs:
            out += _gen_write_attr(a, container)
    else:
        out += "os << \"<%s\";\n" % name

    if isinstance(t.content, (UxsdDfa, UxsdAll)):
        out += "os << \">\";\n"
        for e in t.content.children:
            if e.many:
                out += "for(auto &%s: %s.%s){\n" % (utils.checked(
                    e.name), container, utils.pluralize(e.name))
                out += utils.indent(
                    _gen_write_element(e, utils.checked(e.name)))
                out += "}\n"
            else:
                new_container = "%s.%s" % (container, utils.checked(e.name))
                if e.optional:
                    out += "if(%s.has_%s){\n" % (container, e.name)
                    out += utils.indent(_gen_write_element(e, new_container))
                    out += "}\n"
                else:
                    out += _gen_write_element(e, new_container)
        out += "os << \"</%s>\";\n" % name
    elif isinstance(t.content, UxsdLeaf):
        out += "os << \">\";\n"
        out += _gen_write_simple(t.content.type, container + ".value")
        out += "os << \"</%s>\";\n" % name
    else:
        out += "os << \"/>\";\n"
    return out
Example #2
0
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++ type *out.
	"""
    out = ""

    out += "void load_%s(const pugi::xml_node &root, %s *out){\n" % (t.name,
                                                                     t.cpp)
    if isinstance(t.content, UxsdDfa):
        out = _gen_dfa_table(t) + out
        out += utils.indent(_gen_load_dfa(t))
    elif isinstance(t.content, UxsdAll):
        out += utils.indent(_gen_load_all(t))
    elif isinstance(t.content, UxsdLeaf):
        out += utils.indent(
            _gen_load_simple(t.content.type, "out->value",
                             "root.child_value()"))
    else:
        out += "\tif(root.first_child().type() == pugi::node_element)\n"
        out += "\t\tthrow std::runtime_error(\"Unexpected child element in <%s>.\");\n" % t.name

    out += "\n"
    if t.attrs:
        out += utils.indent(_gen_load_attrs(t))
    else:
        out += "\tif(root.first_attribute())\n"
        out += "\t\tthrow std::runtime_error(\"Unexpected attribute in <%s>.\");\n" % t.name

    out += "}\n"
    return out
Example #3
0
def lexer_from_complex_type(t: UxsdComplex) -> str:
    """Generate one or two C++ functions to convert const char *s to enum values
	generated from an UxsdComplex.

	It's in the form of (a|g)tok_foo lex_(attr|node)_foo(const char *in) and currently uses
	a trie to lex the string. a or g indicates if the token is an attribute token or a group
	(child element) token.
	"""
    out = ""
    if isinstance(t.content, (UxsdDfa, UxsdAll)):
        out += "inline gtok_%s lex_node_%s(const char *in){\n" % (t.cpp, t.cpp)
        triehash_alph = [(e.name,
                          "gtok_%s::%s" % (t.cpp, utils.to_token(e.name)))
                         for e in t.content.children]
        out += utils.indent(triehash.gen_lexer_body(triehash_alph))
        out += "\tthrow std::runtime_error(\"Found unrecognized child \" + std::string(in) + \" of <%s>.\");\n" % t.name
        out += "}\n"
    if t.attrs:
        out += "inline atok_%s lex_attr_%s(const char *in){\n" % (t.cpp, t.cpp)
        triehash_alph = [(x.name,
                          "atok_%s::%s" % (t.cpp, utils.to_token(x.name)))
                         for x in t.attrs]
        out += utils.indent(triehash.gen_lexer_body(triehash_alph))
        out += "\tthrow std::runtime_error(\"Found unrecognized attribute \" + std::string(in) + \" of <%s>.\");\n" % t.name
        out += "}\n"
    return out
Example #4
0
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
Example #5
0
def _gen_write_simple(t: UxsdSimple,
                      container: str,
                      attr_name: str = "") -> str:
    """Partial function to generate code which writes out a simple type.

	The attr_name parameter is passed by _gen_write_attr so that we can
	generate squashed code like `os << "index=\"" << y_list.index << "\"";`.
	"""
    out = ""
    if isinstance(t, UxsdAtomic):
        if attr_name:
            out += "os << \" %s=\\\"\" << %s << \"\\\"\";\n" % (attr_name,
                                                                container)
        else:
            out += "os << %s;\n" % container
    elif isinstance(t, UxsdEnum):
        if attr_name:
            out += "os << \" %s=\\\"\" << lookup_%s[(int)%s] << \"\\\"\";\n" % (
                attr_name, t.name, container)
        else:
            out += "os << lookup_%s[(int)%s];\n" % (t.name, container)
    elif isinstance(t, UxsdUnion):
        for m in t.member_types:
            out += "if(%s.tag == type_tag::%s)" % (container,
                                                   utils.to_token(m.cpp))
            out += utils.indent(
                _gen_write_simple(
                    t, container + "." + utils.to_union_field_name(m.cpp),
                    attr_name))
    else:
        raise NotImplementedError("I don't know how to write out %s." % t)
    return out
Example #6
0
def _gen_load_attrs(t: UxsdComplex) -> str:
    """Partial function to generate the attribute loading portion of a C++
	function load_foo. See _gen_load_all to see how attributes are validated.
	"""
    assert len(t.attrs) > 0
    N = len(t.attrs)
    out = ""
    out += "std::bitset<%d> astate = 0;\n" % N
    out += "for(pugi::xml_attribute attr = root.first_attribute(); attr; attr = attr.next_attribute()){\n"
    out += "\tatok_%s in = lex_attr_%s(attr.name());\n" % (t.cpp, t.cpp)
    out += "\tif(astate[(int)in] == 0) astate[(int)in] = 1;\n"
    out += "\telse throw std::runtime_error(\"Duplicate attribute \" + std::string(attr.name()) + \" in <%s>.\");\n" % t.name

    out += "\tswitch(in){\n"
    for attr in t.attrs:
        out += "\tcase atok_%s::%s:\n" % (t.cpp, utils.to_token(attr.name))
        out += utils.indent(
            _gen_load_simple(attr.type, "out->%s" % utils.checked(attr.name),
                             "attr.value()"), 2)
        out += "\t\tbreak;\n"
    out += "\tdefault: break; /* Not possible. */\n"
    out += "\t}\n"
    out += "}\n"

    mask = "".join(["1" if x.optional else "0" for x in t.attrs][::-1])
    out += "std::bitset<%d> test_astate = astate | std::bitset<%d>(0b%s);\n" % (
        N, N, mask)
    out += "if(!test_astate.all()) attr_error(test_astate, atok_lookup_%s);\n" % t.cpp
    return out
Example #7
0
def _gen_write_attr(a: UxsdAttribute, container: str) -> str:
    """Partial function to generate code which writes out a single XML attribute."""
    out = ""
    new_container = "%s.%s" % (container, a.name)
    if not a.optional or a.default_value:
        out += _gen_write_simple(a.type, new_container, a.name)
    else:
        out += "if((bool)%s)\n" % new_container
        out += utils.indent(_gen_write_simple(a.type, new_container, a.name))
    return out
Example #8
0
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
Example #9
0
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
Example #10
0
def typedefn_from_complex_type(t: UxsdComplex) -> str:
    """Generate a C++ struct definition corresponding to a UxsdComplex.

	Example:
	/**
	 * Generated from:
	 * <xs:complexType... />
	*/
	struct t_foo {
		int bar;
		bool has_my_baz;
		t_baz my_baz;
		collapsed_vec<t_quux, quux_pool> my_quuxes;
	}
	"""
    fields = []
    for attr in t.attrs:
        fields.append("%s %s;" % (attr.type.cpp, utils.checked(attr.name)))
    if isinstance(t.content, (UxsdDfa, UxsdAll)):
        for el in t.content.children:
            if el.many:
                fields.append(
                    "collapsed_vec<%s, %s_pool> %s;" %
                    (el.type.cpp, el.type.name, utils.pluralize(el.name)))
            elif el.optional:
                fields.append("bool has_%s;" % el.name)
                fields.append("%s %s;" % (el.type.cpp, utils.checked(el.name)))
            else:
                fields.append("%s %s;" % (el.type.cpp, utils.checked(el.name)))
    elif isinstance(t.content, UxsdLeaf):
        fields.append("%s value;" % t.content.type.cpp)

    out = ""
    out += "/** Generated from:\n"
    out += utils.to_comment_body(t.source)
    out += "\n*/\n"
    out += "struct %s {\n" % t.cpp
    out += utils.indent("\n".join(fields))
    out += "\n};"
    return out
Example #11
0
def _gen_load_dfa(t: UxsdComplex) -> str:
    """Partial function to generate the child element validation&loading portion
	of a C++ function load_foo, if the model group is an xs:sequence or xs:choice.

	xs:sequence/xs:choice groups can be compiled down to a finite automaton.
	This is done in dfa.py. C++ state table is generated in _gen_dfa_table and the
	stream of child elements are validated according to the table here.

	The C++ table has -1s in place of invalid state transitions. If we step into a -1,
	we call dfa_error. We check again at the end of input. If we aren't in an accepted
	state, we again call dfa_error.
	"""
    assert isinstance(t.content, UxsdDfa)
    dfa = t.content.dfa
    out = ""
    out += "int next, state=%d;\n" % dfa.start
    out += "for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){\n"
    out += "\tgtok_%s in = lex_node_%s(node.name());\n" % (t.cpp, t.cpp)

    out += "\tnext = gstate_%s[state][(int)in];\n" % t.cpp
    out += "\tif(next == -1)\n"
    out += "\t\tdfa_error(gtok_lookup_%s[(int)in], gstate_%s[state], gtok_lookup_%s, %d);\n"\
        % (t.cpp, t.cpp, t.cpp, len(dfa.alphabet))
    out += "\tstate = next;\n"

    out += "\tswitch(in){\n"
    for el in t.content.children:
        out += "\tcase gtok_%s::%s:\n" % (t.cpp, utils.to_token(el.name))
        out += utils.indent(_gen_load_element(el, "out"), 2)
        out += "\t\tbreak;\n"
    out += "\tdefault: break; /* Not possible. */\n"
    out += "\t}\n"

    reject_cond = " && ".join(["state != %d" % x for x in dfa.accepts])
    out += "}\n"
    out += "if(%s) dfa_error(\"end of input\", gstate_%s[state], gtok_lookup_%s, %d);\n"\
      % (reject_cond, t.cpp, t.cpp, len(dfa.alphabet))

    return out
Example #12
0
def lexer_from_enum(t: UxsdEnum) -> str:
    """Generate a C++ function to convert const char *s to enum values generated
	from an UxsdEnum.

	It's in the form of enum_foo lex_enum_foo(const char *in, bool throw_on_invalid)
	and currently uses a trie to parse the string.
	throw_on_invalid is a hacky parameter to determine if we should throw on
	an invalid value. It's currently necessary to read unions - we don't need to
	throw on an invalid value if we are trying to read into an union but we need
	to throw otherwise.
	"""
    out = ""
    out += "inline %s lex_%s(const char *in, bool throw_on_invalid){\n" % (
        t.cpp, t.cpp)
    triehash_alph = [(x, "%s::%s" % (t.cpp, utils.to_token(x)))
                     for x in t.enumeration]
    out += utils.indent(triehash.gen_lexer_body(triehash_alph))
    out += "\tif(throw_on_invalid)\n"
    out += "\t\tthrow std::runtime_error(\"Found unrecognized enum value \" + std::string(in) + \" of %s.\");\n" % t.cpp
    out += "\treturn %s::UXSD_INVALID;\n" % t.cpp
    out += "}\n"
    return out
Example #13
0
def _gen_load_all(t: UxsdComplex) -> str:
    """Partial function to generate the child element validation&loading portion
	of a C++ function load_foo, if the model group is an xs:all.

	xs:alls can be validated in a similar fashion to xs:attributes. We maintain a
	bitset of which elements are found. At the end, we OR our bitset with the value
	corresponding to the optional elements and check if all bits in it are set. If not,
	we call attr_error with the token lookup table and the OR'd bitset.
	"""
    assert isinstance(t.content, UxsdAll)
    N = len(t.content.children)
    out = ""

    out += "std::bitset<%d> gstate = 0;\n" % N
    out += "for(pugi::xml_node node = root.first_child(); node; node = node.next_sibling()){\n"
    out += "\tgtok_%s in = lex_node_%s(node.name());\n" % (t.cpp, t.cpp)

    out += "\tif(gstate[(int)in] == 0) gstate[(int)in] = 1;\n"
    out += "\telse throw std::runtime_error(\"Duplicate element \" + std::string(node.name()) + \" in <%s>.\");\n" % t.name

    out += "\tswitch(in){\n"
    for el in t.content.children:
        out += "\tcase gtok_%s::%s:\n" % (t.cpp, utils.to_token(el.name))
        out += utils.indent(_gen_load_element(el, "out"), 2)
        out += "\t\tbreak;\n"
    out += "\tdefault: break; /* Not possible. */\n"
    out += "\t}\n"
    out += "}\n"

    mask = "".join(["1" if x.optional else "0"
                    for x in t.content.children][::-1])
    out += "std::bitset<%d> test_gstate = gstate | std::bitset<%d>(0b%s);\n" % (
        N, N, mask)
    out += "if(!test_gstate.all()) all_error(test_gstate, gtok_lookup_%s);\n" % t.cpp

    return out
Example #14
0
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
Example #15
0
 def _add_field(ret: str, verb: str, what: str, args: str, impl: str):
     fields.append("inline %s %s_%s_%s(%s) override {\n%s}\n" %
                   (ret, verb, t.name, what, args, utils.indent(impl)))
Example #16
0
def write_fn_from_element(t: UxsdElement) -> str:
    out = ""
    out += "void %s::write(std::ostream &os){\n" % utils.checked(t.name)
    out += utils.indent(_gen_write_element(t, "(*this)"))
    out += "}\n"
    return out