forked from lix-project/lix
match line endings used by parser and error reports
the parser treats a plain \r as a newline, error reports do not. this can lead to interesting divergences if anything makes use of this feature, with error reports pointing to wrong locations in the input (or even outside the input altogether).
This commit is contained in:
parent
f7e3a0b19f
commit
72396e0038
|
@ -29,32 +29,17 @@ std::optional<LinesOfCode> Pos::getCodeLines() const
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if (auto source = getSource()) {
|
if (auto source = getSource()) {
|
||||||
|
LinesIterator lines(*source), end;
|
||||||
std::istringstream iss(*source);
|
|
||||||
// count the newlines.
|
|
||||||
int count = 0;
|
|
||||||
std::string curLine;
|
|
||||||
int pl = line - 1;
|
|
||||||
|
|
||||||
LinesOfCode loc;
|
LinesOfCode loc;
|
||||||
|
|
||||||
do {
|
if (line > 1)
|
||||||
std::getline(iss, curLine);
|
std::advance(lines, line - 2);
|
||||||
++count;
|
if (lines != end && line > 1)
|
||||||
if (count < pl)
|
loc.prevLineOfCode = *lines++;
|
||||||
;
|
if (lines != end)
|
||||||
else if (count == pl) {
|
loc.errLineOfCode = *lines++;
|
||||||
loc.prevLineOfCode = curLine;
|
if (lines != end)
|
||||||
} else if (count == pl + 1) {
|
loc.nextLineOfCode = *lines++;
|
||||||
loc.errLineOfCode = curLine;
|
|
||||||
} else if (count == pl + 2) {
|
|
||||||
loc.nextLineOfCode = curLine;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!iss.good())
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
@ -109,4 +94,26 @@ std::ostream & operator<<(std::ostream & str, const Pos & pos)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pos::LinesIterator::bump(bool atFirst)
|
||||||
|
{
|
||||||
|
if (!atFirst) {
|
||||||
|
pastEnd = input.empty();
|
||||||
|
if (!input.empty() && input[0] == '\r')
|
||||||
|
input.remove_prefix(1);
|
||||||
|
if (!input.empty() && input[0] == '\n')
|
||||||
|
input.remove_prefix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nix line endings are not only \n as eg std::getline assumes, but also
|
||||||
|
// \r\n **and \r alone**. not treating them all the same causes error
|
||||||
|
// reports to not match with line numbers as the parser expects them.
|
||||||
|
auto eol = input.find_first_of("\r\n");
|
||||||
|
|
||||||
|
if (eol > input.size())
|
||||||
|
eol = input.size();
|
||||||
|
|
||||||
|
curLine = input.substr(0, eol);
|
||||||
|
input.remove_prefix(eol);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,47 @@ struct Pos
|
||||||
bool operator==(const Pos & rhs) const = default;
|
bool operator==(const Pos & rhs) const = default;
|
||||||
bool operator!=(const Pos & rhs) const = default;
|
bool operator!=(const Pos & rhs) const = default;
|
||||||
bool operator<(const Pos & rhs) const;
|
bool operator<(const Pos & rhs) const;
|
||||||
|
|
||||||
|
struct LinesIterator {
|
||||||
|
using difference_type = size_t;
|
||||||
|
using value_type = std::string_view;
|
||||||
|
using reference = const std::string_view &;
|
||||||
|
using pointer = const std::string_view *;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
LinesIterator(): pastEnd(true) {}
|
||||||
|
explicit LinesIterator(std::string_view input): input(input) {
|
||||||
|
bump(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinesIterator & operator++() {
|
||||||
|
bump(false);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
LinesIterator operator++(int) {
|
||||||
|
auto result = *this;
|
||||||
|
++*this;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const { return curLine; }
|
||||||
|
pointer operator->() const { return &curLine; }
|
||||||
|
|
||||||
|
bool operator!=(const LinesIterator & other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
bool operator==(const LinesIterator & other) const {
|
||||||
|
return (pastEnd && other.pastEnd)
|
||||||
|
|| (std::forward_as_tuple(input.size(), input.data())
|
||||||
|
== std::forward_as_tuple(other.input.size(), other.input.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string_view input, curLine;
|
||||||
|
bool pastEnd = false;
|
||||||
|
|
||||||
|
void bump(bool atFirst);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & str, const Pos & pos);
|
std::ostream & operator<<(std::ostream & str, const Pos & pos);
|
||||||
|
|
6
tests/functional/lang/eval-fail-eol-1.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-1.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
error: undefined variable 'invalid'
|
||||||
|
at /pwd/lang/eval-fail-eol-1.nix:2:1:
|
||||||
|
1| # foo
|
||||||
|
2| invalid
|
||||||
|
| ^
|
||||||
|
3| # bar
|
3
tests/functional/lang/eval-fail-eol-1.nix
Normal file
3
tests/functional/lang/eval-fail-eol-1.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# foo
|
||||||
|
invalid
|
||||||
|
# bar
|
6
tests/functional/lang/eval-fail-eol-2.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-2.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
error: undefined variable 'invalid'
|
||||||
|
at /pwd/lang/eval-fail-eol-2.nix:2:1:
|
||||||
|
1| # foo
|
||||||
|
2| invalid
|
||||||
|
| ^
|
||||||
|
3| # bar
|
2
tests/functional/lang/eval-fail-eol-2.nix
Normal file
2
tests/functional/lang/eval-fail-eol-2.nix
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# foo
invalid
|
||||||
|
# bar
|
6
tests/functional/lang/eval-fail-eol-3.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-3.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
error: undefined variable 'invalid'
|
||||||
|
at /pwd/lang/eval-fail-eol-3.nix:2:1:
|
||||||
|
1| # foo
|
||||||
|
2| invalid
|
||||||
|
| ^
|
||||||
|
3| # bar
|
3
tests/functional/lang/eval-fail-eol-3.nix
Normal file
3
tests/functional/lang/eval-fail-eol-3.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# foo
|
||||||
|
invalid
|
||||||
|
# bar
|
Loading…
Reference in a new issue