ripper2ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.markdown +10 -0
- data/lib/core_ext/array/flush.rb +5 -0
- data/lib/core_ext/hash/delete_at.rb +5 -0
- data/lib/core_ext/object/meta_class.rb +5 -0
- data/lib/core_ext/object/try.rb +12 -0
- data/lib/erb/stripper.rb +48 -0
- data/lib/highlighters/ansi.rb +29 -0
- data/lib/ripper/event_log.rb +45 -0
- data/lib/ripper/ruby_builder.rb +168 -0
- data/lib/ripper/ruby_builder/buffer.rb +34 -0
- data/lib/ripper/ruby_builder/events/args.rb +40 -0
- data/lib/ripper/ruby_builder/events/array.rb +71 -0
- data/lib/ripper/ruby_builder/events/assignment.rb +55 -0
- data/lib/ripper/ruby_builder/events/block.rb +80 -0
- data/lib/ripper/ruby_builder/events/call.rb +123 -0
- data/lib/ripper/ruby_builder/events/case.rb +17 -0
- data/lib/ripper/ruby_builder/events/const.rb +47 -0
- data/lib/ripper/ruby_builder/events/for.rb +13 -0
- data/lib/ripper/ruby_builder/events/hash.rb +24 -0
- data/lib/ripper/ruby_builder/events/identifier.rb +41 -0
- data/lib/ripper/ruby_builder/events/if.rb +37 -0
- data/lib/ripper/ruby_builder/events/lexer.rb +159 -0
- data/lib/ripper/ruby_builder/events/literal.rb +47 -0
- data/lib/ripper/ruby_builder/events/method.rb +21 -0
- data/lib/ripper/ruby_builder/events/operator.rb +23 -0
- data/lib/ripper/ruby_builder/events/statements.rb +50 -0
- data/lib/ripper/ruby_builder/events/string.rb +117 -0
- data/lib/ripper/ruby_builder/events/symbol.rb +22 -0
- data/lib/ripper/ruby_builder/events/while.rb +27 -0
- data/lib/ripper/ruby_builder/queue.rb +33 -0
- data/lib/ripper/ruby_builder/stack.rb +125 -0
- data/lib/ripper/ruby_builder/token.rb +91 -0
- data/lib/ripper2ruby.rb +1 -0
- data/lib/ruby.rb +28 -0
- data/lib/ruby/aggregate.rb +71 -0
- data/lib/ruby/alternation/args.rb +25 -0
- data/lib/ruby/alternation/hash.rb +25 -0
- data/lib/ruby/alternation/list.rb +19 -0
- data/lib/ruby/args.rb +36 -0
- data/lib/ruby/array.rb +27 -0
- data/lib/ruby/assignment.rb +32 -0
- data/lib/ruby/assoc.rb +17 -0
- data/lib/ruby/block.rb +42 -0
- data/lib/ruby/call.rb +34 -0
- data/lib/ruby/case.rb +30 -0
- data/lib/ruby/const.rb +49 -0
- data/lib/ruby/for.rb +18 -0
- data/lib/ruby/hash.rb +14 -0
- data/lib/ruby/if.rb +35 -0
- data/lib/ruby/list.rb +40 -0
- data/lib/ruby/literal.rb +45 -0
- data/lib/ruby/method.rb +19 -0
- data/lib/ruby/node.rb +47 -0
- data/lib/ruby/node/composite.rb +68 -0
- data/lib/ruby/node/conversions.rb +66 -0
- data/lib/ruby/node/position.rb +35 -0
- data/lib/ruby/node/source.rb +29 -0
- data/lib/ruby/node/text.rb +121 -0
- data/lib/ruby/node/traversal.rb +82 -0
- data/lib/ruby/operator.rb +49 -0
- data/lib/ruby/params.rb +41 -0
- data/lib/ruby/statements.rb +45 -0
- data/lib/ruby/string.rb +40 -0
- data/lib/ruby/symbol.rb +27 -0
- data/lib/ruby/token.rb +51 -0
- data/lib/ruby/while.rb +32 -0
- data/test/all.rb +3 -0
- data/test/builder/stack_test.rb +67 -0
- data/test/builder/text_test.rb +118 -0
- data/test/context_test.rb +54 -0
- data/test/erb_stripper_test.rb +29 -0
- data/test/fixtures/all.rb.src +150 -0
- data/test/fixtures/source_1.rb +16 -0
- data/test/fixtures/source_2.rb +1 -0
- data/test/fixtures/stuff.rb +371 -0
- data/test/fixtures/template.html.erb +22 -0
- data/test/fixtures/tmp.rb +6 -0
- data/test/lib_test.rb +92 -0
- data/test/lib_test_helper.rb +103 -0
- data/test/libs.txt +227 -0
- data/test/nodes/args_test.rb +100 -0
- data/test/nodes/array_test.rb +141 -0
- data/test/nodes/assignment_test.rb +49 -0
- data/test/nodes/block_test.rb +125 -0
- data/test/nodes/call_test.rb +229 -0
- data/test/nodes/case_test.rb +68 -0
- data/test/nodes/comments_test.rb +25 -0
- data/test/nodes/const_test.rb +46 -0
- data/test/nodes/conversions_test.rb +9 -0
- data/test/nodes/for_test.rb +34 -0
- data/test/nodes/hash_test.rb +71 -0
- data/test/nodes/heredoc_test.rb +202 -0
- data/test/nodes/identifier_test.rb +51 -0
- data/test/nodes/if_test.rb +100 -0
- data/test/nodes/literals_test.rb +63 -0
- data/test/nodes/method_test.rb +92 -0
- data/test/nodes/namespaces_test.rb +65 -0
- data/test/nodes/node_test.rb +74 -0
- data/test/nodes/nodes_test.rb +23 -0
- data/test/nodes/operator_test.rb +241 -0
- data/test/nodes/separators_test.rb +97 -0
- data/test/nodes/statements_test.rb +70 -0
- data/test/nodes/string_test.rb +92 -0
- data/test/nodes/symbol_test.rb +57 -0
- data/test/nodes/unless_test.rb +42 -0
- data/test/nodes/until_test.rb +61 -0
- data/test/nodes/while_test.rb +71 -0
- data/test/test_helper.rb +51 -0
- data/test/traversal_test.rb +53 -0
- metadata +163 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Sven Fuchs <svenfuchs@artweb-design.de>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Ripper2Ruby
|
2
|
+
===========
|
3
|
+
|
4
|
+
Similar to ruby2ruby this library allows to parse Ruby code, modify and
|
5
|
+
recompile it back to Ruby.
|
6
|
+
|
7
|
+
Differences:
|
8
|
+
|
9
|
+
* uses Ripper for parsing (shipped with Ruby 1.9)
|
10
|
+
* produces a full object-oriented representation of the Ruby code
|
data/lib/erb/stripper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# replaces html and erb tags with whitespace so that we can parse the result
|
2
|
+
# as pure ruby preserving the exact positions of tokens in the original erb
|
3
|
+
# source code
|
4
|
+
|
5
|
+
require 'erb'
|
6
|
+
$KCODE = 'u' if RUBY_VERSION < '1.9'
|
7
|
+
|
8
|
+
module Erb
|
9
|
+
class Scanner < ERB::Compiler::Scanner
|
10
|
+
def scan
|
11
|
+
stag_reg = /(.*?)(^[ \t]*<%%|<%%=|<%=|<%#|<%-|<%|\z)/m
|
12
|
+
etag_reg = /(.*?)(%%>|\-%>|%>|\z)/m
|
13
|
+
scanner = StringScanner.new(@src)
|
14
|
+
while !scanner.eos?
|
15
|
+
scanner.scan(@stag ? etag_reg : stag_reg)
|
16
|
+
yield(scanner[1]) unless scanner[1].nil?
|
17
|
+
yield(scanner[2]) unless scanner[2].nil?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
ERB::Compiler::Scanner.regist_scanner(Scanner, nil, false)
|
22
|
+
|
23
|
+
class Stripper
|
24
|
+
def to_ruby(source)
|
25
|
+
result = ''
|
26
|
+
comment = false
|
27
|
+
scanner = ERB::Compiler.new(nil).make_scanner(source)
|
28
|
+
scanner.scan do |token|
|
29
|
+
comment = true if token == '<%#'
|
30
|
+
if scanner.stag.nil?
|
31
|
+
result << to_whitespace(token)
|
32
|
+
scanner.stag = token if ['<%', '<%%', '<%-', '<%=', '<%%=', '<%#'].include?(token.strip)
|
33
|
+
elsif ['%>', '%%>', '-%>'].include?(token.strip)
|
34
|
+
result << to_whitespace(token.gsub(/>/, ';'))
|
35
|
+
scanner.stag = nil
|
36
|
+
else
|
37
|
+
result << (comment ? to_whitespace(token) : token)
|
38
|
+
comment = false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_whitespace(str)
|
45
|
+
str.gsub(/[^\s;]/, ' ')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Highlighters
|
2
|
+
class Ansi
|
3
|
+
COLORS = { :red =>";31", :yellow => ";33", :green => ";32" }
|
4
|
+
STYLES = { :bold => ";1", :underline => ";4" }
|
5
|
+
|
6
|
+
def initialize(*formats)
|
7
|
+
@formats = formats
|
8
|
+
end
|
9
|
+
|
10
|
+
def highlight(text)
|
11
|
+
ansi_format(text, @formats)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ansi_format(text, formats)
|
15
|
+
res = "\e[0"
|
16
|
+
if formats.is_a?(Array) || formats.is_a?(Hash)
|
17
|
+
COLORS.each { |k,v| res += v if formats.include?(k) }
|
18
|
+
STYLES.each { |k,v| res += v if formats.include?(k) }
|
19
|
+
elsif formats.is_a?(Symbol)
|
20
|
+
COLORS.each { |k,v| res += v if formats == k }
|
21
|
+
STYLES.each { |k,v| res += v if formats == k }
|
22
|
+
elsif formats.respond_to?(:to_sym)
|
23
|
+
COLORS.each { |k,v| res += v if formats.to_sym == k }
|
24
|
+
STYLES.each { |k,v| res += v if formats.to_sym == k }
|
25
|
+
end
|
26
|
+
res += "m" + text.to_s + "\e[0m"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require 'highlighters/ansi'
|
3
|
+
|
4
|
+
class Ripper
|
5
|
+
class EventLog < Ripper::SexpBuilder
|
6
|
+
class << self
|
7
|
+
def out(src)
|
8
|
+
parser = new(src)
|
9
|
+
parser.parse
|
10
|
+
parser.out
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :log
|
15
|
+
|
16
|
+
def initialize(src)
|
17
|
+
@log = []
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def out
|
22
|
+
log.each do |type, sexp|
|
23
|
+
arg = sexp[1] =~ /\s/ ? sexp[1].inspect : sexp[1]
|
24
|
+
line = (sexp[0].to_s).ljust(20)
|
25
|
+
if type == :scanner
|
26
|
+
puts line + arg[0..30]
|
27
|
+
else
|
28
|
+
puts highlight(line)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def highlight(str)
|
34
|
+
Highlighters::Ansi.new(:bold, :green).highlight(str)
|
35
|
+
end
|
36
|
+
|
37
|
+
{ :scanner => SCANNER_EVENTS, :parser => PARSER_EVENTS }.each do |type, events|
|
38
|
+
events.each do |event|
|
39
|
+
define_method :"on_#{event}" do |*args|
|
40
|
+
log << [type, super(*args)]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require 'ruby'
|
3
|
+
|
4
|
+
require 'core_ext/hash/delete_at'
|
5
|
+
require 'core_ext/array/flush'
|
6
|
+
|
7
|
+
require 'ripper/ruby_builder/token'
|
8
|
+
require 'ripper/ruby_builder/stack'
|
9
|
+
|
10
|
+
Dir[File.dirname(__FILE__) + '/ruby_builder/events/*.rb'].each { |file| require file }
|
11
|
+
|
12
|
+
# Ripper::RubyBuilder extends Ripper's SexpBuilder and builds a rich, object
|
13
|
+
# oriented representation of Ruby code.
|
14
|
+
#
|
15
|
+
# code = Ripper::RubyBuilder.build("foo(1, :bar, %w(baz)"), filename)
|
16
|
+
# code.to_ruby # => "foo(1, :bar, %w(baz)"
|
17
|
+
#
|
18
|
+
# RubyBuilder uses SexpBuilder's lexing and parsing event callbacks (see
|
19
|
+
# ruby_builder/events) and builds up Ruby::Nodes which can then be used.
|
20
|
+
# See RubyBuilder::Stack, RubyBuilder::Queue, RubyBuilder::Buffer and
|
21
|
+
# RubyBuilder::Token for more details about the parsing process.
|
22
|
+
|
23
|
+
class Ripper
|
24
|
+
class RubyBuilder < Ripper::SexpBuilder
|
25
|
+
class ParseError < RuntimeError
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def build(src, filename = nil)
|
30
|
+
new(src, filename).parse
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
NEWLINE = [:@nl, :@ignored_nl]
|
35
|
+
WHITESPACE = [:@sp, :@comment] + NEWLINE
|
36
|
+
OPENERS = [:@lparen, :@lbracket, :@lbrace, :@class, :@module, :@def, :@begin, :@while, :@until,
|
37
|
+
:@for, :@if, :@elsif, :@else, :@unless, :@case, :@when, :@embexpr_beg, :@do, :@rescue,
|
38
|
+
:'@=', :'@::']
|
39
|
+
KEYWORDS = [:@alias, :@and, :@BEGIN, :@begin, :@break, :@case, :@class, :@def, :@defined,
|
40
|
+
:@do, :@else, :@elsif, :@END, :@end, :@ensure, :@false, :@for, :@if, :@in,
|
41
|
+
:@module, :@next, :@nil, :@not, :@or, :@redo, :@rescue, :@retry, :@return,
|
42
|
+
:@self, :@super, :@then, :@true, :@undef, :@unless, :@until, :@when, :@while,
|
43
|
+
:@yield]
|
44
|
+
|
45
|
+
SEPARATORS = [:@semicolon, :@comma]
|
46
|
+
|
47
|
+
UNARY_OPERATORS = [:'@+', :'@-', :'@!', :'@~', :@not, :'@+@', :'@-@']
|
48
|
+
BINARY_OPERATORS = [:'@**', :'@*', :'@/', :'@%', :'@+', :'@-', :'@<<', :'@>>', :'@&', :'@|', :'@^',
|
49
|
+
:'@>', :'@>=', :'@<', :'@<=', :'@<=>', :'@==', :'@===', :'@!=', :'@=~', :'@!~',
|
50
|
+
:'@&&', :'@||', :@and, :@or]
|
51
|
+
TERNARY_OPERATORS = [:'@?', :'@:']
|
52
|
+
ASSIGN_OPERATORS = [:'@=', :'@+=', :'@-=', :'@*=', :'@**=', :'@%=', :'@/=', :'@|=', :'@&=', :'@^=',
|
53
|
+
:'@[]=', :'@||=', :'@&&=', :'@<<=', :'@>>=']
|
54
|
+
ACCESS_OPERATORS = [:'@[]']
|
55
|
+
|
56
|
+
OPERATORS = UNARY_OPERATORS + BINARY_OPERATORS + TERNARY_OPERATORS + ASSIGN_OPERATORS + ACCESS_OPERATORS
|
57
|
+
|
58
|
+
|
59
|
+
include Lexer, Statements, Const, Method, Call, Block, Args, Assignment, Operator,
|
60
|
+
If, Case, For, While, Identifier, Literal, String, Symbol, Array, Hash
|
61
|
+
|
62
|
+
attr_reader :src, :filename, :stack, :string_stack, :trailing_whitespace
|
63
|
+
|
64
|
+
def initialize(src, filename = nil, lineno = nil)
|
65
|
+
@src = src ||= filename && File.read(filename) || ''
|
66
|
+
@src.gsub!(/([\s\n]*)\Z/) { |s| @trailing_whitespace = Ruby::Whitespace.new(s) and nil }
|
67
|
+
|
68
|
+
@filename = filename
|
69
|
+
@stack = []
|
70
|
+
@stack = Stack.new
|
71
|
+
@string_stack = []
|
72
|
+
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def position
|
78
|
+
Ruby::Node::Position.new(lineno.to_i - 1, column)
|
79
|
+
end
|
80
|
+
|
81
|
+
def prolog
|
82
|
+
Ruby::Prolog.new(stack.buffer.flush)
|
83
|
+
end
|
84
|
+
|
85
|
+
def push(sexp = nil)
|
86
|
+
token = Token.new(sexp[0], sexp[1], position) if sexp.is_a?(::Array)
|
87
|
+
stack.push(token)
|
88
|
+
token
|
89
|
+
end
|
90
|
+
|
91
|
+
def pop(*args)
|
92
|
+
stack.pop(*args)
|
93
|
+
end
|
94
|
+
|
95
|
+
def pop_token(*types)
|
96
|
+
options = types.last.is_a?(::Hash) ? types.pop : {}
|
97
|
+
options[:max] = 1
|
98
|
+
pop_tokens(*types << options).first
|
99
|
+
end
|
100
|
+
|
101
|
+
def pop_tokens(*types)
|
102
|
+
pop(*types).map { |token| build_token(token) }.flatten.compact
|
103
|
+
end
|
104
|
+
|
105
|
+
def pop_identifier(type, options = {})
|
106
|
+
pop_token(type, options).to_identifier
|
107
|
+
end
|
108
|
+
|
109
|
+
def pop_string_content
|
110
|
+
pop_token(:@tstring_content).to_string_content
|
111
|
+
end
|
112
|
+
|
113
|
+
def pop_operator(options = {})
|
114
|
+
pop_token(*OPERATORS, options)
|
115
|
+
end
|
116
|
+
|
117
|
+
def pop_unary_operator(options = {})
|
118
|
+
pop_token(*UNARY_OPERATORS, options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def pop_binary_operator(options = {})
|
122
|
+
pop_token(*BINARY_OPERATORS, options)
|
123
|
+
end
|
124
|
+
|
125
|
+
def pop_ternary_operator(options = {})
|
126
|
+
pop_token(*TERNARY_OPERATORS, options)
|
127
|
+
end
|
128
|
+
|
129
|
+
def pop_assignment_operator(options = {})
|
130
|
+
pop_token(*ASSIGN_OPERATORS, options)
|
131
|
+
end
|
132
|
+
|
133
|
+
def pop_end_data
|
134
|
+
token = pop_token(:@__end__)
|
135
|
+
token.token = Ruby::Node::Text::Clip.new(src, token.position).to_s if token # TODO clean up
|
136
|
+
token
|
137
|
+
end
|
138
|
+
|
139
|
+
def build_token(token)
|
140
|
+
Ruby::Token.new(token.token, token.position, token.prolog) if token
|
141
|
+
end
|
142
|
+
|
143
|
+
def build_keyword(token)
|
144
|
+
klass = Ruby.const_get(token.token[0].upcase + token.token[1..-1])
|
145
|
+
klass.new(token, token.position, token.prolog)
|
146
|
+
rescue NameError
|
147
|
+
Ruby::Keyword.new(token, token.position, token.prolog)
|
148
|
+
end
|
149
|
+
|
150
|
+
def build_xstring(token)
|
151
|
+
case token.type
|
152
|
+
when :@symbeg
|
153
|
+
Ruby::DynaSymbol.new(nil, build_token(token))
|
154
|
+
when :@regexp_beg
|
155
|
+
Ruby::Regexp.new(nil, build_token(token))
|
156
|
+
else
|
157
|
+
Ruby::ExecutableString.new(nil, build_token(token))
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# def extract_src(from, to)
|
162
|
+
# lines = Ruby::Node::Text.split(src)
|
163
|
+
# lines[from.row] = lines[from.row][from.col..-1] # || ''
|
164
|
+
# lines[to.row] = lines[to.row][0, to.col]
|
165
|
+
# lines[from.row..to.row].join
|
166
|
+
# end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# When tokens are pushed to the stack they may first be buffered when they
|
2
|
+
# belong to the Prolog part of a Ruby::Node. Buffered tokens will then be
|
3
|
+
# aggregated to the Prolog of the next token that is not buffered. Tokens
|
4
|
+
# belonging to the Prolog part of a Ruby::Node are whitespace, separator and
|
5
|
+
# heredoc tokens.
|
6
|
+
#
|
7
|
+
# E.g. when a whitespace char (" ") is pushed to the stack it will be buffered.
|
8
|
+
# Then when an :@ident token is pushed to the stack the contents of the buffer
|
9
|
+
# will be assigned to the Prolog of the :@ident token. Thus the whitespace char
|
10
|
+
# ends up in the Prolog of the :@ident token.
|
11
|
+
|
12
|
+
class Ripper
|
13
|
+
class RubyBuilder < Ripper::SexpBuilder
|
14
|
+
class Buffer < Array
|
15
|
+
def aggregate(token)
|
16
|
+
if token.nil?
|
17
|
+
false
|
18
|
+
elsif token.whitespace?
|
19
|
+
self << Ruby::Whitespace.new(token.token, token.position)
|
20
|
+
true
|
21
|
+
elsif token.separator?
|
22
|
+
self << Ruby::Token.new(token.token, token.position)
|
23
|
+
true
|
24
|
+
elsif token.heredoc?
|
25
|
+
self << token.token
|
26
|
+
true
|
27
|
+
else
|
28
|
+
token.prolog = Ruby::Prolog.new(flush) unless empty?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Ripper
|
2
|
+
class RubyBuilder < Ripper::SexpBuilder
|
3
|
+
module Args
|
4
|
+
def on_method_add_arg(call, args)
|
5
|
+
call.arguments = args
|
6
|
+
call
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_arg_paren(args)
|
10
|
+
args ||= Ruby::ArgsList.new
|
11
|
+
args.rdelim ||= pop_token(:@rparen)
|
12
|
+
args.ldelim ||= pop_token(:@lparen)
|
13
|
+
args
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_args_add_block(args, block)
|
17
|
+
args << Ruby::Arg.new(block, pop_token(:'@&')) if block
|
18
|
+
args
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_args_add_star(args, arg)
|
22
|
+
args << Ruby::Arg.new(arg, pop_token(:'@*', :pass => true))
|
23
|
+
args
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_args_add(args, arg)
|
27
|
+
args << arg
|
28
|
+
args
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_blockarg(identifier)
|
32
|
+
Ruby::Arg.new(identifier, pop_token(:'@&'))
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_args_new
|
36
|
+
Ruby::ArgsList.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Ripper
|
2
|
+
class RubyBuilder < Ripper::SexpBuilder
|
3
|
+
module Array
|
4
|
+
def on_array(args)
|
5
|
+
rdelim = pop_token(:@rbracket)
|
6
|
+
ldelim = pop_token(:@lbracket)
|
7
|
+
args ? args.to_array(ldelim, rdelim) : Ruby::Array.new(nil, ldelim, rdelim)
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_words_new(*args)
|
11
|
+
rdelim = pop_token(:@words_end)
|
12
|
+
ldelim = pop_token(:@words_beg)
|
13
|
+
words = Ruby::Array.new(nil, ldelim, rdelim)
|
14
|
+
string_stack << words
|
15
|
+
words
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_words_add(array, word)
|
19
|
+
array.rdelim ||= pop_token(:@words_end)
|
20
|
+
array << word
|
21
|
+
array
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_qwords_new(*args)
|
25
|
+
rdelim = pop_token(:@words_end)
|
26
|
+
ldelim = pop_token(:@qwords_beg)
|
27
|
+
words = Ruby::Array.new(nil, ldelim, rdelim)
|
28
|
+
string_stack << words unless rdelim
|
29
|
+
words
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_qwords_add(array, word)
|
33
|
+
word = on_word_add(on_word_new, word) # simulating missing on_word_new and on_word_add events
|
34
|
+
array.rdelim ||= pop_token(:@words_end)
|
35
|
+
array << word
|
36
|
+
array
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_words_end(rdelim = nil)
|
40
|
+
words = string_stack.pop
|
41
|
+
words.rdelim ||= pop_token(:@tstring_end, :@words_sep)
|
42
|
+
words
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_aref(target, args)
|
46
|
+
args ||= Ruby::ArgsList.new
|
47
|
+
args.ldelim ||= pop_token(:@lbracket, :left => target)
|
48
|
+
args.rdelim ||= pop_token(:@rbracket, :reverse => true, :pass => true, :left => args.ldelim)
|
49
|
+
Ruby::Call.new(target, nil, nil, args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_aref_field(target, args)
|
53
|
+
args ||= Ruby::ArgsList.new
|
54
|
+
args.ldelim ||= pop_token(:@lbracket, :left => target)
|
55
|
+
args.rdelim ||= pop_token(:@rbracket, :reverse => true, :pass => true, :left => args.ldelim)
|
56
|
+
Ruby::Call.new(target, nil, nil, args)
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
WORD_DELIMITER_MAP = { '(' => ')', '[' => ']', '{' => '}' }
|
62
|
+
|
63
|
+
def closes_words?(token)
|
64
|
+
return false unless string_stack.last.is_a?(Ruby::Array)
|
65
|
+
return false unless string_stack.last.ldelim.token =~ /^%w\s*([^\s]*)/i
|
66
|
+
(WORD_DELIMITER_MAP[$1] || $1) == token.gsub(/[%w\s]/i, '')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|