src/GuileHandler.h
branchv_0
changeset 5 17bb45570099
parent 4 9b4fe4bc5f0f
child 6 4062b8436838
equal deleted inserted replaced
4:9b4fe4bc5f0f 5:17bb45570099
    23 #include <iostream>
    23 #include <iostream>
    24 #include <sstream>
    24 #include <sstream>
    25 #include <locale>
    25 #include <locale>
    26 #include <codecvt>
    26 #include <codecvt>
    27 #include <regex>
    27 #include <regex>
       
    28 #include <assert.h>
    28 
    29 
    29 #include <libguile.h>
    30 #include <libguile.h>
    30 
    31 
    31 #include <relpipe/reader/typedefs.h>
    32 #include <relpipe/reader/typedefs.h>
    32 #include <relpipe/reader/TypeId.h>
    33 #include <relpipe/reader/TypeId.h>
    33 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
    34 #include <relpipe/reader/handlers/RelationalReaderValueHandler.h>
    34 #include <relpipe/reader/handlers/AttributeMetadata.h>
    35 #include <relpipe/reader/handlers/AttributeMetadata.h>
    35 
    36 
    36 #include <relpipe/writer/Factory.h>
    37 #include <relpipe/writer/Factory.h>
    37 
    38 
    38 #include <relpipe/cli/RelpipeCLIException.h>
    39 #include <relpipe/cli/RelpipeCLIException.h>
    44 using namespace std;
    45 using namespace std;
    45 using namespace relpipe;
    46 using namespace relpipe;
    46 using namespace relpipe::reader;
    47 using namespace relpipe::reader;
    47 using namespace relpipe::reader::handlers;
    48 using namespace relpipe::reader::handlers;
    48 
    49 
    49 class GuileHandler : public RelationalReaderStringHadler {
    50 class GuileHandler : public RelationalReaderValueHadler {
    50 private:
    51 private:
    51 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
    52 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings or use always UTF-8 between C++ and Guile
    52 
    53 
    53 	shared_ptr<writer::RelationalWriter> relationalWriter;
    54 	shared_ptr<writer::RelationalWriter> relationalWriter;
    54 
    55 
    55 	wregex relationNameRegEx;
    56 	wregex relationNameRegEx;
    56 
    57 
    57 	vector<AttributeMetadata> currentMetadata;
    58 	vector<AttributeMetadata> currentReaderMetadata;
       
    59 	vector<writer::AttributeMetadata> currentWriterMetadata;
    58 	vector<string_t> currentRecord;
    60 	vector<string_t> currentRecord;
    59 	integer_t currentAttributeIndex = 0;
    61 	integer_t currentAttributeIndex = 0;
    60 	boolean_t includeCurrentRecord = false;
    62 	boolean_t includeCurrentRecord = false;
    61 	boolean_t filterCurrentRelation = false;
    63 	boolean_t filterCurrentRelation = false;
    62 	string_t guileCode;
    64 	string_t guileCodeWhereCondition;
       
    65 
       
    66 	/**
       
    67 	 * @param attributeName name from relational pipe
       
    68 	 * @return variable name in Guile
       
    69 	 */
       
    70 	string_t a2v(const string_t& attributeName) {
       
    71 		// TODO: escape spaces and special characters
       
    72 		return L"$" + attributeName;
       
    73 	}
    63 
    74 
    64 	SCM toGuileSymbol(const string_t& name) {
    75 	SCM toGuileSymbol(const string_t& name) {
    65 		return scm_string_to_symbol(scm_from_locale_string(convertor.to_bytes(L"$" + name).c_str()));
    76 		return scm_string_to_symbol(scm_from_locale_string(convertor.to_bytes(name).c_str()));
    66 	}
    77 	}
    67 
    78 
    68 	SCM toGuileString(const string_t& value) {
    79 	SCM evalGuileCode(const string_t& value) {
    69 		return scm_from_locale_string(convertor.to_bytes(value).c_str());
    80 		return scm_eval_string(toGuileValue(&value, typeid (string_t), TypeId::STRING));
    70 	}
    81 	}
    71 
    82 
    72 	SCM toGuileInteger(const string_t& value) {
    83 	SCM toGuileValue(const void* value, const std::type_info& typeInfo, TypeId type) {
    73 		return scm_from_uint64(stoul(value));
       
    74 	}
       
    75 
       
    76 	SCM toGuileBoolean(const string_t& value) {
       
    77 		return value == L"true" ? SCM_BOOL_T : SCM_BOOL_F;
       
    78 	}
       
    79 
       
    80 	void defineGuileVariable(const string_t& name, TypeId type, const string_t& value) {
       
    81 		// TODO: RelationalReaderValueHadler?
       
    82 		switch (type) {
    84 		switch (type) {
    83 			case TypeId::BOOLEAN:
    85 			case TypeId::BOOLEAN:
    84 				scm_define(toGuileSymbol(name), toGuileBoolean(value));
    86 			{
    85 				break;
    87 				assert(typeInfo == typeid (boolean_t));
       
    88 				auto* typedValue = static_cast<const boolean_t*> (value);
       
    89 				return *typedValue ? SCM_BOOL_T : SCM_BOOL_F;
       
    90 			}
    86 			case TypeId::INTEGER:
    91 			case TypeId::INTEGER:
    87 				scm_define(toGuileSymbol(name), toGuileInteger(value));
    92 			{
    88 				break;
    93 				assert(typeInfo == typeid (integer_t));
       
    94 				auto* typedValue = static_cast<const integer_t*> (value);
       
    95 				return scm_from_uint64(*typedValue);
       
    96 			}
    89 			case TypeId::STRING:
    97 			case TypeId::STRING:
    90 				scm_define(toGuileSymbol(name), toGuileString(value));
    98 			{
    91 				break;
    99 				assert(typeInfo == typeid (string_t));
       
   100 				auto* typedValue = static_cast<const string_t*> (value);
       
   101 				return scm_from_locale_string(convertor.to_bytes(*typedValue).c_str());
       
   102 			}
    92 			default:
   103 			default:
    93 				throw cli::RelpipeCLIException(L"Unsupported type in defineGuileVariable()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR);
   104 				throw cli::RelpipeCLIException(L"Unsupported type in toGuileValue()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR);
    94 		}
   105 		}
    95 	}
   106 	}
    96 
   107 
    97 	void undefineGuileVariable(const string_t& name, TypeId type, const string_t& value) {
   108 	void defineGuileVariable(const string_t& name, const void* value, const std::type_info& typeInfo, TypeId type) {
       
   109 		scm_define(toGuileSymbol(name), toGuileValue(value, typeInfo, type));
       
   110 	}
       
   111 
       
   112 	void undefineGuileVariable(const string_t& name) {
    98 		scm_define(toGuileSymbol(name), scm_make_undefined_variable()); // undefined != (define n)
   113 		scm_define(toGuileSymbol(name), scm_make_undefined_variable()); // undefined != (define n)
    99 		// TODO: or use: scm_variable_unset_x() ?
   114 		// TODO: or use: scm_variable_unset_x() ?
       
   115 	}
       
   116 
       
   117 	void writeGuileValueToAttribute(const writer::AttributeMetadata& attribute) {
       
   118 		string_t variableName = a2v(attribute.attributeName);
       
   119 		SCM guileValue = scm_eval_string(toGuileValue(&variableName, typeid (variableName), TypeId::STRING));
       
   120 
       
   121 		switch (attribute.typeId) {
       
   122 			case writer::TypeId::BOOLEAN:
       
   123 			{
       
   124 				boolean_t value = scm_to_bool(guileValue);
       
   125 				return relationalWriter->writeAttribute(&value, typeid (value));
       
   126 			}
       
   127 			case writer::TypeId::INTEGER:
       
   128 			{
       
   129 				integer_t value = scm_to_uint64(guileValue);
       
   130 				return relationalWriter->writeAttribute(&value, typeid (value));
       
   131 			}
       
   132 			case writer::TypeId::STRING:
       
   133 			{
       
   134 				char* ch = scm_to_locale_string(guileValue);
       
   135 				string_t value = convertor.from_bytes(ch);
       
   136 				free(ch);
       
   137 				return relationalWriter->writeAttribute(&value, typeid (value));
       
   138 			}
       
   139 			default:
       
   140 				throw cli::RelpipeCLIException(L"Unsupported type in writeGuileValueToAttribute()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR);
       
   141 		}
   100 	}
   142 	}
   101 
   143 
   102 public:
   144 public:
   103 
   145 
   104 	GuileHandler(ostream& output, const vector<string_t>& arguments) {
   146 	GuileHandler(ostream& output, const vector<string_t>& arguments) {
   105 		relationalWriter.reset(writer::Factory::create(output));
   147 		relationalWriter.reset(writer::Factory::create(output));
   106 
   148 
   107 		// TODO: options and parser
   149 		// TODO: options and parser
   108 		if (arguments.size() == 2) {
   150 		if (arguments.size() == 2) {
   109 			relationNameRegEx = wregex(arguments[0]);
   151 			relationNameRegEx = wregex(arguments[0]);
   110 			guileCode = arguments[1];
   152 			guileCodeWhereCondition = arguments[1];
   111 		} else {
   153 		} else {
   112 			throw cli::RelpipeCLIException(L"Usage: relpipe-tr-guile <relationNameRegExp> <whereConditionGuileCode>", cli::CLI::EXIT_CODE_UNKNOWN_COMMAND);
   154 			throw cli::RelpipeCLIException(L"Usage: relpipe-tr-guile <relationNameRegExp> <whereConditionGuileCode>", cli::CLI::EXIT_CODE_UNKNOWN_COMMAND);
   113 		}
   155 		}
   114 	}
   156 	}
   115 
   157 
   116 	void startRelation(string_t name, vector<AttributeMetadata> attributes) override {
   158 	void startRelation(string_t name, vector<AttributeMetadata> attributes) override {
   117 		currentMetadata = attributes;
   159 		for (auto attribute : currentReaderMetadata) undefineGuileVariable(attribute.getAttributeName());
       
   160 		currentReaderMetadata = attributes;
   118 		// TODO: move to a reusable method (or use same metadata on both reader and writer side?)
   161 		// TODO: move to a reusable method (or use same metadata on both reader and writer side?)
   119 		vector<writer::AttributeMetadata> writerMetadata;
   162 		// TODO: allow structural changes during transformation
       
   163 		// TODO: clear Guile variables for attributes from previous relation
       
   164 		currentWriterMetadata.clear();
   120 		for (AttributeMetadata readerMetadata : attributes) {
   165 		for (AttributeMetadata readerMetadata : attributes) {
   121 
   166 			currentWriterMetadata.push_back({readerMetadata.getAttributeName(), relationalWriter->toTypeId(readerMetadata.getTypeName())});
   122 			writerMetadata.push_back({readerMetadata.getAttributeName(), relationalWriter->toTypeId(readerMetadata.getTypeName())});
       
   123 		}
   167 		}
   124 
   168 
   125 		currentRecord.resize(attributes.size());
   169 		currentRecord.resize(attributes.size());
   126 		filterCurrentRelation = regex_match(name, relationNameRegEx);
   170 		filterCurrentRelation = regex_match(name, relationNameRegEx);
   127 
   171 
   128 		relationalWriter->startRelation(name, writerMetadata, true);
   172 		relationalWriter->startRelation(name, currentWriterMetadata, true);
   129 	}
   173 	}
   130 
   174 
   131 	void attribute(const string_t& value) override {
   175 	void attribute(const void* value, const std::type_info& type) override {
   132 		if (filterCurrentRelation) {
   176 		if (filterCurrentRelation) {
   133 			currentRecord[currentAttributeIndex] = value;
   177 			defineGuileVariable(a2v(currentReaderMetadata[currentAttributeIndex].getAttributeName()), value, type, currentReaderMetadata[currentAttributeIndex].getTypeId());
   134 			defineGuileVariable(currentMetadata[currentAttributeIndex].getAttributeName(), currentMetadata[currentAttributeIndex].getTypeId(), value);
   178 
   135 			currentAttributeIndex++;
   179 			currentAttributeIndex++;
   136 
   180 
   137 			if (currentAttributeIndex > 0 && currentAttributeIndex % currentMetadata.size() == 0) {
   181 			if (currentAttributeIndex > 0 && currentAttributeIndex % currentReaderMetadata.size() == 0) {
   138 				// TODO: support also updates in Guile
   182 				includeCurrentRecord = scm_to_bool(evalGuileCode(guileCodeWhereCondition));
   139 				includeCurrentRecord = scm_to_bool(scm_eval_string(toGuileString(guileCode)));
   183 				if (includeCurrentRecord) for (auto attribute : currentWriterMetadata) writeGuileValueToAttribute(attribute);
   140 				if (includeCurrentRecord) for (string_t v : currentRecord) relationalWriter->writeAttribute(v);
       
   141 				includeCurrentRecord = false;
   184 				includeCurrentRecord = false;
   142 			}
   185 			}
   143 
   186 
   144 			currentAttributeIndex = currentAttributeIndex % currentMetadata.size();
   187 			currentAttributeIndex = currentAttributeIndex % currentReaderMetadata.size();
   145 		} else {
   188 		} else {
   146 
   189 			relationalWriter->writeAttribute(value, type);
   147 			relationalWriter->writeAttribute(value);
       
   148 		}
   190 		}
   149 	}
   191 	}
   150 
   192 
   151 	void endOfPipe() {
   193 	void endOfPipe() {
   152 
   194