1 # Implements script-based standard commands for Jim Tcl
3 if {![exists
-command ref
]} {
4 # No support for references, so create a poor-man's reference just good enough for lambda
5 proc ref
{args
} {{count
0}} {
6 format %08x
[incr count
]
10 # Creates an anonymous procedure
11 proc lambda
{arglist args
} {
12 tailcall
proc [ref
{} function lambda.finalizer
] $arglist {*}$args
15 proc lambda.finalizer
{name val
} {
19 # Like alias, but creates and returns an anonyous procedure
21 alias
[ref
{} function lambda.finalizer
] {*}$args
24 # Returns the given argument.
25 # Useful with 'local' as follows:
32 proc function
{value
} {
36 # Returns a live stack trace as a list of proc filename line ...
37 # with 3 entries for each stack frame (proc),
38 # (deepest level first)
39 proc stacktrace
{{skip
0}} {
42 foreach level
[range
$skip [info level
]] {
43 lappend trace {*}[info frame -$level]
48 # Returns a human-readable version of a stack trace
49 proc stackdump
{stacktrace
} {
51 foreach {l f p
} [lreverse
$stacktrace] {
54 append line
"in procedure '$p' "
60 append line
"at file \"$f\", line $l"
69 # Add the given script to $jim::defer, to be evaluated when the current
76 # Sort of replacement for $::errorInfo
77 # Usage: errorInfo error ?stacktrace?
78 proc errorInfo
{msg
{stacktrace
""}} {
79 if {$stacktrace eq
""} {
80 # By default add the stack backtrace and the live stacktrace
81 set stacktrace
[info stacktrace
]
82 # omit the procedure 'errorInfo' from the stack
83 lappend stacktrace
{*}[stacktrace
1]
85 lassign
$stacktrace p f l
87 set result
"$f:$l: Error: "
89 append result
"$msg\n"
90 append result
[stackdump
$stacktrace]
92 # Remove the trailing newline
96 # Needs to be set up by the container app (e.g. jimsh)
97 # Returns the empty string if unknown
98 proc {info nameofexecutable
} {} {
99 if {[exists
::jim::exe]} {
104 # Script-based implementation of 'dict update'
105 proc {dict
update} {&varName args script
} {
107 foreach {n v
} $args {
109 if {[dict exists
$varName $n]} {
110 set var_
$v [dict get
$varName $n]
113 catch {uplevel 1 $script} msg opts
114 if {[info exists varName
]} {
115 foreach {n v
} $args {
116 if {[info exists var_
$v]} {
117 dict
set varName
$n [set var_
$v]
119 dict
unset varName
$n
126 proc {dict replace
} {dictionary
{args
{key value
}}} {
127 if {[llength ${key value
}] % 2} {
128 tailcall
{dict replace
}
130 tailcall dict merge
$dictionary ${key value
}
133 # Script-based implementation of 'dict lappend'
134 proc {dict
lappend} {varName key
{args value
}} {
136 if {[exists dict
] && [dict exists
$dict $key]} {
137 set list [dict get
$dict $key]
139 lappend list {*}$value
140 dict
set dict
$key $list
143 # Script-based implementation of 'dict append'
144 proc {dict
append} {varName key
{args value
}} {
146 if {[exists dict
] && [dict exists
$dict $key]} {
147 set str
[dict get
$dict $key]
150 dict
set dict
$key $str
153 # Script-based implementation of 'dict incr'
154 proc {dict
incr} {varName key
{increment
1}} {
156 if {[exists dict
] && [dict exists
$dict $key]} {
157 set value
[dict get
$dict $key]
159 incr value
$increment
160 dict
set dict
$key $value
163 # Script-based implementation of 'dict remove'
164 proc {dict remove
} {dictionary
{args key
}} {
166 dict
unset dictionary
$k
171 # Script-based implementation of 'dict for'
172 proc {dict
for} {vars dictionary script
} {
173 if {[llength $vars] != 2} {
174 return -code error "must have exactly two variable names"
176 dict size
$dictionary
177 tailcall
foreach $vars $dictionary $script