@@ -26,9 +26,10 @@ use ruff_python_ast::{
26
26
ExprFString , ExprList , ExprName , ExprSlice , ExprStarred , ExprSubscript , ExprTuple , ExprUnaryOp ,
27
27
FString , FStringElement , FStringElements , FStringFlags , FStringPart , Identifier , Int , Keyword ,
28
28
MatchCase , ModExpression , ModModule , Operator , Parameters , Pattern , PatternMatchAs ,
29
- PatternMatchClass , PatternMatchOr , PatternMatchSequence , PatternMatchSingleton ,
30
- PatternMatchStar , PatternMatchValue , Singleton , Stmt , StmtExpr , TypeParam , TypeParamParamSpec ,
31
- TypeParamTypeVar , TypeParamTypeVarTuple , TypeParams , UnaryOp , WithItem ,
29
+ PatternMatchClass , PatternMatchMapping , PatternMatchOr , PatternMatchSequence ,
30
+ PatternMatchSingleton , PatternMatchStar , PatternMatchValue , Singleton , Stmt , StmtExpr ,
31
+ TypeParam , TypeParamParamSpec , TypeParamTypeVar , TypeParamTypeVarTuple , TypeParams , UnaryOp ,
32
+ WithItem ,
32
33
} ;
33
34
use ruff_text_size:: { Ranged , TextRange } ;
34
35
use rustpython_compiler_core:: {
@@ -3149,6 +3150,11 @@ impl Compiler {
3149
3150
3150
3151
/// Duplicate the effect of Python 3.10's ROT_* instructions using SWAPs.
3151
3152
fn pattern_helper_rotate ( & mut self , mut count : usize ) -> CompileResult < ( ) > {
3153
+ // Rotate TOS (top of stack) to position `count` down
3154
+ // This is done by a series of swaps
3155
+ // For count=1, no rotation needed (already at top)
3156
+ // For count=2, swap TOS with item 1 position down
3157
+ // For count=3, swap TOS with item 2 positions down, then with item 1 position down
3152
3158
while count > 1 {
3153
3159
// Emit a SWAP instruction with the current count.
3154
3160
emit ! (
@@ -3442,8 +3448,6 @@ impl Compiler {
3442
3448
} ) ;
3443
3449
}
3444
3450
3445
- use bytecode:: TestOperator :: * ;
3446
-
3447
3451
// Emit instructions:
3448
3452
// 1. Load the new tuple of attribute names.
3449
3453
self . emit_load_const ( ConstantData :: Tuple {
@@ -3456,7 +3460,12 @@ impl Compiler {
3456
3460
// 4. Load None.
3457
3461
self . emit_load_const ( ConstantData :: None ) ;
3458
3462
// 5. Compare with IS_OP 1.
3459
- emit ! ( self , Instruction :: TestOperation { op: IsNot } ) ;
3463
+ emit ! (
3464
+ self ,
3465
+ Instruction :: TestOperation {
3466
+ op: bytecode:: TestOperator :: IsNot
3467
+ }
3468
+ ) ;
3460
3469
3461
3470
// At this point the TOS is a tuple of (nargs + n_attrs) attributes (or None).
3462
3471
pc. on_top += 1 ;
@@ -3487,123 +3496,183 @@ impl Compiler {
3487
3496
Ok ( ( ) )
3488
3497
}
3489
3498
3490
- // fn compile_pattern_mapping(&mut self, p: &PatternMatchMapping, pc: &mut PatternContext) -> CompileResult<()> {
3491
- // // Ensure the pattern is a mapping pattern.
3492
- // let mapping = p; // Extract MatchMapping-specific data.
3493
- // let keys = &mapping.keys;
3494
- // let patterns = &mapping.patterns;
3495
- // let size = keys.len();
3496
- // let n_patterns = patterns.len();
3497
-
3498
- // if size != n_patterns {
3499
- // panic!("keys ({}) / patterns ({}) length mismatch in mapping pattern", size, n_patterns);
3500
- // // return self.compiler_error(
3501
- // // &format!("keys ({}) / patterns ({}) length mismatch in mapping pattern", size, n_patterns)
3502
- // // );
3503
- // }
3504
-
3505
- // // A double-star target is present if `rest` is set.
3506
- // let star_target = mapping.rest;
3507
-
3508
- // // Keep the subject on top during the mapping and length checks.
3509
- // pc.on_top += 1;
3510
- // emit!(self, Instruction::MatchMapping);
3511
- // self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?;
3512
-
3513
- // // If the pattern is just "{}" (empty mapping) and there's no star target,
3514
- // // we're done—pop the subject.
3515
- // if size == 0 && star_target.is_none() {
3516
- // pc.on_top -= 1;
3517
- // emit!(self, Instruction::Pop);
3518
- // return Ok(());
3519
- // }
3520
-
3521
- // // If there are any keys, perform a length check.
3522
- // if size != 0 {
3523
- // emit!(self, Instruction::GetLen);
3524
- // self.emit_load_const(ConstantData::Integer { value: size.into() });
3525
- // emit!(self, Instruction::CompareOperation { op: ComparisonOperator::GreaterOrEqual });
3526
- // self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?;
3527
- // }
3528
-
3529
- // // Check that the number of sub-patterns is not absurd.
3530
- // if size.saturating_sub(1) > (i32::MAX as usize) {
3531
- // panic!("too many sub-patterns in mapping pattern");
3532
- // // return self.compiler_error("too many sub-patterns in mapping pattern");
3533
- // }
3534
-
3535
- // // Collect all keys into a set for duplicate checking.
3536
- // let mut seen = HashSet::new();
3537
-
3538
- // // For each key, validate it and check for duplicates.
3539
- // for (i, key) in keys.iter().enumerate() {
3540
- // if let Some(key_val) = key.as_literal_expr() {
3541
- // let in_seen = seen.contains(&key_val);
3542
- // if in_seen {
3543
- // panic!("mapping pattern checks duplicate key: {:?}", key_val);
3544
- // // return self.compiler_error(format!("mapping pattern checks duplicate key: {:?}", key_val));
3545
- // }
3546
- // seen.insert(key_val);
3547
- // } else if !key.is_attribute_expr() {
3548
- // panic!("mapping pattern keys may only match literals and attribute lookups");
3549
- // // return self.compiler_error("mapping pattern keys may only match literals and attribute lookups");
3550
- // }
3551
-
3552
- // // Visit the key expression.
3553
- // self.compile_expression(key)?;
3554
- // }
3555
- // // Drop the set (its resources will be freed automatically).
3556
-
3557
- // // Build a tuple of keys and emit MATCH_KEYS.
3558
- // emit!(self, Instruction::BuildTuple { size: size as u32 });
3559
- // emit!(self, Instruction::MatchKeys);
3560
- // // Now, on top of the subject there are two new tuples: one of keys and one of values.
3561
- // pc.on_top += 2;
3562
-
3563
- // // Prepare for matching the values.
3564
- // emit!(self, Instruction::CopyItem { index: 1_u32 });
3565
- // self.emit_load_const(ConstantData::None);
3566
- // // TODO: should be is
3567
- // emit!(self, Instruction::TestOperation::IsNot);
3568
- // self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?;
3569
-
3570
- // // Unpack the tuple of values.
3571
- // emit!(self, Instruction::UnpackSequence { size: size as u32 });
3572
- // pc.on_top += size.saturating_sub(1);
3573
-
3574
- // // Compile each subpattern in "subpattern" mode.
3575
- // for pattern in patterns {
3576
- // pc.on_top = pc.on_top.saturating_sub(1);
3577
- // self.compile_pattern_subpattern(pattern, pc)?;
3578
- // }
3579
-
3580
- // // Consume the tuple of keys and the subject.
3581
- // pc.on_top = pc.on_top.saturating_sub(2);
3582
- // if let Some(star_target) = star_target {
3583
- // // If we have a starred name, bind a dict of remaining items to it.
3584
- // // This sequence of instructions performs:
3585
- // // rest = dict(subject)
3586
- // // for key in keys: del rest[key]
3587
- // emit!(self, Instruction::BuildMap { size: 0 }); // Build an empty dict.
3588
- // emit!(self, Instruction::Swap(3)); // Rearrange stack: [empty, keys, subject]
3589
- // emit!(self, Instruction::DictUpdate { size: 2 }); // Update dict with subject.
3590
- // emit!(self, Instruction::UnpackSequence { size: size as u32 }); // Unpack keys.
3591
- // let mut remaining = size;
3592
- // while remaining > 0 {
3593
- // emit!(self, Instruction::CopyItem { index: 1 + remaining as u32 }); // Duplicate subject copy.
3594
- // emit!(self, Instruction::Swap { index: 2_u32 }); // Bring key to top.
3595
- // emit!(self, Instruction::DeleteSubscript); // Delete key from dict.
3596
- // remaining -= 1;
3597
- // }
3598
- // // Bind the dict to the starred target.
3599
- // self.pattern_helper_store_name(Some(&star_target), pc)?;
3600
- // } else {
3601
- // // No starred target: just pop the tuple of keys and the subject.
3602
- // emit!(self, Instruction::Pop);
3603
- // emit!(self, Instruction::Pop);
3604
- // }
3605
- // Ok(())
3606
- // }
3499
+ fn compile_pattern_mapping (
3500
+ & mut self ,
3501
+ p : & PatternMatchMapping ,
3502
+ pc : & mut PatternContext ,
3503
+ ) -> CompileResult < ( ) > {
3504
+ let mapping = p;
3505
+ let keys = & mapping. keys ;
3506
+ let patterns = & mapping. patterns ;
3507
+ let size = keys. len ( ) ;
3508
+ let n_patterns = patterns. len ( ) ;
3509
+
3510
+ if size != n_patterns {
3511
+ return Err ( self . error ( CodegenErrorType :: SyntaxError ( format ! (
3512
+ "keys ({size}) / patterns ({n_patterns}) length mismatch in mapping pattern"
3513
+ ) ) ) ) ;
3514
+ }
3515
+
3516
+ let star_target = & mapping. rest ;
3517
+
3518
+ // Step 1: Check if subject is a mapping
3519
+ // Stack: [subject]
3520
+ // We need to keep the subject on top during the mapping and length checks
3521
+ pc. on_top += 1 ;
3522
+
3523
+ emit ! ( self , Instruction :: MatchMapping ) ;
3524
+ // Stack: [subject, bool]
3525
+
3526
+ // If not a mapping, fail (jump and pop subject)
3527
+ self . jump_to_fail_pop ( pc, JumpOp :: PopJumpIfFalse ) ?;
3528
+ // Stack: [subject] (on success)
3529
+
3530
+ // Step 2: If empty pattern with no star, consume subject
3531
+ if size == 0 && star_target. is_none ( ) {
3532
+ // If the pattern is just "{}", we're done! Pop the subject:
3533
+ pc. on_top -= 1 ;
3534
+ emit ! ( self , Instruction :: Pop ) ;
3535
+ return Ok ( ( ) ) ;
3536
+ }
3537
+
3538
+ // Step 3: Check mapping has enough keys
3539
+ if size != 0 {
3540
+ // Stack: [subject]
3541
+ emit ! ( self , Instruction :: GetLen ) ;
3542
+ // Stack: [subject, len]
3543
+ self . emit_load_const ( ConstantData :: Integer { value : size. into ( ) } ) ;
3544
+ // Stack: [subject, len, size]
3545
+ emit ! (
3546
+ self ,
3547
+ Instruction :: CompareOperation {
3548
+ op: ComparisonOperator :: GreaterOrEqual
3549
+ }
3550
+ ) ;
3551
+ // Stack: [subject, bool]
3552
+
3553
+ // If not enough keys, fail
3554
+ self . jump_to_fail_pop ( pc, JumpOp :: PopJumpIfFalse ) ?;
3555
+ // Stack: [subject]
3556
+ }
3557
+
3558
+ // Step 4: Build keys tuple
3559
+ if size. saturating_sub ( 1 ) > ( i32:: MAX as usize ) {
3560
+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
3561
+ "too many sub-patterns in mapping pattern" . to_string ( ) ,
3562
+ ) ) ) ;
3563
+ }
3564
+
3565
+ // Validate and compile keys
3566
+ // NOTE: RustPython difference - using HashSet<String> for duplicate checking
3567
+ // CPython uses PySet with actual Python objects
3568
+ let mut seen = std:: collections:: HashSet :: new ( ) ;
3569
+
3570
+ for key in keys. iter ( ) {
3571
+ // Validate key
3572
+ let is_constant = matches ! (
3573
+ key,
3574
+ Expr :: NumberLiteral ( _)
3575
+ | Expr :: StringLiteral ( _)
3576
+ | Expr :: BytesLiteral ( _)
3577
+ | Expr :: BooleanLiteral ( _)
3578
+ | Expr :: NoneLiteral ( _)
3579
+ ) ;
3580
+ let is_attribute = matches ! ( key, Expr :: Attribute ( _) ) ;
3581
+
3582
+ if is_constant {
3583
+ let key_repr = format ! ( "{key:?}" ) ;
3584
+ if seen. contains ( & key_repr) {
3585
+ return Err ( self . error ( CodegenErrorType :: SyntaxError ( format ! (
3586
+ "mapping pattern checks duplicate key: {key_repr:?}"
3587
+ ) ) ) ) ;
3588
+ }
3589
+ seen. insert ( key_repr) ;
3590
+ } else if !is_attribute {
3591
+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
3592
+ "mapping pattern keys may only match literals and attribute lookups"
3593
+ . to_string ( ) ,
3594
+ ) ) ) ;
3595
+ }
3596
+
3597
+ // Compile key expression
3598
+ self . compile_expression ( key) ?;
3599
+ }
3600
+ // Stack: [subject, key1, key2, ...]
3601
+
3602
+ // Build tuple of keys
3603
+ emit ! (
3604
+ self ,
3605
+ Instruction :: BuildTuple {
3606
+ size: u32 :: try_from( size) . expect( "too many keys in mapping pattern" )
3607
+ }
3608
+ ) ;
3609
+ // Stack: [subject, keys_tuple]
3610
+
3611
+ // Step 5: Extract values using MATCH_KEYS
3612
+ emit ! ( self , Instruction :: MatchKeys ) ;
3613
+ // Stack: [subject, keys_or_none, values_or_none]
3614
+ // There's now a tuple of keys and a tuple of values on top of the subject
3615
+ pc. on_top += 2 ;
3616
+
3617
+ emit ! ( self , Instruction :: CopyItem { index: 1_u32 } ) ; // Copy values_or_none (TOS)
3618
+ // Stack: [subject, keys_or_none, values_or_none, values_or_none]
3619
+
3620
+ self . emit_load_const ( ConstantData :: None ) ;
3621
+ // Stack: [subject, keys_or_none, values_or_none, values_or_none, None]
3622
+
3623
+ emit ! (
3624
+ self ,
3625
+ Instruction :: TestOperation {
3626
+ op: bytecode:: TestOperator :: IsNot
3627
+ }
3628
+ ) ;
3629
+ // Stack: [subject, keys_or_none, values_or_none, bool]
3630
+
3631
+ // If values_or_none is None, fail (need to clean up 3 items: values_or_none, keys_or_none, subject)
3632
+ self . jump_to_fail_pop ( pc, JumpOp :: PopJumpIfFalse ) ?;
3633
+ // Stack: [subject, keys_or_none, values_or_none] (on success)
3634
+
3635
+ // Step 7: Process patterns
3636
+ if size > 0 {
3637
+ // Unpack values tuple
3638
+ emit ! (
3639
+ self ,
3640
+ Instruction :: UnpackSequence {
3641
+ size: u32 :: try_from( size) . expect( "too many values in mapping pattern" )
3642
+ }
3643
+ ) ;
3644
+ // Stack: [subject, keys_or_none, value_n, ..., value_1]
3645
+ // After UNPACK_SEQUENCE, we have size values on the stack
3646
+ pc. on_top += size - 1 ;
3647
+
3648
+ // Process each pattern with compile_pattern_subpattern
3649
+ for pattern in patterns. iter ( ) {
3650
+ pc. on_top -= 1 ;
3651
+ self . compile_pattern_subpattern ( pattern, pc) ?;
3652
+ }
3653
+ }
3654
+
3655
+ // After all patterns processed, adjust on_top for subject and keys_or_none
3656
+ pc. on_top -= 2 ;
3657
+
3658
+ // Step 8: Clean up
3659
+ // If we get this far, it's a match! Whatever happens next should consume
3660
+ // the tuple of keys and the subject
3661
+
3662
+ if let Some ( _star_target) = star_target {
3663
+ // TODO: Implement **rest pattern support
3664
+ // This would involve BUILD_MAP, DICT_UPDATE, etc.
3665
+ return Err ( self . error ( CodegenErrorType :: SyntaxError (
3666
+ "**rest pattern in mapping not yet implemented" . to_string ( ) ,
3667
+ ) ) ) ;
3668
+ } else {
3669
+ // Pop the tuple of keys
3670
+ emit ! ( self , Instruction :: Pop ) ;
3671
+ // Pop the subject
3672
+ emit ! ( self , Instruction :: Pop ) ;
3673
+ }
3674
+ Ok ( ( ) )
3675
+ }
3607
3676
3608
3677
fn compile_pattern_or (
3609
3678
& mut self ,
@@ -3848,7 +3917,9 @@ impl Compiler {
3848
3917
Pattern :: MatchSequence ( pattern_type) => {
3849
3918
self . compile_pattern_sequence ( pattern_type, pattern_context)
3850
3919
}
3851
- // Pattern::MatchMapping(pattern_type) => self.compile_pattern_mapping(pattern_type, pattern_context),
3920
+ Pattern :: MatchMapping ( pattern_type) => {
3921
+ self . compile_pattern_mapping ( pattern_type, pattern_context)
3922
+ }
3852
3923
Pattern :: MatchClass ( pattern_type) => {
3853
3924
self . compile_pattern_class ( pattern_type, pattern_context)
3854
3925
}
@@ -3861,11 +3932,6 @@ impl Compiler {
3861
3932
Pattern :: MatchOr ( pattern_type) => {
3862
3933
self . compile_pattern_or ( pattern_type, pattern_context)
3863
3934
}
3864
- _ => {
3865
- // The eprintln gives context as to which pattern type is not implemented.
3866
- eprintln ! ( "not implemented pattern type: {pattern_type:?}" ) ;
3867
- Err ( self . error ( CodegenErrorType :: NotImplementedYet ) )
3868
- }
3869
3935
}
3870
3936
}
3871
3937
0 commit comments