ripper2ruby 0.0.1
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.
- 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
|
+
|