@@ -11,7 +11,10 @@ use unicode_casing::CharExt;
11
11
use unicode_segmentation:: UnicodeSegmentation ;
12
12
use unicode_xid:: UnicodeXID ;
13
13
14
- use crate :: cformat:: { CFormatString , CFormatErrorType } ;
14
+ use crate :: cformat:: {
15
+ CFormatErrorType , CFormatPart , CFormatPreconversor , CFormatSpec , CFormatString , CFormatType ,
16
+ CNumberType ,
17
+ } ;
15
18
use crate :: format:: { FormatParseError , FormatPart , FormatPreconversor , FormatString } ;
16
19
use crate :: function:: { single_or_tuple_any, OptionalArg , PyFuncArgs } ;
17
20
use crate :: pyhash;
@@ -27,6 +30,7 @@ use super::objint::{self, PyInt};
27
30
use super :: objnone:: PyNone ;
28
31
use super :: objsequence:: PySliceableSequence ;
29
32
use super :: objslice:: PySlice ;
33
+ use super :: objtuple;
30
34
use super :: objtype:: { self , PyClassRef } ;
31
35
32
36
use unicode_categories:: UnicodeCategories ;
@@ -434,7 +438,7 @@ impl PyString {
434
438
fn modulo ( & self , values : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
435
439
let format_string_text = & self . value ;
436
440
match CFormatString :: from_str ( format_string_text) {
437
- Ok ( format_string) => perform_clike_format ( vm, format_string, values. clone ( ) ) ,
441
+ Ok ( format_string) => do_cformat ( vm, format_string, values. clone ( ) ) ,
438
442
Err ( err) => match err. typ {
439
443
CFormatErrorType :: UnsupportedFormatChar ( c) => Err ( vm. new_value_error ( format ! (
440
444
"unsupported format character '{}' ({:#x}) at index {}" ,
@@ -1076,6 +1080,10 @@ fn count_char(s: &str, c: char) -> usize {
1076
1080
s. chars ( ) . filter ( |x| * x == c) . count ( )
1077
1081
}
1078
1082
1083
+ fn call_getitem ( vm : & VirtualMachine , container : & PyObjectRef , key : & PyObjectRef ) -> PyResult {
1084
+ vm. call_method ( container, "__getitem__" , vec ! [ key. clone( ) ] )
1085
+ }
1086
+
1079
1087
fn call_object_format ( vm : & VirtualMachine , argument : PyObjectRef , format_spec : & str ) -> PyResult {
1080
1088
let ( preconversor, new_format_spec) = FormatPreconversor :: parse_and_consume ( format_spec) ;
1081
1089
let argument = match preconversor {
@@ -1095,13 +1103,125 @@ fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: &
1095
1103
Ok ( result)
1096
1104
}
1097
1105
1098
- fn perform_clike_format (
1106
+ fn do_cformat_specifier (
1107
+ vm : & VirtualMachine ,
1108
+ format_spec : & CFormatSpec ,
1109
+ obj : PyObjectRef ,
1110
+ ) -> Result < String , PyObjectRef > {
1111
+ use CNumberType :: * ;
1112
+ // do the formatting by type
1113
+ let format_type = & format_spec. format_type ;
1114
+
1115
+ match format_type {
1116
+ CFormatType :: String ( preconversor) => {
1117
+ let result = match preconversor {
1118
+ CFormatPreconversor :: Str => vm. call_method ( & obj. clone ( ) , "__str__" , vec ! [ ] ) ?,
1119
+ CFormatPreconversor :: Repr => vm. call_method ( & obj. clone ( ) , "__repr__" , vec ! [ ] ) ?,
1120
+ CFormatPreconversor :: Ascii => vm. call_method ( & obj. clone ( ) , "__repr__" , vec ! [ ] ) ?,
1121
+ } ;
1122
+ Ok ( format_spec. format_string ( get_value ( & result) ) )
1123
+ }
1124
+ CFormatType :: Number ( _) => {
1125
+ if !objtype:: isinstance ( & obj, & vm. ctx . int_type ( ) ) {
1126
+ let required_type_string = match format_type {
1127
+ CFormatType :: Number ( Decimal ) => "a number" ,
1128
+ CFormatType :: Number ( _) => "an integer" ,
1129
+ _ => unreachable ! ( ) ,
1130
+ } ;
1131
+ return Err ( vm. new_type_error ( format ! (
1132
+ "%{} format: {} is required, not {}" ,
1133
+ format_spec. format_char,
1134
+ required_type_string,
1135
+ obj. class( )
1136
+ ) ) ) ;
1137
+ }
1138
+ Ok ( format_spec. format_number ( objint:: get_value ( & obj) ) )
1139
+ }
1140
+ _ => Err ( vm. new_not_implemented_error ( format ! (
1141
+ "Not yet implemented for %{}" ,
1142
+ format_spec. format_char
1143
+ ) ) ) ,
1144
+ }
1145
+ }
1146
+
1147
+ fn do_cformat (
1099
1148
vm : & VirtualMachine ,
1100
1149
format_string : CFormatString ,
1101
1150
values_obj : PyObjectRef ,
1102
1151
) -> PyResult {
1103
- // TODO
1104
- Err ( vm. new_type_error ( "Not implemented" . to_string ( ) ) )
1152
+ let mut final_string = String :: new ( ) ;
1153
+ let num_specifiers = format_string
1154
+ . format_parts
1155
+ . iter ( )
1156
+ . filter ( |( _, part) | CFormatPart :: is_specifier ( part) )
1157
+ . count ( ) ;
1158
+ let mapping_required = format_string
1159
+ . format_parts
1160
+ . iter ( )
1161
+ . any ( |( _, part) | CFormatPart :: has_key ( part) )
1162
+ && format_string
1163
+ . format_parts
1164
+ . iter ( )
1165
+ . filter ( |( _, part) | CFormatPart :: is_specifier ( part) )
1166
+ . all ( |( _, part) | CFormatPart :: has_key ( part) ) ;
1167
+
1168
+ let values_obj = if mapping_required {
1169
+ if !objtype:: isinstance ( & values_obj, & vm. ctx . dict_type ( ) ) {
1170
+ return Err ( vm. new_type_error ( "format requires a mapping" . to_string ( ) ) ) ;
1171
+ }
1172
+ values_obj
1173
+ } else {
1174
+ // check for only literal parts, in which case only empty tuple is allowed
1175
+ if 0 == num_specifiers
1176
+ && ( !objtype:: isinstance ( & values_obj, & vm. ctx . tuple_type ( ) )
1177
+ || !objtuple:: get_value ( & values_obj) . is_empty ( ) )
1178
+ {
1179
+ return Err ( vm. new_type_error (
1180
+ "not all arguments converted during string formatting" . to_string ( ) ,
1181
+ ) ) ;
1182
+ }
1183
+
1184
+ // convert `values_obj` to a new tuple if it's not a tuple
1185
+ if !objtype:: isinstance ( & values_obj, & vm. ctx . tuple_type ( ) ) {
1186
+ vm. ctx . new_tuple ( vec ! [ values_obj. clone( ) ] )
1187
+ } else {
1188
+ values_obj. clone ( )
1189
+ }
1190
+
1191
+ // if values.len() > num_specifiers {
1192
+ // return Err(vm.new_type_error("not all arguments converted during string formatting".to_string()));
1193
+ // } else if values.len() < num_specifiers {
1194
+ // return Err(vm.new_type_error("not enough arguments for format string".to_string()));
1195
+ // }
1196
+ } ;
1197
+
1198
+ let mut auto_index: usize = 0 ;
1199
+ for ( _, part) in & format_string. format_parts {
1200
+ let result_string: String = match part {
1201
+ CFormatPart :: Spec ( format_spec) => {
1202
+ // try to get the object
1203
+ let obj: PyObjectRef = match & format_spec. mapping_key {
1204
+ Some ( key) => {
1205
+ // TODO: change the KeyError message to match the one in cpython
1206
+ call_getitem ( vm, & values_obj, & vm. ctx . new_str ( key. to_string ( ) ) ) ?
1207
+ }
1208
+ None => {
1209
+ // TODO: translate exception from IndexError to TypeError
1210
+ let obj = call_getitem ( vm, & values_obj, & vm. ctx . new_int ( auto_index) ) ?;
1211
+ auto_index += 1 ;
1212
+
1213
+ obj
1214
+ }
1215
+ } ;
1216
+
1217
+ do_cformat_specifier ( vm, & format_spec, obj)
1218
+ }
1219
+ CFormatPart :: Literal ( literal) => Ok ( literal. clone ( ) ) ,
1220
+ } ?;
1221
+ final_string. push_str ( & result_string) ;
1222
+ }
1223
+
1224
+ Ok ( vm. ctx . new_str ( final_string) )
1105
1225
}
1106
1226
1107
1227
fn perform_format (
0 commit comments