11namespace utf8 = fe::utf8;
17Tok idx_tok(Loc loc, u64 mod, u64 val) {
return {loc, mod, mod == 0 ? val : val % mod}; }
19bool is_id_head(
char32_t c) {
return c ==
'_' || utf8::isalpha(c); }
20bool is_id_tail(
char32_t c) {
return c ==
'_' || utf8::isalnum(c); }
23u64 to_u64(std::string_view sv,
int base) {
25 auto ec = std::from_chars(sv.data(), sv.data() + sv.size(), res, base).ec;
26 return ec == std::errc::result_out_of_range ? std::numeric_limits<u64>::max() : res;
29f64 to_f64(std::string_view sv,
int base) {
31 std::from_chars(sv.data(), sv.data() + sv.size(), res,
32 base == 16 ? std::chars_format::hex : std::chars_format::general);
38Lexer::Lexer(Driver& driver, std::string_view buf,
const fe::Src* src, std::ostream* md)
54 if (accept(utf8::EoF)) {
62 if (accept(utf8::isspace))
continue;
63 if (recover_utf8())
continue;
67 if (accept(
'('))
return tok(Tag::D_paren_l);
68 if (accept(
')'))
return tok(Tag::D_paren_r);
69 if (accept(
'['))
return tok(Tag::D_brckt_l);
70 if (accept(
']'))
return tok(Tag::D_brckt_r);
71 if (accept(
'{'))
return tok(Tag::D_brace_l);
72 if (accept(
'}'))
return tok(Tag::D_brace_r);
73 if (accept(U
'⦃'))
return tok(Tag::D_curly_l);
74 if (accept(U
'⦄'))
return tok(Tag::D_curly_r);
75 if (accept(U
'«'))
return tok(Tag::D_quote_l);
76 if (accept(U
'»'))
return tok(Tag::D_quote_r);
77 if (accept(U
'‹'))
return tok(Tag::D_angle_l);
78 if (accept(U
'›'))
return tok(Tag::D_angle_r);
80 if (accept(
'+'))
return tok(Tag::T_add);
82 if (accept(
'>'))
return tok(Tag::T_arrow_r);
83 return tok(Tag::T_sub);
85 if (accept(U
'→'))
return tok(Tag::T_arrow_r);
86 if (accept(U
'←'))
return tok(Tag::T_arrow_l);
87 if (accept(
'@'))
return tok(Tag::T_at);
89 if (accept(
'>'))
return tok(Tag::T_fat_arrow);
90 if (accept(
'='))
return tok(Tag::T_eq);
91 return tok(Tag::T_assign);
94 if (accept(
'='))
return tok(Tag::T_ne);
95 error().e(loc_,
"expected `=` after `!`");
99 if (accept(
'<'))
return tok(Tag::T_shl);
100 if (accept(
'='))
return tok(Tag::T_le);
101 if (accept(
'-'))
return tok(Tag::T_arrow_l);
102 return tok(Tag::T_lt);
105 if (accept(
'>'))
return tok(Tag::T_shr);
106 if (accept(
'='))
return tok(Tag::T_ge);
107 return tok(Tag::T_gt);
109 if (accept(U
'⊥'))
return tok(Tag::T_bot);
110 if (accept(U
'⊤'))
return tok(Tag::T_top);
111 if (accept(U
'□'))
return tok(Tag::T_box);
112 if (accept(
','))
return tok(Tag::T_comma);
113 if (accept(
'$'))
return tok(Tag::T_dollar);
114 if (accept(
'#'))
return tok(Tag::T_extract);
115 if (accept(U
'λ'))
return tok(Tag::T_lm);
116 if (accept(
'%'))
return tok(Tag::T_rem);
117 if (accept(
'|'))
return tok(Tag::T_pipe);
118 if (accept(
';'))
return tok(Tag::T_semicolon);
119 if (accept(U
'★'))
return tok(Tag::T_star);
120 if (accept(
'*'))
return tok(Tag::T_star);
121 if (accept(
':'))
return tok(Tag::T_colon);
122 if (accept(U
'∪'))
return tok(Tag::T_union);
126 if (accept(utf8::isdigit)) {
129 return {loc_, to_f64(view(), 10)};
132 return tok(Tag::T_dot);
136 if (accept(utf8::any(
'+',
'-',
'*',
'/',
'%')))
return {loc_, Tag::M_id, sym()};
138 accept(utf8::any(
'<',
'='));
139 return {loc_, Tag::M_id, sym()};
142 accept(utf8::any(
'>',
'='));
143 return {loc_, Tag::M_id, sym()};
145 if (accept(utf8::any(
'=',
'!')) && accept(
'='))
return {loc_, Tag::M_id, sym()};
146 error().e(loc_,
"expected one of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `<<`, `>>` "
147 "after the escape hatch");
153 if (accept(
'\''))
return {loc_, c};
154 error().e(loc_,
"invalid character literal `{}`", view());
158 if (accept(
'\"'))
return lex_str();
162 if (
auto tag =
driver().keys().find(s))
return {loc_, *tag};
163 return {loc_, Tag::M_id, s};
166 if (utf8::isdigit(ahead()))
return lex_lit();
180 accept_while([](
char32_t c) {
return c !=
'\n'; });
184 return tok(Tag::T_div);
191bool Lexer::lex_id() {
192 if (accept(is_id_head)) {
193 accept_while(is_id_tail);
204 case '\\': res +=
"\\\\";
break;
205 case '\"': res +=
"\\\"";
break;
206 case '\0': res +=
"\\0";
break;
207 case '\a': res +=
"\\a";
break;
208 case '\b': res +=
"\\b";
break;
209 case '\f': res +=
"\\f";
break;
210 case '\n': res +=
"\\n";
break;
211 case '\r': res +=
"\\r";
break;
212 case '\t': res +=
"\\t";
break;
213 case '\v': res +=
"\\v";
break;
223 if (!is_id_head(utf8::decode(str, i)))
return false;
224 while (i != str.size())
225 if (!is_id_tail(utf8::decode(str, i)))
return false;
230Tok Lexer::lex_lit() {
235 if (accept(utf8::any(
'b',
'B'))) base = 2;
236 else if (accept(utf8::any(
'o',
'O'))) base = 8;
237 else if (accept(utf8::any(
'x',
'X'))) base = 16;
241 auto begin = loc_.end.off;
242 auto body = [&](uint32_t end) {
return buf_.substr(begin, end - begin); };
245 auto end = loc_.end.off;
247 if (accept(utf8::any(
'i',
'I'))) {
248 auto val = to_u64(body(end), base);
249 auto i = loc_.end.off;
251 auto width = to_u64(buf_.substr(i, loc_.end.off - i), 10);
252 return Tok{loc_, Idx::bitwidth2size(width), val};
256 if (utf8::isrange(ahead(), U
'₀', U
'₉')) {
257 auto i = to_u64(body(end), 10);
259 while (utf8::isrange(ahead(), U
'₀', U
'₉')) mod += char(next() - U
'₀' +
'0');
260 return idx_tok(loc_, to_u64(mod, 10), i);
261 }
else if (accept(
'_')) {
262 auto i = to_u64(body(end), 10);
263 auto m = loc_.end.off;
264 if (accept(utf8::isdigit)) {
266 return idx_tok(loc_, to_u64(buf_.substr(m, loc_.end.off - m), 10), i);
268 error().e(loc_,
"stray underscore in Idx literal; size is missing");
274 bool is_float =
false;
275 if (base == 10 || base == 16) {
282 bool has_exp = lex_exp(base);
283 if (base == 16 && is_float && !has_exp)
error().e(loc_,
"hexadecimal floating constants require an exponent");
287 if (is_float)
return Tok{loc_, to_f64(body(loc_.end.off), base)};
288 else return Tok{loc_, to_u64(body(end), base)};
291void Lexer::lex_digits(
int base ) {
294 case 2: accept_while(utf8::isbdigit);
break;
295 case 8: accept_while(utf8::isodigit);
break;
296 case 10: accept_while(utf8::isdigit);
break;
297 case 16: accept_while(utf8::isxdigit);
break;
299 default: fe::unreachable();
303bool Lexer::lex_exp(
int base ) {
304 if (accept(base == 10 ? utf8::any(
'e',
'E') : utf8::any(
'p',
'P'))) {
305 accept(utf8::any(
'+',
'-'));
306 if (!utf8::isdigit(ahead()))
error().e(loc_,
"exponent has no digits");
314char8_t Lexer::lex_char() {
317 switch (
auto c = ahead()) {
318 case '\'': next();
return '\'';
319 case '\\': next();
return '\\';
320 case '"': next();
return '\"';
321 case '0': next();
return '\0';
322 case 'a': next();
return '\a';
323 case 'b': next();
return '\b';
324 case 'f': next();
return '\f';
325 case 'n': next();
return '\n';
326 case 'r': next();
return '\r';
327 case 't': next();
return '\t';
328 case 'v': next();
return '\v';
330 if (c != utf8::EoF)
error().e(loc_.anew_end(),
"invalid escape character `\\{}`", (
char)c);
337 if (utf8::isascii(c))
return char8_t(c);
338 error().e(loc_,
"invalid character `{}`", utf8::Char32(c));
343Tok Lexer::lex_str() {
344 auto begin = loc_.end.off;
348 if (accept(
'\"'))
return {loc_, Tag::L_str, sym_str(begin, loc_.end.off - 1, esc)};
350 if (ahead() == utf8::EoF) {
351 error().e(loc_,
"unterminated string literal");
352 return {loc_, Tag::L_str, sym_str(begin, loc_.end.off, esc)};
357 if (ahead() != utf8::EoF) next();
358 }
else if (
auto c = next(); !utf8::isascii(c)) {
359 error().e(loc_,
"invalid character `{}`", utf8::Char32(c));
364Sym Lexer::sym_str(uint32_t begin, uint32_t end,
bool esc) {
365 auto body = buf_.substr(begin, end - begin);
366 return driver().sym(esc ? std::string_view(unquote(body, begin)) : body);
369std::string Lexer::unquote(std::string_view body, uint32_t begin) {
371 res.reserve(body.size());
373 for (
size_t i = 0, e = body.size(); i != e; ++i) {
375 if (c ==
'\\' && i + 1 != e) {
378 case '\'': res +=
'\'';
break;
379 case '\\': res +=
'\\';
break;
380 case '"': res +=
'\"';
break;
381 case '0': res +=
'\0';
break;
382 case 'a': res +=
'\a';
break;
383 case 'b': res +=
'\b';
break;
384 case 'f': res +=
'\f';
break;
385 case 'n': res +=
'\n';
break;
386 case 'r': res +=
'\r';
break;
387 case 't': res +=
'\t';
break;
388 case 'v': res +=
'\v';
break;
391 auto loc = Loc(src_, Pos(begin +
u32(i) - 1), Pos(begin +
u32(i) + 1));
392 error().e(loc,
"invalid escape character `\\{}`", body[i]);
402void Lexer::eat_comments() {
404 accept_while([](
char32_t c) {
return c !=
'*'; });
405 if (accept(utf8::EoF)) {
406 error().e(loc_,
"unterminated multi-line comment");
410 if (accept(
'/'))
break;
414void Lexer::emit_md(
bool start_of_file) {
415 if (!start_of_file) {
421 for (
int i = 0; i != 3; ++i)
426 accept_while([](
char32_t c) {
return c !=
'\n'; });
429 }
while (start_md());
431 if (ahead() != utf8::EoF) md_open();
Driver & driver()
fe::Lexer's default diagnostics go to its Driver::error.
static bool is_id(std::string_view str)
Does str match the id production - the same rule Lexer::lex_id applies to the input?
static std::string escape(std::string_view str)
Inverse of Lexer::lex_char: renders str as the body of a Mim string literal.
Lexer(Driver &driver, const fe::Src &src, std::ostream *md=nullptr)
Creates a lexer to read *.mim files (see Lexical Structure).