Version 3 of An indentation syntax for Tcl

Updated 2002-06-10 12:56:44

Here's a little Tcl script which lets you use indentation as optional subsitute for braces, as pioneered by Python -jcw


First a sample of the new syntax (note that this is NOT standard Tcl!):


  # A little package to deal with an alternate Tcl source code syntax,
  # using optional indentation as a substitute for braces.
  #
  # by Jean-Claude Wippler, June 2002

  package provide indentcl 0.1

  namespace eval indentcl
    namespace export convert ieval isource

    namespace eval v
      variable lb \{
      variable rb \}

    proc emit {line}
      if {[llength $v::last] > 0}
        lappend v::result [join $v::last " "]
      foreach x $v::keep
        lappend v::result $x
      set v::keep {}
      set v::last [list $line]

    proc dedent {i}
      while {$i < [lindex $v::levels end]}
        set v::levels [lreplace $v::levels end end]
        lappend v::last $v::rb

    proc convert {text}
      set v::last {}
      set v::levels 0
      set v::keep {}
      set v::result {}

      foreach line [split $text \n]
        regexp {^(\s*)(.*)$} $line - a b
        if {$b == "" || [string index $b 0] == "#"}
          lappend v::keep $line
          continue
        set i 0
        foreach x [split $a ""]
          switch $x
            " "  { incr i }
            "\t" { incr i [expr {8 - $i%8}] }
        if {$i < [lindex $v::levels end]}
            dedent $i
            if {$i != [lindex $v::levels end]}
              lappend v::last \\
          elseif {$i > [lindex $v::levels end]}
            lappend v::last $v::lb
            lappend v::levels $i
        emit $line

      dedent -1
      emit ""
      return [join $v::result "\n"]

    proc ieval {script}
      uplevel 1 [convert $data]

    proc isource {file}
      set fd [open $argv]
      set data [read $fd]
      close $fd

      uplevel 1 [convert $data]

  if {$argv0 == [info script]}
    if {[llength $argv] != 2}
      puts "usage: $argv0 infile outfile"
      exit

    set fd [open [lindex $argv 0]]
    set data [read $fd]
    close $fd

    set data [indentcl::convert $data]

    set fd [open [lindex $argv 1] w]
    puts -nonewline $fd $data
    close $fd

And here's the Tcl script that can deal with the above input file:


  # A little package to deal with an alternate Tcl source code syntax,
  # using optional indentation as a substitute for braces.
  #
  # by Jean-Claude Wippler, June 2002

  package provide indentcl 0.1

  namespace eval indentcl {
    namespace export convert ieval isource

    namespace eval v {
      variable lb \{
      variable rb \} }

    proc emit {line} {
      if {[llength $v::last] > 0} {
        lappend v::result [join $v::last " "] }
      foreach x $v::keep {
        lappend v::result $x }
      set v::keep {}
      set v::last [list $line] }

    proc dedent {i} {
      while {$i < [lindex $v::levels end]} {
        set v::levels [lreplace $v::levels end end]
        lappend v::last $v::rb } }

    proc convert {text} {
      set v::last {}
      set v::levels 0
      set v::keep {}
      set v::result {}

      foreach line [split $text \n] {
        regexp {^(\s*)(.*)$} $line - a b
        if {$b == "" || [string index $b 0] == "#"} {
          lappend v::keep $line
          continue }
        set i 0
        foreach x [split $a ""] {
          switch $x {
            " "  { incr i }
            "\t" { incr i [expr {8 - $i%8}] } } }
        if {$i < [lindex $v::levels end]} {
            dedent $i
            if {$i != [lindex $v::levels end]} {
              lappend v::last \\ } } \
          elseif {$i > [lindex $v::levels end]} {
            lappend v::last $v::lb
            lappend v::levels $i }
        emit $line }

      dedent -1
      emit ""
      return [join $v::result "\n"] }

    proc ieval {script} {
      uplevel 1 [convert $data] }

    proc isource {file} {
      set fd [open $argv]
      set data [read $fd]
      close $fd

      uplevel 1 [convert $data] } }

  if {$argv0 == [info script]} {
    if {[llength $argv] != 2} {
      puts "usage: $argv0 infile outfile"
      exit }

    set fd [open [lindex $argv 0]]
    set data [read $fd]
    close $fd

    set data [indentcl::convert $data]

    set fd [open [lindex $argv 1] w]
    puts -nonewline $fd $data
    close $fd } }

If you run that second script with the first as input file... get get the second script again :o)