relisp 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+