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