Skip to content

Commit 569a57f

Browse files
committed
Updates in support of ASTInterpreter2
1 parent 39c5844 commit 569a57f

File tree

3 files changed

+197
-15
lines changed

3 files changed

+197
-15
lines changed

codecov.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
comment: false

src/DebuggerFramework.jl

Lines changed: 115 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
__precompile__()
22
module DebuggerFramework
3+
include("LineNumbers.jl")
4+
using LineNumbers: SourceFile, compute_line
5+
36
abstract type StackFrame end
47

58
function print_var(io::IO, name, val::Nullable, undef_callback)
@@ -41,6 +44,16 @@ module DebuggerFramework
4144
return false
4245
end
4346

47+
function execute_command(state, frame, ::Val{Symbol("?")}, cmd)
48+
println("Help not implemented for this debugger.")
49+
return false
50+
end
51+
52+
function execute_command(state, frame, _, cmd)
53+
println("Unknown command `$cmd`. Executing `?` to obtain help.")
54+
execute_command(state, frame, Val{Symbol("?")}(), "?")
55+
end
56+
4457
function execute_command(state, interp, ::Union{Val{:f},Val{:fr}}, command)
4558
subcmds = split(command,' ')[2:end]
4659
if isempty(subcmds) || subcmds[1] == "v"
@@ -69,24 +82,100 @@ module DebuggerFramework
6982
mutable struct DebuggerState
7083
stack
7184
level
85+
repl
7286
main_mode
87+
language_modes
88+
standard_keymap
7389
terminal
7490
end
91+
dummy_state(stack) = DebuggerState(stack, 1, nothing, nothing, nothing, nothing, nothing)
7592

7693
function print_status_synthtic(io, state, frame, lines_before, total_lines)
7794
return 0
7895
end
7996

80-
haslocinfo(frame) = false
97+
struct FileLocInfo
98+
filepath::String
99+
line::Int
100+
# 0 if unknown
101+
column::Int
102+
# The line at which the current context starts, 0 if unknown
103+
defline::Int
104+
end
105+
106+
struct BufferLocInfo
107+
data::String
108+
line::Int
109+
# 0 if unknown
110+
column::Int
111+
defline::Int
112+
end
113+
114+
locinfo(frame) = nothing
81115
locdesc(frame) = "unknown function"
82116

117+
"""
118+
Determine the offsets in the source code to print, based on the offset of the
119+
currently highlighted part of the code, and the start and stop line of the
120+
entire function.
121+
"""
122+
function compute_source_offsets(code, offset, startline, stopline; file = SourceFile(code))
123+
offsetline = compute_line(file, offset)
124+
if offsetline - 3 > length(file.offsets) || startline > length(file.offsets)
125+
return -1, -1
126+
end
127+
startoffset = max(file.offsets[max(offsetline-3,1)], file.offsets[startline])
128+
stopoffset = endof(code)-1
129+
if offsetline + 3 < endof(file.offsets)
130+
stopoffset = min(stopoffset, file.offsets[offsetline + 3]-1)
131+
end
132+
if stopline + 1 < endof(file.offsets)
133+
stopoffset = min(stopoffset, file.offsets[stopline + 1]-1)
134+
end
135+
startoffset, stopoffset
136+
end
137+
138+
function print_sourcecode(io, code, line, defline; file = SourceFile(code))
139+
startoffset, stopoffset = compute_source_offsets(code, file.offsets[line], defline, line+3; file=file)
140+
141+
if startoffset == -1
142+
print_with_color(:bold, io, "Line out of file range (bad debug info?)")
143+
return
144+
end
145+
146+
# Compute necessary data for line numbering
147+
startline = compute_line(file, startoffset)
148+
stopline = compute_line(file, stopoffset)
149+
current_line = line
150+
stoplinelength = length(string(stopline))
151+
152+
code = split(code[(startoffset:stopoffset)+1],'\n')
153+
lineno = startline
154+
155+
if !isempty(code) && isempty(code[end])
156+
pop!(code)
157+
end
158+
159+
for textline in code
160+
print_with_color(lineno == current_line ? :yellow : :bold, io,
161+
string(lineno, " "^(stoplinelength-length(lineno)+1)))
162+
println(io, textline)
163+
lineno += 1
164+
end
165+
println(io)
166+
end
167+
168+
print_next_state(outbuf::IO, state, frame) = nothing
169+
83170
print_status(io, state) = print_status(io, state, state.stack[state.level])
84171
function print_status(io, state, frame)
85172
# Buffer to avoid flickering
86173
outbuf = IOBuffer()
87174
print_with_color(:bold, outbuf, "In ", locdesc(frame), "\n")
88-
if haslocinfo(frame)
89-
# Print location here
175+
loc = locinfo(frame)
176+
if loc !== nothing
177+
print_sourcecode(outbuf, isa(loc, BufferLocInfo) ? loc.data : readstring(loc.filepath),
178+
loc.line, loc.defline)
90179
else
91180
buf = IOBuffer()
92181
active_line = print_status_synthtic(buf, state, frame, 2, 5)::Int
@@ -100,6 +189,7 @@ module DebuggerFramework
100189
end
101190
end
102191
end
192+
print_next_state(outbuf, state, frame)
103193
print(io, String(take!(outbuf)))
104194
end
105195

@@ -108,22 +198,35 @@ module DebuggerFramework
108198
function execute_command
109199
end
110200

201+
function language_specific_prompt
202+
end
203+
204+
function eval_code(state, frame, code)
205+
error("Code evaluation not implemented for this debugger")
206+
end
207+
208+
function eval_code(state, code)
209+
try
210+
result = eval_code(state, state.stack[1], code)
211+
true, result
212+
catch err
213+
bt = catch_backtrace()
214+
false, (err, bt)
215+
end
216+
end
217+
111218
using Base: LineEdit, REPL
219+
promptname(level, name) = "$level|$name > "
112220
function RunDebugger(stack, repl = Base.active_repl, terminal = Base.active_repl.t)
113-
promptname(level, name) = "$level|$name > "
114221

115-
state = DebuggerState(stack, 1, nothing, terminal)
222+
state = DebuggerState(stack, 1, repl, nothing, Dict{Symbol, Any}(), nothing, terminal)
116223

117224
# Setup debug panel
118225
panel = LineEdit.Prompt(promptname(state.level, "debug");
119226
prompt_prefix="\e[38;5;166m",
120227
prompt_suffix=Base.text_colors[:white],
121228
on_enter = s->true)
122229

123-
# For now use the regular REPL completion provider
124-
replc = Base.REPL.REPLCompletionProvider()
125-
126-
127230
panel.hist = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:debug => panel))
128231
Base.REPL.history_reset_state(panel.hist)
129232

@@ -181,13 +284,10 @@ module DebuggerFramework
181284
return true
182285
end
183286

184-
const all_commands = ("q", "s", "si", "finish", "bt", "loc", "ind", "shadow",
185-
"up", "down", "ns", "nc", "n", "se")
186-
187287
const repl_switch = Dict{Any,Any}(
188288
'`' => function (s,args...)
189289
if isempty(s) || position(LineEdit.buffer(s)) == 0
190-
prompt = language_specific_prompt(state, state.interp)
290+
prompt = language_specific_prompt(state, state.stack[1])
191291
buf = copy(LineEdit.buffer(s))
192292
LineEdit.transition(s, prompt) do
193293
LineEdit.state(s, prompt).input_buffer = buf
@@ -198,8 +298,8 @@ module DebuggerFramework
198298
end
199299
)
200300

201-
b = Dict{Any,Any}[skeymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults]
202-
panel.keymap_dict = LineEdit.keymap([repl_switch;b])
301+
state.standard_keymap = Dict{Any,Any}[skeymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults]
302+
panel.keymap_dict = LineEdit.keymap([repl_switch;state.standard_keymap])
203303

204304
# Skip evaluated values (e.g. constants)
205305
print_status(Base.pipe_writer(terminal), state)

src/LineNumbers.jl

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
module LineNumbers
2+
3+
export SourceFile, compute_line, LineBreaking
4+
import Base: getindex, setindex!, length
5+
6+
# Offsets are 0 based
7+
immutable SourceFile
8+
data::Vector{UInt8}
9+
offsets::Vector{UInt64}
10+
end
11+
length(file::SourceFile) = length(file.offsets)
12+
13+
function SourceFile(data)
14+
offsets = UInt64[0]
15+
buf = IOBuffer(data)
16+
local line
17+
while !eof(buf)
18+
line = readuntil(buf,'\n')
19+
!eof(buf) && push!(offsets, position(buf))
20+
end
21+
if !isempty(offsets) && line[end] == '\n'
22+
push!(offsets, position(buf))
23+
end
24+
SourceFile(data,offsets)
25+
end
26+
27+
function compute_line(file::SourceFile, offset)
28+
ind = searchsortedfirst(file.offsets, offset)
29+
ind <= length(file.offsets) && file.offsets[ind] == offset ? ind : ind - 1
30+
end
31+
32+
function getindex(file::SourceFile, line::Int)
33+
if line == length(file.offsets)
34+
return file.data[(file.offsets[end]+1):end]
35+
else
36+
# - 1 to skip the '\n'
37+
return file.data[(file.offsets[line]+1):(file.offsets[line+1]-1)]
38+
end
39+
end
40+
getindex(file::SourceFile, arr::AbstractArray) = [file[x] for x in arr]
41+
42+
# LineBreaking
43+
44+
"""
45+
Indexing adaptor to map from a flat byte offset to a `[line][offset]` pair.
46+
Optionally, off may specify a byte offset relative to which the line number and
47+
offset should be computed
48+
"""
49+
immutable LineBreaking{T}
50+
off::UInt64
51+
file::SourceFile
52+
obj::T
53+
end
54+
55+
function indtransform(lb::LineBreaking, x::Int)
56+
offline = compute_line(lb.file, lb.off)
57+
line = compute_line(lb.file, x)
58+
lineoffset = lb.file.offsets[line]
59+
off = x - lineoffset + 1
60+
if lineoffset < lb.off
61+
off -= lb.off - lineoffset
62+
end
63+
(line - offline + 1), off
64+
end
65+
66+
function getindex(lb::LineBreaking, x::Int)
67+
l, o = indtransform(lb, x)
68+
lb.obj[l][o]
69+
end
70+
71+
function setindex!(lb::LineBreaking, y, x::Int)
72+
l, o = indtransform(lb, x)
73+
lb.obj[l][o] = y
74+
end
75+
function setindex!(lb::LineBreaking, y, x::AbstractArray)
76+
for i in x
77+
lb[i] = y
78+
end
79+
end
80+
81+
end # module

0 commit comments

Comments
 (0)