diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index 96a6aebe2..6ecd85bfe 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -144,3 +144,32 @@ string CharStream::singleLineSnippet(string const& _sourceCode, SourceLocation c return cut; } + +optional CharStream::translateLineColumnToPosition(LineColumn const& _lineColumn) const +{ + return translateLineColumnToPosition(m_source, _lineColumn); +} + +optional CharStream::translateLineColumnToPosition(std::string const& _text, LineColumn const& _input) +{ + if (_input.line < 0) + return nullopt; + + size_t offset = 0; + for (int i = 0; i < _input.line; i++) + { + offset = _text.find('\n', offset); + if (offset == _text.npos) + return nullopt; + offset++; // Skip linefeed. + } + + size_t endOfLine = _text.find('\n', offset); + if (endOfLine == string::npos) + endOfLine = _text.size(); + + if (offset + static_cast(_input.column) > endOfLine) + return nullopt; + return offset + static_cast(_input.column); +} + diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 2e5d79d93..08f86129b 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -51,6 +51,7 @@ #pragma once #include +#include #include #include #include @@ -101,6 +102,12 @@ public: LineColumn translatePositionToLineColumn(int _position) const; ///@} + /// Translates a line:column to the absolute position. + std::optional translateLineColumnToPosition(LineColumn const& _lineColumn) const; + + /// Translates a line:column to the absolute position for the given input text. + static std::optional translateLineColumnToPosition(std::string const& _text, LineColumn const& _input); + /// Tests whether or not given octet sequence is present at the current position in stream. /// @returns true if the sequence could be found, false otherwise. bool prefixMatch(std::string_view _sequence) diff --git a/test/liblangutil/CharStream.cpp b/test/liblangutil/CharStream.cpp index 9321b14e0..766a70098 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -28,6 +28,33 @@ #include +using namespace std; +using namespace solidity::test; + +namespace boost::test_tools::tt_detail +{ + +template<> +struct print_log_value> +{ + void operator()(std::ostream& _out, std::optional const& _value) const + { + _out << (_value ? to_string(*_value) : "[nullopt]"); + } +}; + +template<> +struct print_log_value +{ + void operator()(std::ostream& _out, nullopt_t const&) const + { + _out << "[nullopt]"; + } +}; + +} // namespace boost::test_tools::tt_detail + + namespace solidity::langutil::test { @@ -48,6 +75,62 @@ BOOST_AUTO_TEST_CASE(test_fail) ); } +namespace +{ +std::optional toPosition(int _line, int _column, string const& _text) +{ + return CharStream{_text, "source"}.translateLineColumnToPosition( + LineColumn{_line, _column} + ); +} + +} + +BOOST_AUTO_TEST_CASE(translateLineColumnToPosition) +{ + BOOST_CHECK_EQUAL(toPosition(-1, 0, "ABC"), nullopt); + BOOST_CHECK_EQUAL(toPosition(0, -1, "ABC"), nullopt); + + BOOST_CHECK_EQUAL(toPosition(0, 0, ""), 0); + BOOST_CHECK_EQUAL(toPosition(1, 0, ""), nullopt); + BOOST_CHECK_EQUAL(toPosition(0, 1, ""), nullopt); + + // With last line containing no LF + BOOST_CHECK_EQUAL(toPosition(0, 0, "ABC"), 0); + BOOST_CHECK_EQUAL(toPosition(0, 1, "ABC"), 1); + BOOST_CHECK_EQUAL(toPosition(0, 2, "ABC"), 2); + BOOST_CHECK_EQUAL(toPosition(0, 3, "ABC"), 3); + BOOST_CHECK_EQUAL(toPosition(0, 4, "ABC"), nullopt); + BOOST_CHECK_EQUAL(toPosition(1, 0, "ABC"), nullopt); + + BOOST_CHECK_EQUAL(toPosition(0, 3, "ABC\nDEF"), 3); + BOOST_CHECK_EQUAL(toPosition(0, 4, "ABC\nDEF"), nullopt); + BOOST_CHECK_EQUAL(toPosition(1, 0, "ABC\nDEF"), 4); + BOOST_CHECK_EQUAL(toPosition(1, 1, "ABC\nDEF"), 5); + BOOST_CHECK_EQUAL(toPosition(1, 2, "ABC\nDEF"), 6); + BOOST_CHECK_EQUAL(toPosition(1, 3, "ABC\nDEF"), 7); + BOOST_CHECK_EQUAL(toPosition(1, 4, "ABC\nDEF"), nullopt); + BOOST_CHECK_EQUAL(toPosition(2, 0, "ABC\nDEF"), nullopt); + BOOST_CHECK_EQUAL(toPosition(2, 1, "ABC\nDEF"), nullopt); + + // With last line containing LF + BOOST_CHECK_EQUAL(toPosition(0, 0, "ABC\nDEF\n"), 0); + BOOST_CHECK_EQUAL(toPosition(0, 1, "ABC\nDEF\n"), 1); + BOOST_CHECK_EQUAL(toPosition(0, 2, "ABC\nDEF\n"), 2); + + BOOST_CHECK_EQUAL(toPosition(1, 0, "ABC\nDEF\n"), 4); + BOOST_CHECK_EQUAL(toPosition(1, 1, "ABC\nDEF\n"), 5); + BOOST_CHECK_EQUAL(toPosition(1, 2, "ABC\nDEF\n"), 6); + BOOST_CHECK_EQUAL(toPosition(1, 3, "ABC\nDEF\n"), 7); + BOOST_CHECK_EQUAL(toPosition(1, 4, "ABC\nDEF\n"), nullopt); + BOOST_CHECK_EQUAL(toPosition(2, 0, "ABC\nDEF\n"), 8); + BOOST_CHECK_EQUAL(toPosition(2, 1, "ABC\nDEF\n"), nullopt); + + BOOST_CHECK_EQUAL(toPosition(2, 0, "ABC\nDEF\nGHI\n"), 8); + BOOST_CHECK_EQUAL(toPosition(2, 1, "ABC\nDEF\nGHI\n"), 9); + BOOST_CHECK_EQUAL(toPosition(2, 2, "ABC\nDEF\nGHI\n"), 10); +} + BOOST_AUTO_TEST_SUITE_END() -} // end namespaces +}