@@ -11,7 +11,10 @@ use unicode_casing::CharExt;
1111use unicode_segmentation:: UnicodeSegmentation ;
1212use unicode_xid:: UnicodeXID ;
1313
14- use crate :: cformat:: { CFormatString , CFormatErrorType } ;
14+ use crate :: cformat:: {
15+ CFormatErrorType , CFormatPart , CFormatPreconversor , CFormatSpec , CFormatString , CFormatType ,
16+ CNumberType ,
17+ } ;
1518use crate :: format:: { FormatParseError , FormatPart , FormatPreconversor , FormatString } ;
1619use crate :: function:: { single_or_tuple_any, OptionalArg , PyFuncArgs } ;
1720use crate :: pyhash;
@@ -27,6 +30,7 @@ use super::objint::{self, PyInt};
2730use super :: objnone:: PyNone ;
2831use super :: objsequence:: PySliceableSequence ;
2932use super :: objslice:: PySlice ;
33+ use super :: objtuple;
3034use super :: objtype:: { self , PyClassRef } ;
3135
3236use unicode_categories:: UnicodeCategories ;
@@ -434,7 +438,7 @@ impl PyString {
434438 fn modulo ( & self , values : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
435439 let format_string_text = & self . value ;
436440 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 ( ) ) ,
438442 Err ( err) => match err. typ {
439443 CFormatErrorType :: UnsupportedFormatChar ( c) => Err ( vm. new_value_error ( format ! (
440444 "unsupported format character '{}' ({:#x}) at index {}" ,
@@ -1076,6 +1080,10 @@ fn count_char(s: &str, c: char) -> usize {
10761080 s. chars ( ) . filter ( |x| * x == c) . count ( )
10771081}
10781082
1083+ fn call_getitem ( vm : & VirtualMachine , container : & PyObjectRef , key : & PyObjectRef ) -> PyResult {
1084+ vm. call_method ( container, "__getitem__" , vec ! [ key. clone( ) ] )
1085+ }
1086+
10791087fn call_object_format ( vm : & VirtualMachine , argument : PyObjectRef , format_spec : & str ) -> PyResult {
10801088 let ( preconversor, new_format_spec) = FormatPreconversor :: parse_and_consume ( format_spec) ;
10811089 let argument = match preconversor {
@@ -1095,13 +1103,125 @@ fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: &
10951103 Ok ( result)
10961104}
10971105
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 (
10991148 vm : & VirtualMachine ,
11001149 format_string : CFormatString ,
11011150 values_obj : PyObjectRef ,
11021151) -> 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) )
11051225}
11061226
11071227fn perform_format (
0 commit comments