1//! The Abstract Syntax Tree preserves most details of the source code,
2//! as it is used for formatting. Before semantic analysis, this
3//! structure is normalized into `Hir`, which removes constructs like
4//! whitespace, unneeded let bindings, and other transforms.
5//!
6//! `ast.nodes` contains nodes stored as three parts:
7//! - tag: the type of the node
8//! - main_token: index to its primary token, depends on the tag
9//! - data: two u32 values, depends on the tag
10//!
11//! Many nodes encode all their metadata with just `node.data`, using
12//! facts about the language grammar to be able to infer data without
13//! storing it.
14//!
15//! For example, `var_decl` has the following encoding:
16//! - `main_token` is either a `kw_var` or `kw_let`
17//! - the token after `main_token` is always the identifier
18//! - `data.var_decl` contains `NodeIndex`es pointing at the type
19//! and the initializer, which are both expressions.
20//! - If the keyword before `main_token` is `kw_pub` it is a public variable.
21//!
22//! To make decoding these easier, many nodes contain a "full" variant,
23//! such as `full.VarDecl`, which can be acquired with `ast.fullVarDecl(id)`.
24//!
25//! For nodes that must store more than 64 bits of payload data, an "extra"
26//! struct is stored out-of-line in the `extra` field. These can be read
27//! using `ast.extraData(extra_index, T)`. Again, it is recommended to use
28//! the `full` structures with their convenience functions.
29//!
30//! Documentation comments are not stored anywhere, but you can find them
31//! by iterating backwards from a declaration and seeing if a `.doc_comment`
32//! is present. This is provided by `ast.declDocComment(id)`.
33//!
34//! The `Ast` owns token and node data, but not the source code.
35
36/// Externally owned source code.
37utf8_source: [:0]const u8,
38
39tokens: Token.List.Slice,
40nodes: Node.List.Slice,
41/// TODO: Move this to using array.ExtraStore
42extra: []const u32,
43
44pub const Node = struct {
45 tag: Tag,
46 main_token: TokenIndex,
47 data: Data,
48
49 pub const List = std.MultiArrayList(Node);
50
51 pub const Tag = enum {
52 /// Read with `ast.rootNodes()`
53 /// data is .list (containing stmt slice)
54 root,
55 /// main_token is the {
56 /// data is .list (containing stmt slice)
57 ///
58 /// This guarantees that either the block is empty or the last statement
59 /// ends with a semicolon.
60 block_void,
61 /// main_token is the {
62 /// data is .list (containing stmt slice)
63 ///
64 /// This guarantees that the block is not empty and that the last
65 /// statement is an expression statement without a trailing semicolon.
66 block_implicit_return,
67 /// Variable or constant declaration
68 /// `var x: type = init;`
69 /// `let y = x;`
70 /// main_token is the keyword
71 /// data is .var_decl
72 var_decl,
73 /// Function declaration
74 /// `fn foo(x: i32) -> i32 { rhs }`
75 /// Params and return type are stored in extra data
76 fn_decl,
77 /// Function expression
78 /// `fn (x, y) { x + y }`
79 /// Params and return type are stored in extra data
80 fn_expr,
81 /// 'name: type' declaration
82 /// Main token is the identifier
83 param_decl,
84 ///
85 test_decl,
86 /// An identifier. main_token is the identifier.
87 id,
88 /// Main token is the data. May not fit in i32 range,
89 /// but will contain no dots or e's.
90 number_int,
91 /// Main token is data. Will contain '.' and/or 'e'.
92 number_float,
93 /// A string literal
94 string,
95 /// A multi-line string literal
96 /// data is .token_list of the tokens, which is guaranteed to be contiguous
97 multiline_string,
98 /// Call to a function
99 /// `foo(x, y);`
100 /// main_token is the `(`
101 /// data is .call
102 call,
103 /// Call to a builtin function
104 /// `@raw("hello");`
105 /// data is .list (containing arg slice)
106 builtin_call,
107 /// `if cond { ... } else ...`
108 /// main_token is the `if`
109 /// data is .@"if"
110 @"if",
111 /// `if let id = ... { ... } else ...`
112 /// main_token is the `if`
113 /// data is .@"if"
114 // if_let_nonvoid,
115 /// `id: ...`
116 /// data is un
117 label,
118 /// `expr.field`
119 /// main_token is the `.`
120 /// data is .field
121 field,
122 /// `lhs[rhs]`
123 /// main_token is the `[`
124 /// data is .bin
125 index,
126 /// `return expr;`
127 /// main_token is the `return`
128 /// data is .@"return"
129 @"return",
130 /// `break :brk expr;`
131 /// main_token is the `break`
132 /// data is .@"break"
133 @"break",
134 /// `lhs + rhs`. main_token is the operator.
135 add,
136 /// `lhs - rhs`. main_token is the operator.
137 sub,
138 /// `lhs * rhs`. main_token is the operator.
139 mul,
140 /// `lhs / rhs`. main_token is the operator.
141 div,
142 /// `lhs % rhs`. main_token is the operator.
143 rem,
144 /// `lhs ** rhs`. main_token is the operator.
145 pow,
146 /// `lhs && rhs`. main_token is the operator.
147 bool_and,
148 /// `lhs || rhs`. main_token is the operator.
149 bool_or,
150 /// `lhs & rhs`. main_token is the operator.
151 bit_and,
152 /// `lhs | rhs`. main_token is the operator.
153 bit_or,
154 /// `lhs ^ rhs`. main_token is the operator.
155 bit_xor,
156 /// `lhs << rhs`. main_token is the operator.
157 shl,
158 /// `lhs >> rhs`. main_token is the operator.
159 shr,
160 /// `lhs == rhs`. main_token is the operator.
161 eq,
162 /// `lhs != rhs`. main_token is the operator.
163 ne,
164 /// `lhs < rhs`. main_token is the operator.
165 lt,
166 /// `lhs <= rhs`. main_token is the operator.
167 le,
168 /// `lhs > rhs`. main_token is the operator.
169 gt,
170 /// `lhs >= rhs`. main_token is the operator.
171 ge,
172 /// `lhs = rhs`. main_token is the operator.
173 assign,
174 /// `lhs += rhs`. main_token is the operator.
175 add_assign,
176 /// `lhs -= rhs`. main_token is the operator.
177 sub_assign,
178 /// `lhs *= rhs`. main_token is the operator.
179 mul_assign,
180 /// `lhs /= rhs`. main_token is the operator.
181 div_assign,
182 /// `lhs %= rhs`. main_token is the operator.
183 rem_assign,
184 /// `lhs **= rhs`. main_token is the operator.
185 pow_assign,
186 /// `lhs &&= rhs`. main_token is the operator.
187 and_assign,
188 /// `lhs ||= rhs`. main_token is the operator.
189 or_assign,
190 /// `lhs &= rhs`. main_token is the operator.
191 bit_and_assign,
192 /// `lhs |= rhs`. main_token is the operator.
193 bit_or_assign,
194 /// `lhs ^= rhs`. main_token is the operator.
195 bit_xor_assign,
196 /// `lhs <<= rhs`. main_token is the operator.
197 shl_assign,
198 /// `lhs >>= rhs`. main_token is the operator.
199 shr_assign,
200 /// `!expr`. main_token is the operator.
201 bool_not,
202 /// `-expr`. main_token is the operator.
203 negate,
204 /// `~expr`. main_token is the operator.
205 bit_not,
206 /// `comptime expr`. main_token is the operator.
207 @"comptime",
208 /// `pure expr`. main_token is the operator.
209 pure,
210 /// `assert expr`. main_token is the operator.
211 assert,
212 /// Struct Declaration. main_token is the id if set
213 struct_decl,
214 /// Struct Field.
215 struct_field,
216 /// Struct Init
217 struct_init,
218 /// Array type
219 array_type,
220 /// Array initialization
221 array_init,
222 /// Tuple type
223 tuple_type,
224 /// Tuple Init
225 tuple_init,
226
227 pub fn isAssignBin(op: Tag) bool {
228 return switch (op) {
229 .assign,
230 .add_assign,
231 .sub_assign,
232 .mul_assign,
233 .div_assign,
234 .rem_assign,
235 .pow_assign,
236 .and_assign,
237 .or_assign,
238 .bit_and_assign,
239 .bit_or_assign,
240 .bit_xor_assign,
241 .shl_assign,
242 .shr_assign,
243 => true,
244 .add,
245 .sub,
246 .mul,
247 .div,
248 .rem,
249 .pow,
250 .bool_and,
251 .bool_or,
252 .bit_and,
253 .bit_or,
254 .bit_xor,
255 .shl,
256 .shr,
257 .eq,
258 .ne,
259 .lt,
260 .le,
261 .gt,
262 .ge,
263 => false,
264 else => unreachable, // not a binary operator
265 };
266 }
267
268 pub fn isBasic(op: Tag) bool {
269 return switch (op) {
270 .id,
271 .string,
272 .number_int,
273 .number_float,
274 .builtin_call,
275 => true,
276 else => false,
277 };
278 }
279 };
280
281 pub const data_tags = std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
282 // sort-lines:start
283 .bool_and = .bin,
284 .@"break" = .@"break",
285 .@"comptime" = .un,
286 .@"if" = .@"if",
287 .bool_or = .bin,
288 .@"return" = .@"return",
289 .add = .bin,
290 .add_assign = .bin,
291 .and_assign = .bin,
292 .assert = .un,
293 .assign = .bin,
294 .bit_and = .bin,
295 .bit_and_assign = .bin,
296 .bit_not = .un,
297 .bit_or = .bin,
298 .bit_or_assign = .bin,
299 .bit_xor = .bin,
300 .bit_xor_assign = .bin,
301 .block_implicit_return = .list,
302 .block_void = .list,
303 .bool_not = .un,
304 .builtin_call = .list,
305 .call = .call,
306 .test_decl = .test_decl,
307 .div = .bin,
308 .div_assign = .bin,
309 .eq = .bin,
310 .field = .field,
311 .fn_decl = .function,
312 .fn_expr = .function,
313 .ge = .bin,
314 .gt = .bin,
315 .id = .none,
316 .index = .bin,
317 .label = .label,
318 .le = .bin,
319 .lt = .bin,
320 .mul = .bin,
321 .mul_assign = .bin,
322 .multiline_string = .list,
323 .ne = .bin,
324 .negate = .un,
325 .number_float = .none,
326 .number_int = .none,
327 .or_assign = .bin,
328 .param_decl = .param_decl,
329 .pow = .bin,
330 .pow_assign = .bin,
331 .pure = .un,
332 .rem = .bin,
333 .rem_assign = .bin,
334 .root = .list,
335 .shl = .bin,
336 .shl_assign = .bin,
337 .shr = .bin,
338 .shr_assign = .bin,
339 .string = .none,
340 .struct_decl = .list,
341 .array_type = .un,
342 .array_init = .list,
343 .struct_field = .un,
344 .struct_init = .struct_init,
345 .sub = .bin,
346 .sub_assign = .bin,
347 .var_decl = .var_decl,
348 .tuple_type = .list,
349 .tuple_init = .list,
350 // sort-lines:end
351 });
352
353 pub const Data = union {
354 none: struct { a: Unused = .zero, b: Unused = .zero },
355 function: struct {
356 /// A `Fn`
357 extra: ExtraIndex,
358 body: NodeIndex,
359 },
360 /// is either a node list or a token list, depending on the node's tag
361 list: SubList,
362 var_decl: struct {
363 type: OptionalNodeIndex,
364 init: NodeIndex,
365 },
366 test_decl: struct {
367 doc_test_name: OptionalNodeIndex,
368 block: NodeIndex,
369 },
370 param_decl: struct {
371 type: OptionalNodeIndex,
372 unused: Unused = .zero,
373 },
374 bin: struct {
375 lhs: NodeIndex,
376 rhs: NodeIndex,
377 },
378 field: struct {
379 base: NodeIndex,
380 field_token: TokenIndex,
381 },
382 un: struct {
383 expr: NodeIndex,
384 unused: Unused = .zero,
385 },
386 label: struct {
387 expr: NodeIndex,
388 unused: Unused = .zero,
389 },
390 @"return": struct {
391 expr: OptionalNodeIndex,
392 unused: Unused = .zero,
393 },
394 @"break": struct {
395 label: OptionalTokenIndex,
396 value: OptionalNodeIndex,
397 },
398 call: struct {
399 base: NodeIndex,
400 /// A `Call`
401 extra: ExtraIndex,
402 },
403 struct_init: struct {
404 /// .none => '.{ }'
405 /// else => 'base{ ... }'
406 base: OptionalNodeIndex,
407 /// A `SubList`
408 extra: ExtraIndex,
409 },
410 @"if": struct {
411 cond: NodeIndex,
412 /// A `If`
413 extra: ExtraIndex,
414 },
415
416 const FieldEnum = std.meta.FieldEnum(Data);
417 };
418};
419
420pub const Error = error{ OutOfMemory, ParseFail };
421
422pub const Location = struct {
423 line: Ordinal(u32),
424 column: Ordinal(u32),
425 // Byte offset of the start of the line
426 line_start: ByteOffset,
427 // Byte offset of the end of the line
428 line_end: ByteOffset,
429};
430
431pub const Span = struct {
432 start: u32,
433 end: u32,
434 main: u32,
435};
436
437// This can be removed when the following issues are implemented:
438// See: https://github.com/ziglang/zig/issues/104
439// See: https://github.com/ziglang/zig/issues/3806
440pub const OptionalNodeIndex = enum(u32) {
441 none = 0,
442 _,
443
444 pub fn init(idx: ?NodeIndex) OptionalNodeIndex {
445 assert(idx != 0);
446 return @enumFromInt(idx orelse return .none);
447 }
448
449 pub fn unwrap(idx: OptionalNodeIndex) ?NodeIndex {
450 return switch (idx) {
451 .none => null,
452 _ => @intFromEnum(idx),
453 };
454 }
455};
456
457pub const OptionalTokenIndex = enum(u32) {
458 none = 0,
459 _,
460
461 pub fn init(idx: ?TokenIndex) OptionalTokenIndex {
462 assert(idx != 0);
463 return @enumFromInt(idx orelse return .none);
464 }
465
466 pub fn unwrap(idx: OptionalTokenIndex) ?TokenIndex {
467 return switch (idx) {
468 .none => null,
469 _ => @intFromEnum(idx),
470 };
471 }
472};
473
474pub const OptionalExtraIndex = enum(u32) {
475 none = std.math.maxInt(u32),
476 _,
477
478 pub fn init(idx: ?ExtraIndex) OptionalExtraIndex {
479 assert(idx != std.math.maxInt(u32));
480 return @enumFromInt(idx orelse return .none);
481 }
482
483 pub fn unwrap(idx: OptionalExtraIndex) ?ExtraIndex {
484 return switch (idx) {
485 .none => null,
486 _ => @intFromEnum(idx),
487 };
488 }
489};
490
491pub const SubList = struct {
492 /// Either NodeIndex or TokenIndex, depending on where this came from.
493 start: u32,
494 len: u32,
495
496 pub const empty: SubList = .{ .len = 0, .start = 0 };
497
498 pub fn slice(self: SubList, ast: *const Ast) []const u32 {
499 return ast.extra[self.start..][0..self.len];
500 }
501};
502
503pub const Fn = struct {
504 params: SubList,
505 return_type: OptionalNodeIndex,
506};
507
508pub const Call = struct {
509 args: SubList,
510};
511
512pub const If = struct {
513 /// either .block_void or .block_implicit_return
514 yes: NodeIndex,
515 /// if it exists, it is either
516 /// .block_void, .block_implicit_return, or .@"if"
517 no: OptionalNodeIndex,
518};
519
520pub const full = struct {
521 pub const FnDecl = struct {
522 start_token: TokenIndex,
523 public: bool,
524 id: []const u8,
525 params: []const NodeIndex,
526 return_type: ?NodeIndex,
527 body: NodeIndex,
528 };
529
530 pub const FnExpr = struct {
531 start_token: TokenIndex,
532 params: []const NodeIndex,
533 return_type: ?NodeIndex,
534 body: NodeIndex,
535 };
536
537 pub const VarDecl = struct {
538 start_token: TokenIndex,
539 public: bool,
540 is_const: bool,
541 id: []const u8,
542 type: ?NodeIndex,
543 init: NodeIndex,
544 };
545
546 pub const StructDecl = struct {
547 start_token: TokenIndex,
548 public: bool,
549 id: []const u8,
550 fields: []const NodeIndex,
551 };
552
553 pub const StructField = struct {
554 start_token: TokenIndex,
555 id: []const u8,
556 type: NodeIndex,
557 };
558
559 pub const StructInit = struct {
560 type: ?NodeIndex,
561 fields: []const NodeIndex,
562 };
563
564 pub const ParamDecl = struct {
565 start_token: TokenIndex,
566 id: []const u8,
567 type: ?NodeIndex,
568 };
569};
570
571pub fn deinit(ast: *Ast, gpa: std.mem.Allocator) void {
572 ast.tokens.deinit(gpa);
573 ast.nodes.deinit(gpa);
574 gpa.free(ast.extra);
575 ast.* = undefined;
576}
577
578pub fn nodeTag(ast: Ast, node: NodeIndex) Node.Tag {
579 return ast.nodes.items(.tag)[node];
580}
581
582pub fn nodeData(ast: Ast, node: NodeIndex) Node.Data {
583 return ast.nodes.items(.data)[node];
584}
585
586pub fn nodeToken(ast: Ast, node: NodeIndex) TokenIndex {
587 return ast.nodes.items(.main_token)[node];
588}
589
590pub fn fullFnDecl(ast: Ast, node: NodeIndex) full.FnDecl {
591 const tok_tags = ast.tokens.items(.tag);
592
593 const main_token = ast.nodes.items(.main_token)[node];
594 const public = tok_tags[main_token -| 1] == .kw_pub;
595 assert(tok_tags[main_token] == .kw_fn);
596 assert(tok_tags[main_token + 1] == .id);
597 const id = ast.tokenSlice(main_token + 1);
598
599 const data = ast.nodes.items(.data)[node].function;
600 const extra = ast.extraData(data.extra, Fn);
601
602 return .{
603 .start_token = if (public) main_token - 1 else main_token,
604 .public = public,
605 .id = id,
606 .body = data.body,
607 .params = extra.params.slice(&ast),
608 .return_type = extra.return_type.unwrap(),
609 };
610}
611
612pub fn fullFnExpr(ast: Ast, node: NodeIndex) full.FnDecl {
613 const tok_tags = ast.tokens.items(.tag);
614
615 const main_token = ast.nodes.items(.main_token)[node];
616 const public = tok_tags[main_token -| 1] == .kw_pub;
617 assert(tok_tags[main_token] == .kw_fn);
618 const id = ast.tokenSlice(main_token + 1);
619
620 const data = ast.nodes.items(.data)[node].function;
621 const extra = ast.extraData(data.extra, Fn);
622
623 return .{
624 .start_token = if (public) main_token - 1 else main_token,
625 .public = public,
626 .id = id,
627 .body = data.body,
628 .params = extra.params.slice(&ast),
629 .return_type = extra.return_type.unwrap(),
630 };
631}
632
633pub fn fullStructDecl(ast: Ast, node: NodeIndex) full.StructDecl {
634 const tok_tags = ast.tokens.items(.tag);
635
636 const main_token = ast.nodes.items(.main_token)[node];
637 const public = tok_tags[main_token -| 1] == .kw_pub;
638 assert(tok_tags[main_token] == .kw_struct);
639 assert(tok_tags[main_token + 1] == .id);
640 const id = ast.tokenSlice(main_token + 1);
641
642 const data = ast.nodes.items(.data)[node].list;
643
644 return .{
645 .start_token = if (public) main_token - 1 else main_token,
646 .public = public,
647 .id = id,
648 .fields = data.slice(&ast),
649 };
650}
651
652pub fn fullVarDecl(ast: Ast, node: NodeIndex) full.VarDecl {
653 const tok_tags = ast.tokens.items(.tag);
654
655 const main_token = ast.nodes.items(.main_token)[node];
656 const public = tok_tags[main_token -| 1] == .kw_pub;
657 const is_const = switch (tok_tags[main_token]) {
658 .kw_let => true,
659 .kw_var => false,
660 else => |tag| {
661 if (core.runtime_safety) {
662 std.debug.panic("Expected var decl, got {s}", .{@tagName(tag)});
663 }
664 unreachable;
665 },
666 };
667 assert(tok_tags[main_token + 1] == .id);
668 const id = ast.tokenSlice(main_token + 1);
669 const data = ast.nodes.items(.data)[node].var_decl;
670 return .{
671 .start_token = if (public) main_token - 1 else main_token,
672 .public = public,
673 .is_const = is_const,
674 .id = id,
675 .type = data.type.unwrap(),
676 .init = data.init,
677 };
678}
679
680pub fn fullParamDecl(ast: Ast, node: NodeIndex) full.ParamDecl {
681 const tok_tags = ast.tokens.items(.tag);
682
683 const main_token = ast.nodes.items(.main_token)[node];
684 assert(tok_tags[main_token] == .id);
685 const id = ast.tokenSlice(main_token);
686 const data = ast.nodes.items(.data)[node].param_decl;
687 return .{
688 .start_token = main_token,
689 .id = id,
690 .type = data.type.unwrap(),
691 };
692}
693
694pub fn fullStructField(ast: Ast, node: NodeIndex) full.StructField {
695 const tok_tags = ast.tokens.items(.tag);
696
697 const main_token = ast.nodes.items(.main_token)[node];
698 assert(tok_tags[main_token] == .id);
699 const id = ast.tokenSlice(main_token);
700 const data = ast.nodes.items(.data)[node].un;
701 return .{
702 .start_token = main_token,
703 .id = id,
704 .type = data.expr,
705 };
706}
707
708pub fn fullStructInit(ast: Ast, node: NodeIndex) full.StructInit {
709 assert(ast.nodeTag(node) == .struct_init);
710 const data = ast.nodeData(node).struct_init;
711 return .{
712 .type = data.base.unwrap(),
713 .fields = ast.extraData(data.extra, SubList).slice(&ast),
714 };
715}
716
717pub fn idToken(ast: Ast, node: NodeIndex) TokenIndex {
718 return switch (ast.nodeTag(node)) {
719 .param_decl,
720 .id,
721 .label,
722 => ast.nodeToken(node),
723 .fn_decl,
724 .var_decl,
725 .struct_decl,
726 => ast.nodeToken(node) + 1,
727 else => unreachable, // does not have an id
728 };
729}
730
731pub fn nodeStatements(ast: *const Ast, node: NodeIndex) []const NodeIndex {
732 const data = ast.nodes.items(.data)[node].list;
733 return data.slice(ast);
734}
735
736pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location {
737 const token_start = self.tokens.items(.offset)[token_index];
738 return Tokenizer.tokenLocation(self.utf8_source, start_offset, token_start);
739}
740
741pub fn tokenTag(ast: Ast, token_index: TokenIndex) Token.Tag {
742 return ast.tokens.items(.tag)[token_index];
743}
744
745pub fn tokenOffset(ast: Ast, token_index: TokenIndex) u32 {
746 return ast.tokens.items(.offset)[token_index];
747}
748
749pub fn tokenSlice(ast: Ast, token_index: TokenIndex) []const u8 {
750 return ast.tokens.get(token_index).slice(ast.utf8_source);
751}
752
753pub fn basicNodeSlice(ast: Ast, node: NodeIndex) []const u8 {
754 assert(ast.nodes.items(.tag)[node].isBasic());
755 const token = ast.nodes.items(.main_token)[node];
756 return ast.tokenSlice(token);
757}
758
759pub fn fnDeclIdToken(tree: Ast, node: NodeIndex) TokenIndex {
760 assert(tree.nodeTag(node) == .fn_decl);
761 return tree.nodeToken(node) + 1;
762}
763
764pub fn extraData(tree: Ast, index: usize, comptime T: type) T {
765 const fields = std.meta.fields(T);
766 var result: T = undefined;
767 var i: usize = 0;
768 inline for (fields) |field| {
769 @field(result, field.name) = switch (field.type) {
770 NodeIndex => tree.extra[index + i],
771 SubList => SubList{
772 .start = tree.extra[index + i],
773 .len = tree.extra[index + i + 1],
774 },
775 OptionalNodeIndex => @enumFromInt(tree.extra[index + i]),
776 OptionalTokenIndex => @enumFromInt(tree.extra[index + i]),
777 OptionalExtraIndex => @enumFromInt(tree.extra[index + i]),
778 else => @compileError("ExtraData type not supported: " ++ @typeName(field.type)),
779 };
780 i += switch (field.type) {
781 NodeIndex,
782 OptionalNodeIndex,
783 OptionalTokenIndex,
784 OptionalExtraIndex,
785 => 1,
786 SubList => 2,
787 else => @compileError(unreachable),
788 };
789 }
790 return result;
791}
792
793pub fn dumpToWriter(ast: Ast, writer: anytype) !void {
794 try writer.print("{}", .{Dump{ .ast = ast }});
795}
796
797pub const Dump = @import("Ast.Dump.zig");
798
799pub const Unused = enum(u32) { zero = 0 };
800pub const TokenIndex = u32;
801pub const ExtraIndex = u32;
802pub const NodeIndex = u32;
803
804const Ast = @This();
805const std = @import("std");
806const assert = std.debug.assert;
807
808const core = @import("mod.zig");
809
810const Tokenizer = core.Tokenizer;
811const Token = Tokenizer.Token;
812const TokenList = Tokenizer.Token.List;
813const ByteOffset = Tokenizer.ByteOffset;
814
815const Report = core.Report;
816
817/// For numbers that are used both as user-visible one based numbers and zero
818/// based offsets, use this type to reduce mistakes. A common usage of this
819/// would be line and column numbers for `Report.Item`s
820pub fn Ordinal(Int: type) type {
821 comptime assert(@typeInfo(Int).int.signedness == .unsigned);
822 return enum(Int) {
823 start = 0,
824 _,
825
826 pub fn fromZeroBased(int: Int) @This() {
827 assert(int != std.math.maxInt(Int));
828 return @enumFromInt(int);
829 }
830
831 pub fn fromOneBased(int: Int) @This() {
832 assert(int > 0);
833 return @enumFromInt(int - 1);
834 }
835
836 pub fn zeroBased(ord: @This()) Int {
837 return @intFromEnum(ord);
838 }
839
840 pub fn oneBased(ord: @This()) Int {
841 return @intFromEnum(ord) + 1;
842 }
843
844 pub fn add(ord: @This(), inc: Int) @This() {
845 return fromZeroBased(ord.zeroBased() + inc);
846 }
847 };
848}