According to Wikipedia ,
EKB: FB, I agree with your point. I rewrote this so it reads as documentation, rather than a discussion. Is this OK with you? FB: No problem!
Note that this is not the same as simply putting text inside curly braces. It might seem like it since, for example, the Ruby example from the Wikipedia article:
puts <<GROCERY_LIST Grocery list ------------ 1. Salad mix. 2. Strawberries.* 3. Cereal. 4. Milk.* * Organic GROCERY_LIST
can easily be implemented in Tcl:
puts {Grocery list
------------
1. Salad mix.
2. Strawberries.*
3. Cereal.
4. Milk.*
* Organic}But a "pure" heredoc is format-agnostic. The example above will fail if the string includes an unbalanced brace, e.g.:
Ruby:
puts <<GROCERY_LIST Grocery list } ------------ 1. Salad mix. 2. Strawberries.* 3. Cereal. 4. Milk.* * Organic GROCERY_LIST
Tcl:
puts {Grocery list }
# everything below is a syntax error because of the above close brace.
------------
1. Salad mix.
2. Strawberries.*
3. Cereal.
4. Milk.*
* Organic}So in Tcl you have several ways of specifying string litterals:
Tcl doesn't provide an heredoc like feature, because all quoting rules require the user to escape all significant characters. This is a need that FB tries to address in Cloverfield with the {data} word modifier (see Cloverfield - Tridekalogue, section Word modifiers, item Raw data). The above example becomes:
puts {data}GROCERY_LIST
Grocery list }
------------
1. Salad mix.
2. Strawberries.*
3. Cereal.
4. Milk.*
* Organic
GROCERY_LISTwhich is very similar to the Ruby version. The real strength of heredoc is to let the user supply an arbitrary delimiter (here GROCERY_LIST) to circumvent the regular formatting rules.
BTW, the present Wiki provides heredoc-like markup with the six- and three-equal sign sequences (see Wiki formatting rules). Of course they are not real heredocs because one cannot use these sequences within the quoted text, but the concept is quite similar.
dbohdan 2015-08-06: What follows is a rough sketch of a preprocessor that lets you embed heredocs in Tcl source code. I have not seriously tested it; please fix any bugs you find. Note that with this preprocessor each heredoc must start with the text <<<(LABEL) on a line of its own.
# v0.0.1
package require fileutil
namespace eval ::heredoc {}
# Extract a heredoc from Tcl source code.
proc ::heredoc::extract {source label} {
set lines [split $source \n]
set heredoc {}
set offset [lsearch -exact $lines "<<<$label"]
if {$offset == -1} {
error "can't find a heredoc with label $label"
}
set line {}
for {set i $offset} {$line ne $label} {incr i} {
set line [lindex $lines $i]
lappend heredoc $line
}
return [join [lrange $heredoc 1 end-1] \n]
}
# Convert Tcl source code with embedded heredocs to standard Tcl.
proc ::heredoc::embed-all source {
set lines [split $source \n]
set result {}
set label {}
set on 0
foreach line $lines {
if {$on} {
if {$line eq $label} {
set on 0
lappend result "\[join [list $acc] \\n\]"
} else {
lappend acc $line
}
} else {
if {[regexp ^<<<(.*)$ $line _ label]} {
set on 1
set acc {}
} else {
lappend result $line
}
}
}
return [join $result \n]
}puts \
<<<HEREDOC1
Hello, World!
{{{
}}
HEREDOC1
set a \
<<<HEREDOC2
Blah!
HEREDOC2
puts $aputs \
[join {{Hello, World!} \{\{\{ \}\}} \n]
set a \
[join Blah! \n]
puts $aaspect .. by looking at info frame, one can do (arguably) slightly better:
proc <<< args {
if {[llength $args] < 2} {error "incorrect args: expected \"<<< cmd ?arg ...? marker\""}
set marker [lindex $args end]
set herecmd [lrange $args 0 end-1]
set frame [info frame -1]
dict with frame {}
if {$type ne "source"} {error "Invalid location for <<<"} ;# not safe
set fd [open $file r]
for {set i 0} {$i < $line} {incr i} { ;# skip what's already been evaluated
append script [gets $fd]
}
if {![info complete $script]} {error "invalid location for <<<"} ;# definitely not safe!
while {[gets $fd line]} { ;# get the heredoc
if {$line eq $marker} break
lappend heredoc $line
}
set heredoc [join $heredoc \n]
set script [read $fd] ;# rest of the script
close $fd
uplevel 1 $herecmd [list $heredoc]
uplevel 1 $script
return -code return ;# tell our caller (source) that it's done
}
puts starting
<<< set xxx _END_
Hello, World!
{{{
}}
_END_
puts $xxx
puts etcOf course this breaks horribly if you try to put multiple here-docs in the same file, and (for the same reason) anything after the heredoc will lie in error traces about where it was defined. These could be worked around by mounting and sourcing a vfs proxy of the original file, but that's some pretty serious interpreter abuse and probably illegal in some places.
PYK 2015-08-08:
Heredocs are in the Tcl doesn't have that because it would be a misfeature category. Other languages need heredocs because their syntax is more irregular. Tcl doesn't have that problem. Anything that can be accomplished with a heredoc can be accomplished just as easily with standard Tcl syntax:
puts "
Hello, World!
{{{
}}
"string cat or append can be used to make easy work of more complicated text:
string cat {Hello, World you owe me $5!
} "{{{
}}"