@@ -612,25 +612,48 @@ static Value builtin_lcm(Interpreter* interp, Value* args, int argc, Expr** arg_
612612
613613// ============ Comparison operators ============
614614
615- static Value builtin_eq (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
616- (void )arg_nodes ; (void )env ; (void )interp ;
617-
618- if (args [0 ].type != args [1 ].type ) {
619- return value_int (0 );
620- }
621-
622- switch (args [0 ].type ) {
615+ // Recursive deep equality helper for Values (returns 1 if equal, 0 otherwise)
616+ static int value_deep_eq (Value a , Value b ) {
617+ if (a .type != b .type ) return 0 ;
618+ switch (a .type ) {
623619 case VAL_INT :
624- return value_int ( args [ 0 ] .as .i == args [ 1 ] .as .i ? 1 : 0 ) ;
620+ return a .as .i == b .as .i ? 1 : 0 ;
625621 case VAL_FLT :
626- return value_int ( args [ 0 ] .as .f == args [ 1 ] .as .f ? 1 : 0 ) ;
622+ return a .as .f == b .as .f ? 1 : 0 ;
627623 case VAL_STR :
628- return value_int (strcmp (args [0 ].as .s , args [1 ].as .s ) == 0 ? 1 : 0 );
624+ if (a .as .s == NULL || b .as .s == NULL ) return (a .as .s == b .as .s ) ? 1 : 0 ;
625+ return strcmp (a .as .s , b .as .s ) == 0 ? 1 : 0 ;
629626 case VAL_FUNC :
630- return value_int (args [0 ].as .func == args [1 ].as .func ? 1 : 0 );
627+ return a .as .func == b .as .func ? 1 : 0 ;
628+ case VAL_TNS : {
629+ Tensor * ta = a .as .tns ;
630+ Tensor * tb = b .as .tns ;
631+ if (ta == NULL || tb == NULL ) return (ta == tb ) ? 1 : 0 ;
632+ if (ta -> elem_type != tb -> elem_type ) return 0 ;
633+ if (ta -> ndim != tb -> ndim ) return 0 ;
634+ for (size_t i = 0 ; i < ta -> ndim ; i ++ ) {
635+ if (ta -> shape [i ] != tb -> shape [i ]) return 0 ;
636+ }
637+ if (ta -> length != tb -> length ) return 0 ;
638+ for (size_t i = 0 ; i < ta -> length ; i ++ ) {
639+ if (!value_deep_eq (ta -> data [i ], tb -> data [i ])) return 0 ;
640+ }
641+ return 1 ;
642+ }
631643 default :
632- return value_int (0 );
644+ return 0 ;
645+ }
646+ }
647+
648+ static Value builtin_eq (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
649+ (void )arg_nodes ; (void )env ; (void )interp ;
650+
651+ // If types differ, not equal
652+ if (args [0 ].type != args [1 ].type ) {
653+ return value_int (0 );
633654 }
655+
656+ return value_int (value_deep_eq (args [0 ], args [1 ]) ? 1 : 0 );
634657}
635658
636659static Value builtin_gt (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
@@ -956,39 +979,109 @@ static Value builtin_join(Interpreter* interp, Value* args, int argc, Expr** arg
956979
957980static Value builtin_split (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
958981 (void )arg_nodes ; (void )env ;
959- // SPLIT returns a tensor in Python but for now we can't return tensors
960- // For Stage 3, we'll skip tensor-returning operations
961- RUNTIME_ERROR (interp , "SPLIT requires TNS support (Stage 4)" , line , col );
982+ // SPLIT(str, sep?) -> 1-D TNS of STR
983+ EXPECT_STR (args [0 ], "SPLIT" , interp , line , col );
984+ const char * sep = NULL ;
985+ if (argc >= 2 ) {
986+ EXPECT_STR (args [1 ], "SPLIT" , interp , line , col );
987+ sep = args [1 ].as .s ;
988+ }
989+ const char * s = args [0 ].as .s ;
990+ // simple separator: if sep==NULL split on whitespace, else split on sep exactly
991+ char * copy = strdup (s );
992+ char * saveptr = NULL ;
993+ char * token ;
994+ size_t cap = 8 ;
995+ size_t count = 0 ;
996+ Value * items = malloc (sizeof (Value ) * cap );
997+ if (!items ) { free (copy ); RUNTIME_ERROR (interp , "Out of memory" , line , col ); }
998+ if (sep == NULL ) {
999+ // whitespace split
1000+ token = strtok_s (copy , " \t\r\n" , & saveptr );
1001+ if (token ) {
1002+ if (count + 1 > cap ) { cap *= 2 ; items = realloc (items , sizeof (Value ) * cap ); }
1003+ items [count ++ ] = value_str (token );
1004+ }
1005+ } else {
1006+ // split on sep: iterate
1007+ size_t seplen = strlen (sep );
1008+ char * cur = copy ;
1009+ char * found ;
1010+ while ((found = strstr (cur , sep )) != NULL ) {
1011+ size_t len = (size_t )(found - cur );
1012+ char * piece = malloc (len + 1 );
1013+ memcpy (piece , cur , len );
1014+ piece [len ] = '\0' ;
1015+ if (count + 1 > cap ) { cap *= 2 ; items = realloc (items , sizeof (Value ) * cap ); }
1016+ items [count ++ ] = value_str (piece );
1017+ free (piece );
1018+ cur = found + seplen ;
1019+ }
1020+ // last piece
1021+ if (* cur != '\0' ) {
1022+ if (count + 1 > cap ) { cap *= 2 ; items = realloc (items , sizeof (Value ) * cap ); }
1023+ items [count ++ ] = value_str (cur );
1024+ }
1025+ free (copy );
1026+ if (count == 0 ) {
1027+ free (items );
1028+ return value_tns_new (TYPE_STR , 1 , (const size_t []){0 });
1029+ }
1030+ size_t shape [1 ] = { count };
1031+ Value out = value_tns_from_values (TYPE_STR , 1 , shape , items , count );
1032+ for (size_t i = 0 ; i < count ; i ++ ) value_free (items [i ]);
1033+ free (items );
1034+ return out ;
1035+ }
1036+
1037+ while ((token = strtok_s (NULL , " \t\r\n" , & saveptr )) != NULL ) {
1038+ if (count + 1 > cap ) { cap *= 2 ; items = realloc (items , sizeof (Value ) * cap ); }
1039+ items [count ++ ] = value_str (token );
1040+ }
1041+ free (copy );
1042+ if (count == 0 ) {
1043+ free (items );
1044+ return value_tns_new (TYPE_STR , 1 , (const size_t []){0 });
1045+ }
1046+ size_t shape [1 ] = { count };
1047+ Value out = value_tns_from_values (TYPE_STR , 1 , shape , items , count );
1048+ for (size_t i = 0 ; i < count ; i ++ ) value_free (items [i ]);
1049+ free (items );
1050+ return out ;
9621051}
9631052
9641053static Value builtin_slice (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
9651054 (void )arg_nodes ; (void )env ;
966- EXPECT_STR (args [0 ], "SLICE" , interp , line , col );
967- EXPECT_INT (args [1 ], "SLICE" , interp , line , col );
968- EXPECT_INT (args [2 ], "SLICE" , interp , line , col );
969-
970- const char * s = args [0 ].as .s ;
971- size_t len = strlen (s );
972- int64_t start = args [1 ].as .i ;
973- int64_t end = args [2 ].as .i ;
974-
975- // Convert 1-based to 0-based, handle negative indices
976- if (start < 0 ) start = (int64_t )len + start + 1 ;
977- if (end < 0 ) end = (int64_t )len + end + 1 ;
978- start -- ; // Convert to 0-based
979-
980- if (start < 0 ) start = 0 ;
981- if (end > (int64_t )len ) end = (int64_t )len ;
982- if (start >= end ) return value_str ("" );
983-
984- size_t result_len = (size_t )(end - start );
985- char * result = malloc (result_len + 1 );
986- memcpy (result , s + start , result_len );
987- result [result_len ] = '\0' ;
988-
989- Value v = value_str (result );
990- free (result );
991- return v ;
1055+ // SLICE can operate on STR or TNS for now. Syntax: SLICE(target, start, end)
1056+ if (args [0 ].type == VAL_STR ) {
1057+ EXPECT_INT (args [1 ], "SLICE" , interp , line , col );
1058+ EXPECT_INT (args [2 ], "SLICE" , interp , line , col );
1059+ const char * s = args [0 ].as .s ;
1060+ size_t len = strlen (s );
1061+ int64_t start = args [1 ].as .i ;
1062+ int64_t end = args [2 ].as .i ;
1063+ if (start < 0 ) start = (int64_t )len + start + 1 ;
1064+ if (end < 0 ) end = (int64_t )len + end + 1 ;
1065+ start -- ;
1066+ if (start < 0 ) start = 0 ;
1067+ if (end > (int64_t )len ) end = (int64_t )len ;
1068+ if (start >= end ) return value_str ("" );
1069+ size_t result_len = (size_t )(end - start );
1070+ char * result = malloc (result_len + 1 );
1071+ memcpy (result , s + start , result_len );
1072+ result [result_len ] = '\0' ;
1073+ Value v = value_str (result );
1074+ free (result );
1075+ return v ;
1076+ } else if (args [0 ].type == VAL_TNS ) {
1077+ // slice along first axis using 1-based inclusive indices
1078+ EXPECT_INT (args [1 ], "SLICE" , interp , line , col );
1079+ EXPECT_INT (args [2 ], "SLICE" , interp , line , col );
1080+ int64_t starts [1 ] = { args [1 ].as .i };
1081+ int64_t ends [1 ] = { args [2 ].as .i };
1082+ return value_tns_slice (args [0 ], starts , ends , 1 );
1083+ }
1084+ RUNTIME_ERROR (interp , "SLICE expects STR or TNS" , line , col );
9921085}
9931086
9941087static Value builtin_replace (Interpreter * interp , Value * args , int argc , Expr * * arg_nodes , Env * env , int line , int col ) {
@@ -1496,7 +1589,13 @@ static Value builtin_len(Interpreter* interp, Value* args, int argc, Expr** arg_
14961589 if (args [0 ].type == VAL_STR ) {
14971590 return value_int ((int64_t )strlen (args [0 ].as .s ));
14981591 }
1499- RUNTIME_ERROR (interp , "LEN requires TNS support for non-string arguments (Stage 4)" , line , col );
1592+ if (args [0 ].type == VAL_TNS ) {
1593+ Tensor * t = args [0 ].as .tns ;
1594+ if (!t ) return value_int (0 );
1595+ if (t -> ndim == 0 ) return value_int (0 );
1596+ return value_int ((int64_t )t -> shape [0 ]);
1597+ }
1598+ RUNTIME_ERROR (interp , "LEN expects STR or TNS" , line , col );
15001599}
15011600
15021601// Main, OS
0 commit comments