(You need Pluto#main
to run this notebook)
👀 Reading hidden code
This notebook contains visual debugging:
👀 Reading hidden code
4
9
1.2228445449938519
asdf
👀 Reading hidden code
(1+2) + (7-6)
4+5
sqrt(sqrt(sqrt(5)))
md"# asdf"
end
and visual testing:
👀 Reading hidden code
8
1
2
3
4
5
6
7
👀 Reading hidden code
👀 Reading hidden code
TableOfContents()
👀 Reading hidden code
using HypertextLiteral
: @htl, @htl_str
using PlutoUI
👀 Reading hidden code
Type definitions
👀 Reading hidden code
abstract type TestResult end
👀 Reading hidden code
abstract type Fail <: TestResult end
👀 Reading hidden code
abstract type Pass <: TestResult end
👀 Reading hidden code
Any
const Code = Any
👀 Reading hidden code
struct Correct <: Pass
expr::Code
end
👀 Reading hidden code
struct WrongCall <: Fail
expr::Code
arg_results::Vector
end
👀 Reading hidden code
struct Error <: Fail
expr::Code
error
end
👀 Reading hidden code
struct Wrong <: Fail
expr::Code
result
end
👀 Reading hidden code
struct CorrectCall <: Pass
expr::Code
arg_results::Vector
end
👀 Reading hidden code
Test macro
👀 Reading hidden code
test (generic function with 1 method)
function test(expr)
if Meta.isexpr(expr, :call)
quote
expr_raw = $(QuoteNode(expr))
try
arg_results = [$((expr.args[2:end] .|> esc)...)]
result = $(esc(:eval))(Expr(:call, $(expr.args[1] |> QuoteNode), arg_results...))
if result === true
CorrectCall(expr_raw, arg_results)
elseif result === false
WrongCall(expr_raw, arg_results)
else
Wrong(expr_raw, result)
end
catch e
rethrow(e)
# Error(expr_raw, e)
end
end
end
end
👀 Reading hidden code
👀 Reading hidden code
begin
var"#578#expr_raw" = $(QuoteNode(:(x == [1, 2 + 2])))
try
var"#579#arg_results" = [x, [1, 2 + 2]]
var"#580#result" = eval(Main.workspace#5.Expr(:call, :(==), var"#579#arg_results"...))
if var"#580#result" === true
Main.workspace#5.CorrectCall(var"#578#expr_raw", var"#579#arg_results")
elseif var"#580#result" === false
Main.workspace#5.WrongCall(var"#578#expr_raw", var"#579#arg_results")
else
Main.workspace#5.Wrong(var"#578#expr_raw", var"#580#result")
end
catch var"#583#e"
Main.workspace#5.rethrow(var"#583#e")
end
end
var"@test"; macroexpand(@__MODULE__, :(@test x == [1,2+2]); recursive=false) |> prettycolors
👀 Reading hidden code
:(x == [1, 2 + 2])
e = :(x == [1,2+2])
👀 Reading hidden code
1
3
x = [1,3]
👀 Reading hidden code
:(missing == 2)
missing
@test missing == 2
👀 Reading hidden code
4
4
@test 2+2 == 2+2
👀 Reading hidden code
1
3
1
4
@test x == [1,2+2]
👀 Reading hidden code
0.0264222
0.76064
0.415333
0.113142
0.90947
0.532365
0.140525
0.702461
0.339799
0.678373
0.207536
0.119821
0.670329
0.201361
0.167471
0.538236
0.480548
0.876847
0.718385
0.148892
0.688437
0.840784
0.879963
0.92282
0.345467
0.715087
0.343102
0.288321
0.684855
0.400419
0.912903
0.204335
0.72205
0.892897
0.633039
0.288112
0.705012
0.859979
0.578372
0.719094
2
@test rand(50) == [rand(50),2]
👀 Reading hidden code
0.224982
0.114478
0.0951463
0.224228
0.246427
0.484922
0.580998
0.3973
0.711857
0.943326
0.251156
0.636125
0.995764
0.757872
0.0736692
0.0310471
0.444788
0.645438
0.783277
0.899117
0.925839
0.756081
0.237886
0.645712
0.891478
0.0208639
0.753597
0.395995
0.830775
0.496822
0.716505
0.50469
0.295221
0.455189
0.96873
0.99412
0.912826
0.112604
0.419556
0.410107
0.594378
0.707424
0.766951
0.896903
0.644342
0.93212
0.509718
0.204113
0.801279
0.113455
0.79562
0.912862
0.24709
0.373329
0.748144
0.720708
0.155356
0.97816
0.752812
0.135454
123
@test always_false(rand(50), rand(50),123)
👀 Reading hidden code
false
@visual_debug !always_false(rand(2), rand(2),123)
👀 Reading hidden code
@visual_debug !!always_false(rand(2), rand(2),123; r=123)
👀 Reading hidden code
@visual_debug always_false([1,2,3]...)
👀 Reading hidden code
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol f
2: Int64 1
3: Expr
head: Symbol ...
args: Array{Any}((1,))
1: Int64 2
4: Expr
head: Symbol kw
args: Array{Any}((2,))
1: Symbol x
2: Int64 3
Dump(:(
f(1,2...,x=3)
))
👀 Reading hidden code
always_false (generic function with 1 method)
always_false(args...; kwargs...) = false
👀 Reading hidden code
4
1
@test isless(2+2,1)
👀 Reading hidden code
1
4
@test isless(1,2+2)
👀 Reading hidden code
@bind n Slider(1:10)
👀 Reading hidden code
1
@test iseven(n^2)
👀 Reading hidden code
@bind k Slider(0:15)
👀 Reading hidden code
8
@test 4+4 ∈ [1:k...]
👀 Reading hidden code
@test isempty((1:k) .^ 2)
👀 Reading hidden code
1.0
1.41421
@test isempty([1,sqrt(2)])
👀 Reading hidden code
1
4.47214
5.0
6.0
7.0
8.0
9.0
👀 Reading hidden code
1
0.704528
0.800359
0.672759
0.214909
0.996163
0.139007
0.616701
0.0291403
0.134557
0.616231
0.421127
0.221591
0.574706
0.351953
0.318379
0.20762
0.00560518
0.513376
0.231085
0.302638
0.775061
0.829178
0.896906
0.110473
0.228872
0.757574
0.897979
0.688473
0.912113
0.128988
@test 1 ∈ rand(60)
👀 Reading hidden code
0.975445
0.655446
0.173511
0.215441
0.442065
0.774482
0.342262
0.710536
0.314383
0.271625
0.0656018
0.601421
0.367745
0.240068
0.983101
0.824225
0.766104
0.153355
0.511349
0.296265
0.613964
0.974312
0.772504
0.678984
0.996099
0.194422
0.68852
0.944525
0.486125
0.177688
1
@test rand(60) ∋ 1
👀 Reading hidden code
html"""
<style>
pt-dot {
flex: 0 0 auto;
background: grey;
width: 1em;
height: 1em;
bottom: -.1em;
border-radius: 100%;
margin-right: .7em;
display: block;
position: relative;
}
.fail > pt-dot {
background: #f75d5d;
}
.pass > pt-dot {
background: #56a038;
}
.pluto-test {
font-family: "JuliaMono", monospace;
font-size: 0.75rem;
padding: 4px;
min-height: 25px;
}
.pluto-test.pass {
color: rgba(0,0,0,.5);
}
.pluto-test.fail {
background: linear-gradient(90deg, #ff2e2e14, transparent);
border-radius: 7px;
}
.pluto-test>.arg_result {
flex: 0 0 auto;
}
.pluto-test>.arg_result>div,
.pluto-test>.arg_result>div>pluto-display>div {
display: inline-flex;
}
.pluto-test>.comma {
margin-right: .5em;
}
.pluto-test.call>code {
padding: 0px;
}
.pluto-test.call.infix-operator>div {
overflow-x: auto;
}
.pluto-test {
display: flex;
align-items: baseline;
}
.pluto-test.call.infix-operator>.fname {
margin: 0px .6em;
/*color: darkred;*/
}
"""
👀 Reading hidden code
true
(@test isodd(3)) isa Pass
👀 Reading hidden code
begin
macro test(expr)
test(expr)
end
function Base.show(io::IO, m::MIME"text/html", call::Union{WrongCall,CorrectCall})
fname = call.expr.args[1]
infix = length(call.arg_results) == 2 && Meta.isbinaryoperator(fname)
classes = [
"pluto-test",
"call",
(isa(call,CorrectCall) ? "correct" : "wrong"),
(isa(call,Pass) ? "pass" : "fail"),
infix ? "infix-operator" : "prefix-operator",
]
result = @htl("""
<div class=$(classes)>
<pt-dot></pt-dot>
$(infix ? @htl("""
$(emb(call.arg_results[1]) |> div)<span class="fname">$(fname)</span>$(emb(call.arg_results[2]) |> div)
""") : @htl("""
<span class="fname">$(fname)(</span>$(
commas(
map(div(;class="arg_result"), embed_display.(call.arg_results))
)
)<span>)</span>
"""))
</div>
""")
Base.show(io, m, result)
end
# function Base.show(io::IO, m::MIME"text/html", c::Correct)
# result = @htl("""
# <div class=$([
# "pluto-test", "pass", "correct",
# ])>
# <pt-dot></pt-dot>
# <span>$((string(remove_linenums(c.expr))))</span>
# </div>
# """)
# Base.show(io, m, result)
# end
end
👀 Reading hidden code
flatmap (generic function with 1 method)
flatmap(args...) = vcat(map(args...)...)
👀 Reading hidden code
commas (generic function with 2 methods)
function commas(xs, comma=@htl("<span class='comma'>, </span>"))
flatmap(enumerate(xs)) do (i,x)
if i == length(xs)
[x]
else
[x,comma]
end
end
end
👀 Reading hidden code
embed_display (generic function with 1 method)
emb = embed_display
👀 Reading hidden code
div (generic function with 1 method)
div(x; class="", style="") = @htl("<div class=$(class) style=$(style)>$(x)</div>")
👀 Reading hidden code
div (generic function with 2 methods)
div(; class="", style="") = x -> @htl("<div class=$(class) style=$(style)>$(x)</div>")
👀 Reading hidden code
👀 Reading hidden code
prettycolors (generic function with 1 method)
prettycolors(e) = Markdown.MD([Markdown.Code("julia", string(remove_linenums(e)))])
👀 Reading hidden code
remove_linenums (generic function with 1 method)
remove_linenums(e::Expr) = if e.head === :macrocall
Expr(e.head, (remove_linenums(x) for x in e.args)...)
else
Expr(e.head, (remove_linenums(x) for x in e.args if !(x isa LineNumberNode))...)
end
👀 Reading hidden code
remove_linenums (generic function with 2 methods)
remove_linenums(x) = x
👀 Reading hidden code
DEbugging 1
md"""
# DEbugging 1
"""
👀 Reading hidden code
onestep (generic function with 1 method)
function onestep(e::Expr; m=Module())
results = Any[]
# push!(results, e)
arg_results = Any[a for a in e.args]
for (i,a) in enumerate(e.args)
arg_results[i] = if a isa QuoteNode
a
elseif (e.head === :call || e.head === :let) && i == 1
a
elseif a isa Expr
inner_results = onestep(a; m=m)
for ir in inner_results
arg_results[i] = ir
push!(results, Expr(e.head, arg_results...))
end
inner_results[end]
else
a
end
# push!(results, Expr(e.head, arg_results...))
end
push!(results, Core.eval(m, Expr(e.head, arg_results...)))
results
end
👀 Reading hidden code
expr_debug (generic function with 1 method)
function expr_debug(x)
e = remove_linenums(x)
Any[e, onestep(e)...]
end
👀 Reading hidden code
UInt64
👀 Reading hidden code
expr_hash (generic function with 1 method)
👀 Reading hidden code
expr_hash (generic function with 2 methods)
👀 Reading hidden code
debug_result = expr_debug(:(
let
r = if rand(Bool)
20
else
16
end
y = sqrt(4)
y == sqrt(sqrt(r))
end
));
👀 Reading hidden code
@bind step Slider(1:length(debug_result))
👀 Reading hidden code
let
r = if rand(Bool)
20
else
16
end
y = sqrt(4)
y == sqrt(sqrt(r))
end
debug_result[step] |> prettycolors
👀 Reading hidden code
Debugging 1.5
md"""
# Debugging 1.5
"""
👀 Reading hidden code
struct Computed
x
end
👀 Reading hidden code
computed (generic function with 1 method)
computed(x) = Computed(x)
👀 Reading hidden code
👀 Reading hidden code
expand_computed (generic function with 1 method)
expand_computed(x) = x
👀 Reading hidden code
expand_computed (generic function with 2 methods)
expand_computed(c::Computed) = c.x
👀 Reading hidden code
expand_computed (generic function with 3 methods)
expand_computed(e::Expr) = Expr(e.head, expand_computed.(e.args)...)
👀 Reading hidden code
onestep_light (generic function with 1 method)
function onestep_light(e::Expr; m=Module())
results = Any[]
# will be modified
arg_results = Any[a for a in e.args]
if e.head === :call || e.head === :begin || e.head === :block
# push!(results, e)
for (i,a) in enumerate(e.args)
arg_results[i] = if a isa QuoteNode
a
elseif (Meta.isexpr(e, :call) || Meta.isexpr(e, :let)) && i == 1
a
elseif a isa Expr
inner_results = onestep_light(a; m=m)
for ir in inner_results
arg_results[i] = ir
push!(results, Expr(e.head, arg_results...))
end
inner_results[end]
else
a
end
# push!(results, Expr(e.head, arg_results...))
end
end
push!(results, Computed(Core.eval(m, expand_computed(Expr(e.head, arg_results...)))))
results
end
👀 Reading hidden code
@eval_step_by_step (macro with 1 method)
macro eval_step_by_step(e)
Computed
if can_interpret(e)
quote
Any[$(QuoteNode(e)), $(onestep_light(e; m=__module__))...]
end
else
quote
[$(QuoteNode(e)), Computed($(esc(e)))]
end
end
end
👀 Reading hidden code
quote
Main.workspace#6.Any[$(QuoteNode(:(sqrt(sqrt(3))))), Any[:(sqrt(Computed(1.7320508075688772))), Computed(1.3160740129524924)]...]
end
remove_linenums( @macroexpand @eval_step_by_step sqrt(sqrt(3)) )
👀 Reading hidden code
:(sqrt(sqrt(length([1, 2]))))
:(sqrt(sqrt(length(Computed([1, 2])))))
:(sqrt(sqrt(Computed(2))))
:(sqrt(Computed(1.41421)))
1.18921
@eval_step_by_step sqrt(sqrt(length([1,2])))
👀 Reading hidden code
:(xasdf = 123)
123
@eval_step_by_step xasdf = 123
👀 Reading hidden code
UndefVarError: xasdf not defined
Here is what happened, the most recent locations are first:
xasdf
👀 Reading hidden code
quote Computed(3) 2 + 3 4 + 5 sqrt(sqrt(sqrt(5))) end
quote Computed(3) Computed(5) 4 + 5 sqrt(sqrt(sqrt(5))) end
quote Computed(3) Computed(5) Computed(9) sqrt(sqrt(sqrt(5))) end
quote Computed(3) Computed(5) Computed(9) sqrt(sqrt(Computed(2.23607))) end
quote Computed(3) Computed(5) Computed(9) sqrt(Computed(1.49535)) end
quote Computed(3) Computed(5) Computed(9) Computed(1.22284) end
1.22284
onestep_light(quote
1+2
2+3
4+5
sqrt(sqrt(sqrt(5)))
end |> remove_linenums)
👀 Reading hidden code
can_interpret (generic function with 1 method)
can_interpret(x) = true
👀 Reading hidden code
can_interpret_call_arg (generic function with 1 method)
can_interpret_call_arg(e::Expr) = !(e.head === :(...) || e.head === :kw || e.head === :parameters)
👀 Reading hidden code
can_interpret_call_arg (generic function with 2 methods)
can_interpret_call_arg(x) = true
👀 Reading hidden code
true
Meta.isbinaryoperator(:(==))
👀 Reading hidden code
can_interpret (generic function with 2 methods)
can_interpret(e::Expr) = if false
false
elseif e.head === :call && !all(can_interpret_call_arg, e.args)
false
# elseif e.head === :(=) || e.head === :macrocall
# false
else
all(can_interpret, e.args)
end
👀 Reading hidden code
Displaying
👀 Reading hidden code
👀 Reading hidden code
find_computed! (generic function with 1 method)
find_computed!(found, c::Computed) = push!(found, c)
👀 Reading hidden code
find_computed! (generic function with 2 methods)
find_computed!(found, expr::Expr) = find_computed!.([found], expr.args)
👀 Reading hidden code
find_computed! (generic function with 3 methods)
find_computed!(found, x) = nothing
👀 Reading hidden code
find_computed (generic function with 1 method)
find_computed(x) = let
r = Any[]
find_computed!(r, x)
r
end
👀 Reading hidden code
3
3
5
3
5
9
3
5
9
2.23607
3
5
9
1.49535
3
5
9
1.22284
1.22284
onestep_light(quote
1+2
2+3
4+5
sqrt(sqrt(sqrt(5)))
end |> remove_linenums) .|> find_computed
👀 Reading hidden code
slot! (generic function with 1 method)
slot!(found, c::Computed) = let
k = Symbol("__slot", join(rand('a':'z', 16)), "__")
found[k] = c
k
end
👀 Reading hidden code
slot! (generic function with 2 methods)
slot!(found, x) = x
👀 Reading hidden code
slot! (generic function with 3 methods)
slot!(found, e::Expr) = Expr(e.head, slot!.([found], e.args)...)
👀 Reading hidden code
slot (generic function with 1 method)
slot(e) = let
d = Dict{Symbol,Any}()
new_e = slot!(d, e)
d, new_e
end
👀 Reading hidden code
:__slotuvuomueuerudvmwq__
3
quote __slotuvuomueuerudvmwq__ + (7 - 6) 2 + 3 4 + 5 sqrt(sqrt(sqrt(5))) end
:__slotvueyqwnybajenqeh__
1
:__slottdgijfpjdoyqlwyw__
3
quote __slottdgijfpjdoyqlwyw__ + __slotvueyqwnybajenqeh__ 2 + 3 4 + 5 sqrt(sqrt(sqrt(5))) end
:__slotrkiyprzoqfczzwvx__
4
quote __slotrkiyprzoqfczzwvx__ 2 + 3 4 + 5 sqrt(sqrt(sqrt(5))) end
:__slotayqjsgjdmrpqlxtl__
4
:__slotwajajovqxsbiiyuh__
5
quote __slotayqjsgjdmrpqlxtl__ __slotwajajovqxsbiiyuh__ 4 + 5 sqrt(sqrt(sqrt(5))) end
:__sloteftloyseftpbeiwi__
9
:__slotgsxjfohjnqbgbfdv__
5
:__slotxopwdjkxmorcjpvt__
4
quote __slotxopwdjkxmorcjpvt__ __slotgsxjfohjnqbgbfdv__ __sloteftloyseftpbeiwi__ sqrt(sqrt(sqrt(5))) end
:__slotdkwvghshqiadsvpb__
4
:__slotjeixmfuvwjravedr__
9
:__slotooyehmhbmytrzuxw__
2.23607
:__slotmprbkugrybcxcgwr__
5
quote __slotdkwvghshqiadsvpb__ __slotmprbkugrybcxcgwr__ __slotjeixmfuvwjravedr__ sqrt(sqrt(__slotooyehmhbmytrzuxw__)) end
:__slotdxrgpsyihmuaafpe__
1.49535
:__slotupztrfoqhunarmqy__
9
:__slotmjizyannorwamifq__
4
:__slotblcbwykgtrqjwjay__
5
quote __slotmjizyannorwamifq__ __slotblcbwykgtrqjwjay__ __slotupztrfoqhunarmqy__ sqrt(__slotdxrgpsyihmuaafpe__) end
:__slotkipmjzwbmbxzmydv__
9
:__slotqltzihbaydqksotv__
1.22284
:__slotguqmikxqrxwmilmo__
4
:__slotbeyvnnbeqpkcotce__
5
quote __slotguqmikxqrxwmilmo__ __slotbeyvnnbeqpkcotce__ __slotkipmjzwbmbxzmydv__ __slotqltzihbaydqksotv__ end
:__slotdgdjnoywgdvqgpwf__
1.22284
:__slotdgdjnoywgdvqgpwf__
onestep_light(quote
(1+2) + (7-6)
2+3
4+5
sqrt(sqrt(sqrt(5)))
end |> remove_linenums) .|> slot
👀 Reading hidden code
preish (generic function with 1 method)
preish(x) = @htl("<pre-ish>$(x)</pre-ish>")
👀 Reading hidden code
display_slotted (generic function with 1 method)
function display_slotted(expr)
d, e = slot(expr)
s = string(e |> remove_linenums)
lines = split(s, "\n")
r = r"\_\_slot[a-z]{16}\_\_"
@htl("""<slotted-code>
$(
map(lines) do l
keys = [Symbol(m.match) for m in eachmatch(r, l)]
rest = split(l, r; keepempty=true)
result = vcat((
[preish(r), embed_display(d[k].x)]
for (r,k) in zip(rest, keys)
)...)
push!(result, preish(last(rest)))
@htl("<line-like>$(result)</line-like>")
end
)
</slotted-code>""")
end
👀 Reading hidden code
html"""
<style>
slotted-code {
font-family: "JuliaMono", monospace;
font-size: .75rem;
display: flex;
flex-direction: column;
}
pre-ish {
white-space: pre;
}
slotted-code pluto-display>div {
display: inline-block;
}
line-like {
display: flex;
}
"""
👀 Reading hidden code
# rs = @eval_step_by_step(begin
# (1+2) + (7-6)
# first(2000 .+ 30 .* rand(2+2))
# 4+5
# sqrt(sqrt(sqrt(5)))
# end) .|> display_slotted
👀 Reading hidden code
using Plots
👀 Reading hidden code
plot (generic function with 1 method)
plot(args...; kwargs...) = Plots.plot(args...; size=(100,100), kwargs...)
👀 Reading hidden code
3
3
1
4
4
4
4
0.86079
0.628052
0.896977
0.164148
4
25.8237
18.8416
26.9093
4.92444
4
2025.82
2018.84
2026.91
2004.92
4
4
9
4
9
2.23607
4
9
1.49535
4
9
1.22284
1.22284
rs = @eval_step_by_step(begin
(1+2) + (7-6)
plot(2000 .+ 30 .* rand(2+2))
4+5
sqrt(sqrt(sqrt(5)))
end) .|> display_slotted
👀 Reading hidden code
@bind rindex Slider(eachindex(rs))
👀 Reading hidden code
rs[rindex]
👀 Reading hidden code
4
9
1.2228445449938519
frames(rs)
👀 Reading hidden code
@visual_debug (macro with 1 method)
macro visual_debug(expr)
frames
display_slotted
var"@eval_step_by_step"
quote
@eval_step_by_step($(expr)) .|> display_slotted |> frames
end
end
👀 Reading hidden code
4
9
1.2228445449938519
@visual_debug begin
(1+2) + (7-6)
plot(2000 .+ 30 .* rand(2+2))
4+5
sqrt(sqrt(sqrt(5)))
end
👀 Reading hidden code
frames (generic function with 1 method)
function frames(fs)
@htl("""
<div>
<p-frames>
$(fs)
</p-frames>
<img src="https://cdn.jsdelivr.net/gh/ionic-team/ionicons@5.0.0/src/svg/time-outline.svg" style="width: 1em; transform: scale(-1,1); opacity: .5; margin-left: 2em;">
<input class="timescrub" type=range min=1 max=$(length(fs)) value=$(max(1,length(fs)-1))>
<script>
const div = currentScript.parentElement
const input = div.querySelector("input.timescrub")
const frames = div.querySelector("p-frames")
const setviz = () => {
Array.from(frames.children).forEach((f,i) => {
f.style.display = i + 1 === input.valueAsNumber ? "inherit" : "none"
})
}
setviz()
input.addEventListener("input", setviz)
</script>
</div>
""")
end
👀 Reading hidden code
👀 Reading hidden code
👀 Reading hidden code
👀 Reading hidden code
👀 Reading hidden code
👀 Reading hidden code
👀 Reading hidden code