15
15
//! an implicit concatenation of string literals, as these expressions are considered to
16
16
//! have the same shape in that they evaluate to the same value.
17
17
18
+ use std:: borrow:: Cow ;
19
+
18
20
use crate as ast;
19
21
20
22
#[ derive( Debug , PartialEq , Eq , Hash , Copy , Clone ) ]
@@ -511,7 +513,7 @@ impl<'a> From<&'a ast::ExceptHandler> for ComparableExceptHandler<'a> {
511
513
512
514
#[ derive( Debug , PartialEq , Eq , Hash ) ]
513
515
pub enum ComparableFStringElement < ' a > {
514
- Literal ( & ' a str ) ,
516
+ Literal ( Cow < ' a , str > ) ,
515
517
FStringExpressionElement ( FStringExpressionElement < ' a > ) ,
516
518
}
517
519
@@ -527,23 +529,34 @@ impl<'a> From<&'a ast::FStringElement> for ComparableFStringElement<'a> {
527
529
fn from ( fstring_element : & ' a ast:: FStringElement ) -> Self {
528
530
match fstring_element {
529
531
ast:: FStringElement :: Literal ( ast:: FStringLiteralElement { value, .. } ) => {
530
- Self :: Literal ( value)
531
- }
532
- ast:: FStringElement :: Expression ( formatted_value) => {
533
- Self :: FStringExpressionElement ( FStringExpressionElement {
534
- expression : ( & formatted_value. expression ) . into ( ) ,
535
- debug_text : formatted_value. debug_text . as_ref ( ) ,
536
- conversion : formatted_value. conversion ,
537
- format_spec : formatted_value
538
- . format_spec
539
- . as_ref ( )
540
- . map ( |spec| spec. elements . iter ( ) . map ( Into :: into) . collect ( ) ) ,
541
- } )
532
+ Self :: Literal ( value. as_ref ( ) . into ( ) )
542
533
}
534
+ ast:: FStringElement :: Expression ( formatted_value) => formatted_value. into ( ) ,
543
535
}
544
536
}
545
537
}
546
538
539
+ impl < ' a > From < & ' a ast:: FStringExpressionElement > for ComparableFStringElement < ' a > {
540
+ fn from ( fstring_expression_element : & ' a ast:: FStringExpressionElement ) -> Self {
541
+ let ast:: FStringExpressionElement {
542
+ expression,
543
+ debug_text,
544
+ conversion,
545
+ format_spec,
546
+ range : _,
547
+ } = fstring_expression_element;
548
+
549
+ Self :: FStringExpressionElement ( FStringExpressionElement {
550
+ expression : ( expression) . into ( ) ,
551
+ debug_text : debug_text. as_ref ( ) ,
552
+ conversion : * conversion,
553
+ format_spec : format_spec
554
+ . as_ref ( )
555
+ . map ( |spec| spec. elements . iter ( ) . map ( Into :: into) . collect ( ) ) ,
556
+ } )
557
+ }
558
+ }
559
+
547
560
#[ derive( Debug , PartialEq , Eq , Hash ) ]
548
561
pub struct ComparableElifElseClause < ' a > {
549
562
test : Option < ComparableExpr < ' a > > ,
@@ -597,28 +610,82 @@ impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
597
610
598
611
#[ derive( Debug , PartialEq , Eq , Hash ) ]
599
612
pub struct ComparableFString < ' a > {
600
- elements : Vec < ComparableFStringElement < ' a > > ,
601
- }
613
+ elements : Box < [ ComparableFStringElement < ' a > ] > ,
614
+ }
615
+
616
+ impl < ' a > From < & ' a ast:: FStringValue > for ComparableFString < ' a > {
617
+ // The approach below is somewhat complicated, so it may
618
+ // require some justification.
619
+ //
620
+ // Suppose given an f-string of the form
621
+ // `f"{foo!r} one" " and two " f" and three {bar!s}"`
622
+ // This decomposes as:
623
+ // - An `FStringPart::FString`, `f"{foo!r} one"` with elements
624
+ // - `FStringElement::Expression` encoding `{foo!r}`
625
+ // - `FStringElement::Literal` encoding " one"
626
+ // - An `FStringPart::Literal` capturing `" and two "`
627
+ // - An `FStringPart::FString`, `f" and three {bar!s}"` with elements
628
+ // - `FStringElement::Literal` encoding " and three "
629
+ // - `FStringElement::Expression` encoding `{bar!s}`
630
+ //
631
+ // We would like to extract from this a vector of (comparable) f-string
632
+ // _elements_ which alternate between expression elements and literal
633
+ // elements. In order to do so, we need to concatenate adjacent string
634
+ // literals. String literals may be separated for two reasons: either
635
+ // they appear in adjacent string literal parts, or else a string literal
636
+ // part is adjacent to a string literal _element_ inside of an f-string part.
637
+ fn from ( value : & ' a ast:: FStringValue ) -> Self {
638
+ #[ derive( Default ) ]
639
+ struct Collector < ' a > {
640
+ elements : Vec < ComparableFStringElement < ' a > > ,
641
+ }
602
642
603
- impl < ' a > From < & ' a ast:: FString > for ComparableFString < ' a > {
604
- fn from ( fstring : & ' a ast:: FString ) -> Self {
605
- Self {
606
- elements : fstring. elements . iter ( ) . map ( Into :: into) . collect ( ) ,
643
+ impl < ' a > Collector < ' a > {
644
+ // The logic for concatenating adjacent string literals
645
+ // occurs here, implicitly: when we encounter a sequence
646
+ // of string literals, the first gets pushed to the
647
+ // `elements` vector, while subsequent strings
648
+ // are concatenated onto this top string.
649
+ fn push_literal ( & mut self , literal : & ' a str ) {
650
+ if let Some ( ComparableFStringElement :: Literal ( existing_literal) ) =
651
+ self . elements . last_mut ( )
652
+ {
653
+ existing_literal. to_mut ( ) . push_str ( literal) ;
654
+ } else {
655
+ self . elements
656
+ . push ( ComparableFStringElement :: Literal ( literal. into ( ) ) ) ;
657
+ }
658
+ }
659
+
660
+ fn push_expression ( & mut self , expression : & ' a ast:: FStringExpressionElement ) {
661
+ self . elements . push ( expression. into ( ) ) ;
662
+ }
607
663
}
608
- }
609
- }
610
664
611
- #[ derive( Debug , PartialEq , Eq , Hash ) ]
612
- pub enum ComparableFStringPart < ' a > {
613
- Literal ( ComparableStringLiteral < ' a > ) ,
614
- FString ( ComparableFString < ' a > ) ,
615
- }
665
+ let mut collector = Collector :: default ( ) ;
666
+
667
+ for part in value {
668
+ match part {
669
+ ast:: FStringPart :: Literal ( string_literal) => {
670
+ collector. push_literal ( & string_literal. value ) ;
671
+ }
672
+ ast:: FStringPart :: FString ( fstring) => {
673
+ for element in & fstring. elements {
674
+ match element {
675
+ ast:: FStringElement :: Literal ( literal) => {
676
+ collector. push_literal ( & literal. value ) ;
677
+ }
678
+ ast:: FStringElement :: Expression ( expression) => {
679
+ collector. push_expression ( expression) ;
680
+ }
681
+ }
682
+ }
683
+ }
684
+ }
685
+ }
616
686
617
- impl < ' a > From < & ' a ast:: FStringPart > for ComparableFStringPart < ' a > {
618
- fn from ( f_string_part : & ' a ast:: FStringPart ) -> Self {
619
- match f_string_part {
620
- ast:: FStringPart :: Literal ( string_literal) => Self :: Literal ( string_literal. into ( ) ) ,
621
- ast:: FStringPart :: FString ( f_string) => Self :: FString ( f_string. into ( ) ) ,
687
+ Self {
688
+ elements : collector. elements . into_boxed_slice ( ) ,
622
689
}
623
690
}
624
691
}
@@ -638,13 +705,13 @@ impl<'a> From<&'a ast::StringLiteral> for ComparableStringLiteral<'a> {
638
705
639
706
#[ derive( Debug , PartialEq , Eq , Hash ) ]
640
707
pub struct ComparableBytesLiteral < ' a > {
641
- value : & ' a [ u8 ] ,
708
+ value : Cow < ' a , [ u8 ] > ,
642
709
}
643
710
644
711
impl < ' a > From < & ' a ast:: BytesLiteral > for ComparableBytesLiteral < ' a > {
645
712
fn from ( bytes_literal : & ' a ast:: BytesLiteral ) -> Self {
646
713
Self {
647
- value : & bytes_literal. value ,
714
+ value : Cow :: Borrowed ( & bytes_literal. value ) ,
648
715
}
649
716
}
650
717
}
@@ -775,17 +842,17 @@ pub struct ExprFStringExpressionElement<'a> {
775
842
776
843
#[ derive( Debug , PartialEq , Eq , Hash ) ]
777
844
pub struct ExprFString < ' a > {
778
- parts : Vec < ComparableFStringPart < ' a > > ,
845
+ value : ComparableFString < ' a > ,
779
846
}
780
847
781
848
#[ derive( Debug , PartialEq , Eq , Hash ) ]
782
849
pub struct ExprStringLiteral < ' a > {
783
- parts : Vec < ComparableStringLiteral < ' a > > ,
850
+ value : ComparableStringLiteral < ' a > ,
784
851
}
785
852
786
853
#[ derive( Debug , PartialEq , Eq , Hash ) ]
787
854
pub struct ExprBytesLiteral < ' a > {
788
- parts : Vec < ComparableBytesLiteral < ' a > > ,
855
+ value : ComparableBytesLiteral < ' a > ,
789
856
}
790
857
791
858
#[ derive( Debug , PartialEq , Eq , Hash ) ]
@@ -1019,17 +1086,21 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
1019
1086
} ) ,
1020
1087
ast:: Expr :: FString ( ast:: ExprFString { value, range : _ } ) => {
1021
1088
Self :: FString ( ExprFString {
1022
- parts : value. iter ( ) . map ( Into :: into) . collect ( ) ,
1089
+ value : value. into ( ) ,
1023
1090
} )
1024
1091
}
1025
1092
ast:: Expr :: StringLiteral ( ast:: ExprStringLiteral { value, range : _ } ) => {
1026
1093
Self :: StringLiteral ( ExprStringLiteral {
1027
- parts : value. iter ( ) . map ( Into :: into) . collect ( ) ,
1094
+ value : ComparableStringLiteral {
1095
+ value : value. to_str ( ) ,
1096
+ } ,
1028
1097
} )
1029
1098
}
1030
1099
ast:: Expr :: BytesLiteral ( ast:: ExprBytesLiteral { value, range : _ } ) => {
1031
1100
Self :: BytesLiteral ( ExprBytesLiteral {
1032
- parts : value. iter ( ) . map ( Into :: into) . collect ( ) ,
1101
+ value : ComparableBytesLiteral {
1102
+ value : Cow :: from ( value) ,
1103
+ } ,
1033
1104
} )
1034
1105
}
1035
1106
ast:: Expr :: NumberLiteral ( ast:: ExprNumberLiteral { value, range : _ } ) => {
0 commit comments