-
Notifications
You must be signed in to change notification settings - Fork 0
/
try.tcl
190 lines (161 loc) · 4.8 KB
/
try.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# Do nothing if the try command exists already (8.6+).
if {[llength [info commands try]]} return
# The code below was snarfed from the Tcl core, as a forward
# compatible implementation of try/catch/finally for Tcl 8.5.
# TIP #329: [try]
# This is a *temporary* implementation, to be replaced with one in C and
# bytecode at a later date before 8.6.0
namespace eval ::tcl::control {
# These are not local, since this allows us to [uplevel] a [catch] rather
# than [catch] the [uplevel]ing of something, resulting in a cleaner
# -errorinfo:
variable em {}
variable opts {}
variable magicCodes { ok 0 error 1 return 2 break 3 continue 4 }
namespace export try
# ::tcl::control::try --
#
# Advanced error handling construct.
#
# Arguments:
# See try(n) for details
proc try {args} {
variable magicCodes
# ----- Parse arguments -----
set trybody [lindex $args 0]
set finallybody {}
set handlers [list]
set i 1
while {$i < [llength $args]} {
switch -- [lindex $args $i] {
"on" {
incr i
set code [lindex $args $i]
if {[dict exists $magicCodes $code]} {
set code [dict get $magicCodes $code]
##nagelfar ignore
} elseif {![string is integer -strict $code]} {
set msgPart [join [dict keys $magicCodes] {", "}]
error "bad code '[lindex $args $i]': must be\
integer or \"$msgPart\""
}
##nagelfar ignore
set code [format %d $code]
lappend handlers [lrange $args $i $i] $code {} {*}[lrange $args $i+1 $i+2]
incr i 3
}
"trap" {
incr i
if {![string is list [lindex $args $i]]} {
error "bad prefix '[lindex $args $i]':\
must be a list"
}
lappend handlers [lrange $args $i $i] 1 \
{*}[lrange $args $i $i+2]
incr i 3
}
"finally" {
incr i
set finallybody [lindex $args $i]
incr i
break
}
default {
error "bad handler '[lindex $args $i]': must be\
\"on code varlist body\", or\
\"trap prefix varlist body\""
}
}
}
if {($i != [llength $args]) || ([lindex $handlers end] eq "-")} {
error "wrong # args: should be\
\"try body ?handler ...? ?finally body?\""
}
# ----- Execute 'try' body -----
variable em
variable opts
set EMVAR [namespace which -variable em]
set OPTVAR [namespace which -variable opts]
set code [uplevel 1 [list ::catch $trybody $EMVAR $OPTVAR]]
if {$code == 1} {
set line [dict get $opts -errorline]
dict append opts -errorinfo \
"\n (\"[lindex [info level 0] 0]\" body line $line)"
}
# Keep track of the original error message & options
set _em $em
set _opts $opts
# ----- Find and execute handler -----
set errorcode {}
if {[dict exists $opts -errorcode]} {
set errorcode [dict get $opts -errorcode]
}
set found false
foreach {descrip oncode pattern varlist body} $handlers {
if {!$found} {
if {
($code != $oncode) || ([lrange $pattern 0 end] ne
[lrange $errorcode 0 [llength $pattern]-1] )
} then {
continue
}
}
set found true
if {$body eq "-"} {
continue
}
# Handler found ...
# Assign trybody results into variables
lassign $varlist resultsVarName optionsVarName
if {[llength $varlist] >= 1} {
upvar 1 $resultsVarName resultsvar
set resultsvar $em
}
if {[llength $varlist] >= 2} {
upvar 1 $optionsVarName optsvar
set optsvar $opts
}
# Execute the handler
set code [uplevel 1 [list ::catch $body $EMVAR $OPTVAR]]
if {$code == 1} {
set line [dict get $opts -errorline]
dict append opts -errorinfo \
"\n (\"[lindex [info level 0] 0] ... $descrip\"\
body line $line)"
# On error chain to original outcome
dict set opts -during $_opts
}
# Handler result replaces the original result (whether success or
# failure); capture context of original exception for reference.
set _em $em
set _opts $opts
# Handler has been executed - stop looking for more
break
}
# No catch handler found -- error falls through to caller
# OR catch handler executed -- result falls through to caller
# ----- If we have a finally block then execute it -----
if {$finallybody ne {}} {
set code [uplevel 1 [list ::catch $finallybody $EMVAR $OPTVAR]]
# Finally result takes precedence except on success
if {$code == 1} {
set line [dict get $opts -errorline]
dict append opts -errorinfo \
"\n (\"[lindex [info level 0] 0] ... finally\"\
body line $line)"
# On error chain to original outcome
dict set opts -during $_opts
}
if {$code != 0} {
set _em $em
set _opts $opts
}
# Otherwise our result is not affected
}
# Propagate the error or the result of the executed catch body to the
# caller.
dict incr _opts -level
return -options $_opts $_em
}
}
namespace import ::tcl::control::try