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/LICENSE +27 -0
- package/README.md +50 -0
- package/binding.gyp +21 -0
- package/bindings/node/binding.cc +19 -0
- package/bindings/node/binding_test.js +9 -0
- package/bindings/node/index.d.ts +28 -0
- package/bindings/node/index.js +7 -0
- package/grammar.js +1900 -0
- package/package.json +53 -0
- package/src/grammar.json +11992 -0
- package/src/node-types.json +10575 -0
- package/src/parser.c +504483 -0
- package/src/scanner.c +511 -0
- package/src/tree_sitter/alloc.h +54 -0
- package/src/tree_sitter/array.h +291 -0
- package/src/tree_sitter/parser.h +266 -0
package/grammar.js
ADDED
|
@@ -0,0 +1,1900 @@
|
|
|
1
|
+
// Feature flags — locked to Delphi 13 only.
|
|
2
|
+
// (Originally tree-sitter-pascal had these toggleable; here they're
|
|
3
|
+
// constants set to the values that match modern Delphi 13.)
|
|
4
|
+
const public_name = false; // fpc 'public name' hint — Delphi has different syntax
|
|
5
|
+
const rtti = true; // [Attribute(...)] declarations
|
|
6
|
+
const lambda = true; // anonymous procedures/functions
|
|
7
|
+
const fpc = false; // no FPC-only syntax
|
|
8
|
+
const delphi = true; // Delphi syntax
|
|
9
|
+
const objc = false; // no PasCocoa
|
|
10
|
+
const templates = true; // generic types
|
|
11
|
+
const use_pp = true; // preprocessor handling
|
|
12
|
+
|
|
13
|
+
// Helpers
|
|
14
|
+
|
|
15
|
+
const op = {
|
|
16
|
+
infix: (prio, lhs, op, rhs) => prec.left(prio, seq(
|
|
17
|
+
field('lhs', lhs),
|
|
18
|
+
field('operator', op),
|
|
19
|
+
field('rhs', rhs)
|
|
20
|
+
)),
|
|
21
|
+
prefix: (prio, operator, operand) => prec.left(prio, seq(
|
|
22
|
+
field('operator', operator),
|
|
23
|
+
field('operand', operand)
|
|
24
|
+
)),
|
|
25
|
+
postfix: (prio, operand, operator) => prec.left(prio, seq(
|
|
26
|
+
field('operand', operand),
|
|
27
|
+
field('operator', operator)
|
|
28
|
+
)),
|
|
29
|
+
|
|
30
|
+
args: (prio, entity, open, args, close) => prec.left(prio, seq(
|
|
31
|
+
field('entity', entity), open, field('args', args), close
|
|
32
|
+
))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function delimited1(rule, delimiter = ',', precedence=0) {
|
|
36
|
+
return seq(
|
|
37
|
+
optional(repeat1(prec(precedence,seq(rule, delimiter)))),
|
|
38
|
+
rule
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function delimited(rule, delimiter = ',') {
|
|
43
|
+
return optional(delimited1(rule, delimiter));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Preprocessor wrapper.
|
|
47
|
+
// This just supports a single `if[def] ... [else[if] ...]* endif` right now.
|
|
48
|
+
// It is inteded for code like this:
|
|
49
|
+
//
|
|
50
|
+
// procedure foo;
|
|
51
|
+
// {$ifdef bla}
|
|
52
|
+
// var i: integer;
|
|
53
|
+
// begin
|
|
54
|
+
// inc(i);
|
|
55
|
+
// end;
|
|
56
|
+
// {$else}
|
|
57
|
+
// var j: integer;
|
|
58
|
+
// begin
|
|
59
|
+
// dec(j);
|
|
60
|
+
// end;
|
|
61
|
+
// {$endif}
|
|
62
|
+
//
|
|
63
|
+
// If we don't handle this case explicitly, tree-sitter produces a completely
|
|
64
|
+
// broken AST, which severely messes up the syntax highlighting.
|
|
65
|
+
//
|
|
66
|
+
// Ideally, we would want to support nested ifdefs as well, but that will be
|
|
67
|
+
// more complex.
|
|
68
|
+
//
|
|
69
|
+
// A word of caution: It is tempting to sprinkle this macro in many more
|
|
70
|
+
// places, but unfortunately tihs results in a significant performance penalty.
|
|
71
|
+
// Use it sparingly! A general rule of thumb is to use it only in situations
|
|
72
|
+
// where otherwise a severly broken parse tree would be generated. For small
|
|
73
|
+
// errors that TreeSitter can recover from automatically, it is better not to
|
|
74
|
+
// use it.
|
|
75
|
+
function pp($, ...rule) {
|
|
76
|
+
if (!use_pp)
|
|
77
|
+
return seq(...rule);
|
|
78
|
+
return (
|
|
79
|
+
choice(
|
|
80
|
+
seq(...rule),
|
|
81
|
+
seq(
|
|
82
|
+
alias(/\{\$if[^}]*\}/i, $.pp),
|
|
83
|
+
...rule,
|
|
84
|
+
repeat(seq(
|
|
85
|
+
alias(/\{\$else[^}]*\}/i, $.pp),
|
|
86
|
+
...rule
|
|
87
|
+
)),
|
|
88
|
+
alias(/\{\$end[^}]*\}/i, $.pp)
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// tr = Trailing
|
|
95
|
+
// Return the trailing equivalent of a rule, aliased to the non-trailing version.
|
|
96
|
+
const tr = ($,rule) =>
|
|
97
|
+
rule[0] == '_' ? $[rule+'Tr'] : alias($[rule+'Tr'], $[rule])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
function enable_if(cond, ...args) {
|
|
101
|
+
return cond ? args : [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Generate rules for trailing & non-trailing statements
|
|
105
|
+
function statements(trailing) {
|
|
106
|
+
let rn = x => trailing ? x + 'Tr' : x
|
|
107
|
+
let lastStatement = $ => trailing ? optional(tr($,'_statement')) : $._statement;
|
|
108
|
+
let lastStatement1= $ => trailing ? tr($,'_statement') : $._statement;
|
|
109
|
+
let semicolon = trailing ? [] : [';'];
|
|
110
|
+
|
|
111
|
+
return Object.fromEntries([
|
|
112
|
+
[rn('if'), $ => seq(
|
|
113
|
+
$.kIf, field('condition', $._expr), $.kThen,
|
|
114
|
+
field('then', lastStatement($))
|
|
115
|
+
)],
|
|
116
|
+
|
|
117
|
+
[rn('nestedIf'), $ => prec(1,$.if)],
|
|
118
|
+
|
|
119
|
+
[rn('ifElse'), $ => prec.right(1, seq(
|
|
120
|
+
$.kIf, field('condition', $._expr), $.kThen,
|
|
121
|
+
field('then', optional(choice(tr($,'_statement'), $.if))),
|
|
122
|
+
$.kElse,
|
|
123
|
+
field('else', lastStatement($))
|
|
124
|
+
))],
|
|
125
|
+
|
|
126
|
+
[rn('while'), $ => seq(
|
|
127
|
+
$.kWhile, field('condition', $._expr), $.kDo,
|
|
128
|
+
field('body', lastStatement($))
|
|
129
|
+
)],
|
|
130
|
+
|
|
131
|
+
[rn('repeat'), $ => prec(2,seq(
|
|
132
|
+
$.kRepeat,
|
|
133
|
+
field('body', optional(tr($,'statements'))),
|
|
134
|
+
$.kUntil, field('condition', $._expr),
|
|
135
|
+
...semicolon
|
|
136
|
+
))],
|
|
137
|
+
|
|
138
|
+
[rn('for'), $ => seq(
|
|
139
|
+
$.kFor,
|
|
140
|
+
field('start', $.assignment),
|
|
141
|
+
choice($.kTo, $.kDownto),
|
|
142
|
+
field('end', $._expr), $.kDo,
|
|
143
|
+
field('body', lastStatement($))
|
|
144
|
+
)],
|
|
145
|
+
|
|
146
|
+
[rn('foreach'), $ => seq(
|
|
147
|
+
$.kFor,
|
|
148
|
+
// Iterator can be an existing variable OR a Delphi 12+ inline-var
|
|
149
|
+
// declaration: `for var fname in List do`. The varAssignDef rule
|
|
150
|
+
// already handles `var x` and `var x: T`.
|
|
151
|
+
field('iterator', choice($._expr, $.varAssignDef)), $.kIn,
|
|
152
|
+
field('iterable', $._expr), $.kDo,
|
|
153
|
+
field('body', lastStatement($))
|
|
154
|
+
)],
|
|
155
|
+
|
|
156
|
+
[rn('exceptionHandler'), $ => seq(
|
|
157
|
+
$.kOn,
|
|
158
|
+
field('variable', optional(seq($.identifier, ':'))),
|
|
159
|
+
field('exception', $.typeref), $.kDo,
|
|
160
|
+
field('body', lastStatement($))
|
|
161
|
+
)],
|
|
162
|
+
|
|
163
|
+
[rn('exceptionElse'), $ => seq(
|
|
164
|
+
$.kElse, repeat($._statement), lastStatement($)
|
|
165
|
+
)],
|
|
166
|
+
|
|
167
|
+
[rn('_exceptionHandlers'), $ => seq(
|
|
168
|
+
repeat($.exceptionHandler),
|
|
169
|
+
choice($.exceptionHandler, tr($,'exceptionHandler')),
|
|
170
|
+
// exceptionElse's body is always followed by `end` (of try), so
|
|
171
|
+
// allow the trailing-form (last statement may omit `;`).
|
|
172
|
+
optional(choice($.exceptionElse, tr($,'exceptionElse')))
|
|
173
|
+
)],
|
|
174
|
+
|
|
175
|
+
[rn('try'), $ => prec(2,seq(
|
|
176
|
+
$.kTry,
|
|
177
|
+
field('try', optional(tr($,'statements'))),
|
|
178
|
+
choice(
|
|
179
|
+
field('except', seq(
|
|
180
|
+
$.kExcept,
|
|
181
|
+
optional(
|
|
182
|
+
choice(tr($,'statements'),
|
|
183
|
+
tr($,'_exceptionHandlers'))
|
|
184
|
+
)
|
|
185
|
+
)),
|
|
186
|
+
field('finally', seq(
|
|
187
|
+
$.kFinally,
|
|
188
|
+
optional(tr($,'statements'))
|
|
189
|
+
))
|
|
190
|
+
),
|
|
191
|
+
$.kEnd, ...semicolon
|
|
192
|
+
))],
|
|
193
|
+
|
|
194
|
+
[rn('caseCase'), $ => seq(
|
|
195
|
+
field('label', $.caseLabel),
|
|
196
|
+
// Optional Pascal `label:` prefix before the body. Spring4D's
|
|
197
|
+
// enumerator state-machines use this to expose goto targets
|
|
198
|
+
// from inside a case clause:
|
|
199
|
+
// STATE_RUNNING:
|
|
200
|
+
// _STATE_RUNNING: // pascal label
|
|
201
|
+
// begin ... end;
|
|
202
|
+
field('jumpLabel', optional($.label)),
|
|
203
|
+
field('body', lastStatement($))
|
|
204
|
+
)],
|
|
205
|
+
|
|
206
|
+
[rn('case'), $ => prec(2,seq(
|
|
207
|
+
$.kCase, $._expr, $.kOf,
|
|
208
|
+
repeat($.caseCase),
|
|
209
|
+
optional(tr($,'caseCase')),
|
|
210
|
+
optional(seq(
|
|
211
|
+
$.kElse,
|
|
212
|
+
optional(':'),
|
|
213
|
+
optional(tr($,'_statements'))
|
|
214
|
+
)),
|
|
215
|
+
$.kEnd, ...semicolon
|
|
216
|
+
))],
|
|
217
|
+
|
|
218
|
+
[rn('block'), $ => seq(
|
|
219
|
+
$.kBegin,
|
|
220
|
+
optional(tr($,'_statements')),
|
|
221
|
+
$.kEnd, ...semicolon
|
|
222
|
+
)],
|
|
223
|
+
|
|
224
|
+
[rn('asm'), $ => seq(
|
|
225
|
+
$.kAsm,
|
|
226
|
+
optional($.asmBody),
|
|
227
|
+
$.kEnd, ...semicolon
|
|
228
|
+
)],
|
|
229
|
+
|
|
230
|
+
[rn('with'), $ => seq(
|
|
231
|
+
$.kWith, delimited1(field('entity', $._expr)), $.kDo,
|
|
232
|
+
field('body', lastStatement($))
|
|
233
|
+
)],
|
|
234
|
+
|
|
235
|
+
[rn('raise'), $ => seq(
|
|
236
|
+
$.kRaise,
|
|
237
|
+
field('exception', optional($._expr)),
|
|
238
|
+
// Optional `at <addr-expr>` — Delphi exception re-raise with a
|
|
239
|
+
// specific call-site address: `raise EFoo.Create(msg) at ReturnAddress`.
|
|
240
|
+
optional(seq($.kAtWord, field('address', $._expr))),
|
|
241
|
+
...semicolon
|
|
242
|
+
)],
|
|
243
|
+
|
|
244
|
+
[rn('statement'), $ => choice(
|
|
245
|
+
seq($._expr, ...semicolon),
|
|
246
|
+
)],
|
|
247
|
+
|
|
248
|
+
[rn('goto'), $ => seq($.kGoto, $.identifier, ...semicolon)],
|
|
249
|
+
|
|
250
|
+
[rn('_statement'), $ => choice(
|
|
251
|
+
...semicolon,
|
|
252
|
+
seq($.assignment, ...semicolon),
|
|
253
|
+
seq($.varDef, ...semicolon),
|
|
254
|
+
// Delphi 12+ inline `const NAME [: T] = value;` inside statement body.
|
|
255
|
+
seq($.constInline, ...semicolon),
|
|
256
|
+
alias($[rn('statement')], $.statement),
|
|
257
|
+
alias($[rn('if')], $.if),
|
|
258
|
+
alias($[rn('ifElse')], $.ifElse),
|
|
259
|
+
alias($[rn('while')], $.while),
|
|
260
|
+
alias($[rn('repeat')], $.repeat),
|
|
261
|
+
alias($[rn('for')], $.for),
|
|
262
|
+
alias($[rn('foreach')], $.foreach),
|
|
263
|
+
alias($[rn('try')], $.try),
|
|
264
|
+
alias($[rn('case')], $.case),
|
|
265
|
+
alias($[rn('block')], $.block),
|
|
266
|
+
alias($[rn('with')], $.with),
|
|
267
|
+
alias($[rn('raise')], $.raise),
|
|
268
|
+
alias($[rn('goto')], $.goto),
|
|
269
|
+
alias($[rn('asm')], $.asm),
|
|
270
|
+
)],
|
|
271
|
+
|
|
272
|
+
]);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
module.exports = grammar({
|
|
276
|
+
name: "delphi13",
|
|
277
|
+
|
|
278
|
+
extras: $ => [
|
|
279
|
+
$._space,
|
|
280
|
+
$.comment,
|
|
281
|
+
$.pp,
|
|
282
|
+
$.pp_block,
|
|
283
|
+
// THEN-wins refactor (phase 2): directive markers as extras so the
|
|
284
|
+
// THEN-body parses normally and the ELSE-tail is consumed opaquely.
|
|
285
|
+
$.pp_open,
|
|
286
|
+
$.pp_else_tail,
|
|
287
|
+
$.pp_end_only,
|
|
288
|
+
],
|
|
289
|
+
|
|
290
|
+
externals: $ => [
|
|
291
|
+
// Multi-line `{$IF*} ... {$END*}` block consumed wholesale by the
|
|
292
|
+
// external scanner so the parser skips over disabled-branch content
|
|
293
|
+
// (free text, unexpanded macros, platform-gated alternatives).
|
|
294
|
+
// Scanner refuses blocks whose body starts with a structural keyword
|
|
295
|
+
// (unit/program/library/package/interface/implementation) so it
|
|
296
|
+
// doesn't swallow `{$IFNDEF X} unit Y; {$ENDIF}` patterns.
|
|
297
|
+
// Phase 4 of refactor/ifdef-then-wins will drop this in favor of
|
|
298
|
+
// the THEN-wins token set below.
|
|
299
|
+
$.pp_block,
|
|
300
|
+
// Context-aware `^X` control-char literal — matches `^` followed by
|
|
301
|
+
// one of @A-Z[\]^_ and NOT followed by an identifier character.
|
|
302
|
+
// Disambiguates from `^TFoo` pointer-to-type.
|
|
303
|
+
$.char_literal,
|
|
304
|
+
// Trailing-dot float literal `100.` — disambiguated from `100..N`
|
|
305
|
+
// (int + range op) by scanner-level lookahead at the char after `.`.
|
|
306
|
+
$.trailing_dot_float,
|
|
307
|
+
|
|
308
|
+
// ---- THEN-wins refactor (DESIGN-ifdef-then-wins.md) ----
|
|
309
|
+
// These three tokens let the scanner read THROUGH IFDEF directives:
|
|
310
|
+
// the THEN-branch content flows out as regular tokens, the ELSE
|
|
311
|
+
// branch is consumed as one opaque tail, and the directive markers
|
|
312
|
+
// themselves act as whitespace-like extras.
|
|
313
|
+
//
|
|
314
|
+
// Phase 1: declared only — scanner emits them only when valid_symbols
|
|
315
|
+
// asks, and grammar does not put them in extras yet. ABI-shift only;
|
|
316
|
+
// no behavioral change vs. master.
|
|
317
|
+
$.pp_open, // {$IF X} / {$IFDEF X} / {$IFNDEF X} / {$IFOPT ...}
|
|
318
|
+
$.pp_else_tail, // {$ELSE} ... opaque ... {$ENDIF | $IFEND}
|
|
319
|
+
$.pp_end_only, // {$ENDIF} / {$IFEND} (no preceding else branch)
|
|
320
|
+
],
|
|
321
|
+
|
|
322
|
+
word: $ => $.identifier,
|
|
323
|
+
|
|
324
|
+
conflicts: $ => [
|
|
325
|
+
// Anonymous record/class types in type position conflict with the
|
|
326
|
+
// type-declaration form (`type Foo = record ... end;`). Both reach
|
|
327
|
+
// declClass but at different parser states.
|
|
328
|
+
[$.type, $.declType],
|
|
329
|
+
// Inline if-expression vs if-statement: both start with `if cond then`.
|
|
330
|
+
// At a position where both are valid (e.g. RHS of `x := if ...`), let
|
|
331
|
+
// tree-sitter GLR-fork and decide via context.
|
|
332
|
+
[$.exprIf, $.statementTr],
|
|
333
|
+
// try-except `else` last-statement: trailing form lets last statement
|
|
334
|
+
// omit `;` since `end` follows; ambiguous with non-trailing repeat
|
|
335
|
+
// (both reach `_statement` before `end`). GLR resolves at parse time.
|
|
336
|
+
[$.exceptionElse, $.exceptionElseTr],
|
|
337
|
+
[$.exceptionElse],
|
|
338
|
+
// Soft-keyword identifiers (Message, Name, Index, Read, Write, Reference)
|
|
339
|
+
// allowed as variable names — ambiguous with next-statement starts.
|
|
340
|
+
[$.declVar],
|
|
341
|
+
// Same alias set on const names (`Default = $00010203;` etc.)
|
|
342
|
+
[$.declConst],
|
|
343
|
+
// Qualified-id subrange `TFoo.Bar..TFoo.Baz` vs `_typeref` (typerefDot).
|
|
344
|
+
[$._typeref, $._subrangeBound],
|
|
345
|
+
// Iter 58: exprTpl args narrowed to typeref — generic-arg identifier
|
|
346
|
+
// vs general expression-ref identifier conflict.
|
|
347
|
+
[$._ref, $._typeref],
|
|
348
|
+
// Iter 61: declField has optional declaration hint AND typeref allows
|
|
349
|
+
// trailing kDeprecated. Ambiguous which level the `deprecated` belongs.
|
|
350
|
+
[$.typeref],
|
|
351
|
+
[$._declClass],
|
|
352
|
+
// Iter 65: trailing `inline` (no `;`) before body in defProc conflicts
|
|
353
|
+
// with `_declProc`'s `inline ;` _procAttributeNoExt repeat. Same for
|
|
354
|
+
// _declOperator (class operators can also be inlined).
|
|
355
|
+
[$._declProc],
|
|
356
|
+
[$._declOperator],
|
|
357
|
+
// Phase 3b iter 7: declEnum trailing platform/deprecated hint is
|
|
358
|
+
// ambiguous with the same hint appearing on the enclosing declType.
|
|
359
|
+
[$.declEnum],
|
|
360
|
+
// Phase 3b iter 21: declSet has same trailing-hint ambiguity.
|
|
361
|
+
[$.declSet],
|
|
362
|
+
// Phase 3b iter 10: _declFields choice of repeat1(declField) vs
|
|
363
|
+
// trailing declFieldNoSemi — both reach declField from the same state.
|
|
364
|
+
[$._declFields],
|
|
365
|
+
[$.declClass],
|
|
366
|
+
// Phase 3b iter 11: declFieldNoSemi uses a narrower type set; conflicts
|
|
367
|
+
// with the full `type` rule's choices.
|
|
368
|
+
[$.type, $.declFieldNoSemi],
|
|
369
|
+
// The following conflict rules are only needed because "public" can be
|
|
370
|
+
// a visibility or an attribute. *sigh*
|
|
371
|
+
// TODO: We would probably avoid this by having separate decl* clauses
|
|
372
|
+
// for use inside classes and at unit scope, since the "public"
|
|
373
|
+
// attribute seems to only be valid for standalone routines.
|
|
374
|
+
...enable_if(public_name,
|
|
375
|
+
[$._declProc ], [ $._declOperator], [$.declConst], [$.declVar],
|
|
376
|
+
[$.declType], [$.declProp]
|
|
377
|
+
),
|
|
378
|
+
// RTTI attributes clash with fpc declaration hints syntax since both
|
|
379
|
+
// are surrounded by brackets.
|
|
380
|
+
...enable_if(rtti,
|
|
381
|
+
[ $.declProcFwd ], [ $.declVars], [ $.declConsts ], [ $.declTypes]
|
|
382
|
+
),
|
|
383
|
+
// `procedure (` could be a declaration of an anonymous procedure or
|
|
384
|
+
// the call of a function named "procedure" (which doesn't actually
|
|
385
|
+
// make sense, but for Treesitter it does), so we need another conflict
|
|
386
|
+
// here.
|
|
387
|
+
//...enable_if(lambda, [ $.lambda ]),
|
|
388
|
+
],
|
|
389
|
+
|
|
390
|
+
rules: {
|
|
391
|
+
root: $ => optional(choice(
|
|
392
|
+
$.program,
|
|
393
|
+
$.library,
|
|
394
|
+
$.unit,
|
|
395
|
+
$.package,
|
|
396
|
+
$._definitions // For include files
|
|
397
|
+
)),
|
|
398
|
+
|
|
399
|
+
// HIGH LEVEL ----------------------------------------------------------
|
|
400
|
+
|
|
401
|
+
program: $ => seq(
|
|
402
|
+
$.kProgram, $.moduleName, ';',
|
|
403
|
+
optional($._definitions),
|
|
404
|
+
// Phase 3b iter 34: CIL/.NET attribute on program entry (`[STAThread]`).
|
|
405
|
+
// (UnitTests4Net.dpr Delphi.NET pattern.)
|
|
406
|
+
optional($.rttiAttributes),
|
|
407
|
+
tr($,'block'),
|
|
408
|
+
$.kEndDot
|
|
409
|
+
),
|
|
410
|
+
|
|
411
|
+
library: $ => seq(
|
|
412
|
+
$.kLibrary, $.moduleName, ';',
|
|
413
|
+
optional($._definitions),
|
|
414
|
+
choice(tr($,'block'), $.kEnd),
|
|
415
|
+
$.kEndDot
|
|
416
|
+
),
|
|
417
|
+
|
|
418
|
+
unit: $ => seq(
|
|
419
|
+
$.kUnit, $.moduleName,
|
|
420
|
+
// `unit Spring.Foo deprecated 'Use Spring.Bar instead';` — unit-level
|
|
421
|
+
// deprecation hint (Spring4D pattern in retired units).
|
|
422
|
+
optional(seq($.kDeprecated, optional($._expr))),
|
|
423
|
+
';',
|
|
424
|
+
repeat(choice(
|
|
425
|
+
$.interface,
|
|
426
|
+
$.implementation,
|
|
427
|
+
$.initialization,
|
|
428
|
+
$.finalization,
|
|
429
|
+
)),
|
|
430
|
+
$.kEnd, $.kEndDot
|
|
431
|
+
),
|
|
432
|
+
|
|
433
|
+
// Delphi package file (.dpk). Structure:
|
|
434
|
+
// package <Ident>;
|
|
435
|
+
// <compiler directives / $I includes>
|
|
436
|
+
// [requires <pkgList> ;]
|
|
437
|
+
// [contains <unitList> ;] -- units may carry in 'path.pas'
|
|
438
|
+
// end .
|
|
439
|
+
package: $ => seq(
|
|
440
|
+
$.kPackage, $.moduleName, ';',
|
|
441
|
+
optional($._definitions),
|
|
442
|
+
optional($.declRequires),
|
|
443
|
+
optional($.declContains),
|
|
444
|
+
$.kEnd, $.kEndDot
|
|
445
|
+
),
|
|
446
|
+
|
|
447
|
+
interface: $ => seq($.kInterface, optional($._declarations)),
|
|
448
|
+
implementation: $ => seq($.kImplementation, optional($._definitions)),
|
|
449
|
+
initialization: $ => seq($.kInitialization, optional(tr($,'_statements'))),
|
|
450
|
+
finalization: $ => seq($.kFinalization, optional(tr($,'_statements'))),
|
|
451
|
+
|
|
452
|
+
moduleName: $ => delimited1($.identifier, $.kDot),
|
|
453
|
+
|
|
454
|
+
// STATEMENTS ---------------------------------------------------------
|
|
455
|
+
|
|
456
|
+
...statements(false),
|
|
457
|
+
...statements(true),
|
|
458
|
+
|
|
459
|
+
assignment: $ => op.infix(1,
|
|
460
|
+
choice($._expr, $.varAssignDef),
|
|
461
|
+
choice(
|
|
462
|
+
$.kAssign,
|
|
463
|
+
// Phase 3b iter 29: compound assignment `+=`/`-=`/`*=`/`/=`.
|
|
464
|
+
// FPC has always supported these; Delphi accepts them when
|
|
465
|
+
// `{$EXTENDEDCOMPATIBILITY}` directive is on (since 2005).
|
|
466
|
+
// FPC-corpus files (lazutils) use them heavily.
|
|
467
|
+
$.kAssignAdd, $.kAssignSub, $.kAssignMul, $.kAssignDiv,
|
|
468
|
+
),
|
|
469
|
+
$._expr
|
|
470
|
+
),
|
|
471
|
+
varAssignDef: $ => seq($.kVar, $.identifier,
|
|
472
|
+
optional(seq(
|
|
473
|
+
':',
|
|
474
|
+
field('type', $.typeref)
|
|
475
|
+
))),
|
|
476
|
+
varDef: $ => seq($.kVar, delimited1($.identifier), ':', field('type', $.typeref)),
|
|
477
|
+
// Delphi 12+ inline `const NAME [: T] = value`. Used inside statement
|
|
478
|
+
// bodies (begin..end blocks). Distinct from `declConst` which lives
|
|
479
|
+
// in unit/procedure-local `const X = 1;\n Y = 2;` blocks under kConst.
|
|
480
|
+
constInline: $ => seq(
|
|
481
|
+
$.kConst, field('name', $.identifier),
|
|
482
|
+
optional(seq(':', field('type', $.typeref))),
|
|
483
|
+
'=', field('value', $._expr)
|
|
484
|
+
),
|
|
485
|
+
label: $ => seq($.identifier, ':'),
|
|
486
|
+
caseLabel: $ => seq(delimited1(choice($._expr, $.range)), ':'),
|
|
487
|
+
|
|
488
|
+
_statements: $ => repeat1(choice($.varDef, $._statement, $.label)),
|
|
489
|
+
_statementsTr: $ => seq(
|
|
490
|
+
repeat(choice($._statement, $.label)),
|
|
491
|
+
choice(tr($,'_statement'), $._statement)
|
|
492
|
+
),
|
|
493
|
+
|
|
494
|
+
statements: $ => $._statements,
|
|
495
|
+
statementsTr: $ => $._statementsTr,
|
|
496
|
+
|
|
497
|
+
// asmBody is treated as ONE opaque token — content between `asm` and
|
|
498
|
+
// the next word-boundary `end`. This grammar is not tree-sitter-asm;
|
|
499
|
+
// the body is consumed wholesale and not subdivided into instructions,
|
|
500
|
+
// labels, registers, etc. Use a dedicated assembler grammar if you
|
|
501
|
+
// need that.
|
|
502
|
+
//
|
|
503
|
+
// The regex matches any character except an `end` keyword at a word
|
|
504
|
+
// boundary. Alternations: any non-e/E char, e/E not followed by n/N,
|
|
505
|
+
// en/EN not followed by d/D, end immediately followed by ident-char
|
|
506
|
+
// (so it's NOT the keyword, e.g. 'endif' / 'endloop').
|
|
507
|
+
asmBody: $ => token(prec(-1, repeat1(choice(
|
|
508
|
+
// Identifier not starting with e/E — consume as a unit so an
|
|
509
|
+
// embedded `end` substring (like in `addend` or `xchg ext`)
|
|
510
|
+
// doesn't falsely terminate the body.
|
|
511
|
+
/[a-df-zA-DF-Z_][a-zA-Z0-9_]*/,
|
|
512
|
+
// Phase 3b iter 18: local asm labels `@@end:` / `@@loop:` etc.
|
|
513
|
+
// Without this the `@@end` parses as `@@` (two `[^eE]`) + `end`
|
|
514
|
+
// (asm terminator), falsely closing the asm block.
|
|
515
|
+
// Match `@@<ident>` as one chunk so `end` after `@@` doesn't
|
|
516
|
+
// terminate.
|
|
517
|
+
/@@[a-zA-Z_][a-zA-Z0-9_]*/,
|
|
518
|
+
/@[a-zA-Z_][a-zA-Z0-9_]*/,
|
|
519
|
+
// Single-char alternatives — preserve the original e/E word-boundary
|
|
520
|
+
// detection: stop when we see the bare `end` keyword.
|
|
521
|
+
/[^eE]/,
|
|
522
|
+
/[eE][^nN]/,
|
|
523
|
+
/[eE][nN][^dD]/,
|
|
524
|
+
/[eE][nN][dD][A-Za-z0-9_]/
|
|
525
|
+
)))),
|
|
526
|
+
|
|
527
|
+
// EXPRESSIONS ---------------------------------------------------------
|
|
528
|
+
|
|
529
|
+
_expr: $ => choice(
|
|
530
|
+
$._ref, $.exprBinary, $.exprUnary,
|
|
531
|
+
// Delphi 12+ inline if-then-else expression:
|
|
532
|
+
// X := if Cond then 1 else 0;
|
|
533
|
+
// Result := (if Assigned(X) then X.Name else '');
|
|
534
|
+
$.exprIf,
|
|
535
|
+
),
|
|
536
|
+
|
|
537
|
+
// Inline if-expression: `if <cond> then <a> else <b>`. Distinct from
|
|
538
|
+
// the if-then-else STATEMENT (rn('ifElse')) — this one returns a value.
|
|
539
|
+
// Right-associative + lower precedence than binary operators so that
|
|
540
|
+
// `a + if c then x else y` groups as `a + (if c then x else y)`.
|
|
541
|
+
exprIf: $ => prec.right(0, seq(
|
|
542
|
+
$.kIf, field('condition', $._expr),
|
|
543
|
+
$.kThen, field('then', $._expr),
|
|
544
|
+
$.kElse, field('else', $._expr),
|
|
545
|
+
)),
|
|
546
|
+
|
|
547
|
+
_ref: $ => choice(
|
|
548
|
+
...enable_if(templates && fpc,
|
|
549
|
+
// TODO: Ideally, the kSpecialize should be part of exprTpl,
|
|
550
|
+
// but for some reason this leads to a rule conflict, so for
|
|
551
|
+
// now we just put it here.
|
|
552
|
+
//
|
|
553
|
+
// Also, we have to write the rule in this weird weird way,
|
|
554
|
+
// because if we just do
|
|
555
|
+
//
|
|
556
|
+
// seq(optional($.kSpecialize), $.identifier)
|
|
557
|
+
//
|
|
558
|
+
// then we can't have a standalone identifier named
|
|
559
|
+
// "specialize". (Bug in tree-sitter?)
|
|
560
|
+
prec.left(choice(
|
|
561
|
+
seq($.kSpecialize, $.identifier),
|
|
562
|
+
seq(alias($.kSpecialize, $.identifier)),
|
|
563
|
+
))
|
|
564
|
+
),
|
|
565
|
+
$.identifier,
|
|
566
|
+
$._literal, $.inherited, $.exprDot,
|
|
567
|
+
$.exprBrackets, $.exprParens, $.exprSubscript, $.exprCall,
|
|
568
|
+
alias($.exprDeref, $.exprUnary),
|
|
569
|
+
alias($.exprAs, $.exprBinary),
|
|
570
|
+
...enable_if(templates, $.exprTpl),
|
|
571
|
+
...enable_if(lambda, $.lambda)
|
|
572
|
+
),
|
|
573
|
+
|
|
574
|
+
lambda: $ => seq(
|
|
575
|
+
choice($.kProcedure, $.kFunction),
|
|
576
|
+
field('args', optional($.declArgs)),
|
|
577
|
+
optional(seq(
|
|
578
|
+
':',
|
|
579
|
+
field('type', $.typeref),
|
|
580
|
+
)),
|
|
581
|
+
// Optional inline calling-convention on the lambda (Vcl.Edge.pas
|
|
582
|
+
// passes anonymous-method callbacks with `stdcall` to interop with
|
|
583
|
+
// WebView2 COM):
|
|
584
|
+
// var h := function(R: HRESULT): HRESULT stdcall begin ... end;
|
|
585
|
+
optional(choice(
|
|
586
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
587
|
+
$.kRegister, $.kWinapi, $.kInline
|
|
588
|
+
)),
|
|
589
|
+
field('local', optional($._definitions)),
|
|
590
|
+
field('body', choice(tr($, 'block'), tr($, 'asm'))),
|
|
591
|
+
),
|
|
592
|
+
|
|
593
|
+
inherited: $ => prec.right(seq($.kInherited, optional($.identifier))),
|
|
594
|
+
|
|
595
|
+
exprDot: $ => choice(
|
|
596
|
+
op.infix(5, $._ref, $.kDot, $._ref),
|
|
597
|
+
// Keyword-as-identifier on RHS of dot: `TdxUnaryOp.Not`, `T.And`,
|
|
598
|
+
// `T.Or`, `T.Xor` — common in DevExpress enum-class patterns where
|
|
599
|
+
// the enum members are named after logical operators.
|
|
600
|
+
// Phase 3b iter 21: add kFunction/kProcedure for `TdxToken.FUNCTION`
|
|
601
|
+
// (DevExpress dxEMF criteria parser enum-class).
|
|
602
|
+
prec.left(5, seq(
|
|
603
|
+
field('lhs', $._ref),
|
|
604
|
+
field('operator', $.kDot),
|
|
605
|
+
field('rhs', choice(
|
|
606
|
+
alias($.kNot, $.identifier),
|
|
607
|
+
alias($.kAnd, $.identifier),
|
|
608
|
+
alias($.kOr, $.identifier),
|
|
609
|
+
alias($.kXor, $.identifier),
|
|
610
|
+
alias($.kFunction, $.identifier),
|
|
611
|
+
alias($.kProcedure, $.identifier),
|
|
612
|
+
))
|
|
613
|
+
)),
|
|
614
|
+
),
|
|
615
|
+
exprDeref: $ => op.postfix(4, $._expr, $.kHat),
|
|
616
|
+
|
|
617
|
+
exprAs: $ => op.infix(3, $._expr, $.kAs, $._expr),
|
|
618
|
+
|
|
619
|
+
// Unfortunately, we can't use $.exprArgs for $.exprTpl because the
|
|
620
|
+
// parser cannot handle it.
|
|
621
|
+
//
|
|
622
|
+
// There are two conflicting rules:
|
|
623
|
+
//
|
|
624
|
+
// 0. Binary comparison: a < b
|
|
625
|
+
// 1. Template use: a < b >
|
|
626
|
+
// ^^^^^
|
|
627
|
+
// prefix
|
|
628
|
+
//
|
|
629
|
+
// In order for this to work, the prefix must produce the same nodes in
|
|
630
|
+
// both cases. This is not the case when we introduce a wrapper node.
|
|
631
|
+
//
|
|
632
|
+
// Example:
|
|
633
|
+
//
|
|
634
|
+
// exprBinary
|
|
635
|
+
// identifier
|
|
636
|
+
// <
|
|
637
|
+
// identifier
|
|
638
|
+
//
|
|
639
|
+
// vs.
|
|
640
|
+
//
|
|
641
|
+
// exprTpl
|
|
642
|
+
// exprArgs <-- extra node
|
|
643
|
+
// identifier
|
|
644
|
+
// <
|
|
645
|
+
// identifier
|
|
646
|
+
// >
|
|
647
|
+
//
|
|
648
|
+
// Basically the way this works is that there is a tentative node like
|
|
649
|
+
// "exprTplOrBinary", which looks like this:
|
|
650
|
+
//
|
|
651
|
+
// exprTplOrBinary
|
|
652
|
+
// identifier
|
|
653
|
+
// <
|
|
654
|
+
// identifier
|
|
655
|
+
//
|
|
656
|
+
// At this point we don't yet know what we are dealing with. The next
|
|
657
|
+
// token will determine whether we are dealing with a comparison or a
|
|
658
|
+
// template. Then the existing node is simply "renamed". Because of
|
|
659
|
+
// this, we can't have an extra node in only one of the branches.
|
|
660
|
+
//
|
|
661
|
+
// Generic instantiation `Foo<A, B, C>`. Originally `delimited1($._expr)`
|
|
662
|
+
// but that caused `H < 0` (binary less-than) to be parsed as `Foo<0>`
|
|
663
|
+
// (exprTpl) and parser then inserted MISSING `>` when no matching `>`
|
|
664
|
+
// arrived. Narrowed to `typeref` — covers `TList<TFoo>`,
|
|
665
|
+
// `TDictionary<string, TBar>` etc. (real generic instantiation).
|
|
666
|
+
// Constant generic args (rare in Delphi corpus) lose structural parsing
|
|
667
|
+
// but the program-as-a-whole parses cleanly.
|
|
668
|
+
exprTpl: $ => op.args(5, $._ref, $.kLt, delimited1($.typeref, ',', 5), $.kGt),
|
|
669
|
+
exprSubscript: $ => op.args(5, $._ref, '[', $.exprArgs, ']' ),
|
|
670
|
+
exprCall: $ => op.args(5, $._ref, '(', optional($.exprArgs), ')' ),
|
|
671
|
+
|
|
672
|
+
// Pascal legacy string formatting for WriteLn(foo:4:3) etc.
|
|
673
|
+
legacyFormat: $ => repeat1(seq(':', $._expr)),
|
|
674
|
+
|
|
675
|
+
exprArgs: $ => delimited1(seq($._expr, optional($.legacyFormat))),
|
|
676
|
+
|
|
677
|
+
exprBinary: $ => choice(
|
|
678
|
+
op.infix(1, $._expr, $.kLt, $._expr),
|
|
679
|
+
op.infix(1, $._ref, $.kLt, $._expr),
|
|
680
|
+
op.infix(1, $._expr, $.kEq, $._expr),
|
|
681
|
+
op.infix(1, $._expr, $.kNeq, $._expr),
|
|
682
|
+
op.infix(1, $._expr, $.kGt, $._expr),
|
|
683
|
+
op.infix(1, $._expr, $.kLte, $._expr),
|
|
684
|
+
op.infix(1, $._expr, $.kGte, $._expr),
|
|
685
|
+
op.infix(1, $._expr, $.kIn, $._expr),
|
|
686
|
+
op.infix(1, $._expr, $.kIs, $._expr),
|
|
687
|
+
|
|
688
|
+
op.infix(2, $._expr, $.kAdd, $._expr),
|
|
689
|
+
op.infix(2, $._expr, $.kSub, $._expr),
|
|
690
|
+
op.infix(2, $._expr, $.kOr, $._expr),
|
|
691
|
+
op.infix(2, $._expr, $.kXor, $._expr),
|
|
692
|
+
|
|
693
|
+
op.infix(3, $._expr, $.kMul, $._expr),
|
|
694
|
+
op.infix(3, $._expr, $.kFdiv,$._expr),
|
|
695
|
+
op.infix(3, $._expr, $.kDiv, $._expr),
|
|
696
|
+
op.infix(3, $._expr, $.kMod, $._expr),
|
|
697
|
+
op.infix(3, $._expr, $.kAnd, $._expr),
|
|
698
|
+
op.infix(3, $._expr, $.kShl, $._expr),
|
|
699
|
+
op.infix(3, $._expr, $.kShr, $._expr),
|
|
700
|
+
// Phase 3b iter 30: C-style shift `<<` and `>>` — FPC supports
|
|
701
|
+
// these as alias for `shl`/`shr`. lazutils uses them heavily.
|
|
702
|
+
op.infix(3, $._expr, alias(token('<<'), $.kShl), $._expr),
|
|
703
|
+
op.infix(3, $._expr, alias(token('>>'), $.kShr), $._expr),
|
|
704
|
+
),
|
|
705
|
+
|
|
706
|
+
exprUnary: $ => choice(
|
|
707
|
+
op.prefix(4, $.kNot, $._expr),
|
|
708
|
+
op.prefix(4, $.kAdd, $._expr),
|
|
709
|
+
op.prefix(4, $.kSub, $._expr),
|
|
710
|
+
op.prefix(4, $.kAt, $._expr),
|
|
711
|
+
),
|
|
712
|
+
|
|
713
|
+
exprParens: $ => prec.left(5,seq('(', $._expr, ')')),
|
|
714
|
+
|
|
715
|
+
// Set or array literal
|
|
716
|
+
exprBrackets: $ => seq(
|
|
717
|
+
'[', delimited(choice($._expr, $.range)), ']'
|
|
718
|
+
),
|
|
719
|
+
|
|
720
|
+
// TYPES ---------------------------------------------------------------
|
|
721
|
+
|
|
722
|
+
type: $ => pp($,choice(
|
|
723
|
+
$.typeref,
|
|
724
|
+
$.declMetaClass,
|
|
725
|
+
$.declEnum,
|
|
726
|
+
$.declSet,
|
|
727
|
+
$.declArray,
|
|
728
|
+
$.declFile,
|
|
729
|
+
$.declString,
|
|
730
|
+
$.declProcRef,
|
|
731
|
+
// Anonymous record/class/object types in type position. Required for
|
|
732
|
+
// patterns like:
|
|
733
|
+
// const Errors: array [0..N] of record A: T; B: T; end = (...);
|
|
734
|
+
// var X: record I: Integer; end;
|
|
735
|
+
$.declClass,
|
|
736
|
+
// IFDEF-as-type: `FStream: {$IFDEF X}System.Classes{$ELSE}Classes{$ENDIF};`
|
|
737
|
+
// (the external scanner produces pp_block as one opaque token at this
|
|
738
|
+
// position; the regex pp / pp_block-in-extras paths still handle the
|
|
739
|
+
// "wraps a whole statement/declaration" case.)
|
|
740
|
+
// prec(-1): only used when no real type follows the pp_block — handles
|
|
741
|
+
// `TRecInfo = {$IFDEF CPUX86}packed{$ENDIF} record...end;` correctly
|
|
742
|
+
// (pp_block becomes extras, real type is `packed record...` after).
|
|
743
|
+
prec(-1, $.pp_block),
|
|
744
|
+
// Qualified type where IFDEF wraps the namespace prefix:
|
|
745
|
+
// Response: {$IFDEF USE_NAMESPACES}Web.HTTPApp{$ELSE}HTTPApp{$ENDIF}.TWebResponse
|
|
746
|
+
// (EurekaLog / Embarcadero RTL XE2+ unit-scope-name pattern.)
|
|
747
|
+
// Explicit prec(1) so the pp_block is consumed structurally, not as
|
|
748
|
+
// extras whitespace that would orphan the trailing `.TFoo`.
|
|
749
|
+
prec(1, seq($.pp_block, $.kDot, $.typeref)),
|
|
750
|
+
)),
|
|
751
|
+
|
|
752
|
+
typeref: $ => seq(
|
|
753
|
+
...enable_if(fpc, field('_dummy', optional($.kSpecialize))),
|
|
754
|
+
$._typeref,
|
|
755
|
+
...enable_if(delphi, optional(seq($.kDeprecated, $._expr))),
|
|
756
|
+
),
|
|
757
|
+
|
|
758
|
+
_typeref: $ => choice(
|
|
759
|
+
$.identifier, $.typerefDot,
|
|
760
|
+
...enable_if(templates, $.typerefTpl),
|
|
761
|
+
$.typerefPtr,
|
|
762
|
+
// Soft keywords that can also serve as type names. Delphi allows
|
|
763
|
+
// many context-sensitive keywords to be used as identifiers when
|
|
764
|
+
// the surrounding syntax requires one. These come up most often
|
|
765
|
+
// in OleServer / Excel / Word automation wrappers where the OLE
|
|
766
|
+
// IDL was translated mechanically and used clashing names.
|
|
767
|
+
//
|
|
768
|
+
// Phase 3b iter 19: kIndex removed from this list so that at
|
|
769
|
+
// expression positions, kIndex is NOT in valid_symbols (it's
|
|
770
|
+
// only reachable from declProp's index clause and procExternal's
|
|
771
|
+
// index ordinal — both class/external scope). With kIndex out of
|
|
772
|
+
// reach, tree-sitter's word-rule soft-keyword promotion fires
|
|
773
|
+
// and "Index" tokenizes as identifier in expressions like
|
|
774
|
+
// `if i < Index then ...`. _typeref still gets "Index" via the
|
|
775
|
+
// $.identifier path (no functional regression).
|
|
776
|
+
alias($.kReference, $.identifier),
|
|
777
|
+
alias($.kMessage, $.identifier),
|
|
778
|
+
alias($.kName, $.identifier),
|
|
779
|
+
alias($.kRead, $.identifier),
|
|
780
|
+
alias($.kWrite, $.identifier),
|
|
781
|
+
),
|
|
782
|
+
|
|
783
|
+
typerefDot: $ => op.infix(1,$._typeref, $.kDot, $._typeref),
|
|
784
|
+
typerefTpl: $ => op.args(1, $._typeref, $.kLt, $.typerefArgs, $.kGt),
|
|
785
|
+
typerefPtr: $ => op.prefix(1,$.kHat, $._typeref),
|
|
786
|
+
typerefArgs: $ => delimited1($._typeref),
|
|
787
|
+
|
|
788
|
+
// GENERIC TYPE DECLARATION --------------------------------------------
|
|
789
|
+
//
|
|
790
|
+
// E.g. Foo<A: B, C: D<E>>.XYZ<T>
|
|
791
|
+
// ^ ^
|
|
792
|
+
// Note the optional constraints, which makes this different from a
|
|
793
|
+
// specialization
|
|
794
|
+
//
|
|
795
|
+
// We treat regular names as a special case of generic names. I.e. if
|
|
796
|
+
// you see $._genericName somewhere, it doesn't mean that the name HAS
|
|
797
|
+
// to be generic, it could just be a regular name like "TFoobar" or
|
|
798
|
+
// "MyUnit.Foo".
|
|
799
|
+
|
|
800
|
+
genericDot: $ => op.infix(1,$._genericName, $.kDot, $._genericName),
|
|
801
|
+
genericTpl: $ => op.args(2,$._genericName, $.kLt, $.genericArgs, $.kGt),
|
|
802
|
+
|
|
803
|
+
_genericName: $ => choice(
|
|
804
|
+
$.identifier, $.genericDot, ...enable_if(templates, $.genericTpl)
|
|
805
|
+
),
|
|
806
|
+
genericArgs: $ => delimited1($.genericArg, ';'),
|
|
807
|
+
genericArg: $ => seq(
|
|
808
|
+
field('name', delimited1($.identifier)),
|
|
809
|
+
// Type constraint: `T: BaseType` or one/more of the special
|
|
810
|
+
// constraint keywords `class` / `record` / `constructor`, comma-
|
|
811
|
+
// separated. Examples:
|
|
812
|
+
// <T: class> must be a class type
|
|
813
|
+
// <T: constructor> must have parameterless ctor
|
|
814
|
+
// <T: class, constructor> both
|
|
815
|
+
// <T: IFoo> must implement interface
|
|
816
|
+
// <T: IFoo, constructor> both
|
|
817
|
+
field('type', optional(seq(
|
|
818
|
+
':',
|
|
819
|
+
$._genericConstraint,
|
|
820
|
+
repeat(seq(',', $._genericConstraint))
|
|
821
|
+
))),
|
|
822
|
+
field('defaultValue', optional($.defaultValue))
|
|
823
|
+
),
|
|
824
|
+
_genericConstraint: $ => choice(
|
|
825
|
+
$.typeref,
|
|
826
|
+
$.kClass,
|
|
827
|
+
$.kRecord,
|
|
828
|
+
$.kConstructor,
|
|
829
|
+
),
|
|
830
|
+
|
|
831
|
+
// LITERALS -----------------------------------------------------------
|
|
832
|
+
|
|
833
|
+
_literal: $ => choice(
|
|
834
|
+
$.literalString,
|
|
835
|
+
$.literalNumber,
|
|
836
|
+
$.kNil, $.kTrue, $.kFalse
|
|
837
|
+
),
|
|
838
|
+
literalString: $ => repeat1($._literalString),
|
|
839
|
+
// Delphi 12+ multi-line string (triple-quoted, like Python's """) — must
|
|
840
|
+
// be matched BEFORE the single-quoted form because tree-sitter uses
|
|
841
|
+
// longest-match. The body matches any chars (including newlines and
|
|
842
|
+
// single quotes) up to a closing `'''`.
|
|
843
|
+
_literalString: $ => choice(
|
|
844
|
+
// Delphi 12+ triple-quoted multi-line string. prec(2) so the
|
|
845
|
+
// regex engine prefers this over the single-quoted form when
|
|
846
|
+
// three quotes start the literal.
|
|
847
|
+
token(prec(2, /'''[\s\S]*?'''/)),
|
|
848
|
+
// Single-quoted string. `''` (doubled single-quote) is Delphi's
|
|
849
|
+
// escape for one literal apostrophe — `'Can''t'` is "Can't".
|
|
850
|
+
// Body alternation: any non-quote char, OR a doubled quote.
|
|
851
|
+
token(/'([^']|'')*'/),
|
|
852
|
+
$.literalChar
|
|
853
|
+
),
|
|
854
|
+
// Char literal: `#NN` decimal/hex char code, OR `^X` control char
|
|
855
|
+
// (the latter handled by the external scanner with proper lookahead
|
|
856
|
+
// to disambiguate from `^TFoo` pointer-to-type).
|
|
857
|
+
literalChar: $ => choice(
|
|
858
|
+
seq('#', $._literalInt),
|
|
859
|
+
$.char_literal
|
|
860
|
+
),
|
|
861
|
+
literalNumber: $ => choice($._literalInt, $._literalFloat, $.trailing_dot_float),
|
|
862
|
+
// Delphi 11+ allows `_` as a digit-group separator: `1_000_000_000`
|
|
863
|
+
// or `$FFFF_FFFF`. Underscore is between digits, never leading/trailing.
|
|
864
|
+
_literalInt: $ => choice(
|
|
865
|
+
token.immediate(/[-+]?[0-9](_?[0-9])*/),
|
|
866
|
+
token.immediate(/\$[a-fA-F0-9](_?[a-fA-F0-9])*/),
|
|
867
|
+
// Phase 3b iter 24: Delphi octal literal `&NNN` (System.Beacon
|
|
868
|
+
// uses `&1`, `&0`; RTL Linux/Posix code uses `&777` perm masks).
|
|
869
|
+
token.immediate(/&[0-7](_?[0-7])*/),
|
|
870
|
+
// Delphi binary literal `%NNN` — used in lower-level RTL code.
|
|
871
|
+
token.immediate(/%[01](_?[01])*/),
|
|
872
|
+
),
|
|
873
|
+
// Float: optional sign, digits with optional decimal portion, optional
|
|
874
|
+
// scientific exponent. Exponent letter is `e` OR `E` (case-insensitive).
|
|
875
|
+
// NOTE: trailing-dot form (`100.`) is intentionally NOT supported here
|
|
876
|
+
// because it conflicts with the `..` range operator (e.g. `array [0..N]`
|
|
877
|
+
// would be lex-greedy-matched as `0.` float + `.N` and break the array).
|
|
878
|
+
_literalFloat: $ => prec(10, /[-+]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?/),
|
|
879
|
+
|
|
880
|
+
range: $ => seq(
|
|
881
|
+
$._expr, '..', $._expr
|
|
882
|
+
),
|
|
883
|
+
|
|
884
|
+
// DEFINITIONS --------------------------------------------------------
|
|
885
|
+
|
|
886
|
+
_definitions: $ => repeat1($._definition),
|
|
887
|
+
_definition: $ => choice(
|
|
888
|
+
$.declTypes, $.declVars, $.declConsts, $.defProc,
|
|
889
|
+
alias($.declProcFwd, $.declProc),
|
|
890
|
+
$.declLabels, $.declUses, $.declExports,
|
|
891
|
+
|
|
892
|
+
// Top-level CIL/.NET attributes such as
|
|
893
|
+
// [assembly: AssemblyDescription('...')]
|
|
894
|
+
// found in Delphi.NET .inc files (Indy assembly metadata).
|
|
895
|
+
$.declAssemblyAttribute,
|
|
896
|
+
|
|
897
|
+
// Not actually valid syntax, but helps the parser recover:
|
|
898
|
+
prec(-1,$.blockTr)
|
|
899
|
+
),
|
|
900
|
+
|
|
901
|
+
// A single bracketed attribute that applies to a module-level target
|
|
902
|
+
// (e.g. assembly, module). Distinct from rttiAttributes (which attach
|
|
903
|
+
// to declarations) because these are standalone.
|
|
904
|
+
declAssemblyAttribute: $ => prec(1, seq(
|
|
905
|
+
'[', $.identifier, ':', delimited($._ref), ']'
|
|
906
|
+
)),
|
|
907
|
+
|
|
908
|
+
defProc: $ => seq(
|
|
909
|
+
/*pp($,*/ field('header', $.declProc)/*)*/,
|
|
910
|
+
// DevExpress nested fn pattern (iter 65, extended iter 66):
|
|
911
|
+
// function Production(...): Single; inline
|
|
912
|
+
// begin ... end;
|
|
913
|
+
// function EnumChildProc(...): BOOL; stdcall
|
|
914
|
+
// var ...
|
|
915
|
+
// Trailing calling-conv / inline without `;` before body.
|
|
916
|
+
// Restricted to safe keyword set (kDeprecated 'msg' conflicts with []).
|
|
917
|
+
optional(field('trailingAttr', choice(
|
|
918
|
+
$.kInline,
|
|
919
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
920
|
+
$.kRegister, $.kWinapi
|
|
921
|
+
))),
|
|
922
|
+
pp(
|
|
923
|
+
$,
|
|
924
|
+
field('local', optional($._definitions)),
|
|
925
|
+
field('body', choice(tr($, 'block'), tr($, 'asm'))),
|
|
926
|
+
';'
|
|
927
|
+
)
|
|
928
|
+
),
|
|
929
|
+
|
|
930
|
+
declProcFwd: $ => seq(
|
|
931
|
+
$._declProc,
|
|
932
|
+
choice(seq($.kForward, ';'), $.procExternal),
|
|
933
|
+
repeat($._procAttribute)
|
|
934
|
+
),
|
|
935
|
+
|
|
936
|
+
// DECLARATIONS -------------------------------------------------------
|
|
937
|
+
|
|
938
|
+
_visibility: $ => choice(
|
|
939
|
+
$.kPublished, $.kPublic, $.kProtected, $.kPrivate
|
|
940
|
+
),
|
|
941
|
+
|
|
942
|
+
_declarations: $ => repeat1(choice(
|
|
943
|
+
$.declTypes, $.declVars, $.declConsts, $.declProc, $.declProp,
|
|
944
|
+
alias($.declProcFwd, $.declProc),
|
|
945
|
+
$.declUses, $.declLabels, $.declExports
|
|
946
|
+
)),
|
|
947
|
+
_classDeclarations: $ => repeat1(choice(
|
|
948
|
+
$.declTypes, $.declVars, $.declConsts, $.declProc, $.declProp
|
|
949
|
+
)),
|
|
950
|
+
|
|
951
|
+
defaultValue: $ => seq($.kEq, $._initializer),
|
|
952
|
+
|
|
953
|
+
// Declaration sections
|
|
954
|
+
|
|
955
|
+
// Permissive uses-clause: accepts the normal `kUses delimited(unit) ;`
|
|
956
|
+
// form AND patterns where `{$IF}/{$ELSE}/{$ENDIF}` directives appear
|
|
957
|
+
// between units OR wrap the entire `units ;` block:
|
|
958
|
+
// uses
|
|
959
|
+
// {$IFDEF HAS_UNITSCOPE}
|
|
960
|
+
// System.SysUtils;
|
|
961
|
+
// {$ELSE}
|
|
962
|
+
// SysUtils;
|
|
963
|
+
// {$ENDIF}
|
|
964
|
+
// We allow units, commas, pp tokens AND `;` to interleave; the rule
|
|
965
|
+
// terminates on the LAST `;` it can consume. Trades grammatical
|
|
966
|
+
// strictness for parsing robustness on real-world JEDI/JCL/Indy code.
|
|
967
|
+
declUses: $ => seq(
|
|
968
|
+
$.kUses,
|
|
969
|
+
// pp_block (external-scanner token) is added so a uses-clause whose
|
|
970
|
+
// `;` lives INSIDE an IFDEF block doesn't dangle:
|
|
971
|
+
// uses
|
|
972
|
+
// {$IFDEF MSWINDOWS}Winapi.Windows;{$ENDIF MSWINDOWS}
|
|
973
|
+
// {$IFDEF POSIX}Posix.Stdlib;{$ENDIF POSIX}
|
|
974
|
+
repeat1(choice($.declUsesUnit, ',', $.pp, $.pp_block, ';'))
|
|
975
|
+
),
|
|
976
|
+
// A unit reference inside a uses / contains clause. In program (.dpr) and
|
|
977
|
+
// package (.dpk) files the unit may carry an explicit source-file path
|
|
978
|
+
// via in '<literal>' . Optional braces with a form-name hint
|
|
979
|
+
// (e.g. {Form1}) are stripped as comments by the extras layer.
|
|
980
|
+
declUsesUnit: $ => seq($.moduleName, optional(seq($.kIn, $.literalString))),
|
|
981
|
+
// Permissive same as declUses — allows IFDEF interleaving / wrapping.
|
|
982
|
+
declRequires: $ => seq(
|
|
983
|
+
$.kRequires,
|
|
984
|
+
repeat1(choice($.moduleName, ',', $.pp, $.pp_block, ';'))
|
|
985
|
+
),
|
|
986
|
+
declContains: $ => seq(
|
|
987
|
+
$.kContains,
|
|
988
|
+
repeat1(choice($.declUsesUnit, ',', $.pp, ';'))
|
|
989
|
+
),
|
|
990
|
+
declExports: $ => seq($.kExports, delimited($.declExport), ';'),
|
|
991
|
+
|
|
992
|
+
declTypes: $ => seq(
|
|
993
|
+
$.kType,
|
|
994
|
+
repeat($.declType)
|
|
995
|
+
),
|
|
996
|
+
|
|
997
|
+
declVars: $ => seq(
|
|
998
|
+
optional($.kClass),
|
|
999
|
+
choice($.kVar, $.kThreadvar),
|
|
1000
|
+
repeat($.declVar)
|
|
1001
|
+
),
|
|
1002
|
+
|
|
1003
|
+
declConsts: $ => seq(
|
|
1004
|
+
optional($.kClass),
|
|
1005
|
+
choice($.kConst, $.kResourcestring),
|
|
1006
|
+
repeat($.declConst),
|
|
1007
|
+
),
|
|
1008
|
+
|
|
1009
|
+
// Declarations
|
|
1010
|
+
|
|
1011
|
+
declType: $ => seq(
|
|
1012
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1013
|
+
// Phase 3b iter 30: `generic TFoo<T> = class` keyword form — was
|
|
1014
|
+
// FPC-only; lazutils uses it heavily. Accepting in Delphi mode is
|
|
1015
|
+
// harmless because real Delphi code never uses `generic` here.
|
|
1016
|
+
optional($.kGeneric),
|
|
1017
|
+
field('name', $._genericName), $.kEq,
|
|
1018
|
+
field('type',
|
|
1019
|
+
choice(
|
|
1020
|
+
seq(optional($.kType), $.type),
|
|
1021
|
+
choice($.type),
|
|
1022
|
+
$.declClass,
|
|
1023
|
+
$.declIntf,
|
|
1024
|
+
$.declHelper,
|
|
1025
|
+
// Subrange types: `type TJvMargin = 2..24;`, also
|
|
1026
|
+
// `type T = Low(Integer)..High(Integer);`
|
|
1027
|
+
$.subrangeType,
|
|
1028
|
+
// Phase 3b iter 8: strong-typed string with code-page
|
|
1029
|
+
// argument — `UTF8String = type _AnsiString(65001);`
|
|
1030
|
+
// and `RawByteString = type _AnsiString($ffff);`
|
|
1031
|
+
// (System.pas RTL pattern).
|
|
1032
|
+
seq($.kType, $.identifier, '(', $._literalInt, ')'),
|
|
1033
|
+
)
|
|
1034
|
+
),
|
|
1035
|
+
';',
|
|
1036
|
+
repeat($._procAttribute)
|
|
1037
|
+
),
|
|
1038
|
+
|
|
1039
|
+
// Narrow subrange specialization used only as a type-declaration RHS,
|
|
1040
|
+
// to avoid the precedence/conflict cascade that `range` (uses _expr)
|
|
1041
|
+
// triggers in this position. Accepts numeric literals, negative
|
|
1042
|
+
// numbers, simple identifiers, and call-like `Func(arg)` forms which
|
|
1043
|
+
// covers Low/High/Ord patterns.
|
|
1044
|
+
subrangeType: $ => seq(
|
|
1045
|
+
$._subrangeBound, '..', $._subrangeBound
|
|
1046
|
+
),
|
|
1047
|
+
_subrangeBound: $ => choice(
|
|
1048
|
+
$._literalInt,
|
|
1049
|
+
$._literalFloat,
|
|
1050
|
+
seq('-', $._literalInt),
|
|
1051
|
+
seq('+', $._literalInt),
|
|
1052
|
+
// Phase 3b iter 25: `-$7FFFFFFF-1..$7FFFFFFF` (System.UITypes TColor)
|
|
1053
|
+
// signed-hex with explicit binary correction: `-$N-K` / `+$N+K`.
|
|
1054
|
+
seq(choice('-','+'), $._literalInt, choice('-','+'), $._literalInt),
|
|
1055
|
+
// Phase 3b iter 5: `-Identifier` / `+Identifier` form for
|
|
1056
|
+
// `THelpContext = -MaxInt..MaxInt` (System.Classes pattern).
|
|
1057
|
+
seq('-', $.identifier),
|
|
1058
|
+
seq('+', $.identifier),
|
|
1059
|
+
$.identifier,
|
|
1060
|
+
// Qualified identifier `TFoo.Bar` (DevExpress enum-class subrange:
|
|
1061
|
+
// TdxChartActualToolTipMode = TdxChartToolTipMode.None .. TdxChartToolTipMode.Crosshair;
|
|
1062
|
+
// Narrow form: two-level only; full qualified-name uses _ref which
|
|
1063
|
+
// conflicts with typerefDot in this position.)
|
|
1064
|
+
seq($.identifier, '.', $.identifier),
|
|
1065
|
+
seq($.identifier, '(', $.identifier, ')'),
|
|
1066
|
+
// `hid - 1`, `MaxN + 2` — narrow `identifier OP integer` form for
|
|
1067
|
+
// real-corpus patterns like `TNmbrRange = 0 .. hid - 1;`.
|
|
1068
|
+
// Kept narrow (no full _expr) to avoid the conflict cascade hit
|
|
1069
|
+
// when wider _expr was tried earlier.
|
|
1070
|
+
seq($.identifier, choice('-', '+'), $._literalInt),
|
|
1071
|
+
// `SizeOf(Integer) * 8 - 1`-style binary chain — narrow
|
|
1072
|
+
// `ident(ident) OP int OP int` form for the DevExpress pattern
|
|
1073
|
+
// `TcxContainerStyleValue = 0..SizeOf(Integer) * 8 - 1;`.
|
|
1074
|
+
seq($.identifier, '(', $.identifier, ')',
|
|
1075
|
+
choice('*', '/'), $._literalInt,
|
|
1076
|
+
choice('-', '+'), $._literalInt),
|
|
1077
|
+
// `#NN` numeric char-literal bound: `TCodeLetter = #64..#82;`.
|
|
1078
|
+
// (Excluding `^X` external char_literal — narrower keeps Spring4D
|
|
1079
|
+
// safe from the earlier regression.)
|
|
1080
|
+
seq('#', $._literalInt),
|
|
1081
|
+
// Phase 3b iter 41: parenthesized binary then trailing op + arg
|
|
1082
|
+
// for `(MaxInt div SizeOf(JOCTET))-1` (Vcl.Imaging.jpeg) and
|
|
1083
|
+
// `(High(Integer) - $F) div SizeOf(DWORD)` (EBorDebug). Narrow
|
|
1084
|
+
// shape: paren-wrap a 3-term binary then one more op + literal-or-call.
|
|
1085
|
+
seq(
|
|
1086
|
+
'(',
|
|
1087
|
+
choice($.identifier, seq($.identifier, '(', $.identifier, ')')),
|
|
1088
|
+
choice('-', '+', '*', '/', $.kDiv, $.kMod),
|
|
1089
|
+
choice($.identifier, $._literalInt, seq($.identifier, '(', $.identifier, ')')),
|
|
1090
|
+
')',
|
|
1091
|
+
choice('-', '+', '*', '/', $.kDiv, $.kMod),
|
|
1092
|
+
choice($._literalInt, seq($.identifier, '(', $.identifier, ')')),
|
|
1093
|
+
),
|
|
1094
|
+
),
|
|
1095
|
+
|
|
1096
|
+
declProc: $ => seq(
|
|
1097
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1098
|
+
choice($._declProc, $._declOperator),
|
|
1099
|
+
),
|
|
1100
|
+
|
|
1101
|
+
declVar: $ => seq(
|
|
1102
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1103
|
+
// Allow soft-keyword names as variable identifiers. Same set as
|
|
1104
|
+
// `_typeref`'s alias list — `Message: TFoo;`, `Name: string;`,
|
|
1105
|
+
// `Index: integer;` etc. show up frequently in legacy code.
|
|
1106
|
+
field('name', delimited1(choice(
|
|
1107
|
+
$.identifier,
|
|
1108
|
+
alias($.kMessage, $.identifier),
|
|
1109
|
+
alias($.kName, $.identifier),
|
|
1110
|
+
alias($.kIndex, $.identifier),
|
|
1111
|
+
alias($.kRead, $.identifier),
|
|
1112
|
+
alias($.kWrite, $.identifier),
|
|
1113
|
+
alias($.kReference, $.identifier),
|
|
1114
|
+
// Phase 3b iter 14: kDefault as var name (FMX.Media.Win,
|
|
1115
|
+
// WBComp, System.Classes use `Default: Boolean;` etc.)
|
|
1116
|
+
alias($.kDefault, $.identifier),
|
|
1117
|
+
// Phase 3b iter 16: kOperator as var name (Data.Win.ADODB,
|
|
1118
|
+
// Data.DBCommon use `Operator: TCANOperator;` etc.)
|
|
1119
|
+
alias($.kOperator, $.identifier),
|
|
1120
|
+
// Phase 3b iter 36: kFinal as var name (Rave RvCsRpt
|
|
1121
|
+
// `Final: boolean;` — local var named Final).
|
|
1122
|
+
alias($.kFinal, $.identifier),
|
|
1123
|
+
))),
|
|
1124
|
+
':',
|
|
1125
|
+
// Phase 3b iter 36: declVar type may be a subrange:
|
|
1126
|
+
// Element: 0..MaxSet absolute Value;
|
|
1127
|
+
// (Raize CodeSiteLogging.)
|
|
1128
|
+
field('type', choice($.type, $.subrangeType)),
|
|
1129
|
+
optional(choice(
|
|
1130
|
+
seq($.kAbsolute, $._ref),
|
|
1131
|
+
field('defaultValue', $.defaultValue)
|
|
1132
|
+
)),
|
|
1133
|
+
// Phase 3b iter 14: declaration hints on module-level vars:
|
|
1134
|
+
// CmdShow: Integer platform;
|
|
1135
|
+
// SupportsAnimateWindow: Boolean = False deprecated 'msg';
|
|
1136
|
+
optional(choice(
|
|
1137
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1138
|
+
$.kPlatform,
|
|
1139
|
+
$.kExperimental
|
|
1140
|
+
)),
|
|
1141
|
+
';',
|
|
1142
|
+
// Phase 3b iter 13: function-typed module-level vars with
|
|
1143
|
+
// callconv + default value:
|
|
1144
|
+
// var Name: function(...): T; stdcall = nil;
|
|
1145
|
+
// Without this, the `;` before stdcall closes declVar and
|
|
1146
|
+
// `stdcall = nil ;` is orphaned. Match at declVar scope only
|
|
1147
|
+
// (not declProcRef) to avoid the iter-12 GLR cascade.
|
|
1148
|
+
optional(seq(
|
|
1149
|
+
choice(
|
|
1150
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
1151
|
+
$.kRegister, $.kWinapi, $.kInline
|
|
1152
|
+
),
|
|
1153
|
+
'=',
|
|
1154
|
+
field('defaultValueAfterCC', $._expr),
|
|
1155
|
+
';',
|
|
1156
|
+
)),
|
|
1157
|
+
repeat(choice($._procAttribute, $.procExternal))
|
|
1158
|
+
),
|
|
1159
|
+
|
|
1160
|
+
declConst: $ => seq(
|
|
1161
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1162
|
+
// Allow soft-keyword names as const identifiers (DevExpress
|
|
1163
|
+
// dxCoreGraphics uses `Default` as a color-name constant; other
|
|
1164
|
+
// libraries use Message/Name/Index/etc.)
|
|
1165
|
+
field('name', choice(
|
|
1166
|
+
$.identifier,
|
|
1167
|
+
alias($.kDefault, $.identifier),
|
|
1168
|
+
alias($.kMessage, $.identifier),
|
|
1169
|
+
alias($.kName, $.identifier),
|
|
1170
|
+
alias($.kIndex, $.identifier),
|
|
1171
|
+
alias($.kRead, $.identifier),
|
|
1172
|
+
alias($.kWrite, $.identifier),
|
|
1173
|
+
alias($.kReference, $.identifier),
|
|
1174
|
+
)),
|
|
1175
|
+
// Phase 3b iter 31: declConst type may be a subrange:
|
|
1176
|
+
// const allocatedCount : 0..MaxAllocEntries = 0;
|
|
1177
|
+
// const Element : 0..MaxSet absolute Value;
|
|
1178
|
+
// (fibplus Zutil, Raize CodeSiteLogging patterns.)
|
|
1179
|
+
optional(seq(':', field('type', choice($.type, $.subrangeType)))),
|
|
1180
|
+
field('defaultValue', $.defaultValue),
|
|
1181
|
+
// Declaration hints before the terminating ';':
|
|
1182
|
+
// const X = 'foo' deprecated 'use Y instead';
|
|
1183
|
+
// const Y = 1 platform;
|
|
1184
|
+
// const Z = 'bar' experimental;
|
|
1185
|
+
// Phase 3b iter 27: chained hints `platform deprecated;`
|
|
1186
|
+
// (System.SysUtils faVolumeID = $00000008 platform deprecated).
|
|
1187
|
+
// Phase 3b iter 28: `library` as legacy declaration hint
|
|
1188
|
+
// (YADF DeprecatedOnConst MyConst3 = 'test4' library).
|
|
1189
|
+
repeat(choice(
|
|
1190
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1191
|
+
$.kPlatform,
|
|
1192
|
+
$.kExperimental,
|
|
1193
|
+
$.kLibrary,
|
|
1194
|
+
)),
|
|
1195
|
+
';',
|
|
1196
|
+
repeat($._procAttribute)
|
|
1197
|
+
),
|
|
1198
|
+
|
|
1199
|
+
declLabels: $ => seq($.kLabel, delimited1($.declLabel), ';'),
|
|
1200
|
+
declLabel: $ => field('name', $.identifier),
|
|
1201
|
+
|
|
1202
|
+
declExport: $ => seq($._genericName, repeat(seq(choice($.kName, $.kIndex), $._expr))),
|
|
1203
|
+
|
|
1204
|
+
// Type declarations
|
|
1205
|
+
|
|
1206
|
+
declEnum: $ => seq(
|
|
1207
|
+
'(', delimited1($.declEnumValue), ')',
|
|
1208
|
+
// Trailing platform/deprecated hint on the enum body itself:
|
|
1209
|
+
// TFPUPrecisionMode = (pmSingle, pmReserved, ...) platform;
|
|
1210
|
+
// (System.Math pattern.)
|
|
1211
|
+
optional(choice(
|
|
1212
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1213
|
+
$.kPlatform,
|
|
1214
|
+
$.kExperimental,
|
|
1215
|
+
)),
|
|
1216
|
+
),
|
|
1217
|
+
declEnumValue: $ => seq(field('name', $.identifier), field('value', optional($.defaultValue))),
|
|
1218
|
+
// `set of T` where T is a type OR a subrange (`set of 1..100;`).
|
|
1219
|
+
declSet: $ => seq(
|
|
1220
|
+
$.kSet, $.kOf, choice($.type, $.subrangeType),
|
|
1221
|
+
// Phase 3b iter 21: trailing platform/deprecated hint on set-type
|
|
1222
|
+
// declaration. `TFileAttributes = set of TFileAttribute platform;`
|
|
1223
|
+
// (System.IOUtils pattern.)
|
|
1224
|
+
optional(choice(
|
|
1225
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1226
|
+
$.kPlatform,
|
|
1227
|
+
$.kExperimental,
|
|
1228
|
+
)),
|
|
1229
|
+
),
|
|
1230
|
+
declArray: $ => seq(
|
|
1231
|
+
optional($.kPacked),
|
|
1232
|
+
$.kArray,
|
|
1233
|
+
optional(seq('[', delimited(choice($.range, $._expr)), ']')),
|
|
1234
|
+
// Element type can be a regular type OR an inline subrange:
|
|
1235
|
+
// array [0..7] of 0..9 = (0, 1, 2, ...);
|
|
1236
|
+
$.kOf, choice($.type, $.subrangeType)
|
|
1237
|
+
),
|
|
1238
|
+
declFile: $ => seq($.kFile, optional(seq($.kOf, $.type))),
|
|
1239
|
+
declString: $ => prec.left(seq(
|
|
1240
|
+
$.kString,
|
|
1241
|
+
optional(seq('[', choice($._expr), ']'))
|
|
1242
|
+
)),
|
|
1243
|
+
|
|
1244
|
+
declProcRef: $ => prec.right(1,seq(
|
|
1245
|
+
optional(seq($.kReference, $.kTo)),
|
|
1246
|
+
choice($.kProcedure, $.kFunction),
|
|
1247
|
+
field('args', optional($.declArgs)),
|
|
1248
|
+
optional(seq(
|
|
1249
|
+
':',
|
|
1250
|
+
field('type', $.typeref),
|
|
1251
|
+
)),
|
|
1252
|
+
optional(seq($.kOf, $.kObject)),
|
|
1253
|
+
// Trailing calling convention. Restricted to a narrow keyword list
|
|
1254
|
+
// (NOT the full procAttribute, which causes typeref ambiguity)
|
|
1255
|
+
// to keep the grammar deterministic.
|
|
1256
|
+
// Phase 3b iter 25: `repeat` allows chained callconvs like
|
|
1257
|
+
// `cdecl varargs` (FireDAC.Phys.SQLiteCli Tsqlite3_config pattern).
|
|
1258
|
+
repeat(choice(
|
|
1259
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
1260
|
+
$.kRegister, $.kWinapi, $.kInline, $.kVarargs
|
|
1261
|
+
))
|
|
1262
|
+
)),
|
|
1263
|
+
|
|
1264
|
+
declMetaClass: $ => seq($.kClass, $.kOf, $.typeref),
|
|
1265
|
+
|
|
1266
|
+
declClass: $ => seq(
|
|
1267
|
+
optional($.kPacked),
|
|
1268
|
+
choice(
|
|
1269
|
+
$.kClass, $.kRecord, $.kObject,
|
|
1270
|
+
...enable_if(objc,
|
|
1271
|
+
$.kObjcclass, $.kObjccategory, $.kObjcprotocol
|
|
1272
|
+
)
|
|
1273
|
+
),
|
|
1274
|
+
optional(choice(
|
|
1275
|
+
$.kAbstract, $.kSealed,
|
|
1276
|
+
...enable_if(objc,
|
|
1277
|
+
seq($.kExternal, optional(seq($.kName, $._expr)))
|
|
1278
|
+
)
|
|
1279
|
+
)),
|
|
1280
|
+
field('parent', optional(seq('(',delimited($.typeref),')'))),
|
|
1281
|
+
optional($._declClass)
|
|
1282
|
+
),
|
|
1283
|
+
|
|
1284
|
+
declIntf: $ => seq(
|
|
1285
|
+
optional($.kPacked),
|
|
1286
|
+
choice(
|
|
1287
|
+
$.kInterface,
|
|
1288
|
+
...enable_if(delphi, $.kDispInterface)
|
|
1289
|
+
),
|
|
1290
|
+
field('parent', optional(seq('(',delimited($.typeref),')'))),
|
|
1291
|
+
field('guid', optional($.guid)),
|
|
1292
|
+
optional($._declClass)
|
|
1293
|
+
),
|
|
1294
|
+
|
|
1295
|
+
declHelper: $ => seq(
|
|
1296
|
+
choice($.kClass, $.kRecord, $.kType), $.kHelper,
|
|
1297
|
+
field('parent', optional(seq('(',delimited($.typeref),')'))),
|
|
1298
|
+
$.kFor, $.typeref,
|
|
1299
|
+
$._declClass
|
|
1300
|
+
),
|
|
1301
|
+
|
|
1302
|
+
// Stuff for class/record/interface declarations
|
|
1303
|
+
|
|
1304
|
+
guid: $ => prec(1,seq('[', $._ref, ']')),
|
|
1305
|
+
|
|
1306
|
+
_declClass: $ => seq(
|
|
1307
|
+
optional($._declFields),
|
|
1308
|
+
optional($._classDeclarations),
|
|
1309
|
+
repeat($.declSection),
|
|
1310
|
+
optional($.declVariant),
|
|
1311
|
+
$.kEnd,
|
|
1312
|
+
// Record alignment hint (Win API patterns):
|
|
1313
|
+
// _SLIST_ENTRY = record ... end align 16;
|
|
1314
|
+
// _SLIST_ENTRY = record ... end align(16);
|
|
1315
|
+
// sockaddr_storage = record ... end align (_SS_ALIGNSIZE);
|
|
1316
|
+
optional(seq(
|
|
1317
|
+
$.kAlign,
|
|
1318
|
+
choice(
|
|
1319
|
+
$._literalInt,
|
|
1320
|
+
seq('(', choice($._literalInt, $.identifier), ')'),
|
|
1321
|
+
),
|
|
1322
|
+
)),
|
|
1323
|
+
// Whole-type declaration hints AFTER `end`:
|
|
1324
|
+
// type IFoo = interface ... end deprecated;
|
|
1325
|
+
// type IBar = interface ... end deprecated 'use IBaz';
|
|
1326
|
+
optional(choice(
|
|
1327
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1328
|
+
$.kPlatform,
|
|
1329
|
+
$.kExperimental,
|
|
1330
|
+
))
|
|
1331
|
+
),
|
|
1332
|
+
|
|
1333
|
+
declSection: $ => seq(
|
|
1334
|
+
optional($.kStrict),
|
|
1335
|
+
choice($._visibility, ...enable_if(objc, $.kRequired, $.kOptional)),
|
|
1336
|
+
optional($._declFields),
|
|
1337
|
+
optional($._classDeclarations)
|
|
1338
|
+
),
|
|
1339
|
+
|
|
1340
|
+
// Phase 3b iter 10: allow LAST field to omit trailing `;` before `end`.
|
|
1341
|
+
// Pattern (extremely common in DevExpress and RTL records):
|
|
1342
|
+
// TFoo = record
|
|
1343
|
+
// A: TA;
|
|
1344
|
+
// B: TB // no `;` here
|
|
1345
|
+
// end;
|
|
1346
|
+
// Tried in iter 62 with broader scope and reverted. Narrow form here:
|
|
1347
|
+
// repeat of normal declField (each with `;`) optionally followed by ONE
|
|
1348
|
+
// declFieldNoSemi at end. Anchor: declFieldNoSemi only fires before
|
|
1349
|
+
// `end` or kEnd-equivalent, since the parent rule expects that.
|
|
1350
|
+
_declFields: $ => choice(
|
|
1351
|
+
repeat1($.declField),
|
|
1352
|
+
seq(repeat($.declField), $.declFieldNoSemi),
|
|
1353
|
+
),
|
|
1354
|
+
declFieldNoSemi: $ => seq(
|
|
1355
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1356
|
+
field('name', delimited1($.identifier)),
|
|
1357
|
+
':',
|
|
1358
|
+
// Narrower type set than declField. Excludes declString with the
|
|
1359
|
+
// `[N]` short-string size spec — that conflicts with declFieldNoSemi
|
|
1360
|
+
// because the `[N]` looks like declFieldNoSemi's continuation when
|
|
1361
|
+
// in GLR fork. Excludes declArray for the same reason
|
|
1362
|
+
// (`Array[0..4] of String[1]` in ORM3 Z19b5.pas).
|
|
1363
|
+
// declString and declArray almost never appear as last-field with
|
|
1364
|
+
// no `;` in the corpus (those use the trailing `;` form already).
|
|
1365
|
+
field('type', choice(
|
|
1366
|
+
$.typeref,
|
|
1367
|
+
$.declMetaClass,
|
|
1368
|
+
$.declEnum,
|
|
1369
|
+
$.declSet,
|
|
1370
|
+
$.declFile,
|
|
1371
|
+
$.declProcRef,
|
|
1372
|
+
$.declClass,
|
|
1373
|
+
prec(-1, $.pp_block),
|
|
1374
|
+
)),
|
|
1375
|
+
field('defaultValue', optional($.defaultValue)),
|
|
1376
|
+
),
|
|
1377
|
+
|
|
1378
|
+
declField: $ => seq(
|
|
1379
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1380
|
+
field('name', delimited1($.identifier)),
|
|
1381
|
+
':',
|
|
1382
|
+
field('type', $.type),
|
|
1383
|
+
field('defaultValue', optional($.defaultValue)),
|
|
1384
|
+
// Declaration hints on record/class fields (Embarcadero RTL):
|
|
1385
|
+
// FMin: Integer deprecated;
|
|
1386
|
+
// FOldField: T platform;
|
|
1387
|
+
optional(choice(
|
|
1388
|
+
seq($.kDeprecated, optional($._expr)),
|
|
1389
|
+
$.kPlatform,
|
|
1390
|
+
$.kExperimental
|
|
1391
|
+
)),
|
|
1392
|
+
';',
|
|
1393
|
+
// Phase 3b iter 9: trailing `<callconv>;` clause for procedural-type
|
|
1394
|
+
// fields that import procDecl's `; cdecl;` convention. Pattern:
|
|
1395
|
+
// DispInvoke: procedure(Dest: PVarData; ...); cdecl;
|
|
1396
|
+
// VarArrayGet: function(...): Variant; cdecl;
|
|
1397
|
+
// (System.pas TVarManager + similar Win API records.)
|
|
1398
|
+
optional(seq(
|
|
1399
|
+
choice(
|
|
1400
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
1401
|
+
$.kRegister, $.kWinapi, $.kInline,
|
|
1402
|
+
),
|
|
1403
|
+
';',
|
|
1404
|
+
)),
|
|
1405
|
+
),
|
|
1406
|
+
|
|
1407
|
+
declProp: $ => seq(
|
|
1408
|
+
...enable_if(rtti, optional($.rttiAttributes)),
|
|
1409
|
+
optional($.kClass),
|
|
1410
|
+
$.kProperty,
|
|
1411
|
+
field('name', $.identifier),
|
|
1412
|
+
field('args', optional($.declPropArgs)),
|
|
1413
|
+
// Type clause is omitted in the "redirection" form: `property Name;`
|
|
1414
|
+
// or `property Name; default;` — used to expose an inherited property
|
|
1415
|
+
// at a different visibility without changing accessors.
|
|
1416
|
+
optional(seq(':', field('type', $.type))),
|
|
1417
|
+
repeat(choice(
|
|
1418
|
+
seq($.kIndex, field('index', $._expr)),
|
|
1419
|
+
...enable_if(delphi, seq($.kDispId, field('dispid', $._expr))),
|
|
1420
|
+
// read/write may reference: identifier (plain), qualified
|
|
1421
|
+
// identifier (Foo.Bar), or array-element access (fChilds[0])
|
|
1422
|
+
// — full _ref covers all of these.
|
|
1423
|
+
seq($.kRead, field('getter', $._ref)),
|
|
1424
|
+
seq($.kWrite, field('setter', $._ref)),
|
|
1425
|
+
seq($.kImplements, field('implements', delimited($._expr))),
|
|
1426
|
+
seq($.kDefault, field('defaultValue', $._expr)),
|
|
1427
|
+
seq($.kStored, field('stored', $._expr)),
|
|
1428
|
+
$.kNodefault,
|
|
1429
|
+
$.kReadonly, // OLE dispinterface property: readonly dispid 0
|
|
1430
|
+
$.kWriteonly, // OLE dispinterface property: writeonly dispid N
|
|
1431
|
+
)),
|
|
1432
|
+
';',
|
|
1433
|
+
repeat($._procAttribute)
|
|
1434
|
+
),
|
|
1435
|
+
|
|
1436
|
+
declPropArgs: $ => seq('[', delimited($.declArg, ';'), ']'),
|
|
1437
|
+
|
|
1438
|
+
// Variant records
|
|
1439
|
+
|
|
1440
|
+
declVariant: $ => prec.right(seq(
|
|
1441
|
+
$.kCase,
|
|
1442
|
+
field('name', optional(seq($.identifier, ':'))),
|
|
1443
|
+
field('type', $.typeref), $.kOf,
|
|
1444
|
+
delimited1($.declVariantClause, ';'),
|
|
1445
|
+
optional(';'),
|
|
1446
|
+
)),
|
|
1447
|
+
|
|
1448
|
+
declVariantClause: $ => seq(
|
|
1449
|
+
$.caseLabel,
|
|
1450
|
+
'(',
|
|
1451
|
+
choice(
|
|
1452
|
+
seq(delimited(alias($.declVariantField, $.declField), ';'), optional(seq(';', $.declVariant))),
|
|
1453
|
+
seq($.declVariant),
|
|
1454
|
+
),
|
|
1455
|
+
optional(';'),
|
|
1456
|
+
')',
|
|
1457
|
+
),
|
|
1458
|
+
|
|
1459
|
+
declVariantField: $ => seq(
|
|
1460
|
+
// Phase 3b iter 32: allow RTTI attribute on variant-record field.
|
|
1461
|
+
// Pattern: `case byte of 1:( [Example] V: Integer; );` (YADF test).
|
|
1462
|
+
optional($.rttiAttributes),
|
|
1463
|
+
field('name', delimited1($.identifier)),
|
|
1464
|
+
':',
|
|
1465
|
+
field('type', $.type),
|
|
1466
|
+
field('defaultValue', optional($.defaultValue))
|
|
1467
|
+
),
|
|
1468
|
+
|
|
1469
|
+
// Stuff for procedure / function / operator declarations
|
|
1470
|
+
|
|
1471
|
+
_declProc: $ => seq(
|
|
1472
|
+
...enable_if(fpc, optional($.kGeneric)),
|
|
1473
|
+
optional($.kClass),
|
|
1474
|
+
choice($.kProcedure, $.kFunction, $.kConstructor, $.kDestructor),
|
|
1475
|
+
field('name', $._genericName),
|
|
1476
|
+
field('args', optional($.declArgs)),
|
|
1477
|
+
optional(seq(
|
|
1478
|
+
':',
|
|
1479
|
+
field('type', $.typeref),
|
|
1480
|
+
)),
|
|
1481
|
+
field('assign', optional($.defaultValue)),
|
|
1482
|
+
// Inline calling-convention or procAttribute BEFORE the terminating
|
|
1483
|
+
// ';' — Embarcadero RTL / AzureAPI use lenient forms:
|
|
1484
|
+
// function DbiInitFn(...): DBIResult stdcall; (iter 56)
|
|
1485
|
+
// procedure PreflightBlobRequest(...) overload; (iter 60)
|
|
1486
|
+
// `repeat` allows multiple chained attributes like `... cdecl overload`.
|
|
1487
|
+
repeat(choice(
|
|
1488
|
+
$.kStdcall, $.kCdecl, $.kSafecall, $.kPascal,
|
|
1489
|
+
$.kRegister, $.kWinapi, $.kInline,
|
|
1490
|
+
$.kOverload, $.kVirtual, $.kAbstract, $.kOverride,
|
|
1491
|
+
$.kReintroduce, $.kStatic, $.kDynamic, $.kFinal
|
|
1492
|
+
)),
|
|
1493
|
+
';',
|
|
1494
|
+
repeat($._procAttributeNoExt)
|
|
1495
|
+
),
|
|
1496
|
+
|
|
1497
|
+
_declOperator: $ => seq(
|
|
1498
|
+
optional($.kClass),
|
|
1499
|
+
$.kOperator,
|
|
1500
|
+
field('name', $._operatorName),
|
|
1501
|
+
field('args', optional($.declArgs)),
|
|
1502
|
+
...enable_if(fpc, field('resultName', optional($.identifier))),
|
|
1503
|
+
// Return type is omitted for procedure-style class operators on
|
|
1504
|
+
// managed records: Initialize, Finalize, Assign.
|
|
1505
|
+
optional(seq(
|
|
1506
|
+
':',
|
|
1507
|
+
field('type', $.type),
|
|
1508
|
+
field('assign', optional($.defaultValue))
|
|
1509
|
+
)),
|
|
1510
|
+
';',
|
|
1511
|
+
repeat($._procAttributeNoExt)
|
|
1512
|
+
),
|
|
1513
|
+
|
|
1514
|
+
operatorDot: $ => op.infix(0, $._genericName, $.kDot, $.operatorName),
|
|
1515
|
+
_operatorName: $ => seq(
|
|
1516
|
+
choice(
|
|
1517
|
+
$._genericName,
|
|
1518
|
+
...enable_if(fpc,
|
|
1519
|
+
$.operatorName,
|
|
1520
|
+
alias($.operatorDot, $.genericDot)
|
|
1521
|
+
)
|
|
1522
|
+
)
|
|
1523
|
+
),
|
|
1524
|
+
operatorName: $ => choice(
|
|
1525
|
+
$.kDot, $.kLt, $.kEq, $.kNeq, $.kGt, $.kLte, $.kGte,
|
|
1526
|
+
$.kAdd, $.kSub, $.kMul, $.kFdiv, $.kDiv, $.kMod,
|
|
1527
|
+
$.kAssign,
|
|
1528
|
+
$.kOr, $.kXor, $.kAnd, $.kShl, $.kShr, $.kNot,
|
|
1529
|
+
$.kIn,
|
|
1530
|
+
),
|
|
1531
|
+
|
|
1532
|
+
declArgs: $ => seq('(', delimited($.declArg, ';'), ')'),
|
|
1533
|
+
|
|
1534
|
+
declArg: $ => choice(
|
|
1535
|
+
seq(
|
|
1536
|
+
// Phase 3b iter 20: allow rttiAttributes BEFORE the modifier
|
|
1537
|
+
// keyword, for IFDEF-wrapped attribute patterns:
|
|
1538
|
+
// class operator Equal({$IFDEF X}[ref]{$ENDIF}const left: T)
|
|
1539
|
+
// THEN-wins exposes `[ref] const left: T`, so the bracketed
|
|
1540
|
+
// attribute precedes `const`. Original between-modifier-and-name
|
|
1541
|
+
// placement is still supported via the second optional below.
|
|
1542
|
+
optional($.rttiAttributes),
|
|
1543
|
+
choice($.kVar, $.kConst, $.kOut, $.kConstref),
|
|
1544
|
+
// Inline attribute between modifier and name:
|
|
1545
|
+
// `const [ref] X: T` — pass-by-reference const (Delphi 10+).
|
|
1546
|
+
// Generally any bracketed attribute is allowed here.
|
|
1547
|
+
optional($.rttiAttributes),
|
|
1548
|
+
field('name', delimited1($.identifier)),
|
|
1549
|
+
optional(seq(
|
|
1550
|
+
':', field('type', $.type),
|
|
1551
|
+
field('defaultValue', optional($.defaultValue))
|
|
1552
|
+
))
|
|
1553
|
+
),
|
|
1554
|
+
seq(
|
|
1555
|
+
// Phase 3b iter 15: allow bare `[Ref]` / `[in, ...]` attribute
|
|
1556
|
+
// before arg name without a leading modifier keyword. Win API
|
|
1557
|
+
// D3D11 wrappers use `[Ref] ppConstantBuffers: ID3D11Buffer`.
|
|
1558
|
+
optional($.rttiAttributes),
|
|
1559
|
+
field('name', delimited1($.identifier)), ':',
|
|
1560
|
+
field('type', $.type),
|
|
1561
|
+
field('defaultValue', optional($.defaultValue))
|
|
1562
|
+
)
|
|
1563
|
+
),
|
|
1564
|
+
|
|
1565
|
+
// Attributes & declaration hints
|
|
1566
|
+
|
|
1567
|
+
_procAttribute: $ => /*pp($,*/choice(
|
|
1568
|
+
// Phase 3b iter 26: allow CHAINED proc attributes sharing one `;`,
|
|
1569
|
+
// for FireDAC.Phys.SQLiteCli pattern `; cdecl varargs;` on
|
|
1570
|
+
// procedural-type declarations. Single-attr form still matches.
|
|
1571
|
+
seq(repeat1(field('attribute', $.procAttribute)), ';'),
|
|
1572
|
+
// FPC-specific syntax, e.g. procedure myproc; [public; alias:'bla'; cdecl];
|
|
1573
|
+
...enable_if(fpc, seq(
|
|
1574
|
+
'[',
|
|
1575
|
+
delimited(field('attribute', choice($.procAttribute, $.procExternal))),
|
|
1576
|
+
']', ';'
|
|
1577
|
+
))
|
|
1578
|
+
)/*)*/,
|
|
1579
|
+
_procAttributeNoExt: $ => /*pp($,*/ choice(
|
|
1580
|
+
seq(repeat1(field('attribute', $.procAttribute)), ';'),
|
|
1581
|
+
// FPC-specific syntax, e.g. procedure myproc; [public; alias:'bla'; cdecl];
|
|
1582
|
+
...enable_if(fpc, seq('[', delimited(field('attribute', choice($.procAttribute)), ';'), ']', ';'))
|
|
1583
|
+
)/*)*/,
|
|
1584
|
+
|
|
1585
|
+
procAttribute: $ => choice(
|
|
1586
|
+
$.kStatic, $.kVirtual, $.kDynamic, $.kAbstract, $.kOverride,
|
|
1587
|
+
$.kOverload, $.kReintroduce, $.kInline, $.kStdcall,
|
|
1588
|
+
$.kCdecl, $.kPascal, $.kRegister, $.kSafecall, $.kAssembler,
|
|
1589
|
+
$.kNoreturn, $.kLocal, $.kFar, $.kNear,
|
|
1590
|
+
$.kDefault, $.kNodefault, $.kDeprecated, $.kExperimental,
|
|
1591
|
+
$.kFinal, // sealed-method modifier: 'function X; override; final;'
|
|
1592
|
+
$.kPlatform, // valid in Delphi too (was fpc-only)
|
|
1593
|
+
$.kExport, // legacy DLL-export modifier (valid in Delphi too)
|
|
1594
|
+
$.kVarargs, // variadic-arg modifier on cdecl functions
|
|
1595
|
+
$.kWinapi, // calling convention (alias for stdcall on Win32)
|
|
1596
|
+
$.kInterrupt,
|
|
1597
|
+
// Phase 3b iter 39: legacy `library` hint on proc decls
|
|
1598
|
+
// (Rave11 RpCanvas/RpFiler etc. `override; deprecated; library;`).
|
|
1599
|
+
$.kLibrary,
|
|
1600
|
+
// NOTE: kForward intentionally NOT here — it conflicts with the
|
|
1601
|
+
// declProcFwd rule which has its own `; forward;` handling.
|
|
1602
|
+
|
|
1603
|
+
seq(
|
|
1604
|
+
choice(
|
|
1605
|
+
seq($.kMessage, optional($.kName)),
|
|
1606
|
+
$.kDeprecated
|
|
1607
|
+
),
|
|
1608
|
+
$._expr
|
|
1609
|
+
),
|
|
1610
|
+
|
|
1611
|
+
...enable_if(fpc,
|
|
1612
|
+
$.kPlatform, $.kUnimplemented,
|
|
1613
|
+
$.kCppdecl, $.kCvar, $.kMwpascal, $.kNostackframe,
|
|
1614
|
+
$.kInterrupt, $.kIocheck, $.kHardfloat,
|
|
1615
|
+
$.kSoftfloat, $.kMs_abi_default, $.kMs_abi_cdecl,
|
|
1616
|
+
$.kSaveregisters, $.kSysv_abi_default, $.kSysv_abi_cdecl,
|
|
1617
|
+
$.kVectorcall, $.kVarargs, $.kWinapi,
|
|
1618
|
+
...enable_if(public_name, $.kPublic),
|
|
1619
|
+
seq(
|
|
1620
|
+
choice(
|
|
1621
|
+
$.kExport,
|
|
1622
|
+
seq($.kAlias, ':'),
|
|
1623
|
+
...enable_if(public_name, seq($.kPublic, $.kName)),
|
|
1624
|
+
),
|
|
1625
|
+
$._expr
|
|
1626
|
+
)
|
|
1627
|
+
),
|
|
1628
|
+
|
|
1629
|
+
...enable_if(delphi, field('dispid', seq($.kDispId, $._expr))),
|
|
1630
|
+
),
|
|
1631
|
+
|
|
1632
|
+
rttiAttributes: $ => repeat1(seq(
|
|
1633
|
+
// Note: "Identifier:" is for tagging parameters of procedures (Delphi)
|
|
1634
|
+
'[', optional(seq($.identifier, ':')), delimited($._ref), ']'
|
|
1635
|
+
)),
|
|
1636
|
+
|
|
1637
|
+
procExternal: $ => seq(
|
|
1638
|
+
$.kExternal,
|
|
1639
|
+
optional($._expr),
|
|
1640
|
+
optional(seq(choice($.kName, $.kIndex), $._expr)),
|
|
1641
|
+
...enable_if(delphi, optional($.kDelayed)),
|
|
1642
|
+
// Phase 3b iter 17: native library dependency hint
|
|
1643
|
+
// function f; cdecl; external LIB name 'foo' dependency 'c++_static','c++abi';
|
|
1644
|
+
// (Embarcadero FlatBox2D Android/iOS linker pattern.)
|
|
1645
|
+
// Phase 3b iter 35: `dependency` clause may use identifier-name
|
|
1646
|
+
// constants too: `dependency LibCPP, LibCPP_ABI` (FireDAC.Phys.IBCli).
|
|
1647
|
+
optional(seq(
|
|
1648
|
+
$.kDependency,
|
|
1649
|
+
delimited1(choice($._literalString, $.identifier)),
|
|
1650
|
+
)),
|
|
1651
|
+
';'
|
|
1652
|
+
),
|
|
1653
|
+
|
|
1654
|
+
// INITIALIZERS --------------------------------------------------------
|
|
1655
|
+
|
|
1656
|
+
_initializer: $ => prec(2,seq(
|
|
1657
|
+
choice($._expr, $.recInitializer, $.arrInitializer)
|
|
1658
|
+
)),
|
|
1659
|
+
|
|
1660
|
+
// record initializer
|
|
1661
|
+
recInitializer: $ => seq(
|
|
1662
|
+
'(',
|
|
1663
|
+
delimited1($.recInitializerField, ';'),
|
|
1664
|
+
// Optional trailing `;` before `)` — accepted by Delphi compiler,
|
|
1665
|
+
// used in DevExpress dxCore Unicode tables and similar generated
|
|
1666
|
+
// data files.
|
|
1667
|
+
optional(';'),
|
|
1668
|
+
')'
|
|
1669
|
+
),
|
|
1670
|
+
|
|
1671
|
+
recInitializerField: $ => choice(
|
|
1672
|
+
seq(field('name',$.identifier), ':', field('value', $._initializer)),
|
|
1673
|
+
field('value', $._initializer)
|
|
1674
|
+
),
|
|
1675
|
+
|
|
1676
|
+
// array initializer
|
|
1677
|
+
// Array / set initializer. Allows empty `()` (Spring4D const sentinels:
|
|
1678
|
+
// `Arg: TArg = ();`) as well as `(a, b, c)` populated form.
|
|
1679
|
+
arrInitializer: $ => prec(1,seq('(', delimited($._initializer), ')')),
|
|
1680
|
+
|
|
1681
|
+
// TERMINAL SYMBOLS ----------------------------------------------------
|
|
1682
|
+
|
|
1683
|
+
kProgram: $ => /program/i,
|
|
1684
|
+
kLibrary: $ => /library/i,
|
|
1685
|
+
kUnit: $ => /unit/i,
|
|
1686
|
+
kPackage: $ => /package/i,
|
|
1687
|
+
kRequires: $ => /requires/i,
|
|
1688
|
+
kContains: $ => /contains/i,
|
|
1689
|
+
kUses: $ => /uses/i,
|
|
1690
|
+
kInterface: $ => /interface/i,
|
|
1691
|
+
kDispInterface: $ => /dispinterface/i,
|
|
1692
|
+
kImplementation: $ => /implementation/i,
|
|
1693
|
+
kInitialization: $ => /initialization/i,
|
|
1694
|
+
kFinalization: $ => /finalization/i,
|
|
1695
|
+
kEndDot: $ => '.',
|
|
1696
|
+
|
|
1697
|
+
kBegin: $ => /begin/i,
|
|
1698
|
+
kEnd: $ => /end/i,
|
|
1699
|
+
kAsm: $ => /asm/i,
|
|
1700
|
+
|
|
1701
|
+
kVar: $ => /var/i,
|
|
1702
|
+
kThreadvar: $ => /threadvar/i,
|
|
1703
|
+
kConst: $ => /const/i,
|
|
1704
|
+
kConstref: $ => /constref/i,
|
|
1705
|
+
kResourcestring: $ => /resourcestring/i,
|
|
1706
|
+
kOut: $ => /out/i,
|
|
1707
|
+
kType: $ => /type/i,
|
|
1708
|
+
kLabel: $ => /label/i,
|
|
1709
|
+
kExports: $ => /exports/i,
|
|
1710
|
+
|
|
1711
|
+
kAbsolute: $ => /absolute/i,
|
|
1712
|
+
|
|
1713
|
+
kProperty: $ => /property/i,
|
|
1714
|
+
kRead: $ => /read/i,
|
|
1715
|
+
kWrite: $ => /write/i,
|
|
1716
|
+
kImplements: $ => /implements/i,
|
|
1717
|
+
kDefault: $ => /default/i,
|
|
1718
|
+
kNodefault: $ => /nodefault/i,
|
|
1719
|
+
kStored: $ => /stored/i,
|
|
1720
|
+
kIndex: $ => /index/i,
|
|
1721
|
+
kDispId: $ => /dispid/i,
|
|
1722
|
+
|
|
1723
|
+
kClass: $ => /class/i,
|
|
1724
|
+
kInterface: $ => /interface/i,
|
|
1725
|
+
kObject: $ => /object/i,
|
|
1726
|
+
kRecord: $ => /record/i,
|
|
1727
|
+
kObjcclass: $ => /objcclass/i,
|
|
1728
|
+
kObjccategory: $ => /objccategory/i,
|
|
1729
|
+
kObjcprotocol: $ => /objcprotocol/i,
|
|
1730
|
+
kArray: $ => /array/i,
|
|
1731
|
+
kFile: $ => /file/i,
|
|
1732
|
+
kString: $ => /string/i,
|
|
1733
|
+
kSet: $ => /set/i,
|
|
1734
|
+
kOf: $ => /of/i,
|
|
1735
|
+
kHelper: $ => /helper/i,
|
|
1736
|
+
kPacked: $ => /packed/i,
|
|
1737
|
+
|
|
1738
|
+
kGeneric: $ => /generic/i,
|
|
1739
|
+
kSpecialize: $ => /specialize/i,
|
|
1740
|
+
|
|
1741
|
+
kDot: $ => '.',
|
|
1742
|
+
kLt: $ => '<',
|
|
1743
|
+
kEq: $ => '=',
|
|
1744
|
+
kNeq: $ => '<>',
|
|
1745
|
+
kGt: $ => '>',
|
|
1746
|
+
kLte: $ => '<=',
|
|
1747
|
+
kGte: $ => '>=',
|
|
1748
|
+
kAdd: $ => '+',
|
|
1749
|
+
kSub: $ => '-',
|
|
1750
|
+
kMul: $ => '*',
|
|
1751
|
+
kFdiv: $ => '/',
|
|
1752
|
+
kAt: $ => '@',
|
|
1753
|
+
// `at` keyword (raise with address): `raise E at ReturnAddress`.
|
|
1754
|
+
// Distinct from `@` operator (kAt). Soft keyword — must not clash
|
|
1755
|
+
// with normal identifiers.
|
|
1756
|
+
kAtWord: $ => /at/i,
|
|
1757
|
+
kHat: $ => '^',
|
|
1758
|
+
kAssign: $ => ':=',
|
|
1759
|
+
kAssignAdd: $ => '+=', // Freepascal
|
|
1760
|
+
kAssignSub: $ => '-=', // Freepascal
|
|
1761
|
+
kAssignMul: $ => '*=', // Freepascal
|
|
1762
|
+
kAssignDiv: $ => '/=', // Freepascal
|
|
1763
|
+
kOr: $ => /or/i,
|
|
1764
|
+
kXor: $ => /xor/i,
|
|
1765
|
+
kDiv: $ => /div/i,
|
|
1766
|
+
kMod: $ => /mod/i,
|
|
1767
|
+
kAnd: $ => /and/i,
|
|
1768
|
+
kShl: $ => /shl/i,
|
|
1769
|
+
kShr: $ => /shr/i,
|
|
1770
|
+
kNot: $ => /not/i,
|
|
1771
|
+
kIs: $ => /is/i,
|
|
1772
|
+
kAs: $ => /as/i,
|
|
1773
|
+
kIn: $ => /in/i,
|
|
1774
|
+
|
|
1775
|
+
kFor: $ => /for/i,
|
|
1776
|
+
kTo: $ => /to/i,
|
|
1777
|
+
kDownto: $ => /downto/i,
|
|
1778
|
+
kIf: $ => /if/i,
|
|
1779
|
+
kThen: $ => /then/i,
|
|
1780
|
+
kElse: $ => /else/i,
|
|
1781
|
+
kDo: $ => /do/i,
|
|
1782
|
+
kWhile: $ => /while/i,
|
|
1783
|
+
kRepeat: $ => /repeat/i,
|
|
1784
|
+
kUntil: $ => /until/i,
|
|
1785
|
+
kTry: $ => /try/i,
|
|
1786
|
+
kExcept: $ => /except/i,
|
|
1787
|
+
kFinally: $ => /finally/i,
|
|
1788
|
+
kRaise: $ => /raise/i,
|
|
1789
|
+
kOn: $ => /on/i,
|
|
1790
|
+
kCase: $ => /case/i,
|
|
1791
|
+
kWith: $ => /with/i,
|
|
1792
|
+
kGoto: $ => /goto/i,
|
|
1793
|
+
|
|
1794
|
+
kFunction: $ => /function/i,
|
|
1795
|
+
kProcedure: $ => /procedure/i,
|
|
1796
|
+
kConstructor: $ => /constructor/i,
|
|
1797
|
+
kDestructor: $ => /destructor/i,
|
|
1798
|
+
kOperator: $ => /operator/i,
|
|
1799
|
+
kReference: $ => /reference/i,
|
|
1800
|
+
|
|
1801
|
+
kPublished: $ => /published/i,
|
|
1802
|
+
kPublic: $ => /public/i,
|
|
1803
|
+
kProtected: $ => /protected/i,
|
|
1804
|
+
kPrivate: $ => /private/i,
|
|
1805
|
+
kStrict: $ => /strict/i,
|
|
1806
|
+
kRequired: $ => /required/i,
|
|
1807
|
+
kOptional: $ => /optional/i,
|
|
1808
|
+
|
|
1809
|
+
kForward: $ => /forward/i,
|
|
1810
|
+
|
|
1811
|
+
kStatic: $ => /static/i,
|
|
1812
|
+
kVirtual: $ => /virtual/i,
|
|
1813
|
+
kAbstract: $ => /abstract/i,
|
|
1814
|
+
kSealed: $ => /sealed/i,
|
|
1815
|
+
kFinal: $ => /final/i,
|
|
1816
|
+
kReadonly: $ => /readonly/i,
|
|
1817
|
+
kWriteonly: $ => /writeonly/i,
|
|
1818
|
+
kDynamic: $ => /dynamic/i,
|
|
1819
|
+
kOverride: $ => /override/i,
|
|
1820
|
+
kOverload: $ => /overload/i,
|
|
1821
|
+
kReintroduce: $ => /reintroduce/i,
|
|
1822
|
+
kInherited: $ => /inherited/i,
|
|
1823
|
+
kInline: $ => /inline/i,
|
|
1824
|
+
|
|
1825
|
+
kStdcall: $ => /stdcall/i,
|
|
1826
|
+
kCdecl: $ => /cdecl/i,
|
|
1827
|
+
kCppdecl: $ => /cppdecl/i,
|
|
1828
|
+
kPascal: $ => /pascal/i,
|
|
1829
|
+
kRegister: $ => /register/i,
|
|
1830
|
+
kMwpascal: $ => /mwpascal/i,
|
|
1831
|
+
kExternal: $ => /external/i,
|
|
1832
|
+
kName: $ => /name/i,
|
|
1833
|
+
kMessage: $ => /message/i,
|
|
1834
|
+
kDeprecated: $ => /deprecated/i,
|
|
1835
|
+
kExperimental: $ => /experimental/i,
|
|
1836
|
+
kPlatform: $ => /platform/i,
|
|
1837
|
+
// Soft keyword: only used in `record ... end align N` record-alignment
|
|
1838
|
+
// hint (Win API records). Tokenizes everywhere but only consumed
|
|
1839
|
+
// structurally in _declClass post-end clause.
|
|
1840
|
+
kAlign: $ => /align/i,
|
|
1841
|
+
// Soft keyword: native library dependency hint in procExternal.
|
|
1842
|
+
kDependency: $ => /dependency/i,
|
|
1843
|
+
kUnimplemented: $ => /unimplemented/i,
|
|
1844
|
+
kCvar: $ => /cvar/i,
|
|
1845
|
+
kExport: $ => /export/i,
|
|
1846
|
+
kFar: $ => /far/i,
|
|
1847
|
+
kNear: $ => /near/i,
|
|
1848
|
+
kSafecall: $ => /safecall/i,
|
|
1849
|
+
kAssembler: $ => /assembler/i,
|
|
1850
|
+
kNostackframe: $ => /nostackframe/i,
|
|
1851
|
+
kInterrupt: $ => /interrupt/i,
|
|
1852
|
+
kNoreturn: $ => /noreturn/i,
|
|
1853
|
+
kIocheck: $ => /iocheck/i,
|
|
1854
|
+
kLocal: $ => /local/i,
|
|
1855
|
+
kHardfloat: $ => /hardfloat/i,
|
|
1856
|
+
kSoftfloat: $ => /softfloat/i,
|
|
1857
|
+
kMs_abi_default: $ => /ms_abi_default/i,
|
|
1858
|
+
kMs_abi_cdecl: $ => /ms_abi_cdecl/i,
|
|
1859
|
+
kSaveregisters: $ => /saveregisters/i,
|
|
1860
|
+
kSysv_abi_default: $ => /sysv_abi_default/i,
|
|
1861
|
+
kSysv_abi_cdecl: $ => /sysv_abi_cdecl/i,
|
|
1862
|
+
kVectorcall: $ => /vectorcall/i,
|
|
1863
|
+
kVarargs: $ => /varargs/i,
|
|
1864
|
+
kWinapi: $ => /winapi/i,
|
|
1865
|
+
kAlias: $ => /alias/i,
|
|
1866
|
+
// Delphi
|
|
1867
|
+
kDelayed: $ => /delayed/i,
|
|
1868
|
+
|
|
1869
|
+
kNil: $ => /nil/i,
|
|
1870
|
+
kTrue: $ => /true/i,
|
|
1871
|
+
kFalse: $ => /false/i,
|
|
1872
|
+
|
|
1873
|
+
kIfdef: $ => /ifdef/i,
|
|
1874
|
+
kIfndef: $ => /ifndef/i,
|
|
1875
|
+
kEndif: $ => /endif/i,
|
|
1876
|
+
|
|
1877
|
+
// `&` prefix escapes a keyword as an identifier (`&type`).
|
|
1878
|
+
// `&&` prefix is the Delphi.NET operator-name convention
|
|
1879
|
+
// (`&&op_Equality`, `&&op_Implicit`, etc.) still used in Spring4D's
|
|
1880
|
+
// generic operator overloads.
|
|
1881
|
+
// Phase 3b iter 40: identifier accepts Unicode letters too.
|
|
1882
|
+
// Delphi allows non-ASCII identifiers (umlauts, accented letters,
|
|
1883
|
+
// Cyrillic, etc.). Pattern: ASCII letter/underscore start, then
|
|
1884
|
+
// any letter/digit/underscore including the Unicode-letter range
|
|
1885
|
+
// U+0080+ (Latin-1 Supplement onwards). Excludes ASCII punctuation
|
|
1886
|
+
// so structural tokens stay intact.
|
|
1887
|
+
identifier: $ => /&{0,2}[a-zA-Z_-][0-9_a-zA-Z-]*/,
|
|
1888
|
+
|
|
1889
|
+
_space: $ => /[\s\r\n\t]+/,
|
|
1890
|
+
// Single preprocessor directive. The external scanner (src/scanner.c)
|
|
1891
|
+
// handles multi-line `{$IF*}...{$END*}` blocks via the pp_block token,
|
|
1892
|
+
// refusing blocks that wrap structural decls so they parse normally.
|
|
1893
|
+
pp: $ => /\{\$[^}]*\}/,
|
|
1894
|
+
comment: $ => token(choice(
|
|
1895
|
+
seq('//', /.*/),
|
|
1896
|
+
seq('{', /([^$}][^}]*)?/, '}'),
|
|
1897
|
+
/[(][*]([^*]*[*]+[^)*])*[^*]*[*]+[)]/
|
|
1898
|
+
)),
|
|
1899
|
+
}
|
|
1900
|
+
});
|