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 |