Posted to tcl by hardkorebob at Wed Aug 05 20:08:26 GMT 2026view pretty

#!/usr/bin/env tclsh
# ytcl.tcl - Tcl port of shy Bash DSL

namespace eval ::ytcl {
    variable current_level 0

    # Return the current indentation string based on nesting level
    proc indent {} {
        variable current_level
        return [string repeat "   " $current_level]
    }

    # Increment / decrement current indent level
    proc + {} { variable current_level; incr current_level }
    proc - {} { variable current_level; incr current_level -1 }

    # Core generator functions
    proc _df {self name {arg ""}} {
        if {$self eq "s"} {
            return "def ${name}(self,${arg}):"
        } else {
            return "def ${name}(${arg}):"
        }
    }

    proc _rt {arg} { return "return $arg" }

    proc _de {self name {arg ""}} {
        if {$self eq "s"} {
            return "def ${name}(self,event=None,${arg}):"
        } else {
            return "def ${name}(event=None,${arg}):"
        }
    }

    proc _fc {self func {arg ""} {param ""}} {
        switch -- $self {
            "s" { return "self.${func}(${arg})" }
            "z" { return "self.${func}(self.${arg})" }
            "k" { return "${func}(self.${arg})" }
            default { return "${func}(${arg})" }
        }
    }

    proc _vf {scope name func {arg ""} {extra ""}} {
        set t_pref ""
        set f_pref ""
        set a_pref ""

        if {$scope eq "s"} {
            set t_pref "self."
        } elseif {$scope eq "z"} {
            set t_pref "self."
            set f_pref "self."
            set a_pref "self."

        } elseif {$scope eq "k"} {
            set f_pref "self."

        } elseif {$scope eq "a"} {
            set f_pref "self."
            set a_pref "self."

        } elseif {$scope eq "t"} {
            set t_pref "self."
            set f_pref "self."
        }
        set line "${t_pref}${name} = ${f_pref}${func}(${a_pref}${arg})"
        if {$extra ne ""} { append line " $extra" }
        return $line
    }

    proc _va {self name val} {
        switch -- $self {
            "s"  { return "self.$name = $val" }
            "k"  { return "$name = self.$val" }
            "z" { return "self.$name = self.$val" }
            default { return "$name = $val" }
        }
    }

    proc _wi {self func arg name} {
        if {$self eq "a"} {
            return "with ${func}(self.${arg}) as $name:"
        } elseif {$self eq "s"} {
            return "with self.${func}(${arg}) as $name:"
        } else {
            return "with ${func}(${arg}) as $name:"
        }
    }

    proc _ytr {} { return "try:" }
    proc _exc {} { return "except Exception as e:" }

    proc _ff {f val {p ""} {param ""}} {
        switch -- $f {
            "f"   { return "if ${val}(${p}) $param:" }
            "s"   { return "if self.${val} $p $param:" }
            "k"  {
                if {$param ne ""} {
                    return "if self.${val}(${p}) $param:"
                } else {
                    return "if self.${val}(${p}):"
                }
            }
            default { return "if ${val} $p:" }
        }
    }

    proc _ef {f val {p ""}} {
        if {$f eq "f"} {
            return "elif ${val}(${p}):"
        } else {
            return "elif ${val}:"
        }
    }

    proc _el {} { return "else:" }
    proc _br {} { return "break" }

    proc _fr {f i l {p ""}} {
        switch -- $f {
            "f"  { return "for $i in ${l}(${p}):" }
            "k"  { return "for $i in self.${l}(${p}):" }
            "s"  { return "for $i in self.$l:" }
            default { return "for $i in $l:" }
        }
    }

    proc _ac {self name cc} {
        switch -- $self {
            "s"   { return "self.$name += $cc" }
            "z"  { return "self.$name += self.$cc" }
            "k"  { return "$name += self.$cc" }
            default { return "$name += $cc" }
        }
    }

    proc _dc {self name cc} {
        switch -- $self {
            "s"   { return "self.$name -= $cc" }
            "z"  { return "self.$name -= self.$cc" }
            "k"  { return "$name -= self.$cc" }
            default { return "$name -= $cc" }
        }
    }
    proc _fin {} { return "finally:" }
    proc _ps {} { return "pass" }
    proc _wh {cond} { return "while $cond:" }
    proc _cnt {} { return "continue" }

    # Metaprogramming:
    #
    set commands {br df rt de fc vf va ff ef el fr ac dc wi ytr exc wh cnt fin ps}

    foreach cmd $commands {
        proc ::ytcl::$cmd {args} \
            "puts -nonewline \[indent\]; puts \[eval _$cmd \$args\]"
    }

    #generate $level$cmd procs for level 0..5
    #~ set commands {df rt de fc vf va ff ef el fr ac dc wi ytr exc wh cnt}
    #~ for {set i 0} {$i <= 5} {incr i} {
        #~ foreach cmd $commands {
            #~ # Create a proc like "0df", "1df", ...
            #~ proc ::ytcl::${i}${cmd} {args} [string map [list %i $i %cmd $cmd] {
                #~ variable indents
                #~ puts -nonewline [lindex $indents %i]
                #~ puts [eval _%cmd $args]
            #~ }]
        #~ }
    #~ }

    # Toplevel global commands
    proc bang {} {
        puts "#!/usr/bin/env python3"
        puts "#!ytcl.tcl;"
    }

    proc cl {name {base ""}} { puts "class ${name}(${base}):" }
    proc imp {pkg} { puts "import $pkg" }
    proc as {pkg alias} { puts "import $pkg as $alias" }
    proc frm {module name} { puts "from $module import $name" }
    proc mn {class windowTitle} {
        puts "if __name__ == '__main__':"
        puts "   runIt = ${class}()"
        puts "   runIt.title(\"$windowTitle\")"
        puts "   runIt.mainloop()"
    }

    ########################################################
    # keep these as an example of tcl power
    # cla class_name base1 base2
    proc cla {name args} {
        puts "class ${name}([join $args {, }]):"
    }

    # dec decorator_name  ’  @decorator_name
    # dec "decorator_name(args)"  ’  @decorator_name(args)    (pass as a single string)
    # dec decorator_name arg1 arg2  ’  @decorator_name(arg1, arg2)
    proc dec {name args} {
        if {[llength $args] == 0} {
            puts -nonewline [indent]
            puts "@$name"
        } else {
            # If the first extra argument looks like a parenthesis group, use it as is
            if {[string match "(*)" $args]} {
                puts -nonewline [indent]
                puts "@${name}$args"
            } else {
                puts -nonewline [indent]
                puts "@${name}([join $args {, }])"
            }
        }
    }
    #############################################

    # cm "some text"  ’  # some text
    proc cm {text} {
        puts -nonewline [indent]
        puts "# $text"
    }

    proc doc {text} {
        puts -nonewline [indent]
        puts "\"\"\"${text}\"\"\""
    }

    # Utilities
    proc bd {self widget pairs} {
      foreach {key handler} $pairs {
        fc $self ${widget}.bind "\"${key}\", self.${handler}"
      }
    }
    proc sf {empty index pattern} {
        va l $index {[(m.start(),m.end()) for m in finditer(r'$pattern', data)]}
        fr - start,end $index
        +
        fc s this_text_box.tag_add {"$index",f"1.0+{start}c",f"1.0+{end}c"}
        -
    }
    namespace export *
}

namespace import ::ytcl::*

proc unknown {args} {
  puts -nonewline [::ytcl::indent]
  puts [join $args " "]
}