179 lines
4.0 KiB
Tcl
179 lines
4.0 KiB
Tcl
# Implements script-based standard commands for Jim Tcl
|
|
|
|
if {![exists -command ref]} {
|
|
# No support for references, so create a poor-man's reference just good enough for lambda
|
|
proc ref {args} {{count 0}} {
|
|
format %08x [incr count]
|
|
}
|
|
}
|
|
|
|
# Creates an anonymous procedure
|
|
proc lambda {arglist args} {
|
|
tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args
|
|
}
|
|
|
|
proc lambda.finalizer {name val} {
|
|
rename $name {}
|
|
}
|
|
|
|
# Like alias, but creates and returns an anonyous procedure
|
|
proc curry {args} {
|
|
alias [ref {} function lambda.finalizer] {*}$args
|
|
}
|
|
|
|
# Returns the given argument.
|
|
# Useful with 'local' as follows:
|
|
# proc a {} {...}
|
|
# local function a
|
|
#
|
|
# set x [lambda ...]
|
|
# local function $x
|
|
#
|
|
proc function {value} {
|
|
return $value
|
|
}
|
|
|
|
# Returns a live stack trace as a list of proc filename line ...
|
|
# with 3 entries for each stack frame (proc),
|
|
# (deepest level first)
|
|
proc stacktrace {{skip 0}} {
|
|
set trace {}
|
|
incr skip
|
|
foreach level [range $skip [info level]] {
|
|
lappend trace {*}[info frame -$level]
|
|
}
|
|
return $trace
|
|
}
|
|
|
|
# Returns a human-readable version of a stack trace
|
|
proc stackdump {stacktrace} {
|
|
set lines {}
|
|
foreach {l f p} [lreverse $stacktrace] {
|
|
set line {}
|
|
if {$p ne ""} {
|
|
append line "in procedure '$p' "
|
|
if {$f ne ""} {
|
|
append line "called "
|
|
}
|
|
}
|
|
if {$f ne ""} {
|
|
append line "at file \"$f\", line $l"
|
|
}
|
|
if {$line ne ""} {
|
|
lappend lines $line
|
|
}
|
|
}
|
|
join $lines \n
|
|
}
|
|
|
|
# Add the given script to $jim::defer, to be evaluated when the current
|
|
# procedure exits
|
|
proc defer {script} {
|
|
upvar jim::defer v
|
|
lappend v $script
|
|
}
|
|
|
|
# Sort of replacement for $::errorInfo
|
|
# Usage: errorInfo error ?stacktrace?
|
|
proc errorInfo {msg {stacktrace ""}} {
|
|
if {$stacktrace eq ""} {
|
|
# By default add the stack backtrace and the live stacktrace
|
|
set stacktrace [info stacktrace]
|
|
# omit the procedure 'errorInfo' from the stack
|
|
lappend stacktrace {*}[stacktrace 1]
|
|
}
|
|
lassign $stacktrace p f l
|
|
if {$f ne ""} {
|
|
set result "$f:$l: Error: "
|
|
}
|
|
append result "$msg\n"
|
|
append result [stackdump $stacktrace]
|
|
|
|
# Remove the trailing newline
|
|
string trim $result
|
|
}
|
|
|
|
# Needs to be set up by the container app (e.g. jimsh)
|
|
# Returns the empty string if unknown
|
|
proc {info nameofexecutable} {} {
|
|
if {[exists ::jim::exe]} {
|
|
return $::jim::exe
|
|
}
|
|
}
|
|
|
|
# Script-based implementation of 'dict update'
|
|
proc {dict update} {&varName args script} {
|
|
set keys {}
|
|
foreach {n v} $args {
|
|
upvar $v var_$v
|
|
if {[dict exists $varName $n]} {
|
|
set var_$v [dict get $varName $n]
|
|
}
|
|
}
|
|
catch {uplevel 1 $script} msg opts
|
|
if {[info exists varName]} {
|
|
foreach {n v} $args {
|
|
if {[info exists var_$v]} {
|
|
dict set varName $n [set var_$v]
|
|
} else {
|
|
dict unset varName $n
|
|
}
|
|
}
|
|
}
|
|
return {*}$opts $msg
|
|
}
|
|
|
|
proc {dict replace} {dictionary {args {key value}}} {
|
|
if {[llength ${key value}] % 2} {
|
|
tailcall {dict replace}
|
|
}
|
|
tailcall dict merge $dictionary ${key value}
|
|
}
|
|
|
|
# Script-based implementation of 'dict lappend'
|
|
proc {dict lappend} {varName key {args value}} {
|
|
upvar $varName dict
|
|
if {[exists dict] && [dict exists $dict $key]} {
|
|
set list [dict get $dict $key]
|
|
}
|
|
lappend list {*}$value
|
|
dict set dict $key $list
|
|
}
|
|
|
|
# Script-based implementation of 'dict append'
|
|
proc {dict append} {varName key {args value}} {
|
|
upvar $varName dict
|
|
if {[exists dict] && [dict exists $dict $key]} {
|
|
set str [dict get $dict $key]
|
|
}
|
|
append str {*}$value
|
|
dict set dict $key $str
|
|
}
|
|
|
|
# Script-based implementation of 'dict incr'
|
|
proc {dict incr} {varName key {increment 1}} {
|
|
upvar $varName dict
|
|
if {[exists dict] && [dict exists $dict $key]} {
|
|
set value [dict get $dict $key]
|
|
}
|
|
incr value $increment
|
|
dict set dict $key $value
|
|
}
|
|
|
|
# Script-based implementation of 'dict remove'
|
|
proc {dict remove} {dictionary {args key}} {
|
|
foreach k $key {
|
|
dict unset dictionary $k
|
|
}
|
|
return $dictionary
|
|
}
|
|
|
|
# Script-based implementation of 'dict for'
|
|
proc {dict for} {vars dictionary script} {
|
|
if {[llength $vars] != 2} {
|
|
return -code error "must have exactly two variable names"
|
|
}
|
|
dict size $dictionary
|
|
tailcall foreach $vars $dictionary $script
|
|
}
|