From 2b314c87f0079740e179a1bcd10987dde13e5422 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 1 Mar 2021 15:07:59 +0100 Subject: [PATCH] liblangUtil: Extends CharStream to translate from line/column to offset. --- liblangutil/CharStream.cpp | 34 ++++++++++++++++++++ liblangutil/CharStream.h | 11 +++++++ test/liblangutil/CharStream.cpp | 56 +++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index 3b30bad77..131b8147f 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -144,3 +144,37 @@ string CharStream::singleLineSnippet(string const& _sourceCode, SourceLocation c return cut; } + +optional CharStream::translateLineColumnToPosition(int _line, int _column) const +{ + return translateLineColumnToPosition(m_source, _line, _column); +} + +optional CharStream::translateLineColumnToPosition(std::string const& _text, int _line, int _column) +{ + size_t offset = 0; + + while (_line > 0) + { + offset = _text.find('\n', offset); + if (offset == _text.npos) + return nullopt; + offset++; // Skip linefeed. + _line--; + } + + auto const nextEndOfLineOffset = _text.find('\n', offset); + if (nextEndOfLineOffset != string::npos) + { + if (offset + static_cast(_column) < nextEndOfLineOffset) + return static_cast(offset) + _column; // Column fitting current non-last line. + else + return nullopt; // Column-overflow in current non-last line. + } + + if (offset + static_cast(_column) < _text.size()) + return static_cast(offset) + _column; // Column fitting into last line + + return nullopt; +} + diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 761cece31..97fdd33f8 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -51,6 +51,7 @@ #pragma once #include +#include #include #include #include @@ -97,9 +98,19 @@ public: /// Functions that help pretty-printing parse errors /// Do only use in error cases, they are quite expensive. std::string lineAtPosition(int _position) const; + + /// @returns (1-based) line and column that matches to the byte offset @p _position. std::tuple translatePositionToLineColumn(int _position) const; ///@} + /// Translates a line:column to the absolute position. + /// Parameters @p _line and @p _column are 0-based indices. + std::optional translateLineColumnToPosition(int _line, int _column) const; + + /// Translates a line:column to the absolute position for the given input text. + /// Parameters @p _line and @p _column are 0-based indices. + static std::optional translateLineColumnToPosition(std::string const& _text, int _line, int _column); + /// 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..cc3322092 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -48,6 +48,62 @@ BOOST_AUTO_TEST_CASE(test_fail) ); } +#define testLineColumnToPositionTranslationFail(_line, _column, _text) \ + do \ + { \ + auto const source = std::make_shared((_text), "source"); \ + auto const actualPosition = \ + source->translateLineColumnToPosition((_line), (_column)); \ + BOOST_CHECK_EQUAL(actualPosition.value_or(-1), -1); \ + } \ + while (0) + +#define testLineColumnToPositionTranslation(_line, _column, _expectedPosition, _text) \ + do \ + { \ + auto const source = std::make_shared((_text), "source"); \ + auto const actualPosition = \ + source->translateLineColumnToPosition((_line), (_column)); \ + BOOST_CHECK(actualPosition.has_value()); \ + BOOST_CHECK_EQUAL(*actualPosition, (_expectedPosition)); \ + } \ + while (0) + +BOOST_AUTO_TEST_CASE(translateLineColumnToPosition) +{ + testLineColumnToPositionTranslationFail(0, 0, ""); + + // With last line containing no LF + testLineColumnToPositionTranslation(0, 0, 0, "ABC"); + testLineColumnToPositionTranslation(0, 1, 1, "ABC"); + testLineColumnToPositionTranslation(0, 2, 2, "ABC"); + + testLineColumnToPositionTranslation(1, 0, 4, "ABC\nDEF"); + testLineColumnToPositionTranslation(1, 1, 5, "ABC\nDEF"); + testLineColumnToPositionTranslation(1, 2, 6, "ABC\nDEF"); + + // With last line containing LF + testLineColumnToPositionTranslation(0, 0, 0, "ABC\nDEF\n"); + testLineColumnToPositionTranslation(0, 1, 1, "ABC\nDEF\n"); + testLineColumnToPositionTranslation(0, 2, 2, "ABC\nDEF\n"); + + testLineColumnToPositionTranslation(1, 0, 4, "ABC\nDEF\n"); + testLineColumnToPositionTranslation(1, 1, 5, "ABC\nDEF\n"); + testLineColumnToPositionTranslation(1, 2, 6, "ABC\nDEF\n"); + + testLineColumnToPositionTranslation(2, 0, 8, "ABC\nDEF\nGHI\n"); + testLineColumnToPositionTranslation(2, 1, 9, "ABC\nDEF\nGHI\n"); + testLineColumnToPositionTranslation(2, 2, 10, "ABC\nDEF\nGHI\n"); + + // Column overflows. + testLineColumnToPositionTranslationFail(0, 3, "ABC\nDEF\n"); + testLineColumnToPositionTranslationFail(1, 3, "ABC\nDEF\n"); + testLineColumnToPositionTranslationFail(2, 3, "ABC\nDEF\nGHI\n"); + + // Line overflow. + testLineColumnToPositionTranslationFail(3, 0, "ABC\nDEF\nGHI\n"); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces