@@ -690,6 +690,7 @@ impl Compiler {
690690 match sym {
691691 "quote" => return self . compile_quote ( & items[ 1 ..] , dest) ,
692692 "if" => return self . compile_if ( & items[ 1 ..] , dest, tail_pos) ,
693+ "cond" => return self . compile_cond ( & items[ 1 ..] , dest, tail_pos) ,
693694 "and" => return self . compile_and ( & items[ 1 ..] , dest) ,
694695 "or" => return self . compile_or ( & items[ 1 ..] , dest) ,
695696 "def" => return self . compile_def ( & items[ 1 ..] , dest) ,
@@ -776,6 +777,117 @@ impl Compiler {
776777 Ok ( ( ) )
777778 }
778779
780+ /// Compile cond expression: (cond (test1 result1) (test2 result2) ... (else default))
781+ /// Each clause is a list where the first element is the condition and the rest is the body.
782+ /// The special condition 'else' or 'true' matches unconditionally.
783+ fn compile_cond ( & mut self , clauses : & [ Value ] , dest : Reg , tail_pos : bool ) -> Result < ( ) , String > {
784+ if clauses. is_empty ( ) {
785+ // Empty cond returns nil
786+ self . emit ( Op :: load_nil ( dest) ) ;
787+ return Ok ( ( ) ) ;
788+ }
789+
790+ // Track jumps to end (from successful branches)
791+ let mut jumps_to_end: Vec < usize > = Vec :: new ( ) ;
792+
793+ for ( i, clause) in clauses. iter ( ) . enumerate ( ) {
794+ let is_last = i == clauses. len ( ) - 1 ;
795+
796+ // Each clause should be a list: (condition body...)
797+ let clause_items = clause. as_list ( )
798+ . ok_or_else ( || "cond clause must be a list" . to_string ( ) ) ?;
799+
800+ if clause_items. is_empty ( ) {
801+ return Err ( "cond clause cannot be empty" . to_string ( ) ) ;
802+ }
803+
804+ let condition = & clause_items[ 0 ] ;
805+ let body = & clause_items[ 1 ..] ;
806+
807+ // Check for else/true clause (unconditional)
808+ let is_else = condition. as_symbol ( )
809+ . map ( |s| s == "else" || s == "true" )
810+ . unwrap_or ( false ) ;
811+
812+ if is_else {
813+ // Else clause - just compile the body
814+ if body. is_empty ( ) {
815+ self . emit ( Op :: load_nil ( dest) ) ;
816+ } else if body. len ( ) == 1 {
817+ self . compile_expr ( & body[ 0 ] , dest, tail_pos) ?;
818+ } else {
819+ // Multiple expressions: wrap in implicit do
820+ let mut do_list = vec ! [ Value :: symbol( "do" ) ] ;
821+ do_list. extend ( body. iter ( ) . cloned ( ) ) ;
822+ self . compile_expr ( & Value :: list ( do_list) , dest, tail_pos) ?;
823+ }
824+ // No need to jump - this is the final case
825+ break ;
826+ }
827+
828+ // Compile condition
829+ self . compile_expr ( condition, dest, false ) ?;
830+
831+ if is_last {
832+ // Last clause without else: if false, result is nil
833+ let jump_to_nil = self . emit ( Op :: jump_if_false ( dest, 0 ) ) ;
834+
835+ // Compile body
836+ if body. is_empty ( ) {
837+ // If body is empty, the result is the condition value (already in dest)
838+ // But we need to handle the nil case
839+ } else if body. len ( ) == 1 {
840+ self . compile_expr ( & body[ 0 ] , dest, tail_pos) ?;
841+ } else {
842+ let mut do_list = vec ! [ Value :: symbol( "do" ) ] ;
843+ do_list. extend ( body. iter ( ) . cloned ( ) ) ;
844+ self . compile_expr ( & Value :: list ( do_list) , dest, tail_pos) ?;
845+ }
846+
847+ // Jump over nil
848+ let jump_over_nil = self . emit ( Op :: jump ( 0 ) ) ;
849+
850+ // Nil case
851+ let nil_pos = self . chunk . current_pos ( ) ;
852+ self . chunk . patch_jump ( jump_to_nil, nil_pos) ;
853+ self . emit ( Op :: load_nil ( dest) ) ;
854+
855+ // Patch jump over nil
856+ let end_pos = self . chunk . current_pos ( ) ;
857+ self . chunk . patch_jump ( jump_over_nil, end_pos) ;
858+ } else {
859+ // Not the last clause: if false, jump to next clause
860+ let jump_to_next = self . emit ( Op :: jump_if_false ( dest, 0 ) ) ;
861+
862+ // Compile body
863+ if body. is_empty ( ) {
864+ // Empty body - condition value is the result (already in dest)
865+ } else if body. len ( ) == 1 {
866+ self . compile_expr ( & body[ 0 ] , dest, tail_pos) ?;
867+ } else {
868+ let mut do_list = vec ! [ Value :: symbol( "do" ) ] ;
869+ do_list. extend ( body. iter ( ) . cloned ( ) ) ;
870+ self . compile_expr ( & Value :: list ( do_list) , dest, tail_pos) ?;
871+ }
872+
873+ // Jump to end (skip remaining clauses)
874+ jumps_to_end. push ( self . emit ( Op :: jump ( 0 ) ) ) ;
875+
876+ // Patch jump to next clause
877+ let next_clause_pos = self . chunk . current_pos ( ) ;
878+ self . chunk . patch_jump ( jump_to_next, next_clause_pos) ;
879+ }
880+ }
881+
882+ // Patch all jumps to end
883+ let end_pos = self . chunk . current_pos ( ) ;
884+ for jump_pos in jumps_to_end {
885+ self . chunk . patch_jump ( jump_pos, end_pos) ;
886+ }
887+
888+ Ok ( ( ) )
889+ }
890+
779891 fn compile_and ( & mut self , args : & [ Value ] , dest : Reg ) -> Result < ( ) , String > {
780892 // (and) => true
781893 if args. is_empty ( ) {
0 commit comments