From 9a1586512d6208b91962d053ad8d1fcb8eabd012 Mon Sep 17 00:00:00 2001 From: Toivo Henningsson Date: Thu, 9 Aug 2012 12:32:25 +0200 Subject: [PATCH 1/4] Add recshow.jl to extras/ --- extras/recshow.jl | 161 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 extras/recshow.jl diff --git a/extras/recshow.jl b/extras/recshow.jl new file mode 100644 index 0000000000000..0d94b344125f2 --- /dev/null +++ b/extras/recshow.jl @@ -0,0 +1,161 @@ +# recshow.jl: show for 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, repr(x)) +show(io::RecordIO, x::Float64) = print(io, repr(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 + +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!(trees::Vector{ObjNode}, node::ObjNode) + if !node.reused || + (is_immutable_to_rshow(node.obj) && (0 <= node.strlen <= 11)) + # 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 = "" + end +end +treeify!(trees::Vector{ObjNode}, x) = nothing +function treeify_node!(trees::Vector{ObjNode}, node::ObjNode) + for item in node.items; treeify!(trees, item); end +end + +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 ----------------------- + +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) + +function print_recshow(io::IO, node::ObjNode) + trees = list_trees!(node) + if isempty(trees); print(io, node); return; end + + node.name = "" + if !is(trees[1], node); enqueue(trees, node); end + for node in trees; println(io, node.name, "\t= ", node.items...); end +end From 0f0a8ac5db63e1e330942dc72bed15b50976833a Mon Sep 17 00:00:00 2001 From: Toivo Henningsson Date: Thu, 9 Aug 2012 12:34:32 +0200 Subject: [PATCH 2/4] Add rshow(io, x) = show(io, x) and make print(io, x) call it. --- base/base.jl | 3 +++ base/string.jl | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/base.jl b/base/base.jl index 72a9e614e535e..19b730ea0fbd9 100644 --- a/base/base.jl +++ b/base/base.jl @@ -60,6 +60,9 @@ type ShowError <: Exception err::Exception end +export rshow +rshow(io, x) = show(io, x) # hook to allow showing of self-referential data + show(io, bt::BackTrace) = show(io,bt.e) function show(io, se::ShowError) diff --git a/base/string.jl b/base/string.jl index 8b3b57eeb57e5..62d68f3f19bb4 100644 --- a/base/string.jl +++ b/base/string.jl @@ -1,6 +1,6 @@ ## core text I/O ## -print(io::IO, x) = show(io, x) +print(io::IO, x) = rshow(io, x) print(io::IO, xs...) = for x in xs print(io, x) end println(io::IO, xs...) = print(io, xs..., '\n') From ca8d9bbb19442d14f61c18f12ee13ead8801825d Mon Sep 17 00:00:00 2001 From: Toivo Henningsson Date: Thu, 9 Aug 2012 12:37:06 +0200 Subject: [PATCH 3/4] Default show for composites from within julia, using rshow. --- base/show.jl | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/base/show.jl b/base/show.jl index ca41fbdc872af..60e2b68e46ecc 100644 --- a/base/show.jl +++ b/base/show.jl @@ -3,7 +3,34 @@ show(x) = show(OUTPUT_STREAM::IOStream, x) print(io::IOStream, s::Symbol) = ccall(:jl_print_symbol, Void, (Ptr{Void}, Any,), io, s) -show(io, x) = ccall(:jl_show_any, Void, (Any, Any,), io::IOStream, x) + +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)) = print(io, repr(x)) +function default_show(io::IO, x) + isa(typeof(x), CompositeKind) ? show_composite(io, x) : print(io, repr(x)) +end + +function show_composite(io, x) + T::CompositeKind = 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 showcompact(io, x) = show(io, x) showcompact(x) = showcompact(OUTPUT_STREAM::IOStream, x) From 712d5126c31e36fb9bcfa0ff7103cf906eada241 Mon Sep 17 00:00:00 2001 From: Toivo Henningsson Date: Thu, 9 Aug 2012 12:57:57 +0200 Subject: [PATCH 4/4] Change most calls to show() into rshow(), within base/. --- base/base.jl | 4 ++-- base/complex.jl | 4 ++-- base/dict.jl | 4 ++-- base/expr.jl | 4 ++-- base/io.jl | 2 +- base/rational.jl | 2 +- base/regex.jl | 8 ++++---- base/set.jl | 2 +- base/show.jl | 32 ++++++++++++++++---------------- base/util.jl | 2 +- 10 files changed, 32 insertions(+), 32 deletions(-) diff --git a/base/base.jl b/base/base.jl index 19b730ea0fbd9..1b93af9fcabfc 100644 --- a/base/base.jl +++ b/base/base.jl @@ -63,11 +63,11 @@ end export rshow rshow(io, x) = show(io, x) # hook to allow showing of self-referential data -show(io, bt::BackTrace) = show(io,bt.e) +show(io, bt::BackTrace) = rshow(io,bt.e) function show(io, se::ShowError) println("Error showing value of type ", typeof(se.val), ":") - show(io, se.err) + rshow(io, se.err) end method_missing(f, args...) = throw(MethodError(f, args)) diff --git a/base/complex.jl b/base/complex.jl index de820271b20b1..c310d5363790c 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -17,14 +17,14 @@ reim(z) = (real(z), imag(z)) function _jl_show(io, z::Complex, compact::Bool) r, i = reim(z) if isnan(r) || isfinite(i) - compact ? showcompact(io,r) : show(io,r) + compact ? showcompact(io,r) : rshow(io,r) if signbit(i)==1 && !isnan(i) i = -i print(io, compact ? "-" : " - ") else print(io, compact ? "+" : " + ") end - compact ? showcompact(io, i) : show(io, i) + compact ? showcompact(io, i) : rshow(io, i) if !(isa(i,Integer) || isa(i,Rational) || isa(i,Float) && isfinite(i)) print(io, "*") diff --git a/base/dict.jl b/base/dict.jl index d01ffdc6477d8..7bc40f93c2c9b 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -16,9 +16,9 @@ function show(io, t::Associative) for (k, v) = t first || print(io, ',') first = false - show(io, k) + rshow(io, k) print(io, "=>") - show(io, v) + rshow(io, v) end print(io, "}") end diff --git a/base/expr.jl b/base/expr.jl index 9f4d0c9eeedf2..2189694a0c186 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -40,13 +40,13 @@ isequal(x::Symbol , y::SymbolNode) = is(x,y.name) function show(io, tv::TypeVar) if !is(tv.lb, None) - show(io, tv.lb) + rshow(io, tv.lb) print(io, "<:") end print(io, tv.name) if !is(tv.ub, Any) print(io, "<:") - show(io, tv.ub) + rshow(io, tv.ub) end end diff --git a/base/io.jl b/base/io.jl index b50306091d920..8c6304199f5e2 100644 --- a/base/io.jl +++ b/base/io.jl @@ -270,7 +270,7 @@ sprint(f::Function, args...) = sprint(0, f, args...) function repr(x) s = memio(0, false) - show(s, x) + rshow(s, x) takebuf_string(s) end diff --git a/base/rational.jl b/base/rational.jl index 69c9c2e28b21f..6fdc4caae26a4 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -31,7 +31,7 @@ function show(io, x::Rational) if isinf(x) print(io, x.num > 0 ? "Inf" : "-Inf") else - show(io, num(x)); print(io, "//"); show(io, den(x)) + rshow(io, num(x)); print(io, "//"); rshow(io, den(x)) end end diff --git a/base/regex.jl b/base/regex.jl index a6428380c90d7..e527998a3a6ba 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -51,9 +51,9 @@ function show(io, re::Regex) if (re.options & PCRE.EXTENDED ) != 0; print(io, 'x'); end else print(io, "Regex(") - show(io, re.pattern) + rshow(io, re.pattern) print(io, ',') - show(io, re.options) + rshow(io, re.options) print(io, ')') end end @@ -70,12 +70,12 @@ end function show(io, m::RegexMatch) print(io, "RegexMatch(") - show(io, m.match) + rshow(io, m.match) if !isempty(m.captures) print(io, ", ") for i = 1:length(m.captures) print(io, i, "=") - show(io, m.captures[i]) + rshow(io, m.captures[i]) if i < length(m.captures) print(io, ", ") end diff --git a/base/set.jl b/base/set.jl index 2fbba20ac1e7d..4650a53d5b26c 100644 --- a/base/set.jl +++ b/base/set.jl @@ -8,7 +8,7 @@ Set() = Set{Any}() Set(x...) = Set{Any}(x...) Set{T}(x::T...) = Set{T}(x...) -show(io, s::Set) = (show(io, typeof(s)); show_comma_array(io, s,'(',')')) +show(io, s::Set) = (rshow(io, typeof(s)); show_comma_array(io, s,'(',')')) isempty(s::Set) = isempty(s.hash) length(s::Set) = length(s.hash) diff --git a/base/show.jl b/base/show.jl index 60e2b68e46ecc..84178ddebf161 100644 --- a/base/show.jl +++ b/base/show.jl @@ -32,11 +32,11 @@ function show_comma_list(io::IO, arg, args...) for arg in args print(io, ", "); rshow(io, arg) end end -showcompact(io, x) = show(io, x) +showcompact(io, x) = rshow(io, x) showcompact(x) = showcompact(OUTPUT_STREAM::IOStream, x) show(io, s::Symbol) = print(io, s) -show(io, tn::TypeName) = show(io, tn.name) +show(io, tn::TypeName) = rshow(io, tn.name) show(io, ::Nothing) = print(io, "nothing") show(io, b::Bool) = print(io, b ? "true" : "false") show(io, n::Integer) = (write(io, dec(n));nothing) @@ -57,7 +57,7 @@ end function show(io, l::LambdaStaticData) print(io, "AST(") - show(io, l.ast) + rshow(io, l.ast) print(io, ")") end @@ -73,7 +73,7 @@ function show_delim_array(io, itr, op, delim, cl, delim_one) if newline if multiline; println(io); end end - show(io, x) + rshow(io, x) if done(itr,state) if delim_one && first print(io, delim) @@ -121,29 +121,29 @@ function show(io, e::Expr) elseif is(hd,:return) print(io, "return $(e.args[1])") elseif is(hd,:string) - show(io, e.args[1]) + rshow(io, e.args[1]) elseif is(hd,symbol("::")) - show(io, e.args[1]) + rshow(io, e.args[1]) print(io, "::") - show(io, e.args[2]) + rshow(io, e.args[2]) elseif is(hd,:quote) show_quoted_expr(io, e.args[1]) elseif is(hd,:body) || is(hd,:block) println(io, "\nbegin") for a in e.args print(io, " ") - show(io, a) + rshow(io, a) println(io) end println(io, "end") elseif is(hd,:comparison) for a in e.args - show(io, a) + rshow(io, a) end elseif is(hd,:(.)) - show(io, e.args[1]) + rshow(io, e.args[1]) print(io, '.') - show(io, e.args[2]) + rshow(io, e.args[2]) else print(io, hd) show_comma_array(io, e.args,'(',')') @@ -163,7 +163,7 @@ function show_quoted_expr(io, a1) println(io, "\nquote") for a in a1.args print(io, " ") - show(io, a) + rshow(io, a) println(io, ) end println(io, "end") @@ -193,7 +193,7 @@ function show(io, e::TypeError) end end -show(io, e::LoadError) = (show(io, e.error); print(io, "\nat $(e.file):$(e.line)")) +show(io, e::LoadError) = (rshow(io, e.error); print(io, "\nat $(e.file):$(e.line)")) show(io, e::SystemError) = print(io, "$(e.prefix): $(strerror(e.errnum))") show(io, ::DivideByZeroError) = print(io, "error: integer divide by zero") show(io, ::StackOverflowError) = print(io, "error: stack overflow") @@ -213,7 +213,7 @@ function show(io, e::MethodError) end function show(io, bt::BackTrace) - show(io, bt.e) + rshow(io, bt.e) t = bt.trace # we may not declare :_jl_eval_user_input # directly so that we get a compile error @@ -239,7 +239,7 @@ function show(io, m::Method) if !isempty(tv) show_delim_array(io, tv, '{', ',', '}', false) end - show(io, m.sig) + rshow(io, m.sig) li = m.func.code if li.line > 0 print(io, " at ", li.file, ":", li.line) @@ -252,7 +252,7 @@ function show(io, mt::MethodTable) d = mt.defs while !is(d,()) print(io, name) - show(io, d) + rshow(io, d) d = d.next if !is(d,()) println(io) diff --git a/base/util.jl b/base/util.jl index 0aeb69b12c94c..8cfcba91a8ebf 100644 --- a/base/util.jl +++ b/base/util.jl @@ -84,7 +84,7 @@ function whicht(f, types) while !is(d,()) if is(d.func.code, lsd) print(stdout_stream, f.env.name) - show(stdout_stream, d); println(stdout_stream) + rshow(stdout_stream, d); println(stdout_stream) return end d = d.next