| 122 | }; |
| 123 | |
| 124 | class Lexer { |
| 125 | public: |
| 126 | Lexer(std::string_view src, std::string filename) |
| 127 | : src_(src), filename_(std::move(filename)) {} |
| 128 | |
| 129 | Token next() { |
| 130 | skipWs(); |
| 131 | const int startLine = line_; |
| 132 | const int startCol = column(); |
| 133 | if (pos_ >= src_.size()) return {Tok::End, "", startLine, startCol}; |
| 134 | |
| 135 | const char c = src_[pos_]; |
| 136 | if (c == '{') { ++pos_; return {Tok::LBrace, "{", startLine, startCol}; } |
| 137 | if (c == '}') { ++pos_; return {Tok::RBrace, "}", startLine, startCol}; } |
| 138 | if (c == '(') { ++pos_; return {Tok::LParen, "(", startLine, startCol}; } |
| 139 | if (c == ')') { ++pos_; return {Tok::RParen, ")", startLine, startCol}; } |
| 140 | if (c == '[') { ++pos_; return {Tok::LBrack, "[", startLine, startCol}; } |
| 141 | if (c == ']') { ++pos_; return {Tok::RBrack, "]", startLine, startCol}; } |
| 142 | if (c == ',') { ++pos_; return {Tok::Comma, ",", startLine, startCol}; } |
| 143 | if (c == ';') { ++pos_; return {Tok::Semi, ";", startLine, startCol}; } |
| 144 | if (c == '=') { ++pos_; return {Tok::Equal, "=", startLine, startCol}; } |
| 145 | if (c == ':' && pos_ + 1 < src_.size() && src_[pos_ + 1] == ':') { |
| 146 | pos_ += 2; |
| 147 | return {Tok::Scope, "::", startLine, startCol}; |
| 148 | } |
| 149 | if (c == ':') { ++pos_; return {Tok::Colon, ":", startLine, startCol}; } |
| 150 | |
| 151 | if (std::isalpha(static_cast<unsigned char>(c)) || c == '_') { |
| 152 | const size_t start = pos_++; |
| 153 | while (pos_ < src_.size()) { |
| 154 | const char next = src_[pos_]; |
| 155 | if (!std::isalnum(static_cast<unsigned char>(next)) && next != '_') break; |
| 156 | ++pos_; |
| 157 | } |
| 158 | return {Tok::Ident, std::string(src_.substr(start, pos_ - start)), startLine, startCol}; |
| 159 | } |
| 160 | |
| 161 | if (std::isdigit(static_cast<unsigned char>(c))) { |
| 162 | const size_t start = pos_++; |
| 163 | while (pos_ < src_.size() && |
| 164 | std::isalnum(static_cast<unsigned char>(src_[pos_]))) { |
| 165 | ++pos_; |
| 166 | } |
| 167 | return {Tok::Int, std::string(src_.substr(start, pos_ - start)), startLine, startCol}; |
| 168 | } |
| 169 | |
| 170 | die(startLine, startCol, "unexpected character '" + std::string(1, c) + "'"); |
| 171 | } |
| 172 | |
| 173 | [[noreturn]] void die(int line, int col, const std::string& message) const { |
| 174 | std::fprintf(stderr, "%s:%d:%d: error: %s\n", |
| 175 | filename_.c_str(), line, col, message.c_str()); |
| 176 | std::exit(1); |
| 177 | } |
| 178 | |
| 179 | private: |
| 180 | void skipWs() { |
| 181 | while (pos_ < src_.size()) { |
nothing calls this directly
no outgoing calls
no test coverage detected