tree_sitter_graph/
ast.rs

1// -*- coding: utf-8 -*-
2// ------------------------------------------------------------------------------------------------
3// Copyright © 2021, tree-sitter authors.
4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6// ------------------------------------------------------------------------------------------------
7
8//! Defines the AST structure of a graph DSL file
9
10use regex::Regex;
11use std::collections::HashMap;
12use std::collections::HashSet;
13use std::fmt;
14use tree_sitter::CaptureQuantifier;
15use tree_sitter::Language;
16use tree_sitter::Query;
17
18use crate::parser::Range;
19use crate::Identifier;
20use crate::Location;
21
22/// A graph DSL file
23#[derive(Debug)]
24pub struct File {
25    pub language: Language,
26    /// The expected global variables used in this file
27    pub globals: Vec<Global>,
28    /// The scoped variables that are inherited by child nodes
29    pub inherited_variables: HashSet<Identifier>,
30    /// The combined query of all stanzas in the file
31    pub query: Option<Query>,
32    /// The list of stanzas in the file
33    pub stanzas: Vec<Stanza>,
34    /// Attribute shorthands defined in the file
35    pub shorthands: AttributeShorthands,
36}
37
38impl File {
39    pub fn new(language: Language) -> File {
40        File {
41            language,
42            globals: Vec::new(),
43            inherited_variables: HashSet::new(),
44            query: None,
45            stanzas: Vec::new(),
46            shorthands: AttributeShorthands::new(),
47        }
48    }
49}
50
51/// A global variable
52#[derive(Debug, Eq, PartialEq)]
53pub struct Global {
54    /// The name of the global variable
55    pub name: Identifier,
56    /// The quantifier of the global variable
57    pub quantifier: CaptureQuantifier,
58    /// Default value
59    pub default: Option<String>,
60    pub location: Location,
61}
62
63/// One stanza within a file
64#[derive(Debug)]
65pub struct Stanza {
66    /// The tree-sitter query for this stanza
67    pub query: Query,
68    /// The list of statements in the stanza
69    pub statements: Vec<Statement>,
70    /// Capture index of the full match in the stanza query
71    pub full_match_stanza_capture_index: usize,
72    /// Capture index of the full match in the file query
73    pub full_match_file_capture_index: usize,
74    pub range: Range,
75}
76
77/// A statement that can appear in a graph DSL stanza
78#[derive(Debug, Eq, PartialEq)]
79pub enum Statement {
80    // Variables
81    DeclareImmutable(DeclareImmutable),
82    DeclareMutable(DeclareMutable),
83    Assign(Assign),
84    // Graph nodes
85    CreateGraphNode(CreateGraphNode),
86    AddGraphNodeAttribute(AddGraphNodeAttribute),
87    // Edges
88    CreateEdge(CreateEdge),
89    AddEdgeAttribute(AddEdgeAttribute),
90    // Regular expression
91    Scan(Scan),
92    // Debugging
93    Print(Print),
94    // If
95    If(If),
96    // ForIn
97    ForIn(ForIn),
98}
99
100impl std::fmt::Display for Statement {
101    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
102        match self {
103            Self::DeclareImmutable(stmt) => stmt.fmt(f),
104            Self::DeclareMutable(stmt) => stmt.fmt(f),
105            Self::Assign(stmt) => stmt.fmt(f),
106            Self::CreateGraphNode(stmt) => stmt.fmt(f),
107            Self::AddGraphNodeAttribute(stmt) => stmt.fmt(f),
108            Self::CreateEdge(stmt) => stmt.fmt(f),
109            Self::AddEdgeAttribute(stmt) => stmt.fmt(f),
110            Self::Scan(stmt) => stmt.fmt(f),
111            Self::Print(stmt) => stmt.fmt(f),
112            Self::If(stmt) => stmt.fmt(f),
113            Self::ForIn(stmt) => stmt.fmt(f),
114        }
115    }
116}
117
118/// An `attr` statement that adds an attribute to an edge
119#[derive(Debug, Eq, PartialEq)]
120pub struct AddEdgeAttribute {
121    pub source: Expression,
122    pub sink: Expression,
123    pub attributes: Vec<Attribute>,
124    pub location: Location,
125}
126
127impl From<AddEdgeAttribute> for Statement {
128    fn from(statement: AddEdgeAttribute) -> Statement {
129        Statement::AddEdgeAttribute(statement)
130    }
131}
132
133impl std::fmt::Display for AddEdgeAttribute {
134    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
135        write!(f, "attr ({} -> {})", self.source, self.sink)?;
136        for attr in &self.attributes {
137            write!(f, " {}", attr)?;
138        }
139        write!(f, " at {}", self.location)
140    }
141}
142
143/// An `attr` statement that adds an attribute to a graph node
144#[derive(Debug, Eq, PartialEq)]
145pub struct AddGraphNodeAttribute {
146    pub node: Expression,
147    pub attributes: Vec<Attribute>,
148    pub location: Location,
149}
150
151impl From<AddGraphNodeAttribute> for Statement {
152    fn from(statement: AddGraphNodeAttribute) -> Statement {
153        Statement::AddGraphNodeAttribute(statement)
154    }
155}
156
157impl std::fmt::Display for AddGraphNodeAttribute {
158    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
159        write!(f, "attr ({})", self.node)?;
160        for attr in &self.attributes {
161            write!(f, " {}", attr)?;
162        }
163        write!(f, " at {}", self.location)
164    }
165}
166
167/// A `set` statement that updates the value of a mutable variable
168#[derive(Debug, Eq, PartialEq)]
169pub struct Assign {
170    pub variable: Variable,
171    pub value: Expression,
172    pub location: Location,
173}
174
175impl From<Assign> for Statement {
176    fn from(statement: Assign) -> Statement {
177        Statement::Assign(statement)
178    }
179}
180
181impl std::fmt::Display for Assign {
182    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
183        write!(
184            f,
185            "set {} = {} at {}",
186            self.variable, self.value, self.location,
187        )
188    }
189}
190
191/// The name and value of an attribute
192#[derive(Debug, Eq, PartialEq)]
193pub struct Attribute {
194    pub name: Identifier,
195    pub value: Expression,
196}
197
198impl std::fmt::Display for Attribute {
199    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
200        write!(f, "{} = {}", self.name, self.value)
201    }
202}
203
204/// An `edge` statement that creates a new edge
205#[derive(Debug, Eq, PartialEq)]
206pub struct CreateEdge {
207    pub source: Expression,
208    pub sink: Expression,
209    pub location: Location,
210}
211
212impl From<CreateEdge> for Statement {
213    fn from(statement: CreateEdge) -> Statement {
214        Statement::CreateEdge(statement)
215    }
216}
217
218impl std::fmt::Display for CreateEdge {
219    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
220        write!(
221            f,
222            "edge {} -> {} at {}",
223            self.source, self.sink, self.location,
224        )
225    }
226}
227
228/// A `node` statement that creates a new graph node
229#[derive(Debug, Eq, PartialEq)]
230pub struct CreateGraphNode {
231    pub node: Variable,
232    pub location: Location,
233}
234
235impl From<CreateGraphNode> for Statement {
236    fn from(statement: CreateGraphNode) -> Statement {
237        Statement::CreateGraphNode(statement)
238    }
239}
240
241impl std::fmt::Display for CreateGraphNode {
242    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
243        write!(f, "node {} at {}", self.node, self.location)
244    }
245}
246
247/// A `let` statement that declares a new immutable variable
248#[derive(Debug, Eq, PartialEq)]
249pub struct DeclareImmutable {
250    pub variable: Variable,
251    pub value: Expression,
252    pub location: Location,
253}
254
255impl From<DeclareImmutable> for Statement {
256    fn from(statement: DeclareImmutable) -> Statement {
257        Statement::DeclareImmutable(statement)
258    }
259}
260
261impl std::fmt::Display for DeclareImmutable {
262    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
263        write!(
264            f,
265            "let {} = {} at {}",
266            self.variable, self.value, self.location,
267        )
268    }
269}
270
271/// A `var` statement that declares a new mutable variable
272#[derive(Debug, Eq, PartialEq)]
273pub struct DeclareMutable {
274    pub variable: Variable,
275    pub value: Expression,
276    pub location: Location,
277}
278
279impl From<DeclareMutable> for Statement {
280    fn from(statement: DeclareMutable) -> Statement {
281        Statement::DeclareMutable(statement)
282    }
283}
284
285impl std::fmt::Display for DeclareMutable {
286    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
287        write!(
288            f,
289            "var {} = {} at {}",
290            self.variable, self.value, self.location,
291        )
292    }
293}
294
295/// A `print` statement that prints out some debugging information
296#[derive(Debug, Eq, PartialEq)]
297pub struct Print {
298    pub values: Vec<Expression>,
299    pub location: Location,
300}
301
302impl From<Print> for Statement {
303    fn from(statement: Print) -> Statement {
304        Statement::Print(statement)
305    }
306}
307
308impl std::fmt::Display for Print {
309    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
310        write!(f, "print")?;
311        for val in &self.values {
312            write!(f, " {},", val)?;
313        }
314        write!(f, " at {}", self.location)
315    }
316}
317
318/// A `scan` statement that matches regular expressions against a string
319#[derive(Debug, Eq, PartialEq)]
320pub struct Scan {
321    pub value: Expression,
322    pub arms: Vec<ScanArm>,
323    pub location: Location,
324}
325
326impl From<Scan> for Statement {
327    fn from(statement: Scan) -> Statement {
328        Statement::Scan(statement)
329    }
330}
331
332impl std::fmt::Display for Scan {
333    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
334        write!(f, "scan {} {{ ... }} at {}", self.value, self.location)
335    }
336}
337
338/// One arm of a `scan` statement
339#[derive(Debug)]
340pub struct ScanArm {
341    pub regex: Regex,
342    pub statements: Vec<Statement>,
343    pub location: Location,
344}
345
346impl Eq for ScanArm {}
347
348impl PartialEq for ScanArm {
349    fn eq(&self, other: &ScanArm) -> bool {
350        self.regex.as_str() == other.regex.as_str() && self.statements == other.statements
351    }
352}
353
354impl std::fmt::Display for ScanArm {
355    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
356        write!(f, "{:?} {{ ... }}", self.regex.as_str())
357    }
358}
359
360/// A `cond` conditional statement that selects the first branch with a matching condition
361#[derive(Debug, Eq, PartialEq)]
362pub struct If {
363    pub arms: Vec<IfArm>,
364    pub location: Location,
365}
366
367impl From<If> for Statement {
368    fn from(statement: If) -> Statement {
369        Statement::If(statement)
370    }
371}
372
373impl std::fmt::Display for If {
374    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
375        let mut first = true;
376        for arm in &self.arms {
377            if first {
378                first = false;
379                write!(f, "if {} {{ ... }}", DisplayConditions(&arm.conditions))?;
380            } else {
381                if !arm.conditions.is_empty() {
382                    write!(f, " elif {} {{ ... }}", DisplayConditions(&arm.conditions))?;
383                } else {
384                    write!(f, " else {{ ... }}")?;
385                }
386            }
387        }
388        write!(f, " at {}", self.location)
389    }
390}
391
392/// One arm of a `cond` statement
393#[derive(Debug, PartialEq, Eq)]
394pub struct IfArm {
395    pub conditions: Vec<Condition>,
396    pub statements: Vec<Statement>,
397    pub location: Location,
398}
399
400struct DisplayConditions<'a>(&'a Vec<Condition>);
401
402#[derive(Debug, PartialEq, Eq)]
403pub enum Condition {
404    Some {
405        value: Expression,
406        location: Location,
407    },
408    None {
409        value: Expression,
410        location: Location,
411    },
412    Bool {
413        value: Expression,
414        location: Location,
415    },
416}
417
418impl std::fmt::Display for DisplayConditions<'_> {
419    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
420        let mut first = true;
421        for condition in self.0.iter() {
422            if first {
423                first = false;
424                write!(f, "{}", condition)?;
425            } else {
426                write!(f, ", {}", condition)?;
427            }
428        }
429        Ok(())
430    }
431}
432
433impl std::fmt::Display for Condition {
434    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
435        match self {
436            Condition::Some { value, .. } => {
437                write!(f, "some {}", value)
438            }
439            Condition::None { value, .. } => {
440                write!(f, "none {}", value)
441            }
442            Condition::Bool { value, .. } => {
443                write!(f, "{}", value)
444            }
445        }
446    }
447}
448
449/// A `for in` statement
450#[derive(Debug, Eq, PartialEq)]
451pub struct ForIn {
452    pub variable: UnscopedVariable,
453    pub value: Expression,
454    pub statements: Vec<Statement>,
455    pub location: Location,
456}
457
458impl From<ForIn> for Statement {
459    fn from(statement: ForIn) -> Statement {
460        Statement::ForIn(statement)
461    }
462}
463
464impl std::fmt::Display for ForIn {
465    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
466        write!(
467            f,
468            "for {} in {} {{ ... }} at {}",
469            self.variable, self.value, self.location,
470        )
471    }
472}
473
474/// A reference to a variable
475#[derive(Debug, Eq, PartialEq)]
476pub enum Variable {
477    Scoped(ScopedVariable),
478    Unscoped(UnscopedVariable),
479}
480
481impl std::fmt::Display for Variable {
482    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
483        match self {
484            Variable::Scoped(variable) => variable.fmt(f),
485            Variable::Unscoped(variable) => variable.fmt(f),
486        }
487    }
488}
489
490/// A reference to a scoped variable
491#[derive(Debug, Eq, PartialEq)]
492pub struct ScopedVariable {
493    pub scope: Box<Expression>,
494    pub name: Identifier,
495    pub location: Location,
496}
497
498impl From<ScopedVariable> for Variable {
499    fn from(variable: ScopedVariable) -> Variable {
500        Variable::Scoped(variable)
501    }
502}
503
504impl std::fmt::Display for ScopedVariable {
505    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
506        write!(f, "{}.{}", self.scope, self.name)
507    }
508}
509
510/// A reference to a global or local variable
511#[derive(Debug, Eq, PartialEq)]
512pub struct UnscopedVariable {
513    pub name: Identifier,
514    pub location: Location,
515}
516
517impl From<UnscopedVariable> for Variable {
518    fn from(variable: UnscopedVariable) -> Variable {
519        Variable::Unscoped(variable)
520    }
521}
522
523impl std::fmt::Display for UnscopedVariable {
524    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
525        write!(f, "{}", self.name)
526    }
527}
528
529/// An expression that can appear in a graph DSL file
530#[derive(Debug, Eq, PartialEq)]
531pub enum Expression {
532    // Literals
533    FalseLiteral,
534    NullLiteral,
535    TrueLiteral,
536    // Constants
537    IntegerConstant(IntegerConstant),
538    StringConstant(StringConstant),
539    // Literals
540    ListLiteral(ListLiteral),
541    SetLiteral(SetLiteral),
542    // Comprehensions
543    ListComprehension(ListComprehension),
544    SetComprehension(SetComprehension),
545    // Syntax nodes
546    Capture(Capture),
547    // Variables
548    Variable(Variable),
549    // Functions
550    Call(Call),
551    // Regular expression
552    RegexCapture(RegexCapture),
553}
554
555impl std::fmt::Display for Expression {
556    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
557        match self {
558            Expression::FalseLiteral => write!(f, "false"),
559            Expression::NullLiteral => write!(f, "#null"),
560            Expression::TrueLiteral => write!(f, "true"),
561            Expression::IntegerConstant(expr) => expr.fmt(f),
562            Expression::StringConstant(expr) => expr.fmt(f),
563            Expression::ListLiteral(expr) => expr.fmt(f),
564            Expression::SetLiteral(expr) => expr.fmt(f),
565            Expression::ListComprehension(expr) => expr.fmt(f),
566            Expression::SetComprehension(expr) => expr.fmt(f),
567            Expression::Capture(expr) => expr.fmt(f),
568            Expression::Variable(expr) => expr.fmt(f),
569            Expression::Call(expr) => expr.fmt(f),
570            Expression::RegexCapture(expr) => expr.fmt(f),
571        }
572    }
573}
574
575/// A function call
576#[derive(Debug, Eq, PartialEq)]
577pub struct Call {
578    pub function: Identifier,
579    pub parameters: Vec<Expression>,
580}
581
582impl From<Call> for Expression {
583    fn from(expr: Call) -> Expression {
584        Expression::Call(expr)
585    }
586}
587
588impl std::fmt::Display for Call {
589    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
590        write!(f, "({}", self.function)?;
591        for arg in &self.parameters {
592            write!(f, " {}", arg)?;
593        }
594        write!(f, ")")
595    }
596}
597
598/// A capture expression that references a syntax node
599#[derive(Debug, Eq, PartialEq)]
600pub struct Capture {
601    /// The name of the capture
602    pub name: Identifier,
603    /// The suffix of the capture
604    pub quantifier: CaptureQuantifier,
605    /// Capture index in the merged file query
606    pub file_capture_index: usize,
607    /// Capture index in the stanza query
608    pub stanza_capture_index: usize,
609    pub location: Location,
610}
611
612impl From<Capture> for Expression {
613    fn from(expr: Capture) -> Expression {
614        Expression::Capture(expr)
615    }
616}
617
618impl std::fmt::Display for Capture {
619    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
620        write!(f, "@{}", self.name)
621    }
622}
623
624/// An integer constant
625#[derive(Debug, Eq, PartialEq)]
626pub struct IntegerConstant {
627    pub value: u32,
628}
629
630impl From<IntegerConstant> for Expression {
631    fn from(expr: IntegerConstant) -> Expression {
632        Expression::IntegerConstant(expr)
633    }
634}
635
636impl std::fmt::Display for IntegerConstant {
637    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
638        write!(f, "{}", self.value)
639    }
640}
641
642/// An ordered list of values
643#[derive(Debug, Eq, PartialEq)]
644pub struct ListLiteral {
645    pub elements: Vec<Expression>,
646}
647
648impl From<ListLiteral> for Expression {
649    fn from(expr: ListLiteral) -> Expression {
650        Expression::ListLiteral(expr)
651    }
652}
653
654impl std::fmt::Display for ListLiteral {
655    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
656        write!(f, "[")?;
657        let mut first = true;
658        for elem in &self.elements {
659            if first {
660                write!(f, "{}", elem)?;
661                first = false;
662            } else {
663                write!(f, ", {}", elem)?;
664            }
665        }
666        write!(f, "]")
667    }
668}
669
670/// An list comprehension
671#[derive(Debug, Eq, PartialEq)]
672pub struct ListComprehension {
673    pub element: Box<Expression>,
674    pub variable: UnscopedVariable,
675    pub value: Box<Expression>,
676    pub location: Location,
677}
678
679impl From<ListComprehension> for Expression {
680    fn from(expr: ListComprehension) -> Expression {
681        Expression::ListComprehension(expr)
682    }
683}
684
685impl std::fmt::Display for ListComprehension {
686    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
687        write!(
688            f,
689            "[ {} for {} in {} ]",
690            self.element, self.variable, self.value
691        )
692    }
693}
694
695/// A reference to one of the regex captures in a `scan` statement
696#[derive(Debug, Eq, PartialEq)]
697pub struct RegexCapture {
698    pub match_index: usize,
699}
700
701impl From<RegexCapture> for Expression {
702    fn from(expr: RegexCapture) -> Expression {
703        Expression::RegexCapture(expr)
704    }
705}
706
707impl std::fmt::Display for RegexCapture {
708    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
709        write!(f, "${}", self.match_index)
710    }
711}
712
713/// An unordered set of values
714#[derive(Debug, Eq, PartialEq)]
715pub struct SetLiteral {
716    pub elements: Vec<Expression>,
717}
718
719impl From<SetLiteral> for Expression {
720    fn from(expr: SetLiteral) -> Expression {
721        Expression::SetLiteral(expr)
722    }
723}
724
725impl std::fmt::Display for SetLiteral {
726    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
727        write!(f, "{{")?;
728        let mut first = true;
729        for elem in &self.elements {
730            if first {
731                write!(f, "{}", elem)?;
732                first = false;
733            } else {
734                write!(f, ", {}", elem)?;
735            }
736        }
737        write!(f, "}}")
738    }
739}
740
741/// An set comprehension
742#[derive(Debug, Eq, PartialEq)]
743pub struct SetComprehension {
744    pub element: Box<Expression>,
745    pub variable: UnscopedVariable,
746    pub value: Box<Expression>,
747    pub location: Location,
748}
749
750impl From<SetComprehension> for Expression {
751    fn from(expr: SetComprehension) -> Expression {
752        Expression::SetComprehension(expr)
753    }
754}
755
756impl std::fmt::Display for SetComprehension {
757    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
758        write!(
759            f,
760            "{{ {} for {} in {} }}",
761            self.element, self.variable, self.value
762        )
763    }
764}
765
766/// A string constant
767#[derive(Debug, Eq, PartialEq)]
768pub struct StringConstant {
769    pub value: String,
770}
771
772impl From<StringConstant> for Expression {
773    fn from(expr: StringConstant) -> Expression {
774        Expression::StringConstant(expr)
775    }
776}
777
778impl std::fmt::Display for StringConstant {
779    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
780        write!(f, "{:?}", self.value)
781    }
782}
783
784impl From<String> for Expression {
785    fn from(value: String) -> Expression {
786        Expression::StringConstant(StringConstant { value }.into())
787    }
788}
789
790impl From<UnscopedVariable> for Expression {
791    fn from(variable: UnscopedVariable) -> Expression {
792        Expression::Variable(variable.into())
793    }
794}
795
796impl From<ScopedVariable> for Expression {
797    fn from(variable: ScopedVariable) -> Expression {
798        Expression::Variable(variable.into())
799    }
800}
801
802/// Attribute shorthands
803#[derive(Debug, Eq, PartialEq)]
804pub struct AttributeShorthands(HashMap<Identifier, AttributeShorthand>);
805
806impl AttributeShorthands {
807    pub fn new() -> Self {
808        Self(HashMap::new())
809    }
810
811    pub fn get(&self, name: &Identifier) -> Option<&AttributeShorthand> {
812        self.0.get(name)
813    }
814
815    pub fn add(&mut self, shorthand: AttributeShorthand) {
816        self.0.insert(shorthand.name.clone(), shorthand);
817    }
818
819    pub fn iter(&self) -> impl Iterator<Item = &AttributeShorthand> {
820        self.0.values()
821    }
822
823    pub fn into_iter(self) -> impl Iterator<Item = AttributeShorthand> {
824        self.0.into_values()
825    }
826}
827
828/// An attribute shorthand
829#[derive(Debug, Eq, PartialEq)]
830pub struct AttributeShorthand {
831    pub name: Identifier,
832    pub variable: UnscopedVariable,
833    pub attributes: Vec<Attribute>,
834    pub location: Location,
835}
836
837impl std::fmt::Display for AttributeShorthand {
838    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
839        write!(f, "attribute {} = {} =>", self.name, self.variable,)?;
840        for attr in &self.attributes {
841            write!(f, " {}", attr)?;
842        }
843        write!(f, " at {}", self.location)
844    }
845}