File Coverage

include/litavis_tokeniser.h
Criterion Covered Total %
statement 309 360 85.8
branch 225 368 61.1
condition n/a
subroutine n/a
pod n/a
total 534 728 73.3


line stmt bran cond sub pod time code
1             #ifndef LITAVIS_TOKENISER_H
2             #define LITAVIS_TOKENISER_H
3              
4             #include
5             #include
6             #include
7              
8             /* ── Token types ──────────────────────────────────────────── */
9              
10             typedef enum {
11             LITAVIS_T_SELECTOR, /* .class, #id, element, *, [attr] */
12             LITAVIS_T_LBRACE, /* { */
13             LITAVIS_T_RBRACE, /* } */
14             LITAVIS_T_PROPERTY, /* property-name (before :) */
15             LITAVIS_T_COLON, /* : */
16             LITAVIS_T_VALUE, /* property value (up to ;) */
17             LITAVIS_T_SEMICOLON, /* ; */
18             LITAVIS_T_COMMA, /* , */
19             LITAVIS_T_AT_KEYWORD, /* @media, @keyframes, @layer, @supports, etc. */
20             LITAVIS_T_AT_PRELUDE, /* text between @keyword and { or ; */
21             LITAVIS_T_COMMENT, /* block comment */
22             LITAVIS_T_LINE_COMMENT, /* // single-line comment */
23             LITAVIS_T_PREPROC_VAR_DEF, /* $varname: value; */
24             LITAVIS_T_PREPROC_VAR_REF, /* $varname in a value context */
25             LITAVIS_T_CSS_VAR_DEF, /* --custom-property (as property name) */
26             LITAVIS_T_FUNCTION, /* identifier( ... ) including nested parens */
27             LITAVIS_T_MIXIN_DEF, /* %name: ( ... ); */
28             LITAVIS_T_MIXIN_REF, /* %name; */
29             LITAVIS_T_AMPERSAND, /* & parent selector reference */
30             LITAVIS_T_STRING, /* "..." or '...' */
31             LITAVIS_T_WS, /* whitespace run */
32             LITAVIS_T_EOF
33             } LitavisTokenType;
34              
35             /* ── Token ────────────────────────────────────────────────── */
36              
37             typedef struct {
38             LitavisTokenType type;
39             const char *start; /* pointer into source buffer (no copy) */
40             int length; /* byte length */
41             int line; /* 1-based line number */
42             int col; /* 1-based column */
43             } LitavisToken;
44              
45             /* ── Token list ───────────────────────────────────────────── */
46              
47             typedef struct {
48             LitavisToken *tokens;
49             int count;
50             int capacity;
51             } LitavisTokenList;
52              
53             /* ── Token list operations ────────────────────────────────── */
54              
55 587           static LitavisTokenList* litavis_token_list_new(int initial_cap) {
56 587           LitavisTokenList *list = (LitavisTokenList*)malloc(sizeof(LitavisTokenList));
57 587 50         if (!list) LITAVIS_FATAL("out of memory");
58 587 50         if (initial_cap < 64) initial_cap = 64;
59 587           list->tokens = (LitavisToken*)malloc(sizeof(LitavisToken) * (size_t)initial_cap);
60 587 50         if (!list->tokens) LITAVIS_FATAL("out of memory");
61 587           list->count = 0;
62 587           list->capacity = initial_cap;
63 587           return list;
64             }
65              
66 587           static void litavis_token_list_free(LitavisTokenList *list) {
67 587 50         if (!list) return;
68 587 50         if (list->tokens) free(list->tokens);
69 587           free(list);
70             }
71              
72 7306           static void litavis_token_push(LitavisTokenList *list, LitavisTokenType type,
73             const char *start, int length, int line, int col) {
74 7306 100         if (list->count >= list->capacity) {
75 3           int new_cap = list->capacity * 2;
76 3           LitavisToken *new_tokens = (LitavisToken*)realloc(list->tokens, sizeof(LitavisToken) * (size_t)new_cap);
77 3 50         if (!new_tokens) LITAVIS_FATAL("out of memory");
78 3           list->tokens = new_tokens;
79 3           list->capacity = new_cap;
80             }
81 7306           list->tokens[list->count].type = type;
82 7306           list->tokens[list->count].start = start;
83 7306           list->tokens[list->count].length = length;
84 7306           list->tokens[list->count].line = line;
85 7306           list->tokens[list->count].col = col;
86 7306           list->count++;
87 7306           }
88              
89             /* ── Internal helpers ─────────────────────────────────────── */
90              
91 705           static int litavis_is_ident_char(char c) {
92 705 100         return isalnum((unsigned char)c) || c == '-' || c == '_';
    100          
    50          
93             }
94              
95 0           static int litavis_is_selector_char(char c) {
96 0 0         return c != '{' && c != '}' && c != ';' && c != '\0';
    0          
    0          
    0          
97             }
98              
99             /* Skip whitespace, updating line/col */
100 6905           static void litavis_skip_ws(const char *input, int len, int *pos, int *line, int *col) {
101 16035 100         while (*pos < len) {
102 15900           char c = input[*pos];
103 15900 100         if (c == '\n') {
104 754           (*pos)++;
105 754           (*line)++;
106 754           *col = 1;
107 15146 100         } else if (c == ' ' || c == '\t' || c == '\r' || c == '\f') {
    50          
    50          
    50          
108 8376           (*pos)++;
109 8376           (*col)++;
110             } else {
111             break;
112             }
113             }
114 6905           }
115              
116             /* Advance pos by n, updating col */
117 3929           static void litavis_advance(int *pos, int *col, int n) {
118 3929           *pos += n;
119 3929           *col += n;
120 3929           }
121              
122             /* Scan a quoted string (handles escape sequences) */
123 20           static int litavis_scan_string(const char *input, int len, int pos) {
124 20           char quote = input[pos];
125 20           pos++;
126 163 50         while (pos < len) {
127 163 50         if (input[pos] == '\\' && pos + 1 < len) {
    0          
128 0           pos += 2; /* skip escape */
129 163 100         } else if (input[pos] == quote) {
130 20           return pos + 1; /* past closing quote */
131             } else {
132 143           pos++;
133             }
134             }
135 0           return pos; /* unterminated string — return what we have */
136             }
137              
138             /* Scan balanced parentheses */
139 119           static int litavis_scan_parens(const char *input, int len, int pos) {
140 119           int depth = 0;
141 2603 50         while (pos < len) {
142 2603           char c = input[pos];
143 2603 100         if (c == '(') {
144 121           depth++;
145 2482 100         } else if (c == ')') {
146 121           depth--;
147 121 100         if (depth <= 0) return pos + 1;
148 2361 100         } else if (c == '"' || c == '\'') {
    50          
149 4           pos = litavis_scan_string(input, len, pos);
150 4           continue;
151             }
152 2480           pos++;
153             }
154 0           return pos;
155             }
156              
157             /* Scan an identifier (letters, digits, hyphens, underscores) */
158 122           static int litavis_scan_ident(const char *input, int len, int pos) {
159 705 50         while (pos < len && litavis_is_ident_char(input[pos]))
    100          
160 583           pos++;
161 122           return pos;
162             }
163              
164             /* ── Main tokeniser ───────────────────────────────────────── */
165              
166 587           static LitavisTokenList* litavis_tokenise(const char *input, int len) {
167 587           LitavisTokenList *list = litavis_token_list_new(128);
168 587           int pos = 0, line = 1, col = 1;
169 587           int brace_depth = 0;
170 587           int expecting_value = 0; /* 1 after seeing PROPERTY + COLON */
171              
172 6149 100         while (pos < len) {
173             /* Skip whitespace */
174 5697           int ws_start = pos;
175 5697           litavis_skip_ws(input, len, &pos, &line, &col);
176 5697 100         if (pos >= len) break;
177              
178 5562           char c = input[pos];
179              
180             /* ── Block comments ── */
181 5562 100         if (c == '/' && pos + 1 < len && input[pos + 1] == '*') {
    50          
    100          
182 8           int start = pos;
183 8           int start_line = line, start_col = col;
184 8           pos += 2; col += 2;
185 265 50         while (pos + 1 < len && !(input[pos] == '*' && input[pos + 1] == '/')) {
    100          
    50          
186 257 100         if (input[pos] == '\n') { line++; col = 1; }
187 252           else { col++; }
188 257           pos++;
189             }
190 8 50         if (pos + 1 < len) { pos += 2; col += 2; }
191             /* Skip comments — don't emit */
192 8           continue;
193             }
194              
195             /* ── Line comments ── */
196 5554 100         if (c == '/' && pos + 1 < len && input[pos + 1] == '/') {
    50          
    50          
197 3           int start = pos;
198 3           int start_line = line, start_col = col;
199 68 50         while (pos < len && input[pos] != '\n')
    100          
200 65           pos++;
201             /* Skip line comments — don't emit */
202 3           col = 1;
203 3           continue;
204             }
205              
206             /* ── Braces ── */
207 5551 100         if (c == '{') {
208 824           litavis_token_push(list, LITAVIS_T_LBRACE, &input[pos], 1, line, col);
209 824           litavis_advance(&pos, &col, 1);
210 824           brace_depth++;
211 824           expecting_value = 0;
212 824           continue;
213             }
214 4727 100         if (c == '}') {
215 824           litavis_token_push(list, LITAVIS_T_RBRACE, &input[pos], 1, line, col);
216 824           litavis_advance(&pos, &col, 1);
217 824           brace_depth--;
218 824           expecting_value = 0;
219 824           continue;
220             }
221              
222             /* ── Semicolons ── */
223 3903 100         if (c == ';') {
224 997           litavis_token_push(list, LITAVIS_T_SEMICOLON, &input[pos], 1, line, col);
225 997           litavis_advance(&pos, &col, 1);
226 997           expecting_value = 0;
227 997           continue;
228             }
229              
230             /* ── Commas ── */
231 2906 100         if (c == ',') {
232 10           litavis_token_push(list, LITAVIS_T_COMMA, &input[pos], 1, line, col);
233 10           litavis_advance(&pos, &col, 1);
234 10           continue;
235             }
236              
237             /* ── @-rules ── */
238 2896 100         if (c == '@') {
239 16           int start = pos;
240 16           int start_col = col;
241 16           litavis_advance(&pos, &col, 1); /* skip @ */
242 16           int kw_end = litavis_scan_ident(input, len, pos);
243 16           int kw_len = kw_end - start;
244 16           litavis_token_push(list, LITAVIS_T_AT_KEYWORD, &input[start], kw_len, line, start_col);
245 16           pos = kw_end;
246 16           col = start_col + kw_len;
247              
248             /* Scan prelude — everything up to { or ; */
249 16           litavis_skip_ws(input, len, &pos, &line, &col);
250 16 50         if (pos < len && input[pos] != '{' && input[pos] != ';') {
    50          
    50          
251 16           int pl_start = pos;
252 16           int pl_line = line, pl_col = col;
253 77 50         while (pos < len && input[pos] != '{' && input[pos] != ';') {
    100          
    100          
254 61 100         if (input[pos] == '(') {
255 13           pos = litavis_scan_parens(input, len, pos);
256 13           col = pl_col + (pos - pl_start);
257 48 100         } else if (input[pos] == '"' || input[pos] == '\'') {
    50          
258 1           pos = litavis_scan_string(input, len, pos);
259 1           col = pl_col + (pos - pl_start);
260 47 50         } else if (input[pos] == '\n') {
261 0           pos++; line++; col = 1;
262             } else {
263 47           pos++; col++;
264             }
265             }
266             /* Trim trailing whitespace from prelude */
267 16           int pl_end = pos;
268 28 50         while (pl_end > pl_start && (input[pl_end - 1] == ' ' || input[pl_end - 1] == '\t'
    100          
    50          
269 16 50         || input[pl_end - 1] == '\r' || input[pl_end - 1] == '\n'))
    50          
270 12           pl_end--;
271 16 50         if (pl_end > pl_start)
272 16           litavis_token_push(list, LITAVIS_T_AT_PRELUDE, &input[pl_start], pl_end - pl_start, pl_line, pl_col);
273             }
274 16           continue;
275             }
276              
277             /* ── Preprocessor variables ($var) ── */
278 2880 100         if (c == '$' && !expecting_value) {
    100          
279 65           int start = pos;
280 65           int start_col = col;
281 65           litavis_advance(&pos, &col, 1); /* skip $ */
282 65           int name_end = litavis_scan_ident(input, len, pos);
283 65 50         if (name_end == pos) {
284             /* lone $ — treat as part of selector */
285 0           goto parse_selector;
286             }
287 65           pos = name_end;
288 65           col = start_col + (pos - start);
289              
290             /* Check if this is a definition ($var: value;) or a map ref ($var{key}) */
291 65           int saved_pos = pos, saved_line = line, saved_col = col;
292 65           litavis_skip_ws(input, len, &pos, &line, &col);
293 130 50         if (pos < len && input[pos] == ':') {
    50          
294             /* $var: value; — definition */
295 65           litavis_advance(&pos, &col, 1); /* skip : */
296 65           litavis_skip_ws(input, len, &pos, &line, &col);
297 65           int val_start = pos;
298 65           int val_line = line, val_col = col;
299             /* Scan value up to ; */
300 337 50         while (pos < len && input[pos] != ';') {
    100          
301 272 100         if (input[pos] == '(') {
302 3           pos = litavis_scan_parens(input, len, pos);
303 269 50         } else if (input[pos] == '"' || input[pos] == '\'') {
    50          
304 0           pos = litavis_scan_string(input, len, pos);
305 269 50         } else if (input[pos] == '\n') {
306 0           pos++; line++; col = 1;
307             } else {
308 269           pos++; col++;
309             }
310             }
311 65           int val_end = pos;
312             /* Trim trailing ws */
313 65 50         while (val_end > val_start && (input[val_end - 1] == ' ' || input[val_end - 1] == '\t'))
    50          
    50          
314 0           val_end--;
315             /* Emit: var name token (includes $), then value */
316 65           litavis_token_push(list, LITAVIS_T_PREPROC_VAR_DEF, &input[start], name_end - start, line, start_col);
317 65 50         if (val_end > val_start)
318 65           litavis_token_push(list, LITAVIS_T_VALUE, &input[val_start], val_end - val_start, val_line, val_col);
319 65 50         if (pos < len && input[pos] == ';') {
    50          
320 65           litavis_token_push(list, LITAVIS_T_SEMICOLON, &input[pos], 1, line, col);
321 65           litavis_advance(&pos, &col, 1);
322             }
323             } else {
324             /* $var reference in value or selector */
325 0           pos = saved_pos;
326 0           line = saved_line;
327 0           col = saved_col;
328 0           litavis_token_push(list, LITAVIS_T_PREPROC_VAR_REF, &input[start], name_end - start, line, start_col);
329             }
330 65           expecting_value = 0;
331 65           continue;
332             }
333              
334             /* ── Mixin syntax (%name) ── */
335 2815 100         if (c == '%' && !expecting_value) {
    50          
336 41           int start = pos;
337 41           int start_col = col;
338 41           litavis_advance(&pos, &col, 1); /* skip % */
339 41           int name_end = litavis_scan_ident(input, len, pos);
340 41 50         if (name_end == pos) {
341 0           goto parse_selector;
342             }
343 41           pos = name_end;
344 41           col = start_col + (pos - start);
345              
346 41           int saved_pos = pos, saved_line = line, saved_col = col;
347 41           litavis_skip_ws(input, len, &pos, &line, &col);
348 41 50         if (pos < len && input[pos] == ':') {
    100          
349             /* %name: (...); — mixin definition */
350 23           litavis_advance(&pos, &col, 1); /* skip : */
351 23           litavis_skip_ws(input, len, &pos, &line, &col);
352 46 50         if (pos < len && input[pos] == '(') {
    50          
353 23           int body_start = pos;
354 23           int body_line = line, body_col = col;
355 23           pos = litavis_scan_parens(input, len, pos);
356 23           col = body_col + (pos - body_start);
357 23           litavis_token_push(list, LITAVIS_T_MIXIN_DEF, &input[start], name_end - start, body_line, start_col);
358             /* Emit the body content (inside parens) as VALUE */
359 23 50         if (pos - body_start - 2 > 0)
360 23           litavis_token_push(list, LITAVIS_T_VALUE, &input[body_start + 1], pos - body_start - 2, body_line, body_col + 1);
361 23           litavis_skip_ws(input, len, &pos, &line, &col);
362 23 50         if (pos < len && input[pos] == ';') {
    50          
363 23           litavis_token_push(list, LITAVIS_T_SEMICOLON, &input[pos], 1, line, col);
364 23           litavis_advance(&pos, &col, 1);
365             }
366             } else {
367             /* %name: value; — treat as mixin def with inline value */
368 0           int val_start = pos;
369 0           int val_col = col;
370 0 0         while (pos < len && input[pos] != ';' && input[pos] != '}') {
    0          
    0          
371 0 0         if (input[pos] == '\n') { pos++; line++; col = 1; }
372 0           else { pos++; col++; }
373             }
374 0           litavis_token_push(list, LITAVIS_T_MIXIN_DEF, &input[start], name_end - start, line, start_col);
375 0 0         if (pos > val_start)
376 0           litavis_token_push(list, LITAVIS_T_VALUE, &input[val_start], pos - val_start, line, val_col);
377 0 0         if (pos < len && input[pos] == ';') {
    0          
378 0           litavis_token_push(list, LITAVIS_T_SEMICOLON, &input[pos], 1, line, col);
379 0           litavis_advance(&pos, &col, 1);
380             }
381             }
382             } else {
383             /* %name — mixin reference */
384 18           pos = saved_pos;
385 18           line = saved_line;
386 18           col = saved_col;
387 18           litavis_token_push(list, LITAVIS_T_MIXIN_REF, &input[start], name_end - start, line, start_col);
388             }
389 41           expecting_value = 0;
390 41           continue;
391             }
392              
393             /* ── Ampersand (parent reference) ── */
394 2774 100         if (c == '&') {
395             /* Scan & plus any attached selector suffix (&:hover, &.class, &-mod)
396             then the rest up to { as a combined selector */
397 10           int start = pos;
398 10           int start_col = col;
399             /* Jump to selector parsing which will capture & and everything up to { */
400 10           goto parse_selector;
401             }
402              
403             /* ── Strings (only in selector context, not when expecting a value) ── */
404 2764 100         if ((c == '"' || c == '\'') && !expecting_value) {
    100          
    50          
405 0           int start = pos;
406 0           int start_col = col;
407 0           int end = litavis_scan_string(input, len, pos);
408 0           litavis_token_push(list, LITAVIS_T_STRING, &input[start], end - start, line, start_col);
409 0           col += (end - start);
410 0           pos = end;
411 0           continue;
412             }
413              
414             /* ── Inside a declaration block: parse property/value ── */
415 2764 100         if (brace_depth > 0 && !expecting_value) {
    100          
416             /* Could be a nested selector or a property name */
417             /* Look ahead to determine: if we see ':' before '{' or '}', it's a property */
418 1012           int scan = pos;
419 1012           int found_colon = 0, found_brace = 0;
420 1012           int paren_d = 0;
421 7224 50         while (scan < len) {
422 7224           char sc = input[scan];
423 7224 50         if (sc == '(') { paren_d++; scan++; continue; }
424 7224 50         if (sc == ')') { paren_d--; scan++; continue; }
425 7224 50         if (paren_d > 0) { scan++; continue; }
426 7224 50         if (sc == '"' || sc == '\'') {
    50          
427 0           scan = litavis_scan_string(input, len, scan);
428 0           continue;
429             }
430 7224 100         if (sc == ':' && scan + 1 < len && input[scan + 1] == ':') {
    50          
    50          
431             /* :: is a pseudo-element, not property separator */
432 0           scan += 2;
433 0           continue;
434             }
435 7224 100         if (sc == ':') {
436             /* Check if this is a pseudo-class/element: if preceded by ident chars
437             and followed by ident, it's :hover, :nth-child, etc. */
438 976 50         if (scan + 1 < len && (isalpha((unsigned char)input[scan + 1]) || input[scan + 1] == ':')) {
    50          
    50          
439             /* Could still be a property... check if there's a { or ; after */
440             /* Heuristic: if the thing before : contains selector-like chars (.#&*[>~+)
441             it's a selector pseudo-class, not a property */
442 0           int has_selector_chars = 0;
443             int k;
444 0 0         for (k = pos; k < scan; k++) {
445 0 0         if (input[k] == '.' || input[k] == '#' || input[k] == '&'
    0          
    0          
446 0 0         || input[k] == '*' || input[k] == '[' || input[k] == '>'
    0          
    0          
447 0 0         || input[k] == '~' || input[k] == '+') {
    0          
448 0           has_selector_chars = 1;
449 0           break;
450             }
451             }
452 0 0         if (has_selector_chars) {
453 0           scan++;
454 0           continue;
455             }
456             }
457 976           found_colon = 1;
458 976           break;
459             }
460 6248 100         if (sc == '{' || sc == '}' || sc == ';') {
    50          
    100          
461 36           found_brace = 1;
462 36           break;
463             }
464 6212 50         if (sc == '\n') { scan++; continue; }
465 6212           scan++;
466             }
467              
468 1012 100         if (found_colon && !found_brace) {
    50          
469             /* It's a property: name */
470 976           int prop_start = pos;
471 976           int prop_col = col;
472             /* Scan property name up to : */
473 6994 50         while (pos < len && input[pos] != ':') {
    100          
474 6018           pos++; col++;
475             }
476 976           int prop_end = pos;
477             /* Trim trailing ws from property name */
478 976 50         while (prop_end > prop_start && (input[prop_end - 1] == ' ' || input[prop_end - 1] == '\t'))
    50          
    50          
479 0           prop_end--;
480              
481             /* Check for --custom-property */
482 976 50         if (prop_end - prop_start >= 2 && input[prop_start] == '-' && input[prop_start + 1] == '-')
    100          
    100          
483 7           litavis_token_push(list, LITAVIS_T_CSS_VAR_DEF, &input[prop_start], prop_end - prop_start, line, prop_col);
484             else
485 969           litavis_token_push(list, LITAVIS_T_PROPERTY, &input[prop_start], prop_end - prop_start, line, prop_col);
486              
487             /* Emit colon */
488 976 50         if (pos < len && input[pos] == ':') {
    50          
489 976           litavis_token_push(list, LITAVIS_T_COLON, &input[pos], 1, line, col);
490 976           litavis_advance(&pos, &col, 1);
491             }
492 976           expecting_value = 1;
493 976           continue;
494             }
495              
496             /* Otherwise it's a nested selector — fall through to selector parsing */
497             }
498              
499 1788 100         if (expecting_value) {
500             /* Scan value: up to ; or } */
501 975           int val_start = pos;
502 975           int val_col = col;
503 975           int val_line = line;
504 975           litavis_skip_ws(input, len, &pos, &line, &col);
505 975           val_start = pos;
506 975           val_col = col;
507 975           val_line = line;
508              
509             {
510 975           int val_brace_depth = 0;
511 5105 50         while (pos < len) {
512 5105 100         if (input[pos] == ';' && val_brace_depth == 0) break;
    50          
513 4132 100         if (input[pos] == '}' && val_brace_depth == 0) break;
    100          
514 4130 100         if (input[pos] == '{') {
515 10           val_brace_depth++;
516 10           pos++; col++;
517 4120 100         } else if (input[pos] == '}') {
518 10           val_brace_depth--;
519 10           pos++; col++;
520 4110 100         } else if (input[pos] == '(') {
521 79           pos = litavis_scan_parens(input, len, pos);
522 79           col = val_col + (pos - val_start);
523 4031 100         } else if (input[pos] == '"' || input[pos] == '\'') {
    100          
524 12           pos = litavis_scan_string(input, len, pos);
525 12           col = val_col + (pos - val_start);
526 4019 50         } else if (input[pos] == '\n') {
527 0           pos++; line++; col = 1;
528             } else {
529 4019           pos++; col++;
530             }
531             }
532             }
533 975           int val_end = pos;
534             /* Trim trailing ws */
535 977 50         while (val_end > val_start && (input[val_end - 1] == ' ' || input[val_end - 1] == '\t'
    100          
    50          
536 975 50         || input[val_end - 1] == '\r' || input[val_end - 1] == '\n'))
    50          
537 2           val_end--;
538 975 50         if (val_end > val_start)
539 975           litavis_token_push(list, LITAVIS_T_VALUE, &input[val_start], val_end - val_start, val_line, val_col);
540              
541 975           expecting_value = 0;
542 975           continue;
543             }
544              
545             /* ── Selector context ── */
546 813           parse_selector: {
547 823           int sel_start = pos;
548 823           int sel_col = col;
549 823           int sel_line = line;
550              
551 6257 100         while (pos < len) {
552 6256           char sc = input[pos];
553 6256 100         if (sc == '{' || sc == ';' || sc == '}') break;
    100          
    50          
554 5444 100         if (sc == ',') break;
555 5434 50         if (sc == '/' && pos + 1 < len && (input[pos + 1] == '/' || input[pos + 1] == '*')) break;
    0          
    0          
    0          
556 5434 100         if (sc == '"' || sc == '\'') {
    50          
557 3           pos = litavis_scan_string(input, len, pos);
558 3           col = sel_col + (pos - sel_start);
559 3           continue;
560             }
561 5431 100         if (sc == '(') {
562 1           pos = litavis_scan_parens(input, len, pos);
563 1           col = sel_col + (pos - sel_start);
564 1           continue;
565             }
566 5430 50         if (sc == '\n') {
567 0           pos++; line++; col = 1;
568 0           continue;
569             }
570 5430           pos++; col++;
571             }
572              
573 823           int sel_end = pos;
574             /* Trim trailing ws */
575 1653 50         while (sel_end > sel_start && (input[sel_end - 1] == ' ' || input[sel_end - 1] == '\t'
    100          
    50          
576 823 50         || input[sel_end - 1] == '\r' || input[sel_end - 1] == '\n'))
    50          
577 830           sel_end--;
578              
579 823 50         if (sel_end > sel_start) {
580 823           litavis_token_push(list, LITAVIS_T_SELECTOR, &input[sel_start], sel_end - sel_start, sel_line, sel_col);
581             }
582             }
583             }
584              
585 587           litavis_token_push(list, LITAVIS_T_EOF, &input[pos > len ? len : pos], 0, line, col);
586 587           return list;
587             }
588              
589             #endif /* LITAVIS_TOKENISER_H */