tree-sitter-delphi13 1.0.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,511 @@
1
+ // External scanner for tree-sitter-delphi13.
2
+ //
3
+ // Provides two external tokens that tree-sitter regex can't handle:
4
+ //
5
+ // pp_block Entire `{$IF*} ... {$END*}` block consumed as ONE token,
6
+ // EXCEPT when the body begins with a structural keyword
7
+ // (unit/program/library/package/interface/implementation),
8
+ // in which case the scanner refuses so the parser can see
9
+ // the wrapped declaration. Handles single-level (nested
10
+ // IFDEF pairs close at the first matching ENDIF — tracked
11
+ // with a depth counter so true nesting is supported).
12
+ //
13
+ // char_literal Delphi's `^X` control-character literal (`^M` = #13,
14
+ // `^I` = #9, etc.) where X is in `@A-Z[\]^_` and is NOT
15
+ // followed by an identifier-continuation char. The
16
+ // lookahead requirement disambiguates from `^TFoo`
17
+ // pointer-to-type.
18
+ //
19
+ // Contract (tree-sitter scanner):
20
+ // - Returning false rolls back the lexer to the position from BEFORE
21
+ // scan() was called. Free to probe via advance().
22
+ // - On true return, mark_end(lexer) determines token end.
23
+
24
+ #include "tree_sitter/parser.h"
25
+ #include <stdbool.h>
26
+ #include <stdint.h>
27
+ #include <ctype.h>
28
+
29
+ enum TokenType {
30
+ PP_BLOCK,
31
+ CHAR_LITERAL,
32
+ TRAILING_DOT_FLOAT, // `100.` form — float with trailing dot, no fraction.
33
+ // Distinct from regex `_literalFloat` because tree-sitter
34
+ // regex can't disambiguate `100.` (float) from `100..N`
35
+ // (int + range op). The scanner peeks the char after `.`
36
+ // and only emits if it isn't `.` or a digit.
37
+
38
+ // ---- THEN-wins refactor (DESIGN-ifdef-then-wins.md) ----
39
+ // Order MUST match grammar.js externals list.
40
+ // Phase 1: declared only; not yet emitted.
41
+ PP_OPEN, // {$IF X} / {$IFDEF X} / {$IFNDEF X} / {$IFOPT ...}
42
+ PP_ELSE_TAIL, // {$ELSE} ... opaque ... {$ENDIF | $IFEND}
43
+ PP_END_ONLY, // {$ENDIF} / {$IFEND} (no preceding else branch)
44
+ };
45
+
46
+ void *tree_sitter_delphi13_external_scanner_create(void) { return NULL; }
47
+ void tree_sitter_delphi13_external_scanner_destroy(void *p) { (void)p; }
48
+ unsigned tree_sitter_delphi13_external_scanner_serialize(void *p, char *b) {
49
+ (void)p; (void)b; return 0;
50
+ }
51
+ void tree_sitter_delphi13_external_scanner_deserialize(void *p, const char *b, unsigned n) {
52
+ (void)p; (void)b; (void)n;
53
+ }
54
+
55
+ // ---- helpers ----
56
+
57
+ static inline bool is_ident_cont(int32_t c) {
58
+ return (c >= 'a' && c <= 'z')
59
+ || (c >= 'A' && c <= 'Z')
60
+ || (c >= '0' && c <= '9')
61
+ || c == '_';
62
+ }
63
+
64
+ static inline int32_t to_lower(int32_t c) {
65
+ return (c >= 'A' && c <= 'Z') ? c + 32 : c;
66
+ }
67
+
68
+ static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
69
+ static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
70
+
71
+ // Try to consume the literal keyword `kw` (case-insensitive) from the current
72
+ // lookahead. On success the lookahead is past the keyword AND we've verified
73
+ // the next char is NOT an identifier-continuation char (so it's word-bounded).
74
+ // On failure, characters MAY have been consumed (caller handles rollback by
75
+ // returning false from scan()).
76
+ static bool try_consume_keyword(TSLexer *lexer, const char *kw) {
77
+ for (const char *p = kw; *p; p++) {
78
+ if (to_lower(lexer->lookahead) != *p) return false;
79
+ advance(lexer);
80
+ }
81
+ if (is_ident_cont(lexer->lookahead)) return false;
82
+ return true;
83
+ }
84
+
85
+ // Look at the current lookahead and CONDITIONALLY consume a refused keyword.
86
+ // Returns true if any of these matches at a word boundary:
87
+ // unit / program / library / package / interface / implementation (structural)
88
+ // packed (type modifier;
89
+ // refused so `{$IFDEF X}packed{$ENDIF} record` parses as
90
+ // [pp]packed[pp] record rather than swallowing `packed` into the pp_block.)
91
+ //
92
+ // Hand-coded char-by-char prune: try_consume_keyword can't be cascaded for
93
+ // candidates that share a prefix (e.g. packed vs package both start with `pack`),
94
+ // because it consumes chars on failure without rolling back.
95
+ static bool starts_with_structural_keyword(TSLexer *lexer) {
96
+ int32_t la = to_lower(lexer->lookahead);
97
+ if (la == 'u') return try_consume_keyword(lexer, "unit");
98
+ if (la == 'l') return try_consume_keyword(lexer, "library");
99
+ if (la == 'p') {
100
+ advance(lexer); // consume 'p'
101
+ int32_t la2 = to_lower(lexer->lookahead);
102
+ if (la2 == 'r') return try_consume_keyword(lexer, "rogram");
103
+ if (la2 == 'a') {
104
+ // package OR packed — both start with 'pack', diverge at 5th char.
105
+ advance(lexer); if (to_lower(lexer->lookahead) != 'c') return false;
106
+ advance(lexer); if (to_lower(lexer->lookahead) != 'k') return false;
107
+ advance(lexer);
108
+ int32_t la5 = to_lower(lexer->lookahead);
109
+ if (la5 == 'e') {
110
+ advance(lexer); if (to_lower(lexer->lookahead) != 'd') return false;
111
+ advance(lexer);
112
+ if (is_ident_cont(lexer->lookahead)) return false;
113
+ return true; // packed
114
+ }
115
+ if (la5 == 'a') {
116
+ advance(lexer); if (to_lower(lexer->lookahead) != 'g') return false;
117
+ advance(lexer); if (to_lower(lexer->lookahead) != 'e') return false;
118
+ advance(lexer);
119
+ if (is_ident_cont(lexer->lookahead)) return false;
120
+ return true; // package
121
+ }
122
+ }
123
+ return false;
124
+ }
125
+ if (la == 'i') {
126
+ advance(lexer); // consume 'i'
127
+ int32_t la2 = to_lower(lexer->lookahead);
128
+ if (la2 == 'n') return try_consume_keyword(lexer, "nterface");
129
+ if (la2 == 'm') return try_consume_keyword(lexer, "mplementation");
130
+ return false;
131
+ }
132
+ return false;
133
+ }
134
+
135
+ // ---- THEN-wins refactor (phase 2) ----
136
+ //
137
+ // The refactor reads THROUGH IFDEFs: directive markers become tokens (in
138
+ // `extras` from the parser's POV — like comments), THEN-branch content flows
139
+ // out as regular tokens, ELSE-branch is consumed as one opaque tail.
140
+ //
141
+ // See DESIGN-ifdef-then-wins.md for the full plan.
142
+
143
+ // Classify the directive starting at lookahead (which must be at the char
144
+ // just after `{$`). Consumes 0..N chars of lookahead while classifying.
145
+ // Caller MUST be inside a fresh scan() call (any consumed chars are rolled
146
+ // back by tree-sitter if the scan() returns false).
147
+ typedef enum {
148
+ DIR_OPEN, // if / ifdef / ifndef / ifopt / ifexists / ifabbrevdef ...
149
+ DIR_ELSE, // else / elseif
150
+ DIR_END, // endif / ifend
151
+ DIR_OTHER, // any other {$X} directive — not an if-block boundary
152
+ } DirectiveKind;
153
+
154
+ static DirectiveKind classify_directive(TSLexer *lexer) {
155
+ int32_t c1 = to_lower(lexer->lookahead);
156
+ if (c1 == 'i') {
157
+ advance(lexer);
158
+ if (to_lower(lexer->lookahead) != 'f') return DIR_OTHER;
159
+ advance(lexer);
160
+ // `if<X>`: if it's `ife` followed by `nd`, that's `ifend` (DIR_END).
161
+ // Otherwise it's an `if`-family opener (`if`, `ifdef`, `ifndef`,
162
+ // `ifopt`, `ifexists`, etc.) — DIR_OPEN.
163
+ if (to_lower(lexer->lookahead) == 'e') {
164
+ advance(lexer);
165
+ if (to_lower(lexer->lookahead) == 'n') {
166
+ advance(lexer);
167
+ if (to_lower(lexer->lookahead) == 'd') {
168
+ advance(lexer);
169
+ return DIR_END;
170
+ }
171
+ }
172
+ return DIR_OTHER;
173
+ }
174
+ return DIR_OPEN;
175
+ }
176
+ if (c1 == 'e') {
177
+ advance(lexer);
178
+ int32_t c2 = to_lower(lexer->lookahead);
179
+ if (c2 == 'l') {
180
+ // else / elseif
181
+ advance(lexer);
182
+ if (to_lower(lexer->lookahead) == 's') {
183
+ advance(lexer);
184
+ if (to_lower(lexer->lookahead) == 'e') {
185
+ advance(lexer);
186
+ return DIR_ELSE;
187
+ }
188
+ }
189
+ return DIR_OTHER;
190
+ }
191
+ if (c2 == 'n') {
192
+ // end / endif — both close the IFDEF
193
+ advance(lexer);
194
+ if (to_lower(lexer->lookahead) == 'd') {
195
+ advance(lexer);
196
+ return DIR_END;
197
+ }
198
+ return DIR_OTHER;
199
+ }
200
+ return DIR_OTHER;
201
+ }
202
+ return DIR_OTHER;
203
+ }
204
+
205
+ // Consume the rest of the current directive up through the closing `}`.
206
+ // Called after classify_directive; lookahead is somewhere inside the
207
+ // directive body.
208
+ static bool consume_directive_to_close(TSLexer *lexer) {
209
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') {
210
+ advance(lexer);
211
+ }
212
+ if (lexer->lookahead != '}') return false;
213
+ advance(lexer);
214
+ return true;
215
+ }
216
+
217
+ // Look at the upcoming directive WITHOUT advancing past `{$`. Used by
218
+ // pp_else_tail to decide whether to keep consuming (nested IFDEF) or stop
219
+ // (matched {$END*}). Returns DIR_* and ALWAYS advances past the `{$XXX`
220
+ // keyword portion of the directive (regardless of return value) — the
221
+ // caller then calls consume_directive_to_close() to finish.
222
+ static DirectiveKind peek_and_consume_directive_kw(TSLexer *lexer) {
223
+ if (lexer->lookahead != '{') return DIR_OTHER;
224
+ advance(lexer);
225
+ if (lexer->lookahead != '$') return DIR_OTHER;
226
+ advance(lexer);
227
+ return classify_directive(lexer);
228
+ }
229
+
230
+ // ---- THEN-wins unified dispatch ----
231
+ //
232
+ // Single function that classifies the directive at lookahead, then commits
233
+ // to ONE of: PP_OPEN, PP_END_ONLY, PP_ELSE_TAIL (or returns false). Must be
234
+ // called from a fresh scan() — within a single scan() call we cannot roll
235
+ // back consumed lookahead, so the classification + commit must happen
236
+ // linearly.
237
+ //
238
+ // On entry: lookahead = '{', at least one of PP_OPEN / PP_END_ONLY /
239
+ // PP_ELSE_TAIL is in valid_symbols.
240
+ //
241
+ // Returns true and sets result_symbol on success.
242
+ static bool scan_pp_then_wins(TSLexer *lexer, const bool *valid_symbols) {
243
+ if (lexer->lookahead != '{') return false;
244
+ advance(lexer);
245
+ if (lexer->lookahead != '$') return false;
246
+ advance(lexer);
247
+
248
+ DirectiveKind k = classify_directive(lexer);
249
+
250
+ if (k == DIR_OPEN && valid_symbols[PP_OPEN]) {
251
+ if (!consume_directive_to_close(lexer)) return false;
252
+ lexer->result_symbol = PP_OPEN;
253
+ lexer->mark_end(lexer);
254
+ return true;
255
+ }
256
+
257
+ if (k == DIR_END && valid_symbols[PP_END_ONLY]) {
258
+ if (!consume_directive_to_close(lexer)) return false;
259
+ lexer->result_symbol = PP_END_ONLY;
260
+ lexer->mark_end(lexer);
261
+ return true;
262
+ }
263
+
264
+ if (k == DIR_ELSE && valid_symbols[PP_ELSE_TAIL]) {
265
+ if (!consume_directive_to_close(lexer)) return false;
266
+ // Consume opaque ELSE body up to the matching {$END*}, depth-tracking
267
+ // nested {$IF*}/{$END*} pairs.
268
+ int depth = 1;
269
+ int safety = 8 * 1024 * 1024;
270
+ while (lexer->lookahead != 0 && safety-- > 0) {
271
+ if (lexer->lookahead != '{') { advance(lexer); continue; }
272
+ DirectiveKind dk = peek_and_consume_directive_kw(lexer);
273
+ if (dk == DIR_OPEN) {
274
+ consume_directive_to_close(lexer);
275
+ depth++;
276
+ continue;
277
+ }
278
+ if (dk == DIR_END) {
279
+ consume_directive_to_close(lexer);
280
+ if (--depth == 0) {
281
+ lexer->result_symbol = PP_ELSE_TAIL;
282
+ lexer->mark_end(lexer);
283
+ return true;
284
+ }
285
+ continue;
286
+ }
287
+ // DIR_ELSE inside else body or DIR_OTHER — just skip the directive.
288
+ consume_directive_to_close(lexer);
289
+ }
290
+ return false;
291
+ }
292
+
293
+ // Directive doesn't match any requested token type.
294
+ return false;
295
+ }
296
+
297
+ // ---- pp_block ----
298
+ //
299
+ // On entry: lookahead = '{'. Try to consume the entire `{$IF*}...{$END*}`
300
+ // block as one token. Returns true on success.
301
+ static bool scan_pp_block(TSLexer *lexer) {
302
+ // Opening `{$if...` check.
303
+ if (lexer->lookahead != '{') return false;
304
+ advance(lexer);
305
+ if (lexer->lookahead != '$') return false;
306
+ advance(lexer);
307
+ if (to_lower(lexer->lookahead) != 'i') return false;
308
+ advance(lexer);
309
+ if (to_lower(lexer->lookahead) != 'f') return false;
310
+ advance(lexer);
311
+
312
+ // Consume rest of opening directive up to '}'.
313
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') {
314
+ advance(lexer);
315
+ }
316
+ if (lexer->lookahead != '}') return false;
317
+ advance(lexer);
318
+
319
+ // Skip whitespace.
320
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t'
321
+ || lexer->lookahead == '\n' || lexer->lookahead == '\r') {
322
+ advance(lexer);
323
+ }
324
+
325
+ // Refuse if body begins with a structural keyword — let the parser see
326
+ // the wrapped declaration via the regex-pp fallback.
327
+ if (starts_with_structural_keyword(lexer)) return false;
328
+
329
+ // Scan body, tracking nested {$if*}/{$end*}/{$ifend*} pairs.
330
+ int depth = 1;
331
+ int safety = 8 * 1024 * 1024; // 8 MiB hard cap
332
+ while (lexer->lookahead != 0 && safety-- > 0) {
333
+ if (lexer->lookahead != '{') { advance(lexer); continue; }
334
+ advance(lexer);
335
+ if (lexer->lookahead != '$') continue;
336
+ advance(lexer);
337
+
338
+ int32_t c1 = to_lower(lexer->lookahead);
339
+ if (c1 == 'i') {
340
+ advance(lexer);
341
+ int32_t c2 = to_lower(lexer->lookahead);
342
+ if (c2 == 'f') {
343
+ advance(lexer);
344
+ // Could be `{$if...}` (open, depth++) or `{$ifend...}` (close).
345
+ int32_t c3 = to_lower(lexer->lookahead);
346
+ if (c3 == 'e') {
347
+ advance(lexer);
348
+ if (to_lower(lexer->lookahead) == 'n') {
349
+ advance(lexer);
350
+ if (to_lower(lexer->lookahead) == 'd') {
351
+ advance(lexer);
352
+ // {$ifend...} — consume to '}', decrement.
353
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
354
+ if (lexer->lookahead != '}') return false;
355
+ advance(lexer);
356
+ if (--depth == 0) {
357
+ lexer->result_symbol = PP_BLOCK;
358
+ lexer->mark_end(lexer);
359
+ return true;
360
+ }
361
+ continue;
362
+ }
363
+ }
364
+ // {$ife...} but not ifend; treat as a stray directive,
365
+ // consume to '}' and don't change depth.
366
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
367
+ if (lexer->lookahead == '}') advance(lexer);
368
+ continue;
369
+ }
370
+ // {$if...} — open another nesting level.
371
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
372
+ if (lexer->lookahead != '}') return false;
373
+ advance(lexer);
374
+ depth++;
375
+ continue;
376
+ }
377
+ // {$i...} but not {$if...} — some other directive.
378
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
379
+ if (lexer->lookahead == '}') advance(lexer);
380
+ continue;
381
+ }
382
+ if (c1 == 'e') {
383
+ advance(lexer);
384
+ if (to_lower(lexer->lookahead) == 'n') {
385
+ advance(lexer);
386
+ if (to_lower(lexer->lookahead) == 'd') {
387
+ advance(lexer);
388
+ // {$end...} — consume to '}', decrement depth.
389
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
390
+ if (lexer->lookahead != '}') return false;
391
+ advance(lexer);
392
+ if (--depth == 0) {
393
+ lexer->result_symbol = PP_BLOCK;
394
+ lexer->mark_end(lexer);
395
+ return true;
396
+ }
397
+ continue;
398
+ }
399
+ }
400
+ // {$e...} but not {$end} — other directive, consume.
401
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
402
+ if (lexer->lookahead == '}') advance(lexer);
403
+ continue;
404
+ }
405
+ // Some other {$X...} directive — consume.
406
+ while (lexer->lookahead != 0 && lexer->lookahead != '}') advance(lexer);
407
+ if (lexer->lookahead == '}') advance(lexer);
408
+ }
409
+ return false;
410
+ }
411
+
412
+ // ---- char_literal ----
413
+ //
414
+ // `^X` where X is in @A-Z[\]^_ (case-insensitive) and is NOT followed by an
415
+ // identifier-continuation character (so `^Tx` parses as kHat + identifier,
416
+ // while `^M:` parses as char_literal then ':').
417
+ static bool scan_char_literal(TSLexer *lexer) {
418
+ if (lexer->lookahead != '^') return false;
419
+ advance(lexer);
420
+ int32_t c = lexer->lookahead;
421
+ // Accept @ A-Z [ \ ] ^ _ and lowercase variants.
422
+ bool valid_char =
423
+ c == '@' || c == '[' || c == '\\' || c == ']' ||
424
+ c == '^' || c == '_' ||
425
+ (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
426
+ if (!valid_char) return false;
427
+ advance(lexer);
428
+ // Critical: the NEXT char must not be an identifier-continuation char.
429
+ // Otherwise this is `^TFoo` pointer-to-type, not a char literal.
430
+ if (is_ident_cont(lexer->lookahead)) return false;
431
+ lexer->result_symbol = CHAR_LITERAL;
432
+ lexer->mark_end(lexer);
433
+ return true;
434
+ }
435
+
436
+ // ---- trailing_dot_float ----
437
+ //
438
+ // `123.` style float literal — digits followed by a dot followed by ANYTHING
439
+ // other than a dot or digit. Disambiguates from `123..N` (int + range op)
440
+ // because tree-sitter regex can't peek-without-consuming.
441
+ //
442
+ // Examples:
443
+ // 100. * x -> trailing_dot_float
444
+ // 6. ; -> trailing_dot_float
445
+ // 100..200 -> NOT matched (followed by another `.`)
446
+ // 100.5 -> NOT matched (followed by digit)
447
+ static bool scan_trailing_dot_float(TSLexer *lexer) {
448
+ // Must start with a digit.
449
+ if (lexer->lookahead < '0' || lexer->lookahead > '9') return false;
450
+ // Consume all leading digits.
451
+ while (lexer->lookahead >= '0' && lexer->lookahead <= '9') {
452
+ advance(lexer);
453
+ }
454
+ // Must see `.`
455
+ if (lexer->lookahead != '.') return false;
456
+ advance(lexer);
457
+ // The next char must NOT be `.` (range) or a digit (regular float `.N`).
458
+ if (lexer->lookahead == '.') return false;
459
+ if (lexer->lookahead >= '0' && lexer->lookahead <= '9') return false;
460
+ // Confirmed trailing-dot float.
461
+ lexer->result_symbol = TRAILING_DOT_FLOAT;
462
+ lexer->mark_end(lexer);
463
+ return true;
464
+ }
465
+
466
+ // ---- entry point ----
467
+
468
+ bool tree_sitter_delphi13_external_scanner_scan(
469
+ void *payload, TSLexer *lexer, const bool *valid_symbols)
470
+ {
471
+ (void)payload;
472
+
473
+ // Skip leading whitespace before trying any external token.
474
+ while (lexer->lookahead == ' ' || lexer->lookahead == '\t'
475
+ || lexer->lookahead == '\n' || lexer->lookahead == '\r') {
476
+ skip(lexer);
477
+ }
478
+
479
+ // THEN-wins refactor: unified dispatch on `{$...` directives.
480
+ //
481
+ // Single classification step then commits to one of PP_OPEN /
482
+ // PP_END_ONLY / PP_ELSE_TAIL. If none of those are valid (or the
483
+ // directive shape doesn't match), falls back to legacy PP_BLOCK
484
+ // — which is itself dead code once the new tokens are in extras,
485
+ // but kept as belt-and-braces for now.
486
+ if (lexer->lookahead == '{') {
487
+ bool new_token_valid = valid_symbols[PP_OPEN]
488
+ || valid_symbols[PP_END_ONLY]
489
+ || valid_symbols[PP_ELSE_TAIL];
490
+ if (new_token_valid) {
491
+ if (scan_pp_then_wins(lexer, valid_symbols)) return true;
492
+ // If dispatch returned false, the lexer state is undefined
493
+ // (we may have consumed chars). Tree-sitter rolls back on
494
+ // false-return, so we cannot then try PP_BLOCK in this call.
495
+ // That's fine: in the new model PP_BLOCK is never the
496
+ // expected token when the new ones are valid.
497
+ return false;
498
+ }
499
+ if (valid_symbols[PP_BLOCK]) {
500
+ if (scan_pp_block(lexer)) return true;
501
+ }
502
+ }
503
+ if (valid_symbols[CHAR_LITERAL] && lexer->lookahead == '^') {
504
+ if (scan_char_literal(lexer)) return true;
505
+ }
506
+ if (valid_symbols[TRAILING_DOT_FLOAT] &&
507
+ lexer->lookahead >= '0' && lexer->lookahead <= '9') {
508
+ if (scan_trailing_dot_float(lexer)) return true;
509
+ }
510
+ return false;
511
+ }
@@ -0,0 +1,54 @@
1
+ #ifndef TREE_SITTER_ALLOC_H_
2
+ #define TREE_SITTER_ALLOC_H_
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ #include <stdbool.h>
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+
12
+ // Allow clients to override allocation functions
13
+ #ifdef TREE_SITTER_REUSE_ALLOCATOR
14
+
15
+ extern void *(*ts_current_malloc)(size_t size);
16
+ extern void *(*ts_current_calloc)(size_t count, size_t size);
17
+ extern void *(*ts_current_realloc)(void *ptr, size_t size);
18
+ extern void (*ts_current_free)(void *ptr);
19
+
20
+ #ifndef ts_malloc
21
+ #define ts_malloc ts_current_malloc
22
+ #endif
23
+ #ifndef ts_calloc
24
+ #define ts_calloc ts_current_calloc
25
+ #endif
26
+ #ifndef ts_realloc
27
+ #define ts_realloc ts_current_realloc
28
+ #endif
29
+ #ifndef ts_free
30
+ #define ts_free ts_current_free
31
+ #endif
32
+
33
+ #else
34
+
35
+ #ifndef ts_malloc
36
+ #define ts_malloc malloc
37
+ #endif
38
+ #ifndef ts_calloc
39
+ #define ts_calloc calloc
40
+ #endif
41
+ #ifndef ts_realloc
42
+ #define ts_realloc realloc
43
+ #endif
44
+ #ifndef ts_free
45
+ #define ts_free free
46
+ #endif
47
+
48
+ #endif
49
+
50
+ #ifdef __cplusplus
51
+ }
52
+ #endif
53
+
54
+ #endif // TREE_SITTER_ALLOC_H_