29template<
size_t K,
class S>
32 S& self() {
return *
static_cast<S*
>(
this); }
33 const S& self()
const {
return *
static_cast<const S*
>(
this); }
37 :
Lexer(buf, nullptr) {}
39 :
Lexer(src.buf(), &src) {}
46 if (
buf_.starts_with(utf8::Bom))
cursor_ = utf8::Bom.size();
47 for (
size_t i = 0; i != K; ++i)
54 char32_t c = utf8::EoF;
78 std::string
fold(
char32_t (*f)(
char32_t)
noexcept)
const {
79 std::string res(
view());
81 c = (char)f((
char32_t)(uint8_t)c);
93 return ahead_.put(decode()).c;
98 if (!pred(
ahead()))
return false;
104 bool accept(
char32_t c) {
return accept([c](
char32_t d) {
return c == d; }); }
117 auto begin =
ahead_[0].begin.off;
120 auto run =
ahead_[0].begin.off;
121 for (; run !=
buf_.size() && (uint8_t)
buf_[run] < 0x80 && pred((
char32_t)(uint8_t)
buf_[run]); ++run) {}
123 if (run !=
ahead_[0].begin.off) {
126 for (
size_t i = 0; i != K; ++i)
131 if (c < 0x80 || c == utf8::EoF || !pred(c))
break;
147 assert(a < 0x80 && b < 0x80 &&
"only an ASCII byte can be searched for without decoding");
148 auto begin =
ahead_[0].begin.off;
151 for (
auto e =
buf_.size(); run != e; ++run)
152 if (
auto c = (
char8_t)
buf_[run]; c == a || c == b)
break;
154 return skip_to(begin, run);
159 assert(a < 0x80 &&
"only an ASCII byte can be searched for without decoding");
160 auto begin =
ahead_[0].begin.off;
161 auto pos =
buf_.find((
char)a, begin);
162 return skip_to(begin, pos == std::string_view::npos ?
buf_.size() : pos);
175 assert(!seq.empty() &&
"an empty sequence matches at once, so the lexer would not advance");
176 auto begin =
ahead_[0].begin.off;
177 auto pos =
buf_.find(seq, begin);
178 return skip_to(begin, pos == std::string_view::npos ?
buf_.size() : pos);
189 if (!
accept(utf8::Invalid))
return false;
190 while (
accept(utf8::Invalid)) {}
215 requires(S& s) { s.driver(); },
216 "provide `fe::Driver& driver()` in your lexer - or a `utf8_err` of your own");
217 return error().
e(
loc_,
"invalid UTF-8 sequence");
223 requires(S& s) { s.driver(); },
224 "provide `fe::Driver& driver()` in your lexer - or a `char_err` of your own");
238 std::string_view skip_to(
size_t begin,
size_t run) {
242 for (
size_t i = 0; i != K; ++i)
246 return buf_.substr(begin, run - begin);
Collects diagnostics and hands each to the Diag that lays it out.
Error & e(Loc loc, cite_string< Args... > s, Args &&... args)
char32_t next()
Get next char32_t in Lexer::buf_ and extend Lexer::loc_ to cover it.
void start()
Invoke before assembling the next token.
std::string_view accept_while_none_of(char8_t a)
As Lexer::accept_while_none_of(char8_t, char8_t), but a single stop byte, which one memchr finds outr...
Loc loc_
Location of the token we are currently constructing - see Lexer::view.
Lexer(std::string_view buf)
std::string_view accept_until(std::string_view seq)
Lexer::next up to - but not including - the next occurrence of seq.
Loc peek() const
Location of the next character to be consumed (Lexer::ahead()); empty once the buffer is exhausted.
size_t cursor_
Byte offset of the first not yet decoded character.
bool accept(auto pred)
Accept next character in Lexer::buf_ and Lexer::next it, if pred holds.
const fe::Error & error() const
char32_t ahead(size_t i=0) const
std::string_view accept_while_none_of(char8_t a, char8_t b)
Lexer::next up to - but not including - the next byte that is a or b.
std::string_view view() const
std::string lower() const
Lexer::view, case-folded - what a case-insensitive language like FORTRAN or SQL wants to intern.
fe::Error & char_err(char32_t c)
Lexer::recover_char discarded c at Lexer::loc_.
Lexer(std::string_view buf, const Src *src)
Delegate here to funnel both of the above into a single ctor of your own.
void recover_char()
One character, reported as S::char_err.
fe::Error & utf8_err()
Lexer::recover_utf8 discarded the malformed bytes at Lexer::loc_.
std::string_view accept_while_none_of(char a)
std::string fold(char32_t(*f)(char32_t) noexcept) const
std::string upper() const
std::string_view accept_while(auto pred)
Lexer::next as long as pred holds.
std::string_view accept_while_none_of(char a, char b)
A decoded code point together with the byte range it occupies.
A ring buffer with N elements.
T put(T item)
Puts item into buffer.
The content of one source file together with the offsets its rows start at.
char32_t decode(std::istream &is)
Decodes the next UTF-8 sequence from is into a single char32_t.
constexpr char32_t toupper(char32_t c) noexcept
constexpr char32_t tolower(char32_t c) noexcept
Location within a Src: the half-open byte range [Loc::begin, Loc::end).
constexpr Loc anew_begin() const noexcept
Pos end
It's called end because - just like an STL iterator - it refers to the byte one past the last one wit...
constexpr uint32_t size() const noexcept
Byte offset into a Src; pass around as value.
constexpr Pos() noexcept=default
Creates an invalid Position.
Wrapper for char32_t with an operator<< that writes UTF-8.