| 221 | } // namespace |
| 222 | |
| 223 | std::vector<Token> tokenize(const std::string& source) |
| 224 | { |
| 225 | std::vector<Token> tokens; |
| 226 | const size_t len = source.size(); |
| 227 | size_t i = 0; |
| 228 | |
| 229 | while(i < len) |
| 230 | { |
| 231 | const char c = source[i]; |
| 232 | |
| 233 | // Skip whitespace (space, tab, newline, carriage return) |
| 234 | if(c == ' ' || c == '\t' || c == '\n' || c == '\r') |
| 235 | { |
| 236 | ++i; |
| 237 | continue; |
| 238 | } |
| 239 | |
| 240 | const size_t start = i; |
| 241 | |
| 242 | // Single-quoted string literal |
| 243 | if(c == '\'') |
| 244 | { |
| 245 | ++i; |
| 246 | while(i < len && source[i] != '\'') |
| 247 | { |
| 248 | ++i; |
| 249 | } |
| 250 | if(i < len) |
| 251 | { |
| 252 | // extract content without quotes |
| 253 | std::string_view text(&source[start + 1], i - start - 1); |
| 254 | tokens.push_back({ TokenType::String, text, start }); |
| 255 | ++i; // skip closing quote |
| 256 | } |
| 257 | else |
| 258 | { |
| 259 | std::string_view text(&source[start], i - start); |
| 260 | tokens.push_back({ TokenType::Error, text, start }); |
| 261 | } |
| 262 | continue; |
| 263 | } |
| 264 | |
| 265 | // Number literal (integer or real) |
| 266 | if(isDigit(c)) |
| 267 | { |
| 268 | NumberResult nr; |
| 269 | const bool is_hex = |
| 270 | c == '0' && i + 1 < len && (source[i + 1] == 'x' || source[i + 1] == 'X'); |
| 271 | if(is_hex) |
| 272 | { |
| 273 | nr = scanHexNumber(source, len, i); |
| 274 | } |
| 275 | else |
| 276 | { |
| 277 | nr = scanDecimalNumber(source, len, i); |
| 278 | } |
| 279 | |
| 280 | std::string_view text(&source[start], i - start); |
no test coverage detected