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;
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);

View file

@ -3,45 +3,30 @@
#include "Pgsql_Result.h"
#include "IndentationConfig.h"
#include "LanguageConfig.h"
#include "ResultLoopTemplate.h"
#include "StructureTemplate.h"
#include "util.h"
#include <QTextStream>
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<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) {
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;
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<size_t>(result.cols()));

View file

@ -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<ColumnData>;
ColumnDataList createColumnDataList(const Pgsql::Result &result) const;
void setLanguageConfig(std::shared_ptr<const LanguageConfig> 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<ColumnData>;
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

View file

@ -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<const TypeMappings> GetDefaultCppTypeMappings()
std::shared_ptr<const TypeMappings> GetPglabCppTypeMappings()
{
auto tm = std::make_shared<TypeMappings>();
*tm = {
@ -28,40 +29,57 @@ std::shared_ptr<const TypeMappings> GetDefaultCppTypeMappings()
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>();
config->setTypeMappings(GetDefaultCppTypeMappings());
config->setTypeMappings(GetPglabCppTypeMappings());
config->setNameManglingRules(std::make_shared<NameManglingRules>());
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->setStructureTemplate(buildPglabStructureTemplate());
config->setIndentationConfig(std::make_shared<IndentationConfig>());
return config;
}
std::shared_ptr<const LanguageConfig> getDefaultCppLanguageConfig()
{
static auto config = buildDefaultCppLanguageConfig();
return config;
}
std::shared_ptr<const LanguageConfig> buildPgLabCppLanguageConfig()
{
auto cfg = buildDefaultCppLanguageConfig();
return cfg;
}
std::shared_ptr<const LanguageConfig> getPgLabCppLanguageConfig()
{
static auto config = buildPgLabCppLanguageConfig();
config->setResultLoopTemplate(buildPglabResultLoopTemplate());
return config;
}
std::shared_ptr<const LanguageConfig> getPglabCppLanguageConfig()
{
static auto config = buildPglabCppLanguageConfig();
return config;
}

View file

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

View file

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

View file

@ -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