Skip to content

Instantly share code, notes, and snippets.

@toivoh
Created August 7, 2012 07:21
Show Gist options
  • Save toivoh/3282772 to your computer and use it in GitHub Desktop.
Save toivoh/3282772 to your computer and use it in GitHub Desktop.
recshow: Prototype show() work-alike to show self-referential and DAG-structured objects
# ---- ObjNode: capture of an object's output to show() -----------------------
type ObjNode
# actual capture
obj
reused::Bool
items::Vector # ObjNode:s and strings/chars
# scratch space for recshow etc
strlen::Integer
name::String
ObjNode(obj) = new(obj, false, {}, -1, "")
end
emit(dest::ObjNode, arg) = (push(dest.items, arg); nothing)
function print(io::IO, node::ObjNode)
if node.reused print(io, node.name)
else print(io, node.items...)
end
end
get_strlen(node::ObjNode) = node.strlen
get_strlen(c::Char) = 1
get_strlen(s::String) = strlen(s)
get_strlen(x) = -1
function finish!(node::ObjNode)
lengths = [get_strlen(item) for item in node.items]
if !any(lengths .== -1) node.strlen = sum(lengths) end
end
# ---- RecordIO: IO that recursively captures the output of show() ------------
type RecordIO <: IO
shows::ObjectIdDict # Shows that have started capture so far
dest::ObjNode # Currently capturing
end
emit(io::RecordIO, arg) = emit(io.dest, arg)
## Stuff needed to make an IO subtype: ##
# Redirect character output to one place: emit
print(io::RecordIO, s::ASCIIString) = emit(io, s)
print(io::RecordIO, s::ASCIIString) = emit(io, s)
print(io::RecordIO, s::UTF8String) = emit(io, s)
print(io::RecordIO, s::RopeString) = emit(io, s)
print(io::RecordIO, s::String) = emit(io, s)
print(io::RecordIO, c::Char) = emit(io, c)
write(io::RecordIO, c::Char) = emit(io, c)
write(io::RecordIO, s::ASCIIString) = emit(io, s)
# Work around some types that do funky stuff in show
show(io::RecordIO, x::Float32) = print(io, sshow(x))
show(io::RecordIO, x::Float64) = print(io, sshow(x))
show(io::RecordIO, x::Symbol) = print(io, string(x))
## Recording of show() ##
type RecordShowError <: Exception
cause::Exception
end
function show(io::IO, e::RecordShowError)
println(io, "Exception in recshow:"); show(io, e.cause)
end
function record_show!(shows::ObjectIdDict, dest::ObjNode)
@assert !has(shows, dest.obj)
@assert isempty(dest.items)
shows[dest.obj] = dest
try
show(RecordIO(shows, dest), dest.obj)
catch e
if !isa(e, RecordShowError)
emit(dest, "#encountered exception!")
e = RecordShowError(e)
end
throw(e)
end
finish!(dest)
nothing
end
print(io::IO, x) = rshow(io, x)
rshow(io::IO, x) = show(io, x)
function rshow(io::RecordIO, arg)
if has(io.shows, arg)
# reuse old node
node = io.shows[arg]
node.reused = true
emit(io, node)
else
# record new node
node = ObjNode(arg)
emit(io, node)
record_show!(io.shows, node)
end
end
record_show!(dest::ObjNode) = record_show!(ObjectIdDict(), dest)
record_show(arg) = (dest=ObjNode(arg); record_show!(dest); dest)
# ---- list_trees!: Prepare recshow print list from ObjNode:s -----------------
# is x immutable up to where show() calls rshow()?
is_immutable_to_rshow(x::Union(Number,Function,Type,TypeName,Symbol)) = true
is_immutable_to_rshow(x) = false
function treeify_node!(trees::Vector{ObjNode}, node::ObjNode)
for item in node.items; treeify!(trees, item); end
end
function treeify!(trees::Vector{ObjNode}, node::ObjNode)
if !node.reused ||
(is_immutable_to_rshow(node.obj) && (0 <= node.strlen <= 10))
# node will be printed inline
node.reused = false
treeify_node!(trees, node)
else
if (node.name != "") return end
# First encounter: name the node, add it to the print list
push(trees, node)
k = length(trees)
node.name = "<x$k>"
end
end
treeify!(trees::Vector{ObjNode}, x) = nothing
function list_trees!(args...)
trees = ObjNode[]
for arg in args; treeify!(trees, arg); end
k = 1
while k <= length(trees); treeify_node!(trees, trees[k]); k += 1; end
trees
end
# ---- recshow: Show a possibly self-referential object -----------------------
function print_recshow(io::IO, node::ObjNode)
trees = list_trees!(node)
if isempty(trees); print(io, node); return; end
node.name = "<obj>"
if !is(trees[1], node); enqueue(trees, node); end
for node in trees; println(io, node.name, "\t= ", node.items...); end
end
recshow(io::RecordIO, arg) = rshow(io, arg)
function recshow(io::IO, arg)
node = ObjNode(arg)
try
record_show!(node)
catch e
print_recshow(io, node)
throw(e)
end
print_recshow(io, node)
end
recshow(arg) = recshow(OUTPUT_STREAM, arg)
# ---- default_show: jl_show_any replacement that invokes rshow ---------------
function show(io, x)
if isa(io, IOStream) ccall(:jl_show_any, Void, (Any, Any,), io, x)
else default_show(io, x)
end
end
default_show(io::IO, x::Union(Type, Function, TypeName)) = print(io, sshow(x))
default_show(io::IO, x) = default_show(io, typeof(x), x)
default_show(io::IO, T::CompositeKind, x) = show_composite(io, x)
default_show(io::IO, T, x) = print(io, sshow(x))
function show_composite(io, x)
T = typeof(x)
names = filter(name->(name!=symbol("")), [T.names...])
values = {}
for name in names
try push(values, getfield(x, name))
catch err; error("default_show: Error accessing field \"$name\" in $T")
end
end
print(io, T.name, '('); show_comma_list(io, values...); print(io, ')')
end
show_comma_list(io::IO) = nothing
function show_comma_list(io::IO, arg, args...)
rshow(io, arg)
for arg in args print(io, ", "); rshow(io, arg) end
end
require("recshow.jl")
type T
x
y
end
type Unshowable; end
show(io::IO, x::Unshowable) = error("Tried to show Unshowable()")
println("Simple and tree structured objects: just as show")
recshow(1.55); println()
recshow(155); println()
recshow("abc"); println()
recshow(T(1,2)); println()
println("\nSelf-referential object:")
s = T(1,1); s.y = s
recshow(s); println()
println("DAG structured object:")
t = T(1,1)
recshow(T(t,t)); println()
println("Exception during show recording ==> partial printout:")
recshow(T(s, Unshowable()))
@toivoh
Copy link
Author

toivoh commented Aug 7, 2012

test.jl should produce

julia> load("test.jl")
Simple and tree structured objects: just as show
1.55
155
"abc"
T(1, 2)

Self-referential object:
<obj>   = T(1, <obj>)

DAG structured object:
<obj>   = T(<x1>, <x1>)
<x1>    = T(1, 1)

Exception during show recording ==> partial printout:
<obj>   = T(<x1>, #encountered exception!
<x1>    = T(1, <x1>)
Exception in recshow:
Tried to show Unshowable()
 in recshow at recshow.jl:161
 in recshow at recshow.jl:166
 in load at util.jl:230
 in load at util.jl:242
at test.jl:28
 in load at util.jl:253

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment