ycl

Artifact [f45acb38cf]
Login

Artifact [f45acb38cf]

Artifact f45acb38cf45342550592e51667b36c86f7a6451:


#! /bin/env tclsh

package require {ycl proc}
package require {ycl chan diagnostic}
[yclprefix] proc alias [yclprefix]::proc::alias
[yclprefix] proc alias aliases [yclprefix] proc aliases
aliases {
	{ycl list} {
		sl
	}
	{ycl ns ensemble} {}
	{ycl ns} {
		ensemble
		normalize
		powerimport
	}
}

# [history add] is auto-loaded, so leave it out of [subsume], and let
# the user decide whether to wrap it.
variable subsume {
	after catch {chan event} {dict {for map update with}} 
	eval expr for foreach {interp eval} if lmap
	{namespace eval} source subst switch time try uplevel while
}

variable override {
	{info args body} lindex list set rename return string 
}

# Bootstrap issue: Make aliases to the real commands so that they can be used
# by the name that they would have if [subsume] were run, allowing the commands
# below to function properly both before and after [subsume].
apply [list args {
	foreach cmdset $args {
		foreach name $cmdset {
			lassign $name[set name {}] name subcmds
			if {[catch {
				set ns [namespace ensemble configure ::$name -namespace]
			}]} {
				rename ::$name [namespace current]::$name
				interp alias {} ::$name {} [namespace current]::$name
			} else {
				ensemble duplicate ::$name $name tons [
					namespace ensemble configure ::$name -namespace]

			}
		}
	}
} [namespace current]] $subsume $override


proc cmdhandler {infoname cmdidx ns args} {
	return $args
}

proc errhandler {infoname cmdidx ns cres copts}  {
	# TO DO: fix all this
	variable procs
	catch {
		set body [uplevel 1 [list [namespace current]::info body] {[
			lindex [info level 0] 0]}]
		set cmd1 [uplevel 1 [list [namespace which info] level 0]]
		set frame [uplevel [list [namespace which info] frame]]
	}
	set frameinfo [info frame -1]
	dict update copts -errorinfo2 -errorinfo2 -errorinfo -errorinfo {
		if {![info exists -errorinfo2]} {
			append -errorinfo2 "$cres\n    while executing\n\"[
				dict get $copts -errorstack INNER]\"\n"
		}
		append -errorinfo2 "\n    invoked from within\n\"[
			dict get $copts -errorstack CALL]\"\n"
		if {[dict exists $frameinfo proc]} {
			append -errorinfo2 "\n    (proc \"[
				dict get $frameinfo proc]\" line [dict get $frameinfo line])"
		}
		set -errorinfo ${-errorinfo2}
	}
	return [list return -options $copts $cres]
}

proc info_args name {
	variable procs
	if {![string match ::* $name]} {
		set name [uplevel 1 [list namespace which $name]]
	}
	if {[dict exists $procs $name wrapped]} {
		return [[namespace current]::info args [dict get $procs $name wrapped]]
	}
	tailcall info args $name
}

proc info_body name {
	variable procs
	if {![string match ::* $name]} {
		set name [uplevel 1 [list namespace which $name]]
	}
	if {[dict exists $procs $name]} {
		return [dict get $procs $name body]
	}
	tailcall info body $name
}

proc pre {} {
	upvar config config wrapped wrapped args args name name
	set config [uplevel 2 [list [namespace which stepconfig]] $args]
	if {![string match ::* $wrapped]} {
		set wrapped [
			string trimright [uplevel 2 [
				list [namespace which namespace] current]]::$wrapped]
	}
	if {![string match ::* $name]} {
		set name [string trimright [uplevel 2 [
			list [namespace which namespace] current]]::$name]
	}
}

proc bluepill {} {
	variable matrix 1 
}

proc redpill {} {
	variable matrix 0
}

proc matrix {} {
	variable matrix
	return $matrix
}

proc stepconfig args {
	variable interp
	variable errhandler_body
	variable cmdhandler_errhandler_body
	variable stepconfig_defaults
	set config [info cmdcount]
	set args [dict merge $stepconfig_defaults $args[set args {}]]
	dict update args {*}{cmdhandler cmdhandler
		cmdhandler_template cmdhandler_template
		errhandler errhandler 
		catchcode catchcode catchres catchres 
		catchopts catchopts} {}
	set ns [uplevel 1 [list [namespace which namespace] current]]
	foreach vname {catchcode catchres catchopts} {
		if {[info exists $vname]} {
			set $vname [normalize $vname $ns]
		} else {
			set $vname [normalize $vname]
		}
	}
	foreach cname {cmdhandler errhandler} {
		upvar 0 $cname var
		if {[info exists var]} {
			if {$var eq {}} {
				dict set args $cname {}
			} else {
				# In order to explicitly specify which [namespace] command,
				# don't use [namespace code] .
				set $cname [lreplace $var 0 0 [
					uplevel 1 [list [namespace which namespace] which [
					lindex $var[set var {}] 0]]]]

				dict set args $cname $var 
			}
		} else {
			set $cname [list [namespace which $cname]]
			dict set args $cname $var 
		}
	}

	if {$cmdhandler eq {}}  {
		set cmdhandler_body1 @command@
	} else {
		set cmdhandler_body1 [string map [
			list @cmdhandler@ [list $cmdhandler]] $cmdhandler_template]
	}

	if {$errhandler eq {}} {
		set body1 "[list [namespace which if]] 1 $cmdhandler_body1"
	} else {
		set body1 [string map [list \
			@cmdhandler_body@ [list $cmdhandler_body1] \
			@errhandler@ $errhandler
		] $cmdhandler_errhandler_body]

	}

	set body1 [string map [list \
		@catchcode@ [list $catchcode] \
		@catchres@ [list $catchres] \
		@catchopts@ [list $catchopts] \
	] $body1]

	dict set args cmdhandler_body $body1 
	
	$interp eval [list set $config $args]
	return $config
}


proc subsume args {
	variable subsume
	variable subsumed
	if {$subsumed} {
		return
	}
	package require {ycl proc step builtins}
	foreach name $subsume {

		# temporary code for testing
		if {$name in {uplevel}} {
			continue
		}
		# ::$name has already been renamed and aliased at the
		# beginning of this script . 

		if {[llength $name] == 1} {
			uplevel 1 [list [namespace which builtins] wrap_$name [
				namespace current]::$name ::$name] $args
		} else {
			lassign $name[set name {}] name subcmds
			namespace eval w::$name "
				[list [namespace which namespace]] export *
			"
			if {[catch {set ns [
				namespace ensemble configure ::$name -namespace]} copts cres]
			} {
				#not a true namespace ensemble . Turn it into one .

				# Need some place to stash the original subcommands . Note that
				# this creates a namespace that is not tied to the command by
				# the same name .
				set econfig {}
			
				foreach subcmd $subcmds {
					uplevel 1 [list [namespace which builtins] \
						wrap_${name}_$subcmd $name] [
						namespace current]::w::${name}::$subcmd $args
					dict set econfig -map $subcmd [
						list [namespace current]::w::${name}::$subcmd]
				}
				dict set econfig -unknown [
					list apply [list {wrapped cmd subcmd args} {
					return [list $wrapped $subcmd]
				} [namespace current]] [namespace current]::$name]
				
			} {
				set econfig [namespace ensemble configure ::$name]
				set map [dict get $econfig -map]
				foreach subcmd $subcmds {
					# Create the new proc before renaming the old proc
					# because functionality like [dict update] is needed
					# during proc creation.
					uplevel 1 [list [namespace which builtins] \
						wrap_${name}_$subcmd ${ns}::$subcmd \
						[namespace current]::w::${name}::$subcmd] $args

					dict set map $subcmd [
						namespace current]::w::${name}::$subcmd
				}
				dict unset econfig -namespace
			}
			namespace eval w::$name "
				[list [namespace which namespace]] ensemble create -command [
					list ::$name] -map [list $map]
			"
			namespace ensemble configure ::$name {*}$econfig
		}
	}
	rename ::proc proc
	uplevel 1 [list [namespace which stepproc] ::proc] $args
	set subsumed 1
}

variable doc::stepproc {
	description {
		creates a a new proc-creating proc that implements step functionality.
		Returns the name of a variable holding configuration information for
		the new proc.  To change the behaviour of The contents of this variable can be changed 
	}
	args {
		catchcode {
			description {
				Name of a variable to hold the result of the [catch] command
			}
			default {
				[namespace current]::catchcode
			}
		}
		catchopts {
			description {
				Name to pass to [catch] as optionsVarName
			}
			default {
				[namespace current]::catchopts
			}
		}
		catchres {
			description {
				Name to pass to [catch] as resultVarName
			}
			default {
				[namespace current]::catchres
			}
		}
		cmdhandler {
			description {
				A command prefix to which is appended the namespace of the
				command and the words of the command itself.  If it is the
				empty string, no command handler is installed.
			}
			default {
				The default command handler in this namespace
			}
		}
		cmdhandler_template {
			description {
				The template that the command handler is inserted into.
			}

		}
		errhandler {
			description {
				A command prefix to which is appended the name
			}
			default {
				The default error handler in this namespace
			}
		}
		subs {
			description {
				Boolean indicating whether to descend into command
				substitutions
			}
			default {lindex 1}
		}
		varsubs {
			description {
				A boolean indicating whether to process variable substitutions
			}
			default {lindex 1}
		}
	}
}
proc stepproc {name args} {
	variable interp
	variable procs
	set config [uplevel 1 [list [namespace which stepconfig] {*}$args]]

	uplevel 1 [list [namespace which proc] $name {name args body} [
		string map [list \
			@config@ [list $config] \
			@list@ [list [namespace which list]] \
			@namespace@ [list [namespace which namespace]] \
			@proc@ [list [namespace which proc]] \
			@procs@ [list [namespace which -var procs]] \
			@stepscript@ [list [namespace which stepscript]] \
			@uplevel@ [list [namespace which uplevel]]	
		] {
			lassign [@stepscript@ $body @config@] infoname newbody
			@uplevel@ 1 [@list@ @proc@ $name $args $newbody]
			set name [@uplevel@ 1 [list @namespace@ which $name]]
			dict set @procs@ $name body $body
		}]]
	return $config
}


proc steprename {oldname newname} {
	variable procs
	if {[string match ::* $oldname]} {
		set oldname [uplevel 1 [list namespace which $oldname]]
	}
	uplevel 1 [list [namespace which rename] $oldname $newname]
	if {[dict exists $procs $oldname]} {
		if {$newname ne {}} {
			set newname [uplevel 1 [list namespace which $newname]]
			dict set $procs $newname [dict get $procs $oldname]
		}
		dict unset procs $oldname
	}
}

proc stepscript args {
	variable interp
	lassign [$interp eval stepscript $args [list {}]] infoname body2
	set body2 [string map [list \
		@matrix@ [list [namespace which matrix]] \
		@if@ [list [namespace which if]] \
		@body@ [list [lindex $args 0]] \
		@body2@ [list $body2]
	] {
		@if@ {[@matrix@]} @body2@ else @body@
	}]
	return [list $infoname $body2]
}


variable doc::wrap {
	description {
		Drop a stepproc in place of $name and return the new name of the
		replaced proc
	}
	args {
		indices {
			description {
				A list of variable names contain treated as Tcl
				scripts and transformed by stepproc.

				If the index is the special value, "args", the arguments, the
				values in $args (as seen by the wrapped proc) are joined with
				whitespace and then transformed by stepproc.
			}
		}

		args {
			As described for stepproc
		}
	}
}
proc wrap {wrapped name wrapargs args} {
	variable procs
	pre
	set script [wrapper $wrapped $wrapargs $config]
	uplevel 1 [list proc $name args $script]
	dict set procs $name wrapped $wrapped 
}


proc wrapper {wrapped wrapargs config} {
	variable procs
	if {![string match ::* $wrapped]} {
		return -code error [list {name not normalized} $wrapped]
	}
	set wrapargs [dict merge [dict create indices {} eindices {}] $wrapargs[
		set wrapargs {}]]
	
	dict update wrapargs body body indices indices eindices eindices {}
	set seen {}
	set checkseen {
		if {($idx eq {args} ? {end} : $idx) in $seen} {
			return -code error [list {index already wrapped} $idx]
		}
	}
	foreach idx $indices {
		eval $checkseen
		lappend seen [expr {$idx eq {args} ? {end} : $idx}]
		if {$idx eq {args}} {
			set t {
				lassign [@stepscript@ [join [
					lindex $args end] { }] @config@] infoname newscript
				set args [lreplace $args end end $newscript]
			}
		} else {
			set t {
				lassign [@stepscript@ [lindex $args @idx@] @config@] \
					infoname newscript
				set args [lreplace $args @idx@ @idx@ $newscript]
			}
		}
		append script \n[string map [list \
			@idx@ [list $idx] \
			@stepscript@ [list [namespace which stepscript]] \
			@config@ [list $config] \
		] $t]
	}
	foreach idx $eindices {
		eval $checkseen
		if {$idx eq {args}} {
			set t {
				lassign [@stepexpr@ {} [
					join [lindex $args[set args {}] end] { }] @config@ {}]] \
						info newargs
				set args [lreplace $args end end $newargs]
			}
		} else {
			set t {
				lassign [@stepexpr@ {} [lindex $args @idx@] @config@ {}] \
						info newargs
				set args [lreplace $args @idx@ @idx@ $newargs]
			}
		}
		append script \n[string map [list \
			@idx@ [list $idx] \
			@stepexpr@ [list [namespace which stepexpr]] \
			@config@ [list $config] \
		] $t]
		
	}
	if {[info exists body]} {
		append script \n$body\n 
	}
	append script {
		::tailcall @wrapped@ {*}$args
	}
	set script [string map [list \
		@config@ [list $config] \
		@wrapped@ [list $wrapped] \
		@stepscript@ [list [namespace which stepscript]] \
	] $script[set script {}]]
}


# stepuplevel was abandoned in favor of stepproc. To work, it would have to be
# rewritten to the current implementation of the rest of this script. 
proc stepuplevel {name args body} {
	::uplevel 1 [::list ::tcl::proc $name $args [::list apply [::list args [
		::string map [::list {${body}} [::list $body]] {
			::foreach command [::scriptSplit ${body}] {
				set status [
					::catch [::list ::uplevel 1 [
						::cmdhandler $command]] cres copts]
				::if {$status} {
					::tailcall ::errhandler $status $cres $copts
				}
			}
			lindex $cres
		}
	]] [uplevel 1 {namespace current}]]]
}


variable interp [interp create]
$interp eval [list set auto_path $auto_path] 
$interp eval {
	package require {ycl parse tcl}
	namespace import [yclprefix]::parse::tcl::isvarsub
	namespace import [yclprefix]::parse::tcl::nocomments
	namespace import [yclprefix]::parse::tcl::words
	namespace import [yclprefix]::parse::tcl::wordparts
	namespace import [yclprefix]::parse::tcl::wordparts_info
	namespace import [yclprefix]::parse::tcl::varparts
	namespace import [yclprefix]::parse::tcl::varparts_info

	namespace import [yclprefix]::parse::tcl::exprlex
	namespace import [yclprefix]::parse::tcl::exprlex_info
	namespace import [yclprefix]::parse::tcl::funclex
	namespace import [yclprefix]::parse::tcl::exprval

	package require {ycl parse tcl commands}
	namespace import [yclprefix]::parse::tcl::commands::commands
	namespace import [yclprefix]::parse::tcl::commands::commands_info
	namespace import [yclprefix]::parse::tcl::words_info

	proc exprs args {
		variable exprs
		if {[llength $args] == 1} {
			return [dict get $exprs $arg]
		} elseif {[llength $args] == 0} {
			return $exprs
		}  else {
			return -code error [list {wrong # args}]
		}
	}

	proc scommand {infoname command configvar state} {
		variable $configvar
		dict with $configvar {}
		set command [string map [list  \
			@infoname@ [list $infoname] \
			@cmdidx@ [list [dict get $state cmdidx]] \
			@command@ $command  \
		] $cmdhandler_body]
		if {$errhandler ne {}} {
			set command [string map [list @errhandler@ $errhandler] $command]
		}
		return [list $command]
	}

	proc scriptinfo id {
		set [namespace current]::$id
	}

	proc stepexpr {infoname body configvar state} {
		variable exprs
		dict set exprs $body [info cmdcount]
		set ref [dict get $exprs $body]
		upvar 0 [namespace current]::$configvar config
		set state [dict merge [dict create \
			cmdidx -1 col 0 offset 0 line 0 end [
				expr {[string length $body] - 1}]
		] $state[set state {}]]
		set res {}
		set info {}
		set body2 [string range $body [dict get $state offset] [
			dict get $state end]]
		set idx -1
		set parts [exprlex $body2]
		set report [exprlex_info]
		set ranges [dict get $report ranges]
		foreach part [exprlex $body2] {
			incr idx
			lassign [lindex $ranges $idx] offset end
			set newstate $state
			dict incr newstate offset $offset 
			dict set newstate end [expr {[dict get $newstate offset] + [
				string length $part] - 1}]
			switch [exprval $part] {
				script {
					dict incr newstate offset
					dict incr newstate end -1 
					lassign [stepscript $body $configvar $newstate] \
						infoname res2
					lappend res \[$res2\]
				}
				variable {
					lassign [wordtransform $infoname $body $part[
						set part {}] 0 $configvar $newstate] info res2
					lappend res $res2 
				}
				quoted {
					lassign [wordtransform $infoname $body $part[
						set part {}] 0 $configvar $newstate] info res2
					lappend res \"$res2\"
				}
				function {
					set fargs [lassign [funclex $part[set part {}]] name]
					foreach arg $fargs[set fargs {}] {
						lassign [stepexpr $infoname $arg $configvar $newstate] info res2
						lappend fargs $res2 
					}
					lappend res $name([join $fargs ,])
				}
				braced - 
				default {
					lappend res $part
				}
			}
		}
		set res [join $res { }]
		dict set exprs $res $body 
		set result [list $info $res]
		return $result
	}

	proc stepscript {body configvar state} {
		set infoname info_[info cmdcount]
		namespace upvar [namespace current] $infoname info
		if {[dict exists $state parent]} {
			dict set info parent [dict get $state parent]
		}
		upvar 0 [namespace current]::$configvar config

		set state [dict merge $state [dict create \
			col 0 offset 0 line 0 end [expr {[string length $body] -1}]
		] $state[set state {}]]
		dict set state cmdidx -1
		dict set state widx -1
		dict update state line line offset soffset {}

		dict set info body $body
		dict set info parts {}
		set body2 [string range $body [
			dict get $state offset] [dict get $state end]]

		# Can't use [nocomments] because it throws off the index of the command
		# in $creport
		#set commands [nocomments [commands $body2]]
		set commands [commands $body2]
		set creport [commands_info]
		foreach command $commands report1 $creport {
			dict incr state cmdidx
			dict update state cmdidx idx {}
			dict update report1 line cmdline character cmdcol {}

			# No reason to adjust $col here
			set line [expr {$line + $cmdline}]

			set cmdinfo [dict create type cmd idx $idx line $line offset [
				expr {$cmdcol + $soffset}]]
			dict set cmdinfo end [expr {[
					string length $command] + [dict get $cmdinfo offset] - 1}]

			if {[string match #* $command]} {
				continue
			}
			if {[dict get $config subs]} {
				set words [words $command[set command {}]]
				set wreport [words_info]
				set widx -1
				foreach word $words {
					incr widx
					lassign [lindex $wreport $widx] woffset wline
					set wordinfo [dict create \
						offset [expr {[dict get $cmdinfo offset] + $woffset}] \
						line $wline \
						raw $word]
					set newstate [dict merge $state [dict create \
						offset [dict get $wordinfo offset] \
						line [expr {$line + $wline}]]]
					if {[string match {{[*]}*} $word]} {
						set argexpansion {{*}}
						dict incr state offset 3
						set word [string range $word[set word {}] 3 end]
					} else {
						set argexpansion {} 
					}
					if {[string match \{* $word]} {
						# To do:  Should anything be done here? 2016-09-05
					} else {
						set quoted [expr {[
							string index $word 0] eq "\"" ? "\"" : {}}]
						lassign [
							wordtransform $infoname $body $word[set word {}] \
								0 $configvar $newstate] newinfo word 
							if {$word ne [dict get $wordinfo raw]} {
								dict set wordinfo parts $newinfo
							}
					}
					lappend command $argexpansion$quoted$word$quoted
					dict lappend cmdinfo parts $wordinfo
				}
			}
			dict lappend info parts $cmdinfo
			set newcmd [scommand $infoname [
				join $command { }] $configvar $state]
			lappend newbody {*}$newcmd
		}
		set newbody [join $newbody[set newbody {}] \n]
		set result [list $infoname $newbody]
		dict set scripts $newbody $infoname
		return $result
	}

	proc wordtransform {infoname body word escapewhite configvar state args} {
		variable $configvar
		upvar 0 [namespace current]::$configvar config
		set res {}
		dict with config {}
		set info {}
		set parts [wordparts $word[set word {}]]
		set report [wordparts_info]
		set idx -1
		foreach wordpart $parts {
			incr idx
			set newstate $state
			lassign [lindex $report $idx] poffset pline
			dict set newstate offset [
				expr {[dict get $state offset] + $poffset}]
			dict set newstate line [expr {[dict get $state line] + $pline}]
			set parttype {}
			set partinfo {}
			if {[string match {[[]*]} $wordpart]} {
				dict incr newstate offset
				dict set newstate end [expr {[dict get $newstate offset] + [
					string length $wordpart] - 3}]
				lassign [
					stepscript $body $configvar $newstate] partinfo res2
				set parttype script
				set wordpart \[$res2\]
			} elseif {[string match $* $wordpart] && $varsubs && [
				isvarsub $wordpart]} {
				lassign [vartransform $infoname $body $wordpart \
					$configvar $newstate] partinfo wordpart
			} elseif {[string match {\\*} $wordpart]} {
				#do nothing
			} elseif {$escapewhite} {
				# a regular word part

				# $word must be the ''index'' of a tcl array name, where the
				# whitespace is quoted by parenthesis, but otherwise not quoted
				regsub -all {(\s)} $wordpart[set wordpart {}] {\\\1} wordpart
			}
			lappend info $parttype $partinfo
			append res $wordpart
		}
		return [list $info $res]
	}

	proc vartransform {infoname body var configvar state args} {
		if {[regsub {^\${(.*)}$} $var {\1} var]} {
			set varbraced 1
		} else {
			set var [string range $var[set var {}] 1 end]
			set varbraced 0
		}
		set varparts [varparts $var]
		set report [varparts_info]
		lassign $varparts name index
		dict set varinfo raw $var
		if {[llength $varparts] == 2} {
			if {$varbraced} {
				if {[string first \} $name] >= 0} {
					return -code error [list \
						{braced variable name contains a closing brace} \
						$varparts]
				}
				if {[string first \} $index] >= 0} {
					return -code error [list \
						{braced variable index contains a closing brace} \
						$varparts]
				}
				set script "::set [list ${name}($index)]"
			} else {
				set newstate [dict merge $state [dict create offset [
					expr {[dict get $state offset] + $report}]]]
				lassign [wordtransform $infoname $body $index \
					1 $configvar $newstate] indexinfo index
				dict set varinfo index $indexinfo
				set script "::set ${name}($index)"
			}

			return [list $varinfo "\[[
				join [scommand $infoname $script $configvar $state] \n]\]"]
		} elseif {[llength $varparts] == 1} {
			set script "::set [list $name]"
			return [list $varinfo "\[[
				join [scommand $infoname $script $configvar $state] \n]\]"]
		} elseif {[llength $varparts] == 0} {
			return [list $varinfo $var]
		}
	}

	variable exprs {}
	variable scripts {}
}

variable matrix 1
variable procs {}
variable subsumed 0

interp alias {} [namespace current]::exprs $interp exprs
interp alias {} [namespace current]::stepexpr $interp stepexpr
interp alias {} [namespace current]::scriptinfo $interp scriptinfo

interp alias {} ::rename {} [namespace current]::steprename
apply [list args {
	set map [namespace ensemble configure ::info -map]
	dict set map args [namespace current]::info_args 
	dict set map body [namespace current]::info_body
	namespace ensemble configure ::info -map $map
} [namespace current]]

variable cmdhandler_body [string map [list \
	@if@ [list [namespace which if]] \
	@namespace@ [list [namespace which namespace]] \
	@matrix@ [list [namespace which -variable matrix]] \
	] {@if@ {[set @matrix@]} {
		@if@ 1 [{*}@cmdhandler@ @infoname@ @cmdidx@ [@namespace@ current] @command@]
	} else {
		@command@
	}}]


variable cmdhandler_body_simple {
	{*}@cmdhandler@ @command@
}

#body must not begin with newline
variable cmdhandler_errhandler_body [string map [list \
	@if@ [list [namespace which if]] \
	@try@ [list [namespace which try]] \
	@namespace@ [list [namespace which namespace]] \
	@set@ [list [namespace which set]]
	] {@try@ @cmdhandler_body@ on error {@catchres@ @catchopts@} {
		@if@ 1 [{*}@errhandler@ @infoname@ @cmdidx@ [@namespace@ current] [@set@ @catchres@] [
			@set@ @catchopts@]]
		}}]


variable stepconfig_defaults [sl {
	cmdhandler [namespace which cmdhandler]
	cmdhandler_template [set [namespace current]::cmdhandler_body]
	subs 1 varsubs 1
}]