unparser 0.1.7 → 0.1.8
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 +4 -4
- data/.rubocop.yml +7 -0
- data/.travis.yml +3 -0
- data/Changelog.md +4 -0
- data/README.md +4 -2
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +24 -19
- data/config/rubocop.yml +2 -3
- data/lib/unparser.rb +8 -22
- data/lib/unparser/ast.rb +232 -0
- data/lib/unparser/ast/local_variable_scope.rb +198 -0
- data/lib/unparser/cli.rb +41 -24
- data/lib/unparser/cli/differ.rb +38 -16
- data/lib/unparser/cli/source.rb +46 -17
- data/lib/unparser/constants.rb +23 -6
- data/lib/unparser/emitter.rb +32 -0
- data/lib/unparser/emitter/argument.rb +30 -4
- data/lib/unparser/emitter/assignment.rb +12 -1
- data/lib/unparser/emitter/begin.rb +23 -2
- data/lib/unparser/emitter/case.rb +1 -1
- data/lib/unparser/emitter/class.rb +1 -0
- data/lib/unparser/emitter/def.rb +28 -1
- data/lib/unparser/emitter/defined.rb +3 -1
- data/lib/unparser/emitter/flow_modifier.rb +63 -0
- data/lib/unparser/emitter/if.rb +44 -0
- data/lib/unparser/emitter/literal/dynamic.rb +25 -1
- data/lib/unparser/emitter/literal/hash.rb +3 -3
- data/lib/unparser/emitter/literal/primitive.rb +9 -47
- data/lib/unparser/emitter/literal/regexp.rb +5 -16
- data/lib/unparser/emitter/module.rb +1 -0
- data/lib/unparser/emitter/repetition.rb +52 -0
- data/lib/unparser/emitter/resbody.rb +4 -2
- data/lib/unparser/emitter/rescue.rb +12 -2
- data/lib/unparser/emitter/root.rb +2 -11
- data/lib/unparser/emitter/send.rb +19 -2
- data/lib/unparser/emitter/send/index.rb +42 -4
- data/lib/unparser/emitter/send/unary.rb +4 -0
- data/lib/unparser/emitter/undef.rb +1 -3
- data/lib/unparser/node_helpers.rb +13 -1
- data/lib/unparser/preprocessor.rb +226 -0
- data/lib/unparser/strip_helper.rb +23 -0
- data/rubyspec.sh +20 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/unparser_spec.rb +390 -151
- data/unparser.gemspec +1 -1
- metadata +27 -24
- data/lib/unparser/cli/preprocessor.rb +0 -197
- data/lib/unparser/emitter/break.rb +0 -27
- data/lib/unparser/emitter/next.rb +0 -28
- data/lib/unparser/emitter/return.rb +0 -41
@@ -0,0 +1,198 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Unparser
|
4
|
+
module AST
|
5
|
+
# Local variable scope enumerator
|
6
|
+
class LocalVariableScope
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
RESET_NODES = [:module, :class, :sclass, :def, :defs].freeze
|
10
|
+
INHERIT_NODES = [:block].freeze
|
11
|
+
CLOSE_NODES = (RESET_NODES + INHERIT_NODES).freeze
|
12
|
+
|
13
|
+
# Nodes that assign a local variable
|
14
|
+
#
|
15
|
+
# FIXME: Kwargs are missing.
|
16
|
+
#
|
17
|
+
ASSIGN_NODES = [:lvasgn, :arg, :optarg, :restarg].freeze
|
18
|
+
|
19
|
+
# Initialize object
|
20
|
+
#
|
21
|
+
# @return [undefined]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
def initialize
|
26
|
+
@stack = [Set.new]
|
27
|
+
end
|
28
|
+
|
29
|
+
# Enumerate each node with its local variable scope
|
30
|
+
#
|
31
|
+
# @param [Parser::AST::Node] node
|
32
|
+
#
|
33
|
+
# @return [self]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def self.each(node, &block)
|
38
|
+
new.each(node, &block)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Test for local variable inherited scope reset
|
43
|
+
#
|
44
|
+
# @param [Parser::AST::Node] node
|
45
|
+
#
|
46
|
+
# @return [true]
|
47
|
+
# if local variable scope must NOT be reset
|
48
|
+
#
|
49
|
+
# @return [false]
|
50
|
+
# otherwise
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
def self.not_close_scope?(node)
|
55
|
+
!CLOSE_NODES.include?(node.type)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Test for local variable scope reset
|
59
|
+
#
|
60
|
+
# @param [Parser::AST::Node] node
|
61
|
+
#
|
62
|
+
# @return [true]
|
63
|
+
# if local variable scope must NOT be reset
|
64
|
+
#
|
65
|
+
# @return [false]
|
66
|
+
# otherwise
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
#
|
70
|
+
def self.not_reset_scope?(node)
|
71
|
+
!RESET_NODES.include?(node.type)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Enumerate local variable scope scope
|
75
|
+
#
|
76
|
+
# @return [self]
|
77
|
+
# if block given
|
78
|
+
#
|
79
|
+
# @return [Enumerator<Array<Symbol>>>]
|
80
|
+
# otherwise
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
#
|
84
|
+
def each(node, &block)
|
85
|
+
return to_enum(__method__, node) unless block_given?
|
86
|
+
visit(node, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Return current set of local variables
|
92
|
+
#
|
93
|
+
# @return [Set<Symbol>]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
#
|
97
|
+
def current
|
98
|
+
@stack.last
|
99
|
+
end
|
100
|
+
|
101
|
+
# Visit node and record local variable state
|
102
|
+
#
|
103
|
+
# @param [Parser::AST::Node]
|
104
|
+
#
|
105
|
+
# @return [undefined]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
def visit(node, &block)
|
110
|
+
before = current.dup
|
111
|
+
enter(node)
|
112
|
+
yield node, current, before
|
113
|
+
node.children.each do |child|
|
114
|
+
if child.kind_of?(Parser::AST::Node)
|
115
|
+
visit(child, &block)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
leave(node)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Record local variable state
|
122
|
+
#
|
123
|
+
# @param [Parser::AST::Node]
|
124
|
+
#
|
125
|
+
# @return [undefined]
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
#
|
129
|
+
def enter(node)
|
130
|
+
case node.type
|
131
|
+
when *RESET_NODES
|
132
|
+
push_reset
|
133
|
+
when *ASSIGN_NODES
|
134
|
+
define(node.children.first)
|
135
|
+
when *INHERIT_NODES
|
136
|
+
push_inherit
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Pop from local variable state
|
141
|
+
#
|
142
|
+
# @param [Parser::AST::Node] node
|
143
|
+
#
|
144
|
+
# @return [undefined]
|
145
|
+
#
|
146
|
+
# @api private
|
147
|
+
#
|
148
|
+
def leave(node)
|
149
|
+
if CLOSE_NODES.include?(node.type)
|
150
|
+
pop
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Define a local variable on current stack
|
155
|
+
#
|
156
|
+
# @param [Symbol] name
|
157
|
+
#
|
158
|
+
# @return [undefined]
|
159
|
+
#
|
160
|
+
# @api private
|
161
|
+
#
|
162
|
+
def define(name)
|
163
|
+
current << name
|
164
|
+
end
|
165
|
+
|
166
|
+
# Push reset scope on stack
|
167
|
+
#
|
168
|
+
# @return [undefined]
|
169
|
+
#
|
170
|
+
# @api private
|
171
|
+
#
|
172
|
+
def push_reset
|
173
|
+
@stack << Set.new
|
174
|
+
end
|
175
|
+
|
176
|
+
# Push inherited lvar scope on stack
|
177
|
+
#
|
178
|
+
# @return [undefined]
|
179
|
+
#
|
180
|
+
# @api private
|
181
|
+
#
|
182
|
+
def push_inherit
|
183
|
+
@stack << current.dup
|
184
|
+
end
|
185
|
+
|
186
|
+
# Pop lvar scope from stack
|
187
|
+
#
|
188
|
+
# @return [undefined]
|
189
|
+
#
|
190
|
+
# @api private
|
191
|
+
#
|
192
|
+
def pop
|
193
|
+
@stack.pop
|
194
|
+
end
|
195
|
+
|
196
|
+
end # LocalVariableScope
|
197
|
+
end # AST
|
198
|
+
end # Unparser
|
data/lib/unparser/cli.rb
CHANGED
@@ -5,7 +5,6 @@ require 'optparse'
|
|
5
5
|
require 'diff/lcs'
|
6
6
|
require 'diff/lcs/hunk'
|
7
7
|
|
8
|
-
require 'unparser/cli/preprocessor'
|
9
8
|
require 'unparser/cli/source'
|
10
9
|
require 'unparser/cli/differ'
|
11
10
|
require 'unparser/cli/color'
|
@@ -39,7 +38,7 @@ module Unparser
|
|
39
38
|
# @api private
|
40
39
|
#
|
41
40
|
def initialize(arguments)
|
42
|
-
@sources = []
|
41
|
+
@sources, @ignore = [], Set.new
|
43
42
|
|
44
43
|
@success = true
|
45
44
|
@fail_fast = false
|
@@ -48,14 +47,8 @@ module Unparser
|
|
48
47
|
add_options(builder)
|
49
48
|
end
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
arguments.each do |name|
|
54
|
-
if File.directory?(name)
|
55
|
-
add_directory(name)
|
56
|
-
else
|
57
|
-
add_file(name)
|
58
|
-
end
|
50
|
+
opts.parse!(arguments).each do |name|
|
51
|
+
@sources.concat(sources(name))
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
@@ -73,6 +66,12 @@ module Unparser
|
|
73
66
|
builder.on('-e', '--evaluate SOURCE') do |original_source|
|
74
67
|
@sources << Source::String.new(original_source)
|
75
68
|
end
|
69
|
+
builder.on('--start-with FILE') do |file|
|
70
|
+
@start_with = sources(file).first
|
71
|
+
end
|
72
|
+
builder.on('--ignore FILE') do |file|
|
73
|
+
@ignore.merge(sources(file))
|
74
|
+
end
|
76
75
|
builder.on('--fail-fast') do
|
77
76
|
@fail_fast = true
|
78
77
|
end
|
@@ -85,7 +84,8 @@ module Unparser
|
|
85
84
|
# @api private
|
86
85
|
#
|
87
86
|
def exit_status
|
88
|
-
|
87
|
+
effective_sources.each do |source|
|
88
|
+
next if @ignore.include?(source)
|
89
89
|
process_source(source)
|
90
90
|
if @fail_fast
|
91
91
|
break unless @success
|
@@ -115,30 +115,47 @@ module Unparser
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
#
|
118
|
+
# Return effective sources
|
119
119
|
#
|
120
|
-
# @
|
121
|
-
#
|
122
|
-
# @return [undefined]
|
120
|
+
# @return [Enumerable<CLI::Source>]
|
123
121
|
#
|
124
122
|
# @api private
|
125
123
|
#
|
126
|
-
def
|
127
|
-
@
|
124
|
+
def effective_sources
|
125
|
+
if @start_with
|
126
|
+
reject = true
|
127
|
+
@sources.reject do |source|
|
128
|
+
if reject && source == @start_with
|
129
|
+
reject = false
|
130
|
+
end
|
131
|
+
|
132
|
+
reject
|
133
|
+
end
|
134
|
+
else
|
135
|
+
@sources
|
136
|
+
end
|
128
137
|
end
|
129
138
|
|
130
|
-
#
|
139
|
+
# Return sources for file name
|
131
140
|
#
|
132
|
-
# @param [String]
|
141
|
+
# @param [String] file_name
|
133
142
|
#
|
134
|
-
# @return [
|
143
|
+
# @return [Enumerable<CLI::Source>]
|
135
144
|
#
|
136
145
|
# @api private
|
137
146
|
#
|
138
|
-
def
|
139
|
-
|
140
|
-
|
141
|
-
|
147
|
+
def sources(file_name)
|
148
|
+
files =
|
149
|
+
case
|
150
|
+
when File.directory?(file_name)
|
151
|
+
Dir.glob(File.join(file_name, '**/*.rb')).sort
|
152
|
+
when File.file?(file_name)
|
153
|
+
[file_name]
|
154
|
+
else
|
155
|
+
Dir.glob(file_name).sort
|
156
|
+
end
|
157
|
+
|
158
|
+
files.map(&Source::File.method(:new))
|
142
159
|
end
|
143
160
|
|
144
161
|
end # CLI
|
data/lib/unparser/cli/differ.rb
CHANGED
@@ -6,6 +6,41 @@ module Unparser
|
|
6
6
|
class Differ
|
7
7
|
include Adamantium::Flat, Concord.new(:old, :new), Procto.call(:colorized_diff)
|
8
8
|
|
9
|
+
CONTEXT_LINES = 5
|
10
|
+
|
11
|
+
# Return hunks
|
12
|
+
#
|
13
|
+
# @return [Array<Diff::LCS::Hunk>]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
def hunks
|
18
|
+
file_length_difference = new.length - old.length
|
19
|
+
diffs.map do |piece|
|
20
|
+
hunk = Diff::LCS::Hunk.new(old, new, piece, CONTEXT_LINES, file_length_difference)
|
21
|
+
file_length_difference = hunk.file_length_difference
|
22
|
+
hunk
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return collapsed hunks
|
27
|
+
#
|
28
|
+
# @return [Enumerable<Diff::LCS::Hunk>]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
def collapsed_hunks
|
33
|
+
hunks.each_with_object([]) do |hunk, output|
|
34
|
+
last = output.last
|
35
|
+
|
36
|
+
if last && hunk.merge(last)
|
37
|
+
output.pop
|
38
|
+
end
|
39
|
+
|
40
|
+
output << hunk
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
9
44
|
# Return source diff
|
10
45
|
#
|
11
46
|
# @return [String]
|
@@ -17,24 +52,11 @@ module Unparser
|
|
17
52
|
# @api private
|
18
53
|
#
|
19
54
|
def diff
|
20
|
-
output =
|
21
|
-
lines = 5
|
22
|
-
hunk = oldhunk = nil
|
23
|
-
file_length_difference = new.length - old.length
|
24
|
-
diffs.each do |piece|
|
25
|
-
begin
|
26
|
-
hunk = Diff::LCS::Hunk.new(old, new, piece, lines, file_length_difference)
|
27
|
-
file_length_difference = hunk.file_length_difference
|
55
|
+
output = ''
|
28
56
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
output << oldhunk.diff(:unified) << "\n"
|
33
|
-
ensure
|
34
|
-
oldhunk = hunk
|
35
|
-
end
|
57
|
+
collapsed_hunks.each do |hunk|
|
58
|
+
output << hunk.diff(:unified) << "\n"
|
36
59
|
end
|
37
|
-
output << oldhunk.diff(:unified) << "\n"
|
38
60
|
|
39
61
|
output
|
40
62
|
end
|
data/lib/unparser/cli/source.rb
CHANGED
@@ -26,15 +26,17 @@ module Unparser
|
|
26
26
|
# @api private
|
27
27
|
#
|
28
28
|
def error_report
|
29
|
-
|
29
|
+
case
|
30
|
+
when original_ast && generated_ast
|
30
31
|
error_report_with_ast_diff
|
31
|
-
|
32
|
-
|
32
|
+
when !original_ast
|
33
|
+
error_report_original
|
34
|
+
when !generated_ast
|
35
|
+
error_report_generated
|
33
36
|
end
|
34
37
|
end
|
35
38
|
memoize :error_report
|
36
39
|
|
37
|
-
|
38
40
|
private
|
39
41
|
|
40
42
|
# Return generated source
|
@@ -48,21 +50,33 @@ module Unparser
|
|
48
50
|
end
|
49
51
|
memoize :generated_source
|
50
52
|
|
51
|
-
# Return error report
|
53
|
+
# Return error report for parsing original
|
52
54
|
#
|
53
55
|
# @return [String]
|
54
56
|
#
|
55
57
|
# @api private
|
56
58
|
#
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
return "Parsing of generated source failed:\nOriginal-AST:#{original_ast.inspect}\nSource:\n#{generated_source}"
|
64
|
-
end
|
59
|
+
def error_report_original
|
60
|
+
strip(<<-MESSAGE)
|
61
|
+
Parsing of original source failed:
|
62
|
+
#{original_source}
|
63
|
+
MESSAGE
|
64
|
+
end
|
65
65
|
|
66
|
+
# Return error report for parsing generated
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
def error_report_generated
|
73
|
+
strip(<<-MESSAGE)
|
74
|
+
Parsing of generated source failed:
|
75
|
+
Original-AST:
|
76
|
+
#{original_ast.inspect}
|
77
|
+
Source:
|
78
|
+
#{generated_source}
|
79
|
+
MESSAGE
|
66
80
|
end
|
67
81
|
|
68
82
|
# Return error report with AST difference
|
@@ -72,11 +86,26 @@ module Unparser
|
|
72
86
|
# @api private
|
73
87
|
#
|
74
88
|
def error_report_with_ast_diff
|
75
|
-
|
89
|
+
strip(<<-MESSAGE)
|
90
|
+
#{diff}
|
91
|
+
Original-Source:\n#{original_source}
|
92
|
+
Original-AST:\n#{original_ast.inspect}
|
93
|
+
Generated-Source:\n#{generated_source}
|
94
|
+
Generated-AST:\n#{generated_ast.inspect}
|
95
|
+
MESSAGE
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return ast diff
|
99
|
+
#
|
100
|
+
# @return [String]
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
#
|
104
|
+
def ast_diff
|
105
|
+
Differ.call(
|
76
106
|
original_ast.inspect.lines.map(&:chomp),
|
77
107
|
generated_ast.inspect.lines.map(&:chomp)
|
78
108
|
)
|
79
|
-
"#{diff}\nOriginal:\n#{original_source}\nGenerated:\n#{generated_source}"
|
80
109
|
end
|
81
110
|
|
82
111
|
# Return generated AST
|
@@ -90,7 +119,7 @@ module Unparser
|
|
90
119
|
# @api private
|
91
120
|
#
|
92
121
|
def generated_ast
|
93
|
-
Preprocessor.run(parse(generated_source))
|
122
|
+
Preprocessor.run(parse(generated_source))
|
94
123
|
rescue Parser::SyntaxError
|
95
124
|
nil
|
96
125
|
end
|
@@ -103,7 +132,7 @@ module Unparser
|
|
103
132
|
# @api private
|
104
133
|
#
|
105
134
|
def original_ast
|
106
|
-
Preprocessor.run(parse(original_source))
|
135
|
+
Preprocessor.run(parse(original_source))
|
107
136
|
rescue Parser::SyntaxError
|
108
137
|
nil
|
109
138
|
end
|