@@ -11,7 +11,7 @@ impl<'a> VM<'a> {
1111 match op {
1212 // User functions
1313 OpCode :: Call => self . exec_call ( operand, chunk, slots) ,
14- OpCode :: MakeFunction | OpCode :: MakeCoroutine => self . exec_make_function ( operand, chunk) ,
14+ OpCode :: MakeFunction | OpCode :: MakeCoroutine => self . exec_make_function ( operand, chunk, slots ) ,
1515
1616 // Pure built-ins
1717 OpCode :: CallLen => self . call_len ( ) ,
@@ -46,17 +46,35 @@ impl<'a> VM<'a> {
4646 }
4747 }
4848
49- fn exec_make_function ( & mut self , operand : u16 , chunk : & SSAChunk ) -> Result < ( ) , VmErr > {
50- // `operand` is the local index in `chunk.functions`. Translate to the
51- // global id assigned during VM init so the resulting Func resolves
52- // correctly even after escaping its defining scope.
49+ fn exec_make_function ( & mut self , operand : u16 , chunk : & SSAChunk , slots : & [ Option < Val > ] ) -> Result < ( ) , VmErr > {
5350 let global = self . fn_index
5451 . get ( & ( chunk as * const _ ) )
5552 . and_then ( |v| v. get ( operand as usize ) . copied ( ) )
56- . ok_or ( VmErr :: Runtime ( "MakeFunction: unknown function index" ) ) ?;
57- let n_defaults = self . functions [ global as usize ] . 2 as usize ;
53+ . ok_or ( VmErr :: Runtime ( "MakeFunction: unknown function index" ) ) ? as usize ;
54+
55+ let n_defaults = self . functions [ global] . 2 as usize ;
5856 let defaults = if n_defaults > 0 { self . pop_n ( n_defaults) ? } else { vec ! [ ] } ;
59- let val = self . heap . alloc ( HeapObj :: Func ( global as usize , defaults) ) ?;
57+
58+ // Build chunk name -> slot index map for capture lookup.
59+ let chunk_map: HashMap < & str , usize > = chunk. names . iter ( )
60+ . enumerate ( ) . map ( |( i, n) | ( n. as_str ( ) , i) ) . collect ( ) ;
61+
62+ // Capture free variables from the enclosing frame at function-creation time.
63+ let ( params, body, _, _) = self . functions [ global] ;
64+ let param_names: alloc:: collections:: BTreeSet < String > = params. iter ( )
65+ . map ( |p| format ! ( "{}_0" , p. trim_start_matches( '*' ) ) )
66+ . collect ( ) ;
67+ let mut captures: Vec < ( usize , Val ) > = Vec :: new ( ) ;
68+ for ( bi, bname) in body. names . iter ( ) . enumerate ( ) {
69+ if param_names. contains ( bname. as_str ( ) ) { continue ; }
70+ if let Some ( & si) = chunk_map. get ( bname. as_str ( ) ) {
71+ if let Some ( Some ( v) ) = slots. get ( si) {
72+ captures. push ( ( bi, * v) ) ;
73+ }
74+ }
75+ }
76+
77+ let val = self . heap . alloc ( HeapObj :: Func ( global, defaults, captures) ) ?;
6078 self . push ( val) ;
6179 Ok ( ( ) )
6280 }
@@ -89,8 +107,8 @@ impl<'a> VM<'a> {
89107 return self . exec_bound_method ( recv, id, positional, kw_flat) ;
90108 }
91109
92- let ( fi, captured_defaults) = match self . heap . get ( callee) {
93- HeapObj :: Func ( i, d) => ( * i, d. clone ( ) ) ,
110+ let ( fi, captured_defaults, captured_env ) = match self . heap . get ( callee) {
111+ HeapObj :: Func ( i, d, c ) => ( * i, d. clone ( ) , c . clone ( ) ) ,
94112 _ => return Err ( VmErr :: Type ( "object is not callable" ) ) ,
95113 } ;
96114
@@ -168,6 +186,26 @@ impl<'a> VM<'a> {
168186 }
169187 }
170188
189+ // Apply captured environment from function-creation time (closures).
190+ // Skip nonlocal variables — they must always come from the live enclosing
191+ // scope (handled below) so that mutations between calls are visible.
192+ let nonlocal_body_slots: alloc:: collections:: BTreeSet < usize > = body. nonlocals . iter ( )
193+ . flat_map ( |base| {
194+ body. names . iter ( ) . enumerate ( ) . filter_map ( |( i, n) | {
195+ n. rfind ( '_' ) . filter ( |& p| n[ p+1 ..] . parse :: < u32 > ( ) . is_ok ( ) )
196+ . filter ( |& p| & n[ ..p] == base. as_str ( ) )
197+ . map ( |_| i)
198+ } )
199+ } )
200+ . collect ( ) ;
201+
202+ for ( bi, val) in & captured_env {
203+ if nonlocal_body_slots. contains ( bi) { continue ; }
204+ if * bi < fn_slots. len ( ) && fn_slots[ * bi] . is_none ( ) {
205+ fn_slots[ * bi] = Some ( * val) ;
206+ }
207+ }
208+
171209 // Captura del enclosing scope: nombres del frame que llama (`chunk`),
172210 // valores de sus slots. `.get()` defensivo si las longitudes divergen.
173211 for ( si, sv) in slots. iter ( ) . enumerate ( ) {
@@ -555,7 +593,7 @@ impl<'a> VM<'a> {
555593 self . push ( Val :: int ( idx) ) ;
556594 Ok ( ( ) )
557595 }
558- ListCount => {
596+ ListCount => {
559597 if positional. len ( ) != 1 {
560598 return Err ( VmErr :: Type ( "count() takes exactly one argument" ) ) ;
561599 }
@@ -625,6 +663,132 @@ ListCount => {
625663 self . push ( result) ;
626664 Ok ( ( ) )
627665 }
666+ DictSetDefault => {
667+ if positional. is_empty ( ) {
668+ return Err ( VmErr :: Type ( "setdefault() requires at least one argument" ) ) ;
669+ }
670+ let key = positional[ 0 ] ;
671+ let default = if positional. len ( ) > 1 { positional[ 1 ] } else { Val :: none ( ) } ;
672+ let result = match self . heap . get_mut ( recv) {
673+ HeapObj :: Dict ( rc) => {
674+ let already = rc. borrow ( ) . get ( & key) . copied ( ) ;
675+ if let Some ( v) = already { v } else {
676+ rc. borrow_mut ( ) . insert ( key, default) ;
677+ default
678+ }
679+ }
680+ _ => return Err ( VmErr :: Type ( "setdefault: receiver is not a dict" ) ) ,
681+ } ;
682+ self . mark_impure ( ) ;
683+ self . push ( result) ;
684+ Ok ( ( ) )
685+ }
686+ StrLstrip => {
687+ let s = self . recv_str ( recv) ?;
688+ let result = if positional. is_empty ( ) { s. trim_start ( ) . to_string ( ) }
689+ else { let p = self . val_to_str ( positional[ 0 ] ) ?; s. trim_start_matches ( |c| p. contains ( c) ) . to_string ( ) } ;
690+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
691+ self . push ( val) ; Ok ( ( ) )
692+ }
693+ StrRstrip => {
694+ let s = self . recv_str ( recv) ?;
695+ let result = if positional. is_empty ( ) { s. trim_end ( ) . to_string ( ) }
696+ else { let p = self . val_to_str ( positional[ 0 ] ) ?; s. trim_end_matches ( |c| p. contains ( c) ) . to_string ( ) } ;
697+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
698+ self . push ( val) ; Ok ( ( ) )
699+ }
700+ StrIsDigit => {
701+ let s = self . recv_str ( recv) ?;
702+ self . push ( Val :: bool ( !s. is_empty ( ) && s. chars ( ) . all ( |c| c. is_ascii_digit ( ) ) ) ) ;
703+ Ok ( ( ) )
704+ }
705+ StrIsAlpha => {
706+ let s = self . recv_str ( recv) ?;
707+ self . push ( Val :: bool ( !s. is_empty ( ) && s. chars ( ) . all ( |c| c. is_alphabetic ( ) ) ) ) ;
708+ Ok ( ( ) )
709+ }
710+ StrIsAlnum => {
711+ let s = self . recv_str ( recv) ?;
712+ self . push ( Val :: bool ( !s. is_empty ( ) && s. chars ( ) . all ( |c| c. is_alphanumeric ( ) ) ) ) ;
713+ Ok ( ( ) )
714+ }
715+ StrCapitalize => {
716+ let s = self . recv_str ( recv) ?;
717+ let result = if s. is_empty ( ) { s } else {
718+ let mut cs = s. chars ( ) ;
719+ cs. next ( ) . unwrap ( ) . to_uppercase ( ) . to_string ( ) + & cs. as_str ( ) . to_lowercase ( )
720+ } ;
721+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
722+ self . push ( val) ; Ok ( ( ) )
723+ }
724+ StrTitle => {
725+ let s = self . recv_str ( recv) ?;
726+ let result = s. split_whitespace ( )
727+ . map ( |w| { let mut cs = w. chars ( ) ; cs. next ( ) . map ( |c| c. to_uppercase ( ) . to_string ( ) + cs. as_str ( ) ) . unwrap_or_default ( ) } )
728+ . collect :: < Vec < _ > > ( ) . join ( " " ) ;
729+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
730+ self . push ( val) ; Ok ( ( ) )
731+ }
732+ StrCenter => {
733+ if positional. is_empty ( ) { return Err ( VmErr :: Type ( "center() requires at least one argument" ) ) ; }
734+ let s = self . recv_str ( recv) ?;
735+ if !positional[ 0 ] . is_int ( ) { return Err ( VmErr :: Type ( "center() width must be an integer" ) ) ; }
736+ let width = positional[ 0 ] . as_int ( ) as usize ;
737+ let fill = if positional. len ( ) > 1 { self . val_to_str ( positional[ 1 ] ) ?. chars ( ) . next ( ) . unwrap_or ( ' ' ) } else { ' ' } ;
738+ let pad = width. saturating_sub ( s. len ( ) ) ;
739+ let left = pad / 2 ;
740+ let right = pad - left;
741+ let result = fill. to_string ( ) . repeat ( left) + & s + & fill. to_string ( ) . repeat ( right) ;
742+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
743+ self . push ( val) ; Ok ( ( ) )
744+ }
745+ StrZfill => {
746+ if positional. is_empty ( ) || !positional[ 0 ] . is_int ( ) {
747+ return Err ( VmErr :: Type ( "zfill() requires an integer argument" ) ) ;
748+ }
749+ let s = self . recv_str ( recv) ?;
750+ let width = positional[ 0 ] . as_int ( ) as usize ;
751+ let result = if s. len ( ) >= width { s } else {
752+ let pad = "0" . repeat ( width - s. len ( ) ) ;
753+ if s. starts_with ( '+' ) || s. starts_with ( '-' ) {
754+ s[ ..1 ] . to_string ( ) + & pad + & s[ 1 ..]
755+ } else { pad + & s }
756+ } ;
757+ let val = self . heap . alloc ( HeapObj :: Str ( result) ) ?;
758+ self . push ( val) ; Ok ( ( ) )
759+ }
760+ ListExtend => {
761+ if positional. len ( ) != 1 { return Err ( VmErr :: Type ( "extend() takes exactly one argument" ) ) ; }
762+ let items: Vec < Val > = if positional[ 0 ] . is_heap ( ) {
763+ match self . heap . get ( positional[ 0 ] ) {
764+ HeapObj :: List ( rc) => rc. borrow ( ) . clone ( ) ,
765+ HeapObj :: Tuple ( v) => v. clone ( ) ,
766+ _ => return Err ( VmErr :: Type ( "extend() argument must be iterable" ) ) ,
767+ }
768+ } else { return Err ( VmErr :: Type ( "extend() argument must be iterable" ) ) ; } ;
769+ match self . heap . get_mut ( recv) {
770+ HeapObj :: List ( rc) => rc. borrow_mut ( ) . extend_from_slice ( & items) ,
771+ _ => return Err ( VmErr :: Type ( "extend: receiver is not a list" ) ) ,
772+ }
773+ self . mark_impure ( ) ;
774+ self . push ( Val :: none ( ) ) ; Ok ( ( ) )
775+ }
776+ ListClear => {
777+ match self . heap . get_mut ( recv) {
778+ HeapObj :: List ( rc) => rc. borrow_mut ( ) . clear ( ) ,
779+ _ => return Err ( VmErr :: Type ( "clear: receiver is not a list" ) ) ,
780+ }
781+ self . mark_impure ( ) ;
782+ self . push ( Val :: none ( ) ) ; Ok ( ( ) )
783+ }
784+ ListCopy => {
785+ let items = match self . heap . get ( recv) {
786+ HeapObj :: List ( rc) => rc. borrow ( ) . clone ( ) ,
787+ _ => return Err ( VmErr :: Type ( "copy: receiver is not a list" ) ) ,
788+ } ;
789+ let val = self . heap . alloc ( HeapObj :: List ( Rc :: new ( RefCell :: new ( items) ) ) ) ?;
790+ self . push ( val) ; Ok ( ( ) )
791+ }
628792 }
629793 }
630794
0 commit comments