From cea6fb57ac8c9a893c0f404fef6c1469f0b6222b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 22 Jul 2010 14:33:21 +0200 Subject: Next chunk of functionality Add SQL language lexer. Implement MySQL type declaration parser. Create sub-directories for databases, currently mysql and tracer. Create MySQL-specific context. --- odb/context.cxx | 77 ++------- odb/context.hxx | 33 +++- odb/generator.cxx | 85 ++++++++-- odb/header.cxx | 158 ------------------- odb/header.hxx | 14 -- odb/inline.cxx | 73 --------- odb/inline.hxx | 14 -- odb/makefile | 23 ++- odb/mysql-schema.cxx | 141 ----------------- odb/mysql-schema.hxx | 14 -- odb/mysql/common.cxx | 12 ++ odb/mysql/common.hxx | 16 ++ odb/mysql/context.cxx | 429 ++++++++++++++++++++++++++++++++++++++++++++++++++ odb/mysql/context.hxx | 47 ++++++ odb/mysql/header.cxx | 155 ++++++++++++++++++ odb/mysql/header.hxx | 17 ++ odb/mysql/inline.cxx | 76 +++++++++ odb/mysql/inline.hxx | 17 ++ odb/mysql/schema.cxx | 144 +++++++++++++++++ odb/mysql/schema.hxx | 17 ++ odb/mysql/source.cxx | 106 +++++++++++++ odb/mysql/source.hxx | 17 ++ odb/source.cxx | 148 ----------------- odb/source.hxx | 14 -- odb/sql-lexer.cxx | 240 ++++++++++++++++++++++++++++ odb/sql-lexer.hxx | 128 +++++++++++++++ odb/sql-lexer.ixx | 86 ++++++++++ odb/sql-token.hxx | 79 ++++++++++ odb/sql-token.ixx | 46 ++++++ odb/tracer/header.cxx | 161 +++++++++++++++++++ odb/tracer/header.hxx | 17 ++ odb/tracer/inline.cxx | 76 +++++++++ odb/tracer/inline.hxx | 17 ++ odb/tracer/source.cxx | 147 +++++++++++++++++ odb/tracer/source.hxx | 17 ++ 35 files changed, 2195 insertions(+), 666 deletions(-) delete mode 100644 odb/header.cxx delete mode 100644 odb/header.hxx delete mode 100644 odb/inline.cxx delete mode 100644 odb/inline.hxx delete mode 100644 odb/mysql-schema.cxx delete mode 100644 odb/mysql-schema.hxx create mode 100644 odb/mysql/common.cxx create mode 100644 odb/mysql/common.hxx create mode 100644 odb/mysql/context.cxx create mode 100644 odb/mysql/context.hxx create mode 100644 odb/mysql/header.cxx create mode 100644 odb/mysql/header.hxx create mode 100644 odb/mysql/inline.cxx create mode 100644 odb/mysql/inline.hxx create mode 100644 odb/mysql/schema.cxx create mode 100644 odb/mysql/schema.hxx create mode 100644 odb/mysql/source.cxx create mode 100644 odb/mysql/source.hxx delete mode 100644 odb/source.cxx delete mode 100644 odb/source.hxx create mode 100644 odb/sql-lexer.cxx create mode 100644 odb/sql-lexer.hxx create mode 100644 odb/sql-lexer.ixx create mode 100644 odb/sql-token.hxx create mode 100644 odb/sql-token.ixx create mode 100644 odb/tracer/header.cxx create mode 100644 odb/tracer/header.hxx create mode 100644 odb/tracer/inline.cxx create mode 100644 odb/tracer/inline.hxx create mode 100644 odb/tracer/source.cxx create mode 100644 odb/tracer/source.hxx diff --git a/odb/context.cxx b/odb/context.cxx index eb0aa5d..15d1adb 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -87,79 +87,21 @@ namespace "xor", "xor_eq" }; - - struct type_map_entry - { - const char* const cxx_type; - const char* const db_type; - }; - - type_map_entry mysql_type_map[] = - { - {"bool", "TINYINT(1)"}, - - {"char", "TINYINT"}, - {"signed char", "TINYINT"}, - {"unsigned char", "TINYINT UNSIGNED"}, - - {"short int", "SMALLINT"}, - {"short unsigned int", "SMALLINT UNSIGNED"}, - - {"int", "INT"}, - {"unsigned int", "INT UNSIGNED"}, - - {"long int", "BIGINT"}, - {"long unsigned int", "BIGINT UNSIGNED"}, - - {"long long int", "BIGINT"}, - {"long long unsigned int", "BIGINT UNSIGNED"}, - - {"float", "FLOAT"}, - {"double", "DOUBLE"}, - - {"::std::string", "TEXT"} - }; } context:: context (ostream& os_, - semantics::unit& unit_, - options_type const& ops) - : data_ (new (shared) data), + semantics::unit& u, + options_type const& ops, + data_ptr d) + : data_ (d ? d : data_ptr (new (shared) data)), os (os_), - unit (unit_), + unit (u), options (ops), keyword_set (data_->keyword_set_) { for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i) data_->keyword_set_.insert (keywords[i]); - - // Populate the C++ type to DB type map. - // - { - size_t n; - type_map_entry* p; - - switch (options.database ()) - { - case database::mysql: - { - p = mysql_type_map; - n = sizeof (mysql_type_map) / sizeof (type_map_entry); - break; - } - default: - { - p = 0; - n = 0; - break; - } - } - - for (size_t i (0); i < n; ++i) - data_->type_map_.insert ( - type_map_type::value_type (p[i].cxx_type, p[i].db_type)); - } } context:: @@ -172,6 +114,11 @@ context (context& c) { } +context:: +~context () +{ +} + string context:: table_name (semantics::type& t) const { @@ -209,7 +156,7 @@ column_name (semantics::data_member& m) const } string context:: -db_type (semantics::data_member& m) const +column_type (semantics::data_member& m) const { if (m.count ("type")) return m.get ("type"); @@ -218,7 +165,7 @@ db_type (semantics::data_member& m) const type_map_type::const_iterator i (data_->type_map_.find (name)); if (i != data_->type_map_.end ()) - return i->second; + return m.count ("id") ? i->second.id_type : i->second.type; cerr << m.file () << ":" << m.line () << ":" << m.column () << " error: unable to map C++ type '" << name << "' used in " diff --git a/odb/context.hxx b/odb/context.hxx index 700e604..9f5723a 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -42,7 +42,7 @@ public: column_name (semantics::data_member&) const; string - db_type (semantics::data_member&) const; + column_type (semantics::data_member&) const; public: // Escape C++ keywords, reserved names, and illegal characters. @@ -50,9 +50,10 @@ public: string escape (string const&) const; -private: +protected: struct data; - cutl::shared_ptr data_; + typedef cutl::shared_ptr data_ptr; + data_ptr data_; public: std::ostream& os; @@ -62,19 +63,39 @@ public: typedef std::set keyword_set_type; keyword_set_type const& keyword_set; - typedef std::map type_map_type; + struct db_type_type + { + db_type_type () {} + db_type_type (string const& t, string const& it) + : type (t), id_type (it) + { + } + + string type; + string id_type; + }; -private: + typedef std::map type_map_type; + +protected: struct data { + virtual ~data () {} + keyword_set_type keyword_set_; type_map_type type_map_; }; public: - context (std::ostream&, semantics::unit&, options_type const&); + context (std::ostream&, + semantics::unit&, + options_type const&, + data_ptr = data_ptr ()); context (context&); + virtual + ~context (); + private: context& operator= (context const&); diff --git a/odb/generator.cxx b/odb/generator.cxx index 0d4e0c9..ce5b1cb 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -5,6 +5,7 @@ #include // std::toupper, std::is{alpha,upper,lower} #include +#include // std::auto_ptr #include #include @@ -16,11 +17,15 @@ #include #include -#include -#include -#include +#include +#include +#include -#include +#include +#include +#include +#include +#include using namespace std; using namespace cutl; @@ -188,9 +193,23 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (hxx); - context ctx (hxx, unit, ops); + auto_ptr ctx; - string guard (make_guard (gp + hxx_name, ctx)); + switch (ops.database ()) + { + case database::mysql: + { + ctx.reset (new mysql::context (hxx, unit, ops)); + break; + } + case database::tracer: + { + ctx.reset (new context (hxx, unit, ops)); + break; + } + } + + string guard (make_guard (gp + hxx_name, *ctx)); hxx << "#ifndef " << guard << endl << "#define " << guard << endl @@ -200,7 +219,19 @@ generate (options const& ops, semantics::unit& unit, path const& p) (br ? '>' : '"') << endl << endl; - generate_header (ctx); + switch (ops.database ()) + { + case database::mysql: + { + mysql::generate_header (static_cast (*ctx.get ())); + break; + } + case database::tracer: + { + tracer::generate_header (*ctx); + break; + } + } hxx << "#include " << (br ? '<' : '"') << ip << ixx_name << (br ? '>' : '"') << endl @@ -213,34 +244,60 @@ generate (options const& ops, semantics::unit& unit, path const& p) // { cxx_filter filt (ixx); - context ctx (ixx, unit, ops); - generate_inline (ctx); + + switch (ops.database ()) + { + case database::mysql: + { + mysql::context ctx (ixx, unit, ops); + mysql::generate_inline (ctx); + break; + } + case database::tracer: + { + context ctx (ixx, unit, ops); + tracer::generate_inline (ctx); + break; + } + } } // CXX // { cxx_filter filt (cxx); - context ctx (cxx, unit, ops); cxx << "#include " << (br ? '<' : '"') << ip << hxx_name << (br ? '>' : '"') << endl << endl; - generate_source (ctx); + switch (ops.database ()) + { + case database::mysql: + { + mysql::context ctx (cxx, unit, ops); + mysql::generate_source (ctx); + break; + } + case database::tracer: + { + context ctx (cxx, unit, ops); + tracer::generate_source (ctx); + break; + } + } } // SQL // if (ops.generate_schema ()) { - context ctx (sql, unit, ops); - switch (ops.database ()) { case database::mysql: { - generate_mysql_schema (ctx); + mysql::context ctx (sql, unit, ops); + mysql::generate_schema (ctx); break; } case database::tracer: diff --git a/odb/header.cxx b/odb/header.cxx deleted file mode 100644 index 5b302af..0000000 --- a/odb/header.cxx +++ /dev/null @@ -1,158 +0,0 @@ -// file : odb/header.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#include -#include - -namespace -{ - struct class_: traversal::class_, context - { - class_ (context& c) - : context (c) - { - } - - virtual void - traverse (type& c) - { - if (c.file () != unit.file ()) - return; - - if (!c.count ("object")) - return; - - string const& type (c.fq_name ()); - - // Find the id member and type. - // - id_member t (*this); - t.traverse (c); - - if (t.member () == 0) - { - cerr << c.file () << ":" << c.line () << ":" << c.column () - << " error: no data member designated as object id" << endl; - - cerr << c.file () << ":" << c.line () << ":" << c.column () - << " info: use '#pragma odb id' to specify object id member" - << endl; - - throw generation_failed (); - } - - semantics::data_member& id (*t.member ()); - semantics::type& id_type (id.type ()); - - if (id_type.anonymous ()) - { - // Can be a template-id (which we should handle eventually) or an - // anonymous type in member declaration (e.g., struct {...} m_;). - // - cerr << id.file () << ":" << id.line () << ":" << id.column () - << " error: unnamed type in data member declaration" << endl; - - cerr << id.file () << ":" << id.line () << ":" << id.column () - << " info: use 'typedef' to name this type" - << endl; - - throw generation_failed (); - } - - os << "// " << c.name () << endl - << "//" << endl; - - os << "template <>" << endl - << "class access::object_traits< " << type << " >: " << endl - << " public access::object_memory< " << type << " >," << endl - << " public access::object_factory< " << type << " >" - << "{" - << "public:" << endl; - - // object_type & shared_ptr - // - os << "typedef " << type << " object_type;"; - - // id_type - // - os << "typedef " << id_type.fq_name () << " id_type;" - << endl; - - // id_source - // - os << "static const odb::id_source id_source = odb::ids_assigned;" - << endl; - - // type_name () - // - os << "static const char*" << endl - << "type_name ();" - << endl; - - // id () - // - os << "static id_type" << endl - << "id (const object_type&);" - << endl; - - // insert () - // - os << "static void" << endl - << "insert (database&, object_type&);" - << endl; - - // update () - // - os << "static void" << endl - << "update (database&, object_type&);" - << endl; - - // erase () - // - os << "static void" << endl - << "erase (database&, const id_type&);" - << endl; - - // find () - // - os << "static pointer_type" << endl - << "find (database&, const id_type&);" - << endl; - - os << "static bool" << endl - << "find (database&, const id_type&, object_type&);"; - - os << "};"; - } - }; -} - -void -generate_header (context& ctx) -{ - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - ctx.os << "#include " << endl - << "#include " << endl - << endl; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; -} diff --git a/odb/header.hxx b/odb/header.hxx deleted file mode 100644 index f7f3a34..0000000 --- a/odb/header.hxx +++ /dev/null @@ -1,14 +0,0 @@ -// file : odb/header.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#ifndef ODB_HEADER_HXX -#define ODB_HEADER_HXX - -#include - -void -generate_header (context&); - -#endif // ODB_HEADER_HXX diff --git a/odb/inline.cxx b/odb/inline.cxx deleted file mode 100644 index bf0aec9..0000000 --- a/odb/inline.cxx +++ /dev/null @@ -1,73 +0,0 @@ -// file : odb/inline.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#include -#include - -namespace -{ - struct class_: traversal::class_, context - { - class_ (context& c) - : context (c) - { - } - - virtual void - traverse (type& c) - { - if (c.file () != unit.file ()) - return; - - if (!c.count ("object")) - return; - - string const& type (c.fq_name ()); - string traits ("access::object_traits< " + type + " >"); - - id_member t (*this); - t.traverse (c); - semantics::data_member& id (*t.member ()); - - os << "// " << c.name () << endl - << "//" << endl - << endl; - - // id () - // - os << "inline" << endl - << traits << "::id_type" << endl - << traits << "::" << endl - << "id (const object_type& obj)" - << "{" - << "return obj." << id.name () << ";" << endl - << "}"; - } - }; -} - -void -generate_inline (context& ctx) -{ - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; -} diff --git a/odb/inline.hxx b/odb/inline.hxx deleted file mode 100644 index 77a2d56..0000000 --- a/odb/inline.hxx +++ /dev/null @@ -1,14 +0,0 @@ -// file : odb/inline.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#ifndef ODB_INLINE_HXX -#define ODB_INLINE_HXX - -#include - -void -generate_inline (context&); - -#endif // ODB_INLINE_HXX diff --git a/odb/makefile b/odb/makefile index f6da371..1fb240d 100644 --- a/odb/makefile +++ b/odb/makefile @@ -8,15 +8,28 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../build/bootstrap.make # Plugin units. # cxx_ptun := \ +sql-lexer.cxx \ context.cxx \ generator.cxx \ parser.cxx \ plugin.cxx \ -pragma.cxx \ -header.cxx \ -inline.cxx \ -source.cxx \ -mysql-schema.cxx +pragma.cxx + +# Tracer +# +cxx_ptun += \ +tracer/header.cxx \ +tracer/inline.cxx \ +tracer/source.cxx + +# MySQL +# +cxx_ptun += \ +mysql/context.cxx \ +mysql/schema.cxx \ +mysql/header.cxx \ +mysql/inline.cxx \ +mysql/source.cxx cxx_ptun += \ semantics/class.cxx \ diff --git a/odb/mysql-schema.cxx b/odb/mysql-schema.cxx deleted file mode 100644 index ea3fece..0000000 --- a/odb/mysql-schema.cxx +++ /dev/null @@ -1,141 +0,0 @@ -// file : odb/mysql-schema.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#include - -namespace -{ - struct data_member: traversal::data_member, context - { - data_member (context& c) - : context (c), first_ (true) - { - } - - virtual void - traverse (type& m) - { - if (first_) - first_ = false; - else - os << "," << endl; - - os << " `" << column_name (m) << "` " << db_type (m) << - " NOT NULL"; - - if (m.count ("id")) - os << " PRIMARY KEY"; - } - - private: - bool first_; - }; - - struct class_create: traversal::class_, context - { - class_create (context& c) - : context (c) - { - } - - virtual void - traverse (type& c) - { - if (c.file () != unit.file ()) - return; - - if (!c.count ("object")) - return; - - os << "CREATE TABLE `" << table_name (c) << "` (" << endl; - - { - data_member m (*this); - traversal::names n (m); - names (c, n); - } - - os << ")"; - - string const& engine (options.mysql_engine ()); - - if (engine != "default") - os << endl - << " ENGINE=" << engine; - - os << ";" << endl - << endl; - } - }; - - struct class_drop: traversal::class_, context - { - class_drop (context& c) - : context (c) - { - } - - virtual void - traverse (type& c) - { - if (c.file () != unit.file ()) - return; - - if (!c.count ("object")) - return; - - os << "DROP TABLE IF EXISTS `" << table_name (c) << "`;" << endl; - } - }; -} - -static char const file_header[] = - "/* This file was generated by CodeSynthesis ODB object persistence\n" - " * compiler for C++.\n" - " */\n\n"; - -void -generate_mysql_schema (context& ctx) -{ - ctx.os << file_header; - - // Drop. - // - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_drop c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - unit.dispatch (ctx.unit); - } - - ctx.os << endl; - - // Create. - // - { - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_create c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - unit.dispatch (ctx.unit); - } -} diff --git a/odb/mysql-schema.hxx b/odb/mysql-schema.hxx deleted file mode 100644 index f4a7996..0000000 --- a/odb/mysql-schema.hxx +++ /dev/null @@ -1,14 +0,0 @@ -// file : odb/mysql-schema.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#ifndef ODB_MYSQL_SCHEMA_HXX -#define ODB_MYSQL_SCHEMA_HXX - -#include - -void -generate_mysql_schema (context&); - -#endif // ODB_MYSQL_SCHEMA_HXX diff --git a/odb/mysql/common.cxx b/odb/mysql/common.cxx new file mode 100644 index 0000000..cd572d6 --- /dev/null +++ b/odb/mysql/common.cxx @@ -0,0 +1,12 @@ +// file : odb/mysql/common.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +using namespace std; + +namespace mysql +{ +} diff --git a/odb/mysql/common.hxx b/odb/mysql/common.hxx new file mode 100644 index 0000000..cd4d27f --- /dev/null +++ b/odb/mysql/common.hxx @@ -0,0 +1,16 @@ +// file : odb/mysql/common.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_COMMON_HXX +#define ODB_MYSQL_COMMON_HXX + +#include +#include + +namespace mysql +{ +} + +#endif // ODB_MYSQL_COMMON_HXX diff --git a/odb/mysql/context.cxx b/odb/mysql/context.cxx new file mode 100644 index 0000000..668ecf4 --- /dev/null +++ b/odb/mysql/context.cxx @@ -0,0 +1,429 @@ +// file : odb/mysql/context.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +#include +#include + +#include + +using namespace std; + +namespace mysql +{ + namespace + { + struct type_map_entry + { + const char* const cxx_type; + const char* const db_type; + const char* const db_id_type; + }; + + type_map_entry type_map[] = + { + {"bool", "TINYINT(1)", 0}, + + {"char", "TINYINT", 0}, + {"signed char", "TINYINT", 0}, + {"unsigned char", "TINYINT UNSIGNED", 0}, + + {"short int", "SMALLINT", 0}, + {"short unsigned int", "SMALLINT UNSIGNED", 0}, + + {"int", "INT", 0}, + {"unsigned int", "INT UNSIGNED", 0}, + + {"long int", "BIGINT", 0}, + {"long unsigned int", "BIGINT UNSIGNED", 0}, + + {"long long int", "BIGINT", 0}, + {"long long unsigned int", "BIGINT UNSIGNED", 0}, + + {"float", "FLOAT", 0}, + {"double", "DOUBLE", 0}, + + {"::std::string", "TEXT", "VARCHAR(255)"} + }; + } + + context:: + context (ostream& os, semantics::unit& u, options_type const& ops) + : base_context (os, u, ops, data_ptr (new (shared) data)), + data_ (static_cast (base_context::data_.get ())) + { + // Populate the C++ type to DB type map. + // + for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i) + { + type_map_entry const& e (type_map[i]); + + type_map_type::value_type v ( + e.cxx_type, + db_type_type (e.db_type, e.db_id_type ? e.db_id_type : e.db_type)); + + data_->type_map_.insert (v); + } + } + + context:: + context (context& c) + : base_context (c), + data_ (c.data_) + { + } + + static sql_type + parse_sql_type (semantics::data_member& m, std::string const& sql); + + sql_type const& context:: + db_type (semantics::data_member& m) + { + if (!m.count ("db-type")) + m.set ("db-type", parse_sql_type (m, column_type (m))); + + return m.get ("db-type"); + } + + static sql_type + parse_sql_type (semantics::data_member& m, string const& sql) + { + try + { + sql_type r; + sql_lexer l (sql); + + // While most type names use single identifier, there are + // a couple of exceptions to this rule: + // + // NATIONAL CHAR|VARCHAR + // CHAR BYTE (BINARY) + // CHARACTER VARYING (VARCHAR) + // LONG VARBINARY (MEDIUMBLOB) + // LONG VARCHAR (MEDIUMTEXT) + // + // + enum state + { + parse_prefix, + parse_name, + parse_bounds, + parse_sign + }; + + state s (parse_prefix); + + for (sql_token t (l.next ()); + t.type () != sql_token::t_eos; t = l.next ()) + { + sql_token::token_type tt (t.type ()); + + switch (s) + { + case parse_prefix: + { + if (tt == sql_token::t_identifier) + { + string const& id (t.identifier ()); + + if (id == "NATIONAL" || + id == "CHAR" || + id == "CHARACTER" || + id == "LONG") + { + r.type = id; + continue; + } + } + + // Fall through. + // + s = parse_prefix; + } + case parse_name: + { + if (tt == sql_token::t_identifier) + { + bool match (true); + string const& id (t.identifier ()); + + // Numeric types. + // + if (id == "BIT") + { + r.type = "BIT"; + } + else if (id == "TINYINT" || id == "INT1") + { + r.type = "TINYINT"; + } + else if (id == "BOOL" || id == "BOOLEAN") + { + r.type = "TINYINT"; + r.bounds = true; + r.bounds_value = 1; + } + else if (id == "SMALLINT" || id == "INT2") + { + r.type = "SMALLINT"; + } + else if (id == "MEDIUMINT" || id == "INT3" || id == "MIDDLEINT") + { + r.type = "MEDIUMINT"; + } + else if (id == "INT" || id == "INTEGER" || id == "INT4") + { + r.type = "INT"; + } + else if (id == "BIGINT" || id == "INT8") + { + r.type = "BIGINT"; + } + else if (id == "SERIAL") + { + r.type = "BIGINT"; + r.unsign = true; + } + else if (id == "FLOAT" || id == "FLOAT4") + { + r.type = "FLOAT"; + } + else if (id == "DOUBLE" || id == "FLOAT8") + { + r.type = "DOUBLE"; + } + else if (id == "DECIMAL" || + id == "DEC" || + id == "NUMERIC" || + id == "FIXED") + { + r.type = "DECIMAL"; + } + // + // Date-time types. + // + else if (id == "DATE") + { + r.type = "DATE"; + } + else if (id == "TIME") + { + r.type = "TIME"; + } + else if (id == "DATETIME") + { + r.type = "DATETIME"; + } + else if (id == "TIMESTAMP") + { + r.type = "TIMESTAMP"; + } + else if (id == "YEAR") + { + r.type = "YEAR"; + } + // + // String and binary types. + // + else if (id == "NCHAR") + { + r.type = "CHAR"; + } + else if (id == "VARCHAR") + { + r.type = r.type == "LONG" ? "MEDIUMTEXT" : "VARCHAR"; + } + else if (id == "NVARCHAR") + { + r.type = "VARCHAR"; + } + else if (id == "VARYING" && r.type == "CHARACTER") + { + r.type = "VARCHAR"; + } + else if (id == "BINARY") + { + r.type = "BINARY"; + } + else if (id == "BYTE" && r.type == "CHAR") + { + r.type = "BINARY"; + } + else if (id == "VARBINARY") + { + r.type = r.type == "LONG" ? "MEDIUMBLOB" : "VARBINARY"; + } + else if (id == "TINYBLOB") + { + r.type = "TINYBLOB"; + } + else if (id == "TINYTEXT") + { + r.type = "TINYTEXT"; + } + else if (id == "BLOB") + { + r.type = "BLOB"; + } + else if (id == "TEXT") + { + r.type = "TEXT"; + } + else if (id == "MEDIUMBLOB") + { + r.type = "MEDIUMBLOB"; + } + else if (id == "MEDIUMTEXT") + { + r.type = "MEDIUMTEXT"; + } + else if (id == "LONGBLOB") + { + r.type = "LONGBLOB"; + } + else if (id == "LONGTEXT") + { + r.type = "LONGTEXT"; + } + else if (id == "ENUM") + { + r.type = "ENUM"; + } + else if (id == "SET") + { + r.type = "SET"; + } + else + match = false; + + if (match) + { + s = parse_bounds; + continue; + } + } + + bool match (false); + + // Some prefixes can also be type names if not followed + // by the actual type name. + // + if (!r.type.empty ()) + { + if (r.type == "CHAR") + match = true; + else if (r.type == "CHARACTER") + { + r.type = "CHAR"; + match = true; + } + else if (r.type == "LONG") + { + r.type = "MEDIUMTEXT"; + match = true; + } + } + + if (!match) + { + cerr << m.file () << ":" << m.line () << ":" << m.column (); + + if (tt == sql_token::t_identifier) + cerr << " error: unknown MySQL type '" << + t.identifier () << "'" << endl; + else + cerr << " error: expected MySQL type name" << endl; + + throw generation_failed (); + } + + // Fall through. + // + s = parse_bounds; + } + case parse_bounds: + { + if (t.punctuation () == sql_token::p_lparen) + { + t = l.next (); + + if (t.type () != sql_token::t_int_lit) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << " error: integer bounds expected in MySQL type " + << "declaration" << endl; + + throw generation_failed (); + } + + unsigned int v; + istringstream is (t.literal ()); + + if (!(is >> v && is.eof ())) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << " error: invalid bounds value '" << t.literal () + << "'in MySQL type declaration" << endl; + + throw generation_failed (); + } + + r.bounds = true; + r.bounds_value = v; + + t = l.next (); + + if (t.punctuation () == sql_token::p_comma) + { + // We have the second bounds value. Skip it. + // + l.next (); + t = l.next (); + } + + if (t.punctuation () != sql_token::p_rparen) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << " error: expected ')' in MySQL type declaration" + << endl; + + throw generation_failed (); + } + + s = parse_sign; + continue; + } + + // Fall through. + // + s = parse_sign; + } + case parse_sign: + { + if (tt == sql_token::t_identifier && t.identifier () == "UNSIGNED") + { + r.unsign = true; + } + + return r; + } + } + } + + cerr << m.file () << ":" << m.line () << ":" << m.column () + << " error: empty MySQL type declaration" << endl; + + throw generation_failed (); + } + catch (sql_lexer::invalid_input const& e) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << " error: invalid MySQL type declaration: " << e.message << endl; + + throw generation_failed (); + } + } +} diff --git a/odb/mysql/context.hxx b/odb/mysql/context.hxx new file mode 100644 index 0000000..7c075b2 --- /dev/null +++ b/odb/mysql/context.hxx @@ -0,0 +1,47 @@ +// file : odb/mysql/context.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_CONTEXT_HXX +#define ODB_MYSQL_CONTEXT_HXX + +#include + +#include + +namespace mysql +{ + struct sql_type + { + sql_type () : unsign (false), bounds (false) {} + + std::string type; + bool unsign; + bool bounds; + unsigned int bounds_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT). + }; + + class context: public ::context + { + private: + typedef ::context base_context; + + struct data: base_context::data + { + }; + + private: + data* data_; + + public: + sql_type const& + db_type (semantics::data_member&); + + public: + context (std::ostream&, semantics::unit&, options_type const&); + context (context&); + }; +} + +#endif // ODB_MYSQL_CONTEXT_HXX diff --git a/odb/mysql/header.cxx b/odb/mysql/header.cxx new file mode 100644 index 0000000..a4111ec --- /dev/null +++ b/odb/mysql/header.cxx @@ -0,0 +1,155 @@ +// file : odb/mysql/header.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + + // Find the id member and type. + // + id_member t (*this); + t.traverse (c); + + if (t.member () == 0) + { + cerr << c.file () << ":" << c.line () << ":" << c.column () + << " error: no data member designated as object id" << endl; + + cerr << c.file () << ":" << c.line () << ":" << c.column () + << " info: use '#pragma odb id' to specify object id member" + << endl; + + throw generation_failed (); + } + + semantics::data_member& id (*t.member ()); + semantics::type& id_type (id.type ()); + + if (id_type.anonymous ()) + { + // Can be a template-id (which we should handle eventually) or an + // anonymous type in member declaration (e.g., struct {...} m_;). + // + cerr << id.file () << ":" << id.line () << ":" << id.column () + << " error: unnamed type in data member declaration" << endl; + + cerr << id.file () << ":" << id.line () << ":" << id.column () + << " info: use 'typedef' to name this type" + << endl; + + throw generation_failed (); + } + + os << "// " << c.name () << endl + << "//" << endl; + + os << "template <>" << endl + << "class access::object_traits< " << type << " >: " << endl + << " public access::object_memory< " << type << " >," << endl + << " public access::object_factory< " << type << " >" + << "{" + << "public:" << endl; + + // object_type & shared_ptr + // + os << "typedef " << type << " object_type;"; + + // id_type + // + os << "typedef " << id_type.fq_name () << " id_type;" + << endl; + + // id_source + // + os << "static const odb::id_source id_source = odb::ids_assigned;" + << endl; + + // id () + // + os << "static id_type" << endl + << "id (const object_type&);" + << endl; + + // persist () + // + os << "static void" << endl + << "persist (database&, object_type&);" + << endl; + + // store () + // + os << "static void" << endl + << "store (database&, object_type&);" + << endl; + + // erase () + // + os << "static void" << endl + << "erase (database&, const id_type&);" + << endl; + + // find () + // + os << "static pointer_type" << endl + << "find (database&, const id_type&);" + << endl; + + os << "static bool" << endl + << "find (database&, const id_type&, object_type&);"; + + os << "};"; + } + }; +} + +namespace mysql +{ + void + generate_header (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "#include " << endl + << "#include " << endl + << endl; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/mysql/header.hxx b/odb/mysql/header.hxx new file mode 100644 index 0000000..a31281e --- /dev/null +++ b/odb/mysql/header.hxx @@ -0,0 +1,17 @@ +// file : odb/mysql/header.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_HEADER_HXX +#define ODB_MYSQL_HEADER_HXX + +#include + +namespace mysql +{ + void + generate_header (context&); +} + +#endif // ODB_MYSQL_HEADER_HXX diff --git a/odb/mysql/inline.cxx b/odb/mysql/inline.cxx new file mode 100644 index 0000000..44cf9d0 --- /dev/null +++ b/odb/mysql/inline.cxx @@ -0,0 +1,76 @@ +// file : odb/mysql/inline.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + string traits ("access::object_traits< " + type + " >"); + + id_member t (*this); + t.traverse (c); + semantics::data_member& id (*t.member ()); + + os << "// " << c.name () << endl + << "//" << endl + << endl; + + // id () + // + os << "inline" << endl + << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const object_type& obj)" + << "{" + << "return obj." << id.name () << ";" << endl + << "}"; + } + }; +} + +namespace mysql +{ + void + generate_inline (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/mysql/inline.hxx b/odb/mysql/inline.hxx new file mode 100644 index 0000000..2d76274 --- /dev/null +++ b/odb/mysql/inline.hxx @@ -0,0 +1,17 @@ +// file : odb/mysql/inline.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_INLINE_HXX +#define ODB_MYSQL_INLINE_HXX + +#include + +namespace mysql +{ + void + generate_inline (context&); +} + +#endif // ODB_MYSQL_INLINE_HXX diff --git a/odb/mysql/schema.cxx b/odb/mysql/schema.cxx new file mode 100644 index 0000000..a38729e --- /dev/null +++ b/odb/mysql/schema.cxx @@ -0,0 +1,144 @@ +// file : odb/mysql/schema.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +namespace +{ + struct data_member: traversal::data_member, context + { + data_member (context& c) + : context (c), first_ (true) + { + } + + virtual void + traverse (type& m) + { + if (first_) + first_ = false; + else + os << "," << endl; + + os << " `" << column_name (m) << "` " << column_type (m) << + " NOT NULL"; + + if (m.count ("id")) + os << " PRIMARY KEY"; + } + + private: + bool first_; + }; + + struct class_create: traversal::class_, context + { + class_create (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + os << "CREATE TABLE `" << table_name (c) << "` (" << endl; + + { + data_member m (*this); + traversal::names n (m); + names (c, n); + } + + os << ")"; + + string const& engine (options.mysql_engine ()); + + if (engine != "default") + os << endl + << " ENGINE=" << engine; + + os << ";" << endl + << endl; + } + }; + + struct class_drop: traversal::class_, context + { + class_drop (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + os << "DROP TABLE IF EXISTS `" << table_name (c) << "`;" << endl; + } + }; +} + +static char const file_header[] = + "/* This file was generated by CodeSynthesis ODB object persistence\n" + " * compiler for C++.\n" + " */\n\n"; + +namespace mysql +{ + void + generate_schema (context& ctx) + { + ctx.os << file_header; + + // Drop. + // + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_drop c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + unit.dispatch (ctx.unit); + } + + ctx.os << endl; + + // Create. + // + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_create c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + unit.dispatch (ctx.unit); + } + } +} diff --git a/odb/mysql/schema.hxx b/odb/mysql/schema.hxx new file mode 100644 index 0000000..99699d3 --- /dev/null +++ b/odb/mysql/schema.hxx @@ -0,0 +1,17 @@ +// file : odb/mysql/schema.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_SCHEMA_HXX +#define ODB_MYSQL_SCHEMA_HXX + +#include + +namespace mysql +{ + void + generate_schema (context&); +} + +#endif // ODB_MYSQL_SCHEMA_HXX diff --git a/odb/mysql/source.cxx b/odb/mysql/source.cxx new file mode 100644 index 0000000..0f117ec --- /dev/null +++ b/odb/mysql/source.cxx @@ -0,0 +1,106 @@ +// file : odb/mysql/source.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + string traits ("access::object_traits< " + type + " >"); + + id_member t (*this); + t.traverse (c); + semantics::data_member& id (*t.member ()); + + os << "// " << c.name () << endl + << "//" << endl + << endl; + + // persist () + // + os << "void " << traits << "::" << endl + << "persist (database&, object_type& obj)" + << "{" + << "}"; + + // store () + // + os << "void " << traits << "::" << endl + << "store (database&, object_type& obj)" + << "{" + << "}"; + + // erase () + // + os << "void " << traits << "::" << endl + << "erase (database&, const id_type& id)" + << "{" + << "}"; + + // find () + // + os << traits << "::pointer_type" << endl + << traits << "::" << endl + << "find (database&, const id_type& id)" + << "{" + << "return 0;" + << "}"; + + os << "bool " << traits << "::" << endl + << "find (database&, const id_type& id, object_type& obj)" + << "{" + << "return false;" + << "}"; + } + }; +} + +namespace mysql +{ + void + generate_source (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "#include " << endl + << "#include " << endl + << endl; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/mysql/source.hxx b/odb/mysql/source.hxx new file mode 100644 index 0000000..6a79479 --- /dev/null +++ b/odb/mysql/source.hxx @@ -0,0 +1,17 @@ +// file : odb/mysql/source.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_MYSQL_SOURCE_HXX +#define ODB_MYSQL_SOURCE_HXX + +#include + +namespace mysql +{ + void + generate_source (context&); +} + +#endif // ODB_MYSQL_SOURCE_HXX diff --git a/odb/source.cxx b/odb/source.cxx deleted file mode 100644 index 168dfae..0000000 --- a/odb/source.cxx +++ /dev/null @@ -1,148 +0,0 @@ -// file : odb/source.cxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#include -#include - -namespace -{ - struct class_: traversal::class_, context - { - class_ (context& c) - : context (c) - { - } - - virtual void - traverse (type& c) - { - if (c.file () != unit.file ()) - return; - - if (!c.count ("object")) - return; - - string const& type (c.fq_name ()); - string traits ("access::object_traits< " + type + " >"); - - id_member t (*this); - t.traverse (c); - semantics::data_member& id (*t.member ()); - - os << "// " << c.name () << endl - << "//" << endl - << endl; - - // type_name () - // - os << "const char* " << traits << "::" << endl - << "type_name ()" - << "{" - << "return \"" << type << "\";" - << "}"; - - // insert () - // - os << "void " << traits << "::" << endl - << "insert (database&, object_type& obj)" - << "{" - << "std::cout << \"insert \" << type_name () << \" id \" << " << - "id (obj) << std::endl;" - << endl - << "if (id (obj) == id_type ())" << endl - << "throw object_already_persistent ();" - << "}"; - - // update () - // - os << "void " << traits << "::" << endl - << "update (database&, object_type& obj)" - << "{" - << "std::cout << \"update \" << type_name () << \" id \" << " << - "id (obj) << std::endl;" - << endl - << "if (id (obj) == id_type ())" << endl - << "throw object_not_persistent ();" - << "}"; - - // erase () - // - os << "void " << traits << "::" << endl - << "erase (database&, const id_type& id)" - << "{" - << "std::cout << \"delete \" << type_name () << \" id \" << " << - "id << std::endl;" - << endl - << "if (id == id_type ())" << endl - << "throw object_not_persistent ();" - << "}"; - - // find () - // - os << traits << "::pointer_type" << endl - << traits << "::" << endl - << "find (database&, const id_type& id)" - << "{" - << "std::cout << \"select \" << type_name () << \" id \" << " << - "id << std::endl;" - << endl - << "if (id == id_type ())" << endl - << "return pointer_type (0);" - << endl - << "pointer_type r (access::object_factory< " << type << - " >::create ());" - << "r->" << id.name () << " = id;" - << "return r;" - << "}"; - - os << "bool " << traits << "::" << endl - << "find (database&, const id_type& id, object_type& obj)" - << "{" - << "std::cout << \"select \" << type_name () << \" id \" << " << - "id << std::endl;" - << endl - << "if (id == id_type ())" << endl - << "return false;" - << endl - << "obj." << id.name () << " = id;" - << "return true;" - << "}"; - } - }; -} - -void -generate_source (context& ctx) -{ - traversal::unit unit; - traversal::defines unit_defines; - traversal::namespace_ ns; - class_ c (ctx); - - unit >> unit_defines >> ns; - unit_defines >> c; - - traversal::defines ns_defines; - - ns >> ns_defines >> ns; - ns_defines >> c; - - ctx.os << "#include " << endl - << endl; - - ctx.os << "#include " << endl - << endl; - - //ctx.os << "#include " << endl - // << "#include " << endl - // << endl; - - ctx.os << "namespace odb" - << "{"; - - unit.dispatch (ctx.unit); - - ctx.os << "}"; -} diff --git a/odb/source.hxx b/odb/source.hxx deleted file mode 100644 index 2db3fe2..0000000 --- a/odb/source.hxx +++ /dev/null @@ -1,14 +0,0 @@ -// file : odb/source.hxx -// author : Boris Kolpackov -// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC -// license : GNU GPL v2; see accompanying LICENSE file - -#ifndef ODB_SOURCE_HXX -#define ODB_SOURCE_HXX - -#include - -void -generate_source (context&); - -#endif // ODB_SOURCE_HXX diff --git a/odb/sql-lexer.cxx b/odb/sql-lexer.cxx new file mode 100644 index 0000000..e5b1693 --- /dev/null +++ b/odb/sql-lexer.cxx @@ -0,0 +1,240 @@ +// file : odb/sql-lexer.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +#include + +using namespace std; + +sql_lexer:: +sql_lexer (std::string const& sql) + : loc_ ("C"), + is_ (sql), + l_ (1), + c_(1), + eos_ (false), + buf_ (0, 0, 0), + unget_ (false) +{ +} + +sql_lexer::xchar sql_lexer:: +peek () +{ + if (unget_) + return buf_; + else + { + if (eos_) + return xchar (xchar::traits_type::eof (), l_, c_); + else + { + xchar::int_type i (is_.peek ()); + + if (i == xchar::traits_type::eof ()) + eos_ = true; + + return xchar (i, l_, c_); + } + } +} + +sql_lexer::xchar sql_lexer:: +get () +{ + if (unget_) + { + unget_ = false; + return buf_; + } + else + { + // When is_.get () returns eof, the failbit is also set (stupid, + // isn't?) which may trigger an exception. To work around this + // we will call peek() first and only call get() if it is not + // eof. But we can only call peek() on eof once; any subsequent + // calls will spoil the failbit (even more stupid). + // + xchar c (peek ()); + + if (!is_eos (c)) + { + is_.get (); + + if (c == '\n') + { + l_++; + c_ = 1; + } + else + c_++; + } + + return c; + } +} + +void sql_lexer:: +unget (xchar c) +{ + // Because iostream::unget cannot work once eos is reached, + // we have to provide our own implementation. + // + buf_ = c; + unget_ = true; +} + +sql_token sql_lexer:: +next () +{ + skip_spaces (); + + xchar c (get ()); + + if (is_eos (c)) + return sql_token (); + + switch (c) + { + case '\'': + { + return string_literal (c); + } + case '\"': + { + return string_literal (c); + } + case '`': + { + return string_literal (c); + } + case ';': + { + return sql_token (sql_token::p_semi); + } + case ',': + { + return sql_token (sql_token::p_comma); + } + case '(': + { + return sql_token (sql_token::p_lparen); + } + case ')': + { + return sql_token (sql_token::p_rparen); + } + case '=': + { + return sql_token (sql_token::p_eq); + } + case '-': + { + return int_literal (get (), true); + } + case '+': + { + return int_literal (get (), false); + } + } + + if (is_alpha (c) || c == '_') + { + return identifier (c); + } + + if (is_dec_digit (c)) + { + return int_literal (c); + } + + ostringstream msg; + msg << "unexpected character '" << c << "'"; + throw invalid_input (c.line (), c.column (), msg.str ()); +} + +void sql_lexer:: +skip_spaces () +{ + for (xchar c (peek ());; c = peek ()) + { + if (is_eos (c) || !is_space (c)) + break; + + get (); + } +} + +sql_token sql_lexer:: +identifier (xchar c) +{ + size_t ln (c.line ()), cl (c.column ()); + string lexeme; + lexeme += c; + + for (c = peek (); + !is_eos (c) && (is_alnum (c) || c == '_'); + c = peek ()) + { + get (); + lexeme += c; + } + + return sql_token (sql_token::t_identifier, lexeme); +} + +sql_token sql_lexer:: +int_literal (xchar c, bool neg, size_t ml, size_t mc) +{ + //size_t ln (neg ? ml : c.line ()), cl (neg ? mc : c.column ()); + string lexeme; + + if (neg) + lexeme += '-'; + + lexeme += c; + + for (c = peek (); !is_eos (c) && is_dec_digit (c); c = peek ()) + { + get (); + lexeme += c; + } + + return sql_token (sql_token::t_int_lit, lexeme); +} + +sql_token sql_lexer:: +string_literal (xchar c) +{ + //size_t ln (c.line ()), cl (c.column ()); + char q (c), p ('\0'); + string lexeme; + lexeme += c; + + while (true) + { + xchar c = get (); + + if (is_eos (c)) + throw invalid_input ( + c.line (), c.column (), "unterminated quoted string"); + + lexeme += c; + + if (c == q && p != '\\') + break; + + // We need to keep track of \\ escapings so we don't confuse + // them with \", as in "\\". + // + if (c == '\\' && p == '\\') + p = '\0'; + else + p = c; + } + + return sql_token (sql_token::t_string_lit, lexeme); +} diff --git a/odb/sql-lexer.hxx b/odb/sql-lexer.hxx new file mode 100644 index 0000000..f650dc4 --- /dev/null +++ b/odb/sql-lexer.hxx @@ -0,0 +1,128 @@ +// file : odb/sql-lexer.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_SQL_LEXER_HXX +#define ODB_SQL_LEXER_HXX + +#include +#include +#include // std::size_t +#include + +#include + +class sql_lexer +{ +public: + sql_lexer (std::string const& sql); + + struct invalid_input + { + invalid_input (std::size_t l, std::size_t c, std::string const& m) + : line (l), column (c), message (m) + { + } + + std::size_t line; + std::size_t column; + std::string const message; + }; + + sql_token + next (); + +protected: + class xchar + { + public: + typedef std::char_traits traits_type; + typedef traits_type::int_type int_type; + typedef traits_type::char_type char_type; + + xchar (int_type v, std::size_t l, std::size_t c); + + operator char_type () const; + + int_type + value () const; + + std::size_t + line () const; + + std::size_t + column () const; + + private: + int_type v_; + std::size_t l_; + std::size_t c_; + }; + + xchar + peek (); + + xchar + get (); + + void + unget (xchar); + +protected: + void + skip_spaces (); + + sql_token + identifier (xchar); + + sql_token + int_literal (xchar, + bool neg = false, + std::size_t ml = 0, + std::size_t mc = 0); + + sql_token + string_literal (xchar); + +protected: + bool + is_alpha (char c) const; + + bool + is_oct_digit (char c) const; + + bool + is_dec_digit (char c) const; + + bool + is_hex_digit (char c) const; + + bool + is_alnum (char c) const; + + bool + is_space (char c) const; + + bool + is_eos (xchar const& c) const; + + char + to_upper (char c) const; + +private: + std::locale loc_; + std::istringstream is_; + std::size_t l_; + std::size_t c_; + + bool eos_; + bool include_; + + xchar buf_; + bool unget_; +}; + +#include + +#endif // ODB_SQL_LEXER_HXX diff --git a/odb/sql-lexer.ixx b/odb/sql-lexer.ixx new file mode 100644 index 0000000..96deed1 --- /dev/null +++ b/odb/sql-lexer.ixx @@ -0,0 +1,86 @@ +// file : odb/sql-lexer.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// sql_lexer::xchar +// +inline sql_lexer::xchar:: +xchar (int_type v, std::size_t l, std::size_t c) + : v_ (v), l_ (l), c_ (c) +{ +} + +inline sql_lexer::xchar:: +operator char_type () const +{ + return traits_type::to_char_type (v_); +} + +inline sql_lexer::xchar::int_type sql_lexer::xchar:: +value () const +{ + return v_; +} + +inline std::size_t sql_lexer::xchar:: +line () const +{ + return l_; +} + +inline std::size_t sql_lexer::xchar:: +column () const +{ + return c_; +} + +// lexer +// +inline bool sql_lexer:: +is_alpha (char c) const +{ + return std::isalpha (c, loc_); +} + +inline bool sql_lexer:: +is_oct_digit (char c) const +{ + return std::isdigit (c, loc_) && c != '8' && c != '9'; +} + +inline bool sql_lexer:: +is_dec_digit (char c) const +{ + return std::isdigit (c, loc_); +} + +inline bool sql_lexer:: +is_hex_digit (char c) const +{ + return std::isxdigit (c, loc_); +} + +inline bool sql_lexer:: +is_alnum (char c) const +{ + return std::isalnum (c, loc_); +} + +inline bool sql_lexer:: +is_space (char c) const +{ + return std::isspace (c, loc_); +} + +inline bool sql_lexer:: +is_eos (xchar const& c) const +{ + return c.value () == xchar::traits_type::eof (); +} + +inline char sql_lexer:: +to_upper (char c) const +{ + return std::toupper (c, loc_); +} diff --git a/odb/sql-token.hxx b/odb/sql-token.hxx new file mode 100644 index 0000000..464c9a5 --- /dev/null +++ b/odb/sql-token.hxx @@ -0,0 +1,79 @@ +// file : odb/sql-token.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_SQL_TOKEN_HXX +#define ODB_SQL_TOKEN_HXX + +#include +#include // std::size_t + +class sql_token +{ +public: + enum token_type + { + t_eos, + t_identifier, + t_punctuation, + t_string_lit, + t_int_lit, + t_float_lit, + }; + + token_type + type () const; + + // Identifier + // +public: + std::string const& + identifier () const; + + // Punctuation + // +public: + enum punctuation_type + { + p_semi, + p_comma, + p_lparen, + p_rparen, + p_eq, + p_invalid + }; + + // Return the punctuation id if type is t_punctuation and p_invalid + // otherwise. + // + punctuation_type + punctuation () const; + + // Literals. + // +public: + std::string const& + literal () const; + + // C-tors. + // +public: + // EOS and punctuations. + // + sql_token (); + sql_token (punctuation_type p); + + // Identifier and literals. + // + sql_token (token_type t, std::string const& s); + +private: + token_type type_; + punctuation_type punctuation_; + std::string str_; +}; + +#include + +#endif // ODB_SQL_TOKEN_HXX diff --git a/odb/sql-token.ixx b/odb/sql-token.ixx new file mode 100644 index 0000000..43f522f --- /dev/null +++ b/odb/sql-token.ixx @@ -0,0 +1,46 @@ +// file : odb/sql-token.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +inline sql_token::token_type sql_token:: +type () const +{ + return type_; +} + +inline std::string const& sql_token:: +identifier () const +{ + return str_; +} + +inline sql_token::punctuation_type sql_token:: +punctuation () const +{ + return type_ == t_punctuation ? punctuation_ : p_invalid; +} + +inline std::string const& sql_token:: +literal () const +{ + return str_; +} + +inline sql_token:: +sql_token () + : type_ (t_eos) +{ +} + +inline sql_token:: +sql_token (punctuation_type p) + : type_ (t_punctuation), punctuation_ (p) +{ +} + +inline sql_token:: +sql_token (token_type t, std::string const& s) + : type_ (t), str_ (s) +{ +} diff --git a/odb/tracer/header.cxx b/odb/tracer/header.cxx new file mode 100644 index 0000000..c023e58 --- /dev/null +++ b/odb/tracer/header.cxx @@ -0,0 +1,161 @@ +// file : odb/tracer/header.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + + // Find the id member and type. + // + id_member t (*this); + t.traverse (c); + + if (t.member () == 0) + { + cerr << c.file () << ":" << c.line () << ":" << c.column () + << " error: no data member designated as object id" << endl; + + cerr << c.file () << ":" << c.line () << ":" << c.column () + << " info: use '#pragma odb id' to specify object id member" + << endl; + + throw generation_failed (); + } + + semantics::data_member& id (*t.member ()); + semantics::type& id_type (id.type ()); + + if (id_type.anonymous ()) + { + // Can be a template-id (which we should handle eventually) or an + // anonymous type in member declaration (e.g., struct {...} m_;). + // + cerr << id.file () << ":" << id.line () << ":" << id.column () + << " error: unnamed type in data member declaration" << endl; + + cerr << id.file () << ":" << id.line () << ":" << id.column () + << " info: use 'typedef' to name this type" + << endl; + + throw generation_failed (); + } + + os << "// " << c.name () << endl + << "//" << endl; + + os << "template <>" << endl + << "class access::object_traits< " << type << " >: " << endl + << " public access::object_memory< " << type << " >," << endl + << " public access::object_factory< " << type << " >" + << "{" + << "public:" << endl; + + // object_type & shared_ptr + // + os << "typedef " << type << " object_type;"; + + // id_type + // + os << "typedef " << id_type.fq_name () << " id_type;" + << endl; + + // id_source + // + os << "static const odb::id_source id_source = odb::ids_assigned;" + << endl; + + // type_name () + // + os << "static const char*" << endl + << "type_name ();" + << endl; + + // id () + // + os << "static id_type" << endl + << "id (const object_type&);" + << endl; + + // persist () + // + os << "static void" << endl + << "persist (database&, object_type&);" + << endl; + + // store () + // + os << "static void" << endl + << "store (database&, object_type&);" + << endl; + + // erase () + // + os << "static void" << endl + << "erase (database&, const id_type&);" + << endl; + + // find () + // + os << "static pointer_type" << endl + << "find (database&, const id_type&);" + << endl; + + os << "static bool" << endl + << "find (database&, const id_type&, object_type&);"; + + os << "};"; + } + }; +} + +namespace tracer +{ + void + generate_header (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "#include " << endl + << "#include " << endl + << endl; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/tracer/header.hxx b/odb/tracer/header.hxx new file mode 100644 index 0000000..0e67712 --- /dev/null +++ b/odb/tracer/header.hxx @@ -0,0 +1,17 @@ +// file : odb/tracer/header.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_TRACER_HEADER_HXX +#define ODB_TRACER_HEADER_HXX + +#include + +namespace tracer +{ + void + generate_header (context&); +} + +#endif // ODB_TRACER_HEADER_HXX diff --git a/odb/tracer/inline.cxx b/odb/tracer/inline.cxx new file mode 100644 index 0000000..6beb8a8 --- /dev/null +++ b/odb/tracer/inline.cxx @@ -0,0 +1,76 @@ +// file : odb/tracer/inline.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + string traits ("access::object_traits< " + type + " >"); + + id_member t (*this); + t.traverse (c); + semantics::data_member& id (*t.member ()); + + os << "// " << c.name () << endl + << "//" << endl + << endl; + + // id () + // + os << "inline" << endl + << traits << "::id_type" << endl + << traits << "::" << endl + << "id (const object_type& obj)" + << "{" + << "return obj." << id.name () << ";" << endl + << "}"; + } + }; +} + +namespace tracer +{ + void + generate_inline (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/tracer/inline.hxx b/odb/tracer/inline.hxx new file mode 100644 index 0000000..2b01589 --- /dev/null +++ b/odb/tracer/inline.hxx @@ -0,0 +1,17 @@ +// file : odb/tracer/inline.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_TRACER_INLINE_HXX +#define ODB_TRACER_INLINE_HXX + +#include + +namespace tracer +{ + void + generate_inline (context&); +} + +#endif // ODB_TRACER_INLINE_HXX diff --git a/odb/tracer/source.cxx b/odb/tracer/source.cxx new file mode 100644 index 0000000..e70adf1 --- /dev/null +++ b/odb/tracer/source.cxx @@ -0,0 +1,147 @@ +// file : odb/tracer/source.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include + +namespace +{ + struct class_: traversal::class_, context + { + class_ (context& c) + : context (c) + { + } + + virtual void + traverse (type& c) + { + if (c.file () != unit.file ()) + return; + + if (!c.count ("object")) + return; + + string const& type (c.fq_name ()); + string traits ("access::object_traits< " + type + " >"); + + id_member t (*this); + t.traverse (c); + semantics::data_member& id (*t.member ()); + + os << "// " << c.name () << endl + << "//" << endl + << endl; + + // type_name () + // + os << "const char* " << traits << "::" << endl + << "type_name ()" + << "{" + << "return \"" << type << "\";" + << "}"; + + // persist () + // + os << "void " << traits << "::" << endl + << "persist (database&, object_type& obj)" + << "{" + << "std::cout << \"insert \" << type_name () << \" id \" << " << + "id (obj) << std::endl;" + << endl + << "if (id (obj) == id_type ())" << endl + << "throw object_already_persistent ();" + << "}"; + + // store () + // + os << "void " << traits << "::" << endl + << "store (database&, object_type& obj)" + << "{" + << "std::cout << \"update \" << type_name () << \" id \" << " << + "id (obj) << std::endl;" + << endl + << "if (id (obj) == id_type ())" << endl + << "throw object_not_persistent ();" + << "}"; + + // erase () + // + os << "void " << traits << "::" << endl + << "erase (database&, const id_type& id)" + << "{" + << "std::cout << \"delete \" << type_name () << \" id \" << " << + "id << std::endl;" + << endl + << "if (id == id_type ())" << endl + << "throw object_not_persistent ();" + << "}"; + + // find () + // + os << traits << "::pointer_type" << endl + << traits << "::" << endl + << "find (database&, const id_type& id)" + << "{" + << "std::cout << \"select \" << type_name () << \" id \" << " << + "id << std::endl;" + << endl + << "if (id == id_type ())" << endl + << "return pointer_type (0);" + << endl + << "pointer_type r (access::object_factory< " << type << + " >::create ());" + << "r->" << id.name () << " = id;" + << "return r;" + << "}"; + + os << "bool " << traits << "::" << endl + << "find (database&, const id_type& id, object_type& obj)" + << "{" + << "std::cout << \"select \" << type_name () << \" id \" << " << + "id << std::endl;" + << endl + << "if (id == id_type ())" << endl + << "return false;" + << endl + << "obj." << id.name () << " = id;" + << "return true;" + << "}"; + } + }; +} + +namespace tracer +{ + void + generate_source (context& ctx) + { + traversal::unit unit; + traversal::defines unit_defines; + traversal::namespace_ ns; + class_ c (ctx); + + unit >> unit_defines >> ns; + unit_defines >> c; + + traversal::defines ns_defines; + + ns >> ns_defines >> ns; + ns_defines >> c; + + ctx.os << "#include " << endl + << endl; + + ctx.os << "#include " << endl + << endl; + + ctx.os << "namespace odb" + << "{"; + + unit.dispatch (ctx.unit); + + ctx.os << "}"; + } +} diff --git a/odb/tracer/source.hxx b/odb/tracer/source.hxx new file mode 100644 index 0000000..2c82f3a --- /dev/null +++ b/odb/tracer/source.hxx @@ -0,0 +1,17 @@ +// file : odb/tracer/source.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_TRACER_SOURCE_HXX +#define ODB_TRACER_SOURCE_HXX + +#include + +namespace tracer +{ + void + generate_source (context&); +} + +#endif // ODB_TRACER_SOURCE_HXX -- cgit v1.1