@konomanoasa/tree-sitter-gitattributes 0.5.0 → 0.6.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,20 @@ 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
+ // Include the next decoded character so edits invalidate the run.
139
+ static bool invalid_run(Scanner *s, TSLexer *lexer, const bool *valid) {
140
+ do advance(s, lexer);
141
+ while (!lexer->eof(lexer) && lexer->lookahead == DECODE_ERROR);
142
+ lexer->mark_end(lexer);
143
+ if (!lexer->eof(lexer)) lexer->advance(lexer, false);
144
+ return emit(lexer, valid, INVALID_ENCODING);
145
+ }
146
+
104
147
  static bool consume(
105
148
  Scanner *s,
106
149
  TSLexer *lexer,
@@ -108,16 +151,8 @@ static bool consume(
108
151
  enum Token token,
109
152
  uint32_t end
110
153
  ) {
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
- }
154
+ if (token == INVALID_ENCODING) return invalid_run(s, lexer, valid);
155
+ while (s->position < end) advance(s, lexer);
121
156
  lexer->mark_end(lexer);
122
157
  return emit(lexer, valid, token);
123
158
  }
@@ -134,13 +169,8 @@ static bool text_run(
134
169
  enum Token token,
135
170
  uint32_t end
136
171
  ) {
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
- )
172
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
173
+ while (s->position < end && lexer->lookahead != DECODE_ERROR)
144
174
  advance(s, lexer);
145
175
  lexer->mark_end(lexer);
146
176
  return emit(lexer, valid, token);
@@ -178,9 +208,12 @@ static int32_t simple_escape(int32_t c) {
178
208
  static void quoted_escape(Cursor *r) {
179
209
  const int32_t c = r->lexer->lookahead;
180
210
  r->kind = QUOTED_ESCAPE;
181
- if (r->raw >= r->limit || c == DECODE_ERROR) {
211
+ if (r->raw >= r->limit) {
212
+ r->c = INVALID_UNIT;
213
+ r->kind = absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE);
214
+ } else if (c == DECODE_ERROR) {
182
215
  r->c = INVALID_UNIT;
183
- r->kind = ERROR_SENTINEL;
216
+ r->kind = ESCAPE_PREFIX;
184
217
  } else if (c >= '0' && c <= '3') {
185
218
  int32_t value = 0;
186
219
  unsigned digits = 0;
@@ -194,12 +227,14 @@ static void quoted_escape(Cursor *r) {
194
227
  if (digits == 3) r->c = value;
195
228
  else {
196
229
  r->c = INVALID_UNIT;
197
- r->kind = ERROR_SENTINEL;
230
+ r->kind = r->raw == r->limit
231
+ ? absent(r->lexer, ENDED_QUOTED_ESCAPE, INCOMPLETE_QUOTED_ESCAPE)
232
+ : ENDED_QUOTED_ESCAPE;
198
233
  }
199
234
  } else {
200
235
  raw_step(r);
201
236
  r->c = simple_escape(c);
202
- if (r->c == INVALID_UNIT) r->kind = ERROR_SENTINEL;
237
+ if (r->c == INVALID_UNIT) r->kind = INVALID_QUOTED_ESCAPE;
203
238
  }
204
239
  }
205
240
 
@@ -212,7 +247,7 @@ static void step(Cursor *r) {
212
247
  raw_step(r);
213
248
  r->c = c;
214
249
  if (c == DECODE_ERROR) {
215
- r->kind = ERROR_SENTINEL;
250
+ r->kind = INVALID_ENCODING;
216
251
  while (r->raw < r->limit && r->lexer->lookahead == DECODE_ERROR)
217
252
  raw_step(r);
218
253
  } else if (r->s->quoted && r->start < r->s->content_end && c == '\\')
@@ -369,7 +404,9 @@ static Item item(Cursor *r) {
369
404
  if (r->kind) a.kind = r->kind;
370
405
  step(r);
371
406
  if (c == '\\') {
372
- if (r->c < 0) a.kind = ERROR_SENTINEL;
407
+ if (r->c == CURSOR_END)
408
+ a.kind = absent(r->lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
409
+ else if (r->c < 0) a.kind = ESCAPE_PREFIX;
373
410
  else {
374
411
  a.kind = ESCAPE;
375
412
  a.end = r->end;
@@ -391,18 +428,36 @@ static bool literal_boundary(const Cursor *r) {
391
428
  return c == '\\' || c == '*' || c == '?' || c == '/' || c == '[';
392
429
  }
393
430
 
431
+ static void hold_prefix(Scanner *s, Cursor *r, uint32_t prefix_end) {
432
+ s->next = ESCAPE_PREFIX;
433
+ s->prefix_end = prefix_end;
434
+ s->prefix_kind = r->kind;
435
+ if (r->kind == ESCAPE_PREFIX) {
436
+ s->prefix_end = r->end;
437
+ s->prefix_kind = INVALID_ENCODING;
438
+ step(r);
439
+ }
440
+ s->next_end = r->end;
441
+ }
442
+
394
443
  // Consume lookahead units fully so edits inside them invalidate the decision.
395
444
  static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
396
445
  Cursor r = cursor(s, lexer, s->content_end);
397
446
  bool separator = r.c == '/';
398
447
  const uint32_t start = s->position;
399
448
  s->next_end = r.end;
400
- if (r.c < 0 || (r.c == '!' && start == content_start(s))) return false;
449
+ if (r.c < 0) {
450
+ if (r.kind == ESCAPE_PREFIX) hold_prefix(s, &r, r.end);
451
+ else s->next = r.kind;
452
+ } else if (r.c == '!' && start == content_start(s))
453
+ s->next = NEGATIVE_PATTERN;
401
454
  else if (r.c == '?') s->next = SINGLE_CHARACTER;
402
455
  else if (r.c == '/') s->next = PATH_SEPARATOR;
403
456
  else if (r.c == '\\') {
404
457
  step(&r);
405
- if (r.c < 0) return false;
458
+ if (r.c == CURSOR_END)
459
+ s->next = absent(lexer, ENDED_ESCAPE, INCOMPLETE_ESCAPE);
460
+ else if (r.c < 0) hold_prefix(s, &r, s->next_end);
406
461
  else {
407
462
  s->next = ESCAPE;
408
463
  s->next_end = r.end;
@@ -438,7 +493,7 @@ static bool prepare_glob(Scanner *s, TSLexer *lexer, const bool *valid) {
438
493
  s->component_start = false;
439
494
  return emit(lexer, valid, LITERAL);
440
495
  }
441
- s->component_start = separator;
496
+ s->component_start = separator || s->next == NEGATIVE_PATTERN;
442
497
  if (separator) s->after_separator = true;
443
498
  return emit(lexer, valid, GLOB_START);
444
499
  }
@@ -485,6 +540,7 @@ static bool prepare_set(Scanner *s, TSLexer *lexer, const bool *valid) {
485
540
  if (s->next_end == start) {
486
541
  s->next = current.kind;
487
542
  s->next_end = current.end;
543
+ if (current.kind == ESCAPE_PREFIX) hold_prefix(s, &r, current.end);
488
544
  }
489
545
  break;
490
546
  }
@@ -519,7 +575,7 @@ static bool class_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
519
575
  if (!next_class(&r, false, &s->class)) s->class.start = NONE;
520
576
  return emit(lexer, valid, COMPOUND_CLOSE);
521
577
  }
522
- if (lexer->lookahead == DECODE_ERROR) return false;
578
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
523
579
  Cursor r = cursor(s, lexer, s->class.closing_start);
524
580
  const enum Token token = r.kind ? (enum Token)r.kind : CLASS_NAME;
525
581
  uint32_t end = r.end;
@@ -550,20 +606,26 @@ static bool range_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
550
606
  );
551
607
  }
552
608
 
553
- static enum Token name_token(const Cursor *r) {
554
- if (r->c < 0) return ERROR_SENTINEL;
609
+ static enum Token name_token(const Cursor *r, bool first) {
610
+ if (r->c < 0) return (enum Token)r->kind;
611
+ if (!r->kind && r->c == '\r' && r->lexer->eof(r->lexer))
612
+ return INCOMPLETE_NAME_CHARACTER;
613
+ if (!name_character(r->c, first))
614
+ return r->kind == QUOTED_ESCAPE ? INVALID_NAME_ESCAPE
615
+ : INVALID_NAME_CHARACTER;
555
616
  return r->kind == QUOTED_ESCAPE ? QUOTED_ESCAPE : NAME_TEXT;
556
617
  }
557
618
 
558
619
  static bool name_piece(Scanner *s, TSLexer *lexer, const bool *valid) {
559
- if (lexer->lookahead == DECODE_ERROR) return false;
620
+ if (lexer->lookahead == DECODE_ERROR) return invalid_run(s, lexer, valid);
560
621
  Cursor r = cursor(s, lexer, s->name_end);
561
- const enum Token token = name_token(&r);
622
+ if (r.kind == ESCAPE_PREFIX) s->next = INVALID_ENCODING;
623
+ const enum Token token = name_token(&r, s->position == s->name_start);
562
624
  uint32_t end = r.end;
563
625
  lexer->mark_end(lexer);
564
- if (token == NAME_TEXT) {
626
+ if (token == NAME_TEXT || token == INVALID_NAME_CHARACTER) {
565
627
  step(&r);
566
- while (name_token(&r) == token) {
628
+ while (name_token(&r, false) == token) {
567
629
  end = r.end;
568
630
  lexer->mark_end(lexer);
569
631
  step(&r);
@@ -587,6 +649,21 @@ static bool prepare_attribute(Scanner *s, TSLexer *lexer, const bool *valid) {
587
649
  return emit(lexer, valid, ATTRIBUTE_START);
588
650
  }
589
651
 
652
+ static bool end_line(Scanner *s, TSLexer *lexer, const bool *valid) {
653
+ enum Token token = END_OF_FILE;
654
+ if (!lexer->eof(lexer)) {
655
+ if (!valid[LINE_ENDING]) return false;
656
+ if (lexer->lookahead == '\r') advance(s, lexer);
657
+ if (lexer->lookahead != '\n') return false;
658
+ advance(s, lexer);
659
+ token = LINE_ENDING;
660
+ }
661
+ lexer->mark_end(lexer);
662
+ if (!emit(lexer, valid, token)) return false;
663
+ *s = (Scanner){0};
664
+ return true;
665
+ }
666
+
590
667
  static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
591
668
  if (valid[ERROR_SENTINEL]) return false;
592
669
  if (valid[LINE_START]) return start_line(s, lexer, valid);
@@ -599,11 +676,19 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
599
676
  return start_content(s, lexer, valid);
600
677
  lexer->mark_end(lexer);
601
678
  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);
679
+ (valid[SET_CLOSE] ||
680
+ valid[MISSING_SET_CLOSE] ||
681
+ valid[INCOMPLETE_SET_CLOSE]) &&
682
+ s->position == s->set_end
683
+ ) {
684
+ if (s->set_close_end > s->set_end)
685
+ return consume(s, lexer, valid, SET_CLOSE, s->set_close_end);
686
+ return emit(
687
+ lexer,
688
+ valid,
689
+ absent(lexer, MISSING_SET_CLOSE, INCOMPLETE_SET_CLOSE)
690
+ );
691
+ }
607
692
  if (valid[ATTRIBUTE_START]) return prepare_attribute(s, lexer, valid);
608
693
  if (valid[ATTRIBUTE_END] && s->position == s->token_end)
609
694
  return emit(lexer, valid, ATTRIBUTE_END);
@@ -616,6 +701,15 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
616
701
  return emit(lexer, valid, VALUE_START);
617
702
  if (valid[VALUE_END] && s->position == s->token_end)
618
703
  return emit(lexer, valid, VALUE_END);
704
+ if (
705
+ (valid[MISSING_ATTRIBUTE_NAME] || valid[INCOMPLETE_ATTRIBUTE_NAME]) &&
706
+ s->position == s->name_end
707
+ )
708
+ return emit(
709
+ lexer,
710
+ valid,
711
+ absent(lexer, MISSING_ATTRIBUTE_NAME, INCOMPLETE_ATTRIBUTE_NAME)
712
+ );
619
713
  if (
620
714
  (valid[UNSET_MARKER] || valid[UNSPECIFIED_MARKER]) &&
621
715
  s->position < s->name_start
@@ -632,16 +726,22 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
632
726
  if (valid[ASSIGNMENT_OPERATOR] && lexer->lookahead == '=')
633
727
  return take(s, lexer, valid, ASSIGNMENT_OPERATOR);
634
728
  if (
635
- valid[QUOTE_CLOSE] &&
729
+ (valid[QUOTE_CLOSE] ||
730
+ valid[MISSING_QUOTE_CLOSE] ||
731
+ valid[INCOMPLETE_QUOTE_CLOSE]) &&
636
732
  s->quoted &&
637
- s->position == s->content_end &&
638
- s->content_end < s->line_end
639
- )
640
- return take(s, lexer, valid, QUOTE_CLOSE);
733
+ s->position == s->content_end
734
+ ) {
735
+ if (s->content_end < s->line_end) return take(s, lexer, valid, QUOTE_CLOSE);
736
+ return emit(
737
+ lexer,
738
+ valid,
739
+ absent(lexer, MISSING_QUOTE_CLOSE, INCOMPLETE_QUOTE_CLOSE)
740
+ );
741
+ }
641
742
  if (valid[PATTERN_END] && s->position >= s->content_end)
642
743
  return emit(lexer, valid, PATTERN_END);
643
- if (s->position >= s->line_end)
644
- return lexer->eof(lexer) && emit(lexer, valid, END_OF_FILE);
744
+ if (s->position >= s->line_end) return end_line(s, lexer, valid);
645
745
  if (s->kind == COMMENT_START) {
646
746
  if (s->position == s->leading) return take(s, lexer, valid, COMMENT_MARKER);
647
747
  return text_run(s, lexer, valid, COMMENT_TEXT, s->line_end);
@@ -651,17 +751,15 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
651
751
  if (valid[MACRO_MARKER])
652
752
  return consume(s, lexer, valid, MACRO_MARKER, s->name_start);
653
753
  if (valid[SEPARATOR] || valid[LAYOUT]) {
654
- if (!whitespace(lexer->lookahead)) return false;
754
+ if (!whitespace(lexer->lookahead))
755
+ return emit(lexer, valid, MISSING_ATTRIBUTE_SEPARATOR);
655
756
  while (s->position < s->line_end && whitespace(lexer->lookahead))
656
757
  advance(s, lexer);
657
758
  lexer->mark_end(lexer);
658
759
  return emit(lexer, valid, s->position < s->line_end ? SEPARATOR : LAYOUT);
659
760
  }
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
- )
761
+ if (valid[GLOB_START]) return prepare_glob(s, lexer, valid);
762
+ if (valid[SET_ITEM_START] || valid[SET_NEGATION])
665
763
  return prepare_set(s, lexer, valid);
666
764
  if (s->class.start < s->position && s->position < s->class.end)
667
765
  return class_piece(s, lexer, valid);
@@ -673,6 +771,10 @@ static bool scan(Scanner *s, TSLexer *lexer, const bool *valid) {
673
771
  const enum Token token = (enum Token)s->next;
674
772
  if (token == CLASS_OPEN)
675
773
  return class_delimiter(s, lexer, valid, COMPOUND_OPEN);
774
+ if (token == ESCAPE_PREFIX) {
775
+ s->next = s->prefix_kind;
776
+ return consume(s, lexer, valid, token, s->prefix_end);
777
+ }
676
778
  return consume(s, lexer, valid, token, s->next_end);
677
779
  }
678
780
 
package/tree-sitter.json CHANGED
@@ -12,7 +12,7 @@
12
12
  }
13
13
  ],
14
14
  "metadata": {
15
- "version": "0.5.0",
15
+ "version": "0.6.0",
16
16
  "license": "MIT",
17
17
  "description": "Tree-sitter grammar for gitattributes.",
18
18
  "links": {