add option: --debug-variable-mapping + different variables for attributes with ambiguous names v_0 v0.12
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 26 May 2019 19:46:47 +0200
branchv_0
changeset 23 6ee7a9e311e9
parent 22 d5e80f15d1f7
child 24 c53161138d19
add option: --debug-variable-mapping + different variables for attributes with ambiguous names
src/CLIParser.h
src/Configuration.h
src/GuileException.h
src/GuileHandler.h
src/relpipe-tr-guile.cpp
--- 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);