@@ -24,13 +24,13 @@ use num_traits::{Num, ToPrimitive};
2424use ruff_python_ast:: {
2525 Alias , Arguments , BoolOp , CmpOp , Comprehension , ConversionFlag , DebugText , Decorator , DictItem ,
2626 ExceptHandler , ExceptHandlerExceptHandler , Expr , ExprAttribute , ExprBoolOp , ExprContext ,
27- ExprFString , ExprList , ExprName , ExprSlice , ExprStarred , ExprSubscript , ExprTuple , ExprUnaryOp ,
28- FString , FStringFlags , FStringPart , Identifier , Int , InterpolatedStringElement ,
27+ ExprFString , ExprList , ExprName , ExprSlice , ExprStarred , ExprSubscript , ExprTString , ExprTuple ,
28+ ExprUnaryOp , FString , FStringFlags , FStringPart , Identifier , Int , InterpolatedStringElement ,
2929 InterpolatedStringElements , Keyword , MatchCase , ModExpression , ModModule , Operator , Parameters ,
3030 Pattern , PatternMatchAs , PatternMatchClass , PatternMatchMapping , PatternMatchOr ,
3131 PatternMatchSequence , PatternMatchSingleton , PatternMatchStar , PatternMatchValue , Singleton ,
32- Stmt , StmtExpr , TypeParam , TypeParamParamSpec , TypeParamTypeVar , TypeParamTypeVarTuple ,
33- TypeParams , UnaryOp , WithItem ,
32+ Stmt , StmtExpr , TString , TypeParam , TypeParamParamSpec , TypeParamTypeVar ,
33+ TypeParamTypeVarTuple , TypeParams , UnaryOp , WithItem ,
3434 visitor:: { Visitor , walk_expr} ,
3535} ;
3636use ruff_text_size:: { Ranged , TextRange } ;
@@ -6282,8 +6282,8 @@ impl Compiler {
62826282 Expr :: FString ( fstring) => {
62836283 self . compile_expr_fstring ( fstring) ?;
62846284 }
6285- Expr :: TString ( _ ) => {
6286- return Err ( self . error ( CodegenErrorType :: NotImplementedYet ) ) ;
6285+ Expr :: TString ( tstring ) => {
6286+ self . compile_expr_tstring ( tstring ) ? ;
62876287 }
62886288 Expr :: StringLiteral ( string) => {
62896289 let value = string. value . to_str ( ) ;
@@ -7466,6 +7466,114 @@ impl Compiler {
74667466
74677467 Ok ( ( ) )
74687468 }
7469+
7470+ fn compile_expr_tstring ( & mut self , expr_tstring : & ExprTString ) -> CompileResult < ( ) > {
7471+ // TStringValue can contain multiple TString parts (implicit concatenation)
7472+ // Each TString part should be compiled and the results merged into a single Template
7473+ let tstring_value = & expr_tstring. value ;
7474+
7475+ // Collect all strings and compile all interpolations
7476+ let mut all_strings: Vec < Wtf8Buf > = Vec :: new ( ) ;
7477+ let mut current_string = Wtf8Buf :: new ( ) ;
7478+ let mut interp_count: u32 = 0 ;
7479+
7480+ for tstring in tstring_value. iter ( ) {
7481+ self . compile_tstring_into (
7482+ tstring,
7483+ & mut all_strings,
7484+ & mut current_string,
7485+ & mut interp_count,
7486+ ) ?;
7487+ }
7488+
7489+ // Add trailing string
7490+ all_strings. push ( std:: mem:: take ( & mut current_string) ) ;
7491+
7492+ // Now build the Template:
7493+ // Stack currently has all interpolations from compile_tstring_into calls
7494+
7495+ // 1. Build interpolations tuple from the interpolations on the stack
7496+ emit ! ( self , Instruction :: BuildTuple { size: interp_count } ) ;
7497+
7498+ // 2. Load all string parts
7499+ let string_count: u32 = all_strings
7500+ . len ( )
7501+ . try_into ( )
7502+ . expect ( "t-string string count overflowed" ) ;
7503+ for s in & all_strings {
7504+ self . emit_load_const ( ConstantData :: Str { value : s. clone ( ) } ) ;
7505+ }
7506+
7507+ // 3. Build strings tuple
7508+ emit ! ( self , Instruction :: BuildTuple { size: string_count } ) ;
7509+
7510+ // 4. Swap so strings is below interpolations: [interps, strings] -> [strings, interps]
7511+ emit ! ( self , Instruction :: Swap { index: 2 } ) ;
7512+
7513+ // 5. Build the Template
7514+ emit ! ( self , Instruction :: BuildTemplate ) ;
7515+
7516+ Ok ( ( ) )
7517+ }
7518+
7519+ fn compile_tstring_into (
7520+ & mut self ,
7521+ tstring : & TString ,
7522+ strings : & mut Vec < Wtf8Buf > ,
7523+ current_string : & mut Wtf8Buf ,
7524+ interp_count : & mut u32 ,
7525+ ) -> CompileResult < ( ) > {
7526+ for element in & tstring. elements {
7527+ match element {
7528+ InterpolatedStringElement :: Literal ( lit) => {
7529+ // Accumulate literal parts into current_string
7530+ current_string. push_str ( & lit. value ) ;
7531+ }
7532+ InterpolatedStringElement :: Interpolation ( interp) => {
7533+ // Finish current string segment
7534+ strings. push ( std:: mem:: take ( current_string) ) ;
7535+
7536+ // Compile the interpolation value
7537+ self . compile_expression ( & interp. expression ) ?;
7538+
7539+ // Load the expression source string
7540+ let expr_range = interp. expression . range ( ) ;
7541+ let expr_source = self . source_file . slice ( expr_range) ;
7542+ self . emit_load_const ( ConstantData :: Str {
7543+ value : expr_source. to_string ( ) . into ( ) ,
7544+ } ) ;
7545+
7546+ // Determine conversion code
7547+ let conversion: u32 = match interp. conversion {
7548+ ConversionFlag :: None => 0 ,
7549+ ConversionFlag :: Str => 1 ,
7550+ ConversionFlag :: Repr => 2 ,
7551+ ConversionFlag :: Ascii => 3 ,
7552+ } ;
7553+
7554+ // Handle format_spec
7555+ let has_format_spec = interp. format_spec . is_some ( ) ;
7556+ if let Some ( format_spec) = & interp. format_spec {
7557+ // Compile format_spec as a string using fstring element compilation
7558+ // Use default FStringFlags since format_spec syntax is independent of t-string flags
7559+ self . compile_fstring_elements (
7560+ FStringFlags :: empty ( ) ,
7561+ & format_spec. elements ,
7562+ ) ?;
7563+ }
7564+
7565+ // Emit BUILD_INTERPOLATION
7566+ // oparg encoding: (conversion << 2) | has_format_spec
7567+ let oparg = ( conversion << 2 ) | ( has_format_spec as u32 ) ;
7568+ emit ! ( self , Instruction :: BuildInterpolation { oparg } ) ;
7569+
7570+ * interp_count += 1 ;
7571+ }
7572+ }
7573+ }
7574+
7575+ Ok ( ( ) )
7576+ }
74697577}
74707578
74717579trait EmitArg < Arg : OpArgType > {
0 commit comments