FE
0.13.1
Header-only C++ frontend library
Toggle main menu visibility
Loading...
Searching...
No Matches
lexer.h
Go to the documentation of this file.
1
#pragma once
2
3
#include <cstddef>
4
5
#include <string>
6
#include <string_view>
7
8
#include "
fe/driver.h
"
9
#include "
fe/loc.h
"
10
#include "
fe/ring.h
"
11
#include "
fe/src.h
"
12
#include "
fe/utf8.h
"
13
14
namespace
fe
{
15
16
/// The blueprint for a lexer with a buffer of @p K tokens to peek into the future (Lexer::ahead).
17
/// You can "override" Lexer::next via CRTP (@p S is the child).
18
/// The whole source has to sit in @p buf: a Pos is an index into it, so there is nothing left to
19
/// keep track of - Lexer::next just hands out the byte range the code point it consumed occupied.
20
/// @p S must provide somewhere to report to:
21
/// ```
22
/// class MyLexer : public fe::Lexer<K, MyLexer> {
23
/// fe::Driver& driver(); ///< The default diagnostic below lands in its Driver::error.
24
///
25
/// friend fe::Lexer<K, MyLexer>; ///< Otherwise, this may be private.
26
/// };
27
/// ```
28
/// Lexer::utf8_err and Lexer::char_err come with a default; declare either in @p S to word it differently.
29
template
<
size_t
K,
class
S>
30
class
Lexer
{
31
private
:
32
S& self() {
return
*
static_cast<
S*
>
(
this
); }
33
const
S& self()
const
{
return
*
static_cast<
const
S*
>
(
this
); }
34
35
public
:
36
Lexer
(std::string_view buf)
37
:
Lexer
(buf, nullptr) {}
38
Lexer
(
const
Src
& src)
39
:
Lexer
(src.buf(), &src) {}
40
41
protected
:
42
/// Delegate here to funnel both of the above into a single ctor of your own.
43
Lexer
(std::string_view buf,
const
Src
* src)
44
:
buf_
(buf)
45
,
src_
(src) {
46
if
(
buf_
.starts_with(utf8::Bom))
cursor_
= utf8::Bom.size();
47
for (
size_t
i = 0; i != K; ++i)
48
ahead_
[i] = decode();
49
start
();
50
}
51
52
/// A decoded code point together with the byte range it occupies.
53
struct
Ahead
{
54
char32_t
c
= utf8::EoF;
55
Pos
begin
,
end
;
56
};
57
58
char32_t
ahead
(
size_t
i = 0)
const
{
return
ahead_
[i].c; }
59
60
/// Loc%ation of the next character to be consumed (Lexer::ahead()); empty once the buffer is exhausted.
61
Loc
peek
()
const
{
return
{
src_
,
ahead_
[0].begin,
ahead_
[0].end}; }
62
63
/// Invoke before assembling the next token.
64
void
start
() {
65
loc_
=
peek
().
anew_begin
();
66
str_
.clear();
67
}
68
69
/// Get next `char32_t` in Lexer::buf_ and extend Lexer::loc_ to cover it.
70
/// @returns utf8::Invalid on an invalid UTF-8 sequence.
71
char32_t
next
() {
72
loc_
.
end
=
ahead_
[0].end;
73
return
ahead_
.put(decode()).c;
74
}
75
76
/// @name Accept
77
/// Accept next character in Lexer::buf_, depending on some condition.
78
///@{
79
/// What should happen to the accepted char?
80
/// Normalize identifiers via Append::Lower or Append::Upper for case-insensitive languages like FORTRAN or SQL.
81
enum class
Append
{
82
Off,
///< Do not append accepted char to Lexer::str_.
83
On,
///< Append accepted char as is to Lexer::str_.
84
Lower,
///< Append accepted char via fe::utf8::tolower` to Lexer::str_.
85
Upper,
///< Append accepted char via fe::utf8::toupper` to Lexer::str_.
86
};
87
88
/// @returns `true` if @p pred holds.
89
/// In this case invoke Lexer::next() and append to Lexer::str_, if @p append.
90
template
<Append append = Append::On,
class
Pred>
91
bool
accept
(Pred pred) {
92
if
(pred(
ahead
())) {
93
auto
c = self().next();
94
if
constexpr
(append !=
Append::Off
) {
95
if
constexpr
(append ==
Append::Lower
) c =
fe::utf8::tolower
(c);
96
if
constexpr
(append ==
Append::Upper
) c =
fe::utf8::toupper
(c);
97
str_
+= c;
98
}
99
return
true
;
100
}
101
return
false
;
102
}
103
104
// clang-format off
105
template
<Append append = Append::On>
bool
accept
(
char32_t
c) {
return
accept<append>
([c](
char32_t
d) {
return
c == d; }); }
106
template
<Append append = Append::On>
bool
accept
(
char
c) {
return
accept<append>
((
char32_t
)c); }
107
template
<Append append = Append::On>
bool
accept
(
char8_t
c) {
return
accept<append>
((
char32_t
)c); }
108
// clang-format on
109
///@}
110
111
/// @name Recover
112
/// Lexer::next input that cannot be part of a token, report it, and keep the current lexer going.
113
/// Invoke after Lexer::start, so Lexer::loc_ spans exactly what was discarded.
114
///@{
115
/// A whole run of malformed UTF-8, if any, reported as one `S::utf8_err`.
116
/// Check this *before* your token dispatch: utf8::Invalid is no code point and matches no rule of yours.
117
bool
recover_utf8
() {
118
if
(!
accept<Append::Off>
(utf8::Invalid))
return
false
;
119
while
(
accept<Append::Off>
(utf8::Invalid)) {}
120
self().utf8_err();
121
return
true
;
122
}
123
124
/// One character, reported as `S::char_err`.
125
/// This is the last resort of your token dispatch: nothing in your language starts with it.
126
/// @warning Never at utf8::EoF - accept that first or your lexer will spin.
127
void
recover_char
() {
128
auto
c =
ahead
();
129
self().next();
130
self().char_err(c);
131
}
132
///@}
133
134
/// @name Diagnostics
135
/// The defaults @p S may replace with one of its own.
136
///@{
137
/// Lexer::recover_utf8 discarded the malformed bytes at Lexer::loc_.
138
void
utf8_err
() {
139
static_assert
(
140
requires
(S& s) { s.driver(); },
141
"provide `fe::Driver& driver()` in your lexer - or a `utf8_err` of your own"
);
142
self().driver().error(
loc_
,
"invalid UTF-8 sequence"
);
143
}
144
145
/// Lexer::recover_char discarded @p c at Lexer::loc_.
146
void
char_err
(
char32_t
c) {
147
static_assert
(
148
requires
(S& s) { s.driver(); },
149
"provide `fe::Driver& driver()` in your lexer - or a `char_err` of your own"
);
150
self().driver().error(
loc_
,
"invalid input character `{}`"
,
utf8::Char32
(c));
151
}
152
///@}
153
154
std::string_view
buf_
;
155
const
Src
*
src_
;
156
size_t
cursor_
= 0;
///< Byte offset of the first not yet decoded character.
157
Ring<Ahead, K>
ahead_
;
158
Loc
loc_
;
///< Loc%ation of the token we are currently constructing within Lexer::str_,
159
std::string
str_
;
160
161
private
:
162
Ahead
decode() {
163
auto
begin =
cursor_
;
164
auto
c =
utf8::decode
(
buf_
,
cursor_
);
165
return
{c,
Pos
((uint32_t)begin),
Pos
((uint32_t)
cursor_
)};
166
}
167
};
168
169
}
// namespace fe
fe::Lexer::Lexer
Lexer(const Src &src)
Definition
lexer.h:38
fe::Lexer::accept
bool accept(char8_t c)
Definition
lexer.h:107
fe::Lexer::next
char32_t next()
Get next char32_t in Lexer::buf_ and extend Lexer::loc_ to cover it.
Definition
lexer.h:71
fe::Lexer::start
void start()
Invoke before assembling the next token.
Definition
lexer.h:64
fe::Lexer::recover_utf8
bool recover_utf8()
Definition
lexer.h:117
fe::Lexer::loc_
Loc loc_
Location of the token we are currently constructing within Lexer::str_,.
Definition
lexer.h:158
fe::Lexer::Lexer
Lexer(std::string_view buf)
Definition
lexer.h:36
fe::Lexer::peek
Loc peek() const
Location of the next character to be consumed (Lexer::ahead()); empty once the buffer is exhausted.
Definition
lexer.h:61
fe::Lexer::cursor_
size_t cursor_
Byte offset of the first not yet decoded character.
Definition
lexer.h:156
fe::Lexer::Ahead::c
char32_t c
Definition
lexer.h:54
fe::Lexer::accept
bool accept(char c)
Definition
lexer.h:106
fe::Lexer::utf8_err
void utf8_err()
Definition
lexer.h:138
fe::Lexer::ahead
char32_t ahead(size_t i=0) const
Definition
lexer.h:58
fe::Lexer::ahead_
Ring< Ahead, K > ahead_
Definition
lexer.h:157
fe::Lexer::src_
const Src * src_
Definition
lexer.h:155
fe::Lexer::Ahead::end
Pos end
Definition
lexer.h:55
fe::Lexer::Lexer
Lexer(std::string_view buf, const Src *src)
Delegate here to funnel both of the above into a single ctor of your own.
Definition
lexer.h:43
fe::Lexer::str_
std::string str_
Definition
lexer.h:159
fe::Lexer::char_err
void char_err(char32_t c)
Lexer::recover_char discarded c at Lexer::loc_.
Definition
lexer.h:146
fe::Lexer::recover_char
void recover_char()
One character, reported as S::char_err.
Definition
lexer.h:127
fe::Lexer::Append
Append
Definition
lexer.h:81
fe::Lexer::Append::Upper
@ Upper
Append accepted char via fe::utf8::toupper` to Lexer::str_.
Definition
lexer.h:85
fe::Lexer::Append::Lower
@ Lower
Append accepted char via fe::utf8::tolower` to Lexer::str_.
Definition
lexer.h:84
fe::Lexer::Append::Off
@ Off
Do not append accepted char to Lexer::str_.
Definition
lexer.h:82
fe::Lexer::accept
bool accept(Pred pred)
Definition
lexer.h:91
fe::Lexer::buf_
std::string_view buf_
Definition
lexer.h:154
fe::Lexer::Ahead::begin
Pos begin
Definition
lexer.h:55
fe::Lexer::accept
bool accept(char32_t c)
Definition
lexer.h:105
fe::Lexer::Ahead
A decoded code point together with the byte range it occupies.
Definition
lexer.h:53
fe::Ring
A ring buffer with N elements.
Definition
ring.h:15
fe::Src
The content of one source file together with the offsets its rows start at.
Definition
src.h:44
driver.h
loc.h
fe::utf8::tolower
char32_t tolower(char32_t c) noexcept
Definition
utf8.h:179
fe::utf8::decode
char32_t decode(std::istream &is)
Decodes the next UTF-8 sequence from is into a single char32_t.
Definition
utf8.h:66
fe::utf8::toupper
char32_t toupper(char32_t c) noexcept
Definition
utf8.h:180
fe
Definition
algo.h:17
ring.h
src.h
fe::Loc
Location within a Src: the half-open byte range [Loc::begin, Loc::end).
Definition
loc.h:46
fe::Loc::end
Pos end
It's called end because - just like an STL iterator - it refers to the byte one past the last one wit...
Definition
loc.h:95
fe::Loc::anew_begin
constexpr Loc anew_begin() const
Definition
loc.h:67
fe::Pos
Byte offset into a Src; pass around as value.
Definition
loc.h:18
fe::Pos::Pos
constexpr Pos()=default
Creates an invalid Position.
fe::utf8::Char32
Wrapper for char32_t with an operator<< that writes UTF-8.
Definition
utf8.h:147
utf8.h
fe
lexer.h
Generated by
1.18.0