Expose getLocationLineAndColumn() as a public API
Allows better use of the offset data returned by getStructuredErrors()
This commit is contained in:
parent
42e892d96e
commit
459332db7c
@ -395,6 +395,14 @@ bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
|
||||
*/
|
||||
JSON_API IStream& operator>>(IStream&, Value&);
|
||||
|
||||
/** Line and column within a document (1-based). */
|
||||
struct DocumentLocation {
|
||||
int line;
|
||||
int column;
|
||||
};
|
||||
|
||||
/** Return the location of a character within a string. */
|
||||
DocumentLocation JSON_API locateInDocument(char const* beginDoc, size_t offset);
|
||||
} // namespace Json
|
||||
|
||||
#pragma pack(pop)
|
||||
|
@ -437,7 +437,7 @@ public:
|
||||
/// \post type() is arrayValue
|
||||
void resize(ArrayIndex newSize);
|
||||
|
||||
//@{
|
||||
///@{
|
||||
/// Access an array element (zero based index). If the array contains less
|
||||
/// than index element, then null value are inserted in the array so that
|
||||
/// its size is index+1.
|
||||
@ -445,15 +445,15 @@ public:
|
||||
/// this from the operator[] which takes a string.)
|
||||
Value& operator[](ArrayIndex index);
|
||||
Value& operator[](int index);
|
||||
//@}
|
||||
///@}
|
||||
|
||||
//@{
|
||||
///@{
|
||||
/// Access an array element (zero based index).
|
||||
/// (You may need to say 'value[0u]' to get your compiler to distinguish
|
||||
/// this from the operator[] which takes a string.)
|
||||
const Value& operator[](ArrayIndex index) const;
|
||||
const Value& operator[](int index) const;
|
||||
//@}
|
||||
///@}
|
||||
|
||||
/// If the array contains at least index+1 elements, returns the element
|
||||
/// value, otherwise returns defaultValue.
|
||||
|
@ -608,7 +608,7 @@ bool Reader::decodeDouble(Token& token, Value& decoded) {
|
||||
value = -std::numeric_limits<double>::infinity();
|
||||
else if (!std::isinf(value))
|
||||
return addError(
|
||||
"'" + String(token.start_, token.end_) + "' is not a number.", token);
|
||||
"'" + String(token.start_, token.end_) + "' is not a number.", token);
|
||||
}
|
||||
decoded = value;
|
||||
return true;
|
||||
@ -767,24 +767,9 @@ Reader::Char Reader::getNextChar() {
|
||||
|
||||
void Reader::getLocationLineAndColumn(Location location, int& line,
|
||||
int& column) const {
|
||||
Location current = begin_;
|
||||
Location lastLineStart = current;
|
||||
line = 0;
|
||||
while (current < location && current != end_) {
|
||||
Char c = *current++;
|
||||
if (c == '\r') {
|
||||
if (*current == '\n')
|
||||
++current;
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
} else if (c == '\n') {
|
||||
lastLineStart = current;
|
||||
++line;
|
||||
}
|
||||
}
|
||||
// column & line start at 1
|
||||
column = int(location - lastLineStart) + 1;
|
||||
++line;
|
||||
auto loc = locateInDocument(document_.data(), location - document_.data());
|
||||
line = loc.line;
|
||||
column = loc.column;
|
||||
}
|
||||
|
||||
String Reader::getLocationLineAndColumn(Location location) const {
|
||||
@ -1660,7 +1645,7 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) {
|
||||
value = -std::numeric_limits<double>::infinity();
|
||||
else if (!std::isinf(value))
|
||||
return addError(
|
||||
"'" + String(token.start_, token.end_) + "' is not a number.", token);
|
||||
"'" + String(token.start_, token.end_) + "' is not a number.", token);
|
||||
}
|
||||
decoded = value;
|
||||
return true;
|
||||
@ -1991,6 +1976,28 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
|
||||
return reader->parse(begin, end, root, errs);
|
||||
}
|
||||
|
||||
DocumentLocation locateInDocument(char const* beginDoc, size_t offset) {
|
||||
int line = 1;
|
||||
int col = 1;
|
||||
const char* last = beginDoc + offset;
|
||||
for (; beginDoc != last; ++beginDoc) {
|
||||
switch (*beginDoc) {
|
||||
case '\r':
|
||||
if (beginDoc + 1 != last && beginDoc[1] == '\n')
|
||||
continue; // consume CRLF as a single token.
|
||||
[[fallthrough]]; // CR without a following LF is same as LF
|
||||
case '\n':
|
||||
col = 1;
|
||||
++line;
|
||||
break;
|
||||
default:
|
||||
++col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {line, col};
|
||||
}
|
||||
|
||||
IStream& operator>>(IStream& sin, Value& root) {
|
||||
CharReaderBuilder b;
|
||||
String errs;
|
||||
|
@ -3903,6 +3903,36 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
|
||||
example.size()));
|
||||
}
|
||||
|
||||
struct LocateInDocumentTest : JsonTest::TestCase {
|
||||
void testLocateInDocument(const char* doc, size_t location, int row,
|
||||
int column) {
|
||||
Json::DocumentLocation actualLocation =
|
||||
Json::locateInDocument(doc, location);
|
||||
JSONTEST_ASSERT_EQUAL(row, actualLocation.line);
|
||||
JSONTEST_ASSERT_EQUAL(column, actualLocation.column);
|
||||
}
|
||||
};
|
||||
|
||||
JSONTEST_FIXTURE_LOCAL(LocateInDocumentTest, locateInDocTest) {
|
||||
const std::string example1 = "line 1\nline 2\r\nline 3\rline 4";
|
||||
const char* example2 = "\nline 2\r\r\n\nline 5\0 \n\rline 7\r\n";
|
||||
struct TestSpec {
|
||||
const char* doc;
|
||||
size_t offset;
|
||||
int line;
|
||||
int column;
|
||||
};
|
||||
const TestSpec specs[] = {
|
||||
{example1.data(), example1.find("line 1"), 1, 1},
|
||||
{example1.data(), example1.find("e 4"), 4, 4},
|
||||
// string terminates at \0, can't use find()
|
||||
{example2, 21, 7, 1},
|
||||
};
|
||||
for (const auto& spec : specs) {
|
||||
testLocateInDocument(spec.doc, spec.offset, spec.line, spec.column);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
JsonTest::Runner runner;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user