diff options
Diffstat (limited to 'xsde/cxx/hybrid/parser-name-processor.cxx')
-rw-r--r-- | xsde/cxx/hybrid/parser-name-processor.cxx | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/xsde/cxx/hybrid/parser-name-processor.cxx b/xsde/cxx/hybrid/parser-name-processor.cxx new file mode 100644 index 0000000..482c195 --- /dev/null +++ b/xsde/cxx/hybrid/parser-name-processor.cxx @@ -0,0 +1,709 @@ +// file : xsde/cxx/hybrid/parser-name-processor.cxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2006-2009 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#include <cxx/elements.hxx> +#include <cxx/hybrid/parser-name-processor.hxx> + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <cult/containers/set.hxx> +#include <cult/containers/map.hxx> + +#include <sstream> +#include <iostream> + +namespace CXX +{ + namespace Hybrid + { + namespace + { + // + // + typedef Cult::Containers::Set<String> NameSet; + + class Context: public CXX::Context + { + public: + Context (CLI::Options const& ops, + SemanticGraph::Schema& root, + SemanticGraph::Path const& file) + : CXX::Context (std::wcerr, + root, + "name", + "char", + ops.value<CLI::include_with_brackets> (), + ops.value<CLI::include_prefix> (), + "", // export symbol + ops.value<CLI::namespace_map> (), + ops.value<CLI::namespace_regex> (), + ops.value<CLI::namespace_regex_trace> (), + ops.value<CLI::include_regex> (), + ops.value<CLI::include_regex_trace> (), + ops.value<CLI::generate_inline> (), + ops.value<CLI::reserved_name> ()), + schema_path_ (file), + impl_suffix_ (ops.value<CLI::pimpl_type_suffix> ()), + aggr_suffix_ (ops.value<CLI::paggr_type_suffix> ()), + options (ops), + schema (root), + schema_path (schema_path_), + aggregate (ops.value<CLI::generate_aggregate> ()), + impl_suffix (impl_suffix_), + aggr_suffix (aggr_suffix_), + custom_parser_map (custom_parser_map_), + global_type_names (global_type_names_) + { + // Custom parser mapping. + // + typedef Containers::Vector<NarrowString> Vector; + Vector const& v (ops.value<CLI::custom_parser> ()); + + for (Vector::ConstIterator i (v.begin ()), e (v.end ()); + i != e; ++i) + { + String s (*i); + + if (s.empty ()) + continue; + + // Split the string in two parts at the last '='. + // + Size pos (s.rfind ('=')); + + // If no delimiter found then both base and include are empty. + // + if (pos == String::npos) + { + custom_parser_map_[s].base.clear (); + custom_parser_map_[s].include.clear (); + continue; + } + + String name (s, 0, pos); + String rest (s, pos + 1); + + // See if we've got the include part after '/'. + // + pos = rest.find ('/'); + + String base, include; + + if (pos != String::npos) + { + base.assign (rest, 0, pos); + include.assign (rest, pos + 1, String::npos); + } + else + base = rest; + + custom_parser_map_[name].base = base; + custom_parser_map_[name].include = include; + } + } + + protected: + Context (Context& c) + : CXX::Context (c), + options (c.options), + schema (c.schema), + schema_path (c.schema_path), + aggregate (c.aggregate), + impl_suffix (c.impl_suffix), + aggr_suffix (c.aggr_suffix), + custom_parser_map (c.custom_parser_map), + global_type_names (c.global_type_names) + { + } + + public: + Boolean + fixed_length (SemanticGraph::Type& t) + { + return t.context ().get<Boolean> ("fixed"); + } + + public: + String + find_name (String const& n, String const& suffix, NameSet& set) + { + String name (escape (n + suffix)); + + for (UnsignedLong i (1); set.find (name) != set.end (); ++i) + { + std::wostringstream os; + os << i; + name = escape (n + os.str () + suffix); + } + + set.insert (name); + return name; + } + + String + find_name (String const& n, NameSet& set) + { + return find_name (n, L"", set); + } + + public: + using CXX::Context::ename; + + static String const& + ename (SemanticGraph::Compositor& c) + { + return c.context ().get<String> ("name"); + } + + String + state_name (SemanticGraph::Compositor& c) + { + using namespace SemanticGraph; + + String r; + + for (Compositor* p (&c);; + p = &p->contained_particle ().compositor ()) + { + if (p->context ().count ("type")) + { + // Not a see-through compositor. + // + if (!r) + r = ename (*p); + else + { + String tmp; + tmp.swap (r); + r = ename (*p); + r += L"_"; + r += tmp; + } + } + + if (p->contained_compositor_p ()) + break; + } + + return r; + } + + String + state_name (SemanticGraph::Element& e) + { + String r (state_name (e.contained_particle ().compositor ())); + + if (!r) + r = ename (e); + else + { + r += L"_"; + r += ename (e); + } + + return r; + } + + public: + struct CustomParser + { + CustomParser (String const& b = L"", String const& i = L"") + : base (b), include (i) + { + } + + String base; + String include; + }; + + typedef + Cult::Containers::Map<String, CustomParser> + CustomParserMap; + + private: + SemanticGraph::Path const schema_path_; + String const impl_suffix_; + String const aggr_suffix_; + CustomParserMap custom_parser_map_; + + Cult::Containers::Map<String, NameSet*> global_type_names_; + + public: + CLI::Options const& options; + SemanticGraph::Schema& schema; + SemanticGraph::Path const& schema_path; + Boolean aggregate; + String const& impl_suffix; + String const& aggr_suffix; + CustomParserMap const& custom_parser_map; + + Cult::Containers::Map<String, NameSet*>& global_type_names; + }; + + // + // + struct List: Traversal::List, Context + { + List (Context& c) + : Context (c) + { + } + + virtual Void + traverse (Type& l) + { + SemanticGraph::Context& lc (l.context ()); + + // In case of customization use p:impl-base instead of p:impl. + // If the name is empty then we are not generating anything. + // + String const& name (lc.count ("p:impl-base") + ? lc.get<String> ("p:impl-base") + : lc.get<String> ("p:impl")); + if (!name) + return; + + NameSet set; + set.insert (name); + set.insert (unclash (lc.get<String> ("p:name"), "item")); + + lc.set ("pstate-member", + find_name (lc.get<String> ("name"), "_", set)); + + lc.set ("pstate-base", find_name (name + L"_base", "_", set)); + } + }; + + // + // + struct Union: Traversal::Union, Context + { + Union (Context& c) + : Context (c) + { + } + + virtual Void + traverse (Type& u) + { + SemanticGraph::Context& uc (u.context ()); + + // In case of customization use p:impl-base instead of p:impl. + // If the name is empty then we are not generating anything. + // + String const& name (uc.count ("p:impl-base") + ? uc.get<String> ("p:impl-base") + : uc.get<String> ("p:impl")); + if (!name) + return; + + NameSet set; + set.insert (name); + + String state_type (find_name (name + L"_state", set)); + + uc.set ("pstate-type", state_type); + uc.set ("pstate", find_name (state_type, "_", set)); + + if (!fixed_length (u)) + uc.set ("pstate-base", find_name (name + L"_base", "_", set)); + } + }; + + // State names. + // + + struct CompositorState: Traversal::Compositor, Context + { + CompositorState (Context& c, NameSet& set) + : Context (c), set_ (set) + { + } + + virtual Void + traverse (SemanticGraph::Compositor& c) + { + if (c.max () != 1) + { + SemanticGraph::Context& cc (c.context ()); + cc.set ("pstate-member", find_name (state_name (c), L"_", set_)); + } + + Compositor::traverse (c); + } + + private: + NameSet& set_; + }; + + // + // + struct Complex: Traversal::Complex, Context + { + Complex (Context& c) + : Context (c) + { + } + + virtual Void + traverse (Type& c) + { + Boolean restriction (false); + + if (c.inherits_p ()) + restriction = c.inherits ().is_a<SemanticGraph::Restricts> () && + !c.inherits ().base ().is_a<SemanticGraph::AnyType> (); + + SemanticGraph::Context& cc (c.context ()); + + // In case of customization use p:impl-base instead of p:impl. + // If the name is empty then we are not generating anything. + // + String const& base (cc.count ("p:impl-base") + ? cc.get<String> ("p:impl-base") + : cc.get<String> ("p:impl")); + + if (!base) + return; + + // Use skeleton's name set to make sure we don't clash + // with callbacks which we are overriding. + // + NameSet& set ( + cc.get<NameSet> ("cxx-parser-name-processor-member-set")); + + String state_type (find_name (base + L"_state", set)); + + cc.set ("pstate-type", state_type); + cc.set ("pstate", find_name (state_type, "_", set)); + cc.set ("pstate-base", find_name (base + L"_base", "_", set)); + + // State members are in a nested struct so use a new and + // empty name set. + // + NameSet state_set; + + String member ( + find_name (cc.get<String> ("name"), "_", state_set)); + + cc.set ("pstate-member", member); + state_set.insert (member); + + // Assign state names. + // + if (!restriction && c.contains_compositor_p ()) + { + CompositorState compositor (*this, state_set); + Traversal::ContainsCompositor contains_compositor; + Traversal::ContainsParticle contains_particle; + + contains_compositor >> compositor; + contains_particle >> compositor >> contains_particle; + + Complex::contains_compositor (c, contains_compositor); + } + } + }; + + // + // + struct GlobalType: Traversal::Type, Context + { + GlobalType (Context& c, NameSet& set) + : Context (c), set_ (set) + { + } + + virtual Void + traverse (SemanticGraph::Type& t) + { + SemanticGraph::Context& tc (t.context ()); + String const& name (t.name ()); + + tc.set ("p:impl", find_name (name + impl_suffix, set_)); + + // See if this parser is being customized. + // + CustomParserMap::ConstIterator i (custom_parser_map.find (name)); + + if (i != custom_parser_map.end ()) + { + tc.set ("p:impl-base", i->second.base + ? find_name (i->second.base, set_) + : i->second.base); + + if (i->second.include) + tc.set ("p:impl-include", i->second.include); + } + + if (aggregate) + { + typedef Cult::Containers::Vector<NarrowString> Names; + Names const& names (options.value<CLI::root_type> ()); + + // Hopefully nobody will specify more than a handful of names. + // + for (Names::ConstIterator i (names.begin ()); + i != names.end (); ++i) + { + if (name == String (*i)) + { + tc.set ("paggr", find_name (name + aggr_suffix, set_)); + break; + } + } + } + } + + private: + NameSet& set_; + }; + + // + // + struct GlobalElement: Traversal::Element, Context + { + GlobalElement (Context& c, NameSet& set) + : Context (c), set_ (set), last_ (0) + { + } + + ~GlobalElement () + { + if (last_ != 0 && options.value<CLI::root_element_last> ()) + process (*last_); + } + + virtual Void + traverse (SemanticGraph::Element& e) + { + Boolean p (false); + + if (last_ == 0 && options.value<CLI::root_element_first> ()) + p = true; + + last_ = &e; + + if (!p && + !options.value<CLI::root_element_first> () && + !options.value<CLI::root_element_last> () && + !options.value<CLI::root_element_all> () && + !options.value<CLI::root_element_none> () && + options.value<CLI::root_element> ().empty ()) + { + // By default process them all. + // + p = true; + } + + if (!p && options.value<CLI::root_element_all> ()) + p = true; + + if (!p) + { + typedef Cult::Containers::Vector<NarrowString> Names; + Names const& names (options.value<CLI::root_element> ()); + + // Hopefully nobody will specify more than a handful of names. + // + for (Names::ConstIterator i (names.begin ()); + !p && i != names.end (); + ++i) + { + if (e.name () == String (*i)) + p = true; + } + } + + if (p) + process (e); + } + + private: + Void + process (SemanticGraph::Element& e) + { + SemanticGraph::Context& ec (e.context ()); + + if (!ec.count ("paggr")) + { + ec.set ("paggr", find_name (e.name () + aggr_suffix, set_)); + } + } + + private: + NameSet& set_; + SemanticGraph::Element* last_; + }; + + struct Namespace: Traversal::Namespace, Context + { + Namespace (Context& c) + : Context (c) + { + } + + virtual Void + traverse (Type& ns) + { + SemanticGraph::Context& nsc (ns.context ()); + String const& name (ns.name ()); + + // Use a name set associated with this namespace if present. + // This will make sure that we don't get any conflicts in the + // multi-mapping translation case. Note that here we assume + // that all mappings traverse schemas in the same order which + // is currently the case. + // + if (global_type_names.find (name) == global_type_names.end ()) + { + if (!nsc.count ("name-set")) + nsc.set ("name-set", NameSet ()); + + NameSet& s (nsc.get<NameSet> ("name-set")); + global_type_names[name] = &s; + } + + NameSet& type_set (*global_type_names[name]); + + // Parser implementations. + // + { + GlobalType type (*this, type_set); + Traversal::Names names (type); + + Traversal::Namespace::names (ns, names); + } + + // Parser aggregates. + // + if (aggregate) + { + GlobalElement element (*this, type_set); + Traversal::Names names (element); + + Traversal::Namespace::names (ns, names); + } + } + }; + + // Go into sourced/included/imported schemas while making sure + // we don't process the same stuff more than once. + // + struct Uses: Traversal::Sources, + Traversal::Includes, + Traversal::Imports + { + virtual Void + traverse (SemanticGraph::Sources& sr) + { + SemanticGraph::Schema& s (sr.schema ()); + + if (!s.context ().count (seen_key)) + { + s.context ().set (seen_key, true); + Traversal::Sources::traverse (sr); + } + } + + virtual Void + traverse (SemanticGraph::Includes& i) + { + SemanticGraph::Schema& s (i.schema ()); + + if (!s.context ().count (seen_key)) + { + s.context ().set (seen_key, true); + Traversal::Includes::traverse (i); + } + } + + virtual Void + traverse (SemanticGraph::Imports& i) + { + SemanticGraph::Schema& s (i.schema ()); + + if (!s.context ().count (seen_key)) + { + s.context ().set (seen_key, true); + Traversal::Imports::traverse (i); + } + } + + static Char const* seen_key; + }; + + Char const* Uses::seen_key = "cxx-hybrid-parser-name-processor-seen"; + + Void + process_impl (CLI::Options const& ops, + SemanticGraph::Schema& tu, + SemanticGraph::Path const& file) + { + Context ctx (ops, tu, file); + + // Pass one - assign names to global types. This pass cannot + // be combined with pass two because of possible recursive + // schema inclusions. Also note that we check first if this + // schema has already been processed which may happen in the + // file-per-type compilation mode. + // + if (!tu.context ().count (Uses::seen_key)) + { + Traversal::Schema schema; + Uses uses; + + schema >> uses >> schema; + + Traversal::Names schema_names; + Namespace ns (ctx); + + schema >> schema_names >> ns; + + // Some twisted schemas do recusive self-inclusion. + // + tu.context ().set (Uses::seen_key, true); + + schema.dispatch (tu); + } + + // Pass two - assign names inside complex types. Here we don't + // need to go into included/imported schemas. + // + { + Traversal::Schema schema; + Traversal::Sources sources; + + schema >> sources >> schema; + + Traversal::Names schema_names; + Traversal::Namespace ns; + Traversal::Names ns_names; + + schema >> schema_names >> ns >> ns_names; + + List list (ctx); + Union union_ (ctx); + Complex complex (ctx); + + ns_names >> list; + ns_names >> union_; + ns_names >> complex; + + schema.dispatch (tu); + } + } + } + + Void ParserNameProcessor:: + process (CLI::Options const& ops, + SemanticGraph::Schema& tu, + SemanticGraph::Path const& file) + { + process_impl (ops, tu, file); + } + } +} |