v / vlib / v2 / ast / cursor.v
778 lines · 719 sloc · 20.38 KB · 34038bec7b93f4e3ddbbdcbe88ea21f4693eee43
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module ast
5
6import v2.token
7
8// Cursor is a lightweight, by-value handle pointing at one FlatNode inside a
9// FlatAst. Consumers walk the AST via `match c.kind() {}` and descend via
10// `c.edge(i)` / `c.list_at(i)` without rehydrating to ast.Stmt / ast.Expr.
11//
12// A Cursor is 16 bytes: an &FlatAst pointer plus a FlatNodeId. It is safe to
13// copy, store in arrays, and pass by value. An "invalid" Cursor (id < 0) is
14// used as a sentinel where the legacy AST would use empty_stmt / empty_expr.
15pub struct Cursor {
16pub:
17 flat &FlatAst = unsafe { nil }
18 id FlatNodeId
19}
20
21@[inline]
22pub fn (c Cursor) is_valid() bool {
23 return c.flat != unsafe { nil } && c.id >= 0 && c.id < c.flat.nodes.len
24}
25
26@[inline]
27pub fn (c Cursor) kind() FlatNodeKind {
28 return c.flat.nodes[c.id].kind
29}
30
31@[inline]
32pub fn (c Cursor) pos() token.Pos {
33 return c.flat.nodes[c.id].pos
34}
35
36@[inline]
37pub fn (c Cursor) flags() u8 {
38 return c.flat.nodes[c.id].flags
39}
40
41@[inline]
42pub fn (c Cursor) flag(bit u8) bool {
43 return (c.flat.nodes[c.id].flags & bit) != 0
44}
45
46@[inline]
47pub fn (c Cursor) aux() u16 {
48 return c.flat.nodes[c.id].aux
49}
50
51@[inline]
52pub fn (c Cursor) extra_int() int {
53 return c.flat.nodes[c.id].extra
54}
55
56// extra_str interprets the node's `extra` field as an interned string id.
57// Use only on kinds where the schema documents `extra` as a string slot
58// (e.g. stmt_directive's value, stmt_import's alias).
59@[inline]
60pub fn (c Cursor) extra_str() string {
61 return c.flat.string_at(c.flat.nodes[c.id].extra)
62}
63
64// name returns the interned primary string (FlatNode.name_id) for this node.
65// Most kinds use this for identifiers, type names, fn names, etc.
66@[inline]
67pub fn (c Cursor) name() string {
68 return c.flat.string_at(c.flat.nodes[c.id].name_id)
69}
70
71@[inline]
72pub fn (c Cursor) name_id() int {
73 return c.flat.nodes[c.id].name_id
74}
75
76// ident reads an expr_ident cursor directly into an Ident.
77pub fn (c Cursor) ident() Ident {
78 if !c.is_valid() || c.kind() != .expr_ident {
79 return Ident{}
80 }
81 return Ident{
82 pos: c.pos()
83 name: c.name()
84 }
85}
86
87// import_stmt reads a stmt_import cursor directly into an ImportStmt.
88pub fn (c Cursor) import_stmt() ImportStmt {
89 if !c.is_valid() || c.kind() != .stmt_import {
90 return ImportStmt{}
91 }
92 mut symbols := []Expr{cap: c.edge_count()}
93 for i in 0 .. c.edge_count() {
94 sym := c.edge(i)
95 if sym.kind() == .expr_ident {
96 symbols << Expr(sym.ident())
97 }
98 }
99 return ImportStmt{
100 name: c.name()
101 alias: c.extra_str()
102 is_aliased: c.flag(flag_is_aliased)
103 symbols: symbols
104 }
105}
106
107// fn_decl_signature reads a stmt_fn_decl cursor into a body-less FnDecl. This
108// mirrors FlatAst.decode_fn_decl_signature without going through FlatReader.
109pub fn (c Cursor) fn_decl_signature() FnDecl {
110 if !c.is_valid() || c.kind() != .stmt_fn_decl {
111 return FnDecl{}
112 }
113 return FnDecl{
114 attributes: attrs_from_cursor(c.list_at(2))
115 is_public: c.flag(flag_is_public)
116 is_method: c.flag(flag_is_method)
117 is_static: c.flag(flag_is_static)
118 receiver: parameter_from_cursor(c.edge(0))
119 language: unsafe { Language(int(c.aux())) }
120 name: c.name()
121 typ: fn_type_from_cursor(c.edge(1))
122 pos: c.pos()
123 }
124}
125
126// fn_decl reads a stmt_fn_decl cursor into a legacy FnDecl. Unlike
127// fn_decl_signature, this materializes the body statements for consumers that
128// still use legacy statement walkers internally.
129pub fn (c Cursor) fn_decl() FnDecl {
130 if !c.is_valid() || c.kind() != .stmt_fn_decl {
131 return FnDecl{}
132 }
133 signature := c.fn_decl_signature()
134 return FnDecl{
135 attributes: signature.attributes
136 is_public: signature.is_public
137 is_method: signature.is_method
138 is_static: signature.is_static
139 receiver: signature.receiver
140 language: signature.language
141 name: signature.name
142 typ: signature.typ
143 stmts: c.list_at(3).stmts()
144 pos: signature.pos
145 }
146}
147
148// stmt reads a cursor through FlatAst.decode_stmt. This is the escape hatch
149// for legacy statement walkers; prefer cursor-specific readers when possible.
150pub fn (c Cursor) stmt() Stmt {
151 if !c.is_valid() {
152 return empty_stmt
153 }
154 return c.flat.decode_stmt(c.id)
155}
156
157// expr reads a cursor through FlatAst.decode_expr. This is the escape hatch
158// for legacy expression walkers; prefer cursor-specific readers when possible.
159pub fn (c Cursor) expr() Expr {
160 if !c.is_valid() {
161 return empty_expr
162 }
163 return c.flat.decode_expr(c.id)
164}
165
166// type_expr reads a type-expression cursor into the legacy Expr shape used by
167// signature consumers. It is intentionally narrower than FlatReader.read_expr:
168// non-type payloads such as field defaults or statement bodies stay omitted.
169pub fn (c Cursor) type_expr() Expr {
170 if !c.is_valid() {
171 return empty_expr
172 }
173 match c.kind() {
174 .expr_empty {
175 return empty_expr
176 }
177 .expr_basic_literal {
178 return Expr(BasicLiteral{
179 kind: unsafe { token.Token(int(c.aux())) }
180 value: c.name()
181 pos: c.pos()
182 })
183 }
184 .expr_ident {
185 return Expr(c.ident())
186 }
187 .expr_lifetime {
188 return Expr(LifetimeExpr{
189 name: c.name()
190 pos: c.pos()
191 })
192 }
193 .expr_modifier {
194 return Expr(ModifierExpr{
195 kind: unsafe { token.Token(int(c.aux())) }
196 expr: c.edge(0).type_expr()
197 pos: c.pos()
198 })
199 }
200 .expr_prefix {
201 return Expr(PrefixExpr{
202 op: unsafe { token.Token(int(c.aux())) }
203 expr: c.edge(0).type_expr()
204 pos: c.pos()
205 })
206 }
207 .expr_selector {
208 rhs := c.edge(1)
209 return Expr(SelectorExpr{
210 lhs: c.edge(0).type_expr()
211 rhs: Ident{
212 name: rhs.name()
213 pos: rhs.pos()
214 }
215 pos: c.pos()
216 })
217 }
218 .expr_generic_args {
219 return Expr(GenericArgs{
220 lhs: c.edge(0).type_expr()
221 args: type_exprs_from_edges(c, 1)
222 pos: c.pos()
223 })
224 }
225 .expr_generic_arg_or_index {
226 return Expr(GenericArgOrIndexExpr{
227 lhs: c.edge(0).type_expr()
228 expr: c.edge(1).type_expr()
229 pos: c.pos()
230 })
231 }
232 .typ_anon_struct {
233 return Expr(Type(AnonStructType{
234 generic_params: c.list_at(0).type_exprs()
235 embedded: c.list_at(1).type_exprs()
236 fields: field_decl_type_list(c.list_at(2))
237 }))
238 }
239 .typ_array_fixed {
240 return Expr(Type(ArrayFixedType{
241 len: c.edge(0).type_expr()
242 elem_type: c.edge(1).type_expr()
243 }))
244 }
245 .typ_array {
246 return Expr(Type(ArrayType{
247 elem_type: c.edge(0).type_expr()
248 }))
249 }
250 .typ_channel {
251 return Expr(Type(ChannelType{
252 cap: c.edge(0).type_expr()
253 elem_type: c.edge(1).type_expr()
254 }))
255 }
256 .typ_fn {
257 return Expr(Type(FnType{
258 generic_params: c.list_at(0).type_exprs()
259 params: parameter_list_from_cursor(c.list_at(1))
260 return_type: c.edge(2).type_expr()
261 }))
262 }
263 .typ_generic {
264 return Expr(Type(GenericType{
265 name: c.edge(0).type_expr()
266 params: type_exprs_from_edges(c, 1)
267 }))
268 }
269 .typ_map {
270 return Expr(Type(MapType{
271 key_type: c.edge(0).type_expr()
272 value_type: c.edge(1).type_expr()
273 }))
274 }
275 .typ_nil {
276 return Expr(Type(NilType{}))
277 }
278 .typ_none {
279 return Expr(Type(NoneType{}))
280 }
281 .typ_option {
282 return Expr(Type(OptionType{
283 base_type: c.edge(0).type_expr()
284 }))
285 }
286 .typ_pointer {
287 return Expr(Type(PointerType{
288 base_type: c.edge(0).type_expr()
289 lifetime: c.name()
290 }))
291 }
292 .typ_result {
293 return Expr(Type(ResultType{
294 base_type: c.edge(0).type_expr()
295 }))
296 }
297 .typ_thread {
298 return Expr(Type(ThreadType{
299 elem_type: c.edge(0).type_expr()
300 }))
301 }
302 .typ_tuple {
303 return Expr(Type(TupleType{
304 types: type_exprs_from_edges(c, 0)
305 }))
306 }
307 else {
308 return empty_expr
309 }
310 }
311}
312
313@[inline]
314pub fn (c Cursor) edge_count() int {
315 return c.flat.nodes[c.id].edge_count
316}
317
318// edge returns a Cursor over the i-th direct child edge of this node. Out-of-
319// range indices return an invalid Cursor (id == invalid_flat_node_id), which
320// callers can detect via `c.is_valid()`. This matches FlatReader's behaviour
321// where `r.edge(n, i)` may return invalid_flat_node_id for missing slots.
322@[inline]
323pub fn (c Cursor) edge(i int) Cursor {
324 return Cursor{
325 flat: c.flat
326 id: c.flat.child_at(c.id, i)
327 }
328}
329
330// list_at treats edge `i` as a reference to an `aux_list` node and returns a
331// CursorList over its children. The flat schema represents list-typed fields
332// (e.g. StructDecl.fields, FnDecl.stmts, EnumDecl.attributes) as a single
333// edge to an aux_list whose children are the actual list items. Use `edge()`
334// for fields stored as direct child edges of the parent (e.g. AssignStmt's
335// LHS/RHS, AssertStmt's expr/extra).
336@[inline]
337pub fn (c Cursor) list_at(edge_i int) CursorList {
338 return CursorList{
339 flat: c.flat
340 parent_id: c.flat.child_at(c.id, edge_i)
341 }
342}
343
344// for_body_list views a stmt_for node's body as a CursorList. The stmt_for flat
345// layout is edges [init, cond, post, body_stmt_0, body_stmt_1, ...] — the body
346// is trailing edges of the for-node itself, NOT a separate aux_list — so the
347// body list is this node's children from edge index 3 onward.
348@[inline]
349pub fn (c Cursor) for_body_list() CursorList {
350 return CursorList{
351 flat: c.flat
352 parent_id: c.id
353 offset: 3
354 }
355}
356
357// CursorList is a view over the children of an `aux_list` node. It is the
358// flat equivalent of `[]ast.Stmt` / `[]ast.Expr` / `[]FieldDecl` etc., except
359// no slice is materialised — `at(i)` decodes one child at a time.
360pub struct CursorList {
361pub:
362 flat &FlatAst = unsafe { nil }
363 parent_id FlatNodeId
364 // offset skips the first `offset` child edges of `parent_id`. Default 0 (the
365 // whole child list). Lets a trailing edge range be viewed as a list when the
366 // items are direct child edges of a node rather than an aux_list — e.g. a
367 // ForStmt body lives in edges [3..] of the stmt_for node itself (see
368 // `Cursor.for_body_list`).
369 offset int
370}
371
372@[inline]
373pub fn (l CursorList) len() int {
374 if l.flat == unsafe { nil } || l.parent_id < 0 || l.parent_id >= l.flat.nodes.len {
375 return 0
376 }
377 return l.flat.nodes[l.parent_id].edge_count - l.offset
378}
379
380@[inline]
381pub fn (l CursorList) at(i int) Cursor {
382 return Cursor{
383 flat: l.flat
384 id: l.flat.child_at(l.parent_id, l.offset + i)
385 }
386}
387
388// type_exprs reads every item in a cursor list through Cursor.type_expr.
389pub fn (l CursorList) type_exprs() []Expr {
390 mut out := []Expr{cap: l.len()}
391 for i in 0 .. l.len() {
392 out << l.at(i).type_expr()
393 }
394 return out
395}
396
397// stmts reads every item in a cursor list through FlatAst.decode_stmt. This is
398// the escape hatch for legacy statement walkers; prefer cursor-specific
399// readers when the caller only needs a declaration signature or metadata.
400pub fn (l CursorList) stmts() []Stmt {
401 mut out := []Stmt{cap: l.len()}
402 for i in 0 .. l.len() {
403 c := l.at(i)
404 if !c.is_valid() {
405 continue
406 }
407 out << c.stmt()
408 }
409 return out
410}
411
412// attribute_expr reads the small expression subset used inside attributes.
413// Attribute payloads are normally identifiers or strings; type_expr handles
414// the type-like fallback cases without opening the full FlatReader.
415pub fn (c Cursor) attribute_expr() Expr {
416 if !c.is_valid() {
417 return empty_expr
418 }
419 match c.kind() {
420 .expr_empty {
421 return empty_expr
422 }
423 .expr_basic_literal {
424 return Expr(BasicLiteral{
425 kind: unsafe { token.Token(int(c.aux())) }
426 value: c.name()
427 pos: c.pos()
428 })
429 }
430 .expr_ident {
431 return Expr(c.ident())
432 }
433 .expr_string {
434 return Expr(StringLiteral{
435 kind: unsafe { StringLiteralKind(int(c.aux())) }
436 value: c.name()
437 pos: c.pos()
438 })
439 }
440 .expr_selector {
441 rhs := c.edge(1)
442 return Expr(SelectorExpr{
443 lhs: c.edge(0).attribute_expr()
444 rhs: Ident{
445 name: rhs.name()
446 pos: rhs.pos()
447 }
448 pos: c.pos()
449 })
450 }
451 else {
452 return c.type_expr()
453 }
454 }
455}
456
457// attribute reads an aux_attribute cursor into the legacy Attribute shape.
458pub fn (c Cursor) attribute() Attribute {
459 if !c.is_valid() || c.kind() != .aux_attribute {
460 return Attribute{}
461 }
462 return Attribute{
463 name: c.name()
464 value: c.edge(0).attribute_expr()
465 // comptime_cond (`@[if cond ?]`) can be an arbitrary expression, not the
466 // ident/string subset attribute_expr handles — decode it fully (mirrors
467 // FlatReader.read_attribute), else complex conditions silently become
468 // empty_expr and `@[if ...]` functions are never elided.
469 comptime_cond: c.edge(1).expr()
470 pos: c.pos()
471 }
472}
473
474// attributes reads every aux_attribute in a cursor list.
475pub fn (l CursorList) attributes() []Attribute {
476 mut out := []Attribute{cap: l.len()}
477 for i in 0 .. l.len() {
478 attr := l.at(i)
479 if !attr.is_valid() {
480 continue
481 }
482 out << attr.attribute()
483 }
484 return out
485}
486
487// import_stmts reads every stmt_import in a cursor list.
488pub fn (l CursorList) import_stmts() []ImportStmt {
489 mut out := []ImportStmt{cap: l.len()}
490 for i in 0 .. l.len() {
491 imp := l.at(i)
492 if !imp.is_valid() || imp.kind() != .stmt_import {
493 continue
494 }
495 out << imp.import_stmt()
496 }
497 return out
498}
499
500// field_init reads an aux_field_init cursor. Field values are still legacy
501// expression consumers today, so this materializes only the value expression,
502// not the parent declaration.
503pub fn (c Cursor) field_init() FieldInit {
504 if !c.is_valid() || c.kind() != .aux_field_init {
505 return FieldInit{}
506 }
507 value_c := c.edge(0)
508 return FieldInit{
509 name: c.name()
510 value: value_c.expr()
511 }
512}
513
514// field_inits reads every aux_field_init in a cursor list.
515pub fn (l CursorList) field_inits() []FieldInit {
516 mut out := []FieldInit{cap: l.len()}
517 for i in 0 .. l.len() {
518 out << l.at(i).field_init()
519 }
520 return out
521}
522
523// field_decl reads an aux_field_decl cursor. Set decode_value to false when a
524// caller only needs declaration metadata and type expressions.
525pub fn (c Cursor) field_decl(decode_value bool) FieldDecl {
526 if !c.is_valid() || c.kind() != .aux_field_decl {
527 return FieldDecl{}
528 }
529 value_c := c.edge(1)
530 return FieldDecl{
531 name: c.name()
532 typ: c.edge(0).type_expr()
533 value: if decode_value { value_c.expr() } else { empty_expr }
534 attributes: c.list_at(2).attributes()
535 is_public: c.flag(flag_is_public)
536 is_mut: c.flag(flag_is_mut)
537 is_module_mut: c.flag(flag_field_is_module_mut)
538 is_interface_method: c.flag(flag_field_is_interface_method)
539 }
540}
541
542// field_decls reads every aux_field_decl in a cursor list.
543pub fn (l CursorList) field_decls(decode_values bool) []FieldDecl {
544 mut out := []FieldDecl{cap: l.len()}
545 for i in 0 .. l.len() {
546 out << l.at(i).field_decl(decode_values)
547 }
548 return out
549}
550
551// const_decl reads a stmt_const_decl cursor.
552pub fn (c Cursor) const_decl() ConstDecl {
553 if !c.is_valid() || c.kind() != .stmt_const_decl {
554 return ConstDecl{}
555 }
556 return ConstDecl{
557 is_public: c.flag(flag_is_public)
558 fields: c.list_at(0).field_inits()
559 }
560}
561
562// enum_decl reads a stmt_enum_decl cursor. Set decode_values to false when
563// only field names/attributes are needed.
564pub fn (c Cursor) enum_decl(decode_values bool) EnumDecl {
565 if !c.is_valid() || c.kind() != .stmt_enum_decl {
566 return EnumDecl{}
567 }
568 return EnumDecl{
569 attributes: c.list_at(1).attributes()
570 is_public: c.flag(flag_is_public)
571 name: c.name()
572 as_type: c.edge(0).type_expr()
573 fields: c.list_at(2).field_decls(decode_values)
574 }
575}
576
577// global_decl reads a stmt_global_decl cursor. Set decode_values to false when
578// only field metadata and declared types are needed.
579pub fn (c Cursor) global_decl(decode_values bool) GlobalDecl {
580 if !c.is_valid() || c.kind() != .stmt_global_decl {
581 return GlobalDecl{}
582 }
583 return GlobalDecl{
584 attributes: c.list_at(0).attributes()
585 fields: c.list_at(1).field_decls(decode_values)
586 is_public: c.flag(flag_is_public)
587 }
588}
589
590// interface_decl reads a stmt_interface_decl cursor.
591pub fn (c Cursor) interface_decl() InterfaceDecl {
592 if !c.is_valid() || c.kind() != .stmt_interface_decl {
593 return InterfaceDecl{}
594 }
595 return InterfaceDecl{
596 is_public: c.flag(flag_is_public)
597 attributes: c.list_at(0).attributes()
598 name: c.name()
599 generic_params: c.list_at(1).type_exprs()
600 embedded: c.list_at(2).type_exprs()
601 fields: c.list_at(3).field_decls(true)
602 }
603}
604
605// struct_decl reads a stmt_struct_decl cursor.
606pub fn (c Cursor) struct_decl() StructDecl {
607 if !c.is_valid() || c.kind() != .stmt_struct_decl {
608 return StructDecl{}
609 }
610 return StructDecl{
611 attributes: c.list_at(0).attributes()
612 is_public: c.flag(flag_is_public)
613 is_union: c.flag(flag_is_union)
614 implements: c.list_at(1).type_exprs()
615 embedded: c.list_at(2).type_exprs()
616 language: unsafe { Language(int(c.aux())) }
617 name: c.name()
618 generic_params: c.list_at(3).type_exprs()
619 fields: c.list_at(4).field_decls(true)
620 pos: c.pos()
621 }
622}
623
624// type_decl reads a stmt_type_decl cursor.
625pub fn (c Cursor) type_decl() TypeDecl {
626 if !c.is_valid() || c.kind() != .stmt_type_decl {
627 return TypeDecl{}
628 }
629 return TypeDecl{
630 is_public: c.flag(flag_is_public)
631 language: unsafe { Language(int(c.aux())) }
632 name: c.name()
633 generic_params: c.list_at(2).type_exprs()
634 base_type: c.edge(0).type_expr()
635 variants: c.list_at(3).type_exprs()
636 }
637}
638
639fn attrs_from_cursor(list CursorList) []Attribute {
640 return list.attributes()
641}
642
643fn fn_type_from_cursor(c Cursor) FnType {
644 if !c.is_valid() || c.kind() != .typ_fn {
645 return FnType{}
646 }
647 return FnType{
648 generic_params: c.list_at(0).type_exprs()
649 params: parameter_list_from_cursor(c.list_at(1))
650 return_type: c.edge(2).type_expr()
651 }
652}
653
654fn type_exprs_from_edges(c Cursor, start int) []Expr {
655 if !c.is_valid() || start >= c.edge_count() {
656 return []Expr{}
657 }
658 mut out := []Expr{cap: c.edge_count() - start}
659 for i in start .. c.edge_count() {
660 out << c.edge(i).type_expr()
661 }
662 return out
663}
664
665fn parameter_from_cursor(c Cursor) Parameter {
666 if !c.is_valid() {
667 return Parameter{}
668 }
669 return Parameter{
670 name: c.name()
671 typ: c.edge(0).type_expr()
672 is_mut: c.flag(flag_is_mut)
673 pos: c.pos()
674 }
675}
676
677fn parameter_list_from_cursor(list CursorList) []Parameter {
678 mut out := []Parameter{cap: list.len()}
679 for i in 0 .. list.len() {
680 out << parameter_from_cursor(list.at(i))
681 }
682 return out
683}
684
685fn field_decl_type_list(list CursorList) []FieldDecl {
686 mut out := []FieldDecl{cap: list.len()}
687 for i in 0 .. list.len() {
688 field := list.at(i)
689 if !field.is_valid() {
690 continue
691 }
692 out << FieldDecl{
693 name: field.name()
694 typ: field.edge(0).type_expr()
695 is_public: field.flag(flag_is_public)
696 is_mut: field.flag(flag_is_mut)
697 is_module_mut: field.flag(flag_field_is_module_mut)
698 is_interface_method: field.flag(flag_field_is_interface_method)
699 }
700 }
701 return out
702}
703
704// FileCursor is a typed wrapper over a FlatFile entry. It exposes the
705// file-level metadata (name, mod, selector_names) and the three top-level
706// child lists (attributes, imports, stmts) without rehydrating a full
707// ast.File.
708pub struct FileCursor {
709pub:
710 flat &FlatAst = unsafe { nil }
711 idx int // index into flat.files
712}
713
714// file_cursor returns a FileCursor over the i-th FlatFile in this FlatAst.
715@[inline]
716pub fn (flat &FlatAst) file_cursor(idx int) FileCursor {
717 return FileCursor{
718 flat: unsafe { flat }
719 idx: idx
720 }
721}
722
723// file_cursors returns one FileCursor per FlatFile. Allocates an int-sized
724// array; for hot loops prefer `for i in 0 .. flat.files.len { flat.file_cursor(i) }`.
725pub fn (flat &FlatAst) file_cursors() []FileCursor {
726 mut out := []FileCursor{cap: flat.files.len}
727 for i in 0 .. flat.files.len {
728 out << flat.file_cursor(i)
729 }
730 return out
731}
732
733@[inline]
734pub fn (fc FileCursor) flat_file() FlatFile {
735 return fc.flat.files[fc.idx]
736}
737
738// root returns a Cursor positioned at this file's root FlatNode (kind == .file).
739@[inline]
740pub fn (fc FileCursor) root() Cursor {
741 return Cursor{
742 flat: fc.flat
743 id: fc.flat.files[fc.idx].file_id
744 }
745}
746
747@[inline]
748pub fn (fc FileCursor) name() string {
749 return fc.flat.string_at(fc.flat.files[fc.idx].name_idx)
750}
751
752@[inline]
753pub fn (fc FileCursor) mod() string {
754 return fc.flat.string_at(fc.flat.files[fc.idx].mod_idx)
755}
756
757@[inline]
758pub fn (fc FileCursor) selector_names() map[int]string {
759 return fc.flat.files[fc.idx].selector_names
760}
761
762// attrs returns the file's top-level attribute list (edge 0 of the file node).
763@[inline]
764pub fn (fc FileCursor) attrs() CursorList {
765 return fc.root().list_at(0)
766}
767
768// imports returns the file's top-level import list (edge 1 of the file node).
769@[inline]
770pub fn (fc FileCursor) imports() CursorList {
771 return fc.root().list_at(1)
772}
773
774// stmts returns the file's top-level statement list (edge 2 of the file node).
775@[inline]
776pub fn (fc FileCursor) stmts() CursorList {
777 return fc.root().list_at(2)
778}
779