/[slime]/slime/contrib/swank.rb
ViewVC logotype

Contents of /slime/contrib/swank.rb

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Sun Jul 12 08:01:10 2009 UTC (4 years, 9 months ago) by heller
Branch: MAIN
CVS Tags: SLIME-2-3, FAIRLY-STABLE, byte-stream, SLIME-2-2, HEAD
Changes since 1.1: +1 -1 lines
* swank-mit-scheme.scm (swank:create-repl): Implement it.
* swank-kawa.scm, swank-goo.scm, swank.rb (frame-source-location):
Renamed from frame-source-location-for-emacs.
1 # swank.rb --- swank server for Ruby.
2 #
3 # This is my first Ruby program and looks probably rather strange. Some
4 # people write Scheme interpreters when learning new languages, I
5 # write swank backends.
6 #
7 # Only a few things work.
8 # 1. Start the server with something like: ruby -r swank -e swank
9 # 2. Use M-x slime-connect to establish a connection
10
11 require "socket"
12
13 def swank(port=4005)
14 accept_connections port, false
15 end
16
17 def start_swank(port_file)
18 accept_connections false, port_file
19 end
20
21 def accept_connections(port, port_file)
22 server = TCPServer.new("localhost", port || 0)
23 puts "Listening on #{server.addr.inspect}\n"
24 if port_file
25 write_port_file server.addr[1], port_file
26 end
27 socket = begin server.accept ensure server.close end
28 begin
29 serve socket.to_io
30 ensure
31 socket.close
32 end
33 end
34
35 def write_port_file(port, filename)
36 File.open(filename, File::CREAT|File::EXCL|File::WRONLY) do |f|
37 f.puts port
38 end
39 end
40
41 def serve(io)
42 main_loop(io)
43 end
44
45 def main_loop(io)
46 c = Connection.new(io)
47 while true
48 catch :swank_top_level do
49 c.dispatch(read_packet(io))
50 end
51 end
52 end
53
54 class Connection
55
56 def initialize(io)
57 @io = io
58 end
59
60 def dispatch(event)
61 puts "dispatch: %s\n" % event.inspect
62 case event[0]
63 when :":emacs-rex"
64 emacs_rex *event[1..4]
65 else raise "Unhandled event: #{event.inspect}"
66 end
67 end
68
69 def send_to_emacs(obj)
70 payload = write_sexp_to_string(obj)
71 @io.write("%06x" % payload.length)
72 @io.write payload
73 @io.flush
74 end
75
76 def emacs_rex(form, pkg, thread, id)
77 proc = $rpc_entries[form[0]]
78 args = form[1..-1];
79 begin
80 raise "Undefined function: #{form[0]}" unless proc
81 value = proc[*args]
82 rescue Exception => exc
83 begin
84 pseudo_debug exc
85 ensure
86 send_to_emacs [:":return", [:":abort"], id]
87 end
88 else
89 send_to_emacs [:":return", [:":ok", value], id]
90 end
91 end
92
93 def pseudo_debug(exc)
94 level = 1
95 send_to_emacs [:":debug", 0, level] + sldb_info(exc, 0, 20)
96 begin
97 sldb_loop exc
98 ensure
99 send_to_emacs [:":debug-return", 0, level, :nil]
100 end
101 end
102
103 def sldb_loop(exc)
104 $sldb_context = [self,exc]
105 while true
106 dispatch(read_packet(@io))
107 end
108 end
109
110 def sldb_info(exc, start, _end)
111 [[exc.to_s,
112 " [%s]" % exc.class.name,
113 :nil],
114 sldb_restarts(exc),
115 sldb_backtrace(exc, start, _end),
116 []]
117 end
118
119 def sldb_restarts(exc)
120 [["Quit", "SLIME top-level."]]
121 end
122
123 def sldb_backtrace(exc, start, _end)
124 bt = []
125 exc.backtrace[start.._end].each_with_index do |frame, i|
126 bt << [i, frame]
127 end
128 bt
129 end
130
131 def frame_src_loc(exc, frame)
132 string = exc.backtrace[frame]
133 match = /([^:]+):([0-9]+)/.match(string)
134 if match
135 file,line = match[1..2]
136 [:":location", [:":file", file], [:":line", line.to_i], :nil]
137 else
138 [:":error", "no src-loc for frame: #{string}"]
139 end
140 end
141
142 end
143
144 $rpc_entries = Hash.new
145
146 $rpc_entries[:"swank:connection-info"] = lambda do ||
147 [:":pid", $$,
148 :":package", [:":name", "ruby", :":prompt", "ruby> "],
149 :":lisp-implementation", [:":type", "Ruby",
150 :":name", "ruby",
151 :":version", RUBY_VERSION]]
152 end
153
154 def swank_interactive_eval(string)
155 eval(string,TOPLEVEL_BINDING).inspect
156 end
157
158 $rpc_entries[:"swank:interactive-eval"] = \
159 $rpc_entries[:"swank:interactive-eval-region"] = \
160 $rpc_entries[:"swank:pprint-eval"] = lambda { |string|
161 swank_interactive_eval string
162 }
163
164 $rpc_entries[:"swank:throw-to-toplevel"] = lambda {
165 throw :swank_top_level
166 }
167
168 $rpc_entries[:"swank:backtrace"] = lambda do |from, to|
169 conn, exc = $sldb_context
170 conn.sldb_backtrace(exc, from, to)
171 end
172
173 $rpc_entries[:"swank:frame-source-location"] = lambda do |frame|
174 conn, exc = $sldb_context
175 conn.frame_src_loc(exc, frame)
176 end
177
178 #ignored
179 $rpc_entries[:"swank:buffer-first-change"] = \
180 $rpc_entries[:"swank:operator-arglist"] = lambda do
181 :nil
182 end
183
184 $rpc_entries[:"swank:simple-completions"] = lambda do |prefix, pkg|
185 swank_simple_completions prefix, pkg
186 end
187
188 # def swank_simple_completions(prefix, pkg)
189
190 def read_packet(io)
191 header = read_chunk(io, 6)
192 len = header.hex
193 payload = read_chunk(io, len)
194 #$deferr.puts payload.inspect
195 read_sexp_from_string(payload)
196 end
197
198 def read_chunk(io, len)
199 buffer = io.read(len)
200 raise "short read" if buffer.length != len
201 buffer
202 end
203
204 def write_sexp_to_string(obj)
205 string = ""
206 write_sexp_to_string_loop obj, string
207 string
208 end
209
210 def write_sexp_to_string_loop(obj, string)
211 if obj.is_a? String
212 string << "\""
213 string << obj.gsub(/(["\\])/,'\\\\\1')
214 string << "\""
215 elsif obj.is_a? Array
216 string << "("
217 max = obj.length-1
218 obj.each_with_index do |e,i|
219 write_sexp_to_string_loop e, string
220 string << " " unless i == max
221 end
222 string << ")"
223 elsif obj.is_a? Symbol or obj.is_a? Numeric
224 string << obj.to_s
225 elsif obj == false
226 string << "nil"
227 elsif obj == true
228 string << "t"
229 else raise "Can't write: #{obj.inspect}"
230 end
231 end
232
233 def read_sexp_from_string(string)
234 stream = StringInputStream.new(string)
235 reader = LispReader.new(stream)
236 reader.read
237 end
238
239 class LispReader
240 def initialize(io)
241 @io = io
242 end
243
244 def read(allow_consing_dot=false)
245 skip_whitespace
246 c = @io.getc
247 case c
248 when ?( then read_list(true)
249 when ?" then read_string
250 when ?' then read_quote
251 when nil then raise EOFError.new("EOF during read")
252 else
253 @io.ungetc(c)
254 obj = read_number_or_symbol
255 if obj == :"." and not allow_consing_dot
256 raise "Consing-dot in invalid context"
257 end
258 obj
259 end
260 end
261
262 def read_list(head)
263 list = []
264 loop do
265 skip_whitespace
266 c = @io.readchar
267 if c == ?)
268 break
269 else
270 @io.ungetc(c)
271 obj = read(!head)
272 if obj == :"."
273 error "Consing-dot not implemented" # would need real conses
274 end
275 head = false
276 list << obj
277 end
278 end
279 list
280 end
281
282 def read_string
283 string = ""
284 loop do
285 c = @io.getc
286 case c
287 when ?"
288 break
289 when ?\\
290 c = @io.getc
291 case c
292 when ?\\, ?" then string << c
293 else raise "Invalid escape char: \\%c" % c
294 end
295 else
296 string << c
297 end
298 end
299 string
300 end
301
302 def read_quote
303 [:quote, read]
304 end
305
306 def read_number_or_symbol
307 token = read_token
308 if token.empty?
309 raise EOFError.new
310 elsif /^[0-9]+$/.match(token)
311 token.to_i
312 elsif /^[0-9]+\.[0-9]+$/.match(token)
313 token.to_f
314 else
315 token.intern
316 end
317 end
318
319 def read_token
320 token = ""
321 loop do
322 c = @io.getc
323 if c.nil?
324 break
325 elsif terminating?(c)
326 @io.ungetc(c)
327 break
328 else
329 token << c
330 end
331 end
332 token
333 end
334
335 def skip_whitespace
336 loop do
337 c = @io.getc
338 case c
339 when ?\s, ?\n, ?\t then next
340 when nil then break
341 else @io.ungetc(c); break
342 end
343 end
344 end
345
346 def terminating?(char)
347 " \n\t()\"'".include?(char)
348 end
349
350 end
351
352
353 class StringInputStream
354 def initialize(string)
355 @string = string
356 @pos = 0
357 @max = string.length
358 end
359
360 def pos() @pos end
361
362 def getc
363 if @pos == @max
364 nil
365 else
366 c = @string[@pos]
367 @pos += 1
368 c
369 end
370 end
371
372 def readchar
373 getc or raise EOFError.new
374 end
375
376 def ungetc(c)
377 if @pos > 0 && @string[@pos-1] == c
378 @pos -= 1
379 else
380 raise "Invalid argument: %c [at %d]" % [c, @pos]
381 end
382 end
383
384 end
385

  ViewVC Help
Powered by ViewVC 1.1.5