From b79567fbc72df23f870049652d5f254aba948bea Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 16 Sep 2011 16:03:25 +0200 Subject: Support for views; integrated part --- odb/relational/source.cxx | 544 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 544 insertions(+) (limited to 'odb/relational/source.cxx') diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 8384d96..4f8ecfc 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -3,6 +3,11 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + +#include +#include + #include #include @@ -12,6 +17,545 @@ namespace relational { namespace source { + static inline void + add_space (string& s) + { + string::size_type n (s.size ()); + if (n != 0 && s[n - 1] != ' ') + s += ' '; + } + + static string + translate_name_trailer (string& t, + cpp_ttype& tt, + cpp_ttype& ptt, + cxx_tokens_lexer& lex) + { + string r; + + for (; tt != CPP_EOF; ptt = tt, tt = lex.next (t)) + { + bool done (false); + + switch (tt) + { + case CPP_SCOPE: + case CPP_DOT: + { + r += cxx_lexer::token_spelling[tt]; + break; + } + default: + { + // Handle CPP_KEYWORD here to avoid a warning (it is not + // part of the cpp_ttype enumeration). + // + if (tt == CPP_NAME || tt == CPP_KEYWORD) + { + // For names like 'foo::template bar'. + // + if (ptt == CPP_NAME || ptt == CPP_KEYWORD) + r += ' '; + + r += t; + } + else + done = true; + + break; + } + } + + if (done) + break; + } + + return r; + } + + static class_::expression + translate_name (string& t, + cpp_ttype& tt, + cpp_ttype& ptt, + cxx_tokens_lexer& lex, + tree scope, + location_t loc, + string const& prag, + bool check_ptr, + view_alias_map const& amap, + view_object_map const& omap) + { + using semantics::data_member; + typedef class_::expression expression; + + bool multi_obj ((amap.size () + omap.size ()) > 1); + + string r ("query_type"); + string name; + + bool fail (false); + context& ctx (context::current ()); + + // This code is quite similar to view_data_members in the type + // processor. + // + try + { + tree decl (0); + view_object* vo (0); + + // Check if this is an alias. + // + if (tt == CPP_NAME) + { + view_alias_map::const_iterator i (amap.find (t)); + + if (i != amap.end ()) + { + if (multi_obj) + { + r += "::"; + r += i->first; + } + + vo = i->second; + fail = true; // This must be a data member. + + // Skip '::'. + // + ptt = tt; + tt = lex.next (t); + + if (tt != CPP_SCOPE) + { + error (loc) + << "member name expected after an alias in db pragma " + << prag << endl; + throw generation_failed (); + } + + ptt = tt; + tt = lex.next (t); + + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex, vo->object->tree_node (), name, false); + } + } + + // If it is not an alias, do the normal lookup. + // + if (vo == 0) + { + // Also get the object type. We need to do it so that + // we can get the correct (derived) object name (the + // member can come from a base class). + // + tree type; + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex, scope, name, false, &type); + + type = TYPE_MAIN_VARIANT (type); + + view_object_map::const_iterator i (omap.find (type)); + + if (i == omap.end ()) + { + // Not an object associated with this view. Assume it + // is some other valid name. + // + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + + vo = i->second; + + if (multi_obj) + { + r += "::"; + r += vo->object->name (); + } + } + + // Check that we have a data member. + // + if (TREE_CODE (decl) != FIELD_DECL) + { + if (fail) + { + error (loc) + << "name '" << name << "' in db pragma " << prag << " " + << "does not refer to a data member" << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + + expression e (vo); + + data_member* m (dynamic_cast (ctx.unit.find (decl))); + + r += "::"; + r += ctx.public_name (*m); + + // Assemble the member path if we may need to return a pointer + // expression. + // + if (check_ptr) + e.member_path.push_back (m); + + fail = true; // Now we definitely fail if anything goes wrong. + + // Finally, resolve nested members if any. + // + for (; tt == CPP_DOT; ptt = tt, tt = lex.next (t)) + { + // Check if this member is actually of a composite value type. + // This is to handle expressions like "object::member.is_null ()" + // correctly. The remaining issue here is that in the future + // is_null()/is_not_null() will be valid for composite values + // as well. + // + if (!context::composite_wrapper (m->type ())) + break; + + ptt = tt; + tt = lex.next (t); + + if (tt != CPP_NAME) + { + error (loc) + << "name expected after '.' in db pragma " << prag << endl; + throw generation_failed (); + } + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + decl = lookup_qualified_name ( + type, get_identifier (t.c_str ()), false, false); + + if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL) + { + error (loc) + << "name '" << t << "' in db pragma " << prag << " does not " + << "refer to a data member" << endl; + throw generation_failed (); + } + + m = dynamic_cast (ctx.unit.find (decl)); + + //@@ Temporarily translate '.' to '::' until the query is changed + // to use '.' for composite member access. + // + r += "::"; + r += ctx.public_name (*m); + + if (check_ptr) + e.member_path.push_back (m); + } + + // If requested, check if this member is a pointer. We only do this + // if there is nothing after this name. + // + if (check_ptr && tt == CPP_EOF) + { + using semantics::type; + + type* t (&m->type ()); + + if (type* c = context::container_wrapper (*t)) + t = &context::container_vt (*c); + + if (context::object_pointer (*t)) + return e; + } + + // Read the remainder of the expression (e.g., '.is_null ()') if + // the member is not composite and we bailed out from the above + // loop. + // + if (tt == CPP_DOT) + r += translate_name_trailer (t, tt, ptt, lex); + + return expression (r); + } + catch (lookup::invalid_name const&) + { + if (fail) + { + error (loc) << "invalid name in db pragma " << prag << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + catch (lookup::unable_to_resolve const& e) + { + if (fail) + { + error (loc) << "unable to resolve name '" << e.name () + << "' in db pragma " << prag << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + } + + class_::expression class_:: + translate_expression (type& c, + cxx_tokens const& ts, + tree scope, + location_t loc, + string const& prag, + bool* placeholder) + { + // The overall idea is as folows: read in tokens and add them + // to the string. If a token starts a name, try to resolve it + // to an object member (taking into account aliases). If this + // was successful, translate it to the query column reference. + // Otherwise, output it as is. + // + // If the placeholder argument is not NULL, then we need to + // detect the special '(?)' token sequence and replace it + // with the query variable ('q'). + // + expression e (""); + string& r (e.value); + + view_alias_map const& amap (c.get ("alias-map")); + view_object_map const& omap (c.get ("object-map")); + + cxx_tokens_lexer lex; + lex.start (ts); + + string t; + for (cpp_ttype tt (lex.next (t)), ptt (CPP_EOF); tt != CPP_EOF;) + { + // Try to format the expression to resemble the style of the + // generated code. + // + switch (tt) + { + case CPP_NOT: + { + add_space (r); + r += '!'; + break; + } + case CPP_COMMA: + { + r += ", "; + break; + } + case CPP_OPEN_PAREN: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD) + add_space (r); + + r += '('; + break; + } + case CPP_CLOSE_PAREN: + { + r += ')'; + break; + } + case CPP_OPEN_SQUARE: + { + r += '['; + break; + } + case CPP_CLOSE_SQUARE: + { + r += ']'; + break; + } + case CPP_OPEN_BRACE: + { + add_space (r); + r += "{ "; + break; + } + case CPP_CLOSE_BRACE: + { + add_space (r); + r += '}'; + break; + } + case CPP_SEMICOLON: + { + r += ';'; + break; + } + case CPP_ELLIPSIS: + { + add_space (r); + r += "..."; + break; + } + case CPP_PLUS: + case CPP_MINUS: + { + bool unary (ptt != CPP_NAME && + ptt != CPP_SCOPE && + ptt != CPP_NUMBER && + ptt != CPP_STRING && + ptt != CPP_CLOSE_PAREN && + ptt != CPP_PLUS_PLUS && + ptt != CPP_MINUS_MINUS); + + if (!unary) + add_space (r); + + r += cxx_lexer::token_spelling[tt]; + + if (!unary) + r += ' '; + break; + } + case CPP_PLUS_PLUS: + case CPP_MINUS_MINUS: + { + if (ptt != CPP_NAME && + ptt != CPP_CLOSE_PAREN && + ptt != CPP_CLOSE_SQUARE) + add_space (r); + + r += cxx_lexer::token_spelling[tt]; + break; + } + case CPP_DEREF: + case CPP_DEREF_STAR: + case CPP_DOT: + case CPP_DOT_STAR: + { + r += cxx_lexer::token_spelling[tt]; + break; + } + case CPP_STRING: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += strlit (t); + break; + } + case CPP_NUMBER: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += t; + break; + } + case CPP_SCOPE: + case CPP_NAME: + { + // Start of a name. + // + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + // Check if this is a pointer expression. + // + // If r is not empty, then it means this is not just the + // name. If placeholder is not 0, then we are translating + // a query expression, not a join condition. + // + expression e ( + translate_name ( + t, tt, ptt, lex, + scope, loc, prag, + r.empty () && placeholder == 0, amap, omap)); + + if (e.kind == expression::literal) + r += e.value; + else + return e; + + continue; // We have already extracted the next token. + } + case CPP_QUERY: + { + if (placeholder != 0 && !*placeholder) + { + if (ptt == CPP_OPEN_PAREN) + { + // Get the next token and see if it is ')'. + // + ptt = tt; + tt = lex.next (t); + + if (tt == CPP_CLOSE_PAREN) + { + r += 'q'; + *placeholder = true; + } + else + { + // The same as in the default case. + // + add_space (r); + r += "? "; + } + continue; // We have already gotten the next token. + } + } + // Fall through. + } + default: + { + // Handle CPP_KEYWORD here to avoid a warning (it is not + // part of the cpp_ttype enumeration). + // + if (tt == CPP_KEYWORD) + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += t; + } + else + { + // All the other operators. + // + add_space (r); + r += cxx_lexer::token_spelling[tt]; + r += ' '; + } + break; + } + } + + // + // Watch out for the continue statements above if you add any + // logic here. + // + + ptt = tt; + tt = lex.next (t); + } + + return e; + } + void generate () { -- cgit v1.1