aio: open |... should use ::popen
[jimtcl.git] / stdlib.tcl
blob7aa479f8ea38d8bc3fd7d524fe0c6bf7cf475c05
1 # Creates an anonymous procedure
2 proc lambda {arglist args} {
3 tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args
6 proc lambda.finalizer {name val} {
7 rename $name {}
10 # Like alias, but creates and returns an anonyous procedure
11 proc curry {args} {
12 alias [ref {} function lambda.finalizer] {*}$args
15 # Returns the given argument.
16 # Useful with 'local' as follows:
17 # proc a {} {...}
18 # local function a
20 # set x [lambda ...]
21 # local function $x
23 proc function {value} {
24 return $value
27 # Returns a list of proc filename line ...
28 # with 3 entries for each stack frame (proc),
29 # (deepest level first)
30 proc stacktrace {} {
31 set trace {}
32 foreach level [range 1 [info level]] {
33 lassign [info frame -$level] p f l
34 lappend trace $p $f $l
36 return $trace
39 # Returns a human-readable version of a stack trace
40 proc stackdump {stacktrace} {
41 set result {}
42 set count 0
43 foreach {l f p} [lreverse $stacktrace] {
44 if {$count} {
45 append result \n
47 incr count
48 if {$p ne ""} {
49 append result "in procedure '$p' "
50 if {$f ne ""} {
51 append result "called "
54 if {$f ne ""} {
55 append result "at file \"$f\", line $l"
58 return $result
61 # Sort of replacement for $::errorInfo
62 # Usage: errorInfo error ?stacktrace?
63 proc errorInfo {msg {stacktrace ""}} {
64 if {$stacktrace eq ""} {
65 set stacktrace [info stacktrace]
67 lassign $stacktrace p f l
68 if {$f ne ""} {
69 set result "Runtime Error: $f:$l: "
71 append result "$msg\n"
72 append result [stackdump $stacktrace]
74 # Remove the trailing newline
75 string trim $result
78 # Finds the current executable by searching along the path
79 # Returns the empty string if not found.
80 proc {info nameofexecutable} {} {
81 if {[info exists ::jim_argv0]} {
82 if {[string match "*/*" $::jim_argv0]} {
83 return [file join [pwd] $::jim_argv0]
85 foreach path [split [env PATH ""] $::tcl_platform(pathSeparator)] {
86 set exec [file join [pwd] [string map {\\ /} $path] $::jim_argv0]
87 if {[file executable $exec]} {
88 return $exec
92 return ""
95 # Script-based implementation of 'dict with'
96 proc {dict with} {&dictVar {args key} script} {
97 set keys {}
98 foreach {n v} [dict get $dictVar {*}$key] {
99 upvar $n var_$n
100 set var_$n $v
101 lappend keys $n
103 catch {uplevel 1 $script} msg opts
104 if {[info exists dictVar] && ([llength $key] == 0 || [dict exists $dictVar {*}$key])} {
105 foreach n $keys {
106 if {[info exists var_$n]} {
107 dict set dictVar {*}$key $n [set var_$n]
108 } else {
109 dict unset dictVar {*}$key $n
113 return {*}$opts $msg
116 # Script-based implementation of 'dict update'
117 proc {dict update} {&varName args script} {
118 set keys {}
119 foreach {n v} $args {
120 upvar $v var_$v
121 if {[dict exists $varName $n]} {
122 set var_$v [dict get $varName $n]
125 catch {uplevel 1 $script} msg opts
126 if {[info exists varName]} {
127 foreach {n v} $args {
128 if {[info exists var_$v]} {
129 dict set varName $n [set var_$v]
130 } else {
131 dict unset varName $n
135 return {*}$opts $msg
138 # Script-based implementation of 'dict merge'
139 # This won't get called in the trivial case of no args
140 proc {dict merge} {dict args} {
141 foreach d $args {
142 # Check for a valid dict
143 dict size $d
144 foreach {k v} $d {
145 dict set dict $k $v
148 return $dict
151 proc {dict replace} {dictionary {args {key value}}} {
152 if {[llength ${key value}] % 2} {
153 tailcall {dict replace}
155 tailcall dict merge $dictionary ${key value}
158 # Script-based implementation of 'dict lappend'
159 proc {dict lappend} {varName key {args value}} {
160 upvar $varName dict
161 if {[exists dict] && [dict exists $dict $key]} {
162 set list [dict get $dict $key]
164 lappend list {*}$value
165 dict set dict $key $list
168 # Script-based implementation of 'dict append'
169 proc {dict append} {varName key {args value}} {
170 upvar $varName dict
171 if {[exists dict] && [dict exists $dict $key]} {
172 set str [dict get $dict $key]
174 append str {*}$value
175 dict set dict $key $str
178 # Script-based implementation of 'dict incr'
179 proc {dict incr} {varName key {increment 1}} {
180 upvar $varName dict
181 if {[exists dict] && [dict exists $dict $key]} {
182 set value [dict get $dict $key]
184 incr value $increment
185 dict set dict $key $value
188 # Script-based implementation of 'dict remove'
189 proc {dict remove} {dictionary {args key}} {
190 foreach k $key {
191 dict unset dictionary $k
193 return $dictionary
196 # Script-based implementation of 'dict values'
197 proc {dict values} {dictionary {pattern *}} {
198 dict keys [lreverse $dictionary] $pattern
201 # Script-based implementation of 'dict for'
202 proc {dict for} {vars dictionary script} {
203 if {[llength $vars] != 2} {
204 return -code error "must have exactly two variable names"
206 dict size $dictionary
207 tailcall foreach $vars $dictionary $script