relisp 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/COPYING +674 -0
- data/Manifest +18 -0
- data/README +171 -0
- data/Rakefile +50 -0
- data/examples/elisp_master/elisp_master.el +54 -0
- data/examples/elisp_master/ruby_slave +19 -0
- data/examples/ruby_master/ruby_master_example +27 -0
- data/lib/relisp/editing_types.rb +86 -0
- data/lib/relisp/programming_types.rb +293 -0
- data/lib/relisp/slaves.rb +365 -0
- data/lib/relisp.rb +30 -0
- data/setup.rb +1599 -0
- data/src/relisp.el +264 -0
- data/src/relisp.elc +0 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +39 -0
- data/tasks/rdoc.rake +50 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +279 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/test/test_editing_types.rb +43 -0
- data/test/test_programming_types.rb +275 -0
- data/test/test_slaves.rb +84 -0
- metadata +110 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 <don@ohspite.net>
|
3
|
+
#
|
4
|
+
# This file is part of Relisp.
|
5
|
+
#
|
6
|
+
# Relisp is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Relisp is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
#
|
21
|
+
# Straight from the elisp info manual:
|
22
|
+
#
|
23
|
+
# Programming Types
|
24
|
+
#
|
25
|
+
# Integer Type:: Numbers without fractional parts.
|
26
|
+
# Floating Point Type:: Numbers with fractional parts and with a large range.
|
27
|
+
# Symbol Type:: A multi-use object that refers to a function,
|
28
|
+
# variable, or property list, and has a unique identity.
|
29
|
+
# (Sequence Type):: Both lists and arrays are classified as sequences.
|
30
|
+
# Cons Cell Type:: Cons cells, and lists (which are made from cons cells).
|
31
|
+
# (Array Type):: Arrays include strings and vectors.
|
32
|
+
# String Type:: An (efficient) array of characters.
|
33
|
+
# Vector Type:: One-dimensional arrays.
|
34
|
+
# Hash Table Type:: Super-fast lookup tables.
|
35
|
+
#
|
36
|
+
# These are also elisp programming types, but Relisp doesn't
|
37
|
+
# explicitly deal with them. Characters are basically just integers,
|
38
|
+
# and functions and macros are cons.
|
39
|
+
#
|
40
|
+
# Character Type:: The representation of letters, numbers and
|
41
|
+
# control characters.
|
42
|
+
# Function Type:: A piece of executable code you can call from elsewhere.
|
43
|
+
# Macro Type:: A method of expanding an expression into another
|
44
|
+
# expression, more fundamental but less pretty.
|
45
|
+
#
|
46
|
+
# And then these elisp types are ignored completely.
|
47
|
+
#
|
48
|
+
# Char-Table Type:: One-dimensional sparse arrays indexed by characters.
|
49
|
+
# Bool-Vector Type:: One-dimensional arrays of `t' or `nil'.
|
50
|
+
# Primitive Function Type:: A function written in C, callable from Lisp.
|
51
|
+
# Byte-Code Type:: A function written in Lisp, then compiled.
|
52
|
+
# Autoload Type:: A type used for automatically loading seldom-used
|
53
|
+
# functions.
|
54
|
+
#
|
55
|
+
# Every type in the first group obviously matches a ruby class except
|
56
|
+
# for 'Cons' and 'Vector'. The types that have an analogous ruby
|
57
|
+
# class are mapped directly to that class. 'Cons' and 'Vector' are
|
58
|
+
# mapped to a corresponding subclass of Array; the only difference
|
59
|
+
# between the two is how they are translated back to elisp.
|
60
|
+
#
|
61
|
+
# Every ruby class that corresponds to a elisp data type is duplicated
|
62
|
+
# inside the Relisp module with the rubyized version of the elisp
|
63
|
+
# name; for example, Relisp::Integer is exactly the same as Fixnum and
|
64
|
+
# Relisp::HashTable is the same as Hash.
|
65
|
+
#
|
66
|
+
# Every class needs to have a +from_elisp+ method that accepts a hash
|
67
|
+
# with the object's string representation, variable name (in the elisp
|
68
|
+
# process) and the Slave instance that created the object. The method
|
69
|
+
# returns a ruby object analogous to the elisp value.
|
70
|
+
#
|
71
|
+
# Every object needs a +to_elisp+ method that creates a string which
|
72
|
+
# results in a elisp value equivalent to the ruby object when read and
|
73
|
+
# evaluated in elisp (i.e. <tt>elisp_eval "(eval (read
|
74
|
+
# #{obj.to_elisp}))"</tt>). For most objects this basically amounts
|
75
|
+
# to a variation on the +to_s+ method. However, for objects that
|
76
|
+
# don't have a string representation (hashes, buffers, frames, ...)
|
77
|
+
# the +to_elisp+ method actually results in elisp code that, when run,
|
78
|
+
# returns the appropriate object.
|
79
|
+
|
80
|
+
module Relisp
|
81
|
+
|
82
|
+
Integer = 1.class
|
83
|
+
class Integer
|
84
|
+
def self.from_elisp(object)
|
85
|
+
object[:string].to_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
Float = (3.14).class
|
91
|
+
class Float
|
92
|
+
def self.from_elisp(object)
|
93
|
+
object[:string].to_f
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
Symbol = :flag.class
|
99
|
+
class Symbol
|
100
|
+
def self.from_elisp(object)
|
101
|
+
if object[:string] == 'nil'
|
102
|
+
nil
|
103
|
+
elsif object[:string] == 't'
|
104
|
+
true
|
105
|
+
else
|
106
|
+
object[:string].to_sym
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_elisp
|
111
|
+
"'" + self.to_s
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
class Cons < Array
|
117
|
+
def self.from_elisp(object)
|
118
|
+
new(super(object))
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_elisp
|
122
|
+
print_string = '(list '
|
123
|
+
each do |elt|
|
124
|
+
print_string << elt.to_elisp << ' '
|
125
|
+
end
|
126
|
+
print_string << ')'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
String = "words, words, words".class
|
132
|
+
class String
|
133
|
+
def self.from_elisp(object)
|
134
|
+
new(eval(object[:string]))
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_elisp
|
138
|
+
self.dump
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
class Vector < Array
|
144
|
+
def self.from_elisp(object)
|
145
|
+
new(super(object))
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_elisp
|
149
|
+
print_string = '[ '
|
150
|
+
each do |elt|
|
151
|
+
print_string << elt.to_elisp << ' '
|
152
|
+
end
|
153
|
+
print_string << ']'
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
HashTable = {:money => "power"}.class
|
159
|
+
class HashTable
|
160
|
+
def self.from_elisp(object)
|
161
|
+
slave = object[:slave]
|
162
|
+
object_variable = slave.get_permanent_variable(object[:variable])
|
163
|
+
|
164
|
+
unless slave.elisp_eval("(type-of #{object_variable})") == "hash-table".to_sym
|
165
|
+
raise ArgumentError, "#{object_variable} isn't a hash-table"
|
166
|
+
end
|
167
|
+
keys_var = slave.new_elisp_variable
|
168
|
+
vals_var = slave.new_elisp_variable
|
169
|
+
slave.elisp_execute( "(setq #{keys_var} nil)" )
|
170
|
+
slave.elisp_execute( "(setq #{vals_var} nil)" )
|
171
|
+
slave.elisp_execute( "(maphash (lambda (key val)
|
172
|
+
(setq #{keys_var} (append #{keys_var} (list key)))
|
173
|
+
(setq #{vals_var} (append #{vals_var} (list val)))) #{object_variable})" )
|
174
|
+
keys = slave.elisp_eval( keys_var )
|
175
|
+
vals = slave.elisp_eval( vals_var )
|
176
|
+
keys ||= []
|
177
|
+
hash = Hash.new
|
178
|
+
keys.each_index do |i|
|
179
|
+
hash[keys[i]] = vals[i]
|
180
|
+
end
|
181
|
+
|
182
|
+
slave.makunbound(object_variable)
|
183
|
+
slave.makunbound(keys_var)
|
184
|
+
slave.makunbound(vals_var)
|
185
|
+
|
186
|
+
return hash
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_elisp
|
190
|
+
lisp = "(progn \n"
|
191
|
+
lisp << " (let ((--temp--relisp--variable (make-hash-table))) \n"
|
192
|
+
each_pair do |key, val|
|
193
|
+
lisp << " (puthash #{key.to_elisp} #{val.to_elisp} --temp--relisp--variable) \n"
|
194
|
+
end
|
195
|
+
lisp << "--temp--relisp--variable))"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
# Every object is given a default +to_elisp+ method, and classes get a
|
203
|
+
# dummy +from_elisp+ method.
|
204
|
+
#
|
205
|
+
class Object
|
206
|
+
def to_elisp
|
207
|
+
self.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.from_elisp(*args)
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class Class
|
216
|
+
# Convert classes to the symbol form of their name
|
217
|
+
def to_elisp
|
218
|
+
self.to_s.to_sym.to_elisp
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class NilClass
|
223
|
+
def to_elisp
|
224
|
+
"nil"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
class TrueClass
|
230
|
+
def to_elisp
|
231
|
+
"t"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
class FalseClass
|
237
|
+
# Falseness in elisp is represented by 'nil'.
|
238
|
+
def to_elisp
|
239
|
+
nil.to_elisp
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
# The normal Array class is modified so that an array can be converted
|
245
|
+
# to either Relisp::Cons or Relisp::Vector.
|
246
|
+
#
|
247
|
+
class Array
|
248
|
+
@@default_elisp_type = Relisp::Cons
|
249
|
+
|
250
|
+
# Converts either a 'cons' or 'vector' to a ruby array.
|
251
|
+
def self.from_elisp(object)
|
252
|
+
object_variable = object[:slave].get_permanent_variable(object[:variable])
|
253
|
+
size = object[:slave].elisp_eval( "(length #{object_variable})" )
|
254
|
+
object_array = new
|
255
|
+
size.times do |i|
|
256
|
+
object_array << object[:slave].elisp_eval( "(elt #{object_variable} #{i.to_elisp})" )
|
257
|
+
end
|
258
|
+
|
259
|
+
object[:slave].elisp_execute( "(makunbound #{object_variable.to_elisp})" )
|
260
|
+
return object_array
|
261
|
+
end
|
262
|
+
|
263
|
+
# Set the elisp type that ruby arrays are converted to by default.
|
264
|
+
def self.default_elisp_type=(type)
|
265
|
+
unless type.ancestors.include?(Array)
|
266
|
+
raise ArgumentError, "#{type} is not a kind of Array"
|
267
|
+
end
|
268
|
+
|
269
|
+
@@default_elisp_type = type
|
270
|
+
end
|
271
|
+
|
272
|
+
def self.default_elisp_type
|
273
|
+
@@default_elisp_type
|
274
|
+
end
|
275
|
+
|
276
|
+
# The elisp type that this array will be converted to by to_elisp.
|
277
|
+
def elisp_type
|
278
|
+
@elisp_type = nil unless defined?(@elisp_type) #to avoid uninitialized warning
|
279
|
+
@elisp_type || @@default_elisp_type
|
280
|
+
end
|
281
|
+
|
282
|
+
def elisp_type=(type)
|
283
|
+
unless type.ancestors.include?(Array)
|
284
|
+
raise ArgumentError, "#{type} is not a kind of Array"
|
285
|
+
end
|
286
|
+
|
287
|
+
@elisp_type = type
|
288
|
+
end
|
289
|
+
|
290
|
+
def to_elisp
|
291
|
+
elisp_type.new(self).to_elisp
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 <don@ohspite.net>
|
3
|
+
#
|
4
|
+
# This file is part of Relisp.
|
5
|
+
#
|
6
|
+
# Relisp is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Relisp is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
#
|
21
|
+
# TODO:
|
22
|
+
# * maybe catch Errno::EPIPE to see if slave died
|
23
|
+
|
24
|
+
module Relisp
|
25
|
+
|
26
|
+
@default_slave = nil
|
27
|
+
|
28
|
+
def self.default_slave
|
29
|
+
@default_slave
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.default_slave=(slave)
|
33
|
+
unless slave.kind_of?(Slave)
|
34
|
+
raise ArgumentError, "#{slave} is not a Slave"
|
35
|
+
end
|
36
|
+
@default_slave = slave
|
37
|
+
end
|
38
|
+
|
39
|
+
# This is the base class for RubySlave and ElispSlave--the Slave
|
40
|
+
# class isn't meant to be used itself.
|
41
|
+
#
|
42
|
+
class Slave
|
43
|
+
|
44
|
+
# Ruby and elisp use these strings to terminate messages sent to
|
45
|
+
# each other. This ruby library and the elisp code are tied to
|
46
|
+
# one another so closely that I don't know if it matters, but it
|
47
|
+
# still seemed like a bad idea to hard code the constants in both
|
48
|
+
# places.
|
49
|
+
ANSWER_CODE = '___FORTYTWO___'
|
50
|
+
QUESTION_CODE = '___TOBEORNOTTOBE___'
|
51
|
+
ERROR_CODE = '___NO_THATSNOTTRUE_THATSIMPOSSIBLE___'
|
52
|
+
ENDOFMESSAGE_REGEXP = Regexp.new(ANSWER_CODE + '|' + QUESTION_CODE + '|' + ERROR_CODE)
|
53
|
+
# Every time ruby asks elisp to evaluate an expression, the result
|
54
|
+
# is saved in this variable so ruby can access it if necessary.
|
55
|
+
PREVIOUS_ELISP_RESULT = :"--relisp--previous--result"
|
56
|
+
# A prefix for elisp variables created by ruby.
|
57
|
+
VARIABLE_PREFIX = '--relisp--variable--'
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
# Whenever ruby calls 'eval' on some code at the request of
|
61
|
+
# elisp it is in a context where any new variables set will drop
|
62
|
+
# out of scope immediately. The @local_binding is a way of
|
63
|
+
# allowing these variables to persist through multiple calls.
|
64
|
+
@local_binding = nil
|
65
|
+
@current_elisp_variable_num = '0'
|
66
|
+
@debug = nil
|
67
|
+
Relisp.default_slave = self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return a symbol which corresponds to an unused variable in
|
71
|
+
# elisp.
|
72
|
+
#
|
73
|
+
def new_elisp_variable
|
74
|
+
(VARIABLE_PREFIX + @current_elisp_variable_num.succ!).to_sym
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a symbol corresponding to a new elisp variable which hold
|
78
|
+
# the same information as _old_variable_. Intended primarily for
|
79
|
+
# use in the +from_elisp+ method in various classes.
|
80
|
+
#
|
81
|
+
def get_permanent_variable(old_variable)
|
82
|
+
permanent_variable = new_elisp_variable
|
83
|
+
elisp_execute( "(setq #{permanent_variable} #{old_variable})" )
|
84
|
+
return permanent_variable
|
85
|
+
end
|
86
|
+
|
87
|
+
# Run _code_ in the elisp process.
|
88
|
+
#
|
89
|
+
def elisp_execute(code)
|
90
|
+
code = code.to_s # maybe code is a symbol or something
|
91
|
+
write_to_emacs code
|
92
|
+
write_to_emacs QUESTION_CODE
|
93
|
+
|
94
|
+
output = ''
|
95
|
+
output_line = read_from_emacs
|
96
|
+
until output_line.strip == ANSWER_CODE
|
97
|
+
if output_line.strip == QUESTION_CODE
|
98
|
+
write_to_emacs((eval(output, @local_binding)).to_elisp)
|
99
|
+
write_to_emacs ANSWER_CODE
|
100
|
+
output = ''
|
101
|
+
elsif output_line.strip == ERROR_CODE
|
102
|
+
raise Relisp::ElispError, (eval output)
|
103
|
+
else
|
104
|
+
output << output_line
|
105
|
+
end
|
106
|
+
output_line = read_from_emacs
|
107
|
+
end
|
108
|
+
|
109
|
+
output.gsub!(/\n\z/, '')
|
110
|
+
return output
|
111
|
+
end
|
112
|
+
|
113
|
+
# Run _code_ in the elisp process and return the result as the
|
114
|
+
# corresponding ruby object. If the ruby object is not going to
|
115
|
+
# be used, use elisp_execute instead.
|
116
|
+
#
|
117
|
+
def elisp_eval(code)
|
118
|
+
result_string = elisp_execute(code)
|
119
|
+
to_ruby(result_string)
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# Pass an elisp evaluation result to the appropriate Relisp class
|
125
|
+
# for translation. The first line of _result_string_ is the
|
126
|
+
# 'type-of' the elisp object. The line(s) after that are the text
|
127
|
+
# version of the object. In case the string representation isn't
|
128
|
+
# enough information to translate the object, the result needs to
|
129
|
+
# be kept (in emacs) in the variable +PREVIOUS_ELISP_RESULT+.
|
130
|
+
#
|
131
|
+
def to_ruby(result_string)
|
132
|
+
result_string = result_string.split("\n")
|
133
|
+
elisp_type = result_string.reverse!.pop
|
134
|
+
object_string = result_string.reverse!.join("\n")
|
135
|
+
|
136
|
+
object_info = {
|
137
|
+
:string => object_string,
|
138
|
+
:variable => PREVIOUS_ELISP_RESULT,
|
139
|
+
:slave => self,
|
140
|
+
}
|
141
|
+
|
142
|
+
# Just one more reason to love Ruby. Call the Relisp class
|
143
|
+
# formed by rubyizing the 'type-of' the result (i.e., hash-table
|
144
|
+
# becomes HashTable).
|
145
|
+
ruby_type = (eval elisp_type.split("-").map { |a| a.capitalize }.join)
|
146
|
+
unless ruby_type.kind_of? Class
|
147
|
+
raise "#{ruby_type} not implemented"
|
148
|
+
end
|
149
|
+
ruby_type.from_elisp(object_info)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Send the constants that ruby and elisp need to share.
|
153
|
+
#
|
154
|
+
def pass_constants
|
155
|
+
[ANSWER_CODE, QUESTION_CODE, ERROR_CODE, PREVIOUS_ELISP_RESULT].each do |constant|
|
156
|
+
read_from_emacs
|
157
|
+
write_to_emacs constant
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Forward any missing method to elisp, writing the function and
|
162
|
+
# arguments in prefix notation (calling the +to_elisp+ method of
|
163
|
+
# each of the _args_).
|
164
|
+
#
|
165
|
+
# This automatically allows access to a large portion of elisp
|
166
|
+
# functions a rubyish way.
|
167
|
+
#
|
168
|
+
def method_missing(function, *args) # :doc:
|
169
|
+
function = function.to_s.gsub('_', '-')
|
170
|
+
unless elisp_eval("(functionp '#{function})")
|
171
|
+
raise NameError, "#{function} is not an elisp function"
|
172
|
+
end
|
173
|
+
|
174
|
+
elisp_eval('(' +
|
175
|
+
function + ' ' +
|
176
|
+
args.map{|a| a.to_elisp}.join(' ') +
|
177
|
+
')')
|
178
|
+
end
|
179
|
+
|
180
|
+
public
|
181
|
+
|
182
|
+
# Creates a method in the slave that is a reference to the
|
183
|
+
# variable given by _symbol_ in the scope of _binding_. This is
|
184
|
+
# probably only useful when calling elisp in ruby where the elisp
|
185
|
+
# code itself calls ruby again. When the elisp process calls
|
186
|
+
# +ruby_eval+ the code is executed inside the loop of the slave
|
187
|
+
# object, so the variables in the scope of the original call to
|
188
|
+
# elisp aren't normally available.
|
189
|
+
#
|
190
|
+
# emacs = Relisp::ElispSlave.new
|
191
|
+
# number = 5
|
192
|
+
# emacs.elisp_eval('(ruby-eval "number")') # => [error]
|
193
|
+
#
|
194
|
+
# emacs = Relisp::ElispSlave.new
|
195
|
+
# number = 5
|
196
|
+
# local_binding = binding
|
197
|
+
# emacs.provide(:number, local_binding)
|
198
|
+
# emacs.elisp_eval('(ruby-eval "number")') # => 5
|
199
|
+
#
|
200
|
+
def provide(symbol, binding)
|
201
|
+
eval "@__#{symbol.to_s}__binding = binding"
|
202
|
+
|
203
|
+
instance_eval <<-endstr
|
204
|
+
def #{symbol.to_s}
|
205
|
+
eval("#{symbol.to_s}", @__#{symbol.to_s}__binding)
|
206
|
+
end
|
207
|
+
endstr
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
# This class dedicates the ruby process to responding to queries
|
213
|
+
# from the emacs process that started it. See Relisp::Slave.
|
214
|
+
#
|
215
|
+
class RubySlave < Slave
|
216
|
+
|
217
|
+
# Can be provided with a block, in which case the block is run in
|
218
|
+
# the context of the slave and then the slave is automatically
|
219
|
+
# started. This makes slave methods available to the block
|
220
|
+
# without specifying an explicit receiver, and variables and
|
221
|
+
# functions defined in the block are in scope when requests from
|
222
|
+
# elisp are evaluated.
|
223
|
+
#
|
224
|
+
def initialize
|
225
|
+
super
|
226
|
+
pass_constants
|
227
|
+
|
228
|
+
if block_given?
|
229
|
+
yield self
|
230
|
+
start
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
# Begin the main listening loop.
|
236
|
+
#
|
237
|
+
def start
|
238
|
+
begin
|
239
|
+
@local_binding = binding
|
240
|
+
|
241
|
+
loop do
|
242
|
+
code = ''
|
243
|
+
input = read_from_emacs
|
244
|
+
until input.strip == QUESTION_CODE
|
245
|
+
code << input
|
246
|
+
input = read_from_emacs
|
247
|
+
end
|
248
|
+
code.gsub!(/\n\z/, '')
|
249
|
+
|
250
|
+
write_to_emacs((eval code, @local_binding).to_elisp)
|
251
|
+
write_to_emacs ANSWER_CODE
|
252
|
+
end
|
253
|
+
rescue => dag_yo
|
254
|
+
write_to_emacs dag_yo
|
255
|
+
write_to_emacs ERROR_CODE
|
256
|
+
retry
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
private
|
261
|
+
|
262
|
+
# Emacs sends ruby's stdout to the filter function designated for
|
263
|
+
# the ruby process.
|
264
|
+
#
|
265
|
+
def write_to_emacs(code)
|
266
|
+
puts code
|
267
|
+
end
|
268
|
+
|
269
|
+
# Messages appear on ruby's stdin after emacs sends them to ruby
|
270
|
+
# process.
|
271
|
+
#
|
272
|
+
def read_from_emacs
|
273
|
+
gets
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Provides an interface to an instance of emacs started as an IO
|
278
|
+
# object. See Relisp::Slave.
|
279
|
+
#
|
280
|
+
class ElispSlave < Slave
|
281
|
+
alias do elisp_eval
|
282
|
+
|
283
|
+
# Start an emacs process, load the relisp library, and force the
|
284
|
+
# process to become a slave to ruby's bidding. The string
|
285
|
+
# _cli_options_ specifies arguments to pass to emacs on the
|
286
|
+
# command line, and _load_files_ is array of files to load (with
|
287
|
+
# the '-l' command line option) after the relisp.el library.
|
288
|
+
#
|
289
|
+
def initialize(cli_options = "--no-site-file --no-init-file", load_files = [])
|
290
|
+
super()
|
291
|
+
# load relisp.elc if available
|
292
|
+
elisp_path = File.expand_path(File.join(File.dirname(__FILE__), '../../src/relisp'))
|
293
|
+
|
294
|
+
@local_binding = binding
|
295
|
+
|
296
|
+
emacs_command = if RUBY_PLATFORM.downcase.include?('mswin')
|
297
|
+
"start emacs --batch "
|
298
|
+
else
|
299
|
+
"emacs --batch "
|
300
|
+
end
|
301
|
+
emacs_command << cli_options
|
302
|
+
emacs_command << " -l #{elisp_path}"
|
303
|
+
load_files.each do |file|
|
304
|
+
emacs_command << " -l #{file}"
|
305
|
+
end
|
306
|
+
emacs_command << " --eval '(relisp-become-slave)'"
|
307
|
+
# In batch mode, emacs sends its normal output to stderr for
|
308
|
+
# some reason. I'm sure it's a good one...
|
309
|
+
emacs_command << " 2>&1"
|
310
|
+
@emacs_pipe = IO.popen(emacs_command, "w+")
|
311
|
+
|
312
|
+
# gobble whatever output until emacs reports for duty
|
313
|
+
until read_from_emacs.strip == "SEND CONSTANTS"; end
|
314
|
+
pass_constants
|
315
|
+
end
|
316
|
+
|
317
|
+
attr_accessor :debug
|
318
|
+
|
319
|
+
# When given a block, runs the block with debugging turned on and
|
320
|
+
# then restores the former status of debug messages. Otherwise,
|
321
|
+
# toggles the status of debug messages.
|
322
|
+
#
|
323
|
+
def debugging
|
324
|
+
if block_given?
|
325
|
+
debug_original_val = @debug
|
326
|
+
begin
|
327
|
+
@debug = true
|
328
|
+
puts
|
329
|
+
puts "-----------------"
|
330
|
+
yield
|
331
|
+
ensure
|
332
|
+
@debug = debug_original_val
|
333
|
+
puts "-----------------"
|
334
|
+
end
|
335
|
+
else
|
336
|
+
@debug = ! @debug
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
private
|
341
|
+
|
342
|
+
# Emacs reads from stdin and makes the input available to the
|
343
|
+
# mini-buffer.
|
344
|
+
#
|
345
|
+
def write_to_emacs(code)
|
346
|
+
if @debug
|
347
|
+
puts "ruby> " + code.to_s unless code =~ ENDOFMESSAGE_REGEXP
|
348
|
+
end
|
349
|
+
@emacs_pipe.puts code
|
350
|
+
end
|
351
|
+
|
352
|
+
# Emacs sends whatever it outputs by way of 'message' to stderr,
|
353
|
+
# which is redirected to stdout when the emacs process is started
|
354
|
+
# in initialize.
|
355
|
+
#
|
356
|
+
def read_from_emacs
|
357
|
+
output = @emacs_pipe.gets
|
358
|
+
if @debug
|
359
|
+
puts "lisp> " + output unless output =~ ENDOFMESSAGE_REGEXP
|
360
|
+
end
|
361
|
+
return output
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
data/lib/relisp.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2009 <don@ohspite.net>
|
3
|
+
#
|
4
|
+
# This file is part of Relisp.
|
5
|
+
#
|
6
|
+
# Relisp is free software: you can redistribute it and/or modify it
|
7
|
+
# under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Relisp is distributed in the hope that it will be useful, but
|
12
|
+
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
# General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see
|
18
|
+
# <http://www.gnu.org/licenses/>.
|
19
|
+
#++
|
20
|
+
|
21
|
+
module Relisp
|
22
|
+
VERSION = '0.9.0'
|
23
|
+
|
24
|
+
class ElispError < RuntimeError; end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'relisp/slaves'
|
28
|
+
require 'relisp/programming_types'
|
29
|
+
require 'relisp/editing_types'
|
30
|
+
|