Skip to content

Commit 8a39047

Browse files
authored
Update tree-sitter grammar with new syntactic elements (#29580)
Add support for new syntax to the tree sitter grammar: * `export`ed items * `view fn` * `dyn record` + cast * dynamic-call expressions * record-prototype bodies * multi-segment locators
1 parent 7bd86e5 commit 8a39047

6 files changed

Lines changed: 29542 additions & 21830 deletions

File tree

tree-sitter/grammar.js

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ const ALL_KEYWORDS = [
4646
'let',
4747
'const',
4848
'constant',
49+
'export',
50+
'dyn',
51+
'identifier',
4952
'final',
5053
'view',
5154
'fn',
@@ -108,6 +111,7 @@ module.exports = grammar({
108111
[$.path_locator_expression, $.program_ref_expression],
109112
[$.path_locator_expression, $.locator_type],
110113
[$.path_locator_expression, $.program_ref_expression, $.locator_type],
114+
[$.path_locator_expression],
111115
[$.return_type, $.tuple_type],
112116
[$.return_type_item, $.tuple_type],
113117
[$.optional_type, $.mapping_type],
@@ -141,6 +145,7 @@ module.exports = grammar({
141145
$.struct_definition,
142146
$.function_definition,
143147
$.final_function_definition,
148+
$.view_function_definition,
144149
$.const_declaration,
145150
),
146151

@@ -184,6 +189,7 @@ module.exports = grammar({
184189
),
185190

186191
interface_declaration: $ => seq(
192+
optional('export'),
187193
'interface',
188194
field('name', $.identifier),
189195
optional(seq(':', field('parents', $.parent_list))),
@@ -212,10 +218,16 @@ module.exports = grammar({
212218
record_prototype: $ => seq(
213219
'record',
214220
field('name', $.identifier),
215-
';',
221+
choice(
222+
';',
223+
// Partially-constrained record body in an interface: fields must end
224+
// with a trailing `..` (fully constraining fields is not yet supported).
225+
seq('{', optional(commaSep1($.struct_field)), '..', '}'),
226+
),
216227
),
217228

218229
struct_definition: $ => seq(
230+
optional('export'),
219231
'struct',
220232
field('name', $.identifier),
221233
optional(field('const_parameters', $.const_param_list)),
@@ -267,6 +279,7 @@ module.exports = grammar({
267279
),
268280

269281
const_declaration: $ => seq(
282+
optional('export'),
270283
'const',
271284
field('name', $.identifier),
272285
':',
@@ -278,6 +291,7 @@ module.exports = grammar({
278291

279292
function_definition: $ => seq(
280293
repeat($.annotation),
294+
optional('export'),
281295
'fn',
282296
field('name', $.identifier),
283297
optional(field('const_parameters', $.const_param_list)),
@@ -288,6 +302,7 @@ module.exports = grammar({
288302

289303
final_function_definition: $ => seq(
290304
repeat($.annotation),
305+
optional('export'),
291306
'final',
292307
'fn',
293308
field('name', $.identifier),
@@ -299,6 +314,7 @@ module.exports = grammar({
299314

300315
view_function_definition: $ => seq(
301316
repeat($.annotation),
317+
optional('export'),
302318
'view',
303319
'fn',
304320
field('name', $.identifier),
@@ -318,7 +334,7 @@ module.exports = grammar({
318334
const_param_list: $ => seq(
319335
token(prec(1, '::')),
320336
'[',
321-
commaSep1($.const_param),
337+
optional(commaSep1($.const_param)),
322338
']',
323339
),
324340

@@ -354,7 +370,7 @@ module.exports = grammar({
354370
annotation: $ => seq(
355371
'@',
356372
field('name', $._annotation_name),
357-
optional(seq('(', commaSep1($.annotation_pair), ')')),
373+
optional(seq('(', optional(commaSep1($.annotation_pair)), ')')),
358374
),
359375

360376
annotation_pair: $ => seq(
@@ -529,6 +545,7 @@ module.exports = grammar({
529545
$.field_expression,
530546
$.tuple_access_expression,
531547
$.index_expression,
548+
$.dynamic_op_expression,
532549
$.path_expression,
533550
$.struct_expression,
534551
$.struct_locator_expression,
@@ -555,6 +572,7 @@ module.exports = grammar({
555572
$.field_expression,
556573
$.tuple_access_expression,
557574
$.index_expression,
575+
$.dynamic_op_expression,
558576
$.path_expression,
559577
$.path_locator_expression,
560578
$.program_ref_expression,
@@ -613,7 +631,7 @@ module.exports = grammar({
613631
cast_expression: $ => prec.left(PREC.cast, seq(
614632
field('value', $._expression),
615633
'as',
616-
field('type', $.primitive_type),
634+
field('type', choice($.primitive_type, $.dyn_record_type)),
617635
)),
618636

619637
ternary_expression: $ => prec.right(PREC.ternary, seq(
@@ -661,6 +679,26 @@ module.exports = grammar({
661679
']',
662680
)),
663681

682+
// Dynamic interface/storage access, in three forms:
683+
// iface@(target)::func(args) — dynamic function call
684+
// iface@(target)::storage.op(args) — dynamic mapping/vector access
685+
// iface@(target)::storage — dynamic singleton storage read
686+
// The target may be followed by an optional network argument: `@(target, network)`.
687+
dynamic_op_expression: $ => prec.left(PREC.call, seq(
688+
field('interface', $._expression),
689+
'@',
690+
'(',
691+
field('target', $._expression),
692+
optional(seq(',', field('network', $._expression))),
693+
')',
694+
token(prec(1, '::')),
695+
field('member', $.identifier),
696+
optional(choice(
697+
field('arguments', $.argument_list),
698+
seq('.', field('operation', $.identifier), field('arguments', $.argument_list)),
699+
)),
700+
)),
701+
664702
path_expression: $ => choice(
665703
$.special_path,
666704
seq(
@@ -684,6 +722,7 @@ module.exports = grammar({
684722
field('program', $.program_id),
685723
token(prec(1, '::')),
686724
field('member', $.identifier),
725+
repeat(seq(token(prec(1, '::')), $.identifier)),
687726
optional($.const_arg_list),
688727
),
689728

@@ -722,15 +761,19 @@ module.exports = grammar({
722761
const_arg_list_bracket: $ => seq(
723762
token(prec(1, '::')),
724763
'[',
725-
commaSep1($.const_arg),
764+
optional(commaSep1($.const_arg)),
726765
']',
727766
),
728767

729768
const_arg: $ => choice(
769+
$.dynamic_call_return_type,
730770
$._type,
731771
$._expression,
732772
),
733773

774+
// A visibility-prefixed type argument, e.g. `public u64` in `foo::[public u64]`.
775+
dynamic_call_return_type: $ => seq($.visibility, $._type),
776+
734777
const_arg_list_angle: $ => seq(
735778
token(prec(1, '::')),
736779
'<',
@@ -798,11 +841,13 @@ module.exports = grammar({
798841
/0b[0-9A-Za-z_]+(u8|u16|u32|u64|u128|i8|i16|i32|i64|i128)?/,
799842
)),
800843

801-
field_literal: _ => token(prec(2, /[0-9][0-9_]*field/)),
844+
// Suffix literals accept the same bases as integer literals (decimal, `0x`, `0o`, `0b`);
845+
// the compiler classifies purely by the trailing type suffix.
846+
field_literal: _ => token(prec(2, /[0-9][0-9A-Za-z_]*field/)),
802847

803-
group_literal: _ => token(prec(2, /[0-9][0-9_]*group/)),
848+
group_literal: _ => token(prec(2, /[0-9][0-9A-Za-z_]*group/)),
804849

805-
scalar_literal: _ => token(prec(2, /[0-9][0-9_]*scalar/)),
850+
scalar_literal: _ => token(prec(2, /[0-9][0-9A-Za-z_]*scalar/)),
806851

807852
string_literal: _ => token(/"[^"]*"/),
808853

@@ -817,6 +862,7 @@ module.exports = grammar({
817862

818863
_type: $ => choice(
819864
$.primitive_type,
865+
$.dyn_record_type,
820866
$.path_type,
821867
$.locator_type,
822868
$.array_type,
@@ -827,6 +873,9 @@ module.exports = grammar({
827873
$.mapping_type,
828874
),
829875

876+
// A dynamic record type: `dyn record`.
877+
dyn_record_type: _ => seq('dyn', 'record'),
878+
830879
primitive_type: _ => choice(
831880
'address',
832881
'bool',
@@ -856,7 +905,7 @@ module.exports = grammar({
856905

857906
locator_type: $ => seq(
858907
$.program_id,
859-
optional(seq(token(prec(1, '::')), $.identifier)),
908+
repeat(seq(token(prec(1, '::')), $.identifier)),
860909
optional($.const_arg_list),
861910
),
862911

tree-sitter/queries/highlights.scm

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[
2-
"let" "const" "constant" "fn" "final" "view" "struct" "record"
2+
"let" "const" "constant" "export" "dyn" "fn" "final" "view" "struct" "record"
33
"program" "import" "mapping" "storage" "interface" "constructor"
44
"if" "else" "for" "in" "return"
55
"public" "private" "as" "self" "block" "network"
@@ -34,7 +34,10 @@
3434
(import_declaration name: (program_id) @namespace)
3535

3636
(annotation name: (_) @attribute)
37-
"@" @attribute
37+
(annotation "@" @attribute)
38+
39+
; The `@` in a dynamic interface access reads as an operator, not an annotation.
40+
(dynamic_op_expression "@" @operator)
3841

3942
(field_expression field: (_) @property)
4043
(struct_field name: (identifier) @property)

0 commit comments

Comments
 (0)