@@ -12,18 +12,76 @@ use num_complex::Complex64;
1212use rustpython_wtf8:: { Wtf8 , Wtf8Buf } ;
1313use std:: { collections:: BTreeSet , fmt, hash, marker:: PhantomData , mem, num:: NonZeroU8 , ops:: Deref } ;
1414
15+ /// Oparg values for [`Instruction::ConvertValue`].
16+ ///
17+ /// ## See also
18+ ///
19+ /// - [CPython FVC_* flags](https://github.com/python/cpython/blob/8183fa5e3f78ca6ab862de7fb8b14f3d929421e0/Include/ceval.h#L129-L132)
20+ #[ repr( u8 ) ]
1521#[ derive( Copy , Clone , Debug , Hash , PartialEq , Eq ) ]
16- #[ repr( i8 ) ]
17- #[ allow( clippy:: cast_possible_wrap) ]
18- pub enum ConversionFlag {
19- /// No conversion
20- None = -1 , // CPython uses -1
22+ pub enum ConvertValueOparg {
23+ /// No conversion.
24+ ///
25+ /// ```python
26+ /// f"{x}"
27+ /// f"{x:4}"
28+ /// ```
29+ None = 0 ,
2130 /// Converts by calling `str(<value>)`.
22- Str = b's' as i8 ,
23- /// Converts by calling `ascii(<value>)`.
24- Ascii = b'a' as i8 ,
31+ ///
32+ /// ```python
33+ /// f"{x!s}"
34+ /// f"{x!s:2}"
35+ /// ```
36+ Str = 1 ,
2537 /// Converts by calling `repr(<value>)`.
26- Repr = b'r' as i8 ,
38+ ///
39+ /// ```python
40+ /// f"{x!r}"
41+ /// f"{x!r:2}"
42+ /// ```
43+ Repr = 2 ,
44+ /// Converts by calling `ascii(<value>)`.
45+ ///
46+ /// ```python
47+ /// f"{x!a}"
48+ /// f"{x!a:2}"
49+ /// ```
50+ Ascii = 3 ,
51+ }
52+
53+ impl fmt:: Display for ConvertValueOparg {
54+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
55+ let out = match self {
56+ Self :: Str => "1 (str)" ,
57+ Self :: Repr => "2 (repr)" ,
58+ Self :: Ascii => "3 (ascii)" ,
59+ // We should never reach this. `FVC_NONE` are being handled by `Instruction::FormatSimple`
60+ Self :: None => "" ,
61+ } ;
62+
63+ write ! ( f, "{out}" )
64+ }
65+ }
66+
67+ impl OpArgType for ConvertValueOparg {
68+ #[ inline]
69+ fn from_op_arg ( x : u32 ) -> Option < Self > {
70+ Some ( match x {
71+ // Ruff `ConversionFlag::None` is `-1i8`,
72+ // when its converted to `u8` its value is `u8::MAX`
73+ 0 | 255 => Self :: None ,
74+ 1 => Self :: Str ,
75+ 2 => Self :: Repr ,
76+ 3 => Self :: Ascii ,
77+ _ => return None ,
78+ } )
79+ }
80+
81+ #[ inline]
82+ fn to_op_arg ( self ) -> u32 {
83+ self as u32
84+ }
2785}
2886
2987/// Resume type for the RESUME instruction
@@ -476,24 +534,6 @@ impl fmt::Display for Label {
476534 }
477535}
478536
479- impl OpArgType for ConversionFlag {
480- #[ inline]
481- fn from_op_arg ( x : u32 ) -> Option < Self > {
482- match x as u8 {
483- b's' => Some ( Self :: Str ) ,
484- b'a' => Some ( Self :: Ascii ) ,
485- b'r' => Some ( Self :: Repr ) ,
486- std:: u8:: MAX => Some ( Self :: None ) ,
487- _ => None ,
488- }
489- }
490-
491- #[ inline]
492- fn to_op_arg ( self ) -> u32 {
493- self as i8 as u8 as u32
494- }
495- }
496-
497537op_arg_enum ! (
498538 /// The kind of Raise that occurred.
499539 #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -620,6 +660,18 @@ pub enum Instruction {
620660 Continue {
621661 target : Arg < Label > ,
622662 } ,
663+ /// Convert value to a string, depending on `oparg`:
664+ ///
665+ /// ```python
666+ /// value = STACK.pop()
667+ /// result = func(value)
668+ /// STACK.append(result)
669+ /// ```
670+ ///
671+ /// Used for implementing formatted string literals (f-strings).
672+ ConvertValue {
673+ oparg : Arg < ConvertValueOparg > ,
674+ } ,
623675 CopyItem {
624676 index : Arg < u32 > ,
625677 } ,
@@ -635,24 +687,40 @@ pub enum Instruction {
635687 index : Arg < u32 > ,
636688 } ,
637689 EndAsyncFor ,
638-
639690 /// Marker bytecode for the end of a finally sequence.
640691 /// When this bytecode is executed, the eval loop does one of those things:
641692 /// - Continue at a certain bytecode position
642693 /// - Propagate the exception
643694 /// - Return from a function
644695 /// - Do nothing at all, just continue
645696 EndFinally ,
646-
647697 /// Enter a finally block, without returning, excepting, just because we are there.
648698 EnterFinally ,
649699 ExtendedArg ,
650700 ForIter {
651701 target : Arg < Label > ,
652702 } ,
653- FormatValue {
654- conversion : Arg < ConversionFlag > ,
655- } ,
703+ /// Formats the value on top of stack:
704+ ///
705+ /// ```python
706+ /// value = STACK.pop()
707+ /// result = value.__format__("")
708+ /// STACK.append(result)
709+ /// ```
710+ ///
711+ /// Used for implementing formatted string literals (f-strings).
712+ FormatSimple ,
713+ /// Formats the given value with the given format spec:
714+ ///
715+ /// ```python
716+ /// spec = STACK.pop()
717+ /// value = STACK.pop()
718+ /// result = value.__format__(spec)
719+ /// STACK.append(result)
720+ /// ```
721+ ///
722+ /// Used for implementing formatted string literals (f-strings).
723+ FormatWithSpec ,
656724 GetAIter ,
657725 GetANext ,
658726 GetAwaitable ,
@@ -727,12 +795,10 @@ pub enum Instruction {
727795 PopJumpIfTrue {
728796 target : Arg < Label > ,
729797 } ,
730-
731798 PrintExpr ,
732799 Raise {
733800 kind : Arg < RaiseKind > ,
734801 } ,
735-
736802 /// Resume execution (e.g., at function start, after yield, etc.)
737803 Resume {
738804 arg : Arg < u32 > ,
@@ -750,7 +816,6 @@ pub enum Instruction {
750816 SetFunctionAttribute {
751817 attr : Arg < MakeFunctionFlags > ,
752818 } ,
753-
754819 SetupAnnotation ,
755820 SetupAsyncWith {
756821 end : Arg < Label > ,
@@ -759,7 +824,6 @@ pub enum Instruction {
759824 SetupExcept {
760825 handler : Arg < Label > ,
761826 } ,
762-
763827 /// Setup a finally handler, which will be called whenever one of this events occurs:
764828 /// - the block is popped
765829 /// - the function returns
@@ -1656,6 +1720,9 @@ impl Instruction {
16561720 CallMethodKeyword { nargs } => -1 - ( nargs. get ( arg) as i32 ) - 3 + 1 ,
16571721 CallFunctionEx { has_kwargs } => -1 - ( has_kwargs. get ( arg) as i32 ) - 1 + 1 ,
16581722 CallMethodEx { has_kwargs } => -1 - ( has_kwargs. get ( arg) as i32 ) - 3 + 1 ,
1723+ ConvertValue { .. } => 0 ,
1724+ FormatSimple => 0 ,
1725+ FormatWithSpec => -1 ,
16591726 LoadMethod { .. } => -1 + 3 ,
16601727 ForIter { .. } => {
16611728 if jump {
@@ -1709,7 +1776,6 @@ impl Instruction {
17091776 let UnpackExArgs { before, after } = args. get ( arg) ;
17101777 -1 + before as i32 + 1 + after as i32
17111778 }
1712- FormatValue { .. } => -1 ,
17131779 PopException => 0 ,
17141780 Reverse { .. } => 0 ,
17151781 GetAwaitable => 0 ,
@@ -1824,6 +1890,7 @@ impl Instruction {
18241890 CompareOperation { op } => w ! ( CompareOperation , ?op) ,
18251891 ContainsOp ( inv) => w ! ( CONTAINS_OP , ?inv) ,
18261892 Continue { target } => w ! ( Continue , target) ,
1893+ ConvertValue { oparg } => write ! ( f, "{:pad$}{}" , "CONVERT_VALUE" , oparg. get( arg) ) ,
18271894 CopyItem { index } => w ! ( CopyItem , index) ,
18281895 DeleteAttr { idx } => w ! ( DeleteAttr , name = idx) ,
18291896 DeleteDeref ( idx) => w ! ( DeleteDeref , cell_name = idx) ,
@@ -1837,7 +1904,8 @@ impl Instruction {
18371904 EnterFinally => w ! ( EnterFinally ) ,
18381905 ExtendedArg => w ! ( ExtendedArg , Arg :: <u32 >:: marker( ) ) ,
18391906 ForIter { target } => w ! ( ForIter , target) ,
1840- FormatValue { conversion } => w ! ( FormatValue , ?conversion) ,
1907+ FormatSimple => w ! ( FORMAT_SIMPLE ) ,
1908+ FormatWithSpec => w ! ( FORMAT_WITH_SPEC ) ,
18411909 GetAIter => w ! ( GetAIter ) ,
18421910 GetANext => w ! ( GetANext ) ,
18431911 GetAwaitable => w ! ( GetAwaitable ) ,
0 commit comments