@@ -380,6 +380,7 @@ impl<'a> Compiler<'a> {
380380 self . emit ( Instruction :: Pop , loc) ;
381381 }
382382 Statement :: Loop ( e) => self . compile_loop ( e) ,
383+ Statement :: ForIn ( e) => self . compile_for_in ( e) ,
383384 Statement :: Assign ( e) => self . compile_assign ( e) ,
384385 Statement :: Break ( loc) => {
385386 let end_label = self
@@ -858,6 +859,130 @@ impl<'a> Compiler<'a> {
858859 ) ;
859860 }
860861
862+ fn compile_for_in ( & mut self , for_in : ForInLoop ) {
863+ let loc = for_in. loc ;
864+ let unique_id = self . unique_id ( ) ;
865+ let loop_start = format ! ( "for_in_start_{}" , unique_id) ;
866+ let loop_end = format ! ( "for_in_end_{}" , unique_id) ;
867+ let iter_var = format ! ( "@iter_{}" , unique_id) ;
868+
869+ self . begin_scope ( ) ;
870+
871+ // 1. Compile iterable, call :iter() on it, and store it in a hidden local variable
872+ self . compile_expression ( for_in. iterable ) ;
873+ self . emit ( Instruction :: GetMethod ( "iter" . to_string ( ) ) , loc) ; // [iterable, iter_fn, iterable]
874+ self . emit ( Instruction :: CallStack ( 1 ) , loc) ; // [coroutine]
875+ let iter_loc = self . define_variable ( iter_var) ;
876+ match iter_loc {
877+ VarLocation :: Local ( offset) => {
878+ self . emit ( Instruction :: MovePlusFP ( offset as usize ) , loc) ;
879+ }
880+ _ => unreachable ! ( "Hidden iterator variable must be local" ) ,
881+ }
882+
883+ // loop_start label
884+ self . program . syms . insert (
885+ loop_start. clone ( ) ,
886+ Symbol {
887+ location : self . program . instructions . len ( ) as i32 ,
888+ narguments : 0 ,
889+ nlocals : 0 ,
890+ upvalues : Vec :: new ( ) ,
891+ } ,
892+ ) ;
893+
894+ // 2. Check status: coroutine.status(iterable)
895+ self . emit ( Instruction :: Load ( "coroutine" . to_string ( ) ) , loc) ; // [coroutine]
896+ self . emit ( Instruction :: GetField ( "status" . to_string ( ) ) , loc) ; // [status_fn]
897+ match iter_loc {
898+ VarLocation :: Local ( offset) => {
899+ self . emit ( Instruction :: DupPlusFP ( offset) , loc) ; // [status_fn, iterable]
900+ }
901+ _ => unreachable ! ( ) ,
902+ }
903+ self . emit ( Instruction :: CallStack ( 1 ) , loc) ; // [status_val]
904+ self . emit ( Instruction :: Push ( crate :: value:: Value :: string ( "dead" . to_string ( ) ) ) , loc) ;
905+ self . emit ( Instruction :: Equal , loc) ;
906+ self . emit ( Instruction :: JumpIfTrue ( loop_end. clone ( ) ) , loc) ;
907+
908+ self . emit ( Instruction :: Load ( "coroutine" . to_string ( ) ) , loc) ; // [resume_fn, iterable, coroutine]
909+ self . emit ( Instruction :: GetField ( "resume" . to_string ( ) ) , loc) ; // [resume_fn, iterable, resume_fn]
910+ match iter_loc {
911+ VarLocation :: Local ( offset) => {
912+ self . emit ( Instruction :: DupPlusFP ( offset) , loc) ; // [resume_fn, iterable, resume_fn, iterable]
913+ }
914+ _ => unreachable ! ( ) ,
915+ }
916+ self . emit ( Instruction :: CallStack ( 1 ) , loc) ; // [resume_fn, iterable, yielded_val]
917+
918+ // 3.5 Check status again after resume - if it just died, the returned value is the final result, not an iteration item.
919+ self . emit ( Instruction :: Load ( "coroutine" . to_string ( ) ) , loc) ; // [resume_fn, iterable, yielded_val, coroutine]
920+ self . emit ( Instruction :: GetField ( "status" . to_string ( ) ) , loc) ; // [resume_fn, iterable, yielded_val, status_fn]
921+ match iter_loc {
922+ VarLocation :: Local ( offset) => {
923+ self . emit ( Instruction :: DupPlusFP ( offset) , loc) ; // [..., status_fn, iterable]
924+ }
925+ _ => unreachable ! ( ) ,
926+ }
927+ self . emit ( Instruction :: CallStack ( 1 ) , loc) ; // [resume_fn, iterable, yielded_val, status_val]
928+ self . emit ( Instruction :: Push ( crate :: value:: Value :: string ( "dead" . to_string ( ) ) ) , loc) ;
929+ self . emit ( Instruction :: Equal , loc) ;
930+ let continue_label = format ! ( "for_in_continue_{}" , unique_id) ;
931+ self . emit ( Instruction :: JumpIfFalse ( continue_label. clone ( ) ) , loc) ;
932+ self . emit ( Instruction :: Pop , loc) ; // Pop yielded_val since coroutine is dead
933+ self . emit ( Instruction :: Jump ( loop_end. clone ( ) ) , loc) ;
934+
935+ self . program . syms . insert (
936+ continue_label. clone ( ) ,
937+ Symbol {
938+ location : self . program . instructions . len ( ) as i32 ,
939+ narguments : 0 ,
940+ nlocals : 0 ,
941+ upvalues : Vec :: new ( ) ,
942+ } ,
943+ ) ;
944+
945+ // 4. Define loop variable and assign yielded value
946+ self . begin_scope ( ) ;
947+ let var_loc = self . define_variable ( for_in. var ) ;
948+ match var_loc {
949+ VarLocation :: Local ( offset) => {
950+ self . emit ( Instruction :: MovePlusFP ( offset as usize ) , loc) ;
951+ }
952+ VarLocation :: Global ( name) => {
953+ self . emit ( Instruction :: Store ( name) , loc) ;
954+ }
955+ VarLocation :: Upvalue ( _) => unreachable ! ( ) ,
956+ }
957+
958+ self . current_state ( ) . loop_stack . push ( LoopLabels {
959+ start : loop_start. clone ( ) ,
960+ end : loop_end. clone ( ) ,
961+ } ) ;
962+
963+ // 5. Body
964+ for stmt in for_in. body {
965+ self . compile_statement ( stmt) ;
966+ }
967+
968+ self . end_scope ( loc, false ) ;
969+ self . current_state ( ) . loop_stack . pop ( ) ;
970+
971+ self . emit ( Instruction :: Jump ( loop_start. clone ( ) ) , loc) ;
972+
973+ // loop_end label
974+ self . program . syms . insert (
975+ loop_end. clone ( ) ,
976+ Symbol {
977+ location : self . program . instructions . len ( ) as i32 ,
978+ narguments : 0 ,
979+ nlocals : 0 ,
980+ upvalues : Vec :: new ( ) ,
981+ } ,
982+ ) ;
983+ self . end_scope ( loc, false ) ;
984+ }
985+
861986 fn compile_try_catch ( & mut self , tc : TryCatch ) {
862987 let loc = tc. loc ;
863988 let unique_id = self . unique_id ( ) ;
0 commit comments