Updated 2013-09-19 10:35:45 by RLE

George Peter Staplin - Have you ever wanted to treat strings as objects? Well, now you can.

I'm also working on a class system like Smalltalk's, which will probably appear here in a little while.
  #! /usr/local/bin/tclsh8.3
  proc unknown {args} {

        if {[string is integer [lindex $args 0]]} {
        return [expr $args]
        } 

        if {[string index [lindex $args 0] 0] == "@"} {
        set astring [string range [lindex $args 0] 1 end]
        set numArgs [llength $args]
                for {set i 1} {$i < $numArgs} {incr i} {
                set arg$i [lindex $args $i]        
                }
                switch -- $arg1 {
                        byteLength {
                        return [string bytelength $astring]
                        }
                        
                        first {
                                if {$numArgs < 3} {
                                error "Please use \"@string1\" first \"string2\" ?startIndex?"
                                }
                                if {$numArgs == 3} {
                                return [string first $astring $arg2] 
                                }
                                if {$numArgs == 4} {
                                return [string first $astring $arg2 $arg3]
                                }
                        }                
                                
                        index {
                                if {$numArgs < 3} {
                                error "Please use \"@string\" index num  Where num is the index."
                                }
                                return [string index $astring $arg2]
                        }
                        is {
                                if {$numArgs < 3} {
                                error "Please use \"@string\" is class  Where class is alpha, digit, etc."
                                }
                        return [string is $arg2 $astring]
                        }
                        
                        toLower {
                        return [string tolower $astring]                
                        }
                        
                        toUpper {
                        return [string toupper $astring]
                        }
                        
                        default {
                        }
                }
        }
  }

Test code:
  puts [2 + 3 + 2]
  puts ["@HELLO WORLD" toLower]

  proc hello {args} {puts $args}
  hello world

  puts ["@hello world" toUpper]

  puts ["@blahblah" byteLength]
  puts ["@wonder" first "wonder"]
  puts ["@wonder" first "wonder" 0]
  set blah "long string"
  puts ["@$blah" index 1]
  puts ["@long string" index 5]
  puts ["@fun" is ascii]        

To get a real @ at the beginning of a string double it (just like in Smalltalk). I suggest that you don't name a proc @something, because it will mess this up.

RS: One note - @ isn't exactly beautiful as string marker. The Tcl parser consumes double quotes, so you don't see those in the unknown proc. But single quotes are available - only they don't group... You could modify the validity check so the examples could be
 puts ["'hello world'" toUpper]
 puts ['blahblah' byteLength]

GPS - How about this:
  #! /usr/local/bin/tclsh8.3

  #Kevin Kenny wrote the original source and meth (formerly to) procs.
  #I've made it so that having multiple files is not needed and 
  #a bunch of other stuff.
 
  proc cfork {} {
  global cstate 

        if {[info exists cstate] != 1} {
        set cstate 1
        rename source ::tcl::source

                proc source {fileName} {
                variable source_channel
                set source_channel [open $fileName r]
                set command {}
                set sep {} 
                set escaped 0
                        while {[gets $source_channel line] >= 0} {

                                for {set i 0} {$i < [string length $line]} {incr i} {
                                
                                        set ch [string index $line $i]
                                                                                
                                        if {$ch == "'" && $escaped == 0} {
                                        #puts "WONDER $i"
                                        #puts $line
                                        set line [string replace $line $i $i "\""]
                                        } elseif {$ch == "\\"} {
                                        set escaped 1
                                        } else {
                                        set escaped 0
                                        }
                                        
                                }
        
                        append command $sep $line

                                if {[info complete $command]} {
                                set result [uplevel 1 $command]
                                set command {}
                                set sep {}
                                } else {
                                        set sep \n
                                }
                        }
                return $result
                }

                proc meth {name args} {
                variable source_channel
                set body {}
                set sep {}

                        while {[gets $source_channel line] >= 0} {
                                if {[string equal [string trim $line] @]} {
                                return [proc $name $args $body]
                                } else {
                                        append body $sep $line
                                        set sep \n
                                        }
                        }
                return -code error {unterminated 'meth'}
                }

                proc unknown {args} {
                #puts $args
                set numArgs [llength $args]
                set astring [lindex $args 0]
                
                for {set i 1} {$i < $numArgs} {incr i} {
                set arg$i [lindex $args $i]        
                }        

                        if {[string is integer $astring] || [string index $astring 0] == "("} {
                        return [expr $args]
                        } 
                
                set sop [lindex $args 1]
                
                #puts "sop $sop astring $astring"
                
                        switch -- $sop {
                                byteLength {
                                return [string bytelength $astring]
                                }
                        
                                first: {
                                        if {$numArgs < 3} {
                                        error "Please use \"string1\" first \"string2\" ?startIndex?"
                                        }
                
                                        if {$numArgs == 3} {
                                        return [string first $astring $arg2] 
                                        }
                                        if {$numArgs == 4} {
                                        return [string first $astring $arg2 $arg3]
                                        }
                                }                        
                                
                                index: {
                                        if {$numArgs < 3} {
                                        error "Please use \"string\" index num  Where num is the index."
                                        }
                                return [string index $astring $arg2]
                                }
                
                                is: {
                                        if {$numArgs < 3} {
                                        error "Please use \"string\" is class  Where class is alpha, digit, etc."
                                        }
                                return [string is $arg2 $astring]
                                }
                        
                                toLower {
                                return [string tolower $astring]                
                                }
                        
                                toUpper {
                                return [string toupper $astring]
                                }
                        
                                default {
                                }                                
                        }        
                }
        source [info script]
        return 1
        }
  return 0
  }

        if {[cfork] == 1} {
        return
        }


  meth add x y
  return [expr {$x + $y}]
  @

  puts [add 6 27]
  puts ['This is Rebecca\'s flower not George\'s' toLower]
  puts ['Hello Bob!' toUpper]
  puts ['Blah Blah!' index: 0] 
  puts ['hmm' is: ascii]
  puts [200 + 23]
  puts [(200 + 33000)]