mvz-live_ast 1.1.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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.rdoc +93 -0
  3. data/README.rdoc +419 -0
  4. data/Rakefile +21 -0
  5. data/devel/levitate.rb +853 -0
  6. data/devel/levitate_config.rb +4 -0
  7. data/lib/live_ast.rb +4 -0
  8. data/lib/live_ast/ast_eval.rb +11 -0
  9. data/lib/live_ast/ast_load.rb +15 -0
  10. data/lib/live_ast/base.rb +73 -0
  11. data/lib/live_ast/common.rb +48 -0
  12. data/lib/live_ast/error.rb +20 -0
  13. data/lib/live_ast/evaler.rb +32 -0
  14. data/lib/live_ast/full.rb +2 -0
  15. data/lib/live_ast/irb_spy.rb +43 -0
  16. data/lib/live_ast/linker.rb +122 -0
  17. data/lib/live_ast/loader.rb +60 -0
  18. data/lib/live_ast/reader.rb +26 -0
  19. data/lib/live_ast/replace_eval.rb +121 -0
  20. data/lib/live_ast/replace_load.rb +14 -0
  21. data/lib/live_ast/replace_raise.rb +18 -0
  22. data/lib/live_ast/ruby_parser.rb +36 -0
  23. data/lib/live_ast/ruby_parser/test.rb +197 -0
  24. data/lib/live_ast/ruby_parser/unparser.rb +13 -0
  25. data/lib/live_ast/to_ast.rb +26 -0
  26. data/lib/live_ast/to_ruby.rb +24 -0
  27. data/lib/live_ast/version.rb +3 -0
  28. data/test/ast_eval_feature_test.rb +11 -0
  29. data/test/ast_load_feature_test.rb +11 -0
  30. data/test/attr_test.rb +24 -0
  31. data/test/backtrace_test.rb +158 -0
  32. data/test/covert_define_method_test.rb +23 -0
  33. data/test/def_test.rb +35 -0
  34. data/test/define_method_test.rb +67 -0
  35. data/test/define_singleton_method_test.rb +15 -0
  36. data/test/encoding_test.rb +52 -0
  37. data/test/encoding_test/bad.rb +1 -0
  38. data/test/encoding_test/cp932.rb +6 -0
  39. data/test/encoding_test/default.rb +5 -0
  40. data/test/encoding_test/eucjp.rb +6 -0
  41. data/test/encoding_test/koi8.rb +6 -0
  42. data/test/encoding_test/koi8_shebang.rb +7 -0
  43. data/test/encoding_test/koi8_with_utf8bom.rb +6 -0
  44. data/test/encoding_test/usascii.rb +6 -0
  45. data/test/encoding_test/usascii_with_utf8bom.rb +6 -0
  46. data/test/encoding_test/utf8.rb +6 -0
  47. data/test/encoding_test/utf8bom.rb +6 -0
  48. data/test/encoding_test/utf8bom_only.rb +5 -0
  49. data/test/encoding_test/utf8dos.rb +6 -0
  50. data/test/encoding_test/utf8mac.rb +6 -0
  51. data/test/encoding_test/utf8mac_alt.rb +6 -0
  52. data/test/encoding_test/utf8unix.rb +6 -0
  53. data/test/error_test.rb +116 -0
  54. data/test/eval_test.rb +269 -0
  55. data/test/flush_cache_test.rb +98 -0
  56. data/test/irb_test.rb +25 -0
  57. data/test/lambda_test.rb +56 -0
  58. data/test/load_path_test.rb +78 -0
  59. data/test/load_test.rb +123 -0
  60. data/test/main.rb +140 -0
  61. data/test/nested_test.rb +29 -0
  62. data/test/noninvasive_test.rb +51 -0
  63. data/test/readme_test.rb +16 -0
  64. data/test/recursive_eval_test.rb +52 -0
  65. data/test/redefine_method_test.rb +83 -0
  66. data/test/reload_test.rb +105 -0
  67. data/test/replace_eval_test.rb +405 -0
  68. data/test/rubygems_test.rb +25 -0
  69. data/test/rubyspec_test.rb +39 -0
  70. data/test/singleton_test.rb +25 -0
  71. data/test/stdlib_test.rb +13 -0
  72. data/test/thread_test.rb +44 -0
  73. data/test/to_ast_feature_test.rb +15 -0
  74. data/test/to_ruby_feature_test.rb +15 -0
  75. data/test/to_ruby_test.rb +87 -0
  76. metadata +275 -0
@@ -0,0 +1,26 @@
1
+ # encoding: us-ascii
2
+ module LiveAST
3
+ module Reader
4
+ UTF8_BOM = /\A\xef\xbb\xbf/
5
+ MAGIC_COMMENT = /\A(?:#!.*?\n)?\s*\#.*(?:en)?coding\s*[:=]\s*([^\s;]+)/
6
+
7
+ def self.read(file)
8
+ contents = File.read(file, :encoding => "BINARY")
9
+
10
+ utf8 = contents.sub!(UTF8_BOM, "") ? "UTF-8" : nil
11
+
12
+ # magic comment overrides BOM
13
+ encoding = contents[MAGIC_COMMENT, 1] || utf8 || "US-ASCII"
14
+
15
+ contents.force_encoding(strip_special(encoding))
16
+ end
17
+
18
+ def self.strip_special(encoding)
19
+ if encoding =~ /\Autf8-mac\Z/i
20
+ "UTF8-MAC"
21
+ else
22
+ encoding.sub(/-(unix|dos|mac)\Z/i, "")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,121 @@
1
+ require 'live_ast/base'
2
+ require 'binding_of_caller'
3
+
4
+ module LiveAST
5
+ module ReplaceEval
6
+ class << self
7
+ def module_or_instance_eval(which, remote_self, bind, args)
8
+ handle_args(args)
9
+
10
+ cache[:remote_self] = remote_self
11
+ cache[:args] = args
12
+
13
+ code = %{
14
+ ::LiveAST::ReplaceEval.cache[:remote_self].
15
+ live_ast_original_#{which}_eval %{
16
+ ::LiveAST.eval(
17
+ ::LiveAST::ReplaceEval.cache[:args][0],
18
+ ::Kernel.binding,
19
+ *::LiveAST::ReplaceEval.cache[:args][1..-1])
20
+ }
21
+ }
22
+
23
+ live_ast_original_eval(code, bind)
24
+ ensure
25
+ cache.clear
26
+ end
27
+
28
+ def cache
29
+ Thread.current[:_live_ast_arg_cache] ||= {}
30
+ end
31
+
32
+ def handle_args(args)
33
+ if args.empty?
34
+ raise ArgumentError, "block not supplied"
35
+ end
36
+
37
+ args[0] = Common.arg_to_str(args[0])
38
+
39
+ unless (1..3).include? args.size
40
+ raise ArgumentError,
41
+ "wrong number of arguments: instance_eval(src) or instance_eval{..}"
42
+ end
43
+
44
+ args[1] = Common.arg_to_str(args[1]) if args[1]
45
+ end
46
+ end
47
+ end
48
+
49
+ # ensure the parser is loaded -- rubygems calls eval
50
+ parser
51
+ end
52
+
53
+ # squelch alias warnings
54
+ prev_verbose = $VERBOSE
55
+ $VERBOSE = nil
56
+
57
+ module Kernel
58
+ class << self
59
+ alias_method :live_ast_original_singleton_eval, :eval
60
+
61
+ def eval(*args)
62
+ LiveAST::Common.check_arity(args, 1..4)
63
+ LiveAST.eval(
64
+ "::Kernel.live_ast_original_instance_eval do;" << args[0] << ";end",
65
+ args[1] || binding.of_caller(1),
66
+ *LiveAST::Common.location_for_eval(*args[1..3]))
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ alias_method :live_ast_original_eval, :eval
73
+
74
+ def eval(*args)
75
+ LiveAST::Common.check_arity(args, 1..4)
76
+ LiveAST.eval(
77
+ args[0],
78
+ args[1] || binding.of_caller(1),
79
+ *LiveAST::Common.location_for_eval(*args[1..3]))
80
+ end
81
+ end
82
+
83
+ class Binding
84
+ alias_method :live_ast_original_binding_eval, :eval
85
+
86
+ def eval(*args)
87
+ LiveAST.eval(args[0], self, *args[1..-1])
88
+ end
89
+ end
90
+
91
+ class BasicObject
92
+ alias_method :live_ast_original_instance_eval, :instance_eval
93
+
94
+ def instance_eval(*args, &block)
95
+ if block
96
+ live_ast_original_instance_eval(*args, &block)
97
+ else
98
+ ::LiveAST::ReplaceEval.
99
+ module_or_instance_eval(:instance, self, ::Kernel.binding.of_caller(1), args)
100
+ end
101
+ end
102
+ end
103
+
104
+ class Module
105
+ alias_method :live_ast_original_module_eval, :module_eval
106
+
107
+ def module_eval(*args, &block)
108
+ if block
109
+ live_ast_original_module_eval(*args, &block)
110
+ else
111
+ LiveAST::ReplaceEval.
112
+ module_or_instance_eval(:module, self, binding.of_caller(1), args)
113
+ end
114
+ end
115
+
116
+ remove_method :class_eval
117
+ alias_method :class_eval, :module_eval
118
+ end
119
+
120
+ # unsquelch alias warnings
121
+ $VERBOSE = prev_verbose
@@ -0,0 +1,14 @@
1
+ require 'live_ast/base'
2
+
3
+ module Kernel
4
+ alias_method :live_ast_original_load, :load
5
+
6
+ def load(file, wrap = false)
7
+ LiveAST.load(file, wrap)
8
+ end
9
+
10
+ class << self
11
+ remove_method :load
12
+ end
13
+ module_function :load
14
+ end
@@ -0,0 +1,18 @@
1
+ require 'live_ast/base'
2
+
3
+ module Kernel
4
+ private
5
+
6
+ alias_method :live_ast_original_raise, :raise
7
+
8
+ def raise(*args)
9
+ ex = begin
10
+ live_ast_original_raise(*args)
11
+ rescue Exception => t
12
+ t
13
+ end
14
+ ex.backtrace.reject! { |line| line.index __FILE__ }
15
+ ex.backtrace.map! { |line| LiveAST.strip_token line }
16
+ live_ast_original_raise ex
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ require 'ruby_parser'
2
+ require 'live_ast/base'
3
+
4
+ class LiveAST::RubyParser
5
+ #
6
+ # Returns a line-to-sexp hash where sexp corresponds to the method
7
+ # or block defined at the given line.
8
+ #
9
+ # This method is the only requirement of a LiveAST parser plugin.
10
+ #
11
+ def parse(source)
12
+ @defs = {}
13
+ process RubyParser.new.parse(source)
14
+ @defs
15
+ end
16
+
17
+ def process(sexp)
18
+ case sexp.first
19
+ when :defn, :defs, :iter
20
+ store_sexp(sexp, sexp.line)
21
+ end
22
+
23
+ sexp.each do |elem|
24
+ process(elem) if elem.is_a? Sexp
25
+ end
26
+ end
27
+
28
+ def store_sexp(sexp, line)
29
+ @defs[line] = @defs.has_key?(line) ? :multiple : sexp
30
+ end
31
+ end
32
+
33
+ LiveAST::RubyParser.autoload :Unparser, 'live_ast/ruby_parser/unparser'
34
+ LiveAST::RubyParser.autoload :Test, 'live_ast/ruby_parser/test'
35
+
36
+ LiveAST.parser = LiveAST::RubyParser
@@ -0,0 +1,197 @@
1
+
2
+ #
3
+ # Used by the LiveAST test suite.
4
+ #
5
+ module LiveAST::RubyParser::Test
6
+ class << self
7
+ #
8
+ # Whether this is Ryan Davis's unified sexp format.
9
+ #
10
+ def unified_sexp?
11
+ true
12
+ end
13
+
14
+ #
15
+ # Whether the unparser output matches that of ruby2ruby.
16
+ #
17
+ def unparser_matches_ruby2ruby?
18
+ true
19
+ end
20
+ end
21
+
22
+ #
23
+ # no_arg_def(:f, "A#f") returns the ast of
24
+ #
25
+ # def f
26
+ # "A#f"
27
+ # end
28
+ #
29
+ def no_arg_def(name, ret)
30
+ s(:defn, name, s(:args), s(:str, ret))
31
+ end
32
+
33
+ #
34
+ # singleton_no_arg_def(:f, "foo") returns the ast of
35
+ #
36
+ # def self.f
37
+ # "foo"
38
+ # end
39
+ #
40
+ def singleton_no_arg_def(name, ret)
41
+ s(:defs, s(:self), name, s(:args), s(:str, ret))
42
+ end
43
+
44
+ #
45
+ # no_arg_def_return(no_arg_def(:f, "A#f")) == "A#f"
46
+ #
47
+ def no_arg_def_return(ast)
48
+ ast[3][1]
49
+ end
50
+
51
+ #
52
+ # binop_def(:f, :+) returns the ast of
53
+ #
54
+ # def f(x, y)
55
+ # x + y
56
+ # end
57
+ #
58
+ def binop_def(name, op)
59
+ s(:defn,
60
+ name,
61
+ s(:args, :x, :y),
62
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
63
+ end
64
+
65
+ #
66
+ # singleton_binop_def(:A, :f, :+) returns the ast of
67
+ #
68
+ # def A.f(x, y)
69
+ # x + y
70
+ # end
71
+ #
72
+ def singleton_binop_def(const, name, op)
73
+ s(:defs,
74
+ s(:const, const),
75
+ name,
76
+ s(:args, :x, :y),
77
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
78
+ end
79
+
80
+ #
81
+ # binop_define_method(:f, :*) returns the ast of
82
+ #
83
+ # define_method :f do |x, y|
84
+ # x * y
85
+ # end
86
+ #
87
+ # binop_define_method(:f, :-, :my_def) returns the ast of
88
+ #
89
+ # my_def :f do |x, y|
90
+ # x - y
91
+ # end
92
+ #
93
+ def binop_define_method(name, op, using = :define_method)
94
+ s(:iter,
95
+ s(:call, nil, using, s(:lit, name)),
96
+ s(:args, :x, :y),
97
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
98
+ end
99
+
100
+ #
101
+ # binop_define_method_with_var(:method_name, :/) returns the ast of
102
+ #
103
+ # define_method method_name do |x, y|
104
+ # x / y
105
+ # end
106
+ #
107
+ def binop_define_method_with_var(var_name, op)
108
+ s(:iter,
109
+ s(:call, nil, :define_method, s(:lvar, var_name)),
110
+ s(:args, :x, :y),
111
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
112
+ end
113
+
114
+ #
115
+ # binop_define_singleton_method(:f, :+, :a) returns the ast of
116
+ #
117
+ # a.define_singleton_method :f do |x, y|
118
+ # x + y
119
+ # end
120
+ #
121
+ def binop_define_singleton_method(name, op, receiver)
122
+ s(:iter,
123
+ s(:call, s(:lvar, receiver), :define_singleton_method,
124
+ s(:lit, name)),
125
+ s(:args, :x, :y),
126
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
127
+ end
128
+
129
+ #
130
+ # no_arg_block(:foo, "bar") returns the ast of
131
+ #
132
+ # foo { "bar" }
133
+ #
134
+ def no_arg_block(name, ret)
135
+ s(:iter, s(:call, nil, name), s(:args), s(:str, ret))
136
+ end
137
+
138
+ #
139
+ # binop_block(:foo, :+) returns the ast of
140
+ #
141
+ # foo { |x, y| x + y }
142
+ #
143
+ def binop_block(name, op)
144
+ s(:iter,
145
+ s(:call, nil, name),
146
+ s(:args, :x, :y),
147
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
148
+ end
149
+
150
+ #
151
+ # binop_proc_new(:*) returns the ast of
152
+ #
153
+ # Proc.new { |x, y| x * y }
154
+ #
155
+ def binop_proc_new(op)
156
+ s(:iter,
157
+ s(:call, s(:const, :Proc), :new),
158
+ s(:args, :x, :y),
159
+ s(:call, s(:lvar, :x), op, s(:lvar, :y)))
160
+ end
161
+
162
+ #
163
+ # nested_lambdas("foo") returns the ast of
164
+ #
165
+ # lambda {
166
+ # lambda {
167
+ # "foo"
168
+ # }
169
+ # }
170
+ #
171
+ def nested_lambdas(str)
172
+ s(:iter,
173
+ s(:call, nil, :lambda),
174
+ s(:args),
175
+ s(:iter, s(:call, nil, :lambda), s(:args), s(:str, str)))
176
+ end
177
+
178
+ # nested_defs(:f, :g, "foo") returns the ast of
179
+ #
180
+ # def f
181
+ # Class.new do
182
+ # def g
183
+ # "foo"
184
+ # end
185
+ # end
186
+ # end
187
+ #
188
+ def nested_defs(u, v, str)
189
+ s(:defn,
190
+ u,
191
+ s(:args),
192
+ s(:iter,
193
+ s(:call, s(:const, :Class), :new),
194
+ s(:args),
195
+ s(:defn, v, s(:args), s(:str, str))))
196
+ end
197
+ end
@@ -0,0 +1,13 @@
1
+ require 'ruby2ruby'
2
+
3
+ #
4
+ # Used by +to_ruby+ in LiveAST.
5
+ #
6
+ module LiveAST::RubyParser::Unparser
7
+ #
8
+ # Return a ruby source string which reflects the given AST.
9
+ #
10
+ def self.unparse(sexp)
11
+ Ruby2Ruby.new.process(sexp)
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'live_ast/base'
2
+
3
+ [Method, UnboundMethod].each do |klass|
4
+ klass.class_eval do
5
+ def to_ast #:nodoc:
6
+ LiveAST::Linker.find_method_ast(owner, name, *source_location)
7
+ end
8
+ end
9
+ end
10
+
11
+ class Proc
12
+ # Extract the AST of this object.
13
+ def to_ast
14
+ LiveAST::Linker.find_proc_ast(self)
15
+ end
16
+ end
17
+
18
+ class Method
19
+ # :method: to_ast
20
+ # Extract the AST of this object.
21
+ end
22
+
23
+ class UnboundMethod
24
+ # :method: to_ast
25
+ # Extract the AST of this object.
26
+ end