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.
This commit is contained in:
eelke 2018-09-19 09:55:43 +02:00
parent 0ba632afd1
commit 0c3bb27e58
10 changed files with 143 additions and 91 deletions

View file

@ -42,7 +42,7 @@ void CodeGenerator::generateCode()
QString buffer; QString buffer;
QTextStream stream(&buffer); QTextStream stream(&buffer);
CodeBuilder builder; CodeBuilder builder;
builder.setLanguageConfig(getDefaultCppLanguageConfig()); builder.setLanguageConfig(getPglabCppLanguageConfig());
builder.GenCodeForExecutingQuery(stream, m_query, *m_dbres, struct_name); builder.GenCodeForExecutingQuery(stream, m_query, *m_dbres, struct_name);
stream.flush(); stream.flush();
ui->generatedCodeEditor->setPlainText(buffer); ui->generatedCodeEditor->setPlainText(buffer);

View file

@ -3,45 +3,30 @@
#include "Pgsql_Result.h" #include "Pgsql_Result.h"
#include "IndentationConfig.h" #include "IndentationConfig.h"
#include "LanguageConfig.h" #include "LanguageConfig.h"
#include "ResultLoopTemplate.h"
#include "StructureTemplate.h" #include "StructureTemplate.h"
#include "util.h" #include "util.h"
#include <QTextStream> #include <QTextStream>
void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname) void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname)
{ {
// %1 query string QString loop_template = m_configuration->resultLoopTemplate()->m_loopTemplate;;
// %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 query_string_literal = ConvertToMultiLineCString(query); QString query_string_literal = ConvertToMultiLineCString(query);
ColumnDataList columnData = createColumnDataList(result); ColumnDataList columnData = createColumnDataList(result);
auto get_var_func = [&] (QTextStream &out, QString var) { auto get_var_func = [&] (QTextStream &out, QString var) {
if (var == "structname") out << structname; if (var == "structname") out << structname;
//else if (var == "assign_fields") GenFieldAssignments(out);
else if (var == "queryliteral") out << query; else if (var == "queryliteral") out << query;
else if (var == "assignfields") genFieldRetrieval(out, columnData);
}; };
// [optional] Gen declaration of result structure // [optional] Gen declaration of result structure
GenReturnStructDefinition(q, result, structname); GenReturnStructDefinition(q, columnData, structname);
// assume we have connection? What name??? // 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 // gen code for creating and executing statement
// - bind parameters // - 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<const StructureTemplate> templ = m_configuration->structureTemplate(); std::shared_ptr<const StructureTemplate> 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) { auto struct_callback = [&] (QTextStream &out, QString var) {
if (var == "structname") out << structname; 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<const StructureTemplate> 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; q << field_indent_string;
genFieldDeclaration(q, field_format, res_col_name, type_oid); genFieldDeclaration(q, field_format, field_name, type_oid);
q << "\n"; q << "\n";
// Unfortunatly there is no easy reliable way to determine if a column can contain null values // 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 // 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 // - boolean flags
// - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) // - 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 QString CodeBuilder::columnNameToVariableName(QString column_name) const
@ -101,18 +85,26 @@ QString CodeBuilder::getTypeName(Oid dbtype) const
return m_configuration->getTypeName(dbtype); 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); QString type_name = getTypeName(column_type);
//q << QString(format).arg(field_name).arg(type_name);
FormatToStream(q, format, [&] (QTextStream &out, QString var) { FormatToStream(q, format, [&] (QTextStream &out, QString var) {
if (var == "typename") out << type_name; if (var == "typename") out << type_name;
else if (var == "varname") out << field_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; ColumnDataList list;
list.reserve(static_cast<size_t>(result.cols())); list.reserve(static_cast<size_t>(result.cols()));

View file

@ -10,12 +10,22 @@ class QTextStream;
class CodeBuilder { class CodeBuilder {
public: 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<ColumnData>;
ColumnDataList createColumnDataList(const Pgsql::Result &result) const;
void setLanguageConfig(std::shared_ptr<const LanguageConfig> config){ void setLanguageConfig(std::shared_ptr<const LanguageConfig> config){
m_configuration = config; m_configuration = config;
} }
void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname); 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 // Generating code for performing query and going through the result
// - Code for executing the query // - Code for executing the query
@ -25,21 +35,13 @@ public:
QString columnNameToVariableName(QString column_name) const; QString columnNameToVariableName(QString column_name) const;
QString getTypeName(Oid dbtype) const; QString getTypeName(Oid dbtype) const;
private: 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<ColumnData>;
std::shared_ptr<const LanguageConfig> m_configuration; std::shared_ptr<const LanguageConfig> 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 #endif // CODEBUILDER_H

View file

@ -2,13 +2,14 @@
#include "IndentationConfig.h" #include "IndentationConfig.h"
#include "LanguageConfig.h" #include "LanguageConfig.h"
#include "NameManglingRules.h" #include "NameManglingRules.h"
#include "ResultLoopTemplate.h"
#include "StructureTemplate.h" #include "StructureTemplate.h"
#include "TypeMappings.h" #include "TypeMappings.h"
#include "Pgsql_oids.h" #include "Pgsql_oids.h"
using namespace Pgsql; using namespace Pgsql;
std::shared_ptr<const TypeMappings> GetDefaultCppTypeMappings() std::shared_ptr<const TypeMappings> GetPglabCppTypeMappings()
{ {
auto tm = std::make_shared<TypeMappings>(); auto tm = std::make_shared<TypeMappings>();
*tm = { *tm = {
@ -28,40 +29,57 @@ std::shared_ptr<const TypeMappings> GetDefaultCppTypeMappings()
return tm; return tm;
} }
std::shared_ptr<StructureTemplate> buildPglabStructureTemplate()
{
auto t = std::make_shared<StructureTemplate>();
t->m_structTemplate =
R"__(class /%structname%/ {
public:
/%structfields%/
};
using /%structname%/Lst = std::vector</%structname%/>;
)__";
std::shared_ptr<LanguageConfig> buildDefaultCppLanguageConfig() t->m_fieldTemplate = "/%typename%/ /%varname%/;";
//st_templ->m_fieldSeparator;
return t;
}
std::shared_ptr<ResultLoopTemplate> buildPglabResultLoopTemplate()
{
auto t = std::make_shared<ResultLoopTemplate>();
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<LanguageConfig> buildPglabCppLanguageConfig()
{ {
auto config = std::make_shared<LanguageConfig>(); auto config = std::make_shared<LanguageConfig>();
config->setTypeMappings(GetDefaultCppTypeMappings()); config->setTypeMappings(GetPglabCppTypeMappings());
config->setNameManglingRules(std::make_shared<NameManglingRules>()); config->setNameManglingRules(std::make_shared<NameManglingRules>());
config->setStructureTemplate(buildPglabStructureTemplate());
auto st_templ = std::make_shared<StructureTemplate>();
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->setIndentationConfig(std::make_shared<IndentationConfig>()); config->setIndentationConfig(std::make_shared<IndentationConfig>());
config->setResultLoopTemplate(buildPglabResultLoopTemplate());
return config; return config;
} }
std::shared_ptr<const LanguageConfig> getDefaultCppLanguageConfig()
{ std::shared_ptr<const LanguageConfig> getPglabCppLanguageConfig()
static auto config = buildDefaultCppLanguageConfig(); {
return config; static auto config = buildPglabCppLanguageConfig();
}
std::shared_ptr<const LanguageConfig> buildPgLabCppLanguageConfig()
{
auto cfg = buildDefaultCppLanguageConfig();
return cfg;
}
std::shared_ptr<const LanguageConfig> getPgLabCppLanguageConfig()
{
static auto config = buildPgLabCppLanguageConfig();
return config; return config;
} }

View file

@ -5,7 +5,7 @@
class LanguageConfig; class LanguageConfig;
std::shared_ptr<const LanguageConfig> getDefaultCppLanguageConfig(); //std::shared_ptr<const LanguageConfig> getDefaultCppLanguageConfig();
std::shared_ptr<const LanguageConfig> getPgLabCppLanguageConfig(); std::shared_ptr<const LanguageConfig> getPglabCppLanguageConfig();
#endif // DEFAULTCONFIGS_H #endif // DEFAULTCONFIGS_H

View file

@ -48,3 +48,13 @@ void LanguageConfig::setIndentationConfig(std::shared_ptr<const IndentationConfi
{ {
m_indentationConfig = indentation_config; m_indentationConfig = indentation_config;
} }
std::shared_ptr<const ResultLoopTemplate> LanguageConfig::resultLoopTemplate() const
{
return m_resultLoopTemplate;
}
void LanguageConfig::setResultLoopTemplate(std::shared_ptr<const ResultLoopTemplate> result_loop_template)
{
m_resultLoopTemplate = result_loop_template;
}

View file

@ -8,6 +8,7 @@ class NameManglingRules;
class TypeMappings; class TypeMappings;
class StructureTemplate; class StructureTemplate;
class IndentationConfig; class IndentationConfig;
class ResultLoopTemplate;
/** /**
* *
*/ */
@ -25,6 +26,8 @@ public:
void setStructureTemplate(std::shared_ptr<const StructureTemplate> structure_template); void setStructureTemplate(std::shared_ptr<const StructureTemplate> structure_template);
std::shared_ptr<const IndentationConfig> indentationConfig() const; std::shared_ptr<const IndentationConfig> indentationConfig() const;
void setIndentationConfig(std::shared_ptr<const IndentationConfig> indentation_config); void setIndentationConfig(std::shared_ptr<const IndentationConfig> indentation_config);
std::shared_ptr<const ResultLoopTemplate> resultLoopTemplate() const;
void setResultLoopTemplate(std::shared_ptr<const ResultLoopTemplate> result_loop_template);
private: private:
/** Default template for declaring a variable of the correct type. /** Default template for declaring a variable of the correct type.
* exmaple: "{$type} {$varname};" * exmaple: "{$type} {$varname};"
@ -34,13 +37,12 @@ private:
std::shared_ptr<const TypeMappings> m_typeMappings; std::shared_ptr<const TypeMappings> m_typeMappings;
std::shared_ptr<const StructureTemplate> m_structureTemplate; std::shared_ptr<const StructureTemplate> m_structureTemplate;
std::shared_ptr<const IndentationConfig> m_indentationConfig; std::shared_ptr<const IndentationConfig> m_indentationConfig;
std::shared_ptr<const ResultLoopTemplate> m_resultLoopTemplate;
enum class VariableStrategy { enum class VariableStrategy {
UseLocalVariables, UseLocalVariables,
DeclareClass DeclareClass
}; };
}; };
#endif // LANGUAGECONFIG_H #endif // LANGUAGECONFIG_H

View file

@ -0,0 +1,26 @@
#ifndef RESULTLOOPTEMPLATE_H
#define RESULTLOOPTEMPLATE_H
#include <QString>
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

View file

@ -6,8 +6,9 @@
class StructureTemplate { class StructureTemplate {
public: public:
QString m_startTemplate; // class /$structname/ {\npublic:\n // QString m_startTemplate; // class /$structname/ {\npublic:\n
QString m_endTemplate; // }; // QString m_endTemplate; // };
QString m_structTemplate;
QString m_fieldTemplate; // /$typename/ /$varname/; QString m_fieldTemplate; // /$typename/ /$varname/;
QString m_fieldSeparator; // use when you need something between fields but not after the last field QString m_fieldSeparator; // use when you need something between fields but not after the last field
int m_fieldIndentation = 1; int m_fieldIndentation = 1;

View file

@ -104,7 +104,8 @@ codebuilder/DefaultConfigs.h \
codebuilder/TypeMappings.h \ codebuilder/TypeMappings.h \
codebuilder/IndentationConfig.h \ codebuilder/IndentationConfig.h \
codebuilder/StructureTemplate.h \ codebuilder/StructureTemplate.h \
FormatToStream.h FormatToStream.h \
codebuilder/ResultLoopTemplate.h
unix { unix {
target.path = /usr/lib target.path = /usr/lib