here's a little [tcl] script which lets you use indentation as optional substitute for braces, as pioneered by [python] -[jcw]<
>
See Also [http://jeelabs.org/2011/09/09/playing-with-indentation/]
----
''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.3
namespace eval indentcl
namespace export iconvert 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 iconvert {text}
set v::last {}
set v::levels 0
set v::keep {}
set v::result {}
foreach x [split $text \n]
regexp {^(\s*)(.*)$} $x - a b
if {$b eq "" || [string index $b 0] eq "#"}
lappend v::keep $x
continue
set i 0
foreach y [split $a ""]
switch $y
" " { incr i }
\t { incr i [expr {8 - $i%8}] }
if {$i < [lindex $v::levels end]}
dedent $i
if {$i != [lindex $v::levels end] || [regsub {^(\s*)\\:} $x {\1} x]}
lappend v::last \\
\:elseif {$i > [lindex $v::levels end]}
lappend v::last $v::lb
lappend v::levels $i
emit $x
dedent 0
emit ""
set r [join $v::result \n]
while {[llength $r] == 1 && [lindex $r 0] ne $r}
set r [lindex $r 0] ;# strip top level indentation
return $r
proc ieval {script}
uplevel 1 [iconvert $script]
proc isource {file}
set fd [open $file]
set data [read $fd]
close $fd
set prev [info source]
info source $file
set result [uplevel 1 [iconvert $data]]
info source $prev
return $result
if {$argv0 eq [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::iconvert $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.3
namespace eval indentcl {
namespace export iconvert 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 iconvert {text} {
set v::last {}
set v::levels 0
set v::keep {}
set v::result {}
foreach x [split $text \n] {
regexp {^(\s*)(.*)$} $x - a b
if {$b eq "" || [string index $b 0] eq "#"} {
lappend v::keep $x
continue }
set i 0
foreach y [split $a ""] {
switch $y {
" " { incr i }
\t { incr i [expr {8 - $i%8}] } } }
if {$i < [lindex $v::levels end]} {
dedent $i
if {$i != [lindex $v::levels end] || [regsub {^(\s*)\\:} $x {\1} x]} {
lappend v::last \\ } } \
elseif {$i > [lindex $v::levels end]} {
lappend v::last $v::lb
lappend v::levels $i }
emit $x }
dedent 0
emit ""
set r [join $v::result "\n"]
while {[llength $r] == 1 && [lindex $r 0] ne $r} {
set r [lindex $r 0] ;# strip top level indentation }
return $r }
proc ieval {script} {
uplevel 1 [iconvert $script] }
proc isource {file} {
set fd [open $file]
set data [read $fd]
close $fd
set prev [info source]
info source $file
set result [uplevel 1 [iconvert $data]]
info source $prev
return $result } }
if {$argv0 eq [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::iconvert $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... you get the second script again :o)
[jcw] 2003-04-14: Adjusted to deal with "re-indentation" by starting a line with "\:". There is one example of it in the above. It is needed to support mixed indents/dedents of the form:
======
if {...} {
...
} else {
...
}
======
This can now be written as:
======
if {...}
...
\:else
...
======
In essence, `\:` means: "dedent, but keep on continuing last block". Ugly, but this gets around the issue. Note that `\:` is not just for else's.
[Larry Smith] Might I suggest the method I like to use with Modula?
======
if
then
s1
s2
...
sn
else
s1
s2
...
sn
end
======
Having a closing marker for such things makes syntax a ''lot'' cleaner. This is why all the Wirth languages after Pascal were designed this way. The various members of the Modula family, Oberon and its spin-offs, and finally Component Pascal all use this method. CP has a non-Wirthian successor called Zonnon that also uses this feature.
----
How's this for a twisted example:
======
package require indentcl
indentcl::ieval {
package require critcl
critcl::ccode fraction {int n} double
double f = 1;
while (--n >= 0)
f /= 2;
return f;
puts [fraction 6]
}
======
[jcw] 2004-11-08: Heh, interesting one :)
Here's another example I toyed with recently, using htmlgen to generate HTML code:
======
proc do_html {}
global info cmds objs this desc sect
html
head
meta http-equiv=Content-type {content=text/html; charset=utf-8} - ""
title - $info(name) $info(version)
style type=text/css +
body
do_sect name
p - [b $info(name) - $info(title)]
do_sect synopsis
p ! $info(preamble)
foreach {type name} $info(calls)
do_call $type $name
br -
do_sect description
do_text $desc(~)
dl
foreach {type name} $info(calls)
dt
do_call $type $name
dd
do_text $desc($name)
foreach name $::info(sections)
do_sect $name
do_text $sect($name)
if {$info(examples) ne ""}
do_sect examples
foreach {x y} $info(examples)
set x [string trim $x]
if {$x ne ""}
do_text $x
regsub {^\n} $y {} y
set y [string trimright $y]
if {$y ne ""}
pre width=81n - " [esc $y]"
foreach x {author copyright bugs website see-also keywords}
if {$info($x) ne ""}
do_sect [string map {- " "} $x]
set y $info($x)
switch $x
author { p - Written by $y. }
copyright { p - Copyright {©} $y }
keywords { p - [join $y ", "] }
website { p - See [a href=$y $y]. }
default { p - $y }
======
----
[IL]: Simply amazing.
[jblz]: I freakin' love tcl.
<> Syntax