1
+ use crate :: exceptions:: PyBaseExceptionRef ;
1
2
use crate :: function:: PyFuncArgs ;
2
- use crate :: obj:: objint:: PyInt ;
3
- use crate :: obj:: objstr:: PyString ;
4
3
use crate :: obj:: { objstr, objtype} ;
5
4
use crate :: pyobject:: { IntoPyObject , ItemProtocol , PyObjectRef , PyResult , TypeProtocol } ;
6
5
use crate :: vm:: VirtualMachine ;
@@ -558,11 +557,23 @@ pub(crate) enum FormatParseError {
558
557
MissingStartBracket ,
559
558
UnescapedStartBracketInLiteral ,
560
559
InvalidFormatSpecifier ,
560
+ UnknownConversion ,
561
561
EmptyAttribute ,
562
562
MissingRightBracket ,
563
563
InvalidCharacterAfterRightBracket ,
564
564
}
565
565
566
+ impl FormatParseError {
567
+ pub fn into_pyobject ( self , vm : & VirtualMachine ) -> PyBaseExceptionRef {
568
+ match self {
569
+ FormatParseError :: UnmatchedBracket => {
570
+ vm. new_value_error ( "expected '}' before end of string" . to_owned ( ) )
571
+ }
572
+ _ => vm. new_value_error ( "Unexpected error parsing format string" . to_owned ( ) ) ,
573
+ }
574
+ }
575
+ }
576
+
566
577
impl FromStr for FormatSpec {
567
578
type Err = & ' static str ;
568
579
fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
@@ -622,12 +633,12 @@ pub(crate) enum FieldType {
622
633
623
634
#[ derive( Debug , PartialEq ) ]
624
635
pub ( crate ) struct FieldName {
625
- field_type : FieldType ,
626
- parts : Vec < FieldNamePart > ,
636
+ pub ( crate ) field_type : FieldType ,
637
+ pub ( crate ) parts : Vec < FieldNamePart > ,
627
638
}
628
639
629
640
impl FieldName {
630
- fn parse ( text : & str ) -> Result < FieldName , FormatParseError > {
641
+ pub ( crate ) fn parse ( text : & str ) -> Result < FieldName , FormatParseError > {
631
642
let mut chars = text. chars ( ) . peekable ( ) ;
632
643
let mut first = String :: new ( ) ;
633
644
for ch in chars. peeking_take_while ( |ch| * ch != '.' && * ch != '[' ) {
@@ -652,42 +663,18 @@ impl FieldName {
652
663
}
653
664
654
665
#[ derive( Debug , PartialEq ) ]
655
- enum FormatPart {
656
- Field ( FieldName , String ) ,
666
+ pub ( crate ) enum FormatPart {
667
+ Field {
668
+ field_name : String ,
669
+ preconversion_spec : Option < char > ,
670
+ format_spec : String ,
671
+ } ,
657
672
Literal ( String ) ,
658
673
}
659
674
660
- impl FormatPart {
661
- fn is_auto ( & self ) -> bool {
662
- match self {
663
- FormatPart :: Field (
664
- FieldName {
665
- field_type : FieldType :: AutoSpec ,
666
- ..
667
- } ,
668
- _,
669
- ) => true ,
670
- _ => false ,
671
- }
672
- }
673
-
674
- fn is_index ( & self ) -> bool {
675
- match self {
676
- FormatPart :: Field (
677
- FieldName {
678
- field_type : FieldType :: IndexSpec ( _) ,
679
- ..
680
- } ,
681
- _,
682
- ) => true ,
683
- _ => false ,
684
- }
685
- }
686
- }
687
-
688
675
#[ derive( Debug , PartialEq ) ]
689
676
pub ( crate ) struct FormatString {
690
- format_parts : Vec < FormatPart > ,
677
+ pub ( crate ) format_parts : Vec < FormatPart > ,
691
678
}
692
679
693
680
impl FormatString {
@@ -744,22 +731,31 @@ impl FormatString {
744
731
// before the bang is a keyword or arg index, after the comma is maybe a conversor spec.
745
732
let arg_part = parts[ 0 ] ;
746
733
747
- let preconversor_spec = if parts. len ( ) > 1 {
748
- "!" . to_owned ( ) + parts[ 1 ]
734
+ let preconversion_spec = if let Some ( conversion) = parts. get ( 1 ) {
735
+ let mut chars = conversion. chars ( ) ;
736
+ if let Some ( ch) = chars. next ( ) {
737
+ // conversions are only every one character
738
+ if chars. next ( ) . is_some ( ) {
739
+ return Err ( FormatParseError :: UnknownConversion ) ;
740
+ }
741
+ Some ( ch)
742
+ } else {
743
+ return Err ( FormatParseError :: UnknownConversion ) ;
744
+ }
749
745
} else {
750
- String :: new ( )
746
+ None
751
747
} ;
752
- let format_spec = preconversor_spec + & format_spec;
753
- Ok ( FormatPart :: Field ( FieldName :: parse ( arg_part) ?, format_spec) )
748
+
749
+ Ok ( FormatPart :: Field {
750
+ field_name : arg_part. to_owned ( ) ,
751
+ preconversion_spec,
752
+ format_spec,
753
+ } )
754
754
}
755
755
756
- fn parse_spec < ' a > (
757
- text : & ' a str ,
758
- args : & ' a PyFuncArgs ,
759
- ) -> Result < ( FormatPart , & ' a str ) , FormatParseError > {
756
+ fn parse_spec ( text : & str ) -> Result < ( FormatPart , & str ) , FormatParseError > {
760
757
let mut nested = false ;
761
758
let mut end_bracket_pos = None ;
762
- let mut spec_template = String :: new ( ) ;
763
759
let mut left = String :: new ( ) ;
764
760
765
761
// There may be one layer nesting brackets in spec
@@ -773,30 +769,18 @@ impl FormatString {
773
769
return Err ( FormatParseError :: InvalidFormatSpecifier ) ;
774
770
} else {
775
771
nested = true ;
772
+ left. push ( c) ;
776
773
continue ;
777
774
}
778
775
} else if c == '}' {
779
776
if nested {
780
777
nested = false ;
781
- if let Some ( obj) = args. kwargs . get ( & spec_template) {
782
- if let Some ( s) = ( * obj) . clone ( ) . payload :: < PyString > ( ) {
783
- left. push_str ( s. as_str ( ) ) ;
784
- } else if let Some ( i) = ( * obj) . clone ( ) . payload :: < PyInt > ( ) {
785
- left. push_str ( & PyInt :: repr ( i) ) ;
786
- } else {
787
- return Err ( FormatParseError :: InvalidFormatSpecifier ) ;
788
- }
789
- } else {
790
- // CPython return KeyError here
791
- return Err ( FormatParseError :: InvalidFormatSpecifier ) ;
792
- }
778
+ left. push ( c) ;
793
779
continue ;
794
780
} else {
795
781
end_bracket_pos = Some ( idx) ;
796
782
break ;
797
783
}
798
- } else if nested {
799
- spec_template. push ( c) ;
800
784
} else {
801
785
left. push ( c) ;
802
786
}
@@ -813,13 +797,20 @@ impl FormatString {
813
797
fn format_internal (
814
798
& self ,
815
799
vm : & VirtualMachine ,
816
- mut field_func : impl FnMut ( & FieldType ) -> PyResult ,
817
- ) -> PyResult {
800
+ field_func : & mut impl FnMut ( & FieldType ) -> PyResult ,
801
+ ) -> PyResult < String > {
818
802
let mut final_string = String :: new ( ) ;
819
803
for part in & self . format_parts {
820
804
let result_string: String = match part {
821
- FormatPart :: Field ( FieldName { field_type, parts } , format_spec) => {
822
- let mut argument = field_func ( field_type) ?;
805
+ FormatPart :: Field {
806
+ field_name,
807
+ preconversion_spec,
808
+ format_spec,
809
+ } => {
810
+ let FieldName { field_type, parts } =
811
+ FieldName :: parse ( field_name. as_str ( ) ) . map_err ( |e| e. into_pyobject ( vm) ) ?;
812
+
813
+ let mut argument = field_func ( & field_type) ?;
823
814
824
815
for name_part in parts {
825
816
match name_part {
@@ -831,54 +822,66 @@ impl FormatString {
831
822
argument = argument. get_item ( & index. into_pyobject ( vm) ?, vm) ?;
832
823
}
833
824
FieldNamePart :: StringIndex ( index) => {
834
- argument = argument. get_item ( index, vm) ?;
825
+ argument = argument. get_item ( & index, vm) ?;
835
826
}
836
827
}
837
828
}
838
829
839
- let value = call_object_format ( vm, argument, & format_spec) ?;
830
+ let nested_format =
831
+ FormatString :: from_str ( & format_spec) . map_err ( |e| e. into_pyobject ( vm) ) ?;
832
+ let format_spec = nested_format. format_internal ( vm, field_func) ?;
833
+
834
+ let value =
835
+ call_object_format ( vm, argument, * preconversion_spec, & format_spec) ?;
840
836
objstr:: clone_value ( & value)
841
837
}
842
838
FormatPart :: Literal ( literal) => literal. clone ( ) ,
843
839
} ;
844
840
final_string. push_str ( & result_string) ;
845
841
}
846
- Ok ( vm . ctx . new_str ( final_string) )
842
+ Ok ( final_string)
847
843
}
848
844
849
- pub ( crate ) fn format ( & self , arguments : & PyFuncArgs , vm : & VirtualMachine ) -> PyResult {
850
- if self . format_parts . iter ( ) . any ( FormatPart :: is_auto)
851
- && self . format_parts . iter ( ) . any ( FormatPart :: is_index)
852
- {
853
- return Err ( vm. new_value_error (
854
- "cannot switch from automatic field numbering to manual field specification"
855
- . to_owned ( ) ,
856
- ) ) ;
857
- }
858
-
859
- let mut auto_argument_index: usize = 1 ;
860
- self . format_internal ( vm, |field_type| match field_type {
845
+ pub ( crate ) fn format ( & self , arguments : & PyFuncArgs , vm : & VirtualMachine ) -> PyResult < String > {
846
+ let mut auto_argument_index: usize = 0 ;
847
+ let mut seen_index = false ;
848
+ self . format_internal ( vm, & mut |field_type| match field_type {
861
849
FieldType :: AutoSpec => {
850
+ if seen_index {
851
+ return Err ( vm. new_value_error (
852
+ "cannot switch from manual field specification to automatic field numbering"
853
+ . to_owned ( ) ,
854
+ ) ) ;
855
+ }
862
856
auto_argument_index += 1 ;
863
857
arguments
864
858
. args
865
- . get ( auto_argument_index - 1 )
859
+ . get ( auto_argument_index)
860
+ . cloned ( )
861
+ . ok_or_else ( || vm. new_index_error ( "tuple index out of range" . to_owned ( ) ) )
862
+ }
863
+ FieldType :: IndexSpec ( index) => {
864
+ if auto_argument_index != 0 {
865
+ return Err ( vm. new_value_error (
866
+ "cannot switch from automatic field numbering to manual field specification"
867
+ . to_owned ( ) ,
868
+ ) ) ;
869
+ }
870
+ seen_index = true ;
871
+ arguments
872
+ . args
873
+ . get ( * index)
866
874
. cloned ( )
867
875
. ok_or_else ( || vm. new_index_error ( "tuple index out of range" . to_owned ( ) ) )
868
876
}
869
- FieldType :: IndexSpec ( index) => arguments
870
- . args
871
- . get ( * index + 1 )
872
- . cloned ( )
873
- . ok_or_else ( || vm. new_index_error ( "tuple index out of range" . to_owned ( ) ) ) ,
874
877
FieldType :: KeywordSpec ( keyword) => arguments
875
878
. get_optional_kwarg ( & keyword)
876
879
. ok_or_else ( || vm. new_key_error ( vm. new_str ( keyword. to_owned ( ) ) ) ) ,
877
880
} )
878
881
}
879
882
880
- pub ( crate ) fn format_map ( & self , dict : & PyObjectRef , vm : & VirtualMachine ) -> PyResult {
881
- self . format_internal ( vm, |field_type| match field_type {
883
+ pub ( crate ) fn format_map ( & self , dict : & PyObjectRef , vm : & VirtualMachine ) -> PyResult < String > {
884
+ self . format_internal ( vm, & mut |field_type| match field_type {
882
885
FieldType :: AutoSpec | FieldType :: IndexSpec ( _) => {
883
886
Err ( vm. new_value_error ( "Format string contains positional fields" . to_owned ( ) ) )
884
887
}
@@ -887,16 +890,20 @@ impl FormatString {
887
890
}
888
891
}
889
892
890
- fn call_object_format ( vm : & VirtualMachine , argument : PyObjectRef , format_spec : & str ) -> PyResult {
891
- let ( preconversor, new_format_spec) = FormatPreconversor :: parse_and_consume ( format_spec) ;
892
- let argument = match preconversor {
893
+ fn call_object_format (
894
+ vm : & VirtualMachine ,
895
+ argument : PyObjectRef ,
896
+ preconversion_spec : Option < char > ,
897
+ format_spec : & str ,
898
+ ) -> PyResult {
899
+ let argument = match preconversion_spec. and_then ( |c| FormatPreconversor :: from_char ( c) ) {
893
900
Some ( FormatPreconversor :: Str ) => vm. call_method ( & argument, "__str__" , vec ! [ ] ) ?,
894
901
Some ( FormatPreconversor :: Repr ) => vm. call_method ( & argument, "__repr__" , vec ! [ ] ) ?,
895
902
Some ( FormatPreconversor :: Ascii ) => vm. call_method ( & argument, "__repr__" , vec ! [ ] ) ?,
896
903
Some ( FormatPreconversor :: Bytes ) => vm. call_method ( & argument, "decode" , vec ! [ ] ) ?,
897
904
None => argument,
898
905
} ;
899
- let returned_type = vm. ctx . new_str ( new_format_spec . to_owned ( ) ) ;
906
+ let returned_type = vm. ctx . new_str ( format_spec . to_owned ( ) ) ;
900
907
901
908
let result = vm. call_method ( & argument, "__format__" , vec ! [ returned_type] ) ?;
902
909
if !objtype:: isinstance ( & result, & vm. ctx . types . str_type ) {
@@ -909,20 +916,20 @@ fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: &
909
916
910
917
pub ( crate ) trait FromTemplate < ' a > : Sized {
911
918
type Err ;
912
- fn from_str ( s : & ' a str , arg : & ' a PyFuncArgs ) -> Result < Self , Self :: Err > ;
919
+ fn from_str ( s : & ' a str ) -> Result < Self , Self :: Err > ;
913
920
}
914
921
915
922
impl < ' a > FromTemplate < ' a > for FormatString {
916
923
type Err = FormatParseError ;
917
924
918
- fn from_str ( text : & ' a str , args : & ' a PyFuncArgs ) -> Result < Self , Self :: Err > {
925
+ fn from_str ( text : & ' a str ) -> Result < Self , Self :: Err > {
919
926
let mut cur_text: & str = text;
920
927
let mut parts: Vec < FormatPart > = Vec :: new ( ) ;
921
928
while !cur_text. is_empty ( ) {
922
929
// Try to parse both literals and bracketed format parts util we
923
930
// run out of text
924
931
cur_text = FormatString :: parse_literal ( cur_text)
925
- . or_else ( |_| FormatString :: parse_spec ( cur_text, & args ) )
932
+ . or_else ( |_| FormatString :: parse_spec ( cur_text) )
926
933
. map ( |( part, new_text) | {
927
934
parts. push ( part) ;
928
935
new_text
@@ -1061,21 +1068,17 @@ mod tests {
1061
1068
let expected = Ok ( FormatString {
1062
1069
format_parts : vec ! [
1063
1070
FormatPart :: Literal ( "abcd" . to_owned( ) ) ,
1064
- FormatPart :: Field (
1065
- FieldName {
1066
- field_type: FieldType :: IndexSpec ( 1 ) ,
1067
- parts: Vec :: new( ) ,
1068
- } ,
1069
- String :: new( ) ,
1070
- ) ,
1071
+ FormatPart :: Field {
1072
+ field_name: "1" . to_owned( ) ,
1073
+ preconversion_spec: None ,
1074
+ format_spec: String :: new( ) ,
1075
+ } ,
1071
1076
FormatPart :: Literal ( ":" . to_owned( ) ) ,
1072
- FormatPart :: Field (
1073
- FieldName {
1074
- field_type: FieldType :: KeywordSpec ( "key" . to_owned( ) ) ,
1075
- parts: Vec :: new( ) ,
1076
- } ,
1077
- String :: new( ) ,
1078
- ) ,
1077
+ FormatPart :: Field {
1078
+ field_name: "key" . to_owned( ) ,
1079
+ preconversion_spec: None ,
1080
+ format_spec: String :: new( ) ,
1081
+ } ,
1079
1082
] ,
1080
1083
} ) ;
1081
1084
@@ -1098,13 +1101,11 @@ mod tests {
1098
1101
let expected = Ok ( FormatString {
1099
1102
format_parts : vec ! [
1100
1103
FormatPart :: Literal ( "{" . to_owned( ) ) ,
1101
- FormatPart :: Field (
1102
- FieldName {
1103
- field_type: FieldType :: KeywordSpec ( "key" . to_owned( ) ) ,
1104
- parts: Vec :: new( ) ,
1105
- } ,
1106
- String :: new( ) ,
1107
- ) ,
1104
+ FormatPart :: Field {
1105
+ field_name: "key" . to_owned( ) ,
1106
+ preconversion_spec: None ,
1107
+ format_spec: String :: new( ) ,
1108
+ } ,
1108
1109
FormatPart :: Literal ( "}ddfe" . to_owned( ) ) ,
1109
1110
] ,
1110
1111
} ) ;
0 commit comments