knit, by PYK, is another macro facility for Tcl
knit uses tailcall to provide some of the features that were problematic in earlier macro systems. Each macro is a procedure that fills out a template according to the arguments it receives, and then tailcalls the template.
knit takes an EIAS approach to macros, providing just three template substitutions:
knitbuild can be used to build a macro procedure specification without actually creating the macro procedure. knit is implemented as a trivial wrapper around knitbuild.
knit is also available as ycl::knit::knit
proc knit {name varnames body} { set proc [uplevel [ list [namespace current]::knitbuild $name $varnames $body]] uplevel $proc } proc knitbuild {name varnames body} { variable knittemplate foreach varname $varnames { lappend mapparts [string map [list @varname@ [list $varname]] { \ \${@varname@} "\[lindex [list [set @varname@]]]" \ #{@varname@} [set @varname@] \ !{@varname@} "\[set [set @varname@]]" \ }] } set map [string map [ list @mapparts@ [join $mapparts { }]] {[list @mapparts@]}] set body [string map [list @map@ $map @template@ [ list $body]] $knittemplate] list proc $name $varnames $body } variable knittemplate { set body [string map @map@ @template@] tailcall eval $body }
These examples show how the macros presented in Sugar, along with various other macros are implemented in knit
knit double x {expr {${x} * 2}} knit exp2 x {* ${x} * ${x}} knit clear arg1 {unset ${arg1}} knit first list {lindex ${list} 0} knit rest list {lrange ${list} 1 end} knit last list {lindex ${list} end} knit drop list {lrange ${list} 0 end-1} knit K {x y} { first [list ${x} ${y}] } knit yank varname { K [set ${varname}] [set ${varname} {}] } knit lremove {varname idx} { set ${varname} [lreplace [yank ${varname}] ${idx} ${idx}] } knit lpop listname { K [lindex [set ${listname}] end] [lremove ${listname} end] } knit lpop2 listname { K [lindex !{listname} end] [lremove ${listname} end] } foreach cmdname {* + - /} { knit $cmdname args " expr \[join \${args} [list $cmdname]] " } knit sete {varname exp} { set ${varname} [expr {#{exp}}] } knit greeting? x {expr {${x} in {hello hi}}} knit until {expr body} { while {!(#{expr})} ${body} }