live_ast 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.rdoc CHANGED
@@ -1,6 +1,15 @@
1
1
 
2
2
  = live_ast Changes
3
3
 
4
+ == Version 0.7.0
5
+
6
+ * eval replacement option now included in the gem
7
+ * new 'boc' gem for eval replacement -- no more syntax restrictions
8
+ * add require 'live_ast/full' -- shortcut for 'live_ast' + 'live_ast/replace_eval'
9
+ * rubyspec conformance for require 'live_ast/full'
10
+ * added required_ruby_version to gemspec
11
+ * fixed error when IRB is partially required
12
+
4
13
  == Version 0.6.3
5
14
 
6
15
  * minor change for Ruby 1.9.3
data/MANIFEST CHANGED
@@ -7,12 +7,15 @@ lib/live_ast.rb
7
7
  lib/live_ast/ast_eval.rb
8
8
  lib/live_ast/ast_load.rb
9
9
  lib/live_ast/base.rb
10
+ lib/live_ast/common.rb
10
11
  lib/live_ast/error.rb
11
12
  lib/live_ast/evaler.rb
13
+ lib/live_ast/full.rb
12
14
  lib/live_ast/irb_spy.rb
13
15
  lib/live_ast/linker.rb
14
16
  lib/live_ast/loader.rb
15
17
  lib/live_ast/reader.rb
18
+ lib/live_ast/replace_eval.rb
16
19
  lib/live_ast/replace_load.rb
17
20
  lib/live_ast/replace_raise.rb
18
21
  lib/live_ast/to_ast.rb
@@ -53,6 +56,8 @@ test/readme_test.rb
53
56
  test/recursive_eval_test.rb
54
57
  test/redefine_method_test.rb
55
58
  test/reload_test.rb
59
+ test/replace_eval_test.rb
60
+ test/rubyspec_test.rb
56
61
  test/stdlib_test.rb
57
62
  test/thread_test.rb
58
63
  test/to_ast_feature_test.rb
data/README.rdoc CHANGED
@@ -3,8 +3,7 @@
3
3
 
4
4
  == Summary
5
5
 
6
- A pure Ruby library for obtaining live abstract syntax trees of
7
- methods and procs.
6
+ Live abstract syntax trees of methods and procs.
8
7
 
9
8
  == Synopsis
10
9
 
@@ -18,15 +17,12 @@ methods and procs.
18
17
 
19
18
  #### ASTs of methods
20
19
 
21
- m = Greet.instance_method(:default)
22
-
23
- p m.to_ast
20
+ p Greet.instance_method(:default).to_ast
24
21
  # => s(:defn, :default, s(:args), s(:scope, s(:block, s(:str, "hello"))))
25
22
 
26
23
  #### ASTs of lambdas, procs, blocks
27
24
 
28
25
  f = lambda { "foo" }
29
-
30
26
  p f.to_ast
31
27
  # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "foo"))
32
28
 
@@ -39,18 +35,27 @@ methods and procs.
39
35
  "bar"
40
36
  end
41
37
 
42
- #### ASTs from dynamic code
38
+ #### ASTs from dynamic code -- fully integrated version
43
39
 
44
- f = ast_eval "lambda { 'dynamic' }", binding
40
+ require 'live_ast/full'
45
41
 
42
+ f = eval "lambda { 'dynamic1' }"
46
43
  p f.to_ast
47
- # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "dynamic"))
44
+ # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "dynamic1"))
45
+
46
+ eval "def g ; 'dynamic2' ; end"
47
+ p method(:g).to_ast
48
+ # => s(:defn, :g, s(:args), s(:scope, s(:block, s(:str, "dynamic2"))))
49
+
50
+ #### ASTs from dynamic code -- pure ruby version
48
51
 
49
- ast_eval "def g ; 'dynamic' ; end", binding
50
- m = method(:g)
52
+ u = ast_eval "lambda { 'dynamic3' }", binding
53
+ p u.to_ast
54
+ # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "dynamic3"))
51
55
 
52
- p m.to_ast
53
- # => s(:defn, :g, s(:args), s(:scope, s(:block, s(:str, "dynamic"))))
56
+ ast_eval "def v ; 'dynamic4' ; end", binding
57
+ p method(:v).to_ast
58
+ # => s(:defn, :v, s(:args), s(:scope, s(:block, s(:str, "dynamic4"))))
54
59
 
55
60
  == Install
56
61
 
@@ -64,20 +69,19 @@ Or from inside an unpacked .tgz download, <code>rake install</code> /
64
69
  LiveAST enables a program to find the ASTs of objects created by
65
70
  dynamically generated code. It may be used in a strictly noninvasive
66
71
  manner, where no standard classes or methods are modified, or it may
67
- be transparently integrated into Ruby (experimental). The default
68
- setting is in between.
72
+ be transparently integrated into Ruby. The default setting is in
73
+ between.
69
74
 
70
- RubyParser is responsible for parsing and building the ASTs, though
71
- another parser may be easily substituted (in fact the name +to_ast+ is
72
- used instead of +to_sexp+ because LiveAST has no understanding of what
73
- the parser outputs).
75
+ RubyParser is the default parsing engine. To replace it with Ripper,
76
+ <code>gem install live_ast_ripper</code> and then <code>require
77
+ 'live_ast_ripper'</code>. A simple plug-in interface allows LiveAST to
78
+ work with any parser.
74
79
 
75
- Note that RubyParser does not currently support the newer Ruby 1.9
76
- syntax features (<code>Racc::ParseError</code> will be raised).
77
-
78
- To use the Ripper parser instead, <code>gem install
79
- live_ast_ripper</code> and then <code>require
80
- 'live_ast_ripper'</code>.
80
+ The advantage of RubyParser is that it gives the traditional ParseTree
81
+ sexps used by tools such as <code>ruby2ruby</code>. The disadvantage
82
+ is that the newer Ruby 1.9 syntax is not supported. However the 1.8
83
+ syntax restriction only applies to files from which ASTs are actually
84
+ requested. All other files may use 1.9 syntax.
81
85
 
82
86
  LiveAST is thread-safe.
83
87
 
@@ -92,15 +96,14 @@ Ruby 1.9.2 or higher is required.
92
96
 
93
97
  == +to_ruby+
94
98
 
95
- Although +ruby2ruby+ is not required by default,
99
+ When the default parser is active,
96
100
 
97
101
  require 'live_ast/to_ruby'
98
102
 
99
- will require +ruby2ruby+ and define the +to_ruby+ method for +Method+,
100
- +UnboundMethod+, and +Proc+. These methods are one-liners which pass
101
- the extracted ASTs to +ruby2ruby+.
103
+ will define the +to_ruby+ method for the +Method+, +UnboundMethod+,
104
+ and +Proc+ classes. These methods are one-liners which pass the
105
+ extracted ASTs to <code>ruby2ruby</code>.
102
106
 
103
- require 'live_ast'
104
107
  require 'live_ast/to_ruby'
105
108
 
106
109
  p lambda { |x, y| x + y }.to_ruby # => "lambda { |x, y| (x + y) }"
@@ -114,38 +117,50 @@ the extracted ASTs to +ruby2ruby+.
114
117
  p A.instance_method(:f).to_ruby # => "def f\n \"A#f\"\nend"
115
118
 
116
119
  In general, +to_ruby+ will hook into the unparser provided by the
117
- parser, if one is found.
120
+ parser plug-in, if one is found.
118
121
 
119
- == Loading Source
122
+ == Pure Ruby and +ast_eval+
120
123
 
121
- Objects created via +require+ and +load+ will be AST-accessible.
122
- However +ast_eval+ must be used instead of +eval+ for AST-accessible
123
- objects. +ast_eval+ has the same semantics as +eval+ except that the
124
- binding argument is required.
124
+ An essential feature of <code>require 'live_ast'</code> is that it is
125
+ implemented in pure ruby. However since pure ruby is not powerful
126
+ enough to replace +eval+, one must use +ast_eval+ instead of +eval+
127
+ for AST-accessible objects. +ast_eval+ has the same semantics as
128
+ +eval+ except that the binding argument is required.
125
129
 
126
130
  require 'live_ast'
131
+
132
+ u = ast_eval "lambda { 'dynamic3' }", binding
133
+ p u.to_ast
134
+ # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "dynamic3"))
127
135
 
128
- class A
129
- ast_eval %{
130
- def f
131
- "A#f"
132
- end
133
-
134
- # nested evals OK
135
- ast_eval %{
136
- def g
137
- "A#g"
138
- end
139
- }, binding
140
-
141
- }, binding
142
- end
136
+ == Full integration
143
137
 
144
- p A.instance_method(:f).to_ast
145
- # => s(:defn, :f, s(:args), s(:scope, s(:block, s(:str, "A#f"))))
138
+ In order for LiveAST to be transparent to the user, +eval+ must be
139
+ replaced. This requires the +boc+ gem (http://quix.github.com/boc)
140
+ which at present only targets MRI 1.9.2 (other platforms are planned).
141
+
142
+ To replace +eval+,
143
+
144
+ % gem install boc
145
+
146
+ and then
147
+
148
+ require 'live_ast/full'
146
149
 
147
- p A.instance_method(:g).to_ast
148
- # => s(:defn, :g, s(:args), s(:scope, s(:block, s(:str, "A#g"))))
150
+ The new AST-electrified +eval+, +instance_eval+, +module_eval+,
151
+ +class_eval+, and <code>Binding#eval</code> all pass RubySpec
152
+ (http://rubyspec.org) with the minor exception of backtraces sometimes
153
+ not matching that of the original +eval+. See the "Backtraces" section
154
+ below for details.
155
+
156
+ require 'live_ast/full'
157
+
158
+ f = eval "lambda { 'dynamic1' }"
159
+ p f.to_ast
160
+ # => s(:iter, s(:call, nil, :lambda, s(:arglist)), nil, s(:str, "dynamic1"))
161
+
162
+ Since LiveAST itself is pure ruby, any new platforms supported by
163
+ +boc+ will automatically work with <code>live_ast/full</code>.
149
164
 
150
165
  == Limitations
151
166
 
@@ -173,6 +188,49 @@ You can probably skip these next sections. Goodbye.
173
188
 
174
189
  ----
175
190
 
191
+ == Backtraces
192
+
193
+ +ast_eval+ is meant to be compatible with +eval+. For instance the
194
+ first line of +ast_eval+'s backtrace should be identical to that of
195
+ +eval+:
196
+
197
+ require 'live_ast'
198
+
199
+ ast_eval %{ raise "boom" }, binding
200
+ # => test.rb:3:in `<main>': boom (RuntimeError)
201
+
202
+ Let's make a slight change,
203
+
204
+ require 'live_ast'
205
+
206
+ f = ast_eval %{ lambda { raise "boom" } }, binding
207
+ f.call
208
+ # => test.rb|ast@a:3:in `block in <main>': boom (RuntimeError)
209
+
210
+ What the heck is '<code>|ast@a</code>' doing there? LiveAST's
211
+ implementation has just been exposed: each source input is assigned a
212
+ unique key which enables a Ruby object to find its own definition.
213
+
214
+ In the first case above, +ast_eval+ has removed the key from the
215
+ exception backtrace. But in the second case there is no opportunity to
216
+ remove it since +ast_eval+ has already returned.
217
+
218
+ If you find this to be problem---for example if you cannot add a
219
+ filter for the jump-to-location feature in your editor---then +raise+
220
+ may be redefined to strip these tokens,
221
+
222
+ require 'live_ast'
223
+ require 'live_ast/replace_raise'
224
+
225
+ f = ast_eval %{ lambda { raise "boom" } }, binding
226
+ f.call
227
+ # => test.rb:4:in `block in <main>': boom (RuntimeError)
228
+
229
+ However this only applies to a +raise+ call originating from Ruby
230
+ code. An exception from within a native method will likely still
231
+ contain the token in its backtrace (e.g., in MRI the exception raised
232
+ by <code>1/0</code> comes from C).
233
+
176
234
  == Replacing the Parser
177
235
 
178
236
  Despite its name, LiveAST knows nothing about ASTs. It merely reports
@@ -180,7 +238,7 @@ what it finds in the line-to-AST hash returned by the parser's +parse+
180
238
  method. Replacing the parser class is therefore easy: the only
181
239
  specification is that the +parse+ instance method return such a hash.
182
240
 
183
- To override the default parser,
241
+ To override the default parser with your own,
184
242
 
185
243
  LiveAST.parser = YourParser
186
244
 
@@ -274,9 +332,10 @@ paranoia; the noninvasive option may be the most appropriate.
274
332
 
275
333
  +ast_eval+ and +load+ cache all incoming code, while
276
334
  <code>require</code>d files are cached on a need-to-know basis. When
277
- an AST is requested, the corresponding source is parsed and discarded,
278
- leaving behind method and block ASTs. +to_ast+ removes an AST from the
279
- cache and attaches it to the appropriate object (a Proc or Module).
335
+ an AST is requested, the corresponding source file is parsed and
336
+ discarded, leaving behind method and block ASTs. +to_ast+ removes an
337
+ AST from the cache and attaches it to the appropriate object (a Proc
338
+ or Module).
280
339
 
281
340
  Ignored, unextracted ASTs will therefore linger in the cache. Since
282
341
  sexps are generally small there is little need for concern unless one
@@ -311,69 +370,6 @@ only once. Furthermore, if a file has not been reloaded then it is
311
370
  assumed that the file is unmodified between the moment it is
312
371
  <code>require</code>d and the moment the first AST is pulled from it.
313
372
 
314
- == Backtraces
315
-
316
- +ast_eval+ is meant to be compatible with +eval+. For instance the
317
- first line of +ast_eval+'s backtrace should be identical to that of
318
- +eval+:
319
-
320
- require 'live_ast'
321
-
322
- ast_eval %{ raise "boom" }, binding
323
- # => test.rb:3:in `<main>': boom (RuntimeError)
324
-
325
- Let's make a slight change,
326
-
327
- require 'live_ast'
328
-
329
- f = ast_eval %{ lambda { raise "boom" } }, binding
330
- f.call
331
- # => test.rb|ast@a:3:in `block in <main>': boom (RuntimeError)
332
-
333
- What the heck is '<code>|ast@a</code>' doing there? LiveAST's
334
- implementation has just been exposed: each source input is assigned a
335
- unique key which enables a Ruby object to find its own definition.
336
-
337
- In the first case above, +ast_eval+ has removed the key from the
338
- exception backtrace. But in the second case there is no opportunity to
339
- remove it since +ast_eval+ has already returned.
340
-
341
- If you find this to be problem---for example if you cannot add a
342
- filter for the jump-to-location feature in your editor---then +raise+
343
- may be redefined to strip these tokens,
344
-
345
- require 'live_ast'
346
- require 'live_ast/replace_raise'
347
-
348
- f = ast_eval %{ lambda { raise "boom" } }, binding
349
- f.call
350
- # => test.rb:4:in `block in <main>': boom (RuntimeError)
351
-
352
- However this only applies to a +raise+ call originating from Ruby
353
- code. An exception raised within a native method will likely still
354
- contain the token in its backtrace (e.g., in MRI the exception raised
355
- by <code>1/0</code> comes from C). In principle this could be fixed by
356
- having the Ruby interpreter dynamically call +raise+.
357
-
358
-
359
- == Replacing +eval+
360
-
361
- If +ast_eval+ did not require a binding argument then it could assume
362
- the role of +eval+, thereby making LiveAST fully transparent to the
363
- user. Is this possible in pure Ruby?
364
-
365
- The only option which has been investigated thus far is MRI, which can
366
- summon <code>Binding.of_caller</code> (recently rewritten for 1.9.2)
367
- to fill in the missing binding argument. Unfortunately a limitation
368
- with tracing events in MRI places a few odd restrictions on the syntax
369
- surrounding +eval+, though all restrictions can be trivially
370
- sidestepped. Nonetheless it does work (it passes rubyspec minus the
371
- above backtrace issue) despite being somewhat impractical.
372
-
373
- This (mis)feature is maintained in a separate branch named
374
- +replace_eval+ on github (not part of the gem). For more information see
375
- replace_eval.rb[http://github.com/quix/live_ast/blob/replace_eval/lib/live_ast/replace_eval.rb].
376
-
377
373
  == Author
378
374
 
379
375
  * James M. Lawrence < quixoticsycophant@gmail.com >
data/Rakefile CHANGED
@@ -2,11 +2,11 @@ require_relative 'devel/levitate'
2
2
 
3
3
  Levitate.new "live_ast" do |s|
4
4
  s.developers << ["James M. Lawrence", "quixoticsycophant@gmail.com"]
5
- s.github_user = "quix"
5
+ s.username = "quix"
6
6
  s.rubyforge_info = ["quix", "liveast"]
7
7
  s.camel_name = "LiveAST"
8
-
9
- s.rdoc_title = "LiveAST: Live Abstract Syntax Trees"
8
+ s.required_ruby_version = ">= 1.9.2"
9
+ s.dependencies << ["live_ast_ruby_parser", ">= 0.6.0"]
10
10
  s.rdoc_files = %w[
11
11
  lib/live_ast/ast_eval.rb
12
12
  lib/live_ast/base.rb
@@ -15,6 +15,4 @@ Levitate.new "live_ast" do |s|
15
15
  lib/live_ast/version.rb
16
16
  ]
17
17
  s.rdoc_options << "-a"
18
-
19
- s.dependencies << ["live_ast_ruby_parser", ">= 0.6.0"]
20
18
  end
data/devel/levitate.rb CHANGED
@@ -219,31 +219,18 @@ class Levitate
219
219
  }
220
220
  contents
221
221
  end
222
- end
223
-
224
- module InstanceEvalWithArgs
225
- module_function
226
222
 
227
- def with_temp_method(instance, method_name, method_block)
228
- (class << instance ; self ; end).class_eval do
229
- define_method(method_name, &method_block)
223
+ def instance_exec2(obj, *args, &block)
224
+ method_name = ["_", obj.object_id, "_", Thread.current.object_id].join
225
+ (class << obj ; self ; end).class_eval do
226
+ define_method method_name, &block
230
227
  begin
231
- yield method_name
228
+ obj.send(method_name, *args)
232
229
  ensure
233
- remove_method(method_name)
230
+ remove_method method_name
234
231
  end
235
232
  end
236
233
  end
237
-
238
- def call_temp_method(instance, method_name, *args, &method_block)
239
- with_temp_method(instance, method_name, method_block) {
240
- instance.send(method_name, *args)
241
- }
242
- end
243
-
244
- def instance_eval_with_args(instance, *args, &block)
245
- call_temp_method(instance, :__temp_method, *args, &block)
246
- end
247
234
  end
248
235
 
249
236
  include AttrLazy
@@ -296,6 +283,10 @@ class Levitate
296
283
  mod.const_get(version_constant_name)
297
284
  end or "0.0.0"
298
285
  end
286
+
287
+ attribute :required_ruby_version do
288
+ ">= 0"
289
+ end
299
290
 
300
291
  attribute :readme_file do
301
292
  "README.rdoc"
@@ -362,6 +353,10 @@ class Levitate
362
353
  []
363
354
  end
364
355
 
356
+ attribute :extra_gemspec do
357
+ lambda { |spec| }
358
+ end
359
+
365
360
  attribute :files do
366
361
  if File.file? manifest_file
367
362
  File.read(manifest_file).split("\n")
@@ -381,7 +376,7 @@ class Levitate
381
376
  end
382
377
 
383
378
  attribute :rdoc_title do
384
- "#{gem_name}: #{summary}"
379
+ "#{gem_name}: #{summary}".sub(/\.\Z/, "")
385
380
  end
386
381
 
387
382
  attribute :require_paths do
@@ -426,6 +421,8 @@ class Levitate
426
421
  rdoc_options
427
422
  extra_rdoc_files
428
423
  require_paths
424
+ required_ruby_version
425
+ extensions
429
426
  ].each do |param|
430
427
  t = send(param) and g.send("#{param}=", t)
431
428
  end
@@ -438,6 +435,7 @@ class Levitate
438
435
  development_dependencies.each { |dep|
439
436
  g.add_development_dependency(*dep)
440
437
  }
438
+ extra_gemspec.call(g)
441
439
  end
442
440
  end
443
441
 
@@ -490,22 +488,22 @@ class Levitate
490
488
  }
491
489
 
492
490
  attribute :url do
493
- "http://#{github_user}.github.com/#{gem_name}"
491
+ "http://#{username}.github.com/#{gem_name}"
494
492
  end
495
493
 
496
- attribute :github_user do
497
- raise "github_user not set"
494
+ attribute :username do
495
+ raise "username not set"
498
496
  end
499
497
 
500
498
  attribute :rubyforge_info do
501
499
  nil
502
500
  end
503
501
 
504
- attribute :authors do
502
+ def authors
505
503
  developers.map { |d| d[0] }
506
504
  end
507
505
 
508
- attribute :email do
506
+ def email
509
507
  developers.map { |d| d[1] }
510
508
  end
511
509
 
@@ -521,6 +519,10 @@ class Levitate
521
519
  []
522
520
  end
523
521
 
522
+ attribute :extensions do
523
+ ["ext/#{gem_name}/extconf.rb"].select { |f| File.file? f }
524
+ end
525
+
524
526
  def define_clean
525
527
  require 'rake/clean'
526
528
  task :clean do
@@ -800,6 +802,35 @@ class Levitate
800
802
  puts gemspec.to_ruby
801
803
  end
802
804
  end
805
+
806
+ def define_extension
807
+ unless extensions.empty?
808
+ require 'rbconfig'
809
+ require 'rake/extensiontask'
810
+
811
+ Rake::ExtensionTask.new gem_name, gemspec do |ext|
812
+ ext.cross_compile = true
813
+ ext.cross_platform = 'i386-mswin32'
814
+ ext.cross_compiling do |gemspec|
815
+ gemspec.post_install_message =
816
+ "U got dat binary versionation of this gemination!"
817
+ end
818
+ end
819
+
820
+ so = "lib/#{gem_name}.#{RbConfig::CONFIG["DLEXT"]}"
821
+ if Rake::Task[so].needed?
822
+ task :test => so
823
+ end
824
+
825
+ task :cross_native_gem do
826
+ Rake::Task[:gem].reenable
827
+ Rake.application.top_level_tasks.replace %w[cross native gem]
828
+ Rake.application.top_level
829
+ end
830
+
831
+ task :gem => :cross_native_gem
832
+ end
833
+ end
803
834
 
804
835
  def open_browser(*files)
805
836
  sh(*([browser].flatten + files))
@@ -827,7 +858,6 @@ class Levitate
827
858
 
828
859
  class << self
829
860
  include Util
830
- include InstanceEvalWithArgs
831
861
 
832
862
  # From minitest, part of the Ruby source; by Ryan Davis.
833
863
  def capture_io
@@ -867,7 +897,7 @@ class Levitate
867
897
  actual = Ruby.run_file_and_capture(temp_file.path).chomp
868
898
  }
869
899
 
870
- instance_eval_with_args(instance, expected, actual, index, &block)
900
+ instance_exec2(instance, expected, actual, index, &block)
871
901
  end
872
902
 
873
903
  def run_doc_section(file, section, instance, &block)
data/lib/live_ast/base.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'thread'
2
2
 
3
+ require 'live_ast/common'
3
4
  require 'live_ast/reader'
4
5
  require 'live_ast/evaler'
5
6
  require 'live_ast/linker'
@@ -61,5 +62,12 @@ module LiveAST
61
62
  def load(file, wrap = false) #:nodoc:
62
63
  Loader.load(file, wrap)
63
64
  end
65
+
66
+ #
67
+ # strip the revision token from a string
68
+ #
69
+ def strip_token(file) #:nodoc:
70
+ file.sub(/#{Regexp.quote Linker::REVISION_TOKEN}[a-z]+/, "")
71
+ end
64
72
  end
65
73
  end
@@ -0,0 +1,48 @@
1
+
2
+ module LiveAST
3
+ module Common
4
+ module_function
5
+
6
+ def arg_to_str(arg)
7
+ begin
8
+ arg.to_str
9
+ rescue NameError
10
+ thing = if arg.nil? then nil else arg.class end
11
+
12
+ raise TypeError, "can't convert #{thing.inspect} into String"
13
+ end
14
+ end
15
+
16
+ def check_arity(args, range)
17
+ unless range.include? args.size
18
+ range = 0 if range == (0..0)
19
+
20
+ raise ArgumentError,
21
+ "wrong number of arguments (#{args.size} for #{range})"
22
+ end
23
+ end
24
+
25
+ def check_type(obj, klass)
26
+ unless obj.is_a? klass
27
+ raise TypeError, "wrong argument type #{obj.class} (expected #{klass})"
28
+ end
29
+ end
30
+
31
+ def location_for_eval(*args)
32
+ bind, *location = args
33
+
34
+ if bind
35
+ case location.size
36
+ when 0
37
+ NATIVE_EVAL.call("[__FILE__, __LINE__]", bind)
38
+ when 1
39
+ [location.first, 1]
40
+ else
41
+ location
42
+ end
43
+ else
44
+ ["(eval)", 1]
45
+ end
46
+ end
47
+ end
48
+ end