fork of https://github.com/tree-sitter/tree-sitter-graph
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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