@konomanoasa/tree-sitter-gitattributes 0.5.0 → 0.7.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 CHANGED
@@ -9,6 +9,7 @@ enum Token {
9
9
  RULE_START,
10
10
  MACRO_START,
11
11
  END_OF_FILE,
12
+ LINE_ENDING,
12
13
  LAYOUT,
13
14
  SEPARATOR,
14
15
  ATTRIBUTE_START,
@@ -37,6 +38,7 @@ enum Token {
37
38
  PATH_SEPARATOR,
38
39
  ESCAPE,
39
40
  QUOTED_ESCAPE,
41
+ ESCAPE_PREFIX,
40
42
  SET_OPEN,
41
43
  SET_NEGATION,
42
44
  SET_TEXT,
@@ -49,6 +51,23 @@ enum Token {
49
51
  CLASS_OPEN,
50
52
  CLASS_NAME,
51
53
  CLASS_CLOSE,
54
+ MISSING_SET_CLOSE,
55
+ INCOMPLETE_SET_CLOSE,
56
+ INVALID_ENCODING,
57
+ NEGATIVE_PATTERN,
58
+ ENDED_ESCAPE,
59
+ INCOMPLETE_ESCAPE,
60
+ INVALID_QUOTED_ESCAPE,
61
+ ENDED_QUOTED_ESCAPE,
62
+ INCOMPLETE_QUOTED_ESCAPE,
63
+ MISSING_QUOTE_CLOSE,
64
+ INCOMPLETE_QUOTE_CLOSE,
65
+ MISSING_ATTRIBUTE_SEPARATOR,
66
+ MISSING_ATTRIBUTE_NAME,
67
+ INCOMPLETE_ATTRIBUTE_NAME,
68
+ INVALID_NAME_CHARACTER,
69
+ INCOMPLETE_NAME_CHARACTER,
70
+ INVALID_NAME_ESCAPE,
52
71
  ERROR_SENTINEL
53
72
  };
54
73
 
@@ -69,10 +88,10 @@ typedef struct {
69
88
  typedef struct {
70
89
  uint32_t position, leading, line_end, content_end;
71
90
  uint32_t token_end, name_start, name_end;
72
- uint32_t next_end, set_end, set_close_end;
91
+ uint32_t next_end, prefix_end, set_end, set_close_end;
73
92
  Class class;
74
93
  uint32_t lower_end, range_operator_end, upper_end;
75
- uint8_t kind, next, lower_kind, upper_kind;
94
+ uint8_t kind, next, prefix_kind, lower_kind, upper_kind;
76
95
  uint8_t quoted, component_start, after_separator;
77
96
  } Scanner;
78
97
 
@@ -90,6 +109,16 @@ static bool whitespace(int32_t c) {
90
109
  return c == ' ' || c == '\t' || c == '\r';
91
110
  }
92
111
 
112
+ static bool alphanumeric(int32_t c) {
113
+ return (c >= 'a' && c <= 'z') ||
114
+ (c >= 'A' && c <= 'Z') ||
115
+ (c >= '0' && c <= '9');
116
+ }
117
+
118
+ static bool name_character(int32_t c, bool first) {
119
+ return alphanumeric(c) || c == '_' || c == '.' || (!first && c == '-');
120
+ }
121
+
93
122
  static void advance(Scanner *s, TSLexer *lexer) {
94
123
  lexer->advance(lexer, false);
95
124
  s->position++;
@@ -101,6 +130,19 @@ static bool emit(TSLexer *lexer, const bool *valid, enum Token token) {
101
130
  return true;
102
131
  }
103
132
 
133
+ static enum Token
134
+ absent(TSLexer *lexer, enum Token missing, enum Token incomplete) {
135
+ return lexer->eof(lexer) ? incomplete : missing;
136
+ }
137
+
138
+ static bool invalid_run(Scanner *s, TSLexer *lexer, const bool *valid) {
139
+ do advance(s, lexer);
140
+ while (!lexer->eof(lexer) && lexer->lookahead == DECODE_ERROR);
141
+ lexer->mark_end(lexer);
142
+ if (!lexer->eof(lexer)) lexer->advance(lexer, false);
143
+ return emit(lexer, valid, INVALID_ENCODING);
144
+ }
145
+
104
146
  static bool consume(
105
147
  Scanner *s,
106
148
  TSLexer *lexer,
@@ -108,16 +150,8 @@ static bool consume(
108
150
  enum Token token,
109
151
  uint32_t end
110
152
  ) {
111
- if (!valid[token]) return false;
112
- while (s->position < end) {
113
- if (
114
- lexer->eof(lexer) ||
115
- lexer->lookahead == '\n' ||
116
- lexer->lookahead == DECODE_ERROR
117
- )
118
- return false;
119
- advance(s, lexer);
120
- }
153
+ if (token == INVALID_ENCODING) return invalid_run(s, lexer, valid);
154
+ while (s->position < end) advance(s, lexer);
121
155
  lexer->mark_end(lexer);
122
156
  return emit(lexer, valid, token);
123
157
  }
@@ -134,13 +168,8 @@ static bool text_run(
134
168
  enum Token token,
135
169
  uint32_t end
136
170
  ) {
137
- if (lexer->lookahead == DECODE_ERROR) return false;
138
- while (
139
- s->position < end &&
140
- !lexer->eof(lexer) &&
141
- lexer->lookahead != '\n' &&
142
- lexer->lookahead != DECODE_ERROR
143
- )
171
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
172
+ while (s->position < end && lexer->lookahead != DECODE_ERROR)
144
173
  advance(s, lexer);
145
174
  lexer->mark_end(lexer);
146
175
  return emit(lexer, valid, token);
@@ -178,9 +207,12 @@ static int32_t simple_escape(int32_t c) {
178
207
  static void quoted_escape(Cursor *r) {
179
208
  const int32_t c = r->lexer->lookahead;
180
209
  r->kind = QUOTED_ESCAPE;
181
- if (r->raw >= r->limit || c == DECODE_ERROR) {
210
+ if (r->raw >= r->limit) {
211
+ r->c = INVALID_UNIT;
212
+ r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
213
+ } else if (c == DECODE_ERROR) {
182
214
  r->c = INVALID_UNIT;
183
- r->kind = ERROR_SENTINEL;
215
+ r->kind = ESCAPE_PREFIX;
184
216
  } else if (c >= '0' && c <= '3') {
185
217
  int32_t value = 0;
186
218
  unsigned digits = 0;
@@ -194,12 +226,14 @@ static void quoted_escape(Cursor *r) {
194
226
  if (digits == 3) r->c = value;
195
227
  else {
196
228
  r->c = INVALID_UNIT;
197
- r->kind = ERROR_SENTINEL;
229
+ r->kind = r->raw == r->limit
230
+ ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
231
+ : ENDED_QUOTED_ESCAPE;
198
232
  }
199
233
  } else {
200
234
  raw_step(r);
201
235
  r->c = simple_escape(c);
202
- if (r->c == INVALID_UNIT) r->kind = ERROR_SENTINEL;
236
+ if (r->c == INVALID_UNIT) r->kind = INVALID_QUOTED_ESCAPE;
203
237
  }
204
238
  }
205
239
 
@@ -212,7 +246,7 @@ static void step(Cursor *r) {
212
246
  raw_step(r);
213
247
  r->c = c;
214
248
  if (c == DECODE_ERROR) {
215
- r->kind = ERROR_SENTINEL;
249
+ r->kind = INVALID_ENCODING;
216
250
  while (r->raw < r->limit && r->lexer->lookahead == DECODE_ERROR)
217
251
  raw_step(r);
218
252
  } else if (r->s->quoted && r->start < r->s->content_end && c == '\\')
@@ -369,7 +403,9 @@ static Item item(Cursor *r) {
369
403
  if (r->kind) a.kind = r->kind;
370
404
  step(r);
371
405
  if (c == '\\') {
372
- if (r->c < 0) a.kind = ERROR_SENTINEL;
406
+ if (r->c == CURSOR_END)
407
+ a.kind = absent(r->lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
408
+ else if (r->c < 0) a.kind = ESCAPE_PREFIX;
373
409
  else {
374
410
  a.kind = ESCAPE;
375
411
  a.end = r->end;
@@ -391,18 +427,35 @@ static bool literal_boundary(const Cursor *r) {
391
427
  return c == '\\' || c == '*' || c == '?' || c == '/' || c == '[';
392
428
  }
393
429
 
394
- // Consume lookahead units fully so edits inside them invalidate the decision.
430
+ static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
431
+ s->next = ESCAPE_PREFIX;
432
+ s->prefix_end = prefix_end;
433
+ s->prefix_kind = r->kind;
434
+ if (r->kind == ESCAPE_PREFIX) {
435
+ s->prefix_end = r->end;
436
+ s->prefix_kind = INVALID_ENCODING;
437
+ step(r);
438
+ }
439
+ s->next_end = r->end;
440
+ }
441
+
395
442
  static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
396
443
  Cursor r = cursor(s, lexer, s->content_end);
397
444
  bool separator = r.c == '/';
398
445
  const uint32_t start = s->position;
399
446
  s->next_end = r.end;
400
- if (r.c < 0 || (r.c == '!' && start == content_start(s))) return false;
447
+ if (r.c < 0) {
448
+ if (r.kind == ESCAPE_PREFIX) hold_prefix(s, &r, r.end);
449
+ else s->next = r.kind;
450
+ } else if (r.c == '!' && start == content_start(s))
451
+ s->next = NEGATIVE_PATTERN;
401
452
  else if (r.c == '?') s->next = SINGLE_CHARACTER;
402
453
  else if (r.c == '/') s->next = PATH_SEPARATOR;
403
454
  else if (r.c == '\\') {
404
455
  step(&r);
405
- if (r.c < 0) return false;
456
+ if (r.c == CURSOR_END)
457
+ s->next = absent(lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
458
+ else if (r.c < 0) hold_prefix(s, &r, s->next_end);
406
459
  else {
407
460
  s->next = ESCAPE;
408
461
  s->next_end = r.end;
@@ -415,11 +468,11 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
415
468
  s->next_end = r.end;
416
469
  step(&r);
417
470
  }
418
- const bool pair = count == 2 && s->component_start;
471
+ const bool multiple = count >= 2 && s->component_start;
419
472
  const bool tail = r.c == CURSOR_END && s->after_separator;
420
473
  if (r.c == '\\') step(&r);
421
474
  const bool slash = r.c == '/';
422
- s->next = pair && (slash || tail) ? RECURSIVE_WILDCARD : WILDCARD;
475
+ s->next = multiple && (slash || tail) ? RECURSIVE_WILDCARD : WILDCARD;
423
476
  } else if (r.kind == QUOTED_ESCAPE && r.c != '[') s->next = QUOTED_ESCAPE;
424
477
  else if (r.c == '[') {
425
478
  s->next = SET_OPEN;
@@ -438,7 +491,7 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
438
491
  s->component_start = false;
439
492
  return emit(lexer, valid, LITERAL);
440
493
  }
441
- s->component_start = separator;
494
+ s->component_start = separator || s->next == NEGATIVE_PATTERN;
442
495
  if (separator) s->after_separator = true;
443
496
  return emit(lexer, valid, GLOB_START);
444
497
  }
@@ -485,6 +538,7 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
485
538
  if (s->next_end == start) {
486
539
  s->next = current.kind;
487
540
  s->next_end = current.end;
541
+ if (current.kind == ESCAPE_PREFIX) hold_prefix(s, &r, current.end);
488
542
  }
489
543
  break;
490
544
  }
@@ -519,7 +573,7 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
519
573
  if (!next_class(&r, false, &s->class)) s->class.start = NONE;
520
574
  return emit(lexer, valid, COMPOUND_CLOSE);
521
575
  }
522
- if (lexer->lookahead == DECODE_ERROR) return false;
576
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
523
577
  Cursor r = cursor(s, lexer, s->class.closing_start);
524
578
  const enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
525
579
  uint32_t end = r.end;
@@ -550,20 +604,26 @@ static bool range_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
550
604
  );
551
605
  }
552
606
 
553
- static enum Token name_token(const Cursor *r) {
554
- if (r->c < 0) return ERROR_SENTINEL;
607
+ static enum Token name_token(const Cursor *r, bool first) {
608
+ if (r->c < 0) return (enum Token)r->kind;
609
+ if (!r->kind && r->c == '\r' && r->lexer->eof(r->lexer))
610
+ return INCOMPLETE_NAME_CHARACTER;
611
+ if (!name_character(r->c, first))
612
+ return r->kind == QUOTED_ESCAPE ? INVALID_NAME_ESCAPE
613
+ : INVALID_NAME_CHARACTER;
555
614
  return r->kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
556
615
  }
557
616
 
558
617
  static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
559
- if (lexer->lookahead == DECODE_ERROR) return false;
618
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
560
619
  Cursor r = cursor(s, lexer, s->name_end);
561
- const enum Token token = name_token(&r);
620
+ if (r.kind == ESCAPE_PREFIX) s->next = INVALID_ENCODING;
621
+ const enum Token token = name_token(&r, s->position == s->name_start);
562
622
  uint32_t end = r.end;
563
623
  lexer->mark_end(lexer);
564
- if (token == NAME_TEXT) {
624
+ if (token == NAME_TEXT || token == INVALID_NAME_CHARACTER) {
565
625
  step(&r);
566
- while (name_token(&r) == token) {
626
+ while (name_token(&r, false) == token) {
567
627
  end = r.end;
568
628
  lexer->mark_end(lexer);
569
629
  step(&r);
@@ -587,6 +647,21 @@ static bool prepare_attribute(Scanner *s, TSLexer *lexer, const bool *valid) {
587
647
  return emit(lexer, valid, ATTRIBUTE_START);
588
648
  }
589
649
 
650
+ static bool end_line(Scanner *s, TSLexer *lexer, const bool *valid) {
651
+ enum Token token = END_OF_FILE;
652
+ if (!lexer->eof(lexer)) {
653
+ if (!valid[LINE_ENDING]) return false;
654
+ if (lexer->lookahead == '\r') advance(s, lexer);
655
+ if (lexer->lookahead != '\n') return false;
656
+ advance(s, lexer);
657
+ token = LINE_ENDING;
658
+ }
659
+ lexer->mark_end(lexer);
660
+ if (!emit(lexer, valid, token)) return false;
661
+ *s = (Scanner){0};
662
+ return true;
663
+ }
664
+
590
665
  static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
591
666
  if (valid[ERROR_SENTINEL]) return false;
592
667
  if (valid[LINE_START]) return start_line(s, lexer, valid);
@@ -599,11 +674,19 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
599
674
  return start_content(s, lexer, valid);
600
675
  lexer->mark_end(lexer);
601
676
  if (
602
- valid[SET_CLOSE] &&
603
- s->position == s->set_end &&
604
- s->set_close_end > s->set_end
605
- )
606
- return consume(s, lexer, valid, SET_CLOSE, s->set_close_end);
677
+ (valid[SET_CLOSE] ||
678
+ valid[MISSING_SET_CLOSE] ||
679
+ valid[INCOMPLETE_SET_CLOSE]) &&
680
+ s->position == s->set_end
681
+ ) {
682
+ if (s->set_close_end > s->set_end)
683
+ return consume(s, lexer, valid, SET_CLOSE, s->set_close_end);
684
+ return emit(
685
+ lexer,
686
+ valid,
687
+ absent(lexer, MISSING_SET_CLOSE, INCOMPLETE_SET_CLOSE)
688
+ );
689
+ }
607
690
  if (valid[ATTRIBUTE_START]) return prepare_attribute(s, lexer, valid);
608
691
  if (valid[ATTRIBUTE_END] && s->position == s->token_end)
609
692
  return emit(lexer, valid, ATTRIBUTE_END);
@@ -616,6 +699,15 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
616
699
  return emit(lexer, valid, VALUE_START);
617
700
  if (valid[VALUE_END] && s->position == s->token_end)
618
701
  return emit(lexer, valid, VALUE_END);
702
+ if (
703
+ (valid[MISSING_ATTRIBUTE_NAME] || valid[INCOMPLETE_ATTRIBUTE_NAME]) &&
704
+ s->position == s->name_end
705
+ )
706
+ return emit(
707
+ lexer,
708
+ valid,
709
+ absent(lexer, MISSING_ATTRIBUTE_NAME, INCOMPLETE_ATTRIBUTE_NAME)
710
+ );
619
711
  if (
620
712
  (valid[UNSET_MARKER] || valid[UNSPECIFIED_MARKER]) &&
621
713
  s->position < s->name_start
@@ -632,16 +724,22 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
632
724
  if (valid[ASSIGNMENT_OPERATOR] && lexer->lookahead == '=')
633
725
  return take(s, lexer, valid, ASSIGNMENT_OPERATOR);
634
726
  if (
635
- valid[QUOTE_CLOSE] &&
727
+ (valid[QUOTE_CLOSE] ||
728
+ valid[MISSING_QUOTE_CLOSE] ||
729
+ valid[INCOMPLETE_QUOTE_CLOSE]) &&
636
730
  s->quoted &&
637
- s->position == s->content_end &&
638
- s->content_end < s->line_end
639
- )
640
- return take(s, lexer, valid, QUOTE_CLOSE);
731
+ s->position == s->content_end
732
+ ) {
733
+ if (s->content_end < s->line_end) return take(s, lexer, valid, QUOTE_CLOSE);
734
+ return emit(
735
+ lexer,
736
+ valid,
737
+ absent(lexer, MISSING_QUOTE_CLOSE, INCOMPLETE_QUOTE_CLOSE)
738
+ );
739
+ }
641
740
  if (valid[PATTERN_END] && s->position >= s->content_end)
642
741
  return emit(lexer, valid, PATTERN_END);
643
- if (s->position >= s->line_end)
644
- return lexer->eof(lexer) && emit(lexer, valid, END_OF_FILE);
742
+ if (s->position >= s->line_end) return end_line(s, lexer, valid);
645
743
  if (s->kind == COMMENT_START) {
646
744
  if (s->position == s->leading) return take(s, lexer, valid, COMMENT_MARKER);
647
745
  return text_run(s, lexer, valid, COMMENT_TEXT, s->line_end);
@@ -651,17 +749,15 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
651
749
  if (valid[MACRO_MARKER])
652
750
  return consume(s, lexer, valid, MACRO_MARKER, s->name_start);
653
751
  if (valid[SEPARATOR] || valid[LAYOUT]) {
654
- if (!whitespace(lexer->lookahead)) return false;
752
+ if (!whitespace(lexer->lookahead))
753
+ return emit(lexer, valid, MISSING_ATTRIBUTE_SEPARATOR);
655
754
  while (s->position < s->line_end && whitespace(lexer->lookahead))
656
755
  advance(s, lexer);
657
756
  lexer->mark_end(lexer);
658
757
  return emit(lexer, valid, s->position < s->line_end ? SEPARATOR : LAYOUT);
659
758
  }
660
- if (valid[GLOB_START] && s->position < s->content_end)
661
- return prepare_glob(s, lexer, valid);
662
- if (
663
- (valid[SET_ITEM_START] || valid[SET_NEGATION]) && s->position < s->set_end
664
- )
759
+ if (valid[GLOB_START]) return prepare_glob(s, lexer, valid);
760
+ if (valid[SET_ITEM_START] || valid[SET_NEGATION])
665
761
  return prepare_set(s, lexer, valid);
666
762
  if (s->class.start < s->position && s->position < s->class.end)
667
763
  return class_piece(s, lexer, valid);
@@ -673,6 +769,10 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
673
769
  const enum Token token = (enum Token)s->next;
674
770
  if (token == CLASS_OPEN)
675
771
  return class_delimiter(s, lexer, valid, COMPOUND_OPEN);
772
+ if (token == ESCAPE_PREFIX) {
773
+ s->next = s->prefix_kind;
774
+ return consume(s, lexer, valid, token, s->prefix_end);
775
+ }
676
776
  return consume(s, lexer, valid, token, s->next_end);
677
777
  }
678
778
 
package/tree-sitter.json CHANGED
@@ -12,7 +12,7 @@
12
12
  }
13
13
  ],
14
14
  "metadata": {
15
- "version": "0.5.0",
15
+ "version": "0.7.0",
16
16
  "license": "MIT",
17
17
  "description": "Tree-sitter grammar for gitattributes.",
18
18
  "links": {
@@ -22,7 +22,7 @@
22
22
  "bindings": {
23
23
  "c": false,
24
24
  "go": false,
25
- "node": false,
25
+ "node": true,
26
26
  "python": true,
27
27
  "rust": true,
28
28
  "swift": false