Remove the first line of stack trace.
[riece] / lisp / riece-ruby.el
1 ;;; riece-ruby.el --- interact with Ruby interpreter
2 ;; Copyright (C) 1998-2005 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Created: 1998-09-28
6 ;; Keywords: IRC, riece
7
8 ;; This file is part of Riece.
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;; riece-ruby.el is a library to interact with the Ruby interpreter.
28 ;; It supports concurrent execution of Ruby programs in a single
29 ;; session.  For example:
30 ;; 
31 ;; (riece-ruby-execute "t1" "sleep 30"); returns immediately
32 ;; => nil
33 ;;
34 ;; (riece-ruby-execute "t2" "1 + 1")
35 ;; => nil
36 ;;
37 ;; (riece-ruby-execute "t3" "\"")
38 ;; => nil
39 ;;
40 ;; (riece-ruby-inspect "t1")
41 ;; => ((OK nil) nil "running")
42 ;;
43 ;; (riece-ruby-inspect "t2")
44 ;; => ((OK nil) "2" "finished")
45 ;;
46 ;; (riece-ruby-inspect "t3")
47 ;; => ((OK nil) "(eval):1:in `dispatch_eval': compile error
48 ;; (eval):1: unterminated string meets end of file" "exited")
49
50 ;;; Code:
51
52 (defvar riece-ruby-command "ruby"
53   "Command name for Ruby interpreter.")
54
55 (defvar riece-ruby-process nil)
56
57 (defvar riece-ruby-lock nil)
58 (defvar riece-ruby-response nil)
59 (defvar riece-ruby-data nil)
60 (defvar riece-ruby-input nil)
61 (defvar riece-ruby-status nil)
62
63 (defvar riece-ruby-output-handler-alist nil)
64 (defvar riece-ruby-exit-handler-alist nil)
65
66 (defun riece-ruby-substitute-variables (program variable value)
67   (setq program (copy-sequence program))
68   (let ((pointer program))
69     (while pointer
70       (setq pointer (memq variable program))
71       (if pointer
72           (setcar pointer value)))
73     program))
74
75 (defun riece-ruby-escape-data (data)
76   (let ((index 0))
77     (while (string-match "[%\r\n]+" data index)
78       (setq data (replace-match
79                   (mapconcat (lambda (c) (format "%%%02X" c))
80                              (match-string 0 data) "")
81                   nil nil data)
82             index (+ (match-end 0)
83                      (* (- (match-end 0) (match-beginning 0)) 2))))
84     data))
85
86 (defun riece-ruby-unescape-data (data)
87   (let ((index 0))
88     (while (string-match "%\\([0-9A-F][0-9A-F]\\)" data index)
89       (setq data (replace-match
90                   (read (concat "\"\\x" (match-string 1 data) "\""))
91                   nil nil data)
92             index (- (match-end 0) 2)))
93     data))
94
95 (defun riece-ruby-send-eval (name program)
96   (let* ((string (riece-ruby-escape-data program))
97          (length (- (length string) 998))
98          (index 0)
99          data)
100     (while (< index length)
101       (setq data (cons (substring string index (setq index (+ index 998)))
102                        data)))
103     (setq data (cons (substring string index) data)
104           data (nreverse data))
105     (save-excursion
106       (set-buffer (process-buffer riece-ruby-process))
107       (make-local-variable 'riece-ruby-lock)
108       (setq riece-ruby-lock nil)
109       (make-local-variable 'riece-ruby-response)
110       (setq riece-ruby-response nil)
111       (make-local-variable 'riece-ruby-data)
112       (setq riece-ruby-data nil)
113       (make-local-variable 'riece-ruby-input)
114       (setq riece-ruby-input nil)
115       (make-local-variable 'riece-ruby-status)
116       (setq riece-ruby-status nil))
117     (process-send-string riece-ruby-process
118                          (concat "EVAL " name "\r\n"))
119     (while data
120       (process-send-string riece-ruby-process
121                            (concat "D " (car data) "\r\n"))
122       (setq data (cdr data)))
123     (process-send-string riece-ruby-process "END\r\n")))
124     
125 (defun riece-ruby-send-poll (name)
126   (save-excursion
127     (set-buffer (process-buffer riece-ruby-process))
128     (make-local-variable 'riece-ruby-lock)
129     (setq riece-ruby-lock nil)
130     (make-local-variable 'riece-ruby-response)
131     (setq riece-ruby-response nil)
132     (make-local-variable 'riece-ruby-data)
133     (setq riece-ruby-data nil)
134     (make-local-variable 'riece-ruby-input)
135     (setq riece-ruby-input nil)
136     (make-local-variable 'riece-ruby-status)
137     (setq riece-ruby-status nil))
138   (process-send-string riece-ruby-process
139                        (concat "POLL " name "\r\n")))
140
141 (defun riece-ruby-filter (process input)
142   (save-excursion
143     (set-buffer (process-buffer process))
144     (goto-char (point-max))
145     (insert input)
146     (goto-char (process-mark process))
147     (beginning-of-line)
148     (while (looking-at ".*\r?\n")
149       (if (looking-at "OK\\( \\(.*\\)\\)?\r")
150           (progn
151             (if riece-ruby-input
152                 (setq riece-ruby-data (mapconcat #'riece-ruby-unescape-data
153                                                  riece-ruby-input "")))
154             (setq riece-ruby-input nil
155                   riece-ruby-response (list 'OK (match-string 2))
156                   riece-ruby-lock nil))
157         (if (looking-at "ERR \\([0-9]+\\)\\( \\(.*\\)\\)?\r")
158             (progn
159               (setq riece-ruby-input nil
160                     riece-ruby-response
161                     (list 'ERR (string-to-number (match-string 2))
162                           (match-string 3))
163                     riece-ruby-lock nil))
164           (if (looking-at "D \\(.*\\)\r")
165               (setq riece-ruby-input (cons (match-string 1) riece-ruby-input))
166             (if (looking-at "S program \\(.*\\)\r")
167                 (setq riece-ruby-status (match-string 1))
168               (if (looking-at "# output \\(.*\\) \\(.*\\)\r")
169                   (let ((entry (assoc (match-string 1)
170                                       riece-ruby-output-handler-alist)))
171                     (if entry
172                         (funcall (cdr entry) (match-string 2))))
173                 (if (looking-at "# exit \\(.*\\)\r")
174                     (let ((entry (assoc (match-string 1)
175                                         riece-ruby-exit-handler-alist)))
176                       (if entry
177                           (funcall (cdr entry))))))))))
178       (forward-line))
179     (set-marker (process-mark process) (point-marker))))
180
181 (defun riece-ruby-sentinel (process status)
182   (kill-buffer (process-buffer process)))
183
184 (defun riece-ruby-execute (name program)
185   (unless (and riece-ruby-process
186                (eq (process-status riece-ruby-process) 'run))
187     (setq riece-ruby-process
188           (start-process "riece-ruby" (generate-new-buffer " *Ruby*")
189                          riece-ruby-command
190                          (expand-file-name
191                           "rubyserv.rb"
192                           (file-name-directory
193                            (symbol-file 'riece-ruby-execute)))))
194     (set-process-filter riece-ruby-process #'riece-ruby-filter)
195     (set-process-sentinel riece-ruby-process #'riece-ruby-sentinel))
196   (save-excursion
197     (set-buffer (process-buffer riece-ruby-process))
198     (setq riece-ruby-lock t)
199     (riece-ruby-send-eval name program)
200     (while riece-ruby-lock
201       (accept-process-output riece-ruby-process))))
202
203 (defun riece-ruby-inspect (name)
204   (save-excursion
205     (set-buffer (process-buffer riece-ruby-process))
206     (setq riece-ruby-lock t)
207     (riece-ruby-send-poll name)
208     (while (null riece-ruby-response)
209       (accept-process-output riece-ruby-process))
210     (list riece-ruby-response
211           riece-ruby-data
212           riece-ruby-status)))
213
214 (provide 'riece-ruby)
215
216 ;;; riece-ruby.el ends here