# HG changeset patch # User František Kučera # Date 1558892807 -7200 # Node ID 6ee7a9e311e903a8651fc819bc3676744daf7285 # Parent d5e80f15d1f7946ac74733c9f6aa2ec699f18328 add option: --debug-variable-mapping + different variables for attributes with ambiguous names diff -r d5e80f15d1f7 -r 6ee7a9e311e9 src/CLIParser.h --- 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"; diff -r d5e80f15d1f7 -r 6ee7a9e311e9 src/Configuration.h --- 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. diff -r d5e80f15d1f7 -r 6ee7a9e311e9 src/GuileException.h --- /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 . + */ +#pragma once + +#include + +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 diff -r d5e80f15d1f7 -r 6ee7a9e311e9 src/GuileHandler.h --- 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 #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include "Configuration.h" +#include "GuileException.h" namespace relpipe { namespace tr { @@ -59,6 +61,7 @@ RelationConfiguration* currentRelationConfiguration = nullptr; vector currentReaderMetadata; vector currentWriterMetadata; + std::map 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 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 bool containsValue(std::map map, V value) { // TODO: common function (Guile, AWK) + for (std::pair 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 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) { diff -r d5e80f15d1f7 -r 6ee7a9e311e9 src/relpipe-tr-guile.cpp --- 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);