--- 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 <locale>
#include <codecvt>
#include <regex>
+#include <assert.h>
#include <libguile.h>
#include <relpipe/reader/typedefs.h>
#include <relpipe/reader/TypeId.h>
-#include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
+#include <relpipe/reader/handlers/RelationalReaderValueHandler.h>
#include <relpipe/reader/handlers/AttributeMetadata.h>
#include <relpipe/writer/Factory.h>
@@ -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<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+ std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings or use always UTF-8 between C++ and Guile
shared_ptr<writer::RelationalWriter> relationalWriter;
wregex relationNameRegEx;
- vector<AttributeMetadata> currentMetadata;
+ vector<AttributeMetadata> currentReaderMetadata;
+ vector<writer::AttributeMetadata> currentWriterMetadata;
vector<string_t> 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<const boolean_t*> (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<const integer_t*> (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<const string_t*> (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<string_t>& 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 <relationNameRegExp> <whereConditionGuileCode>", cli::CLI::EXIT_CODE_UNKNOWN_COMMAND);
}
}
void startRelation(string_t name, vector<AttributeMetadata> 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<writer::AttributeMetadata> 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);
}
}