Skip to content

Commit a9477a2

Browse files
committed
Fix more issues from Copilot review
1 parent 27fde8c commit a9477a2

5 files changed

Lines changed: 170 additions & 94 deletions

File tree

include/gul17/DataTree.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ class DataTree
351351
}
352352

353353
// Iterator return types, only works for arrays
354-
using iterator = DataTree*;
355-
using const_iterator = const DataTree*;
354+
using iterator = std::vector<DataTree>::iterator;
355+
using const_iterator = const std::vector<DataTree>::iterator;
356356

357357
// Iterators
358358

@@ -366,7 +366,7 @@ class DataTree
366366
if (!is_array())
367367
throw std::runtime_error("DataTree is not an array");
368368
auto& arr = std::get<Array>(value_);
369-
return arr.data();
369+
return arr.begin();
370370
}
371371

372372
/**
@@ -379,7 +379,7 @@ class DataTree
379379
if (!is_array())
380380
throw std::runtime_error("DataTree is not an array");
381381
auto& arr = std::get<Array>(value_);
382-
return arr.data() + arr.size();
382+
return arr.end();
383383
}
384384

385385
/**
@@ -585,6 +585,8 @@ class DataTree
585585
* - int to double
586586
* - boolean to int
587587
* - int/double/boolean/null to string
588+
*
589+
* It is not possible to convert complex types (array/object) to primitive types.
588590
*/
589591
template<typename T>
590592
T as() const
@@ -615,7 +617,6 @@ class DataTree
615617
if (is_double()) return std::to_string(std::get<double>(value_));
616618
if (is_boolean()) return std::get<bool>(value_) ? "true" : "false";
617619
if (is_null()) return "null";
618-
// TODO: Add conversion logic for other types to string if needed
619620
}
620621
else if constexpr (std::is_same_v<T, DataTree::Array>)
621622
{

include/gul17/data_processors.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ DataTree from_xml_string(const std::string_view& data);
100100
/**
101101
* Serialize a DataTree object to an XML string.
102102
*
103-
* The function serializes the given DataTree object into a XML-formatted string.
103+
* The function serializes the given DataTree object into an XML-formatted string.
104104
* The optional \c indent parameter specifies the number of spaces to use for
105105
* indentation in the output string (default is 0, meaning no pretty-printing).
106106
*
@@ -119,7 +119,7 @@ std::string to_xml_string(const DataTree& value, size_t indent = 0,
119119
const std::string& root_tag_name = "root");
120120

121121
/**
122-
* Parse an YAML string and return the corresponding DataTree representation.
122+
* Parse a YAML string and return the corresponding DataTree representation.
123123
*
124124
* The function parses the input YAML string and constructs a DataTree object
125125
* representing the hierarchical structure and data contained in the YAML.

src/data_processors/json_processor.cc

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ struct JsonDataParser
143143
}
144144
else if (c == '\\')
145145
{
146-
// TODO - Implement full JSON string unescaping
147-
148146
advance();
149147
char esc = current_char();
150148
switch (esc)
@@ -163,44 +161,42 @@ struct JsonDataParser
163161
if (pos_ + 5 < data_.length())
164162
{
165163
auto num = data_.substr(pos_ + 1, 4);
166-
try {
167-
auto ch = std::stoi(std::string(num), nullptr, 16);
168-
if (ch < 0x80)
169-
{
170-
result += static_cast<char>(ch);
171-
}
172-
else if (ch < 0x800)
173-
{
174-
result += static_cast<char>(0xC0 | (ch >> 6));
175-
result += static_cast<char>(0x80 | (ch & 0x3F));
176-
}
177-
else if (ch < 0x10000)
178-
{
179-
result += static_cast<char>(0xE0 | (ch >> 12));
180-
result += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
181-
result += static_cast<char>(0x80 | (ch & 0x3F));
182-
}
183-
else
184-
{
185-
result += static_cast<char>(0xF0 | (ch >> 18));
186-
result += static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
187-
result += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
188-
result += static_cast<char>(0x80 | (ch & 0x3F));
189-
}
190-
pos_ += 4;
164+
unsigned int ch;
165+
try
166+
{
167+
ch = std::stoi(std::string(num), nullptr, 16);
168+
}
169+
catch (...)
170+
{
171+
throw std::runtime_error(gul17::cat("Invalid number format in Unicode escape at position ", pos_));
172+
}
173+
174+
if (ch < 0x80)
175+
{
176+
result += static_cast<char>(ch);
177+
}
178+
else if (ch < 0x800)
179+
{
180+
result += static_cast<char>(0xC0 | (ch >> 6));
181+
result += static_cast<char>(0x80 | (ch & 0x3F));
191182
}
192-
catch (...) {
193-
result += data_[pos_ + 1]; // Invalid number, treat as literal
194-
pos_ += 1;
183+
else if (ch < 0x10000)
184+
{
185+
result += static_cast<char>(0xE0 | (ch >> 12));
186+
result += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
187+
result += static_cast<char>(0x80 | (ch & 0x3F));
195188
}
189+
else
190+
{
191+
// Note: JSON \uXXXX escapes only support BMP (<= 0xFFFF).
192+
throw std::runtime_error(gul17::cat("Invalid Unicode code point at position ", pos_));
193+
}
194+
pos_ += 4;
196195
}
197196
break;
198197

199198
case 'U':
200-
// Unicode escape sequence (e.g., \UXXXXXXXX)
201-
// FIXME - Unicode escape sequences not implemented yet
202-
throw std::runtime_error("Unicode escape sequences not supported");
203-
199+
// TODO - Unicode escape sequence (e.g., \UXXXXXXXX) not implemented yet
204200
default:
205201
throw std::runtime_error(gul17::cat("Invalid escape sequence: ", esc, " at position ", pos_));
206202
}
@@ -267,13 +263,27 @@ struct JsonDataParser
267263
{
268264
advance();
269265
}
270-
double value = std::stod(std::string(data_.substr(start_pos, pos_ - start_pos)));
271-
return DataTree(value);
266+
try
267+
{
268+
double value = std::stod(std::string(data_.substr(start_pos, pos_ - start_pos)));
269+
return DataTree(value);
270+
}
271+
catch (...)
272+
{
273+
throw std::runtime_error(gul17::cat("Invalid number format at position ", start_pos));
274+
}
272275
}
273276
else
274277
{
275-
int value = std::stoi(std::string(data_.substr(start_pos, pos_ - start_pos)));
276-
return DataTree(value);
278+
try
279+
{
280+
int value = std::stoi(std::string(data_.substr(start_pos, pos_ - start_pos)));
281+
return DataTree(value);
282+
}
283+
catch (...)
284+
{
285+
throw std::runtime_error(gul17::cat("Invalid number format at position ", start_pos));
286+
}
277287
}
278288
}
279289

@@ -402,18 +412,20 @@ struct JsonDataSerializer
402412

403413
void serialize_array(const DataTree::Array& arr, size_t indent, size_t current_indent)
404414
{
415+
std::string newline = indent > 0 ? "\n" : ""; // Add newlines if indenting
416+
405417
output_ << "[";
406418
if (!arr.empty())
407419
{
408-
output_ << "\n";
420+
output_ << newline;
409421
for (size_t i = 0; i < arr.size(); ++i)
410422
{
411423
output_ << std::string(current_indent + indent, ' ');
412424
serialize_value(arr[i], indent, current_indent + indent);
413425

414426
if (i < arr.size() - 1)
415427
output_ << ",";
416-
output_ << "\n";
428+
output_ << newline;
417429
}
418430
output_ << std::string(current_indent, ' ');
419431
}
@@ -422,6 +434,8 @@ struct JsonDataSerializer
422434

423435
void serialize_object(const DataTree::Object& obj, size_t indent, size_t current_indent)
424436
{
437+
std::string newline = indent > 0 ? "\n" : ""; // Add newlines if indenting
438+
425439
output_ << "{";
426440
if (!obj.empty())
427441
{
@@ -431,7 +445,7 @@ struct JsonDataSerializer
431445
[](const auto& pair) { return pair.first; });
432446
std::sort(keys.begin(), keys.end());
433447

434-
output_ << "\n";
448+
output_ << newline;
435449
for (size_t i = 0; i < keys.size(); ++i)
436450
{
437451
const auto& key = keys[i];
@@ -443,7 +457,7 @@ struct JsonDataSerializer
443457

444458
if (i < keys.size() - 1)
445459
output_ << ",";
446-
output_ << "\n";
460+
output_ << newline;
447461
}
448462
output_ << std::string(current_indent, ' ');
449463
}

src/data_processors/xml_processor.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ struct XmlDataParser
7171

7272
// Parse attribute name
7373
auto attr_name = parse_attribute_name();
74+
if (attr_name.empty())
75+
{
76+
throw std::runtime_error(gul17::cat("Malformed XML: attribute name cannot be empty at position ", pos_));
77+
}
7478

7579
skip_whitespace();
7680
expect('=');
@@ -127,7 +131,7 @@ struct XmlDataParser
127131

128132
if (closing_tag != tag_name)
129133
{
130-
throw std::runtime_error(gul17::cat("Mismatched tags: ", tag_name, " vs ", closing_tag));
134+
throw std::runtime_error(gul17::cat("Mismatched tags: ", tag_name, " vs ", closing_tag, " at position ", pos_));
131135
}
132136
}
133137

@@ -163,7 +167,7 @@ struct XmlDataParser
163167
auto key = "@" + attr_name;
164168
if (obj.find(key) != obj.end())
165169
{
166-
throw std::runtime_error(gul17::cat("Duplicate attribute name: ", attr_name));
170+
throw std::runtime_error(gul17::cat("Duplicate attribute name: ", attr_name, " at position ", pos_));
167171
}
168172
obj[key] = attr_value;
169173
}
@@ -200,7 +204,7 @@ struct XmlDataParser
200204
}
201205
else
202206
{
203-
throw std::runtime_error(gul17::cat("Multiple text contents in simple element: ", tag_name));
207+
throw std::runtime_error(gul17::cat("Multiple text contents in simple element at position ", pos_));
204208
}
205209
}
206210
else
@@ -247,7 +251,7 @@ struct XmlDataParser
247251
{
248252
char quote_char = current_char();
249253
if (quote_char != '"' && quote_char != '\'')
250-
throw std::runtime_error("Expected quote for attribute value");
254+
throw std::runtime_error(gul17::cat("Expected quote for attribute value at position ", pos_));
251255

252256
advance(); // skip opening quote
253257

0 commit comments

Comments
 (0)