1use std::{borrow::Cow, fmt::Display, num::NonZeroU32};
2
3use chumsky::{
4 IterParser, Parser,
5 error::Rich,
6 extra,
7 input::{Input, MappedInput},
8 prelude::{choice, just, recursive},
9 select,
10};
11use device_driver_common::{
12 span::{Span, SpanExt, Spanned},
13 specifiers::{Access, AddressMode, BaseType, ByteOrder, Integer},
14};
15use device_driver_diagnostics::{Diagnostics, errors::ParsingError};
16use device_driver_lexer::Token;
17
18use crate::parse_num::{ParseIntRadix, ParseIntRadixError, ParseIntRadixErrorKind, parse_num};
19
20#[cfg(feature = "gen-docs")]
21pub mod gen_docs;
22mod parse_num;
23
24pub fn parse<'src>(tokens: &[Spanned<Token<'src>>], diagnostics: &mut Diagnostics) -> Ast<'src> {
25 let (ast, parse_errs) = node()
26 .map_with(|ast, e| (ast, e.span()))
27 .parse(
28 tokens.map(
29 tokens
30 .last()
31 .map(|t| Span::from(t.span.end..t.span.end))
32 .unwrap_or_default(),
33 |token| (&token.value, &token.span),
34 ),
35 )
36 .into_output_errors();
37
38 for error in parse_errs {
39 diagnostics.add(ParsingError {
40 reason: error.to_string(),
41 span: *error.span(),
42 });
43 }
44
45 ast.map(|(root_node, span)| Ast {
46 root_node: Some(root_node),
47 span,
48 })
49 .unwrap_or_default()
50}
51
52#[derive(Debug, Default)]
54pub struct Ast<'src> {
55 pub root_node: Option<Node<'src>>,
56 pub span: Span,
57}
58
59#[derive(Debug, Clone)]
60pub struct Node<'src> {
61 pub doc_comments: Vec<Spanned<&'src str>>,
62 pub node_type: Ident<'src>,
63 pub name: Ident<'src>,
64 pub repeat: Option<Spanned<Repeat<'src>>>,
65 pub type_specifier: Option<Spanned<TypeSpecifier<'src>>>,
66 pub short_properties: Vec<Spanned<Expression<'src>>>,
67 pub properties: Vec<Spanned<Property<'src>>>,
68 pub sub_nodes: Vec<Node<'src>>,
69 pub span: Span,
70}
71
72impl<'src> Display for Node<'src> {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 let indentation_level = f.width().unwrap_or_default();
75 let indentation = format!("{:width$}", "", width = indentation_level * 4);
76
77 for doc_comment in &self.doc_comments {
78 writeln!(
79 f,
80 "{indentation}///{}{doc_comment}",
81 if doc_comment.starts_with(" ") {
82 ""
83 } else {
84 " "
85 }
86 )?;
87 }
88 write!(f, "{indentation}{} {}", self.node_type.val, self.name.val)?;
89
90 if let Some(repeat) = self.repeat {
91 write!(f, "[{} stride {}]", repeat.source, repeat.stride)?;
92 }
93
94 for expression in self.short_properties.iter() {
95 write!(f, " {}", expression.get_human_string())?;
96 }
97
98 if let Some(type_specifier) = self.type_specifier.as_ref() {
99 write!(f, " -> {}", type_specifier.base_type)?;
100
101 if let Some(conversion) = type_specifier.conversion.as_ref() {
102 write!(f, " as")?;
103 if type_specifier.use_try {
104 write!(f, " try")?;
105 }
106
107 match conversion {
108 TypeConversion::Reference(ident) => write!(f, " {}", ident.val)?,
109 TypeConversion::Subnode(node) => {
110 if node.doc_comments.is_empty() {
111 for (i, line) in node.to_string().lines().enumerate() {
112 if i == 0 {
113 write!(f, " {line}")?;
114 } else {
115 write!(f, "\n{indentation}{line}")?;
116 }
117 }
118 } else {
119 write!(f, "\n{node:width$}", width = indentation_level + 1)?;
120 }
121 }
122 }
123 }
124 }
125
126 if !self.sub_nodes.is_empty() || !self.properties.is_empty() {
127 writeln!(f, " {{")?;
128
129 for property in self.properties.iter() {
130 for doc_comment in property.doc_comments.iter() {
131 writeln!(
132 f,
133 "{indentation} ///{}{}",
134 if doc_comment.starts_with(" ") {
135 ""
136 } else {
137 " "
138 },
139 doc_comment
140 )?;
141 }
142
143 write!(f, "{indentation} {}:", property.name.val)?;
144
145 let expression = property.expression.get_human_string();
146
147 if expression.starts_with("///") {
148 for line in expression.lines() {
149 write!(f, "\n{indentation} {line}")?;
150 }
151 } else {
152 for (i, line) in expression.lines().enumerate() {
153 if i == 0 {
154 write!(f, " {line}")?;
155 } else {
156 write!(f, "\n{indentation} {line}")?;
157 }
158 }
159 }
160
161 writeln!(f, ",")?;
162 }
163
164 if !self.properties.is_empty() && !self.sub_nodes.is_empty() {
165 writeln!(f, "{indentation}",)?;
166 }
167
168 for node in self.sub_nodes.iter() {
169 writeln!(f, "{node:width$},", width = indentation_level + 1)?;
170 }
171
172 write!(f, "{indentation}}}")?;
173 }
174
175 Ok(())
176 }
177}
178
179#[derive(Debug, Clone)]
180pub struct TypeSpecifier<'src> {
181 pub base_type: Spanned<BaseType>,
182 pub use_try: bool,
183 pub conversion: Option<TypeConversion<'src>>,
184}
185
186#[derive(Debug, Clone)]
187pub enum TypeConversion<'src> {
188 Reference(Ident<'src>),
189 Subnode(Box<Node<'src>>),
190}
191
192#[derive(Debug, Clone)]
193pub struct Property<'src> {
194 pub doc_comments: Vec<Spanned<&'src str>>,
195 pub name: Ident<'src>,
196 pub expression: Spanned<Expression<'src>>,
197}
198
199#[derive(Debug, Clone)]
200pub enum Expression<'src> {
201 AddressRange { end: i128, start: i128 },
202 ByteArray(Vec<u8>),
203 BaseType(BaseType),
204 Integer(Integer),
205 Allow,
206 Number(i128),
207 DefaultNumber(Option<i128>),
208 CatchAllNumber(Option<i128>),
209 String(&'src str),
210 Access(Access),
211 ByteOrder(ByteOrder),
212 TypeReference(Ident<'src>),
213 SubNode(Box<Node<'src>>),
214 Auto,
215 AddressMode(AddressMode),
216 Error,
217}
218
219impl<'src> Expression<'src> {
220 pub fn as_range(&self) -> Option<(i128, i128)> {
221 if let Self::AddressRange { end, start } = self {
222 Some((*end, *start))
223 } else {
224 None
225 }
226 }
227
228 pub fn as_byte_order(&self) -> Option<ByteOrder> {
229 if let Self::ByteOrder(v) = self {
230 Some(*v)
231 } else {
232 None
233 }
234 }
235
236 pub fn as_access(&self) -> Option<Access> {
237 if let Self::Access(v) = self {
238 Some(*v)
239 } else {
240 None
241 }
242 }
243
244 pub fn as_integer(&self) -> Option<Integer> {
245 if let Self::Integer(v) = self {
246 Some(*v)
247 } else {
248 None
249 }
250 }
251
252 pub fn as_unsigned_integer(&self) -> Option<Integer> {
253 if let Self::Integer(v) = self {
254 Some(*v)
255 } else {
256 None
257 }
258 }
259
260 pub fn as_number(&self) -> Option<i128> {
261 if let Self::Number(v) = self {
262 Some(*v)
263 } else {
264 None
265 }
266 }
267
268 pub fn as_string(&self) -> Option<&'src str> {
269 if let Self::String(v) = self {
270 Some(*v)
271 } else {
272 None
273 }
274 }
275
276 pub fn as_address_mode(&self) -> Option<AddressMode> {
277 if let Self::AddressMode(v) = self {
278 Some(*v)
279 } else {
280 None
281 }
282 }
283
284 pub fn get_human_string(&self) -> Cow<'static, str> {
285 match self {
286 Expression::AddressRange { end, start } => format!("{end}:{start}").into(),
287 Expression::ByteArray(items) => format!("{items:?}").into(),
288 Expression::BaseType(base_type) => base_type.to_string().into(),
289 Expression::Integer(integer) => integer.to_string().into(),
290 Expression::Allow => "allow".into(),
291 Expression::Number(num) => num.to_string().into(),
292 Expression::DefaultNumber(Some(num)) => format!("default {num}").into(),
293 Expression::DefaultNumber(None) => "default _".into(),
294 Expression::CatchAllNumber(Some(num)) => format!("catch-all {num}").into(),
295 Expression::CatchAllNumber(None) => "catch-all _".into(),
296 Expression::String(val) => format!("\"{val}\"").into(),
297 Expression::Access(val) => val.to_string().into(),
298 Expression::ByteOrder(val) => val.to_string().into(),
299 Expression::TypeReference(ident) => ident.val.to_string().into(),
300 Expression::SubNode(val) => val.to_string().into(),
301 Expression::Auto => "_".into(),
302 Expression::AddressMode(val) => val.to_string().into(),
303 Expression::Error => "ERROR".into(),
304 }
305 }
306}
307
308impl<'src> Display for Expression<'src> {
309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310 match self {
311 Expression::AddressRange { .. } => write!(f, "range"),
312 Expression::ByteArray(_) => write!(f, "[bytes]"),
313 Expression::BaseType(_) => write!(f, "base type"),
314 Expression::Integer(_) => write!(f, "integer type"),
315 Expression::Allow => write!(f, "allow"),
316 Expression::Number(_) => write!(f, "number"),
317 Expression::DefaultNumber(None) => write!(f, "default auto"),
318 Expression::CatchAllNumber(None) => write!(f, "catch-all auto"),
319 Expression::DefaultNumber(Some(_)) => write!(f, "default number"),
320 Expression::CatchAllNumber(Some(_)) => write!(f, "catch-all number"),
321 Expression::String(_) => write!(f, "string"),
322 Expression::Access(_) => write!(f, "access specifier"),
323 Expression::ByteOrder(_) => write!(f, "byte order"),
324 Expression::TypeReference(_) => write!(f, "type reference"),
325 Expression::SubNode(_) => write!(f, "sub node"),
326 Expression::Auto => write!(f, "auto"),
327 Expression::AddressMode(_) => write!(f, "address mode"),
328 Expression::Error => write!(f, "error"),
329 }
330 }
331}
332
333#[derive(Debug, Clone, Copy, Default)]
334pub struct Repeat<'src> {
335 pub source: Spanned<RepeatSource<'src>>,
336 pub stride: Spanned<i32>,
337}
338
339#[derive(Debug, Clone, Copy)]
340pub enum RepeatSource<'src> {
341 Count(NonZeroU32),
342 Enum(Ident<'src>),
343}
344
345impl<'src> Default for RepeatSource<'src> {
346 fn default() -> Self {
347 Self::Count(1.try_into().unwrap())
348 }
349}
350
351impl Display for RepeatSource<'_> {
352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353 match self {
354 RepeatSource::Count(non_zero) => write!(f, "{non_zero}"),
355 RepeatSource::Enum(ident) => write!(f, "{}", ident.val),
356 }
357 }
358}
359
360#[derive(Debug, Clone, Copy)]
361pub struct Ident<'src> {
362 pub val: &'src str,
363 pub span: Span,
364 is_auto: bool,
365}
366
367impl<'src> Ident<'src> {
368 pub const fn new(val: &'src str, span: Span) -> Self {
369 Self {
370 val,
371 span,
372 is_auto: false,
373 }
374 }
375
376 pub const fn new_no_span(val: &'src str) -> Self {
377 Self {
378 val,
379 span: Span::empty(),
380 is_auto: false,
381 }
382 }
383
384 pub const fn new_auto(span: Span) -> Self {
385 Self {
386 val: "_",
387 span,
388 is_auto: true,
389 }
390 }
391
392 pub fn is_auto(&self) -> bool {
394 self.is_auto
395 }
396}
397
398fn try_num<'tokens, 'src: 'tokens, I: ParseIntRadix>(
399 num_str: &'src str,
400 span: Span,
401) -> Result<I, RichErr<'tokens, 'src>> {
402 match parse_num::<I>(num_str) {
403 Ok(num) => Ok(num),
404 Err(ParseIntRadixError {
405 source,
406 kind,
407 target_bits,
408 target_signed,
409 }) => match kind {
410 ParseIntRadixErrorKind::Overflow => Err(Rich::custom(
411 span,
412 format!(
413 "number `{source}` is parsed as a {}{target_bits}, but overflows.",
414 if target_signed { 'i' } else { 'u' }
415 ),
416 )),
417 ParseIntRadixErrorKind::Underflow => Err(Rich::custom(
418 span,
419 format!(
420 "number `{source}` is parsed as a {}{target_bits}, but underflows.",
421 if target_signed { 'i' } else { 'u' }
422 ),
423 )),
424 ParseIntRadixErrorKind::Empty => Err(Rich::custom(
425 span,
426 format!("could not parse `{source}` as a number because it contains no numbers"),
427 )),
428 ParseIntRadixErrorKind::Zero => {
429 Err(Rich::custom(span, "number can't be 0 in this position"))
430 }
431 },
432 }
433}
434
435pub type InputType<'tokens, 'src> =
436 MappedInput<'tokens, Token<'src>, Span, &'tokens [Spanned<Token<'src>>]>;
437pub type RichErr<'tokens, 'src> = Rich<'tokens, Token<'src>, Span>;
438pub type RichExtra<'tokens, 'src> = extra::Err<RichErr<'tokens, 'src>>;
439
440pub fn ident<'tokens, 'src: 'tokens>(
441 allow_auto: bool,
442) -> impl Parser<'tokens, InputType<'tokens, 'src>, Ident<'src>, RichExtra<'tokens, 'src>> + Clone {
443 select! {
444 Token::Ident(val) = e => Ident::new(val, e.span()),
445 Token::Underscore = e if allow_auto => Ident::new_auto(e.span()),
446 }
447 .labelled(format!(
448 "Ident{}",
449 if allow_auto { "|Underscore" } else { "" }
450 ))
451 .as_terminal()
452}
453
454pub fn doc_comment<'tokens, 'src: 'tokens>()
455-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<&'src str>, RichExtra<'tokens, 'src>> + Copy
456{
457 select! {
458 Token::DocCommentLine(val) => val
459 }
460 .map_with(|line, extra| line.spanned(extra.span()))
461 .labelled("DocCommentLine")
462 .as_terminal()
463}
464
465pub fn num<'tokens, 'src: 'tokens, I: ParseIntRadix>()
466-> impl Parser<'tokens, InputType<'tokens, 'src>, I, RichExtra<'tokens, 'src>> + Clone {
467 select! {
468 Token::Num(num) => num
469 }
470 .try_map(try_num::<I>)
471 .labelled(format!(
472 "Num<{}>",
473 std::any::type_name::<I>().split("::").last().unwrap()
475 ))
476 .as_terminal()
477}
478
479pub fn range<'tokens, 'src: 'tokens>()
480-> impl Parser<'tokens, InputType<'tokens, 'src>, Expression<'src>, RichExtra<'tokens, 'src>> + Clone
481{
482 num::<i128>()
483 .then_ignore(just(Token::Colon))
484 .then(num::<i128>())
485 .map(|(end, start)| Expression::AddressRange { end, start })
486 .labelled("range")
487}
488
489pub fn base_type<'tokens, 'src: 'tokens>()
490-> impl Parser<'tokens, InputType<'tokens, 'src>, BaseType, RichExtra<'tokens, 'src>> + Copy {
491 select! { Token::BaseType(bt) => bt }
492 .labelled("BaseType")
493 .as_terminal()
494}
495
496pub fn integer<'tokens, 'src: 'tokens>()
497-> impl Parser<'tokens, InputType<'tokens, 'src>, Integer, RichExtra<'tokens, 'src>> + Copy {
498 select! { Token::Integer(i) => i }
499 .labelled("Integer")
500 .as_terminal()
501}
502
503pub fn byte_array<'tokens, 'src: 'tokens>()
504-> impl Parser<'tokens, InputType<'tokens, 'src>, Expression<'src>, RichExtra<'tokens, 'src>> + Clone
505{
506 num::<u8>()
507 .separated_by(just(Token::Comma))
508 .collect::<Vec<_>>()
509 .map(Expression::ByteArray)
510 .then_ignore(just(Token::Comma).or_not())
511 .delimited_by(just(Token::BracketOpen), just(Token::BracketClose))
512 .labelled("byte-array")
513}
514
515pub fn simple_expression<'tokens, 'src: 'tokens>()
517-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Expression<'src>>, RichExtra<'tokens, 'src>>
518+ Clone {
519 choice((
520 range().labelled("range").as_non_terminal(),
521 base_type().map(Expression::BaseType),
522 integer().map(Expression::Integer),
523 num::<i128>().map(Expression::Number),
524 just(Token::Default)
525 .ignore_then(
526 num::<i128>()
527 .map(Some)
528 .or(just(Token::Underscore).map(|_| None)),
529 )
530 .map(Expression::DefaultNumber)
531 .labelled("default-number"),
532 just(Token::CatchAll)
533 .ignore_then(
534 num::<i128>()
535 .map(Some)
536 .or(just(Token::Underscore).map(|_| None)),
537 )
538 .map(Expression::CatchAllNumber)
539 .labelled("catch-all-number"),
540 byte_array().labelled("byte-array").as_non_terminal(),
541 just(Token::Allow).map(|_| Expression::Allow),
542 select! { Token::Access(val) => val }
543 .map(Expression::Access)
544 .labelled("Access")
545 .as_terminal(),
546 select! { Token::ByteOrder(val) => val }
547 .map(Expression::ByteOrder)
548 .labelled("ByteOrder")
549 .as_terminal(),
550 just(Token::Underscore).map(|_| Expression::Auto),
551 select! { Token::String(val) => val }
552 .map(Expression::String)
553 .labelled("String")
554 .as_terminal(),
555 select! { Token::AddressMode(val) => val }
556 .map(Expression::AddressMode)
557 .labelled("AddressMode")
558 .as_terminal(),
559 ))
560 .map_with(|expression, extra| expression.spanned(extra.span()))
561 .labelled("simple-expression")
562}
563
564pub fn repeat<'tokens, 'src: 'tokens>()
565-> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Repeat<'src>>, RichExtra<'tokens, 'src>>
566+ Clone {
567 choice((
568 num::<NonZeroU32>().map(RepeatSource::Count),
569 ident(false).map(RepeatSource::Enum),
570 ))
571 .map_with(|repeat_source, extra| repeat_source.with_span(extra.span()))
572 .then(
573 just(Token::Stride)
574 .ignore_then(num::<i32>().map_with(|num, extra| num.with_span(extra.span()))),
575 )
576 .delimited_by(just(Token::BracketOpen), just(Token::BracketClose))
577 .map_with(|(source, stride), extra| Repeat { source, stride }.spanned(extra.span()))
578 .labelled("repeat")
579}
580
581pub fn property<'tokens, 'src: 'tokens, 'node>(
582 node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
583) -> impl Parser<'tokens, InputType<'tokens, 'src>, Spanned<Property<'src>>, RichExtra<'tokens, 'src>>
584+ Clone {
585 doc_comment()
586 .repeated()
587 .collect()
588 .then(
589 ident(false)
590 .then(
591 just(Token::Colon).ignore_then(choice((
592 simple_expression()
593 .labelled("simple-expression")
594 .as_non_terminal(),
595 node.clone()
596 .map_with(|node, extra| {
597 Expression::SubNode(Box::new(node)).spanned(extra.span())
598 })
599 .labelled("node")
600 .as_non_terminal(),
601 ident(false)
602 .map(Expression::TypeReference)
603 .map_with(|expression, extra| expression.spanned(extra.span())),
604 ))),
605 )
606 .map_with(|(name, expression), extra| {
607 Property {
608 doc_comments: Vec::new(),
609 name,
610 expression,
611 }
612 .spanned(extra.span())
613 }),
614 )
615 .map(|(docs, mut prop)| {
616 prop.doc_comments = docs;
617 prop
618 })
619 .labelled("property")
620}
621
622pub fn type_specifier<'tokens, 'src: 'tokens>(
623 node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
624) -> impl Parser<
625 'tokens,
626 InputType<'tokens, 'src>,
627 Spanned<TypeSpecifier<'src>>,
628 RichExtra<'tokens, 'src>,
629> + Clone {
630 let type_conversion = just(Token::As).ignore_then(just(Token::Try).or_not()).then(
631 node.labelled("node")
632 .as_non_terminal()
633 .map(|node| TypeConversion::Subnode(Box::new(node)))
634 .or(ident(false).map(TypeConversion::Reference)),
635 );
636 just(Token::Arrow)
637 .ignore_then(
638 choice((
639 base_type(),
640 integer().map(BaseType::FixedSize),
641 just(Token::Underscore).map(|_| BaseType::Unspecified),
642 ))
643 .map_with(|b, e| b.spanned(e.span())),
644 )
645 .then(type_conversion.or_not())
646 .map(|(base_type, conversion)| TypeSpecifier {
647 base_type,
648 use_try: conversion
649 .as_ref()
650 .map(|(try_token, _)| try_token.is_some())
651 .unwrap_or_default(),
652 conversion: conversion.map(|(_, conversion)| conversion),
653 })
654 .map_with(|ts, e| ts.spanned(e.span()))
655 .labelled("type-specifier")
656}
657
658pub fn node_body<'tokens, 'src: 'tokens>(
659 node: impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone,
660) -> impl Parser<
661 'tokens,
662 InputType<'tokens, 'src>,
663 (Vec<Spanned<Property<'src>>>, Vec<Node<'src>>),
664 RichExtra<'tokens, 'src>,
665> + Clone {
666 let properties = property(node.clone())
667 .labelled("property")
668 .as_non_terminal()
669 .separated_by(just(Token::Comma))
670 .at_least(1)
671 .collect::<Vec<_>>();
672 let nodes = node
673 .labelled("node")
674 .as_non_terminal()
675 .separated_by(just(Token::Comma))
676 .at_least(1)
677 .collect::<Vec<_>>();
678
679 choice((
681 properties
683 .clone()
684 .then_ignore(just(Token::Comma))
685 .then(nodes.clone()),
686 properties
688 .clone()
689 .map(|properties| (properties, Vec::new())),
690 nodes.map(|nodes| (Vec::new(), nodes)),
692 ))
693 .then_ignore(just(Token::Comma).or_not())
694 .or_not()
695 .map(|body| body.unwrap_or_default())
696 .delimited_by(just(Token::CurlyOpen), just(Token::CurlyClose))
697 .labelled("node-body")
698}
699
700pub fn node<'tokens, 'src: 'tokens>()
701-> impl Parser<'tokens, InputType<'tokens, 'src>, Node<'src>, RichExtra<'tokens, 'src>> + Clone {
702 recursive(|node| {
703 let node = node.labelled("node").as_non_terminal();
704
705 doc_comment()
706 .repeated()
707 .collect()
708 .then(ident(false).labelled("node-type"))
709 .then(ident(true).labelled("node-name"))
710 .then(repeat().labelled("repeat").as_non_terminal().or_not())
711 .then(
712 simple_expression()
713 .labelled("simple-expression")
714 .as_non_terminal()
715 .repeated()
716 .collect::<Vec<_>>(),
717 )
718 .then(
719 type_specifier(node.clone())
720 .labelled("type-specifier")
721 .as_non_terminal()
722 .or_not(),
723 )
724 .then(
725 node_body(node.clone())
726 .labelled("node-body")
727 .as_non_terminal()
728 .or_not(),
729 )
730 .map_with(
731 |(
732 (((((doc_comments, node_type), name), repeat), expressions), type_specifier),
733 body,
734 ),
735 extra| {
736 let (properties, sub_nodes) = body.unwrap_or_default();
737
738 let mut span: Span = extra.span();
739 span = span.start_from(node_type.span);
740
741 Node {
742 doc_comments,
743 node_type,
744 name,
745 repeat,
746 type_specifier,
747 properties,
748 short_properties: expressions,
749 sub_nodes,
750 span,
751 }
752 },
753 )
754 .labelled("node")
755 })
756}