scrobj

What: scrobj - scripted Tcl_Objects

 Where: http://nullhomotopie.de/programs/scrobj-page.html
 Description: extension that allows to implement Tcl_Object types on the script level.
 Updated: 12/2008
 Version: 1.0

Quote from the man page:

       Tcl's fundamental mantra says that "everything is a string" and much of
       Tcl's simplicity derives from this rule. Strings  can,  however,  carry
       with  them an internal representation as some other data type, and this
       is one reason for Tcl's  astonishing  performance.   For  example,  Tcl
       maintains  such  an  internal representation for integers, lists, byte-
       codes, and many other types.

       Extension writers have long been able to add  new  custom  types  using
       Tcl's  C  interface.  The scrobj package allows you to do the same from
       the script level.

       Unfortunately, in order to achieve this the package has  to  violate  a
       basic  assumption  of Tcl's bytecode engine, namely that it is not get-
       ting invoked recursively from a call to Tcl_GetString.   A  consequence
       is that scrobj can be used to crash the application, although this does
       require a rather contrived setup.

       To prevent such crashes in the legitimate cases the package uses a ded-
       icated  Tcl interpreter for every registered type. Both Tcl 8.5 and Tcl
       8.6 seem to tolerate recursive invocations of the bytecode  engine,  as
       long as they execute in different interpreters.

Here's an example that's taken from the included scrobj::rational package: it implements a new Tcl_Object type for rational numbers:

  package require scrobj 1.0

  # Register the object type for rational numbers. A fraction $a/$b
  # is represented as [list $a $b].

  scrobj register scrobj::rational {
    irep {
        foreach {num den} $irep break
        # update string from internal rep
        if {$den != 1} {
            return $num/$den
        }
        return $num
    }
  } {
    inp {
        # parse rational number from string
        if {[regexp {^([*-]?[0-9]+)(/[0-9]+)?$} $inp -> num den]} {
            if {$den eq ""} {
                return [list $num 1]
            }
            set den [string range $den 1 end]
            if {$den != 0} {
                return [list $num $den]
            }
            return -code error "denominator must not be zero"
        }
        return -code error "rational number expected"
    }
  }

This declares a new type "scrobj::rational" with the given conversion routines between the string representation and the internal representation. Implicitly, a dedicated new Tcl interpreter is created where these conversion routines are executed. The type can then be used as follows:

   # get the internal representation of $a as a rational number:
   scrobj convert scrobj::rational $a

   # generate a new value of type "rational" with given numerator and denominator
   scrobj value scrobj::rational {17 456}

The package comes equipped with two sample subpackages scrobj::expression and scrobj::rational that serve to illustrate its use. See the files "rational.tcl" and "expression.tcl" for details.


Lars H: Interesting! In proper integers implementation I toyed a bit with the idea of having Tcl_Objs whose intrep was a "parsed" form of the string rep, but that still required writing C for the conversions (and the project was thoroughly obsoleted by TIP#237 anyway). Making it possible to use Tcl also for the conversions is a nice step forward. Having to run the conversion code in a separate interpreter may feel like a hack, but it could also be a good thing, since generating a string or internal representation is supposed to be side-effect free.