add option: --debug-variable-mapping + different variables for attributes with ambiguous names
--- a/src/CLIParser.h Tue May 07 01:03:21 2019 +0200
+++ b/src/CLIParser.h Sun May 26 19:46:47 2019 +0200
@@ -60,6 +60,7 @@
static const string_t OPTION_OUTPUT_ATTRIBUTE;
static const string_t OPTION_INPUT_ATTRIBUTES_APPEND;
static const string_t OPTION_INPUT_ATTRIBUTES_PREPEND;
+ static const string_t OPTION_DEBUG_VARIABLE_MAPPING;
static const string_t OPTION_BEFORE_RECORDS;
static const string_t OPTION_AFTER_RECORDS;
static const string_t OPTION_FOR_EACH;
@@ -83,6 +84,7 @@
else if (option == OPTION_DROP) currentRelation.drop = true;
else if (option == OPTION_INPUT_ATTRIBUTES_APPEND) currentRelation.inputAttributesAppend = true;
else if (option == OPTION_INPUT_ATTRIBUTES_PREPEND) currentRelation.inputAttributesPrepend = true;
+ else if (option == OPTION_DEBUG_VARIABLE_MAPPING) currentRelation.debugVariableMapping = true;
else if (option == OPTION_RELATION) {
addRelation(c, currentRelation); // previous relation
currentRelation.relation = readNext(arguments, i);
@@ -113,6 +115,7 @@
const string_t CLIParser::OPTION_OUTPUT_ATTRIBUTE = L"--output-attribute";
const string_t CLIParser::OPTION_INPUT_ATTRIBUTES_APPEND = L"--input-attributes-append";
const string_t CLIParser::OPTION_INPUT_ATTRIBUTES_PREPEND = L"--input-attributes-prepend";
+const string_t CLIParser::OPTION_DEBUG_VARIABLE_MAPPING = L"--debug-variable-mapping";
const string_t CLIParser::OPTION_BEFORE_RECORDS = L"--before-records";
const string_t CLIParser::OPTION_AFTER_RECORDS = L"--after-records";
const string_t CLIParser::OPTION_FOR_EACH = L"--for-each";
--- a/src/Configuration.h Tue May 07 01:03:21 2019 +0200
+++ b/src/Configuration.h Sun May 26 19:46:47 2019 +0200
@@ -50,6 +50,11 @@
relpipe::writer::string_t guileForEach;
relpipe::writer::string_t guileWhere;
relpipe::writer::string_t guileHasMoreRecords;
+
+ /**
+ * If true, additional relation will be generated: mapping between relpipe attribute names and Guile variable names
+ */
+ bool debugVariableMapping = false;
/**
* If true, Guile code will be executed, but no output will be generated.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/GuileException.h Sun May 26 19:46:47 2019 +0200
@@ -0,0 +1,44 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <string>
+
+using namespace std;
+
+namespace relpipe {
+namespace tr {
+namespace guile {
+
+class GuileException {
+private:
+ wstring message;
+public:
+
+ GuileException(wstring message) : message(message) {
+ }
+
+ wstring getMessge() {
+ return message;
+ }
+
+};
+
+}
+}
+}
\ No newline at end of file
--- a/src/GuileHandler.h Tue May 07 01:03:21 2019 +0200
+++ b/src/GuileHandler.h Sun May 26 19:46:47 2019 +0200
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <vector>
+#include <map>
#include <iostream>
#include <sstream>
#include <locale>
@@ -39,6 +40,7 @@
#include <relpipe/cli/RelpipeCLIException.h>
#include "Configuration.h"
+#include "GuileException.h"
namespace relpipe {
namespace tr {
@@ -59,6 +61,7 @@
RelationConfiguration* currentRelationConfiguration = nullptr;
vector<AttributeMetadata> currentReaderMetadata;
vector<writer::AttributeMetadata> currentWriterMetadata;
+ std::map<string_t, string_t> currenVariablesMapping;
integer_t currentAttributeIndex = 0;
boolean_t includeCurrentRecord = false;
@@ -70,13 +73,51 @@
});
}
+ void generateVariableMappings() {
+ currenVariablesMapping.clear();
+ for (AttributeMetadata m : currentReaderMetadata) currenVariablesMapping[m.getAttributeName()] = L"";
+ for (writer::AttributeMetadata m : currentWriterMetadata) currenVariablesMapping[m.attributeName] = L"";
+
+ for (std::pair<string_t, string_t> m : currenVariablesMapping) {
+ currenVariablesMapping[m.first] = escapeAwkVariableName(m.first);
+ }
+ }
+
/**
* @param attributeName name from relational pipe
* @return variable name in Guile
*/
string_t a2v(const string_t& attributeName) {
- // TODO: escape spaces and special characters
- return L"$" + attributeName;
+ if (currenVariablesMapping.find(attributeName) != currenVariablesMapping.end()) return currenVariablesMapping[attributeName];
+ else throw GuileException(L"Unable to find value in currenVariablesMapping");
+ }
+
+ template <typename K, typename V> bool containsValue(std::map<K, V> map, V value) { // TODO: common function (Guile, AWK)
+ for (std::pair<K, V> p : map) if (p.second == value) return true;
+ return false;
+ }
+
+ string_t escapeAwkVariableName(const string_t& attributeName, bool addPrefix = true) {
+ std::wregex badCharacters(L"\\s");
+ string_t name = std::regex_replace(attributeName, badCharacters, L"-");
+
+ if (addPrefix) name = L"$" + name; // $ = standard attribute-variable prefix
+
+ if (containsValue(currenVariablesMapping, name)) return escapeAwkVariableName(L"$" + name, false); // $ = different prefix added to distinguish two attributes with ambiguous names
+ else return name;
+
+ }
+
+ void debugVariableMapping(const string_t& relationName) {
+ relationalWriter->startRelation(relationName + L".variableMapping",{
+ {L"attribute", writer::TypeId::STRING},
+ {L"variable", writer::TypeId::STRING},
+ }, true);
+
+ for (std::pair<string_t, string_t> m : currenVariablesMapping) {
+ relationalWriter->writeAttribute(m.first);
+ relationalWriter->writeAttribute(m.second);
+ }
}
SCM toGuileSymbol(const string_t& name) {
@@ -233,6 +274,10 @@
add(currentReaderMetadata, currentWriterMetadata);
}
+ generateVariableMappings();
+
+ if (currentRelationConfiguration->debugVariableMapping) debugVariableMapping(name);
+
if (!currentRelationConfiguration || !currentRelationConfiguration->drop) relationalWriter->startRelation(name, currentWriterMetadata, true);
if (currentRelationConfiguration) {
--- a/src/relpipe-tr-guile.cpp Tue May 07 01:03:21 2019 +0200
+++ b/src/relpipe-tr-guile.cpp Sun May 26 19:46:47 2019 +0200
@@ -64,6 +64,10 @@
fwprintf(stderr, L"Caught Reader exception: %ls\n", e.getMessge().c_str());
fwprintf(stderr, L"Debug: Input stream: eof=%ls, lastRead=%d\n", (cin.eof() ? L"true" : L"false"), cin.gcount());
resultCode = CLI::EXIT_CODE_DATA_ERROR;
+ } catch (GuileException& e) {
+ fwprintf(stderr, L"Caught Guile exception: %ls\n", e.getMessge().c_str());
+ fwprintf(stderr, L"Debug: Input stream: eof=%ls, lastRead=%d\n", (cin.eof() ? L"true" : L"false"), cin.gcount());
+ resultCode = CLI::EXIT_CODE_UNEXPECTED_ERROR;
}
exit(resultCode);