MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at type-hints-typescript 666 lines 21 kB view raw
1#include <string.h> 2#include <stdbool.h> 3 4#include "tokens.h" 5#include "highlight.h" 6#include "highlight/regex.h" 7#include "silver/lexer.h" 8 9typedef struct { 10 const char *op; 11 int len; 12 hl_token_class cls; 13} op_entry_t; 14 15static const op_entry_t operators[] = { 16 { "===", 3, HL_OPERATOR }, 17 { "!==", 3, HL_OPERATOR }, 18 { "...", 3, HL_OPERATOR }, 19 { "=>", 2, HL_OPERATOR }, 20 { "==", 2, HL_OPERATOR }, 21 { "!=", 2, HL_OPERATOR }, 22 { "<=", 2, HL_OPERATOR }, 23 { ">=", 2, HL_OPERATOR }, 24 { "&&", 2, HL_OPERATOR }, 25 { "||", 2, HL_OPERATOR }, 26 { "??", 2, HL_OPERATOR }, 27 { "?.", 2, HL_OPTIONAL_CHAIN }, 28}; 29 30#define OP_COUNT (sizeof(operators) / sizeof(operators[0])) 31#define K(s, t) if (len == sizeof(s)-1 && !memcmp(word, s, sizeof(s)-1)) return t 32 33static hl_token_class lookup_extra_keyword(const char *word, size_t len) { 34 switch (word[0]) { 35 case 'a': 36 K("abstract", HL_TYPE); 37 K("async", HL_KEYWORD_ITALIC); 38 break; 39 case 'b': K("boolean", HL_TYPE_BOOLEAN); break; 40 case 'd': K("declare", HL_TYPE); break; 41 case 'e': 42 K("enum", HL_TYPE); 43 K("export", HL_KEYWORD_ITALIC); 44 break; 45 case 'g': K("global", HL_KEYWORD_ITALIC); break; 46 case 'i': 47 K("interface", HL_TYPE); 48 K("implements", HL_TYPE); 49 break; 50 case 'n': 51 K("namespace", HL_TYPE); 52 K("never", HL_TYPE); 53 break; 54 case 'o': K("object", HL_TYPE); break; 55 case 'p': 56 K("package", HL_KEYWORD); 57 K("private", HL_KEYWORD); 58 K("protected", HL_KEYWORD); 59 K("public", HL_KEYWORD); 60 break; 61 case 'r': K("readonly", HL_TYPE); break; 62 case 's': 63 K("string", HL_TYPE_STRING); 64 K("symbol", HL_TYPE_STRING); 65 break; 66 case 't': K("type", HL_TYPE); break; 67 case 'u': K("unknown", HL_TYPE); break; 68 } return HL_NONE; 69} 70 71#undef K 72 73static hl_token_class tok_to_class(uint8_t tok) { 74 static const void *dispatch[] = { 75 [TOK_ASYNC] = &&l_kw_italic, 76 [TOK_EXPORT] = &&l_kw_italic, 77 [TOK_THIS] = &&l_kw_italic, 78 [TOK_GLOBAL_THIS] = &&l_kw_italic, 79 [TOK_WINDOW] = &&l_kw_italic, 80 [TOK_DELETE] = &&l_kw_delete, 81 [TOK_TYPEOF] = &&l_type, 82 [TOK_INSTANCEOF] = &&l_type, 83 [TOK_OF] = &&l_type, 84 [TOK_IN] = &&l_type, 85 [TOK_AS] = &&l_type, 86 [TOK_TRUE] = &&l_bool, 87 [TOK_FALSE] = &&l_bool, 88 [TOK_NULL] = &&l_null, 89 [TOK_UNDEF] = &&l_null, 90 }; 91 92 if (tok <= TOK_IDENTIFIER || tok >= TOK_IDENT_LIKE_END) return HL_NONE; 93 if (tok < sizeof(dispatch) / sizeof(*dispatch) && dispatch[tok]) goto *dispatch[tok]; 94 95 return HL_KEYWORD; 96 97 l_kw_italic: return HL_KEYWORD_ITALIC; 98 l_kw_delete: return HL_KEYWORD_DELETE; 99 l_type: return HL_TYPE; 100 l_bool: return HL_BOOLEAN; 101 l_null: return HL_LITERAL_NULL; 102} 103 104void hl_iter_init(hl_iter *it, const char *input, size_t input_len, const highlight_state *state) { 105 it->input = input; 106 it->input_len = input_len; 107 it->pos = 0; 108 it->state = state ? *state : HL_STATE_INIT; 109 it->ctx = HL_CTX_NONE; 110} 111 112static hl_context keyword_sets_context(const char *word, size_t len) { 113 if (len == 8 && memcmp(word, "function", 8) == 0) return HL_CTX_AFTER_FUNCTION; 114 if (len == 5 && memcmp(word, "class", 5) == 0) return HL_CTX_AFTER_CLASS; 115 if (len == 7 && memcmp(word, "extends", 7) == 0) return HL_CTX_AFTER_EXTENDS; 116 return HL_CTX_NONE; 117} 118 119static size_t skip_inline_ws_forward(const char *input, size_t input_len, size_t i) { 120 while (i < input_len && (input[i] == ' ' || input[i] == '\t' || input[i] == '\n' || input[i] == '\r')) i++; 121 return i; 122} 123 124static size_t skip_inline_ws_backward(const char *input, size_t i) { 125 while (i > 0 && (input[i - 1] == ' ' || input[i - 1] == '\t' || input[i - 1] == '\n' || input[i - 1] == '\r')) i--; 126 return i; 127} 128 129static bool read_prev_word(const char *input, size_t end, size_t *word_start, size_t *word_len) { 130 size_t i = skip_inline_ws_backward(input, end); 131 if (i == 0 || !is_ident_continue((unsigned char)input[i - 1])) return false; 132 133 size_t wend = i; 134 while (i > 0 && is_ident_continue((unsigned char)input[i - 1])) i--; 135 136 *word_start = i; 137 *word_len = wend - i; 138 return true; 139} 140 141static bool is_arrow_after(const char *input, size_t input_len, size_t pos) { 142 size_t i = skip_inline_ws_forward(input, input_len, pos); 143 return (i + 1 < input_len && input[i] == '=' && input[i + 1] == '>'); 144} 145 146static bool find_matching_close_paren(const char *input, size_t input_len, size_t open_paren, size_t *close_paren) { 147 size_t depth = 0; 148 for (size_t i = open_paren + 1; i < input_len; i++) { 149 unsigned char ch = (unsigned char)input[i]; 150 if (ch == '(') { 151 depth++; 152 continue; 153 } 154 if (ch == ')') { 155 if (depth == 0) { 156 *close_paren = i; 157 return true; 158 } 159 depth--; 160 } 161 } 162 return false; 163} 164 165static bool has_declaration_keyword_before(const char *input, size_t ident_start) { 166 size_t ws, wstart, wlen; 167 ws = skip_inline_ws_backward(input, ident_start); 168 if (ws == 0) return false; 169 170 size_t wend = ws; 171 size_t i = ws; 172 while (i > 0 && is_ident_continue((unsigned char)input[i - 1])) i--; 173 wstart = i; 174 wlen = wend - i; 175 176 return 177 (wlen == 5 && memcmp(input + wstart, "const", 5) == 0) || 178 (wlen == 3 && memcmp(input + wstart, "let", 3) == 0) || 179 (wlen == 5 && memcmp(input + wstart, "using", 5) == 0) || 180 (wlen == 3 && memcmp(input + wstart, "var", 3) == 0); 181} 182 183static bool is_assigned_arrow_function(const char *input, size_t input_len, size_t ident_start, size_t ident_end) { 184 if (!has_declaration_keyword_before(input, ident_start)) return false; 185 size_t i = skip_inline_ws_forward(input, input_len, ident_end); 186 187 if (i >= input_len || input[i] != '=') return false; 188 if (i + 1 < input_len && input[i + 1] == '>') return false; 189 i++; i = skip_inline_ws_forward(input, input_len, i); 190 191 if (i >= input_len) return false; 192 if (is_arrow_after(input, input_len, i)) return false; 193 194 if (is_ident_begin((unsigned char)input[i])) { 195 size_t j = i + 1; 196 while (j < input_len && is_ident_continue((unsigned char)input[j])) j++; 197 if (is_arrow_after(input, input_len, j)) return true; 198 } 199 200 if (input[i] == '(') { 201 size_t close = 0; 202 if (find_matching_close_paren(input, input_len, i, &close)) 203 if (is_arrow_after(input, input_len, close + 1)) return true; 204 } 205 206 if (is_ident_begin((unsigned char)input[i]) || input[i] == '(') { 207 size_t j = i; 208 if (input[j] == '(') { 209 size_t close = 0; 210 if (find_matching_close_paren(input, input_len, j, &close)) j = close + 1; 211 else return false; 212 } else { 213 j++; 214 while (j < input_len && is_ident_continue((unsigned char)input[j])) j++; 215 } 216 217 j = skip_inline_ws_forward(input, input_len, j); 218 if (j < input_len && input[j] == ':') { 219 j++; 220 size_t depth = 0; 221 while (j < input_len) { 222 unsigned char ch = (unsigned char)input[j]; 223 if (ch == '(' || ch == '<') { depth++; j++; continue; } 224 if (ch == ')' || ch == '>') { 225 if (depth > 0) depth--; 226 j++; 227 continue; 228 } 229 if (depth == 0 && (ch == '=' || ch == ',')) break; 230 j++; 231 } 232 if (is_arrow_after(input, input_len, j)) return true; 233 } 234 } 235 236 if (input[i] == 'a') { 237 if (i + 5 <= input_len && memcmp(input + i, "async", 5) == 0 && 238 (i + 5 >= input_len || !is_ident_continue((unsigned char)input[i + 5]))) { 239 size_t j = skip_inline_ws_forward(input, input_len, i + 5); 240 if (j < input_len && input[j] == '(') { 241 size_t close = 0; 242 if (find_matching_close_paren(input, input_len, j, &close)) 243 if (is_arrow_after(input, input_len, close + 1)) return true; 244 } 245 if (j < input_len && is_ident_begin((unsigned char)input[j])) { 246 size_t k = j + 1; 247 while (k < input_len && is_ident_continue((unsigned char)input[k])) k++; 248 if (is_arrow_after(input, input_len, k)) return true; 249 } 250 } 251 } 252 253 if (input[i] == 'f') { 254 if (i + 8 <= input_len && memcmp(input + i, "function", 8) == 0 && 255 (i + 8 >= input_len || !is_ident_continue((unsigned char)input[i + 8]))) 256 return true; 257 } 258 259 return false; 260} 261 262static bool has_function_keyword_before_paren(const char *input, size_t open_paren) { 263 size_t word_start = 0; 264 size_t word_len = 0; 265 266 if (!read_prev_word(input, open_paren, &word_start, &word_len)) return false; 267 if (word_len == 8 && memcmp(input + word_start, "function", 8) == 0) return true; 268 269 if (!read_prev_word(input, word_start, &word_start, &word_len)) return false; 270 return (word_len == 8 && memcmp(input + word_start, "function", 8) == 0); 271} 272 273static bool is_control_paren_prefix(const char *input, size_t open_paren) { 274 size_t word_start = 0; 275 size_t word_len = 0; 276 if (!read_prev_word(input, open_paren, &word_start, &word_len)) return false; 277 278#define C(s) (word_len == sizeof(s) - 1 && memcmp(input + word_start, s, sizeof(s) - 1) == 0) 279 return C("if") || C("for") || C("while") || C("switch") || C("catch") || C("with"); 280#undef C 281} 282 283static bool is_likely_function_param_paren( 284 const char *input, size_t input_len, 285 size_t open_paren, size_t close_paren 286) { 287 if (is_arrow_after(input, input_len, close_paren + 1)) return true; 288 if (has_function_keyword_before_paren(input, open_paren)) return true; 289 290 size_t after = skip_inline_ws_forward(input, input_len, close_paren + 1); 291 if (after < input_len && input[after] == '{' && !is_control_paren_prefix(input, open_paren)) 292 return true; 293 294 return false; 295} 296 297static bool find_enclosing_open_paren(const char *input, size_t pos, size_t *open_paren) { 298 size_t depth = 0; 299 size_t i = pos; 300 301 while (i > 0) { 302 i--; 303 unsigned char ch = (unsigned char)input[i]; 304 if (ch == ')') { 305 depth++; 306 continue; 307 } 308 if (ch == '(') { 309 if (depth == 0) { 310 *open_paren = i; 311 return true; 312 } 313 depth--; 314 } 315 } 316 return false; 317} 318 319static bool is_function_argument_identifier(const char *input, size_t input_len, size_t start, size_t end) { 320 if (is_arrow_after(input, input_len, end)) { 321 size_t left = skip_inline_ws_backward(input, start); 322 if (left > 0 && input[left - 1] == '.') return false; 323 return true; 324 } 325 326 size_t prev = skip_inline_ws_backward(input, start); 327 if (prev == 0) return false; 328 unsigned char prev_ch = (unsigned char)input[prev - 1]; 329 if (!(prev_ch == '(' || prev_ch == ',' || prev_ch == '{' || prev_ch == '[' || prev_ch == ':')) 330 return false; 331 332 size_t open_paren = 0; 333 if (!find_enclosing_open_paren(input, start, &open_paren)) return false; 334 335 size_t close_paren = 0; 336 if (!find_matching_close_paren(input, input_len, open_paren, &close_paren)) return false; 337 return is_likely_function_param_paren(input, input_len, open_paren, close_paren); 338} 339 340static inline bool is_line_comment_terminator(unsigned char c) { 341 return c == '\n' || c == '\r'; 342} 343 344bool hl_iter_next(hl_iter *it, hl_span *out) { 345 const char *input = it->input; 346 size_t input_len = it->input_len; 347 size_t i = it->pos; 348 349 if (i >= input_len) return false; 350 unsigned char c = (unsigned char)input[i]; 351 352 if (it->state.mode == HL_STATE_BLOCK_COMMENT) { 353 size_t start = i; 354 while (i < input_len) { 355 if (input[i] == '*' && i + 1 < input_len && input[i + 1] == '/') { 356 i += 2; 357 it->state.mode = HL_STATE_NORMAL; 358 break; 359 } 360 i++; 361 } 362 *out = (hl_span){ start, i - start, HL_COMMENT }; 363 it->pos = i; 364 return true; 365 } 366 367 if (i == 0 && c == '#' && i + 1 < input_len && input[i + 1] == '!') { 368 while (i < input_len && input[i] != '\n') i++; 369 *out = (hl_span){ 0, i, HL_COMMENT }; 370 it->pos = i; 371 return true; 372 } 373 374 if (it->state.mode == HL_STATE_STRING_SINGLE || it->state.mode == HL_STATE_STRING_DOUBLE) { 375 char quote = (it->state.mode == HL_STATE_STRING_SINGLE) ? '\'' : '"'; 376 size_t start = i; 377 while (i < input_len) { 378 if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 379 if (input[i] == quote) { 380 i++; 381 it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 382 break; 383 } 384 i++; 385 } 386 *out = (hl_span){ start, i - start, HL_STRING }; 387 it->pos = i; 388 return true; 389 } 390 391 if (it->state.mode == HL_STATE_TEMPLATE) { 392 size_t start = i; 393 while (i < input_len) { 394 if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 395 if (input[i] == '$' && i + 1 < input_len && input[i + 1] == '{') { 396 i += 2; 397 it->state.mode = HL_STATE_TEMPLATE_EXPR; 398 it->state.template_depth++; 399 break; 400 } 401 if (input[i] == '`') { 402 i++; 403 it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 404 break; 405 } 406 i++; 407 } 408 *out = (hl_span){ start, i - start, HL_STRING }; 409 it->pos = i; 410 return true; 411 } 412 413 if (it->state.mode == HL_STATE_TEMPLATE_EXPR && c == '}') { 414 it->state.template_depth--; 415 if (it->state.template_depth <= 0) { 416 it->state.mode = HL_STATE_TEMPLATE; 417 it->state.template_depth = 0; 418 *out = (hl_span){ i, 1, HL_BRACKET }; 419 it->pos = i + 1; 420 return true; 421 } 422 } 423 if (it->state.mode == HL_STATE_TEMPLATE_EXPR && c == '{') { 424 it->state.template_depth++; 425 *out = (hl_span){ i, 1, HL_BRACKET }; 426 it->pos = i + 1; 427 return true; 428 } 429 430 if (c == '/' && i + 1 < input_len && input[i + 1] == '/') { 431 it->ctx = HL_CTX_NONE; 432 size_t start = i; i += 2; 433 while (i < input_len && !is_line_comment_terminator((unsigned char)input[i])) i++; 434 *out = (hl_span){ start, i - start, HL_COMMENT }; 435 it->pos = i; 436 return true; 437 } 438 439 if (c == '/' && i + 1 < input_len && input[i + 1] == '*') { 440 it->ctx = HL_CTX_NONE; 441 size_t start = i; 442 i += 2; 443 while (i + 1 < input_len && !(input[i] == '*' && input[i + 1] == '/')) i++; 444 if (i + 1 < input_len) { 445 i += 2; 446 } else { 447 i = input_len; 448 it->state.mode = HL_STATE_BLOCK_COMMENT; 449 } 450 *out = (hl_span){ start, i - start, HL_COMMENT }; 451 it->pos = i; 452 return true; 453 } 454 455 if (c == '/') { 456 size_t regex_end = 0; 457 if (js_scan_regex_literal(input, input_len, i, &regex_end)) { 458 it->ctx = HL_CTX_NONE; 459 *out = (hl_span){ i, regex_end - i, HL_REGEX }; 460 it->pos = regex_end; 461 return true; 462 } 463 } 464 465 if (c == '\'' || c == '"') { 466 it->ctx = HL_CTX_NONE; 467 size_t start = i; 468 it->state.mode = (c == '\'') ? HL_STATE_STRING_SINGLE : HL_STATE_STRING_DOUBLE; 469 i++; 470 while (i < input_len) { 471 if (input[i] == '\\' && i + 1 < input_len) { i += 2; continue; } 472 if ((unsigned char)input[i] == c) { 473 i++; 474 it->state.mode = (it->state.template_depth > 0) ? HL_STATE_TEMPLATE_EXPR : HL_STATE_NORMAL; 475 break; 476 } 477 i++; 478 } 479 *out = (hl_span){ start, i - start, HL_STRING }; 480 it->pos = i; 481 return true; 482 } 483 484 if (c == '`') { 485 it->ctx = HL_CTX_NONE; 486 it->state.mode = HL_STATE_TEMPLATE; 487 *out = (hl_span){ i, 1, HL_STRING }; 488 it->pos = i + 1; 489 return true; 490 } 491 492 if (c == ';') { 493 it->ctx = HL_CTX_NONE; 494 *out = (hl_span){ i, 1, HL_SEMICOLON }; 495 it->pos = i + 1; 496 return true; 497 } 498 499 if (IS_DIGIT(c) || (c == '.' && i + 1 < input_len && IS_DIGIT(input[i + 1]))) { 500 it->ctx = HL_CTX_NONE; 501 size_t start = i; 502 if (c == '0' && i + 1 < input_len) { 503 unsigned char next = (unsigned char)input[i + 1]; 504 if (next == 'x' || next == 'X') { 505 i += 2; 506 while (i < input_len && (IS_XDIGIT(input[i]) || input[i] == '_')) i++; 507 goto num_done; 508 } else if (next == 'b' || next == 'B') { 509 i += 2; 510 while (i < input_len && (input[i] == '0' || input[i] == '1' || input[i] == '_')) i++; 511 goto num_done; 512 } else if (next == 'o' || next == 'O') { 513 i += 2; 514 while (i < input_len && (IS_OCTAL(input[i]) || input[i] == '_')) i++; 515 goto num_done; 516 } 517 } 518 while (i < input_len && (IS_DIGIT(input[i]) || input[i] == '_')) i++; 519 if (i < input_len && input[i] == '.') { 520 i++; 521 while (i < input_len && (IS_DIGIT(input[i]) || input[i] == '_')) i++; 522 } 523 if (i < input_len && (input[i] == 'e' || input[i] == 'E')) { 524 i++; 525 if (i < input_len && (input[i] == '+' || input[i] == '-')) i++; 526 while (i < input_len && (IS_DIGIT(input[i]) || input[i] == '_')) i++; 527 } 528 num_done: 529 if (i < input_len && input[i] == 'n') i++; 530 *out = (hl_span){ start, i - start, HL_NUMBER }; 531 it->pos = i; 532 return true; 533 } 534 535 for (int k = 0; k < (int)OP_COUNT; k++) { 536 int oplen = operators[k].len; 537 if (i + (size_t)oplen <= input_len && 538 memcmp(input + i, operators[k].op, (size_t)oplen) == 0) { 539 it->ctx = HL_CTX_NONE; 540 *out = (hl_span){ i, (size_t)oplen, operators[k].cls }; 541 it->pos = i + (size_t)oplen; 542 return true; 543 } 544 } 545 546 if (c == '#' && i + 1 < input_len && is_ident_begin((unsigned char)input[i + 1])) { 547 size_t start = i; 548 i += 2; 549 while (i < input_len && is_ident_continue((unsigned char)input[i])) i++; 550 it->ctx = HL_CTX_NONE; 551 *out = (hl_span){ start, i - start, HL_PROPERTY }; 552 it->pos = i; 553 return true; 554 } 555 556 if (is_ident_begin(c)) { 557 size_t start = i; 558 i++; 559 while (i < input_len && is_ident_continue(input[i])) i++; 560 size_t word_len = i - start; 561 const char *word = input + start; 562 563 bool is_member_access = (start > 0 && input[start - 1] == '.' && 564 (start < 2 || input[start - 2] != '.')); 565 bool is_method = false; 566 if (is_member_access) { 567 size_t peek = i; 568 while (peek < input_len && input[peek] == ' ') peek++; 569 if (peek < input_len && input[peek] == '(') is_method = true; 570 } 571 size_t after_word = i; 572 while (after_word < input_len && input[after_word] == ' ') after_word++; 573 bool is_call = (after_word < input_len && input[after_word] == '('); 574 575 hl_token_class cls = HL_NONE; 576 bool is_console = (word_len == 7 && memcmp(word, "console", 7) == 0); 577 578 if (is_console) { 579 cls = HL_PROPERTY; 580 } else if (is_function_argument_identifier(input, input_len, start, i)) { 581 cls = HL_ARGUMENT; 582 } else if (is_method) { 583 cls = HL_FUNCTION; 584 } else if (is_member_access) { 585 cls = HL_PROPERTY; 586 } else if (is_assigned_arrow_function(input, input_len, start, i)) { 587 cls = HL_FUNCTION_NAME; 588 } else if (it->ctx == HL_CTX_AFTER_FUNCTION) { 589 cls = HL_FUNCTION_NAME; 590 it->ctx = HL_CTX_NONE; 591 } else if (it->ctx == HL_CTX_AFTER_CLASS) { 592 cls = HL_CLASS_NAME; 593 it->ctx = HL_CTX_NONE; 594 } else if (it->ctx == HL_CTX_AFTER_EXTENDS) { 595 cls = HL_PARENT_CLASS; 596 it->ctx = HL_CTX_NONE; 597 } else { 598 cls = lookup_extra_keyword(word, word_len); 599 600 if (cls == HL_NONE) { 601 if ((word_len == 3 && memcmp(word, "NaN", 3) == 0) || 602 (word_len == 8 && memcmp(word, "Infinity", 8) == 0)) { 603 cls = HL_NUMBER; 604 } 605 else if (word_len == 7 && memcmp(word, "extends", 7) == 0) { 606 cls = HL_KEYWORD_EXTENDS; 607 } else { 608 cls = tok_to_class(sv_parsekeyword(word, word_len)); 609 } 610 } 611 612 if (cls == HL_NONE) { 613 size_t peek = i; 614 while (peek < input_len && input[peek] == ' ') peek++; 615 if (peek < input_len && input[peek] == ':' && 616 (peek + 1 >= input_len || input[peek + 1] != ':')) 617 cls = HL_PROPERTY; 618 } 619 620 if (cls == HL_NONE && word[0] >= 'A' && word[0] <= 'Z') { 621 cls = HL_TYPE; 622 } 623 624 if (cls == HL_NONE && is_call) { 625 cls = HL_FUNCTION; 626 } 627 628 hl_context next_ctx = keyword_sets_context(word, word_len); 629 if (next_ctx != HL_CTX_NONE) it->ctx = next_ctx; 630 } 631 632 *out = (hl_span){ start, word_len, cls }; 633 it->pos = i; 634 return true; 635 } 636 637 if (c == '<' || c == '>' || c == '=' || 638 c == '+' || c == '-' || c == '*' || c == '/' || 639 c == '%' || c == '&' || c == '|' || c == '^' || 640 c == '~' || c == '!' || c == '?') { 641 it->ctx = HL_CTX_NONE; 642 *out = (hl_span){ i, 1, HL_OPERATOR }; 643 it->pos = i + 1; 644 return true; 645 } 646 647 if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') { 648 it->ctx = HL_CTX_NONE; 649 *out = (hl_span){ i, 1, HL_BRACKET }; 650 it->pos = i + 1; 651 return true; 652 } 653 654 if (c == ' ' || c == '\t') { 655 size_t start = i; 656 while (i < input_len && (input[i] == ' ' || input[i] == '\t')) i++; 657 *out = (hl_span){ start, i - start, HL_NONE }; 658 it->pos = i; 659 return true; 660 } 661 662 it->ctx = HL_CTX_NONE; 663 *out = (hl_span){ i, 1, HL_NONE }; 664 it->pos = i + 1; 665 return true; 666}