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.
- checksums.yaml +7 -0
- data/CHANGES.rdoc +93 -0
- data/README.rdoc +419 -0
- data/Rakefile +21 -0
- data/devel/levitate.rb +853 -0
- data/devel/levitate_config.rb +4 -0
- data/lib/live_ast.rb +4 -0
- data/lib/live_ast/ast_eval.rb +11 -0
- data/lib/live_ast/ast_load.rb +15 -0
- data/lib/live_ast/base.rb +73 -0
- data/lib/live_ast/common.rb +48 -0
- data/lib/live_ast/error.rb +20 -0
- data/lib/live_ast/evaler.rb +32 -0
- data/lib/live_ast/full.rb +2 -0
- data/lib/live_ast/irb_spy.rb +43 -0
- data/lib/live_ast/linker.rb +122 -0
- data/lib/live_ast/loader.rb +60 -0
- data/lib/live_ast/reader.rb +26 -0
- data/lib/live_ast/replace_eval.rb +121 -0
- data/lib/live_ast/replace_load.rb +14 -0
- data/lib/live_ast/replace_raise.rb +18 -0
- data/lib/live_ast/ruby_parser.rb +36 -0
- data/lib/live_ast/ruby_parser/test.rb +197 -0
- data/lib/live_ast/ruby_parser/unparser.rb +13 -0
- data/lib/live_ast/to_ast.rb +26 -0
- data/lib/live_ast/to_ruby.rb +24 -0
- data/lib/live_ast/version.rb +3 -0
- data/test/ast_eval_feature_test.rb +11 -0
- data/test/ast_load_feature_test.rb +11 -0
- data/test/attr_test.rb +24 -0
- data/test/backtrace_test.rb +158 -0
- data/test/covert_define_method_test.rb +23 -0
- data/test/def_test.rb +35 -0
- data/test/define_method_test.rb +67 -0
- data/test/define_singleton_method_test.rb +15 -0
- data/test/encoding_test.rb +52 -0
- data/test/encoding_test/bad.rb +1 -0
- data/test/encoding_test/cp932.rb +6 -0
- data/test/encoding_test/default.rb +5 -0
- data/test/encoding_test/eucjp.rb +6 -0
- data/test/encoding_test/koi8.rb +6 -0
- data/test/encoding_test/koi8_shebang.rb +7 -0
- data/test/encoding_test/koi8_with_utf8bom.rb +6 -0
- data/test/encoding_test/usascii.rb +6 -0
- data/test/encoding_test/usascii_with_utf8bom.rb +6 -0
- data/test/encoding_test/utf8.rb +6 -0
- data/test/encoding_test/utf8bom.rb +6 -0
- data/test/encoding_test/utf8bom_only.rb +5 -0
- data/test/encoding_test/utf8dos.rb +6 -0
- data/test/encoding_test/utf8mac.rb +6 -0
- data/test/encoding_test/utf8mac_alt.rb +6 -0
- data/test/encoding_test/utf8unix.rb +6 -0
- data/test/error_test.rb +116 -0
- data/test/eval_test.rb +269 -0
- data/test/flush_cache_test.rb +98 -0
- data/test/irb_test.rb +25 -0
- data/test/lambda_test.rb +56 -0
- data/test/load_path_test.rb +78 -0
- data/test/load_test.rb +123 -0
- data/test/main.rb +140 -0
- data/test/nested_test.rb +29 -0
- data/test/noninvasive_test.rb +51 -0
- data/test/readme_test.rb +16 -0
- data/test/recursive_eval_test.rb +52 -0
- data/test/redefine_method_test.rb +83 -0
- data/test/reload_test.rb +105 -0
- data/test/replace_eval_test.rb +405 -0
- data/test/rubygems_test.rb +25 -0
- data/test/rubyspec_test.rb +39 -0
- data/test/singleton_test.rb +25 -0
- data/test/stdlib_test.rb +13 -0
- data/test/thread_test.rb +44 -0
- data/test/to_ast_feature_test.rb +15 -0
- data/test/to_ruby_feature_test.rb +15 -0
- data/test/to_ruby_test.rb +87 -0
- 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,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,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
|