5#include <unordered_map>
14const std::unordered_map<std::string, TokenType> keywords = {
29[[nodiscard]]
bool is_identifier_start(
char c)
31 return std::isalpha(
static_cast<unsigned char>(c)) != 0 || c ==
'_';
34[[nodiscard]]
bool is_identifier_char(
char c)
36 return std::isalnum(
static_cast<unsigned char>(c)) != 0 || c ==
'_';
39[[nodiscard]]
bool is_digit(
char c)
41 return std::isdigit(
static_cast<unsigned char>(c)) != 0;
44[[nodiscard]]
bool is_hex_digit(
char c)
46 return std::isxdigit(
static_cast<unsigned char>(c)) != 0;
49[[nodiscard]]
bool is_octal_digit(
char c)
51 return c >=
'0' && c <=
'7';
54[[nodiscard]]
bool is_binary_digit(
char c)
56 return c ==
'0' || c ==
'1';
95 return "integer_literal";
97 return "string_literal";
99 return "char_literal";
172Lexer::Lexer(gsl::not_null<const TextFormatter *> format, std::string content)
173 : format_{format}, content_{std::move(content)}
178 : format_{format}, content_{std::move(content)}, context_{context}
184 if (context_ !=
nullptr) {
185 return context_->
make_error(pos, std::move(message));
187 return FormattableError{format_->
format(
"{}:{}: {}", pos.
line, pos.
column, message)};
192 std::vector<Token> tokens;
194 while (!is_at_end()) {
195 skip_whitespace_except_newline();
212 if (peek_next() ==
'/') {
216 if (peek_next() ==
'*') {
217 auto result = skip_block_comment();
218 if (!result.has_value()) {
233 if (is_identifier_start(c)) {
234 tokens.push_back(consume_identifier_or_keyword());
240 auto result = consume_number();
241 if (!result.has_value()) {
244 tokens.push_back(std::move(result).value());
250 auto result = consume_string();
251 if (!result.has_value()) {
254 tokens.push_back(std::move(result).value());
259 tokens.push_back(consume_operator());
266char Lexer::peek()
const
271 return content_[current_];
274char Lexer::peek_next()
const
276 if (current_ + 1 >= content_.size()) {
279 return content_[current_ + 1];
287 char c = content_[current_];
299bool Lexer::is_at_end()
const
301 return current_ >= content_.size();
304void Lexer::skip_whitespace_except_newline()
306 while (!is_at_end()) {
308 if (c ==
' ' || c ==
'\t' || c ==
'\r') {
317void Lexer::skip_line_comment()
323 while (!is_at_end() && peek() !=
'\n') {
329ChainableResult<void> Lexer::skip_block_comment()
331 SourcePosition start_pos = current_position();
337 while (!is_at_end()) {
338 if (peek() ==
'*' && peek_next() ==
'/') {
346 return make_error(start_pos,
"unterminated block comment");
349Token Lexer::consume_identifier_or_keyword()
351 SourcePosition start_pos = current_position();
354 while (!is_at_end() && is_identifier_char(peek())) {
358 auto it = keywords.find(text);
359 if (it != keywords.end()) {
360 return Token{it->second, std::move(text), start_pos};
366ChainableResult<Token> Lexer::consume_number()
368 SourcePosition start_pos = current_position();
372 if (peek() ==
'0' && !is_at_end()) {
377 if (next ==
'x' || next ==
'X') {
379 if (!is_hex_digit(peek())) {
380 return make_error(start_pos, format_->
format(
"invalid hexadecimal literal '{}'", text));
382 while (!is_at_end() && is_hex_digit(peek())) {
386 while (!is_at_end() && (peek() ==
'u' || peek() ==
'U' || peek() ==
'l' || peek() ==
'L')) {
389 std::int64_t value = 0;
390 auto [ptr, ec] = std::from_chars(text.data() + 2, text.data() + text.size(), value, 16);
391 if (ec != std::errc{}) {
392 return make_error(start_pos, format_->
format(
"invalid hexadecimal literal '{}'", text));
394 return Token{std::move(text), value, start_pos};
398 if (next ==
'b' || next ==
'B') {
400 if (!is_binary_digit(peek())) {
401 return make_error(start_pos, format_->
format(
"invalid binary literal '{}'", text));
403 while (!is_at_end() && is_binary_digit(peek())) {
407 while (!is_at_end() && (peek() ==
'u' || peek() ==
'U' || peek() ==
'l' || peek() ==
'L')) {
410 std::int64_t value = 0;
411 auto [ptr, ec] = std::from_chars(text.data() + 2, text.data() + text.size(), value, 2);
412 if (ec != std::errc{}) {
413 return make_error(start_pos, format_->
format(
"invalid binary literal '{}'", text));
415 return Token{std::move(text), value, start_pos};
419 if (is_octal_digit(next)) {
420 while (!is_at_end() && is_octal_digit(peek())) {
424 while (!is_at_end() && (peek() ==
'u' || peek() ==
'U' || peek() ==
'l' || peek() ==
'L')) {
427 std::int64_t value = 0;
428 auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), value, 8);
429 if (ec != std::errc{}) {
430 return make_error(start_pos, format_->
format(
"invalid octal literal '{}'", text));
432 return Token{std::move(text), value, start_pos};
436 return Token{std::move(text), 0, start_pos};
440 while (!is_at_end() && is_digit(peek())) {
444 while (!is_at_end() && (peek() ==
'u' || peek() ==
'U' || peek() ==
'l' || peek() ==
'L')) {
448 std::int64_t value = 0;
449 auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), value, 10);
450 if (ec != std::errc{}) {
451 return make_error(start_pos, format_->
format(
"invalid decimal literal '{}'", text));
453 return Token{std::move(text), value, start_pos};
456ChainableResult<Token> Lexer::consume_string()
458 SourcePosition start_pos = current_position();
464 while (!is_at_end() && peek() !=
'"') {
469 return make_error(start_pos,
"unterminated string literal");
473 if (c ==
'\\' && !is_at_end()) {
476 char escaped = advance();
511 return make_error(start_pos,
"unterminated string literal");
521Token Lexer::consume_operator()
523 SourcePosition start_pos = current_position();
619SourcePosition Lexer::current_position()
const
621 return SourcePosition{line_, column_};
Context object providing rich error formatting for C/C++ parsing.
FormattableError make_error(SourcePosition pos, const std::string &message) const
Creates a FormattableError with source context.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
ChainableResult< std::vector< Token > > lex()
Tokenizes the entire source content.
Lexer(gsl::not_null< const TextFormatter * > format, std::string content)
Constructs a lexer for the given source content.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > ¶ms) const
Formats a string with styled parameters using fmtlib syntax.
std::string token_type_name(TokenType type)
Returns a human-readable name for a token type.
TokenType
Enumeration of token types recognized by the C parser lexer.
Represents a position within source content.
std::size_t line
1-based line number
std::size_t column
1-based column number