@konomanoasa/tree-sitter-gitattributes 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/scanner.c ADDED
@@ -0,0 +1,841 @@
1
+ #include "tree_sitter/alloc.h"
2
+ #include "tree_sitter/parser.h"
3
+ #include <string.h>
4
+
5
+ enum Token {
6
+ LINE_START,
7
+ BLANK_START,
8
+ COMMENT_START,
9
+ RULE_START,
10
+ MACRO_START,
11
+ END_OF_FILE,
12
+ LINE_ENDING,
13
+ LAYOUT,
14
+ SEPARATOR,
15
+ ATTRIBUTE_START,
16
+ ATTRIBUTE_END,
17
+ NAME_START,
18
+ NAME_END,
19
+ VALUE_START,
20
+ VALUE_END,
21
+ PATTERN_END,
22
+ GLOB_START,
23
+ SET_ITEM_START,
24
+ COMMENT_MARKER,
25
+ COMMENT_TEXT,
26
+ MACRO_MARKER,
27
+ UNSET_MARKER,
28
+ UNSPECIFIED_MARKER,
29
+ ASSIGNMENT_OPERATOR,
30
+ NAME_TEXT,
31
+ VALUE_TEXT,
32
+ QUOTE_OPEN,
33
+ QUOTE_CLOSE,
34
+ LITERAL,
35
+ WILDCARD,
36
+ RECURSIVE_WILDCARD,
37
+ SINGLE_CHARACTER,
38
+ PATH_SEPARATOR,
39
+ ESCAPE,
40
+ QUOTED_ESCAPE,
41
+ ESCAPE_PREFIX,
42
+ SET_OPEN,
43
+ SET_NEGATION,
44
+ SET_TEXT,
45
+ SET_CLOSE,
46
+ RANGE_START,
47
+ RANGE_CHARACTER,
48
+ RANGE_OPERATOR,
49
+ COMPOUND_OPEN,
50
+ COMPOUND_CLOSE,
51
+ CLASS_OPEN,
52
+ CLASS_NAME,
53
+ CLASS_CLOSE,
54
+ MISSING_SET_CLOSE,
55
+ INCOMPLETE_SET_CLOSE,
56
+ INVALID_ENCODING,
57
+ NEGATIVE_PATTERN,
58
+ INVALID_ESCAPE,
59
+ INCOMPLETE_ESCAPE,
60
+ INVALID_QUOTED_ESCAPE,
61
+ ENDED_QUOTED_ESCAPE,
62
+ INCOMPLETE_QUOTED_ESCAPE,
63
+ MISSING_QUOTE,
64
+ INCOMPLETE_QUOTE,
65
+ MISSING_SEPARATOR,
66
+ MISSING_NAME,
67
+ INCOMPLETE_NAME,
68
+ INVALID_NAME_CHARACTER,
69
+ ERROR_SENTINEL
70
+ };
71
+
72
+ #define NONE UINT32_MAX
73
+
74
+ // A bracket item: text, an escape, or a class term such as `[:alpha:]`.
75
+ typedef struct {
76
+ uint32_t kind, end, opening_end, closing_start;
77
+ } Item;
78
+
79
+ // Positions count source characters from the current line start. Quoted units
80
+ // are characters or C escapes; ranges stay on the original source. Zero-width
81
+ // start tokens save lookahead decisions before their source is consumed.
82
+ typedef struct {
83
+ uint32_t position, leading, line_end, content_start, content_end, kind;
84
+ uint32_t token_end, name_start, name_end;
85
+ uint32_t next, next_end, prefix_end, prefix_kind;
86
+ uint32_t set_end, set_close_end, class_start, range_operator_end;
87
+ Item class, lower, upper;
88
+ bool quoted, component_start, after_separator;
89
+ } Scanner;
90
+
91
+ typedef char scanner_fits_buffer
92
+ [sizeof(Scanner) <= TREE_SITTER_SERIALIZATION_BUFFER_SIZE ? 1 : -1];
93
+
94
+ // Negative c values: -1 decode failure, -2 cursor limit, -3 invalid or
95
+ // incomplete escape.
96
+ typedef struct {
97
+ Scanner *s;
98
+ TSLexer *lexer;
99
+ uint32_t raw, limit, start, end, kind;
100
+ int32_t c;
101
+ } Cursor;
102
+
103
+ static bool whitespace(int32_t c) {
104
+ return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r';
105
+ }
106
+
107
+ static bool alphanumeric(int32_t c) {
108
+ return (c >= 'a' && c <= 'z') ||
109
+ (c >= 'A' && c <= 'Z') ||
110
+ (c >= '0' && c <= '9');
111
+ }
112
+
113
+ static bool name_character(int32_t c, bool first) {
114
+ return alphanumeric(c) || c == '_' || c == '.' || (!first && c == '-');
115
+ }
116
+
117
+ static void advance(Scanner *s, TSLexer *lexer) {
118
+ lexer->advance(lexer, false);
119
+ s->position++;
120
+ }
121
+
122
+ static bool emit(TSLexer *lexer, const bool *valid, enum Token token) {
123
+ if (!valid[token])
124
+ return false;
125
+ lexer->result_symbol = token;
126
+ return true;
127
+ }
128
+
129
+ static enum Token
130
+ absent(TSLexer *lexer, enum Token missing, enum Token incomplete) {
131
+ return lexer->eof(lexer) ? incomplete : missing;
132
+ }
133
+
134
+ static bool consume(
135
+ Scanner *s,
136
+ TSLexer *lexer,
137
+ const bool *valid,
138
+ enum Token token,
139
+ uint32_t end
140
+ ) {
141
+ while (s->position < end)
142
+ advance(s, lexer);
143
+ lexer->mark_end(lexer);
144
+ return emit(lexer, valid, token);
145
+ }
146
+
147
+ static bool text_run(
148
+ Scanner *s,
149
+ TSLexer *lexer,
150
+ const bool *valid,
151
+ enum Token token,
152
+ uint32_t end
153
+ ) {
154
+ if (lexer->lookahead == -1)
155
+ return consume(s, lexer, valid, INVALID_ENCODING, s->position + 1);
156
+ while (s->position < end && lexer->lookahead != -1)
157
+ advance(s, lexer);
158
+ lexer->mark_end(lexer);
159
+ return emit(lexer, valid, token);
160
+ }
161
+
162
+ static void raw_step(Cursor *r) {
163
+ r->lexer->advance(r->lexer, false);
164
+ r->raw++;
165
+ }
166
+
167
+ // Quoted units decode named C escapes or three octal digits starting with 0
168
+ // to 3.
169
+ static void step(Cursor *r) {
170
+ r->start = r->end = r->raw;
171
+ r->kind = 0;
172
+ r->c = -2;
173
+ if (r->raw >= r->limit)
174
+ return;
175
+ int32_t c = r->lexer->lookahead;
176
+ raw_step(r);
177
+ r->c = c;
178
+ if (c == -1)
179
+ r->kind = INVALID_ENCODING;
180
+ if (r->s->quoted && r->start < r->s->content_end && c == '\\') {
181
+ r->kind = QUOTED_ESCAPE;
182
+ c = r->lexer->lookahead;
183
+ if (r->raw >= r->limit) {
184
+ r->c = -3;
185
+ r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
186
+ } else if (c == -1) {
187
+ r->c = -3;
188
+ r->kind = ESCAPE_PREFIX;
189
+ } else if (c >= '0' && c <= '3') {
190
+ int32_t value = 0;
191
+ unsigned digits = 0;
192
+ while (digits < 3 && r->raw < r->limit) {
193
+ int32_t digit = r->lexer->lookahead;
194
+ if (digit < '0' || digit > '7')
195
+ break;
196
+ value = value * 8 + digit - '0';
197
+ raw_step(r);
198
+ digits++;
199
+ }
200
+ r->c = value;
201
+ if (digits != 3) {
202
+ r->c = -3;
203
+ r->kind = r->raw == r->limit
204
+ ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
205
+ : ENDED_QUOTED_ESCAPE;
206
+ }
207
+ } else {
208
+ raw_step(r);
209
+ switch (c) {
210
+ case 'a':
211
+ r->c = '\a';
212
+ break;
213
+ case 'b':
214
+ r->c = '\b';
215
+ break;
216
+ case 't':
217
+ r->c = '\t';
218
+ break;
219
+ case 'n':
220
+ r->c = '\n';
221
+ break;
222
+ case 'v':
223
+ r->c = '\v';
224
+ break;
225
+ case 'f':
226
+ r->c = '\f';
227
+ break;
228
+ case 'r':
229
+ r->c = '\r';
230
+ break;
231
+ case '\\':
232
+ case '"':
233
+ r->c = c;
234
+ break;
235
+ default:
236
+ r->c = -3;
237
+ r->kind = INVALID_QUOTED_ESCAPE;
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ r->end = r->raw;
243
+ }
244
+
245
+ static Cursor cursor(Scanner *s, TSLexer *lexer, uint32_t limit) {
246
+ Cursor r = {.s = s, .lexer = lexer, .raw = s->position, .limit = limit};
247
+ step(&r);
248
+ return r;
249
+ }
250
+
251
+ // An unclosed quote leaves the pattern content ending at the line end.
252
+ static bool start_line(Scanner *s, TSLexer *lexer, const bool *valid) {
253
+ if (lexer->eof(lexer))
254
+ return false;
255
+ Scanner line;
256
+ memset(&line, 0, sizeof(line));
257
+ int32_t last = 0;
258
+ lexer->mark_end(lexer);
259
+ while (!lexer->eof(lexer) && whitespace(lexer->lookahead)) {
260
+ last = lexer->lookahead;
261
+ advance(&line, lexer);
262
+ }
263
+ line.leading = line.position;
264
+ line.kind = lexer->lookahead == '#' ? COMMENT_START : RULE_START;
265
+ line.quoted = lexer->lookahead == '"';
266
+ line.content_start = line.leading + (line.quoted ? 1 : 0);
267
+ line.content_end = NONE;
268
+ bool escaped = false;
269
+ while (!lexer->eof(lexer) && lexer->lookahead != '\n') {
270
+ int32_t c = lexer->lookahead;
271
+ if (line.position >= line.content_start && line.content_end == NONE) {
272
+ if (line.quoted) {
273
+ if (c == '"' && !escaped)
274
+ line.content_end = line.position;
275
+ escaped = c == '\\' && !escaped;
276
+ } else if (whitespace(c))
277
+ line.content_end = line.position;
278
+ }
279
+ last = c;
280
+ advance(&line, lexer);
281
+ }
282
+ line.line_end = line.position;
283
+ if (last == '\r' && lexer->lookahead == '\n')
284
+ line.line_end--;
285
+ // The CR of a CRLF ending is neither leading whitespace nor content.
286
+ if (line.leading > line.line_end)
287
+ line.leading = line.line_end;
288
+ if (line.content_end == NONE || line.content_end > line.line_end)
289
+ line.content_end = line.line_end;
290
+ if (line.leading >= line.line_end)
291
+ line.kind = BLANK_START;
292
+ line.position = 0;
293
+ line.class_start = NONE;
294
+ line.component_start = true;
295
+ *s = line;
296
+ return emit(lexer, valid, LINE_START);
297
+ }
298
+
299
+ // Compare the macro marker after decoding C escapes.
300
+ static bool start_content(Scanner *s, TSLexer *lexer, const bool *valid) {
301
+ while (s->position < s->leading)
302
+ advance(s, lexer);
303
+ lexer->mark_end(lexer);
304
+ if (s->kind == RULE_START) {
305
+ if (s->quoted)
306
+ lexer->advance(lexer, false);
307
+ Cursor r = {
308
+ .s = s,
309
+ .lexer = lexer,
310
+ .raw = s->content_start,
311
+ .limit = s->content_end
312
+ };
313
+ static const char marker[] = "[attr]";
314
+ bool macro = true;
315
+ for (unsigned i = 0; i < sizeof(marker) - 1; i++) {
316
+ step(&r);
317
+ if (r.c != marker[i]) {
318
+ macro = false;
319
+ break;
320
+ }
321
+ }
322
+ if (macro) {
323
+ s->kind = MACRO_START;
324
+ s->name_start = r.end;
325
+ s->name_end = s->content_end;
326
+ }
327
+ }
328
+ return emit(lexer, valid, (enum Token)s->kind);
329
+ }
330
+
331
+ static void skip_escaped(Cursor *r, Cursor *last, uint32_t *candidate) {
332
+ *last = *r;
333
+ if (r->c == ']')
334
+ *candidate = NONE;
335
+ step(r);
336
+ }
337
+
338
+ // Escapes and range upper endpoints take precedence over class candidates.
339
+ // A candidate starts at [: and ends at the first unescaped ], preceded by a
340
+ // colon other than the opening one. An escaped ] cancels it; candidate units
341
+ // remain ordinary items. `lower` records an available lower endpoint.
342
+ // Success leaves the cursor after the class; failure at ] or the limit.
343
+ static bool next_class(Cursor *r, bool lower, uint32_t *start, Item *class) {
344
+ Item a = {CLASS_OPEN, 0, 0, 0};
345
+ uint32_t candidate = NONE;
346
+ Cursor last = *r;
347
+ while (r->c != -2) {
348
+ int32_t here = r->c;
349
+ if (here == ']') {
350
+ if (candidate == NONE || last.c != ':' || last.end == a.opening_end)
351
+ return false;
352
+ a.closing_start = last.start;
353
+ a.end = r->end;
354
+ step(r);
355
+ *start = candidate;
356
+ *class = a;
357
+ return true;
358
+ }
359
+ last = *r;
360
+ step(r);
361
+ if (here == '\\') {
362
+ lower = r->c >= 0;
363
+ if (r->c != -2)
364
+ skip_escaped(r, &last, &candidate);
365
+ continue;
366
+ }
367
+ if (here == '-' && lower && r->c != -2 && r->c != ']') {
368
+ int32_t upper = r->c;
369
+ skip_escaped(r, &last, &candidate);
370
+ if (upper == '\\' && r->c != -2)
371
+ skip_escaped(r, &last, &candidate);
372
+ lower = false;
373
+ continue;
374
+ }
375
+ lower = here >= 0;
376
+ if (candidate == NONE && here == '[' && r->c == ':') {
377
+ candidate = last.start;
378
+ a.opening_end = r->end;
379
+ }
380
+ }
381
+ return false;
382
+ }
383
+
384
+ // The cursor must be after the opening bracket.
385
+ static void find_set(Scanner *s, Cursor *r) {
386
+ s->set_end = s->set_close_end = s->content_end;
387
+ s->class_start = NONE;
388
+ if (r->c == '!' || r->c == '^')
389
+ step(r);
390
+ bool lower = r->c == ']';
391
+ if (lower)
392
+ step(r);
393
+ Item class;
394
+ uint32_t class_start;
395
+ while (next_class(r, lower, &class_start, &class)) {
396
+ lower = false;
397
+ if (s->class_start == NONE) {
398
+ s->class_start = class_start;
399
+ s->class = class;
400
+ }
401
+ }
402
+ if (r->c == ']') {
403
+ s->set_end = r->start;
404
+ s->set_close_end = r->end;
405
+ }
406
+ }
407
+
408
+ static Item item(Cursor *r) {
409
+ Item a = {SET_TEXT, 0, 0, 0};
410
+ if (r->c == -2)
411
+ return a;
412
+ int32_t c = r->c;
413
+ uint32_t end = r->end;
414
+ if (c == '[' && r->start == r->s->class_start) {
415
+ a = r->s->class;
416
+ while (r->c != -2 && r->start < a.end)
417
+ step(r);
418
+ return a;
419
+ }
420
+ if (r->kind)
421
+ a.kind = r->kind;
422
+ step(r);
423
+ if (c == '\\') {
424
+ if (r->c == -2)
425
+ a.kind = absent(r->lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
426
+ else if (r->c < 0)
427
+ a.kind = ESCAPE_PREFIX;
428
+ else {
429
+ a.kind = ESCAPE;
430
+ a.end = r->end;
431
+ step(r);
432
+ return a;
433
+ }
434
+ }
435
+ a.end = end;
436
+ return a;
437
+ }
438
+
439
+ static bool endpoint(Item a) {
440
+ return a.end &&
441
+ (a.kind == SET_TEXT || a.kind == ESCAPE || a.kind == QUOTED_ESCAPE);
442
+ }
443
+
444
+ static bool glob_special(int32_t c) {
445
+ return c == '\\' || c == '*' || c == '?' || c == '/';
446
+ }
447
+
448
+ // End literal runs before [ so the set can be decided.
449
+ static bool special(const Cursor *r) {
450
+ if (r->c < 0 || r->kind == QUOTED_ESCAPE)
451
+ return true;
452
+ return glob_special(r->c) || r->c == '[';
453
+ }
454
+
455
+ // A backslash followed by a unit that cannot be escaped is held by the owner
456
+ // up to `prefix_end`, and the unit's own issue follows. A C escape prefix
457
+ // before an undecodable byte extends the held prefix to that byte.
458
+ static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
459
+ s->next = ESCAPE_PREFIX;
460
+ s->prefix_end = prefix_end;
461
+ s->prefix_kind = r->kind;
462
+ if (r->kind == ESCAPE_PREFIX) {
463
+ s->prefix_end = r->end;
464
+ s->prefix_kind = INVALID_ENCODING;
465
+ step(r);
466
+ }
467
+ s->next_end = r->end;
468
+ }
469
+
470
+ // Advance through every lookahead unit so edits inside it invalidate the
471
+ // decision. Nonliteral tokens are decided here and consumed by the next call.
472
+ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
473
+ lexer->mark_end(lexer);
474
+ Cursor r = cursor(s, lexer, s->content_end);
475
+ uint32_t start = s->position;
476
+ s->next_end = r.end;
477
+ if (r.c < 0) {
478
+ if (r.kind == ESCAPE_PREFIX)
479
+ hold_prefix(s, &r, r.end);
480
+ else
481
+ s->next = r.kind;
482
+ } else if (r.c == '!' && start == s->content_start)
483
+ s->next = NEGATIVE_PATTERN;
484
+ else if (r.c == '?')
485
+ s->next = SINGLE_CHARACTER;
486
+ else if (r.c == '/')
487
+ s->next = PATH_SEPARATOR;
488
+ else if (r.c == '\\') {
489
+ step(&r);
490
+ if (r.c == -2)
491
+ s->next = absent(lexer, INVALID_ESCAPE, INCOMPLETE_ESCAPE);
492
+ else if (r.c < 0)
493
+ hold_prefix(s, &r, s->next_end);
494
+ else {
495
+ s->next = ESCAPE;
496
+ s->next_end = r.end;
497
+ }
498
+ } else if (r.c == '*') {
499
+ unsigned count = 0;
500
+ while (r.c == '*') {
501
+ count++;
502
+ s->next_end = r.end;
503
+ step(&r);
504
+ }
505
+ bool pair = count == 2 && s->component_start;
506
+ bool slash = r.c == '/';
507
+ bool tail = r.c == -2 && s->after_separator;
508
+ s->next = pair && (slash || tail) ? RECURSIVE_WILDCARD : WILDCARD;
509
+ } else if (r.kind == QUOTED_ESCAPE && r.c != '[')
510
+ s->next = QUOTED_ESCAPE;
511
+ else if (r.c == '[') {
512
+ s->next = SET_OPEN;
513
+ step(&r);
514
+ find_set(s, &r);
515
+ } else {
516
+ uint32_t end = r.end;
517
+ lexer->mark_end(lexer);
518
+ step(&r);
519
+ while (!special(&r)) {
520
+ end = r.end;
521
+ lexer->mark_end(lexer);
522
+ step(&r);
523
+ }
524
+ s->position = end;
525
+ s->component_start = false;
526
+ return emit(lexer, valid, LITERAL);
527
+ }
528
+ return emit(lexer, valid, GLOB_START);
529
+ }
530
+
531
+ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
532
+ lexer->mark_end(lexer);
533
+ Cursor r = cursor(s, lexer, s->set_end);
534
+ uint32_t start = s->position;
535
+ if (valid[SET_NEGATION] && (r.c == '!' || r.c == '^')) {
536
+ s->position = r.end;
537
+ lexer->mark_end(lexer);
538
+ return emit(lexer, valid, SET_NEGATION);
539
+ }
540
+ s->next = SET_TEXT;
541
+ s->next_end = start;
542
+ uint32_t here = start;
543
+ Item current = item(&r);
544
+ for (;;) {
545
+ if (endpoint(current) && r.c == '-' && r.end < s->set_end) {
546
+ uint32_t operator_end = r.end;
547
+ bool plain_operator = r.kind != QUOTED_ESCAPE;
548
+ step(&r);
549
+ Item upper = item(&r);
550
+ if (endpoint(upper)) {
551
+ if (here == start) {
552
+ s->next = RANGE_START;
553
+ s->lower = current;
554
+ s->upper = upper;
555
+ s->range_operator_end = operator_end;
556
+ } else
557
+ s->next_end = here;
558
+ break;
559
+ }
560
+ if (current.kind == SET_TEXT) {
561
+ // A literal hyphen joins the text run; an escaped one stays a
562
+ // separate `quoted_escape`.
563
+ if (!plain_operator) {
564
+ s->next_end = current.end;
565
+ break;
566
+ }
567
+ s->next_end = operator_end;
568
+ here = operator_end;
569
+ current = upper;
570
+ continue;
571
+ }
572
+ }
573
+ if (current.kind != SET_TEXT) {
574
+ if (here == start) {
575
+ s->next = current.kind;
576
+ s->next_end = current.end;
577
+ if (current.kind == ESCAPE_PREFIX)
578
+ hold_prefix(s, &r, current.end);
579
+ } else
580
+ s->next_end = here;
581
+ break;
582
+ }
583
+ s->next_end = current.end;
584
+ here = r.start;
585
+ if (r.c == -2)
586
+ break;
587
+ current = item(&r);
588
+ }
589
+ return emit(lexer, valid, SET_ITEM_START);
590
+ }
591
+
592
+ static bool
593
+ unit_token(Scanner *s, TSLexer *lexer, const bool *valid, enum Token token) {
594
+ Cursor r = cursor(s, lexer, s->class.end);
595
+ s->position = r.end;
596
+ lexer->mark_end(lexer);
597
+ return emit(lexer, valid, token);
598
+ }
599
+
600
+ // Closing a class term must find the next class in the set.
601
+ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
602
+ if (s->position < s->class.opening_end)
603
+ return consume(s, lexer, valid, CLASS_OPEN, s->class.opening_end);
604
+ if (s->position == s->class.closing_start)
605
+ return unit_token(s, lexer, valid, CLASS_CLOSE);
606
+ if (s->position > s->class.closing_start) {
607
+ while (s->position < s->class.end)
608
+ advance(s, lexer);
609
+ lexer->mark_end(lexer);
610
+ Cursor r = cursor(s, lexer, s->set_end);
611
+ if (!next_class(&r, false, &s->class_start, &s->class))
612
+ s->class_start = NONE;
613
+ return emit(lexer, valid, COMPOUND_CLOSE);
614
+ }
615
+ Cursor r = cursor(s, lexer, s->class.closing_start);
616
+ enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
617
+ uint32_t end = r.end;
618
+ lexer->mark_end(lexer);
619
+ if (token == CLASS_NAME) {
620
+ step(&r);
621
+ while (r.c >= 0 && !r.kind) {
622
+ end = r.end;
623
+ lexer->mark_end(lexer);
624
+ step(&r);
625
+ }
626
+ }
627
+ s->position = end;
628
+ return emit(lexer, valid, token);
629
+ }
630
+
631
+ static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
632
+ Cursor r = cursor(s, lexer, s->name_end);
633
+ enum Token token;
634
+ if (r.kind == ESCAPE_PREFIX) {
635
+ s->next = INVALID_ENCODING;
636
+ s->next_end = r.end + 1;
637
+ }
638
+ if (r.c < 0)
639
+ token = (enum Token)r.kind;
640
+ else if (!name_character(r.c, s->position == s->name_start))
641
+ token = INVALID_NAME_CHARACTER;
642
+ else
643
+ token = r.kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
644
+ uint32_t end = r.end;
645
+ lexer->mark_end(lexer);
646
+ if (token == NAME_TEXT) {
647
+ step(&r);
648
+ while (r.c >= 0 && !r.kind && name_character(r.c, false)) {
649
+ end = r.end;
650
+ lexer->mark_end(lexer);
651
+ step(&r);
652
+ }
653
+ }
654
+ s->position = end;
655
+ return emit(lexer, valid, token);
656
+ }
657
+
658
+ static bool prepare_attribute(Scanner *s, TSLexer *lexer, const bool *valid) {
659
+ lexer->mark_end(lexer);
660
+ uint32_t position = s->position;
661
+ bool modified = lexer->lookahead == '-' || lexer->lookahead == '!';
662
+ s->name_start = s->position + (modified ? 1 : 0);
663
+ s->name_end = NONE;
664
+ while (position < s->line_end && !whitespace(lexer->lookahead)) {
665
+ if (lexer->lookahead == '=' && s->name_end == NONE)
666
+ s->name_end = position;
667
+ lexer->advance(lexer, false);
668
+ position++;
669
+ }
670
+ s->token_end = position;
671
+ if (s->name_end == NONE)
672
+ s->name_end = position;
673
+ return emit(lexer, valid, ATTRIBUTE_START);
674
+ }
675
+
676
+ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
677
+ if (valid[ERROR_SENTINEL])
678
+ return false;
679
+ if (valid[LINE_START])
680
+ return start_line(s, lexer, valid);
681
+ if (
682
+ valid[BLANK_START] ||
683
+ valid[COMMENT_START] ||
684
+ valid[RULE_START] ||
685
+ valid[MACRO_START]
686
+ )
687
+ return start_content(s, lexer, valid);
688
+ lexer->mark_end(lexer);
689
+ if (
690
+ (valid[SET_CLOSE] ||
691
+ valid[MISSING_SET_CLOSE] ||
692
+ valid[INCOMPLETE_SET_CLOSE]) &&
693
+ s->position == s->set_end
694
+ ) {
695
+ if (s->set_close_end > s->set_end)
696
+ return consume(s, lexer, valid, SET_CLOSE, s->set_close_end);
697
+ return emit(
698
+ lexer,
699
+ valid,
700
+ absent(lexer, MISSING_SET_CLOSE, INCOMPLETE_SET_CLOSE)
701
+ );
702
+ }
703
+ if (valid[ATTRIBUTE_START])
704
+ return prepare_attribute(s, lexer, valid);
705
+ if (valid[ATTRIBUTE_END] && s->position == s->token_end)
706
+ return emit(lexer, valid, ATTRIBUTE_END);
707
+ bool at_name = s->position == s->name_start && s->position < s->name_end;
708
+ if (valid[NAME_START] && at_name)
709
+ return emit(lexer, valid, NAME_START);
710
+ if (valid[NAME_END] && s->position == s->name_end)
711
+ return emit(lexer, valid, NAME_END);
712
+ if (valid[VALUE_START] && s->position < s->token_end)
713
+ return emit(lexer, valid, VALUE_START);
714
+ if (valid[VALUE_END] && s->position == s->token_end)
715
+ return emit(lexer, valid, VALUE_END);
716
+ if (valid[MISSING_NAME] && s->position == s->name_end)
717
+ return emit(lexer, valid, absent(lexer, MISSING_NAME, INCOMPLETE_NAME));
718
+ if (
719
+ (valid[QUOTE_CLOSE] || valid[MISSING_QUOTE]) &&
720
+ s->quoted &&
721
+ s->position == s->content_end
722
+ ) {
723
+ if (s->content_end < s->line_end)
724
+ return consume(s, lexer, valid, QUOTE_CLOSE, s->position + 1);
725
+ return emit(lexer, valid, absent(lexer, MISSING_QUOTE, INCOMPLETE_QUOTE));
726
+ }
727
+ if (valid[PATTERN_END] && s->position >= s->content_end)
728
+ return emit(lexer, valid, PATTERN_END);
729
+ if (s->position >= s->line_end) {
730
+ if (lexer->eof(lexer))
731
+ return emit(lexer, valid, END_OF_FILE);
732
+ if (lexer->lookahead == '\r')
733
+ advance(s, lexer);
734
+ if (lexer->lookahead != '\n')
735
+ return false;
736
+ advance(s, lexer);
737
+ lexer->mark_end(lexer);
738
+ return emit(lexer, valid, LINE_ENDING);
739
+ }
740
+ if (s->kind == COMMENT_START) {
741
+ if (s->position == s->leading)
742
+ return consume(s, lexer, valid, COMMENT_MARKER, s->position + 1);
743
+ return text_run(s, lexer, valid, COMMENT_TEXT, s->line_end);
744
+ }
745
+ if (valid[QUOTE_OPEN] && s->quoted && s->position == s->leading)
746
+ return consume(s, lexer, valid, QUOTE_OPEN, s->position + 1);
747
+ if (valid[MACRO_MARKER])
748
+ return consume(s, lexer, valid, MACRO_MARKER, s->name_start);
749
+ if (
750
+ (valid[UNSET_MARKER] || valid[UNSPECIFIED_MARKER]) &&
751
+ s->position < s->name_start
752
+ )
753
+ return consume(
754
+ s,
755
+ lexer,
756
+ valid,
757
+ lexer->lookahead == '-' ? UNSET_MARKER : UNSPECIFIED_MARKER,
758
+ s->position + 1
759
+ );
760
+ if (valid[NAME_TEXT])
761
+ return name_piece(s, lexer, valid);
762
+ if (valid[VALUE_TEXT])
763
+ return text_run(s, lexer, valid, VALUE_TEXT, s->token_end);
764
+ if (valid[ASSIGNMENT_OPERATOR] && lexer->lookahead == '=')
765
+ return consume(s, lexer, valid, ASSIGNMENT_OPERATOR, s->position + 1);
766
+ if (valid[SEPARATOR] || valid[LAYOUT]) {
767
+ if (!whitespace(lexer->lookahead))
768
+ return emit(lexer, valid, MISSING_SEPARATOR);
769
+ while (s->position < s->line_end && whitespace(lexer->lookahead))
770
+ advance(s, lexer);
771
+ lexer->mark_end(lexer);
772
+ return emit(lexer, valid, s->position < s->line_end ? SEPARATOR : LAYOUT);
773
+ }
774
+ if (valid[GLOB_START])
775
+ return prepare_glob(s, lexer, valid);
776
+ if (valid[SET_ITEM_START] || valid[SET_NEGATION])
777
+ return prepare_set(s, lexer, valid);
778
+ if (s->class_start < s->position && s->position < s->class.end)
779
+ return class_piece(s, lexer, valid);
780
+ if (s->next == RANGE_START) {
781
+ s->next = RANGE_CHARACTER;
782
+ return emit(lexer, valid, RANGE_START);
783
+ }
784
+ if (s->upper.end > s->position) {
785
+ if (s->position == s->lower.end)
786
+ return consume(s, lexer, valid, RANGE_OPERATOR, s->range_operator_end);
787
+ Item a = s->position < s->lower.end ? s->lower : s->upper;
788
+ return consume(
789
+ s,
790
+ lexer,
791
+ valid,
792
+ a.kind == SET_TEXT ? RANGE_CHARACTER : (enum Token)a.kind,
793
+ a.end
794
+ );
795
+ }
796
+ enum Token token = (enum Token)s->next;
797
+ if (token == CLASS_OPEN)
798
+ return unit_token(s, lexer, valid, COMPOUND_OPEN);
799
+ if (token == ESCAPE_PREFIX) {
800
+ s->next = s->prefix_kind;
801
+ return consume(s, lexer, valid, token, s->prefix_end);
802
+ }
803
+ s->component_start = token == PATH_SEPARATOR || token == NEGATIVE_PATTERN;
804
+ if (token == PATH_SEPARATOR)
805
+ s->after_separator = true;
806
+ return consume(s, lexer, valid, token, s->next_end);
807
+ }
808
+
809
+ void *tree_sitter_gitattributes_external_scanner_create(void) {
810
+ return ts_calloc(1, sizeof(Scanner));
811
+ }
812
+ void tree_sitter_gitattributes_external_scanner_destroy(void *payload) {
813
+ ts_free(payload);
814
+ }
815
+ unsigned tree_sitter_gitattributes_external_scanner_serialize(
816
+ void *payload,
817
+ char *buffer
818
+ ) {
819
+ memcpy(buffer, payload, sizeof(Scanner));
820
+ return sizeof(Scanner);
821
+ }
822
+ void tree_sitter_gitattributes_external_scanner_deserialize(
823
+ void *payload,
824
+ const char *buffer,
825
+ unsigned length
826
+ ) {
827
+ memset(payload, 0, sizeof(Scanner));
828
+ if (length == sizeof(Scanner))
829
+ memcpy(payload, buffer, length);
830
+ }
831
+ bool tree_sitter_gitattributes_external_scanner_scan(
832
+ void *payload,
833
+ TSLexer *lexer,
834
+ const bool *valid
835
+ ) {
836
+ Scanner next = *(Scanner *)payload;
837
+ if (!scan(&next, lexer, valid))
838
+ return false;
839
+ *(Scanner *)payload = next;
840
+ return true;
841
+ }