# HG changeset patch # User František Kučera # Date 1548608223 -3600 # Node ID 17bb45570099047c46ef4c7d4cca29941e9d2921 # Parent 9b4fe4bc5f0fe0d1b5635c9f8ca8146daf1c9b83 ValueHandler instead of StringHandler interface; Guile now allows filtering and record modifications diff -r 9b4fe4bc5f0f -r 17bb45570099 src/GuileHandler.h --- a/src/GuileHandler.h Tue Jan 22 16:12:32 2019 +0100 +++ b/src/GuileHandler.h Sun Jan 27 17:57:03 2019 +0100 @@ -25,12 +25,13 @@ #include #include #include +#include #include #include #include -#include +#include #include #include @@ -46,59 +47,100 @@ using namespace relpipe::reader; using namespace relpipe::reader::handlers; -class GuileHandler : public RelationalReaderStringHadler { +class GuileHandler : public RelationalReaderValueHadler { private: - std::wstring_convert> convertor; // TODO: support also other encodings. + std::wstring_convert> convertor; // TODO: support also other encodings or use always UTF-8 between C++ and Guile shared_ptr relationalWriter; wregex relationNameRegEx; - vector currentMetadata; + vector currentReaderMetadata; + vector currentWriterMetadata; vector currentRecord; integer_t currentAttributeIndex = 0; boolean_t includeCurrentRecord = false; boolean_t filterCurrentRelation = false; - string_t guileCode; + string_t guileCodeWhereCondition; - SCM toGuileSymbol(const string_t& name) { - return scm_string_to_symbol(scm_from_locale_string(convertor.to_bytes(L"$" + name).c_str())); + /** + * @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; } - SCM toGuileString(const string_t& value) { - return scm_from_locale_string(convertor.to_bytes(value).c_str()); + SCM toGuileSymbol(const string_t& name) { + return scm_string_to_symbol(scm_from_locale_string(convertor.to_bytes(name).c_str())); } - SCM toGuileInteger(const string_t& value) { - return scm_from_uint64(stoul(value)); + SCM evalGuileCode(const string_t& value) { + return scm_eval_string(toGuileValue(&value, typeid (string_t), TypeId::STRING)); } - SCM toGuileBoolean(const string_t& value) { - return value == L"true" ? SCM_BOOL_T : SCM_BOOL_F; - } - - void defineGuileVariable(const string_t& name, TypeId type, const string_t& value) { - // TODO: RelationalReaderValueHadler? + SCM toGuileValue(const void* value, const std::type_info& typeInfo, TypeId type) { switch (type) { case TypeId::BOOLEAN: - scm_define(toGuileSymbol(name), toGuileBoolean(value)); - break; + { + assert(typeInfo == typeid (boolean_t)); + auto* typedValue = static_cast (value); + return *typedValue ? SCM_BOOL_T : SCM_BOOL_F; + } case TypeId::INTEGER: - scm_define(toGuileSymbol(name), toGuileInteger(value)); - break; + { + assert(typeInfo == typeid (integer_t)); + auto* typedValue = static_cast (value); + return scm_from_uint64(*typedValue); + } case TypeId::STRING: - scm_define(toGuileSymbol(name), toGuileString(value)); - break; + { + assert(typeInfo == typeid (string_t)); + auto* typedValue = static_cast (value); + return scm_from_locale_string(convertor.to_bytes(*typedValue).c_str()); + } default: - throw cli::RelpipeCLIException(L"Unsupported type in defineGuileVariable()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); + throw cli::RelpipeCLIException(L"Unsupported type in toGuileValue()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); } } - void undefineGuileVariable(const string_t& name, TypeId type, const string_t& value) { + void defineGuileVariable(const string_t& name, const void* value, const std::type_info& typeInfo, TypeId type) { + scm_define(toGuileSymbol(name), toGuileValue(value, typeInfo, type)); + } + + void undefineGuileVariable(const string_t& name) { scm_define(toGuileSymbol(name), scm_make_undefined_variable()); // undefined != (define n) // TODO: or use: scm_variable_unset_x() ? } + void writeGuileValueToAttribute(const writer::AttributeMetadata& attribute) { + string_t variableName = a2v(attribute.attributeName); + SCM guileValue = scm_eval_string(toGuileValue(&variableName, typeid (variableName), TypeId::STRING)); + + switch (attribute.typeId) { + case writer::TypeId::BOOLEAN: + { + boolean_t value = scm_to_bool(guileValue); + return relationalWriter->writeAttribute(&value, typeid (value)); + } + case writer::TypeId::INTEGER: + { + integer_t value = scm_to_uint64(guileValue); + return relationalWriter->writeAttribute(&value, typeid (value)); + } + case writer::TypeId::STRING: + { + char* ch = scm_to_locale_string(guileValue); + string_t value = convertor.from_bytes(ch); + free(ch); + return relationalWriter->writeAttribute(&value, typeid (value)); + } + default: + throw cli::RelpipeCLIException(L"Unsupported type in writeGuileValueToAttribute()", cli::CLI::EXIT_CODE_UNEXPECTED_ERROR); + } + } + public: GuileHandler(ostream& output, const vector& arguments) { @@ -107,44 +149,44 @@ // TODO: options and parser if (arguments.size() == 2) { relationNameRegEx = wregex(arguments[0]); - guileCode = arguments[1]; + guileCodeWhereCondition = arguments[1]; } else { throw cli::RelpipeCLIException(L"Usage: relpipe-tr-guile ", cli::CLI::EXIT_CODE_UNKNOWN_COMMAND); } } void startRelation(string_t name, vector attributes) override { - currentMetadata = attributes; + for (auto attribute : currentReaderMetadata) undefineGuileVariable(attribute.getAttributeName()); + currentReaderMetadata = attributes; // TODO: move to a reusable method (or use same metadata on both reader and writer side?) - vector writerMetadata; + // TODO: allow structural changes during transformation + // TODO: clear Guile variables for attributes from previous relation + currentWriterMetadata.clear(); for (AttributeMetadata readerMetadata : attributes) { - - writerMetadata.push_back({readerMetadata.getAttributeName(), relationalWriter->toTypeId(readerMetadata.getTypeName())}); + currentWriterMetadata.push_back({readerMetadata.getAttributeName(), relationalWriter->toTypeId(readerMetadata.getTypeName())}); } currentRecord.resize(attributes.size()); filterCurrentRelation = regex_match(name, relationNameRegEx); - relationalWriter->startRelation(name, writerMetadata, true); + relationalWriter->startRelation(name, currentWriterMetadata, true); } - void attribute(const string_t& value) override { + void attribute(const void* value, const std::type_info& type) override { if (filterCurrentRelation) { - currentRecord[currentAttributeIndex] = value; - defineGuileVariable(currentMetadata[currentAttributeIndex].getAttributeName(), currentMetadata[currentAttributeIndex].getTypeId(), value); + defineGuileVariable(a2v(currentReaderMetadata[currentAttributeIndex].getAttributeName()), value, type, currentReaderMetadata[currentAttributeIndex].getTypeId()); + currentAttributeIndex++; - if (currentAttributeIndex > 0 && currentAttributeIndex % currentMetadata.size() == 0) { - // TODO: support also updates in Guile - includeCurrentRecord = scm_to_bool(scm_eval_string(toGuileString(guileCode))); - if (includeCurrentRecord) for (string_t v : currentRecord) relationalWriter->writeAttribute(v); + if (currentAttributeIndex > 0 && currentAttributeIndex % currentReaderMetadata.size() == 0) { + includeCurrentRecord = scm_to_bool(evalGuileCode(guileCodeWhereCondition)); + if (includeCurrentRecord) for (auto attribute : currentWriterMetadata) writeGuileValueToAttribute(attribute); includeCurrentRecord = false; } - currentAttributeIndex = currentAttributeIndex % currentMetadata.size(); + currentAttributeIndex = currentAttributeIndex % currentReaderMetadata.size(); } else { - - relationalWriter->writeAttribute(value); + relationalWriter->writeAttribute(value, type); } }