@@ -275,6 +275,126 @@ impl<'a> VM<'a> {
275275 self . push ( val) ;
276276 Ok ( ( ) )
277277 }
278+ StrUpper => {
279+ let s = self . recv_str ( recv) ?;
280+ let val = self . heap . alloc ( HeapObj :: Str ( s. to_uppercase ( ) ) ) ?;
281+ self . push ( val) ;
282+ Ok ( ( ) )
283+ }
284+ StrLower => {
285+ let s = self . recv_str ( recv) ?;
286+ let val = self . heap . alloc ( HeapObj :: Str ( s. to_lowercase ( ) ) ) ?;
287+ self . push ( val) ;
288+ Ok ( ( ) )
289+ }
290+ StrStrip => {
291+ let s = self . recv_str ( recv) ?;
292+ let val = self . heap . alloc ( HeapObj :: Str ( s. trim ( ) . to_string ( ) ) ) ?;
293+ self . push ( val) ;
294+ Ok ( ( ) )
295+ }
296+ StrSplit => {
297+ let s = self . recv_str ( recv) ?;
298+ let parts: Vec < Val > = if positional. is_empty ( ) {
299+ s. split_whitespace ( )
300+ . map ( |p| self . heap . alloc ( HeapObj :: Str ( p. to_string ( ) ) ) )
301+ . collect :: < Result < _ , _ > > ( ) ?
302+ } else {
303+ let sep = self . val_to_str ( positional[ 0 ] ) ?;
304+ s. split ( sep. as_str ( ) )
305+ . map ( |p| self . heap . alloc ( HeapObj :: Str ( p. to_string ( ) ) ) )
306+ . collect :: < Result < _ , _ > > ( ) ?
307+ } ;
308+ let val = self . heap . alloc ( HeapObj :: List ( Rc :: new ( RefCell :: new ( parts) ) ) ) ?;
309+ self . push ( val) ;
310+ Ok ( ( ) )
311+ }
312+ StrJoin => {
313+ let sep = self . recv_str ( recv) ?;
314+ if positional. len ( ) != 1 {
315+ return Err ( VmErr :: Type ( "join() takes exactly one argument" ) ) ;
316+ }
317+ let items = match self . heap . get ( positional[ 0 ] ) {
318+ HeapObj :: List ( rc) => rc. borrow ( ) . clone ( ) ,
319+ HeapObj :: Tuple ( v) => v. clone ( ) ,
320+ _ => return Err ( VmErr :: Type ( "join() argument must be iterable" ) ) ,
321+ } ;
322+ let mut parts: Vec < String > = Vec :: with_capacity ( items. len ( ) ) ;
323+ for v in items {
324+ parts. push ( self . val_to_str ( v) ?) ;
325+ }
326+ let val = self . heap . alloc ( HeapObj :: Str ( parts. join ( sep. as_str ( ) ) ) ) ?;
327+ self . push ( val) ;
328+ Ok ( ( ) )
329+ }
330+ StrReplace => {
331+ if positional. len ( ) != 2 {
332+ return Err ( VmErr :: Type ( "replace() takes exactly 2 arguments" ) ) ;
333+ }
334+ let s = self . recv_str ( recv) ?;
335+ let old = self . val_to_str ( positional[ 0 ] ) ?;
336+ let new = self . val_to_str ( positional[ 1 ] ) ?;
337+ let val = self . heap . alloc ( HeapObj :: Str ( s. replace ( old. as_str ( ) , new. as_str ( ) ) ) ) ?;
338+ self . push ( val) ;
339+ Ok ( ( ) )
340+ }
341+ StrStartswith => {
342+ if positional. len ( ) != 1 {
343+ return Err ( VmErr :: Type ( "startswith() takes exactly one argument" ) ) ;
344+ }
345+ let s = self . recv_str ( recv) ?;
346+ let prefix = self . val_to_str ( positional[ 0 ] ) ?;
347+ self . push ( Val :: bool ( s. starts_with ( prefix. as_str ( ) ) ) ) ;
348+ Ok ( ( ) )
349+ }
350+ StrEndswith => {
351+ if positional. len ( ) != 1 {
352+ return Err ( VmErr :: Type ( "endswith() takes exactly one argument" ) ) ;
353+ }
354+ let s = self . recv_str ( recv) ?;
355+ let suffix = self . val_to_str ( positional[ 0 ] ) ?;
356+ self . push ( Val :: bool ( s. ends_with ( suffix. as_str ( ) ) ) ) ;
357+ Ok ( ( ) )
358+ }
359+ StrFind => {
360+ if positional. len ( ) != 1 {
361+ return Err ( VmErr :: Type ( "find() takes exactly one argument" ) ) ;
362+ }
363+ let s = self . recv_str ( recv) ?;
364+ let sub = self . val_to_str ( positional[ 0 ] ) ?;
365+ let idx = s. find ( sub. as_str ( ) )
366+ . map ( |i| s[ ..i] . chars ( ) . count ( ) as i64 )
367+ . unwrap_or ( -1 ) ;
368+ self . push ( Val :: int ( idx) ) ;
369+ Ok ( ( ) )
370+ }
371+ StrCount => {
372+ if positional. len ( ) != 1 {
373+ return Err ( VmErr :: Type ( "count() takes exactly one argument" ) ) ;
374+ }
375+ let s = self . recv_str ( recv) ?;
376+ let sub = self . val_to_str ( positional[ 0 ] ) ?;
377+ let n = s. matches ( sub. as_str ( ) ) . count ( ) as i64 ;
378+ self . push ( Val :: int ( n) ) ;
379+ Ok ( ( ) )
380+ }
381+ }
382+ }
383+
384+ // Extracts the string content from a Str receiver.
385+ fn recv_str ( & self , recv : Val ) -> Result < String , VmErr > {
386+ match self . heap . get ( recv) {
387+ HeapObj :: Str ( s) => Ok ( s. clone ( ) ) ,
388+ _ => Err ( VmErr :: Type ( "method requires a string receiver" ) ) ,
389+ }
390+ }
391+
392+ // Converts a Val to String for use as a method argument.
393+ fn val_to_str ( & self , v : Val ) -> Result < String , VmErr > {
394+ match self . heap . get ( v) {
395+ HeapObj :: Str ( s) => Ok ( s. clone ( ) ) ,
396+ _ => Err ( VmErr :: Type ( "argument must be a string" ) ) ,
278397 }
279398 }
399+
280400}
0 commit comments