From 0c3bb27e585fad25607f075a305c1e0345af7213 Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Sep 2018 09:55:43 +0200 Subject: [PATCH] Further improvements to codegeneration the defaultcpp config is now called the PgLab config as it is very specific to the PgLab codebase. More hard programmed templates moved out of codebuilder to the language config. --- pglab/CodeGenerator.cpp | 2 +- pglablib/codebuilder/CodeBuilder.cpp | 74 ++++++++++----------- pglablib/codebuilder/CodeBuilder.h | 26 ++++---- pglablib/codebuilder/DefaultConfigs.cpp | 78 ++++++++++++++--------- pglablib/codebuilder/DefaultConfigs.h | 4 +- pglablib/codebuilder/LanguageConfig.cpp | 10 +++ pglablib/codebuilder/LanguageConfig.h | 6 +- pglablib/codebuilder/ResultLoopTemplate.h | 26 ++++++++ pglablib/codebuilder/StructureTemplate.h | 5 +- pglablib/pglablib.pro | 3 +- 10 files changed, 143 insertions(+), 91 deletions(-) create mode 100644 pglablib/codebuilder/ResultLoopTemplate.h diff --git a/pglab/CodeGenerator.cpp b/pglab/CodeGenerator.cpp index 92c4c2f..0cf97aa 100644 --- a/pglab/CodeGenerator.cpp +++ b/pglab/CodeGenerator.cpp @@ -42,7 +42,7 @@ void CodeGenerator::generateCode() QString buffer; QTextStream stream(&buffer); CodeBuilder builder; - builder.setLanguageConfig(getDefaultCppLanguageConfig()); + builder.setLanguageConfig(getPglabCppLanguageConfig()); builder.GenCodeForExecutingQuery(stream, m_query, *m_dbres, struct_name); stream.flush(); ui->generatedCodeEditor->setPlainText(buffer); diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp index dd782c0..763c567 100644 --- a/pglablib/codebuilder/CodeBuilder.cpp +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -3,45 +3,30 @@ #include "Pgsql_Result.h" #include "IndentationConfig.h" #include "LanguageConfig.h" +#include "ResultLoopTemplate.h" #include "StructureTemplate.h" #include "util.h" #include void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname) { -// %1 query string -// %2 struct_name -// %3 list of field assignments - QString exec_query_template = "Pgsql::Result result = conn.query(%1);"; - - QString loop_start_template = R"__(Pgsql::Result result = conn.query(/%queryliteral%/); -for (auto row: result) { - Pgsql::Col col(row); - /%structname%/ v; - /%assignfields%/ -})__"; - - // %field_name% - // %column_name% - // %column_index% - QString assign_result_field_template = "col >> v./%fieldname%/"; + QString loop_template = m_configuration->resultLoopTemplate()->m_loopTemplate;; QString query_string_literal = ConvertToMultiLineCString(query); ColumnDataList columnData = createColumnDataList(result); auto get_var_func = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; - //else if (var == "assign_fields") GenFieldAssignments(out); else if (var == "queryliteral") out << query; - + else if (var == "assignfields") genFieldRetrieval(out, columnData); }; // [optional] Gen declaration of result structure - GenReturnStructDefinition(q, result, structname); + GenReturnStructDefinition(q, columnData, structname); // assume we have connection? What name??? - FormatToStream(q, loop_start_template, get_var_func); + FormatToStream(q, loop_template, get_var_func); // gen code for creating and executing statement // - bind parameters @@ -51,27 +36,30 @@ for (auto row: result) { } -void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const +void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const ColumnDataList &columns, QString structname) const { std::shared_ptr templ = m_configuration->structureTemplate(); - QString field_format = templ->m_fieldTemplate; - int field_indent_levels = templ->m_fieldIndentation; - QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); - auto struct_callback = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; + else if (var == "structfields") genStructFields(out, columns); }; - // Make struct start - //q << QString(templ->m_startTemplate).arg(struct_name); - FormatToStream(q, templ->m_startTemplate, struct_callback); - // Process fields - for (int column = 0; column < result.cols(); ++column) { - QString res_col_name = result.getColName(column); - Oid type_oid = result.type(column); + FormatToStream(q, templ->m_structTemplate, struct_callback); +} + +void CodeBuilder::genStructFields(QTextStream &q, const ColumnDataList &columns) const +{ + std::shared_ptr templ = m_configuration->structureTemplate(); + QString field_format = templ->m_fieldTemplate; + const int field_indent_levels = templ->m_fieldIndentation; + QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); + + for (auto col : columns) { + QString field_name = col.varName; + Oid type_oid = col.oid; q << field_indent_string; - genFieldDeclaration(q, field_format, res_col_name, type_oid); + genFieldDeclaration(q, field_format, field_name, type_oid); q << "\n"; // Unfortunatly there is no easy reliable way to determine if a column can contain null values // For simple result columns we can have a look at the original column and if that has a NOT NULL constraint @@ -85,10 +73,6 @@ void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result // - boolean flags // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } - // Finish struct - - //q << QString(templ->m_endTemplate).arg(struct_name); - FormatToStream(q, templ->m_endTemplate, struct_callback); } QString CodeBuilder::columnNameToVariableName(QString column_name) const @@ -101,18 +85,26 @@ QString CodeBuilder::getTypeName(Oid dbtype) const return m_configuration->getTypeName(dbtype); } -void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const +void CodeBuilder::genFieldRetrieval(QTextStream &q, const ColumnDataList &columns) const +{ + QString assign_result_field_template = m_configuration->resultLoopTemplate()->m_retrieveValueTemplate; + for (auto col : columns) { + FormatToStream(q, assign_result_field_template, [&col] (QTextStream &out, QString var) { + if (var == "varname") out << col.varName; + }); + } +} + +void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &field_name, Oid column_type) const { - QString field_name = columnNameToVariableName(column_name); QString type_name = getTypeName(column_type); - //q << QString(format).arg(field_name).arg(type_name); FormatToStream(q, format, [&] (QTextStream &out, QString var) { if (var == "typename") out << type_name; else if (var == "varname") out << field_name; }); } -CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) +CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) const { ColumnDataList list; list.reserve(static_cast(result.cols())); diff --git a/pglablib/codebuilder/CodeBuilder.h b/pglablib/codebuilder/CodeBuilder.h index aad070d..01694e7 100644 --- a/pglablib/codebuilder/CodeBuilder.h +++ b/pglablib/codebuilder/CodeBuilder.h @@ -10,12 +10,22 @@ class QTextStream; class CodeBuilder { public: + class ColumnData { + public: + Oid oid; + QString columnName; + QString varName; ///< either field of the struct or in loop local var name + }; + using ColumnDataList = std::vector; + + ColumnDataList createColumnDataList(const Pgsql::Result &result) const; + void setLanguageConfig(std::shared_ptr config){ m_configuration = config; } void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname); - void GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const; + void GenReturnStructDefinition(QTextStream &q, const ColumnDataList &columns, QString structname) const; // Generating code for performing query and going through the result // - Code for executing the query @@ -25,21 +35,13 @@ public: QString columnNameToVariableName(QString column_name) const; QString getTypeName(Oid dbtype) const; private: - class ColumnData { - public: - Oid oid; - QString columnName; - QString varName; ///< either field of the struct or in loop local var name - }; - using ColumnDataList = std::vector; std::shared_ptr m_configuration; + void genStructFields(QTextStream &q, const ColumnDataList &columns) const; + void genFieldRetrieval(QTextStream &q, const ColumnDataList &columns) const; + void genFieldDeclaration(QTextStream &q, const QString &format, const QString &field_name, Oid column_type) const; - void GenFieldAssignments(QTextStream &q, const Pgsql::Result &result); - void genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const; - - ColumnDataList createColumnDataList(const Pgsql::Result &result); }; #endif // CODEBUILDER_H diff --git a/pglablib/codebuilder/DefaultConfigs.cpp b/pglablib/codebuilder/DefaultConfigs.cpp index d9f31c3..da1eba2 100644 --- a/pglablib/codebuilder/DefaultConfigs.cpp +++ b/pglablib/codebuilder/DefaultConfigs.cpp @@ -2,13 +2,14 @@ #include "IndentationConfig.h" #include "LanguageConfig.h" #include "NameManglingRules.h" +#include "ResultLoopTemplate.h" #include "StructureTemplate.h" #include "TypeMappings.h" #include "Pgsql_oids.h" using namespace Pgsql; -std::shared_ptr GetDefaultCppTypeMappings() +std::shared_ptr GetPglabCppTypeMappings() { auto tm = std::make_shared(); *tm = { @@ -28,40 +29,57 @@ std::shared_ptr GetDefaultCppTypeMappings() return tm; } +std::shared_ptr buildPglabStructureTemplate() +{ + auto t = std::make_shared(); + t->m_structTemplate = +R"__(class /%structname%/ { +public: +/%structfields%/ +}; +using /%structname%/Lst = std::vector; +)__"; -std::shared_ptr buildDefaultCppLanguageConfig() + t->m_fieldTemplate = "/%typename%/ /%varname%/;"; + //st_templ->m_fieldSeparator; + + return t; +} + +std::shared_ptr buildPglabResultLoopTemplate() +{ + auto t = std::make_shared(); + t->m_loopTemplate = +R"__(Pgsql::Result result = conn.query(/%queryliteral%/); +/%structname%/Lst lst; +for (auto row: result) { + Pgsql::Col col(row); + /%structname%/ v; + col + /%assignfields%/; + lst.push_back(v); +} +)__"; + t->m_retrieveValueTemplate = " >> v./%varname%/"; + + return t; +} + +std::shared_ptr buildPglabCppLanguageConfig() { auto config = std::make_shared(); - config->setTypeMappings(GetDefaultCppTypeMappings()); + config->setTypeMappings(GetPglabCppTypeMappings()); config->setNameManglingRules(std::make_shared()); - - auto st_templ = std::make_shared(); - st_templ->m_startTemplate = "class /%structname%/ {\npublic:\n"; - st_templ->m_endTemplate = "};\n"; // }; - st_templ->m_fieldTemplate = "/%typename%/ /%varname%/;"; - //st_templ->m_fieldSeparator; - config->setStructureTemplate(st_templ); + config->setStructureTemplate(buildPglabStructureTemplate()); config->setIndentationConfig(std::make_shared()); - - return config; -} - -std::shared_ptr getDefaultCppLanguageConfig() -{ - static auto config = buildDefaultCppLanguageConfig(); - return config; -} - -std::shared_ptr buildPgLabCppLanguageConfig() -{ - auto cfg = buildDefaultCppLanguageConfig(); - - return cfg; -} - -std::shared_ptr getPgLabCppLanguageConfig() -{ - static auto config = buildPgLabCppLanguageConfig(); + config->setResultLoopTemplate(buildPglabResultLoopTemplate()); + return config; +} + + +std::shared_ptr getPglabCppLanguageConfig() +{ + static auto config = buildPglabCppLanguageConfig(); return config; } diff --git a/pglablib/codebuilder/DefaultConfigs.h b/pglablib/codebuilder/DefaultConfigs.h index d446d22..bb04da9 100644 --- a/pglablib/codebuilder/DefaultConfigs.h +++ b/pglablib/codebuilder/DefaultConfigs.h @@ -5,7 +5,7 @@ class LanguageConfig; -std::shared_ptr getDefaultCppLanguageConfig(); -std::shared_ptr getPgLabCppLanguageConfig(); +//std::shared_ptr getDefaultCppLanguageConfig(); +std::shared_ptr getPglabCppLanguageConfig(); #endif // DEFAULTCONFIGS_H diff --git a/pglablib/codebuilder/LanguageConfig.cpp b/pglablib/codebuilder/LanguageConfig.cpp index 33100f8..2fa4b74 100644 --- a/pglablib/codebuilder/LanguageConfig.cpp +++ b/pglablib/codebuilder/LanguageConfig.cpp @@ -48,3 +48,13 @@ void LanguageConfig::setIndentationConfig(std::shared_ptr LanguageConfig::resultLoopTemplate() const +{ + return m_resultLoopTemplate; +} + +void LanguageConfig::setResultLoopTemplate(std::shared_ptr result_loop_template) +{ + m_resultLoopTemplate = result_loop_template; +} diff --git a/pglablib/codebuilder/LanguageConfig.h b/pglablib/codebuilder/LanguageConfig.h index 3f9ac46..8631a99 100644 --- a/pglablib/codebuilder/LanguageConfig.h +++ b/pglablib/codebuilder/LanguageConfig.h @@ -8,6 +8,7 @@ class NameManglingRules; class TypeMappings; class StructureTemplate; class IndentationConfig; +class ResultLoopTemplate; /** * */ @@ -25,6 +26,8 @@ public: void setStructureTemplate(std::shared_ptr structure_template); std::shared_ptr indentationConfig() const; void setIndentationConfig(std::shared_ptr indentation_config); + std::shared_ptr resultLoopTemplate() const; + void setResultLoopTemplate(std::shared_ptr result_loop_template); private: /** Default template for declaring a variable of the correct type. * exmaple: "{$type} {$varname};" @@ -34,13 +37,12 @@ private: std::shared_ptr m_typeMappings; std::shared_ptr m_structureTemplate; std::shared_ptr m_indentationConfig; + std::shared_ptr m_resultLoopTemplate; enum class VariableStrategy { UseLocalVariables, DeclareClass }; - - }; #endif // LANGUAGECONFIG_H diff --git a/pglablib/codebuilder/ResultLoopTemplate.h b/pglablib/codebuilder/ResultLoopTemplate.h new file mode 100644 index 0000000..660d437 --- /dev/null +++ b/pglablib/codebuilder/ResultLoopTemplate.h @@ -0,0 +1,26 @@ +#ifndef RESULTLOOPTEMPLATE_H +#define RESULTLOOPTEMPLATE_H + +#include + +class ResultLoopTemplate { +public: + /** + * @brief m_loopTemplate + * + * Excepted template fields + * - structname: The typename of the struct/class + * - queryliteral: The query + * - assignfields: Replaced with the field assignment + */ + QString m_loopTemplate; + /** + * @brief m_retrieveValueTemplate + * + * Excepted template fields + * - varname: the name of the field of the struct or normal variable + */ + QString m_retrieveValueTemplate; +}; + +#endif // RESULTLOOPTEMPLATE_H diff --git a/pglablib/codebuilder/StructureTemplate.h b/pglablib/codebuilder/StructureTemplate.h index 716031a..f93a1ef 100644 --- a/pglablib/codebuilder/StructureTemplate.h +++ b/pglablib/codebuilder/StructureTemplate.h @@ -6,8 +6,9 @@ class StructureTemplate { public: - QString m_startTemplate; // class /$structname/ {\npublic:\n - QString m_endTemplate; // }; +// QString m_startTemplate; // class /$structname/ {\npublic:\n +// QString m_endTemplate; // }; + QString m_structTemplate; QString m_fieldTemplate; // /$typename/ /$varname/; QString m_fieldSeparator; // use when you need something between fields but not after the last field int m_fieldIndentation = 1; diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 1d0acf9..b355769 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -104,7 +104,8 @@ codebuilder/DefaultConfigs.h \ codebuilder/TypeMappings.h \ codebuilder/IndentationConfig.h \ codebuilder/StructureTemplate.h \ - FormatToStream.h + FormatToStream.h \ + codebuilder/ResultLoopTemplate.h unix { target.path = /usr/lib