用 Rebol 编写的跟踪函数
Tracer function written in Rebol
可以在Rebol(Red, R3 Ren-c)中写一个类似于TRACE的函数,产生如下结果:
foo: func [val1 val2 /local temp] [
temp: val1 + 5
val2 + temp
]
bar: func [x /ris 'var "blablalba"][
if ris [set var "fatto"]
(foo x 2) + 8
]
trace [foo bar]
bar/ris 7 yyy
Enter BAR
x = 7
var = yyy
Enter FOO
val1 = 7
val2 = 2
FOO returned 14
BAR returned 22
在用户级别,最直接的方法是编写一个类似闭包的包装器,它会在调用主代码之前和之后调用您提供的跟踪挂钩,然后 return评价.
Red 中该想法的粗略草图如下:
frame-of: function [
func [function!]
/local
match
][
parse spec-of :func [
collect any [
set match [not quote return: all-word!] keep (to word! match)
| skip
]
]
]
report: function [frame [block!]][
environment: construct collect [forall frame [keep to set-word! frame/1]]
also environment set environment reduce frame
]
trace: function [
'target [word!]
enter [block!]
leave [block!]
][
chain: reduce [
'do enter
'set/any quote 'result 'do body-of get target
'do leave
quote :result
]
new: func spec-of get target chain
info: context [
frame: bind frame-of get target :new
name: target
result: none
]
bind body-of :new info
set target :new
exit
]
有了这些,您就可以:
enter: [print ['entering name 'with mold/flat body-of report frame]]
leave: [print [name 'returned result]]
trace foo enter leave
trace bar enter leave
其中,对于您的示例,给出:
>> bar/ris 7 yyy
entering bar with [x: 7 ris: true var: 'yyy]
entering foo with [val1: 7 val2: 2 local: false temp: none]
foo returned 14
bar returned 22
== 22
请注意,这只是一个 PoC。基本思想是您的原始函数被检测版本替换,该版本通过对某些带有调试信息的内部名称空间的闭包创建。您还可以将一些 Red/System 放入组合中以获得对运行时信息的细粒度访问,例如评估堆栈。
作为 reader ;)
的练习,我将保留带有缩进和禁用跟踪的漂亮打印
这个解决方案,不是很优雅,同时适用于Red和Rebol,应该能满足需求(我希望我已经考虑了所有情况):
extract-args: func [args-block /local emit result rules w ref] [
result: copy []
emit: func [x] [append result x]
rules: [
any [
/local some [word!]
| set w [word! | get-word!] (emit w)
| set w lit-word! (emit to-get-word w)
| [string! | block!]
| set ref refinement! set w [word! | lit-word! | get-word!]
(if ref [emit w])
| refinement!
]
]
parse args-block rules
result
]
pretty-print-args: func [spc /wrd] [
foreach wrd extract-args spc [
print [**indentation** " " to-string wrd " = " get wrd]
]
]
insert-**result**: func [spc] [
either find spc quote /local
[append spc [**result**]]
[append spc [/local **result**]]
]
**indentation**: copy ""
add-1-ind: func [][append **indentation** copy " "]
remove-1-ind: func [] [remove/part skip tail **indentation** -3 3]
my-trace: func [wrds /local w spc bdy bdy' uw] [
foreach w wrds [
uw: uppercase to-string w
spc: copy spec-of get w
insert-**result** spc
bdy: body-of get w
bdy': compose/deep copy [
add-1-ind
print [**indentation** "Enter" (uw)]
pretty-print-args [(spc)]
set/any '**result** do [(bdy)]
print [**indentation** (uw) "returned" mold :**result**]
remove-1-ind
:**result**
]
set w func spc bdy'
]
]
然后:
>> my-trace [foo bar]
== func [x /ris 'var "blablalba" /local **result**][
add-1-ind
...
>> bar/ris 7 yyy
Enter BAR
x = 7
var = yyy
Enter FOO
val1 = 7
val2 = 2
FOO returned 14
BAR returned 22
== 22
>> source bar
bar: func [x /ris 'var "blablalba" /local **result**][
add-1-ind
print [**indentation** "Enter" "BAR"]
pretty-print-args [x /ris 'var "blablalba" /local **result**]
set/any '**result** do [
if ris [set var "fatto"]
(foo x 2) + 8
]
print [**indentation** "BAR" "returned" mold :**result**]
remove-1-ind
:**result**
]
可以在Rebol(Red, R3 Ren-c)中写一个类似于TRACE的函数,产生如下结果:
foo: func [val1 val2 /local temp] [
temp: val1 + 5
val2 + temp
]
bar: func [x /ris 'var "blablalba"][
if ris [set var "fatto"]
(foo x 2) + 8
]
trace [foo bar]
bar/ris 7 yyy
Enter BAR
x = 7
var = yyy
Enter FOO
val1 = 7
val2 = 2
FOO returned 14
BAR returned 22
在用户级别,最直接的方法是编写一个类似闭包的包装器,它会在调用主代码之前和之后调用您提供的跟踪挂钩,然后 return评价.
Red 中该想法的粗略草图如下:
frame-of: function [
func [function!]
/local
match
][
parse spec-of :func [
collect any [
set match [not quote return: all-word!] keep (to word! match)
| skip
]
]
]
report: function [frame [block!]][
environment: construct collect [forall frame [keep to set-word! frame/1]]
also environment set environment reduce frame
]
trace: function [
'target [word!]
enter [block!]
leave [block!]
][
chain: reduce [
'do enter
'set/any quote 'result 'do body-of get target
'do leave
quote :result
]
new: func spec-of get target chain
info: context [
frame: bind frame-of get target :new
name: target
result: none
]
bind body-of :new info
set target :new
exit
]
有了这些,您就可以:
enter: [print ['entering name 'with mold/flat body-of report frame]]
leave: [print [name 'returned result]]
trace foo enter leave
trace bar enter leave
其中,对于您的示例,给出:
>> bar/ris 7 yyy
entering bar with [x: 7 ris: true var: 'yyy]
entering foo with [val1: 7 val2: 2 local: false temp: none]
foo returned 14
bar returned 22
== 22
请注意,这只是一个 PoC。基本思想是您的原始函数被检测版本替换,该版本通过对某些带有调试信息的内部名称空间的闭包创建。您还可以将一些 Red/System 放入组合中以获得对运行时信息的细粒度访问,例如评估堆栈。
作为 reader ;)
的练习,我将保留带有缩进和禁用跟踪的漂亮打印这个解决方案,不是很优雅,同时适用于Red和Rebol,应该能满足需求(我希望我已经考虑了所有情况):
extract-args: func [args-block /local emit result rules w ref] [
result: copy []
emit: func [x] [append result x]
rules: [
any [
/local some [word!]
| set w [word! | get-word!] (emit w)
| set w lit-word! (emit to-get-word w)
| [string! | block!]
| set ref refinement! set w [word! | lit-word! | get-word!]
(if ref [emit w])
| refinement!
]
]
parse args-block rules
result
]
pretty-print-args: func [spc /wrd] [
foreach wrd extract-args spc [
print [**indentation** " " to-string wrd " = " get wrd]
]
]
insert-**result**: func [spc] [
either find spc quote /local
[append spc [**result**]]
[append spc [/local **result**]]
]
**indentation**: copy ""
add-1-ind: func [][append **indentation** copy " "]
remove-1-ind: func [] [remove/part skip tail **indentation** -3 3]
my-trace: func [wrds /local w spc bdy bdy' uw] [
foreach w wrds [
uw: uppercase to-string w
spc: copy spec-of get w
insert-**result** spc
bdy: body-of get w
bdy': compose/deep copy [
add-1-ind
print [**indentation** "Enter" (uw)]
pretty-print-args [(spc)]
set/any '**result** do [(bdy)]
print [**indentation** (uw) "returned" mold :**result**]
remove-1-ind
:**result**
]
set w func spc bdy'
]
]
然后:
>> my-trace [foo bar]
== func [x /ris 'var "blablalba" /local **result**][
add-1-ind
...
>> bar/ris 7 yyy
Enter BAR
x = 7
var = yyy
Enter FOO
val1 = 7
val2 = 2
FOO returned 14
BAR returned 22
== 22
>> source bar
bar: func [x /ris 'var "blablalba" /local **result**][
add-1-ind
print [**indentation** "Enter" "BAR"]
pretty-print-args [x /ris 'var "blablalba" /local **result**]
set/any '**result** do [
if ris [set var "fatto"]
(foo x 2) + 8
]
print [**indentation** "BAR" "returned" mold :**result**]
remove-1-ind
:**result**
]